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 <algorithm>
17#include <array>
18#include <basix/mdspan.hpp>
19#include <cassert>
20#include <concepts>
21#include <cstdint>
22#include <dolfinx/mesh/EntityMap.h>
23#include <dolfinx/mesh/Mesh.h>
24#include <dolfinx/mesh/Topology.h>
25#include <functional>
26#include <iterator>
27#include <memory>
28#include <numeric>
29#include <optional>
30#include <ranges>
31#include <span>
32#include <stdexcept>
33#include <type_traits>
34#include <vector>
35
38
39namespace dolfinx::fem
40{
41template <dolfinx::scalar T, std::floating_point U>
42class Expression;
43
44namespace impl
45{
53template <dolfinx::scalar T, std::floating_point U>
54std::span<const std::uint32_t>
55get_cell_orientation_info(const Function<T, U>& coefficient)
56{
57 std::span<const std::uint32_t> cell_info;
58 auto element = coefficient.function_space()->element();
59 assert(element);
60 if (element->needs_dof_transformations())
61 {
62 auto mesh = coefficient.function_space()->mesh();
63 mesh->topology_mutable()->create_cell_permutations();
64 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
65 }
66
67 return cell_info;
68}
69
89template <dolfinx::scalar T>
90void pack_impl(std::span<T> coeffs, std::int32_t cell, auto bs,
91 std::span<const T> v, std::span<const std::uint32_t> cell_info,
92 const DofMap& dofmap, auto transform, bool transform_set)
93{
94 std::span<const std::int32_t> dofs = dofmap.cell_dofs(cell);
95 for (std::size_t i = 0; i < dofs.size(); ++i)
96 std::copy_n(v.data() + bs * dofs[i], bs, coeffs.data() + bs * i);
97
98 if (transform_set)
99 transform(coeffs, cell_info, cell, 1);
100}
101
118template <dolfinx::scalar T, std::floating_point U>
119void pack_coefficient_entity(std::span<T> c, int cstride,
120 const Function<T, U>& u,
121 std::span<const std::uint32_t> cell_info,
122 auto cells, std::int32_t offset)
123{
124 static_assert(cells.rank() == 1);
125
126 // Read data from coefficient Function u
127 std::span<const T> v = u.x()->array();
128 const DofMap& dofmap = *u.function_space()->dofmap();
129 auto element = u.function_space()->element();
130 assert(element);
131 int space_dim = element->space_dimension();
132
133 // Transformation from conforming degrees-of-freedom to reference
134 // degrees-of-freedom
135 auto transformation
136 = element->template dof_transformation_fn<T>(doftransform::transpose);
137 const int bs = dofmap.bs();
138
139 // `transformation` does not change across cells in this call, so
140 // whether it is a set (non-null) transform is loop-invariant --
141 // checked once here rather than on every cell.
142 const bool transform_set = is_transform_set(transformation);
143
144 // Passing the block size as a compile-time constant
145 // (std::integral_constant<int, N>) lets `pack_impl` unroll its inner
146 // (per-DOF) loop for the common block sizes 1, 2, and 3, rather than
147 // looping `bs` times at runtime for every cell.
148 auto pack_for_bs = [&cells, &c, &cstride, &offset, &space_dim, &v, &cell_info,
149 &dofmap, &transformation, transform_set](auto bs)
150 {
151 for (std::size_t e = 0; e < cells.extent(0); ++e)
152 {
153 if (std::int32_t cell = cells(e); cell >= 0)
154 {
155 auto cell_coeff = c.subspan(e * cstride + offset, space_dim);
156 pack_impl(cell_coeff, cell, bs, v, cell_info, dofmap, transformation,
157 transform_set);
158 }
159 }
160 };
161
162 switch (bs)
163 {
164 case 1:
165 pack_for_bs(std::integral_constant<int, 1>());
166 break;
167 case 2:
168 pack_for_bs(std::integral_constant<int, 2>());
169 break;
170 case 3:
171 pack_for_bs(std::integral_constant<int, 3>());
172 break;
173 default:
174 pack_for_bs(bs);
175 break;
176 }
177}
178} // namespace impl
179
187template <dolfinx::scalar T, std::floating_point U>
188std::pair<std::vector<T>, int>
190 int idx)
191{
192 std::size_t num_entities = 0;
193 int cstride = 0;
194 if (const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients
195 = form.coefficients();
196 !coefficients.empty())
197 {
198 const std::vector<int> offsets = form.coefficient_offsets();
199 cstride = offsets.back();
200
201 // `domain()` returns entities flattened as (cell,) for cell
202 // integrals, (cell, local_entity_index) pairs for exterior_facet/
203 // vertex/ridge integrals, and (cell, local_facet, cell,
204 // local_facet) quadruples for interior_facet integrals (one '+'
205 // and one '-' side). Dividing by 2 therefore gives the number of
206 // entities for exterior_facet/vertex/ridge integrals, but *twice*
207 // the number of facets for interior_facet integrals -- which is
208 // exactly the entity count required, since interior_facet
209 // coefficient data is packed at a doubled `cstride` (one side
210 // each), see ::pack_coefficients.
211 num_entities = form.domain(integral_type, idx, 0).size();
212 if (integral_type != IntegralType::cell)
213 num_entities /= 2;
214 }
215
216 return {std::vector<T>(num_entities * cstride), cstride};
217}
218
224template <dolfinx::scalar T, std::floating_point U>
225std::map<std::pair<IntegralType, int>, std::pair<std::vector<T>, int>>
227{
228 std::map<std::pair<IntegralType, int>, std::pair<std::vector<T>, int>> coeffs;
229 for (fem::IntegralType type : form.integral_types())
230 {
231 // `num_integrals` scans all of `form`'s integrals, so it is
232 // evaluated once per `type` here rather than as the loop
233 // condition (which would re-scan on every iteration).
234 const int n = form.num_integrals(type, 0);
235 for (int idx = 0; idx < n; ++idx)
236 {
237 coeffs.emplace_hint(coeffs.end(), std::pair{type, idx},
238 allocate_coefficient_storage(form, type, idx));
239 }
240 }
241
242 return coeffs;
243}
244
266template <dolfinx::scalar T, std::floating_point U>
268 std::map<std::pair<IntegralType, int>,
269 std::pair<std::vector<T>, int>>& coeffs)
270{
271 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients
272 = form.coefficients();
273 const std::vector<int> offsets = form.coefficient_offsets();
274
275 for (auto& [integral_key, coeff_data] : coeffs)
276 {
277 auto [integral_type, idx] = integral_key;
278 std::vector<T>& c = coeff_data.first;
279 int cstride = coeff_data.second;
280 if (!coefficients.empty())
281 {
282 switch (integral_type)
283 {
285 {
286 // `form.mesh()` is fixed for the whole call, so its dimension
287 // is fetched once rather than once per active coefficient.
288 const int form_tdim = form.mesh()->topology()->dim();
289
290 // Iterate over coefficients that are active in cell integrals
291 for (int coeff : form.active_coeffs(IntegralType::cell, idx))
292 {
293 // Get coefficient mesh
294 auto mesh = coefficients[coeff]->function_space()->mesh();
295 assert(mesh);
296
297 // A cell-integral coefficient must be defined over cells (or
298 // a mesh view of them), not lower-codimension entities such
299 // as facets -- that combination doesn't make sense and is a
300 // logic error, so fail loudly rather than pack it anyway.
301 if (int codim = form_tdim - mesh->topology()->dim(); codim > 0)
302 {
303 throw std::invalid_argument(
304 "Should not be packing coefficients with "
305 "codim>0 in a cell integral");
306 }
307
308 std::span<const std::int32_t> cells_b
309 = form.domain_coeff(IntegralType::cell, idx, coeff);
310 md::mdspan cells(cells_b.data(), cells_b.size());
311 std::span<const std::uint32_t> cell_info
312 = impl::get_cell_orientation_info(*coefficients[coeff]);
313 impl::pack_coefficient_entity(std::span(c), cstride,
314 *coefficients[coeff], cell_info, cells,
315 offsets[coeff]);
316 }
317 break;
318 }
320 {
321 // Iterate over coefficients that are active in interior
322 // facet integrals
323 for (int coeff : form.active_coeffs(IntegralType::interior_facet, idx))
324 {
325 auto mesh = coefficients[coeff]->function_space()->mesh();
326 std::span<const std::int32_t> facets_b
327 = form.domain_coeff(IntegralType::interior_facet, idx, coeff);
328 md::mdspan<const std::int32_t,
329 md::extents<std::size_t, md::dynamic_extent, 4>>
330 facets(facets_b.data(), facets_b.size() / 4, 4);
331
332 std::span<const std::uint32_t> cell_info
333 = impl::get_cell_orientation_info(*coefficients[coeff]);
334
335 // Data for the '+' and '-' sides of coefficient `coeff` are
336 // interleaved per-coefficient (not stored as two contiguous
337 // blocks), i.e. layout is [coeff0 '+', coeff0 '-', coeff1
338 // '+', coeff1 '-', ...]. `2 * offsets[coeff]` is therefore
339 // the start of coefficient `coeff`'s '+' data, immediately
340 // followed by its '-' data at `offsets[coeff] +
341 // offsets[coeff + 1]`.
342
343 // Pack coefficient ['+']
344 auto cells0 = md::submdspan(facets, md::full_extent, 0);
345 impl::pack_coefficient_entity(std::span(c), 2 * cstride,
346 *coefficients[coeff], cell_info, cells0,
347 2 * offsets[coeff]);
348
349 // Pack coefficient ['-']
350 auto cells1 = md::submdspan(facets, md::full_extent, 2);
351 impl::pack_coefficient_entity(std::span(c), 2 * cstride,
352 *coefficients[coeff], cell_info, cells1,
353 offsets[coeff] + offsets[coeff + 1]);
354 }
355 break;
356 }
360 {
361 // Iterate over coefficients that are active in exterior_facet,
362 // vertex, and ridge integrals (all use the same (cell,
363 // local_entity_index) entity layout)
364 for (int coeff : form.active_coeffs(integral_type, idx))
365 {
366 // Get coefficient mesh
367 auto mesh = coefficients[coeff]->function_space()->mesh();
368 assert(mesh);
369
370 std::span<const std::int32_t> entities_b
371 = form.domain_coeff(integral_type, idx, coeff);
372 md::mdspan<const std::int32_t,
373 md::extents<std::size_t, md::dynamic_extent, 2>>
374 entities(entities_b.data(), entities_b.size() / 2, 2);
375 std::span<const std::uint32_t> cell_info
376 = impl::get_cell_orientation_info(*coefficients[coeff]);
377 impl::pack_coefficient_entity(
378 std::span(c), cstride, *coefficients[coeff], cell_info,
379 md::submdspan(entities, md::full_extent, 0), offsets[coeff]);
380 }
381 break;
382 }
383 default:
384 throw std::invalid_argument(
385 "Could not pack coefficient. Integral type not supported.");
386 }
387 }
388 }
389}
390
406template <dolfinx::scalar T, std::floating_point U>
408 const std::vector<std::reference_wrapper<const Function<T, U>>>& coeffs,
409 const mesh::Mesh<U>& mesh, fem::MDSpan2 auto entities,
410 const std::vector<std::reference_wrapper<const dolfinx::mesh::EntityMap>>&
411 entity_maps,
412 std::span<const int> offsets, std::span<T> c)
413{
414
415 assert(!offsets.empty());
416 const int cstride = offsets.back();
417
418 if (c.size() < entities.extent(0) * offsets.back())
419 throw std::invalid_argument("Coefficient packing span is too small.");
420
421 // Iterate over coefficients
422 const mesh::Topology& topology = *mesh.topology();
423 for (std::size_t coeff = 0; coeff < coeffs.size(); ++coeff)
424 {
425 // Get topology of coefficient and check if entity map is required
426 std::shared_ptr<const mesh::Mesh<U>> mesh_c
427 = coeffs[coeff].get().function_space()->mesh();
428 assert(mesh_c);
429 const mesh::Topology& topology_c = *mesh_c->topology();
430 std::vector<std::int32_t> coefficient_cells;
431 if (&topology_c == &topology)
432 {
433 coefficient_cells = mesh::extract_cells_from_entities(
434 topology_c, topology, entities, std::nullopt);
435 }
436 else
437 {
438 const mesh::EntityMap& emap
439 = mesh::find_entity_map(entity_maps, topology, topology_c);
440 coefficient_cells = mesh::extract_cells_from_entities(
441 topology_c, topology, entities, std::cref(emap));
442 }
443
444 std::span<const std::uint32_t> cell_info
445 = impl::get_cell_orientation_info(coeffs[coeff].get());
446 md::mdspan cells(coefficient_cells.data(), coefficient_cells.size());
447 impl::pack_coefficient_entity(std::span(c), cstride, coeffs[coeff].get(),
448 cell_info, cells, offsets[coeff]);
449 }
450}
451
458template <typename T>
459std::vector<T> pack_constants(
460 const std::vector<std::reference_wrapper<const fem::Constant<T>>>& c)
461{
462 // Calculate size of array needed to store packed constants
463 std::int32_t size = std::accumulate(
464 c.cbegin(), c.cend(), 0, [](std::int32_t sum, auto& constant)
465 { return sum + constant.get().value.size(); });
466
467 // Pack constants
468 std::vector<T> constant_values(size);
469 std::int32_t offset = 0;
470 for (auto& constant : c)
471 {
472 std::ranges::copy(constant.get().value,
473 std::next(constant_values.begin(), offset));
474 offset += constant.get().value.size();
475 }
476
477 return constant_values;
478}
479
484template <typename U>
485 requires std::convertible_to<
487 typename std::decay_t<U>::geometry_type>>
488 or std::convertible_to<
490 typename std::decay_t<U>::geometry_type>>
491std::vector<typename U::scalar_type> pack_constants(const U& u)
492{
493 using T = typename std::decay_t<U>::scalar_type;
494 std::vector<std::reference_wrapper<const Constant<T>>> c;
495 c.reserve(u.constants().size());
496 std::ranges::transform(u.constants(), std::back_inserter(c),
497 [](auto& c) -> const Constant<T>& { return *c; });
498 return fem::pack_constants(c);
499}
500
501} // 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:189
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:177
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:478
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Access coefficients.
Definition Form.h:605
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:621
std::span< const std::int32_t > domain_coeff(IntegralType type, int idx, int c) const
Coefficient function mesh integration entity indices.
Definition Form.h:591
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
Common mesh for the form (the 'integration domain').
Definition Form.h:395
std::vector< int > active_coeffs(IntegralType type, int idx) const
Indices of coefficients that are active for a given integral (kernel).
Definition Form.h:449
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:428
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:522
Definition Function.h:44
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:145
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:151
A bidirectional map relating entities in one topology to another.
Definition EntityMap.h:36
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:25
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:49
Concept for mdspan of rank 1 or 2.
Definition traits.h:57
Finite element method functionality.
Definition assemble_expression_impl.h:28
@ transpose
Transpose.
Definition FiniteElement.h:34
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:267
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:189
IntegralType
Type of integral.
Definition Form.h:43
@ vertex
Vertex.
Definition Form.h:47
@ interior_facet
Interior facet.
Definition Form.h:46
@ ridge
Ridge.
Definition Form.h:48
@ cell
Cell.
Definition Form.h:44
@ exterior_facet
Exterior facet.
Definition Form.h:45
constexpr bool is_transform_set(const F &fn)
Whether a DofTransformKernel fn should be invoked.
Definition traits.h:34
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:459
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
const EntityMap & find_entity_map(std::span< const std::reference_wrapper< const EntityMap > > entity_maps, const Topology &topology0, const Topology &topology1)
Find the entity map relating two topologies.
Definition EntityMap.cpp:31
std::vector< std::int32_t > extract_cells_from_entities(const Topology &topology_c, const Topology &topology, E entities, std::optional< std::reference_wrapper< const EntityMap > > entity_map)
Map integration entities of a topology to the cells of a related topology.
Definition EntityMap.h:231
void pack_impl(std::span< T > coeffs, std::int32_t cell, auto bs, std::span< const T > v, std::span< const std::uint32_t > cell_info, const DofMap &dofmap, auto transform, bool transform_set)
Gather a single coefficient's degrees-of-freedom for a single cell and apply its DOF transformation.
Definition pack.h:90
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:119