DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
Form.h
1// Copyright (C) 2019-2026 Garth N. Wells, Chris Richardson, Joseph P. Dean and
2// Jørgen S. Dokken
3//
4// This file is part of DOLFINx (https://www.fenicsproject.org)
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7
8#pragma once
9
10#include "FunctionSpace.h"
11#include "traits.h"
12#include <algorithm>
13#include <basix/mdspan.hpp>
14#include <concepts>
15#include <cstdint>
16#include <dolfinx/common/types.h>
17#include <dolfinx/mesh/EntityMap.h>
18#include <dolfinx/mesh/Mesh.h>
19#include <dolfinx/mesh/Topology.h>
20#include <dolfinx/mesh/cell_types.h>
21#include <format>
22#include <functional>
23#include <map>
24#include <memory>
25#include <ranges>
26#include <set>
27#include <span>
28#include <stdexcept>
29#include <tuple>
30#include <utility>
31#include <variant>
32#include <vector>
33
34namespace dolfinx::fem
35{
36template <dolfinx::scalar T>
37class Constant;
38template <dolfinx::scalar T, std::floating_point U>
39class Function;
40
42enum class IntegralType : std::int8_t
43{
44 cell = 0,
47 vertex = 3,
48 ridge = 4
49};
50
57constexpr int integral_entity_dim(IntegralType type, int tdim)
58{
59 switch (type)
60 {
63 return tdim - 1;
65 return tdim - 2;
67 return 0;
69 return tdim;
70 }
71
72 throw std::invalid_argument("Unknown integral type.");
73}
74
75namespace impl
76{
90inline md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>>
91entity_permutations(mesh::Topology& topology, IntegralType type,
92 mesh::CellType cell_type)
93{
94 if (type == IntegralType::cell)
95 return {};
96
97 const int tdim = topology.dim();
98 const int dim = integral_entity_dim(type, tdim);
99
100 topology.create_entity_permutations(dim);
101 const std::vector<std::uint8_t>& p = topology.get_entity_permutations(dim);
102 const int num_entities_per_cell = mesh::cell_num_entities(cell_type, dim);
103 return md::mdspan(p.data(), p.size() / num_entities_per_cell,
104 num_entities_per_cell);
105}
106} // namespace impl
107
111template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
113{
119 template <typename K, typename V, typename W>
120 requires std::is_convertible_v<
121 std::remove_cvref_t<K>,
122 std::function<void(T*, const T*, const T*, const U*,
123 const int*, const uint8_t*, void*)>>
124 and std::is_convertible_v<std::remove_cvref_t<V>,
125 std::vector<std::int32_t>>
126 and std::is_convertible_v<std::remove_cvref_t<W>,
127 std::vector<int>>
129 : kernel(std::forward<K>(kernel)), entities(std::forward<V>(entities)),
130 coeffs(std::forward<W>(coeffs))
131 {
132 }
133
135 std::function<void(T*, const T*, const T*, const U*, const int*,
136 const uint8_t*, void*)>
138
141 std::vector<std::int32_t> entities;
142
145 std::vector<int> coeffs;
146};
147
175template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
176class Form
177{
178public:
180 using scalar_type = T;
181
183 using geometry_type = U;
184
210 template <typename X>
211 requires std::is_convertible_v<
212 std::remove_cvref_t<X>,
213 std::map<std::tuple<IntegralType, int, int>,
216 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>& V,
217 X&& integrals, std::shared_ptr<const mesh::Mesh<geometry_type>> mesh,
218 const std::vector<
219 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
221 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
222 constants,
224 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
225 entity_maps)
226 : _function_spaces(V), _integrals(std::forward<X>(integrals)),
227 _mesh(std::move(mesh)), _coefficients(coefficients),
228 _constants(constants),
229 _needs_facet_permutations(needs_facet_permutations)
230 {
231 if (!_mesh)
232 throw std::invalid_argument("Form Mesh is null.");
233
234 // `_mesh` is fixed for the remainder of construction, so its
235 // topology and dimension are fetched once and reused below rather
236 // than being re-fetched for every integral/coefficient.
237 const mesh::Topology& topology = *_mesh->topology();
238 const int tdim = topology.dim();
239
240 // Map the integration entities of one integral to the
241 // argument/coefficient domain, checking first that the mapping is
242 // expressible: an integration entity maps to a *cell* of that mesh,
243 // so unless the meshes have equal dimension the integral's entity
244 // dimension must be that mesh's.
245 auto map_entities
246 = [tdim,
247 &topology](IntegralType type, std::span<const std::int32_t> entities,
248 const mesh::Topology& topology0,
249 const mesh::EntityMap& emap) -> std::vector<std::int32_t>
250 {
251 if (type == IntegralType::cell)
252 {
254 topology0, topology, md::mdspan(entities.data(), entities.size()),
255 std::cref(emap));
256 }
257
258 if (type == IntegralType::vertex)
259 {
260 throw std::invalid_argument(
261 "Vertex integrals are not supported for a form with an argument "
262 "or coefficient on another mesh. Supported types are cell, "
263 "exterior facet, interior facet and ridge.");
264 }
265
266 const int dim0 = topology0.dim();
267 const int edim = integral_entity_dim(type, tdim);
268 if (tdim > dim0 and edim != dim0)
269 {
270 throw std::invalid_argument(std::format(
271 "Cannot map integration entities of dimension {} to cells of a "
272 "mesh of dimension {}. An argument or coefficient on another mesh "
273 "must live on the entities being integrated over.",
274 edim, dim0));
275 }
276
277 // Map the (cell, local_entity) pairs, flattened (interior facets
278 // hold two pairs per entity), to cells of the argument/coefficient
279 // mesh
280 std::vector<std::int32_t> cells = mesh::extract_cells_from_entities(
281 topology0, topology,
282 md::mdspan<const std::int32_t,
283 md::extents<std::size_t, md::dynamic_extent, 2>>(
284 entities.data(), entities.size() / 2, 2),
285 std::cref(emap));
286
287 // Replace the cell of each pair. Only the cell column is
288 // meaningful: for codim > 0 the entity is itself the cell, so it
289 // has no local index. The local index column is kept for the
290 // layout used in packing/assembly.
291 std::vector<std::int32_t> e(entities.begin(), entities.end());
292 for (std::size_t i = 0; i < cells.size(); ++i)
293 e[2 * i] = cells[i];
294 return e;
295 };
296
297 _edata.reserve(_function_spaces.size());
298 for (auto& space : _function_spaces)
299 {
300 // Working map: [integral type, integral_idx, kernel_idx]->entities
301 std::map<std::tuple<IntegralType, int, int>,
302 std::variant<std::vector<std::int32_t>,
303 std::span<const std::int32_t>>>
304 vdata;
305
306 if (auto mesh0 = space->mesh(); mesh0 == _mesh)
307 {
308 for (auto& [key, integral] : _integrals)
309 vdata.insert({key, std::span(integral.entities)});
310 }
311 else
312 {
313 // Find correct entity map
314 const mesh::Topology& topology0 = *mesh0->topology();
315 const mesh::EntityMap& emap
316 = mesh::find_entity_map(entity_maps, topology, topology0);
317 for (auto& [key, itg] : _integrals)
318 {
319 auto [type, idx, kernel_idx] = key;
320 vdata.insert(
321 {key, map_entities(type, itg.entities, topology0, emap)});
322 }
323 }
324
325 _edata.push_back(std::move(vdata));
326 }
327
328 for (auto& [key, integral] : _integrals)
329 {
330 auto [type, idx, kernel_idx] = key;
331 for (int c : integral.coeffs)
332 {
333 if (auto mesh0 = coefficients.at(c)->function_space()->mesh();
334 mesh0 == _mesh)
335 {
336 _cdata.insert({{type, idx, c}, std::span(integral.entities)});
337 }
338 else
339 {
340 // Find correct entity map
341 const mesh::Topology& topology0 = *mesh0->topology();
342 const mesh::EntityMap& emap
343 = mesh::find_entity_map(entity_maps, topology, topology0);
344 _cdata.insert(
345 {{type, idx, c},
346 map_entities(type, integral.entities, topology0, emap)});
347 }
348 }
349 }
350 }
351
352 // Copy constructor (deleted). _edata and _cdata cache std::spans
353 // aliasing the entity vectors owned by _integrals; a shallow copy
354 // would leave the copy's spans pointing into the original's data.
355 Form(const Form& form) = delete;
356
361#ifdef _MSC_VER
367 Form(Form&& form) noexcept = default;
368#else
369 Form(Form&& form) = default;
370#endif
371
373 ~Form() = default;
374
375 // Copy assignment (deleted). Same aliasing reason as the copy
376 // constructor.
377 Form& operator=(const Form& form) = delete;
378
384 Form& operator=(Form&& form) = default;
385
391 int rank() const { return _function_spaces.size(); }
392
395 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
396 {
397 return _mesh;
398 }
399
402 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>&
404 {
405 return _function_spaces;
406 }
407
416 std::function<void(scalar_type*, const scalar_type*, const scalar_type*,
417 const geometry_type*, const int*, const uint8_t*, void*)>
418 kernel(IntegralType type, int idx, int kernel_idx) const
419 {
420 auto it = _integrals.find({type, idx, kernel_idx});
421 if (it == _integrals.end())
422 throw std::out_of_range("Requested integral kernel not found.");
423 return it->second.kernel;
424 }
425
428 std::set<IntegralType> integral_types() const
429 {
430 std::set<IntegralType> types;
431 for (auto& [key, integral] : _integrals)
432 types.insert(std::get<0>(key));
433 return types;
434 }
435
449 std::vector<int> active_coeffs(IntegralType type, int idx) const
450 {
451 auto it = std::ranges::find_if(_integrals,
452 [type, idx](auto& x)
453 {
454 auto [t, idx_, kernel_idx] = x.first;
455 return t == type and idx_ == idx;
456 });
457 if (it == _integrals.end())
458 throw std::out_of_range("Could not find active coefficient list.");
459 return it->second.coeffs;
460 }
461
478 int num_integrals(IntegralType type, int kernel_idx) const
479 {
480 return std::ranges::count_if(_integrals,
481 [type, kernel_idx](auto& x)
482 {
483 auto [t, id, k_idx] = x.first;
484 return t == type and k_idx == kernel_idx;
485 });
486 }
487
522 std::span<const std::int32_t> domain(IntegralType type, int idx,
523 int kernel_idx) const
524 {
525 auto it = _integrals.find({type, idx, kernel_idx});
526 if (it == _integrals.end())
527 throw std::out_of_range("Requested domain not found.");
528 return it->second.entities;
529 }
530
566 std::span<const std::int32_t> domain_arg(IntegralType type, int rank, int idx,
567 int kernel_idx) const
568 {
569 auto it = _edata.at(rank).find({type, idx, kernel_idx});
570 if (it == _edata.at(rank).end())
571 throw std::out_of_range("Requested domain for argument not found.");
572
573 return std::visit([](const auto& v) -> std::span<const std::int32_t>
574 { return v; }, it->second);
575 }
576
591 std::span<const std::int32_t> domain_coeff(IntegralType type, int idx,
592 int c) const
593 {
594 auto it = _cdata.find({type, idx, c});
595 if (it == _cdata.end())
596 throw std::out_of_range("No domain for requested integral.");
597 return std::visit([](const auto& v) -> std::span<const std::int32_t>
598 { return v; }, it->second);
599 }
600
603 const std::vector<
604 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
606 {
607 return _coefficients;
608 }
609
613 bool needs_facet_permutations() const { return _needs_facet_permutations; }
614
621 std::vector<int> coefficient_offsets() const
622 {
623 std::vector<int> n{0};
624 n.reserve(_coefficients.size() + 1);
625 for (auto& c : _coefficients)
626 {
627 if (!c)
628 throw std::runtime_error("Not all form coefficients have been set.");
629 n.push_back(n.back() + c->function_space()->element()->space_dimension());
630 }
631 return n;
632 }
633
636 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
637 constants() const
638 {
639 return _constants;
640 }
641
642private:
643 // Function spaces (one for each argument)
644 std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>
645 _function_spaces;
646
647 // Integrals (integral type, idx, kernel_idx)
648 std::map<std::tuple<IntegralType, int, int>,
650 _integrals;
651
652 // The mesh
653 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
654
655 // Form coefficients
656 std::vector<std::shared_ptr<const Function<scalar_type, geometry_type>>>
657 _coefficients;
658
659 // Constants associated with the Form
660 std::vector<std::shared_ptr<const Constant<scalar_type>>> _constants;
661
662 // True if permutation data needs to be passed into these integrals
663 bool _needs_facet_permutations;
664
665 // Mapped domain index data for argument functions.
666 //
667 // Consider:
668 //
669 // entities = this->domain(type, idx, kernel_idx);
670 // entities0 = _edata[0][{type, idx, kernel_idx}];
671 //
672 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
673 // `_mesh`, and `entities0[i]` is the index of the same entity but in
674 // the mesh associated with the argument 0 (test function) space.
675 std::vector<std::map<
676 std::tuple<IntegralType, int, int>,
677 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>>
678 _edata;
679
680 // Mapped domain index data for coefficient functions.
681 //
682 // Consider:
683 //
684 // entities = this->domain(type, idx, kernel_idx);
685 // entities0 = _cdata[{type, idx, c}];
686 //
687 // where `c` is the coefficient index.
688 //
689 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
690 // `_mesh`, and `entities0[i]` is the index of the same entity but in
691 // the mesh associated with the coefficient Function.
692 std::map<
693 std::tuple<IntegralType, int, int>,
694 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>
695 _cdata;
696};
697} // namespace dolfinx::fem
Constant (in space) value which can be attached to a Form.
Definition Constant.h:22
A representation of finite element variational forms.
Definition Form.h:177
U geometry_type
Geometry type.
Definition Form.h:183
Form & operator=(Form &&form)=default
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
~Form()=default
Destructor.
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition Form.h:613
Form(Form &&form)=default
int rank() const
Rank of the form.
Definition Form.h:391
Form(const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > &V, X &&integrals, std::shared_ptr< const mesh::Mesh< geometry_type > > mesh, const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > &coefficients, const std::vector< std::shared_ptr< const Constant< scalar_type > > > &constants, bool needs_facet_permutations, const std::vector< std::reference_wrapper< const mesh::EntityMap > > &entity_maps)
Create a finite element form.
Definition Form.h:215
const std::vector< std::shared_ptr< const Constant< scalar_type > > > & constants() const
Access constants.
Definition Form.h:637
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:621
std::function< void(scalar_type *, const scalar_type *, const scalar_type *, const geometry_type *, const int *, const uint8_t *, void *)> kernel(IntegralType type, int idx, int kernel_idx) const
Get the kernel function for an integral.
Definition Form.h:418
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
const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > & function_spaces() const
Function spaces for all arguments.
Definition Form.h:403
std::span< const std::int32_t > domain_arg(IntegralType type, int rank, int idx, int kernel_idx) const
Argument function mesh integration entity indices.
Definition Form.h:566
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
T scalar_type
Scalar type.
Definition Form.h:180
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:36
Definition Function.h:44
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
void create_entity_permutations(int dim, int num_threads=1)
Compute entity permutations and reflections.
Definition Topology.cpp:1183
int dim() const noexcept
Topological dimension of the mesh.
Definition Topology.cpp:897
const std::vector< std::uint8_t > & get_entity_permutations(int dim) const
Get the numbers that encode the permutation to apply to each cell-local entity of a given dimension.
Definition Topology.cpp:1058
Finite element method functionality.
Definition assemble_expression_impl.h:28
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 int integral_entity_dim(IntegralType type, int tdim)
Topological dimension of the mesh entities an integral of the given type is over.
Definition Form.h:57
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
CellType
Cell type identifier.
Definition cell_types.h:24
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
int cell_num_entities(CellType type, int dim)
Number of entities of dimension.
Definition cell_types.cpp:97
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:113
std::function< void(T *, const T *, const T *, const U *, const int *, const uint8_t *, void *)> kernel
The integration kernel.
Definition Form.h:137
integral_data(K &&kernel, V &&entities, W &&coeffs)
Create a structure to hold integral data.
Definition Form.h:128
std::vector< int > coeffs
Indices of coefficients (from the form) that are in this integral.
Definition Form.h:145
std::vector< std::int32_t > entities
The entities to integrate over for this integral. These are the entities in 'full' mesh.
Definition Form.h:141