DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
discreteoperators.h
1// Copyright (C) 2015-2026 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 <algorithm>
13#include <array>
14#include <concepts>
15#include <dolfinx/common/IndexMap.h>
16#include <dolfinx/common/math.h>
17#include <dolfinx/la/utils.h>
18#include <dolfinx/mesh/Mesh.h>
19#include <memory>
20#include <span>
21#include <stdexcept>
22#include <vector>
23
24namespace dolfinx::fem
25{
26
86template <std::floating_point T, dolfinx::scalar U = T>
88 la::MatSet<U> auto&& mat_set)
89{
90 // Get mesh
91 auto mesh = V0.mesh();
92 assert(mesh);
93 assert(V1.mesh());
94 if (mesh != V1.mesh())
95 throw std::invalid_argument("Meshes must be the same.");
96
97 if (mesh->geometry().dim() != 3)
98 throw std::invalid_argument("Geometric must be equal to 3..");
99 if (mesh->geometry().dim() != mesh->topology()->dim())
100 {
101 throw std::invalid_argument(
102 "Geometric and topological dimensions must be equal.");
103 }
104 constexpr int gdim = 3;
105
106 // Get elements
107 std::shared_ptr<const FiniteElement<T>> e0 = V0.element();
108 assert(e0);
109 if (e0->map_type() != basix::maps::type::covariantPiola)
110 {
111 throw std::invalid_argument(
112 "Finite element for parent space must be covariant Piola.");
113 }
114
115 std::shared_ptr<const FiniteElement<T>> e1 = V1.element();
116 assert(e1);
117 if (e1->map_type() != basix::maps::type::contravariantPiola)
118 {
119 throw std::invalid_argument(
120 "Finite element for target space must be contracovariant Piola.");
121 }
122
123 // Get cell orientation information
124 std::span<const std::uint32_t> cell_info;
125 if (e1->needs_dof_transformations() or e0->needs_dof_transformations())
126 {
127 mesh->topology_mutable()->create_entity_permutations();
128 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
129 }
130
131 // Get dofmaps
132 auto dofmap0 = V0.dofmap();
133 assert(dofmap0);
134 auto dofmap1 = V1.dofmap();
135 assert(dofmap1);
136
137 // Get dof transformation operators
138 auto apply_dof_transformation0
139 = e0->template dof_transformation_fn<T>(doftransform::standard, false);
140 auto apply_inverse_dof_transform1 = e1->template dof_transformation_fn<U>(
142
143 // Get sizes of elements
144 const std::size_t space_dim0 = e0->space_dimension();
145 const std::size_t space_dim1 = e1->space_dimension();
146 if (e0->reference_value_size() != 3)
147 throw std::invalid_argument("Value size for parent space should be 3.");
148 if (e1->reference_value_size() != 3)
149 throw std::invalid_argument("Value size for target space should be 3.");
150
151 // Get the V1 reference interpolation points
152 const auto [X, Xshape] = e1->interpolation_points();
153
154 // Evaluate V0 basis function derivatives at reference interpolation
155 // points for V1, (deriv, pt_idx, phi (dof), comp)
156 const auto [Phi0_b, Phi0_shape] = e0->tabulate(X, Xshape, 1);
157 md::mdspan<const T, md::extents<std::size_t, 4, md::dynamic_extent,
158 md::dynamic_extent, 3>>
159 Phi0(Phi0_b.data(), Phi0_shape);
160
161 // Create working arrays, (point, phi (dof), deriv, comp)
162 md::extents<std::size_t, md::dynamic_extent, md::dynamic_extent, 3, 3>
163 dPhi0_ext(Phi0.extent(1), Phi0.extent(2), Phi0.extent(0) - 1,
164 Phi0.extent(3));
165 std::vector<T> dPhi0_b(dPhi0_ext.extent(0) * dPhi0_ext.extent(1)
166 * dPhi0_ext.extent(2) * dPhi0_ext.extent(3));
167 md::mdspan<T, decltype(dPhi0_ext)> dPhi0(dPhi0_b.data(), dPhi0_ext);
168
169 // Get the interpolation operator (matrix) Pi that maps a function
170 // evaluated at the interpolation points to the V1 element degrees of
171 // freedom, i.e. dofs = Pi f_x
172 const auto [Pi1_b, pi_shape] = e1->interpolation_operator();
173 md::mdspan<const T, md::dextents<std::size_t, 2>> Pi_1(Pi1_b.data(),
174 pi_shape);
175
176 // curl data structure, (pt_idx, phi (dof), comp)
177 std::vector<T> curl_b(dPhi0.extent(0) * dPhi0.extent(1) * dPhi0.extent(3));
178 md::mdspan<
179 T, md::extents<std::size_t, md::dynamic_extent, md::dynamic_extent, 3>>
180 curl(curl_b.data(), dPhi0.extent(0), dPhi0.extent(1), dPhi0.extent(3));
181
182 std::vector<U> Ab(space_dim0 * space_dim1);
183
184 // Iterate over mesh and interpolate on each cell
185 assert(mesh->topology()->index_map(gdim));
186 for (std::int32_t c = 0; c < mesh->topology()->index_map(gdim)->size_local();
187 ++c)
188 {
189 // TODO: re-order loops and/or re-pack Phi0 to allow a simple flat
190 // copy?
191
192 // Copy (d)Phi0 (on reference) and apply DOF transformation
193 // Phi0: (deriv, pt_idx, phi (dof), comp)
194 // dPhi0: (pt_idx, phi (dov), deriv, comp)
195 for (std::size_t p = 0; p < Phi0.extent(1); ++p) // point
196 for (std::size_t phi = 0; phi < Phi0.extent(2); ++phi) // phi_i
197 for (std::size_t d = 0; d < Phi0.extent(3); ++d) // Comp. of phi
198 for (std::size_t dx = 0; dx < dPhi0.extent(2); ++dx) // dx
199 dPhi0(p, phi, dx, d) = Phi0(dx + 1, p, phi, d);
200
201 for (std::size_t p = 0; p < dPhi0.extent(0); ++p) // point
202 {
203 // Size: num_phi * num_derivs * num_components
204 std::size_t size = dPhi0.extent(1) * dPhi0.extent(2) * dPhi0.extent(3);
205 std::size_t offset = p * size; // Offset for point p
206
207 // Shape: (num_phi , (value_size * num_derivs))
208 if (apply_dof_transformation0)
209 {
210 apply_dof_transformation0(std::span(dPhi0.data_handle() + offset, size),
211 cell_info, c,
212 dPhi0.extent(2) * dPhi0.extent(3));
213 }
214 }
215
216 // Compute curl
217 // dPhi0: (pt_idx, phi_idx, deriv, comp)
218 // curl: (pt_idx, phi_idx, comp)
219 for (std::size_t p = 0; p < curl.extent(0); ++p) // point
220 {
221 for (std::size_t i = 0; i < curl.extent(1); ++i) // phi_i
222 {
223 curl(p, i, 0) = dPhi0(p, i, 1, 2) - dPhi0(p, i, 2, 1);
224 curl(p, i, 1) = dPhi0(p, i, 2, 0) - dPhi0(p, i, 0, 2);
225 curl(p, i, 2) = dPhi0(p, i, 0, 1) - dPhi0(p, i, 1, 0);
226 }
227 }
228
229 // Apply interpolation matrix to basis derivative values of V0 at
230 // the interpolation points of V1. Pi_1 does not depend on the
231 // basis function index i, so all space_dim0 applications are
232 // combined into a single loop nest (rather than calling
233 // interpolation_apply once per basis function) so that each row
234 // of Pi_1 is read from memory once instead of space_dim0 times.
235 std::ranges::fill(Ab, U(0));
236 for (std::size_t j = 0; j < space_dim1; ++j) // V1 dof
237 for (std::size_t k = 0; k < curl.extent(2); ++k) // component
238 for (std::size_t p = 0; p < curl.extent(0); ++p) // point
239 {
240 T pi_val = Pi_1(j, k * curl.extent(0) + p);
241 for (std::size_t i = 0; i < space_dim0; ++i) // V0 basis function
242 Ab[space_dim0 * j + i] += static_cast<U>(pi_val * curl(p, i, k));
243 }
244
245 if (apply_inverse_dof_transform1)
246 apply_inverse_dof_transform1(Ab, cell_info, c, space_dim0);
247 mat_set(dofmap1->cell_dofs(c), dofmap0->cell_dofs(c), Ab);
248 }
249}
250
277template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
279 std::pair<std::reference_wrapper<const FiniteElement<U>>,
280 std::reference_wrapper<const DofMap>>
281 V0,
282 std::pair<std::reference_wrapper<const FiniteElement<U>>,
283 std::reference_wrapper<const DofMap>>
284 V1,
285 auto&& mat_set)
286{
287 auto& e0 = V0.first.get();
288 const DofMap& dofmap0 = V0.second.get();
289 auto& e1 = V1.first.get();
290 const DofMap& dofmap1 = V1.second.get();
291
292 using cmdspan2_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
293 using cmdspan4_t = md::mdspan<const U, md::dextents<std::size_t, 4>>;
294
295 // Check elements
296 if (e0.map_type() != basix::maps::type::identity)
297 throw std::invalid_argument("Wrong finite element space for V0.");
298 if (e0.block_size() != 1)
299 throw std::invalid_argument("Block size is greater than 1 for V0.");
300 if (e0.reference_value_size() != 1)
301 throw std::invalid_argument("Wrong value size for V0.");
302
303 if (e1.map_type() != basix::maps::type::covariantPiola)
304 throw std::invalid_argument("Wrong finite element space for V1.");
305 if (e1.block_size() != 1)
306 throw std::invalid_argument("Block size is greater than 1 for V1.");
307
308 // Get V1 (H(curl)) space interpolation points
309 const auto [X, Xshape] = e1.interpolation_points();
310
311 // Tabulate first derivatives of Lagrange space at V1 interpolation
312 // points
313 const int ndofs0 = e0.space_dimension();
314 const int tdim = topology.dim();
315 std::vector<U> phi0_b((tdim + 1) * Xshape[0] * ndofs0 * 1);
316 cmdspan4_t phi0(phi0_b.data(), tdim + 1, Xshape[0], ndofs0, 1);
317 e0.tabulate(phi0_b, X, Xshape, 1);
318
319 // Reshape Lagrange basis derivatives as a matrix of shape (tdim *
320 // num_points, num_dofs_per_cell)
321 cmdspan2_t dphi_reshaped(
322 phi0_b.data() + phi0.extent(3) * phi0.extent(2) * phi0.extent(1),
323 tdim * phi0.extent(1), phi0.extent(2));
324
325 // Get inverse DOF transform function
326 auto apply_inverse_dof_transform = e1.template dof_transformation_fn<T>(
328
329 // Generate cell permutations
331 const std::vector<std::uint32_t>& cell_info
332 = topology.get_cell_permutation_info();
333
334 // Create element kernel function
335
336 // Build the element interpolation matrix
337 std::vector<T> Ab(e1.space_dimension() * ndofs0);
338 {
339 md::mdspan<T, md::dextents<std::size_t, 2>> A(Ab.data(),
340 e1.space_dimension(), ndofs0);
341 const auto [Pi, shape] = e1.interpolation_operator();
342 cmdspan2_t _Pi(Pi.data(), shape);
343 math::dot(_Pi, dphi_reshaped, A);
344 }
345
346 // Insert local interpolation matrix for each cell
347 auto cell_map = topology.index_map(tdim);
348 assert(cell_map);
349 std::int32_t num_cells = cell_map->size_local();
350 std::vector<T> Ae(Ab.size());
351 for (std::int32_t c = 0; c < num_cells; ++c)
352 {
353 std::ranges::copy(Ab, Ae.begin());
354 if (apply_inverse_dof_transform)
355 apply_inverse_dof_transform(Ae, cell_info, c, ndofs0);
356 mat_set(dofmap1.cell_dofs(c), dofmap0.cell_dofs(c), Ae);
357 }
358}
359
375template <dolfinx::scalar T, std::floating_point U>
377 const FunctionSpace<U>& V1, auto&& mat_add)
378{
379 // Get mesh
380 auto mesh = V0.mesh();
381 assert(mesh);
382
383 // Mesh dims
384 const int tdim = mesh->topology()->dim();
385 const int gdim = mesh->geometry().dim();
386
387 // Get elements
388 std::shared_ptr<const FiniteElement<U>> e0 = V0.element();
389 assert(e0);
390 std::shared_ptr<const FiniteElement<U>> e1 = V1.element();
391 assert(e1);
392
393 std::span<const std::uint32_t> cell_info;
394 if (e1->needs_dof_transformations() or e0->needs_dof_transformations())
395 {
396 mesh->topology_mutable()->create_entity_permutations();
397 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
398 }
399
400 // Get dofmaps
401 auto dofmap0 = V0.dofmap();
402 assert(dofmap0);
403 auto dofmap1 = V1.dofmap();
404 assert(dofmap1);
405
406 // Get block sizes and dof transformation operators
407 const int bs0 = e0->block_size();
408 const int bs1 = e1->block_size();
409 auto apply_dof_transformation0
410 = e0->template dof_transformation_fn<U>(doftransform::standard, false);
411 auto apply_inverse_dof_transform1 = e1->template dof_transformation_fn<T>(
413
414 // Get sizes of elements
415 const std::size_t space_dim0 = e0->space_dimension();
416 const std::size_t space_dim1 = e1->space_dimension();
417 const std::size_t dim0 = space_dim0 / bs0;
418 const std::size_t value_size_ref0 = e0->reference_value_size();
419 const std::size_t value_size0 = V0.element()->reference_value_size();
420
421 // Get geometry data
422 const CoordinateElement<U>& cmap = mesh->geometry().cmaps().front();
423 auto x_dofmap = mesh->geometry().dofmaps().front();
424 const std::size_t num_dofs_g = cmap.dim();
425 std::span<const U> x_g = mesh->geometry().x();
426
427 using mdspan2_t = md::mdspan<U, md::dextents<std::size_t, 2>>;
428 using cmdspan2_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
429 using cmdspan4_t = md::mdspan<const U, md::dextents<std::size_t, 4>>;
430 using mdspan3_t = md::mdspan<U, md::dextents<std::size_t, 3>>;
431
432 // Evaluate coordinate map basis at reference interpolation points
433 const auto [X, Xshape] = e1->interpolation_points();
434 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
435 std::vector<U> phi_b(
436 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
437 cmdspan4_t phi(phi_b.data(), phi_shape);
438 cmap.tabulate(1, X, Xshape, phi_b);
439
440 // Evaluate V0 basis functions at reference interpolation points for V1
441 std::vector<U> basis_derivatives_reference0_b(Xshape[0] * dim0
442 * value_size_ref0);
443 cmdspan4_t basis_derivatives_reference0(basis_derivatives_reference0_b.data(),
444 1, Xshape[0], dim0, value_size_ref0);
445 e0->tabulate(basis_derivatives_reference0_b, X, Xshape, 0);
446
447 // Clamp values
448 std::ranges::transform(
449 basis_derivatives_reference0_b, basis_derivatives_reference0_b.begin(),
450 [atol = 1e-14](auto x) { return std::abs(x) < atol ? 0.0 : x; });
451
452 // Create working arrays
453 std::vector<U> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
454 mdspan3_t basis_reference0(basis_reference0_b.data(), Xshape[0], dim0,
455 value_size_ref0);
456 std::vector<U> J_b(Xshape[0] * gdim * tdim);
457 mdspan3_t J(J_b.data(), Xshape[0], gdim, tdim);
458 std::vector<U> K_b(Xshape[0] * tdim * gdim);
459 mdspan3_t K(K_b.data(), Xshape[0], tdim, gdim);
460 std::vector<U> detJ(Xshape[0]);
461 std::vector<U> det_scratch(2 * tdim * gdim);
462
463 // Get the interpolation operator (matrix) `Pi` that maps a function
464 // evaluated at the interpolation points to the element degrees of
465 // freedom, i.e. dofs = Pi f_x
466 const auto [_Pi_1, pi_shape] = e1->interpolation_operator();
467 cmdspan2_t Pi_1(_Pi_1.data(), pi_shape);
468
469 bool interpolation_ident = e1->interpolation_ident();
470
471 using u_t = md::mdspan<U, md::dextents<std::size_t, 2>>;
472 using U_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
473 using J_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
474 using K_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
475 auto push_forward_fn0
476 = e0->basix_element().template map_fn<u_t, U_t, J_t, K_t>();
477
478 // Basis values of Lagrange space unrolled for block size
479 // (num_quadrature_points, Lagrange dof, value_size)
480 std::vector<U> basis_values_b(Xshape[0] * bs0 * dim0
481 * V1.element()->value_size());
482 mdspan3_t basis_values(basis_values_b.data(), Xshape[0], bs0 * dim0,
483 V1.element()->value_size());
484 std::vector<U> mapped_values_b(Xshape[0] * bs0 * dim0
485 * V1.element()->value_size());
486 mdspan3_t mapped_values(mapped_values_b.data(), Xshape[0], bs0 * dim0,
487 V1.element()->value_size());
488
489 auto pull_back_fn1
490 = e1->basix_element().template map_fn<u_t, U_t, K_t, J_t>();
491
492 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
493 mdspan2_t coord_dofs(coord_dofs_b.data(), num_dofs_g, gdim);
494 std::vector<U> basis0_b(Xshape[0] * dim0 * value_size0);
495 mdspan3_t basis0(basis0_b.data(), Xshape[0], dim0, value_size0);
496
497 // Buffers
498 std::vector<T> Ab(space_dim0 * space_dim1);
499
500 // Iterate over mesh and interpolate on each cell
501 auto cell_map = mesh->topology()->index_map(tdim);
502 assert(cell_map);
503 std::int32_t num_cells = cell_map->size_local();
504 int row_bs = dofmap1->bs();
505 std::int32_t num_owned_rows
506 = dofmap1->index_map->size_local() * dofmap1->index_map_bs() / row_bs;
507 std::int32_t num_ghosted_rows
508 = dofmap1->index_map->num_ghosts() * dofmap1->index_map_bs() / row_bs;
509 std::vector<std::int8_t> row_added(num_owned_rows + num_ghosted_rows, 0);
510
511 for (std::int32_t c = 0; c < num_cells; ++c)
512 {
513 // Get cell geometry (coordinate dofs)
514 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
515 for (std::size_t i = 0; i < x_dofs.size(); ++i)
516 {
517 for (int j = 0; j < gdim; ++j)
518 coord_dofs(i, j) = x_g[3 * x_dofs[i] + j];
519 }
520
521 // Compute Jacobians and reference points for current cell. For an
522 // affine map the Jacobian (and hence its inverse and determinant)
523 // is constant over the cell, so it is computed once and broadcast
524 // to the remaining interpolation points rather than being
525 // recomputed Xshape[0] times.
526 std::ranges::fill(J_b, 0);
527 if (cmap.is_affine() and Xshape[0] > 0)
528 {
529 auto dphi
530 = md::submdspan(phi, std::pair(1, tdim + 1), 0, md::full_extent, 0);
531 auto _J = md::submdspan(J, 0, md::full_extent, md::full_extent);
532 cmap.compute_jacobian(dphi, coord_dofs, _J);
533 auto _K = md::submdspan(K, 0, md::full_extent, md::full_extent);
534 cmap.compute_jacobian_inverse(_J, _K);
535 detJ[0] = cmap.compute_jacobian_determinant(_J, det_scratch);
536 for (std::size_t p = 1; p < Xshape[0]; ++p)
537 {
538 std::copy_n(J_b.begin(), gdim * tdim, J_b.begin() + p * gdim * tdim);
539 std::copy_n(K_b.begin(), tdim * gdim, K_b.begin() + p * tdim * gdim);
540 detJ[p] = detJ[0];
541 }
542 }
543 else
544 {
545 for (std::size_t p = 0; p < Xshape[0]; ++p)
546 {
547 auto dphi
548 = md::submdspan(phi, std::pair(1, tdim + 1), p, md::full_extent, 0);
549 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
550 cmap.compute_jacobian(dphi, coord_dofs, _J);
551 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
552 cmap.compute_jacobian_inverse(_J, _K);
553 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
554 }
555 }
556
557 // Copy evaluated basis on reference (a fresh copy is needed each
558 // cell as DOF transformations below are applied in place), and
559 // push forward to physical element. The source and destination
560 // have identical memory layout (the leading, unit-length
561 // derivative axis of basis_derivatives_reference0 is a no-op for
562 // indexing purposes), so a flat copy is used instead of an
563 // element-by-element mdspan loop.
564 std::ranges::copy(basis_derivatives_reference0_b,
565 basis_reference0_b.begin());
566 if (apply_dof_transformation0)
567 {
568 for (std::size_t p = 0; p < Xshape[0]; ++p)
569 {
570 apply_dof_transformation0(std::span(basis_reference0.data_handle()
571 + p * dim0 * value_size_ref0,
572 dim0 * value_size_ref0),
573 cell_info, c, value_size_ref0);
574 }
575 }
576
577 for (std::size_t p = 0; p < basis0.extent(0); ++p)
578 {
579 auto _u = md::submdspan(basis0, p, md::full_extent, md::full_extent);
580 auto _U = md::submdspan(basis_reference0, p, md::full_extent,
581 md::full_extent);
582 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
583 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
584 push_forward_fn0(_u, _U, _J, detJ[p], _K);
585 }
586
587 // Unroll basis function for input space for block size
588 for (std::size_t p = 0; p < Xshape[0]; ++p)
589 for (std::size_t i = 0; i < dim0; ++i)
590 for (std::size_t j = 0; j < value_size0; ++j)
591 for (int k = 0; k < bs0; ++k)
592 basis_values(p, i * bs0 + k, j * bs0 + k) = basis0(p, i, j);
593
594 // Pull back the physical values to the reference of output space
595 for (std::size_t p = 0; p < basis_values.extent(0); ++p)
596 {
597 auto _u
598 = md::submdspan(basis_values, p, md::full_extent, md::full_extent);
599 auto _U
600 = md::submdspan(mapped_values, p, md::full_extent, md::full_extent);
601 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
602 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
603 pull_back_fn1(_U, _u, _K, 1.0 / detJ[p], _J);
604 }
605
606 // Apply interpolation matrix to basis values of V0 at the
607 // interpolation points of V1
608 if (interpolation_ident)
609 {
610 md::mdspan<T, md::dextents<std::size_t, 3>> A(
611 Ab.data(), Xshape[0], V1.element()->value_size(), space_dim0);
612 for (std::size_t i = 0; i < mapped_values.extent(0); ++i)
613 for (std::size_t j = 0; j < mapped_values.extent(1); ++j)
614 for (std::size_t k = 0; k < mapped_values.extent(2); ++k)
615 A(i, k, j) = mapped_values(i, j, k);
616 }
617 else
618 {
619 // Apply interpolation matrix to basis values of V0 at the
620 // interpolation points of V1. Pi_1 does not depend on the basis
621 // function index i, so all space_dim0 applications are combined
622 // into a single loop nest (rather than calling
623 // interpolation_apply once per basis function) so that each row
624 // of Pi_1 is read from memory once instead of space_dim0 times.
625 // This mirrors the two cases handled by interpolation_apply
626 // (interpolate.h): bs1 == 1 (columns of Pi_1 fold the point and
627 // component indices together) and bs1 != 1 (each block/component
628 // is handled by a separate outer loop, columns of Pi_1 index
629 // points only).
630 std::ranges::fill(Ab, T(0));
631 std::size_t num_pts = mapped_values.extent(0);
632 if (bs1 == 1)
633 {
634 for (std::size_t idof = 0; idof < Pi_1.extent(0); ++idof)
635 for (std::size_t k = 0; k < mapped_values.extent(2); ++k)
636 for (std::size_t p = 0; p < num_pts; ++p)
637 {
638 U pi_val = Pi_1(idof, k * num_pts + p);
639 for (std::size_t i = 0; i < space_dim0; ++i) // V0 basis fn
640 Ab[space_dim0 * idof + i]
641 += static_cast<T>(pi_val * mapped_values(p, i, k));
642 }
643 }
644 else
645 {
646 for (std::size_t idof = 0; idof < Pi_1.extent(0); ++idof)
647 for (int k = 0; k < bs1; ++k)
648 for (std::size_t p = 0; p < num_pts; ++p)
649 {
650 U pi_val = Pi_1(idof, p);
651 for (std::size_t i = 0; i < space_dim0; ++i) // V0 basis fn
652 Ab[space_dim0 * (bs1 * idof + k) + i]
653 += static_cast<T>(pi_val * mapped_values(p, i, k));
654 }
655 }
656 }
657
658 if (apply_inverse_dof_transform1)
659 apply_inverse_dof_transform1(Ab, cell_info, c, space_dim0);
660
661 // Zero out all rows that have already been added on this process
662 // and only add owned rows
663 {
664 md::mdspan<T, md::dextents<std::size_t, 2>> A(Ab.data(), space_dim1,
665 space_dim0);
666 auto row_dofs = dofmap1->cell_dofs(c);
667 for (std::size_t i = 0; i < row_dofs.size(); ++i)
668 {
669 std::int32_t r = row_dofs[i];
670 if (r >= num_owned_rows || row_added[r])
671 {
672 for (std::size_t j = 0; j < space_dim0; ++j)
673 for (int k = 0; k < row_bs; ++k)
674 A(i * row_bs + k, j) = 0.0;
675 }
676 row_added[r] = 1;
677 }
678 }
679 mat_add(dofmap1->cell_dofs(c), dofmap0->cell_dofs(c), Ab);
680 }
681}
682
683} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:38
static void compute_jacobian(const U &dphi, const V &cell_geometry, W &&J)
Definition CoordinateElement.h:133
void tabulate(int nd, std::span< const T > X, std::array< std::size_t, 2 > shape, std::span< T > basis) const
Evaluate basis values and derivatives at set of points.
Definition CoordinateElement.cpp:59
static void compute_jacobian_inverse(const U &J, V &&K)
Compute the inverse of the Jacobian.
Definition CoordinateElement.h:142
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 tabulate.
Definition CoordinateElement.cpp:52
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:222
bool is_affine() const noexcept
Check if geometry map is affine.
Definition CoordinateElement.h:289
static double compute_jacobian_determinant(const U &J, std::span< typename U::value_type > w)
Compute the determinant of the Jacobian.
Definition CoordinateElement.h:159
Degree-of-freedom map.
Definition DofMap.h:73
std::span< const std::int32_t > cell_dofs(std::int32_t c) const
Local-to-global mapping of dofs on a cell.
Definition DofMap.h:127
Model of a finite element.
Definition FiniteElement.h:62
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:35
std::shared_ptr< const DofMap > dofmap() const
The dofmap.
Definition FunctionSpace.h:396
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
The mesh.
Definition FunctionSpace.h:371
std::shared_ptr< const FiniteElement< geometry_type > > element() const
The finite element.
Definition FunctionSpace.h:377
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:49
std::shared_ptr< const common::IndexMap > index_map(int dim) const
Get the IndexMap that describes the parallel distribution of the mesh entities.
Definition Topology.cpp:928
const std::vector< std::uint32_t > & get_cell_permutation_info() const
Get the cell permutation information.
Definition Topology.cpp:970
void create_entity_permutations(int num_threads=1)
Compute entity permutations and reflections.
Definition Topology.cpp:1108
int dim() const noexcept
Topological dimension of the mesh.
Definition Topology.cpp:888
Matrix accumulate/set concept for functions that can be used in assemblers to accumulate or set value...
Definition utils.h:28
Finite element method functionality.
Definition assemble_expression_impl.h:24
void discrete_curl(const FunctionSpace< T > &V0, const FunctionSpace< T > &V1, la::MatSet< U > auto &&mat_set)
Assemble a discrete curl operator.
Definition discreteoperators.h:87
void discrete_gradient(mesh::Topology &topology, std::pair< std::reference_wrapper< const FiniteElement< U > >, std::reference_wrapper< const DofMap > > V0, std::pair< std::reference_wrapper< const FiniteElement< U > >, std::reference_wrapper< const DofMap > > V1, auto &&mat_set)
Assemble a discrete gradient operator.
Definition discreteoperators.h:278
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:32
@ standard
Standard.
Definition FiniteElement.h:29
void interpolation_matrix(const FunctionSpace< U > &V0, const FunctionSpace< U > &V1, auto &&mat_add)
Assemble an interpolation operator matrix.
Definition discreteoperators.h:376
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32