File: CoordinateElement.h

package info (click to toggle)
fenics-dolfinx 1%3A0.10.0.post4-1exp1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 6,028 kB
  • sloc: cpp: 36,535; python: 25,391; makefile: 226; sh: 171; xml: 55
file content (275 lines) | stat: -rw-r--r-- 10,409 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
// Copyright (C) 2018-2020 Garth N. Wells and Chris N. Richardson
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier:    LGPL-3.0-or-later

#pragma once

#include "ElementDofLayout.h"
#include <algorithm>
#include <array>
#include <basix/element-families.h>
#include <basix/mdspan.hpp>
#include <cmath>
#include <concepts>
#include <cstdint>
#include <dolfinx/common/math.h>
#include <dolfinx/mesh/cell_types.h>
#include <limits>
#include <memory>
#include <span>

namespace basix
{
template <std::floating_point T>
class FiniteElement;
}

namespace dolfinx::fem
{
/// A CoordinateElement manages coordinate mappings for isoparametric
/// cells.
/// @todo A dof layout on a reference cell needs to be defined.
/// @tparam T Floating point (real) type for the geometry and for the
/// element basis.
template <std::floating_point T>
class CoordinateElement
{
public:
  /// @brief Create a coordinate element from a Basix element.
  /// @param[in] element Basix finite element
  explicit CoordinateElement(
      std::shared_ptr<const basix::FiniteElement<T>> element);

  /// @brief Create a Lagrange coordinate element.
  /// @param[in] celltype Cell shape.
  /// @param[in] degree Polynomial degree of the map.
  /// @param[in] type Type of Lagrange element (see Basix documentation
  /// for possible types).
  CoordinateElement(mesh::CellType celltype, int degree,
                    basix::element::lagrange_variant type
                    = basix::element::lagrange_variant::unset);

  /// Destructor
  virtual ~CoordinateElement() = default;

  /// @brief Cell shape.
  /// @return The cell shape
  mesh::CellType cell_shape() const;

  /// @brief The polynomial degree of the element.
  /// @return The degree
  int degree() const;

  /// @brief The dimension of the coordinate element space.
  ///
  /// The number of basis function is returned. E.g., for a linear
  /// triangle cell the dimension will be 3.
  ///
  /// @return Dimension of the coordinate element space.
  int dim() const;

  /// @brief Variant of the element
  basix::element::lagrange_variant variant() const;

  /// @brief Element hash.
  ///
  /// This is the Basix element hash.
  std::uint64_t hash() const;

  /// @brief Shape of array to fill when calling `tabulate`.
  /// @param[in] nd The order of derivatives, up to and including, to
  /// compute. Use 0 for the basis functions only
  /// @param[in] num_points Number of points at which to evaluate the
  /// basis functions.
  /// @return Shape of the array to be filled by `tabulate`, where (0)
  /// is derivative index, (1) is the point index, (2) is the basis
  /// function index and (3) is the basis function component.
  std::array<std::size_t, 4> tabulate_shape(std::size_t nd,
                                            std::size_t num_points) const;

  /// @brief Evaluate basis values and derivatives at set of points.
  /// @param[in] nd The order of derivatives, up to and including, to
  /// compute. Use 0 for the basis functions only.
  /// @param[in] X The points at which to compute the basis functions.
  /// The shape of X is (number of points, geometric dimension).
  /// @param[in] shape The shape of `X`.
  /// @param[out] basis The array to fill with the basis function
  /// values. The shape can be computed using `tabulate_shape`.
  void tabulate(int nd, std::span<const T> X, std::array<std::size_t, 2> shape,
                std::span<T> basis) const;

  /// @brief Given the closure DOFs \f$\tilde{d}\f$ of a cell sub-entity in
  /// reference ordering, this function computes the permuted degrees-of-freedom
  ///   \f[ d = P \tilde{d},\f]
  /// ordered to be consistent with the entity's mesh orientation, where
  /// \f$P\f$ is a permutation matrix. This accounts for orientation
  /// discrepancies between the entity's cell and mesh orientation. All DOFs are
  /// rotated and reflected together, unlike `permute`, which considered
  /// sub-entities independently.
  ///
  /// @param[in,out] d Indices associated with the reference element
  /// degree-of-freedom (in). Indices associated with each physical
  /// element degree-of-freedom (out).
  /// @param[in] cell_info Permutation info for the cell
  /// @param[in] entity_type The cell type of the sub-entity
  /// @param[in] entity_index The local (with respect to the cell) index of the
  /// entity
  void permute_subentity_closure(std::span<std::int32_t> d,
                                 std::uint32_t cell_info,
                                 mesh::CellType entity_type,
                                 int entity_index) const;

  /// Compute Jacobian for a cell with given geometry using the
  /// basis functions and first order derivatives.
  /// @param[in] dphi Derivatives of the basis functions (shape=(tdim,
  /// num geometry nodes))
  /// @param[in] cell_geometry The cell nodes coordinates (shape=(num
  /// geometry nodes, gdim))
  /// @param[out] J The Jacobian. It must have shape=(gdim, tdim) and
  /// must initialized to zero
  template <typename U, typename V, typename W>
  static void compute_jacobian(const U& dphi, const V& cell_geometry, W&& J)
  {
    math::dot(cell_geometry, dphi, J, true);
  }

  /// @brief Compute the inverse of the Jacobian.
  /// @param[in] J Jacobian (shape=(gdim, tdim)).
  /// @param[out] K Inverse Jacobian (shape=(tdim, gdim)).
  template <typename U, typename V>
  static void compute_jacobian_inverse(const U& J, V&& K)
  {
    int gdim = J.extent(0);
    int tdim = K.extent(0);
    if (gdim == tdim)
      math::inv(J, K);
    else
      math::pinv(J, K);
  }

  /// @brief Compute the determinant of the Jacobian.
  /// @param[in] J Jacobian (shape=(gdim, tdim)).
  /// @param[in] w Working memory, required when gdim != tdim. Size
  /// must be at least 2 * gdim * tdim.
  /// @return Determinant of `J`.
  template <typename U>
  static double
  compute_jacobian_determinant(const U& J, std::span<typename U::value_type> w)
  {
    static_assert(U::rank() == 2, "Must be rank 2");
    if (J.extent(0) == J.extent(1))
      return math::det(J);
    else
    {
      assert(w.size() >= 2 * J.extent(0) * J.extent(1));
      using X = typename U::element_type;
      using mdspan2_t = md::mdspan<X, md::dextents<std::size_t, 2>>;
      mdspan2_t B(w.data(), J.extent(1), J.extent(0));
      mdspan2_t BA(w.data() + J.extent(0) * J.extent(1), B.extent(0),
                   J.extent(1));
      for (std::size_t i = 0; i < B.extent(0); ++i)
        for (std::size_t j = 0; j < B.extent(1); ++j)
          B(i, j) = J(j, i);

      // Zero working memory of BA
      std::fill_n(BA.data_handle(), BA.size(), 0);
      math::dot(B, J, BA);
      return std::sqrt(math::det(BA));
    }
  }

  /// Compute and return the dof layout
  ElementDofLayout create_dof_layout() const;

  /// @brief Compute physical coordinates x for points X  in the
  /// reference configuration.
  /// @param[in,out] x The physical coordinates of the reference points
  /// X (rank 2).
  /// @param[in] cell_geometry Cell node physical coordinates (rank 2).
  /// @param[in] phi Tabulated basis functions at reference points X
  /// (rank 2).
  template <typename U, typename V, typename W>
  static void push_forward(U&& x, const V& cell_geometry, const W& phi)
  {
    for (std::size_t i = 0; i < x.extent(0); ++i)
      for (std::size_t j = 0; j < x.extent(1); ++j)
        x(i, j) = 0;

    // Compute x = phi * cell_geometry;
    math::dot(phi, cell_geometry, x);
  }

  /// @brief Compute reference coordinates X for physical coordinates x
  /// for an affine map. For the affine case, `x = J X + x0`, and this
  /// function computes `X = K(x -x0)` where `K = J^{-1}`.
  /// @param[out] X Reference coordinates to compute
  /// (`shape=(num_points, tdim)`),
  /// @param[in] K Inverse of the geometry Jacobian (`shape=(tdim,
  /// gdim)`).
  /// @param[in] x0 Physical coordinate of reference coordinate `X0=(0,
  /// 0, 0)`.
  /// @param[in] x Physical coordinates (shape=(num_points, gdim)).
  template <typename U, typename V, typename W>
  static void pull_back_affine(U&& X, const V& K, std::array<T, 3> x0,
                               const W& x)
  {
    assert(X.extent(0) == x.extent(0));
    assert(X.extent(1) == K.extent(0));
    assert(x.extent(1) == K.extent(1));
    for (std::size_t i = 0; i < X.extent(0); ++i)
      for (std::size_t j = 0; j < X.extent(1); ++j)
        X(i, j) = 0;

    // Calculate X for each point
    for (std::size_t p = 0; p < x.extent(0); ++p)
      for (std::size_t i = 0; i < K.extent(0); ++i)
        for (std::size_t j = 0; j < K.extent(1); ++j)
          X(p, i) += K(i, j) * (x(p, j) - x0[j]);
  }

  /// mdspan typedef
  template <typename X>
  using mdspan2_t = md::mdspan<X, md::dextents<std::size_t, 2>>;

  /// @brief Compute reference coordinates `X` for physical coordinates
  /// `x` for a non-affine map.
  /// @param [in,out] X The reference coordinates to compute
  /// (shape=`(num_points, tdim)`).
  /// @param [in] x Physical coordinates (`shape=(num_points, gdim)`).
  /// @param [in] cell_geometry Cell nodes coordinates (`shape=(num
  /// geometry nodes, gdim)`).
  /// @param [in] tol Tolerance for termination of Newton method.
  /// @param [in] maxit Maximum number of Newton iterations
  /// @note If convergence is not achieved within `maxit`, the function
  /// throws a runtime error.
  void pull_back_nonaffine(mdspan2_t<T> X, mdspan2_t<const T> x,
                           mdspan2_t<const T> cell_geometry,
                           double tol = 1.0e-6, int maxit = 15) const;

  /// @brief Permute a list of DOF numbers on a cell.
  void permute(std::span<std::int32_t> dofs, std::uint32_t cell_perm) const;

  /// @brief Reverses a DOF permutation
  void permute_inv(std::span<std::int32_t> dofs, std::uint32_t cell_perm) const;

  /// @brief Indicates whether the geometry DOF numbers on each cell
  /// need permuting.
  ///
  /// For higher order geometries (where there is more than one DOF on a
  /// subentity of the cell), this will be true.
  bool needs_dof_permutations() const;

  /// Check is geometry map is affine
  /// @return True is geometry map is affine
  bool is_affine() const noexcept { return _is_affine; }

private:
  // Flag denoting affine map
  bool _is_affine;

  // Basix Element
  std::shared_ptr<const basix::FiniteElement<T>> _element;
};
} // namespace dolfinx::fem