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
410template <dolfinx::scalar T, std::floating_point U>
412 const std::vector<std::reference_wrapper<const Function<T, U>>>& coeffs,
413 const mesh::Topology& topology, fem::MDSpan2 auto entities, int dim,
414 const std::vector<std::reference_wrapper<const dolfinx::mesh::EntityMap>>&
415 entity_maps,
416 std::span<const int> offsets, std::span<T> c)
417{
418
419 assert(!offsets.empty());
420 const int cstride = offsets.back();
421
422 if (c.size() < entities.extent(0) * offsets.back())
423 throw std::invalid_argument("Coefficient packing span is too small.");
424
425 // Check that the entities can be mapped to the cells of each
426 // coefficient's mesh before packing
427 std::vector<std::optional<std::reference_wrapper<const mesh::EntityMap>>>
428 emaps(coeffs.size());
429 for (std::size_t coeff = 0; coeff < coeffs.size(); ++coeff)
430 {
431 const mesh::Topology& topology_c
432 = *coeffs[coeff].get().function_space()->mesh()->topology();
433 if (&topology_c != &topology)
434 {
435 mesh::check_entity_cell_mapping(topology_c, topology, dim);
436 emaps[coeff]
437 = std::cref(mesh::find_entity_map(entity_maps, topology, topology_c));
438 }
439 }
440
441 // Cells of `topology`, shared by all coefficients on it
442 std::vector<std::int32_t> cells;
443 if (std::ranges::any_of(
444 emaps,
445 [](const std::optional<std::reference_wrapper<const mesh::EntityMap>>&
446 emap) { return !emap; }))
447 {
448 cells = mesh::extract_cells_from_entities(topology, topology, entities,
449 std::nullopt);
450 }
451
452 for (std::size_t coeff = 0; coeff < coeffs.size(); ++coeff)
453 {
454 std::vector<std::int32_t> cells_c;
455 if (emaps[coeff])
456 {
458 *coeffs[coeff].get().function_space()->mesh()->topology(), topology,
459 entities, emaps[coeff]);
460 }
461 std::span<const std::int32_t> coeff_cells = emaps[coeff] ? cells_c : cells;
462
463 std::span<const std::uint32_t> cell_info
464 = impl::get_cell_orientation_info(coeffs[coeff].get());
465 impl::pack_coefficient_entity(
466 std::span(c), cstride, coeffs[coeff].get(), cell_info,
467 md::mdspan(coeff_cells.data(), coeff_cells.size()), offsets[coeff]);
468 }
469}
470
477template <typename T>
478std::vector<T> pack_constants(
479 const std::vector<std::reference_wrapper<const fem::Constant<T>>>& c)
480{
481 // Calculate size of array needed to store packed constants
482 std::int32_t size = std::accumulate(
483 c.cbegin(), c.cend(), 0, [](std::int32_t sum, auto& constant)
484 { return sum + constant.get().value.size(); });
485
486 // Pack constants
487 std::vector<T> constant_values(size);
488 std::int32_t offset = 0;
489 for (auto& constant : c)
490 {
491 std::ranges::copy(constant.get().value,
492 std::next(constant_values.begin(), offset));
493 offset += constant.get().value.size();
494 }
495
496 return constant_values;
497}
498
503template <typename U>
504 requires std::convertible_to<
506 typename std::decay_t<U>::geometry_type>>
507 or std::convertible_to<
509 typename std::decay_t<U>::geometry_type>>
510std::vector<typename U::scalar_type> pack_constants(const U& u)
511{
512 using T = typename std::decay_t<U>::scalar_type;
513 std::vector<std::reference_wrapper<const Constant<T>>> c;
514 c.reserve(u.constants().size());
515 std::ranges::transform(u.constants(), std::back_inserter(c),
516 [](auto& c) -> const Constant<T>& { return *c; });
517 return fem::pack_constants(c);
518}
519
520} // 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:45
A representation of finite element variational forms.
Definition Form.h:142
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:434
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Access coefficients.
Definition Form.h:561
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:577
std::span< const std::int32_t > domain_coeff(IntegralType type, int idx, int c) const
Coefficient function mesh integration entity indices.
Definition Form.h:547
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
Common mesh for the form (the 'integration domain').
Definition Form.h:350
std::vector< int > active_coeffs(IntegralType type, int idx) const
Indices of coefficients that are active for a given integral (kernel).
Definition Form.h:405
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:384
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:478
Definition Function.h:44
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:149
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:155
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:52
Concept for mdspan of rank 1 or 2.
Definition traits.h:57
Finite element method functionality.
Definition assemble_expression_impl.h:28
std::pair< std::vector< T >, int > pack_coefficients(const fem::Expression< T, U > &e, const mesh::Mesh< U > &mesh, fem::MDSpan2 auto entities)
Pack the coefficients of an Expression over entities of a mesh.
Definition expression_evaluate.h:121
@ transpose
Transpose.
Definition FiniteElement.h:35
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: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
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:478
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 entities of a topology to the cells of a related topology, e.g. the integration entities of a for...
Definition EntityMap.h:253
void check_entity_cell_mapping(const Topology &topology_c, const Topology &topology, int dim)
Check that entities of a topology can be mapped to cells of a related topology.
Definition EntityMap.cpp:54
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