DOLFINx 0.9.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
Form.h
1// Copyright (C) 2019-2023 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 <array>
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 <memory>
20#include <span>
21#include <string>
22#include <tuple>
23#include <vector>
24
25namespace dolfinx::fem
26{
27
28template <dolfinx::scalar T>
30template <dolfinx::scalar T, std::floating_point U>
31class Function;
32
34enum class IntegralType : std::int8_t
35{
36 cell = 0,
37 exterior_facet = 1,
38 interior_facet = 2,
39 vertex = 3
40};
41
44template <dolfinx::scalar T, std::floating_point U = scalar_value_type_t<T>>
46{
53 template <typename K, typename V, typename W>
54 requires std::is_convertible_v<
55 std::remove_cvref_t<K>,
56 std::function<void(T*, const T*, const T*, const U*,
57 const int*, const uint8_t*)>>
58 and std::is_convertible_v<std::remove_cvref_t<V>,
59 std::vector<std::int32_t>>
60 and std::is_convertible_v<std::remove_cvref_t<W>,
61 std::vector<int>>
62 integral_data(int id, K&& kernel, V&& entities, W&& coeffs)
63 : id(id), kernel(std::forward<K>(kernel)),
64 entities(std::forward<V>(entities)), coeffs(std::forward<W>(coeffs))
65 {
66 }
67
78 template <typename K, typename W>
79 requires std::is_convertible_v<
80 std::remove_cvref_t<K>,
81 std::function<void(T*, const T*, const T*, const U*,
82 const int*, const uint8_t*)>>
83 and std::is_convertible_v<std::remove_cvref_t<W>,
84 std::vector<int>>
85 integral_data(int id, K&& kernel, std::span<const std::int32_t> entities,
86 W&& coeffs)
87 : id(id), kernel(std::forward<K>(kernel)),
88 entities(entities.begin(), entities.end()),
89 coeffs(std::forward<W>(coeffs))
90 {
91 }
92
94 int id;
95
97 std::function<void(T*, const T*, const T*, const U*, const int*,
98 const uint8_t*)>
100
102 std::vector<std::int32_t> entities;
103
106 std::vector<int> coeffs;
107};
108
136template <dolfinx::scalar T,
137 std::floating_point U = dolfinx::scalar_value_type_t<T>>
138class Form
139{
140public:
142 using scalar_type = T;
143
145 using geometry_type = U;
146
175 template <typename X>
176 requires std::is_convertible_v<
177 std::remove_cvref_t<X>,
178 std::map<IntegralType, std::vector<integral_data<
181 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>& V,
182 X&& integrals,
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::map<std::shared_ptr<const mesh::Mesh<geometry_type>>,
190 std::span<const std::int32_t>>& entity_maps,
191 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh = nullptr)
192 : _function_spaces(V), _coefficients(coefficients), _constants(constants),
193 _mesh(mesh), _needs_facet_permutations(needs_facet_permutations)
194 {
195 // Extract _mesh from FunctionSpace, and check they are the same
196 if (!_mesh and !V.empty())
197 _mesh = V[0]->mesh();
198 for (auto& space : V)
199 {
200 if (_mesh != space->mesh()
201 and entity_maps.find(space->mesh()) == entity_maps.end())
202 {
203 throw std::runtime_error(
204 "Incompatible mesh. entity_maps must be provided.");
205 }
206 }
207 if (!_mesh)
208 throw std::runtime_error("No mesh could be associated with the Form.");
209
210 // Store kernels, looping over integrals by domain type (dimension)
211 for (auto&& [domain_type, data] : integrals)
212 {
213 if (!std::ranges::is_sorted(data,
214 [](auto& a, auto& b) { return a.id < b.id; }))
215 {
216 throw std::runtime_error("Integral IDs not sorted");
217 }
218
219 std::vector<integral_data<scalar_type, geometry_type>>& itg
220 = _integrals[static_cast<std::size_t>(domain_type)];
221 for (auto&& [id, kern, e, c] : data)
222 itg.emplace_back(id, kern, std::move(e), std::move(c));
223 }
224
225 // Store entity maps
226 for (auto [msh, map] : entity_maps)
227 _entity_maps.insert({msh, std::vector(map.begin(), map.end())});
228 }
229
231 Form(const Form& form) = delete;
232
234 Form(Form&& form) = default;
235
237 virtual ~Form() = default;
238
244 int rank() const { return _function_spaces.size(); }
245
248 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
249 {
250 return _mesh;
251 }
252
255 const std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>&
257 {
258 return _function_spaces;
259 }
260
266 std::function<void(scalar_type*, const scalar_type*, const scalar_type*,
267 const geometry_type*, const int*, const uint8_t*)>
268 kernel(IntegralType type, int i) const
269 {
270 const auto& integrals = _integrals[static_cast<std::size_t>(type)];
271 auto it = std::ranges::lower_bound(integrals, i, std::less<>{},
272 [](const auto& a) { return a.id; });
273 if (it != integrals.end() and it->id == i)
274 return it->kernel;
275 else
276 throw std::runtime_error("No kernel for requested domain index.");
277 }
278
281 std::set<IntegralType> integral_types() const
282 {
283 std::set<IntegralType> set;
284 for (std::size_t i = 0; i < _integrals.size(); ++i)
285 {
286 if (!_integrals[i].empty())
287 set.insert(static_cast<IntegralType>(i));
288 }
289
290 return set;
291 }
292
297 {
298 return _integrals[static_cast<std::size_t>(type)].size();
299 }
300
311 std::vector<int> active_coeffs(IntegralType type, std::size_t i) const
312 {
313 assert(i < _integrals[static_cast<std::size_t>(type)].size());
314 return _integrals[static_cast<std::size_t>(type)][i].coeffs;
315 }
316
324 std::vector<int> integral_ids(IntegralType type) const
325 {
326 std::vector<int> ids;
327 const auto& integrals = _integrals[static_cast<std::size_t>(type)];
328 std::ranges::transform(integrals, std::back_inserter(ids),
329 [](auto& integral) { return integral.id; });
330 return ids;
331 }
332
350 std::span<const std::int32_t> domain(IntegralType type, int i) const
351 {
352 const auto& integrals = _integrals[static_cast<std::size_t>(type)];
353 auto it = std::ranges::lower_bound(integrals, i, std::less<>{},
354 [](const auto& a) { return a.id; });
355 if (it != integrals.end() and it->id == i)
356 return it->entities;
357 else
358 throw std::runtime_error("No mesh entities for requested domain index.");
359 }
360
369 std::vector<std::int32_t> domain(IntegralType type, int i,
370 const mesh::Mesh<geometry_type>& mesh) const
371 {
372 // Hack to avoid passing shared pointer to this function
373 std::shared_ptr<const mesh::Mesh<geometry_type>> msh_ptr(
374 &mesh, [](const mesh::Mesh<geometry_type>*) {});
375
376 std::span<const std::int32_t> entities = domain(type, i);
377 if (msh_ptr == _mesh)
378 return std::vector(entities.begin(), entities.end());
379 else
380 {
381 std::span<const std::int32_t> entity_map = _entity_maps.at(msh_ptr);
382 std::vector<std::int32_t> mapped_entities;
383 mapped_entities.reserve(entities.size());
384 switch (type)
385 {
387 {
388 std::ranges::transform(entities, std::back_inserter(mapped_entities),
389 [&entity_map](auto e) { return entity_map[e]; });
390 break;
391 }
393 {
394 // Get the codimension of the mesh
395 const int tdim = _mesh->topology()->dim();
396 const int codim = tdim - mesh.topology()->dim();
397 assert(codim >= 0);
398 if (codim == 0)
399 {
400 for (std::size_t i = 0; i < entities.size(); i += 2)
401 {
402 // Add cell and the local facet index
403 mapped_entities.insert(mapped_entities.end(),
404 {entity_map[entities[i]], entities[i + 1]});
405 }
406 }
407 else if (codim == 1)
408 {
409 // In this case, the entity maps take facets in (`_mesh`) to cells in
410 // `mesh`, so we need to get the facet number from the (cell,
411 // local_facet pair) first.
412 auto c_to_f = _mesh->topology()->connectivity(tdim, tdim - 1);
413 assert(c_to_f);
414 for (std::size_t i = 0; i < entities.size(); i += 2)
415 {
416 // Get the facet index
417 const std::int32_t facet
418 = c_to_f->links(entities[i])[entities[i + 1]];
419 // Add cell and the local facet index
420 mapped_entities.insert(mapped_entities.end(),
421 {entity_map[facet], entities[i + 1]});
422 }
423 }
424 else
425 throw std::runtime_error("Codimension > 1 not supported.");
426
427 break;
428 }
430 {
431 // Get the codimension of the mesh
432 const int tdim = _mesh->topology()->dim();
433 const int codim = tdim - mesh.topology()->dim();
434 assert(codim >= 0);
435 if (codim == 0)
436 {
437 for (std::size_t i = 0; i < entities.size(); i += 2)
438 {
439 // Add cell and the local facet index
440 mapped_entities.insert(mapped_entities.end(),
441 {entity_map[entities[i]], entities[i + 1]});
442 }
443 }
444 else if (codim == 1)
445 {
446 // In this case, the entity maps take facets in (`_mesh`) to cells in
447 // `mesh`, so we need to get the facet number from the (cell,
448 // local_facet pair) first.
449 auto c_to_f = _mesh->topology()->connectivity(tdim, tdim - 1);
450 assert(c_to_f);
451 for (std::size_t i = 0; i < entities.size(); i += 2)
452 {
453 // Get the facet index
454 const std::int32_t facet
455 = c_to_f->links(entities[i])[entities[i + 1]];
456 // Add cell and the local facet index
457 mapped_entities.insert(mapped_entities.end(),
458 {entity_map[facet], entities[i + 1]});
459 }
460 }
461 break;
462 }
463 default:
464 throw std::runtime_error("Integral type not supported.");
465 }
466
467 return mapped_entities;
468 }
469 }
470
472 const std::vector<
473 std::shared_ptr<const Function<scalar_type, geometry_type>>>&
475 {
476 return _coefficients;
477 }
478
482 bool needs_facet_permutations() const { return _needs_facet_permutations; }
483
488 std::vector<int> coefficient_offsets() const
489 {
490 std::vector<int> n = {0};
491 for (auto& c : _coefficients)
492 {
493 if (!c)
494 throw std::runtime_error("Not all form coefficients have been set.");
495 n.push_back(n.back() + c->function_space()->element()->space_dimension());
496 }
497 return n;
498 }
499
501 const std::vector<std::shared_ptr<const Constant<scalar_type>>>&
502 constants() const
503 {
504 return _constants;
505 }
506
507private:
508 // Function spaces (one for each argument)
509 std::vector<std::shared_ptr<const FunctionSpace<geometry_type>>>
510 _function_spaces;
511
512 // Integrals. Array index is
513 // static_cast<std::size_t(IntegralType::foo)
514 std::array<std::vector<integral_data<scalar_type, geometry_type>>, 4>
515 _integrals;
516
517 // Form coefficients
518 std::vector<std::shared_ptr<const Function<scalar_type, geometry_type>>>
519 _coefficients;
520
521 // Constants associated with the Form
522 std::vector<std::shared_ptr<const Constant<scalar_type>>> _constants;
523
524 // The mesh
525 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
526
527 // True if permutation data needs to be passed into these integrals
528 bool _needs_facet_permutations;
529
530 // Entity maps (see Form documentation)
531 std::map<std::shared_ptr<const mesh::Mesh<geometry_type>>,
532 std::vector<std::int32_t>>
533 _entity_maps;
534}; // namespace dolfinx::fem
535} // namespace dolfinx::fem
Constant value which can be attached to a Form.
Definition Form.h:29
A representation of finite element variational forms.
Definition Form.h:139
U geometry_type
Geometry type.
Definition Form.h:145
const std::vector< std::shared_ptr< const Function< scalar_type, geometry_type > > > & coefficients() const
Access coefficients.
Definition Form.h:474
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition Form.h:482
Form(Form &&form)=default
Move constructor.
int rank() const
Rank of the form.
Definition Form.h:244
Form(const Form &form)=delete
Copy constructor.
virtual ~Form()=default
Destructor.
const std::vector< std::shared_ptr< const Constant< scalar_type > > > & constants() const
Access constants.
Definition Form.h:502
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell.
Definition Form.h:488
Form(const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > &V, X &&integrals, 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, std::shared_ptr< const mesh::Mesh< geometry_type > > mesh=nullptr)
Create a finite element form.
Definition Form.h:180
std::span< const std::int32_t > domain(IntegralType type, int i) const
Get the list of mesh entity indices for the ith integral (kernel) of a given type.
Definition Form.h:350
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
Extract common mesh for the form.
Definition Form.h:248
std::vector< std::int32_t > domain(IntegralType type, int i, const mesh::Mesh< geometry_type > &mesh) const
Compute the list of entity indices in mesh for the ith integral (kernel) of a given type (i....
Definition Form.h:369
std::vector< int > integral_ids(IntegralType type) const
Get the IDs for integrals (kernels) for given integral type.
Definition Form.h:324
std::vector< int > active_coeffs(IntegralType type, std::size_t i) const
Indices of coefficients that are active for a given integral (kernel).
Definition Form.h:311
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:281
const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > & function_spaces() const
Function spaces for all arguments.
Definition Form.h:256
std::function< void(scalar_type *, const scalar_type *, const scalar_type *, const geometry_type *, const int *, const uint8_t *)> kernel(IntegralType type, int i) const
Get the kernel function for integral i on given domain type.
Definition Form.h:268
int num_integrals(IntegralType type) const
Number of integrals on given domain type.
Definition Form.h:296
T scalar_type
Scalar type.
Definition Form.h:142
This class represents a finite element function space defined by a mesh, a finite element,...
Definition vtk_utils.h:32
Definition XDMFFile.h:29
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Definition types.h:20
Finite element method functionality.
Definition assemble_matrix_impl.h:26
IntegralType
Type of integral.
Definition Form.h:35
@ interior_facet
Interior facet.
@ exterior_facet
Exterior facet.
Represents integral data, containing the integral ID, the kernel, and a list of entities to integrate...
Definition Form.h:46
int id
Integral ID.
Definition Form.h:94
integral_data(int id, K &&kernel, V &&entities, W &&coeffs)
Create a structure to hold integral data.
Definition Form.h:62
std::function< void(T *, const T *, const T *, const U *, const int *, const uint8_t *)> kernel
The integration kernel.
Definition Form.h:99
std::vector< int > coeffs
Indices of coefficients (from the form) that are in this integral.
Definition Form.h:106
std::vector< std::int32_t > entities
The entities to integrate over.
Definition Form.h:102
integral_data(int id, K &&kernel, std::span< const std::int32_t > entities, W &&coeffs)
Create a structure to hold integral data.
Definition Form.h:85