DOLFINx 0.11.0.0
DOLFINx C++
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{
28using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
29
65template <dolfinx::scalar T, bool LiftingMode = false>
66void assemble_cells_matrix(
67 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
68 md::mdspan<const scalar_value_t<T>,
69 md::extents<std::size_t, md::dynamic_extent, 3>>
70 x,
71 std::span<const std::int32_t> cells,
72 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
73 fem::DofTransformKernel<T> auto P0,
74 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
75 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
76 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
77 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
78 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
79 std::span<const std::uint32_t> cell_info1)
80{
81 if (cells.empty())
82 return;
83
84 const auto [dmap0, bs0, cells0] = dofmap0;
85 const auto [dmap1, bs1, cells1] = dofmap1;
86
87 // Iterate over active cells
88 const int num_dofs0 = dmap0.extent(1);
89 const int num_dofs1 = dmap1.extent(1);
90 const int ndim0 = bs0 * num_dofs0;
91 const int ndim1 = bs1 * num_dofs1;
92 std::vector<T> Ae(ndim0 * ndim1);
93 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
94
95 // Iterate over active cells
96 assert(cells0.size() == cells.size());
97 assert(cells1.size() == cells.size());
98 for (std::size_t c = 0; c < cells.size(); ++c)
99 {
100 // Cell index in integration domain mesh (c), test function mesh
101 // (c0) and trial function mesh (c1)
102 std::int32_t cell = cells[c];
103 std::int32_t cell0 = cells0[c];
104 std::int32_t cell1 = cells1[c];
105
106 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
107 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
108
109 // In "LiftingMode" only execute kernel if there are BCs on column space
110 if constexpr (LiftingMode)
111 {
112 auto has_bc = [&]()
113 {
114 for (std::int32_t dof : dofs1)
115 {
116 for (int k = 0; k < bs1; ++k)
117 {
118 if (bc1[bs1 * dof + k])
119 return true;
120 }
121 }
122 return false;
123 };
124
125 if (!has_bc())
126 continue;
127 }
128
129 // Get cell coordinates/geometry
130 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
131 for (std::size_t i = 0; i < x_dofs.size(); ++i)
132 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
133
134 // Tabulate tensor
135 std::ranges::fill(Ae, 0);
136 kernel(Ae.data(), &coeffs(c, 0), constants.data(), cdofs.data(), nullptr,
137 nullptr, nullptr);
138
139 // Compute A = P_0 \tilde{A} P_1^T (dof transformation)
140 P0(Ae, cell_info0, cell0, ndim1); // B = P0 \tilde{A}
141 P1T(Ae, cell_info1, cell1, ndim0); // A = B P1_T
142
143 // In lifting mode only BC dofs are assembled, while in standard mode these
144 // row/column dofs are zeroed.
145 if constexpr (!LiftingMode)
146 {
147 // Zero rows and columns for BCs
148 if (!bc0.empty())
149 {
150 for (int i = 0; i < num_dofs0; ++i)
151 {
152 for (int k = 0; k < bs0; ++k)
153 {
154 if (bc0[bs0 * dofs0[i] + k])
155 {
156 // Zero row bs0 * i + k
157 const int row = bs0 * i + k;
158 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
159 }
160 }
161 }
162 }
163 if (!bc1.empty())
164 {
165 for (int j = 0; j < num_dofs1; ++j)
166 {
167 for (int k = 0; k < bs1; ++k)
168 {
169 if (bc1[bs1 * dofs1[j] + k])
170 {
171 // Zero col bs1 * j + k
172 const int col = bs1 * j + k;
173 for (int row = 0; row < ndim0; ++row)
174 Ae[row * ndim1 + col] = 0;
175 }
176 }
177 }
178 }
179 }
180
181 mat_set(dofs0, dofs1, Ae);
182 }
183}
184
229template <dolfinx::scalar T, bool LiftingMode = false>
230void assemble_entities(
231 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
232 md::mdspan<const scalar_value_t<T>,
233 md::extents<std::size_t, md::dynamic_extent, 3>>
234 x,
235 md::mdspan<const std::int32_t,
236 std::extents<std::size_t, md::dynamic_extent, 2>>
237 entities,
238 std::tuple<mdspan2_t, int,
239 md::mdspan<const std::int32_t,
240 std::extents<std::size_t, md::dynamic_extent, 2>>>
241 dofmap0,
242 fem::DofTransformKernel<T> auto P0,
243 std::tuple<mdspan2_t, int,
244 md::mdspan<const std::int32_t,
245 std::extents<std::size_t, md::dynamic_extent, 2>>>
246 dofmap1,
247 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
248 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
249 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
250 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
251 std::span<const std::uint32_t> cell_info1,
252 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
253{
254 if (entities.empty())
255 return;
256
257 const auto [dmap0, bs0, entities0] = dofmap0;
258 const auto [dmap1, bs1, entities1] = dofmap1;
259
260 // Data structures used in assembly
261 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
262 const int num_dofs0 = dmap0.extent(1);
263 const int num_dofs1 = dmap1.extent(1);
264 const int ndim0 = bs0 * num_dofs0;
265 const int ndim1 = bs1 * num_dofs1;
266 std::vector<T> Ae(ndim0 * ndim1);
267
268 assert(entities0.size() == entities.size());
269 assert(entities1.size() == entities.size());
270 for (std::size_t f = 0; f < entities.extent(0); ++f)
271 {
272 // Cell in the integration domain, local entity index relative to the
273 // integration domain cell, and cells in the test and trial function
274 // meshes
275 std::int32_t cell = entities(f, 0);
276 std::int32_t local_entity = entities(f, 1);
277 std::int32_t cell0 = entities0(f, 0);
278 std::int32_t cell1 = entities1(f, 0);
279
280 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
281 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
282
283 // Check for BCs on column space
284 if constexpr (LiftingMode)
285 {
286 auto has_bc = [&]()
287 {
288 for (std::int32_t dof : dofs1)
289 {
290 for (int k = 0; k < bs1; ++k)
291 {
292 if (bc1[bs1 * dof + k])
293 return true;
294 }
295 }
296 return false;
297 };
298 if (!has_bc())
299 continue;
300 }
301
302 // Get cell coordinates/geometry
303 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
304 for (std::size_t i = 0; i < x_dofs.size(); ++i)
305 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
306
307 // Permutations
308 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
309
310 // Tabulate tensor
311 std::ranges::fill(Ae, 0);
312 kernel(Ae.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
313 &local_entity, &perm, nullptr);
314 P0(Ae, cell_info0, cell0, ndim1);
315 P1T(Ae, cell_info1, cell1, ndim0);
316
317 // Don't clear rows/cols in LiftingMode
318 if constexpr (!LiftingMode)
319 {
320 // Zero rows and columns for BCs
321 if (!bc0.empty())
322 {
323 for (int i = 0; i < num_dofs0; ++i)
324 {
325 for (int k = 0; k < bs0; ++k)
326 {
327 if (bc0[bs0 * dofs0[i] + k])
328 {
329 // Zero row bs0 * i + k
330 const int row = bs0 * i + k;
331 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
332 }
333 }
334 }
335 }
336 if (!bc1.empty())
337 {
338 for (int j = 0; j < num_dofs1; ++j)
339 {
340 for (int k = 0; k < bs1; ++k)
341 {
342 if (bc1[bs1 * dofs1[j] + k])
343 {
344 // Zero column bs1 * j + k
345 const int col = bs1 * j + k;
346 for (int row = 0; row < ndim0; ++row)
347 Ae[row * ndim1 + col] = 0;
348 }
349 }
350 }
351 }
352 }
353
354 mat_set(dofs0, dofs1, Ae);
355 }
356}
357
396template <dolfinx::scalar T, bool LiftingMode = false>
397void assemble_interior_facets(
398 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
399 md::mdspan<const scalar_value_t<T>,
400 md::extents<std::size_t, md::dynamic_extent, 3>>
401 x,
402 md::mdspan<const std::int32_t,
403 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
404 facets,
405 std::tuple<const DofMap&, int,
406 md::mdspan<const std::int32_t,
407 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
408 dofmap0,
409 fem::DofTransformKernel<T> auto P0,
410 std::tuple<const DofMap&, int,
411 md::mdspan<const std::int32_t,
412 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
413 dofmap1,
414 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
415 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
416 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
417 md::dynamic_extent>>
418 coeffs,
419 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
420 std::span<const std::uint32_t> cell_info1,
421 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
422{
423 if (facets.empty())
424 return;
425
426 const auto [dmap0, bs0, facets0] = dofmap0;
427 const auto [dmap1, bs1, facets1] = dofmap1;
428
429 // Data structures used in assembly
430 using X = scalar_value_t<T>;
431 std::vector<X> cdofs(2 * x_dofmap.extent(1) * 3);
432 std::span<X> cdofs0(cdofs.data(), x_dofmap.extent(1) * 3);
433 std::span<X> cdofs1(cdofs.data() + x_dofmap.extent(1) * 3,
434 x_dofmap.extent(1) * 3);
435
436 const std::size_t dmap0_size = dmap0.map().extent(1);
437 const std::size_t dmap1_size = dmap1.map().extent(1);
438 const int num_rows = bs0 * 2 * dmap0_size;
439 const int num_cols = bs1 * 2 * dmap1_size;
440
441 // Temporaries for joint dofmaps
442 std::vector<T> Ae(num_rows * num_cols);
443 std::vector<std::int32_t> dmapjoint0(2 * dmap0_size);
444 std::vector<std::int32_t> dmapjoint1(2 * dmap1_size);
445 assert(facets0.size() == facets.size());
446 assert(facets1.size() == facets.size());
447 for (std::size_t f = 0; f < facets.extent(0); ++f)
448 {
449 // Cells in integration domain, test function domain and trial
450 // function domain
451 std::array cells{facets(f, 0, 0), facets(f, 1, 0)};
452 std::array cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
453 std::array cells1{facets1(f, 0, 0), facets1(f, 1, 0)};
454
455 // Local facets indices
456 std::array local_facet{facets(f, 0, 1), facets(f, 1, 1)};
457
458 // Get cell geometry
459 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
460 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
461 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
462 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
463 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
464 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
465
466 // Get dof maps for cells and pack
467 // When integrating over interfaces between two domains, the test function
468 // might only be defined on one side, so we check which cells exist in the
469 // test function domain
470 std::span<const std::int32_t> dmap0_cell0
471 = cells0[0] >= 0 ? dmap0.cell_dofs(cells0[0])
472 : std::span<const std::int32_t>();
473 std::span<const std::int32_t> dmap0_cell1
474 = cells0[1] >= 0 ? dmap0.cell_dofs(cells0[1])
475 : std::span<const std::int32_t>();
476
477 std::ranges::copy(dmap0_cell0, dmapjoint0.begin());
478 std::ranges::copy(dmap0_cell1, std::next(dmapjoint0.begin(), dmap0_size));
479
480 // Check which cells exist in the trial function domain
481 std::span<const std::int32_t> dmap1_cell0
482 = cells1[0] >= 0 ? dmap1.cell_dofs(cells1[0])
483 : std::span<const std::int32_t>();
484 std::span<const std::int32_t> dmap1_cell1
485 = cells1[1] >= 0 ? dmap1.cell_dofs(cells1[1])
486 : std::span<const std::int32_t>();
487
488 std::ranges::copy(dmap1_cell0, dmapjoint1.begin());
489 std::ranges::copy(dmap1_cell1, std::next(dmapjoint1.begin(), dmap1_size));
490
491 // Check for BCs on column space
492 if constexpr (LiftingMode)
493 {
494 auto has_bc = [&]()
495 {
496 for (std::int32_t dof : dmapjoint1)
497 {
498 for (int k = 0; k < bs1; ++k)
499 {
500 if (bc1[bs1 * dof + k])
501 return true;
502 }
503 }
504 return false;
505 };
506
507 if (!has_bc())
508 continue;
509 }
510
511 // Tabulate tensor
512 std::ranges::fill(Ae, 0);
513 std::array perm = perms.empty()
514 ? std::array<std::uint8_t, 2>{0, 0}
515 : std::array{perms(cells[0], local_facet[0]),
516 perms(cells[1], local_facet[1])};
517 kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
518 local_facet.data(), perm.data(), nullptr);
519
520 // Local element layout is a 2x2 block matrix with structure
521 //
522 // cell0cell0 | cell0cell1
523 // cell1cell0 | cell1cell1
524 //
525 // where each block is element tensor of size (dmap0, dmap1).
526
527 // Only apply transformation when cells exist
528 if (cells0[0] >= 0)
529 P0(Ae, cell_info0, cells0[0], num_cols);
530 if (cells0[1] >= 0)
531 {
532 std::span sub_Ae0(Ae.data() + bs0 * dmap0_size * num_cols,
533 bs0 * dmap0_size * num_cols);
534
535 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
536 }
537 if (cells1[0] >= 0)
538 P1T(Ae, cell_info1, cells1[0], num_rows);
539
540 if (cells1[1] >= 0)
541 {
542 for (int row = 0; row < num_rows; ++row)
543 {
544 // DOFs for dmap1 and cell1 are not stored contiguously in the
545 // block matrix, so each row needs a separate span access
546 std::span sub_Ae1(Ae.data() + row * num_cols + bs1 * dmap1_size,
547 bs1 * dmap1_size);
548 P1T(sub_Ae1, cell_info1, cells1[1], 1);
549 }
550 }
551
552 // Don't clear rows/cols in LiftingMode
553 if constexpr (!LiftingMode)
554 {
555 // Zero rows and columns for BCs
556 if (!bc0.empty())
557 {
558 for (std::size_t i = 0; i < dmapjoint0.size(); ++i)
559 {
560 for (int k = 0; k < bs0; ++k)
561 {
562 if (bc0[bs0 * dmapjoint0[i] + k])
563 {
564 // Zero row bs0 * i + k
565 int row = bs0 * i + k;
566 std::fill_n(std::next(Ae.begin(), num_cols * row), num_cols, 0);
567 }
568 }
569 }
570 }
571 if (!bc1.empty())
572 {
573 for (std::size_t j = 0; j < dmapjoint1.size(); ++j)
574 {
575 for (int k = 0; k < bs1; ++k)
576 {
577 if (bc1[bs1 * dmapjoint1[j] + k])
578 {
579 // Zero column bs1 * j + k
580 for (int m = 0; m < num_rows; ++m)
581 Ae[m * num_cols + bs1 * j + k] = 0;
582 }
583 }
584 }
585 }
586 }
587
588 mat_set(dmapjoint0, dmapjoint1, Ae);
589 }
590}
591
612template <dolfinx::scalar T, std::floating_point U, bool LiftingMode = false>
613void assemble_matrix(
614 la::MatSet<T> auto mat_set, const Form<T, U>& a,
615 md::mdspan<const scalar_value_t<T>,
616 md::extents<std::size_t, md::dynamic_extent, 3>>
617 x,
618 std::span<const T> constants,
619 const std::map<std::pair<IntegralType, int>,
620 std::pair<std::span<const T>, int>>& coefficients,
621 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1)
622{
623 // Integration domain mesh
624 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
625 assert(mesh);
626
627 // Test function mesh
628 auto mesh0 = a.function_spaces().at(0)->mesh();
629 assert(mesh0);
630
631 // Trial function mesh
632 auto mesh1 = a.function_spaces().at(1)->mesh();
633 assert(mesh1);
634
635 // TODO: Mixed topology with exterior and interior facet integrals.
636 //
637 // NOTE: Can't just loop over cell types for interior facet integrals
638 // because we have a kernel per combination of comparable cell types,
639 // rather than one per cell type. Also, we need the dofmaps for two
640 // different cell types at the same time.
641 const int num_cell_types = mesh->topology()->cell_types().size();
642 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
643 {
644 // Geometry dofmap and data
645 mdspan2_t x_dofmap = mesh->geometry().dofmap(cell_type_idx);
646
647 // Get dofmap data
648 std::shared_ptr<const fem::DofMap> dofmap0
649 = a.function_spaces().at(0)->dofmaps(cell_type_idx);
650 std::shared_ptr<const fem::DofMap> dofmap1
651 = a.function_spaces().at(1)->dofmaps(cell_type_idx);
652 assert(dofmap0);
653 assert(dofmap1);
654 auto dofs0 = dofmap0->map();
655 const int bs0 = dofmap0->bs();
656 auto dofs1 = dofmap1->map();
657 const int bs1 = dofmap1->bs();
658
659 auto element0 = a.function_spaces().at(0)->elements(cell_type_idx);
660 assert(element0);
661 auto element1 = a.function_spaces().at(1)->elements(cell_type_idx);
662 assert(element1);
663 fem::DofTransformKernel<T> auto P0
664 = element0->template dof_transformation_fn<T>(doftransform::standard);
665 fem::DofTransformKernel<T> auto P1T
666 = element1->template dof_transformation_right_fn<T>(
668
669 std::span<const std::uint32_t> cell_info0;
670 std::span<const std::uint32_t> cell_info1;
671 if (element0->needs_dof_transformations()
672 or element1->needs_dof_transformations()
673 or a.needs_facet_permutations())
674 {
675 mesh0->topology_mutable()->create_entity_permutations();
676 mesh1->topology_mutable()->create_entity_permutations();
677 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
678 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
679 }
680
681 for (int i = 0; i < a.num_integrals(IntegralType::cell, cell_type_idx); ++i)
682 {
683 auto fn = a.kernel(IntegralType::cell, i, cell_type_idx);
684 assert(fn);
685 std::span cells = a.domain(IntegralType::cell, i, cell_type_idx);
686 std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
687 std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, cell_type_idx);
688 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
689 assert(cells.size() * cstride == coeffs.size());
690 impl::assemble_cells_matrix<T, LiftingMode>(
691 mat_set, x_dofmap, x, cells, {dofs0, bs0, cells0}, P0,
692 {dofs1, bs1, cells1}, P1T, bc0, bc1, fn,
693 md::mdspan(coeffs.data(), cells.size(), cstride), constants,
694 cell_info0, cell_info1);
695 }
696
697 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
698 if (a.needs_facet_permutations())
699 {
700 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
701 int num_facets_per_cell
702 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
703 mesh->topology_mutable()->create_entity_permutations();
704 const std::vector<std::uint8_t>& p
705 = mesh->topology()->get_facet_permutations();
706 facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
707 num_facets_per_cell);
708 }
709
710 for (int i = 0;
711 i < a.num_integrals(IntegralType::interior_facet, cell_type_idx); ++i)
712 {
713 if (num_cell_types > 1)
714 {
715 throw std::runtime_error("Interior facet integrals with mixed "
716 "topology aren't supported yet");
717 }
718
719 using mdspanx22_t
720 = md::mdspan<const std::int32_t,
721 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
722 using mdspanx2x_t
723 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
724 md::dynamic_extent>>;
725
726 auto fn = a.kernel(IntegralType::interior_facet, i, 0);
727 assert(fn);
728 auto& [coeffs, cstride]
729 = coefficients.at({IntegralType::interior_facet, i});
730
731 std::span facets = a.domain(IntegralType::interior_facet, i, 0);
732 std::span facets0 = a.domain_arg(IntegralType::interior_facet, 0, i, 0);
733 std::span facets1 = a.domain_arg(IntegralType::interior_facet, 1, i, 0);
734 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
735 impl::assemble_interior_facets<T, LiftingMode>(
736 mat_set, x_dofmap, x,
737 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
738 {*dofmap0, bs0,
739 mdspanx22_t(facets0.data(), facets0.size() / 4, 2, 2)},
740 P0,
741 {*dofmap1, bs1,
742 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
743 P1T, bc0, bc1, fn,
744 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride), constants,
745 cell_info0, cell_info1, facet_perms);
746 }
747
748 for (auto itg_type : {fem::IntegralType::exterior_facet,
750 {
751 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
753 ? facet_perms
754 : md::mdspan<const std::uint8_t,
755 md::dextents<std::size_t, 2>>{};
756
757 for (int i = 0; i < a.num_integrals(itg_type, cell_type_idx); ++i)
758 {
759 if (num_cell_types > 1)
760 {
761 throw std::runtime_error("Exterior facet integrals with mixed "
762 "topology aren't supported yet");
763 }
764
765 using mdspanx2_t
766 = md::mdspan<const std::int32_t,
767 md::extents<std::size_t, md::dynamic_extent, 2>>;
768
769 auto fn = a.kernel(itg_type, i, 0);
770 assert(fn);
771 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
772
773 std::span e = a.domain(itg_type, i, 0);
774 mdspanx2_t entities(e.data(), e.size() / 2, 2);
775 std::span e0 = a.domain_arg(itg_type, 0, i, 0);
776 mdspanx2_t entities0(e0.data(), e0.size() / 2, 2);
777 std::span e1 = a.domain_arg(itg_type, 1, i, 0);
778 mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
779 assert((entities.size() / 2) * cstride == coeffs.size());
780 impl::assemble_entities<T, LiftingMode>(
781 mat_set, x_dofmap, x, entities, {dofs0, bs0, entities0}, P0,
782 {dofs1, bs1, entities1}, P1T, bc0, bc1, fn,
783 md::mdspan(coeffs.data(), entities.extent(0), cstride), constants,
784 cell_info0, cell_info1, perms);
785 }
786 }
787 }
788}
789
790} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Functions supporting finite element method operations.
void cells(la::SparsityPattern &pattern, const std::pair< R0, R1 > &cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.h:37
@ transpose
Transpose.
Definition FiniteElement.h:28
@ standard
Standard.
Definition FiniteElement.h:27
@ vertex
Vertex.
Definition Form.h:43
@ interior_facet
Interior facet.
Definition Form.h:42
@ ridge
Ridge.
Definition Form.h:44
@ cell
Cell.
Definition Form.h:40
@ exterior_facet
Facet.
Definition Form.h:41
CellType
Cell type identifier.
Definition cell_types.h:21
int cell_num_entities(CellType type, int dim)
Number of entities of dimension.
Definition cell_types.cpp:90