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
4// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md
5// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later
6
7#ifndef DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH
8#define DUNE_FUNCTIONS_FUNCTIONSPACEBASES_BSPLINEBASIS_HH
9
14#include <array>
15#include <numeric>
16
18#include <dune/common/dynmatrix.hh>
19
20#include <dune/localfunctions/common/localbasis.hh>
21#include <dune/common/diagonalmatrix.hh>
22#include <dune/localfunctions/common/localkey.hh>
23#include <dune/localfunctions/common/localfiniteelementtraits.hh>
24#include <dune/geometry/type.hh>
25#include <dune/functions/functionspacebases/nodes.hh>
26#include <dune/functions/functionspacebases/defaultglobalbasis.hh>
27#include <dune/functions/functionspacebases/leafprebasismixin.hh>
28
29namespace Dune
30{
31namespace Functions {
32
33// A maze of dependencies between the different parts of this. We need a few forward declarations
34template<typename GV, typename R>
35class BSplineLocalFiniteElement;
36
37template<typename GV>
38class BSplinePreBasis;
39
40
49template<class GV, class R>
51{
52 friend class BSplineLocalFiniteElement<GV,R>;
53
54 typedef typename GV::ctype D;
55 enum {dim = GV::dimension};
56public:
57
59 typedef LocalBasisTraits<D,dim,FieldVector<D,dim>,R,1,FieldVector<R,1>,
60 FieldMatrix<R,1,dim> > Traits;
61
68 : preBasis_(preBasis),
69 lFE_(lFE)
70 {}
71
76 void evaluateFunction (const FieldVector<D,dim>& in,
77 std::vector<FieldVector<R,1> >& out) const
78 {
79 FieldVector<D,dim> globalIn = offset_;
80 scaling_.umv(in,globalIn);
81
82 preBasis_.evaluateFunction(globalIn, out, lFE_.currentKnotSpan_);
83 }
84
89 void evaluateJacobian (const FieldVector<D,dim>& in,
90 std::vector<FieldMatrix<D,1,dim> >& out) const
91 {
92 FieldVector<D,dim> globalIn = offset_;
93 scaling_.umv(in,globalIn);
94
95 preBasis_.evaluateJacobian(globalIn, out, lFE_.currentKnotSpan_);
96
97 for (size_t i=0; i<out.size(); i++)
98 for (int j=0; j<dim; j++)
99 out[i][0][j] *= scaling_[j][j];
100 }
101
103 template<size_t k>
104 inline void evaluate (const typename std::array<int,k>& directions,
105 const typename Traits::DomainType& in,
106 std::vector<typename Traits::RangeType>& out) const
107 {
108 switch(k)
109 {
110 case 0:
111 evaluateFunction(in, out);
112 break;
113 case 1:
114 {
115 FieldVector<D,dim> globalIn = offset_;
116 scaling_.umv(in,globalIn);
117
118 preBasis_.evaluate(directions, globalIn, out, lFE_.currentKnotSpan_);
119
120 for (size_t i=0; i<out.size(); i++)
121 out[i][0] *= scaling_[directions[0]][directions[0]];
122 break;
123 }
124 case 2:
125 {
126 FieldVector<D,dim> globalIn = offset_;
127 scaling_.umv(in,globalIn);
128
129 preBasis_.evaluate(directions, globalIn, out, lFE_.currentKnotSpan_);
130
131 for (size_t i=0; i<out.size(); i++)
132 out[i][0] *= scaling_[directions[0]][directions[0]]*scaling_[directions[1]][directions[1]];
133 break;
134 }
135 default:
136 DUNE_THROW(NotImplemented, "B-Spline derivatives of order " << k << " not implemented yet!");
137 }
138 }
139
147 unsigned int order () const
148 {
149 return *std::max_element(preBasis_.order_.begin(), preBasis_.order_.end());
150 }
151
154 std::size_t size() const
155 {
156 return lFE_.size();
157 }
158
159private:
160 const BSplinePreBasis<GV>& preBasis_;
161
163
164 // Coordinates in a single knot span differ from coordinates on the B-spline patch
165 // by an affine transformation. This transformation is stored in offset_ and scaling_.
166 FieldVector<D,dim> offset_;
167 DiagonalMatrix<D,dim> scaling_;
168};
169
183template<int dim>
185{
186 // Return i as a d-digit number in the (k+1)-nary system
187 std::array<unsigned int,dim> multiindex (unsigned int i) const
188 {
189 std::array<unsigned int,dim> alpha;
190 for (int j=0; j<dim; j++)
191 {
192 alpha[j] = i % sizes_[j];
193 i = i/sizes_[j];
194 }
195 return alpha;
196 }
197
199 void setup1d(std::vector<unsigned int>& subEntity)
200 {
201 if (sizes_[0]==1)
202 {
203 subEntity[0] = 0;
204 return;
205 }
206
207 /* edge and vertex numbering
208 0----0----1
209 */
210 unsigned lastIndex=0;
211 subEntity[lastIndex++] = 0; // corner 0
212 for (unsigned i = 0; i < sizes_[0] - 2; ++i)
213 subEntity[lastIndex++] = 0; // inner dofs of element (0)
214
215 subEntity[lastIndex++] = 1; // corner 1
216
217 assert(size()==lastIndex);
218 }
219
220 void setup2d(std::vector<unsigned int>& subEntity)
221 {
222 unsigned lastIndex=0;
223
224 // LocalKey: entity number , entity codim, dof indices within each entity
225 /* edge and vertex numbering
226 2----3----3
227 | |
228 | |
229 0 1
230 | |
231 | |
232 0----2----1
233 */
234
235 // lower edge (2)
236 subEntity[lastIndex++] = 0; // corner 0
237 for (unsigned i = 0; i < sizes_[0]-2; ++i)
238 subEntity[lastIndex++] = 2; // inner dofs of lower edge (2)
239
240 subEntity[lastIndex++] = 1; // corner 1
241
242 // iterate from bottom to top over inner edge dofs
243 for (unsigned e = 0; e < sizes_[1]-2; ++e)
244 {
245 subEntity[lastIndex++] = 0; // left edge (0)
246 for (unsigned i = 0; i < sizes_[0]-2; ++i)
247 subEntity[lastIndex++] = 0; // face dofs
248 subEntity[lastIndex++] = 1; // right edge (1)
249 }
250
251 // upper edge (3)
252 subEntity[lastIndex++] = 2; // corner 2
253 for (unsigned i = 0; i < sizes_[0]-2; ++i)
254 subEntity[lastIndex++] = 3; // inner dofs of upper edge (3)
255
256 subEntity[lastIndex++] = 3; // corner 3
257
258 assert(size()==lastIndex);
259 }
260
261
262public:
263 void init(const std::array<unsigned,dim>& sizes)
264 {
265 sizes_ = sizes;
266
267 li_.resize(size());
268
269 // Set up array of codimension-per-dof-number
270 std::vector<unsigned int> codim(li_.size());
271
272 for (std::size_t i=0; i<codim.size(); i++)
273 {
274 codim[i] = 0;
275 // Codimension gets increased by 1 for each coordinate direction
276 // where dof is on boundary
277 std::array<unsigned int,dim> mIdx = multiindex(i);
278 for (int j=0; j<dim; j++)
279 if (mIdx[j]==0 or mIdx[j]==sizes[j]-1)
280 codim[i]++;
281 }
282
283 // Set up index vector (the index of the dof in the set of dofs of a given subentity)
284 // Algorithm: the 'index' has the same ordering as the dof number 'i'.
285 // To make it consecutive we interpret 'i' in the (k+1)-adic system, omit all digits
286 // that correspond to axes where the dof is on the element boundary, and transform the
287 // rest to the (k-1)-adic system.
288 std::vector<unsigned int> index(size());
289
290 for (std::size_t i=0; i<index.size(); i++)
291 {
292 index[i] = 0;
293
294 std::array<unsigned int,dim> mIdx = multiindex(i);
295
296 for (int j=dim-1; j>=0; j--)
297 if (mIdx[j]>0 and mIdx[j]<sizes[j]-1)
298 index[i] = (sizes[j]-1)*index[i] + (mIdx[j]-1);
299 }
300
301 // Set up entity and dof numbers for each (supported) dimension separately
302 std::vector<unsigned int> subEntity(li_.size());
303
304 if (subEntity.size() > 0)
305 {
306 if (dim==1) {
307
308 setup1d(subEntity);
309
310 } else if (dim==2 and sizes_[0]>1 and sizes_[1]>1) {
311
312 setup2d(subEntity);
313
314 }
315 }
316
317 for (size_t i=0; i<li_.size(); i++)
318 li_[i] = LocalKey(subEntity[i], codim[i], index[i]);
319 }
320
322 std::size_t size () const
323 {
324 return std::accumulate(sizes_.begin(), sizes_.end(), 1, std::multiplies<unsigned int>());
325 }
326
328 const LocalKey& localKey (std::size_t i) const
329 {
330 return li_[i];
331 }
332
333private:
334
335 // Number of shape functions on this element per coordinate direction
336 std::array<unsigned, dim> sizes_;
337
338 std::vector<LocalKey> li_;
339};
340
345template<int dim, class LB>
347{
348public:
350 template<typename F, typename C>
351 void interpolate (const F& f, std::vector<C>& out) const
352 {
353 DUNE_THROW(NotImplemented, "BSplineLocalInterpolation::interpolate");
354 }
355
356};
357
367template<class GV, class R>
369{
370 typedef typename GV::ctype D;
371 enum {dim = GV::dimension};
372 friend class BSplineLocalBasis<GV,R>;
373public:
374
377 typedef LocalFiniteElementTraits<BSplineLocalBasis<GV,R>,
380
384 : preBasis_(preBasis),
385 localBasis_(preBasis,*this)
386 {}
387
391 : preBasis_(other.preBasis_),
392 localBasis_(preBasis_,*this)
393 {}
394
401 void bind(const std::array<unsigned,dim>& elementIdx)
402 {
403 /* \todo In the long run we need to precompute a table for this */
404 for (size_t i=0; i<elementIdx.size(); i++)
405 {
406 currentKnotSpan_[i] = 0;
407
408 // Skip over degenerate knot spans
409 while (preBasis_.knotVectors_[i][currentKnotSpan_[i]+1] < preBasis_.knotVectors_[i][currentKnotSpan_[i]]+1e-8)
410 currentKnotSpan_[i]++;
411
412 for (size_t j=0; j<elementIdx[i]; j++)
413 {
414 currentKnotSpan_[i]++;
415
416 // Skip over degenerate knot spans
417 while (preBasis_.knotVectors_[i][currentKnotSpan_[i]+1] < preBasis_.knotVectors_[i][currentKnotSpan_[i]]+1e-8)
418 currentKnotSpan_[i]++;
419 }
420
421 // Compute the geometric transformation from knotspan-local to global coordinates
422 localBasis_.offset_[i] = preBasis_.knotVectors_[i][currentKnotSpan_[i]];
423 localBasis_.scaling_[i][i] = preBasis_.knotVectors_[i][currentKnotSpan_[i]+1] - preBasis_.knotVectors_[i][currentKnotSpan_[i]];
424 }
425
426 // Set up the LocalCoefficients object
427 std::array<unsigned int, dim> sizes;
428 for (size_t i=0; i<dim; i++)
429 sizes[i] = size(i);
430 localCoefficients_.init(sizes);
431 }
432
435 {
436 return localBasis_;
437 }
438
441 {
442 return localCoefficients_;
443 }
444
447 {
448 return localInterpolation_;
449 }
450
452 unsigned size () const
453 {
454 std::size_t r = 1;
455 for (int i=0; i<dim; i++)
456 r *= size(i);
457 return r;
458 }
459
462 GeometryType type () const
463 {
464 return GeometryTypes::cube(dim);
465 }
466
467//private:
468
470 unsigned int size(int i) const
471 {
472 const auto& order = preBasis_.order_;
473 unsigned int r = order[i]+1; // The 'normal' value
474 if (currentKnotSpan_[i]<order[i]) // Less near the left end of the knot vector
475 r -= (order[i] - currentKnotSpan_[i]);
476 if ( order[i] > (preBasis_.knotVectors_[i].size() - currentKnotSpan_[i] - 2) )
477 r -= order[i] - (preBasis_.knotVectors_[i].size() - currentKnotSpan_[i] - 2);
478 return r;
479 }
480
481 const BSplinePreBasis<GV>& preBasis_;
482
483 BSplineLocalBasis<GV,R> localBasis_;
484 BSplineLocalCoefficients<dim> localCoefficients_;
486
487 // The knot span we are bound to
488 std::array<unsigned,dim> currentKnotSpan_;
489};
490
491
492template<typename GV>
493class BSplineNode;
494
504template<typename GV>
506 public LeafPreBasisMixin< BSplinePreBasis<GV> >
507{
509
510 static const int dim = GV::dimension;
511
513 class MultiDigitCounter
514 {
515 public:
516
520 MultiDigitCounter(const std::array<unsigned int,dim>& limits)
521 : limits_(limits)
522 {
523 std::fill(counter_.begin(), counter_.end(), 0);
524 }
525
527 MultiDigitCounter& operator++()
528 {
529 for (int i=0; i<dim; i++)
530 {
531 ++counter_[i];
532
533 // no overflow?
534 if (counter_[i] < limits_[i])
535 break;
536
537 counter_[i] = 0;
538 }
539 return *this;
540 }
541
543 const unsigned int& operator[](int i) const
544 {
545 return counter_[i];
546 }
547
549 unsigned int cycle() const
550 {
551 unsigned int r = 1;
552 for (int i=0; i<dim; i++)
553 r *= limits_[i];
554 return r;
555 }
556
557 private:
558
560 const std::array<unsigned int,dim> limits_;
561
563 std::array<unsigned int,dim> counter_;
564
565 };
566
567public:
568
570 using GridView = GV;
571 using size_type = std::size_t;
572
573 using Node = BSplineNode<GV>;
574
575 // Type used for function values
576 using R = double;
577
598 const std::vector<double>& knotVector,
599 unsigned int order,
600 bool makeOpen = true)
601 : gridView_(gridView)
602 {
603 // \todo Detection of duplicate knots
604 std::fill(elements_.begin(), elements_.end(), knotVector.size()-1);
605
606 // Mediocre sanity check: we don't know the number of grid elements in each direction.
607 // but at least we know the total number of elements.
608 assert( std::accumulate(elements_.begin(), elements_.end(), 1, std::multiplies<unsigned>()) == gridView_.size(0) );
609
610 for (int i=0; i<dim; i++)
611 {
612 // Prepend the correct number of additional knots to open the knot vector
614 if (makeOpen)
615 for (unsigned int j=0; j<order; j++)
616 knotVectors_[i].push_back(knotVector[0]);
617
618 knotVectors_[i].insert(knotVectors_[i].end(), knotVector.begin(), knotVector.end());
619
620 if (makeOpen)
621 for (unsigned int j=0; j<order; j++)
622 knotVectors_[i].push_back(knotVector.back());
623 }
624
625 std::fill(order_.begin(), order_.end(), order);
626 }
627
650 const FieldVector<double,dim>& lowerLeft,
651 const FieldVector<double,dim>& upperRight,
652 const std::array<unsigned int,dim>& elements,
653 unsigned int order,
654 bool makeOpen = true)
656 gridView_(gridView)
657 {
658 // Mediocre sanity check: we don't know the number of grid elements in each direction.
659 // but at least we know the total number of elements.
660 assert( std::accumulate(elements_.begin(), elements_.end(), 1, std::multiplies<unsigned>()) == gridView_.size(0) );
661
662 for (int i=0; i<dim; i++)
663 {
664 // Prepend the correct number of additional knots to open the knot vector
666 if (makeOpen)
667 for (unsigned int j=0; j<order; j++)
668 knotVectors_[i].push_back(lowerLeft[i]);
669
670 // Construct the actual knot vector
671 for (size_t j=0; j<elements[i]+1; j++)
672 knotVectors_[i].push_back(lowerLeft[i] + j*(upperRight[i]-lowerLeft[i]) / elements[i]);
673
674 if (makeOpen)
675 for (unsigned int j=0; j<order; j++)
676 knotVectors_[i].push_back(upperRight[i]);
677 }
678
679 std::fill(order_.begin(), order_.end(), order);
680 }
681
684 {}
685
687 const GridView& gridView() const
688 {
689 return gridView_;
690 }
691
693 void update(const GridView& gv)
694 {
695 gridView_ = gv;
696 }
697
701 Node makeNode() const
702 {
703 return Node{this};
704 }
705
707 size_type maxNodeSize() const
708 {
709 size_type result = 1;
710 for (int i=0; i<dim; i++)
711 result *= order_[i]+1;
712 return result;
713 }
714
716 template<typename It>
717 It indices(const Node& node, It it) const
718 {
719 // Local degrees of freedom are arranged in a lattice.
720 // We need the lattice dimensions to be able to compute lattice coordinates from a local index
721 std::array<unsigned int, dim> localSizes;
722 for (int i=0; i<dim; i++)
723 localSizes[i] = node.finiteElement().size(i);
724 for (size_type i = 0, end = node.size() ; i < end ; ++i, ++it)
725 {
726 std::array<unsigned int,dim> localIJK = getIJK(i, localSizes);
727
728 const auto currentKnotSpan = node.finiteElement().currentKnotSpan_;
729 const auto order = order_;
730
731 std::array<unsigned int,dim> globalIJK;
732 for (int i=0; i<dim; i++)
733 globalIJK[i] = std::max((int)currentKnotSpan[i] - (int)order[i], 0) + localIJK[i]; // needs to be a signed type!
734
735 // Make one global flat index from the globalIJK tuple
736 size_type globalIdx = globalIJK[dim-1];
737
738 for (int i=dim-2; i>=0; i--)
739 globalIdx = globalIdx * size(i) + globalIJK[i];
740
741 *it = {{globalIdx}};
742 }
743 return it;
744 }
745
747 unsigned int dimension () const
748 {
749 unsigned int result = 1;
750 for (size_t i=0; i<dim; i++)
751 result *= size(i);
752 return result;
753 }
754
756 unsigned int size (size_t d) const
757 {
758 return knotVectors_[d].size() - order_[d] - 1;
759 }
760
761 using Base::size;
762
765 void evaluateFunction (const FieldVector<typename GV::ctype,dim>& in,
766 std::vector<FieldVector<R,1> >& out,
767 const std::array<unsigned,dim>& currentKnotSpan) const
768 {
769 // Evaluate
770 std::array<std::vector<R>, dim> oneDValues;
771
772 for (size_t i=0; i<dim; i++)
773 evaluateFunction(in[i], oneDValues[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
774
775 std::array<unsigned int, dim> limits;
776 for (int i=0; i<dim; i++)
777 limits[i] = oneDValues[i].size();
778
779 MultiDigitCounter ijkCounter(limits);
780
781 out.resize(ijkCounter.cycle());
782
783 for (size_t i=0; i<out.size(); i++, ++ijkCounter)
784 {
785 out[i] = R(1.0);
786 for (size_t j=0; j<dim; j++)
787 out[i] *= oneDValues[j][ijkCounter[j]];
788 }
789 }
790
796 void evaluateJacobian (const FieldVector<typename GV::ctype,dim>& in,
797 std::vector<FieldMatrix<R,1,dim> >& out,
798 const std::array<unsigned,dim>& currentKnotSpan) const
799 {
800 // How many shape functions to we have in each coordinate direction?
801 std::array<unsigned int, dim> limits;
802 for (int i=0; i<dim; i++)
803 {
804 limits[i] = order_[i]+1; // The 'standard' value away from the boundaries of the knot vector
805 if (currentKnotSpan[i]<order_[i])
806 limits[i] -= (order_[i] - currentKnotSpan[i]);
807 if ( order_[i] > (knotVectors_[i].size() - currentKnotSpan[i] - 2) )
808 limits[i] -= order_[i] - (knotVectors_[i].size() - currentKnotSpan[i] - 2);
809 }
810
811 // The lowest knot spans that we need values from
812 std::array<unsigned int, dim> offset;
813 for (int i=0; i<dim; i++)
814 offset[i] = std::max((int)(currentKnotSpan[i] - order_[i]),0);
815
816 // Evaluate 1d function values (needed for the product rule)
817 std::array<std::vector<R>, dim> oneDValues;
818
819 // Evaluate 1d function values of one order lower (needed for the derivative formula)
820 std::array<std::vector<R>, dim> lowOrderOneDValues;
821
822 std::array<DynamicMatrix<R>, dim> values;
823
824 for (size_t i=0; i<dim; i++)
825 {
826 evaluateFunctionFull(in[i], values[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
827 oneDValues[i].resize(knotVectors_[i].size()-order_[i]-1);
828 for (size_t j=0; j<oneDValues[i].size(); j++)
829 oneDValues[i][j] = values[i][order_[i]][j];
830
831 if (order_[i]!=0)
832 {
833 lowOrderOneDValues[i].resize(knotVectors_[i].size()-(order_[i]-1)-1);
834 for (size_t j=0; j<lowOrderOneDValues[i].size(); j++)
835 lowOrderOneDValues[i][j] = values[i][order_[i]-1][j];
836 }
837 }
838
839
840 // Evaluate 1d function derivatives
841 std::array<std::vector<R>, dim> oneDDerivatives;
842 for (size_t i=0; i<dim; i++)
843 {
844 oneDDerivatives[i].resize(limits[i]);
845
846 if (order_[i]==0) // order-zero functions are piecewise constant, hence all derivatives are zero
847 std::fill(oneDDerivatives[i].begin(), oneDDerivatives[i].end(), R(0.0));
848 else
849 {
850 for (size_t j=offset[i]; j<offset[i]+limits[i]; j++)
851 {
852 R derivativeAddend1 = lowOrderOneDValues[i][j] / (knotVectors_[i][j+order_[i]]-knotVectors_[i][j]);
853 R derivativeAddend2 = lowOrderOneDValues[i][j+1] / (knotVectors_[i][j+order_[i]+1]-knotVectors_[i][j+1]);
854 // The two previous terms may evaluate as 0/0. This is to be interpreted as 0.
855 if (std::isnan(derivativeAddend1))
856 derivativeAddend1 = 0;
857 if (std::isnan(derivativeAddend2))
858 derivativeAddend2 = 0;
859 oneDDerivatives[i][j-offset[i]] = order_[i] * ( derivativeAddend1 - derivativeAddend2 );
860 }
861 }
862 }
863
864 // Working towards computing only the parts that we really need:
865 // Let's copy them out into a separate array
866 std::array<std::vector<R>, dim> oneDValuesShort;
867
868 for (int i=0; i<dim; i++)
869 {
870 oneDValuesShort[i].resize(limits[i]);
871
872 for (size_t j=0; j<limits[i]; j++)
873 oneDValuesShort[i][j] = oneDValues[i][offset[i] + j];
874 }
875
876
877
878 // Set up a multi-index to go from consecutive indices to integer coordinates
879 MultiDigitCounter ijkCounter(limits);
880
881 out.resize(ijkCounter.cycle());
882
883 // Complete Jacobian is given by the product rule
884 for (size_t i=0; i<out.size(); i++, ++ijkCounter)
885 for (int j=0; j<dim; j++)
886 {
887 out[i][0][j] = 1.0;
888 for (int k=0; k<dim; k++)
889 out[i][0][j] *= (j==k) ? oneDDerivatives[k][ijkCounter[k]]
890 : oneDValuesShort[k][ijkCounter[k]];
891 }
892
893 }
894
896 template <size_type k>
897 void evaluate(const typename std::array<int,k>& directions,
898 const FieldVector<typename GV::ctype,dim>& in,
899 std::vector<FieldVector<R,1> >& out,
900 const std::array<unsigned,dim>& currentKnotSpan) const
901 {
902 if (k != 1 && k != 2)
903 DUNE_THROW(RangeError, "Differentiation order greater than 2 is not supported!");
904
905 // Evaluate 1d function values (needed for the product rule)
906 std::array<std::vector<R>, dim> oneDValues;
907 std::array<std::vector<R>, dim> oneDDerivatives;
908 std::array<std::vector<R>, dim> oneDSecondDerivatives;
909
910 // Evaluate 1d function derivatives
911 if (k==1)
912 for (size_t i=0; i<dim; i++)
913 evaluateAll(in[i], oneDValues[i], true, oneDDerivatives[i], false, oneDSecondDerivatives[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
914 else
915 for (size_t i=0; i<dim; i++)
916 evaluateAll(in[i], oneDValues[i], true, oneDDerivatives[i], true, oneDSecondDerivatives[i], knotVectors_[i], order_[i], currentKnotSpan[i]);
917
918 // The lowest knot spans that we need values from
919 std::array<unsigned int, dim> offset;
920 for (int i=0; i<dim; i++)
921 offset[i] = std::max((int)(currentKnotSpan[i] - order_[i]),0);
922
923 // Set up a multi-index to go from consecutive indices to integer coordinates
924 std::array<unsigned int, dim> limits;
925 for (int i=0; i<dim; i++)
926 {
927 // In a proper implementation, the following line would do
928 //limits[i] = oneDValues[i].size();
929 limits[i] = order_[i]+1; // The 'standard' value away from the boundaries of the knot vector
930 if (currentKnotSpan[i]<order_[i])
931 limits[i] -= (order_[i] - currentKnotSpan[i]);
932 if ( order_[i] > (knotVectors_[i].size() - currentKnotSpan[i] - 2) )
933 limits[i] -= order_[i] - (knotVectors_[i].size() - currentKnotSpan[i] - 2);
934 }
935
936 // Working towards computing only the parts that we really need:
937 // Let's copy them out into a separate array
938 std::array<std::vector<R>, dim> oneDValuesShort;
939
940 for (int i=0; i<dim; i++)
941 {
942 oneDValuesShort[i].resize(limits[i]);
943
944 for (size_t j=0; j<limits[i]; j++)
945 oneDValuesShort[i][j] = oneDValues[i][offset[i] + j];
946 }
947
948
949 MultiDigitCounter ijkCounter(limits);
950
951 out.resize(ijkCounter.cycle());
952
953 if (k == 1)
954 {
955 // Complete Jacobian is given by the product rule
956 for (size_t i=0; i<out.size(); i++, ++ijkCounter)
957 {
958 out[i][0] = 1.0;
959 for (int l=0; l<dim; l++)
960 out[i][0] *= (directions[0]==l) ? oneDDerivatives[l][ijkCounter[l]]
961 : oneDValuesShort[l][ijkCounter[l]];
962 }
963 }
964
965 if (k == 2)
966 {
967 // Complete derivation by deriving the tensor product
968 for (size_t i=0; i<out.size(); i++, ++ijkCounter)
969 {
970 out[i][0] = 1.0;
971 for (int j=0; j<dim; j++)
972 {
973 if (directions[0] != directions[1]) //derivation in two different variables
974 if (directions[0] == j || directions[1] == j) //the spline has to be derived (once) in this direction
975 out[i][0] *= oneDDerivatives[j][ijkCounter[j]];
976 else //no derivation in this direction
977 out[i][0] *= oneDValuesShort[j][ijkCounter[j]];
978 else //spline is derived two times in the same direction
979 if (directions[0] == j) //the spline is derived two times in this direction
980 out[i][0] *= oneDSecondDerivatives[j][ijkCounter[j]];
981 else //no derivation in this direction
982 out[i][0] *= oneDValuesShort[j][ijkCounter[j]];
983 }
984 }
985 }
986 }
987
988
993 static std::array<unsigned int,dim> getIJK(typename GridView::IndexSet::IndexType idx, std::array<unsigned int,dim> elements)
994 {
995 std::array<unsigned,dim> result;
996 for (int i=0; i<dim; i++)
997 {
998 result[i] = idx%elements[i];
999 idx /= elements[i];
1000 }
1001 return result;
1002 }
1003
1012 static void evaluateFunction (const typename GV::ctype& in, std::vector<R>& out,
1013 const std::vector<R>& knotVector,
1014 unsigned int order,
1015 unsigned int currentKnotSpan)
1016 {
1017 std::size_t outSize = order+1; // The 'standard' value away from the boundaries of the knot vector
1018 if (currentKnotSpan<order) // Less near the left end of the knot vector
1019 outSize -= (order - currentKnotSpan);
1020 if ( order > (knotVector.size() - currentKnotSpan - 2) )
1021 outSize -= order - (knotVector.size() - currentKnotSpan - 2);
1022 out.resize(outSize);
1023
1024 // It's not really a matrix that is needed here, a plain 2d array would do
1025 DynamicMatrix<R> N(order+1, knotVector.size()-1);
1026
1027 // The text books on splines use the following geometric condition here to fill the array N
1028 // (see for example Cottrell, Hughes, Bazilevs, Formula (2.1). However, this condition
1029 // only works if splines are never evaluated exactly on the knots.
1030 //
1031 // for (size_t i=0; i<knotVector.size()-1; i++)
1032 // N[0][i] = (knotVector[i] <= in) and (in < knotVector[i+1]);
1033 for (size_t i=0; i<knotVector.size()-1; i++)
1034 N[0][i] = (i == currentKnotSpan);
1035
1036 for (size_t r=1; r<=order; r++)
1037 for (size_t i=0; i<knotVector.size()-r-1; i++)
1038 {
1039 R factor1 = ((knotVector[i+r] - knotVector[i]) > 1e-10)
1040 ? (in - knotVector[i]) / (knotVector[i+r] - knotVector[i])
1041 : 0;
1042 R factor2 = ((knotVector[i+r+1] - knotVector[i+1]) > 1e-10)
1043 ? (knotVector[i+r+1] - in) / (knotVector[i+r+1] - knotVector[i+1])
1044 : 0;
1045 N[r][i] = factor1 * N[r-1][i] + factor2 * N[r-1][i+1];
1046 }
1047
1052 for (size_t i=0; i<out.size(); i++) {
1053 out[i] = N[order][std::max((int)(currentKnotSpan - order),0) + i];
1054 }
1055 }
1056
1069 static void evaluateFunctionFull(const typename GV::ctype& in,
1070 DynamicMatrix<R>& out,
1071 const std::vector<R>& knotVector,
1072 unsigned int order,
1073 unsigned int currentKnotSpan)
1074 {
1075 // It's not really a matrix that is needed here, a plain 2d array would do
1076 DynamicMatrix<R>& N = out;
1077
1078 N.resize(order+1, knotVector.size()-1);
1079
1080 // The text books on splines use the following geometric condition here to fill the array N
1081 // (see for example Cottrell, Hughes, Bazilevs, Formula (2.1). However, this condition
1082 // only works if splines are never evaluated exactly on the knots.
1083 //
1084 // for (size_t i=0; i<knotVector.size()-1; i++)
1085 // N[0][i] = (knotVector[i] <= in) and (in < knotVector[i+1]);
1086 for (size_t i=0; i<knotVector.size()-1; i++)
1087 N[0][i] = (i == currentKnotSpan);
1088
1089 for (size_t r=1; r<=order; r++)
1090 for (size_t i=0; i<knotVector.size()-r-1; i++)
1091 {
1092 R factor1 = ((knotVector[i+r] - knotVector[i]) > 1e-10)
1093 ? (in - knotVector[i]) / (knotVector[i+r] - knotVector[i])
1094 : 0;
1095 R factor2 = ((knotVector[i+r+1] - knotVector[i+1]) > 1e-10)
1096 ? (knotVector[i+r+1] - in) / (knotVector[i+r+1] - knotVector[i+1])
1097 : 0;
1098 N[r][i] = factor1 * N[r-1][i] + factor2 * N[r-1][i+1];
1099 }
1100 }
1101
1102
1111 static void evaluateAll(const typename GV::ctype& in,
1112 std::vector<R>& out,
1113 bool evaluateJacobian, std::vector<R>& outJac,
1114 bool evaluateHessian, std::vector<R>& outHess,
1115 const std::vector<R>& knotVector,
1116 unsigned int order,
1117 unsigned int currentKnotSpan)
1118 {
1119 // How many shape functions to we have in each coordinate direction?
1120 unsigned int limit;
1121 limit = order+1; // The 'standard' value away from the boundaries of the knot vector
1122 if (currentKnotSpan<order)
1123 limit -= (order - currentKnotSpan);
1124 if ( order > (knotVector.size() - currentKnotSpan - 2) )
1125 limit -= order - (knotVector.size() - currentKnotSpan - 2);
1126
1127 // The lowest knot spans that we need values from
1128 unsigned int offset;
1129 offset = std::max((int)(currentKnotSpan - order),0);
1130
1131 // Evaluate 1d function values (needed for the product rule)
1132 DynamicMatrix<R> values;
1133
1134 evaluateFunctionFull(in, values, knotVector, order, currentKnotSpan);
1135
1136 out.resize(knotVector.size()-order-1);
1137 for (size_t j=0; j<out.size(); j++)
1138 out[j] = values[order][j];
1139
1140 // Evaluate 1d function values of one order lower (needed for the derivative formula)
1141 std::vector<R> lowOrderOneDValues;
1142
1143 if (order!=0)
1144 {
1145 lowOrderOneDValues.resize(knotVector.size()-(order-1)-1);
1146 for (size_t j=0; j<lowOrderOneDValues.size(); j++)
1147 lowOrderOneDValues[j] = values[order-1][j];
1148 }
1149
1150 // Evaluate 1d function values of two order lower (needed for the (second) derivative formula)
1151 std::vector<R> lowOrderTwoDValues;
1152
1153 if (order>1 && evaluateHessian)
1154 {
1155 lowOrderTwoDValues.resize(knotVector.size()-(order-2)-1);
1156 for (size_t j=0; j<lowOrderTwoDValues.size(); j++)
1157 lowOrderTwoDValues[j] = values[order-2][j];
1158 }
1159
1160 // Evaluate 1d function derivatives
1161 if (evaluateJacobian)
1162 {
1163 outJac.resize(limit);
1164
1165 if (order==0) // order-zero functions are piecewise constant, hence all derivatives are zero
1166 std::fill(outJac.begin(), outJac.end(), R(0.0));
1167 else
1168 {
1169 for (size_t j=offset; j<offset+limit; j++)
1170 {
1171 R derivativeAddend1 = lowOrderOneDValues[j] / (knotVector[j+order]-knotVector[j]);
1172 R derivativeAddend2 = lowOrderOneDValues[j+1] / (knotVector[j+order+1]-knotVector[j+1]);
1173 // The two previous terms may evaluate as 0/0. This is to be interpreted as 0.
1174 if (std::isnan(derivativeAddend1))
1175 derivativeAddend1 = 0;
1176 if (std::isnan(derivativeAddend2))
1177 derivativeAddend2 = 0;
1178 outJac[j-offset] = order * ( derivativeAddend1 - derivativeAddend2 );
1179 }
1180 }
1181 }
1182
1183 // Evaluate 1d function second derivatives
1184 if (evaluateHessian)
1185 {
1186 outHess.resize(limit);
1187
1188 if (order<2) // order-zero functions are piecewise constant, hence all derivatives are zero
1189 std::fill(outHess.begin(), outHess.end(), R(0.0));
1190 else
1191 {
1192 for (size_t j=offset; j<offset+limit; j++)
1193 {
1194 assert(j+2 < lowOrderTwoDValues.size());
1195 R derivativeAddend1 = lowOrderTwoDValues[j] / (knotVector[j+order]-knotVector[j]) / (knotVector[j+order-1]-knotVector[j]);
1196 R derivativeAddend2 = lowOrderTwoDValues[j+1] / (knotVector[j+order]-knotVector[j]) / (knotVector[j+order]-knotVector[j+1]);
1197 R derivativeAddend3 = lowOrderTwoDValues[j+1] / (knotVector[j+order+1]-knotVector[j+1]) / (knotVector[j+order]-knotVector[j+1]);
1198 R derivativeAddend4 = lowOrderTwoDValues[j+2] / (knotVector[j+order+1]-knotVector[j+1]) / (knotVector[j+1+order]-knotVector[j+2]);
1199 // The two previous terms may evaluate as 0/0. This is to be interpreted as 0.
1200
1201 if (std::isnan(derivativeAddend1))
1202 derivativeAddend1 = 0;
1203 if (std::isnan(derivativeAddend2))
1204 derivativeAddend2 = 0;
1205 if (std::isnan(derivativeAddend3))
1206 derivativeAddend3 = 0;
1207 if (std::isnan(derivativeAddend4))
1208 derivativeAddend4 = 0;
1209 outHess[j-offset] = order * (order-1) * ( derivativeAddend1 - derivativeAddend2 -derivativeAddend3 + derivativeAddend4 );
1210 }
1211 }
1212 }
1213 }
1214
1215
1217 std::array<unsigned int, dim> order_;
1218
1220 std::array<std::vector<double>, dim> knotVectors_;
1221
1223 std::array<unsigned,dim> elements_;
1224
1225 GridView gridView_;
1226};
1227
1228
1229
1230template<typename GV>
1231class BSplineNode :
1232 public LeafBasisNode
1233{
1234 static const int dim = GV::dimension;
1235
1236public:
1237
1238 using size_type = std::size_t;
1239 using Element = typename GV::template Codim<0>::Entity;
1240 using FiniteElement = BSplineLocalFiniteElement<GV,double>;
1241
1242 BSplineNode(const BSplinePreBasis<GV>* preBasis) :
1243 preBasis_(preBasis),
1244 finiteElement_(*preBasis)
1245 {}
1246
1248 const Element& element() const
1249 {
1250 return element_;
1251 }
1252
1257 const FiniteElement& finiteElement() const
1258 {
1259 return finiteElement_;
1260 }
1261
1263 void bind(const Element& e)
1264 {
1265 element_ = e;
1266 auto elementIndex = preBasis_->gridView().indexSet().index(e);
1267 finiteElement_.bind(preBasis_->getIJK(elementIndex,preBasis_->elements_));
1268 this->setSize(finiteElement_.size());
1269 }
1270
1271protected:
1272
1273 const BSplinePreBasis<GV>* preBasis_;
1274
1275 FiniteElement finiteElement_;
1276 Element element_;
1277};
1278
1279
1280
1281namespace BasisFactory {
1282
1289inline auto bSpline(const std::vector<double>& knotVector,
1290 unsigned int order,
1291 bool makeOpen = true)
1292{
1293 return [&knotVector, order, makeOpen](const auto& gridView) {
1294 return BSplinePreBasis<std::decay_t<decltype(gridView)>>(gridView, knotVector, order, makeOpen);
1295 };
1296}
1297
1298} // end namespace BasisFactory
1299
1300// *****************************************************************************
1301// This is the actual global basis implementation based on the reusable parts.
1302// *****************************************************************************
1303
1310template<typename GV>
1312
1313
1314} // namespace Functions
1315
1316} // namespace Dune
1317
1318#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:51
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:60
unsigned int order() const
Polynomial order of the shape functions.
Definition: bsplinebasis.hh:147
std::size_t size() const
Return the number of basis functions on the current knot span.
Definition: bsplinebasis.hh:154
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:104
void evaluateFunction(const FieldVector< D, dim > &in, std::vector< FieldVector< R, 1 > > &out) const
Evaluate all shape functions.
Definition: bsplinebasis.hh:76
void evaluateJacobian(const FieldVector< D, dim > &in, std::vector< FieldMatrix< D, 1, dim > > &out) const
Evaluate Jacobian of all shape functions.
Definition: bsplinebasis.hh:89
BSplineLocalBasis(const BSplinePreBasis< GV > &preBasis, const BSplineLocalFiniteElement< GV, R > &lFE)
Constructor with a given B-spline patch.
Definition: bsplinebasis.hh:66
Attaches a shape function to an entity.
Definition: bsplinebasis.hh:185
const LocalKey & localKey(std::size_t i) const
get i'th index
Definition: bsplinebasis.hh:328
std::size_t size() const
number of coefficients
Definition: bsplinebasis.hh:322
LocalFiniteElement in the sense of dune-localfunctions, for the B-spline basis on tensor-product grid...
Definition: bsplinebasis.hh:369
BSplineLocalFiniteElement(const BSplineLocalFiniteElement &other)
Copy constructor.
Definition: bsplinebasis.hh:390
const BSplineLocalInterpolation< dim, BSplineLocalBasis< GV, R > > & localInterpolation() const
Hand out a LocalInterpolation object.
Definition: bsplinebasis.hh:446
LocalFiniteElementTraits< BSplineLocalBasis< GV, R >, BSplineLocalCoefficients< dim >, BSplineLocalInterpolation< dim, BSplineLocalBasis< GV, R > > > Traits
Export various types related to this LocalFiniteElement.
Definition: bsplinebasis.hh:379
BSplineLocalFiniteElement(const BSplinePreBasis< GV > &preBasis)
Constructor with a given B-spline basis.
Definition: bsplinebasis.hh:383
const BSplineLocalCoefficients< dim > & localCoefficients() const
Hand out a LocalCoefficients object.
Definition: bsplinebasis.hh:440
void bind(const std::array< unsigned, dim > &elementIdx)
Bind LocalFiniteElement to a specific knot span of the spline patch.
Definition: bsplinebasis.hh:401
GeometryType type() const
Return the reference element that the local finite element is defined on (here, a hypercube)
Definition: bsplinebasis.hh:462
unsigned size() const
Number of shape functions in this finite element.
Definition: bsplinebasis.hh:452
unsigned int size(int i) const
Number of degrees of freedom for one coordinate direction.
Definition: bsplinebasis.hh:470
const BSplineLocalBasis< GV, R > & localBasis() const
Hand out a LocalBasis object.
Definition: bsplinebasis.hh:434
Local interpolation in the sense of dune-localfunctions, for the B-spline basis on tensor-product gri...
Definition: bsplinebasis.hh:347
void interpolate(const F &f, std::vector< C > &out) const
Local interpolation of a function.
Definition: bsplinebasis.hh:351
Pre-basis for B-spline basis.
Definition: bsplinebasis.hh:507
std::array< unsigned, dim > elements_
Number of grid elements in the different coordinate directions.
Definition: bsplinebasis.hh:1223
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:1069
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:765
std::array< unsigned int, dim > order_
Order of the B-spline for each space dimension.
Definition: bsplinebasis.hh:1217
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:1111
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:1012
unsigned int size(size_t d) const
Number of shape functions in one direction.
Definition: bsplinebasis.hh:756
GV GridView
The grid view that the FE space is defined on.
Definition: bsplinebasis.hh:570
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:717
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:897
void update(const GridView &gv)
Update the stored grid view, to be called if the grid has changed.
Definition: bsplinebasis.hh:693
unsigned int dimension() const
Total number of B-spline basis functions.
Definition: bsplinebasis.hh:747
void initializeIndices()
Initialize the global indices.
Definition: bsplinebasis.hh:683
const GridView & gridView() const
Obtain the grid view that the basis is defined on.
Definition: bsplinebasis.hh:687
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:993
Node makeNode() const
Create tree node.
Definition: bsplinebasis.hh:701
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:597
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:796
std::array< std::vector< double >, dim > knotVectors_
The knot vectors, one for each space dimension.
Definition: bsplinebasis.hh:1220
size_type maxNodeSize() const
Get the maximal number of DOFs associated to node for any element.
Definition: bsplinebasis.hh:707
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:649
Global basis for given pre-basis.
Definition: defaultglobalbasis.hh:53
A generic MixIn class for PreBasis.
Definition: leafprebasismixin.hh:36
size_type size() const
Get the total dimension of the space spanned by this basis.
Definition: leafprebasismixin.hh:60
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:1289
auto elements(const SubDomainGridView< HostGridView > &subDomainGridView)
ADL findable access to element range for a SubDomainGridView.
Definition: subdomain.hh:487
Definition: monomialset.hh:19
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden & Uni Heidelberg  |  generated with Hugo v0.111.3 (Jan 9, 23:34, 2026)