DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
Function.h
1// Copyright (C) 2003-2026 Anders Logg, Garth N. Wells and Massimiliano Leoni
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 "DofMap.h"
10#include "FiniteElement.h"
11#include "FunctionSpace.h"
12#include "interpolate.h"
13#include <algorithm>
14#include <basix/mdspan.hpp>
15#include <concepts>
16#include <dolfinx/common/IndexMap.h>
17#include <dolfinx/common/types.h>
18#include <dolfinx/la/Vector.h>
19#include <dolfinx/mesh/Geometry.h>
20#include <dolfinx/mesh/Mesh.h>
21#include <dolfinx/mesh/Topology.h>
22#include <functional>
23#include <memory>
24#include <numeric>
25#include <span>
26#include <stdexcept>
27#include <string>
28#include <utility>
29#include <vector>
30
31namespace dolfinx::fem
32{
42template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
44{
45public:
48 using value_type = T;
50 using geometry_type = U;
51
54 explicit Function(std::shared_ptr<const FunctionSpace<geometry_type>> V)
55 : _function_space(V), _x(std::make_shared<la::Vector<value_type>>(
56 V->dofmaps().front()->index_map,
57 V->dofmaps().front()->index_map_bs()))
58 {
59 if (!V->component().empty())
60 {
61 throw std::invalid_argument(
62 "Cannot create Function from subspace. Consider "
63 "collapsing the function space");
64 }
65 }
66
75 Function(std::shared_ptr<const FunctionSpace<geometry_type>> V,
76 std::shared_ptr<la::Vector<value_type>> x)
77 : _function_space(V), _x(x)
78 {
79 // NOTE: We do not check for a subspace since this constructor is
80 // used for creating subfunctions
81
82 // Assertion uses '<=' to deal with sub-functions
83 assert(V->dofmap());
84 assert(V->dofmap()->index_map->size_global() * V->dofmap()->index_map_bs()
85 <= _x->bs() * _x->index_map()->size_global());
86 }
87
88 // Copy constructor
89 Function(const Function& v) = delete;
90
92 Function(Function&& v) = default;
93
95 ~Function() = default;
96
98 Function& operator=(Function&& v) = default;
99
100 // Assignment
101 Function& operator=(const Function& v) = delete;
102
106 Function sub(int i) const
107 {
108 auto sub_space = std::make_shared<FunctionSpace<geometry_type>>(
109 _function_space->sub({i}));
110 assert(sub_space);
111 return Function(sub_space, _x);
112 }
113
118 {
119 // Create new collapsed FunctionSpace
120 auto [V, map] = _function_space->collapse();
121
122 // Create new vector
123 auto x = std::make_shared<la::Vector<value_type>>(
124 V.dofmap()->index_map, V.dofmap()->index_map_bs());
125
126 // Copy values into new vector
127 std::span<const value_type> x_old = _x->array();
128 std::span<value_type> x_new = x->array();
129 for (auto mapj : map)
130 {
131 for (std::size_t i = 0; i < mapj.size(); ++i)
132 {
133 assert(i < x_new.size());
134 assert(static_cast<std::size_t>(mapj[i]) < x_old.size());
135 x_new[i] = x_old[mapj[i]];
136 }
137 }
138
139 return Function(
140 std::make_shared<FunctionSpace<geometry_type>>(std::move(V)), x);
141 }
142
145 std::shared_ptr<const FunctionSpace<geometry_type>> function_space() const
146 {
147 return _function_space;
148 }
149
151 std::shared_ptr<const la::Vector<value_type>> x() const { return _x; }
152
154 std::shared_ptr<la::Vector<value_type>> x() { return _x; }
155
161 const std::function<
162 std::pair<std::vector<value_type>, std::vector<std::size_t>>(
163 md::mdspan<const geometry_type,
164 md::extents<std::size_t, 3, md::dynamic_extent>>)>& f,
165 mesh::CellRange auto&& cells)
166 {
167 assert(_function_space);
168 assert(_function_space->element());
169 assert(_function_space->mesh());
170 const std::vector<geometry_type> x
172 *_function_space->element(), _function_space->mesh()->geometry(),
173 cells);
174 md::mdspan<const geometry_type,
175 md::extents<std::size_t, 3, md::dynamic_extent>>
176 x_view(x.data(), 3, x.size() / 3);
177
178 const auto [fx, fshape] = f(x_view);
179 assert(fshape.size() <= 2);
180 if (std::size_t vs = _function_space->element()->value_size();
181 vs == 1 and fshape.size() == 1)
182 {
183 // Check for scalar-valued functions
184 if (fshape.front() != x.size() / 3)
185 {
186 throw std::invalid_argument(
187 "Data returned by callable has wrong length");
188 }
189 }
190 else
191 {
192 // Check for vector/tensor value
193 if (fshape.size() != 2)
194 throw std::invalid_argument("Expected 2D array of data");
195
196 if (fshape[0] != vs)
197 {
198 throw std::invalid_argument(
199 "Data returned by callable has wrong shape(0) size");
200 }
201
202 if (fshape[1] != x.size() / 3)
203 {
204 throw std::invalid_argument(
205 "Data returned by callable has wrong shape(1) size");
206 }
207 }
208
209 std::array<std::size_t, 2> _fshape;
210 if (fshape.size() == 1)
211 _fshape = {1, fshape[0]};
212 else
213 _fshape = {fshape[0], fshape[1]};
214
215 fem::interpolate(*this, std::span<const value_type>(fx.data(), fx.size()),
216 _fshape, cells);
217 }
218
223 const std::function<
224 std::pair<std::vector<value_type>, std::vector<std::size_t>>(
225 md::mdspan<const geometry_type,
226 md::extents<std::size_t, 3, md::dynamic_extent>>)>& f)
227 {
228 assert(_function_space);
229 assert(_function_space->mesh());
230 int tdim = _function_space->mesh()->topology()->dim();
231 auto cmap = _function_space->mesh()->topology()->index_map(tdim);
232 assert(cmap);
233 std::int32_t num_cells = cmap->size_local() + cmap->num_ghosts();
234 interpolate(f, std::ranges::iota_view(0, num_cells));
235 }
236
253 mesh::CellRange auto&& cells0, mesh::CellRange auto&& cells1)
254 {
255 fem::interpolate(*this, cells1, u0, cells0);
256 }
257
265 mesh::CellRange auto&& cells)
266 {
267 fem::interpolate(*this, u, cells);
268 }
269
276 {
277 assert(_function_space);
278 assert(_function_space->mesh());
279 int tdim = _function_space->mesh()->topology()->dim();
280 auto cmap = _function_space->mesh()->topology()->index_map(tdim);
281 assert(cmap);
282 fem::interpolate(*this, u);
283 }
284
298 mesh::CellRange auto&& cells, double tol, int maxit,
299 const geometry::PointOwnershipData<U>& interpolation_data)
300 {
301 fem::interpolate(*this, u, cells, tol, maxit, interpolation_data);
302 }
303
320 void eval(std::span<const geometry_type> x, std::array<std::size_t, 2> xshape,
321 mesh::CellRange auto&& cells, std::span<value_type> u,
322 std::array<std::size_t, 2> ushape, double tol, int maxit) const
323 {
324 if (cells.empty())
325 return;
326
327 assert(x.size() == xshape[0] * xshape[1]);
328 assert(u.size() == ushape[0] * ushape[1]);
329
330 // TODO: This could be easily made more efficient by exploiting
331 // points being ordered by the cell to which they belong.
332
333 if (xshape[0] != cells.size())
334 {
335 throw std::invalid_argument(
336 "Number of points and number of cells must be equal.");
337 }
338
339 if (xshape[0] != ushape[0])
340 {
341 throw std::invalid_argument(
342 "Length of array for Function values must be the "
343 "same as the number of points.");
344 }
345
346 // Get mesh
347 assert(_function_space);
348 auto mesh = _function_space->mesh();
349 assert(mesh);
350 const std::size_t gdim = mesh->geometry().dim();
351 const std::size_t tdim = mesh->topology()->dim();
352 auto map = mesh->topology()->index_map(tdim);
353
354 // Get coordinate map
356 = mesh->geometry().cmaps().front();
357
358 // Get geometry data
359 auto x_dofmap = mesh->geometry().dofmaps().front();
360 const std::size_t num_dofs_g = cmap.dim();
361 auto x_g = mesh->geometry().x();
362
363 // Get element
364 auto element = _function_space->element();
365 assert(element);
366 const int bs_element = element->block_size();
367 const std::size_t reference_value_size = element->reference_value_size();
368 // Size of the push-forward output for one block. On a manifold a
369 // Piola-mapped element has gdim physical components and only tdim
370 // reference ones.
371 const std::size_t value_size = element->physical_base_value_size();
372 const std::size_t space_dimension = element->space_dimension() / bs_element;
373
374 // If the space has sub elements, concatenate the evaluations on the
375 // sub elements
376 const int num_sub_elements = element->num_sub_elements();
377 if (num_sub_elements > 1 and num_sub_elements != bs_element)
378 {
379 throw std::invalid_argument("Function::eval is not supported for mixed "
380 "elements. Extract subspaces.");
381 }
382
383 // Create work vector for expansion coefficients
384 std::vector<value_type> coefficients(space_dimension * bs_element);
385
386 // Get dofmap
387 std::shared_ptr<const DofMap> dofmap = _function_space->dofmap();
388 assert(dofmap);
389 const int bs_dof = dofmap->bs();
390
391 std::span<const std::uint32_t> cell_info;
392 if (element->needs_dof_transformations())
393 {
394 mesh->topology_mutable()->create_cell_permutations();
395 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
396 }
397
398 std::vector<geometry_type> coord_dofs_b(num_dofs_g * gdim);
399 impl::mdspan_t<geometry_type, 2> coord_dofs(coord_dofs_b.data(), num_dofs_g,
400 gdim);
401 std::vector<geometry_type> xp_b(1 * gdim);
402 impl::mdspan_t<geometry_type, 2> xp(xp_b.data(), 1, gdim);
403
404 // Loop over points
405 std::ranges::fill(u, 0);
406 std::span<const value_type> _v = _x->array();
407
408 // Evaluate geometry basis at point (0, 0, 0) on the reference cell.
409 // Used in affine case.
410 std::array<std::size_t, 4> phi0_shape = cmap.tabulate_shape(1, 1);
411 std::vector<geometry_type> phi0_b(std::reduce(
412 phi0_shape.begin(), phi0_shape.end(), 1, std::multiplies{}));
413 impl::mdspan_t<const geometry_type, 4> phi0(phi0_b.data(), phi0_shape);
414 cmap.tabulate(1, std::vector<geometry_type>(tdim), {1, tdim}, phi0_b);
415 auto dphi0
416 = md::submdspan(phi0, std::pair(1, tdim + 1), 0, md::full_extent, 0);
417
418 // Data structure for evaluating geometry basis at specific points.
419 // Used in non-affine case.
420 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, 1);
421 std::vector<geometry_type> phi_b(
422 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
423 impl::mdspan_t<const geometry_type, 4> phi(phi_b.data(), phi_shape);
424 auto dphi
425 = md::submdspan(phi, std::pair(1, tdim + 1), 0, md::full_extent, 0);
426
427 // Reference coordinates for each point
428 std::vector<geometry_type> Xb(xshape[0] * tdim);
429 impl::mdspan_t<geometry_type, 2> X(Xb.data(), xshape[0], tdim);
430
431 // Geometry data at each point
432 std::vector<geometry_type> J_b(xshape[0] * gdim * tdim);
433 impl::mdspan_t<geometry_type, 3> J(J_b.data(), xshape[0], gdim, tdim);
434 std::vector<geometry_type> K_b(xshape[0] * tdim * gdim);
435 impl::mdspan_t<geometry_type, 3> K(K_b.data(), xshape[0], tdim, gdim);
436 std::vector<geometry_type> detJ(xshape[0]);
437 std::vector<geometry_type> det_scratch(2 * gdim * tdim);
438
439 // Scratch space for pull-back of point coordinates for non-affine cells
440 std::vector<geometry_type> pull_back_scratch(
441 cmap.is_affine() ? 0 : cmap.pull_back_working_size(gdim));
442
443 // Prepare geometry data in each cell
444 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
445 {
446 // Skip negative cell indices
447 if (*cell_it < 0)
448 continue;
449
450 // Get cell geometry (coordinate dofs)
451 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
452 assert(x_dofs.size() == num_dofs_g);
453 for (std::size_t i = 0; i < num_dofs_g; ++i)
454 {
455 const int pos = 3 * x_dofs[i];
456 for (std::size_t j = 0; j < gdim; ++j)
457 coord_dofs(i, j) = x_g[pos + j];
458 }
459
460 std::size_t p = std::ranges::distance(cells.begin(), cell_it);
461 for (std::size_t j = 0; j < gdim; ++j)
462 xp(0, j) = x[p * xshape[1] + j];
463
464 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
465 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
466
467 std::array<geometry_type, 3> Xpb{0, 0, 0};
468 md::mdspan<geometry_type, md::extents<std::size_t, 1, md::dynamic_extent>>
469 Xp(Xpb.data(), 1, tdim);
470
471 // Compute reference coordinates X, and J, detJ and K
472 if (cmap.is_affine())
473 {
475 _J);
477 std::array<geometry_type, 3> x0{0, 0, 0};
478 for (std::size_t i = 0; i < coord_dofs.extent(1); ++i)
479 x0[i] += coord_dofs(0, i);
481 detJ[p]
483 _J, det_scratch);
484 }
485 else
486 {
487 // Pull-back physical point xp to reference coordinate Xp
488 cmap.pull_back_nonaffine(Xp, xp, coord_dofs, pull_back_scratch, tol,
489 maxit);
490 cmap.tabulate(1, std::span(Xpb.data(), tdim), {1, tdim}, phi_b);
492 _J);
494 detJ[p]
496 _J, det_scratch);
497 }
498
499 for (std::size_t j = 0; j < X.extent(1); ++j)
500 X(p, j) = Xpb[j];
501 }
502
503 // Prepare basis function data structures
504 std::vector<geometry_type> basis_derivatives_reference_values_b(
505 1 * xshape[0] * space_dimension * reference_value_size);
506 impl::mdspan_t<const geometry_type, 4> basis_derivatives_reference_values(
507 basis_derivatives_reference_values_b.data(), 1, xshape[0],
508 space_dimension, reference_value_size);
509 std::vector<geometry_type> basis_values_b(space_dimension * value_size);
510 impl::mdspan_t<geometry_type, 2> basis_values(basis_values_b.data(),
511 space_dimension, value_size);
512
513 // Compute basis on reference element
514 element->tabulate(basis_derivatives_reference_values_b, Xb,
515 {X.extent(0), X.extent(1)}, 0);
516
517 using xu_t = impl::mdspan_t<geometry_type, 2>;
518 using xU_t = impl::mdspan_t<const geometry_type, 2>;
519 using xJ_t = impl::mdspan_t<const geometry_type, 2>;
520 using xK_t = impl::mdspan_t<const geometry_type, 2>;
521 auto push_forward_fn
522 = element->basix_element().template map_fn<xu_t, xU_t, xJ_t, xK_t>();
523
524 // Transformation function for basis function values
525 auto apply_dof_transformation
526 = element->template dof_transformation_fn<geometry_type>(
528
529 // Size of tensor for symmetric elements, unused in non-symmetric
530 // case, but placed outside the loop for pre-computation.
531 int matrix_size = 0;
532 if (element->symmetric())
533 {
534 while (matrix_size * matrix_size < (int)ushape[1])
535 ++matrix_size;
536 }
537
538 const std::size_t num_basis_values = space_dimension * reference_value_size;
539 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
540 {
541 if (*cell_it < 0) // Skip negative cell indices
542 continue;
543
544 // Permute the reference basis function values to account for the
545 // cell's orientation
546 std::size_t p = std::ranges::distance(cells.begin(), cell_it);
547 if (apply_dof_transformation)
548 {
549 apply_dof_transformation(
550 std::span(basis_derivatives_reference_values_b.data()
551 + p * num_basis_values,
552 num_basis_values),
553 cell_info, *cell_it, reference_value_size);
554 }
555
556 {
557 auto _U = md::submdspan(basis_derivatives_reference_values, 0, p,
558 md::full_extent, md::full_extent);
559 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
560 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
561 push_forward_fn(basis_values, _U, _J, detJ[p], _K);
562 }
563
564 // Get degrees of freedom for current cell
565 std::span<const std::int32_t> dofs = dofmap->cell_dofs(*cell_it);
566 for (std::size_t i = 0; i < dofs.size(); ++i)
567 for (int k = 0; k < bs_dof; ++k)
568 coefficients[bs_dof * i + k] = _v[bs_dof * dofs[i] + k];
569
570 if (element->symmetric())
571 {
572 // Compute expansion
573 int row = 0;
574 int rowstart = 0;
575 for (int k = 0; k < bs_element; ++k)
576 {
577 if (k - rowstart > row)
578 {
579 row++;
580 rowstart = k;
581 }
582 for (std::size_t i = 0; i < space_dimension; ++i)
583 {
584 for (std::size_t j = 0; j < value_size; ++j)
585 {
586 u[p * ushape[1]
587 + (j * bs_element + row * matrix_size + k - rowstart)]
588 += coefficients[bs_element * i + k] * basis_values(i, j);
589 if (k - rowstart != row)
590 {
591 u[p * ushape[1]
592 + (j * bs_element + row + matrix_size * (k - rowstart))]
593 += coefficients[bs_element * i + k] * basis_values(i, j);
594 }
595 }
596 }
597 }
598 }
599 else
600 {
601 // Compute expansion
602 for (int k = 0; k < bs_element; ++k)
603 {
604 for (std::size_t i = 0; i < space_dimension; ++i)
605 {
606 for (std::size_t j = 0; j < value_size; ++j)
607 {
608 u[p * ushape[1] + (j * bs_element + k)]
609 += coefficients[bs_element * i + k] * basis_values(i, j);
610 }
611 }
612 }
613 }
614 }
615 }
616
618 std::string name = "u";
619
620private:
621 // Function space
622 std::shared_ptr<const FunctionSpace<geometry_type>> _function_space;
623
624 // Vector of expansion coefficients (local)
625 std::shared_ptr<la::Vector<value_type>> _x;
626};
627
628} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:39
static void compute_jacobian(const U &dphi, const V &cell_geometry, W &&J)
Definition CoordinateElement.h:139
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:61
std::size_t pull_back_working_size(std::size_t gdim) const
Compute the working array size required for pull back.
Definition CoordinateElement.cpp:253
static void compute_jacobian_inverse(const U &J, V &&K)
Compute the inverse of the Jacobian.
Definition CoordinateElement.h:148
void pull_back_nonaffine(mdspan2_t< T > X, mdspan2_t< const T > x, mdspan2_t< const T > cell_geometry, std::span< T > working_array, double tol, int maxit) const
Compute reference coordinates X for physical coordinates x for a non-affine map.
Definition CoordinateElement.cpp:90
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:54
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:225
bool is_affine() const noexcept
Check if geometry map is affine.
Definition CoordinateElement.h:295
static double compute_jacobian_determinant(const U &J, std::span< typename U::value_type > w)
Compute the determinant of the Jacobian.
Definition CoordinateElement.h:165
static void pull_back_affine(U &&X, const V &K, std::array< T, 3 > x0, const W &x)
Compute reference coordinates X for physical coordinates x for an affine map. For the affine case,...
Definition CoordinateElement.h:221
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:36
Definition Function.h:44
U geometry_type
Geometry type of the Mesh that the Function is defined on.
Definition Function.h:50
void interpolate(const Function< value_type, geometry_type > &u, mesh::CellRange auto &&cells)
Interpolate a Function over a subset of cells.
Definition Function.h:264
void interpolate(const Function< value_type, geometry_type > &u)
Interpolate a Function over all cells.
Definition Function.h:275
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:145
Function(std::shared_ptr< const FunctionSpace< geometry_type > > V)
Create function on given function space.
Definition Function.h:54
void interpolate(const std::function< std::pair< std::vector< value_type >, std::vector< std::size_t > >(md::mdspan< const geometry_type, md::extents< std::size_t, 3, md::dynamic_extent > >)> &f, mesh::CellRange auto &&cells)
Interpolate an expression f(x) over a set of cells.
Definition Function.h:160
void interpolate(const Function< value_type, geometry_type > &u0, mesh::CellRange auto &&cells0, mesh::CellRange auto &&cells1)
Interpolate a Function over a subset of cells.
Definition Function.h:252
void eval(std::span< const geometry_type > x, std::array< std::size_t, 2 > xshape, mesh::CellRange auto &&cells, std::span< value_type > u, std::array< std::size_t, 2 > ushape, double tol, int maxit) const
Evaluate the Function at points.
Definition Function.h:320
Function collapse() const
Collapse a subfunction (view into a Function) to a stand-alone Function.
Definition Function.h:117
void interpolate(const std::function< std::pair< std::vector< value_type >, std::vector< std::size_t > >(md::mdspan< const geometry_type, md::extents< std::size_t, 3, md::dynamic_extent > >)> &f)
Interpolate an expression f(x) on the whole domain.
Definition Function.h:222
Function sub(int i) const
Extract a sub-function (a view into the Function).
Definition Function.h:106
std::string name
Name.
Definition Function.h:618
std::shared_ptr< la::Vector< value_type > > x()
Underlying vector.
Definition Function.h:154
void interpolate(const Function< value_type, geometry_type > &u, mesh::CellRange auto &&cells, double tol, int maxit, const geometry::PointOwnershipData< U > &interpolation_data)
Interpolate a Function defined on a different mesh.
Definition Function.h:297
~Function()=default
Destructor.
Function(Function &&v)=default
Move constructor.
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:151
Function & operator=(Function &&v)=default
Move assignment.
Function(std::shared_ptr< const FunctionSpace< geometry_type > > V, std::shared_ptr< la::Vector< value_type > > x)
Create function on given function space with a given vector.
Definition Function.h:75
T value_type
Definition Function.h:48
A vector that can be distributed across processes.
Definition Vector.h:53
Requirement on range of cell indices.
Definition Topology.h:32
Finite element method functionality.
Definition assemble_expression_impl.h:28
@ standard
Standard.
Definition FiniteElement.h:33
std::vector< T > interpolation_coords(const fem::FiniteElement< T > &element, const mesh::Geometry< T > &geometry, mesh::CellRange auto &&cells)
Compute the evaluation points in the physical space at which an expression should be computed to inte...
Definition interpolate.h:46
void interpolate(Function< T, U > &u1, mesh::CellRange auto &&cells1, const Expression< T, U > &e0, mesh::CellRange auto &&cells0)
Interpolate an Expression into a Function over a subset of cells.
Definition expression_evaluate.h:209
Linear algebra interface.
Definition dolfinx_la.h:7
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
Information on the ownership of points distributed across processes.
Definition utils.h:36