Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/d3/d12/discreteoperators_8h_source.html
DOLFINx 0.6.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
discreteoperators.h
1// Copyright (C) 2015-2022 Garth N. Wells, Jørgen S. Dokken
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 "DofMap.h"
10#include "FiniteElement.h"
11#include "FunctionSpace.h"
12#include <array>
13#include <dolfinx/common/IndexMap.h>
14#include <dolfinx/common/math.h>
15#include <dolfinx/mesh/Mesh.h>
16#include <memory>
17#include <span>
18#include <vector>
19
20namespace dolfinx::fem
21{
22
46template <typename T>
48 auto&& mat_set)
49{
50 namespace stdex = std::experimental;
51 using cmdspan2_t
52 = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
53 using mdspan2_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
54 using cmdspan4_t
55 = stdex::mdspan<const double, stdex::dextents<std::size_t, 4>>;
56
57 // Get mesh
58 std::shared_ptr<const mesh::Mesh> mesh = V1.mesh();
59 assert(mesh);
60
61 // Check spaces
62 std::shared_ptr<const FiniteElement> e0 = V0.element();
63 assert(e0);
64 if (e0->map_type() != basix::maps::type::identity)
65 throw std::runtime_error("Wrong finite element space for V0.");
66 if (e0->block_size() != 1)
67 throw std::runtime_error("Block size is greater than 1 for V0.");
68 if (e0->reference_value_size() != 1)
69 throw std::runtime_error("Wrong value size for V0.");
70
71 std::shared_ptr<const FiniteElement> e1 = V1.element();
72 assert(e1);
73 if (e1->map_type() != basix::maps::type::covariantPiola)
74 throw std::runtime_error("Wrong finite element space for V1.");
75 if (e1->block_size() != 1)
76 throw std::runtime_error("Block size is greater than 1 for V1.");
77
78 // Get V0 (H(curl)) space interpolation points
79 const auto [X, Xshape] = e1->interpolation_points();
80
81 // Tabulate first order derivatives of Lagrange space at H(curl)
82 // interpolation points
83 const int ndofs0 = e0->space_dimension();
84 const int tdim = mesh->topology().dim();
85 std::vector<double> phi0_b((tdim + 1) * Xshape[0] * ndofs0 * 1);
86 cmdspan4_t phi0(phi0_b.data(), tdim + 1, Xshape[0], ndofs0, 1);
87 e0->tabulate(phi0_b, X, Xshape, 1);
88
89 // Reshape lagrange basis derivatives as a matrix of shape (tdim *
90 // num_points, num_dofs_per_cell)
91 cmdspan2_t dphi_reshaped(
92 phi0_b.data() + phi0.extent(3) * phi0.extent(2) * phi0.extent(1),
93 tdim * phi0.extent(1), phi0.extent(2));
94
95 // Get inverse DOF transform function
96 auto apply_inverse_dof_transform
97 = e1->get_dof_transformation_function<T>(true, true, false);
98
99 // Generate cell permutations
100 mesh->topology_mutable().create_entity_permutations();
101 const std::vector<std::uint32_t>& cell_info
102 = mesh->topology().get_cell_permutation_info();
103
104 // Create element kernel function
105 std::shared_ptr<const DofMap> dofmap0 = V0.dofmap();
106 assert(dofmap0);
107 std::shared_ptr<const DofMap> dofmap1 = V1.dofmap();
108 assert(dofmap1);
109
110 // Build the element interpolation matrix
111 std::vector<T> Ab(e1->space_dimension() * ndofs0);
112 {
113 stdex::mdspan<T, stdex::dextents<std::size_t, 2>> A(
114 Ab.data(), e1->space_dimension(), ndofs0);
115 const auto [Pi, shape] = e1->interpolation_operator();
116 cmdspan2_t _Pi(Pi.data(), shape);
117 math::dot(_Pi, dphi_reshaped, A);
118 }
119
120 // Insert local interpolation matrix for each cell
121 auto cell_map = mesh->topology().index_map(tdim);
122 assert(cell_map);
123 std::int32_t num_cells = cell_map->size_local();
124 std::vector<T> Ae(Ab.size());
125 for (std::int32_t c = 0; c < num_cells; ++c)
126 {
127 std::copy(Ab.cbegin(), Ab.cend(), Ae.begin());
128 apply_inverse_dof_transform(Ae, cell_info, c, ndofs0);
129 mat_set(dofmap1->cell_dofs(c), dofmap0->cell_dofs(c), Ae);
130 }
131}
132
148template <typename T>
150 auto&& mat_set)
151{
152 // Get mesh
153 auto mesh = V0.mesh();
154 assert(mesh);
155
156 // Mesh dims
157 const int tdim = mesh->topology().dim();
158 const int gdim = mesh->geometry().dim();
159
160 // Get elements
161 std::shared_ptr<const FiniteElement> e0 = V0.element();
162 assert(e0);
163 std::shared_ptr<const FiniteElement> e1 = V1.element();
164 assert(e1);
165
166 std::span<const std::uint32_t> cell_info;
167 if (e1->needs_dof_transformations() or e0->needs_dof_transformations())
168 {
169 mesh->topology_mutable().create_entity_permutations();
170 cell_info = std::span(mesh->topology().get_cell_permutation_info());
171 }
172
173 // Get dofmaps
174 auto dofmap0 = V0.dofmap();
175 assert(dofmap0);
176 auto dofmap1 = V1.dofmap();
177 assert(dofmap1);
178
179 // Get block sizes and dof transformation operators
180 const int bs0 = e0->block_size();
181 const int bs1 = e1->block_size();
182 const auto apply_dof_transformation0
183 = e0->get_dof_transformation_function<double>(false, false, false);
184 const auto apply_inverse_dof_transform1
185 = e1->get_dof_transformation_function<T>(true, true, false);
186
187 // Get sizes of elements
188 const std::size_t space_dim0 = e0->space_dimension();
189 const std::size_t space_dim1 = e1->space_dimension();
190 const std::size_t dim0 = space_dim0 / bs0;
191 const std::size_t value_size_ref0 = e0->reference_value_size() / bs0;
192 const std::size_t value_size0 = e0->value_size() / bs0;
193 const std::size_t value_size1 = e1->value_size() / bs1;
194
195 // Get geometry data
196 const CoordinateElement& cmap = mesh->geometry().cmap();
198 = mesh->geometry().dofmap();
199 const std::size_t num_dofs_g = cmap.dim();
200 std::span<const double> x_g = mesh->geometry().x();
201
202 namespace stdex = std::experimental;
203 using mdspan2_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
204 using cmdspan2_t
205 = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
206 using cmdspan3_t
207 = stdex::mdspan<const double, stdex::dextents<std::size_t, 3>>;
208 using cmdspan4_t
209 = stdex::mdspan<const double, stdex::dextents<std::size_t, 4>>;
210 using mdspan3_t = stdex::mdspan<double, stdex::dextents<std::size_t, 3>>;
211
212 // Evaluate coordinate map basis at reference interpolation points
213 const auto [X, Xshape] = e1->interpolation_points();
214 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
215 std::vector<double> phi_b(
216 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
217 cmdspan4_t phi(phi_b.data(), phi_shape);
218 cmap.tabulate(1, X, Xshape, phi_b);
219
220 // Evaluate V0 basis functions at reference interpolation points for V1
221 std::vector<double> basis_derivatives_reference0_b(Xshape[0] * dim0
222 * value_size_ref0);
223 cmdspan4_t basis_derivatives_reference0(basis_derivatives_reference0_b.data(),
224 1, Xshape[0], dim0, value_size_ref0);
225 e0->tabulate(basis_derivatives_reference0_b, X, Xshape, 0);
226
227 // Clamp values
228 std::transform(basis_derivatives_reference0_b.begin(),
229 basis_derivatives_reference0_b.end(),
230 basis_derivatives_reference0_b.begin(),
231 [atol = 1e-14](auto x)
232 { return std::abs(x) < atol ? 0.0 : x; });
233
234 // Create working arrays
235 std::vector<double> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
236 mdspan3_t basis_reference0(basis_reference0_b.data(), Xshape[0], dim0,
237 value_size_ref0);
238 std::vector<double> J_b(Xshape[0] * gdim * tdim);
239 mdspan3_t J(J_b.data(), Xshape[0], gdim, tdim);
240 std::vector<double> K_b(Xshape[0] * tdim * gdim);
241 mdspan3_t K(K_b.data(), Xshape[0], tdim, gdim);
242 std::vector<double> detJ(Xshape[0]);
243 std::vector<double> det_scratch(2 * tdim * gdim);
244
245 // Get the interpolation operator (matrix) `Pi` that maps a function
246 // evaluated at the interpolation points to the element degrees of
247 // freedom, i.e. dofs = Pi f_x
248 const auto [_Pi_1, pi_shape] = e1->interpolation_operator();
249 cmdspan2_t Pi_1(_Pi_1.data(), pi_shape);
250
251 bool interpolation_ident = e1->interpolation_ident();
252
253 namespace stdex = std::experimental;
254 using u_t = stdex::mdspan<double, stdex::dextents<std::size_t, 2>>;
255 using U_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
256 using J_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
257 using K_t = stdex::mdspan<const double, stdex::dextents<std::size_t, 2>>;
258 auto push_forward_fn0 = e0->basix_element().map_fn<u_t, U_t, J_t, K_t>();
259
260 // Basis values of Lagrange space unrolled for block size
261 // (num_quadrature_points, Lagrange dof, value_size)
262 std::vector<double> basis_values_b(Xshape[0] * bs0 * dim0 * e1->value_size());
263 mdspan3_t basis_values(basis_values_b.data(), Xshape[0], bs0 * dim0,
264 e1->value_size());
265 std::vector<double> mapped_values_b(Xshape[0] * bs0 * dim0
266 * e1->value_size());
267 mdspan3_t mapped_values(mapped_values_b.data(), Xshape[0], bs0 * dim0,
268 e1->value_size());
269
270 auto pull_back_fn1 = e1->basix_element().map_fn<u_t, U_t, K_t, J_t>();
271
272 std::vector<double> coord_dofs_b(num_dofs_g * gdim);
273 mdspan2_t coord_dofs(coord_dofs_b.data(), num_dofs_g, gdim);
274 std::vector<double> basis0_b(Xshape[0] * dim0 * value_size0);
275 mdspan3_t basis0(basis0_b.data(), Xshape[0], dim0, value_size0);
276
277 // Buffers
278 std::vector<T> Ab(space_dim0 * space_dim1);
279 std::vector<T> local1(space_dim1);
280
281 // Iterate over mesh and interpolate on each cell
282 auto cell_map = mesh->topology().index_map(tdim);
283 assert(cell_map);
284 std::int32_t num_cells = cell_map->size_local();
285 for (std::int32_t c = 0; c < num_cells; ++c)
286 {
287 // Get cell geometry (coordinate dofs)
288 auto x_dofs = x_dofmap.links(c);
289 for (std::size_t i = 0; i < x_dofs.size(); ++i)
290 {
291 for (std::size_t j = 0; j < gdim; ++j)
292 coord_dofs(i, j) = x_g[3 * x_dofs[i] + j];
293 }
294
295 // Compute Jacobians and reference points for current cell
296 std::fill(J_b.begin(), J_b.end(), 0);
297 for (std::size_t p = 0; p < Xshape[0]; ++p)
298 {
299 auto dphi = stdex::submdspan(phi, std::pair(1, tdim + 1), p,
300 stdex::full_extent, 0);
301 auto _J = stdex::submdspan(J, p, stdex::full_extent, stdex::full_extent);
302 cmap.compute_jacobian(dphi, coord_dofs, _J);
303 auto _K = stdex::submdspan(K, p, stdex::full_extent, stdex::full_extent);
304 cmap.compute_jacobian_inverse(_J, _K);
305 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
306 }
307
308 // Copy evaluated basis on reference, apply DOF transformations, and
309 // push forward to physical element
310 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
311 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
312 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
313 basis_reference0(k0, k1, k2)
314 = basis_derivatives_reference0(0, k0, k1, k2);
315 for (std::size_t p = 0; p < Xshape[0]; ++p)
316 {
317 apply_dof_transformation0(
318 std::span(basis_reference0.data_handle() + p * dim0 * value_size_ref0,
319 dim0 * value_size_ref0),
320 cell_info, c, value_size_ref0);
321 }
322
323 for (std::size_t p = 0; p < basis0.extent(0); ++p)
324 {
325 auto _u
326 = stdex::submdspan(basis0, p, stdex::full_extent, stdex::full_extent);
327 auto _U = stdex::submdspan(basis_reference0, p, stdex::full_extent,
328 stdex::full_extent);
329 auto _K = stdex::submdspan(K, p, stdex::full_extent, stdex::full_extent);
330 auto _J = stdex::submdspan(J, p, stdex::full_extent, stdex::full_extent);
331 push_forward_fn0(_u, _U, _J, detJ[p], _K);
332 }
333
334 // Unroll basis function for input space for block size
335 for (std::size_t p = 0; p < Xshape[0]; ++p)
336 for (std::size_t i = 0; i < dim0; ++i)
337 for (std::size_t j = 0; j < value_size0; ++j)
338 for (int k = 0; k < bs0; ++k)
339 basis_values(p, i * bs0 + k, j * bs0 + k) = basis0(p, i, j);
340
341 // Pull back the physical values to the reference of output space
342 for (std::size_t p = 0; p < basis_values.extent(0); ++p)
343 {
344 auto _u = stdex::submdspan(basis_values, p, stdex::full_extent,
345 stdex::full_extent);
346 auto _U = stdex::submdspan(mapped_values, p, stdex::full_extent,
347 stdex::full_extent);
348 auto _K = stdex::submdspan(K, p, stdex::full_extent, stdex::full_extent);
349 auto _J = stdex::submdspan(J, p, stdex::full_extent, stdex::full_extent);
350 pull_back_fn1(_U, _u, _K, 1.0 / detJ[p], _J);
351 }
352
353 // Apply interpolation matrix to basis values of V0 at the
354 // interpolation points of V1
355 if (interpolation_ident)
356 {
357 stdex::mdspan<T, stdex::dextents<std::size_t, 3>> A(
358 Ab.data(), Xshape[0], e1->value_size(), space_dim0);
359 for (std::size_t i = 0; i < mapped_values.extent(0); ++i)
360 for (std::size_t j = 0; j < mapped_values.extent(1); ++j)
361 for (std::size_t k = 0; k < mapped_values.extent(2); ++k)
362 A(i, k, j) = mapped_values(i, j, k);
363 }
364 else
365 {
366 for (std::size_t i = 0; i < mapped_values.extent(1); ++i)
367 {
368 auto values = stdex::submdspan(mapped_values, stdex::full_extent, i,
369 stdex::full_extent);
370 impl::interpolation_apply(Pi_1, values, std::span(local1), bs1);
371 for (std::size_t j = 0; j < local1.size(); j++)
372 Ab[space_dim0 * j + i] = local1[j];
373 }
374 }
375
376 apply_inverse_dof_transform1(Ab, cell_info, c, space_dim0);
377 mat_set(dofmap1->cell_dofs(c), dofmap0->cell_dofs(c), Ab);
378 }
379}
380
381} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
A CoordinateElement manages coordinate mappings for isoparametric cells.
Definition: CoordinateElement.h:32
static void compute_jacobian(const U &dphi, const V &cell_geometry, W &&J)
Compute Jacobian for a cell with given geometry using the basis functions and first order derivatives...
Definition: CoordinateElement.h:100
static void compute_jacobian_inverse(const U &J, V &&K)
Compute the inverse of the Jacobian.
Definition: CoordinateElement.h:109
std::array< std::size_t, 4 > tabulate_shape(std::size_t nd, std::size_t num_points) const
Shape of array to fill when calling FiniteElement::tabulate
Definition: CoordinateElement.cpp:42
void tabulate(int nd, std::span< const double > X, std::array< std::size_t, 2 > shape, std::span< double > basis) const
Evaluate basis values and derivatives at set of points.
Definition: CoordinateElement.cpp:47
int dim() const
The dimension of the geometry element space.
Definition: CoordinateElement.cpp:183
static double compute_jacobian_determinant(const U &J, std::span< typename U::value_type > w)
Compute the determinant of the Jacobian.
Definition: CoordinateElement.h:126
This class represents a finite element function space defined by a mesh, a finite element,...
Definition: FunctionSpace.h:31
std::shared_ptr< const DofMap > dofmap() const
The dofmap.
Definition: FunctionSpace.cpp:253
std::shared_ptr< const mesh::Mesh > mesh() const
The mesh.
Definition: FunctionSpace.cpp:246
std::shared_ptr< const FiniteElement > element() const
The finite element.
Definition: FunctionSpace.cpp:248
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:27
std::span< T > links(int node)
Get the links (edges) for given node.
Definition: AdjacencyList.h:109
Finite element method functionality.
Definition: assemble_matrix_impl.h:25
void interpolation_matrix(const FunctionSpace &V0, const FunctionSpace &V1, auto &&mat_set)
Assemble an interpolation operator matrix.
Definition: discreteoperators.h:149
void discrete_gradient(const FunctionSpace &V0, const FunctionSpace &V1, auto &&mat_set)
Assemble a discrete gradient operator.
Definition: discreteoperators.h:47