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 <functional>
21#include <map>
22#include <memory>
23#include <ranges>
24#include <set>
25#include <span>
26#include <stdexcept>
27#include <tuple>
28#include <utility>
29#include <variant>
30#include <vector>
31
32namespace dolfinx::fem
33{
34template <dolfinx::scalar T>
35class Constant;
36template <dolfinx::scalar T, std::floating_point U>
37class Function;
38
40enum class IntegralType : std::int8_t
41{
42 cell = 0,
45 vertex = 3,
46 ridge = 4
47};
48
55constexpr int integral_entity_dim(IntegralType type, int tdim)
56{
57 switch (type)
58 {
61 return tdim - 1;
63 return tdim - 2;
65 return 0;
67 return tdim;
68 }
69
70 throw std::invalid_argument("Unknown integral type.");
71}
72
76template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
78{
84 template <typename K, typename V, typename W>
85 requires std::is_convertible_v<
86 std::remove_cvref_t<K>,
87 std::function<void(T*, const T*, const T*, const U*,
88 const int*, const std::uint8_t*, void*)>>
89 and std::is_convertible_v<std::remove_cvref_t<V>,
90 std::vector<std::int32_t>>
91 and std::is_convertible_v<std::remove_cvref_t<W>,
92 std::vector<int>>
94 : kernel(std::forward<K>(kernel)), entities(std::forward<V>(entities)),
95 coeffs(std::forward<W>(coeffs))
96 {
97 }
98
100 std::function<void(T*, const T*, const T*, const U*, const int*,
101 const std::uint8_t*, void*)>
103
106 std::vector<std::int32_t> entities;
107
110 std::vector<int> coeffs;
111};
112
140template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
141class Form
142{
143public:
145 using scalar_type = T;
146
148 using geometry_type = U;
149
175 template <typename X>
176 requires std::is_convertible_v<
177 std::remove_cvref_t<X>,
178 std::map<std::tuple<IntegralType, int, int>,
181 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>& V,
182 X&& integrals, std::shared_ptr<const mesh::Mesh<geometry_type>> mesh,
183 const std::vector<
184 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
186 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
187 constants,
189 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
190 entity_maps)
191 : _function_spaces(V), _integrals(std::forward<X>(integrals)),
192 _mesh(std::move(mesh)), _coefficients(coefficients),
193 _constants(constants),
194 _needs_facet_permutations(needs_facet_permutations)
195 {
196 if (!_mesh)
197 throw std::invalid_argument("Form Mesh is null.");
198
199 // `_mesh` is fixed for the remainder of construction, so its
200 // topology and dimension are fetched once and reused below rather
201 // than being re-fetched for every integral/coefficient.
202 const mesh::Topology& topology = *_mesh->topology();
203 const int tdim = topology.dim();
204
205 // Map the integration entities of one integral to the
206 // argument/coefficient domain, checking first that the mapping is
207 // expressible
208 auto map_entities
209 = [tdim,
210 &topology](IntegralType type, std::span<const std::int32_t> entities,
211 const mesh::Topology& topology0,
212 const mesh::EntityMap& emap) -> std::vector<std::int32_t>
213 {
214 if (type == IntegralType::cell)
215 {
217 topology0, topology, md::mdspan(entities.data(), entities.size()),
218 std::cref(emap));
219 }
220
221 if (type == IntegralType::vertex)
222 {
223 throw std::invalid_argument(
224 "Vertex integrals are not supported for a form with an argument "
225 "or coefficient on another mesh. Supported types are cell, "
226 "exterior facet, interior facet and ridge.");
227 }
228
229 mesh::check_entity_cell_mapping(topology0, topology,
230 integral_entity_dim(type, tdim));
231
232 // Map the (cell, local_entity) pairs, flattened (interior facets
233 // hold two pairs per entity), to cells of the argument/coefficient
234 // mesh
235 std::vector<std::int32_t> cells = mesh::extract_cells_from_entities(
236 topology0, topology,
237 md::mdspan<const std::int32_t,
238 md::extents<std::size_t, md::dynamic_extent, 2>>(
239 entities.data(), entities.size() / 2, 2),
240 std::cref(emap));
241
242 // Replace the cell of each pair. Only the cell column is
243 // meaningful: for codim > 0 the entity is itself the cell, so it
244 // has no local index. The local index column is kept for the
245 // layout used in packing/assembly.
246 std::vector<std::int32_t> e(entities.begin(), entities.end());
247 for (std::size_t i = 0; i < cells.size(); ++i)
248 e[2 * i] = cells[i];
249 return e;
250 };
251
252 _edata.reserve(_function_spaces.size());
253 for (auto& space : _function_spaces)
254 {
255 // Working map: [integral type, integral_idx, kernel_idx]->entities
256 std::map<std::tuple<IntegralType, int, int>,
257 std::variant<std::vector<std::int32_t>,
258 std::span<const std::int32_t>>>
259 vdata;
260
261 if (auto mesh0 = space->mesh(); mesh0 == _mesh)
262 {
263 for (auto& [key, integral] : _integrals)
264 vdata.insert({key, std::span(integral.entities)});
265 }
266 else
267 {
268 // Find correct entity map
269 const mesh::Topology& topology0 = *mesh0->topology();
270 const mesh::EntityMap& emap
271 = mesh::find_entity_map(entity_maps, topology, topology0);
272 for (auto& [key, itg] : _integrals)
273 {
274 auto [type, idx, kernel_idx] = key;
275 vdata.insert(
276 {key, map_entities(type, itg.entities, topology0, emap)});
277 }
278 }
279
280 _edata.push_back(std::move(vdata));
281 }
282
283 for (auto& [key, integral] : _integrals)
284 {
285 auto [type, idx, kernel_idx] = key;
286 for (int c : integral.coeffs)
287 {
288 if (auto mesh0 = coefficients.at(c)->function_space()->mesh();
289 mesh0 == _mesh)
290 {
291 _cdata.insert({{type, idx, c}, std::span(integral.entities)});
292 }
293 else
294 {
295 // Find correct entity map
296 const mesh::Topology& topology0 = *mesh0->topology();
297 const mesh::EntityMap& emap
298 = mesh::find_entity_map(entity_maps, topology, topology0);
299 _cdata.insert(
300 {{type, idx, c},
301 map_entities(type, integral.entities, topology0, emap)});
302 }
303 }
304 }
305 }
306
307 // Copy constructor (deleted). _edata and _cdata cache std::spans
308 // aliasing the entity vectors owned by _integrals; a shallow copy
309 // would leave the copy's spans pointing into the original's data.
310 Form(const Form& form) = delete;
311
316#ifdef _MSC_VER
322 Form(Form&& form) noexcept = default;
323#else
324 Form(Form&& form) = default;
325#endif
326
328 ~Form() = default;
329
330 // Copy assignment (deleted). Same aliasing reason as the copy
331 // constructor.
332 Form& operator=(const Form& form) = delete;
333
339 Form& operator=(Form&& form) = default;
340
346 int rank() const { return _function_spaces.size(); }
347
350 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
351 {
352 return _mesh;
353 }
354
357 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>&
359 {
360 return _function_spaces;
361 }
362
371 std::function<void(scalar_type*, const scalar_type*, const scalar_type*,
372 const geometry_type*, const int*, const std::uint8_t*,
373 void*)>
374 kernel(IntegralType type, int idx, int kernel_idx) const
375 {
376 auto it = _integrals.find({type, idx, kernel_idx});
377 if (it == _integrals.end())
378 throw std::out_of_range("Requested integral kernel not found.");
379 return it->second.kernel;
380 }
381
384 std::set<IntegralType> integral_types() const
385 {
386 std::set<IntegralType> types;
387 for (auto& [key, integral] : _integrals)
388 types.insert(std::get<0>(key));
389 return types;
390 }
391
405 std::vector<int> active_coeffs(IntegralType type, int idx) const
406 {
407 auto it = std::ranges::find_if(_integrals,
408 [type, idx](auto& x)
409 {
410 auto [t, idx_, kernel_idx] = x.first;
411 return t == type and idx_ == idx;
412 });
413 if (it == _integrals.end())
414 throw std::out_of_range("Could not find active coefficient list.");
415 return it->second.coeffs;
416 }
417
434 int num_integrals(IntegralType type, int kernel_idx) const
435 {
436 return std::ranges::count_if(_integrals,
437 [type, kernel_idx](auto& x)
438 {
439 auto [t, id, k_idx] = x.first;
440 return t == type and k_idx == kernel_idx;
441 });
442 }
443
478 std::span<const std::int32_t> domain(IntegralType type, int idx,
479 int kernel_idx) const
480 {
481 auto it = _integrals.find({type, idx, kernel_idx});
482 if (it == _integrals.end())
483 throw std::out_of_range("Requested domain not found.");
484 return it->second.entities;
485 }
486
522 std::span<const std::int32_t> domain_arg(IntegralType type, int rank, int idx,
523 int kernel_idx) const
524 {
525 auto it = _edata.at(rank).find({type, idx, kernel_idx});
526 if (it == _edata.at(rank).end())
527 throw std::out_of_range("Requested domain for argument not found.");
528
529 return std::visit([](const auto& v) -> std::span<const std::int32_t>
530 { return v; }, it->second);
531 }
532
547 std::span<const std::int32_t> domain_coeff(IntegralType type, int idx,
548 int c) const
549 {
550 auto it = _cdata.find({type, idx, c});
551 if (it == _cdata.end())
552 throw std::out_of_range("No domain for requested integral.");
553 return std::visit([](const auto& v) -> std::span<const std::int32_t>
554 { return v; }, it->second);
555 }
556
559 const std::vector<
560 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
562 {
563 return _coefficients;
564 }
565
569 bool needs_facet_permutations() const { return _needs_facet_permutations; }
570
577 std::vector<int> coefficient_offsets() const
578 {
579 std::vector<int> n{0};
580 n.reserve(_coefficients.size() + 1);
581 for (auto& c : _coefficients)
582 {
583 if (!c)
584 throw std::runtime_error("Not all form coefficients have been set.");
585 n.push_back(n.back() + c->function_space()->element()->space_dimension());
586 }
587 return n;
588 }
589
592 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
593 constants() const
594 {
595 return _constants;
596 }
597
598private:
599 // Function spaces (one for each argument)
600 std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>
601 _function_spaces;
602
603 // Integrals (integral type, idx, kernel_idx)
604 std::map<std::tuple<IntegralType, int, int>,
606 _integrals;
607
608 // The mesh
609 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
610
611 // Form coefficients
612 std::vector<std::shared_ptr<const Function<scalar_type, geometry_type>>>
613 _coefficients;
614
615 // Constants associated with the Form
616 std::vector<std::shared_ptr<const Constant<scalar_type>>> _constants;
617
618 // True if permutation data needs to be passed into these integrals
619 bool _needs_facet_permutations;
620
621 // Mapped domain index data for argument functions.
622 //
623 // Consider:
624 //
625 // entities = this->domain(type, idx, kernel_idx);
626 // entities0 = _edata[0][{type, idx, kernel_idx}];
627 //
628 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
629 // `_mesh`, and `entities0[i]` is the index of the same entity but in
630 // the mesh associated with the argument 0 (test function) space.
631 std::vector<std::map<
632 std::tuple<IntegralType, int, int>,
633 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>>
634 _edata;
635
636 // Mapped domain index data for coefficient functions.
637 //
638 // Consider:
639 //
640 // entities = this->domain(type, idx, kernel_idx);
641 // entities0 = _cdata[{type, idx, c}];
642 //
643 // where `c` is the coefficient index.
644 //
645 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
646 // `_mesh`, and `entities0[i]` is the index of the same entity but in
647 // the mesh associated with the coefficient Function.
648 std::map<
649 std::tuple<IntegralType, int, int>,
650 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>
651 _cdata;
652};
653} // 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:142
U geometry_type
Geometry type.
Definition Form.h:148
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:434
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Access coefficients.
Definition Form.h:561
~Form()=default
Destructor.
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition Form.h:569
Form(Form &&form)=default
int rank() const
Rank of the form.
Definition Form.h:346
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:180
const std::vector< std::shared_ptr< const Constant< scalar_type > > > & constants() const
Access constants.
Definition Form.h:593
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
const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > & function_spaces() const
Function spaces for all arguments.
Definition Form.h:358
std::function< void(scalar_type *, const scalar_type *, const scalar_type *, const geometry_type *, const int *, const std::uint8_t *, void *)> kernel(IntegralType type, int idx, int kernel_idx) const
Get the kernel function for an integral.
Definition Form.h:374
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:522
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
T scalar_type
Scalar type.
Definition Form.h:145
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:52
int dim() const noexcept
Topological dimension of the mesh.
Definition Topology.cpp:898
Finite element method functionality.
Definition assemble_expression_impl.h:28
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 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:55
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
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:78
integral_data(K &&kernel, V &&entities, W &&coeffs)
Create a structure to hold integral data.
Definition Form.h:93
std::function< void(T *, const T *, const T *, const U *, const int *, const std::uint8_t *, void *)> kernel
The integration kernel.
Definition Form.h:102
std::vector< int > coeffs
Indices of coefficients (from the form) that are in this integral.
Definition Form.h:110
std::vector< std::int32_t > entities
The entities to integrate over for this integral. These are the entities in 'full' mesh.
Definition Form.h:106