DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1// Copyright (C) 2013-2025 Johan Hake, Jan Blechta, Garth N. Wells and Paul T.
2// Kühner
3//
4// This file is part of DOLFINx (https://www.fenicsproject.org)
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7
8#pragma once
9
10#include "Constant.h"
11#include "CoordinateElement.h"
12#include "DofMap.h"
13#include "ElementDofLayout.h"
14#include "Expression.h"
15#include "Form.h"
16#include "Function.h"
17#include "FunctionSpace.h"
18#include "kernel.h"
19#include "sparsitybuild.h"
20#include <algorithm>
21#include <array>
22#include <concepts>
23#include <cstddef>
24#include <dolfinx/common/defines.h>
25#include <dolfinx/common/types.h>
26#include <dolfinx/la/SparsityPattern.h>
27#include <dolfinx/mesh/EntityMap.h>
28#include <dolfinx/mesh/Topology.h>
29#include <dolfinx/mesh/cell_types.h>
30#include <dolfinx/mesh/utils.h>
31#include <format>
32#include <functional>
33#include <memory>
34#include <optional>
35#include <ranges>
36#include <set>
37#include <span>
38#include <stdexcept>
39#include <string>
40#include <tuple>
41#include <type_traits>
42#include <ufcx.h>
43#include <utility>
44#include <vector>
45
48namespace basix
49{
50template <std::floating_point T>
51class FiniteElement;
52}
53
54namespace dolfinx::common
55{
56class IndexMap;
57}
58
59namespace dolfinx::fem
60{
61template <dolfinx::scalar T, std::floating_point U>
62class Expression;
63
64namespace impl
65{
72template <int num_cells>
73std::array<std::int32_t, 2 * num_cells>
74get_cell_facet_pairs(std::int32_t f, std::span<const std::int32_t> cells,
76{
77 // Loop over cells sharing facet
78 assert(cells.size() == num_cells);
79 std::array<std::int32_t, 2 * num_cells> cell_local_facet_pairs;
80 for (int c = 0; c < num_cells; ++c)
81 {
82 // Get local index of facet with respect to the cell
83 std::int32_t cell = cells[c];
84 auto cell_facets = c_to_f.links(cell);
85 auto facet_it = std::find(cell_facets.begin(), cell_facets.end(), f);
86 assert(facet_it != cell_facets.end());
87 int local_f = std::distance(cell_facets.begin(), facet_it);
88 cell_local_facet_pairs[2 * c] = cell;
89 cell_local_facet_pairs[2 * c + 1] = local_f;
90 }
91
92 return cell_local_facet_pairs;
93}
94
102template <int num_cells>
103std::array<std::int32_t, 2 * num_cells>
104get_cell_vertex_pairs(std::int32_t v, std::span<const std::int32_t> cells,
106{
107 static_assert(num_cells == 1); // Patch assembly not supported.
108
109 assert(cells.size() > 0);
110
111 // Use first cell for assembly over by default
112 std::int32_t cell = cells[0];
113
114 // Find local index of vertex within cell
115 auto cell_vertices = c_to_v.links(cell);
116 auto it = std::ranges::find(cell_vertices, v);
117 assert(it != cell_vertices.end());
118 std::int32_t local_index = std::distance(cell_vertices.begin(), it);
119
120 return {cell, local_index};
121}
122
123} // namespace impl
124
158std::vector<std::int32_t>
160 const mesh::Topology& topology,
161 std::span<const std::int32_t> entities);
162
170template <dolfinx::scalar T, std::floating_point U>
171std::vector<std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
172extract_function_spaces(const std::vector<std::vector<const Form<T, U>*>>& a)
173{
174 std::vector<
175 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
176 spaces(
177 a.size(),
178 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>(
179 a.front().size()));
180 for (std::size_t i = 0; i < a.size(); ++i)
181 {
182 for (std::size_t j = 0; j < a[i].size(); ++j)
183 {
184 if (const Form<T, U>* form = a[i][j]; form)
185 spaces[i][j] = {form->function_spaces()[0], form->function_spaces()[1]};
186 }
187 }
188 return spaces;
189}
190
196template <dolfinx::scalar T, std::floating_point U>
198{
199 std::shared_ptr mesh = a.mesh();
200 assert(mesh);
201
202 // Get index maps and block sizes from the DOF maps. Note that in
203 // mixed-topology meshes, despite there being multiple DOF maps, the
204 // index maps and block sizes are the same.
205 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
206 *a.function_spaces().at(0)->dofmaps(0),
207 *a.function_spaces().at(1)->dofmaps(0)};
208
209 const std::array index_maps{dofmaps[0].get().index_map,
210 dofmaps[1].get().index_map};
211 const std::array bs
212 = {dofmaps[0].get().index_map_bs(), dofmaps[1].get().index_map_bs()};
213
214 la::SparsityPattern pattern(mesh->comm(), index_maps, bs);
215 build_sparsity_pattern(pattern, a);
216 return pattern;
217}
218
224template <dolfinx::scalar T, std::floating_point U>
226{
227 if (a.rank() != 2)
228 {
229 throw std::runtime_error(
230 "Cannot create sparsity pattern. Form is not a bilinear.");
231 }
232
233 std::shared_ptr mesh = a.mesh();
234 assert(mesh);
235 std::shared_ptr mesh0 = a.function_spaces().at(0)->mesh();
236 assert(mesh0);
237 std::shared_ptr mesh1 = a.function_spaces().at(1)->mesh();
238 assert(mesh1);
239
240 const std::set<IntegralType> types = a.integral_types();
241 if (types.find(IntegralType::interior_facet) != types.end()
242 or types.find(IntegralType::exterior_facet) != types.end())
243 {
244 // FIXME: cleanup these calls? Some of the happen internally again.
245 int tdim = mesh->topology()->dim();
246 mesh->topology_mutable()->create_entities(tdim - 1);
247 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
248 }
249
250 common::Timer t0("Build sparsity");
251
252 auto extract_cells = [](std::span<const std::int32_t> facets)
253 {
254 assert(facets.size() % 2 == 0);
255 std::vector<std::int32_t> cells;
256 cells.reserve(facets.size() / 2);
257 for (std::size_t i = 0; i < facets.size(); i += 2)
258 cells.push_back(facets[i]);
259 return cells;
260 };
261
262 const int num_cell_types = mesh->topology()->cell_types().size();
263 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
264 {
265 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
266 *a.function_spaces().at(0)->dofmaps(cell_type_idx),
267 *a.function_spaces().at(1)->dofmaps(cell_type_idx)};
268
269 // Create and build sparsity pattern
270 for (auto type : types)
271 {
272 switch (type)
273 {
275 for (int i = 0; i < a.num_integrals(type, cell_type_idx); ++i)
276 {
277 sparsitybuild::cells(pattern,
278 {a.domain_arg(type, 0, i, cell_type_idx),
279 a.domain_arg(type, 1, i, cell_type_idx)},
280 {{dofmaps[0], dofmaps[1]}});
281 }
282 break;
284 for (int i = 0; i < a.num_integrals(type, cell_type_idx); ++i)
285 {
287 pattern,
288 {extract_cells(a.domain_arg(type, 0, i, 0)),
289 extract_cells(a.domain_arg(type, 1, i, 0))},
290 {{dofmaps[0], dofmaps[1]}});
291 }
292 break;
294 for (int i = 0; i < a.num_integrals(type, cell_type_idx); ++i)
295 {
296 sparsitybuild::cells(pattern,
297 {extract_cells(a.domain_arg(type, 0, i, 0)),
298 extract_cells(a.domain_arg(type, 1, i, 0))},
299 {{dofmaps[0], dofmaps[1]}});
300 }
301 break;
302 default:
303 throw std::runtime_error("Unsupported integral type");
304 }
305 }
306 }
307
308 t0.stop();
309}
310
312template <std::floating_point T>
314 const std::vector<int>& parent_map
315 = {})
316{
317 // Create subdofmaps and compute offset
318 std::vector<int> offsets(1, 0);
319 std::vector<dolfinx::fem::ElementDofLayout> sub_doflayout;
320 int bs = element.block_size();
321 for (int i = 0; i < element.num_sub_elements(); ++i)
322 {
323 // The ith sub-element. For mixed elements this is subelements()[i]. For
324 // blocked elements, the sub-element will always be the same, so we'll use
325 // sub_elements()[0]
326 std::shared_ptr<const fem::FiniteElement<T>> sub_e
327 = element.sub_elements()[bs > 1 ? 0 : i];
328
329 // In a mixed element DOFs are ordered element by element, so the offset to
330 // the next sub-element is sub_e->space_dimension(). Blocked elements use
331 // xxyyzz ordering, so the offset to the next sub-element is 1
332
333 std::vector<int> parent_map_sub(sub_e->space_dimension(), offsets.back());
334 for (std::size_t j = 0; j < parent_map_sub.size(); ++j)
335 parent_map_sub[j] += bs * j;
336 offsets.push_back(offsets.back() + (bs > 1 ? 1 : sub_e->space_dimension()));
337 sub_doflayout.push_back(
338 dolfinx::fem::create_element_dof_layout(*sub_e, parent_map_sub));
339 }
340
341 return ElementDofLayout(bs, element.entity_dofs(),
342 element.entity_closure_dofs(), parent_map,
343 sub_doflayout);
344}
345
354DofMap
355create_dofmap(MPI_Comm comm, const ElementDofLayout& layout,
356 mesh::Topology& topology,
357 const std::function<void(std::span<std::int32_t>, std::uint32_t)>&
358 permute_inv,
359 const std::function<std::vector<int>(
360 const graph::AdjacencyList<std::int32_t>&)>& reorder_fn);
361
372std::vector<DofMap> create_dofmaps(
373 MPI_Comm comm, const std::vector<ElementDofLayout>& layouts,
374 mesh::Topology& topology,
375 const std::function<void(std::span<std::int32_t>, std::uint32_t)>&
376 permute_inv,
377 const std::function<std::vector<int>(
378 const graph::AdjacencyList<std::int32_t>&)>& reorder_fn);
379
383std::vector<std::string> get_coefficient_names(const ufcx_form& ufcx_form);
384
388std::vector<std::string> get_constant_names(const ufcx_form& ufcx_form);
389
408template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
410 const std::vector<std::reference_wrapper<const ufcx_form>>& ufcx_forms,
411 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
412 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
413 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
414 const std::map<
416 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
417 subdomains,
418 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
419 entity_maps,
420 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
421{
422 for (const ufcx_form& ufcx_form : ufcx_forms)
423 {
424 if (ufcx_form.rank != (int)spaces.size())
425 throw std::runtime_error("Wrong number of argument spaces for Form.");
426 if (ufcx_form.num_coefficients != (int)coefficients.size())
427 {
428 throw std::runtime_error("Mismatch between number of expected and "
429 "provided Form coefficients.");
430 }
431
432 // Check Constants for rank and size consistency
433 if (ufcx_form.num_constants != (int)constants.size())
434 {
435 throw std::runtime_error(std::format(
436 "Mismatch between number of expected and "
437 "provided Form Constants. Expected {} constants, but got {}.",
438 ufcx_form.num_constants, constants.size()));
439 }
440 for (std::size_t c = 0; c < constants.size(); ++c)
441 {
442 if (ufcx_form.constant_ranks[c] != (int)constants[c]->shape.size())
443 {
444 throw std::runtime_error(std::format(
445 "Mismatch between expected and actual rank of "
446 "Form Constant. Rank of Constant {} should be {}, but got rank {}.",
447 c, ufcx_form.constant_ranks[c], constants[c]->shape.size()));
448 }
449 if (!std::equal(constants[c]->shape.begin(), constants[c]->shape.end(),
450 ufcx_form.constant_shapes[c]))
451 {
452 throw std::runtime_error(
453 std::format("Mismatch between expected and actual shape of Form "
454 "Constant for Constant {}.",
455 c));
456 }
457 }
458 }
459
460 // Check argument function spaces
461 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
462 {
463 for (std::size_t i = 0; i < spaces.size(); ++i)
464 {
465 assert(spaces[i]->elements(form_idx));
466 if (auto element_hash
467 = ufcx_forms[form_idx].get().finite_element_hashes[i];
468 element_hash != 0
469 and element_hash
470 != spaces[i]->elements(form_idx)->basix_element().hash())
471 {
472 throw std::runtime_error(
473 "Cannot create form. Elements are different to "
474 "those used to compile the form.");
475 }
476 }
477 }
478
479 // Extract mesh from FunctionSpace, and check they are the same
480 if (!mesh and !spaces.empty())
481 mesh = spaces.front()->mesh();
482 if (!mesh)
483 throw std::runtime_error("No mesh could be associated with the Form.");
484
485 auto topology = mesh->topology();
486 assert(topology);
487 const int tdim = topology->dim();
488
489 // NOTE: This assumes all forms in mixed-topology meshes have the same
490 // integral offsets. Since the UFL forms for each type of cell should be
491 // the same, I think this assumption is OK.
492 const int* integral_offsets = ufcx_forms[0].get().form_integral_offsets;
493 std::array<int, 4> num_integrals_type;
494 for (std::size_t i = 0; i < num_integrals_type.size(); ++i)
495 num_integrals_type[i] = integral_offsets[i + 1] - integral_offsets[i];
496
497 // Create vertices, if required
498 if (num_integrals_type[vertex] > 0)
499 {
500 mesh->topology_mutable()->create_connectivity(0, tdim);
501 mesh->topology_mutable()->create_connectivity(tdim, 0);
502 }
503
504 // Create facets, if required
505 if (num_integrals_type[exterior_facet] > 0
506 or num_integrals_type[interior_facet] > 0)
507 {
508 mesh->topology_mutable()->create_entities(tdim - 1);
509 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
510 mesh->topology_mutable()->create_connectivity(tdim, tdim - 1);
511 }
512
513 // Get list of integral IDs, and load tabulate tensor into memory for
514 // each
515 std::map<std::tuple<IntegralType, int, int>, integral_data<T, U>> integrals;
516
517 auto check_geometry_hash
518 = [&geo = mesh->geometry()](const ufcx_integral& integral,
519 std::size_t cell_idx)
520 {
521 if (integral.coordinate_element_hash != geo.cmaps().at(cell_idx).hash())
522 {
523 throw std::runtime_error(
524 "Generated integral geometry element does not match mesh geometry: "
525 + std::to_string(integral.coordinate_element_hash) + ", "
526 + std::to_string(geo.cmaps().at(cell_idx).hash()));
527 }
528 };
529
530 // Attach cell kernels
531 bool needs_facet_permutations = false;
532 {
533 std::vector<std::int32_t> default_cells;
534 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
535 + integral_offsets[cell],
536 num_integrals_type[cell]);
537 auto sd = subdomains.find(IntegralType::cell);
538 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
539 {
540 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
541 for (int i = 0; i < num_integrals_type[cell]; ++i)
542 {
543 const int id = ids[i];
544 ufcx_integral* integral
545 = ufcx_form.form_integrals[integral_offsets[cell] + i];
546 assert(integral);
547 check_geometry_hash(*integral, form_idx);
548
549 // Build list of active coefficients
550 std::vector<int> active_coeffs;
551 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
552 {
553 if (integral->enabled_coefficients[j])
554 active_coeffs.push_back(j);
555 }
556
557 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
558 if (!k)
559 {
560 throw std::runtime_error(
561 "UFCx kernel function is NULL. Check requested types.");
562 }
563
564 // Build list of entities to assemble over
565 if (id == -1)
566 {
567 // Default kernel, operates on all (owned) cells
568 assert(topology->index_maps(tdim).at(form_idx));
569 default_cells.resize(
570 topology->index_maps(tdim).at(form_idx)->size_local(), 0);
571 std::iota(default_cells.begin(), default_cells.end(), 0);
572 integrals.insert({{IntegralType::cell, i, form_idx},
573 {k, default_cells, active_coeffs}});
574 }
575 else if (sd != subdomains.end())
576 {
577 // NOTE: This requires that pairs are sorted
578 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
579 [](auto& a) { return a.first; });
580 if (it != sd->second.end() and it->first == id)
581 {
582 integrals.insert({{IntegralType::cell, i, form_idx},
583 {k,
584 std::vector<std::int32_t>(it->second.begin(),
585 it->second.end()),
586 active_coeffs}});
587 }
588 }
589
590 if (integral->needs_facet_permutations)
591 needs_facet_permutations = true;
592 }
593 }
594 }
595
596 // Attach exterior facet kernels
597 std::vector<std::int32_t> default_facets_ext;
598 {
599 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
600 + integral_offsets[exterior_facet],
601 num_integrals_type[exterior_facet]);
602 auto sd = subdomains.find(IntegralType::exterior_facet);
603 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
604 {
605 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
606 for (int i = 0; i < num_integrals_type[exterior_facet]; ++i)
607 {
608 const int id = ids[i];
609 ufcx_integral* integral
610 = ufcx_form.form_integrals[integral_offsets[exterior_facet] + i];
611 assert(integral);
612 check_geometry_hash(*integral, form_idx);
613
614 std::vector<int> active_coeffs;
615 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
616 {
617 if (integral->enabled_coefficients[j])
618 active_coeffs.push_back(j);
619 }
620
621 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
622
623 // Build list of entities to assembler over
624 const std::vector bfacets = mesh::exterior_facet_indices(*topology);
625 auto f_to_c = topology->connectivity(tdim - 1, tdim);
626 assert(f_to_c);
627 auto c_to_f = topology->connectivity(tdim, tdim - 1);
628 assert(c_to_f);
629 if (id == -1)
630 {
631 // Default kernel, operates on all (owned) exterior facets
632 default_facets_ext.reserve(2 * bfacets.size());
633 for (std::int32_t f : bfacets)
634 {
635 // There will only be one pair for an exterior facet integral
636 std::array<std::int32_t, 2> pair
637 = impl::get_cell_facet_pairs<1>(f, f_to_c->links(f), *c_to_f);
638 default_facets_ext.insert(default_facets_ext.end(), pair.begin(),
639 pair.end());
640 }
641 integrals.insert({{IntegralType::exterior_facet, i, form_idx},
642 {k, default_facets_ext, active_coeffs}});
643 }
644 else if (sd != subdomains.end())
645 {
646 // NOTE: This requires that pairs are sorted
647 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
648 [](auto& a) { return a.first; });
649 if (it != sd->second.end() and it->first == id)
650 {
651 integrals.insert({{IntegralType::exterior_facet, i, form_idx},
652 {k,
653 std::vector<std::int32_t>(it->second.begin(),
654 it->second.end()),
655 active_coeffs}});
656 }
657 }
658
659 if (integral->needs_facet_permutations)
660 needs_facet_permutations = true;
661 }
662 }
663 }
664
665 // Attach interior facet kernels
666 std::vector<std::int32_t> default_facets_int;
667 {
668 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
669 + integral_offsets[interior_facet],
670 num_integrals_type[interior_facet]);
671 auto sd = subdomains.find(IntegralType::interior_facet);
672 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
673 {
674 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
675
676 // Create indicator for interprocess facets
677 std::vector<std::int8_t> interprocess_marker;
678 if (num_integrals_type[interior_facet] > 0)
679 {
680 assert(topology->index_map(tdim - 1));
681 const std::vector<std::int32_t>& interprocess_facets
682 = topology->interprocess_facets();
683 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local()
684 + topology->index_map(tdim - 1)->num_ghosts();
685 interprocess_marker.resize(num_facets, 0);
686 std::ranges::for_each(interprocess_facets,
687 [&interprocess_marker](auto f)
688 { interprocess_marker[f] = 1; });
689 }
690
691 for (int i = 0; i < num_integrals_type[interior_facet]; ++i)
692 {
693 const int id = ids[i];
694 ufcx_integral* integral
695 = ufcx_form.form_integrals[integral_offsets[interior_facet] + i];
696 assert(integral);
697 check_geometry_hash(*integral, form_idx);
698
699 std::vector<int> active_coeffs;
700 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
701 {
702 if (integral->enabled_coefficients[j])
703 active_coeffs.push_back(j);
704 }
705
706 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
707 assert(k);
708
709 // Build list of entities to assembler over
710 auto f_to_c = topology->connectivity(tdim - 1, tdim);
711 assert(f_to_c);
712 auto c_to_f = topology->connectivity(tdim, tdim - 1);
713 assert(c_to_f);
714 if (id == -1)
715 {
716 // Default kernel, operates on all (owned) interior facets
717 assert(topology->index_map(tdim - 1));
718 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local();
719 default_facets_int.reserve(4 * num_facets);
720 for (std::int32_t f = 0; f < num_facets; ++f)
721 {
722 if (f_to_c->num_links(f) == 2)
723 {
724 std::array<std::int32_t, 4> pairs
725 = impl::get_cell_facet_pairs<2>(f, f_to_c->links(f), *c_to_f);
726 default_facets_int.insert(default_facets_int.end(), pairs.begin(),
727 pairs.end());
728 }
729 else if (interprocess_marker[f])
730 {
731 throw std::runtime_error(
732 "Cannot compute interior facet integral over interprocess "
733 "facet. Please use ghost mode shared facet when creating the "
734 "mesh");
735 }
736 }
737 integrals.insert({{IntegralType::interior_facet, i, form_idx},
738 {k, default_facets_int, active_coeffs}});
739 }
740 else if (sd != subdomains.end())
741 {
742 auto it = std::ranges::lower_bound(sd->second, id, std::less{},
743 [](auto& a) { return a.first; });
744 if (it != sd->second.end() and it->first == id)
745 {
746 integrals.insert({{IntegralType::interior_facet, i, form_idx},
747 {k,
748 std::vector<std::int32_t>(it->second.begin(),
749 it->second.end()),
750 active_coeffs}});
751 }
752 }
753
754 if (integral->needs_facet_permutations)
755 needs_facet_permutations = true;
756 }
757 }
758 }
759
760 // Attach vertex kernels
761 {
762 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
763 {
764 const ufcx_form& form = ufcx_forms[form_idx];
765
766 std::span<const int> ids(form.form_integral_ids
767 + integral_offsets[vertex],
768 num_integrals_type[vertex]);
769 auto sd = subdomains.find(IntegralType::vertex);
770 for (int i = 0; i < num_integrals_type[vertex]; ++i)
771 {
772 const int id = ids[i];
773 ufcx_integral* integral
774 = form.form_integrals[integral_offsets[vertex] + i];
775 assert(integral);
776 check_geometry_hash(*integral, form_idx);
777
778 std::vector<int> active_coeffs;
779 for (int j = 0; j < form.num_coefficients; ++j)
780 {
781 if (integral->enabled_coefficients[j])
782 active_coeffs.push_back(j);
783 }
784
785 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
786 assert(k);
787
788 // Build list of entities to assembler over
789 auto v_to_c = topology->connectivity(0, tdim);
790 assert(v_to_c);
791 auto c_to_v = topology->connectivity(tdim, 0);
792 assert(c_to_v);
793
794 // pack for a range of vertices a flattened list of cell index c_i and
795 // local vertex index l_i:
796 // [c_0, l_0, ..., c_n, l_n]
797 auto get_cells_and_vertices = [v_to_c, c_to_v](auto vertices_range)
798 {
799 std::vector<std::int32_t> cell_and_vertex;
800 cell_and_vertex.reserve(2 * vertices_range.size());
801 for (std::int32_t vertex : vertices_range)
802 {
803 std::array<std::int32_t, 2> pair = impl::get_cell_vertex_pairs<1>(
804 vertex, v_to_c->links(vertex), *c_to_v);
805
806 cell_and_vertex.insert(cell_and_vertex.end(), pair.begin(),
807 pair.end());
808 }
809 assert(cell_and_vertex.size() == 2 * vertices_range.size());
810 return cell_and_vertex;
811 };
812
813 if (id == -1)
814 {
815 // Default vertex kernel operates on all (owned) vertices
816 std::int32_t num_vertices = topology->index_map(0)->size_local();
817 std::vector<std::int32_t> cells_and_vertices = get_cells_and_vertices(
818 std::ranges::views::iota(0, num_vertices));
819
820 integrals.insert({{IntegralType::vertex, i, form_idx},
821 {k, cells_and_vertices, active_coeffs}});
822 }
823 else if (sd != subdomains.end())
824 {
825 // NOTE: This requires that pairs are sorted
826 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
827 [](auto& a) { return a.first; });
828 if (it != sd->second.end() and it->first == id)
829 {
830 integrals.insert({{IntegralType::vertex, i, form_idx},
831 {k,
832 std::vector<std::int32_t>(it->second.begin(),
833 it->second.end()),
834 active_coeffs}});
835 }
836 }
837 }
838 }
839 }
840
841 return Form<T, U>(spaces, std::move(integrals), mesh, coefficients, constants,
842 needs_facet_permutations, entity_maps);
843}
844
859template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
861 const ufcx_form& ufcx_form,
862 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
863 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
864 coefficients,
865 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
866 const std::map<
868 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
869 subdomains,
870 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
871 entity_maps,
872 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
873{
874 // Place coefficients in appropriate order
875 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
876 for (const std::string& name : get_coefficient_names(ufcx_form))
877 {
878 if (auto it = coefficients.find(name); it != coefficients.end())
879 coeff_map.push_back(it->second);
880 else
881 {
882 throw std::runtime_error("Form coefficient \"" + name
883 + "\" not provided.");
884 }
885 }
886
887 // Place constants in appropriate order
888 std::vector<std::shared_ptr<const Constant<T>>> const_map;
889 for (const std::string& name : get_constant_names(ufcx_form))
890 {
891 if (auto it = constants.find(name); it != constants.end())
892 const_map.push_back(it->second);
893 else
894 throw std::runtime_error("Form constant \"" + name + "\" not provided.");
895 }
896
897 return create_form_factory({ufcx_form}, spaces, coeff_map, const_map,
898 subdomains, entity_maps, mesh);
899}
900
919template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
921 ufcx_form* (*fptr)(),
922 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
923 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
924 coefficients,
925 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
926 const std::map<
928 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
929 subdomains,
930 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
931 entity_maps,
932 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
933{
934 ufcx_form* form = fptr();
935 Form<T, U> L = create_form<T, U>(*form, spaces, coefficients, constants,
936 subdomains, entity_maps, mesh);
937 std::free(form);
938 return L;
939}
940
942template <std::floating_point T>
944 std::shared_ptr<mesh::Mesh<T>> mesh,
945 std::shared_ptr<const fem::FiniteElement<T>> e,
946 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
947 reorder_fn
948 = nullptr)
949{
950 // TODO: check cell type of e (need to add method to fem::FiniteElement)
951 assert(e);
952 assert(mesh);
953 assert(mesh->topology());
954 if (e->cell_type() != mesh->topology()->cell_type())
955 throw std::runtime_error("Cell type of element and mesh must match.");
956
957 // Create element dof layout
959
960 // Create a dofmap
961 std::function<void(std::span<std::int32_t>, std::uint32_t)> permute_inv
962 = e->needs_dof_permutations() ? e->dof_permutation_fn(true, true)
963 : nullptr;
964 auto dofmap = std::make_shared<const DofMap>(create_dofmap(
965 mesh->comm(), layout, *mesh->topology(), permute_inv, reorder_fn));
966
967 return FunctionSpace(mesh, e, dofmap);
968}
969
971template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
973 const ufcx_expression& e,
974 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
975 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
976 std::shared_ptr<const FunctionSpace<U>> argument_space = nullptr)
977{
978 if (!coefficients.empty())
979 {
980 assert(coefficients.front());
981 assert(coefficients.front()->function_space());
982 std::shared_ptr<const mesh::Mesh<U>> mesh
983 = coefficients.front()->function_space()->mesh();
984 if (mesh->geometry().cmap().hash() != e.coordinate_element_hash)
985 {
986 throw std::runtime_error(
987 "Expression and mesh geometric maps do not match.");
988 }
989 }
990
991 if (e.rank > 0 and !argument_space)
992 {
993 throw std::runtime_error("Expression has Argument but no Argument "
994 "function space was provided.");
995 }
996
997 std::vector<U> X(e.points, e.points + e.num_points * e.entity_dimension);
998 std::array<std::size_t, 2> Xshape
999 = {static_cast<std::size_t>(e.num_points),
1000 static_cast<std::size_t>(e.entity_dimension)};
1001 std::vector<std::size_t> value_shape(e.value_shape,
1002 e.value_shape + e.num_components);
1003 std::function<void(T*, const T*, const T*, const scalar_value_t<T>*,
1004 const int*, const std::uint8_t*, void*)>
1005 tabulate_tensor = nullptr;
1006 if constexpr (std::is_same_v<T, float>)
1007 tabulate_tensor = e.tabulate_tensor_float32;
1008#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
1009 else if constexpr (std::is_same_v<T, std::complex<float>>)
1010 {
1011 tabulate_tensor = reinterpret_cast<void (*)(
1012 T*, const T*, const T*, const scalar_value_t<T>*, const int*,
1013 const unsigned char*, void*)>(e.tabulate_tensor_complex64);
1014 }
1015#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
1016 else if constexpr (std::is_same_v<T, double>)
1017 tabulate_tensor = e.tabulate_tensor_float64;
1018#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
1019 else if constexpr (std::is_same_v<T, std::complex<double>>)
1020 {
1021 tabulate_tensor = reinterpret_cast<void (*)(
1022 T*, const T*, const T*, const scalar_value_t<T>*, const int*,
1023 const unsigned char*, void*)>(e.tabulate_tensor_complex128);
1024 }
1025#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
1026 else
1027 throw std::runtime_error("Type not supported.");
1028
1029 assert(tabulate_tensor);
1030 return Expression(coefficients, constants, std::span<const U>(X), Xshape,
1031 tabulate_tensor, value_shape, argument_space);
1032}
1033
1036template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
1038 const ufcx_expression& e,
1039 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
1040 coefficients,
1041 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
1042 std::shared_ptr<const FunctionSpace<U>> argument_space = nullptr)
1043{
1044 // Place coefficients in appropriate order
1045 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
1046 std::vector<std::string> coefficient_names;
1047 coefficient_names.reserve(e.num_coefficients);
1048 for (int i = 0; i < e.num_coefficients; ++i)
1049 coefficient_names.push_back(e.coefficient_names[i]);
1050
1051 for (const std::string& name : coefficient_names)
1052 {
1053 if (auto it = coefficients.find(name); it != coefficients.end())
1054 coeff_map.push_back(it->second);
1055 else
1056 {
1057 throw std::runtime_error("Expression coefficient \"" + name
1058 + "\" not provided.");
1059 }
1060 }
1061
1062 // Place constants in appropriate order
1063 std::vector<std::shared_ptr<const Constant<T>>> const_map;
1064 std::vector<std::string> constant_names;
1065 constant_names.reserve(e.num_constants);
1066 for (int i = 0; i < e.num_constants; ++i)
1067 constant_names.push_back(e.constant_names[i]);
1068
1069 for (const std::string& name : constant_names)
1070 {
1071 if (auto it = constants.find(name); it != constants.end())
1072 const_map.push_back(it->second);
1073 else
1074 {
1075 throw std::runtime_error("Expression constant \"" + name
1076 + "\" not provided.");
1077 }
1078 }
1079
1080 return create_expression(e, coeff_map, const_map, argument_space);
1081}
1082
1083} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:26
Definition IndexMap.h:97
Timer for measuring and logging elapsed time durations.
Definition Timer.h:40
std::chrono::duration< double, Period > stop()
Stop timer and return elapsed time.
Definition Timer.h:92
Constant (in space) value which can be attached to a Form.
Definition Constant.h:22
Degree-of-freedom map.
Definition DofMap.h:73
Definition ElementDofLayout.h:31
An Expression represents a mathematical expression evaluated at a pre-defined points on a reference c...
Definition Expression.h:41
Model of a finite element.
Definition FiniteElement.h:57
const std::vector< std::shared_ptr< const FiniteElement< geometry_type > > > & sub_elements() const noexcept
Get subelements (if any)
Definition FiniteElement.cpp:396
const std::vector< std::vector< std::vector< int > > > & entity_dofs() const noexcept
Local DOFs associated with each sub-entity of the cell.
Definition FiniteElement.cpp:340
int num_sub_elements() const noexcept
Number of sub elements (for a mixed or blocked element).
Definition FiniteElement.cpp:383
const std::vector< std::vector< std::vector< int > > > & entity_closure_dofs() const noexcept
Local DOFs associated with the closure of each sub-entity of the cell.
Definition FiniteElement.cpp:347
int block_size() const noexcept
Block size of the finite element function space.
Definition FiniteElement.cpp:359
A representation of finite element variational forms.
Definition Form.h:116
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:34
Definition Function.h:47
This class provides a static adjacency list data structure.
Definition AdjacencyList.h:38
std::span< LinkData > links(std::size_t node)
Get the links (edges) for given node.
Definition AdjacencyList.h:156
Definition SparsityPattern.h:26
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:41
std::array< std::int32_t, 2 *num_cells > get_cell_vertex_pairs(std::int32_t v, std::span< const std::int32_t > cells, const graph::AdjacencyList< std::int32_t > &c_to_v)
Definition utils.h:104
std::array< std::int32_t, 2 *num_cells > get_cell_facet_pairs(std::int32_t f, std::span< const std::int32_t > cells, const graph::AdjacencyList< std::int32_t > &c_to_f)
Definition utils.h:74
Functions supporting mesh operations.
Miscellaneous classes, functions and types.
Definition dolfinx_common.h:8
void interior_facets(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over interior facets and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:28
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
Finite element method functionality.
Definition assemble_expression_impl.h:23
std::vector< std::string > get_constant_names(const ufcx_form &ufcx_form)
Get the name of each constant in a UFC form.
Definition utils.cpp:128
FunctionSpace< T > create_functionspace(std::shared_ptr< mesh::Mesh< T > > mesh, std::shared_ptr< const fem::FiniteElement< T > > e, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn=nullptr)
NEW Create a function space from a fem::FiniteElement.
Definition utils.h:943
Form< T, U > create_form_factory(const std::vector< std::reference_wrapper< const ufcx_form > > &ufcx_forms, const std::vector< std::shared_ptr< const FunctionSpace< U > > > &spaces, const std::vector< std::shared_ptr< const Function< T, U > > > &coefficients, const std::vector< std::shared_ptr< const Constant< T > > > &constants, const std::map< IntegralType, std::vector< std::pair< std::int32_t, std::span< const std::int32_t > > > > &subdomains, const std::vector< std::reference_wrapper< const mesh::EntityMap > > &entity_maps, std::shared_ptr< const mesh::Mesh< U > > mesh=nullptr)
Create a Form from UFCx input with coefficients and constants passed in the required order.
Definition utils.h:409
std::vector< std::string > get_coefficient_names(const ufcx_form &ufcx_form)
Definition utils.cpp:121
std::vector< std::vector< std::array< std::shared_ptr< const FunctionSpace< U > >, 2 > > > extract_function_spaces(const std::vector< std::vector< const Form< T, U > * > > &a)
Extract test (0) and trial (1) function spaces pairs for each bilinear form for a rectangular array o...
Definition utils.h:172
Expression< T, U > create_expression(const ufcx_expression &e, const std::vector< std::shared_ptr< const Function< T, U > > > &coefficients, const std::vector< std::shared_ptr< const Constant< T > > > &constants, std::shared_ptr< const FunctionSpace< U > > argument_space=nullptr)
Create Expression from UFC.
Definition utils.h:972
std::vector< std::int32_t > compute_integration_domains(IntegralType integral_type, const mesh::Topology &topology, std::span< const std::int32_t > entities)
Given an integral type and a set of entities, computes and return data for the entities that should b...
Definition utils.cpp:136
Form< T, U > create_form(const ufcx_form &ufcx_form, const std::vector< std::shared_ptr< const FunctionSpace< U > > > &spaces, const std::map< std::string, std::shared_ptr< const Function< T, U > > > &coefficients, const std::map< std::string, std::shared_ptr< const Constant< T > > > &constants, const std::map< IntegralType, std::vector< std::pair< std::int32_t, std::span< const std::int32_t > > > > &subdomains, const std::vector< std::reference_wrapper< const mesh::EntityMap > > &entity_maps, std::shared_ptr< const mesh::Mesh< U > > mesh=nullptr)
Create a Form from UFC input with coefficients and constants resolved by name.
Definition utils.h:860
ElementDofLayout create_element_dof_layout(const fem::FiniteElement< T > &element, const std::vector< int > &parent_map={})
Create an ElementDofLayout from a FiniteElement.
Definition utils.h:313
IntegralType
Type of integral.
Definition Form.h:39
@ vertex
Vertex.
Definition Form.h:43
@ interior_facet
Interior facet.
Definition Form.h:42
@ cell
Cell.
Definition Form.h:40
@ exterior_facet
Exterior facet.
Definition Form.h:41
la::SparsityPattern create_sparsity_pattern(const Form< T, U > &a)
Create a sparsity pattern for a given form.
Definition utils.h:197
DofMap create_dofmap(MPI_Comm comm, const ElementDofLayout &layout, mesh::Topology &topology, const std::function< void(std::span< std::int32_t >, std::uint32_t)> &permute_inv, const std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> &reorder_fn)
Create a dof map on mesh.
Definition utils.cpp:32
std::vector< DofMap > create_dofmaps(MPI_Comm comm, const std::vector< ElementDofLayout > &layouts, mesh::Topology &topology, const std::function< void(std::span< std::int32_t >, std::uint32_t)> &permute_inv, const std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> &reorder_fn)
Create a set of dofmaps on a given topology.
Definition utils.cpp:70
FunctionSpace(U mesh, V element, W dofmap) -> FunctionSpace< typename std::remove_cvref< typename U::element_type >::type::geometry_type::value_type >
Type deduction.
void build_sparsity_pattern(la::SparsityPattern &pattern, const Form< T, U > &a)
Build a sparsity pattern for a given form.
Definition utils.h:225
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
std::vector< std::int32_t > exterior_facet_indices(const Topology &topology, int facet_type_idx)
Compute the indices of all exterior facets that are owned by the caller.
Definition utils.cpp:59
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:51