Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/d8/d18/Form_8h_source.html
DOLFINx 0.6.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
Form.h
1// Copyright (C) 2019-2020 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 <algorithm>
11#include <array>
12#include <dolfinx/common/IndexMap.h>
13#include <dolfinx/mesh/Mesh.h>
14#include <dolfinx/mesh/MeshTags.h>
15#include <functional>
16#include <memory>
17#include <span>
18#include <string>
19#include <tuple>
20#include <vector>
21
22namespace dolfinx::fem
23{
24
25template <typename T>
26class Constant;
27template <typename T>
28class Function;
29
31enum class IntegralType : std::int8_t
32{
33 cell = 0,
34 exterior_facet = 1,
35 interior_facet = 2,
36 vertex = 3
37};
38
62template <typename T>
63class Form
64{
65 template <typename X, typename = void>
66 struct scalar_value_type
67 {
68 typedef X value_type;
69 };
70 template <typename X>
71 struct scalar_value_type<X, std::void_t<typename X::value_type>>
72 {
73 typedef typename X::value_type value_type;
74 };
75 using scalar_value_type_t = typename scalar_value_type<T>::value_type;
76
77public:
94 Form(const std::vector<std::shared_ptr<const FunctionSpace>>& function_spaces,
95 const std::map<
97 std::pair<
98 std::vector<std::pair<
99 int, std::function<void(T*, const T*, const T*,
100 const scalar_value_type_t*,
101 const int*, const std::uint8_t*)>>>,
102 const mesh::MeshTags<int>*>>& integrals,
103 const std::vector<std::shared_ptr<const Function<T>>>& coefficients,
104 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
106 std::shared_ptr<const mesh::Mesh> mesh = nullptr)
107 : _function_spaces(function_spaces), _coefficients(coefficients),
108 _constants(constants), _mesh(mesh),
109 _needs_facet_permutations(needs_facet_permutations)
110 {
111 // Extract _mesh from FunctionSpace, and check they are the same
112 if (!_mesh and !function_spaces.empty())
113 _mesh = function_spaces[0]->mesh();
114 for (const auto& V : function_spaces)
115 {
116 if (_mesh != V->mesh())
117 throw std::runtime_error("Incompatible mesh");
118 }
119 if (!_mesh)
120 throw std::runtime_error("No mesh could be associated with the Form.");
121
122 // Store kernels, looping over integrals by domain type (dimension)
123 for (auto& integral_type : integrals)
124 {
125 const IntegralType type = integral_type.first;
126 // Loop over integrals kernels and set domains
127 switch (type)
128 {
130 for (auto& integral : integral_type.second.first)
131 _cell_integrals.insert({integral.first, {integral.second, {}}});
132 break;
134 for (auto& integral : integral_type.second.first)
135 {
136 _exterior_facet_integrals.insert(
137 {integral.first, {integral.second, {}}});
138 }
139 break;
141 for (auto& integral : integral_type.second.first)
142 {
143 _interior_facet_integrals.insert(
144 {integral.first, {integral.second, {}}});
145 }
146 break;
147 }
148
149 if (integral_type.second.second)
150 {
151 assert(_mesh == integral_type.second.second->mesh());
152 set_domains(type, *integral_type.second.second);
153 }
154 }
155
156 // FIXME: do this neatly via a static function
157 // Set markers for default integrals
158 set_default_domains(*_mesh);
159 }
160
162 Form(const Form& form) = delete;
163
165 Form(Form&& form) = default;
166
168 virtual ~Form() = default;
169
173 int rank() const { return _function_spaces.size(); }
174
177 std::shared_ptr<const mesh::Mesh> mesh() const { return _mesh; }
178
181 const std::vector<std::shared_ptr<const FunctionSpace>>&
183 {
184 return _function_spaces;
185 }
186
192 const std::function<void(T*, const T*, const T*, const scalar_value_type_t*,
193 const int*, const std::uint8_t*)>&
194 kernel(IntegralType type, int i) const
195 {
196 switch (type)
197 {
199 return get_kernel_from_integrals(_cell_integrals, i);
201 return get_kernel_from_integrals(_exterior_facet_integrals, i);
203 return get_kernel_from_integrals(_interior_facet_integrals, i);
204 default:
205 throw std::runtime_error(
206 "Cannot access kernel. Integral type not supported.");
207 }
208 }
209
212 std::set<IntegralType> integral_types() const
213 {
214 std::set<IntegralType> set;
215 if (!_cell_integrals.empty())
216 set.insert(IntegralType::cell);
217 if (!_exterior_facet_integrals.empty())
219 if (!_interior_facet_integrals.empty())
221
222 return set;
223 }
224
229 {
230 switch (type)
231 {
233 return _cell_integrals.size();
235 return _exterior_facet_integrals.size();
237 return _interior_facet_integrals.size();
238 default:
239 throw std::runtime_error("Integral type not supported.");
240 }
241 }
242
249 std::vector<int> integral_ids(IntegralType type) const
250 {
251 std::vector<int> ids;
252 switch (type)
253 {
255 std::transform(_cell_integrals.cbegin(), _cell_integrals.cend(),
256 std::back_inserter(ids),
257 [](auto& integral) { return integral.first; });
258 break;
260 std::transform(_exterior_facet_integrals.cbegin(),
261 _exterior_facet_integrals.cend(), std::back_inserter(ids),
262 [](auto& integral) { return integral.first; });
263 break;
265 std::transform(_interior_facet_integrals.cbegin(),
266 _interior_facet_integrals.cend(), std::back_inserter(ids),
267 [](auto& integral) { return integral.first; });
268 break;
269 default:
270 throw std::runtime_error(
271 "Cannot return IDs. Integral type not supported.");
272 }
273
274 return ids;
275 }
276
281 const std::vector<std::int32_t>& cell_domains(int i) const
282 {
283 auto it = _cell_integrals.find(i);
284 if (it == _cell_integrals.end())
285 throw std::runtime_error("No mesh entities for requested domain index.");
286 return it->second.second;
287 }
288
294 const std::vector<std::int32_t>& exterior_facet_domains(int i) const
295 {
296 auto it = _exterior_facet_integrals.find(i);
297 if (it == _exterior_facet_integrals.end())
298 throw std::runtime_error("No mesh entities for requested domain index.");
299 return it->second.second;
300 }
301
309 const std::vector<std::int32_t>& interior_facet_domains(int i) const
310 {
311 auto it = _interior_facet_integrals.find(i);
312 if (it == _interior_facet_integrals.end())
313 throw std::runtime_error("No mesh entities for requested domain index.");
314 return it->second.second;
315 }
316
318 const std::vector<std::shared_ptr<const Function<T>>>& coefficients() const
319 {
320 return _coefficients;
321 }
322
326 bool needs_facet_permutations() const { return _needs_facet_permutations; }
327
331 std::vector<int> coefficient_offsets() const
332 {
333 std::vector<int> n = {0};
334 for (const auto& c : _coefficients)
335 {
336 if (!c)
337 throw std::runtime_error("Not all form coefficients have been set.");
338 n.push_back(n.back() + c->function_space()->element()->space_dimension());
339 }
340 return n;
341 }
342
344 const std::vector<std::shared_ptr<const Constant<T>>>& constants() const
345 {
346 return _constants;
347 }
348
350 using scalar_type = T;
351
352private:
353 using kern
354 = std::function<void(T*, const T*, const T*, const scalar_value_type_t*,
355 const int*, const std::uint8_t*)>;
356
357 // Helper function to get the kernel for integral i from a map
358 // of integrals i.e. from _cell_integrals
359 // @param[in] integrals Map of integrals
360 // @param[in] i Domain index
361 // @return Function to call for tabulate_tensor
362 template <typename U>
363 const std::function<void(T*, const T*, const T*, const scalar_value_type_t*,
364 const int*, const std::uint8_t*)>&
365 get_kernel_from_integrals(const U& integrals, int i) const
366 {
367 auto it = integrals.find(i);
368 if (it == integrals.end())
369 throw std::runtime_error("No kernel for requested domain index.");
370 return it->second.first;
371 }
372
373 // Helper function to get an array of of (cell, local_facet) pairs
374 // corresponding to a given facet index.
375 // @param[in] f Facet index
376 // @param[in] f_to_c Facet to cell connectivity
377 // @param[in] c_to_f Cell to facet connectivity
378 // @return Vector of (cell, local_facet) pairs
379 template <int num_cells>
380 static std::array<std::array<std::int32_t, 2>, num_cells>
381 get_cell_local_facet_pairs(std::int32_t f,
382 std::span<const std::int32_t> cells,
384 {
385 // Loop over cells sharing facet
386 assert(cells.size() == num_cells);
387 std::array<std::array<std::int32_t, 2>, num_cells> cell_local_facet_pairs;
388 for (int c = 0; c < num_cells; ++c)
389 {
390 // Get local index of facet with respect to the cell
391 std::int32_t cell = cells[c];
392 auto cell_facets = c_to_f.links(cell);
393 auto facet_it = std::find(cell_facets.begin(), cell_facets.end(), f);
394 assert(facet_it != cell_facets.end());
395 int local_f = std::distance(cell_facets.begin(), facet_it);
396 cell_local_facet_pairs[c] = {cell, local_f};
397 }
398
399 return cell_local_facet_pairs;
400 }
401
402 // Set cell domains
403 void set_cell_domains(
404 std::map<int, std::pair<kern, std::vector<std::int32_t>>>& integrals,
405 std::span<const std::int32_t> tagged_cells, std::span<const int> tags)
406 {
407 // For cell integrals use all markers
408 for (std::size_t i = 0; i < tagged_cells.size(); ++i)
409 {
410 if (auto it = integrals.find(tags[i]); it != integrals.end())
411 {
412 std::vector<std::int32_t>& integration_entities = it->second.second;
413 integration_entities.push_back(tagged_cells[i]);
414 }
415 }
416 }
417
418 // Set exterior facet domains
419 // @param[in] topology The mesh topology
420 // @param[in] integrals The integrals to set exterior facet domains for
421 // @param[in] tagged_facets A list of facets
422 // @param[in] tags A list of tags
423 // @pre The list of tagged facets must be sorted
424 void set_exterior_facet_domains(
425 const mesh::Topology& topology,
426 std::map<int, std::pair<kern, std::vector<std::int32_t>>>& integrals,
427 std::span<const std::int32_t> tagged_facets, std::span<const int> tags)
428 {
429 const std::vector<std::int32_t> boundary_facets
431
432 // Create list of tagged boundary facets
433 std::vector<std::int32_t> tagged_ext_facets;
434 std::set_intersection(tagged_facets.begin(), tagged_facets.end(),
435 boundary_facets.begin(), boundary_facets.end(),
436 std::back_inserter(tagged_ext_facets));
437
438 const int tdim = topology.dim();
439 auto f_to_c = topology.connectivity(tdim - 1, tdim);
440 assert(f_to_c);
441 auto c_to_f = topology.connectivity(tdim, tdim - 1);
442 assert(c_to_f);
443
444 // Loop through tagged boundary facets and add to respective
445 // integral
446 for (std::int32_t f : tagged_ext_facets)
447 {
448 // Find index of f in tagged facets so that we can access the
449 // associated tag
450 // FIXME: Would it be better to avoid calling std::lower_bound in
451 // a loop>
452 auto index_it
453 = std::lower_bound(tagged_facets.begin(), tagged_facets.end(), f);
454 assert(index_it != tagged_facets.end() and *index_it == f);
455 const int index = std::distance(tagged_facets.begin(), index_it);
456 if (auto it = integrals.find(tags[index]); it != integrals.end())
457 {
458 // Get the facet as a (cell, local_facet) pair. There will only
459 // be one pair for an exterior facet integral
460 std::array<std::int32_t, 2> facet
461 = get_cell_local_facet_pairs<1>(f, f_to_c->links(f), *c_to_f)
462 .front();
463 std::vector<std::int32_t>& integration_entities = it->second.second;
464 integration_entities.insert(integration_entities.end(), facet.cbegin(),
465 facet.cend());
466 }
467 }
468 }
469
470 // Set interior facet domains
471 static void set_interior_facet_domains(
472 const mesh::Topology& topology,
473 std::map<int, std::pair<kern, std::vector<std::int32_t>>>& integrals,
474 std::span<const std::int32_t> tagged_facets, std::span<const int> tags)
475 {
476 int tdim = topology.dim();
477 auto f_to_c = topology.connectivity(tdim - 1, tdim);
478 assert(f_to_c);
479 auto c_to_f = topology.connectivity(tdim, tdim - 1);
480 assert(c_to_f);
481 for (std::size_t i = 0; i < tagged_facets.size(); ++i)
482 {
483 const std::int32_t f = tagged_facets[i];
484 if (f_to_c->num_links(f) == 2)
485 {
486 if (auto it = integrals.find(tags[i]); it != integrals.end())
487 {
488 // Ge the facet as a pair of (cell, local facet) pairs, one for each
489 // cell
490 auto [facet_0, facet_1]
491 = get_cell_local_facet_pairs<2>(f, f_to_c->links(f), *c_to_f);
492 std::vector<std::int32_t>& integration_entities = it->second.second;
493 integration_entities.insert(integration_entities.end(),
494 facet_0.cbegin(), facet_0.cend());
495 integration_entities.insert(integration_entities.end(),
496 facet_1.cbegin(), facet_1.cend());
497 }
498 }
499 }
500 }
501
502 // Sets the entity indices to assemble over for kernels with a domain
503 // ID
504 // @param[in] type Integral type
505 // @param[in] marker MeshTags with domain ID. Entities with marker 'i'
506 // will be assembled over using the kernel with ID 'i'. The MeshTags
507 // is not stored.
508 void set_domains(IntegralType type, const mesh::MeshTags<int>& marker)
509 {
510 std::shared_ptr<const mesh::Mesh> mesh = marker.mesh();
511 const mesh::Topology& topology = mesh->topology();
512 const int tdim = topology.dim();
513 int dim = type == IntegralType::cell ? tdim : tdim - 1;
514 if (dim != marker.dim())
515 {
516 throw std::runtime_error("Invalid MeshTags dimension: "
517 + std::to_string(marker.dim()));
518 }
519
520 // Get mesh tag data
521 assert(topology.index_map(dim));
522 auto entity_end
523 = std::lower_bound(marker.indices().begin(), marker.indices().end(),
524 topology.index_map(dim)->size_local());
525 // Only include owned entities in integration domains
526 std::span<const std::int32_t> owned_tagged_entities(
527 marker.indices().data(),
528 std::distance(marker.indices().begin(), entity_end));
529 switch (type)
530 {
532 set_cell_domains(_cell_integrals, owned_tagged_entities, marker.values());
533 break;
534 default:
535 mesh->topology_mutable().create_connectivity(dim, tdim);
536 mesh->topology_mutable().create_connectivity(tdim, dim);
537 switch (type)
538 {
540 set_exterior_facet_domains(topology, _exterior_facet_integrals,
541 owned_tagged_entities, marker.values());
542 break;
544 set_interior_facet_domains(topology, _interior_facet_integrals,
545 owned_tagged_entities, marker.values());
546 break;
547 default:
548 throw std::runtime_error(
549 "Cannot set domains. Integral type not supported.");
550 }
551 }
552 }
553
559 void set_default_domains(const mesh::Mesh& mesh)
560 {
561 const mesh::Topology& topology = mesh.topology();
562 const int tdim = topology.dim();
563
564 // Cells. If there is a default integral, define it on all owned
565 // cells
566 for (auto& [domain_id, kernel_cells] : _cell_integrals)
567 {
568 if (domain_id == -1)
569 {
570 std::vector<std::int32_t>& cells = kernel_cells.second;
571 const int num_cells = topology.index_map(tdim)->size_local();
572 cells.resize(num_cells);
573 std::iota(cells.begin(), cells.end(), 0);
574 }
575 }
576
577 // Exterior facets. If there is a default integral, define it only
578 // on owned surface facets.
579
580 if (!_exterior_facet_integrals.empty())
581 {
582 mesh.topology_mutable().create_connectivity(tdim - 1, tdim);
583 mesh.topology_mutable().create_connectivity(tdim, tdim - 1);
584 }
585
586 const std::vector<std::int32_t> boundary_facets
587 = _exterior_facet_integrals.empty()
588 ? std::vector<std::int32_t>()
589 : mesh::exterior_facet_indices(topology);
590 for (auto& [domain_id, kernel_facets] : _exterior_facet_integrals)
591 {
592 if (domain_id == -1)
593 {
594 std::vector<std::int32_t>& facets = kernel_facets.second;
595 facets.clear();
596
597 auto f_to_c = topology.connectivity(tdim - 1, tdim);
598 assert(f_to_c);
599 auto c_to_f = topology.connectivity(tdim, tdim - 1);
600 assert(c_to_f);
601 for (std::int32_t f : boundary_facets)
602 {
603 // There will only be one pair for an exterior facet integral
604 std::array<std::int32_t, 2> pair
605 = get_cell_local_facet_pairs<1>(f, f_to_c->links(f), *c_to_f)[0];
606 facets.insert(facets.end(), pair.cbegin(), pair.cend());
607 }
608 }
609 }
610
611 // Interior facets. If there is a default integral, define it only on
612 // owned interior facets.
613 for (auto& [domain_id, kernel_facets] : _interior_facet_integrals)
614 {
615 if (domain_id == -1)
616 {
617 std::vector<std::int32_t>& facets = kernel_facets.second;
618 facets.clear();
619
620 mesh.topology_mutable().create_connectivity(tdim - 1, tdim);
621 auto f_to_c = topology.connectivity(tdim - 1, tdim);
622 assert(f_to_c);
623 mesh.topology_mutable().create_connectivity(tdim, tdim - 1);
624 auto c_to_f = mesh.topology().connectivity(tdim, tdim - 1);
625 assert(c_to_f);
626
627 // Get number of facets owned by this process
628 assert(topology.index_map(tdim - 1));
629 const int num_facets = topology.index_map(tdim - 1)->size_local();
630 facets.reserve(num_facets);
631 for (int f = 0; f < num_facets; ++f)
632 {
633 if (f_to_c->num_links(f) == 2)
634 {
635 const std::array<std::array<std::int32_t, 2>, 2> pairs
636 = get_cell_local_facet_pairs<2>(f, f_to_c->links(f), *c_to_f);
637 facets.insert(facets.end(), pairs[0].cbegin(), pairs[0].cend());
638 facets.insert(facets.end(), pairs[1].cbegin(), pairs[1].cend());
639 }
640 }
641 }
642 }
643 }
644
645 // Function spaces (one for each argument)
646 std::vector<std::shared_ptr<const FunctionSpace>> _function_spaces;
647
648 // Form coefficients
649 std::vector<std::shared_ptr<const Function<T>>> _coefficients;
650
651 // Constants associated with the Form
652 std::vector<std::shared_ptr<const Constant<T>>> _constants;
653
654 // The mesh
655 std::shared_ptr<const mesh::Mesh> _mesh;
656
657 // Cell integrals
658 std::map<int, std::pair<kern, std::vector<std::int32_t>>> _cell_integrals;
659
660 // Exterior facet integrals
661 std::map<int, std::pair<kern, std::vector<std::int32_t>>>
662 _exterior_facet_integrals;
663
664 // Interior facet integrals
665 std::map<int, std::pair<kern, std::vector<std::int32_t>>>
666 _interior_facet_integrals;
667
668 // True if permutation data needs to be passed into these integrals
669 bool _needs_facet_permutations;
670};
671} // namespace dolfinx::fem
Constant value which can be attached to a Form. Constants may be scalar (rank 0), vector (rank 1),...
Definition: Constant.h:20
A representation of finite element variational forms.
Definition: Form.h:64
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition: Form.h:326
Form(Form &&form)=default
Move constructor.
int rank() const
Rank of the form (bilinear form = 2, linear form = 1, functional = 0, etc)
Definition: Form.h:173
const std::vector< std::int32_t > & cell_domains(int i) const
Get the list of cell indices for the ith integral (kernel) for the cell domain type.
Definition: Form.h:281
Form(const Form &form)=delete
Copy constructor.
virtual ~Form()=default
Destructor.
const std::vector< std::int32_t > & exterior_facet_domains(int i) const
List of (cell_index, local_facet_index) pairs for the ith integral (kernel) for the exterior facet do...
Definition: Form.h:294
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell. Used to pack data for multiple coefficients in...
Definition: Form.h:331
const std::function< void(T *, const T *, const T *, const scalar_value_type_t *, const int *, const std::uint8_t *)> & kernel(IntegralType type, int i) const
Get the function for 'kernel' for integral i of given type.
Definition: Form.h:194
const std::vector< std::shared_ptr< const FunctionSpace > > & function_spaces() const
Return function spaces for all arguments.
Definition: Form.h:182
const std::vector< std::shared_ptr< const Function< T > > > & coefficients() const
Access coefficients.
Definition: Form.h:318
const std::vector< std::shared_ptr< const Constant< T > > > & constants() const
Access constants.
Definition: Form.h:344
std::vector< int > integral_ids(IntegralType type) const
Get the IDs for integrals (kernels) for given integral type. The IDs correspond to the domain IDs whi...
Definition: Form.h:249
Form(const std::vector< std::shared_ptr< const FunctionSpace > > &function_spaces, const std::map< IntegralType, std::pair< std::vector< std::pair< int, std::function< void(T *, const T *, const T *, const scalar_value_type_t *, const int *, const std::uint8_t *)> > >, const mesh::MeshTags< int > * > > &integrals, const std::vector< std::shared_ptr< const Function< T > > > &coefficients, const std::vector< std::shared_ptr< const Constant< T > > > &constants, bool needs_facet_permutations, std::shared_ptr< const mesh::Mesh > mesh=nullptr)
Create a finite element form.
Definition: Form.h:94
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition: Form.h:212
std::shared_ptr< const mesh::Mesh > mesh() const
Extract common mesh for the form.
Definition: Form.h:177
const std::vector< std::int32_t > & interior_facet_domains(int i) const
Get the list of (cell_index_0, local_facet_index_0, cell_index_1, local_facet_index_1) quadruplets fo...
Definition: Form.h:309
int num_integrals(IntegralType type) const
Number of integrals of given type.
Definition: Form.h:228
T scalar_type
Scalar type (T)
Definition: Form.h:350
This class represents a function in a finite element function space , given by.
Definition: Function.h:43
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:27
std::span< T > links(int node)
Get the links (edges) for given node.
Definition: AdjacencyList.h:109
MeshTags associate values with mesh entities.
Definition: MeshTags.h:37
void cells(la::SparsityPattern &pattern, const mesh::Topology &topology, const std::array< const std::reference_wrapper< const DofMap >, 2 > &dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition: sparsitybuild.cpp:18
Finite element method functionality.
Definition: assemble_matrix_impl.h:25
IntegralType
Type of integral.
Definition: Form.h:32
@ interior_facet
Interior facet.
@ exterior_facet
Exterior facet.
std::vector< std::int32_t > exterior_facet_indices(const Topology &topology)
Compute the indices of all exterior facets that are owned by the caller.
Definition: utils.cpp:580