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
assemble_matrix_impl.h
1 // Copyright (C) 2018-2019 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 "DofMap.h"
10 #include "Form.h"
11 #include "utils.h"
12 #include <dolfinx/fem/FunctionSpace.h>
13 #include <dolfinx/graph/AdjacencyList.h>
14 #include <dolfinx/la/utils.h>
15 #include <dolfinx/mesh/Geometry.h>
16 #include <dolfinx/mesh/Mesh.h>
17 #include <dolfinx/mesh/Topology.h>
18 #include <functional>
19 #include <iterator>
20 #include <vector>
21 
22 namespace dolfinx::fem::impl
23 {
24 
30 
31 template <typename T>
32 void assemble_matrix(
33  const std::function<int(std::int32_t, const std::int32_t*, std::int32_t,
34  const std::int32_t*, const T*)>& mat_set_values,
35  const Form<T>& a, const xtl::span<const T>& constants,
36  const array2d<T>& coeffs, const std::vector<bool>& bc0,
37  const std::vector<bool>& bc1);
38 
40 template <typename T>
41 void assemble_cells(
42  const std::function<int(std::int32_t, const std::int32_t*, std::int32_t,
43  const std::int32_t*, const T*)>& mat_set,
44  const mesh::Geometry& geometry,
45  const xtl::span<const std::int32_t>& active_cells,
46  const std::function<void(const xtl::span<T>&,
47  const xtl::span<const std::uint32_t>&,
48  std::int32_t, int)>& dof_transform,
49  const graph::AdjacencyList<std::int32_t>& dofmap0, const int bs0,
50  const std::function<void(const xtl::span<T>&,
51  const xtl::span<const std::uint32_t>&,
52  std::int32_t, int)>& dof_transform_to_transpose,
53  const graph::AdjacencyList<std::int32_t>& dofmap1, const int bs1,
54  const std::vector<bool>& bc0, const std::vector<bool>& bc1,
55  const std::function<void(T*, const T*, const T*, const double*, const int*,
56  const std::uint8_t*)>& kernel,
57  const array2d<T>& coeffs, const xtl::span<const T>& constants,
58  const xtl::span<const std::uint32_t>& cell_info)
59 {
60  // Prepare cell geometry
61  const graph::AdjacencyList<std::int32_t>& x_dofmap = geometry.dofmap();
62 
63  // FIXME: Add proper interface for num coordinate dofs
64  const std::size_t num_dofs_g = x_dofmap.num_links(0);
65  const xt::xtensor<double, 2>& x_g = geometry.x();
66 
67  // Iterate over active cells
68  const int num_dofs0 = dofmap0.links(0).size();
69  const int num_dofs1 = dofmap1.links(0).size();
70  const int ndim0 = bs0 * num_dofs0;
71  const int ndim1 = bs1 * num_dofs1;
72  std::vector<T> Ae(ndim0 * ndim1);
73  const xtl::span<T> _Ae(Ae);
74  std::vector<double> coordinate_dofs(3 * num_dofs_g);
75 
76  for (std::int32_t c : active_cells)
77  {
78  // Get cell coordinates/geometry
79  auto x_dofs = x_dofmap.links(c);
80  for (std::size_t i = 0; i < x_dofs.size(); ++i)
81  {
82  std::copy_n(xt::row(x_g, x_dofs[i]).begin(), 3,
83  std::next(coordinate_dofs.begin(), 3 * i));
84  }
85 
86  // Tabulate tensor
87  std::fill(Ae.begin(), Ae.end(), 0);
88  kernel(Ae.data(), coeffs.row(c).data(), constants.data(),
89  coordinate_dofs.data(), nullptr, nullptr);
90 
91  dof_transform(_Ae, cell_info, c, ndim1);
92  dof_transform_to_transpose(_Ae, cell_info, c, ndim0);
93 
94  // Zero rows/columns for essential bcs
95  auto dofs0 = dofmap0.links(c);
96  auto dofs1 = dofmap1.links(c);
97  if (!bc0.empty())
98  {
99  for (int i = 0; i < num_dofs0; ++i)
100  {
101  for (int k = 0; k < bs0; ++k)
102  {
103  if (bc0[bs0 * dofs0[i] + k])
104  {
105  // Zero row bs0 * i + k
106  const int row = bs0 * i + k;
107  std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0.0);
108  }
109  }
110  }
111  }
112 
113  if (!bc1.empty())
114  {
115  for (int j = 0; j < num_dofs1; ++j)
116  {
117  for (int k = 0; k < bs1; ++k)
118  {
119  if (bc1[bs1 * dofs1[j] + k])
120  {
121  // Zero column bs1 * j + k
122  const int col = bs1 * j + k;
123  for (int row = 0; row < ndim0; ++row)
124  Ae[row * ndim1 + col] = 0.0;
125  }
126  }
127  }
128  }
129 
130  mat_set(dofs0.size(), dofs0.data(), dofs1.size(), dofs1.data(), Ae.data());
131  }
132 }
133 
135 template <typename T>
136 void assemble_exterior_facets(
137  const std::function<int(std::int32_t, const std::int32_t*, std::int32_t,
138  const std::int32_t*, const T*)>& mat_set,
139  const mesh::Mesh& mesh, const xtl::span<const std::int32_t>& active_facets,
140  const std::function<void(const xtl::span<T>&,
141  const xtl::span<const std::uint32_t>&,
142  std::int32_t, int)>& dof_transform,
143  const graph::AdjacencyList<std::int32_t>& dofmap0, int bs0,
144  const std::function<void(const xtl::span<T>&,
145  const xtl::span<const std::uint32_t>&,
146  std::int32_t, int)>& dof_transform_to_transpose,
147  const graph::AdjacencyList<std::int32_t>& dofmap1, int bs1,
148  const std::vector<bool>& bc0, const std::vector<bool>& bc1,
149  const std::function<void(T*, const T*, const T*, const double*, const int*,
150  const std::uint8_t*)>& kernel,
151  const array2d<T>& coeffs, const xtl::span<const T>& constants,
152  const xtl::span<const std::uint32_t>& cell_info,
153  const std::function<std::uint8_t(std::size_t)>& get_perm)
154 {
155  const int tdim = mesh.topology().dim();
156 
157  // Prepare cell geometry
158  const graph::AdjacencyList<std::int32_t>& x_dofmap = mesh.geometry().dofmap();
159 
160  // FIXME: Add proper interface for num coordinate dofs
161  const std::size_t num_dofs_g = x_dofmap.num_links(0);
162  const xt::xtensor<double, 2>& x_g = mesh.geometry().x();
163 
164  // Data structures used in assembly
165  std::vector<double> coordinate_dofs(3 * num_dofs_g);
166  const int num_dofs0 = dofmap0.links(0).size();
167  const int num_dofs1 = dofmap1.links(0).size();
168  const int ndim0 = bs0 * num_dofs0;
169  const int ndim1 = bs1 * num_dofs1;
170  std::vector<T> Ae(ndim0 * ndim1);
171  const xtl::span<T> _Ae(Ae);
172 
173  // Iterate over all facets
174  auto f_to_c = mesh.topology().connectivity(tdim - 1, tdim);
175  assert(f_to_c);
176  auto c_to_f = mesh.topology().connectivity(tdim, tdim - 1);
177  assert(c_to_f);
178 
179  for (std::int32_t f : active_facets)
180  {
181  auto cells = f_to_c->links(f);
182  assert(cells.size() == 1);
183 
184  // Get local index of facet with respect to the cell
185  auto facets = c_to_f->links(cells[0]);
186  auto it = std::find(facets.begin(), facets.end(), f);
187  assert(it != facets.end());
188  const int local_facet = std::distance(facets.begin(), it);
189 
190  // Get cell coordinates/geometry
191  auto x_dofs = x_dofmap.links(cells[0]);
192  for (std::size_t i = 0; i < x_dofs.size(); ++i)
193  {
194  std::copy_n(xt::row(x_g, x_dofs[i]).begin(), 3,
195  std::next(coordinate_dofs.begin(), 3 * i));
196  }
197 
198  // Tabulate tensor
199  std::uint8_t perm = get_perm(cells[0] * facets.size() + local_facet);
200  std::fill(Ae.begin(), Ae.end(), 0);
201  kernel(Ae.data(), coeffs.row(cells[0]).data(), constants.data(),
202  coordinate_dofs.data(), &local_facet, &perm);
203 
204  dof_transform(_Ae, cell_info, cells[0], ndim1);
205  dof_transform_to_transpose(_Ae, cell_info, cells[0], ndim0);
206 
207  // Zero rows/columns for essential bcs
208  auto dofs0 = dofmap0.links(cells[0]);
209  auto dofs1 = dofmap1.links(cells[0]);
210  if (!bc0.empty())
211  {
212  for (int i = 0; i < num_dofs0; ++i)
213  {
214  for (int k = 0; k < bs0; ++k)
215  {
216  if (bc0[bs0 * dofs0[i] + k])
217  {
218  // Zero row bs0 * i + k
219  const int row = bs0 * i + k;
220  std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0.0);
221  }
222  }
223  }
224  }
225  if (!bc1.empty())
226  {
227  for (int j = 0; j < num_dofs1; ++j)
228  {
229  for (int k = 0; k < bs1; ++k)
230  {
231  if (bc1[bs1 * dofs1[j] + k])
232  {
233  // Zero column bs1 * j + k
234  const int col = bs1 * j + k;
235  for (int row = 0; row < ndim0; ++row)
236  Ae[row * ndim1 + col] = 0.0;
237  }
238  }
239  }
240  }
241 
242  mat_set(dofs0.size(), dofs0.data(), dofs1.size(), dofs1.data(), Ae.data());
243  }
244 }
245 
247 template <typename T>
248 void assemble_interior_facets(
249  const std::function<int(std::int32_t, const std::int32_t*, std::int32_t,
250  const std::int32_t*, const T*)>& mat_set,
251  const mesh::Mesh& mesh, const xtl::span<const std::int32_t>& active_facets,
252  const std::function<void(const xtl::span<T>&,
253  const xtl::span<const std::uint32_t>&,
254  std::int32_t, int)>& dof_transform,
255  const DofMap& dofmap0, int bs0,
256  const std::function<void(const xtl::span<T>&,
257  const xtl::span<const std::uint32_t>&,
258  std::int32_t, int)>& dof_transform_to_transpose,
259  const DofMap& dofmap1, int bs1, const std::vector<bool>& bc0,
260  const std::vector<bool>& bc1,
261  const std::function<void(T*, const T*, const T*, const double*, const int*,
262  const std::uint8_t*)>& kernel,
263  const array2d<T>& coeffs, const xtl::span<const int>& offsets,
264  const xtl::span<const T>& constants,
265  const xtl::span<const std::uint32_t>& cell_info,
266  const std::function<std::uint8_t(std::size_t)>& get_perm)
267 {
268  const int tdim = mesh.topology().dim();
269 
270  // Prepare cell geometry
271  const graph::AdjacencyList<std::int32_t>& x_dofmap = mesh.geometry().dofmap();
272 
273  // FIXME: Add proper interface for num coordinate dofs
274  const std::size_t num_dofs_g = x_dofmap.num_links(0);
275  const xt::xtensor<double, 2>& x_g = mesh.geometry().x();
276 
277  // Data structures used in assembly
278  xt::xtensor<double, 3> coordinate_dofs({2, num_dofs_g, 3});
279  std::vector<T> Ae, be;
280  std::vector<T> coeff_array(2 * offsets.back());
281  assert(offsets.back() == coeffs.shape[1]);
282 
283  // Temporaries for joint dofmaps
284  std::vector<std::int32_t> dmapjoint0, dmapjoint1;
285 
286  // Iterate over all facets
287  auto c = mesh.topology().connectivity(tdim - 1, tdim);
288  assert(c);
289  auto c_to_f = mesh.topology().connectivity(tdim, tdim - 1);
290  assert(c_to_f);
291 
292  for (std::int32_t facet_index : active_facets)
293  {
294  // Create attached cells
295  auto cells = c->links(facet_index);
296  assert(cells.size() == 2);
297 
298  // Get local index of facet with respect to the cell
299  auto facets0 = c_to_f->links(cells[0]);
300  const auto* it0 = std::find(facets0.begin(), facets0.end(), facet_index);
301  assert(it0 != facets0.end());
302  const int local_facet0 = std::distance(facets0.begin(), it0);
303  auto facets1 = c_to_f->links(cells[1]);
304  const auto* it1 = std::find(facets1.begin(), facets1.end(), facet_index);
305  assert(it1 != facets1.end());
306  const int local_facet1 = std::distance(facets1.begin(), it1);
307 
308  const std::array local_facet = {local_facet0, local_facet1};
309 
310  // Get cell geometry
311  auto x_dofs0 = x_dofmap.links(cells[0]);
312  for (std::size_t i = 0; i < x_dofs0.size(); ++i)
313  {
314  std::copy_n(xt::view(x_g, x_dofs0[i]).begin(), 3,
315  xt::view(coordinate_dofs, 0, i, xt::all()).begin());
316  }
317  auto x_dofs1 = x_dofmap.links(cells[1]);
318  for (std::size_t i = 0; i < x_dofs1.size(); ++i)
319  {
320  std::copy_n(xt::view(x_g, x_dofs1[i]).begin(), 3,
321  xt::view(coordinate_dofs, 1, i, xt::all()).begin());
322  }
323 
324  // Get dof maps for cells and pack
325  xtl::span<const std::int32_t> dmap0_cell0 = dofmap0.cell_dofs(cells[0]);
326  xtl::span<const std::int32_t> dmap0_cell1 = dofmap0.cell_dofs(cells[1]);
327  dmapjoint0.resize(dmap0_cell0.size() + dmap0_cell1.size());
328  std::copy(dmap0_cell0.begin(), dmap0_cell0.end(), dmapjoint0.begin());
329  std::copy(dmap0_cell1.begin(), dmap0_cell1.end(),
330  std::next(dmapjoint0.begin(), dmap0_cell0.size()));
331 
332  xtl::span<const std::int32_t> dmap1_cell0 = dofmap1.cell_dofs(cells[0]);
333  xtl::span<const std::int32_t> dmap1_cell1 = dofmap1.cell_dofs(cells[1]);
334  dmapjoint1.resize(dmap1_cell0.size() + dmap1_cell1.size());
335  std::copy(dmap1_cell0.begin(), dmap1_cell0.end(), dmapjoint1.begin());
336  std::copy(dmap1_cell1.begin(), dmap1_cell1.end(),
337  std::next(dmapjoint1.begin(), dmap1_cell0.size()));
338 
339  // Layout for the restricted coefficients is flattened
340  // w[coefficient][restriction][dof]
341  auto coeff_cell0 = coeffs.row(cells[0]);
342  auto coeff_cell1 = coeffs.row(cells[1]);
343 
344  // Loop over coefficients
345  for (std::size_t i = 0; i < offsets.size() - 1; ++i)
346  {
347  // Loop over entries for coefficient i
348  const int num_entries = offsets[i + 1] - offsets[i];
349  std::copy_n(coeff_cell0.data() + offsets[i], num_entries,
350  std::next(coeff_array.begin(), 2 * offsets[i]));
351  std::copy_n(coeff_cell1.data() + offsets[i], num_entries,
352  std::next(coeff_array.begin(), offsets[i + 1] + offsets[i]));
353  }
354 
355  const int num_rows = bs0 * dmapjoint0.size();
356  const int num_cols = bs1 * dmapjoint1.size();
357 
358  // Tabulate tensor
359  Ae.resize(num_rows * num_cols);
360  std::fill(Ae.begin(), Ae.end(), 0);
361 
362  const int facets_per_cell = facets0.size();
363  const std::array perm{
364  get_perm(cells[0] * facets_per_cell + local_facet[0]),
365  get_perm(cells[1] * facets_per_cell + local_facet[1])};
366  kernel(Ae.data(), coeff_array.data(), constants.data(),
367  coordinate_dofs.data(), local_facet.data(), perm.data());
368 
369  dof_transform(Ae, cell_info, cells[0], num_cols);
370  dof_transform_to_transpose(Ae, cell_info, cells[0], num_rows);
371 
372  // Zero rows/columns for essential bcs
373  if (!bc0.empty())
374  {
375  for (std::size_t i = 0; i < dmapjoint0.size(); ++i)
376  {
377  for (int k = 0; k < bs0; ++k)
378  {
379  if (bc0[bs0 * dmapjoint0[i] + k])
380  {
381  // Zero row bs0 * i + k
382  std::fill_n(std::next(Ae.begin(), num_cols * (bs0 * i + k)),
383  num_cols, 0.0);
384  }
385  }
386  }
387  }
388  if (!bc1.empty())
389  {
390  for (std::size_t j = 0; j < dmapjoint1.size(); ++j)
391  {
392  for (int k = 0; k < bs1; ++k)
393  {
394  if (bc1[bs1 * dmapjoint1[j] + k])
395  {
396  // Zero column bs1 * j + k
397  for (int m = 0; m < num_rows; ++m)
398  Ae[m * num_cols + bs1 * j + k] = 0.0;
399  }
400  }
401  }
402  }
403 
404  mat_set(dmapjoint0.size(), dmapjoint0.data(), dmapjoint1.size(),
405  dmapjoint1.data(), Ae.data());
406  }
407 }
408 
409 template <typename T>
410 void assemble_matrix(
411  const std::function<int(std::int32_t, const std::int32_t*, std::int32_t,
412  const std::int32_t*, const T*)>& mat_set,
413  const Form<T>& a, const xtl::span<const T>& constants,
414  const array2d<T>& coeffs, const std::vector<bool>& bc0,
415  const std::vector<bool>& bc1)
416 {
417  std::shared_ptr<const mesh::Mesh> mesh = a.mesh();
418  assert(mesh);
419  const int tdim = mesh->topology().dim();
420 
421  // Get dofmap data
422  std::shared_ptr<const fem::DofMap> dofmap0
423  = a.function_spaces().at(0)->dofmap();
424  std::shared_ptr<const fem::DofMap> dofmap1
425  = a.function_spaces().at(1)->dofmap();
426  assert(dofmap0);
427  assert(dofmap1);
428  const graph::AdjacencyList<std::int32_t>& dofs0 = dofmap0->list();
429  const int bs0 = dofmap0->bs();
430  const graph::AdjacencyList<std::int32_t>& dofs1 = dofmap1->list();
431  const int bs1 = dofmap1->bs();
432 
433  std::shared_ptr<const fem::FiniteElement> element0
434  = a.function_spaces().at(0)->element();
435  std::shared_ptr<const fem::FiniteElement> element1
436  = a.function_spaces().at(1)->element();
437  const std::function<void(const xtl::span<T>&,
438  const xtl::span<const std::uint32_t>&, std::int32_t,
439  int)>& dof_transform
440  = element0->get_dof_transformation_function<T>();
441  const std::function<void(const xtl::span<T>&,
442  const xtl::span<const std::uint32_t>&, std::int32_t,
443  int)>& dof_transform_to_transpose
444  = element1->get_dof_transformation_to_transpose_function<T>();
445 
446  const bool needs_transformation_data
447  = element0->needs_dof_transformations()
448  or element1->needs_dof_transformations()
450  xtl::span<const std::uint32_t> cell_info;
451  if (needs_transformation_data)
452  {
453  mesh->topology_mutable().create_entity_permutations();
454  cell_info = xtl::span(mesh->topology().get_cell_permutation_info());
455  }
456 
457  for (int i : a.integral_ids(IntegralType::cell))
458  {
459  const auto& fn = a.kernel(IntegralType::cell, i);
460  const std::vector<std::int32_t>& active_cells
461  = a.domains(IntegralType::cell, i);
462  impl::assemble_cells<T>(mat_set, mesh->geometry(), active_cells,
463  dof_transform, dofs0, bs0,
464  dof_transform_to_transpose, dofs1, bs1, bc0, bc1,
465  fn, coeffs, constants, cell_info);
466  }
467 
468  if (a.num_integrals(IntegralType::exterior_facet) > 0
469  or a.num_integrals(IntegralType::interior_facet) > 0)
470  {
471  mesh->topology_mutable().create_connectivity(tdim - 1, tdim);
472  std::function<std::uint8_t(std::size_t)> get_perm;
473  if (a.needs_facet_permutations())
474  {
475  mesh->topology_mutable().create_entity_permutations();
476  const std::vector<std::uint8_t>& perms
477  = mesh->topology().get_facet_permutations();
478  get_perm = [&perms](std::size_t i) { return perms[i]; };
479  }
480  else
481  get_perm = [](std::size_t) { return 0; };
482 
483  for (int i : a.integral_ids(IntegralType::exterior_facet))
484  {
485  const auto& fn = a.kernel(IntegralType::exterior_facet, i);
486  const std::vector<std::int32_t>& active_facets
487  = a.domains(IntegralType::exterior_facet, i);
488  impl::assemble_exterior_facets<T>(
489  mat_set, *mesh, active_facets, dof_transform, dofs0, bs0,
490  dof_transform_to_transpose, dofs1, bs1, bc0, bc1, fn, coeffs,
491  constants, cell_info, get_perm);
492  }
493 
494  const std::vector<int> c_offsets = a.coefficient_offsets();
495  for (int i : a.integral_ids(IntegralType::interior_facet))
496  {
497  const auto& fn = a.kernel(IntegralType::interior_facet, i);
498  const std::vector<std::int32_t>& active_facets
499  = a.domains(IntegralType::interior_facet, i);
500  impl::assemble_interior_facets<T>(
501  mat_set, *mesh, active_facets, dof_transform, *dofmap0, bs0,
502  dof_transform_to_transpose, *dofmap1, bs1, bc0, bc1, fn, coeffs,
503  c_offsets, constants, cell_info, get_perm);
504  }
505  }
506 }
507 
508 } // namespace dolfinx::fem::impl
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
std::array< size_type, 2 > shape
The shape of the array.
Definition: array2d.h:155
Degree-of-freedom map.
Definition: DofMap.h:68
Class for variational forms.
Definition: Form.h:60
std::vector< int > integral_ids(IntegralType type) const
Get the IDs for integrals (kernels) for given integral type. The IDs correspond to the domain IDs whi...
Definition: Form.h:200
const std::function< void(T *, const T *, const T *, const double *, const int *, const std::uint8_t *)> & kernel(IntegralType type, int i) const
Get the function for 'kernel' for integral i of given type.
Definition: Form.h:161
std::shared_ptr< const mesh::Mesh > mesh() const
Extract common mesh for the form.
Definition: Form.h:144
bool needs_facet_permutations() const
Get bool indicating whether permutation data needs to be passed into these integrals.
Definition: Form.h:238
std::vector< int > coefficient_offsets() const
Offset for each coefficient expansion array on a cell. Used to pack data for multiple coefficients in...
Definition: Form.h:243
const std::vector< std::int32_t > & domains(IntegralType type, int i) const
Get the list of mesh entity indices for the ith integral (kernel) for the given domain type,...
Definition: Form.h:217
const std::vector< std::shared_ptr< const fem::FunctionSpace > > & function_spaces() const
Return function spaces for all arguments.
Definition: Form.h:149
int num_integrals(IntegralType type) const
Number of integrals of given type.
Definition: Form.h:186
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
Geometry stores the geometry imposed on a mesh.
Definition: Geometry.h:37
const graph::AdjacencyList< std::int32_t > & dofmap() const
DOF map.
Definition: Geometry.cpp:22
xt::xtensor< double, 2 > & x()
Geometry degrees-of-freedom.
Definition: Geometry.cpp:32
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition: Mesh.h:53
Geometry & geometry()
Get mesh geometry.
Definition: Mesh.cpp:179
Topology & topology()
Get mesh topology.
Definition: Mesh.cpp:173
int dim() const noexcept
Return the topological dimension of the mesh.
Definition: Topology.cpp:163
std::shared_ptr< const graph::AdjacencyList< std::int32_t > > connectivity(int d0, int d1) const
Return connectivity from entities of dimension d0 to entities of dimension d1.
Definition: Topology.cpp:253
void assemble_matrix(const std::function< int(std::int32_t, const std::int32_t *, std::int32_t, const std::int32_t *, const T *)> &mat_add, const Form< T > &a, const xtl::span< const T > &constants, const array2d< T > &coeffs, const std::vector< std::shared_ptr< const DirichletBC< T >>> &bcs)
Assemble bilinear form into a matrix.
Definition: assembler.h:166