Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/v0.2.0a0/v0.9.0/cpp
DOLFINx  0.2.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 
186  return _petsc_vector;
187  }
188  else
189  {
190  throw std::runtime_error(
191  "Cannot return PETSc vector wrapper. Type mismatch");
192  }
193  }
194 
196  std::shared_ptr<const la::Vector<T>> x() const { return _x; }
197 
199  std::shared_ptr<la::Vector<T>> x() { return _x; }
200 
203  void interpolate(const Function<T>& v) { fem::interpolate(*this, v); }
204 
208  const std::function<xt::xarray<T>(const xt::xtensor<double, 2>&)>& f)
209  {
210  assert(_function_space);
211  assert(_function_space->element());
212  assert(_function_space->mesh());
213  const int tdim = _function_space->mesh()->topology().dim();
214  auto cell_map = _function_space->mesh()->topology().index_map(tdim);
215  assert(cell_map);
216  const int num_cells = cell_map->size_local() + cell_map->num_ghosts();
217  std::vector<std::int32_t> cells(num_cells, 0);
218  std::iota(cells.begin(), cells.end(), 0);
219  // FIXME: Remove interpolation coords as it should be done internally in
220  // fem::interpolate
221  const xt::xtensor<double, 2> x = fem::interpolation_coords(
222  *_function_space->element(), *_function_space->mesh(), cells);
223  fem::interpolate(*this, f, x, cells);
224  }
225 
236  void eval(const xt::xtensor<double, 2>& x,
237  const xtl::span<const std::int32_t>& cells,
238  xt::xtensor<T, 2>& u) const
239  {
240  // TODO: This could be easily made more efficient by exploiting points
241  // being ordered by the cell to which they belong.
242 
243  if (x.shape(0) != cells.size())
244  {
245  throw std::runtime_error(
246  "Number of points and number of cells must be equal.");
247  }
248  if (x.shape(0) != u.shape(0))
249  {
250  throw std::runtime_error(
251  "Length of array for Function values must be the "
252  "same as the number of points.");
253  }
254 
255  // Get mesh
256  assert(_function_space);
257  std::shared_ptr<const mesh::Mesh> mesh = _function_space->mesh();
258  assert(mesh);
259  const std::size_t gdim = mesh->geometry().dim();
260  const std::size_t tdim = mesh->topology().dim();
261  auto map = mesh->topology().index_map(tdim);
262 
263  // Get geometry data
264  const graph::AdjacencyList<std::int32_t>& x_dofmap
265  = mesh->geometry().dofmap();
266  // FIXME: Add proper interface for num coordinate dofs
267  const std::size_t num_dofs_g = x_dofmap.num_links(0);
268  const xt::xtensor<double, 2>& x_g = mesh->geometry().x();
269 
270  // Get coordinate map
271  const fem::CoordinateElement& cmap = mesh->geometry().cmap();
272 
273  // Get element
274  assert(_function_space->element());
275  std::shared_ptr<const fem::FiniteElement> element
276  = _function_space->element();
277  assert(element);
278  const int bs_element = element->block_size();
279  const std::size_t reference_value_size
280  = element->reference_value_size() / bs_element;
281  const std::size_t value_size = element->value_size() / bs_element;
282  const std::size_t space_dimension = element->space_dimension() / bs_element;
283 
284  // If the space has sub elements, concatenate the evaluations on the sub
285  // elements
286  const int num_sub_elements = element->num_sub_elements();
287  if (num_sub_elements > 1 and num_sub_elements != bs_element)
288  {
289  throw std::runtime_error("Function::eval is not supported for mixed "
290  "elements. Extract subspaces.");
291  }
292 
293  // Prepare geometry data structures
294  xt::xtensor<double, 2> X({1, tdim});
295  xt::xtensor<double, 3> J
296  = xt::zeros<double>({static_cast<std::size_t>(1), gdim, tdim});
297  xt::xtensor<double, 3> K
298  = xt::zeros<double>({static_cast<std::size_t>(1), tdim, gdim});
299  xt::xtensor<double, 1> detJ = xt::zeros<double>({1});
300 
301  // Prepare basis function data structures
302  xt::xtensor<double, 4> basis_derivatives_reference_values(
303  {1, 1, space_dimension, reference_value_size});
304  auto basis_reference_values = xt::view(basis_derivatives_reference_values,
305  0, xt::all(), xt::all(), xt::all());
306  xt::xtensor<double, 3> basis_values(
307  {static_cast<std::size_t>(1), space_dimension, value_size});
308 
309  // Create work vector for expansion coefficients
310  std::vector<T> coefficients(space_dimension * bs_element);
311 
312  // Get dofmap
313  std::shared_ptr<const fem::DofMap> dofmap = _function_space->dofmap();
314  assert(dofmap);
315  const int bs_dof = dofmap->bs();
316 
317  mesh->topology_mutable().create_entity_permutations();
318  const std::vector<std::uint32_t>& cell_info
319  = mesh->topology().get_cell_permutation_info();
320  xt::xtensor<double, 2> coordinate_dofs
321  = xt::zeros<double>({num_dofs_g, gdim});
322  xt::xtensor<double, 2> xp
323  = xt::zeros<double>({static_cast<std::size_t>(1), gdim});
324 
325  // Loop over points
326  std::fill(u.data(), u.data() + u.size(), 0.0);
327  const std::vector<T>& _v = _x->mutable_array();
328 
329  const std::function<void(const xtl::span<double>&,
330  const xtl::span<const std::uint32_t>&,
331  std::int32_t, int)>
332  apply_dof_transformation
333  = element->get_dof_transformation_function<double>();
334 
335  for (std::size_t p = 0; p < cells.size(); ++p)
336  {
337  const int cell_index = cells[p];
338 
339  // Skip negative cell indices
340  if (cell_index < 0)
341  continue;
342 
343  // Get cell geometry (coordinate dofs)
344  auto x_dofs = x_dofmap.links(cell_index);
345  for (std::size_t i = 0; i < num_dofs_g; ++i)
346  for (std::size_t j = 0; j < gdim; ++j)
347  coordinate_dofs(i, j) = x_g(x_dofs[i], j);
348 
349  for (std::size_t j = 0; j < gdim; ++j)
350  xp(0, j) = x(p, j);
351 
352  // Compute reference coordinates X, and J, detJ and K
353  cmap.pull_back(X, J, detJ, K, xp, coordinate_dofs);
354 
355  // Compute basis on reference element
356  element->tabulate(basis_derivatives_reference_values, X, 0);
357 
358  // Permute the reference values to account for the cell's orientation
359  apply_dof_transformation(xtl::span(basis_reference_values.data(),
360  basis_reference_values.size()),
361  cell_info, cell_index, reference_value_size);
362 
363  // Push basis forward to physical element
364  element->transform_reference_basis(basis_values, basis_reference_values,
365  J, detJ, K);
366 
367  // Get degrees of freedom for current cell
368  xtl::span<const std::int32_t> dofs = dofmap->cell_dofs(cell_index);
369  for (std::size_t i = 0; i < dofs.size(); ++i)
370  for (int k = 0; k < bs_dof; ++k)
371  coefficients[bs_dof * i + k] = _v[bs_dof * dofs[i] + k];
372 
373  // Compute expansion
374  auto u_row = xt::row(u, p);
375  for (int k = 0; k < bs_element; ++k)
376  {
377  for (std::size_t i = 0; i < space_dimension; ++i)
378  {
379  for (std::size_t j = 0; j < value_size; ++j)
380  {
381  u_row[j * bs_element + k]
382  += coefficients[bs_element * i + k] * basis_values(0, i, j);
383  }
384  }
385  }
386  }
387  }
388 
391  xt::xtensor<T, 2> compute_point_values() const
392  {
393  assert(_function_space);
394  std::shared_ptr<const mesh::Mesh> mesh = _function_space->mesh();
395  assert(mesh);
396  const int tdim = mesh->topology().dim();
397 
398  // Compute in tensor (one for scalar function, . . .)
399  const std::size_t value_size_loc = _function_space->element()->value_size();
400 
401  // Resize Array for holding point values
402  xt::xtensor<T, 2> point_values(
403  {mesh->geometry().x().shape(0), value_size_loc});
404 
405  // Prepare cell geometry
406  const graph::AdjacencyList<std::int32_t>& x_dofmap
407  = mesh->geometry().dofmap();
408 
409  // FIXME: Add proper interface for num coordinate dofs
410  const int num_dofs_g = x_dofmap.num_links(0);
411  const xt::xtensor<double, 2>& x_g = mesh->geometry().x();
412 
413  // Interpolate point values on each cell (using last computed value if
414  // not continuous, e.g. discontinuous Galerkin methods)
415  auto map = mesh->topology().index_map(tdim);
416  assert(map);
417  const std::int32_t num_cells = map->size_local() + map->num_ghosts();
418 
419  std::vector<std::int32_t> cells(x_g.shape(0));
420  for (std::int32_t c = 0; c < num_cells; ++c)
421  {
422  // Get coordinates for all points in cell
423  xtl::span<const std::int32_t> dofs = x_dofmap.links(c);
424  for (int i = 0; i < num_dofs_g; ++i)
425  cells[dofs[i]] = c;
426  }
427 
428  eval(x_g, cells, point_values);
429 
430  return point_values;
431  }
432 
434  std::string name = "u";
435 
437  std::size_t id() const { return _id; }
438 
439 private:
440  // ID
441  std::size_t _id;
442 
443  // The function space
444  std::shared_ptr<const FunctionSpace> _function_space;
445 
446  // The vector of expansion coefficients (local)
447  std::shared_ptr<la::Vector<T>> _x;
448 
449  // PETSc wrapper of the expansion coefficients
450  mutable Vec _petsc_vector = nullptr;
451 };
452 } // namespace dolfinx::fem
dolfinx::fem::Function::~Function
virtual ~Function()
Destructor.
Definition: Function.h:94
dolfinx::graph::AdjacencyList::num_links
int num_links(int node) const
Number of connections for given node.
Definition: AdjacencyList.h:120
dolfinx::fem::Function::function_space
std::shared_ptr< const FunctionSpace > function_space() const
Return shared pointer to function space.
Definition: Function.h:155
dolfinx::fem::Function::eval
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:236
dolfinx::fem::Function
This class represents a function in a finite element function space , given by.
Definition: Form.h:23
dolfinx::fem::Function::x
std::shared_ptr< la::Vector< T > > x()
Underlying vector.
Definition: Function.h:199
dolfinx::fem::Function::interpolate
void interpolate(const std::function< xt::xarray< T >(const xt::xtensor< double, 2 > &)> &f)
Interpolate an expression.
Definition: Function.h:207
dolfinx::graph::AdjacencyList
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:46
dolfinx::fem::Function::collapse
Function collapse() const
Collapse a subfunction (view into the Function) to a stand-alone Function.
Definition: Function.h:128
dolfinx::fem::Function::Function
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
dolfinx::fem::CoordinateElement::pull_back
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:205
dolfinx::fem::Function::operator=
Function & operator=(Function &&v) noexcept
Move assignment.
Definition: Function.h:101
dolfinx::la::create_ghosted_vector
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
dolfinx::la::Vector
Distributed vector.
Definition: Vector.h:24
dolfinx::fem::interpolation_coords
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
dolfinx::fem::Function::name
std::string name
Name.
Definition: Function.h:434
dolfinx::fem
Finite element method functionality.
Definition: assemble_matrix_impl.h:22
dolfinx::fem::Function::id
std::size_t id() const
ID.
Definition: Function.h:437
dolfinx::fem::interpolate
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
dolfinx::fem::Function::Function
Function(std::shared_ptr< const FunctionSpace > V)
Create function on given function space.
Definition: Function.h:51
dolfinx::fem::Function::vector
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
dolfinx::fem::Function::Function
Function(Function &&v)
Move constructor.
Definition: Function.h:86
dolfinx::graph::AdjacencyList::links
xtl::span< T > links(int node)
Get the links (edges) for given node.
Definition: AdjacencyList.h:130
dolfinx::fem::Function::compute_point_values
xt::xtensor< T, 2 > compute_point_values() const
Compute values at all mesh 'nodes'.
Definition: Function.h:391
dolfinx::fem::Function::x
std::shared_ptr< const la::Vector< T > > x() const
Underlying vector.
Definition: Function.h:196
dolfinx::fem::Function::interpolate
void interpolate(const Function< T > &v)
Interpolate a Function (on possibly non-matching meshes)
Definition: Function.h:203
dolfinx::fem::Function::sub
Function sub(int i) const
Extract subfunction (view into the Function)
Definition: Function.h:118
dolfinx::fem::CoordinateElement
This class manages coordinate mappings for isoparametric cells.
Definition: CoordinateElement.h:28