DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
FunctionSpace.h
1// Copyright (C) 2008-2026 Anders Logg and Garth N. Wells
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/mesh/Geometry.h>
19#include <dolfinx/mesh/Mesh.h>
20#include <dolfinx/mesh/Topology.h>
21#include <map>
22#include <memory>
23#include <stdexcept>
24#include <vector>
25
26namespace dolfinx::fem
27{
33template <std::floating_point T>
35{
36public:
38 using geometry_type = T;
39
46 std::shared_ptr<const FiniteElement<geometry_type>> element,
47 std::shared_ptr<const DofMap> dofmap)
48 : _mesh(mesh), _elements{element}, _dofmaps{std::move(dofmap)},
49 _id(boost::uuids::random_generator()()), _root_space_id(_id)
50 {
51 // Do nothing
52 }
53
63 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh,
64 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>> elements,
65 std::vector<std::shared_ptr<const DofMap>> dofmaps)
66 : _mesh(mesh), _elements(elements), _dofmaps(std::move(dofmaps)),
67 _id(boost::uuids::random_generator()()), _root_space_id(_id)
68 {
69 std::vector<mesh::CellType> cell_types = mesh->topology()->cell_types();
70 std::size_t num_cell_types = cell_types.size();
71 if (elements.size() != num_cell_types)
72 {
73 throw std::invalid_argument(
74 "Number of elements must match number of cell types");
75 }
76 if (_dofmaps.size() != num_cell_types)
77 {
78 throw std::invalid_argument(
79 "Number of dofmaps must match number of cell types");
80 }
81 for (std::size_t i = 0; i < num_cell_types; ++i)
82 {
83 if (elements.at(i)->cell_type() != cell_types.at(i))
84 throw std::invalid_argument(
85 "Element cell types must match mesh cell types");
86 }
87 }
88
89 // Copy constructor (deleted)
90 FunctionSpace(const FunctionSpace& V) = delete;
91
94
96 virtual ~FunctionSpace() = default;
97
98 // Assignment operator (delete)
99 FunctionSpace& operator=(const FunctionSpace& V) = delete;
100
103
112 FunctionSpace sub(const std::vector<int>& component) const
113 {
114 assert(_mesh);
115 assert(_elements.size() > 0);
116 assert(_dofmaps.size() > 0);
117
118 // Check that component is valid
119 if (component.empty())
120 throw std::invalid_argument("Component must be non-empty");
121
122 // Extract sub-element
123 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>>
124 sub_elements;
125 for (auto e : _elements)
126 {
127 assert(e);
128 sub_elements.push_back(e->extract_sub_element(component));
129 }
130 // Extract sub dofmap
131 std::vector<std::shared_ptr<const DofMap>> sub_dofmaps;
132 for (auto d : _dofmaps)
133 {
134 assert(d);
135 sub_dofmaps.push_back(
136 std::make_shared<const DofMap>(d->extract_sub_dofmap(component)));
137 }
138
139 // Create new sub space
140 FunctionSpace sub_space(_mesh, sub_elements, sub_dofmaps);
141
142 // Set root space id and component w.r.t. root
143 sub_space._root_space_id = _root_space_id;
144 sub_space._component = _component;
145 sub_space._component.insert(sub_space._component.end(), component.begin(),
146 component.end());
147 return sub_space;
148 }
149
154 bool contains(const FunctionSpace& V) const
155 {
156 if (this == std::addressof(V)) // Spaces are the same (same memory address)
157 return true;
158 else if (_root_space_id != V._root_space_id) // Different root spaces
159 return false;
160 else if (_component.size()
161 > V._component.size()) // V is a superspace of *this
162 {
163 return false;
164 }
165 else if (!std::equal(_component.begin(), _component.end(),
166 V._component.begin())) // Components of 'this' are not
167 // the same as the leading
168 // components of V
169 {
170 return false;
171 }
172 else // Ok, V is really our subspace
173 return true;
174 }
175
179 std::pair<FunctionSpace, std::vector<std::vector<std::int32_t>>>
180 collapse() const
181 {
182 spdlog::debug("FunctionSpace::collapse");
183 if (_component.empty())
184 throw std::invalid_argument("Function space is not a subspace");
185
186 // Create collapsed DofMap
187 std::vector<std::shared_ptr<const DofMap>> collapsed_dofmaps;
188 std::vector<std::vector<std::int32_t>> collapsed_dofs;
189 for (auto d : _dofmaps)
190 {
191 assert(d);
192 spdlog::debug("Call DofMap::collapse");
193 auto [_collapsed_dofmap, _collapsed_dofs]
194 = d->collapse(_mesh->comm(), *_mesh->topology());
195 spdlog::debug("Got collapsed dofmap");
196 collapsed_dofmaps.push_back(
197 std::make_shared<DofMap>(std::move(_collapsed_dofmap)));
198 collapsed_dofs.push_back(_collapsed_dofs);
199 }
200
201 return {FunctionSpace(_mesh, _elements, collapsed_dofmaps),
202 std::move(collapsed_dofs)};
203 }
204
208 std::vector<int> component() const { return _component; }
209
212 bool symmetric() const
213 {
214 if (_elements.front())
215 return _elements.front()->symmetric();
216 return false;
217 }
218
230 std::vector<geometry_type> tabulate_dof_coordinates(bool transpose) const
231 {
232 if (!_component.empty())
233 {
234 throw std::invalid_argument("Cannot tabulate coordinates for a "
235 "FunctionSpace that is a subspace.");
236 }
237
238 assert(_elements.front());
239 if (_elements.front()->is_mixed())
240 {
241 throw std::invalid_argument(
242 "Cannot tabulate coordinates for a mixed FunctionSpace.");
243 }
244
245 // Geometric dimension
246 assert(_mesh);
247 assert(_elements.front());
248 const std::size_t gdim = _mesh->geometry().dim();
249 const int tdim = _mesh->topology()->dim();
250
251 // Get dofmap local size
252 assert(_dofmaps.front());
253 std::shared_ptr<const common::IndexMap> index_map
254 = _dofmaps.front()->index_map;
255 assert(index_map);
256 const int index_map_bs = _dofmaps.front()->index_map_bs();
257 const int dofmap_bs = _dofmaps.front()->bs();
258
259 const int element_block_size = _elements.front()->block_size();
260 const std::size_t scalar_dofs
261 = _elements.front()->space_dimension() / element_block_size;
262 const std::int32_t num_dofs
263 = index_map_bs * (index_map->size_local() + index_map->num_ghosts())
264 / dofmap_bs;
265
266 std::size_t num_cell_types = _elements.size();
267
268 // Array to hold coordinates to return
269 const std::size_t shape_c0 = transpose ? 3 : num_dofs;
270 const std::size_t shape_c1 = transpose ? num_dofs : 3;
271 std::vector<geometry_type> coords(shape_c0 * shape_c1, 0);
272 using mdspan2_t = md::mdspan<geometry_type, md::dextents<std::size_t, 2>>;
273 using cmdspan4_t
274 = md::mdspan<const geometry_type, md::dextents<std::size_t, 4>>;
275 std::span<const geometry_type> x_g = _mesh->geometry().x();
276 std::vector<geometry_type> x_b(scalar_dofs * gdim);
277 mdspan2_t x(x_b.data(), scalar_dofs, gdim);
278
279 for (std::size_t i = 0; i < num_cell_types; ++i)
280 {
281 // Get the dof coordinates on the reference element
282 if (!_elements[i]->interpolation_ident())
283 {
284 throw std::runtime_error(
285 "Cannot evaluate dof coordinates - this element "
286 "does not have pointwise evaluation.");
287 }
288 const auto [X, Xshape] = _elements[i]->interpolation_points();
289
290 // Get coordinate map
292 = _mesh->geometry().cmaps().at(i);
293
294 // Prepare cell geometry
295 auto x_dofmap = _mesh->geometry().dofmaps().at(i);
296 const std::size_t num_dofs_g = cmap.dim();
297 std::vector<geometry_type> coordinate_dofs_b(num_dofs_g * gdim);
298 mdspan2_t coordinate_dofs(coordinate_dofs_b.data(), num_dofs_g, gdim);
299
300 auto map = _mesh->topology()->index_maps(tdim)[i];
301 assert(map);
302 const int num_cells = map->size_local() + map->num_ghosts();
303
304 std::span<const std::uint32_t> cell_info;
305 if (_elements[i]->needs_dof_transformations())
306 {
307 _mesh->topology_mutable()->create_entity_permutations();
308 cell_info = std::span(_mesh->topology()->get_cell_permutation_info());
309 }
310
311 const std::array<std::size_t, 4> phi_shape
312 = cmap.tabulate_shape(0, Xshape[0]);
313 std::vector<geometry_type> phi_b(std::reduce(
314 phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
315 cmdspan4_t phi_full(phi_b.data(), phi_shape);
316 cmap.tabulate(0, X, Xshape, phi_b);
317 auto phi
318 = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
319
320 // TODO: Check transform
321 //
322 // Basis function reference-to-conforming transformation function.
323 // This function is only reachable for elements with
324 // interpolation_ident() (point evaluation, e.g. Lagrange), which
325 // never need a DOF transformation, so this is always a no-op
326 // (nullptr) closure.
327 auto apply_dof_transformation
328 = _elements[i]->template dof_transformation_fn<geometry_type>(
330
331 for (int c = 0; c < num_cells; ++c)
332 {
333 // Extract cell geometry 'dofs'
334 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
335 for (std::size_t j = 0; j < x_dofs.size(); ++j)
336 for (std::size_t k = 0; k < gdim; ++k)
337 coordinate_dofs(j, k) = x_g[3 * x_dofs[j] + k];
338
339 // Tabulate dof coordinates on cell
340 cmap.push_forward(x, coordinate_dofs, phi);
341 if (apply_dof_transformation)
342 {
343 apply_dof_transformation(
344 x_b, std::span(cell_info.data(), cell_info.size()), c,
345 x.extent(1));
346 }
347
348 // Get cell dofmap
349 auto dofs = _dofmaps[i]->cell_dofs(c);
350
351 // Copy dof coordinates into vector
352 if (!transpose)
353 {
354 for (std::size_t j = 0; j < dofs.size(); ++j)
355 for (std::size_t k = 0; k < gdim; ++k)
356 coords[dofs[j] * 3 + k] = x(j, k);
357 }
358 else
359 {
360 for (std::size_t j = 0; j < dofs.size(); ++j)
361 for (std::size_t k = 0; k < gdim; ++k)
362 coords[k * num_dofs + dofs[j]] = x(j, k);
363 }
364 }
365 }
366
367 return coords;
368 }
369
371 std::shared_ptr<const mesh::Mesh<geometry_type>> mesh() const
372 {
373 return _mesh;
374 }
375
377 std::shared_ptr<const FiniteElement<geometry_type>> element() const
378 {
379 if (_elements.size() > 1)
380 {
381 throw std::invalid_argument(
382 "FunctionSpace has multiple elements, call `elements` instead.");
383 }
384
385 return elements(0);
386 }
387
389 std::shared_ptr<const FiniteElement<geometry_type>>
390 elements(int cell_type_idx) const
391 {
392 return _elements.at(cell_type_idx);
393 }
394
396 std::shared_ptr<const DofMap> dofmap() const
397 {
398 if (_dofmaps.size() > 1)
399 {
400 throw std::invalid_argument(
401 "FunctionSpace has multiple dofmaps, call `dofmaps` instead.");
402 }
403
404 return dofmaps().front();
405 }
406
408 const std::vector<std::shared_ptr<const DofMap>>& dofmaps() const
409 {
410 return _dofmaps;
411 }
412
413private:
414 // The mesh
415 std::shared_ptr<const mesh::Mesh<geometry_type>> _mesh;
416
417 // The finite element
418 std::vector<std::shared_ptr<const FiniteElement<geometry_type>>> _elements;
419
420 // The dofmap
421 std::vector<std::shared_ptr<const DofMap>> _dofmaps;
422
423 // The component w.r.t. to root space
424 std::vector<int> _component;
425
426 // Unique identifier for the space and for its root space
427 boost::uuids::uuid _id;
428 boost::uuids::uuid _root_space_id;
429};
430
441template <dolfinx::scalar T>
442std::array<std::vector<std::shared_ptr<const FunctionSpace<T>>>, 2>
444 const std::vector<
445 std::vector<std::array<std::shared_ptr<const FunctionSpace<T>>, 2>>>& V)
446{
447 assert(!V.empty());
448 std::vector<std::shared_ptr<const FunctionSpace<T>>> spaces0(V.size(),
449 nullptr);
450 std::vector<std::shared_ptr<const FunctionSpace<T>>> spaces1(V.front().size(),
451 nullptr);
452
453 // Loop over rows
454 for (std::size_t i = 0; i < V.size(); ++i)
455 {
456 // Loop over columns
457 for (std::size_t j = 0; j < V[i].size(); ++j)
458 {
459 auto& V0 = V[i][j][0];
460 auto& V1 = V[i][j][1];
461 if (V0 and V1)
462 {
463 if (!spaces0[i])
464 spaces0[i] = V0;
465 else
466 {
467 if (spaces0[i] != V0)
468 throw std::invalid_argument("Mismatched test space for row.");
469 }
470
471 if (!spaces1[j])
472 spaces1[j] = V1;
473 else
474 {
475 if (spaces1[j] != V1)
476 throw std::invalid_argument("Mismatched trial space for column.");
477 }
478 }
479 }
480 }
481
482 // Check that there are no null entries
483 if (std::find(spaces0.begin(), spaces0.end(), nullptr) != spaces0.end())
484 throw std::invalid_argument("Could not deduce all block test spaces.");
485 if (std::find(spaces1.begin(), spaces1.end(), nullptr) != spaces1.end())
486 throw std::invalid_argument("Could not deduce all block trial spaces.");
487
488 return {spaces0, spaces1};
489}
490
492template <typename U, typename V, typename W>
493FunctionSpace(U mesh, V element, W dofmap)
494 -> FunctionSpace<typename std::remove_cvref<
495 typename U::element_type>::type::geometry_type::value_type>;
496
497} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:38
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:59
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:52
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:222
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:194
Model of a finite element.
Definition FiniteElement.h:62
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:35
std::vector< int > component() const
Get the component with respect to the root superspace.
Definition FunctionSpace.h:208
bool symmetric() const
Indicate whether this function space represents a symmetric 2-tensor.
Definition FunctionSpace.h:212
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:390
std::shared_ptr< const DofMap > dofmap() const
The dofmap.
Definition FunctionSpace.h:396
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:62
FunctionSpace sub(const std::vector< int > &component) const
Create a subspace (view) for a specific component.
Definition FunctionSpace.h:112
bool contains(const FunctionSpace &V) const
Check whether V is subspace of this, or this itself.
Definition FunctionSpace.h:154
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
The mesh.
Definition FunctionSpace.h:371
virtual ~FunctionSpace()=default
Destructor.
std::shared_ptr< const FiniteElement< geometry_type > > element() const
The finite element.
Definition FunctionSpace.h:377
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:45
T geometry_type
Geometry type of the Mesh that the FunctionSpace is defined on.
Definition FunctionSpace.h:38
std::vector< geometry_type > tabulate_dof_coordinates(bool transpose) const
Tabulate the physical coordinates of all dofs on this process.
Definition FunctionSpace.h:230
const std::vector< std::shared_ptr< const DofMap > > & dofmaps() const
The dofmaps.
Definition FunctionSpace.h:408
FunctionSpace(FunctionSpace &&V)=default
Move constructor.
std::pair< FunctionSpace, std::vector< std::vector< std::int32_t > > > collapse() const
Definition FunctionSpace.h:180
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Finite element method functionality.
Definition assemble_expression_impl.h:24
@ transpose
Transpose.
Definition FiniteElement.h:30
@ standard
Standard.
Definition FiniteElement.h:29
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:443
FunctionSpace(U mesh, V element, W dofmap) -> FunctionSpace< typename std::remove_cvref< typename U::element_type >::type::geometry_type::value_type >
Type deduction.
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32