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