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