DUNE-FUNCTIONS (unstable)

bsplinebasis.hh
Go to the documentation of this file.
1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH
4 #define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH
5 
10 #include <array>
11 #include <numeric>
12 
14 #include <dune/common/dynmatrix.hh>
15 
16 #include <dune/localfunctions/common/localbasis.hh>
17 #include <dune/common/diagonalmatrix.hh>
18 #include <dune/localfunctions/common/localkey.hh>
19 #include <dune/localfunctions/common/localfiniteelementtraits.hh>
20 #include <dune/geometry/type.hh>
21 #include <dune/functions/functionspacebases/nodes.hh>
22 #include <dune/functions/functionspacebases/defaultglobalbasis.hh>
23 #include <dune/functions/functionspacebases/leafprebasismixin.hh>
24 
25 namespace Dune
26 {
27 namespace Functions {
28 
29 // A maze of dependencies between the different parts of this. We need a few forward declarations
30 template<typename GV, typename R>
31 class BSplineLocalFiniteElement;
32 
33 template<typename GV>
34 class BSplinePreBasis;
35 
36 
45 template<class GV, class R>
47 {
48  friend class BSplineLocalFiniteElement<GV,R>;
49 
50  typedef typename GV::ctype D;
51  enum {dim = GV::dimension};
52 public:
53 
55  typedef LocalBasisTraits<D,dim,FieldVector<D,dim>,R,1,FieldVector<R,1>,
56  FieldMatrix<R,1,dim> > Traits;
57 
64  : preBasis_(preBasis),
65  lFE_(lFE)
66  {}
67 
71  void evaluateFunction (const FieldVector<D,dim>& in,
72  std::vector<FieldVector<R,1> >& out) const
73  {
74  FieldVector<D,dim> globalIn = offset_;
75  scaling_.umv(in,globalIn);
76 
77  preBasis_.evaluateFunction(globalIn, out, lFE_.currentKnotSpan_);
78  }
79 
83  void evaluateJacobian (const FieldVector<D,dim>& in,
84  std::vector<FieldMatrix<D,1,dim> >& out) const
85  {
86  FieldVector<D,dim> globalIn = offset_;
87  scaling_.umv(in,globalIn);
88 
89  preBasis_.evaluateJacobian(globalIn, out, lFE_.currentKnotSpan_);
90 
91  for (size_t i=0; i<out.size(); i++)
92  for (int j=0; j<dim; j++)
93  out[i][0][j] *= scaling_[j][j];
94  }
95 
97  template<size_t k>
98  inline void evaluate (const typename std::array<int,k>& directions,
99  const typename Traits::DomainType& in,
100  std::vector<typename Traits::RangeType>& out) const
101  {
102  switch(k)
103  {
104  case 0:
105  evaluateFunction(in, out);
106  break;
107  case 1:
108  {
109  FieldVector<D,dim> globalIn = offset_;
110  scaling_.umv(in,globalIn);
111 
112  preBasis_.evaluate(directions, globalIn, out, lFE_.currentKnotSpan_);
113 
114  for (size_t i=0; i<out.size(); i++)
115  out[i][0] *= scaling_[directions[0]][directions[0]];
116  break;
117  }
118  case 2:
119  {
120  FieldVector<D,dim> globalIn = offset_;
121  scaling_.umv(in,globalIn);
122 
123  preBasis_.evaluate(directions, globalIn, out, lFE_.currentKnotSpan_);
124 
125  for (size_t i=0; i<out.size(); i++)
126  out[i][0] *= scaling_[directions[0]][directions[0]]*scaling_[directions[1]][directions[1]];
127  break;
128  }
129  default:
130  DUNE_THROW(NotImplemented, "B-Spline derivatives of order " << k << " not implemented yet!");
131  }
132  }
133 
141  unsigned int order () const
142  {
143  return *std::max_element(preBasis_.order_.begin(), preBasis_.order_.end());
144  }
145 
148  std::size_t size() const
149  {
150  return lFE_.size();
151  }
152 
153 private:
154  const BSplinePreBasis<GV>& preBasis_;
155 
157 
158  // Coordinates in a single knot span differ from coordinates on the B-spline patch
159  // by an affine transformation. This transformation is stored in offset_ and scaling_.
160  FieldVector<D,dim> offset_;
161  DiagonalMatrix<D,dim> scaling_;
162 };
163 
177 template<int dim>
179 {
180  // Return i as a d-digit number in the (k+1)-nary system
181  std::array<unsigned int,dim> multiindex (unsigned int i) const
182  {
183  std::array<unsigned int,dim> alpha;
184  for (int j=0; j<dim; j++)
185  {
186  alpha[j] = i % sizes_[j];
187  i = i/sizes_[j];
188  }
189  return alpha;
190  }
191 
193  void setup1d(std::vector<unsigned int>& subEntity)
194  {
195  if (sizes_[0]==1)
196  {
197  subEntity[0] = 0;
198  return;
199  }
200 
201  /* edge and vertex numbering
202  0----0----1
203  */
204  unsigned lastIndex=0;
205  subEntity[lastIndex++] = 0; // corner 0
206  for (unsigned i = 0; i < sizes_[0] - 2; ++i)
207  subEntity[lastIndex++] = 0; // inner dofs of element (0)
208 
209  subEntity[lastIndex++] = 1; // corner 1
210 
211  assert(size()==lastIndex);
212  }
213 
214  void setup2d(std::vector<unsigned int>& subEntity)
215  {
216  unsigned lastIndex=0;
217 
218  // LocalKey: entity number , entity codim, dof indices within each entity
219  /* edge and vertex numbering
220  2----3----3
221  | |
222  | |
223  0 1
224  | |
225  | |
226  0----2----1
227  */
228 
229  // lower edge (2)
230  subEntity[lastIndex++] = 0; // corner 0
231  for (unsigned i = 0; i < sizes_[0]-2; ++i)
232  subEntity[lastIndex++] = 2; // inner dofs of lower edge (2)
233 
234  subEntity[lastIndex++] = 1; // corner 1
235 
236  // iterate from bottom to top over inner edge dofs
237  for (unsigned e = 0; e < sizes_[1]-2; ++e)
238  {
239  subEntity[lastIndex++] = 0; // left edge (0)
240  for (unsigned i = 0; i < sizes_[0]-2; ++i)
241  subEntity[lastIndex++] = 0; // face dofs
242  subEntity[lastIndex++] = 1; // right edge (1)
243  }
244 
245  // upper edge (3)
246  subEntity[lastIndex++] = 2; // corner 2
247  for (unsigned i = 0; i < sizes_[0]-2; ++i)
248  subEntity[lastIndex++] = 3; // inner dofs of upper edge (3)
249 
250  subEntity[lastIndex++] = 3; // corner 3
251 
252  assert(size()==lastIndex);
253  }
254 
255 
256 public:
257  void init(const std::array<unsigned,dim>& sizes)
258  {
259  sizes_ = sizes;
260 
261  li_.resize(size());
262 
263  // Set up array of codimension-per-dof-number
264  std::vector<unsigned int> codim(li_.size());
265 
266  for (std::size_t i=0; i<codim.size(); i++)
267  {
268  codim[i] = 0;
269  // Codimension gets increased by 1 for each coordinate direction
270  // where dof is on boundary
271  std::array<unsigned int,dim> mIdx = multiindex(i);
272  for (int j=0; j<dim; j++)
273  if (mIdx[j]==0 or mIdx[j]==sizes[j]-1)
274  codim[i]++;
275  }
276 
277  // Set up index vector (the index of the dof in the set of dofs of a given subentity)
278  // Algorithm: the 'index' has the same ordering as the dof number 'i'.
279  // To make it consecutive we interpret 'i' in the (k+1)-adic system, omit all digits
280  // that correspond to axes where the dof is on the element boundary, and transform the
281  // rest to the (k-1)-adic system.
282  std::vector<unsigned int> index(size());
283 
284  for (std::size_t i=0; i<index.size(); i++)
285  {
286  index[i] = 0;
287 
288  std::array<unsigned int,dim> mIdx = multiindex(i);
289 
290  for (int j=dim-1; j>=0; j--)
291  if (mIdx[j]>0 and mIdx[j]<sizes[j]-1)
292  index[i] = (sizes[j]-1)*index[i] + (mIdx[j]-1);
293  }
294 
295  // Set up entity and dof numbers for each (supported) dimension separately
296  std::vector<unsigned int> subEntity(li_.size());
297 
298  if (subEntity.size() > 0)
299  {
300  if (dim==1) {
301 
302  setup1d(subEntity);
303 
304  } else if (dim==2 and sizes_[0]>1 and sizes_[1]>1) {
305 
306  setup2d(subEntity);
307 
308  }
309  }
310 
311  for (size_t i=0; i<li_.size(); i++)
312  li_[i] = LocalKey(subEntity[i], codim[i], index[i]);
313  }
314 
316  std::size_t size () const
317  {
318  return std::accumulate(sizes_.begin(), sizes_.end(), 1, std::multiplies<unsigned int>());
319  }
320 
322  const LocalKey& localKey (std::size_t i) const
323  {
324  return li_[i];
325  }
326 
327 private:
328 
329  // Number of shape functions on this element per coordinate direction
330  std::array<unsigned, dim> sizes_;
331 
332  std::vector<LocalKey> li_;
333 };
334 
339 template<int dim, class LB>
341 {
342 public:
344  template<typename F, typename C>
345  void interpolate (const F& f, std::vector<C>& out) const
346  {
347  DUNE_THROW(NotImplemented, "BSplineLocalInterpolation::interpolate");
348  }
349 
350 };
351 
361 template<class GV, class R>
363 {
364  typedef typename GV::ctype D;
365  enum {dim = GV::dimension};
366  friend class BSplineLocalBasis<GV,R>;
367 public:
368 
371  typedef LocalFiniteElementTraits<BSplineLocalBasis<GV,R>,
374 
378  : preBasis_(preBasis),
379  localBasis_(preBasis,*this)
380  {}
381 
385  : preBasis_(other.preBasis_),
386  localBasis_(preBasis_,*this)
387  {}
388 
395  void bind(const std::array<unsigned,dim>& elementIdx)
396  {
397  /* \todo In the long run we need to precompute a table for this */
398  for (size_t i=0; i<elementIdx.size(); i++)
399  {
400  currentKnotSpan_[i] = 0;
401 
402  // Skip over degenerate knot spans
403  while (preBasis_.knotVectors_[i][currentKnotSpan_[i]+1] < preBasis_.knotVectors_[i][currentKnotSpan_[i]]+1e-8)
404  currentKnotSpan_[i]++;
405 
406  for (size_t j=0; j<elementIdx[i]; j++)
407  {
408  currentKnotSpan_[i]++;
409 
410  // Skip over degenerate knot spans
411  while (preBasis_.knotVectors_[i][currentKnotSpan_[i]+1] < preBasis_.knotVectors_[i][currentKnotSpan_[i]]+1e-8)
412  currentKnotSpan_[i]++;
413  }
414 
415  // Compute the geometric transformation from knotspan-local to global coordinates
416  localBasis_.offset_[i] = preBasis_.knotVectors_[i][currentKnotSpan_[i]];
417  localBasis_.scaling_[i][i] = preBasis_.knotVectors_[i][currentKnotSpan_[i]+1] - preBasis_.knotVectors_[i][currentKnotSpan_[i]];
418  }
419 
420  // Set up the LocalCoefficients object
421  std::array<unsigned int, dim> sizes;
422  for (size_t i=0; i<dim; i++)
423  sizes[i] = size(i);
424  localCoefficients_.init(sizes);
425  }
426 
429  {
430  return localBasis_;
431  }
432 
435  {
436  return localCoefficients_;
437  }
438 
441  {
442  return localInterpolation_;
443  }
444 
446  unsigned size () const
447  {
448  std::size_t r = 1;
449  for (int i=0; i<dim; i++)
450  r *= size(i);
451  return r;
452  }
453 
456  GeometryType type () const
457  {
458  return GeometryTypes::cube(dim);
459  }
460 
461 //private:
462 
464  unsigned int size(int i) const
465  {
466  const auto& order = preBasis_.order_;
467  unsigned int r = order[i]+1; // The 'normal' value
468  if (currentKnotSpan_[i]<order[i]) // Less near the left end of the knot vector
469  r -= (order[i] - currentKnotSpan_[i]);
470  if ( order[i] > (preBasis_.knotVectors_[i].size() - currentKnotSpan_[i] - 2) )
471  r -= order[i] - (preBasis_.knotVectors_[i].size() - currentKnotSpan_[i] - 2);
472  return r;
473  }
474 
475  const BSplinePreBasis<GV>& preBasis_;
476 
477  BSplineLocalBasis<GV,R> localBasis_;
478  BSplineLocalCoefficients<dim> localCoefficients_;
480 
481  // The knot span we are bound to
482  std::array<unsigned,dim> currentKnotSpan_;
483 };
484 
485 
486 template<typename GV>
487 class BSplineNode;
488 
498 template<typename GV>
500  public LeafPreBasisMixin< BSplinePreBasis<GV> >
501 {
503 
504  static const int dim = GV::dimension;
505 
507  class MultiDigitCounter
508  {
509  public:
510 
514  MultiDigitCounter(const std::array<unsigned int,dim>& limits)
515  : limits_(limits)
516  {
517  std::fill(counter_.begin(), counter_.end(), 0);
518  }
519 
521  MultiDigitCounter& operator++()
522  {
523  for (int i=0; i<dim; i++)
524  {
525  ++counter_[i];
526 
527  // no overflow?
528  if (counter_[i] < limits_[i])
529  break;
530 
531  counter_[i] = 0;
532  }
533  return *this;
534  }
535 
537  const unsigned int& operator[](int i) const
538  {
539  return counter_[i];
540  }
541 
543  unsigned int cycle() const
544  {
545  unsigned int r = 1;
546  for (int i=0; i<dim; i++)
547  r *= limits_[i];
548  return r;
549  }
550 
551  private:
552 
554  const std::array<unsigned int,dim> limits_;
555 
557  std::array<unsigned int,dim> counter_;
558 
559  };
560 
561 public:
562 
564  using GridView = GV;
565  using size_type = std::size_t;
566 
567  using Node = BSplineNode<GV>;
568 
569  // Type used for function values
570  using R = double;
571 
591  const std::vector<double>& knotVector,
592  unsigned int order,
593  bool makeOpen = true)
594  : gridView_(gridView)
595  {
596  // \todo Detection of duplicate knots
597  std::fill(elements_.begin(), elements_.end(), knotVector.size()-1);
598 
599  // Mediocre sanity check: we don't know the number of grid elements in each direction.
600  // but at least we know the total number of elements.
601  assert( std::accumulate(elements_.begin(), elements_.end(), 1, std::multiplies<unsigned>()) == gridView_.size(0) );
602 
603  for (int i=0; i<dim; i++)
604  {
605  // Prepend the correct number of additional knots to open the knot vector
607  if (makeOpen)
608  for (unsigned int j=0; j<order; j++)
609  knotVectors_[i].push_back(knotVector[0]);
610 
611  knotVectors_[i].insert(knotVectors_[i].end(), knotVector.begin(), knotVector.end());
612 
613  if (makeOpen)
614  for (unsigned int j=0; j<order; j++)
615  knotVectors_[i].push_back(knotVector.back());
616  }
617 
618  std::fill(order_.begin(), order_.end(), order);
619  }
620 
643  const FieldVector<double,dim>& lowerLeft,
644  const FieldVector<double,dim>& upperRight,
645  const std::array<unsigned int,dim>& elements,
646  unsigned int order,
647  bool makeOpen = true)
648  : elements_(elements),
649  gridView_(gridView)
650  {
651  // Mediocre sanity check: we don't know the number of grid elements in each direction.
652  // but at least we know the total number of elements.
653  assert( std::accumulate(elements_.begin(), elements_.end(), 1, std::multiplies<unsigned>()) == gridView_.size(0) );
654 
655  for (int i=0; i<dim; i++)
656  {
657  // Prepend the correct number of additional knots to open the knot vector
659  if (makeOpen)
660  for (unsigned int j=0; j<order; j++)
661  knotVectors_[i].push_back(lowerLeft[i]);
662 
663  // Construct the actual knot vector
664  for (size_t j=0; j<elements[i]+1; j++)
665  knotVectors_[i].push_back(lowerLeft[i] + j*(upperRight[i]-lowerLeft[i]) / elements[i]);
666 
667  if (makeOpen)
668  for (unsigned int j=0; j<order; j++)
669  knotVectors_[i].push_back(upperRight[i]);
670  }
671 
672  std::fill(order_.begin(), order_.end(), order);
673  }
674 
677  {}
678 
680  const GridView& gridView() const
681  {
682  return gridView_;
683  }
684 
686  void update(const GridView& gv)
687  {
688  gridView_ = gv;
689  }
690 
694  Node makeNode() const
695  {
696  return Node{this};
697  }
698 
700  size_type maxNodeSize() const
701  {
702  size_type result = 1;
703  for (int i=0; i<dim; i++)
704  result *= order_[i]+1;
705  return result;
706  }
707 
709  template<typename It>
710  It indices(const Node& node, It it) const
711  {
712  // Local degrees of freedom are arranged in a lattice.
713  // We need the lattice dimensions to be able to compute lattice coordinates from a local index
714  std::array<unsigned int, dim> localSizes;
715  for (int i=0; i<dim; i++)
716  localSizes[i] = node.finiteElement().size(i);
717  for (size_type i = 0, end = node.size() ; i < end ; ++i, ++it)
718  {
719  std::array<unsigned int,dim> localIJK = getIJK(i, localSizes);
720 
721  const auto currentKnotSpan = node.finiteElement().currentKnotSpan_;
722  const auto order = order_;
723 
724  std::array<unsigned int,dim> globalIJK;
725  for (int i=0; i<dim; i++)
726  globalIJK[i] = std::max((int)currentKnotSpan[i] - (int)order[i], 0) + localIJK[i]; // needs to be a signed type!
727 
728  // Make one global flat index from the globalIJK tuple
729  size_type globalIdx = globalIJK[dim-1];
730 
731  for (int i=dim-2; i>=0; i--)
732  globalIdx = globalIdx * size(i) + globalIJK[i];
733 
734  *it = {{globalIdx}};
735  }
736  return it;
737  }
738 
740  unsigned int dimension () const
741  {
742  unsigned int result = 1;
743  for (size_t i=0; i<dim; i++)
744  result *= size(i);
745  return result;
746  }
747 
749  unsigned int size (size_t d) const
750  {
751  return knotVectors_[d].size() - order_[d] - 1;
752  }
753 
754  using Base::size;
755 
758  void evaluateFunction (const FieldVector<typename GV::ctype,dim>& in,
759  std::vector<FieldVector<R,1> >& out,
760  const std::array<unsigned,dim>& currentKnotSpan) const
761  {
762  // Evaluate
763  std::array<std::vector<R>, dim> oneDValues;
764 
765  for (size_t i=0; i<dim; i++)
766  evaluateFunction(in[i], oneDValues[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
767 
768  std::array<unsigned int, dim> limits;
769  for (int i=0; i<dim; i++)
770  limits[i] = oneDValues[i].size();
771 
772  MultiDigitCounter ijkCounter(limits);
773 
774  out.resize(ijkCounter.cycle());
775 
776  for (size_t i=0; i<out.size(); i++, ++ijkCounter)
777  {
778  out[i] = R(1.0);
779  for (size_t j=0; j<dim; j++)
780  out[i] *= oneDValues[j][ijkCounter[j]];
781  }
782  }
783 
789  void evaluateJacobian (const FieldVector<typename GV::ctype,dim>& in,
790  std::vector<FieldMatrix<R,1,dim> >& out,
791  const std::array<unsigned,dim>& currentKnotSpan) const
792  {
793  // How many shape functions to we have in each coordinate direction?
794  std::array<unsigned int, dim> limits;
795  for (int i=0; i<dim; i++)
796  {
797  limits[i] = order_[i]+1; // The 'standard' value away from the boundaries of the knot vector
798  if (currentKnotSpan[i]<order_[i])
799  limits[i] -= (order_[i] - currentKnotSpan[i]);
800  if ( order_[i] > (knotVectors_[i].size() - currentKnotSpan[i] - 2) )
801  limits[i] -= order_[i] - (knotVectors_[i].size() - currentKnotSpan[i] - 2);
802  }
803 
804  // The lowest knot spans that we need values from
805  std::array<unsigned int, dim> offset;
806  for (int i=0; i<dim; i++)
807  offset[i] = std::max((int)(currentKnotSpan[i] - order_[i]),0);
808 
809  // Evaluate 1d function values (needed for the product rule)
810  std::array<std::vector<R>, dim> oneDValues;
811 
812  // Evaluate 1d function values of one order lower (needed for the derivative formula)
813  std::array<std::vector<R>, dim> lowOrderOneDValues;
814 
815  std::array<DynamicMatrix<R>, dim> values;
816 
817  for (size_t i=0; i<dim; i++)
818  {
819  evaluateFunctionFull(in[i], values[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
820  oneDValues[i].resize(knotVectors_[i].size()-order_[i]-1);
821  for (size_t j=0; j<oneDValues[i].size(); j++)
822  oneDValues[i][j] = values[i][order_[i]][j];
823 
824  if (order_[i]!=0)
825  {
826  lowOrderOneDValues[i].resize(knotVectors_[i].size()-(order_[i]-1)-1);
827  for (size_t j=0; j<lowOrderOneDValues[i].size(); j++)
828  lowOrderOneDValues[i][j] = values[i][order_[i]-1][j];
829  }
830  }
831 
832 
833  // Evaluate 1d function derivatives
834  std::array<std::vector<R>, dim> oneDDerivatives;
835  for (size_t i=0; i<dim; i++)
836  {
837  oneDDerivatives[i].resize(limits[i]);
838 
839  if (order_[i]==0) // order-zero functions are piecewise constant, hence all derivatives are zero
840  std::fill(oneDDerivatives[i].begin(), oneDDerivatives[i].end(), R(0.0));
841  else
842  {
843  for (size_t j=offset[i]; j<offset[i]+limits[i]; j++)
844  {
845  R derivativeAddend1 = lowOrderOneDValues[i][j] / (knotVectors_[i][j+order_[i]]-knotVectors_[i][j]);
846  R derivativeAddend2 = lowOrderOneDValues[i][j+1] / (knotVectors_[i][j+order_[i]+1]-knotVectors_[i][j+1]);
847  // The two previous terms may evaluate as 0/0. This is to be interpreted as 0.
848  if (std::isnan(derivativeAddend1))
849  derivativeAddend1 = 0;
850  if (std::isnan(derivativeAddend2))
851  derivativeAddend2 = 0;
852  oneDDerivatives[i][j-offset[i]] = order_[i] * ( derivativeAddend1 - derivativeAddend2 );
853  }
854  }
855  }
856 
857  // Working towards computing only the parts that we really need:
858  // Let's copy them out into a separate array
859  std::array<std::vector<R>, dim> oneDValuesShort;
860 
861  for (int i=0; i<dim; i++)
862  {
863  oneDValuesShort[i].resize(limits[i]);
864 
865  for (size_t j=0; j<limits[i]; j++)
866  oneDValuesShort[i][j] = oneDValues[i][offset[i] + j];
867  }
868 
869 
870 
871  // Set up a multi-index to go from consecutive indices to integer coordinates
872  MultiDigitCounter ijkCounter(limits);
873 
874  out.resize(ijkCounter.cycle());
875 
876  // Complete Jacobian is given by the product rule
877  for (size_t i=0; i<out.size(); i++, ++ijkCounter)
878  for (int j=0; j<dim; j++)
879  {
880  out[i][0][j] = 1.0;
881  for (int k=0; k<dim; k++)
882  out[i][0][j] *= (j==k) ? oneDDerivatives[k][ijkCounter[k]]
883  : oneDValuesShort[k][ijkCounter[k]];
884  }
885 
886  }
887 
889  template <size_type k>
890  void evaluate(const typename std::array<int,k>& directions,
891  const FieldVector<typename GV::ctype,dim>& in,
892  std::vector<FieldVector<R,1> >& out,
893  const std::array<unsigned,dim>& currentKnotSpan) const
894  {
895  if (k != 1 && k != 2)
896  DUNE_THROW(RangeError, "Differentiation order greater than 2 is not supported!");
897 
898  // Evaluate 1d function values (needed for the product rule)
899  std::array<std::vector<R>, dim> oneDValues;
900  std::array<std::vector<R>, dim> oneDDerivatives;
901  std::array<std::vector<R>, dim> oneDSecondDerivatives;
902 
903  // Evaluate 1d function derivatives
904  if (k==1)
905  for (size_t i=0; i<dim; i++)
906  evaluateAll(in[i], oneDValues[i], true, oneDDerivatives[i], false, oneDSecondDerivatives[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
907  else
908  for (size_t i=0; i<dim; i++)
909  evaluateAll(in[i], oneDValues[i], true, oneDDerivatives[i], true, oneDSecondDerivatives[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
910 
911  // The lowest knot spans that we need values from
912  std::array<unsigned int, dim> offset;
913  for (int i=0; i<dim; i++)
914  offset[i] = std::max((int)(currentKnotSpan[i] - order_[i]),0);
915 
916  // Set up a multi-index to go from consecutive indices to integer coordinates
917  std::array<unsigned int, dim> limits;
918  for (int i=0; i<dim; i++)
919  {
920  // In a proper implementation, the following line would do
921  //limits[i] = oneDValues[i].size();
922  limits[i] = order_[i]+1; // The 'standard' value away from the boundaries of the knot vector
923  if (currentKnotSpan[i]<order_[i])
924  limits[i] -= (order_[i] - currentKnotSpan[i]);
925  if ( order_[i] > (knotVectors_[i].size() - currentKnotSpan[i] - 2) )
926  limits[i] -= order_[i] - (knotVectors_[i].size() - currentKnotSpan[i] - 2);
927  }
928 
929  // Working towards computing only the parts that we really need:
930  // Let's copy them out into a separate array
931  std::array<std::vector<R>, dim> oneDValuesShort;
932 
933  for (int i=0; i<dim; i++)
934  {
935  oneDValuesShort[i].resize(limits[i]);
936 
937  for (size_t j=0; j<limits[i]; j++)
938  oneDValuesShort[i][j] = oneDValues[i][offset[i] + j];
939  }
940 
941 
942  MultiDigitCounter ijkCounter(limits);
943 
944  out.resize(ijkCounter.cycle());
945 
946  if (k == 1)
947  {
948  // Complete Jacobian is given by the product rule
949  for (size_t i=0; i<out.size(); i++, ++ijkCounter)
950  {
951  out[i][0] = 1.0;
952  for (int l=0; l<dim; l++)
953  out[i][0] *= (directions[0]==l) ? oneDDerivatives[l][ijkCounter[l]]
954  : oneDValuesShort[l][ijkCounter[l]];
955  }
956  }
957 
958  if (k == 2)
959  {
960  // Complete derivation by deriving the tensor product
961  for (size_t i=0; i<out.size(); i++, ++ijkCounter)
962  {
963  out[i][0] = 1.0;
964  for (int j=0; j<dim; j++)
965  {
966  if (directions[0] != directions[1]) //derivation in two different variables
967  if (directions[0] == j || directions[1] == j) //the spline has to be derived (once) in this direction
968  out[i][0] *= oneDDerivatives[j][ijkCounter[j]];
969  else //no derivation in this direction
970  out[i][0] *= oneDValuesShort[j][ijkCounter[j]];
971  else //spline is derived two times in the same direction
972  if (directions[0] == j) //the spline is derived two times in this direction
973  out[i][0] *= oneDSecondDerivatives[j][ijkCounter[j]];
974  else //no derivation in this direction
975  out[i][0] *= oneDValuesShort[j][ijkCounter[j]];
976  }
977  }
978  }
979  }
980 
981 
986  static std::array<unsigned int,dim> getIJK(typename GridView::IndexSet::IndexType idx, std::array<unsigned int,dim> elements)
987  {
988  std::array<unsigned,dim> result;
989  for (int i=0; i<dim; i++)
990  {
991  result[i] = idx%elements[i];
992  idx /= elements[i];
993  }
994  return result;
995  }
996 
1005  static void evaluateFunction (const typename GV::ctype& in, std::vector<R>& out,
1006  const std::vector<R>& knotVector,
1007  unsigned int order,
1008  unsigned int currentKnotSpan)
1009  {
1010  std::size_t outSize = order+1; // The 'standard' value away from the boundaries of the knot vector
1011  if (currentKnotSpan<order) // Less near the left end of the knot vector
1012  outSize -= (order - currentKnotSpan);
1013  if ( order > (knotVector.size() - currentKnotSpan - 2) )
1014  outSize -= order - (knotVector.size() - currentKnotSpan - 2);
1015  out.resize(outSize);
1016 
1017  // It's not really a matrix that is needed here, a plain 2d array would do
1018  DynamicMatrix<R> N(order+1, knotVector.size()-1);
1019 
1020  // The text books on splines use the following geometric condition here to fill the array N
1021  // (see for example Cottrell, Hughes, Bazilevs, Formula (2.1). However, this condition
1022  // only works if splines are never evaluated exactly on the knots.
1023  //
1024  // for (size_t i=0; i<knotVector.size()-1; i++)
1025  // N[0][i] = (knotVector[i] <= in) and (in < knotVector[i+1]);
1026  for (size_t i=0; i<knotVector.size()-1; i++)
1027  N[0][i] = (i == currentKnotSpan);
1028 
1029  for (size_t r=1; r<=order; r++)
1030  for (size_t i=0; i<knotVector.size()-r-1; i++)
1031  {
1032  R factor1 = ((knotVector[i+r] - knotVector[i]) > 1e-10)
1033  ? (in - knotVector[i]) / (knotVector[i+r] - knotVector[i])
1034  : 0;
1035  R factor2 = ((knotVector[i+r+1] - knotVector[i+1]) > 1e-10)
1036  ? (knotVector[i+r+1] - in) / (knotVector[i+r+1] - knotVector[i+1])
1037  : 0;
1038  N[r][i] = factor1 * N[r-1][i] + factor2 * N[r-1][i+1];
1039  }
1040 
1045  for (size_t i=0; i<out.size(); i++) {
1046  out[i] = N[order][std::max((int)(currentKnotSpan - order),0) + i];
1047  }
1048  }
1049 
1062  static void evaluateFunctionFull(const typename GV::ctype& in,
1063  DynamicMatrix<R>& out,
1064  const std::vector<R>& knotVector,
1065  unsigned int order,
1066  unsigned int currentKnotSpan)
1067  {
1068  // It's not really a matrix that is needed here, a plain 2d array would do
1069  DynamicMatrix<R>& N = out;
1070 
1071  N.resize(order+1, knotVector.size()-1);
1072 
1073  // The text books on splines use the following geometric condition here to fill the array N
1074  // (see for example Cottrell, Hughes, Bazilevs, Formula (2.1). However, this condition
1075  // only works if splines are never evaluated exactly on the knots.
1076  //
1077  // for (size_t i=0; i<knotVector.size()-1; i++)
1078  // N[0][i] = (knotVector[i] <= in) and (in < knotVector[i+1]);
1079  for (size_t i=0; i<knotVector.size()-1; i++)
1080  N[0][i] = (i == currentKnotSpan);
1081 
1082  for (size_t r=1; r<=order; r++)
1083  for (size_t i=0; i<knotVector.size()-r-1; i++)
1084  {
1085  R factor1 = ((knotVector[i+r] - knotVector[i]) > 1e-10)
1086  ? (in - knotVector[i]) / (knotVector[i+r] - knotVector[i])
1087  : 0;
1088  R factor2 = ((knotVector[i+r+1] - knotVector[i+1]) > 1e-10)
1089  ? (knotVector[i+r+1] - in) / (knotVector[i+r+1] - knotVector[i+1])
1090  : 0;
1091  N[r][i] = factor1 * N[r-1][i] + factor2 * N[r-1][i+1];
1092  }
1093  }
1094 
1095 
1105  static void evaluateAll(const typename GV::ctype& in,
1106  std::vector<R>& out,
1107  bool evaluateJacobian, std::vector<R>& outJac,
1108  bool evaluateHessian, std::vector<R>& outHess,
1109  const std::vector<R>& knotVector,
1110  unsigned int order,
1111  unsigned int currentKnotSpan)
1112  {
1113  // How many shape functions to we have in each coordinate direction?
1114  unsigned int limit;
1115  limit = order+1; // The 'standard' value away from the boundaries of the knot vector
1116  if (currentKnotSpan<order)
1117  limit -= (order - currentKnotSpan);
1118  if ( order > (knotVector.size() - currentKnotSpan - 2) )
1119  limit -= order - (knotVector.size() - currentKnotSpan - 2);
1120 
1121  // The lowest knot spans that we need values from
1122  unsigned int offset;
1123  offset = std::max((int)(currentKnotSpan - order),0);
1124 
1125  // Evaluate 1d function values (needed for the product rule)
1126  DynamicMatrix<R> values;
1127 
1128  evaluateFunctionFull(in, values, knotVector, order, currentKnotSpan);
1129 
1130  out.resize(knotVector.size()-order-1);
1131  for (size_t j=0; j<out.size(); j++)
1132  out[j] = values[order][j];
1133 
1134  // Evaluate 1d function values of one order lower (needed for the derivative formula)
1135  std::vector<R> lowOrderOneDValues;
1136 
1137  if (order!=0)
1138  {
1139  lowOrderOneDValues.resize(knotVector.size()-(order-1)-1);
1140  for (size_t j=0; j<lowOrderOneDValues.size(); j++)
1141  lowOrderOneDValues[j] = values[order-1][j];
1142  }
1143 
1144  // Evaluate 1d function values of two order lower (needed for the (second) derivative formula)
1145  std::vector<R> lowOrderTwoDValues;
1146 
1147  if (order>1 && evaluateHessian)
1148  {
1149  lowOrderTwoDValues.resize(knotVector.size()-(order-2)-1);
1150  for (size_t j=0; j<lowOrderTwoDValues.size(); j++)
1151  lowOrderTwoDValues[j] = values[order-2][j];
1152  }
1153 
1154  // Evaluate 1d function derivatives
1155  if (evaluateJacobian)
1156  {
1157  outJac.resize(limit);
1158 
1159  if (order==0) // order-zero functions are piecewise constant, hence all derivatives are zero
1160  std::fill(outJac.begin(), outJac.end(), R(0.0));
1161  else
1162  {
1163  for (size_t j=offset; j<offset+limit; j++)
1164  {
1165  R derivativeAddend1 = lowOrderOneDValues[j] / (knotVector[j+order]-knotVector[j]);
1166  R derivativeAddend2 = lowOrderOneDValues[j+1] / (knotVector[j+order+1]-knotVector[j+1]);
1167  // The two previous terms may evaluate as 0/0. This is to be interpreted as 0.
1168  if (std::isnan(derivativeAddend1))
1169  derivativeAddend1 = 0;
1170  if (std::isnan(derivativeAddend2))
1171  derivativeAddend2 = 0;
1172  outJac[j-offset] = order * ( derivativeAddend1 - derivativeAddend2 );
1173  }
1174  }
1175  }
1176 
1177  // Evaluate 1d function second derivatives
1178  if (evaluateHessian)
1179  {
1180  outHess.resize(limit);
1181 
1182  if (order<2) // order-zero functions are piecewise constant, hence all derivatives are zero
1183  std::fill(outHess.begin(), outHess.end(), R(0.0));
1184  else
1185  {
1186  for (size_t j=offset; j<offset+limit; j++)
1187  {
1188  assert(j+2 < lowOrderTwoDValues.size());
1189  R derivativeAddend1 = lowOrderTwoDValues[j] / (knotVector[j+order]-knotVector[j]) / (knotVector[j+order-1]-knotVector[j]);
1190  R derivativeAddend2 = lowOrderTwoDValues[j+1] / (knotVector[j+order]-knotVector[j]) / (knotVector[j+order]-knotVector[j+1]);
1191  R derivativeAddend3 = lowOrderTwoDValues[j+1] / (knotVector[j+order+1]-knotVector[j+1]) / (knotVector[j+order]-knotVector[j+1]);
1192  R derivativeAddend4 = lowOrderTwoDValues[j+2] / (knotVector[j+order+1]-knotVector[j+1]) / (knotVector[j+1+order]-knotVector[j+2]);
1193  // The two previous terms may evaluate as 0/0. This is to be interpreted as 0.
1194 
1195  if (std::isnan(derivativeAddend1))
1196  derivativeAddend1 = 0;
1197  if (std::isnan(derivativeAddend2))
1198  derivativeAddend2 = 0;
1199  if (std::isnan(derivativeAddend3))
1200  derivativeAddend3 = 0;
1201  if (std::isnan(derivativeAddend4))
1202  derivativeAddend4 = 0;
1203  outHess[j-offset] = order * (order-1) * ( derivativeAddend1 - derivativeAddend2 -derivativeAddend3 + derivativeAddend4 );
1204  }
1205  }
1206  }
1207  }
1208 
1209 
1211  std::array<unsigned int, dim> order_;
1212 
1214  std::array<std::vector<double>, dim> knotVectors_;
1215 
1217  std::array<unsigned,dim> elements_;
1218 
1219  GridView gridView_;
1220 };
1221 
1222 
1223 
1224 template<typename GV>
1225 class BSplineNode :
1226  public LeafBasisNode
1227 {
1228  static const int dim = GV::dimension;
1229 
1230 public:
1231 
1232  using size_type = std::size_t;
1233  using Element = typename GV::template Codim<0>::Entity;
1234  using FiniteElement = BSplineLocalFiniteElement<GV,double>;
1235 
1236  BSplineNode(const BSplinePreBasis<GV>* preBasis) :
1237  preBasis_(preBasis),
1238  finiteElement_(*preBasis)
1239  {}
1240 
1242  const Element& element() const
1243  {
1244  return element_;
1245  }
1246 
1251  const FiniteElement& finiteElement() const
1252  {
1253  return finiteElement_;
1254  }
1255 
1257  void bind(const Element& e)
1258  {
1259  element_ = e;
1260  auto elementIndex = preBasis_->gridView().indexSet().index(e);
1261  finiteElement_.bind(preBasis_->getIJK(elementIndex,preBasis_->elements_));
1262  this->setSize(finiteElement_.size());
1263  }
1264 
1265 protected:
1266 
1267  const BSplinePreBasis<GV>* preBasis_;
1268 
1269  FiniteElement finiteElement_;
1270  Element element_;
1271 };
1272 
1273 
1274 
1275 namespace BasisFactory {
1276 
1283 inline auto bSpline(const std::vector<double>& knotVector,
1284  unsigned int order,
1285  bool makeOpen = true)
1286 {
1287  return [&knotVector, order, makeOpen](const auto& gridView) {
1288  return BSplinePreBasis<std::decay_t<decltype(gridView)>>(gridView, knotVector, order, makeOpen);
1289  };
1290 }
1291 
1292 } // end namespace BasisFactory
1293 
1294 // *****************************************************************************
1295 // This is the actual global basis implementation based on the reusable parts.
1296 // *****************************************************************************
1297 
1304 template<typename GV>
1306 
1307 
1308 } // namespace Functions
1309 
1310 } // namespace Dune
1311 
1312 #endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH
LocalBasis class in the sense of dune-localfunctions, presenting the restriction of a B-spline patch ...
Definition: bsplinebasis.hh:47
LocalBasisTraits< D, dim, FieldVector< D, dim >, R, 1, FieldVector< R, 1 >, FieldMatrix< R, 1, dim > > Traits
export type traits for function signature
Definition: bsplinebasis.hh:56
unsigned int order() const
Polynomial order of the shape functions.
Definition: bsplinebasis.hh:141
std::size_t size() const
Return the number of basis functions on the current knot span.
Definition: bsplinebasis.hh:148
void evaluate(const typename std::array< int, k > &directions, const typename Traits::DomainType &in, std::vector< typename Traits::RangeType > &out) const
Evaluate all shape functions and derivatives of any order.
Definition: bsplinebasis.hh:98
void evaluateFunction(const FieldVector< D, dim > &in, std::vector< FieldVector< R, 1 > > &out) const
Evaluate all shape functions.
Definition: bsplinebasis.hh:71
void evaluateJacobian(const FieldVector< D, dim > &in, std::vector< FieldMatrix< D, 1, dim > > &out) const
Evaluate Jacobian of all shape functions.
Definition: bsplinebasis.hh:83
BSplineLocalBasis(const BSplinePreBasis< GV > &preBasis, const BSplineLocalFiniteElement< GV, R > &lFE)
Constructor with a given B-spline patch.
Definition: bsplinebasis.hh:62
Attaches a shape function to an entity.
Definition: bsplinebasis.hh:179
const LocalKey & localKey(std::size_t i) const
get i'th index
Definition: bsplinebasis.hh:322
std::size_t size() const
number of coefficients
Definition: bsplinebasis.hh:316
LocalFiniteElement in the sense of dune-localfunctions, for the B-spline basis on tensor-product grid...
Definition: bsplinebasis.hh:363
BSplineLocalFiniteElement(const BSplineLocalFiniteElement &other)
Copy constructor.
Definition: bsplinebasis.hh:384
const BSplineLocalBasis< GV, R > & localBasis() const
Hand out a LocalBasis object.
Definition: bsplinebasis.hh:428
LocalFiniteElementTraits< BSplineLocalBasis< GV, R >, BSplineLocalCoefficients< dim >, BSplineLocalInterpolation< dim, BSplineLocalBasis< GV, R > > > Traits
Export various types related to this LocalFiniteElement.
Definition: bsplinebasis.hh:373
BSplineLocalFiniteElement(const BSplinePreBasis< GV > &preBasis)
Constructor with a given B-spline basis.
Definition: bsplinebasis.hh:377
void bind(const std::array< unsigned, dim > &elementIdx)
Bind LocalFiniteElement to a specific knot span of the spline patch.
Definition: bsplinebasis.hh:395
const BSplineLocalCoefficients< dim > & localCoefficients() const
Hand out a LocalCoefficients object.
Definition: bsplinebasis.hh:434
GeometryType type() const
Return the reference element that the local finite element is defined on (here, a hypercube)
Definition: bsplinebasis.hh:456
unsigned size() const
Number of shape functions in this finite element.
Definition: bsplinebasis.hh:446
unsigned int size(int i) const
Number of degrees of freedom for one coordinate direction.
Definition: bsplinebasis.hh:464
const BSplineLocalInterpolation< dim, BSplineLocalBasis< GV, R > > & localInterpolation() const
Hand out a LocalInterpolation object.
Definition: bsplinebasis.hh:440
Local interpolation in the sense of dune-localfunctions, for the B-spline basis on tensor-product gri...
Definition: bsplinebasis.hh:341
void interpolate(const F &f, std::vector< C > &out) const
Local interpolation of a function.
Definition: bsplinebasis.hh:345
Pre-basis for B-spline basis.
Definition: bsplinebasis.hh:501
std::array< unsigned, dim > elements_
Number of grid elements in the different coordinate directions.
Definition: bsplinebasis.hh:1217
static std::array< unsigned int, dim > getIJK(typename GridView::IndexSet::IndexType idx, std::array< unsigned int, dim > elements)
Compute integer element coordinates from the element index.
Definition: bsplinebasis.hh:986
const GridView & gridView() const
Obtain the grid view that the basis is defined on.
Definition: bsplinebasis.hh:680
static void evaluateFunctionFull(const typename GV::ctype &in, DynamicMatrix< R > &out, const std::vector< R > &knotVector, unsigned int order, unsigned int currentKnotSpan)
Evaluate all one-dimensional B-spline functions for a given coordinate direction.
Definition: bsplinebasis.hh:1062
void evaluateFunction(const FieldVector< typename GV::ctype, dim > &in, std::vector< FieldVector< R, 1 > > &out, const std::array< unsigned, dim > &currentKnotSpan) const
Evaluate all B-spline basis functions at a given point.
Definition: bsplinebasis.hh:758
std::array< unsigned int, dim > order_
Order of the B-spline for each space dimension.
Definition: bsplinebasis.hh:1211
static void evaluateAll(const typename GV::ctype &in, std::vector< R > &out, bool evaluateJacobian, std::vector< R > &outJac, bool evaluateHessian, std::vector< R > &outHess, const std::vector< R > &knotVector, unsigned int order, unsigned int currentKnotSpan)
Evaluate the second derivatives of all one-dimensional B-spline functions for a given coordinate dire...
Definition: bsplinebasis.hh:1105
static void evaluateFunction(const typename GV::ctype &in, std::vector< R > &out, const std::vector< R > &knotVector, unsigned int order, unsigned int currentKnotSpan)
Evaluate all one-dimensional B-spline functions for a given coordinate direction.
Definition: bsplinebasis.hh:1005
unsigned int size(size_t d) const
Number of shape functions in one direction.
Definition: bsplinebasis.hh:749
GV GridView
The grid view that the FE space is defined on.
Definition: bsplinebasis.hh:564
It indices(const Node &node, It it) const
Maps from subtree index set [0..size-1] to a globally unique multi index in global basis.
Definition: bsplinebasis.hh:710
void evaluate(const typename std::array< int, k > &directions, const FieldVector< typename GV::ctype, dim > &in, std::vector< FieldVector< R, 1 > > &out, const std::array< unsigned, dim > &currentKnotSpan) const
Evaluate Derivatives of all B-spline basis functions.
Definition: bsplinebasis.hh:890
void update(const GridView &gv)
Update the stored grid view, to be called if the grid has changed.
Definition: bsplinebasis.hh:686
unsigned int dimension() const
Total number of B-spline basis functions.
Definition: bsplinebasis.hh:740
void initializeIndices()
Initialize the global indices.
Definition: bsplinebasis.hh:676
Node makeNode() const
Create tree node.
Definition: bsplinebasis.hh:694
BSplinePreBasis(const GridView &gridView, const std::vector< double > &knotVector, unsigned int order, bool makeOpen=true)
Construct a B-spline basis for a given grid view and set of knot vectors.
Definition: bsplinebasis.hh:590
void evaluateJacobian(const FieldVector< typename GV::ctype, dim > &in, std::vector< FieldMatrix< R, 1, dim > > &out, const std::array< unsigned, dim > &currentKnotSpan) const
Evaluate Jacobian of all B-spline basis functions.
Definition: bsplinebasis.hh:789
std::array< std::vector< double >, dim > knotVectors_
The knot vectors, one for each space dimension.
Definition: bsplinebasis.hh:1214
size_type maxNodeSize() const
Get the maximal number of DOFs associated to node for any element.
Definition: bsplinebasis.hh:700
BSplinePreBasis(const GridView &gridView, const FieldVector< double, dim > &lowerLeft, const FieldVector< double, dim > &upperRight, const std::array< unsigned int, dim > &elements, unsigned int order, bool makeOpen=true)
Construct a B-spline basis for a given grid view with uniform knot vectors.
Definition: bsplinebasis.hh:642
Global basis for given pre-basis.
Definition: defaultglobalbasis.hh:46
A generic MixIn class for PreBasis.
Definition: leafprebasismixin.hh:32
size_type size() const
Get the total dimension of the space spanned by this basis.
Definition: leafprebasismixin.hh:56
auto bSpline(const std::vector< double > &knotVector, unsigned int order, bool makeOpen=true)
Create a pre-basis factory that can create a B-spline pre-basis.
Definition: bsplinebasis.hh:1283
Definition: polynomial.hh:13
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.80.0 (May 16, 22:29, 2024)