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
interpolate.h
1 // Copyright (C) 2020-2021 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 <dolfinx/fem/DofMap.h>
11 #include <dolfinx/fem/FiniteElement.h>
12 #include <dolfinx/mesh/Mesh.h>
13 #include <functional>
14 #include <variant>
15 #include <xtensor/xadapt.hpp>
16 #include <xtensor/xarray.hpp>
17 #include <xtensor/xtensor.hpp>
18 #include <xtensor/xview.hpp>
19 #include <xtl/xspan.hpp>
20 
21 namespace dolfinx::fem
22 {
23 
24 template <typename T>
25 class Function;
26 
37 xt::xtensor<double, 2>
38 interpolation_coords(const fem::FiniteElement& element, const mesh::Mesh& mesh,
39  const xtl::span<const std::int32_t>& cells);
40 
45 template <typename T>
46 void interpolate(Function<T>& u, const Function<T>& v);
47 
59 template <typename T>
60 void interpolate(
61  Function<T>& u,
62  const std::function<xt::xarray<T>(const xt::xtensor<double, 2>&)>& f,
63  const xt::xtensor<double, 2>& x,
64  const xtl::span<const std::int32_t>& cells);
65 
82 template <typename T>
83 void interpolate_c(
84  Function<T>& u,
85  const std::function<void(xt::xarray<T>&, const xt::xtensor<double, 2>&)>& f,
86  const xt::xtensor<double, 2>& x,
87  const xtl::span<const std::int32_t>& cells);
88 
89 namespace detail
90 {
91 
92 template <typename T>
93 void interpolate_from_any(Function<T>& u, const Function<T>& v)
94 {
95  assert(v.function_space());
96  const auto element = u.function_space()->element();
97  assert(element);
98  if (v.function_space()->element()->hash() != element->hash())
99  {
100  throw std::runtime_error("Restricting finite elements function in "
101  "different elements not supported.");
102  }
103 
104  const auto mesh = u.function_space()->mesh();
105  assert(mesh);
106  assert(v.function_space()->mesh());
107  if (mesh->id() != v.function_space()->mesh()->id())
108  {
109  throw std::runtime_error(
110  "Interpolation on different meshes not supported (yet).");
111  }
112  const int tdim = mesh->topology().dim();
113 
114  // Get dofmaps
115  assert(v.function_space());
116  std::shared_ptr<const fem::DofMap> dofmap_v = v.function_space()->dofmap();
117  assert(dofmap_v);
118  auto map = mesh->topology().index_map(tdim);
119  assert(map);
120 
121  std::vector<T>& coeffs = u.x()->mutable_array();
122 
123  // Iterate over mesh and interpolate on each cell
124  const auto dofmap_u = u.function_space()->dofmap();
125  const std::vector<T>& v_array = v.x()->array();
126  const int num_cells = map->size_local() + map->num_ghosts();
127  const int bs = dofmap_v->bs();
128  assert(bs == dofmap_u->bs());
129  for (int c = 0; c < num_cells; ++c)
130  {
131  xtl::span<const std::int32_t> dofs_v = dofmap_v->cell_dofs(c);
132  xtl::span<const std::int32_t> cell_dofs = dofmap_u->cell_dofs(c);
133  assert(dofs_v.size() == cell_dofs.size());
134  for (std::size_t i = 0; i < dofs_v.size(); ++i)
135  {
136  for (int k = 0; k < bs; ++k)
137  coeffs[bs * cell_dofs[i] + k] = v_array[bs * dofs_v[i] + k];
138  }
139  }
140 }
141 
142 } // namespace detail
143 
144 //----------------------------------------------------------------------------
145 template <typename T>
147 {
148  assert(u.function_space());
149  const std::shared_ptr<const FiniteElement> element
150  = u.function_space()->element();
151  assert(element);
152 
153  // Check that function ranks match
154  if (int rank_v = v.function_space()->element()->value_rank();
155  element->value_rank() != rank_v)
156  {
157  throw std::runtime_error("Cannot interpolate function into function space. "
158  "Rank of function ("
159  + std::to_string(rank_v)
160  + ") does not match rank of function space ("
161  + std::to_string(element->value_rank()) + ")");
162  }
163 
164  // Check that function dimension match
165  for (int i = 0; i < element->value_rank(); ++i)
166  {
167  if (int v_dim = v.function_space()->element()->value_dimension(i);
168  element->value_dimension(i) != v_dim)
169  {
170  throw std::runtime_error(
171  "Cannot interpolate function into function space. "
172  "Dimension "
173  + std::to_string(i) + " of function (" + std::to_string(v_dim)
174  + ") does not match dimension " + std::to_string(i)
175  + " of function space(" + std::to_string(element->value_dimension(i))
176  + ")");
177  }
178  }
179 
180  detail::interpolate_from_any(u, v);
181 }
182 //----------------------------------------------------------------------------
183 template <typename T>
185  Function<T>& u,
186  const std::function<xt::xarray<T>(const xt::xtensor<double, 2>&)>& f,
187  const xt::xtensor<double, 2>& x, const xtl::span<const std::int32_t>& cells)
188 {
189  const std::shared_ptr<const FiniteElement> element
190  = u.function_space()->element();
191  assert(element);
192  const int element_bs = element->block_size();
193  if (int num_sub = element->num_sub_elements();
194  num_sub > 0 and num_sub != element_bs)
195  {
196  throw std::runtime_error("Cannot directly interpolate a mixed space. "
197  "Interpolate into subspaces.");
198  }
199 
200  // Get mesh
201  assert(u.function_space());
202  auto mesh = u.function_space()->mesh();
203  assert(mesh);
204 
205  const int gdim = mesh->geometry().dim();
206  const int tdim = mesh->topology().dim();
207 
208  // Get the interpolation points on the reference cells
209  const xt::xtensor<double, 2>& X = element->interpolation_points();
210 
211  if (X.shape(0) == 0)
212  {
213  throw std::runtime_error(
214  "Interpolation into this space is not yet supported.");
215  }
216 
217  mesh->topology_mutable().create_entity_permutations();
218  const std::vector<std::uint32_t>& cell_info
219  = mesh->topology().get_cell_permutation_info();
220 
221  // Evaluate function at physical points. The returned array has a
222  // number of rows equal to the number of components of the function,
223  // and the number of columns is equal to the number of evaluation
224  // points.
225  xt::xarray<T> values = f(x);
226 
227  if (values.dimension() == 1)
228  {
229  if (element->value_size() != 1)
230  throw std::runtime_error("Interpolation data has the wrong shape.");
231  values.reshape(
232  {static_cast<std::size_t>(element->value_size()), x.shape(1)});
233  }
234 
235  if (values.shape(0) != element->value_size())
236  throw std::runtime_error("Interpolation data has the wrong shape.");
237 
238  if (values.shape(1) != cells.size() * X.shape(0))
239  throw std::runtime_error("Interpolation data has the wrong shape.");
240 
241  // Get dofmap
242  const auto dofmap = u.function_space()->dofmap();
243  assert(dofmap);
244  const int dofmap_bs = dofmap->bs();
245 
246  // Loop over cells and compute interpolation dofs
247  const int num_scalar_dofs = element->space_dimension() / element_bs;
248  const int value_size = element->value_size() / element_bs;
249 
250  std::vector<T>& coeffs = u.x()->mutable_array();
251  std::vector<T> _coeffs(num_scalar_dofs);
252 
253  const std::function<void(const xtl::span<T>&,
254  const xtl::span<const std::uint32_t>&, std::int32_t,
255  int)>
256  apply_inverse_transpose_dof_transformation
257  = element->get_dof_transformation_function<T>(true, true, true);
258 
259  // This assumes that any element with an identity interpolation matrix is a
260  // point evaluation
261  if (element->interpolation_ident())
262  {
263  for (std::int32_t c : cells)
264  {
265  xtl::span<const std::int32_t> dofs = dofmap->cell_dofs(c);
266  for (int k = 0; k < element_bs; ++k)
267  {
268  for (int i = 0; i < num_scalar_dofs; ++i)
269  _coeffs[i] = values(k, c * num_scalar_dofs + i);
270  apply_inverse_transpose_dof_transformation(_coeffs, cell_info, c, 1);
271  for (int i = 0; i < num_scalar_dofs; ++i)
272  {
273  const int dof = i * element_bs + k;
274  std::div_t pos = std::div(dof, dofmap_bs);
275  coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
276  }
277  }
278  }
279  }
280  else
281  {
282  // Get coordinate map
283  const fem::CoordinateElement& cmap = mesh->geometry().cmap();
284 
285  // Get geometry data
286  const graph::AdjacencyList<std::int32_t>& x_dofmap
287  = mesh->geometry().dofmap();
288  // FIXME: Add proper interface for num coordinate dofs
289  const int num_dofs_g = x_dofmap.num_links(0);
290  const xt::xtensor<double, 2>& x_g = mesh->geometry().x();
291 
292  // Create data structures for Jacobian info
293  xt::xtensor<double, 3> J = xt::empty<double>({int(X.shape(0)), gdim, tdim});
294  xt::xtensor<double, 3> K = xt::empty<double>({int(X.shape(0)), tdim, gdim});
295  xt::xtensor<double, 1> detJ = xt::empty<double>({X.shape(0)});
296 
297  xt::xtensor<double, 2> coordinate_dofs
298  = xt::empty<double>({num_dofs_g, gdim});
299 
300  xt::xtensor<T, 3> reference_data({X.shape(0), 1, value_size});
301  xt::xtensor<T, 3> _vals({X.shape(0), 1, value_size});
302 
303  // Tabulate 1st order derivatives of shape functions at interpolation coords
304  xt::xtensor<double, 4> dphi
305  = xt::view(cmap.tabulate(1, X), xt::range(1, tdim + 1), xt::all(),
306  xt::all(), xt::all());
307 
308  const std::function<void(const xtl::span<T>&,
309  const xtl::span<const std::uint32_t>&,
310  std::int32_t, int)>
311  apply_inverse_transpose_dof_transformation
312  = element->get_dof_transformation_function<T>(true, true);
313 
314  for (std::int32_t c : cells)
315  {
316  auto x_dofs = x_dofmap.links(c);
317  for (int i = 0; i < num_dofs_g; ++i)
318  for (int j = 0; j < gdim; ++j)
319  coordinate_dofs(i, j) = x_g(x_dofs[i], j);
320 
321  // Compute J, detJ and K
322  cmap.compute_jacobian(dphi, coordinate_dofs, J);
323  cmap.compute_jacobian_inverse(J, K);
324  cmap.compute_jacobian_determinant(J, detJ);
325 
326  xtl::span<const std::int32_t> dofs = dofmap->cell_dofs(c);
327  for (int k = 0; k < element_bs; ++k)
328  {
329  // Extract computed expression values for element block k
330  for (int m = 0; m < value_size; ++m)
331  {
332  std::copy_n(&values(k * value_size + m, c * X.shape(0)), X.shape(0),
333  xt::view(_vals, xt::all(), 0, m).begin());
334  }
335 
336  // Get element degrees of freedom for block
337  element->map_pull_back(_vals, J, detJ, K, reference_data);
338 
339  xt::xtensor<T, 2> ref_data
340  = xt::transpose(xt::view(reference_data, xt::all(), 0, xt::all()));
341  element->interpolate(ref_data, tcb::make_span(_coeffs));
342  apply_inverse_transpose_dof_transformation(_coeffs, cell_info, c, 1);
343 
344  assert(_coeffs.size() == num_scalar_dofs);
345 
346  // Copy interpolation dofs into coefficient vector
347  for (int i = 0; i < num_scalar_dofs; ++i)
348  {
349  const int dof = i * element_bs + k;
350  std::div_t pos = std::div(dof, dofmap_bs);
351  coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
352  }
353  }
354  }
355  }
356 }
357 //----------------------------------------------------------------------------
358 template <typename T>
360  Function<T>& u,
361  const std::function<void(xt::xarray<T>&, const xt::xtensor<double, 2>&)>& f,
362  const xt::xtensor<double, 2>& x, const xtl::span<const std::int32_t>& cells)
363 {
364  const std::shared_ptr<const FiniteElement> element
365  = u.function_space()->element();
366  assert(element);
367  std::vector<int> vshape(element->value_rank(), 1);
368  for (std::size_t i = 0; i < vshape.size(); ++i)
369  vshape[i] = element->value_dimension(i);
370  const std::size_t value_size = std::reduce(
371  std::begin(vshape), std::end(vshape), 1, std::multiplies<>());
372 
373  auto fn = [value_size, &f](const xt::xtensor<double, 2>& x)
374  {
375  xt::xarray<T> values = xt::empty<T>({value_size, x.shape(1)});
376  f(values, x);
377  return values;
378  };
379 
380  interpolate<T>(u, fn, x, cells);
381 }
382 //----------------------------------------------------------------------------
383 
384 } // namespace dolfinx::fem
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
This class represents a function in a finite element function space , given by.
Definition: Form.h:23
dolfinx::fem::CoordinateElement::compute_jacobian
void compute_jacobian(const xt::xtensor< double, 4 > &dphi, const xt::xtensor< double, 2 > &cell_geometry, xt::xtensor< double, 3 > &J) const
Compute Jacobian for a cell with given geometry using the basis functions and first order derivatives...
Definition: CoordinateElement.cpp:81
dolfinx::fem::CoordinateElement::compute_jacobian_determinant
void compute_jacobian_determinant(const xt::xtensor< double, 3 > &J, xt::xtensor< double, 1 > &detJ) const
Compute the determinant of the Jacobian. If the coordinate element is affine, it computes the determi...
Definition: CoordinateElement.cpp:159
dolfinx::fem::CoordinateElement::compute_jacobian_inverse
void compute_jacobian_inverse(const xt::xtensor< double, 3 > &J, xt::xtensor< double, 3 > &K) const
Compute the inverse of the Jacobian. If the coordinate element is affine, it computes the inverse at ...
Definition: CoordinateElement.cpp:122
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::CoordinateElement::tabulate
xt::xtensor< double, 4 > tabulate(int n, const xt::xtensor< double, 2 > &X) const
Compute basis values and derivatives at set of points.
Definition: CoordinateElement.cpp:76
dolfinx::fem::interpolate_c
void interpolate_c(Function< T > &u, const std::function< void(xt::xarray< T > &, const xt::xtensor< double, 2 > &)> &f, const xt::xtensor< double, 2 > &x, const xtl::span< const std::int32_t > &cells)
Interpolate an expression f(x)
Definition: interpolate.h:359
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
Finite element method functionality.
Definition: assemble_matrix_impl.h:22
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::graph::AdjacencyList::links
xtl::span< T > links(int node)
Get the links (edges) for given node.
Definition: AdjacencyList.h:130
dolfinx::fem::Function::x
std::shared_ptr< const la::Vector< T > > x() const
Underlying vector.
Definition: Function.h:196
dolfinx::fem::CoordinateElement
This class manages coordinate mappings for isoparametric cells.
Definition: CoordinateElement.h:28