DOLFINx 0.8.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
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 "FunctionSpace.h"
12#include "traits.h"
13#include "utils.h"
14#include <algorithm>
15#include <dolfinx/la/utils.h>
16#include <dolfinx/mesh/Geometry.h>
17#include <dolfinx/mesh/Mesh.h>
18#include <dolfinx/mesh/Topology.h>
19#include <functional>
20#include <iterator>
21#include <span>
22#include <tuple>
23#include <vector>
24
25namespace dolfinx::fem::impl
26{
27
28using mdspan2_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
29 const std::int32_t,
30 MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<std::size_t, 2>>;
31
60template <dolfinx::scalar T>
61void assemble_cells(
62 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
63 std::span<const scalar_value_type_t<T>> x,
64 std::span<const std::int32_t> cells,
65 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
67 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
68 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
69 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
70 std::span<const T> coeffs, int cstride, std::span<const T> constants,
71 std::span<const std::uint32_t> cell_info0,
72 std::span<const std::uint32_t> cell_info1)
73{
74 if (cells.empty())
75 return;
76
77 const auto [dmap0, bs0, cells0] = dofmap0;
78 const auto [dmap1, bs1, cells1] = dofmap1;
79
80 // Iterate over active cells
81 const int num_dofs0 = dmap0.extent(1);
82 const int num_dofs1 = dmap1.extent(1);
83 const int ndim0 = bs0 * num_dofs0;
84 const int ndim1 = bs1 * num_dofs1;
85 std::vector<T> Ae(ndim0 * ndim1);
86 std::span<T> _Ae(Ae);
87 std::vector<scalar_value_type_t<T>> coordinate_dofs(3 * x_dofmap.extent(1));
88
89 // Iterate over active cells
90 assert(cells0.size() == cells.size());
91 assert(cells1.size() == cells.size());
92 for (std::size_t index = 0; index < cells.size(); ++index)
93 {
94 // Cell index in integration domain mesh (c), test function mesh
95 // (c0) and trial function mesh (c1)
96 std::int32_t c = cells[index];
97 std::int32_t c0 = cells0[index];
98 std::int32_t c1 = cells1[index];
99
100 // Get cell coordinates/geometry
101 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
102 x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
103 for (std::size_t i = 0; i < x_dofs.size(); ++i)
104 {
105 std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
106 std::next(coordinate_dofs.begin(), 3 * i));
107 }
108
109 // Tabulate tensor
110 std::fill(Ae.begin(), Ae.end(), 0);
111 kernel(Ae.data(), coeffs.data() + index * cstride, constants.data(),
112 coordinate_dofs.data(), nullptr, nullptr);
113
114 // Compute A = P_0 \tilde{A} P_1^T (dof transformation)
115 P0(_Ae, cell_info0, c0, ndim1); // B = P0 \tilde{A}
116 P1T(_Ae, cell_info1, c1, ndim0); // A = B P1_T
117
118 // Zero rows/columns for essential bcs
119 auto dofs0 = std::span(dmap0.data_handle() + c0 * num_dofs0, num_dofs0);
120 auto dofs1 = std::span(dmap1.data_handle() + c1 * num_dofs1, num_dofs1);
121
122 if (!bc0.empty())
123 {
124 for (int i = 0; i < num_dofs0; ++i)
125 {
126 for (int k = 0; k < bs0; ++k)
127 {
128 if (bc0[bs0 * dofs0[i] + k])
129 {
130 // Zero row bs0 * i + k
131 const int row = bs0 * i + k;
132 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
133 }
134 }
135 }
136 }
137
138 if (!bc1.empty())
139 {
140 for (int j = 0; j < num_dofs1; ++j)
141 {
142 for (int k = 0; k < bs1; ++k)
143 {
144 if (bc1[bs1 * dofs1[j] + k])
145 {
146 // Zero column bs1 * j + k
147 const int col = bs1 * j + k;
148 for (int row = 0; row < ndim0; ++row)
149 Ae[row * ndim1 + col] = 0;
150 }
151 }
152 }
153 }
154
155 mat_set(dofs0, dofs1, Ae);
156 }
157}
158
188template <dolfinx::scalar T>
189void assemble_exterior_facets(
190 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
191 std::span<const scalar_value_type_t<T>> x,
192 std::span<const std::int32_t> facets,
193 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
195 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
196 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
197 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
198 std::span<const T> coeffs, int cstride, std::span<const T> constants,
199 std::span<const std::uint32_t> cell_info0,
200 std::span<const std::uint32_t> cell_info1)
201{
202 if (facets.empty())
203 return;
204
205 const auto [dmap0, bs0, facets0] = dofmap0;
206 const auto [dmap1, bs1, facets1] = dofmap1;
207
208 // Data structures used in assembly
209 std::vector<scalar_value_type_t<T>> coordinate_dofs(3 * x_dofmap.extent(1));
210 const int num_dofs0 = dmap0.extent(1);
211 const int num_dofs1 = dmap1.extent(1);
212 const int ndim0 = bs0 * num_dofs0;
213 const int ndim1 = bs1 * num_dofs1;
214 std::vector<T> Ae(ndim0 * ndim1);
215 std::span<T> _Ae(Ae);
216 assert(facets.size() % 2 == 0);
217 assert(facets0.size() == facets.size());
218 assert(facets1.size() == facets.size());
219 for (std::size_t index = 0; index < facets.size(); index += 2)
220 {
221 // Cell in the integration domain, local facet index relative to the
222 // integration domain cell, and cells in the test and trial function
223 // meshes
224 std::int32_t cell = facets[index];
225 std::int32_t local_facet = facets[index + 1];
226 std::int32_t cell0 = facets0[index];
227 std::int32_t cell1 = facets1[index];
228
229 // Get cell coordinates/geometry
230 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
231 x_dofmap, cell, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
232 for (std::size_t i = 0; i < x_dofs.size(); ++i)
233 {
234 std::copy_n(std::next(x.begin(), 3 * x_dofs[i]), 3,
235 std::next(coordinate_dofs.begin(), 3 * i));
236 }
237
238 // Tabulate tensor
239 std::fill(Ae.begin(), Ae.end(), 0);
240 kernel(Ae.data(), coeffs.data() + index / 2 * cstride, constants.data(),
241 coordinate_dofs.data(), &local_facet, nullptr);
242
243 P0(_Ae, cell_info0, cell0, ndim1);
244 P1T(_Ae, cell_info1, cell1, ndim0);
245
246 // Zero rows/columns for essential bcs
247 auto dofs0 = std::span(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
248 auto dofs1 = std::span(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
249 if (!bc0.empty())
250 {
251 for (int i = 0; i < num_dofs0; ++i)
252 {
253 for (int k = 0; k < bs0; ++k)
254 {
255 if (bc0[bs0 * dofs0[i] + k])
256 {
257 // Zero row bs0 * i + k
258 const int row = bs0 * i + k;
259 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
260 }
261 }
262 }
263 }
264 if (!bc1.empty())
265 {
266 for (int j = 0; j < num_dofs1; ++j)
267 {
268 for (int k = 0; k < bs1; ++k)
269 {
270 if (bc1[bs1 * dofs1[j] + k])
271 {
272 // Zero column bs1 * j + k
273 const int col = bs1 * j + k;
274 for (int row = 0; row < ndim0; ++row)
275 Ae[row * ndim1 + col] = 0;
276 }
277 }
278 }
279 }
280
281 mat_set(dofs0, dofs1, Ae);
282 }
283}
284
317template <dolfinx::scalar T>
318void assemble_interior_facets(
319 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
320 std::span<const scalar_value_type_t<T>> x, int num_cell_facets,
321 std::span<const std::int32_t> facets,
322 std::tuple<const DofMap&, int, std::span<const std::int32_t>> dofmap0,
324 std::tuple<const DofMap&, int, std::span<const std::int32_t>> dofmap1,
325 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
326 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
327 std::span<const T> coeffs, int cstride, std::span<const int> offsets,
328 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
329 std::span<const std::uint32_t> cell_info1,
330 const std::function<std::uint8_t(std::size_t)>& get_perm)
331{
332 if (facets.empty())
333 return;
334
335 const auto [dmap0, bs0, facets0] = dofmap0;
336 const auto [dmap1, bs1, facets1] = dofmap1;
337
338 // Data structures used in assembly
339 using X = scalar_value_type_t<T>;
340 std::vector<X> coordinate_dofs(2 * x_dofmap.extent(1) * 3);
341 std::span<X> cdofs0(coordinate_dofs.data(), x_dofmap.extent(1) * 3);
342 std::span<X> cdofs1(coordinate_dofs.data() + x_dofmap.extent(1) * 3,
343 x_dofmap.extent(1) * 3);
344
345 std::vector<T> Ae, be;
346 std::vector<T> coeff_array(2 * offsets.back());
347 assert(offsets.back() == cstride);
348
349 // Temporaries for joint dofmaps
350 std::vector<std::int32_t> dmapjoint0, dmapjoint1;
351 assert(facets.size() % 4 == 0);
352 assert(facets0.size() == facets.size());
353 assert(facets1.size() == facets.size());
354 for (std::size_t index = 0; index < facets.size(); index += 4)
355 {
356 // Cells in integration domain, test function domain and trial
357 // function domain
358 std::array cells{facets[index], facets[index + 2]};
359 std::array cells0{facets0[index], facets0[index + 2]};
360 std::array cells1{facets1[index], facets1[index + 2]};
361
362 // Local facets indices
363 std::array local_facet{facets[index + 1], facets[index + 3]};
364
365 // Get cell geometry
366 auto x_dofs0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
367 x_dofmap, cells[0], MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
368 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
369 {
370 std::copy_n(std::next(x.begin(), 3 * x_dofs0[i]), 3,
371 std::next(cdofs0.begin(), 3 * i));
372 }
373 auto x_dofs1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
374 x_dofmap, cells[1], MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
375 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
376 {
377 std::copy_n(std::next(x.begin(), 3 * x_dofs1[i]), 3,
378 std::next(cdofs1.begin(), 3 * i));
379 }
380
381 // Get dof maps for cells and pack
382 std::span<const std::int32_t> dmap0_cell0 = dmap0.cell_dofs(cells0[0]);
383 std::span<const std::int32_t> dmap0_cell1 = dmap0.cell_dofs(cells0[1]);
384 dmapjoint0.resize(dmap0_cell0.size() + dmap0_cell1.size());
385 std::copy(dmap0_cell0.begin(), dmap0_cell0.end(), dmapjoint0.begin());
386 std::copy(dmap0_cell1.begin(), dmap0_cell1.end(),
387 std::next(dmapjoint0.begin(), dmap0_cell0.size()));
388
389 std::span<const std::int32_t> dmap1_cell0 = dmap1.cell_dofs(cells1[0]);
390 std::span<const std::int32_t> dmap1_cell1 = dmap1.cell_dofs(cells1[1]);
391 dmapjoint1.resize(dmap1_cell0.size() + dmap1_cell1.size());
392 std::copy(dmap1_cell0.begin(), dmap1_cell0.end(), dmapjoint1.begin());
393 std::copy(dmap1_cell1.begin(), dmap1_cell1.end(),
394 std::next(dmapjoint1.begin(), dmap1_cell0.size()));
395
396 const int num_rows = bs0 * dmapjoint0.size();
397 const int num_cols = bs1 * dmapjoint1.size();
398
399 // Tabulate tensor
400 Ae.resize(num_rows * num_cols);
401 std::fill(Ae.begin(), Ae.end(), 0);
402
403 const std::array perm{
404 get_perm(cells[0] * num_cell_facets + local_facet[0]),
405 get_perm(cells[1] * num_cell_facets + local_facet[1])};
406 kernel(Ae.data(), coeffs.data() + index / 2 * cstride, constants.data(),
407 coordinate_dofs.data(), local_facet.data(), perm.data());
408
409 // Local element layout is a 2x2 block matrix with structure
410 //
411 // cell0cell0 | cell0cell1
412 // cell1cell0 | cell1cell1
413 //
414 // where each block is element tensor of size (dmap0, dmap1).
415
416 std::span<T> _Ae(Ae);
417 std::span<T> sub_Ae0 = _Ae.subspan(bs0 * dmap0_cell0.size() * num_cols,
418 bs0 * dmap0_cell1.size() * num_cols);
419
420 P0(_Ae, cell_info0, cells0[0], num_cols);
421 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
422 P1T(_Ae, cell_info1, cells1[0], num_rows);
423
424 for (int row = 0; row < num_rows; ++row)
425 {
426 // DOFs for dmap1 and cell1 are not stored contiguously in
427 // the block matrix, so each row needs a separate span access
428 std::span<T> sub_Ae1 = _Ae.subspan(
429 row * num_cols + bs1 * dmap1_cell0.size(), bs1 * dmap1_cell1.size());
430 P1T(sub_Ae1, cell_info1, cells1[1], 1);
431 }
432
433 // Zero rows/columns for essential bcs
434 if (!bc0.empty())
435 {
436 for (std::size_t i = 0; i < dmapjoint0.size(); ++i)
437 {
438 for (int k = 0; k < bs0; ++k)
439 {
440 if (bc0[bs0 * dmapjoint0[i] + k])
441 {
442 // Zero row bs0 * i + k
443 std::fill_n(std::next(Ae.begin(), num_cols * (bs0 * i + k)),
444 num_cols, 0);
445 }
446 }
447 }
448 }
449 if (!bc1.empty())
450 {
451 for (std::size_t j = 0; j < dmapjoint1.size(); ++j)
452 {
453 for (int k = 0; k < bs1; ++k)
454 {
455 if (bc1[bs1 * dmapjoint1[j] + k])
456 {
457 // Zero column bs1 * j + k
458 for (int m = 0; m < num_rows; ++m)
459 Ae[m * num_cols + bs1 * j + k] = 0;
460 }
461 }
462 }
463 }
464
465 mat_set(dmapjoint0, dmapjoint1, Ae);
466 }
467}
468
474template <dolfinx::scalar T, std::floating_point U>
475void assemble_matrix(
476 la::MatSet<T> auto mat_set, const Form<T, U>& a, mdspan2_t x_dofmap,
477 std::span<const scalar_value_type_t<T>> x, std::span<const T> constants,
478 const std::map<std::pair<IntegralType, int>,
479 std::pair<std::span<const T>, int>>& coefficients,
480 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1)
481{
482 // Integration domain mesh
483 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
484 assert(mesh);
485 // Test function mesh
486 auto mesh0 = a.function_spaces().at(0)->mesh();
487 assert(mesh0);
488 // Trial function mesh
489 auto mesh1 = a.function_spaces().at(1)->mesh();
490 assert(mesh1);
491
492 // Get dofmap data
493 std::shared_ptr<const fem::DofMap> dofmap0
494 = a.function_spaces().at(0)->dofmap();
495 std::shared_ptr<const fem::DofMap> dofmap1
496 = a.function_spaces().at(1)->dofmap();
497 assert(dofmap0);
498 assert(dofmap1);
499 auto dofs0 = dofmap0->map();
500 const int bs0 = dofmap0->bs();
501 auto dofs1 = dofmap1->map();
502 const int bs1 = dofmap1->bs();
503
504 auto element0 = a.function_spaces().at(0)->element();
505 assert(element0);
506 auto element1 = a.function_spaces().at(1)->element();
507 assert(element1);
509 = element0->template dof_transformation_fn<T>(doftransform::standard);
511 = element1->template dof_transformation_right_fn<T>(
513
514 std::span<const std::uint32_t> cell_info0;
515 std::span<const std::uint32_t> cell_info1;
516 if (element0->needs_dof_transformations()
517 or element1->needs_dof_transformations() or a.needs_facet_permutations())
518 {
519 mesh0->topology_mutable()->create_entity_permutations();
520 mesh1->topology_mutable()->create_entity_permutations();
521 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
522 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
523 }
524
525 for (int i : a.integral_ids(IntegralType::cell))
526 {
527 auto fn = a.kernel(IntegralType::cell, i);
528 assert(fn);
529 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
530 impl::assemble_cells(
531 mat_set, x_dofmap, x, a.domain(IntegralType::cell, i),
532 {dofs0, bs0, a.domain(IntegralType::cell, i, *mesh0)}, P0,
533 {dofs1, bs1, a.domain(IntegralType::cell, i, *mesh1)}, P1T, bc0, bc1,
534 fn, coeffs, cstride, constants, cell_info0, cell_info1);
535 }
536
537 for (int i : a.integral_ids(IntegralType::exterior_facet))
538 {
539 auto fn = a.kernel(IntegralType::exterior_facet, i);
540 assert(fn);
541 auto& [coeffs, cstride]
542 = coefficients.at({IntegralType::exterior_facet, i});
543 impl::assemble_exterior_facets(
544 mat_set, x_dofmap, x, a.domain(IntegralType::exterior_facet, i),
545 {dofs0, bs0, a.domain(IntegralType::exterior_facet, i, *mesh0)}, P0,
546 {dofs1, bs1, a.domain(IntegralType::exterior_facet, i, *mesh1)}, P1T,
547 bc0, bc1, fn, coeffs, cstride, constants, cell_info0, cell_info1);
548 }
549
550 if (a.num_integrals(IntegralType::interior_facet) > 0)
551 {
552 std::function<std::uint8_t(std::size_t)> get_perm;
553 if (a.needs_facet_permutations())
554 {
555 mesh->topology_mutable()->create_entity_permutations();
556 const std::vector<std::uint8_t>& perms
557 = mesh->topology()->get_facet_permutations();
558 get_perm = [&perms](std::size_t i) { return perms[i]; };
559 }
560 else
561 get_perm = [](std::size_t) { return 0; };
562
563 mesh::CellType cell_type = mesh->topology()->cell_type();
564 int num_cell_facets
565 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
566 const std::vector<int> c_offsets = a.coefficient_offsets();
567 for (int i : a.integral_ids(IntegralType::interior_facet))
568 {
569 auto fn = a.kernel(IntegralType::interior_facet, i);
570 assert(fn);
571 auto& [coeffs, cstride]
572 = coefficients.at({IntegralType::interior_facet, i});
573 impl::assemble_interior_facets(
574 mat_set, x_dofmap, x, num_cell_facets,
575 a.domain(IntegralType::interior_facet, i),
576 {*dofmap0, bs0, a.domain(IntegralType::interior_facet, i, *mesh0)},
577 P0,
578 {*dofmap1, bs1, a.domain(IntegralType::interior_facet, i, *mesh1)},
579 P1T, bc0, bc1, fn, coeffs, cstride, c_offsets, constants, cell_info0,
580 cell_info1, get_perm);
581 }
582 }
583}
584
585} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Degree-of-freedom map.
Definition DofMap.h:76
A representation of finite element variational forms.
Definition Form.h:120
DOF transform kernel concept.
Definition traits.h:20
Finite element cell kernel concept.
Definition traits.h:28
Matrix accumulate/set concept for functions that can be used in assemblers to accumulate or set value...
Definition utils.h:28
Functions supporting finite element method operations.
@ interior_facet
Interior facet.
@ exterior_facet
Exterior facet.
int cell_num_entities(CellType type, int dim)
Definition cell_types.cpp:139
CellType
Cell type identifier.
Definition cell_types.h:22