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
utils.h
1 // Copyright (C) 2013-2020 Johan Hake, Jan Blechta 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 "CoordinateElement.h"
10 #include "DofMap.h"
11 #include "ElementDofLayout.h"
12 #include <dolfinx/common/types.h>
13 #include <dolfinx/fem/Form.h>
14 #include <dolfinx/fem/Function.h>
15 #include <dolfinx/la/SparsityPattern.h>
16 #include <dolfinx/mesh/cell_types.h>
17 #include <functional>
18 #include <memory>
19 #include <set>
20 #include <string>
21 #include <ufc.h>
22 #include <utility>
23 #include <vector>
24 
25 namespace dolfinx::common
26 {
27 class IndexMap;
28 }
29 
30 namespace dolfinx::mesh
31 {
32 class Mesh;
33 class Topology;
34 } // namespace dolfinx::mesh
35 
36 namespace dolfinx::fem
37 {
38 template <typename T>
39 class Constant;
40 template <typename T>
41 class Form;
42 template <typename T>
43 class Function;
44 class FunctionSpace;
45 
53 template <typename T>
54 std::vector<
55  std::vector<std::array<std::shared_ptr<const fem::FunctionSpace>, 2>>>
56 extract_function_spaces(const std::vector<std::vector<const fem::Form<T>*>>& a)
57 {
58  std::vector<
59  std::vector<std::array<std::shared_ptr<const fem::FunctionSpace>, 2>>>
60  spaces(
61  a.size(),
62  std::vector<std::array<std::shared_ptr<const fem::FunctionSpace>, 2>>(
63  a[0].size()));
64  for (std::size_t i = 0; i < a.size(); ++i)
65  {
66  for (std::size_t j = 0; j < a[i].size(); ++j)
67  {
68  if (const fem::Form<T>* form = a[i][j]; form)
69  spaces[i][j] = {form->function_spaces()[0], form->function_spaces()[1]};
70  }
71  }
72  return spaces;
73 }
74 
80 template <typename T>
82 {
83  if (a.rank() != 2)
84  {
85  throw std::runtime_error(
86  "Cannot create sparsity pattern. Form is not a bilinear form");
87  }
88 
89  // Get dof maps and mesh
90  std::array<const std::reference_wrapper<const fem::DofMap>, 2> dofmaps{
91  *a.function_spaces().at(0)->dofmap(),
92  *a.function_spaces().at(1)->dofmap()};
93  std::shared_ptr mesh = a.mesh();
94  assert(mesh);
95 
96  const std::set<IntegralType> types = a.integral_types();
97  if (types.find(IntegralType::interior_facet) != types.end()
98  or types.find(IntegralType::exterior_facet) != types.end())
99  {
100  // FIXME: cleanup these calls? Some of the happen internally again.
101  const int tdim = mesh->topology().dim();
102  mesh->topology_mutable().create_entities(tdim - 1);
103  mesh->topology_mutable().create_connectivity(tdim - 1, tdim);
104  }
105 
106  return create_sparsity_pattern(mesh->topology(), dofmaps, types);
107 }
108 
113  const mesh::Topology& topology,
114  const std::array<const std::reference_wrapper<const fem::DofMap>, 2>&
115  dofmaps,
116  const std::set<IntegralType>& integrals);
117 
119 ElementDofLayout create_element_dof_layout(const ufc_dofmap& dofmap,
120  const mesh::CellType cell_type,
121  const std::vector<int>& parent_map
122  = {});
123 
131 DofMap
132 create_dofmap(MPI_Comm comm, const ufc_dofmap& dofmap, mesh::Topology& topology,
133  const std::function<std::vector<int>(
134  const graph::AdjacencyList<std::int32_t>&)>& reorder_fn,
135  std::shared_ptr<const dolfinx::fem::FiniteElement> element);
136 
140 std::vector<std::string> get_coefficient_names(const ufc_form& ufc_form);
141 
145 std::vector<std::string> get_constant_names(const ufc_form& ufc_form);
146 
154 template <typename T>
156  const ufc_form& ufc_form,
157  const std::vector<std::shared_ptr<const fem::FunctionSpace>>& spaces,
158  const std::vector<std::shared_ptr<const fem::Function<T>>>& coefficients,
159  const std::vector<std::shared_ptr<const fem::Constant<T>>>& constants,
160  const std::map<IntegralType, const mesh::MeshTags<int>*>& subdomains,
161  const std::shared_ptr<const mesh::Mesh>& mesh = nullptr)
162 {
163  if (ufc_form.rank != (int)spaces.size())
164  throw std::runtime_error("Wrong number of argument spaces for Form.");
165  if (ufc_form.num_coefficients != (int)coefficients.size())
166  {
167  throw std::runtime_error(
168  "Mismatch between number of expected and provided Form coefficients.");
169  }
170  if (ufc_form.num_constants != (int)constants.size())
171  {
172  throw std::runtime_error(
173  "Mismatch between number of expected and provided Form constants.");
174  }
175 
176  // Check argument function spaces
177 #ifdef DEBUG
178  for (std::size_t i = 0; i < spaces.size(); ++i)
179  {
180  assert(spaces[i]->element());
181  ufc_finite_element* ufc_element = ufc_form.finite_elements[i];
182  assert(ufc_element);
183  if (std::string(ufc_element->signature)
184  != spaces[i]->element()->signature())
185  {
186  throw std::runtime_error(
187  "Cannot create form. Wrong type of function space for argument.");
188  }
189  }
190 #endif
191 
192  // Get list of integral IDs, and load tabulate tensor into memory for
193  // each
194  using kern = std::function<void(T*, const T*, const T*, const double*,
195  const int*, const std::uint8_t*)>;
196  std::map<IntegralType, std::pair<std::vector<std::pair<int, kern>>,
197  const mesh::MeshTags<int>*>>
198  integral_data;
199 
200  bool needs_facet_permutations = false;
201 
202  // Attach cell kernels
203  std::vector<int> cell_integral_ids(ufc_form.integral_ids(cell),
204  ufc_form.integral_ids(cell)
205  + ufc_form.num_integrals(cell));
206  for (int i = 0; i < ufc_form.num_integrals(cell); ++i)
207  {
208  ufc_integral* integral = ufc_form.integrals(cell)[i];
209  assert(integral);
210  integral_data[IntegralType::cell].first.emplace_back(
211  cell_integral_ids[i], integral->tabulate_tensor);
212  if (integral->needs_facet_permutations)
213  needs_facet_permutations = true;
214  }
215 
216  // Attach cell subdomain data
217  if (auto it = subdomains.find(IntegralType::cell);
218  it != subdomains.end() and !cell_integral_ids.empty())
219  {
220  integral_data[IntegralType::cell].second = it->second;
221  }
222 
223  // FIXME: Can facets be handled better?
224 
225  // Create facets, if required
226  if (ufc_form.num_integrals(exterior_facet) > 0
227  or ufc_form.num_integrals(interior_facet) > 0)
228  {
229  if (!spaces.empty())
230  {
231  auto mesh = spaces[0]->mesh();
232  const int tdim = mesh->topology().dim();
233  spaces[0]->mesh()->topology_mutable().create_entities(tdim - 1);
234  }
235  }
236 
237  // Attach exterior facet kernels
238  std::vector<int> exterior_facet_integral_ids(
239  ufc_form.integral_ids(exterior_facet),
240  ufc_form.integral_ids(exterior_facet)
241  + ufc_form.num_integrals(exterior_facet));
242  for (int i = 0; i < ufc_form.num_integrals(exterior_facet); ++i)
243  {
244  ufc_integral* integral = ufc_form.integrals(exterior_facet)[i];
245  assert(integral);
246  integral_data[IntegralType::exterior_facet].first.emplace_back(
247  exterior_facet_integral_ids[i], integral->tabulate_tensor);
248  if (integral->needs_facet_permutations)
249  needs_facet_permutations = true;
250  }
251 
252  // Attach exterior facet subdomain data
253  if (auto it = subdomains.find(IntegralType::exterior_facet);
254  it != subdomains.end() and !exterior_facet_integral_ids.empty())
255  {
256  integral_data[IntegralType::exterior_facet].second = it->second;
257  }
258 
259  // Attach interior facet kernels
260  std::vector<int> interior_facet_integral_ids(
261  ufc_form.integral_ids(interior_facet),
262  ufc_form.integral_ids(interior_facet)
263  + ufc_form.num_integrals(interior_facet));
264  for (int i = 0; i < ufc_form.num_integrals(interior_facet); ++i)
265  {
266  ufc_integral* integral = ufc_form.integrals(interior_facet)[i];
267  assert(integral);
268  integral_data[IntegralType::interior_facet].first.emplace_back(
269  interior_facet_integral_ids[i], integral->tabulate_tensor);
270  if (integral->needs_facet_permutations)
271  needs_facet_permutations = true;
272  }
273 
274  // Attach interior facet subdomain data
275  if (auto it = subdomains.find(IntegralType::interior_facet);
276  it != subdomains.end() and !interior_facet_integral_ids.empty())
277  {
278  integral_data[IntegralType::interior_facet].second = it->second;
279  }
280 
281  return fem::Form(spaces, integral_data, coefficients, constants,
282  needs_facet_permutations, mesh);
283 }
284 
294 template <typename T>
296  const ufc_form& ufc_form,
297  const std::vector<std::shared_ptr<const fem::FunctionSpace>>& spaces,
298  const std::map<std::string, std::shared_ptr<const fem::Function<T>>>&
299  coefficients,
300  const std::map<std::string, std::shared_ptr<const fem::Constant<T>>>&
301  constants,
302  const std::map<IntegralType, const mesh::MeshTags<int>*>& subdomains,
303  const std::shared_ptr<const mesh::Mesh>& mesh = nullptr)
304 {
305  // Place coefficients in appropriate order
306  std::vector<std::shared_ptr<const fem::Function<T>>> coeff_map;
307  for (const std::string& name : get_coefficient_names(ufc_form))
308  {
309  if (auto it = coefficients.find(name); it != coefficients.end())
310  coeff_map.push_back(it->second);
311  else
312  {
313  throw std::runtime_error("Form coefficient \"" + name
314  + "\" not provided.");
315  }
316  }
317 
318  // Place constants in appropriate order
319  std::vector<std::shared_ptr<const fem::Constant<T>>> const_map;
320  for (const std::string& name : get_constant_names(ufc_form))
321  {
322  if (auto it = constants.find(name); it != constants.end())
323  const_map.push_back(it->second);
324  else
325  {
326  throw std::runtime_error("Form constant \"" + name + "\" not provided.");
327  }
328  }
329 
330  return create_form(ufc_form, spaces, coeff_map, const_map, subdomains, mesh);
331 }
332 
344 template <typename T>
345 std::shared_ptr<Form<T>> create_form(
346  ufc_form* (*fptr)(),
347  const std::vector<std::shared_ptr<const fem::FunctionSpace>>& spaces,
348  const std::map<std::string, std::shared_ptr<const fem::Function<T>>>&
349  coefficients,
350  const std::map<std::string, std::shared_ptr<const fem::Constant<T>>>&
351  constants,
352  const std::map<IntegralType, const mesh::MeshTags<int>*>& subdomains,
353  const std::shared_ptr<const mesh::Mesh>& mesh = nullptr)
354 {
355  ufc_form* form = fptr();
356  auto L = std::make_shared<fem::Form<T>>(fem::create_form<T>(
357  *form, spaces, coefficients, constants, subdomains, mesh));
358  std::free(form);
359  return L;
360 }
361 
373 std::shared_ptr<fem::FunctionSpace> create_functionspace(
374  ufc_function_space* (*fptr)(const char*), const std::string function_name,
375  std::shared_ptr<mesh::Mesh> mesh,
376  const std::function<
377  std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>& reorder_fn
378  = nullptr);
379 
380 namespace impl
381 {
382 // Pack a single coefficient
383 template <typename T, int _bs = -1>
384 void pack_coefficient(
385  array2d<T>& c, const std::vector<T>& v,
386  const xtl::span<const std::uint32_t>& cell_info, const fem::DofMap& dofmap,
387  std::int32_t num_cells, std::int32_t offset, int space_dim,
388  const std::function<void(const xtl::span<T>&,
389  const xtl::span<const std::uint32_t>&,
390  std::int32_t, int)>& transform)
391 {
392  const int bs = dofmap.bs();
393  assert(_bs < 0 or _bs == bs);
394  for (std::int32_t cell = 0; cell < num_cells; ++cell)
395  {
396  auto dofs = dofmap.cell_dofs(cell);
397  auto cell_coeff = c.row(cell).subspan(offset, space_dim);
398  for (std::size_t i = 0; i < dofs.size(); ++i)
399  {
400  if constexpr (_bs < 0)
401  {
402  const int pos_c = bs * i;
403  const int pos_v = bs * dofs[i];
404  for (int k = 0; k < bs; ++k)
405  cell_coeff[pos_c + k] = v[pos_v + k];
406  }
407  else
408  {
409  const int pos_c = _bs * i;
410  const int pos_v = _bs * dofs[i];
411  for (int k = 0; k < _bs; ++k)
412  cell_coeff[pos_c + k] = v[pos_v + k];
413  }
414  }
415  transform(cell_coeff, cell_info, cell, 1);
416  }
417 }
418 } // namespace impl
419 
420 // NOTE: This is subject to change
422 template <typename U>
424 {
425  using T = typename U::scalar_type;
426 
427  // Get form coefficient offsets and dofmaps
428  const std::vector<std::shared_ptr<const fem::Function<T>>> coefficients
429  = u.coefficients();
430  const std::vector<int> offsets = u.coefficient_offsets();
431  std::vector<const fem::DofMap*> dofmaps(coefficients.size());
432  std::vector<const fem::FiniteElement*> elements(coefficients.size());
433  std::vector<std::reference_wrapper<const std::vector<T>>> v;
434  v.reserve(coefficients.size());
435  for (std::size_t i = 0; i < coefficients.size(); ++i)
436  {
437  elements[i] = coefficients[i]->function_space()->element().get();
438  dofmaps[i] = coefficients[i]->function_space()->dofmap().get();
439  v.push_back(coefficients[i]->x()->array());
440  }
441 
442  // Get mesh
443  std::shared_ptr<const mesh::Mesh> mesh = u.mesh();
444  assert(mesh);
445  const int tdim = mesh->topology().dim();
446  const std::int32_t num_cells
447  = mesh->topology().index_map(tdim)->size_local()
448  + mesh->topology().index_map(tdim)->num_ghosts();
449 
450  // Copy data into coefficient array
451  array2d<T> c(num_cells, offsets.back());
452  if (!coefficients.empty())
453  {
454  bool needs_dof_transformations = false;
455  for (std::size_t coeff = 0; coeff < dofmaps.size(); ++coeff)
456  {
457  if (elements[coeff]->needs_dof_transformations())
458  {
459  needs_dof_transformations = true;
460  mesh->topology_mutable().create_entity_permutations();
461  }
462  }
463 
464  // Iterate over coefficients
465  xtl::span<const std::uint32_t> cell_info;
466  if (needs_dof_transformations)
467  cell_info = xtl::span(mesh->topology().get_cell_permutation_info());
468  for (std::size_t coeff = 0; coeff < dofmaps.size(); ++coeff)
469  {
470  const std::function<void(const xtl::span<T>&,
471  const xtl::span<const std::uint32_t>&,
472  std::int32_t, int)>
473  transformation
474  = elements[coeff]->get_dof_transformation_function<T>(false, true);
475  if (int bs = dofmaps[coeff]->bs(); bs == 1)
476  {
477  impl::pack_coefficient<T, 1>(
478  c, v[coeff], cell_info, *dofmaps[coeff], num_cells, offsets[coeff],
479  elements[coeff]->space_dimension(), transformation);
480  }
481  else if (bs == 2)
482  {
483  impl::pack_coefficient<T, 2>(
484  c, v[coeff], cell_info, *dofmaps[coeff], num_cells, offsets[coeff],
485  elements[coeff]->space_dimension(), transformation);
486  }
487  else if (bs == 3)
488  {
489  impl::pack_coefficient<T, 3>(
490  c, v[coeff], cell_info, *dofmaps[coeff], num_cells, offsets[coeff],
491  elements[coeff]->space_dimension(), transformation);
492  }
493  else
494  {
495  impl::pack_coefficient<T>(
496  c, v[coeff], cell_info, *dofmaps[coeff], num_cells, offsets[coeff],
497  elements[coeff]->space_dimension(), transformation);
498  }
499  }
500  }
501 
502  return c;
503 }
504 
505 // NOTE: This is subject to change
507 template <typename U>
508 std::vector<typename U::scalar_type> pack_constants(const U& u)
509 {
510  using T = typename U::scalar_type;
511  const std::vector<std::shared_ptr<const fem::Constant<T>>>& constants
512  = u.constants();
513 
514  // Calculate size of array needed to store packed constants
515  std::int32_t size = std::accumulate(constants.begin(), constants.end(), 0,
516  [](std::int32_t sum, const auto& constant)
517  { return sum + constant->value.size(); });
518 
519  // Pack constants
520  std::vector<T> constant_values(size);
521  std::int32_t offset = 0;
522  for (const auto& constant : constants)
523  {
524  const std::vector<T>& value = constant->value;
525  for (std::size_t i = 0; i < value.size(); ++i)
526  constant_values[offset + i] = value[i];
527  offset += value.size();
528  }
529 
530  return constant_values;
531 }
532 
533 } // namespace dolfinx::fem
This class provides a dynamic 2-dimensional row-wise array data structure.
Definition: array2d.h:21
constexpr xtl::span< value_type > row(size_type i)
Access a row in the array.
Definition: array2d.h:114
A constant value which can be attached to a Form. Constants may be scalar (rank 0),...
Definition: Constant.h:19
Degree-of-freedom map.
Definition: DofMap.h:68
xtl::span< const std::int32_t > cell_dofs(int cell) const
Local-to-global mapping of dofs on a cell.
Definition: DofMap.h:113
int bs() const noexcept
Return the block size for the dofmap.
Definition: DofMap.cpp:190
Class for variational forms.
Definition: Form.h:60
std::shared_ptr< const mesh::Mesh > mesh() const
Extract common mesh for the form.
Definition: Form.h:144
int rank() const
Rank of the form (bilinear form = 2, linear form = 1, functional = 0, etc)
Definition: Form.h:140
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition: Form.h:175
const std::vector< std::shared_ptr< const fem::FunctionSpace > > & function_spaces() const
Return function spaces for all arguments.
Definition: Form.h:149
This class represents a function in a finite element function space , given by.
Definition: Function.h:47
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:47
This class provides a sparsity pattern data structure that can be used to initialize sparse matrices.
Definition: SparsityPattern.h:33
A MeshTags are used to associate mesh entities with values. The entity index (local to process) ident...
Definition: MeshTags.h:36
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition: Topology.h:56
Miscellaneous classes, functions and types.
Finite element method functionality.
Definition: assemble_matrix_impl.h:23
std::vector< typename U::scalar_type > pack_constants(const U &u)
Pack constants of u of generic type U ready for assembly.
Definition: utils.h:508
Form< T > create_form(const ufc_form &ufc_form, const std::vector< std::shared_ptr< const fem::FunctionSpace >> &spaces, const std::vector< std::shared_ptr< const fem::Function< T >>> &coefficients, const std::vector< std::shared_ptr< const fem::Constant< T >>> &constants, const std::map< IntegralType, const mesh::MeshTags< int > * > &subdomains, const std::shared_ptr< const mesh::Mesh > &mesh=nullptr)
Create a Form from UFC input.
Definition: utils.h:155
ElementDofLayout create_element_dof_layout(const ufc_dofmap &dofmap, const mesh::CellType cell_type, const std::vector< int > &parent_map={})
Create an ElementDofLayout from a ufc_dofmap.
Definition: utils.cpp:77
DofMap create_dofmap(MPI_Comm comm, const ufc_dofmap &dofmap, mesh::Topology &topology, const std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> &reorder_fn, std::shared_ptr< const dolfinx::fem::FiniteElement > element)
Create a dof map on mesh from a ufc_dofmap.
Definition: utils.cpp:145
std::shared_ptr< fem::FunctionSpace > create_functionspace(ufc_function_space *(*fptr)(const char *), const std::string function_name, std::shared_ptr< mesh::Mesh > mesh, const std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> &reorder_fn=nullptr)
Create a FunctionSpace from UFC data.
Definition: utils.cpp:213
std::vector< std::vector< std::array< std::shared_ptr< const fem::FunctionSpace >, 2 > > > extract_function_spaces(const std::vector< std::vector< const fem::Form< T > * >> &a)
Extract test (0) and trial (1) function spaces pairs for each bilinear form for a rectangular array o...
Definition: utils.h:56
std::vector< std::string > get_coefficient_names(const ufc_form &ufc_form)
Get the name of each coefficient in a UFC form.
Definition: utils.cpp:195
array2d< typename U::scalar_type > pack_coefficients(const U &u)
Pack coefficients of u of generic type U ready for assembly.
Definition: utils.h:423
std::vector< std::string > get_constant_names(const ufc_form &ufc_form)
Get the name of each constant in a UFC form.
Definition: utils.cpp:204
IntegralType
Type of integral.
Definition: Form.h:27
la::SparsityPattern create_sparsity_pattern(const Form< T > &a)
Create a sparsity pattern for a given form. The pattern is not finalised, i.e. the caller is responsi...
Definition: utils.h:81
Mesh data structures and algorithms on meshes.
Definition: DirichletBC.h:20
CellType
Cell type identifier.
Definition: cell_types.h:22