dune-localfunctions 2.12-git
Loading...
Searching...
No Matches
lagrangesimplex.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// 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 <iterator>
11#include <numeric>
12#include <algorithm>
13#include <span>
14#include <type_traits>
15
20#include <dune/common/math.hh>
24
26
31
32namespace Dune { namespace Impl
33{
34 // The traits provide static or dynamic order and size information
35 template<unsigned int dim, int compileTimeOrder>
36 struct LagrangeSimplexTraits;
37
38 // The traits provide static order and size information
39 template<unsigned int dim, int compileTimeOrder>
40 requires (compileTimeOrder >= 0)
41 struct LagrangeSimplexTraits<dim,compileTimeOrder>
42 {
43 static constexpr bool is_static_order = true;
44
45 constexpr LagrangeSimplexTraits(int /*runTimeOrder*/ = compileTimeOrder) {}
46
48 static constexpr unsigned int order() { return compileTimeOrder; }
49
54 static constexpr unsigned int size() { return binomial(compileTimeOrder+dim,dim); }
55 };
56
57
58 // this specialization is for the dynamic order case
59 template<unsigned int dim>
60 struct LagrangeSimplexTraits<dim,-1>
61 {
62 unsigned int order_;
63 std::size_t size_;
64
65 static constexpr bool is_static_order = false;
66
67 constexpr explicit LagrangeSimplexTraits(int runTimeOrder)
68 : order_(runTimeOrder >= 0 ? (unsigned int)(runTimeOrder) : 0u)
69 , size_(binomial(order_+dim,dim))
70 {
71 if (runTimeOrder < 0)
72 DUNE_THROW(Dune::InvalidStateException, "LagrangeSimplex: order must be non-negative");
73 }
74
76 constexpr unsigned int order() const
77 {
78 return order_;
79 }
80
82 constexpr std::size_t size() const
83 {
84 return size_;
85 }
86 };
87
88
97 template<class R, unsigned int dim, int k>
98 struct LagrangeSimplexLocalBasisBuffers
99 {
100 LagrangeSimplexLocalBasisBuffers(int /*order*/) {}
101
102 // Buffer for the evaluation of shape function, used in evaluateFunction()
103 static auto makeValueBuffer(int /*order*/)
104 {
105 using E = Std::extents<int,dim+1,k+1>;
106 using C = std::array<R,(dim+1)*(k+1)>;
107 return Std::mdarray<R,E,Std::layout_right,C>{};
108 }
109
110 // Buffer for the evaluation of shape function Jacobians, used in evaluateJacobian()
111 static auto makeJacobianBuffer(int /*order*/)
112 {
113 using E = Std::extents<int,dim+1,2,k+1>;
114 using C = std::array<R,(dim+1)*2*(k+1)>;
115 return Std::mdarray<R,E,Std::layout_right,C>{};
116 }
117
118 // Buffer for the evaluation of shape function partial derivatives, used in partial()
119 template <class T, T partialOrders>
120 static auto makePartialBuffer(std::integral_constant<T,partialOrders>, int /*order*/)
121 {
122 using E = Std::extents<int,dim+1,partialOrders+1,k+1>;
123 using C = std::array<R,(dim+1)*(partialOrders+1)*(k+1)>;
124 return Std::mdarray<R,E,Std::layout_right,C>{};
125 }
126 };
127
128
136 template<class R, unsigned int dim>
137 struct LagrangeSimplexLocalBasisBuffers<R,dim,-1>
138 {
139 LagrangeSimplexLocalBasisBuffers(int order)
140 : buffer_((dim+1)*std::max<int>(2,order+1)*(order+1))
141 {}
142
143 mutable std::vector<R> buffer_{};
144
145 // Buffer for the evaluation of shape function, used in evaluateFunction()
146 auto makeValueBuffer(int order) const
147 {
148 using E = Std::extents<int,dim+1,std::dynamic_extent>;
149 return Std::mdspan<R,E>{buffer_.data(), E(order+1)};
150 }
151
152 // Buffer for the evaluation of shape function Jacobians, used in evaluateJacobian()
153 auto makeJacobianBuffer(int order) const
154 {
155 using E = Std::extents<int,dim+1,2,std::dynamic_extent>;
156 return Std::mdspan<R,E>{buffer_.data(), E(order+1)};
157 }
158
159 // Buffer for the evaluation of shape function partial derivatives, used in partial()
160 auto makePartialBuffer(int partialOrders, int order) const
161 {
162 using E = Std::extents<int,dim+1,std::dynamic_extent,std::dynamic_extent>;
163 return Std::mdspan<R,E>{buffer_.data(), E(partialOrders+1, order+1)};
164 }
165 };
166
167
178 template<class D, class R, unsigned int dim, int compileTimeOrder>
179 class LagrangeSimplexLocalBasis
180 : private LagrangeSimplexTraits<dim,compileTimeOrder>
181 , private LagrangeSimplexLocalBasisBuffers<R,dim,compileTimeOrder>
182 {
183 using OrderTraits = LagrangeSimplexTraits<dim,compileTimeOrder>;
184 using Buffers = LagrangeSimplexLocalBasisBuffers<R,dim,compileTimeOrder>;
185 static constexpr bool is_static_order = OrderTraits::is_static_order;
186
187 public:
189 constexpr LagrangeSimplexLocalBasis () requires (OrderTraits::is_static_order)
190 : LagrangeSimplexLocalBasis(OrderTraits())
191 {}
192
194 explicit constexpr LagrangeSimplexLocalBasis(OrderTraits orderTraits)
195 : OrderTraits(orderTraits)
196 , Buffers(orderTraits.order())
197 {}
198
199 using OrderTraits::order;
200 using OrderTraits::size;
201
202 private:
203
204 // Fix the first index in a multi-dimensional array to `i`. For matrices this
205 // corresponds to the ith row of the matrix.
206 // Equivalent to std::submdspan(L, i, std::full_extent...)
207 template <class I, std::size_t e0, std::size_t... ee, class A>
208 static auto slice(Std::mdspan<R,Std::extents<I,e0,ee...>,Std::layout_right,A> L, int i)
209 {
210 return Dune::unpackIntegerSequence([&](auto... ii) {
211 return Std::mdspan<R,Std::extents<I,ee...>,Std::layout_right>{
212 L.data_handle() + i * L.stride(0), L.extent(ii+1)...};
213 }, std::make_index_sequence<sizeof...(ee)>{});
214 }
215
216 // Overload for mdarray type: forward to the mdspan implementation
217 template <class I, std::size_t e0, std::size_t... ee, class C>
218 static auto slice(Std::mdarray<R,Std::extents<I,e0,ee...>,Std::layout_right,C>& L, int i)
219 {
220 return slice(L.to_mdspan(), i);
221 }
222
223 // Compute the rescaled barycentric coordinates of x.
224 // We rescale the simplex by k and then compute the
225 // barycentric coordinates with respect to the points
226 // p_i = e_i (for i=0,...,dim-1) and p_dim=0.
227 // Notice that then the Lagrange points have the barycentric
228 // coordinates (i_0,...,i_d) where i_j are all non-negative
229 // integers satisfying the constraint sum i_j = k.
230 constexpr auto barycentric(const auto& x) const
231 {
232 auto b = std::array<R,dim+1>{};
233 b[dim] = order();
234 for (auto i : Dune::range(dim))
235 {
236 b[i] = order() * x[i];
237 b[dim] -= b[i];
238 }
239 return b;
240 }
241
242 // Evaluate the univariate Lagrange polynomials L_i(t) for i=0,...,k where
243 //
244 // L_i(t) = (t-0)/(i-0) * ... * (t-(i-1))/(i-(i-1))
245 // = (t-0)*...*(t-(i-1))/(i!);
246 static constexpr void evaluateLagrangePolynomials(const R& t, auto&& L, int k)
247 {
248 L[0] = 1;
249 for (auto i : Dune::range(k))
250 L[i+1] = L[i] * (t - i) / (i+1);
251 }
252
253 // Evaluate the univariate Lagrange polynomial derivatives L_i(t) for i=0,...,k
254 // up to given maxDerivativeOrder.
255 static constexpr void evaluateLagrangePolynomialDerivative(const R& t, auto&& LL, unsigned int maxDerivativeOrder, int k)
256 {
257 auto L = slice(LL,0);
258 L[0] = 1;
259 for (auto i : Dune::range(k))
260 L[i+1] = L[i] * (t - i) / (i+1);
261 for(auto j : Dune::range(maxDerivativeOrder))
262 {
263 auto F = slice(LL,j);
264 auto DF = slice(LL,j+1);
265 DF[0] = 0;
266 for (auto i : Dune::range(k))
267 DF[i+1] = (DF[i] * (t - i) + (j+1)*F[i]) / (i+1);
268 }
269 }
270
271 using BarycentricMultiIndex = std::array<int,dim+1>;
272
273
274 // This computed the required partial derivative given by the multi-index
275 // beta of a product of a function given as a product of dim+1 derivatives
276 // of univariate Lagrange polynomials of the dim+1 barycentric coordinates.
277 // The polynomials in the product are specified as follows:
278 //
279 // The table L contains all required derivatives of all univariate
280 // polynomials evaluated at all barycentric coordinates. The two
281 // multi-indices i and alpha encode that the polynomial for the
282 // j-th barycentric coordinate is the alpha-j-th derivative of
283 // the i_j-the Lagrange polynomial.
284 //
285 // Hence this method computes D_beta f(x) where f(x) is the product
286 // \f$f(x) = \prod_{j=0}^{d} L_{i_j}^{(alpha_j)}(x_j) \f$.
287 static constexpr R barycentricDerivative(
288 BarycentricMultiIndex beta,
289 const auto&L,
290 int k,
291 const BarycentricMultiIndex& i,
292 const BarycentricMultiIndex& alpha = {})
293 {
294 // If there are unprocessed derivatives left we search the first unprocessed
295 // partial derivative direction j and compute it using the product and chain rule.
296 // The remaining partial derivatives are forwarded to the recursion.
297 // Notice that the partial derivative of the last barycentric component
298 // wrt to the j-th component is -1 which is responsible for the second
299 // term in the sum. Furthermore we get the factor k due to the scaling of
300 // the simplex.
301 for(auto j : Dune::range(dim))
302 {
303 if (beta[j] > 0)
304 {
305 auto leftDerivatives = alpha;
306 auto rightDerivatives = alpha;
307 leftDerivatives[j]++;
308 rightDerivatives.back()++;
309 beta[j]--;
310 return (barycentricDerivative(beta, L, k, i, leftDerivatives) -
311 barycentricDerivative(beta, L, k, i, rightDerivatives)) * k;
312 }
313 }
314 // If there are no unprocessed derivatives we can simply evaluate
315 // the product of the derivatives of the Lagrange polynomials with
316 // given indices i_j and derivative orders alpha_j
317 // Evaluate the product of the univariate Lagrange polynomials
318 // with given indices and orders.
319 auto y = R(1);
320 for(auto j : Dune::range(dim+1))
321 y *= L(j,alpha[j],i[j]);
322 return y;
323 }
324
325
326 public:
327 using Traits = LocalBasisTraits<D,dim,FieldVector<D,dim>,R,1,FieldVector<R,1>,FieldMatrix<R,1,dim> >;
328
330 void evaluateFunction(const typename Traits::DomainType& x,
332 {
333 const int k = order();
334 out.resize(size());
335
336 // Specialization for zero-order case
337 if (k==0)
338 {
339 out[0] = 1;
340 return;
341 }
342
343 // Specialization for first-order case
344 if (k==1)
345 {
346 out[0] = 1.0;
347 for (size_t i=0; i<dim; i++)
348 {
349 out[0] -= x[i];
350 out[i+1] = x[i];
351 }
352 return;
353 }
354
355 // Compute rescaled barycentric coordinates of x
356 auto z = barycentric(x);
357
358 auto L = Buffers::makeValueBuffer(k);
359 for (auto j : Dune::range(dim+1))
360 evaluateLagrangePolynomials(z[j], slice(L,j), k);
361
362 if (dim==1)
363 {
364 unsigned int n = 0;
365 for (auto i0 : Dune::range(k + 1))
366 for (auto i1 : std::array{k - i0})
367 out[n++] = L(0,i0) * L(1,i1);
368 return;
369 }
370 if (dim==2)
371 {
372 unsigned int n=0;
373 for (auto i1 : Dune::range(k + 1))
374 for (auto i0 : Dune::range(k - i1 + 1))
375 for (auto i2 : std::array{k - i1 - i0})
376 out[n++] = L(0,i0) * L(1,i1) * L(2,i2);
377 return;
378 }
379 if (dim==3)
380 {
381 unsigned int n = 0;
382 for (auto i2 : Dune::range(k + 1))
383 for (auto i1 : Dune::range(k - i2 + 1))
384 for (auto i0 : Dune::range(k - i2 - i1 + 1))
385 for (auto i3 : std::array{k - i2 - i1 - i0})
386 out[n++] = L(0,i0) * L(1,i1) * L(2,i2) * L(3,i3);
387 return;
388 }
389
390 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalBasis for k>=2 only implemented for dim<=3");
391 }
392
398 void evaluateJacobian(const typename Traits::DomainType& x,
400 {
401 const int k = order();
402 out.resize(size());
403
404 // Specialization for k==0
405 if (k==0)
406 {
407 std::fill(out[0][0].begin(), out[0][0].end(), 0);
408 return;
409 }
410
411 // Specialization for k==1
412 if (k==1)
413 {
414 std::fill(out[0][0].begin(), out[0][0].end(), -1);
415
416 for (unsigned int i=0; i<dim; i++)
417 for (unsigned int j=0; j<dim; j++)
418 out[i+1][0][j] = (i==j);
419
420 return;
421 }
422
423 // Compute rescaled barycentric coordinates of x
424 auto z = barycentric(x);
425
426 // L(j,m,i) is the m-th derivative of the i-th Lagrange polynomial at z[j]
427 auto L = Buffers::makeJacobianBuffer(k);
428 for (auto j : Dune::range(dim+1))
429 evaluateLagrangePolynomialDerivative(z[j], slice(L,j), 1, k);
430
431 if (dim==1)
432 {
433 unsigned int n = 0;
434 for (auto i0 : Dune::range(k + 1))
435 {
436 for (auto i1 : std::array{k-i0})
437 {
438 out[n][0][0] = (L(0,1,i0) * L(1,0,i1) - L(0,0,i0) * L(1,1,i1))*k;
439 ++n;
440 }
441 }
442 return;
443 }
444 if (dim==2)
445 {
446 unsigned int n=0;
447 for (auto i1 : Dune::range(k + 1))
448 {
449 for (auto i0 : Dune::range(k - i1 + 1))
450 {
451 for (auto i2 : std::array{k - i1 - i0})
452 {
453 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;
454 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;
455 ++n;
456 }
457 }
458 }
459 return;
460 }
461 if (dim==3)
462 {
463 unsigned int n = 0;
464 for (auto i2 : Dune::range(k + 1))
465 {
466 for (auto i1 : Dune::range(k - i2 + 1))
467 {
468 for (auto i0 : Dune::range(k - i2 - i1 + 1))
469 {
470 for (auto i3 : std::array{k - i2 - i1 - i0})
471 {
472 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;
473 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;
474 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;
475 ++n;
476 }
477 }
478 }
479 }
480
481 return;
482 }
483
484 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalBasis for k>=2 only implemented for dim<=3");
485 }
486
493 void partial(const std::array<unsigned int,dim>& partialOrders,
494 const typename Traits::DomainType& in,
496 {
497 const int k = order();
498 int totalOrder = std::accumulate(partialOrders.begin(), partialOrders.end(), 0u);
499
500 out.resize(size());
501
502 // Derivative order zero corresponds to the function evaluation.
503 if (totalOrder == 0)
504 {
505 evaluateFunction(in, out);
506 return;
507 }
508
509 // Derivatives of order >k are all zero.
510 if (totalOrder > k)
511 {
512 for(auto& out_i : out)
513 out_i = 0;
514 return;
515 }
516
517 // It remains to cover the cases 0 < totalOrder<= k.
518
519 if (k==1)
520 {
521 if (totalOrder==1)
522 {
523 auto direction = std::find(partialOrders.begin(), partialOrders.end(), 1);
524 out[0] = -1;
525 for (unsigned int i=0; i<dim; i++)
526 out[i+1] = (i==(direction-partialOrders.begin()));
527 }
528 return;
529 }
530
531 // Since the required stack storage depends on the dynamic total order,
532 // we need to do a dynamic to static dispatch by enumerating all supported
533 // static orders.
534 auto supportedOrders = [&]{
535 if constexpr(is_static_order)
537 else
538 return Dune::range(1, k+1);
539 }();
540 return Dune::Hybrid::switchCases(supportedOrders, totalOrder, [&](auto staticTotalOrder) {
541
542 // Compute rescaled barycentric coordinates of x
543 auto z = barycentric(in);
544
545 // L(j,m,i) is the m-th derivative of the i-th Lagrange polynomial at z[j]
546 auto L = Buffers::makePartialBuffer(staticTotalOrder,k);
547 for (auto j : Dune::range(dim))
548 evaluateLagrangePolynomialDerivative(z[j], slice(L,j), partialOrders[j], k);
549 evaluateLagrangePolynomialDerivative(z[dim], slice(L,dim), totalOrder, k);
550
551 auto barycentricOrder = BarycentricMultiIndex{};
552 for (auto j : Dune::range(dim))
553 barycentricOrder[j] = partialOrders[j];
554 barycentricOrder[dim] = 0;
555
556 if constexpr (dim==1)
557 {
558 unsigned int n = 0;
559 for (auto i0 : Dune::range(k + 1))
560 for (auto i1 : std::array{k - i0})
561 out[n++] = barycentricDerivative(barycentricOrder, L, k, BarycentricMultiIndex{i0, i1});
562 }
563 if constexpr (dim==2)
564 {
565 unsigned int n=0;
566 for (auto i1 : Dune::range(k + 1))
567 for (auto i0 : Dune::range(k - i1 + 1))
568 for (auto i2 : std::array{k - i1 - i0})
569 out[n++] = barycentricDerivative(barycentricOrder, L, k, BarycentricMultiIndex{i0, i1, i2});
570 }
571 if constexpr (dim==3)
572 {
573 unsigned int n = 0;
574 for (auto i2 : Dune::range(k + 1))
575 for (auto i1 : Dune::range(k - i2 + 1))
576 for (auto i0 : Dune::range(k - i2 - i1 + 1))
577 for (auto i3 : std::array{k - i2 - i1 - i0})
578 out[n++] = barycentricDerivative(barycentricOrder, L, k, BarycentricMultiIndex{i0, i1, i2, i3});
579 }
580 });
581 }
582 };
583
589 template<unsigned int dim, int compileTimeOrder>
590 class LagrangeSimplexLocalCoefficients
591 : private LagrangeSimplexTraits<dim,compileTimeOrder>
592 {
593 using OrderTraits = LagrangeSimplexTraits<dim,compileTimeOrder>;
594
595 public:
597 LagrangeSimplexLocalCoefficients () requires (OrderTraits::is_static_order)
598 : LagrangeSimplexLocalCoefficients(OrderTraits())
599 {}
600
602 LagrangeSimplexLocalCoefficients (OrderTraits orderTraits)
603 : OrderTraits(orderTraits)
604 , localKeys_(OrderTraits::size())
605 {
606 const int k = OrderTraits::order();
607 if (k==0)
608 {
609 localKeys_[0] = LocalKey(0,0,0);
610 return;
611 }
612
613 if (k==1)
614 {
615 for (std::size_t i=0; i<OrderTraits::size(); i++)
616 localKeys_[i] = LocalKey(i,dim,0);
617 return;
618 }
619
620 if (dim==1)
621 {
622 // Order is at least 2 here
623 localKeys_[0] = LocalKey(0,1,0); // vertex dof
624 for (int i=1; i<k; i++)
625 localKeys_[i] = LocalKey(0,0,i-1); // element dofs
626 localKeys_.back() = LocalKey(1,1,0); // vertex dof
627 return;
628 }
629
630 if (dim==2)
631 {
632 int n=0;
633 int c=0;
634 for (int j=0; j<=k; j++)
635 for (int i=0; i<=k-j; i++)
636 {
637 if (i==0 && j==0)
638 {
639 localKeys_[n++] = LocalKey(0,2,0);
640 continue;
641 }
642 if (i==k && j==0)
643 {
644 localKeys_[n++] = LocalKey(1,2,0);
645 continue;
646 }
647 if (i==0 && j==k)
648 {
649 localKeys_[n++] = LocalKey(2,2,0);
650 continue;
651 }
652 if (j==0)
653 {
654 localKeys_[n++] = LocalKey(0,1,i-1);
655 continue;
656 }
657 if (i==0)
658 {
659 localKeys_[n++] = LocalKey(1,1,j-1);
660 continue;
661 }
662 if (i+j==k)
663 {
664 localKeys_[n++] = LocalKey(2,1,j-1);
665 continue;
666 }
667 localKeys_[n++] = LocalKey(0,0,c++);
668 }
669 return;
670 }
671
672 if (dim==3)
673 {
675 for (unsigned int i=0; i<=dim; i++)
676 vertexMap[i] = i;
677 generateLocalKeys(vertexMap);
678 return;
679 }
680 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for k<=1 or dim<=3!");
681 }
682
689 constexpr LagrangeSimplexLocalCoefficients (const std::array<unsigned int, dim+1> vertexMap) requires (OrderTraits::is_static_order)
690 : localKeys_(OrderTraits::size())
691 {
692 if (dim!=2 && dim!=3)
693 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for dim==2 and dim==3!");
694
695 generateLocalKeys(vertexMap);
696 }
697
698
699 template<std::input_iterator VertexMap>
700 constexpr LagrangeSimplexLocalCoefficients(const VertexMap &vertexmap) requires (OrderTraits::is_static_order)
701 : localKeys_(OrderTraits::size())
702 {
703 if (dim!=2 && dim!=3)
704 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for dim==2 and dim==3!");
705
706 std::array<unsigned int, dim+1> vertexmap_array;
707 std::copy(vertexmap, vertexmap + dim + 1, vertexmap_array.begin());
708 generateLocalKeys(vertexmap_array);
709 }
710
712 constexpr const LocalKey& localKey (std::size_t i) const
713 {
714 return localKeys_[i];
715 }
716
717 using OrderTraits::order;
718 using OrderTraits::size;
719
720 private:
721 std::vector<LocalKey> localKeys_;
722
723 constexpr void generateLocalKeys(const std::array<unsigned int, dim+1> vertexMap)
724 {
725 const int k = OrderTraits::order();
726 if (k==0)
727 {
728 localKeys_[0] = LocalKey(0,0,0);
729 return;
730 }
731
732 if (dim==2)
733 {
734 // Create default assignment
735 int n=0;
736 int c=0;
737 for (int j=0; j<=k; j++)
738 for (int i=0; i<=k-j; i++)
739 {
740 if (i==0 && j==0)
741 {
742 localKeys_[n++] = LocalKey(0,2,0);
743 continue;
744 }
745 if (i==k && j==0)
746 {
747 localKeys_[n++] = LocalKey(1,2,0);
748 continue;
749 }
750 if (i==0 && j==k)
751 {
752 localKeys_[n++] = LocalKey(2,2,0);
753 continue;
754 }
755 if (j==0)
756 {
757 localKeys_[n++] = LocalKey(0,1,i-1);
758 continue;
759 }
760 if (i==0)
761 {
762 localKeys_[n++] = LocalKey(1,1,j-1);
763 continue;
764 }
765 if (i+j==k)
766 {
767 localKeys_[n++] = LocalKey(2,1,j-1);
768 continue;
769 }
770 localKeys_[n++] = LocalKey(0,0,c++);
771 }
772
773 // Flip edge orientations, if requested
774 bool flip[3];
775 flip[0] = vertexMap[0] > vertexMap[1];
776 flip[1] = vertexMap[0] > vertexMap[2];
777 flip[2] = vertexMap[1] > vertexMap[2];
778 for (std::size_t i=0; i<OrderTraits::size(); i++)
779 if (localKeys_[i].codim()==1 && flip[localKeys_[i].subEntity()])
780 localKeys_[i].index(k-2-localKeys_[i].index());
781
782 return;
783 }
784
785 if (dim!=3)
786 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalCoefficients only implemented for dim==3!");
787
788 unsigned int subindex[16];
789 unsigned int codim_count[4] = {0};
790 for (unsigned int m = 1; m < 16; ++m)
791 {
792 unsigned int codim = !(m&1) + !(m&2) + !(m&4) + !(m&8);
793 subindex[m] = codim_count[codim]++;
794 }
795
796 int a1 = (3*k + 12)*k + 11;
797 int a2 = -3*k - 6;
798 unsigned int dof_count[16] = {0};
799 int i[4];
800 for (i[3] = 0; i[3] <= k; ++i[3])
801 for (i[2] = 0; i[2] <= k - i[3]; ++i[2])
802 for (i[1] = 0; i[1] <= k - i[2] - i[3]; ++i[1])
803 {
804 i[0] = k - i[1] - i[2] - i[3];
805 int j[4];
806 unsigned int entity = 0;
807 unsigned int codim = 0;
808 for (unsigned int m = 0; m < 4; ++m)
809 {
810 j[m] = i[vertexMap[m]];
811 entity += !!j[m] << m;
812 codim += !j[m];
813 }
814 int local_index = j[3]*(a1 + (a2 + j[3])*j[3])/6
815 + j[2]*(2*(k - j[3]) + 3 - j[2])/2 + j[1];
816 localKeys_[local_index] = LocalKey(subindex[entity], codim, dof_count[entity]++);
817 }
818 }
819 };
820
828 template<class D, class R, int dim, int compileTimeOrder>
829 class LagrangeSimplexLocalInterpolation
830 : private LagrangeSimplexTraits<dim, compileTimeOrder>
831 {
832 using OrderTraits = LagrangeSimplexTraits<dim, compileTimeOrder>;
833 using Traits = typename LagrangeSimplexLocalBasis<D, R, dim, compileTimeOrder>::Traits;
834
835 using OrderTraits::size;
836
837 public:
838 constexpr LagrangeSimplexLocalInterpolation () requires (OrderTraits::is_static_order)
839 : OrderTraits()
840 {}
841
843 explicit constexpr LagrangeSimplexLocalInterpolation (OrderTraits orderTraits)
844 : OrderTraits(orderTraits)
845 {}
846
854 template<typename F, typename C>
855 constexpr void interpolate (const F& f, std::vector<C>& out) const
856 {
857 const int k = OrderTraits::order();
858 out.resize(OrderTraits::size());
859
860 // Specialization for zero-order case
861 if (k==0)
862 {
863 auto center = ReferenceElements<D,dim>::simplex().position(0,0);
864 out[0] = Impl::rangeCast(f(center));
865 return;
866 }
867
868 typename Traits::DomainType x;
869
870 // Specialization for first-order case
871 if (k==1)
872 {
873 // vertex 0
874 std::fill(x.begin(), x.end(), 0);
875 out[0] = Impl::rangeCast(f(x));
876
877 // remaining vertices
878 for (unsigned int i=0; i<dim; i++)
879 {
880 for (unsigned int j=0; j<dim; j++)
881 x[j] = (i==j);
882
883 out[i+1] = Impl::rangeCast(f(x));
884 }
885 return;
886 }
887
888 if (dim==1)
889 {
890 for (int i=0; i<k+1; i++)
891 {
892 x[0] = ((D)i)/k;
893 out[i] = Impl::rangeCast(f(x));
894 }
895 return;
896 }
897
898 if (dim==2)
899 {
900 int n=0;
901 for (int j=0; j<=k; j++)
902 for (int i=0; i<=k-j; i++)
903 {
904 x = { ((D)i)/k, ((D)j)/k };
905 out[n] = Impl::rangeCast(f(x));
906 n++;
907 }
908 return;
909 }
910
911 if (dim!=3)
912 DUNE_THROW(NotImplemented, "LagrangeSimplexLocalInterpolation only implemented for dim<=3!");
913
914 const int kdiv = (k==0 ? 1 : k);
915
916 int n=0;
917 for (int i2 = 0; i2 <= k; i2++)
918 for (int i1 = 0; i1 <= k-i2; i1++)
919 for (int i0 = 0; i0 <= k-i1-i2; i0++)
920 {
921 x[0] = ((D)i0)/((D)kdiv);
922 x[1] = ((D)i1)/((D)kdiv);
923 x[2] = ((D)i2)/((D)kdiv);
924 out[n] = Impl::rangeCast(f(x));
925 n++;
926 }
927 }
928
929 };
930
931} } // namespace Dune::Impl
932
933namespace Dune
934{
988 template<class D, class R, int dim, int compileTimeOrder = -1>
990 : private Impl::LagrangeSimplexTraits<dim, compileTimeOrder>
991 {
992 using OrderTraits = Impl::LagrangeSimplexTraits<dim, compileTimeOrder>;
993
994 public:
998 Impl::LagrangeSimplexLocalBasis<D,R,dim,compileTimeOrder>,
999 Impl::LagrangeSimplexLocalCoefficients<dim,compileTimeOrder>,
1000 Impl::LagrangeSimplexLocalInterpolation<D,R,dim,compileTimeOrder> >;
1001
1004 : OrderTraits()
1005 , basis_(*this)
1006 , coefficients_(*this)
1007 , interpolation_(*this)
1008 {
1009 static_assert(OrderTraits::is_static_order, "Default constructor only allowed for compile-time order >= 0");
1010 static_assert(compileTimeOrder >= 0, "Default constructor only allowed for compile-time order >= 0");
1011 }
1012
1014 explicit constexpr LagrangeSimplexLocalFiniteElement(int runTimeOrder)
1015 : OrderTraits(runTimeOrder)
1016 , basis_(*this)
1017 , coefficients_(*this)
1018 , interpolation_(*this)
1019 {
1020 if (runTimeOrder < 0)
1021 DUNE_THROW(Dune::InvalidStateException, "LagrangeSimplexLocalFiniteElement: run-time order must be non-negative!");
1022 if (compileTimeOrder >= 0 && compileTimeOrder != runTimeOrder)
1023 DUNE_THROW(Dune::InvalidStateException, "LagrangeSimplexLocalFiniteElement: Compile-time order must be identical to run-time order!");
1024 }
1025
1028 template<typename VertexMap>
1029 explicit constexpr LagrangeSimplexLocalFiniteElement(const VertexMap& vertexmap) requires(compileTimeOrder >= 0)
1030 : OrderTraits()
1031 , basis_(*this)
1032 , coefficients_(vertexmap)
1033 , interpolation_(*this)
1034 {}
1035
1038 constexpr const typename Traits::LocalBasisType& localBasis () const
1039 {
1040 return basis_;
1041 }
1042
1045 constexpr const typename Traits::LocalCoefficientsType& localCoefficients () const
1046 {
1047 return coefficients_;
1048 }
1049
1052 constexpr const typename Traits::LocalInterpolationType& localInterpolation () const
1053 {
1054 return interpolation_;
1055 }
1056
1058 using OrderTraits::size;
1059
1062 static constexpr GeometryType type ()
1063 {
1064 return GeometryTypes::simplex(dim);
1065 }
1066
1067 private:
1068 typename Traits::LocalBasisType basis_;
1069 typename Traits::LocalCoefficientsType coefficients_;
1070 typename Traits::LocalInterpolationType interpolation_;
1071 };
1072
1073} // namespace Dune
1074
1075#endif // DUNE_LOCALFUNCTIONS_LAGRANGE_LAGRANGESIMPLEX_HH
int size() const
iterator end()
iterator begin()
static constexpr IntegralRange< T > range(T from, T to) noexcept
decltype(auto) constexpr unpackIntegerSequence(F &&f, std::integer_sequence< I, i... > sequence)
constexpr decltype(auto) switchCases(const Cases &cases, const Value &value, Branches &&branches, ElseBranch &&elseBranch)
constexpr auto max
size_type dim() const
std::ptrdiff_t index() const
#define DUNE_THROW(E,...)
static constexpr T binomial(const T &n, const T &k) noexcept
STL namespace.
D DomainType
domain type
Definition common/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
Lagrange finite element for simplices with arbitrary compile-time dimension and static or dynamic pol...
Definition lagrangesimplex.hh:991
constexpr const Traits::LocalCoefficientsType & localCoefficients() const
Returns the assignment of the degrees of freedom to the element subentities.
Definition lagrangesimplex.hh:1045
constexpr LagrangeSimplexLocalFiniteElement(const VertexMap &vertexmap)
Constructs a finite element given a vertex reordering.
Definition lagrangesimplex.hh:1029
static constexpr GeometryType type()
The reference element that the local finite element is defined on.
Definition lagrangesimplex.hh:1062
constexpr const Traits::LocalBasisType & localBasis() const
Returns the local basis, i.e., the set of shape functions.
Definition lagrangesimplex.hh:1038
constexpr const Traits::LocalInterpolationType & localInterpolation() const
Returns object that evaluates degrees of freedom.
Definition lagrangesimplex.hh:1052
constexpr LagrangeSimplexLocalFiniteElement(int runTimeOrder)
Constructor for run-time order.
Definition lagrangesimplex.hh:1014
constexpr LagrangeSimplexLocalFiniteElement()
Constructor for compile-time order.
Definition lagrangesimplex.hh:1003
T accumulate(T... args)
T back(T... args)
T begin(T... args)
T beta(T... args)
T copy(T... args)
T data(T... args)
T end(T... args)
T fill(T... args)
T find(T... args)
T resize(T... args)