DOLFINx 0.10.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{
28using mdspan2_t = md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>;
29
63template <dolfinx::scalar T>
64void assemble_cells_matrix(
65 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
66 md::mdspan<const scalar_value_t<T>,
67 md::extents<std::size_t, md::dynamic_extent, 3>>
68 x,
69 std::span<const std::int32_t> cells,
70 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap0,
71 fem::DofTransformKernel<T> auto P0,
72 std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap1,
73 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
74 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
75 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
76 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
77 std::span<const std::uint32_t> cell_info1)
78{
79 if (cells.empty())
80 return;
81
82 const auto [dmap0, bs0, cells0] = dofmap0;
83 const auto [dmap1, bs1, cells1] = dofmap1;
84
85 // Iterate over active cells
86 const int num_dofs0 = dmap0.extent(1);
87 const int num_dofs1 = dmap1.extent(1);
88 const int ndim0 = bs0 * num_dofs0;
89 const int ndim1 = bs1 * num_dofs1;
90 std::vector<T> Ae(ndim0 * ndim1);
91 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
92
93 // Iterate over active cells
94 assert(cells0.size() == cells.size());
95 assert(cells1.size() == cells.size());
96 for (std::size_t c = 0; c < cells.size(); ++c)
97 {
98 // Cell index in integration domain mesh (c), test function mesh
99 // (c0) and trial function mesh (c1)
100 std::int32_t cell = cells[c];
101 std::int32_t cell0 = cells0[c];
102 std::int32_t cell1 = cells1[c];
103
104 // Get cell coordinates/geometry
105 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
106 for (std::size_t i = 0; i < x_dofs.size(); ++i)
107 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
108
109 // Tabulate tensor
110 std::ranges::fill(Ae, 0);
111 kernel(Ae.data(), &coeffs(c, 0), constants.data(), cdofs.data(), nullptr,
112 nullptr, nullptr);
113
114 // Compute A = P_0 \tilde{A} P_1^T (dof transformation)
115 P0(Ae, cell_info0, cell0, ndim1); // B = P0 \tilde{A}
116 P1T(Ae, cell_info1, cell1, ndim0); // A = B P1_T
117
118 // Zero rows/columns for essential bcs
119 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
120 std::span dofs1(dmap1.data_handle() + cell1 * 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
201template <dolfinx::scalar T>
202void assemble_entities(
203 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
204 md::mdspan<const scalar_value_t<T>,
205 md::extents<std::size_t, md::dynamic_extent, 3>>
206 x,
207 md::mdspan<const std::int32_t,
208 std::extents<std::size_t, md::dynamic_extent, 2>>
209 entities,
210 std::tuple<mdspan2_t, int,
211 md::mdspan<const std::int32_t,
212 std::extents<std::size_t, md::dynamic_extent, 2>>>
213 dofmap0,
214 fem::DofTransformKernel<T> auto P0,
215 std::tuple<mdspan2_t, int,
216 md::mdspan<const std::int32_t,
217 std::extents<std::size_t, md::dynamic_extent, 2>>>
218 dofmap1,
219 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
220 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
221 md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
222 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
223 std::span<const std::uint32_t> cell_info1,
224 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
225{
226 if (entities.empty())
227 return;
228
229 const auto [dmap0, bs0, entities0] = dofmap0;
230 const auto [dmap1, bs1, entities1] = dofmap1;
231
232 // Data structures used in assembly
233 std::vector<scalar_value_t<T>> cdofs(3 * x_dofmap.extent(1));
234 const int num_dofs0 = dmap0.extent(1);
235 const int num_dofs1 = dmap1.extent(1);
236 const int ndim0 = bs0 * num_dofs0;
237 const int ndim1 = bs1 * num_dofs1;
238 std::vector<T> Ae(ndim0 * ndim1);
239 assert(entities0.size() == entities.size());
240 assert(entities1.size() == entities.size());
241 for (std::size_t f = 0; f < entities.extent(0); ++f)
242 {
243 // Cell in the integration domain, local entity index relative to the
244 // integration domain cell, and cells in the test and trial function
245 // meshes
246 std::int32_t cell = entities(f, 0);
247 std::int32_t local_entity = entities(f, 1);
248 std::int32_t cell0 = entities0(f, 0);
249 std::int32_t cell1 = entities1(f, 0);
250
251 // Get cell coordinates/geometry
252 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
253 for (std::size_t i = 0; i < x_dofs.size(); ++i)
254 std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs.begin(), 3 * i));
255
256 // Permutations
257 std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
258
259 // Tabulate tensor
260 std::ranges::fill(Ae, 0);
261 kernel(Ae.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
262 &local_entity, &perm, nullptr);
263 P0(Ae, cell_info0, cell0, ndim1);
264 P1T(Ae, cell_info1, cell1, ndim0);
265
266 // Zero rows/columns for essential bcs
267 std::span dofs0(dmap0.data_handle() + cell0 * num_dofs0, num_dofs0);
268 std::span dofs1(dmap1.data_handle() + cell1 * num_dofs1, num_dofs1);
269 if (!bc0.empty())
270 {
271 for (int i = 0; i < num_dofs0; ++i)
272 {
273 for (int k = 0; k < bs0; ++k)
274 {
275 if (bc0[bs0 * dofs0[i] + k])
276 {
277 // Zero row bs0 * i + k
278 const int row = bs0 * i + k;
279 std::fill_n(std::next(Ae.begin(), ndim1 * row), ndim1, 0);
280 }
281 }
282 }
283 }
284 if (!bc1.empty())
285 {
286 for (int j = 0; j < num_dofs1; ++j)
287 {
288 for (int k = 0; k < bs1; ++k)
289 {
290 if (bc1[bs1 * dofs1[j] + k])
291 {
292 // Zero column bs1 * j + k
293 const int col = bs1 * j + k;
294 for (int row = 0; row < ndim0; ++row)
295 Ae[row * ndim1 + col] = 0;
296 }
297 }
298 }
299 }
300
301 mat_set(dofs0, dofs1, Ae);
302 }
303}
304
341template <dolfinx::scalar T>
342void assemble_interior_facets(
343 la::MatSet<T> auto mat_set, mdspan2_t x_dofmap,
344 md::mdspan<const scalar_value_t<T>,
345 md::extents<std::size_t, md::dynamic_extent, 3>>
346 x,
347 md::mdspan<const std::int32_t,
348 std::extents<std::size_t, md::dynamic_extent, 2, 2>>
349 facets,
350 std::tuple<const DofMap&, int,
351 md::mdspan<const std::int32_t,
352 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
353 dofmap0,
354 fem::DofTransformKernel<T> auto P0,
355 std::tuple<const DofMap&, int,
356 md::mdspan<const std::int32_t,
357 std::extents<std::size_t, md::dynamic_extent, 2, 2>>>
358 dofmap1,
359 fem::DofTransformKernel<T> auto P1T, std::span<const std::int8_t> bc0,
360 std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
361 md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
362 md::dynamic_extent>>
363 coeffs,
364 std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
365 std::span<const std::uint32_t> cell_info1,
366 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms)
367{
368 if (facets.empty())
369 return;
370
371 const auto [dmap0, bs0, facets0] = dofmap0;
372 const auto [dmap1, bs1, facets1] = dofmap1;
373
374 // Data structures used in assembly
375 using X = scalar_value_t<T>;
376 std::vector<X> cdofs(2 * x_dofmap.extent(1) * 3);
377 std::span<X> cdofs0(cdofs.data(), x_dofmap.extent(1) * 3);
378 std::span<X> cdofs1(cdofs.data() + x_dofmap.extent(1) * 3,
379 x_dofmap.extent(1) * 3);
380
381 const std::size_t dmap0_size = dmap0.map().extent(1);
382 const std::size_t dmap1_size = dmap1.map().extent(1);
383 const int num_rows = bs0 * 2 * dmap0_size;
384 const int num_cols = bs1 * 2 * dmap1_size;
385
386 // Temporaries for joint dofmaps
387 std::vector<T> Ae(num_rows * num_cols), be(num_rows);
388 std::vector<std::int32_t> dmapjoint0(2 * dmap0_size);
389 std::vector<std::int32_t> dmapjoint1(2 * dmap1_size);
390 assert(facets0.size() == facets.size());
391 assert(facets1.size() == facets.size());
392 for (std::size_t f = 0; f < facets.extent(0); ++f)
393 {
394 // Cells in integration domain, test function domain and trial
395 // function domain
396 std::array cells{facets(f, 0, 0), facets(f, 1, 0)};
397 std::array cells0{facets0(f, 0, 0), facets0(f, 1, 0)};
398 std::array cells1{facets1(f, 0, 0), facets1(f, 1, 0)};
399
400 // Local facets indices
401 std::array local_facet{facets(f, 0, 1), facets(f, 1, 1)};
402
403 // Get cell geometry
404 auto x_dofs0 = md::submdspan(x_dofmap, cells[0], md::full_extent);
405 for (std::size_t i = 0; i < x_dofs0.size(); ++i)
406 std::copy_n(&x(x_dofs0[i], 0), 3, std::next(cdofs0.begin(), 3 * i));
407 auto x_dofs1 = md::submdspan(x_dofmap, cells[1], md::full_extent);
408 for (std::size_t i = 0; i < x_dofs1.size(); ++i)
409 std::copy_n(&x(x_dofs1[i], 0), 3, std::next(cdofs1.begin(), 3 * i));
410
411 // Get dof maps for cells and pack
412 // When integrating over interfaces between two domains, the test function
413 // might only be defined on one side, so we check which cells exist in the
414 // test function domain
415 std::span<const std::int32_t> dmap0_cell0
416 = cells0[0] >= 0 ? dmap0.cell_dofs(cells0[0])
417 : std::span<const std::int32_t>();
418 std::span<const std::int32_t> dmap0_cell1
419 = cells0[1] >= 0 ? dmap0.cell_dofs(cells0[1])
420 : std::span<const std::int32_t>();
421
422 std::ranges::copy(dmap0_cell0, dmapjoint0.begin());
423 std::ranges::copy(dmap0_cell1, std::next(dmapjoint0.begin(), dmap0_size));
424
425 // Check which cells exist in the trial function domain
426 std::span<const std::int32_t> dmap1_cell0
427 = cells1[0] >= 0 ? dmap1.cell_dofs(cells1[0])
428 : std::span<const std::int32_t>();
429 std::span<const std::int32_t> dmap1_cell1
430 = cells1[1] >= 0 ? dmap1.cell_dofs(cells1[1])
431 : std::span<const std::int32_t>();
432
433 std::ranges::copy(dmap1_cell0, dmapjoint1.begin());
434 std::ranges::copy(dmap1_cell1, std::next(dmapjoint1.begin(), dmap1_size));
435
436 // Tabulate tensor
437 std::ranges::fill(Ae, 0);
438 std::array perm = perms.empty()
439 ? std::array<std::uint8_t, 2>{0, 0}
440 : std::array{perms(cells[0], local_facet[0]),
441 perms(cells[1], local_facet[1])};
442 kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
443 local_facet.data(), perm.data(), nullptr);
444
445 // Local element layout is a 2x2 block matrix with structure
446 //
447 // cell0cell0 | cell0cell1
448 // cell1cell0 | cell1cell1
449 //
450 // where each block is element tensor of size (dmap0, dmap1).
451
452 // Only apply transformation when cells exist
453 if (cells0[0] >= 0)
454 P0(Ae, cell_info0, cells0[0], num_cols);
455 if (cells0[1] >= 0)
456 {
457 std::span sub_Ae0(Ae.data() + bs0 * dmap0_size * num_cols,
458 bs0 * dmap0_size * num_cols);
459
460 P0(sub_Ae0, cell_info0, cells0[1], num_cols);
461 }
462 if (cells1[0] >= 0)
463 P1T(Ae, cell_info1, cells1[0], num_rows);
464
465 if (cells1[1] >= 0)
466 {
467 for (int row = 0; row < num_rows; ++row)
468 {
469 // DOFs for dmap1 and cell1 are not stored contiguously in the
470 // block matrix, so each row needs a separate span access
471 std::span sub_Ae1(Ae.data() + row * num_cols + bs1 * dmap1_size,
472 bs1 * dmap1_size);
473 P1T(sub_Ae1, cell_info1, cells1[1], 1);
474 }
475 }
476
477 // Zero rows/columns for essential bcs
478 if (!bc0.empty())
479 {
480 for (std::size_t i = 0; i < dmapjoint0.size(); ++i)
481 {
482 for (int k = 0; k < bs0; ++k)
483 {
484 if (bc0[bs0 * dmapjoint0[i] + k])
485 {
486 // Zero row bs0 * i + k
487 std::fill_n(std::next(Ae.begin(), num_cols * (bs0 * i + k)),
488 num_cols, 0);
489 }
490 }
491 }
492 }
493 if (!bc1.empty())
494 {
495 for (std::size_t j = 0; j < dmapjoint1.size(); ++j)
496 {
497 for (int k = 0; k < bs1; ++k)
498 {
499 if (bc1[bs1 * dmapjoint1[j] + k])
500 {
501 // Zero column bs1 * j + k
502 for (int m = 0; m < num_rows; ++m)
503 Ae[m * num_cols + bs1 * j + k] = 0;
504 }
505 }
506 }
507 }
508
509 mat_set(dmapjoint0, dmapjoint1, Ae);
510 }
511}
512
531template <dolfinx::scalar T, std::floating_point U>
532void assemble_matrix(
533 la::MatSet<T> auto mat_set, const Form<T, U>& a,
534 md::mdspan<const scalar_value_t<T>,
535 md::extents<std::size_t, md::dynamic_extent, 3>>
536 x,
537 std::span<const T> constants,
538 const std::map<std::pair<IntegralType, int>,
539 std::pair<std::span<const T>, int>>& coefficients,
540 std::span<const std::int8_t> bc0, std::span<const std::int8_t> bc1)
541{
542 // Integration domain mesh
543 std::shared_ptr<const mesh::Mesh<U>> mesh = a.mesh();
544 assert(mesh);
545
546 // Test function mesh
547 auto mesh0 = a.function_spaces().at(0)->mesh();
548 assert(mesh0);
549
550 // Trial function mesh
551 auto mesh1 = a.function_spaces().at(1)->mesh();
552 assert(mesh1);
553
554 // TODO: Mixed topology with exterior and interior facet integrals.
555 //
556 // NOTE: Can't just loop over cell types for interior facet integrals
557 // because we have a kernel per combination of comparable cell types,
558 // rather than one per cell type. Also, we need the dofmaps for two
559 // different cell types at the same time.
560 const int num_cell_types = mesh->topology()->cell_types().size();
561 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
562 {
563 // Geometry dofmap and data
564 mdspan2_t x_dofmap = mesh->geometry().dofmap(cell_type_idx);
565
566 // Get dofmap data
567 std::shared_ptr<const fem::DofMap> dofmap0
568 = a.function_spaces().at(0)->dofmaps(cell_type_idx);
569 std::shared_ptr<const fem::DofMap> dofmap1
570 = a.function_spaces().at(1)->dofmaps(cell_type_idx);
571 assert(dofmap0);
572 assert(dofmap1);
573 auto dofs0 = dofmap0->map();
574 const int bs0 = dofmap0->bs();
575 auto dofs1 = dofmap1->map();
576 const int bs1 = dofmap1->bs();
577
578 auto element0 = a.function_spaces().at(0)->elements(cell_type_idx);
579 assert(element0);
580 auto element1 = a.function_spaces().at(1)->elements(cell_type_idx);
581 assert(element1);
582 fem::DofTransformKernel<T> auto P0
583 = element0->template dof_transformation_fn<T>(doftransform::standard);
584 fem::DofTransformKernel<T> auto P1T
585 = element1->template dof_transformation_right_fn<T>(
587
588 std::span<const std::uint32_t> cell_info0;
589 std::span<const std::uint32_t> cell_info1;
590 if (element0->needs_dof_transformations()
591 or element1->needs_dof_transformations()
592 or a.needs_facet_permutations())
593 {
594 mesh0->topology_mutable()->create_entity_permutations();
595 mesh1->topology_mutable()->create_entity_permutations();
596 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
597 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
598 }
599
600 for (int i = 0; i < a.num_integrals(IntegralType::cell, cell_type_idx); ++i)
601 {
602 auto fn = a.kernel(IntegralType::cell, i, cell_type_idx);
603 assert(fn);
604 std::span cells = a.domain(IntegralType::cell, i, cell_type_idx);
605 std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
606 std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, cell_type_idx);
607 auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
608 assert(cells.size() * cstride == coeffs.size());
609 impl::assemble_cells_matrix(
610 mat_set, x_dofmap, x, cells, {dofs0, bs0, cells0}, P0,
611 {dofs1, bs1, cells1}, P1T, bc0, bc1, fn,
612 md::mdspan(coeffs.data(), cells.size(), cstride), constants,
613 cell_info0, cell_info1);
614 }
615
616 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> facet_perms;
617 if (a.needs_facet_permutations())
618 {
619 mesh::CellType cell_type = mesh->topology()->cell_types()[cell_type_idx];
620 int num_facets_per_cell
621 = mesh::cell_num_entities(cell_type, mesh->topology()->dim() - 1);
622 mesh->topology_mutable()->create_entity_permutations();
623 const std::vector<std::uint8_t>& p
624 = mesh->topology()->get_facet_permutations();
625 facet_perms = md::mdspan(p.data(), p.size() / num_facets_per_cell,
626 num_facets_per_cell);
627 }
628
629 for (int i = 0;
630 i < a.num_integrals(IntegralType::interior_facet, cell_type_idx); ++i)
631 {
632 if (num_cell_types > 1)
633 {
634 throw std::runtime_error("Interior facet integrals with mixed "
635 "topology aren't supported yet");
636 }
637
638 using mdspanx22_t
639 = md::mdspan<const std::int32_t,
640 md::extents<std::size_t, md::dynamic_extent, 2, 2>>;
641 using mdspanx2x_t
642 = md::mdspan<const T, md::extents<std::size_t, md::dynamic_extent, 2,
643 md::dynamic_extent>>;
644
645 auto fn = a.kernel(IntegralType::interior_facet, i, 0);
646 assert(fn);
647 auto& [coeffs, cstride]
648 = coefficients.at({IntegralType::interior_facet, i});
649
650 std::span facets = a.domain(IntegralType::interior_facet, i, 0);
651 std::span facets0 = a.domain_arg(IntegralType::interior_facet, 0, i, 0);
652 std::span facets1 = a.domain_arg(IntegralType::interior_facet, 1, i, 0);
653 assert((facets.size() / 4) * 2 * cstride == coeffs.size());
654 impl::assemble_interior_facets(
655 mat_set, x_dofmap, x,
656 mdspanx22_t(facets.data(), facets.size() / 4, 2, 2),
657 {*dofmap0, bs0,
658 mdspanx22_t(facets0.data(), facets0.size() / 4, 2, 2)},
659 P0,
660 {*dofmap1, bs1,
661 mdspanx22_t(facets1.data(), facets1.size() / 4, 2, 2)},
662 P1T, bc0, bc1, fn,
663 mdspanx2x_t(coeffs.data(), facets.size() / 4, 2, cstride), constants,
664 cell_info0, cell_info1, facet_perms);
665 }
666
667 for (auto itg_type : {fem::IntegralType::exterior_facet,
669 {
670 md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms
672 ? facet_perms
673 : md::mdspan<const std::uint8_t,
674 md::dextents<std::size_t, 2>>{};
675
676 for (int i = 0; i < a.num_integrals(itg_type, cell_type_idx); ++i)
677 {
678 if (num_cell_types > 1)
679 {
680 throw std::runtime_error("Exterior facet integrals with mixed "
681 "topology aren't supported yet");
682 }
683
684 using mdspanx2_t
685 = md::mdspan<const std::int32_t,
686 md::extents<std::size_t, md::dynamic_extent, 2>>;
687
688 auto fn = a.kernel(itg_type, i, 0);
689 assert(fn);
690 auto& [coeffs, cstride] = coefficients.at({itg_type, i});
691
692 std::span e = a.domain(itg_type, i, 0);
693 mdspanx2_t entities(e.data(), e.size() / 2, 2);
694 std::span e0 = a.domain_arg(itg_type, 0, i, 0);
695 mdspanx2_t entities0(e0.data(), e0.size() / 2, 2);
696 std::span e1 = a.domain_arg(itg_type, 1, i, 0);
697 mdspanx2_t entities1(e1.data(), e1.size() / 2, 2);
698 assert((entities.size() / 2) * cstride == coeffs.size());
699 impl::assemble_entities(
700 mat_set, x_dofmap, x, entities, {dofs0, bs0, entities0}, P0,
701 {dofs1, bs1, entities1}, P1T, bc0, bc1, fn,
702 md::mdspan(coeffs.data(), entities.extent(0), cstride), constants,
703 cell_info0, cell_info1, perms);
704 }
705 }
706 }
707}
708
709} // namespace dolfinx::fem::impl
Degree-of-freedom map representations and tools.
Functions supporting finite element method operations.
void cells(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:16
@ 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:22
int cell_num_entities(CellType type, int dim)
Definition cell_types.cpp:139