DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
pack.h
Go to the documentation of this file.
1// Copyright (C) 2013-2026 Garth N. Wells and 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 "Constant.h"
10#include "DofMap.h"
11#include "FiniteElement.h"
12#include "Form.h"
13#include "Function.h"
14#include "FunctionSpace.h"
15#include "traits.h"
16#include <array>
17#include <basix/mdspan.hpp>
18#include <concepts>
19#include <dolfinx/mesh/Topology.h>
20#include <format>
21#include <ranges>
22#include <span>
23#include <stdexcept>
24#include <type_traits>
25#include <vector>
26
29
30namespace dolfinx::fem
31{
32template <dolfinx::scalar T, std::floating_point U>
33class Expression;
34
35namespace impl
36{
44template <dolfinx::scalar T, std::floating_point U>
45std::span<const std::uint32_t>
46get_cell_orientation_info(const Function<T, U>& coefficient)
47{
48 std::span<const std::uint32_t> cell_info;
49 auto element = coefficient.function_space()->element();
50 assert(element);
51 if (element->needs_dof_transformations())
52 {
53 auto mesh = coefficient.function_space()->mesh();
54 mesh->topology_mutable()->create_entity_permutations();
55 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
56 }
57
58 return cell_info;
59}
60
77template <int _bs, dolfinx::scalar T>
78void pack_impl(std::span<T> coeffs, std::int32_t cell, int bs,
79 std::span<const T> v, std::span<const std::uint32_t> cell_info,
80 const DofMap& dofmap, auto transform)
81{
82 std::span<const std::int32_t> dofs = dofmap.cell_dofs(cell);
83 for (std::size_t i = 0; i < dofs.size(); ++i)
84 {
85 if constexpr (_bs < 0)
86 {
87 const int pos_c = bs * i;
88 const int pos_v = bs * dofs[i];
89 for (int k = 0; k < bs; ++k)
90 coeffs[pos_c + k] = v[pos_v + k];
91 }
92 else
93 {
94 assert(_bs == bs);
95 const int pos_c = _bs * i;
96 const int pos_v = _bs * dofs[i];
97 for (int k = 0; k < _bs; ++k)
98 coeffs[pos_c + k] = v[pos_v + k];
99 }
100 }
101
102 transform(coeffs, cell_info, cell, 1);
103}
104
121template <dolfinx::scalar T, std::floating_point U>
122void pack_coefficient_entity(std::span<T> c, int cstride,
123 const Function<T, U>& u,
124 std::span<const std::uint32_t> cell_info,
125 auto cells, std::int32_t offset)
126{
127 static_assert(cells.rank() == 1);
128
129 // Read data from coefficient Function u
130 std::span<const T> v = u.x()->array();
131 const DofMap& dofmap = *u.function_space()->dofmap();
132 auto element = u.function_space()->element();
133 assert(element);
134 int space_dim = element->space_dimension();
135
136 // Transformation from conforming degrees-of-freedom to reference
137 // degrees-of-freedom
138 auto transformation
139 = element->template dof_transformation_fn<T>(doftransform::transpose);
140 const int bs = dofmap.bs();
141
142 // Passing the block size as a compile-time constant lets `pack_impl`
143 // unroll its inner (per-DOF) loop for the common block sizes 1, 2,
144 // and 3, rather than looping `bs` times at runtime for every cell.
145 // `bs_c` is a `std::integral_constant<int, N>`, converted to `N` via
146 // `bs_c()` where `pack_impl`'s `_bs` template parameter is needed.
147 auto pack_for_bs = [&cells, &c, &cstride, &offset, &space_dim, &bs, &v,
148 &cell_info, &dofmap, &transformation](auto bs_c)
149 {
150 for (std::size_t e = 0; e < cells.extent(0); ++e)
151 {
152 if (std::int32_t cell = cells(e); cell >= 0)
153 {
154 auto cell_coeff = c.subspan(e * cstride + offset, space_dim);
155 pack_impl<bs_c()>(cell_coeff, cell, bs, v, cell_info, dofmap,
156 transformation);
157 }
158 }
159 };
160
161 switch (bs)
162 {
163 case 1:
164 pack_for_bs(std::integral_constant<int, 1>());
165 break;
166 case 2:
167 pack_for_bs(std::integral_constant<int, 2>());
168 break;
169 case 3:
170 pack_for_bs(std::integral_constant<int, 3>());
171 break;
172 default:
173 pack_for_bs(std::integral_constant<int, -1>());
174 break;
175 }
176}
177} // namespace impl
178
186template <dolfinx::scalar T, std::floating_point U>
187std::pair<std::vector<T>, int>
189 int idx)
190{
191 std::size_t num_entities = 0;
192 int cstride = 0;
193 if (const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients
194 = form.coefficients();
195 !coefficients.empty())
196 {
197 const std::vector<int> offsets = form.coefficient_offsets();
198 cstride = offsets.back();
199
200 // `domain()` returns entities flattened as (cell,) for cell
201 // integrals, (cell, local_entity_index) pairs for exterior_facet/
202 // vertex/ridge integrals, and (cell, local_facet, cell,
203 // local_facet) quadruples for interior_facet integrals (one '+'
204 // and one '-' side). Dividing by 2 therefore gives the number of
205 // entities for exterior_facet/vertex/ridge integrals, but *twice*
206 // the number of facets for interior_facet integrals -- which is
207 // exactly the entity count required, since interior_facet
208 // coefficient data is packed at a doubled `cstride` (one side
209 // each), see ::pack_coefficients.
210 num_entities = form.domain(integral_type, idx, 0).size();
211 if (integral_type != IntegralType::cell)
212 num_entities /= 2;
213 }
214
215 return {std::vector<T>(num_entities * cstride), cstride};
216}
217
223template <dolfinx::scalar T, std::floating_point U>
224std::map<std::pair<IntegralType, int>, std::pair<std::vector<T>, int>>
226{
227 std::map<std::pair<IntegralType, int>, std::pair<std::vector<T>, int>> coeffs;
228 for (fem::IntegralType type : form.integral_types())
229 {
230 // `num_integrals` scans all of `form`'s integrals, so it is
231 // evaluated once per `type` here rather than as the loop
232 // condition (which would re-scan on every iteration).
233 const int n = form.num_integrals(type, 0);
234 for (int idx = 0; idx < n; ++idx)
235 {
236 coeffs.emplace_hint(coeffs.end(), std::pair{type, idx},
237 allocate_coefficient_storage(form, type, idx));
238 }
239 }
240
241 return coeffs;
242}
243
265template <dolfinx::scalar T, std::floating_point U>
267 std::map<std::pair<IntegralType, int>,
268 std::pair<std::vector<T>, int>>& coeffs)
269{
270 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients
271 = form.coefficients();
272 const std::vector<int> offsets = form.coefficient_offsets();
273
274 for (auto& [integral_key, coeff_data] : coeffs)
275 {
276 auto [integral_type, idx] = integral_key;
277 std::vector<T>& c = coeff_data.first;
278 int cstride = coeff_data.second;
279 if (!coefficients.empty())
280 {
281 switch (integral_type)
282 {
284 {
285 // `form.mesh()` is fixed for the whole call, so its dimension
286 // is fetched once rather than once per active coefficient.
287 const int form_tdim = form.mesh()->topology()->dim();
288
289 // Iterate over coefficients that are active in cell integrals
290 for (int coeff : form.active_coeffs(IntegralType::cell, idx))
291 {
292 // Get coefficient mesh
293 auto mesh = coefficients[coeff]->function_space()->mesh();
294 assert(mesh);
295
296 // A cell-integral coefficient must be defined over cells (or
297 // a mesh view of them), not lower-codimension entities such
298 // as facets -- that combination doesn't make sense and is a
299 // logic error, so fail loudly rather than pack it anyway.
300 if (int codim = form_tdim - mesh->topology()->dim(); codim > 0)
301 {
302 throw std::runtime_error("Should not be packing coefficients with "
303 "codim>0 in a cell integral");
304 }
305
306 std::span<const std::int32_t> cells_b
307 = form.domain_coeff(IntegralType::cell, idx, coeff);
308 md::mdspan cells(cells_b.data(), cells_b.size());
309 std::span<const std::uint32_t> cell_info
310 = impl::get_cell_orientation_info(*coefficients[coeff]);
311 impl::pack_coefficient_entity(std::span(c), cstride,
312 *coefficients[coeff], cell_info, cells,
313 offsets[coeff]);
314 }
315 break;
316 }
318 {
319 // Iterate over coefficients that are active in interior
320 // facet integrals
321 for (int coeff : form.active_coeffs(IntegralType::interior_facet, idx))
322 {
323 auto mesh = coefficients[coeff]->function_space()->mesh();
324 std::span<const std::int32_t> facets_b
325 = form.domain_coeff(IntegralType::interior_facet, idx, coeff);
326 md::mdspan<const std::int32_t,
327 md::extents<std::size_t, md::dynamic_extent, 4>>
328 facets(facets_b.data(), facets_b.size() / 4, 4);
329
330 std::span<const std::uint32_t> cell_info
331 = impl::get_cell_orientation_info(*coefficients[coeff]);
332
333 // Data for the '+' and '-' sides of coefficient `coeff` are
334 // interleaved per-coefficient (not stored as two contiguous
335 // blocks), i.e. layout is [coeff0 '+', coeff0 '-', coeff1
336 // '+', coeff1 '-', ...]. `2 * offsets[coeff]` is therefore
337 // the start of coefficient `coeff`'s '+' data, immediately
338 // followed by its '-' data at `offsets[coeff] +
339 // offsets[coeff + 1]`.
340
341 // Pack coefficient ['+']
342 auto cells0 = md::submdspan(facets, md::full_extent, 0);
343 impl::pack_coefficient_entity(std::span(c), 2 * cstride,
344 *coefficients[coeff], cell_info, cells0,
345 2 * offsets[coeff]);
346
347 // Pack coefficient ['-']
348 auto cells1 = md::submdspan(facets, md::full_extent, 2);
349 impl::pack_coefficient_entity(std::span(c), 2 * cstride,
350 *coefficients[coeff], cell_info, cells1,
351 offsets[coeff] + offsets[coeff + 1]);
352 }
353 break;
354 }
358 {
359 // Iterate over coefficients that are active in exterior_facet,
360 // vertex, and ridge integrals (all use the same (cell,
361 // local_entity_index) entity layout)
362 for (int coeff : form.active_coeffs(integral_type, idx))
363 {
364 // Get coefficient mesh
365 auto mesh = coefficients[coeff]->function_space()->mesh();
366 assert(mesh);
367
368 std::span<const std::int32_t> entities_b
369 = form.domain_coeff(integral_type, idx, coeff);
370 md::mdspan<const std::int32_t,
371 md::extents<std::size_t, md::dynamic_extent, 2>>
372 entities(entities_b.data(), entities_b.size() / 2, 2);
373 std::span<const std::uint32_t> cell_info
374 = impl::get_cell_orientation_info(*coefficients[coeff]);
375 impl::pack_coefficient_entity(
376 std::span(c), cstride, *coefficients[coeff], cell_info,
377 md::submdspan(entities, md::full_extent, 0), offsets[coeff]);
378 }
379 break;
380 }
381 default:
382 throw std::runtime_error(
383 "Could not pack coefficient. Integral type not supported.");
384 }
385 }
386 }
387}
388
402template <dolfinx::scalar T, std::floating_point U>
404 const fem::Function<T, U>& coeff, const mesh::Mesh<U>& mesh,
405 fem::MDSpan2 auto entities,
406 std::optional<std::reference_wrapper<const dolfinx::mesh::EntityMap>>
407 entity_map)
408{
409 auto mesh_c = coeff.function_space()->mesh();
410 assert(mesh_c);
411
412 auto span_to_vector = [](auto entities)
413 {
414 assert(entities.rank() == 1);
415
416 std::vector<std::int32_t> vec;
417 vec.reserve(entities.extent(0));
418 for (std::size_t i = 0; i < entities.extent(0); ++i)
419 vec.push_back(entities[i]);
420 return vec;
421 };
422
423 if (mesh_c->topology() == mesh.topology())
424 {
425 // If same mesh no mapping is needed
426 if constexpr (entities.rank() == 1)
427 return span_to_vector(entities);
428
429 else
430 // If (cell, local_index) pairs are given, extract the cells
431 return span_to_vector(md::submdspan(entities, md::full_extent, 0));
432 }
433 else
434 {
435 assert(entity_map.has_value());
436 const mesh::Topology& topology = *mesh.topology();
437 int tdim = topology.dim();
438 int codim = tdim - mesh_c->topology()->dim();
439 const dolfinx::mesh::EntityMap& emap = entity_map.value().get();
440 bool inverse = emap.sub_topology() == mesh_c->topology();
441 // If cells are supplied on the parent mesh, we can directly map them to
442 // cells on the coefficient mesh.
443 if constexpr (entities.rank() == 1)
444 {
445 assert(codim == 0);
446
447 return emap.sub_topology_to_topology(span_to_vector(entities), inverse);
448 }
449 else if constexpr (entities.rank() == 2)
450 {
451 if (codim == 0)
452 {
453 // If codim is zero we extract the cells and map them
454 auto cells = md::submdspan(entities, md::full_extent, 0);
455 return emap.sub_topology_to_topology(span_to_vector(cells), inverse);
456 }
457 else
458 {
459 // Any other codim needs to map (cell, local index) to facets and then
460 // to cells of the submesh
461 if (!inverse)
462 {
463 throw std::runtime_error(
464 "Unsupported mapping. Can only map from submesh to parent mesh.");
465 }
466 assert(codim > 0);
467 auto c_to_e = topology.connectivity(tdim, tdim - codim);
468 if (!c_to_e)
469 {
470 throw std::runtime_error(std::format(
471 "Topology connectivity from codim {} to {} not found.", tdim,
472 tdim - codim));
473 }
474 // Map parent (cell, local_index) to parent facet
475 std::vector<std::int32_t> contiguous_cells;
476 contiguous_cells.reserve(entities.extent(0));
477 for (std::size_t e = 0; e < entities.extent(0); ++e)
478 {
479 contiguous_cells.push_back(
480 c_to_e->links(entities(e, 0))[entities(e, 1)]);
481 }
482 // Map parent facet to submesh cell
483 return emap.sub_topology_to_topology(contiguous_cells, inverse);
484 }
485 }
486 }
487}
488
504template <dolfinx::scalar T, std::floating_point U>
506 const std::vector<std::reference_wrapper<const Function<T, U>>>& coeffs,
507 const mesh::Mesh<U>& mesh, fem::MDSpan2 auto entities,
508 const std::vector<std::reference_wrapper<const dolfinx::mesh::EntityMap>>&
509 entity_maps,
510 std::span<const int> offsets, std::span<T> c)
511{
512
513 assert(!offsets.empty());
514 const int cstride = offsets.back();
515
516 if (c.size() < entities.extent(0) * offsets.back())
517 throw std::runtime_error("Coefficient packing span is too small.");
518
519 // Helper function to get correct entity map. Note: `mesh` is
520 // captured by reference -- capturing it by value would copy the
521 // whole Mesh (including its Geometry's coordinate array) on every
522 // call.
523 auto get_entity_map
524 = [&mesh, &entity_maps](auto& mesh0) -> const mesh::EntityMap&
525 {
526 auto it = std::ranges::find_if(
527 entity_maps,
528 [&mesh, mesh0](const mesh::EntityMap& em)
529 {
530 return (em.topology() == mesh0->topology()
531 and em.sub_topology() == mesh.topology())
532 or (em.sub_topology() == mesh0->topology()
533 and em.topology() == mesh.topology());
534 });
535
536 if (it == entity_maps.end())
537 {
538 throw std::runtime_error(
539 "Incompatible mesh. argument entity_maps must be provided.");
540 }
541 return *it;
542 };
543
544 // Iterate over coefficients
545 for (std::size_t coeff = 0; coeff < coeffs.size(); ++coeff)
546 {
547 // Get mesh of coefficient and check if entity map is required
548 auto mesh_c = coeffs[coeff].get().function_space()->mesh();
549 std::vector<std::int32_t> coefficient_cells;
550 if (mesh_c->topology() == mesh.topology())
551 {
552 coefficient_cells = extract_coefficient_cells_from_entities(
553 coeffs[coeff].get(), mesh, entities, std::nullopt);
554 }
555 else
556 {
557 // Find correct entity map and determine direction of the map
558 const mesh::EntityMap& emap = get_entity_map(mesh_c);
559 coefficient_cells = extract_coefficient_cells_from_entities(
560 coeffs[coeff].get(), mesh, entities,
561 std::reference_wrapper<const mesh::EntityMap>(emap));
562 }
563
564 std::span<const std::uint32_t> cell_info
565 = impl::get_cell_orientation_info(coeffs[coeff].get());
566 md::mdspan cells(coefficient_cells.data(), coefficient_cells.size());
567 impl::pack_coefficient_entity(std::span(c), cstride, coeffs[coeff].get(),
568 cell_info, cells, offsets[coeff]);
569 }
570}
571
578template <typename T>
579std::vector<T> pack_constants(
580 const std::vector<std::reference_wrapper<const fem::Constant<T>>>& c)
581{
582 // Calculate size of array needed to store packed constants
583 std::int32_t size = std::accumulate(
584 c.cbegin(), c.cend(), 0, [](std::int32_t sum, auto& constant)
585 { return sum + constant.get().value.size(); });
586
587 // Pack constants
588 std::vector<T> constant_values(size);
589 std::int32_t offset = 0;
590 for (auto& constant : c)
591 {
592 std::ranges::copy(constant.get().value,
593 std::next(constant_values.begin(), offset));
594 offset += constant.get().value.size();
595 }
596
597 return constant_values;
598}
599
604template <typename U>
605 requires std::convertible_to<
607 typename std::decay_t<U>::geometry_type>>
608 or std::convertible_to<
610 typename std::decay_t<U>::geometry_type>>
611std::vector<typename U::scalar_type> pack_constants(const U& u)
612{
613 using T = typename std::decay_t<U>::scalar_type;
614 std::vector<std::reference_wrapper<const Constant<T>>> c;
615 c.reserve(u.constants().size());
616 std::ranges::transform(u.constants(), std::back_inserter(c),
617 [](auto& c) -> const Constant<T>& { return *c; });
618 return fem::pack_constants(c);
619}
620
621} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Constant (in space) value which can be attached to a Form.
Definition Constant.h:22
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
int bs() const noexcept
Return the block size for the dofmap.
Definition DofMap.cpp:164
An Expression represents a mathematical expression evaluated at a pre-defined points on a reference c...
Definition Expression.h:43
A representation of finite element variational forms.
Definition Form.h:118
int num_integrals(IntegralType type, int kernel_idx) const
Get number of integrals (kernels) for a given integral type and kernel index.
Definition Form.h:450
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Access coefficients.
Definition Form.h:577
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:593
std::span< const std::int32_t > domain_coeff(IntegralType type, int idx, int c) const
Coefficient function mesh integration entity indices.
Definition Form.h:563
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
Common mesh for the form (the 'integration domain').
Definition Form.h:367
std::vector< int > active_coeffs(IntegralType type, int idx) const
Indices of coefficients that are active for a given integral (kernel).
Definition Form.h:421
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:400
std::span< const std::int32_t > domain(IntegralType type, int idx, int kernel_idx) const
Mesh entity indices to integrate over for a given integral (kernel).
Definition Form.h:494
Definition Function.h:47
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:147
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:153
A bidirectional map relating entities in one topology to another.
Definition EntityMap.h:22
std::vector< std::int32_t > sub_topology_to_topology(CellRange auto &&entities, bool inverse) const
Map entities between the sub-topology and the parent topology.
Definition EntityMap.h:104
std::shared_ptr< const Topology > sub_topology() const
Get the sub-topology.
Definition EntityMap.cpp:23
std::shared_ptr< const Topology > topology() const
Get the (parent) topology.
Definition EntityMap.cpp:18
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:49
std::shared_ptr< const graph::AdjacencyList< std::int32_t > > connectivity(std::array< int, 2 > d0, std::array< int, 2 > d1) const
Get the connectivity from entities of topological dimension d0 to dimension d1.
Definition Topology.cpp:950
int dim() const noexcept
Topological dimension of the mesh.
Definition Topology.cpp:888
Concept for mdspan of rank 1 or 2.
Definition traits.h:35
Finite element method functionality.
Definition assemble_expression_impl.h:23
@ transpose
Transpose.
Definition FiniteElement.h:28
@ inverse
Inverse.
Definition FiniteElement.h:29
void pack_coefficients(const Form< T, U > &form, std::map< std::pair< IntegralType, int >, std::pair< std::vector< T >, int > > &coeffs)
Pack coefficients of a Form.
Definition pack.h:266
std::pair< std::vector< T >, int > allocate_coefficient_storage(const Form< T, U > &form, IntegralType integral_type, int idx)
Allocate storage for coefficients of a pair (integral_type, idx) from a Form.
Definition pack.h:188
std::vector< std::int32_t > extract_coefficient_cells_from_entities(const fem::Function< T, U > &coeff, const mesh::Mesh< U > &mesh, fem::MDSpan2 auto entities, std::optional< std::reference_wrapper< const dolfinx::mesh::EntityMap > > entity_map)
Given a Function and a related mesh and its integration entities, extract the cell indices of the coe...
Definition pack.h:403
IntegralType
Type of integral.
Definition Form.h:41
@ vertex
Vertex.
Definition Form.h:45
@ interior_facet
Interior facet.
Definition Form.h:44
@ ridge
Ridge.
Definition Form.h:46
@ cell
Cell.
Definition Form.h:42
@ exterior_facet
Exterior facet.
Definition Form.h:43
std::vector< T > pack_constants(const std::vector< std::reference_wrapper< const fem::Constant< T > > > &c)
Pack constants of an Expression or Form into a single array ready for assembly.
Definition pack.h:579
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
void pack_impl(std::span< T > coeffs, std::int32_t cell, int bs, std::span< const T > v, std::span< const std::uint32_t > cell_info, const DofMap &dofmap, auto transform)
Gather a single coefficient's degrees-of-freedom for a single cell and apply its DOF transformation.
Definition pack.h:78
void pack_coefficient_entity(std::span< T > c, int cstride, const Function< T, U > &u, std::span< const std::uint32_t > cell_info, auto cells, std::int32_t offset)
Pack a single coefficient for a set of active entities.
Definition pack.h:122