DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
FunctionSpace.h
1// Copyright (C) 2008-2026 Anders Logg, Garth N. Wells and Jørgen S. Dokken
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 "CoordinateElement.h"
10#include "DofMap.h"
11#include "FiniteElement.h"
12#include <boost/uuid/uuid.hpp>
13#include <boost/uuid/uuid_generators.hpp>
14#include <concepts>
15#include <cstddef>
16#include <cstdint>
17#include <dolfinx/common/IndexMap.h>
18#include <dolfinx/common/log.h>
19#include <dolfinx/mesh/Geometry.h>
20#include <dolfinx/mesh/Mesh.h>
21#include <dolfinx/mesh/Topology.h>
22#include <map>
23#include <memory>
24#include <stdexcept>
25#include <vector>
26
27namespace dolfinx::fem
28{
34template <std::floating_point T>
36{
37public:
39 using geometry_type = T;
40
51 std::shared_ptr<const FiniteElement<geometry_type>> element,
52 std::shared_ptr<const DofMap> dofmap)
53 : _mesh(mesh), _elements{element}, _dofmaps{std::move(dofmap)},
54 _id(boost::uuids::random_generator()()), _root_space_id(_id)
55 {
56 check_facet_orientations();
57 }
58
72 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh,
73 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>> elements,
74 std::vector<std::shared_ptr<const DofMap>> dofmaps)
75 : _mesh(mesh), _elements(elements), _dofmaps(std::move(dofmaps)),
76 _id(boost::uuids::random_generator()()), _root_space_id(_id)
77 {
78 std::vector<mesh::CellType> cell_types = mesh->topology()->cell_types();
79 std::size_t num_cell_types = cell_types.size();
80 if (elements.size() != num_cell_types)
81 {
82 throw std::invalid_argument(
83 "Number of elements must match number of cell types");
84 }
85 if (_dofmaps.size() != num_cell_types)
86 {
87 throw std::invalid_argument(
88 "Number of dofmaps must match number of cell types");
89 }
90 for (std::size_t i = 0; i < num_cell_types; ++i)
91 {
92 if (elements.at(i)->cell_type() != cell_types.at(i))
93 {
94 throw std::invalid_argument(
95 "Element cell types must match mesh cell types");
96 }
97 }
98 check_facet_orientations();
99 }
100
101 // Copy constructor (deleted)
102 FunctionSpace(const FunctionSpace& V) = delete;
103
106
108 ~FunctionSpace() = default;
109
110 // Assignment operator (delete)
111 FunctionSpace& operator=(const FunctionSpace& V) = delete;
112
115
124 FunctionSpace sub(const std::vector<int>& component) const
125 {
126 assert(_mesh);
127 assert(_elements.size() > 0);
128 assert(_dofmaps.size() > 0);
129
130 // Check that component is valid
131 if (component.empty())
132 throw std::invalid_argument("Component must be non-empty");
133
134 // Extract sub-element
135 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>>
136 sub_elements;
137 for (auto e : _elements)
138 {
139 assert(e);
140 sub_elements.push_back(e->extract_sub_element(component));
141 }
142 // Extract sub dofmap
143 std::vector<std::shared_ptr<const DofMap>> sub_dofmaps;
144 for (auto d : _dofmaps)
145 {
146 assert(d);
147 sub_dofmaps.push_back(
148 std::make_shared<const DofMap>(d->extract_sub_dofmap(component)));
149 }
150
151 // Create new sub space
152 FunctionSpace sub_space(_mesh, sub_elements, sub_dofmaps);
153
154 // Set root space id and component w.r.t. root
155 sub_space._root_space_id = _root_space_id;
156 sub_space._component = _component;
157 sub_space._component.insert(sub_space._component.end(), component.begin(),
158 component.end());
159 return sub_space;
160 }
161
166 bool contains(const FunctionSpace& V) const
167 {
168 if (this == std::addressof(V)) // Spaces are the same (same memory address)
169 return true;
170 else if (_root_space_id != V._root_space_id) // Different root spaces
171 return false;
172 else if (_component.size()
173 > V._component.size()) // V is a superspace of *this
174 {
175 return false;
176 }
177 else if (!std::equal(_component.begin(), _component.end(),
178 V._component.begin())) // Components of 'this' are not
179 // the same as the leading
180 // components of V
181 {
182 return false;
183 }
184 else // Ok, V is really our subspace
185 return true;
186 }
187
191 std::pair<FunctionSpace, std::vector<std::vector<std::int32_t>>>
192 collapse() const
193 {
194 logger().debug("FunctionSpace::collapse");
195 if (_component.empty())
196 throw std::invalid_argument("Function space is not a subspace");
197
198 // Create collapsed DofMap
199 std::vector<std::shared_ptr<const DofMap>> collapsed_dofmaps;
200 std::vector<std::vector<std::int32_t>> collapsed_dofs;
201 for (auto d : _dofmaps)
202 {
203 assert(d);
204 logger().debug("Call DofMap::collapse");
205 auto [_collapsed_dofmap, _collapsed_dofs]
206 = d->collapse(_mesh->comm(), *_mesh->topology());
207 logger().debug("Got collapsed dofmap");
208 collapsed_dofmaps.push_back(
209 std::make_shared<DofMap>(std::move(_collapsed_dofmap)));
210 collapsed_dofs.push_back(_collapsed_dofs);
211 }
212
213 return {FunctionSpace(_mesh, _elements, collapsed_dofmaps),
214 std::move(collapsed_dofs)};
215 }
216
220 std::vector<int> component() const { return _component; }
221
224 bool symmetric() const
225 {
226 if (_elements.front())
227 return _elements.front()->symmetric();
228 return false;
229 }
230
242 std::vector<geometry_type> tabulate_dof_coordinates(bool transpose) const
243 {
244 if (!_component.empty())
245 {
246 throw std::invalid_argument("Cannot tabulate coordinates for a "
247 "FunctionSpace that is a subspace.");
248 }
249
250 assert(_elements.front());
251 if (_elements.front()->is_mixed())
252 {
253 throw std::invalid_argument(
254 "Cannot tabulate coordinates for a mixed FunctionSpace.");
255 }
256
257 // Geometric dimension
258 assert(_mesh);
259 assert(_elements.front());
260 const std::size_t gdim = _mesh->geometry().dim();
261 const int tdim = _mesh->topology()->dim();
262
263 // Get dofmap local size
264 assert(_dofmaps.front());
265 std::shared_ptr<const common::IndexMap> index_map
266 = _dofmaps.front()->index_map;
267 assert(index_map);
268 const int index_map_bs = _dofmaps.front()->index_map_bs();
269 const int dofmap_bs = _dofmaps.front()->bs();
270
271 const int element_block_size = _elements.front()->block_size();
272 const std::size_t scalar_dofs
273 = _elements.front()->space_dimension() / element_block_size;
274 const std::int32_t num_dofs
275 = index_map_bs * (index_map->size_local() + index_map->num_ghosts())
276 / dofmap_bs;
277
278 std::size_t num_cell_types = _elements.size();
279
280 // Array to hold coordinates to return
281 const std::size_t shape_c0 = transpose ? 3 : num_dofs;
282 const std::size_t shape_c1 = transpose ? num_dofs : 3;
283 std::vector<geometry_type> coords(shape_c0 * shape_c1, 0);
284 using mdspan2_t = md::mdspan<geometry_type, md::dextents<std::size_t, 2>>;
285 using cmdspan4_t
286 = md::mdspan<const geometry_type, md::dextents<std::size_t, 4>>;
287 std::span<const geometry_type> x_g = _mesh->geometry().x();
288 std::vector<geometry_type> x_b(scalar_dofs * gdim);
289 mdspan2_t x(x_b.data(), scalar_dofs, gdim);
290
291 for (std::size_t i = 0; i < num_cell_types; ++i)
292 {
293 // Get the dof coordinates on the reference element
294 if (!_elements[i]->interpolation_ident())
295 {
296 throw std::runtime_error(
297 "Cannot evaluate dof coordinates - this element "
298 "does not have pointwise evaluation.");
299 }
300 const auto [X, Xshape] = _elements[i]->interpolation_points();
301
302 // Get coordinate map
304 = _mesh->geometry().cmaps().at(i);
305
306 // Prepare cell geometry
307 auto x_dofmap = _mesh->geometry().dofmaps().at(i);
308 const std::size_t num_dofs_g = cmap.dim();
309 std::vector<geometry_type> coordinate_dofs_b(num_dofs_g * gdim);
310 mdspan2_t coordinate_dofs(coordinate_dofs_b.data(), num_dofs_g, gdim);
311
312 auto map = _mesh->topology()->index_maps(tdim)[i];
313 assert(map);
314 const int num_cells = map->size_local() + map->num_ghosts();
315
316 std::span<const std::uint32_t> cell_info;
317 if (_elements[i]->needs_dof_transformations())
318 {
319 _mesh->topology_mutable()->create_cell_permutations();
320 cell_info = std::span(_mesh->topology()->get_cell_permutation_info());
321 }
322
323 const std::array<std::size_t, 4> phi_shape
324 = cmap.tabulate_shape(0, Xshape[0]);
325 std::vector<geometry_type> phi_b(std::reduce(
326 phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
327 cmdspan4_t phi_full(phi_b.data(), phi_shape);
328 cmap.tabulate(0, X, Xshape, phi_b);
329 auto phi
330 = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
331
332 // TODO: Check transform
333 //
334 // Basis function reference-to-conforming transformation function.
335 // This function is only reachable for elements with
336 // interpolation_ident() (point evaluation, e.g. Lagrange), which
337 // never need a DOF transformation, so this is always a no-op
338 // (nullptr) closure.
339 auto apply_dof_transformation
340 = _elements[i]->template dof_transformation_fn<geometry_type>(
342
343 for (int c = 0; c < num_cells; ++c)
344 {
345 // Extract cell geometry 'dofs'
346 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
347 for (std::size_t j = 0; j < x_dofs.size(); ++j)
348 for (std::size_t k = 0; k < gdim; ++k)
349 coordinate_dofs(j, k) = x_g[3 * x_dofs[j] + k];
350
351 // Tabulate dof coordinates on cell
352 cmap.push_forward(x, coordinate_dofs, phi);
353 if (apply_dof_transformation)
354 {
355 apply_dof_transformation(
356 x_b, std::span(cell_info.data(), cell_info.size()), c,
357 x.extent(1));
358 }
359
360 // Get cell dofmap
361 auto dofs = _dofmaps[i]->cell_dofs(c);
362
363 // Copy dof coordinates into vector
364 if (!transpose)
365 {
366 for (std::size_t j = 0; j < dofs.size(); ++j)
367 for (std::size_t k = 0; k < gdim; ++k)
368 coords[dofs[j] * 3 + k] = x(j, k);
369 }
370 else
371 {
372 for (std::size_t j = 0; j < dofs.size(); ++j)
373 for (std::size_t k = 0; k < gdim; ++k)
374 coords[k * num_dofs + dofs[j]] = x(j, k);
375 }
376 }
377 }
378
379 return coords;
380 }
381
383 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
384 {
385 return _mesh;
386 }
387
389 std::shared_ptr<const FiniteElement<geometry_type>> element() const
390 {
391 if (_elements.size() > 1)
392 {
393 throw std::invalid_argument(
394 "FunctionSpace has multiple elements, call `elements` instead.");
395 }
396
397 return elements(0);
398 }
399
401 std::shared_ptr<const FiniteElement<geometry_type>>
402 elements(int cell_type_idx) const
403 {
404 return _elements.at(cell_type_idx);
405 }
406
408 std::shared_ptr<const DofMap> dofmap() const
409 {
410 if (_dofmaps.size() > 1)
411 {
412 throw std::invalid_argument(
413 "FunctionSpace has multiple dofmaps, call `dofmaps` instead.");
414 }
415
416 return dofmaps().front();
417 }
418
420 const std::vector<std::shared_ptr<const DofMap>>& dofmaps() const
421 {
422 return _dofmaps;
423 }
424
425private:
426 // Throw if an element depends on the mesh's facet signs and they have
427 // not been computed. The check is local, and its outcome is the same on
428 // all ranks.
429 void check_facet_orientations() const
430 {
431 if (_mesh->topology()->has_facet_orientations())
432 return;
433 for (const std::shared_ptr<const FiniteElement<geometry_type>>& e :
434 _elements)
435 {
436 if (e->depends_on_facet_orientations())
437 {
438 throw std::runtime_error(
439 "The basis of this element, e.g. Raviart-Thomas on a manifold, "
440 "depends on the facet signs, which have not been computed. "
441 "Call Topology::create_facet_orientations before creating the "
442 "function space.");
443 }
444 }
445 }
446
447 // The mesh
448 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
449
450 // The finite element
451 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>> _elements;
452
453 // The dofmap
454 std::vector<std::shared_ptr<const DofMap>> _dofmaps;
455
456 // The component w.r.t. to root space
457 std::vector<int> _component;
458
459 // Unique identifier for the space and for its root space
460 boost::uuids::uuid _id;
461 boost::uuids::uuid _root_space_id;
462};
463
474template <dolfinx::scalar T>
475std::array<std::vector<std::shared_ptr<const FunctionSpace<T>>>, 2>
477 const std::vector<
478 std::vector<std::array<std::shared_ptr<const FunctionSpace<T>>, 2>>>& V)
479{
480 assert(!V.empty());
481 std::vector<std::shared_ptr<const FunctionSpace<T>>> spaces0(V.size(),
482 nullptr);
483 std::vector<std::shared_ptr<const FunctionSpace<T>>> spaces1(V.front().size(),
484 nullptr);
485
486 // Loop over rows
487 for (std::size_t i = 0; i < V.size(); ++i)
488 {
489 // Loop over columns
490 for (std::size_t j = 0; j < V[i].size(); ++j)
491 {
492 auto& V0 = V[i][j][0];
493 auto& V1 = V[i][j][1];
494 if (V0 and V1)
495 {
496 if (!spaces0[i])
497 spaces0[i] = V0;
498 else
499 {
500 if (spaces0[i] != V0)
501 throw std::invalid_argument("Mismatched test space for row.");
502 }
503
504 if (!spaces1[j])
505 spaces1[j] = V1;
506 else
507 {
508 if (spaces1[j] != V1)
509 throw std::invalid_argument("Mismatched trial space for column.");
510 }
511 }
512 }
513 }
514
515 // Check that there are no null entries
516 if (std::find(spaces0.begin(), spaces0.end(), nullptr) != spaces0.end())
517 throw std::invalid_argument("Could not deduce all block test spaces.");
518 if (std::find(spaces1.begin(), spaces1.end(), nullptr) != spaces1.end())
519 throw std::invalid_argument("Could not deduce all block trial spaces.");
520
521 return {spaces0, spaces1};
522}
523
525template <typename U, typename V, typename W>
526FunctionSpace(U mesh, V element, W dofmap)
527 -> FunctionSpace<typename std::remove_cvref<
528 typename U::element_type>::type::geometry_type::value_type>;
529
530} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:39
void tabulate(int nd, std::span< const T > X, std::array< std::size_t, 2 > shape, std::span< T > basis) const
Evaluate basis values and derivatives at set of points.
Definition CoordinateElement.cpp:71
std::array< std::size_t, 4 > tabulate_shape(std::size_t nd, std::size_t num_points) const
Shape of array to fill when calling tabulate.
Definition CoordinateElement.cpp:64
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:237
static void push_forward(U &&x, const V &cell_geometry, const W &phi)
Compute physical coordinates x for points X in the reference configuration.
Definition CoordinateElement.h:202
Model of a finite element.
Definition FiniteElement.h:202
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:36
std::vector< int > component() const
Get the component with respect to the root superspace.
Definition FunctionSpace.h:220
bool symmetric() const
Indicate whether this function space represents a symmetric 2-tensor.
Definition FunctionSpace.h:224
FunctionSpace & operator=(FunctionSpace &&V)=default
Move assignment operator.
std::shared_ptr< const FiniteElement< geometry_type > > elements(int cell_type_idx) const
The finite elements.
Definition FunctionSpace.h:402
std::shared_ptr< const DofMap > dofmap() const
The dofmap.
Definition FunctionSpace.h:408
FunctionSpace(std::shared_ptr< const mesh::Mesh< geometry_type > > mesh, std::vector< std::shared_ptr< const FiniteElement< geometry_type > > > elements, std::vector< std::shared_ptr< const DofMap > > dofmaps)
Create function space for given mesh, elements and degree-of-freedom maps.
Definition FunctionSpace.h:71
FunctionSpace sub(const std::vector< int > &component) const
Create a subspace (view) for a specific component.
Definition FunctionSpace.h:124
bool contains(const FunctionSpace &V) const
Check whether V is subspace of this, or this itself.
Definition FunctionSpace.h:166
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
The mesh.
Definition FunctionSpace.h:383
std::shared_ptr< const FiniteElement< geometry_type > > element() const
The finite element.
Definition FunctionSpace.h:389
FunctionSpace(std::shared_ptr< const mesh::Mesh< geometry_type > > mesh, std::shared_ptr< const FiniteElement< geometry_type > > element, std::shared_ptr< const DofMap > dofmap)
Create function space for given mesh, element and degree-of-freedom map.
Definition FunctionSpace.h:50
T geometry_type
Geometry type of the Mesh that the FunctionSpace is defined on.
Definition FunctionSpace.h:39
~FunctionSpace()=default
Destructor.
std::vector< geometry_type > tabulate_dof_coordinates(bool transpose) const
Tabulate the physical coordinates of all dofs on this process.
Definition FunctionSpace.h:242
const std::vector< std::shared_ptr< const DofMap > > & dofmaps() const
The dofmaps.
Definition FunctionSpace.h:420
FunctionSpace(FunctionSpace &&V)=default
Move constructor.
std::pair< FunctionSpace, std::vector< std::vector< std::int32_t > > > collapse() const
Definition FunctionSpace.h:192
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:25
Finite element method functionality.
Definition assemble_expression_impl.h:28
@ standard
Standard.
Definition FiniteElement.h:34
std::array< std::vector< std::shared_ptr< const FunctionSpace< T > > >, 2 > common_function_spaces(const std::vector< std::vector< std::array< std::shared_ptr< const FunctionSpace< T > >, 2 > > > &V)
Extract FunctionSpaces for (0) rows blocks and (1) columns blocks from a rectangular array of (test,...
Definition FunctionSpace.h:476
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
auto logger() -> spdlog::logger &
Dolfinx's logger.
Definition log.cpp:24