DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
Form.h
1// Copyright (C) 2019-2025 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 <cassert>
15#include <concepts>
16#include <cstdint>
17#include <dolfinx/common/types.h>
18#include <dolfinx/mesh/EntityMap.h>
19#include <dolfinx/mesh/Mesh.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
52template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
54{
60 template <typename K, typename V, typename W>
61 requires std::is_convertible_v<
62 std::remove_cvref_t<K>,
63 std::function<void(T*, const T*, const T*, const U*,
64 const int*, const uint8_t*, void*)>>
65 and std::is_convertible_v<std::remove_cvref_t<V>,
66 std::vector<std::int32_t>>
67 and std::is_convertible_v<std::remove_cvref_t<W>,
68 std::vector<int>>
70 : kernel(std::forward<K>(kernel)), entities(std::forward<V>(entities)),
71 coeffs(std::forward<W>(coeffs))
72 {
73 }
74
76 std::function<void(T*, const T*, const T*, const U*, const int*,
77 const uint8_t*, void*)>
79
82 std::vector<std::int32_t> entities;
83
86 std::vector<int> coeffs;
87};
88
116template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
117class Form
118{
119public:
121 using scalar_type = T;
122
124 using geometry_type = U;
125
151 template <typename X>
152 requires std::is_convertible_v<
153 std::remove_cvref_t<X>,
154 std::map<std::tuple<IntegralType, int, int>,
157 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>& V,
158 X&& integrals, std::shared_ptr<const mesh::Mesh<geometry_type>> mesh,
159 const std::vector<
160 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
162 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
163 constants,
165 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
166 entity_maps)
167 : _function_spaces(V), _integrals(std::forward<X>(integrals)),
168 _mesh(mesh), _coefficients(coefficients), _constants(constants),
169 _needs_facet_permutations(needs_facet_permutations)
170 {
171 if (!_mesh)
172 throw std::runtime_error("Form Mesh is null.");
173
174 // `_mesh` is fixed for the remainder of construction, so its
175 // topology and dimension are fetched once and reused below rather
176 // than being re-fetched for every integral/coefficient.
177 const mesh::Topology& topology = *_mesh->topology();
178 const int tdim = topology.dim();
179
180 // A helper function to find the correct entity map for a given mesh
181 auto get_entity_map
182 = [mesh, &entity_maps](auto& mesh0) -> const mesh::EntityMap&
183 {
184 auto it = std::ranges::find_if(
185 entity_maps,
186 [mesh, mesh0](const mesh::EntityMap& em)
187 {
188 return ((em.topology() == mesh0->topology()
189 and em.sub_topology() == mesh->topology()))
190 or ((em.sub_topology() == mesh0->topology()
191 and em.topology() == mesh->topology()));
192 });
193
194 if (it == entity_maps.end())
195 {
196 throw std::runtime_error(
197 "Incompatible mesh. argument entity_maps must be provided.");
198 }
199 return *it;
200 };
201
202 // A helper function to compute the (cell, local_facet) pairs in the
203 // argument/coefficient domain from the (cell, local_facet) pairs in
204 // `this->mesh()`.
205 auto compute_facet_domains
206 = [](const auto& int_ents_mesh, int codim, const auto& c_to_f,
207 const auto& emap, bool inverse)
208 {
209 // TODO: This function would be much neater using
210 // `std::views::stride(2)` from C++ 23
211
212 // Get a list of entities to map to the argument/coefficient
213 // domain
214 std::vector<std::int32_t> entities;
215 entities.reserve(int_ents_mesh.size() / 2);
216 if (codim == 0)
217 {
218 // In the codim 0 case, we need to map from cells in
219 // `this->mesh()` to cells in the argument/coefficient mesh, so
220 // here we extract the cells.
221 for (std::size_t i = 0; i < int_ents_mesh.size(); i += 2)
222 entities.push_back(int_ents_mesh[i]);
223 }
224 else if (codim == 1)
225 {
226 // In the codim 1 case, we need to map facets in `this->mesh()`
227 // to cells in the argument/coefficient mesh, so here we extract
228 // the facet index using the cell-to-facet connectivity.
229 for (std::size_t i = 0; i < int_ents_mesh.size(); i += 2)
230 {
231 entities.push_back(
232 c_to_f->links(int_ents_mesh[i])[int_ents_mesh[i + 1]]);
233 }
234 }
235 else
236 throw std::runtime_error("Codimension > 1 not supported.");
237
238 // Map from entity indices in `this->mesh()` to the corresponding
239 // cell indices in the argument/coefficient mesh
240 std::vector<std::int32_t> cells_mesh0
241 = emap.sub_topology_to_topology(entities, inverse);
242
243 // Create a list of (cell, local_facet_index) pairs in the
244 // argument/coefficient domain. Since `create_submesh`preserves
245 // the local facet index (with respect to the cell), we can use
246 // the local facet indices from the input integration entities
247 std::vector<std::int32_t> e = int_ents_mesh;
248 for (std::size_t i = 0; i < cells_mesh0.size(); ++i)
249 e[2 * i] = cells_mesh0[i];
250
251 return e;
252 };
253
254 _edata.reserve(_function_spaces.size());
255 for (auto& space : _function_spaces)
256 {
257 // Working map: [integral type, integral_idx, kernel_idx]->entities
258 std::map<std::tuple<IntegralType, int, int>,
259 std::variant<std::vector<std::int32_t>,
260 std::span<const std::int32_t>>>
261 vdata;
262
263 if (auto mesh0 = space->mesh(); mesh0 == _mesh)
264 {
265 for (auto& [key, integral] : _integrals)
266 vdata.insert({key, std::span(integral.entities)});
267 }
268 else
269 {
270 // Find correct entity map
271 const mesh::EntityMap& emap = get_entity_map(mesh0);
272
273 // Determine direction of the map. We need to map from
274 // `this->mesh()` to `mesh0`, so if `emap->sub_topology()` isn't
275 // the source topology, we need the inverse map
276 bool inverse = emap.sub_topology() == mesh0->topology();
277 for (auto& [key, itg] : _integrals)
278 {
279 auto [type, idx, kernel_idx] = key;
280 std::vector<std::int32_t> e;
281 if (type == IntegralType::cell)
282 e = emap.sub_topology_to_topology(itg.entities, inverse);
283 else if (type == IntegralType::exterior_facet
285 {
286 assert(mesh0);
287 int codim = tdim - mesh0->topology()->dim();
288 assert(codim >= 0);
289 auto c_to_f = topology.connectivity(tdim, tdim - 1);
290 assert(c_to_f);
291 e = compute_facet_domains(itg.entities, codim, c_to_f, emap,
292 inverse);
293 }
294 else
295 throw std::runtime_error("Integral type not supported.");
296
297 vdata.insert({key, std::move(e)});
298 }
299 }
300
301 _edata.push_back(std::move(vdata));
302 }
303
304 for (auto& [key, integral] : _integrals)
305 {
306 auto [type, idx, kernel_idx] = key;
307 for (int c : integral.coeffs)
308 {
309 if (auto mesh0 = coefficients.at(c)->function_space()->mesh();
310 mesh0 == _mesh)
311 {
312 _cdata.insert({{type, idx, c}, std::span(integral.entities)});
313 }
314 else
315 {
316 // Find correct entity map and determine direction of the map
317 const mesh::EntityMap& emap = get_entity_map(mesh0);
318 bool inverse = emap.sub_topology() == mesh0->topology();
319
320 std::vector<std::int32_t> e;
321 if (type == IntegralType::cell)
322 e = emap.sub_topology_to_topology(integral.entities, inverse);
323 else if (type == IntegralType::exterior_facet
325 {
326 assert(mesh0);
327 int codim = tdim - mesh0->topology()->dim();
328 auto c_to_f = topology.connectivity(tdim, tdim - 1);
329 assert(c_to_f);
330 e = compute_facet_domains(integral.entities, codim, c_to_f, emap,
331 inverse);
332 }
333 else
334 throw std::runtime_error("Integral type not supported.");
335 _cdata.insert({{type, idx, c}, std::move(e)});
336 }
337 }
338 }
339 }
340
346 Form(const Form& form) = delete;
347
353 Form(Form&& form) = default;
354
356 virtual ~Form() = default;
357
363 int rank() const { return _function_spaces.size(); }
364
367 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
368 {
369 return _mesh;
370 }
371
374 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>&
376 {
377 return _function_spaces;
378 }
379
388 std::function<void(scalar_type*, const scalar_type*, const scalar_type*,
389 const geometry_type*, const int*, const uint8_t*, void*)>
390 kernel(IntegralType type, int idx, int kernel_idx) const
391 {
392 auto it = _integrals.find({type, idx, kernel_idx});
393 if (it == _integrals.end())
394 throw std::runtime_error("Requested integral kernel not found.");
395 return it->second.kernel;
396 }
397
400 std::set<IntegralType> integral_types() const
401 {
402 std::set<IntegralType> types;
403 for (auto& [key, integral] : _integrals)
404 types.insert(std::get<0>(key));
405 return types;
406 }
407
421 std::vector<int> active_coeffs(IntegralType type, int idx) const
422 {
423 auto it = std::ranges::find_if(_integrals,
424 [type, idx](auto& x)
425 {
426 auto [t, idx_, kernel_idx] = x.first;
427 return t == type and idx_ == idx;
428 });
429 if (it == _integrals.end())
430 throw std::runtime_error("Could not find active coefficient list.");
431 return it->second.coeffs;
432 }
433
450 int num_integrals(IntegralType type, int kernel_idx) const
451 {
452 return std::ranges::count_if(_integrals,
453 [type, kernel_idx](auto& x)
454 {
455 auto [t, id, k_idx] = x.first;
456 return t == type and k_idx == kernel_idx;
457 });
458 }
459
494 std::span<const std::int32_t> domain(IntegralType type, int idx,
495 int kernel_idx) const
496 {
497 auto it = _integrals.find({type, idx, kernel_idx});
498 if (it == _integrals.end())
499 throw std::runtime_error("Requested domain not found.");
500 return it->second.entities;
501 }
502
538 std::span<const std::int32_t> domain_arg(IntegralType type, int rank, int idx,
539 int kernel_idx) const
540 {
541 auto it = _edata.at(rank).find({type, idx, kernel_idx});
542 if (it == _edata.at(rank).end())
543 throw std::runtime_error("Requested domain for argument not found.");
544
545 return std::visit([](const auto& v) -> std::span<const std::int32_t>
546 { return v; }, it->second);
547 }
548
563 std::span<const std::int32_t> domain_coeff(IntegralType type, int idx,
564 int c) const
565 {
566 auto it = _cdata.find({type, idx, c});
567 if (it == _cdata.end())
568 throw std::runtime_error("No domain for requested integral.");
569 return std::visit([](const auto& v) -> std::span<const std::int32_t>
570 { return v; }, it->second);
571 }
572
575 const std::vector<
576 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
578 {
579 return _coefficients;
580 }
581
585 bool needs_facet_permutations() const { return _needs_facet_permutations; }
586
593 std::vector<int> coefficient_offsets() const
594 {
595 std::vector<int> n{0};
596 n.reserve(_coefficients.size() + 1);
597 for (auto& c : _coefficients)
598 {
599 if (!c)
600 throw std::runtime_error("Not all form coefficients have been set.");
601 n.push_back(n.back() + c->function_space()->element()->space_dimension());
602 }
603 return n;
604 }
605
608 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
609 constants() const
610 {
611 return _constants;
612 }
613
614private:
615 // Function spaces (one for each argument)
616 std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>
617 _function_spaces;
618
619 // Integrals (integral type, idx, kernel_idx)
620 std::map<std::tuple<IntegralType, int, int>,
622 _integrals;
623
624 // The mesh
625 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
626
627 // Form coefficients
628 std::vector<std::shared_ptr<const Function<scalar_type, geometry_type>>>
629 _coefficients;
630
631 // Constants associated with the Form
632 std::vector<std::shared_ptr<const Constant<scalar_type>>> _constants;
633
634 // True if permutation data needs to be passed into these integrals
635 bool _needs_facet_permutations;
636
637 // Mapped domain index data for argument functions.
638 //
639 // Consider:
640 //
641 // entities = this->domain(type, idx, kernel_idx);
642 // entities0 = _edata[0][{type, idx, kernel_idx}];
643 //
644 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
645 // `_mesh`, and `entities0[i]` is the index of the same entity but in
646 // the mesh associated with the argument 0 (test function) space.
647 std::vector<std::map<
648 std::tuple<IntegralType, int, int>,
649 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>>
650 _edata;
651
652 // Mapped domain index data for coefficient functions.
653 //
654 // Consider:
655 //
656 // entities = this->domain(type, idx, kernel_idx);
657 // entities0 = _cdata[{type, idx, c}];
658 //
659 // where `c` is the coefficient index.
660 //
661 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
662 // `_mesh`, and `entities0[i]` is the index of the same entity but in
663 // the mesh associated with the coefficient Function.
664 std::map<
665 std::tuple<IntegralType, int, int>,
666 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>
667 _cdata;
668};
669} // namespace dolfinx::fem
Constant (in space) value which can be attached to a Form.
Definition Constant.h:22
U geometry_type
Geometry type.
Definition Form.h:124
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
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition Form.h:585
Form(Form &&form)=default
Move constructor.
int rank() const
Rank of the form.
Definition Form.h:363
Form(const Form &form)=delete
Copy constructor (deleted).
virtual ~Form()=default
Destructor.
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:156
const std::vector< std::shared_ptr< const Constant< scalar_type > > > & constants() const
Access constants.
Definition Form.h:609
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:593
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:390
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
const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > & function_spaces() const
Function spaces for all arguments.
Definition Form.h:375
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:538
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
T scalar_type
Scalar type.
Definition Form.h:121
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:34
Definition Function.h:47
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:929
int dim() const noexcept
Topological dimension of the mesh.
Definition Topology.cpp:867
Finite element method functionality.
Definition assemble_expression_impl.h:23
@ inverse
Inverse.
Definition FiniteElement.h:29
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
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:54
std::function< void(T *, const T *, const T *, const U *, const int *, const uint8_t *, void *)> kernel
The integration kernel.
Definition Form.h:78
integral_data(K &&kernel, V &&entities, W &&coeffs)
Create a structure to hold integral data.
Definition Form.h:69
std::vector< int > coeffs
Indices of coefficients (from the form) that are in this integral.
Definition Form.h:86
std::vector< std::int32_t > entities
The entities to integrate over for this integral. These are the entities in 'full' mesh.
Definition Form.h:82