Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/v0.3.0/v0.9.0/cpp
DOLFINx  0.3.0
DOLFINx C++ interface
Function.h
1 // Copyright (C) 2003-2020 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 "FunctionSpace.h"
10 #include "interpolate.h"
11 #include <dolfinx/common/IndexMap.h>
12 #include <dolfinx/common/UniqueIdGenerator.h>
13 #include <dolfinx/common/array2d.h>
14 #include <dolfinx/fem/DofMap.h>
15 #include <dolfinx/fem/FiniteElement.h>
16 #include <dolfinx/la/PETScVector.h>
17 #include <dolfinx/la/Vector.h>
18 #include <dolfinx/mesh/Geometry.h>
19 #include <dolfinx/mesh/Mesh.h>
20 #include <dolfinx/mesh/Topology.h>
21 #include <functional>
22 #include <memory>
23 #include <numeric>
24 #include <petscvec.h>
25 #include <string>
26 #include <utility>
27 #include <variant>
28 #include <vector>
29 #include <xtensor/xadapt.hpp>
30 #include <xtensor/xtensor.hpp>
31 #include <xtl/xspan.hpp>
32 
33 namespace dolfinx::fem
34 {
35 
36 class FunctionSpace;
37 
44 
45 template <typename T>
46 class Function
47 {
48 public:
51  explicit Function(std::shared_ptr<const FunctionSpace> V)
52  : _id(common::UniqueIdGenerator::id()), _function_space(V),
53  _x(std::make_shared<la::Vector<T>>(V->dofmap()->index_map,
54  V->dofmap()->index_map_bs()))
55  {
56  if (!V->component().empty())
57  {
58  throw std::runtime_error("Cannot create Function from subspace. Consider "
59  "collapsing the function space");
60  }
61  }
62 
69  Function(std::shared_ptr<const FunctionSpace> V,
70  std::shared_ptr<la::Vector<T>> x)
71  : _id(common::UniqueIdGenerator::id()), _function_space(V), _x(x)
72  {
73  // We do not check for a subspace since this constructor is used for
74  // creating subfunctions
75 
76  // Assertion uses '<=' to deal with sub-functions
77  assert(V->dofmap());
78  assert(V->dofmap()->index_map->size_global() * V->dofmap()->index_map_bs()
79  <= _x->bs() * _x->map()->size_global());
80  }
81 
82  // Copy constructor
83  Function(const Function& v) = delete;
84 
87  : name(std::move(v.name)), _id(std::move(v._id)),
88  _function_space(std::move(v._function_space)), _x(std::move(v._x)),
89  _petsc_vector(std::exchange(v._petsc_vector, nullptr))
90  {
91  }
92 
94  virtual ~Function()
95  {
96  if (_petsc_vector)
97  VecDestroy(&_petsc_vector);
98  }
99 
101  Function& operator=(Function&& v) noexcept
102  {
103  name = std::move(v.name);
104  _id = std::move(v._id);
105  _function_space = std::move(v._function_space);
106  _x = std::move(v._x);
107  std::swap(_petsc_vector, v._petsc_vector);
108 
109  return *this;
110  }
111 
112  // Assignment
113  Function& operator=(const Function& v) = delete;
114 
118  Function sub(int i) const
119  {
120  auto sub_space = _function_space->sub({i});
121  assert(sub_space);
122  return Function(sub_space, _x);
123  }
124 
129  {
130  // Create new collapsed FunctionSpace
131  const auto [function_space_new, collapsed_map]
132  = _function_space->collapse();
133 
134  // Create new vector
135  assert(function_space_new);
136  auto vector_new = std::make_shared<la::Vector<T>>(
137  function_space_new->dofmap()->index_map,
138  function_space_new->dofmap()->index_map_bs());
139 
140  // Copy values into new vector
141  const std::vector<T>& x_old = _x->array();
142  std::vector<T>& x_new = vector_new->mutable_array();
143  for (std::size_t i = 0; i < collapsed_map.size(); ++i)
144  {
145  assert((int)i < x_new.size());
146  assert(collapsed_map[i] < x_old.size());
147  x_new[i] = x_old[collapsed_map[i]];
148  }
149 
150  return Function(function_space_new, vector_new);
151  }
152 
155  std::shared_ptr<const FunctionSpace> function_space() const
156  {
157  return _function_space;
158  }
159 
163  Vec vector() const
164  {
165  // Check that this is not a sub function
166  assert(_function_space->dofmap());
167  assert(_function_space->dofmap()->index_map);
168  if (_x->bs() * _x->map()->size_global()
169  != _function_space->dofmap()->index_map->size_global()
170  * _function_space->dofmap()->index_map_bs())
171  {
172  throw std::runtime_error(
173  "Cannot access a non-const vector from a subfunction");
174  }
175 
176  // Check that data type is the same as the PETSc build
177  if constexpr (std::is_same<T, PetscScalar>::value)
178  {
179  if (!_petsc_vector)
180  {
181  _petsc_vector = la::create_ghosted_vector(
182  *_function_space->dofmap()->index_map,
183  _function_space->dofmap()->index_map_bs(), _x->mutable_array());
184  }
185  return _petsc_vector;
186  }
187  else
188  {
189  throw std::runtime_error(
190  "Cannot return PETSc vector wrapper. Type mismatch");
191  }
192  }
193 
195  std::shared_ptr<const la::Vector<T>> x() const { return _x; }
196 
198  std::shared_ptr<la::Vector<T>> x() { return _x; }
199 
202  void interpolate(const Function<T>& v) { fem::interpolate(*this, v); }
203 
207  const std::function<xt::xarray<T>(const xt::xtensor<double, 2>&)>& f)
208  {
209  assert(_function_space);
210  assert(_function_space->element());
211  assert(_function_space->mesh());
212  const int tdim = _function_space->mesh()->topology().dim();
213  auto cell_map = _function_space->mesh()->topology().index_map(tdim);
214  assert(cell_map);
215  const int num_cells = cell_map->size_local() + cell_map->num_ghosts();
216  std::vector<std::int32_t> cells(num_cells, 0);
217  std::iota(cells.begin(), cells.end(), 0);
218  // FIXME: Remove interpolation coords as it should be done internally in
219  // fem::interpolate
220  const xt::xtensor<double, 2> x = fem::interpolation_coords(
221  *_function_space->element(), *_function_space->mesh(), cells);
222  fem::interpolate(*this, f, x, cells);
223  }
224 
235  void eval(const xt::xtensor<double, 2>& x,
236  const xtl::span<const std::int32_t>& cells,
237  xt::xtensor<T, 2>& u) const
238  {
239  // TODO: This could be easily made more efficient by exploiting points
240  // being ordered by the cell to which they belong.
241 
242  if (x.shape(0) != cells.size())
243  {
244  throw std::runtime_error(
245  "Number of points and number of cells must be equal.");
246  }
247  if (x.shape(0) != u.shape(0))
248  {
249  throw std::runtime_error(
250  "Length of array for Function values must be the "
251  "same as the number of points.");
252  }
253 
254  // Get mesh
255  assert(_function_space);
256  std::shared_ptr<const mesh::Mesh> mesh = _function_space->mesh();
257  assert(mesh);
258  const std::size_t gdim = mesh->geometry().dim();
259  const std::size_t tdim = mesh->topology().dim();
260  auto map = mesh->topology().index_map(tdim);
261 
262  // Get geometry data
263  const graph::AdjacencyList<std::int32_t>& x_dofmap
264  = mesh->geometry().dofmap();
265  // FIXME: Add proper interface for num coordinate dofs
266  const std::size_t num_dofs_g = x_dofmap.num_links(0);
267  const xt::xtensor<double, 2>& x_g = mesh->geometry().x();
268 
269  // Get coordinate map
270  const fem::CoordinateElement& cmap = mesh->geometry().cmap();
271 
272  // Get element
273  assert(_function_space->element());
274  std::shared_ptr<const fem::FiniteElement> element
275  = _function_space->element();
276  assert(element);
277  const int bs_element = element->block_size();
278  const std::size_t reference_value_size
279  = element->reference_value_size() / bs_element;
280  const std::size_t value_size = element->value_size() / bs_element;
281  const std::size_t space_dimension = element->space_dimension() / bs_element;
282 
283  // If the space has sub elements, concatenate the evaluations on the sub
284  // elements
285  const int num_sub_elements = element->num_sub_elements();
286  if (num_sub_elements > 1 and num_sub_elements != bs_element)
287  {
288  throw std::runtime_error("Function::eval is not supported for mixed "
289  "elements. Extract subspaces.");
290  }
291 
292  // Prepare geometry data structures
293  xt::xtensor<double, 2> X({1, tdim});
294  xt::xtensor<double, 3> J
295  = xt::zeros<double>({static_cast<std::size_t>(1), gdim, tdim});
296  xt::xtensor<double, 3> K
297  = xt::zeros<double>({static_cast<std::size_t>(1), tdim, gdim});
298  xt::xtensor<double, 1> detJ = xt::zeros<double>({1});
299 
300  // Prepare basis function data structures
301  xt::xtensor<double, 4> basis_derivatives_reference_values(
302  {1, 1, space_dimension, reference_value_size});
303  auto basis_reference_values = xt::view(basis_derivatives_reference_values,
304  0, xt::all(), xt::all(), xt::all());
305  xt::xtensor<double, 3> basis_values(
306  {static_cast<std::size_t>(1), space_dimension, value_size});
307 
308  // Create work vector for expansion coefficients
309  std::vector<T> coefficients(space_dimension * bs_element);
310 
311  // Get dofmap
312  std::shared_ptr<const fem::DofMap> dofmap = _function_space->dofmap();
313  assert(dofmap);
314  const int bs_dof = dofmap->bs();
315 
316  xtl::span<const std::uint32_t> cell_info;
317  if (element->needs_dof_transformations())
318  {
319  mesh->topology_mutable().create_entity_permutations();
320  cell_info = xtl::span(mesh->topology().get_cell_permutation_info());
321  }
322 
323  xt::xtensor<double, 2> coordinate_dofs
324  = xt::zeros<double>({num_dofs_g, gdim});
325  xt::xtensor<double, 2> xp
326  = xt::zeros<double>({static_cast<std::size_t>(1), gdim});
327 
328  // Loop over points
329  std::fill(u.data(), u.data() + u.size(), 0.0);
330  const std::vector<T>& _v = _x->mutable_array();
331 
332  const std::function<void(const xtl::span<double>&,
333  const xtl::span<const std::uint32_t>&,
334  std::int32_t, int)>
335  apply_dof_transformation
336  = element->get_dof_transformation_function<double>();
337 
338  for (std::size_t p = 0; p < cells.size(); ++p)
339  {
340  const int cell_index = cells[p];
341 
342  // Skip negative cell indices
343  if (cell_index < 0)
344  continue;
345 
346  // Get cell geometry (coordinate dofs)
347  auto x_dofs = x_dofmap.links(cell_index);
348  for (std::size_t i = 0; i < num_dofs_g; ++i)
349  for (std::size_t j = 0; j < gdim; ++j)
350  coordinate_dofs(i, j) = x_g(x_dofs[i], j);
351 
352  for (std::size_t j = 0; j < gdim; ++j)
353  xp(0, j) = x(p, j);
354 
355  // Compute reference coordinates X, and J, detJ and K
356  cmap.pull_back(X, J, detJ, K, xp, coordinate_dofs);
357 
358  // Compute basis on reference element
359  element->tabulate(basis_derivatives_reference_values, X, 0);
360 
361  // Permute the reference values to account for the cell's orientation
362  apply_dof_transformation(xtl::span(basis_reference_values.data(),
363  basis_reference_values.size()),
364  cell_info, cell_index, reference_value_size);
365 
366  // Push basis forward to physical element
367  element->transform_reference_basis(basis_values, basis_reference_values,
368  J, detJ, K);
369 
370  // Get degrees of freedom for current cell
371  xtl::span<const std::int32_t> dofs = dofmap->cell_dofs(cell_index);
372  for (std::size_t i = 0; i < dofs.size(); ++i)
373  for (int k = 0; k < bs_dof; ++k)
374  coefficients[bs_dof * i + k] = _v[bs_dof * dofs[i] + k];
375 
376  // Compute expansion
377  auto u_row = xt::row(u, p);
378  for (int k = 0; k < bs_element; ++k)
379  {
380  for (std::size_t i = 0; i < space_dimension; ++i)
381  {
382  for (std::size_t j = 0; j < value_size; ++j)
383  {
384  u_row[j * bs_element + k]
385  += coefficients[bs_element * i + k] * basis_values(0, i, j);
386  }
387  }
388  }
389  }
390  }
391 
394  xt::xtensor<T, 2> compute_point_values() const
395  {
396  assert(_function_space);
397  std::shared_ptr<const mesh::Mesh> mesh = _function_space->mesh();
398  assert(mesh);
399  const int tdim = mesh->topology().dim();
400 
401  // Compute in tensor (one for scalar function, . . .)
402  const std::size_t value_size_loc = _function_space->element()->value_size();
403 
404  // Resize Array for holding point values
405  xt::xtensor<T, 2> point_values(
406  {mesh->geometry().x().shape(0), value_size_loc});
407 
408  // Prepare cell geometry
409  const graph::AdjacencyList<std::int32_t>& x_dofmap
410  = mesh->geometry().dofmap();
411 
412  // FIXME: Add proper interface for num coordinate dofs
413  const int num_dofs_g = x_dofmap.num_links(0);
414  const xt::xtensor<double, 2>& x_g = mesh->geometry().x();
415 
416  // Interpolate point values on each cell (using last computed value if
417  // not continuous, e.g. discontinuous Galerkin methods)
418  auto map = mesh->topology().index_map(tdim);
419  assert(map);
420  const std::int32_t num_cells = map->size_local() + map->num_ghosts();
421 
422  std::vector<std::int32_t> cells(x_g.shape(0));
423  for (std::int32_t c = 0; c < num_cells; ++c)
424  {
425  // Get coordinates for all points in cell
426  xtl::span<const std::int32_t> dofs = x_dofmap.links(c);
427  for (int i = 0; i < num_dofs_g; ++i)
428  cells[dofs[i]] = c;
429  }
430 
431  eval(x_g, cells, point_values);
432 
433  return point_values;
434  }
435 
437  std::string name = "u";
438 
440  std::size_t id() const { return _id; }
441 
442 private:
443  // ID
444  std::size_t _id;
445 
446  // The function space
447  std::shared_ptr<const FunctionSpace> _function_space;
448 
449  // The vector of expansion coefficients (local)
450  std::shared_ptr<la::Vector<T>> _x;
451 
452  // PETSc wrapper of the expansion coefficients
453  mutable Vec _petsc_vector = nullptr;
454 };
455 } // namespace dolfinx::fem
This class manages coordinate mappings for isoparametric cells.
Definition: CoordinateElement.h:29
void pull_back(xt::xtensor< double, 2 > &X, xt::xtensor< double, 3 > &J, xt::xtensor< double, 1 > &detJ, xt::xtensor< double, 3 > &K, const xt::xtensor< double, 2 > &x, const xt::xtensor< double, 2 > &cell_geometry) const
Compute reference coordinates X, and J, detJ and K for physical coordinates x.
Definition: CoordinateElement.cpp:208
This class represents a function in a finite element function space , given by.
Definition: Function.h:47
Vec vector() const
Return vector of expansion coefficients as a PETSc Vec. Throws an exception if a PETSc Vec cannot be ...
Definition: Function.h:163
void eval(const xt::xtensor< double, 2 > &x, const xtl::span< const std::int32_t > &cells, xt::xtensor< T, 2 > &u) const
Evaluate the Function at points.
Definition: Function.h:235
Function(std::shared_ptr< const FunctionSpace > V, std::shared_ptr< la::Vector< T >> x)
Create function on given function space with a given vector.
Definition: Function.h:69
void interpolate(const Function< T > &v)
Interpolate a Function (on possibly non-matching meshes)
Definition: Function.h:202
Function & operator=(Function &&v) noexcept
Move assignment.
Definition: Function.h:101
Function(Function &&v)
Move constructor.
Definition: Function.h:86
std::shared_ptr< la::Vector< T > > x()
Underlying vector.
Definition: Function.h:198
xt::xtensor< T, 2 > compute_point_values() const
Compute values at all mesh 'nodes'.
Definition: Function.h:394
std::shared_ptr< const FunctionSpace > function_space() const
Return shared pointer to function space.
Definition: Function.h:155
virtual ~Function()
Destructor.
Definition: Function.h:94
std::size_t id() const
ID.
Definition: Function.h:440
std::string name
Name.
Definition: Function.h:437
std::shared_ptr< const la::Vector< T > > x() const
Underlying vector.
Definition: Function.h:195
Function collapse() const
Collapse a subfunction (view into the Function) to a stand-alone Function.
Definition: Function.h:128
Function(std::shared_ptr< const FunctionSpace > V)
Create function on given function space.
Definition: Function.h:51
void interpolate(const std::function< xt::xarray< T >(const xt::xtensor< double, 2 > &)> &f)
Interpolate an expression.
Definition: Function.h:206
Function sub(int i) const
Extract subfunction (view into the Function)
Definition: Function.h:118
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:47
xtl::span< T > links(int node)
Get the links (edges) for given node.
Definition: AdjacencyList.h:130
int num_links(int node) const
Number of connections for given node.
Definition: AdjacencyList.h:120
Distributed vector.
Definition: Vector.h:25
Finite element method functionality.
Definition: assemble_matrix_impl.h:23
void interpolate(Function< T > &u, const Function< T > &v)
Interpolate a finite element Function (on possibly non-matching meshes) in another finite element spa...
Definition: interpolate.h:146
xt::xtensor< double, 2 > interpolation_coords(const fem::FiniteElement &element, const mesh::Mesh &mesh, const xtl::span< const std::int32_t > &cells)
Compute the evaluation points in the physical space at which an expression should be computed to inte...
Definition: interpolate.cpp:18
Vec create_ghosted_vector(const common::IndexMap &map, int bs, xtl::span< PetscScalar > x)
Create a PETSc Vec that wraps the data in an array.
Definition: PETScVector.cpp:64