DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
FiniteElement.h
1// Copyright (C) 2020-2026 Garth N. Wells and Matthew W. Scroggs
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include "traits.h"
10#include <array>
11#include <basix/finite-element.h>
12#include <basix/maps.h>
13#include <cassert>
14#include <concepts>
15#include <cstddef>
16#include <cstdint>
17#include <dolfinx/mesh/cell_types.h>
18#include <functional>
19#include <memory>
20#include <optional>
21#include <span>
22#include <stdexcept>
23#include <utility>
24#include <vector>
25
26namespace dolfinx::fem
27{
29enum class doftransform : std::uint8_t
30{
33 inverse = 2,
35};
36
37template <std::floating_point T>
38class FiniteElement;
39
74std::vector<std::size_t>
75compute_value_shape(basix::maps::type map_type,
76 std::span<const std::size_t> reference_value_shape,
77 std::size_t gdim);
78
81template <std::floating_point T>
83{
84 std::reference_wrapper<const basix::FiniteElement<T>>
90 std::optional<std::vector<std::size_t>> value_shape = std::nullopt;
91 bool symmetry = false;
93};
94
96template <typename U, typename V, typename W>
97BasixElementData(U element, V bs, W symmetry)
99
197template <std::floating_point T>
199{
200public:
202 using geometry_type = T;
203
215 std::size_t gdim,
216 const std::optional<std::vector<std::size_t>>& value_shape
217 = std::nullopt,
218 bool symmetric = false);
219
231 std::size_t gdim);
232
251 const std::vector<std::shared_ptr<const FiniteElement<geometry_type>>>&
252 elements);
253
263 FiniteElement(mesh::CellType cell_type, std::span<const geometry_type> points,
264 std::array<std::size_t, 2> pshape,
265 std::vector<std::size_t> value_shape = {},
266 bool symmetric = false);
267
269 FiniteElement(const FiniteElement& element) = delete;
270
272 FiniteElement(FiniteElement&& element) = default;
273
275 ~FiniteElement() = default;
276
278 FiniteElement& operator=(const FiniteElement& element) = delete;
279
281 FiniteElement& operator=(FiniteElement&& element) = default;
282
287 bool operator==(const FiniteElement& e) const;
288
293 bool operator!=(const FiniteElement& e) const;
294
296 mesh::CellType cell_type() const noexcept;
297
304 const std::string& signature() const noexcept;
305
313 int space_dimension() const noexcept;
314
326 int block_size() const noexcept;
327
340 int value_size() const;
341
359 std::span<const std::size_t> value_shape() const;
360
383 int physical_base_value_size() const;
384
400 int reference_value_size() const;
401
416 std::span<const std::size_t> reference_value_shape() const;
417
419 const std::vector<std::vector<std::vector<int>>>&
420 entity_dofs() const noexcept;
421
424 const std::vector<std::vector<std::vector<int>>>&
425 entity_closure_dofs() const noexcept;
426
433 bool symmetric() const;
434
447 void tabulate(std::span<geometry_type> values,
448 std::span<const geometry_type> X,
449 std::array<std::size_t, 2> shape, int order) const;
450
461 std::pair<std::vector<geometry_type>, std::array<std::size_t, 4>>
462 tabulate(std::span<const geometry_type> X, std::array<std::size_t, 2> shape,
463 int order) const;
464
467 int num_sub_elements() const noexcept;
468
476 bool is_mixed() const noexcept;
477
479 const std::vector<std::shared_ptr<const FiniteElement<geometry_type>>>&
480 sub_elements() const noexcept;
481
483 std::shared_ptr<const FiniteElement<geometry_type>>
484 extract_sub_element(const std::vector<int>& component) const;
485
488 const basix::FiniteElement<geometry_type>& basix_element() const;
489
491 basix::maps::type map_type() const;
492
499 bool interpolation_ident() const noexcept;
500
505 bool map_ident() const noexcept;
506
517 std::pair<std::vector<geometry_type>, std::array<std::size_t, 2>>
518 interpolation_points() const;
519
529 std::pair<std::vector<geometry_type>, std::array<std::size_t, 2>>
531
544 std::pair<std::vector<geometry_type>, std::array<std::size_t, 2>>
546
560 bool needs_dof_transformations() const noexcept;
561
576 bool needs_dof_permutations() const noexcept;
577
617 template <typename U>
618 std::function<void(std::span<U>, std::span<const std::uint32_t>, std::int32_t,
619 int)>
620 dof_transformation_fn(doftransform ttype, bool scalar_element = false) const
621 {
623 return nullptr;
624
625 if (!_sub_elements.empty())
626 {
627 if (!_reference_value_shape) // Mixed element
628 {
629 std::vector<std::function<void(
630 std::span<U>, std::span<const std::uint32_t>, std::int32_t, int)>>
631 sub_element_fns;
632 std::vector<int> dims;
633 sub_element_fns.reserve(_sub_elements.size());
634 dims.reserve(_sub_elements.size());
635 for (std::size_t i = 0; i < _sub_elements.size(); ++i)
636 {
637 sub_element_fns.push_back(
638 _sub_elements[i]->template dof_transformation_fn<U>(ttype));
639 dims.push_back(_sub_elements[i]->space_dimension());
640 }
641
642 return [dims = std::move(dims),
643 sub_element_fns = std::move(sub_element_fns)](
644 std::span<U> data, std::span<const std::uint32_t> cell_info,
645 std::int32_t cell, int block_size)
646 {
647 std::size_t offset = 0;
648 for (std::size_t e = 0; e < sub_element_fns.size(); ++e)
649 {
650 const std::size_t width = dims[e] * block_size;
651 if (sub_element_fns[e])
652 {
653 sub_element_fns[e](data.subspan(offset, width), cell_info, cell,
654 block_size);
655 }
656 offset += width;
657 }
658 };
659 }
660 else if (!scalar_element)
661 {
662 // Blocked element
663 std::function<void(std::span<U>, std::span<const std::uint32_t>,
664 std::int32_t, int)>
665 sub_fn
666 = _sub_elements.front()->template dof_transformation_fn<U>(ttype);
667 assert(sub_fn); // Consistent with needs_dof_transformations() above
668 const int ebs = _bs;
669 return [ebs, sub_fn = std::move(sub_fn)](
670 std::span<U> data, std::span<const std::uint32_t> cell_info,
671 std::int32_t cell, int data_block_size)
672 { sub_fn(data, cell_info, cell, ebs * data_block_size); };
673 }
674 }
675
676 switch (ttype)
677 {
679 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
680 std::int32_t cell, int block_size)
681 { Tt_inv_apply(data, cell_info[cell], block_size); };
683 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
684 std::int32_t cell, int block_size)
685 { Tt_apply(data, cell_info[cell], block_size); };
687 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
688 std::int32_t cell, int block_size)
689 { Tinv_apply(data, cell_info[cell], block_size); };
691 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
692 std::int32_t cell, int block_size)
693 { T_apply(data, cell_info[cell], block_size); };
694 default:
695 throw std::invalid_argument("Unknown transformation type");
696 }
697 }
698
722 template <typename U>
723 std::function<void(std::span<U>, std::span<const std::uint32_t>, std::int32_t,
724 int)>
726 bool scalar_element = false) const
727 {
729 return nullptr;
730 else if (!_sub_elements.empty())
731 {
732 if (!_reference_value_shape) // Mixed element
733 {
734 std::vector<std::function<void(
735 std::span<U>, std::span<const std::uint32_t>, std::int32_t, int)>>
736 sub_element_fns;
737 std::vector<int> dims;
738 sub_element_fns.reserve(_sub_elements.size());
739 dims.reserve(_sub_elements.size());
740 for (std::size_t i = 0; i < _sub_elements.size(); ++i)
741 {
742 sub_element_fns.push_back(
743 _sub_elements[i]->template dof_transformation_right_fn<U>(ttype));
744 dims.push_back(_sub_elements[i]->space_dimension());
745 }
746
747 return [dims = std::move(dims),
748 sub_element_fns = std::move(sub_element_fns)](
749 std::span<U> data, std::span<const std::uint32_t> cell_info,
750 std::int32_t cell, int block_size)
751 {
752 // `data` is (block_size, ndofs), row-major. Sub-element `e`
753 // owns columns [offset, offset + dims[e]) of every row, so the
754 // rows are transformed one at a time.
755 const std::size_t ndofs = data.size() / block_size;
756 std::size_t offset = 0;
757 for (std::size_t e = 0; e < sub_element_fns.size(); ++e)
758 {
759 if (sub_element_fns[e])
760 {
761 for (int i = 0; i < block_size; ++i)
762 {
763 sub_element_fns[e](data.subspan(i * ndofs + offset, dims[e]),
764 cell_info, cell, 1);
765 }
766 }
767 offset += dims[e];
768 }
769 };
770 }
771 else if (!scalar_element)
772 {
773 // Blocked element
774 // The transformation from the left can be used here as blocked
775 // elements use xyzxyzxyz ordering, and so applying the DOF
776 // transformation from the right is equivalent to applying the DOF
777 // transformation from the left to data using xxxyyyzzz ordering
778 std::function<void(std::span<U>, std::span<const std::uint32_t>,
779 std::int32_t, int)>
780 sub_fn
781 = _sub_elements.front()->template dof_transformation_fn<U>(ttype);
782 assert(sub_fn); // Consistent with needs_dof_transformations() above
783 return [this, sub_fn = std::move(sub_fn)](
784 std::span<U> data, std::span<const std::uint32_t> cell_info,
785 std::int32_t cell, int data_block_size)
786 {
787 const int ebs = block_size();
788 const std::size_t dof_count = data.size() / data_block_size;
789 for (int block = 0; block < data_block_size; ++block)
790 {
791 sub_fn(data.subspan(block * dof_count, dof_count), cell_info, cell,
792 ebs);
793 }
794 };
795 }
796 }
797
798 switch (ttype)
799 {
801 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
802 std::int32_t cell, int n)
803 { Tt_inv_apply_right(data, cell_info[cell], n); };
805 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
806 std::int32_t cell, int n)
807 { Tt_apply_right(data, cell_info[cell], n); };
809 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
810 std::int32_t cell, int n)
811 { Tinv_apply_right(data, cell_info[cell], n); };
813 return [this](std::span<U> data, std::span<const std::uint32_t> cell_info,
814 std::int32_t cell, int n)
815 { T_apply_right(data, cell_info[cell], n); };
816 default:
817 throw std::invalid_argument("Unknown transformation type");
818 }
819 }
820
857 template <typename U>
858 void T_apply(std::span<U> data, std::uint32_t cell_permutation, int n) const
859 {
860 assert(_element);
861 _element->T_apply(data, n, cell_permutation);
862 }
863
873 template <typename U>
874 void Tt_inv_apply(std::span<U> data, std::uint32_t cell_permutation,
875 int n) const
876 {
877 assert(_element);
878 _element->Tt_inv_apply(data, n, cell_permutation);
879 }
880
890 template <typename U>
891 void Tt_apply(std::span<U> data, std::uint32_t cell_permutation, int n) const
892 {
893 assert(_element);
894 _element->Tt_apply(data, n, cell_permutation);
895 }
896
905 template <typename U>
906 void Tinv_apply(std::span<U> data, std::uint32_t cell_permutation,
907 int n) const
908 {
909 assert(_element);
910 _element->Tinv_apply(data, n, cell_permutation);
911 }
912
921 template <typename U>
922 void T_apply_right(std::span<U> data, std::uint32_t cell_permutation,
923 int n) const
924 {
925 assert(_element);
926 _element->T_apply_right(data, n, cell_permutation);
927 }
928
938 template <typename U>
939 void Tinv_apply_right(std::span<U> data, std::uint32_t cell_permutation,
940 int n) const
941 {
942 assert(_element);
943 _element->Tinv_apply_right(data, n, cell_permutation);
944 }
945
955 template <typename U>
956 void Tt_apply_right(std::span<U> data, std::uint32_t cell_permutation,
957 int n) const
958 {
959 assert(_element);
960 _element->Tt_apply_right(data, n, cell_permutation);
961 }
962
972 template <typename U>
973 void Tt_inv_apply_right(std::span<U> data, std::uint32_t cell_permutation,
974 int n) const
975 {
976 assert(_element);
977 _element->Tt_inv_apply_right(data, n, cell_permutation);
978 }
979
996 void permute(std::span<std::int32_t> doflist,
997 std::uint32_t cell_permutation) const;
998
1015 void permute_inv(std::span<std::int32_t> doflist,
1016 std::uint32_t cell_permutation) const;
1017
1032 std::function<void(std::span<std::int32_t>, std::uint32_t)>
1033 dof_permutation_fn(bool inverse = false, bool scalar_element = false) const;
1034
1035private:
1036 // Value shape in physical space. For blocked elements this is larger
1037 // than _reference_value_shape. For non-blocked elements it equals
1038 // _reference_value_shape, except for a Piola-mapped element on a
1039 // manifold, where the trailing axes have extent gdim rather than
1040 // tdim. For mixed elements, it is std::nullopt.
1041 std::optional<std::vector<std::size_t>> _value_shape;
1042
1043 // Block size for BlockedElements. This gives the number of DOFs
1044 // co-located at each dof 'point'.
1045 int _bs;
1046
1047 // Element cell shape
1048 mesh::CellType _cell_type;
1049
1050 // Element signature
1051 std::string _signature;
1052
1053 // Dimension of the finite element space (accounting for any blocking)
1054 int _space_dim;
1055
1056 // List of sub-elements (if any)
1057 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>>
1058 _sub_elements;
1059
1060 // Value shape on the reference cell, i.e. the shape Basix tabulates
1061 // in, e.g. {} for a scalar, {3, 3} for a tensor in 3D. For a mixed
1062 // element it is std::nullopt, which is the sentinel used by
1063 // is_mixed() and the dof transformation functions.
1064 std::optional<std::vector<std::size_t>> _reference_value_shape;
1065
1066 // Basix Element (nullptr for mixed elements)
1067 std::unique_ptr<basix::FiniteElement<geometry_type>> _element;
1068
1069 // Indicate whether this element represents a symmetric 2-tensor
1070 bool _symmetric;
1071
1072 // Indicate whether the element needs permutations or transformations
1073 bool _needs_dof_permutations;
1074 bool _needs_dof_transformations;
1075
1076 std::vector<std::vector<std::vector<int>>> _entity_dofs;
1077 std::vector<std::vector<std::vector<int>>> _entity_closure_dofs;
1078
1079 // Quadrature points of a quadrature element (0 dimensional array for
1080 // all elements except quadrature elements)
1081 std::pair<std::vector<geometry_type>, std::array<std::size_t, 2>> _points;
1082};
1083
1084} // namespace dolfinx::fem
Definition CoordinateElement.h:27
bool symmetric() const
Does the element represent a symmetric 2-tensor?
Definition FiniteElement.cpp:425
FiniteElement & operator=(const FiniteElement &element)=delete
Copy assignment.
const std::vector< std::shared_ptr< const FiniteElement< geometry_type > > > & sub_elements() const noexcept
Get subelements (if any).
Definition FiniteElement.cpp:468
void Tinv_apply(std::span< U > data, std::uint32_t cell_permutation, int n) const
Apply the inverse of the operator applied by T_apply().
Definition FiniteElement.h:906
std::span< const std::size_t > value_shape() const
Value shape of the finite element field in physical space.
Definition FiniteElement.cpp:363
std::pair< std::vector< geometry_type >, std::array< std::size_t, 2 > > create_interpolation_operator(const FiniteElement &from) const
Create a matrix that maps degrees of freedom from one element to this element (interpolation).
Definition FiniteElement.cpp:569
void T_apply(std::span< U > data, std::uint32_t cell_permutation, int n) const
Transform basis functions from the reference element ordering and orientation to the globally consist...
Definition FiniteElement.h:858
basix::maps::type map_type() const
Get the map type used by the element.
Definition FiniteElement.cpp:497
int space_dimension() const noexcept
Dimension of the finite element function space (the number of degrees-of-freedom for the element).
Definition FiniteElement.cpp:345
void tabulate(std::span< geometry_type > values, std::span< const geometry_type > X, std::array< std::size_t, 2 > shape, int order) const
Evaluate derivatives of the basis functions up to given order at points in the reference cell.
Definition FiniteElement.cpp:437
void Tt_inv_apply_right(std::span< U > data, std::uint32_t cell_permutation, int n) const
Right(post)-apply the transpose inverse of the operator applied by T_apply().
Definition FiniteElement.h:973
FiniteElement & operator=(FiniteElement &&element)=default
Move assignment.
bool needs_dof_transformations() const noexcept
Check if DOF transformations are needed for this element.
Definition FiniteElement.cpp:612
FiniteElement(const FiniteElement &element)=delete
Copy constructor.
void Tt_inv_apply(std::span< U > data, std::uint32_t cell_permutation, int n) const
Apply the inverse transpose of the operator applied by T_apply().
Definition FiniteElement.h:874
std::pair< std::vector< geometry_type >, std::array< std::size_t, 2 > > interpolation_points() const
Points on the reference cell at which an expression needs to be evaluated in order to interpolate the...
Definition FiniteElement.cpp:537
std::shared_ptr< const FiniteElement< geometry_type > > extract_sub_element(const std::vector< int > &component) const
Extract sub finite element for component.
Definition FiniteElement.cpp:475
void T_apply_right(std::span< U > data, std::uint32_t cell_permutation, int n) const
Right(post)-apply the operator applied by T_apply().
Definition FiniteElement.h:922
bool needs_dof_permutations() const noexcept
Check if DOF permutations are needed for this element.
Definition FiniteElement.cpp:618
const std::string & signature() const noexcept
String identifying the finite element.
Definition FiniteElement.cpp:339
const std::vector< std::vector< std::vector< int > > > & entity_dofs() const noexcept
Local DOFs associated with each sub-entity of the cell.
Definition FiniteElement.cpp:412
bool is_mixed() const noexcept
Check if element is a mixed element.
Definition FiniteElement.cpp:461
FiniteElement(const basix::FiniteElement< geometry_type > &element, std::size_t gdim, const std::optional< std::vector< std::size_t > > &value_shape=std::nullopt, bool symmetric=false)
Create a finite element from a Basix finite element.
Definition FiniteElement.cpp:159
int reference_value_size() const
Value size of the base (non-blocked) finite element field on the reference cell.
Definition FiniteElement.cpp:390
int value_size() const
Value size of the finite element field in physical space.
Definition FiniteElement.cpp:351
std::function< void(std::span< U >, std::span< const std::uint32_t >, std::int32_t, int)> dof_transformation_fn(doftransform ttype, bool scalar_element=false) const
Return a function that applies a DOF transformation operator to some data (see T_apply()).
Definition FiniteElement.h:620
bool operator!=(const FiniteElement &e) const
Check if two elements are not equivalent.
Definition FiniteElement.cpp:327
~FiniteElement()=default
Destructor.
T geometry_type
Geometry type of the Mesh that the FunctionSpace is defined on.
Definition FiniteElement.h:202
int physical_base_value_size() const
Number of physical components in one block of the finite element field.
Definition FiniteElement.cpp:372
const basix::FiniteElement< geometry_type > & basix_element() const
Return underlying Basix element (if it exists).
Definition FiniteElement.cpp:485
std::function< void(std::span< std::int32_t >, std::uint32_t)> dof_permutation_fn(bool inverse=false, bool scalar_element=false) const
Return a function that applies a degree-of-freedom permutation to some data.
FiniteElement(FiniteElement &&element)=default
Move constructor.
int num_sub_elements() const noexcept
Number of sub elements (for a mixed or blocked element).
Definition FiniteElement.cpp:455
void Tt_apply(std::span< U > data, std::uint32_t cell_permutation, int n) const
Apply the transpose of the operator applied by T_apply().
Definition FiniteElement.h:891
const std::vector< std::vector< std::vector< int > > > & entity_closure_dofs() const noexcept
Local DOFs associated with the closure of each sub-entity of the cell.
Definition FiniteElement.cpp:419
std::pair< std::vector< geometry_type >, std::array< std::size_t, 2 > > interpolation_operator() const
Definition FiniteElement.cpp:556
int block_size() const noexcept
Block size of the finite element function space.
Definition FiniteElement.cpp:431
std::span< const std::size_t > reference_value_shape() const
Value shape of the base (non-blocked) finite element field on the reference cell.
Definition FiniteElement.cpp:402
void Tt_apply_right(std::span< U > data, std::uint32_t cell_permutation, int n) const
Right(post)-apply the transpose of the operator applied by T_apply().
Definition FiniteElement.h:956
std::function< void(std::span< U >, std::span< const std::uint32_t >, std::int32_t, int)> dof_transformation_right_fn(doftransform ttype, bool scalar_element=false) const
Return a function that applies DOF transformation to some transposed data (see T_apply_right()).
Definition FiniteElement.h:725
mesh::CellType cell_type() const noexcept
Cell shape that the element is defined on.
Definition FiniteElement.cpp:333
bool interpolation_ident() const noexcept
Definition FiniteElement.cpp:524
void Tinv_apply_right(std::span< U > data, std::uint32_t cell_permutation, int n) const
Right(post)-apply the inverse of the operator applied by T_apply().
Definition FiniteElement.h:939
bool operator==(const FiniteElement &e) const
Check if two elements are equivalent.
Definition FiniteElement.cpp:311
bool map_ident() const noexcept
Definition FiniteElement.cpp:510
Finite element method functionality.
Definition assemble_expression_impl.h:22
BasixElementData(U element, V bs, W symmetry) -> BasixElementData< typename std::remove_cvref< U >::type::scalar_type >
Type deduction helper.
doftransform
DOF transformation type.
Definition FiniteElement.h:30
@ transpose
Transpose.
Definition FiniteElement.h:32
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:34
@ inverse
Inverse.
Definition FiniteElement.h:33
@ standard
Standard.
Definition FiniteElement.h:31
std::vector< std::size_t > compute_value_shape(basix::maps::type map_type, std::span< const std::size_t > reference_value_shape, std::size_t gdim)
Value shape of a finite element field in physical space.
Definition FiniteElement.cpp:118
@ cell
Cell.
Definition Form.h:44
CellType
Cell type identifier.
Definition cell_types.h:24
Basix element holder.
Definition FiniteElement.h:83
std::optional< std::vector< std::size_t > > value_shape
Definition FiniteElement.h:90
bool symmetry
Definition FiniteElement.h:91
std::reference_wrapper< const basix::FiniteElement< T > > element
Definition FiniteElement.h:85