1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_LOCALFUNCTIONS_NEDELEC_NEDELEC1STKINDSIMPLEX_HH
4 #define DUNE_LOCALFUNCTIONS_NEDELEC_NEDELEC1STKINDSIMPLEX_HH
5 
6 #include <numeric>
7 
8 #include <dune/common/fmatrix.hh>
9 #include <dune/common/fvector.hh>
10 
11 #include <dune/geometry/referenceelements.hh>
12 #include <dune/geometry/type.hh>
13 
14 #include <dune/localfunctions/common/localbasis.hh>
15 #include <dune/localfunctions/common/localfiniteelementtraits.hh>
16 #include <dune/localfunctions/common/localinterpolation.hh>   // For deprecated makeFunctionWithCallOperator
17 #include <dune/localfunctions/common/localkey.hh>
18 
19 namespace Dune
20 {
21 namespace Impl
22 {
23   /** \brief Nedelec shape functions of the first kind on reference simplices
24    *
25    * \note These shape functions are implemented for the reference simplices only!
26    *   The transformation to other simplices has to be done by the user.
27    *
28    * \tparam D Number type used for domain coordinates
29    * \tparam R Number type used for function values
30    * \tparam dim Dimension of the reference element
31    * \tparam k Order of the Nedelec element
32    */
33   template<class D, class R, int dim, int k>
34   class Nedelec1stKindSimplexLocalBasis
35   {
36     // Number of edges of the reference simplex
37     constexpr static std::size_t numberOfEdges = dim*(dim+1)/2;
38 
39   public:
40     using Traits = LocalBasisTraits<D,dim,FieldVector<D,dim>,
41                                     R,dim,FieldVector<R,dim>,
42                                     FieldMatrix<R,dim,dim> >;
43 
44     /** \brief Constructor with default edge orientation
45      *
46      * Default orientation means that all edges point from the vertex with lower index
47      * to the vertex with higher index. The tangential parts of all shape functions
48      * point in the direction of the edge.
49      */
Nedelec1stKindSimplexLocalBasis()50     Nedelec1stKindSimplexLocalBasis()
51     {
52       std::fill(edgeOrientation_.begin(), edgeOrientation_.end(), 1.0);
53     }
54 
55     /** \brief Constructor for a given edge orientation
56      */
Nedelec1stKindSimplexLocalBasis(std::bitset<numberOfEdges> edgeOrientation)57     Nedelec1stKindSimplexLocalBasis(std::bitset<numberOfEdges> edgeOrientation)
58     : Nedelec1stKindSimplexLocalBasis()
59     {
60       for (std::size_t i=0; i<edgeOrientation_.size(); i++)
61         edgeOrientation_[i] *= edgeOrientation[i] ? -1.0 : 1.0;
62     }
63 
64     //! \brief Number of shape functions
size()65     static constexpr unsigned int size()
66     {
67       static_assert(dim==2 || dim==3, "Nedelec shape functions are implemented only for 2d and 3d simplices.");
68       if (dim==2)
69         return k * (k+2);
70       if (dim==3)
71         return k * (k+2) * (k+3) / 2;
72     }
73 
74     /** \brief Evaluate values of all shape functions at a given point
75      *
76      * \param[in]  in  The evaluation point
77      * \param[out] out Jacobians of all shape functions at that point
78      */
evaluateFunction(const typename Traits::DomainType & in,std::vector<typename Traits::RangeType> & out) const79     void evaluateFunction(const typename Traits::DomainType& in,
80                            std::vector<typename Traits::RangeType>& out) const
81     {
82       static_assert(k==1, "Evaluating Nédélec shape functions is implemented only for first order.");
83       out.resize(size());
84 
85       if (dim==2)
86       {
87         // First-order Nédélec shape functions on a triangle are of the form
88         //
89         //         (a1, a2) + b(-x2, x1)^T,     a_1, a_2, b \in R
90         out[0] = {D(1) - in[1],  in[0]};
91         out[1] = {in[1],        -in[0]+D(1)};
92         out[2] = {-in[1],        in[0]};
93       }
94 
95       if constexpr (dim==3)
96       {
97         // First-order Nédélec shape functions on a tetrahedron are of the form
98         //
99         //          a + b \times x,       a, b \in R^3
100         //
101         // The following coefficients create the six basis vectors
102         // that are dual to the edge degrees of freedom:
103         //
104         // a[0] = { 1,  0,  0}              b[0] = { 0, -1,  1}
105         // a[1] = { 0,  1,  0}              b[1] = { 1,  0, -1}
106         // a[2] = { 0,  0,  0}              b[2] = { 0,  0,  1}
107         // a[3] = { 0,  0,  1}              b[3] = {-1,  1,  0}
108         // a[4] = { 0,  0,  0}              b[4] = { 0, -1,  0}
109         // a[5] = { 0,  0,  0}              b[5] = { 1,  0,  0}
110         //
111         // The following implementation uses these values, and simply
112         // skips all the zeros.
113 
114         out[0] = { 1 - in[1] - in[2],     in[0]        ,     in[0]        };
115         out[1] = {     in[1]        , 1 - in[0] - in[2],             in[1]};
116         out[2] = {   - in[1]        ,     in[0]        , 0                };
117         out[3] = {             in[2],             in[2], 1 - in[0] - in[1]};
118         out[4] = {            -in[2], 0                ,     in[0]        };
119         out[5] = { 0                ,            -in[2],             in[1]};
120       }
121 
122       for (std::size_t i=0; i<out.size(); i++)
123         out[i] *= edgeOrientation_[i];
124     }
125 
126     /** \brief Evaluate Jacobians of all shape functions at a given point
127      *
128      * \param[in]  in  The evaluation point
129      * \param[out] out Jacobians of all shape functions at that point
130      */
evaluateJacobian(const typename Traits::DomainType & in,std::vector<typename Traits::JacobianType> & out) const131     void evaluateJacobian(const typename Traits::DomainType& in,
132                           std::vector<typename Traits::JacobianType>& out) const
133     {
134       out.resize(size());
135       if (dim==2)
136       {
137         out[0][0] = { 0, -1};
138         out[0][1] = { 1,  0};
139 
140         out[1][0] = { 0,  1};
141         out[1][1] = {-1,  0};
142 
143         out[2][0] = { 0, -1};
144         out[2][1] = { 1,  0};
145       }
146       if (dim==3)
147       {
148         out[0][0] = { 0,-1,-1};
149         out[0][1] = { 1, 0, 0};
150         out[0][2] = { 1, 0, 0};
151 
152         out[1][0] = { 0, 1,  0};
153         out[1][1] = {-1, 0, -1};
154         out[1][2] = { 0, 1,  0};
155 
156         out[2][0] = { 0, -1, 0};
157         out[2][1] = { 1,  0, 0};
158         out[2][2] = { 0,  0, 0};
159 
160         out[3][0] = { 0,  0, 1};
161         out[3][1] = { 0,  0, 1};
162         out[3][2] = {-1, -1, 0};
163 
164         out[4][0] = { 0, 0, -1};
165         out[4][1] = { 0, 0,  0};
166         out[4][2] = { 1, 0,  0};
167 
168         out[5][0] = { 0, 0,  0};
169         out[5][1] = { 0, 0, -1};
170         out[5][2] = { 0, 1,  0};
171       }
172 
173       for (std::size_t i=0; i<out.size(); i++)
174         out[i] *= edgeOrientation_[i];
175 
176     }
177 
178     /** \brief Evaluate partial derivatives of all shape functions at a given point
179      *
180      * \param[in] order The partial derivative to be computed, as a multi-index
181      * \param[in] in  The evaluation point
182      * \param[out] out Jacobians of all shape functions at that point
183      */
partial(const std::array<unsigned int,dim> & order,const typename Traits::DomainType & in,std::vector<typename Traits::RangeType> & out) const184     void partial(const std::array<unsigned int, dim>& order,
185                  const typename Traits::DomainType& in,
186                  std::vector<typename Traits::RangeType>& out) const
187     {
188       auto totalOrder = std::accumulate(order.begin(), order.end(), 0);
189       if (totalOrder == 0) {
190         evaluateFunction(in, out);
191       } else if (totalOrder == 1) {
192         auto const direction = std::distance(order.begin(), std::find(order.begin(), order.end(), 1));
193         out.resize(size());
194 
195         if (dim==2)
196         {
197           if (direction==0)
198           {
199             out[0] = {0,  1};
200             out[1] = {0, -1};
201             out[2] = {0,  1};
202           }
203           else
204           {
205             out[0] = {-1, 0};
206             out[1] = { 1, 0};
207             out[2] = {-1, 0};
208           }
209         }
210 
211         if (dim==3)
212         {
213           switch (direction)
214           {
215             case 0:
216             out[0] = { 0, 1, 1};
217             out[1] = { 0,-1, 0};
218             out[2] = { 0, 1, 0};
219             out[3] = { 0, 0,-1};
220             out[4] = { 0, 0, 1};
221             out[5] = { 0, 0, 0};
222             break;
223 
224             case 1:
225             out[0] = {-1, 0, 0};
226             out[1] = { 1, 0, 1};
227             out[2] = {-1, 0, 0};
228             out[3] = { 0, 0,-1};
229             out[4] = { 0, 0, 0};
230             out[5] = { 0, 0, 1};
231             break;
232 
233             case 2:
234             out[0] = {-1, 0, 0};
235             out[1] = { 0,-1, 0};
236             out[2] = { 0, 0, 0};
237             out[3] = { 1, 1, 0};
238             out[4] = {-1, 0, 0};
239             out[5] = { 0,-1, 0};
240             break;
241           }
242         }
243 
244         for (std::size_t i=0; i<out.size(); i++)
245           out[i] *= edgeOrientation_[i];
246 
247       } else {
248         out.resize(size());
249         for (std::size_t i = 0; i < size(); ++i)
250           for (std::size_t j = 0; j < dim; ++j)
251             out[i][j] = 0;
252       }
253 
254     }
255 
256     //! \brief Polynomial order of the shape functions
order() const257     unsigned int order() const
258     {
259       return k;
260     }
261 
262   private:
263 
264     // Orientations of the simplex edges
265     std::array<R,numberOfEdges> edgeOrientation_;
266   };
267 
268 
269   /** \brief Assignment of Nedelec degrees of freedom to subentities of the reference simplex
270    * \tparam dim Dimension of the domain
271    * \tparam k Order of the Nedelec finite element
272    */
273   template <int dim, int k>
274   class Nedelec1stKindSimplexLocalCoefficients
275   {
276   public:
277     //! \brief Default constructor
Nedelec1stKindSimplexLocalCoefficients()278     Nedelec1stKindSimplexLocalCoefficients ()
279     : localKey_(size())
280     {
281       static_assert(k==1, "Only first-order Nédélec local coefficients are implemented.");
282       // Assign all degrees of freedom to edges
283       // TODO: This is correct only for first-order Nédélec elements
284       for (std::size_t i=0; i<size(); i++)
285         localKey_[i] = LocalKey(i,dim-1,0);
286     }
287 
288     //! Number of degrees of freedom
size() const289     std::size_t size() const
290     {
291       static_assert(dim==2 || dim==3, "Nédélec shape functions are implemented only for 2d and 3d simplices.");
292       return (dim==2) ? k * (k+2)
293                       : k * (k+2) * (k+3) / 2;
294     }
295 
296     /** \brief Get assignment of i-th degree of freedom to a reference simplex subentity
297      */
localKey(std::size_t i) const298     const LocalKey& localKey (std::size_t i) const
299     {
300       return localKey_[i];
301     }
302 
303   private:
304     std::vector<LocalKey> localKey_;
305   };
306 
307   /** \brief Project a given function into the Nedelec space
308    *
309    * \tparam LB The LocalBasis that spans the space
310    */
311   template<class LB>
312   class Nedelec1stKindSimplexLocalInterpolation
313   {
314     static constexpr auto dim = LB::Traits::dimDomain;
315     static constexpr auto size = LB::size();
316 
317     // Number of edges of the reference simplex
318     constexpr static std::size_t numberOfEdges = dim*(dim+1)/2;
319 
320   public:
321 
322     //! \brief Constructor with given set of edge orientations
Nedelec1stKindSimplexLocalInterpolation(std::bitset<numberOfEdges> s=0)323     Nedelec1stKindSimplexLocalInterpolation (std::bitset<numberOfEdges> s = 0)
324     {
325       auto refElement = Dune::referenceElement<double,dim>(GeometryTypes::simplex(dim));
326 
327       for (std::size_t i=0; i<numberOfEdges; i++)
328         m_[i] = refElement.position(i,dim-1);
329 
330       for (std::size_t i=0; i<numberOfEdges; i++)
331       {
332         auto vertexIterator = refElement.subEntities(i,dim-1,dim).begin();
333         auto v0 = *vertexIterator;
334         auto v1 = *(++vertexIterator);
335         // By default, edges point from the vertex with the smaller index
336         // to the vertex with the larger index.
337         if (v0>v1)
338           std::swap(v0,v1);
339         edge_[i] = refElement.position(v1,dim) - refElement.position(v0,dim);
340         edge_[i] *= (s[i]) ? -1.0 : 1.0;
341       }
342     }
343 
344     /** \brief Project a given function into the Nedelec space
345      *
346      * \param f The function to interpolate, must implement RangeField operator(DomainType)
347      * \param[out] out The coefficients of the projection
348      */
349     template<typename F, typename C>
interpolate(const F & ff,std::vector<C> & out) const350     void interpolate (const F& ff, std::vector<C>& out) const
351     {
352       out.resize(size);
353       auto&& f = Impl::makeFunctionWithCallOperator<typename LB::Traits::DomainType>(ff);
354 
355       for (std::size_t i=0; i<size; i++)
356       {
357         auto y = f(m_[i]);
358         out[i] = 0.0;
359         for (int j=0; j<dim; j++)
360           out[i] += y[j]*edge_[i][j];
361       }
362     }
363 
364   private:
365     // Edge midpoints of the reference simplex
366     std::array<typename LB::Traits::DomainType, numberOfEdges> m_;
367     // Edges of the reference simplex
368     std::array<typename LB::Traits::DomainType, numberOfEdges> edge_;
369   };
370 
371 }
372 
373 
374   /**
375    * \brief Nédélec elements of the first kind for simplex elements
376    *
377    * These elements have been described in :
378    *
379    *   J.C. Nédélec, "Mixed finite elements in R^3", Numer.Math., 35(3):315-341,1980.
380    *   DOI: http://dx.doi.org/10.1007/BF01396415
381    *
382    * The order count starts at '1'.  This is the counting used, e.g., by Nédélec himself,
383    * and by Kirby, Logg, Rognes, Terrel, "Common and unusual finite elements",
384    * https://doi.org/10.1007/978-3-642-23099-8_3
385    *
386    * \note These shape functions are implemented for the reference simplices only!
387    *   The transformation to other simplices has to be done by the user.
388    *   One way of doing that is using the implementation of the covariant
389    *   Piola transform in globalvaluedlocalfinitelement.hh in dune-functions.
390    *
391    * \ingroup Nedelec
392    *
393    * \tparam D Number type used for domain coordinates
394    * \tparam R Number type use for shape function values
395    * \tparam dim Dimension of the domain
396    * \tparam k Order of the Nedelec finite element (lowest is 1)
397    *
398    */
399   template<class D, class R, int dim, int k>
400   class Nedelec1stKindSimplexLocalFiniteElement
401   {
402   public:
403     using Traits = LocalFiniteElementTraits<Impl::Nedelec1stKindSimplexLocalBasis<D,R,dim,k>,
404                                             Impl::Nedelec1stKindSimplexLocalCoefficients<dim,k>,
405                                             Impl::Nedelec1stKindSimplexLocalInterpolation<Impl::Nedelec1stKindSimplexLocalBasis<D,R,dim,k> > >;
406 
407     static_assert(dim==2 || dim==3, "Nedelec elements are only implemented for 2d and 3d elements.");
408     static_assert(k==1,   "Nedelec elements of the first kind are currently only implemented for order k==1.");
409 
410     /** \brief Default constructor
411      */
412     Nedelec1stKindSimplexLocalFiniteElement() = default;
413 
414     /**
415      * \brief Constructor with explicitly given edge orientations
416      *
417      * \param s Edge orientation indicator
418      */
Nedelec1stKindSimplexLocalFiniteElement(std::bitset<dim * (dim+1)/2> s)419     Nedelec1stKindSimplexLocalFiniteElement (std::bitset<dim*(dim+1)/2> s) :
420       basis_(s),
421       interpolation_(s)
422     {}
423 
localBasis() const424     const typename Traits::LocalBasisType& localBasis () const
425     {
426       return basis_;
427     }
428 
localCoefficients() const429     const typename Traits::LocalCoefficientsType& localCoefficients () const
430     {
431       return coefficients_;
432     }
433 
localInterpolation() const434     const typename Traits::LocalInterpolationType& localInterpolation () const
435     {
436       return interpolation_;
437     }
438 
size()439     static constexpr unsigned int size ()
440     {
441       return Traits::LocalBasisType::size();
442     }
443 
type()444     static constexpr GeometryType type ()
445     {
446       return GeometryTypes::simplex(dim);
447     }
448 
449   private:
450     typename Traits::LocalBasisType basis_;
451     typename Traits::LocalCoefficientsType coefficients_;
452     typename Traits::LocalInterpolationType interpolation_;
453   };
454 
455 }
456 
457 #endif
458