DUNE PDELab (unstable)

lagrangesimplex.hh
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
4// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
5#ifndef DUNE_LOCALFUNCTIONS_LAGRANGE_LAGRANGESIMPLEX_HH
6#define DUNE_LOCALFUNCTIONS_LAGRANGE_LAGRANGESIMPLEX_HH
7
8#include <array>
9#include <cstddef>
10#include <numeric>
11#include <algorithm>
12#include <span>
13#include <type_traits>
14
18#include <dune/common/hybridutilities.hh>
19#include <dune/common/math.hh>
21#include <dune/common/std/mdarray.hh>
22#include <dune/common/std/mdspan.hh>
23
24#include <dune/geometry/referenceelements.hh>
25
26#include <dune/localfunctions/common/localbasis.hh>
27#include <dune/localfunctions/common/localfiniteelementtraits.hh>
28#include <dune/localfunctions/common/localkey.hh>
29
30namespace Dune { namespace Impl
31{
32 // The traits provide static or dynamic order and size information
33 template<unsigned int dim, int compileTimeOrder>
34 struct LagrangeSimplexTraits
35 {
36 static constexpr bool is_static_order = (compileTimeOrder >= 0);
37
38 constexpr LagrangeSimplexTraits(int /*runTimeOrder*/ = compileTimeOrder)
39 {
40 static_assert(compileTimeOrder >= 0, "LagrangeSimplex: order must be non-negative");
41 }
42
44 static constexpr unsigned int order() { return compileTimeOrder; }
45
50 static constexpr unsigned int size() { return binomial(compileTimeOrder+dim,dim); }
51 };
52
53
54 // this specialization is for the dynamic order case
55 template<unsigned int dim>
56 struct LagrangeSimplexTraits<dim,-1>
57 {
58 const unsigned int k_;
59 const unsigned int size_;
60
61 static constexpr bool is_static_order = false;
62
63 constexpr explicit LagrangeSimplexTraits(int runTimeOrder)
64 : k_(runTimeOrder >= 0 ? (unsigned int)(runTimeOrder) : 0u)
65 , size_(binomial(k_+dim,dim))
66 {
67 if (runTimeOrder < 0)
68 DUNE_THROW(Dune::InvalidStateException, "LagrangeSimplex: order must be non-negative");
69 }
70
72 constexpr unsigned int order() const
73 {
74 return k_;
75 }
76
78 constexpr unsigned int size() const
79 {
80 return size_;
81 }
82 };
83
84
93 template<class R, unsigned int dim, int k>
94 struct LagrangeSimplexLocalBasisBuffers
95 {
96 LagrangeSimplexLocalBasisBuffers(int /*order*/) {}
97
98 // Buffer for the evaluation of shape function, used in evaluateFunction()
99 static auto makeValueBuffer(int /*order*/)
100 {
101 using E = Std::extents<int,dim+1,k+1>;
102 using C = std::array<R,(dim+1)*(k+1)>;
103 return Std::mdarray<R,E,Std::layout_right,C>{};
104 }
105
106 // Buffer for the evaluation of shape function Jacobians, used in evaluateJacobian()
107 static auto makeJacobianBuffer(int /*order*/)
108 {
109 using E = Std::extents<int,dim+1,2,k+1>;
110 using C = std::array<R,(dim+1)*2*(k+1)>;
111 return Std::mdarray<R,E,Std::layout_right,C>{};
112 }
113
114 // Buffer for the evaluation of shape function partial derivatives, used in partial()
115 template <class T, T derivativeOrder>
116 static auto makePartialBuffer(std::integral_constant<T,derivativeOrder>, int /*order*/)
117 {
118 using E = Std::extents<int,dim+1,derivativeOrder+1,k+1>;
119 using C = std::array<R,(dim+1)*(derivativeOrder+1)*(k+1)>;
120 return Std::mdarray<R,E,Std::layout_right,C>{};
121 }
122 };
123
124
132 template<class R, unsigned int dim>
133 struct LagrangeSimplexLocalBasisBuffers<R,dim,-1>
134 {
135 LagrangeSimplexLocalBasisBuffers(int order)
136 : buffer_((dim+1)*std::max<int>(2,order+1)*(order+1))
137 {}
138
139 mutable std::vector<R> buffer_{};
140
141 // Buffer for the evaluation of shape function, used in evaluateFunction()
142 auto makeValueBuffer(int order) const
143 {
144 using E = Std::extents<int,dim+1,std::dynamic_extent>;
145 return Std::mdspan<R,E>{buffer_.data(), E(order+1)};
146 }
147
148 // Buffer for the evaluation of shape function Jacobians, used in evaluateJacobian()
149 auto makeJacobianBuffer(int order) const
150 {
151 using E = Std::extents<int,dim+1,2,std::dynamic_extent>;
152 return Std::mdspan<R,E>{buffer_.data(), E(order+1)};
153 }
154
155 // Buffer for the evaluation of shape function partial derivatives, used in partial()
156 auto makePartialBuffer(int derivativeOrder, int order) const
157 {
158 using E = Std::extents<int,dim+1,std::dynamic_extent,std::dynamic_extent>;
159 return Std::mdspan<R,E>{buffer_.data(), E(derivativeOrder+1, order+1)};
160 }
161 };
162
163
174 template<class D, class R, unsigned int dim, int compileTimeOrder>
175 class LagrangeSimplexLocalBasis
176 : public LagrangeSimplexTraits<dim,compileTimeOrder>
177 , private LagrangeSimplexLocalBasisBuffers<R,dim,compileTimeOrder>
178 {
179 template <class> friend class LagrangeSimplexLocalInterpolation;
180
181 public:
182
183 using Base = LagrangeSimplexTraits<dim,compileTimeOrder>;
184 using Buffers = LagrangeSimplexLocalBasisBuffers<R,dim,compileTimeOrder>;
185 static constexpr bool is_static_order = Base::is_static_order;
186
187 constexpr LagrangeSimplexLocalBasis(int runTimeOrder = compileTimeOrder)
188 : Base(runTimeOrder)
189 , Buffers(runTimeOrder)
190 {}
191
192 using Base::order;
193 using Base::size;
194
195 private:
196
197 // Fix the first index in a multi-dimensional array to `i`. For matrices this
198 // corresponds to the ith row of the matrix.
199 // Equivalent to std::submdspan(L, i, std::full_extent...)
200 template <class I, std::size_t e0, std::size_t... ee, class A>
201 static auto slice(Std::mdspan<R,Std::extents<I,e0,ee...>,Std::layout_right,A> L, int i)
202 {
203 return Dune::unpackIntegerSequence([&](auto... ii) {
204 return Std::mdspan<R,Std::extents<I,ee...>,Std::layout_right>{
205 L.data_handle() + i * L.stride(0), L.extent(ii+1)...};
206 }, std::make_index_sequence<sizeof...(ee)>{});
207 }
208
209 // Overload for mdarray type: forward to the mdspan implementation
210 template <class I, std::size_t e0, std::size_t... ee, class C>
211 static auto slice(Std::mdarray<R,Std::extents<I,e0,ee...>,Std::layout_right,C>& L, int i)
212 {
213 return slice(L.to_mdspan(), i);
214 }
215
216 // Compute the rescaled barycentric coordinates of x.
217 // We rescale the simplex by k and then compute the
218 // barycentric coordinates with respect to the points
219 // p_i = e_i (for i=0,...,dim-1) and p_dim=0.
220 // Notice that then the Lagrange points have the barycentric
221 // coordinates (i_0,...,i_d) where i_j are all non-negative
222 // integers satisfying the constraint sum i_j = k.
223 constexpr auto barycentric(const auto& x) const
224 {
225 auto b = std::array<R,dim+1>{};
226 b[dim] = order();
227 for (auto i : Dune::range(dim))
228 {
229 b[i] = order() * x[i];
230 b[dim] -= b[i];
231 }
232 return b;
233 }
234
235 // Evaluate the univariate Lagrange polynomials L_i(t) for i=0,...,k where
236 //
237 // L_i(t) = (t-0)/(i-0) * ... * (t-(i-1))/(i-(i-1))
238 // = (t-0)*...*(t-(i-1))/(i!);
239 static constexpr void evaluateLagrangePolynomials(const R& t, auto&& L, int k)
240 {
241 L[0] = 1;
242 for (auto i : Dune::range(k))
243 L[i+1] = L[i] * (t - i) / (i+1);
244 }
245
246 // Evaluate the univariate Lagrange polynomial derivatives L_i(t) for i=0,...,k
247 // up to given maxDerivativeOrder.
248 static constexpr void evaluateLagrangePolynomialDerivative(const R& t, auto&& LL, unsigned int maxDerivativeOrder, int k)
249 {
250 auto L = slice(LL,0);
251 L[0] = 1;
252 for (auto i : Dune::range(k))
253 L[i+1] = L[i] * (t - i) / (i+1);
254 for(auto j : Dune::range(maxDerivativeOrder))
255 {
256 auto F = slice(LL,j);
257 auto DF = slice(LL,j+1);
258 DF[0] = 0;
259 for (auto i : Dune::range(k))
260 DF[i+1] = (DF[i] * (t - i) + (j+1)*F[i]) / (i+1);
261 }
262 }
263
264 using BarycentricMultiIndex = std::array<int,dim+1>;
265
266
267 // This computed the required partial derivative given by the multi-index
268 // beta of a product of a function given as a product of dim+1 derivatives
269 // of univariate Lagrange polynomials of the dim+1 barycentric coordinates.
270 // The polynomials in the product are specified as follows:
271 //
272 // The table L contains all required derivatives of all univariate
273 // polynomials evaluated at all barycentric coordinates. The two
274 // multi-indices i and alpha encode that the polynomial for the
275 // j-th barycentric coordinate is the alpha-j-th derivative of
276 // the i_j-the Lagrange polynomial.
277 //
278 // Hence this method computes D_beta f(x) where f(x) is the product
279 // \f$f(x) = \prod_{j=0}^{d} L_{i_j}^{(alpha_j)}(x_j) \f$.
280 static constexpr R barycentricDerivative(
281 BarycentricMultiIndex beta,
282 const auto&L,
283 int k,
284 const BarycentricMultiIndex& i,
285 const BarycentricMultiIndex& alpha = {})
286 {
287 // If there are unprocessed derivatives left we search the first unprocessed
288 // partial derivative direction j and compute it using the product and chain rule.
289 // The remaining partial derivatives are forwarded to the recursion.
290 // Notice that the partial derivative of the last barycentric component
291 // wrt to the j-th component is -1 which is responsible for the second
292 // term in the sum. Furthermore we get the factor k due to the scaling of
293 // the simplex.
294 for(auto j : Dune::range(dim))
295 {
296 if (beta[j] > 0)
297 {
298 auto leftDerivatives = alpha;
299 auto rightDerivatives = alpha;
300 leftDerivatives[j]++;
301 rightDerivatives.back()++;
302 beta[j]--;
303 return (barycentricDerivative(beta, L, k, i, leftDerivatives) -
304 barycentricDerivative(beta, L, k, i, rightDerivatives)) * k;
305 }
306 }
307 // If there are no unprocessed derivatives we can simply evaluate
308 // the product of the derivatives of the Lagrange polynomials with
309 // given indices i_j and derivative orders alpha_j
310 // Evaluate the product of the univariate Lagrange polynomials
311 // with given indices and orders.
312 auto y = R(1);
313 for(auto j : Dune::range(dim+1))
314 y *= L(j,alpha[j],i[j]);
315 return y;
316 }
317
318
319 public:
320 using Traits = LocalBasisTraits<D,dim,FieldVector<D,dim>,R,1,FieldVector<R,1>,FieldMatrix<R,1,dim> >;
321
323 void evaluateFunction(const typename Traits::DomainType& x,
324 std::vector<typename Traits::RangeType>& out) const
325 {
326 const int k = order();
327 out.resize(size());
328
329 // Specialization for zero-order case
330 if (k==0)
331 {
332 out[0] = 1;
333 return;
334 }
335
336 // Specialization for first-order case
337 if (k==1)
338 {
339 out[0] = 1.0;
340 for (size_t i=0; i<dim; i++)
341 {
342 out[0] -= x[i];
343 out[i+1] = x[i];
344 }
345 return;
346 }
347
348 // Compute rescaled barycentric coordinates of x
349 auto z = barycentric(x);
350
351 auto L = Buffers::makeValueBuffer(k);
352 for (auto j : Dune::range(dim+1))
353 evaluateLagrangePolynomials(z[j], slice(L,j), k);
354
355 if (dim==1)
356 {
357 unsigned int n = 0;
358 for (auto i0 : Dune::range(k + 1))
359 for (auto i1 : std::array{k - i0})
360 out[n++] = L(0,i0) * L(1,i1);
361 return;
362 }
363 if (dim==2)
364 {
365 unsigned int n=0;
366 for (auto i1 : Dune::range(k + 1))
367 for (auto i0 : Dune::range(k - i1 + 1))
368 for (auto i2 : std::array{k - i1 - i0})
369 out[n++] = L(0,i0) * L(1,i1) * L(2,i2);
370 return;
371 }
372 if (dim==3)
373 {
374 unsigned int n = 0;
375 for (auto i2 : Dune::range(k + 1))
376 for (auto i1 : Dune::range(k - i2 + 1))
377 for (auto i0 : Dune::range(k - i2 - i1 + 1))
378 for (auto i3 : std::array{k - i2 - i1 - i0})
379 out[n++] = L(0,i0) * L(1,i1) * L(2,i2) * L(3,i3);
380 return;
381 }
382
383 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalBasis for k>=2 only implemented for dim<=3");
384 }
385
391 void evaluateJacobian(const typename Traits::DomainType& x,
392 std::vector<typename Traits::JacobianType>& out) const
393 {
394 const int k = order();
395 out.resize(size());
396
397 // Specialization for k==0
398 if (k==0)
399 {
400 std::fill(out[0][0].begin(), out[0][0].end(), 0);
401 return;
402 }
403
404 // Specialization for k==1
405 if (k==1)
406 {
407 std::fill(out[0][0].begin(), out[0][0].end(), -1);
408
409 for (unsigned int i=0; i<dim; i++)
410 for (unsigned int j=0; j<dim; j++)
411 out[i+1][0][j] = (i==j);
412
413 return;
414 }
415
416 // Compute rescaled barycentric coordinates of x
417 auto z = barycentric(x);
418
419 // L(j,m,i) is the m-th derivative of the i-th Lagrange polynomial at z[j]
420 auto L = Buffers::makeJacobianBuffer(k);
421 for (auto j : Dune::range(dim+1))
422 evaluateLagrangePolynomialDerivative(z[j], slice(L,j), 1, k);
423
424 if (dim==1)
425 {
426 unsigned int n = 0;
427 for (auto i0 : Dune::range(k + 1))
428 {
429 for (auto i1 : std::array{k-i0})
430 {
431 out[n][0][0] = (L(0,1,i0) * L(1,0,i1) - L(0,0,i0) * L(1,1,i1))*k;
432 ++n;
433 }
434 }
435 return;
436 }
437 if (dim==2)
438 {
439 unsigned int n=0;
440 for (auto i1 : Dune::range(k + 1))
441 {
442 for (auto i0 : Dune::range(k - i1 + 1))
443 {
444 for (auto i2 : std::array{k - i1 - i0})
445 {
446 out[n][0][0] = (L(0,1,i0) * L(1,0,i1) * L(2,0,i2) - L(0,0,i0) * L(1,0,i1) * L(2,1,i2))*k;
447 out[n][0][1] = (L(0,0,i0) * L(1,1,i1) * L(2,0,i2) - L(0,0,i0) * L(1,0,i1) * L(2,1,i2))*k;
448 ++n;
449 }
450 }
451 }
452 return;
453 }
454 if (dim==3)
455 {
456 unsigned int n = 0;
457 for (auto i2 : Dune::range(k + 1))
458 {
459 for (auto i1 : Dune::range(k - i2 + 1))
460 {
461 for (auto i0 : Dune::range(k - i2 - i1 + 1))
462 {
463 for (auto i3 : std::array{k - i2 - i1 - i0})
464 {
465 out[n][0][0] = (L(0,1,i0) * L(1,0,i1) * L(2,0,i2) * L(3,0,i3) - L(0,0,i0) * L(1,0,i1) * L(2,0,i2) * L(3,1,i3))*k;
466 out[n][0][1] = (L(0,0,i0) * L(1,1,i1) * L(2,0,i2) * L(3,0,i3) - L(0,0,i0) * L(1,0,i1) * L(2,0,i2) * L(3,1,i3))*k;
467 out[n][0][2] = (L(0,0,i0) * L(1,0,i1) * L(2,1,i2) * L(3,0,i3) - L(0,0,i0) * L(1,0,i1) * L(2,0,i2) * L(3,1,i3))*k;
468 ++n;
469 }
470 }
471 }
472 }
473
474 return;
475 }
476
477 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalBasis for k>=2 only implemented for dim<=3");
478 }
479
486 void partial(const std::array<unsigned int,dim>& derivativeOrder,
487 const typename Traits::DomainType& in,
488 std::vector<typename Traits::RangeType>& out) const
489 {
490 const int k = order();
491 int totalOrder = std::accumulate(derivativeOrder.begin(), derivativeOrder.end(), 0u);
492
493 out.resize(size());
494
495 // Derivative order zero corresponds to the function evaluation.
496 if (totalOrder == 0)
497 {
498 evaluateFunction(in, out);
499 return;
500 }
501
502 // Derivatives of order >k are all zero.
503 if (totalOrder > k)
504 {
505 for(auto& out_i : out)
506 out_i = 0;
507 return;
508 }
509
510 // It remains to cover the cases 0 < totalOrder<= k.
511
512 if (k==1)
513 {
514 if (totalOrder==1)
515 {
516 auto direction = std::find(derivativeOrder.begin(), derivativeOrder.end(), 1);
517 out[0] = -1;
518 for (unsigned int i=0; i<dim; i++)
519 out[i+1] = (i==(direction-derivativeOrder.begin()));
520 }
521 return;
522 }
523
524 // Since the required stack storage depends on the dynamic total order,
525 // we need to do a dynamic to static dispatch by enumerating all supported
526 // static orders.
527 auto supportedOrders = [&]{
528 if constexpr(is_static_order)
530 else
531 return Dune::range(1, k+1);
532 }();
533 return Dune::Hybrid::switchCases(supportedOrders, totalOrder, [&](auto staticTotalOrder) {
534
535 // Compute rescaled barycentric coordinates of x
536 auto z = barycentric(in);
537
538 // L(j,m,i) is the m-th derivative of the i-th Lagrange polynomial at z[j]
539 auto L = Buffers::makePartialBuffer(staticTotalOrder,k);
540 for (auto j : Dune::range(dim))
541 evaluateLagrangePolynomialDerivative(z[j], slice(L,j), derivativeOrder[j], k);
542 evaluateLagrangePolynomialDerivative(z[dim], slice(L,dim), totalOrder, k);
543
544 auto barycentricOrder = BarycentricMultiIndex{};
545 for (auto j : Dune::range(dim))
546 barycentricOrder[j] = derivativeOrder[j];
547 barycentricOrder[dim] = 0;
548
549 if constexpr (dim==1)
550 {
551 unsigned int n = 0;
552 for (auto i0 : Dune::range(k + 1))
553 for (auto i1 : std::array{k - i0})
554 out[n++] = barycentricDerivative(barycentricOrder, L, k, BarycentricMultiIndex{i0, i1});
555 }
556 if constexpr (dim==2)
557 {
558 unsigned int n=0;
559 for (auto i1 : Dune::range(k + 1))
560 for (auto i0 : Dune::range(k - i1 + 1))
561 for (auto i2 : std::array{k - i1 - i0})
562 out[n++] = barycentricDerivative(barycentricOrder, L, k, BarycentricMultiIndex{i0, i1, i2});
563 }
564 if constexpr (dim==3)
565 {
566 unsigned int n = 0;
567 for (auto i2 : Dune::range(k + 1))
568 for (auto i1 : Dune::range(k - i2 + 1))
569 for (auto i0 : Dune::range(k - i2 - i1 + 1))
570 for (auto i3 : std::array{k - i2 - i1 - i0})
571 out[n++] = barycentricDerivative(barycentricOrder, L, k, BarycentricMultiIndex{i0, i1, i2, i3});
572 }
573 });
574 }
575 };
576
582 template<unsigned int dim, int compileTimeOrder>
583 class LagrangeSimplexLocalCoefficients
584 : public LagrangeSimplexTraits<dim,compileTimeOrder>
585 {
586 using Base = LagrangeSimplexTraits<dim,compileTimeOrder>;
587
588 public:
590 LagrangeSimplexLocalCoefficients (int runTimeOrder = compileTimeOrder)
591 : Base(runTimeOrder)
592 , localKeys_(Base::size())
593 {
594 const int k = Base::order();
595 if (k==0)
596 {
597 localKeys_[0] = LocalKey(0,0,0);
598 return;
599 }
600
601 if (k==1)
602 {
603 for (std::size_t i=0; i<Base::size(); i++)
604 localKeys_[i] = LocalKey(i,dim,0);
605 return;
606 }
607
608 if (dim==1)
609 {
610 // Order is at least 2 here
611 localKeys_[0] = LocalKey(0,1,0); // vertex dof
612 for (int i=1; i<k; i++)
613 localKeys_[i] = LocalKey(0,0,i-1); // element dofs
614 localKeys_.back() = LocalKey(1,1,0); // vertex dof
615 return;
616 }
617
618 if (dim==2)
619 {
620 int n=0;
621 int c=0;
622 for (int j=0; j<=k; j++)
623 for (int i=0; i<=k-j; i++)
624 {
625 if (i==0 && j==0)
626 {
627 localKeys_[n++] = LocalKey(0,2,0);
628 continue;
629 }
630 if (i==k && j==0)
631 {
632 localKeys_[n++] = LocalKey(1,2,0);
633 continue;
634 }
635 if (i==0 && j==k)
636 {
637 localKeys_[n++] = LocalKey(2,2,0);
638 continue;
639 }
640 if (j==0)
641 {
642 localKeys_[n++] = LocalKey(0,1,i-1);
643 continue;
644 }
645 if (i==0)
646 {
647 localKeys_[n++] = LocalKey(1,1,j-1);
648 continue;
649 }
650 if (i+j==k)
651 {
652 localKeys_[n++] = LocalKey(2,1,j-1);
653 continue;
654 }
655 localKeys_[n++] = LocalKey(0,0,c++);
656 }
657 return;
658 }
659
660 if (dim==3)
661 {
662 std::array<unsigned int, dim+1> vertexMap;
663 for (unsigned int i=0; i<=dim; i++)
664 vertexMap[i] = i;
665 generateLocalKeys(vertexMap);
666 return;
667 }
668 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for k<=1 or dim<=3!");
669 }
670
677 constexpr LagrangeSimplexLocalCoefficients (const std::array<unsigned int, dim+1> vertexMap)
678 : localKeys_(Base::size())
679 {
680 if (dim!=2 && dim!=3)
681 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for dim==2 and dim==3!");
682
683 generateLocalKeys(vertexMap);
684 }
685
686
687 template<class VertexMap>
688 constexpr LagrangeSimplexLocalCoefficients(const VertexMap &vertexmap)
689 : localKeys_(Base::size())
690 {
691 if (dim!=2 && dim!=3)
692 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for dim==2 and dim==3!");
693
694 std::array<unsigned int, dim+1> vertexmap_array;
695 std::copy(vertexmap, vertexmap + dim + 1, vertexmap_array.begin());
696 generateLocalKeys(vertexmap_array);
697 }
698
700 constexpr const LocalKey& localKey (std::size_t i) const
701 {
702 return localKeys_[i];
703 }
704
705 using Base::order;
706 using Base::size;
707
708 private:
709 std::vector<LocalKey> localKeys_;
710
711 constexpr void generateLocalKeys(const std::array<unsigned int, dim+1> vertexMap)
712 {
713 const int k = Base::order();
714 if (k==0)
715 {
716 localKeys_[0] = LocalKey(0,0,0);
717 return;
718 }
719
720 if (dim==2)
721 {
722 // Create default assignment
723 int n=0;
724 int c=0;
725 for (int j=0; j<=k; j++)
726 for (int i=0; i<=k-j; i++)
727 {
728 if (i==0 && j==0)
729 {
730 localKeys_[n++] = LocalKey(0,2,0);
731 continue;
732 }
733 if (i==k && j==0)
734 {
735 localKeys_[n++] = LocalKey(1,2,0);
736 continue;
737 }
738 if (i==0 && j==k)
739 {
740 localKeys_[n++] = LocalKey(2,2,0);
741 continue;
742 }
743 if (j==0)
744 {
745 localKeys_[n++] = LocalKey(0,1,i-1);
746 continue;
747 }
748 if (i==0)
749 {
750 localKeys_[n++] = LocalKey(1,1,j-1);
751 continue;
752 }
753 if (i+j==k)
754 {
755 localKeys_[n++] = LocalKey(2,1,j-1);
756 continue;
757 }
758 localKeys_[n++] = LocalKey(0,0,c++);
759 }
760
761 // Flip edge orientations, if requested
762 bool flip[3];
763 flip[0] = vertexMap[0] > vertexMap[1];
764 flip[1] = vertexMap[0] > vertexMap[2];
765 flip[2] = vertexMap[1] > vertexMap[2];
766 for (std::size_t i=0; i<Base::size(); i++)
767 if (localKeys_[i].codim()==1 && flip[localKeys_[i].subEntity()])
768 localKeys_[i].index(k-2-localKeys_[i].index());
769
770 return;
771 }
772
773 if (dim!=3)
774 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for dim==3!");
775
776 unsigned int subindex[16];
777 unsigned int codim_count[4] = {0};
778 for (unsigned int m = 1; m < 16; ++m)
779 {
780 unsigned int codim = !(m&1) + !(m&2) + !(m&4) + !(m&8);
781 subindex[m] = codim_count[codim]++;
782 }
783
784 int a1 = (3*k + 12)*k + 11;
785 int a2 = -3*k - 6;
786 unsigned int dof_count[16] = {0};
787 int i[4];
788 for (i[3] = 0; i[3] <= k; ++i[3])
789 for (i[2] = 0; i[2] <= k - i[3]; ++i[2])
790 for (i[1] = 0; i[1] <= k - i[2] - i[3]; ++i[1])
791 {
792 i[0] = k - i[1] - i[2] - i[3];
793 int j[4];
794 unsigned int entity = 0;
795 unsigned int codim = 0;
796 for (unsigned int m = 0; m < 4; ++m)
797 {
798 j[m] = i[vertexMap[m]];
799 entity += !!j[m] << m;
800 codim += !j[m];
801 }
802 int local_index = j[3]*(a1 + (a2 + j[3])*j[3])/6
803 + j[2]*(2*(k - j[3]) + 3 - j[2])/2 + j[1];
804 localKeys_[local_index] = LocalKey(subindex[entity], codim, dof_count[entity]++);
805 }
806 }
807 };
808
813 template<class LocalBasis>
814 class LagrangeSimplexLocalInterpolation
815 : public LocalBasis::Base
816 {
817 using Base = typename LocalBasis::Base;
818
819 public:
820 constexpr LagrangeSimplexLocalInterpolation () = default;
821
823 explicit constexpr LagrangeSimplexLocalInterpolation (const LocalBasis& localBasis)
824 : Base(localBasis)
825 {}
826
834 template<typename F, typename C>
835 constexpr void interpolate (const F& f, std::vector<C>& out) const
836 {
837 constexpr auto dim = LocalBasis::Traits::dimDomain;
838 const int k = Base::order();
839 using D = typename LocalBasis::Traits::DomainFieldType;
840
841 typename LocalBasis::Traits::DomainType x;
842
843 out.resize(Base::size());
844
845 // Specialization for zero-order case
846 if (k==0)
847 {
848 auto center = ReferenceElements<D,dim>::simplex().position(0,0);
849 out[0] = f(center);
850 return;
851 }
852
853 // Specialization for first-order case
854 if (k==1)
855 {
856 // vertex 0
857 std::fill(x.begin(), x.end(), 0);
858 out[0] = f(x);
859
860 // remaining vertices
861 for (int i=0; i<dim; i++)
862 {
863 for (int j=0; j<dim; j++)
864 x[j] = (i==j);
865
866 out[i+1] = f(x);
867 }
868 return;
869 }
870
871 if (dim==1)
872 {
873 for (int i=0; i<k+1; i++)
874 {
875 x[0] = ((D)i)/k;
876 out[i] = f(x);
877 }
878 return;
879 }
880
881 if (dim==2)
882 {
883 int n=0;
884 for (int j=0; j<=k; j++)
885 for (int i=0; i<=k-j; i++)
886 {
887 x = { ((D)i)/k, ((D)j)/k };
888 out[n] = f(x);
889 n++;
890 }
891 return;
892 }
893
894 if (dim!=3)
895 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalInterpolation only implemented for dim<=3!");
896
897 const int kdiv = (k==0 ? 1 : k);
898
899 int n=0;
900 for (int i2 = 0; i2 <= k; i2++)
901 for (int i1 = 0; i1 <= k-i2; i1++)
902 for (int i0 = 0; i0 <= k-i1-i2; i0++)
903 {
904 x[0] = ((D)i0)/((D)kdiv);
905 x[1] = ((D)i1)/((D)kdiv);
906 x[2] = ((D)i2)/((D)kdiv);
907 out[n] = f(x);
908 n++;
909 }
910 }
911
912 };
913
914} } // namespace Dune::Impl
915
916namespace Dune
917{
971 template<class D, class R, int d, int compileTimeOrder = -1>
973 {
974 public:
978 Impl::LagrangeSimplexLocalBasis<D,R,d,compileTimeOrder>,
979 Impl::LagrangeSimplexLocalCoefficients<d,compileTimeOrder>,
980 Impl::LagrangeSimplexLocalInterpolation<Impl::LagrangeSimplexLocalBasis<D,R,d,compileTimeOrder> > >;
981
984 : basis_()
985 , coefficients_()
986 , interpolation_(basis_)
987 {
988 static_assert(compileTimeOrder >= 0, "Default constructor only allowed for compile-time >= 0");
989 }
990
992 explicit constexpr LagrangeSimplexLocalFiniteElement(int runTimeOrder)
993 : basis_(runTimeOrder)
994 , coefficients_(runTimeOrder)
995 , interpolation_(basis_)
996 {
997 if (runTimeOrder < 0)
998 DUNE_THROW(Dune::InvalidStateException, "LagrangeSimplexLocalFiniteElement: run-time order must be non-negative!");
999 }
1000
1003 template<typename VertexMap>
1004 explicit constexpr LagrangeSimplexLocalFiniteElement(const VertexMap& vertexmap) requires(compileTimeOrder >= 0)
1005 : basis_()
1006 , coefficients_(vertexmap)
1007 , interpolation_(basis_)
1008 {}
1009
1012 constexpr const typename Traits::LocalBasisType& localBasis () const
1013 {
1014 return basis_;
1015 }
1016
1019 constexpr const typename Traits::LocalCoefficientsType& localCoefficients () const
1020 {
1021 return coefficients_;
1022 }
1023
1026 constexpr const typename Traits::LocalInterpolationType& localInterpolation () const
1027 {
1028 return interpolation_;
1029 }
1030
1032 constexpr std::size_t size () const
1033 {
1034 return basis_.size();
1035 }
1036
1039 static constexpr GeometryType type ()
1040 {
1041 return GeometryTypes::simplex(d);
1042 }
1043
1044 private:
1045 typename Traits::LocalBasisType basis_;
1046 typename Traits::LocalCoefficientsType coefficients_;
1047 typename Traits::LocalInterpolationType interpolation_;
1048 };
1049
1050} // namespace Dune
1051
1052#endif // DUNE_LOCALFUNCTIONS_LAGRANGE_LAGRANGESIMPLEX_HH
Unique label for each type of entities that can occur in DUNE grids.
Definition: type.hh:114
Default exception if a function was called while the object is not in a valid state for that function...
Definition: exceptions.hh:375
Lagrange finite element for simplices with arbitrary compile-time dimension and static or dynamic pol...
Definition: lagrangesimplex.hh:973
constexpr std::size_t size() const
The number of shape functions.
Definition: lagrangesimplex.hh:1032
constexpr const Traits::LocalCoefficientsType & localCoefficients() const
Returns the assignment of the degrees of freedom to the element subentities.
Definition: lagrangesimplex.hh:1019
constexpr LagrangeSimplexLocalFiniteElement(const VertexMap &vertexmap)
Constructs a finite element given a vertex reordering.
Definition: lagrangesimplex.hh:1004
static constexpr GeometryType type()
The reference element that the local finite element is defined on.
Definition: lagrangesimplex.hh:1039
constexpr LagrangeSimplexLocalFiniteElement()
Constructor for compile-time order.
Definition: lagrangesimplex.hh:983
constexpr LagrangeSimplexLocalFiniteElement(int runTimeOrder)
Constructor for run-time order.
Definition: lagrangesimplex.hh:992
constexpr const Traits::LocalBasisType & localBasis() const
Returns the local basis, i.e., the set of shape functions.
Definition: lagrangesimplex.hh:1012
constexpr const Traits::LocalInterpolationType & localInterpolation() const
Returns object that evaluates degrees of freedom.
Definition: lagrangesimplex.hh:1026
A few common exception classes.
Implements a matrix constructed from a given type representing a field and compile-time given number ...
Implements a vector constructed from a given type representing a field and a compile-time given size.
decltype(auto) constexpr unpackIntegerSequence(F &&f, std::integer_sequence< I, i... > sequence)
Unpack an std::integer_sequence<I,i...> to std::integral_constant<I,i>...
Definition: indices.hh:124
std::integral_constant< std::size_t, i > index_constant
An index constant with value i.
Definition: indices.hh:29
#define DUNE_THROW(E,...)
Definition: exceptions.hh:314
constexpr GeometryType simplex(unsigned int dim)
Returns a GeometryType representing a simplex of dimension dim.
Definition: type.hh:453
void interpolate(const F &f, const GFS &gfs, XG &xg)
interpolation from a given grid function
Definition: interpolate.hh:177
constexpr decltype(auto) switchCases(const Cases &cases, const Value &value, Branches &&branches, ElseBranch &&elseBranch)
Switch statement.
Definition: hybridutilities.hh:661
constexpr auto max
Function object that returns the greater of the given values.
Definition: hybridutilities.hh:489
constexpr T accumulate(Range &&range, T value, F &&f)
Accumulate values.
Definition: hybridutilities.hh:284
static constexpr IntegralRange< std::decay_t< T > > range(T &&from, U &&to) noexcept
free standing function for setting up a range based for loop over an integer range for (auto i: range...
Definition: rangeutilities.hh:288
Some useful basic math stuff.
Dune namespace
Definition: alignedallocator.hh:13
constexpr std::integral_constant< std::size_t, sizeof...(II)> size(std::integer_sequence< T, II... >)
Return the size of the sequence.
Definition: integersequence.hh:75
static constexpr T binomial(const T &n, const T &k) noexcept
calculate the binomial coefficient n over k as a constexpr
Definition: math.hh:113
STL namespace.
Utilities for reduction like operations on ranges.
static const ReferenceElement & simplex()
get simplex reference elements
Definition: referenceelements.hh:162
D DomainType
domain type
Definition: localbasis.hh:43
traits helper struct
Definition: localfiniteelementtraits.hh:13
LB LocalBasisType
Definition: localfiniteelementtraits.hh:16
LC LocalCoefficientsType
Definition: localfiniteelementtraits.hh:20
LI LocalInterpolationType
Definition: localfiniteelementtraits.hh:24
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden & Uni Heidelberg  |  generated with Hugo v0.111.3 (Apr 8, 22:39, 2026)