DUNE PDELab (git)

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
15
16#include <dune/localfunctions/common/localbasis.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
25namespace Dune
26{
27namespace Functions {
28
29// A maze of dependencies between the different parts of this. We need a few forward declarations
30template<typename GV, typename R>
31class BSplineLocalFiniteElement;
32
33template<typename GV>
34class BSplinePreBasis;
35
36
45template<class GV, class R>
47{
48 friend class BSplineLocalFiniteElement<GV,R>;
49
50 typedef typename GV::ctype D;
51 enum {dim = GV::dimension};
52public:
53
57
64 : preBasis_(preBasis),
65 lFE_(lFE)
66 {}
67
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
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
153private:
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
177template<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
256public:
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
327private:
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
339template<int dim, class LB>
341{
342public:
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
361template<class GV, class R>
363{
364 typedef typename GV::ctype D;
365 enum {dim = GV::dimension};
366 friend class BSplineLocalBasis<GV,R>;
367public:
368
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
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
486template<typename GV>
487class BSplineNode;
488
498template<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
561public:
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
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
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,
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
1224template<typename GV>
1225class BSplineNode :
1226 public LeafBasisNode
1227{
1228 static const int dim = GV::dimension;
1229
1230public:
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
1265protected:
1266
1267 const BSplinePreBasis<GV>* preBasis_;
1268
1269 FiniteElement finiteElement_;
1270 Element element_;
1271};
1272
1273
1274
1275namespace BasisFactory {
1276
1283inline 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
1304template<typename GV>
1306
1307
1308} // namespace Functions
1309
1310} // namespace Dune
1311
1312#endif // DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH
Construct a matrix with a dynamic size.
Definition: dynmatrix.hh:61
void resize(size_type r, size_type c, value_type v=value_type())
resize matrix to r × c
Definition: dynmatrix.hh:106
A dense n x m matrix.
Definition: fmatrix.hh:117
vector space out of a tensor product of fields.
Definition: fvector.hh:95
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 BSplineLocalInterpolation< dim, BSplineLocalBasis< GV, R > > & localInterpolation() const
Hand out a LocalInterpolation object.
Definition: bsplinebasis.hh:440
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
const BSplineLocalCoefficients< dim > & localCoefficients() const
Hand out a LocalCoefficients object.
Definition: bsplinebasis.hh:434
void bind(const std::array< unsigned, dim > &elementIdx)
Bind LocalFiniteElement to a specific knot span of the spline patch.
Definition: bsplinebasis.hh:395
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 BSplineLocalBasis< GV, R > & localBasis() const
Hand out a LocalBasis object.
Definition: bsplinebasis.hh:428
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 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
const GridView & gridView() const
Obtain the grid view that the basis is defined on.
Definition: bsplinebasis.hh:680
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
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
Unique label for each type of entities that can occur in DUNE grids.
Definition: type.hh:114
Describe position of one degree of freedom.
Definition: localkey.hh:24
Default exception for dummy implementations.
Definition: exceptions.hh:263
Default exception class for range errors.
Definition: exceptions.hh:254
This file implements a quadratic diagonal matrix of fixed size.
This file implements a dense matrix with dynamic numbers of rows and columns.
void umv(const X &x, Y &y) const
y += A x
Definition: diagonalmatrix.hh:297
#define DUNE_THROW(E, m)
Definition: exceptions.hh:218
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
constexpr GeometryType cube(unsigned int dim)
Returns a GeometryType representing a hypercube of dimension dim.
Definition: type.hh:462
constexpr auto max
Function object that returns the greater of the given values.
Definition: hybridutilities.hh:484
constexpr T accumulate(Range &&range, T value, F &&f)
Accumulate values.
Definition: hybridutilities.hh:279
Dune namespace.
Definition: alignedallocator.hh:13
constexpr std::integer_sequence< T, II..., T(IN)> push_back(std::integer_sequence< T, II... >, std::integral_constant< T, IN >={})
Append an index IN to the back of the sequence.
Definition: integersequence.hh:69
Static tag representing a codimension.
Definition: dimension.hh:24
Type traits for LocalBasisVirtualInterface.
Definition: localbasis.hh:35
D DomainType
domain type
Definition: localbasis.hh:43
traits helper struct
Definition: localfiniteelementtraits.hh:13
A unique label for each type of element that can occur in a grid.
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Jul 15, 22:36, 2024)