DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
Form.h
1// Copyright (C) 2019-2025 Garth N. Wells and Chris Richardson
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 "FunctionSpace.h"
10#include "traits.h"
11#include <algorithm>
12#include <basix/mdspan.hpp>
13#include <concepts>
14#include <cstdint>
15#include <dolfinx/common/IndexMap.h>
16#include <dolfinx/common/types.h>
17#include <dolfinx/mesh/Mesh.h>
18#include <functional>
19#include <map>
20#include <memory>
21#include <optional>
22#include <span>
23#include <tuple>
24#include <utility>
25#include <vector>
26
27namespace dolfinx::fem
28{
29template <dolfinx::scalar T>
30class Constant;
31template <dolfinx::scalar T, std::floating_point U>
32class Function;
33
35enum class IntegralType : std::int8_t
36{
37 cell = 0,
40 vertex = 3
41};
42
43namespace impl
44{
52std::vector<std::int32_t> compute_domain(
53 auto entities, std::span<const std::int32_t> entity_map,
54 std::optional<int> codim = std::nullopt,
55 std::optional<
56 std::reference_wrapper<const graph::AdjacencyList<std::int32_t>>>
57 c_to_f
58 = std::nullopt)
59{
60 static_assert(entities.rank() == 1 or entities.rank() == 2);
61 std::vector<std::int32_t> mapped_entities;
62 mapped_entities.reserve(entities.size());
63 if constexpr (entities.rank() == 1)
64 {
65 std::span ents(entities.data_handle(), entities.size());
66 std::ranges::transform(ents, std::back_inserter(mapped_entities),
67 [&entity_map](auto e) { return entity_map[e]; });
68 }
69 else if (entities.rank() == 2)
70 {
71 assert(codim.value() >= 0);
72 if (codim.value() == 0)
73 {
74 for (std::size_t i = 0; i < entities.extent(0); ++i)
75 {
76 // Add cell and the local facet index
77 mapped_entities.insert(mapped_entities.end(),
78 {entity_map[entities(i, 0)], entities(i, 1)});
79 }
80 }
81 else if (codim.value() == 1)
82 {
83 // In this case, the entity maps take facets in (`_mesh`) to cells
84 // in `mesh`, so we need to get the facet number from the (cell,
85 // local_facet pair) first.
86 for (std::size_t i = 0; i < entities.extent(0); ++i)
87 {
88 // Get the facet index, and add cell and the local facet index
89 std::int32_t facet
90 = c_to_f->get().links(entities(i, 0))[entities(i, 1)];
91 mapped_entities.insert(mapped_entities.end(),
92 {entity_map[facet], entities(i, 1)});
93 }
94 }
95 else
96 throw std::runtime_error("Codimension > 1 not supported.");
97 }
98 else
99 throw std::runtime_error("Integral type not supported.");
100
101 return mapped_entities;
102}
103} // namespace impl
104
108template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
110{
116 template <typename K, typename V, typename W>
117 requires std::is_convertible_v<
118 std::remove_cvref_t<K>,
119 std::function<void(T*, const T*, const T*, const U*,
120 const int*, const uint8_t*, void*)>>
121 and std::is_convertible_v<std::remove_cvref_t<V>,
122 std::vector<std::int32_t>>
123 and std::is_convertible_v<std::remove_cvref_t<W>,
124 std::vector<int>>
126 : kernel(std::forward<K>(kernel)), entities(std::forward<V>(entities)),
127 coeffs(std::forward<W>(coeffs))
128 {
129 }
130
132 std::function<void(T*, const T*, const T*, const U*, const int*,
133 const uint8_t*, void*)>
135
138 std::vector<std::int32_t> entities;
139
142 std::vector<int> coeffs;
143};
144
173template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
174class Form
175{
176public:
178 using scalar_type = T;
179
181 using geometry_type = U;
182
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::map<std::shared_ptr<const mesh::Mesh<geometry_type>>,
225 std::span<const std::int32_t>>& entity_maps)
226 : _function_spaces(V), _integrals(std::forward<X>(integrals)),
227 _mesh(mesh), _coefficients(coefficients), _constants(constants),
228 _needs_facet_permutations(needs_facet_permutations)
229 {
230 if (!_mesh)
231 throw std::runtime_error("Form Mesh is null.");
232
233 // Check consistency of mesh(es)
234 {
235 // Integration domain mesh is passed, so check that it is (1)
236 // common for spaces and coefficients (2) or an entity_map is
237 // available
238 for (auto& space : _function_spaces)
239 {
240 if (auto mesh0 = space->mesh();
241 mesh0 != _mesh and !entity_maps.contains(mesh0))
242 {
243 throw std::runtime_error(
244 "Incompatible mesh. argument entity_maps must be provided.");
245 }
246 }
247 for (auto& c : coefficients)
248 {
249 if (auto mesh0 = c->function_space()->mesh();
250 mesh0 != _mesh and !entity_maps.contains(mesh0))
251 {
252 throw std::runtime_error(
253 "Incompatible mesh. coefficient entity_maps must be provided.");
254 }
255 }
256 }
257
258 for (auto& space : _function_spaces)
259 {
260 // Working map: [integral type, domain ID, kernel_idx]->entities
261 std::map<std::tuple<IntegralType, int, int>,
262 std::variant<std::vector<std::int32_t>,
263 std::span<const std::int32_t>>>
264 vdata;
265
266 if (auto mesh0 = space->mesh(); mesh0 == _mesh)
267 {
268 for (auto& [key, integral] : _integrals)
269 vdata.insert({key, std::span(integral.entities)});
270 }
271 else
272 {
273 auto it = entity_maps.find(mesh0);
274 assert(it != entity_maps.end());
275 std::span<const std::int32_t> entity_map = it->second;
276 for (auto& [key, itg] : _integrals)
277 {
278 auto [type, id, kernel_idx] = key;
279 std::vector<std::int32_t> e;
280 if (type == IntegralType::cell)
281 {
282 e = impl::compute_domain(
283 md::mdspan(itg.entities.data(), itg.entities.size()),
284 entity_map);
285 }
286 else if (type == IntegralType::exterior_facet
288 {
289 const mesh::Topology topology = *_mesh->topology();
290 int tdim = topology.dim();
291 assert(mesh0);
292 int codim = tdim - mesh0->topology()->dim();
293 auto c_to_f = topology.connectivity(tdim, tdim - 1);
294 assert(c_to_f);
295 e = impl::compute_domain(
296 md::mdspan<const std::int32_t,
297 md::extents<std::size_t, md::dynamic_extent, 2>>(
298 itg.entities.data(), itg.entities.size() / 2, 2),
299 entity_map, codim, *c_to_f);
300 }
301 else
302 throw std::runtime_error("Integral type not supported.");
303 vdata.insert({key, std::move(e)});
304 }
305 }
306
307 _edata.push_back(vdata);
308 }
309
310 for (auto& [key, integral] : _integrals)
311 {
312 auto [type, id, kernel_idx] = key;
313 for (int c : integral.coeffs)
314 {
315 if (auto mesh0 = coefficients.at(c)->function_space()->mesh();
316 mesh0 == _mesh)
317 {
318 _cdata.insert({{type, id, c}, std::span(integral.entities)});
319 }
320 else
321 {
322 auto it = entity_maps.find(mesh0);
323 assert(it != entity_maps.end());
324 std::span<const std::int32_t> entity_map = it->second;
325 std::vector<std::int32_t> e;
326 if (type == IntegralType::cell)
327 {
328 e = impl::compute_domain(
329 md::mdspan(integral.entities.data(), integral.entities.size()),
330 entity_map);
331 }
332 else if (type == IntegralType::exterior_facet
334 {
335 const mesh::Topology topology = *_mesh->topology();
336 int tdim = topology.dim();
337 assert(mesh0);
338 int codim = tdim - mesh0->topology()->dim();
339 auto c_to_f = topology.connectivity(tdim, tdim - 1);
340 assert(c_to_f);
341 e = impl::compute_domain(
342 md::mdspan<const std::int32_t,
343 md::extents<std::size_t, md::dynamic_extent, 2>>(
344 integral.entities.data(), integral.entities.size() / 2, 2),
345 entity_map, codim, *c_to_f);
346 }
347 else
348 throw std::runtime_error("Integral type not supported.");
349 _cdata.insert({{type, id, c}, std::move(e)});
350 }
351 }
352 }
353 }
354
356 Form(const Form& form) = delete;
357
359 Form(Form&& form) = default;
360
362 virtual ~Form() = default;
363
369 int rank() const { return _function_spaces.size(); }
370
373 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
374 {
375 return _mesh;
376 }
377
380 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>&
382 {
383 return _function_spaces;
384 }
385
392 std::function<void(scalar_type*, const scalar_type*, const scalar_type*,
393 const geometry_type*, const int*, const uint8_t*, void*)>
394 kernel(IntegralType type, int id, int kernel_idx) const
395 {
396 auto it = _integrals.find({type, id, kernel_idx});
397 if (it == _integrals.end())
398 throw std::runtime_error("Requested integral kernel not found.");
399 return it->second.kernel;
400 }
401
404 std::set<IntegralType> integral_types() const
405 {
406 std::set<IntegralType> set;
407 std::ranges::for_each(_integrals, [&set](auto& x)
408 { set.insert(std::get<0>(x.first)); });
409 return set;
410 }
411
422 std::vector<int> active_coeffs(IntegralType type, int id) const
423 {
424 auto it = std::ranges::find_if(_integrals,
425 [type, id](auto& x)
426 {
427 auto [t, id_, kernel_idx] = x.first;
428 return t == type and id_ == id;
429 });
430 if (it == _integrals.end())
431 throw std::runtime_error("Could not find active coefficient list.");
432 return it->second.coeffs;
433 }
434
444 std::vector<int> integral_ids(IntegralType type) const
445 {
446 std::vector<int> ids;
447 for (auto& [key, integral] : _integrals)
448 {
449 auto [t, id, kernel_idx] = key;
450 if (t == type)
451 ids.push_back(id);
452 }
453
454 // IDs may be repeated in mixed-topology meshes, so remove
455 // duplicates
456 std::sort(ids.begin(), ids.end());
457 auto it = std::unique(ids.begin(), ids.end());
458 ids.erase(it, ids.end());
459 return ids;
460 }
461
486 std::span<const std::int32_t> domain(IntegralType type, int id,
487 int kernel_idx) const
488 {
489 auto it = _integrals.find({type, id, kernel_idx});
490 if (it == _integrals.end())
491 throw std::runtime_error("Requested domain not found.");
492 return it->second.entities;
493 }
494
526 std::span<const std::int32_t> domain_arg(IntegralType type, int rank, int id,
527 int kernel_idx) const
528 {
529 auto it = _edata.at(rank).find({type, id, kernel_idx});
530 if (it == _edata.at(rank).end())
531 throw std::runtime_error("Requested domain for argument not found.");
532 try
533 {
534 return std::get<std::span<const std::int32_t>>(it->second);
535 }
536 catch (std::bad_variant_access& e)
537 {
538 return std::get<std::vector<std::int32_t>>(it->second);
539 }
540 }
541
556 std::span<const std::int32_t> domain_coeff(IntegralType type, int id,
557 int c) const
558 {
559 auto it = _cdata.find({type, id, c});
560 if (it == _cdata.end())
561 throw std::runtime_error("No domain for requested integral.");
562 try
563 {
564 return std::get<std::span<const std::int32_t>>(it->second);
565 }
566 catch (std::bad_variant_access& e)
567 {
568 return std::get<std::vector<std::int32_t>>(it->second);
569 }
570 }
571
573 const std::vector<
574 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
576 {
577 return _coefficients;
578 }
579
583 bool needs_facet_permutations() const { return _needs_facet_permutations; }
584
589 std::vector<int> coefficient_offsets() const
590 {
591 std::vector<int> n = {0};
592 for (auto& c : _coefficients)
593 {
594 if (!c)
595 throw std::runtime_error("Not all form coefficients have been set.");
596 n.push_back(n.back() + c->function_space()->element()->space_dimension());
597 }
598 return n;
599 }
600
602 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
603 constants() const
604 {
605 return _constants;
606 }
607
608private:
609 // Function spaces (one for each argument)
610 std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>
611 _function_spaces;
612
613 // Integrals (integral type, id, celltype)
614 std::map<std::tuple<IntegralType, int, int>,
616 _integrals;
617
618 // The mesh
619 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
620
621 // Form coefficients
622 std::vector<std::shared_ptr<const Function<scalar_type, geometry_type>>>
623 _coefficients;
624
625 // Constants associated with the Form
626 std::vector<std::shared_ptr<const Constant<scalar_type>>> _constants;
627
628 // True if permutation data needs to be passed into these integrals
629 bool _needs_facet_permutations;
630
631 // Mapped domain index data for argument functions.
632 //
633 // Consider:
634 //
635 // entities = this->domain(IntegralType, integral(id), kernel_idx];
636 // entities0 = _edata[0][IntegralType, integral(id), coefficient_index];
637 //
638 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
639 // `_mesh`, and `entities0[i]` is the index of the same entity but in
640 // the mesh associated with the argument 0 (test function) space.
641 std::vector<std::map<
642 std::tuple<IntegralType, int, int>,
643 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>>
644 _edata;
645
646 // Mapped domain index data for coefficient functions.
647 //
648 // Consider:
649 //
650 // entities = this->domain(IntegralType, integral(id), kernel_idx];
651 // entities0 = _cdata[IntegralType, integral(id), coefficient_index];
652 //
653 // Then `entities[i]` is a mesh entity index (e.g., cell index) in
654 // `_mesh`, and `entities0[i]` is the index of the same entity but in
655 // the mesh associated with the coefficient Function.
656 std::map<
657 std::tuple<IntegralType, int, int>,
658 std::variant<std::vector<std::int32_t>, std::span<const std::int32_t>>>
659 _cdata;
660};
661} // namespace dolfinx::fem
Constant value which can be attached to a Form.
Definition Constant.h:23
U geometry_type
Geometry type.
Definition Form.h:181
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Access coefficients.
Definition Form.h:575
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition Form.h:583
Form(Form &&form)=default
Move constructor.
int rank() const
Rank of the form.
Definition Form.h:369
Form(const Form &form)=delete
Copy constructor.
virtual ~Form()=default
Destructor.
std::span< const std::int32_t > domain_arg(IntegralType type, int rank, int id, int kernel_idx) const
Argument function mesh integration entity indices.
Definition Form.h:526
const std::vector< std::shared_ptr< const Constant< scalar_type > > > & constants() const
Access constants.
Definition Form.h:603
std::function< void(scalar_type *, const scalar_type *, const scalar_type *, const geometry_type *, const int *, const uint8_t *, void *)> kernel(IntegralType type, int id, int kernel_idx) const
Get the kernel function for an integral.
Definition Form.h:394
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:589
std::vector< int > active_coeffs(IntegralType type, int id) const
Indices of coefficients that are active for a given integral (kernel).
Definition Form.h:422
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
Common mesh for the form (the 'integration domain').
Definition Form.h:373
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::map< std::shared_ptr< const mesh::Mesh< geometry_type > >, std::span< const std::int32_t > > &entity_maps)
Create a finite element form.
Definition Form.h:215
std::span< const std::int32_t > domain_coeff(IntegralType type, int id, int c) const
Coefficient function mesh integration entity indices.
Definition Form.h:556
std::vector< int > integral_ids(IntegralType type) const
Get the IDs for integrals (kernels) for given integral domain type.
Definition Form.h:444
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:404
const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > & function_spaces() const
Function spaces for all arguments.
Definition Form.h:381
T scalar_type
Scalar type.
Definition Form.h:178
std::span< const std::int32_t > domain(IntegralType type, int id, int kernel_idx) const
Mesh entity indices to integrate over for a given integral (kernel).
Definition Form.h:486
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:34
Definition Function.h:47
Definition AdjacencyList.h:27
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:46
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:832
int dim() const noexcept
Topological dimension of the mesh.
Definition Topology.cpp:785
Finite element method functionality.
Definition assemble_expression_impl.h:23
IntegralType
Type of integral.
Definition Form.h:36
@ vertex
Vertex.
Definition Form.h:40
@ interior_facet
Interior facet.
Definition Form.h:39
@ cell
Cell.
Definition Form.h:37
@ exterior_facet
Exterior facet.
Definition Form.h:38
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:110
std::function< void(T *, const T *, const T *, const U *, const int *, const uint8_t *, void *)> kernel
The integration kernel.
Definition Form.h:134
integral_data(K &&kernel, V &&entities, W &&coeffs)
Create a structure to hold integral data.
Definition Form.h:125
std::vector< int > coeffs
Indices of coefficients (from the form) that are in this integral.
Definition Form.h:142
std::vector< std::int32_t > entities
The entities to integrate over for this integral. These are the entities in 'full' mesh.
Definition Form.h:138