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_vector_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 "DirichletBC.h"
10 #include "DofMap.h"
11 #include "Form.h"
12 #include "utils.h"
13 #include <dolfinx/common/IndexMap.h>
14 #include <dolfinx/common/types.h>
15 #include <dolfinx/fem/Constant.h>
16 #include <dolfinx/fem/FunctionSpace.h>
17 #include <dolfinx/graph/AdjacencyList.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 <vector>
24 #include <xtensor/xbuilder.hpp>
25 #include <xtensor/xtensor.hpp>
26 
27 namespace dolfinx::fem::impl
28 {
29 
31 
39 template <typename T, int _bs0 = -1, int _bs1 = -1>
40 void _lift_bc_cells(
41  xtl::span<T> b, const mesh::Geometry& geometry,
42  const std::function<void(T*, const T*, const T*, const double*, const int*,
43  const std::uint8_t*)>& kernel,
44  const xtl::span<const std::int32_t>& active_cells,
45  const std::function<void(const xtl::span<T>&,
46  const xtl::span<const std::uint32_t>&,
47  std::int32_t, int)>& dof_transform,
48  const graph::AdjacencyList<std::int32_t>& dofmap0, int bs0,
49  const std::function<void(const xtl::span<T>&,
50  const xtl::span<const std::uint32_t>&,
51  std::int32_t, int)>& dof_transform_to_transpose,
52  const graph::AdjacencyList<std::int32_t>& dofmap1, int bs1,
53  const xtl::span<const T>& constants, const array2d<T>& coeffs,
54  const xtl::span<const std::uint32_t>& cell_info,
55  const xtl::span<const T>& bc_values1, const std::vector<bool>& bc_markers1,
56  const xtl::span<const T>& x0, double scale)
57 {
58  assert(_bs0 < 0 or _bs0 == bs0);
59  assert(_bs1 < 0 or _bs1 == bs1);
60 
61  // Prepare cell geometry
62  const graph::AdjacencyList<std::int32_t>& x_dofmap = geometry.dofmap();
63 
64  // FIXME: Add proper interface for num coordinate dofs
65  const std::size_t num_dofs_g = x_dofmap.num_links(0);
66  const xt::xtensor<double, 2>& x_g = geometry.x();
67 
68  // Data structures used in bc application
69  std::vector<double> coordinate_dofs(3 * num_dofs_g);
70  std::vector<T> Ae, be;
71  for (std::int32_t c : active_cells)
72  {
73  // Get dof maps for cell
74  auto dmap1 = dofmap1.links(c);
75 
76  // Check if bc is applied to cell
77  bool has_bc = false;
78  for (std::size_t j = 0; j < dmap1.size(); ++j)
79  {
80  if constexpr (_bs1 > 0)
81  {
82  for (int k = 0; k < _bs1; ++k)
83  {
84  assert(_bs1 * dmap1[j] + k < (int)bc_markers1.size());
85  if (bc_markers1[_bs1 * dmap1[j] + k])
86  {
87  has_bc = true;
88  break;
89  }
90  }
91  }
92  else
93  {
94  for (int k = 0; k < bs1; ++k)
95  {
96  assert(bs1 * dmap1[j] + k < (int)bc_markers1.size());
97  if (bc_markers1[bs1 * dmap1[j] + k])
98  {
99  has_bc = true;
100  break;
101  }
102  }
103  }
104  }
105 
106  if (!has_bc)
107  continue;
108 
109  // Get cell coordinates/geometry
110  auto x_dofs = x_dofmap.links(c);
111  for (std::size_t i = 0; i < x_dofs.size(); ++i)
112  {
113  std::copy_n(xt::row(x_g, x_dofs[i]).begin(), 3,
114  std::next(coordinate_dofs.begin(), i * 3));
115  }
116 
117  // Size data structure for assembly
118  auto dmap0 = dofmap0.links(c);
119 
120  const int num_rows = bs0 * dmap0.size();
121  const int num_cols = bs1 * dmap1.size();
122 
123  auto coeff_array = coeffs.row(c);
124  Ae.resize(num_rows * num_cols);
125  std::fill(Ae.begin(), Ae.end(), 0);
126  kernel(Ae.data(), coeff_array.data(), constants.data(),
127  coordinate_dofs.data(), nullptr, nullptr);
128  dof_transform(Ae, cell_info, c, num_cols);
129  dof_transform_to_transpose(Ae, cell_info, c, num_rows);
130 
131  // Size data structure for assembly
132  be.resize(num_rows);
133  std::fill(be.begin(), be.end(), 0);
134  for (std::size_t j = 0; j < dmap1.size(); ++j)
135  {
136  if constexpr (_bs1 > 0)
137  {
138  for (int k = 0; k < _bs1; ++k)
139  {
140  const std::int32_t jj = _bs1 * dmap1[j] + k;
141  assert(jj < (int)bc_markers1.size());
142  if (bc_markers1[jj])
143  {
144  const T bc = bc_values1[jj];
145  const T _x0 = x0.empty() ? 0.0 : x0[jj];
146  // const T _x0 = 0.0;
147  // be -= Ae.col(bs1 * j + k) * scale * (bc - _x0);
148  for (int m = 0; m < num_rows; ++m)
149  be[m] -= Ae[m * num_cols + _bs1 * j + k] * scale * (bc - _x0);
150  }
151  }
152  }
153  else
154  {
155  for (int k = 0; k < bs1; ++k)
156  {
157  const std::int32_t jj = bs1 * dmap1[j] + k;
158  assert(jj < (int)bc_markers1.size());
159  if (bc_markers1[jj])
160  {
161  const T bc = bc_values1[jj];
162  const T _x0 = x0.empty() ? 0.0 : x0[jj];
163  // be -= Ae.col(bs1 * j + k) * scale * (bc - _x0);
164  for (int m = 0; m < num_rows; ++m)
165  be[m] -= Ae[m * num_cols + bs1 * j + k] * scale * (bc - _x0);
166  }
167  }
168  }
169  }
170 
171  for (std::size_t i = 0; i < dmap0.size(); ++i)
172  {
173  if constexpr (_bs0 > 0)
174  {
175  for (int k = 0; k < _bs0; ++k)
176  b[_bs0 * dmap0[i] + k] += be[_bs0 * i + k];
177  }
178  else
179  {
180  for (int k = 0; k < bs0; ++k)
181  b[bs0 * dmap0[i] + k] += be[bs0 * i + k];
182  }
183  }
184  }
185 }
186 
193 template <typename T, int _bs = -1>
194 void _lift_bc_exterior_facets(
195  xtl::span<T> b, const mesh::Mesh& mesh,
196  const std::function<void(T*, const T*, const T*, const double*, const int*,
197  const std::uint8_t*)>& kernel,
198  const xtl::span<const std::int32_t>& active_facets,
199  const std::function<void(const xtl::span<T>&,
200  const xtl::span<const std::uint32_t>&,
201  std::int32_t, int)>& dof_transform,
202  const graph::AdjacencyList<std::int32_t>& dofmap0, int bs0,
203  const std::function<void(const xtl::span<T>&,
204  const xtl::span<const std::uint32_t>&,
205  std::int32_t, int)>& dof_transform_to_transpose,
206  const graph::AdjacencyList<std::int32_t>& dofmap1, int bs1,
207  const xtl::span<const T>& constants, const array2d<T>& coeffs,
208  const xtl::span<const std::uint32_t>& cell_info,
209  const std::function<std::uint8_t(std::size_t)>& get_perm,
210  const xtl::span<const T>& bc_values1, const std::vector<bool>& bc_markers1,
211  const xtl::span<const T>& x0, double scale)
212 {
213  const int tdim = mesh.topology().dim();
214 
215  // Prepare cell geometry
216  const graph::AdjacencyList<std::int32_t>& x_dofmap = mesh.geometry().dofmap();
217 
218  // FIXME: Add proper interface for num coordinate dofs
219  const std::size_t num_dofs_g = x_dofmap.num_links(0);
220  const xt::xtensor<double, 2>& x_g = mesh.geometry().x();
221 
222  // Data structures used in bc application
223  std::vector<double> coordinate_dofs(3 * num_dofs_g);
224  std::vector<T> Ae, be;
225 
226  // Iterate over owned facets
227  const mesh::Topology& topology = mesh.topology();
228  auto connectivity = topology.connectivity(tdim - 1, tdim);
229  assert(connectivity);
230  auto c_to_f = topology.connectivity(tdim, tdim - 1);
231  assert(c_to_f);
232  auto map = topology.index_map(tdim - 1);
233  assert(map);
234 
235  for (std::int32_t f : active_facets)
236  {
237  // Create attached cell
238  assert(connectivity->num_links(f) == 1);
239  const std::int32_t cell = connectivity->links(f)[0];
240 
241  // Get local index of facet with respect to the cell
242  auto facets = c_to_f->links(cell);
243  auto it = std::find(facets.begin(), facets.end(), f);
244  assert(it != facets.end());
245  const int local_facet = std::distance(facets.begin(), it);
246 
247  // Get dof maps for cell
248  auto dmap1 = dofmap1.links(cell);
249 
250  // Check if bc is applied to cell
251  bool has_bc = false;
252  for (std::size_t j = 0; j < dmap1.size(); ++j)
253  {
254  for (int k = 0; k < bs1; ++k)
255  {
256  if (bc_markers1[bs1 * dmap1[j] + k])
257  {
258  has_bc = true;
259  break;
260  }
261  }
262  }
263 
264  if (!has_bc)
265  continue;
266 
267  // Get cell coordinates/geometry
268  auto x_dofs = x_dofmap.links(cell);
269  for (std::size_t i = 0; i < x_dofs.size(); ++i)
270  {
271  std::copy_n(xt::row(x_g, x_dofs[i]).begin(), 3,
272  std::next(coordinate_dofs.begin(), 3 * i));
273  }
274 
275  // Size data structure for assembly
276  auto dmap0 = dofmap0.links(cell);
277 
278  const int num_rows = bs0 * dmap0.size();
279  const int num_cols = bs1 * dmap1.size();
280 
281  auto coeff_array = coeffs.row(cell);
282  Ae.resize(num_rows * num_cols);
283  std::fill(Ae.begin(), Ae.end(), 0);
284  const std::uint8_t perm = get_perm(cell * facets.size() + local_facet);
285  kernel(Ae.data(), coeff_array.data(), constants.data(),
286  coordinate_dofs.data(), &local_facet, &perm);
287  dof_transform(Ae, cell_info, cell, num_cols);
288  dof_transform_to_transpose(Ae, cell_info, cell, num_rows);
289 
290  // Size data structure for assembly
291  be.resize(num_rows);
292  std::fill(be.begin(), be.end(), 0);
293  for (std::size_t j = 0; j < dmap1.size(); ++j)
294  {
295  for (int k = 0; k < bs1; ++k)
296  {
297  const std::int32_t jj = bs1 * dmap1[j] + k;
298  if (bc_markers1[jj])
299  {
300  const T bc = bc_values1[jj];
301  const T _x0 = x0.empty() ? 0.0 : x0[jj];
302  // be -= Ae.col(bs1 * j + k) * scale * (bc - _x0);
303  for (int m = 0; m < num_rows; ++m)
304  be[m] -= Ae[m * num_cols + bs1 * j + k] * scale * (bc - _x0);
305  }
306  }
307  }
308 
309  for (std::size_t i = 0; i < dmap0.size(); ++i)
310  for (int k = 0; k < bs0; ++k)
311  b[bs0 * dmap0[i] + k] += be[bs0 * i + k];
312  }
313 }
314 
321 template <typename T, int _bs = -1>
322 void _lift_bc_interior_facets(
323  xtl::span<T> b, const mesh::Mesh& mesh,
324  const std::function<void(T*, const T*, const T*, const double*, const int*,
325  const std::uint8_t*)>& kernel,
326  const xtl::span<const std::int32_t>& active_facets,
327  const std::function<void(const xtl::span<T>&,
328  const xtl::span<const std::uint32_t>&,
329  std::int32_t, int)>& dof_transform,
330  const graph::AdjacencyList<std::int32_t>& dofmap0, int bs0,
331  const std::function<void(const xtl::span<T>&,
332  const xtl::span<const std::uint32_t>&,
333  std::int32_t, int)>& dof_transform_to_transpose,
334  const graph::AdjacencyList<std::int32_t>& dofmap1, int bs1,
335  const xtl::span<const T>& constants, const array2d<T>& coeffs,
336  const std::vector<int>& offsets,
337  const xtl::span<const std::uint32_t>& cell_info,
338  const std::function<std::uint8_t(std::size_t)>& get_perm,
339  const xtl::span<const T>& bc_values1, const std::vector<bool>& bc_markers1,
340  const xtl::span<const T>& x0, double scale)
341 {
342  const int tdim = mesh.topology().dim();
343 
344  // Prepare cell geometry
345  const graph::AdjacencyList<std::int32_t>& x_dofmap = mesh.geometry().dofmap();
346 
347  // FIXME: Add proper interface for num coordinate dofs
348  const std::size_t num_dofs_g = x_dofmap.num_links(0);
349  const xt::xtensor<double, 2>& x_g = mesh.geometry().x();
350 
351  // Data structures used in assembly
352  xt::xtensor<double, 3> coordinate_dofs({2, num_dofs_g, 3});
353  std::vector<T> coeff_array(2 * offsets.back());
354  assert(offsets.back() == int(coeffs.shape[1]));
355  std::vector<T> Ae, be;
356 
357  // Temporaries for joint dofmaps
358  std::vector<std::int32_t> dmapjoint0, dmapjoint1;
359 
360  const mesh::Topology& topology = mesh.topology();
361  auto connectivity = topology.connectivity(tdim - 1, tdim);
362  assert(connectivity);
363  auto c_to_f = topology.connectivity(tdim, tdim - 1);
364  assert(c_to_f);
365  auto f_to_c = topology.connectivity(tdim - 1, tdim);
366  assert(f_to_c);
367  auto map = topology.index_map(tdim - 1);
368  assert(map);
369 
370  for (std::int32_t f : active_facets)
371  {
372  // Create attached cells
373  auto cells = f_to_c->links(f);
374  assert(cells.size() == 2);
375 
376  // Get local index of facet with respect to the cell
377  auto facets0 = c_to_f->links(cells[0]);
378  auto it0 = std::find(facets0.begin(), facets0.end(), f);
379  assert(it0 != facets0.end());
380  const int local_facet0 = std::distance(facets0.begin(), it0);
381  auto facets1 = c_to_f->links(cells[1]);
382  auto it1 = std::find(facets1.begin(), facets1.end(), f);
383  assert(it1 != facets1.end());
384  const int local_facet1 = std::distance(facets1.begin(), it1);
385 
386  const std::array local_facet{local_facet0, local_facet1};
387 
388  // Get cell geometry
389  auto x_dofs0 = x_dofmap.links(cells[0]);
390  for (std::size_t i = 0; i < x_dofs0.size(); ++i)
391  {
392  std::copy_n(xt::view(x_g, x_dofs0[i]).begin(), 3,
393  xt::view(coordinate_dofs, 0, i, xt::all()).begin());
394  }
395  auto x_dofs1 = x_dofmap.links(cells[1]);
396  for (std::size_t i = 0; i < x_dofs1.size(); ++i)
397  {
398  std::copy_n(xt::view(x_g, x_dofs1[i]).begin(), 3,
399  xt::view(coordinate_dofs, 1, i, xt::all()).begin());
400  }
401 
402  // Get dof maps for cells and pack
403  const xtl::span<const std::int32_t> dmap0_cell0 = dofmap0.links(cells[0]);
404  const xtl::span<const std::int32_t> dmap0_cell1 = dofmap0.links(cells[1]);
405  dmapjoint0.resize(dmap0_cell0.size() + dmap0_cell1.size());
406  std::copy(dmap0_cell0.begin(), dmap0_cell0.end(), dmapjoint0.begin());
407  std::copy(dmap0_cell1.begin(), dmap0_cell1.end(),
408  std::next(dmapjoint0.begin(), dmap0_cell0.size()));
409 
410  const xtl::span<const std::int32_t> dmap1_cell0 = dofmap1.links(cells[0]);
411  const xtl::span<const std::int32_t> dmap1_cell1 = dofmap1.links(cells[1]);
412  dmapjoint1.resize(dmap1_cell0.size() + dmap1_cell1.size());
413  std::copy(dmap1_cell0.begin(), dmap1_cell0.end(), dmapjoint1.begin());
414  std::copy(dmap1_cell1.begin(), dmap1_cell1.end(),
415  std::next(dmapjoint1.begin(), dmap1_cell0.size()));
416 
417  // Check if bc is applied to cell0
418  bool has_bc = false;
419  for (std::size_t j = 0; j < dmap1_cell0.size(); ++j)
420  {
421  for (int k = 0; k < bs1; ++k)
422  {
423  if (bc_markers1[bs1 * dmap1_cell0[j] + k])
424  {
425  has_bc = true;
426  break;
427  }
428  }
429  }
430 
431  // Check if bc is applied to cell1
432  for (std::size_t j = 0; j < dmap1_cell1.size(); ++j)
433  {
434  for (int k = 0; k < bs1; ++k)
435  {
436  if (bc_markers1[bs1 * dmap1_cell1[j] + k])
437  {
438  has_bc = true;
439  break;
440  }
441  }
442  }
443 
444  if (!has_bc)
445  continue;
446 
447  // Layout for the restricted coefficients is flattened
448  // w[coefficient][restriction][dof]
449  const auto coeff_cell0 = coeffs.row(cells[0]);
450  const auto coeff_cell1 = coeffs.row(cells[1]);
451 
452  // Loop over coefficients
453  for (std::size_t i = 0; i < offsets.size() - 1; ++i)
454  {
455  // Loop over entries for coefficient i
456  const int num_entries = offsets[i + 1] - offsets[i];
457  std::copy_n(coeff_cell0.data() + offsets[i], num_entries,
458  std::next(coeff_array.begin(), 2 * offsets[i]));
459  std::copy_n(coeff_cell1.data() + offsets[i], num_entries,
460  std::next(coeff_array.begin(), offsets[i + 1] + offsets[i]));
461  }
462 
463  const int num_rows = bs0 * dmapjoint0.size();
464  const int num_cols = bs1 * dmapjoint1.size();
465 
466  // Tabulate tensor
467  Ae.resize(num_rows * num_cols);
468  std::fill(Ae.begin(), Ae.end(), 0);
469  const int facets_per_cell = facets0.size();
470  const std::array perm{
471  get_perm(cells[0] * facets_per_cell + local_facet[0]),
472  get_perm(cells[1] * facets_per_cell + local_facet[1])};
473  kernel(Ae.data(), coeff_array.data(), constants.data(),
474  coordinate_dofs.data(), local_facet.data(), perm.data());
475  dof_transform(Ae, cell_info, cells[0], num_cols);
476  dof_transform_to_transpose(Ae, cell_info, cells[0], num_rows);
477 
478  be.resize(num_rows);
479  std::fill(be.begin(), be.end(), 0);
480 
481  // Compute b = b - A*b for cell0
482  for (std::size_t j = 0; j < dmap1_cell0.size(); ++j)
483  {
484  for (int k = 0; k < bs1; ++k)
485  {
486  const std::int32_t jj = bs1 * dmap1_cell0[j] + k;
487  if (bc_markers1[jj])
488  {
489  const T bc = bc_values1[jj];
490  const T _x0 = x0.empty() ? 0.0 : x0[jj];
491  // be -= Ae.col(bs1 * j + k) * scale * (bc - _x0);
492  for (int m = 0; m < num_rows; ++m)
493  be[m] -= Ae[m * num_cols + bs1 * j + k] * scale * (bc - _x0);
494  }
495  }
496  }
497 
498  // Compute b = b - A*b for cell1
499  const int offset = bs1 * dmap1_cell0.size();
500  for (std::size_t j = 0; j < dmap1_cell1.size(); ++j)
501  {
502  for (int k = 0; k < bs1; ++k)
503  {
504  const std::int32_t jj = bs1 * dmap1_cell1[j] + k;
505  if (bc_markers1[jj])
506  {
507  const T bc = bc_values1[jj];
508  const T _x0 = x0.empty() ? 0.0 : x0[jj];
509  // be -= Ae.col(offset + bs1 * j + k) * scale * (bc - x0[jj]);
510  for (int m = 0; m < num_rows; ++m)
511  {
512  be[m]
513  -= Ae[m * num_cols + offset + bs1 * j + k] * scale * (bc - _x0);
514  }
515  }
516  }
517  }
518 
519  for (std::size_t i = 0; i < dmap0_cell0.size(); ++i)
520  for (int k = 0; k < bs0; ++k)
521  b[bs0 * dmap0_cell0[i] + k] += be[bs0 * i + k];
522 
523  for (std::size_t i = 0; i < dmap0_cell1.size(); ++i)
524  for (int k = 0; k < bs0; ++k)
525  b[bs0 * dmap0_cell1[i] + k] += be[offset + bs0 * i + k];
526  }
527 }
534 template <typename T, int _bs = -1>
535 void assemble_cells(
536  const std::function<void(const xtl::span<T>&,
537  const xtl::span<const std::uint32_t>&,
538  std::int32_t, int)>& dof_transform,
539  xtl::span<T> b, const mesh::Geometry& geometry,
540  const xtl::span<const std::int32_t>& active_cells,
541  const graph::AdjacencyList<std::int32_t>& dofmap, int bs,
542  const std::function<void(T*, const T*, const T*, const double*, const int*,
543  const std::uint8_t*)>& kernel,
544  const xtl::span<const T>& constants, const array2d<T>& coeffs,
545  const xtl::span<const std::uint32_t>& cell_info)
546 {
547  assert(_bs < 0 or _bs == bs);
548 
549  // Prepare cell geometry
550  const graph::AdjacencyList<std::int32_t>& x_dofmap = geometry.dofmap();
551 
552  // FIXME: Add proper interface for num coordinate dofs
553  const int num_dofs_g = x_dofmap.num_links(0);
554  const xt::xtensor<double, 2>& x_g = geometry.x();
555 
556  // FIXME: Add proper interface for num_dofs
557  // Create data structures used in assembly
558  const int num_dofs = dofmap.links(0).size();
559  std::vector<double> coordinate_dofs(3 * num_dofs_g);
560  std::vector<T> be(bs * num_dofs);
561  const xtl::span<T> _be(be);
562 
563  // Iterate over active cells
564  for (std::int32_t c : active_cells)
565  {
566  // Get cell coordinates/geometry
567  auto x_dofs = x_dofmap.links(c);
568  for (std::size_t i = 0; i < x_dofs.size(); ++i)
569  {
570  std::copy_n(xt::row(x_g, x_dofs[i]).begin(), 3,
571  std::next(coordinate_dofs.begin(), 3 * i));
572  }
573 
574  // Tabulate vector for cell
575  std::fill(be.begin(), be.end(), 0);
576  kernel(be.data(), coeffs.row(c).data(), constants.data(),
577  coordinate_dofs.data(), nullptr, nullptr);
578  dof_transform(_be, cell_info, c, 1);
579 
580  // Scatter cell vector to 'global' vector array
581  auto dofs = dofmap.links(c);
582  if constexpr (_bs > 0)
583  {
584  for (int i = 0; i < num_dofs; ++i)
585  for (int k = 0; k < _bs; ++k)
586  b[_bs * dofs[i] + k] += be[_bs * i + k];
587  }
588  else
589  {
590  for (int i = 0; i < num_dofs; ++i)
591  for (int k = 0; k < bs; ++k)
592  b[bs * dofs[i] + k] += be[bs * i + k];
593  }
594  }
595 }
596 
603 template <typename T, int _bs = -1>
604 void assemble_exterior_facets(
605  const std::function<void(const xtl::span<T>&,
606  const xtl::span<const std::uint32_t>&,
607  std::int32_t, int)>& dof_transform,
608  xtl::span<T> b, const mesh::Mesh& mesh,
609  const xtl::span<const std::int32_t>& active_facets,
610  const graph::AdjacencyList<std::int32_t>& dofmap, const int bs,
611  const std::function<void(T*, const T*, const T*, const double*, const int*,
612  const std::uint8_t*)>& fn,
613  const xtl::span<const T>& constants, const array2d<T>& coeffs,
614  const xtl::span<const std::uint32_t>& cell_info,
615  const std::function<std::uint8_t(std::size_t)>& get_perm)
616 {
617  assert(_bs < 0 or _bs == bs);
618 
619  const int tdim = mesh.topology().dim();
620 
621  // Prepare cell geometry
622  const graph::AdjacencyList<std::int32_t>& x_dofmap = mesh.geometry().dofmap();
623 
624  // FIXME: Add proper interface for num coordinate dofs
625  const std::size_t num_dofs_g = x_dofmap.num_links(0);
626  const xt::xtensor<double, 2>& x_g = mesh.geometry().x();
627 
628  // FIXME: Add proper interface for num_dofs
629  // Create data structures used in assembly
630  const int num_dofs = dofmap.links(0).size();
631  std::vector<double> coordinate_dofs(3 * num_dofs_g);
632  std::vector<T> be(bs * num_dofs);
633  const xtl::span<T> _be(be);
634 
635  auto f_to_c = mesh.topology().connectivity(tdim - 1, tdim);
636  assert(f_to_c);
637  auto c_to_f = mesh.topology().connectivity(tdim, tdim - 1);
638  assert(c_to_f);
639  for (std::int32_t f : active_facets)
640  {
641  // Get index of first attached cell
642  assert(f_to_c->num_links(f) > 0);
643  const std::int32_t cell = f_to_c->links(f)[0];
644 
645  // Get local index of facet with respect to the cell
646  auto facets = c_to_f->links(cell);
647  auto it = std::find(facets.begin(), facets.end(), f);
648  assert(it != facets.end());
649  const int local_facet = std::distance(facets.begin(), it);
650 
651  // Get cell coordinates/geometry
652  auto x_dofs = x_dofmap.links(cell);
653  for (std::size_t i = 0; i < x_dofs.size(); ++i)
654  {
655  std::copy_n(xt::row(x_g, x_dofs[i]).begin(), 3,
656  std::next(coordinate_dofs.begin(), 3 * i));
657  }
658 
659  // Tabulate element vector
660  std::fill(be.begin(), be.end(), 0);
661  const std::uint8_t perm = get_perm(cell * facets.size() + local_facet);
662  fn(be.data(), coeffs.row(cell).data(), constants.data(),
663  coordinate_dofs.data(), &local_facet, &perm);
664 
665  dof_transform(_be, cell_info, cell, 1);
666 
667  // Add element vector to global vector
668  auto dofs = dofmap.links(cell);
669  if constexpr (_bs > 0)
670  {
671  for (int i = 0; i < num_dofs; ++i)
672  for (int k = 0; k < _bs; ++k)
673  b[_bs * dofs[i] + k] += be[_bs * i + k];
674  }
675  else
676  {
677  for (int i = 0; i < num_dofs; ++i)
678  for (int k = 0; k < bs; ++k)
679  b[bs * dofs[i] + k] += be[bs * i + k];
680  }
681  }
682 }
683 
690 template <typename T, int _bs = -1>
691 void assemble_interior_facets(
692  const std::function<void(const xtl::span<T>&,
693  const xtl::span<const std::uint32_t>&,
694  std::int32_t, int)>& dof_transform,
695  xtl::span<T> b, const mesh::Mesh& mesh,
696  const xtl::span<const std::int32_t>& active_facets,
697  const fem::DofMap& dofmap,
698  const std::function<void(T*, const T*, const T*, const double*, const int*,
699  const std::uint8_t*)>& fn,
700  const xtl::span<const T>& constants, const array2d<T>& coeffs,
701  const xtl::span<const int>& offsets,
702  const xtl::span<const std::uint32_t>& cell_info,
703  const std::function<std::uint8_t(std::size_t)>& get_perm)
704 {
705  const int tdim = mesh.topology().dim();
706 
707  // Prepare cell geometry
708  const graph::AdjacencyList<std::int32_t>& x_dofmap = mesh.geometry().dofmap();
709 
710  // FIXME: Add proper interface for num coordinate dofs
711  const std::size_t num_dofs_g = x_dofmap.num_links(0);
712  const xt::xtensor<double, 2>& x_g = mesh.geometry().x();
713 
714  // Create data structures used in assembly
715  xt::xtensor<double, 3> coordinate_dofs({2, num_dofs_g, 3});
716  std::vector<T> be;
717  std::vector<T> coeff_array(2 * offsets.back());
718  assert(offsets.back() == int(coeffs.shape[1]));
719 
720  const int bs = dofmap.bs();
721  assert(_bs < 0 or _bs == bs);
722  auto f_to_c = mesh.topology().connectivity(tdim - 1, tdim);
723  assert(f_to_c);
724  auto c_to_f = mesh.topology().connectivity(tdim, tdim - 1);
725  assert(c_to_f);
726  for (std::int32_t f : active_facets)
727  {
728  // Get attached cell indices
729  auto cells = f_to_c->links(f);
730  assert(cells.size() == 2);
731 
732  const int facets_per_cell = c_to_f->num_links(cells[0]);
733 
734  // Create attached cells
735  std::array<int, 2> local_facet;
736  for (int i = 0; i < 2; ++i)
737  {
738  auto facets = c_to_f->links(cells[i]);
739  auto it = std::find(facets.begin(), facets.end(), f);
740  assert(it != facets.end());
741  local_facet[i] = std::distance(facets.begin(), it);
742  }
743 
744  // Get cell geometry
745  auto x_dofs0 = x_dofmap.links(cells[0]);
746  for (std::size_t i = 0; i < x_dofs0.size(); ++i)
747  {
748  std::copy_n(xt::view(x_g, x_dofs0[i]).begin(), 3,
749  xt::view(coordinate_dofs, 0, i, xt::all()).begin());
750  }
751  auto x_dofs1 = x_dofmap.links(cells[1]);
752  for (std::size_t i = 0; i < x_dofs1.size(); ++i)
753  {
754  std::copy_n(xt::view(x_g, x_dofs1[i]).begin(), 3,
755  xt::view(coordinate_dofs, 1, i, xt::all()).begin());
756  }
757 
758  // Layout for the restricted coefficients is flattened
759  // w[coefficient][restriction][dof]
760  auto coeff_cell0 = coeffs.row(cells[0]);
761  auto coeff_cell1 = coeffs.row(cells[1]);
762 
763  // Loop over coefficients
764  for (std::size_t i = 0; i < offsets.size() - 1; ++i)
765  {
766  // Loop over entries for coefficient i
767  const int num_entries = offsets[i + 1] - offsets[i];
768  std::copy_n(coeff_cell0.data() + offsets[i], num_entries,
769  std::next(coeff_array.begin(), 2 * offsets[i]));
770  std::copy_n(coeff_cell1.data() + offsets[i], num_entries,
771  std::next(coeff_array.begin(), offsets[i + 1] + offsets[i]));
772  }
773 
774  // Get dofmaps for cells
775  xtl::span<const std::int32_t> dmap0 = dofmap.cell_dofs(cells[0]);
776  xtl::span<const std::int32_t> dmap1 = dofmap.cell_dofs(cells[1]);
777 
778  // Tabulate element vector
779  be.resize(bs * (dmap0.size() + dmap1.size()));
780  std::fill(be.begin(), be.end(), 0);
781  const std::array perm{
782  get_perm(cells[0] * facets_per_cell + local_facet[0]),
783  get_perm(cells[1] * facets_per_cell + local_facet[1])};
784  fn(be.data(), coeff_array.data(), constants.data(), coordinate_dofs.data(),
785  local_facet.data(), perm.data());
786 
787  dof_transform(be, cell_info, cells[0], 1);
788 
789  // Add element vector to global vector
790  if constexpr (_bs > 0)
791  {
792  for (std::size_t i = 0; i < dmap0.size(); ++i)
793  for (int k = 0; k < _bs; ++k)
794  b[_bs * dmap0[i] + k] += be[_bs * i + k];
795  for (std::size_t i = 0; i < dmap1.size(); ++i)
796  for (int k = 0; k < _bs; ++k)
797  b[_bs * dmap1[i] + k] += be[_bs * (i + dmap0.size()) + k];
798  }
799  else
800  {
801  for (std::size_t i = 0; i < dmap0.size(); ++i)
802  for (int k = 0; k < bs; ++k)
803  b[bs * dmap0[i] + k] += be[bs * i + k];
804  for (std::size_t i = 0; i < dmap1.size(); ++i)
805  for (int k = 0; k < bs; ++k)
806  b[bs * dmap1[i] + k] += be[bs * (i + dmap0.size()) + k];
807  }
808  }
809 }
810 
825 template <typename T>
826 void lift_bc(xtl::span<T> b, const Form<T>& a,
827  const xtl::span<const T>& constants, const array2d<T>& coeffs,
828  const xtl::span<const T>& bc_values1,
829  const std::vector<bool>& bc_markers1, const xtl::span<const T>& x0,
830  double scale)
831 {
832  std::shared_ptr<const mesh::Mesh> mesh = a.mesh();
833  assert(mesh);
834  const int tdim = mesh->topology().dim();
835 
836  // Get dofmap for columns and rows of a
837  assert(a.function_spaces().at(0));
838  assert(a.function_spaces().at(1));
839  const graph::AdjacencyList<std::int32_t>& dofmap0
840  = a.function_spaces()[0]->dofmap()->list();
841  const int bs0 = a.function_spaces()[0]->dofmap()->bs();
842  std::shared_ptr<const fem::FiniteElement> element0
843  = a.function_spaces()[0]->element();
844  const graph::AdjacencyList<std::int32_t>& dofmap1
845  = a.function_spaces()[1]->dofmap()->list();
846  const int bs1 = a.function_spaces()[1]->dofmap()->bs();
847  std::shared_ptr<const fem::FiniteElement> element1
848  = a.function_spaces()[1]->element();
849 
850  const bool needs_transformation_data
851  = element0->needs_dof_transformations()
852  or element1->needs_dof_transformations()
853  or a.needs_facet_permutations();
854 
855  xtl::span<const std::uint32_t> cell_info;
856  if (needs_transformation_data)
857  {
858  mesh->topology_mutable().create_entity_permutations();
859  cell_info = xtl::span(mesh->topology().get_cell_permutation_info());
860  }
861 
862  const std::function<void(const xtl::span<T>&,
863  const xtl::span<const std::uint32_t>&, std::int32_t,
864  int)>
865  dof_transform = element0->get_dof_transformation_function<T>();
866  const std::function<void(const xtl::span<T>&,
867  const xtl::span<const std::uint32_t>&, std::int32_t,
868  int)>
869  dof_transform_to_transpose
870  = element1->get_dof_transformation_to_transpose_function<T>();
871 
872  for (int i : a.integral_ids(IntegralType::cell))
873  {
874  const auto& kernel = a.kernel(IntegralType::cell, i);
875  const std::vector<std::int32_t>& active_cells
876  = a.domains(IntegralType::cell, i);
877  if (bs0 == 1 and bs1 == 1)
878  {
879  _lift_bc_cells<T, 1, 1>(
880  b, mesh->geometry(), kernel, active_cells, dof_transform, dofmap0,
881  bs0, dof_transform_to_transpose, dofmap1, bs1, constants, coeffs,
882  cell_info, bc_values1, bc_markers1, x0, scale);
883  }
884  else if (bs0 == 3 and bs1 == 3)
885  {
886  _lift_bc_cells<T, 3, 3>(
887  b, mesh->geometry(), kernel, active_cells, dof_transform, dofmap0,
888  bs0, dof_transform_to_transpose, dofmap1, bs1, constants, coeffs,
889  cell_info, bc_values1, bc_markers1, x0, scale);
890  }
891  else
892  {
893  _lift_bc_cells(b, mesh->geometry(), kernel, active_cells, dof_transform,
894  dofmap0, bs0, dof_transform_to_transpose, dofmap1, bs1,
895  constants, coeffs, cell_info, bc_values1, bc_markers1, x0,
896  scale);
897  }
898  }
899 
900  if (a.num_integrals(IntegralType::exterior_facet) > 0
901  or a.num_integrals(IntegralType::interior_facet) > 0)
902  {
903  // FIXME: cleanup these calls? Some of the happen internally again.
904  mesh->topology_mutable().create_entities(tdim - 1);
905  mesh->topology_mutable().create_connectivity(tdim - 1, tdim);
906 
907  std::function<std::uint8_t(std::size_t)> get_perm;
908  if (a.needs_facet_permutations())
909  {
910  mesh->topology_mutable().create_entity_permutations();
911  const std::vector<std::uint8_t>& perms
912  = mesh->topology().get_facet_permutations();
913  get_perm = [&perms](std::size_t i) { return perms[i]; };
914  }
915  else
916  get_perm = [](std::size_t) { return 0; };
917 
918  for (int i : a.integral_ids(IntegralType::exterior_facet))
919  {
920  const auto& kernel = a.kernel(IntegralType::exterior_facet, i);
921  const std::vector<std::int32_t>& active_facets
922  = a.domains(IntegralType::exterior_facet, i);
923  _lift_bc_exterior_facets(b, *mesh, kernel, active_facets, dof_transform,
924  dofmap0, bs0, dof_transform_to_transpose,
925  dofmap1, bs1, constants, coeffs, cell_info,
926  get_perm, bc_values1, bc_markers1, x0, scale);
927  }
928 
929  const std::vector<int> c_offsets = a.coefficient_offsets();
930  for (int i : a.integral_ids(IntegralType::interior_facet))
931  {
932  const auto& kernel = a.kernel(IntegralType::interior_facet, i);
933  const std::vector<std::int32_t>& active_facets
934  = a.domains(IntegralType::interior_facet, i);
935  _lift_bc_interior_facets(
936  b, *mesh, kernel, active_facets, dof_transform, dofmap0, bs0,
937  dof_transform_to_transpose, dofmap1, bs1, constants, coeffs,
938  c_offsets, cell_info, get_perm, bc_values1, bc_markers1, x0, scale);
939  }
940  }
941 }
942 
962 template <typename T>
963 void apply_lifting(
964  xtl::span<T> b, const std::vector<std::shared_ptr<const Form<T>>> a,
965  const std::vector<xtl::span<const T>>& constants,
966  const std::vector<const array2d<T>*>& coeffs,
967  const std::vector<std::vector<std::shared_ptr<const DirichletBC<T>>>>& bcs1,
968  const std::vector<xtl::span<const T>>& x0, double scale)
969 {
970  // FIXME: make changes to reactivate this check
971  if (!x0.empty() and x0.size() != a.size())
972  {
973  throw std::runtime_error(
974  "Mismatch in size between x0 and bilinear form in assembler.");
975  }
976 
977  if (a.size() != bcs1.size())
978  {
979  throw std::runtime_error(
980  "Mismatch in size between a and bcs in assembler.");
981  }
982 
983  for (std::size_t j = 0; j < a.size(); ++j)
984  {
985  std::vector<bool> bc_markers1;
986  std::vector<T> bc_values1;
987  if (a[j] and !bcs1[j].empty())
988  {
989  assert(a[j]->function_spaces().at(0));
990 
991  auto V1 = a[j]->function_spaces()[1];
992  assert(V1);
993  auto map1 = V1->dofmap()->index_map;
994  const int bs1 = V1->dofmap()->index_map_bs();
995  assert(map1);
996  const int crange = bs1 * (map1->size_local() + map1->num_ghosts());
997  bc_markers1.assign(crange, false);
998  bc_values1.assign(crange, 0.0);
999  for (const std::shared_ptr<const DirichletBC<T>>& bc : bcs1[j])
1000  {
1001  bc->mark_dofs(bc_markers1);
1002  bc->dof_values(bc_values1);
1003  }
1004 
1005  assert(coeffs[j]);
1006  if (!x0.empty())
1007  {
1008  lift_bc<T>(b, *a[j], constants[j], *coeffs[j], bc_values1, bc_markers1,
1009  x0[j], scale);
1010  }
1011  else
1012  {
1013  lift_bc<T>(b, *a[j], constants[j], *coeffs[j], bc_values1, bc_markers1,
1014  xtl::span<const T>(), scale);
1015  }
1016  }
1017  }
1018 }
1019 
1026 template <typename T>
1027 void assemble_vector(xtl::span<T> b, const Form<T>& L,
1028  const xtl::span<const T>& constants,
1029  const array2d<T>& coeffs)
1030 {
1031  std::shared_ptr<const mesh::Mesh> mesh = L.mesh();
1032  assert(mesh);
1033  const int tdim = mesh->topology().dim();
1034 
1035  // Get dofmap data
1036  assert(L.function_spaces().at(0));
1037  std::shared_ptr<const fem::FiniteElement> element
1038  = L.function_spaces().at(0)->element();
1039  std::shared_ptr<const fem::DofMap> dofmap
1040  = L.function_spaces().at(0)->dofmap();
1041  assert(dofmap);
1042  const graph::AdjacencyList<std::int32_t>& dofs = dofmap->list();
1043  const int bs = dofmap->bs();
1044 
1045  const std::function<void(const xtl::span<T>&,
1046  const xtl::span<const std::uint32_t>&, std::int32_t,
1047  int)>
1048  dof_transform = element->get_dof_transformation_function<T>();
1049 
1050  const bool needs_transformation_data
1051  = element->needs_dof_transformations() or L.needs_facet_permutations();
1052  xtl::span<const std::uint32_t> cell_info;
1053  if (needs_transformation_data)
1054  {
1055  mesh->topology_mutable().create_entity_permutations();
1056  cell_info = xtl::span(mesh->topology().get_cell_permutation_info());
1057  }
1058 
1059  for (int i : L.integral_ids(IntegralType::cell))
1060  {
1061  const auto& fn = L.kernel(IntegralType::cell, i);
1062  const std::vector<std::int32_t>& active_cells
1063  = L.domains(IntegralType::cell, i);
1064  if (bs == 1)
1065  {
1066  impl::assemble_cells<T, 1>(dof_transform, b, mesh->geometry(),
1067  active_cells, dofs, bs, fn, constants, coeffs,
1068  cell_info);
1069  }
1070  else if (bs == 3)
1071  {
1072  impl::assemble_cells<T, 3>(dof_transform, b, mesh->geometry(),
1073  active_cells, dofs, bs, fn, constants, coeffs,
1074  cell_info);
1075  }
1076  else
1077  {
1078  impl::assemble_cells(dof_transform, b, mesh->geometry(), active_cells,
1079  dofs, bs, fn, constants, coeffs, cell_info);
1080  }
1081  }
1082 
1083  if (L.num_integrals(IntegralType::exterior_facet) > 0
1084  or L.num_integrals(IntegralType::interior_facet) > 0)
1085  {
1086  // FIXME: cleanup these calls? Some of the happen internally again.
1087  mesh->topology_mutable().create_entities(tdim - 1);
1088  mesh->topology_mutable().create_connectivity(tdim - 1, tdim);
1089  std::function<std::uint8_t(std::size_t)> get_perm;
1090  if (L.needs_facet_permutations())
1091  {
1092  mesh->topology_mutable().create_entity_permutations();
1093  const std::vector<std::uint8_t>& perms
1094  = mesh->topology().get_facet_permutations();
1095  get_perm = [&perms](std::size_t i) { return perms[i]; };
1096  }
1097  else
1098  get_perm = [](std::size_t) { return 0; };
1099 
1100  for (int i : L.integral_ids(IntegralType::exterior_facet))
1101  {
1102  const auto& fn = L.kernel(IntegralType::exterior_facet, i);
1103  const std::vector<std::int32_t>& active_facets
1104  = L.domains(IntegralType::exterior_facet, i);
1105  if (bs == 1)
1106  {
1107  impl::assemble_exterior_facets<T, 1>(
1108  dof_transform, b, *mesh, active_facets, dofs, bs, fn, constants,
1109  coeffs, cell_info, get_perm);
1110  }
1111  else if (bs == 3)
1112  {
1113  impl::assemble_exterior_facets<T, 3>(
1114  dof_transform, b, *mesh, active_facets, dofs, bs, fn, constants,
1115  coeffs, cell_info, get_perm);
1116  }
1117  else
1118  {
1119  impl::assemble_exterior_facets(dof_transform, b, *mesh, active_facets,
1120  dofs, bs, fn, constants, coeffs,
1121  cell_info, get_perm);
1122  }
1123  }
1124 
1125  const std::vector<int> c_offsets = L.coefficient_offsets();
1126  for (int i : L.integral_ids(IntegralType::interior_facet))
1127  {
1128  const auto& fn = L.kernel(IntegralType::interior_facet, i);
1129  const std::vector<std::int32_t>& active_facets
1130  = L.domains(IntegralType::interior_facet, i);
1131  if (bs == 1)
1132  {
1133  impl::assemble_interior_facets<T, 1>(
1134  dof_transform, b, *mesh, active_facets, *dofmap, fn, constants,
1135  coeffs, c_offsets, cell_info, get_perm);
1136  }
1137  else if (bs == 3)
1138  {
1139  impl::assemble_interior_facets<T, 3>(
1140  dof_transform, b, *mesh, active_facets, *dofmap, fn, constants,
1141  coeffs, c_offsets, cell_info, get_perm);
1142  }
1143  else
1144  {
1145  impl::assemble_interior_facets(dof_transform, b, *mesh, active_facets,
1146  *dofmap, fn, constants, coeffs,
1147  c_offsets, cell_info, get_perm);
1148  }
1149  }
1150  }
1151 }
1152 } // namespace dolfinx::fem::impl
void cells(la::SparsityPattern &pattern, const mesh::Topology &topology, const std::array< const std::reference_wrapper< const fem::DofMap >, 2 > &dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition: sparsitybuild.cpp:18
void assemble_vector(xtl::span< T > b, const Form< T > &L, const xtl::span< const T > &constants, const array2d< T > &coeffs)
Assemble linear form into a vector, The caller supplies the form constants and coefficients for this ...
Definition: assembler.h:66
void apply_lifting(xtl::span< T > b, const std::vector< std::shared_ptr< const Form< T >>> &a, const std::vector< xtl::span< const T >> &constants, const std::vector< const array2d< T > * > &coeffs, const std::vector< std::vector< std::shared_ptr< const DirichletBC< T >>>> &bcs1, const std::vector< xtl::span< const T >> &x0, double scale)
Modify b such that:
Definition: assembler.h:104