Basix 0.11.0.dev0

Home     Installation     Demos     C++ docs     Python docs

finite-element.h
1 // Copyright (c) 2020-2024 Chris Richardson, Matthew Scroggs and Garth . Wells
2 // FEniCS Project
3 // SPDX-License-Identifier: MIT
4 
5 #pragma once
6 
7 #include "cell.h"
8 #include "element-families.h"
9 #include "maps.h"
10 #include "mdspan.hpp"
11 #include "polyset.h"
12 #include "precompute.h"
13 #include "sobolev-spaces.h"
14 #include <array>
15 #include <concepts>
16 #include <cstdint>
17 #include <functional>
18 #include <map>
19 #include <numeric>
20 #include <optional>
21 #include <span>
22 #include <string>
23 #include <tuple>
24 #include <utility>
25 #include <vector>
26 
28 namespace basix
29 {
30 namespace impl
31 {
32 template <typename T, std::size_t d>
33 using mdspan_t = md::mdspan<T, md::dextents<std::size_t, d>>;
34 template <typename T, std::size_t d>
35 using mdarray_t
36  = md::MDSPAN_IMPL_PROPOSED_NAMESPACE::mdarray<T,
37  md::dextents<std::size_t, d>>;
38 
41 template <typename T>
42 std::array<std::vector<mdspan_t<const T, 2>>, 4>
43 to_mdspan(std::array<std::vector<mdarray_t<T, 2>>, 4>& x)
44 {
45  std::array<std::vector<mdspan_t<const T, 2>>, 4> x1;
46  for (std::size_t i = 0; i < x.size(); ++i)
47  for (std::size_t j = 0; j < x[i].size(); ++j)
48  x1[i].emplace_back(x[i][j].data(), x[i][j].extents());
49 
50  return x1;
51 }
52 
55 template <typename T>
56 std::array<std::vector<mdspan_t<const T, 4>>, 4>
57 to_mdspan(std::array<std::vector<mdarray_t<T, 4>>, 4>& M)
58 {
59  std::array<std::vector<mdspan_t<const T, 4>>, 4> M1;
60  for (std::size_t i = 0; i < M.size(); ++i)
61  for (std::size_t j = 0; j < M[i].size(); ++j)
62  M1[i].emplace_back(M[i][j].data(), M[i][j].extents());
63 
64  return M1;
65 }
66 
69 template <typename T>
70 std::array<std::vector<mdspan_t<const T, 2>>, 4>
71 to_mdspan(const std::array<std::vector<std::vector<T>>, 4>& x,
72  const std::array<std::vector<std::array<std::size_t, 2>>, 4>& shape)
73 {
74  std::array<std::vector<mdspan_t<const T, 2>>, 4> x1;
75  for (std::size_t i = 0; i < x.size(); ++i)
76  for (std::size_t j = 0; j < x[i].size(); ++j)
77  x1[i].push_back(mdspan_t<const T, 2>(x[i][j].data(), shape[i][j]));
78 
79  return x1;
80 }
81 
84 template <typename T>
85 std::array<std::vector<mdspan_t<const T, 4>>, 4>
86 to_mdspan(const std::array<std::vector<std::vector<T>>, 4>& M,
87  const std::array<std::vector<std::array<std::size_t, 4>>, 4>& shape)
88 {
89  std::array<std::vector<mdspan_t<const T, 4>>, 4> M1;
90  for (std::size_t i = 0; i < M.size(); ++i)
91  for (std::size_t j = 0; j < M[i].size(); ++j)
92  M1[i].push_back(mdspan_t<const T, 4>(M[i][j].data(), shape[i][j]));
93 
94  return M1;
95 }
96 
97 } // namespace impl
98 
99 namespace element
100 {
102 template <typename T, std::size_t d>
103 using mdspan_t = impl::mdspan_t<T, d>;
104 
120 template <std::floating_point T>
121 std::tuple<std::array<std::vector<std::vector<T>>, 4>,
122  std::array<std::vector<std::array<std::size_t, 2>>, 4>,
123  std::array<std::vector<std::vector<T>>, 4>,
124  std::array<std::vector<std::array<std::size_t, 4>>, 4>>
125 make_discontinuous(const std::array<std::vector<mdspan_t<const T, 2>>, 4>& x,
126  const std::array<std::vector<mdspan_t<const T, 4>>, 4>& M,
127  std::size_t tdim, std::size_t value_size);
128 
129 } // namespace element
130 
136 template <std::floating_point F>
138 {
139  template <typename T, std::size_t d>
140  using mdspan_t = md::mdspan<T, md::dextents<std::size_t, d>>;
141 
142 public:
144  using scalar_type = F;
145 
319  polyset::type poly_type, int degree,
320  const std::vector<std::size_t>& value_shape,
321  mdspan_t<const F, 2> wcoeffs,
322  const std::array<std::vector<mdspan_t<const F, 2>>, 4>& x,
323  const std::array<std::vector<mdspan_t<const F, 4>>, 4>& M,
327  element::lagrange_variant lvariant,
328  element::dpc_variant dvariant,
329  std::vector<int> dof_ordering = {});
330 
332  FiniteElement(const FiniteElement& element) = default;
333 
335  FiniteElement(FiniteElement&& element) = default;
336 
338  ~FiniteElement() = default;
339 
341  FiniteElement& operator=(const FiniteElement& element) = default;
342 
344  FiniteElement& operator=(FiniteElement&& element) = default;
345 
350  bool operator==(const FiniteElement& e) const;
351 
353  std::size_t hash() const;
354 
364  std::array<std::size_t, 4> tabulate_shape(std::size_t nd,
365  std::size_t num_points) const
366  {
367  std::size_t ndsize = 1;
368  for (std::size_t i = 1; i <= nd; ++i)
369  ndsize *= (_cell_tdim + i);
370  for (std::size_t i = 1; i <= nd; ++i)
371  ndsize /= i;
372  std::size_t vs = std::accumulate(_value_shape.begin(), _value_shape.end(),
373  1, std::multiplies{});
374  std::size_t ndofs = _coeffs.second[0];
375  return {ndsize, num_points, ndofs, vs};
376  }
377 
399  std::pair<std::vector<F>, std::array<std::size_t, 4>>
400  tabulate(int nd, impl::mdspan_t<const F, 2> x) const;
401 
425  std::pair<std::vector<F>, std::array<std::size_t, 4>>
426  tabulate(int nd, std::span<const F> x,
427  std::array<std::size_t, 2> shape) const;
428 
454  void tabulate(int nd, impl::mdspan_t<const F, 2> x,
455  mdspan_t<F, 4> basis) const;
456 
482  void tabulate(int nd, std::span<const F> x, std::array<std::size_t, 2> xshape,
483  std::span<F> basis) const;
484 
487  cell::type cell_type() const { return _cell_type; }
488 
491  polyset::type polyset_type() const { return _poly_type; }
492 
495  int degree() const { return _degree; }
496 
501  int embedded_superdegree() const { return _embedded_superdegree; }
502 
506  int embedded_subdegree() const { return _embedded_subdegree; }
507 
513  const std::vector<std::size_t>& value_shape() const { return _value_shape; }
514 
519  int dim() const { return _coeffs.second[0]; }
520 
523  element::family family() const { return _family; }
524 
528  {
529  return _lagrange_variant;
530  }
531 
534  element::dpc_variant dpc_variant() const { return _dpc_variant; }
535 
538  maps::type map_type() const { return _map_type; }
539 
542  sobolev::space sobolev_space() const { return _sobolev_space; }
543 
548  bool discontinuous() const { return _discontinuous; }
549 
553  {
554  return _dof_transformations_are_permutations;
555  }
556 
559  {
560  return _dof_transformations_are_identity;
561  }
562 
577  std::pair<std::vector<F>, std::array<std::size_t, 3>>
578  push_forward(impl::mdspan_t<const F, 3> U, impl::mdspan_t<const F, 3> J,
579  std::span<const F> detJ, impl::mdspan_t<const F, 3> K) const;
580 
588  std::pair<std::vector<F>, std::array<std::size_t, 3>>
589  pull_back(impl::mdspan_t<const F, 3> u, impl::mdspan_t<const F, 3> J,
590  std::span<const F> detJ, impl::mdspan_t<const F, 3> K) const;
591 
623  template <typename O, typename P, typename Q, typename R>
624  std::function<void(O&, const P&, const Q&, F, const R&)> map_fn() const
625  {
626  switch (_map_type)
627  {
628  case maps::type::identity:
629  return [](O& u, const P& U, const Q&, F, const R&)
630  {
631  assert(U.extent(0) == u.extent(0));
632  assert(U.extent(1) == u.extent(1));
633  for (std::size_t i = 0; i < U.extent(0); ++i)
634  for (std::size_t j = 0; j < U.extent(1); ++j)
635  u(i, j) = U(i, j);
636  };
637  case maps::type::covariantPiola:
638  return [](O& u, const P& U, const Q& J, F detJ, const R& K)
639  { maps::covariant_piola(u, U, J, detJ, K); };
640  case maps::type::contravariantPiola:
641  return [](O& u, const P& U, const Q& J, F detJ, const R& K)
642  { maps::contravariant_piola(u, U, J, detJ, K); };
643  case maps::type::doubleCovariantPiola:
644  return [](O& u, const P& U, const Q& J, F detJ, const R& K)
645  { maps::double_covariant_piola(u, U, J, detJ, K); };
646  case maps::type::doubleContravariantPiola:
647  return [](O& u, const P& U, const Q& J, F detJ, const R& K)
648  { maps::double_contravariant_piola(u, U, J, detJ, K); };
649  default:
650  throw std::runtime_error("Map not implemented");
651  }
652  }
653 
661  const std::vector<std::vector<std::vector<int>>>& entity_dofs() const
662  {
663  return _edofs;
664  }
665 
675  const std::vector<std::vector<std::vector<int>>>& entity_closure_dofs() const
676  {
677  return _e_closure_dofs;
678  }
679 
761  std::pair<std::vector<F>, std::array<std::size_t, 3>>
762  base_transformations() const;
763 
768  std::map<cell::type, std::pair<std::vector<F>, std::array<std::size_t, 3>>>
770  {
771  return _entity_transformations;
772  }
773 
796  void permute(std::span<std::int32_t> d, std::uint32_t cell_info) const
797  {
798  if (!_dof_transformations_are_permutations)
799  {
800  throw std::runtime_error(
801  "The DOF transformations for this element are not permutations");
802  }
803 
804  if (_dof_transformations_are_identity)
805  return;
806  else
807  permute_data<std::int32_t, false>(d, 1, cell_info, _eperm);
808  }
809 
827  void permute_inv(std::span<std::int32_t> d, std::uint32_t cell_info) const
828  {
829  if (!_dof_transformations_are_permutations)
830  {
831  throw std::runtime_error(
832  "The DOF transformations for this element are not permutations");
833  }
834 
835  if (_dof_transformations_are_identity)
836  return;
837  else
838  permute_data<std::int32_t, true>(d, 1, cell_info, _eperm_inv);
839  }
840 
856  void permute_subentity_closure(std::span<std::int32_t> d,
857  std::uint32_t cell_info,
858  cell::type entity_type, int entity_index) const
859  {
860  const int entity_dim = cell::topological_dimension(entity_type);
861 
862  int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0;
863 
864  std::uint32_t entity_info = 0;
865  switch (entity_dim)
866  {
867  case 0:
868  return;
869  case 1:
870  entity_info = cell_info >> (face_start + entity_index) & 1;
871  break;
872  case 2:
873  entity_info = cell_info >> (3 * entity_index) & 7;
874  break;
875  default:
876  throw std::runtime_error("Unsupported cell dimension");
877  }
878  permute_subentity_closure(d, entity_info, entity_type);
879  }
880 
893  void permute_subentity_closure_inv(std::span<std::int32_t> d,
894  std::uint32_t cell_info,
895  cell::type entity_type,
896  int entity_index) const
897  {
898  const int entity_dim = cell::topological_dimension(entity_type);
899 
900  int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0;
901 
902  std::uint32_t entity_info;
903  switch (entity_dim)
904  {
905  case 0:
906  return;
907  case 1:
908  entity_info = cell_info >> (face_start + entity_index) & 1;
909  break;
910  case 2:
911  entity_info = cell_info >> (3 * entity_index) & 7;
912  break;
913  default:
914  throw std::runtime_error("Unsupported cell dimension");
915  }
916  permute_subentity_closure_inv(d, entity_info, entity_type);
917  }
918 
933 
934  void permute_subentity_closure(std::span<std::int32_t> d,
935  std::uint32_t entity_info,
936  cell::type entity_type) const
937  {
938  if (!_dof_transformations_are_permutations)
939  {
940  throw std::runtime_error(
941  "The DOF transformations for this element are not permutations");
942  }
943 
944  const int entity_dim = cell::topological_dimension(entity_type);
945 
946  if (entity_dim == 0)
947  return;
948 
949  auto& perm = _subentity_closure_perm.at(entity_type);
950  if (entity_dim == 1)
951  {
952  if (entity_info & 1)
953  {
954  precompute::apply_permutation(perm[0], d);
955  }
956  }
957  else if (entity_dim == 2)
958  {
959  // Rotate a face
960  for (std::uint32_t r = 0; r < (entity_info >> 1 & 3); ++r)
961  {
962  precompute::apply_permutation(perm[0], d);
963  }
964 
965  // Reflect a face (post rotate)
966  if (entity_info & 1)
967  {
968  precompute::apply_permutation(perm[1], d);
969  }
970  }
971  else
972  {
973  throw std::runtime_error(
974  "Invalid dimension for permute_subentity_closure");
975  }
976  }
977 
989  void permute_subentity_closure_inv(std::span<std::int32_t> d,
990  std::uint32_t entity_info,
991  cell::type entity_type) const
992  {
993  if (!_dof_transformations_are_permutations)
994  {
995  throw std::runtime_error(
996  "The DOF transformations for this element are not permutations");
997  }
998 
999  const int entity_dim = cell::topological_dimension(entity_type);
1000 
1001  if (entity_dim == 0)
1002  return;
1003 
1004  auto& perm = _subentity_closure_perm_inv.at(entity_type);
1005  if (entity_dim == 1)
1006  {
1007  if (entity_info & 1)
1008  {
1009  precompute::apply_permutation(perm[0], d);
1010  }
1011  }
1012  else if (entity_dim == 2)
1013  {
1014  // Reflect a face (pre rotate)
1015  if (entity_info & 1)
1016  {
1017  precompute::apply_permutation(perm[1], d);
1018  }
1019 
1020  // Rotate a face
1021  for (std::uint32_t r = 0; r < (entity_info >> 1 & 3); ++r)
1022  {
1023  precompute::apply_permutation(perm[0], d);
1024  }
1025  }
1026  else
1027  {
1028  throw std::runtime_error(
1029  "Invalid dimension for permute_subentity_closure");
1030  }
1031  }
1032 
1063  template <typename T>
1064  void T_apply(std::span<T> u, int n, std::uint32_t cell_info) const;
1065 
1076  template <typename T>
1077  void Tt_apply(std::span<T> u, int n, std::uint32_t cell_info) const;
1078 
1090  template <typename T>
1091  void Tt_inv_apply(std::span<T> u, int n, std::uint32_t cell_info) const;
1092 
1103  template <typename T>
1104  void Tinv_apply(std::span<T> u, int n, std::uint32_t cell_info) const;
1105 
1116  template <typename T>
1117  void Tt_apply_right(std::span<T> u, int n, std::uint32_t cell_info) const;
1118 
1128  template <typename T>
1129  void T_apply_right(std::span<T> u, int n, std::uint32_t cell_info) const;
1130 
1141  template <typename T>
1142  void Tinv_apply_right(std::span<T> u, int n, std::uint32_t cell_info) const;
1143 
1154  template <typename T>
1155  void Tt_inv_apply_right(std::span<T> u, int n, std::uint32_t cell_info) const;
1156 
1163  const std::pair<std::vector<F>, std::array<std::size_t, 2>>& points() const
1164  {
1165  return _points;
1166  }
1167 
1219  const std::pair<std::vector<F>, std::array<std::size_t, 2>>&
1221  {
1222  return _matM;
1223  }
1224 
1230  const std::pair<std::vector<F>, std::array<std::size_t, 2>>&
1231  dual_matrix() const
1232  {
1233  return _dual_matrix;
1234  }
1235 
1272  const std::pair<std::vector<F>, std::array<std::size_t, 2>>& wcoeffs() const
1273  {
1274  return _wcoeffs;
1275  }
1276 
1281  const std::array<
1282  std::vector<std::pair<std::vector<F>, std::array<std::size_t, 2>>>, 4>&
1283  x() const
1284  {
1285  return _x;
1286  }
1287 
1324  const std::array<
1325  std::vector<std::pair<std::vector<F>, std::array<std::size_t, 4>>>, 4>&
1326  M() const
1327  {
1328  return _M;
1329  }
1330 
1334  const std::pair<std::vector<F>, std::array<std::size_t, 2>>&
1336  {
1337  return _coeffs;
1338  }
1339 
1351  {
1352  return !_tensor_factors.empty();
1353  }
1354 
1367  std::vector<std::vector<FiniteElement<F>>>
1369  {
1371  throw std::runtime_error("Element has no tensor product representation.");
1372  return _tensor_factors;
1373  }
1374 
1379  bool interpolation_is_identity() const { return _interpolation_is_identity; }
1380 
1382  int interpolation_nderivs() const { return _interpolation_nderivs; }
1383 
1385  const std::vector<int>& dof_ordering() const { return _dof_ordering; }
1386 
1387 private:
1394  template <typename T, bool post>
1395  void permute_data(
1396  std::span<T> data, int block_size, std::uint32_t cell_info,
1397  const std::map<cell::type, std::vector<std::vector<std::size_t>>>& eperm)
1398  const;
1399 
1400  using array2_t = std::pair<std::vector<F>, std::array<std::size_t, 2>>;
1401  using array3_t = std::pair<std::vector<F>, std::array<std::size_t, 3>>;
1402  using trans_data_t
1403  = std::vector<std::pair<std::vector<std::size_t>, array2_t>>;
1404 
1411  template <typename T, bool post, typename OP>
1412  void
1413  transform_data(std::span<T> data, int block_size, std::uint32_t cell_info,
1414  const std::map<cell::type, trans_data_t>& etrans, OP op) const;
1415 
1416  // Cell type
1417  cell::type _cell_type;
1418 
1419  // Polyset type
1420  polyset::type _poly_type;
1421 
1422  // Topological dimension of the cell
1423  std::size_t _cell_tdim;
1424 
1425  // Topological dimension of the cell
1426  std::vector<std::vector<cell::type>> _cell_subentity_types;
1427 
1428  // Finite element family
1429  element::family _family;
1430 
1431  // Lagrange variant
1432  element::lagrange_variant _lagrange_variant;
1433 
1434  // DPC variant
1435  element::dpc_variant _dpc_variant;
1436 
1437  // Degree that was input when creating the element
1438  int _degree;
1439 
1440  // Degree
1441  int _interpolation_nderivs;
1442 
1443  // Highest degree polynomial in element's polyset
1444  int _embedded_superdegree;
1445 
1446  // Highest degree space that is a subspace of element's polyset
1447  int _embedded_subdegree;
1448 
1449  // Value shape
1450  std::vector<std::size_t> _value_shape;
1451 
1453  maps::type _map_type;
1454 
1456  sobolev::space _sobolev_space;
1457 
1458  // Shape function coefficient of expansion sets on cell. If shape
1459  // function is given by @f$\psi_i = \sum_{k} \phi_{k}
1460  // \alpha^{i}_{k}@f$, then _coeffs(i, j) = @f$\alpha^i_k@f$. ie
1461  // _coeffs.row(i) are the expansion coefficients for shape function i
1462  // (@f$\psi_{i}@f$).
1463  std::pair<std::vector<F>, std::array<std::size_t, 2>> _coeffs;
1464 
1465  // Dofs associated with each cell (sub-)entity
1466  std::vector<std::vector<std::vector<int>>> _edofs;
1467 
1468  // Dofs associated with the closdure of each cell (sub-)entity
1469  std::vector<std::vector<std::vector<int>>> _e_closure_dofs;
1470 
1471  // Entity transformations
1472  std::map<cell::type, array3_t> _entity_transformations;
1473 
1474  // Set of points used for point evaluation
1475  // Experimental - currently used for an implementation of
1476  // "tabulate_dof_coordinates" Most useful for Lagrange. This may change or go
1477  // away. For non-Lagrange elements, these points will be used in combination
1478  // with _interpolation_matrix to perform interpolation
1479  std::pair<std::vector<F>, std::array<std::size_t, 2>> _points;
1480 
1481  // Interpolation points on the cell. The shape is (entity_dim, num
1482  // entities of given dimension, num_points, tdim)
1483  std::array<std::vector<std::pair<std::vector<F>, std::array<std::size_t, 2>>>,
1484  4>
1485  _x;
1486 
1488  std::pair<std::vector<F>, std::array<std::size_t, 2>> _matM;
1489 
1490  // Indicates whether or not the DOF transformations are all
1491  // permutations
1492  bool _dof_transformations_are_permutations;
1493 
1494  // Indicates whether or not the DOF transformations are all identity
1495  bool _dof_transformations_are_identity;
1496 
1497  // The entity permutations (factorised). This will only be set if
1498  // _dof_transformations_are_permutations is True and
1499  // _dof_transformations_are_identity is False
1500  std::map<cell::type, std::vector<std::vector<std::size_t>>> _eperm;
1501 
1502  // The reverse entity permutations (factorised). This will only be set
1503  // if _dof_transformations_are_permutations is True and
1504  // _dof_transformations_are_identity is False
1505  std::map<cell::type, std::vector<std::vector<std::size_t>>> _eperm_inv;
1506 
1507  // The entity transformations in precomputed form
1508  std::map<cell::type, trans_data_t> _etrans;
1509 
1510  // The transposed entity transformations in precomputed form
1511  std::map<cell::type, trans_data_t> _etransT;
1512 
1513  // The inverse entity transformations in precomputed form
1514  std::map<cell::type, trans_data_t> _etrans_inv;
1515 
1516  // The inverse transpose entity transformations in precomputed form
1517  std::map<cell::type, trans_data_t> _etrans_invT;
1518 
1519  // The subentity closure permutations (factorised). This will only be set if
1520  // _dof_transformations_are_permutations is True
1521  std::map<cell::type, std::vector<std::vector<std::size_t>>>
1522  _subentity_closure_perm;
1523 
1524  // The inverse subentity closure permutations (factorised). This will only be
1525  // set if _dof_transformations_are_permutations is True
1526  std::map<cell::type, std::vector<std::vector<std::size_t>>>
1527  _subentity_closure_perm_inv;
1528 
1529  // Indicates whether or not this is the discontinuous version of the
1530  // element
1531  bool _discontinuous;
1532 
1533  // The dual matrix
1534  std::pair<std::vector<F>, std::array<std::size_t, 2>> _dual_matrix;
1535 
1536  // Dof reordering for different element dof layout compatibility.
1537  // The reference basix layout is ordered by entity, i.e. dofs on
1538  // vertices, followed by edges, faces, then internal dofs.
1539  // _dof_ordering stores the map to the new order required, e.g.
1540  // for a P2 triangle, _dof_ordering=[0 3 5 1 2 4] will place
1541  // dofs 0, 3, 5 on the vertices and 1, 2, 4, on the edges.
1542  std::vector<int> _dof_ordering;
1543 
1544  // Tensor product representation
1545  // Entries of tuple are (list of elements on an interval, permutation
1546  // of DOF numbers)
1547  // @todo: For vector-valued elements, a tensor product type and a
1548  // scaling factor may additionally be needed.
1549  std::vector<std::vector<FiniteElement>> _tensor_factors;
1550 
1551  // Is the interpolation matrix an identity?
1552  bool _interpolation_is_identity;
1553 
1554  // The coefficients that define the polynomial set in terms of the
1555  // orthonormal polynomials
1556  std::pair<std::vector<F>, std::array<std::size_t, 2>> _wcoeffs;
1557 
1558  // Interpolation matrices for each entity
1559  using array4_t
1560  = std::vector<std::pair<std::vector<F>, std::array<std::size_t, 4>>>;
1561  std::array<array4_t, 4> _M;
1562 };
1563 
1589 template <std::floating_point T>
1590 FiniteElement<T> create_custom_element(
1591  cell::type cell_type, const std::vector<std::size_t>& value_shape,
1592  impl::mdspan_t<const T, 2> wcoeffs,
1593  const std::array<std::vector<impl::mdspan_t<const T, 2>>, 4>& x,
1594  const std::array<std::vector<impl::mdspan_t<const T, 4>>, 4>& M,
1595  int interpolation_nderivs, maps::type map_type,
1596  sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree,
1597  int embedded_superdegree, polyset::type poly_type);
1598 
1610 template <std::floating_point T>
1611 FiniteElement<T> create_element(element::family family, cell::type cell,
1612  int degree, element::lagrange_variant lvariant,
1613  element::dpc_variant dvariant,
1614  bool discontinuous,
1615  std::vector<int> dof_ordering = {});
1616 
1629 std::optional<std::vector<int>>
1630 tp_dof_ordering(element::family family, cell::type cell, int degree,
1631  element::lagrange_variant lvariant,
1632  element::dpc_variant dvariant, bool discontinuous);
1633 
1644 std::vector<int> lex_dof_ordering(element::family family, cell::type cell,
1645  int degree, element::lagrange_variant lvariant,
1646  element::dpc_variant dvariant,
1647  bool discontinuous);
1648 
1662 template <std::floating_point T>
1663 std::optional<std::vector<std::vector<FiniteElement<T>>>>
1664 tp_factors(element::family family, cell::type cell, int degree,
1666  bool discontinuous, const std::vector<int>& dof_ordering);
1667 
1679 template <std::floating_point T>
1680 FiniteElement<T>
1681 create_tp_element(element::family family, cell::type cell, int degree,
1682  element::lagrange_variant lvariant,
1683  element::dpc_variant dvariant, bool discontinuous);
1684 
1687 std::string version();
1688 
1689 //-----------------------------------------------------------------------------
1690 template <std::floating_point F>
1691 template <typename T, bool post>
1692 void FiniteElement<F>::permute_data(
1693  std::span<T> data, int block_size, std::uint32_t cell_info,
1694  const std::map<cell::type, std::vector<std::vector<std::size_t>>>& eperm)
1695  const
1696 {
1697  if (_cell_tdim >= 2)
1698  {
1699  // This assumes 3 bits are used per face. This will need updating if 3D
1700  // cells with faces with more than 4 sides are implemented
1701  int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0;
1702 
1703  // Permute DOFs on edges
1704  {
1705  auto& trans = eperm.at(cell::type::interval)[0];
1706  for (std::size_t e = 0; e < _edofs[1].size(); ++e)
1707  {
1708  // Reverse an edge
1709  if (cell_info >> (face_start + e) & 1)
1710  {
1711  precompute::apply_permutation_mapped(trans, data, _edofs[1][e],
1712  block_size);
1713  }
1714  }
1715  }
1716 
1717  if (_cell_tdim == 3)
1718  {
1719  // Permute DOFs on faces
1720  for (std::size_t f = 0; f < _edofs[2].size(); ++f)
1721  {
1722  auto& trans = eperm.at(_cell_subentity_types[2][f]);
1723 
1724  // Reflect a face (pre rotate)
1725  if (!post and cell_info >> (3 * f) & 1)
1726  {
1727  precompute::apply_permutation_mapped(trans[1], data, _edofs[2][f],
1728  block_size);
1729  }
1730 
1731  // Rotate a face
1732  for (std::uint32_t r = 0; r < (cell_info >> (3 * f + 1) & 3); ++r)
1733  {
1734  precompute::apply_permutation_mapped(trans[0], data, _edofs[2][f],
1735  block_size);
1736  }
1737 
1738  // Reflect a face (post rotate)
1739  if (post and cell_info >> (3 * f) & 1)
1740  {
1741  precompute::apply_permutation_mapped(trans[1], data, _edofs[2][f],
1742  block_size);
1743  }
1744  }
1745  }
1746  }
1747 }
1748 //-----------------------------------------------------------------------------
1749 template <std::floating_point F>
1750 template <typename T, bool post, typename OP>
1751 void FiniteElement<F>::transform_data(
1752  std::span<T> data, int block_size, std::uint32_t cell_info,
1753  const std::map<cell::type, trans_data_t>& etrans, OP op) const
1754 {
1755  if (_cell_tdim >= 2)
1756  {
1757  // This assumes 3 bits are used per face. This will need updating if
1758  // 3D cells with faces with more than 4 sides are implemented
1759  int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0;
1760  int dofstart = 0;
1761  for (auto& edofs0 : _edofs[0])
1762  dofstart += edofs0.size();
1763 
1764  // Transform DOFs on edges
1765  {
1766  auto& [v_size_t, matrix] = etrans.at(cell::type::interval)[0];
1767  for (std::size_t e = 0; e < _edofs[1].size(); ++e)
1768  {
1769  // Reverse an edge
1770  if (cell_info >> (face_start + e) & 1)
1771  {
1772  op(std::span(v_size_t),
1773  mdspan_t<const F, 2>(matrix.first.data(), matrix.second), data,
1774  dofstart, block_size);
1775  }
1776  dofstart += _edofs[1][e].size();
1777  }
1778  }
1779 
1780  if (_cell_tdim == 3)
1781  {
1782  // Permute DOFs on faces
1783  for (std::size_t f = 0; f < _edofs[2].size(); ++f)
1784  {
1785  auto& trans = etrans.at(_cell_subentity_types[2][f]);
1786 
1787  // Reflect a face (pre rotation)
1788  if (!post and cell_info >> (3 * f) & 1)
1789  {
1790  const auto& m = trans[1];
1791  const auto& v_size_t = std::get<0>(m);
1792  const auto& matrix = std::get<1>(m);
1793  op(std::span(v_size_t),
1794  mdspan_t<const F, 2>(matrix.first.data(), matrix.second), data,
1795  dofstart, block_size);
1796  }
1797 
1798  // Rotate a face
1799  for (std::uint32_t r = 0; r < (cell_info >> (3 * f + 1) & 3); ++r)
1800  {
1801  const auto& m = trans[0];
1802  const auto& v_size_t = std::get<0>(m);
1803  const auto& matrix = std::get<1>(m);
1804  op(std::span(v_size_t),
1805  mdspan_t<const F, 2>(matrix.first.data(), matrix.second), data,
1806  dofstart, block_size);
1807  }
1808 
1809  // Reflect a face (post rotation)
1810  if (post and cell_info >> (3 * f) & 1)
1811  {
1812  const auto& m = trans[1];
1813  const auto& v_size_t = std::get<0>(m);
1814  const auto& matrix = std::get<1>(m);
1815  op(std::span(v_size_t),
1816  mdspan_t<const F, 2>(matrix.first.data(), matrix.second), data,
1817  dofstart, block_size);
1818  }
1819 
1820  dofstart += _edofs[2][f].size();
1821  }
1822  }
1823  }
1824 }
1825 //-----------------------------------------------------------------------------
1826 template <std::floating_point F>
1827 template <typename T>
1828 void FiniteElement<F>::T_apply(std::span<T> u, int n,
1829  std::uint32_t cell_info) const
1830 {
1831  if (_dof_transformations_are_identity)
1832  return;
1833 
1834  if (_dof_transformations_are_permutations)
1835  permute_data<T, false>(u, n, cell_info, _eperm);
1836  else
1837  {
1838  transform_data<T, false>(u, n, cell_info, _etrans,
1839  precompute::apply_matrix<F, T>);
1840  }
1841 }
1842 //-----------------------------------------------------------------------------
1843 template <std::floating_point F>
1844 template <typename T>
1845 void FiniteElement<F>::Tt_apply(std::span<T> u, int n,
1846  std::uint32_t cell_info) const
1847 {
1848  if (_dof_transformations_are_identity)
1849  return;
1850  else if (_dof_transformations_are_permutations)
1851  permute_data<T, true>(u, n, cell_info, _eperm_inv);
1852  else
1853  {
1854  transform_data<T, true>(u, n, cell_info, _etransT,
1855  precompute::apply_matrix<F, T>);
1856  }
1857 }
1858 //-----------------------------------------------------------------------------
1859 template <std::floating_point F>
1860 template <typename T>
1861 void FiniteElement<F>::Tt_inv_apply(std::span<T> u, int n,
1862  std::uint32_t cell_info) const
1863 {
1864  if (_dof_transformations_are_identity)
1865  return;
1866  else if (_dof_transformations_are_permutations)
1867  permute_data<T, false>(u, n, cell_info, _eperm);
1868  else
1869  {
1870  transform_data<T, false>(u, n, cell_info, _etrans_invT,
1871  precompute::apply_matrix<F, T>);
1872  }
1873 }
1874 //-----------------------------------------------------------------------------
1875 template <std::floating_point F>
1876 template <typename T>
1877 void FiniteElement<F>::Tinv_apply(std::span<T> u, int n,
1878  std::uint32_t cell_info) const
1879 {
1880  if (_dof_transformations_are_identity)
1881  return;
1882  else if (_dof_transformations_are_permutations)
1883  permute_data<T, true>(u, n, cell_info, _eperm_inv);
1884  else
1885  {
1886  transform_data<T, true>(u, n, cell_info, _etrans_inv,
1887  precompute::apply_matrix<F, T>);
1888  }
1889 }
1890 //-----------------------------------------------------------------------------
1891 template <std::floating_point F>
1892 template <typename T>
1893 void FiniteElement<F>::Tt_apply_right(std::span<T> u, int n,
1894  std::uint32_t cell_info) const
1895 {
1896  if (_dof_transformations_are_identity)
1897  return;
1898  else if (_dof_transformations_are_permutations)
1899  {
1900  assert(u.size() % n == 0);
1901  const int step = u.size() / n;
1902  for (int i = 0; i < n; ++i)
1903  {
1904  std::span<T> dblock(u.data() + i * step, step);
1905  permute_data<T, false>(dblock, 1, cell_info, _eperm);
1906  }
1907  }
1908  else
1909  {
1910  transform_data<T, false>(u, n, cell_info, _etrans,
1911  precompute::apply_tranpose_matrix_right<F, T>);
1912  }
1913 }
1914 //-----------------------------------------------------------------------------
1915 template <std::floating_point F>
1916 template <typename T>
1917 void FiniteElement<F>::Tinv_apply_right(std::span<T> u, int n,
1918  std::uint32_t cell_info) const
1919 {
1920  if (_dof_transformations_are_identity)
1921  return;
1922  else if (_dof_transformations_are_permutations)
1923  {
1924  assert(u.size() % n == 0);
1925  const int step = u.size() / n;
1926  for (int i = 0; i < n; ++i)
1927  {
1928  std::span<T> dblock(u.data() + i * step, step);
1929  permute_data<T, false>(dblock, 1, cell_info, _eperm);
1930  }
1931  }
1932  else
1933  {
1934  transform_data<T, false>(u, n, cell_info, _etrans_invT,
1935  precompute::apply_tranpose_matrix_right<F, T>);
1936  }
1937 }
1938 //-----------------------------------------------------------------------------
1939 template <std::floating_point F>
1940 template <typename T>
1941 void FiniteElement<F>::T_apply_right(std::span<T> u, int n,
1942  std::uint32_t cell_info) const
1943 {
1944  if (_dof_transformations_are_identity)
1945  return;
1946  else if (_dof_transformations_are_permutations)
1947  {
1948  assert(u.size() % n == 0);
1949  const int step = u.size() / n;
1950  for (int i = 0; i < n; ++i)
1951  {
1952  std::span<T> dblock(u.data() + i * step, step);
1953  permute_data<T, true>(dblock, 1, cell_info, _eperm_inv);
1954  }
1955  }
1956  else
1957  {
1958  transform_data<T, true>(u, n, cell_info, _etransT,
1959  precompute::apply_tranpose_matrix_right<F, T>);
1960  }
1961 }
1962 //-----------------------------------------------------------------------------
1963 template <std::floating_point F>
1964 template <typename T>
1965 void FiniteElement<F>::Tt_inv_apply_right(std::span<T> u, int n,
1966  std::uint32_t cell_info) const
1967 {
1968  if (_dof_transformations_are_identity)
1969  return;
1970  else if (_dof_transformations_are_permutations)
1971  {
1972  assert(u.size() % n == 0);
1973  const int step = u.size() / n;
1974  for (int i = 0; i < n; ++i)
1975  {
1976  std::span<T> dblock(u.data() + i * step, step);
1977  permute_data<T, true>(dblock, 1, cell_info, _eperm_inv);
1978  }
1979  }
1980  else
1981  {
1982  transform_data<T, true>(u, n, cell_info, _etrans_inv,
1983  precompute::apply_tranpose_matrix_right<F, T>);
1984  }
1985 }
1986 //-----------------------------------------------------------------------------
1987 
1988 } // namespace basix
A finite element.
Definition: finite-element.h:138
std::map< cell::type, std::pair< std::vector< F >, std::array< std::size_t, 3 > > > entity_transformations() const
Return the entity dof transformation matrices.
Definition: finite-element.h:769
void Tinv_apply_right(std::span< T > u, int n, std::uint32_t cell_info) const
Right(post)-apply the inverse of the operator applied by T_apply().
Definition: finite-element.h:1917
const std::vector< int > & dof_ordering() const
Get dof layout.
Definition: finite-element.h:1385
std::vector< std::vector< FiniteElement< F > > > get_tensor_product_representation() const
Get the tensor product representation of this element.
Definition: finite-element.h:1368
const std::pair< std::vector< F >, std::array< std::size_t, 2 > > & wcoeffs() const
Get the coefficients that define the polynomial set in terms of the orthonormal polynomials.
Definition: finite-element.h:1272
const std::array< std::vector< std::pair< std::vector< F >, std::array< std::size_t, 4 > > >, 4 > & M() const
Get the interpolation matrices for each subentity.
Definition: finite-element.h:1326
std::pair< std::vector< F >, std::array< std::size_t, 3 > > base_transformations() const
Get the base transformations.
Definition: finite-element.cpp:1881
void T_apply(std::span< T > u, int n, std::uint32_t cell_info) const
Transform basis functions from the reference element ordering and orientation to the globally consist...
Definition: finite-element.h:1828
void Tt_apply(std::span< T > u, int n, std::uint32_t cell_info) const
Apply the transpose of the operator applied by T_apply().
Definition: finite-element.h:1845
void permute_subentity_closure_inv(std::span< std::int32_t > d, std::uint32_t cell_info, cell::type entity_type, int entity_index) const
Perform the inverse of the operation applied by permute_subentity_closure().
Definition: finite-element.h:893
const std::pair< std::vector< F >, std::array< std::size_t, 2 > > & coefficient_matrix() const
Get the matrix of coefficients.
Definition: finite-element.h:1335
const std::pair< std::vector< F >, std::array< std::size_t, 2 > > & dual_matrix() const
Get the dual matrix.
Definition: finite-element.h:1231
bool dof_transformations_are_identity() const
Indicates is the dof transformations are all the identity.
Definition: finite-element.h:558
bool operator==(const FiniteElement &e) const
Check if two elements are the same.
Definition: finite-element.cpp:1703
void permute_subentity_closure_inv(std::span< std::int32_t > d, std::uint32_t entity_info, cell::type entity_type) const
Perform the inverse of the operation applied by permute_subentity_closure().
Definition: finite-element.h:989
int embedded_subdegree() const
Highest degree n such that a Lagrange (or vector Lagrange) element of degree n is a subspace of this ...
Definition: finite-element.h:506
void T_apply_right(std::span< T > u, int n, std::uint32_t cell_info) const
Right(post)-apply the operator applied by T_apply().
Definition: finite-element.h:1941
FiniteElement(FiniteElement &&element)=default
Move constructor.
bool has_tensor_product_factorisation() const
Indicates whether or not this element can be represented as a product of elements defined on lower-di...
Definition: finite-element.h:1350
const std::vector< std::vector< std::vector< int > > > & entity_closure_dofs() const
Get the dofs on the closure of each topological entity: (vertices, edges, faces, cell) in that order.
Definition: finite-element.h:675
int interpolation_nderivs() const
The number of derivatives needed when interpolating.
Definition: finite-element.h:1382
std::pair< std::vector< F >, std::array< std::size_t, 4 > > tabulate(int nd, impl::mdspan_t< const F, 2 > x) const
Compute basis values and derivatives at set of points.
Definition: finite-element.cpp:1789
int embedded_superdegree() const
Lowest degree n such that the highest degree polynomial in this element is contained in a Lagrange (o...
Definition: finite-element.h:501
std::function< void(O &, const P &, const Q &, F, const R &)> map_fn() const
Return a function that performs the appropriate push-forward/pull-back for the element type.
Definition: finite-element.h:624
int dim() const
Dimension of the finite element space.
Definition: finite-element.h:519
FiniteElement(element::family family, cell::type cell_type, polyset::type poly_type, int degree, const std::vector< std::size_t > &value_shape, mdspan_t< const F, 2 > wcoeffs, const std::array< std::vector< mdspan_t< const F, 2 >>, 4 > &x, const std::array< std::vector< mdspan_t< const F, 4 >>, 4 > &M, int interpolation_nderivs, maps::type map_type, sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree, int embedded_superdegree, element::lagrange_variant lvariant, element::dpc_variant dvariant, std::vector< int > dof_ordering={})
Construct a finite element.
void permute(std::span< std::int32_t > d, std::uint32_t cell_info) const
Permute indices associated with degree-of-freedoms on the reference element ordering to the globally ...
Definition: finite-element.h:796
std::size_t hash() const
Get a unique hash of this element.
Definition: finite-element.cpp:1742
F scalar_type
Scalar type.
Definition: finite-element.h:144
void permute_inv(std::span< std::int32_t > d, std::uint32_t cell_info) const
Perform the inverse of the operation applied by permute().
Definition: finite-element.h:827
void Tt_apply_right(std::span< T > u, int n, std::uint32_t cell_info) const
Right(post)-apply the transpose of the operator applied by T_apply().
Definition: finite-element.h:1893
void Tt_inv_apply(std::span< T > u, int n, std::uint32_t cell_info) const
Apply the inverse transpose of the operator applied by T_apply().
Definition: finite-element.h:1861
void permute_subentity_closure(std::span< std::int32_t > d, std::uint32_t cell_info, cell::type entity_type, int entity_index) const
Permute indices associated with degree-of-freedoms on the closure of a sub-entity of the reference el...
Definition: finite-element.h:856
element::lagrange_variant lagrange_variant() const
Lagrange variant of the element.
Definition: finite-element.h:527
void Tt_inv_apply_right(std::span< T > u, int n, std::uint32_t cell_info) const
Right(post)-apply the transpose inverse of the operator applied by T_apply().
Definition: finite-element.h:1965
const std::vector< std::size_t > & value_shape() const
Element value tensor shape.
Definition: finite-element.h:513
int degree() const
Get the element polynomial degree.
Definition: finite-element.h:495
void permute_subentity_closure(std::span< std::int32_t > d, std::uint32_t entity_info, cell::type entity_type) const
Permute indices associated with degree-of-freedoms on the closure of a sub-entity of the reference el...
Definition: finite-element.h:934
const std::pair< std::vector< F >, std::array< std::size_t, 2 > > & points() const
Return the interpolation points.
Definition: finite-element.h:1163
void Tinv_apply(std::span< T > u, int n, std::uint32_t cell_info) const
Apply the inverse of the operator applied by T_apply().
Definition: finite-element.h:1877
std::array< std::size_t, 4 > tabulate_shape(std::size_t nd, std::size_t num_points) const
Array shape for tabulate basis values and derivatives at set of points.
Definition: finite-element.h:364
FiniteElement & operator=(const FiniteElement &element)=default
Assignment operator.
sobolev::space sobolev_space() const
Underlying Sobolev space for this element.
Definition: finite-element.h:542
FiniteElement(const FiniteElement &element)=default
Copy constructor.
polyset::type polyset_type() const
Get the element polyset type.
Definition: finite-element.h:491
const std::pair< std::vector< F >, std::array< std::size_t, 2 > > & interpolation_matrix() const
Return a matrix of weights interpolation.
Definition: finite-element.h:1220
std::pair< std::vector< F >, std::array< std::size_t, 3 > > pull_back(impl::mdspan_t< const F, 3 > u, impl::mdspan_t< const F, 3 > J, std::span< const F > detJ, impl::mdspan_t< const F, 3 > K) const
Map function values from a physical cell to the reference.
Definition: finite-element.cpp:1986
bool dof_transformations_are_permutations() const
Indicates if the degree-of-freedom transformations are all permutations.
Definition: finite-element.h:552
std::pair< std::vector< F >, std::array< std::size_t, 3 > > push_forward(impl::mdspan_t< const F, 3 > U, impl::mdspan_t< const F, 3 > J, std::span< const F > detJ, impl::mdspan_t< const F, 3 > K) const
Map function values from the reference to a physical cell.
Definition: finite-element.cpp:1950
const std::vector< std::vector< std::vector< int > > > & entity_dofs() const
Get the dofs on each topological entity: (vertices, edges, faces, cell) in that order.
Definition: finite-element.h:661
cell::type cell_type() const
Get the element cell type.
Definition: finite-element.h:487
bool interpolation_is_identity() const
Indicates whether or not the interpolation matrix for this element is an identity matrix.
Definition: finite-element.h:1379
const std::array< std::vector< std::pair< std::vector< F >, std::array< std::size_t, 2 > > >, 4 > & x() const
Get the interpolation points for each subentity.
Definition: finite-element.h:1283
FiniteElement & operator=(FiniteElement &&element)=default
Move assignment operator.
bool discontinuous() const
Indicates whether this element is the discontinuous variant.
Definition: finite-element.h:548
maps::type map_type() const
Map type for the element.
Definition: finite-element.h:538
element::dpc_variant dpc_variant() const
DPC variant of the element.
Definition: finite-element.h:534
element::family family() const
The finite element family.
Definition: finite-element.h:523
~FiniteElement()=default
Destructor.
type
Cell type.
Definition: cell.h:21
int topological_dimension(cell::type celltype)
Definition: cell.cpp:294
lagrange_variant
Variants of a Lagrange space that can be created.
Definition: element-families.h:12
impl::mdspan_t< T, d > mdspan_t
Typedef for mdspan.
Definition: finite-element.h:103
std::tuple< std::array< std::vector< std::vector< T > >, 4 >, std::array< std::vector< std::array< std::size_t, 2 > >, 4 >, std::array< std::vector< std::vector< T > >, 4 >, std::array< std::vector< std::array< std::size_t, 4 > >, 4 > > make_discontinuous(const std::array< std::vector< mdspan_t< const T, 2 >>, 4 > &x, const std::array< std::vector< mdspan_t< const T, 4 >>, 4 > &M, std::size_t tdim, std::size_t value_size)
Definition: finite-element.cpp:816
dpc_variant
Definition: element-families.h:32
family
Available element families.
Definition: element-families.h:45
void covariant_piola(O &&r, const P &U, const Q &, double, const R &K)
Covariant Piola map.
Definition: maps.h:62
void contravariant_piola(O &&r, const P &U, const Q &J, double detJ, const R &)
Contravariant Piola map.
Definition: maps.h:82
void double_contravariant_piola(O &&r, const P &U, const Q &J, double detJ, const R &)
Double contravariant Piola map.
Definition: maps.h:132
void double_covariant_piola(O &&r, const P &U, const Q &, double, const R &K)
Double covariant Piola map.
Definition: maps.h:104
type
Map type.
Definition: maps.h:40
type
Cell type.
Definition: polyset.h:137
void apply_permutation(std::span< const std::size_t > perm, std::span< E > data, std::size_t offset=0, std::size_t n=1)
Apply a (precomputed) permutation .
Definition: precompute.h:137
void apply_permutation_mapped(std::span< const std::size_t > perm, std::span< E > data, std::span< const int > emap, std::size_t n=1)
Permutation of mapped data.
Definition: precompute.h:147
space
Sobolev space type.
Definition: sobolev-spaces.h:13
Basix: FEniCS runtime basis evaluation library.
Definition: cell.h:17
FiniteElement< T > create_element(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous, std::vector< int > dof_ordering={})
Definition: finite-element.cpp:192
std::optional< std::vector< std::vector< FiniteElement< T > > > > tp_factors(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous, const std::vector< int > &dof_ordering)
Definition: finite-element.cpp:353
std::string version()
Definition: finite-element.cpp:2020
std::vector< int > lex_dof_ordering(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous)
Definition: finite-element.cpp:509
std::optional< std::vector< int > > tp_dof_ordering(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous)
Definition: finite-element.cpp:398
FiniteElement< T > create_tp_element(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous)
Definition: finite-element.cpp:329
FiniteElement< T > create_custom_element(cell::type cell_type, const std::vector< std::size_t > &value_shape, impl::mdspan_t< const T, 2 > wcoeffs, const std::array< std::vector< impl::mdspan_t< const T, 2 >>, 4 > &x, const std::array< std::vector< impl::mdspan_t< const T, 4 >>, 4 > &M, int interpolation_nderivs, maps::type map_type, sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree, int embedded_superdegree, polyset::type poly_type)
Definition: finite-element.cpp:901