DOLFINx 0.11.0.0
DOLFINx C++
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 <numeric>
35#include <optional>
36#include <ranges>
37#include <set>
38#include <span>
39#include <stdexcept>
40#include <string>
41#include <tuple>
42#include <type_traits>
43#include <ufcx.h>
44#include <utility>
45#include <vector>
46
49namespace basix
50{
51template <std::floating_point T>
52class FiniteElement;
53}
54
55namespace dolfinx::common
56{
57class IndexMap;
58}
59
60namespace dolfinx::fem
61{
62template <dolfinx::scalar T, std::floating_point U>
63class Expression;
64
65namespace impl
66{
73template <int num_cells>
74std::array<std::int32_t, 2 * num_cells>
75get_cell_facet_pairs(std::int32_t f, std::span<const std::int32_t> cells,
77{
78 // Loop over cells sharing facet
79 assert(cells.size() == num_cells);
80 std::array<std::int32_t, 2 * num_cells> cell_local_facet_pairs;
81 for (int c = 0; c < num_cells; ++c)
82 {
83 // Get local index of facet with respect to the cell
84 std::int32_t cell = cells[c];
85 auto cell_facets = c_to_f.links(cell);
86 auto facet_it = std::find(cell_facets.begin(), cell_facets.end(), f);
87 assert(facet_it != cell_facets.end());
88 int local_f = std::distance(cell_facets.begin(), facet_it);
89 cell_local_facet_pairs[2 * c] = cell;
90 cell_local_facet_pairs[2 * c + 1] = local_f;
91 }
92
93 return cell_local_facet_pairs;
94}
95
103template <int num_cells>
104std::array<std::int32_t, 2 * num_cells>
105get_cell_entity_pairs(std::int32_t e, std::span<const std::int32_t> cells,
107{
108 static_assert(num_cells == 1); // Patch assembly not supported.
109
110 assert(cells.size() > 0);
111
112 // Use first cell for assembly over by default
113 std::int32_t cell = cells[0];
114
115 // Find local index of entity within cell
116 auto cell_entities = c_to_e.links(cell);
117 auto it = std::ranges::find(cell_entities, e);
118 assert(it != cell_entities.end());
119 std::int32_t local_index = std::distance(cell_entities.begin(), it);
120
121 return {cell, local_index};
122}
123
124} // namespace impl
125
159std::vector<std::int32_t>
161 const mesh::Topology& topology,
162 std::span<const std::int32_t> entities);
163
171template <dolfinx::scalar T, std::floating_point U>
172std::vector<std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
173extract_function_spaces(const std::vector<std::vector<const Form<T, U>*>>& a)
174{
175 std::vector<
176 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>>
177 spaces(
178 a.size(),
179 std::vector<std::array<std::shared_ptr<const FunctionSpace<U>>, 2>>(
180 a.front().size()));
181 for (std::size_t i = 0; i < a.size(); ++i)
182 {
183 for (std::size_t j = 0; j < a[i].size(); ++j)
184 {
185 if (const Form<T, U>* form = a[i][j]; form)
186 spaces[i][j] = {form->function_spaces()[0], form->function_spaces()[1]};
187 }
188 }
189 return spaces;
190}
191
197template <dolfinx::scalar T, std::floating_point U>
199{
200 std::shared_ptr mesh = a.mesh();
201 assert(mesh);
202
203 // Get index maps and block sizes from the DOF maps. Note that in
204 // mixed-topology meshes, despite there being multiple DOF maps, the
205 // index maps and block sizes are the same.
206 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
207 *a.function_spaces().at(0)->dofmaps(0),
208 *a.function_spaces().at(1)->dofmaps(0)};
209
210 const std::array index_maps{dofmaps[0].get().index_map,
211 dofmaps[1].get().index_map};
212 const std::array bs
213 = {dofmaps[0].get().index_map_bs(), dofmaps[1].get().index_map_bs()};
214
215 la::SparsityPattern pattern(mesh->comm(), index_maps, bs);
216 build_sparsity_pattern(pattern, a);
217 return pattern;
218}
219
225template <dolfinx::scalar T, std::floating_point U>
227{
228 if (a.rank() != 2)
229 {
230 throw std::runtime_error(
231 "Cannot create sparsity pattern. Form is not a bilinear.");
232 }
233
234 std::shared_ptr mesh = a.mesh();
235 assert(mesh);
236 std::shared_ptr mesh0 = a.function_spaces().at(0)->mesh();
237 assert(mesh0);
238 std::shared_ptr mesh1 = a.function_spaces().at(1)->mesh();
239 assert(mesh1);
240
241 const std::set<IntegralType> types = a.integral_types();
242 if (types.find(IntegralType::interior_facet) != types.end()
243 or types.find(IntegralType::exterior_facet) != types.end())
244 {
245 // FIXME: cleanup these calls? Some of the happen internally again.
246 int tdim = mesh->topology()->dim();
247 mesh->topology_mutable()->create_entities(tdim - 1);
248 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
249 }
250
251 common::Timer t0("Build sparsity");
252
253 auto extract_cells = [](std::span<const std::int32_t> facets)
254 {
255 assert(facets.size() % 2 == 0);
256 std::vector<std::int32_t> cells;
257 cells.reserve(facets.size() / 2);
258 for (std::size_t i = 0; i < facets.size(); i += 2)
259 cells.push_back(facets[i]);
260 return cells;
261 };
262
263 const int num_cell_types = mesh->topology()->cell_types().size();
264 for (int cell_type_idx = 0; cell_type_idx < num_cell_types; ++cell_type_idx)
265 {
266 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps{
267 *a.function_spaces().at(0)->dofmaps(cell_type_idx),
268 *a.function_spaces().at(1)->dofmaps(cell_type_idx)};
269
270 // Create and build sparsity pattern
271 for (auto type : types)
272 {
273 switch (type)
274 {
276 for (int i = 0; i < a.num_integrals(type, cell_type_idx); ++i)
277 {
279 pattern,
280 std::pair{a.domain_arg(type, 0, i, cell_type_idx),
281 a.domain_arg(type, 1, i, cell_type_idx)},
282 {{dofmaps[0], dofmaps[1]}});
283 }
284 break;
286 for (int i = 0; i < a.num_integrals(type, cell_type_idx); ++i)
287 {
289 pattern,
290 {extract_cells(a.domain_arg(type, 0, i, 0)),
291 extract_cells(a.domain_arg(type, 1, i, 0))},
292 {{dofmaps[0], dofmaps[1]}});
293 }
294 break;
298 for (int i = 0; i < a.num_integrals(type, cell_type_idx); ++i)
299 {
301 pattern,
302 std::pair{extract_cells(a.domain_arg(type, 0, i, 0)),
303 extract_cells(a.domain_arg(type, 1, i, 0))},
304 {{dofmaps[0], dofmaps[1]}});
305 }
306 break;
307 default:
308 throw std::runtime_error("Unsupported integral type");
309 }
310 }
311 }
312
313 t0.stop();
314}
315
317template <std::floating_point T>
319 const std::vector<int>& parent_map
320 = {})
321{
322 // Create subdofmaps and compute offset
323 std::vector<int> offsets(1, 0);
324 std::vector<dolfinx::fem::ElementDofLayout> sub_doflayout;
325 int bs = element.block_size();
326 for (int i = 0; i < element.num_sub_elements(); ++i)
327 {
328 // The ith sub-element. For mixed elements this is subelements()[i]. For
329 // blocked elements, the sub-element will always be the same, so we'll use
330 // sub_elements()[0]
331 std::shared_ptr<const fem::FiniteElement<T>> sub_e
332 = element.sub_elements()[bs > 1 ? 0 : i];
333
334 // In a mixed element DOFs are ordered element by element, so the offset to
335 // the next sub-element is sub_e->space_dimension(). Blocked elements use
336 // xxyyzz ordering, so the offset to the next sub-element is 1
337
338 std::vector<int> parent_map_sub(sub_e->space_dimension(), offsets.back());
339 for (std::size_t j = 0; j < parent_map_sub.size(); ++j)
340 parent_map_sub[j] += bs * j;
341 offsets.push_back(offsets.back() + (bs > 1 ? 1 : sub_e->space_dimension()));
342 sub_doflayout.push_back(
343 dolfinx::fem::create_element_dof_layout(*sub_e, parent_map_sub));
344 }
345
346 return ElementDofLayout(bs, element.entity_dofs(),
347 element.entity_closure_dofs(), parent_map,
348 sub_doflayout);
349}
350
359DofMap
360create_dofmap(MPI_Comm comm, const ElementDofLayout& layout,
361 mesh::Topology& topology,
362 const std::function<void(std::span<std::int32_t>, std::uint32_t)>&
363 permute_inv,
364 const std::function<std::vector<int>(
365 const graph::AdjacencyList<std::int32_t>&)>& reorder_fn);
366
377std::vector<DofMap> create_dofmaps(
378 MPI_Comm comm, const std::vector<ElementDofLayout>& layouts,
379 mesh::Topology& topology,
380 const std::function<void(std::span<std::int32_t>, std::uint32_t)>&
381 permute_inv,
382 const std::function<std::vector<int>(
383 const graph::AdjacencyList<std::int32_t>&)>& reorder_fn);
384
388std::vector<std::string> get_coefficient_names(const ufcx_form& ufcx_form);
389
393std::vector<std::string> get_constant_names(const ufcx_form& ufcx_form);
394
413template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
415 const std::vector<std::reference_wrapper<const ufcx_form>>& ufcx_forms,
416 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
417 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
418 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
419 const std::map<
421 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
422 subdomains,
423 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
424 entity_maps,
425 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
426{
427 for (const ufcx_form& ufcx_form : ufcx_forms)
428 {
429 if (ufcx_form.rank != (int)spaces.size())
430 throw std::runtime_error("Wrong number of argument spaces for Form.");
431 if (ufcx_form.num_coefficients != (int)coefficients.size())
432 {
433 throw std::runtime_error("Mismatch between number of expected and "
434 "provided Form coefficients.");
435 }
436
437 // Check Constants for rank and size consistency
438 if (ufcx_form.num_constants != (int)constants.size())
439 {
440 throw std::runtime_error(std::format(
441 "Mismatch between number of expected and "
442 "provided Form Constants. Expected {} constants, but got {}.",
443 ufcx_form.num_constants, constants.size()));
444 }
445 for (std::size_t c = 0; c < constants.size(); ++c)
446 {
447 if (ufcx_form.constant_ranks[c] != (int)constants[c]->shape.size())
448 {
449 throw std::runtime_error(std::format(
450 "Mismatch between expected and actual rank of "
451 "Form Constant. Rank of Constant {} should be {}, but got rank {}.",
452 c, ufcx_form.constant_ranks[c], constants[c]->shape.size()));
453 }
454 if (!std::equal(constants[c]->shape.begin(), constants[c]->shape.end(),
455 ufcx_form.constant_shapes[c]))
456 {
457 throw std::runtime_error(
458 std::format("Mismatch between expected and actual shape of Form "
459 "Constant for Constant {}.",
460 c));
461 }
462 }
463 }
464
465 // Check argument function spaces
466 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
467 {
468 for (std::size_t i = 0; i < spaces.size(); ++i)
469 {
470 assert(spaces[i]->elements(form_idx));
471 if (auto element_hash
472 = ufcx_forms[form_idx].get().finite_element_hashes[i];
473 element_hash != 0
474 and element_hash
475 != spaces[i]->elements(form_idx)->basix_element().hash())
476 {
477 throw std::runtime_error(
478 "Cannot create form. Elements are different to "
479 "those used to compile the form.");
480 }
481 }
482 }
483
484 // Extract mesh from FunctionSpace, and check they are the same
485 if (!mesh and !spaces.empty())
486 mesh = spaces.front()->mesh();
487 if (!mesh)
488 throw std::runtime_error("No mesh could be associated with the Form.");
489
490 auto topology = mesh->topology();
491 assert(topology);
492 const int tdim = topology->dim();
493
494 // NOTE: This assumes all forms in mixed-topology meshes have the same
495 // integral offsets. Since the UFL forms for each type of cell should be
496 // the same, I think this assumption is OK.
497 const int* integral_offsets = ufcx_forms[0].get().form_integral_offsets;
498 std::array<int, 5> num_integrals_type;
499 for (std::size_t i = 0; i < num_integrals_type.size(); ++i)
500 num_integrals_type[i] = integral_offsets[i + 1] - integral_offsets[i];
501
502 // Create vertices, if required
503 if (num_integrals_type[vertex] > 0)
504 {
505 mesh->topology_mutable()->create_connectivity(0, tdim);
506 mesh->topology_mutable()->create_connectivity(tdim, 0);
507 }
508
509 // Create facets, if required
510 // NOTE: exterior_facet and interior_facet is declared in ufcx.h
511 if (num_integrals_type[exterior_facet] > 0
512 or num_integrals_type[interior_facet] > 0)
513 {
514 mesh->topology_mutable()->create_entities(tdim - 1);
515 mesh->topology_mutable()->create_connectivity(tdim - 1, tdim);
516 mesh->topology_mutable()->create_connectivity(tdim, tdim - 1);
517 }
518
519 // Create ridges, if required
520 if (num_integrals_type[ridge] > 0)
521 {
522 mesh->topology_mutable()->create_entities(tdim - 2);
523 mesh->topology_mutable()->create_connectivity(tdim - 2, tdim);
524 mesh->topology_mutable()->create_connectivity(tdim, tdim - 2);
525 }
526
527 // Get list of integral IDs, and load tabulate tensor into memory for
528 // each
529 std::map<std::tuple<IntegralType, int, int>, integral_data<T, U>> integrals;
530
531 auto check_geometry_hash
532 = [&geo = mesh->geometry()](const ufcx_integral& integral,
533 std::size_t cell_idx)
534 {
535 if (integral.coordinate_element_hash != geo.cmap(cell_idx).hash())
536 {
537 throw std::runtime_error(
538 "Generated integral geometry element does not match mesh geometry: "
539 + std::to_string(integral.coordinate_element_hash) + ", "
540 + std::to_string(geo.cmap(cell_idx).hash()));
541 }
542 };
543
544 // Attach cell kernels
545 bool needs_facet_permutations = false;
546 {
547 std::vector<std::int32_t> default_cells;
548 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
549 + integral_offsets[cell],
550 num_integrals_type[cell]);
551 auto sd = subdomains.find(IntegralType::cell);
552 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
553 {
554 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
555 for (int i = 0; i < num_integrals_type[cell]; ++i)
556 {
557 const int id = ids[i];
558 ufcx_integral* integral
559 = ufcx_form.form_integrals[integral_offsets[cell] + i];
560 assert(integral);
561 check_geometry_hash(*integral, form_idx);
562
563 // Build list of active coefficients
564 std::vector<int> active_coeffs;
565 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
566 {
567 if (integral->enabled_coefficients[j])
568 active_coeffs.push_back(j);
569 }
570
571 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
572 if (!k)
573 {
574 throw std::runtime_error(
575 "UFCx kernel function is NULL. Check requested types.");
576 }
577
578 // Build list of entities to assemble over
579 if (id == -1)
580 {
581 // Default kernel, operates on all (owned) cells
582 assert(topology->index_maps(tdim).at(form_idx));
583 default_cells.resize(
584 topology->index_maps(tdim).at(form_idx)->size_local(), 0);
585 std::iota(default_cells.begin(), default_cells.end(), 0);
586 integrals.insert({{IntegralType::cell, i, form_idx},
587 {k, default_cells, active_coeffs}});
588 }
589 else if (sd != subdomains.end())
590 {
591 // NOTE: This requires that pairs are sorted
592 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
593 [](auto& a) { return a.first; });
594 if (it != sd->second.end() and it->first == id)
595 {
596 integrals.insert({{IntegralType::cell, i, form_idx},
597 {k,
598 std::vector<std::int32_t>(it->second.begin(),
599 it->second.end()),
600 active_coeffs}});
601 }
602 }
603
604 if (integral->needs_facet_permutations)
605 needs_facet_permutations = true;
606 }
607 }
608 }
609
610 // Attach interior facet kernels
611 {
612 std::vector<std::int32_t> default_facets_int;
613 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
614 + integral_offsets[interior_facet],
615 num_integrals_type[interior_facet]);
616 auto sd = subdomains.find(IntegralType::interior_facet);
617 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
618 {
619 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
620
621 // Create indicator for interprocess facets
622 std::vector<std::int8_t> interprocess_marker;
623 if (num_integrals_type[interior_facet] > 0)
624 {
625 assert(topology->index_map(tdim - 1));
626 const std::vector<std::int32_t>& interprocess_facets
627 = topology->interprocess_facets();
628 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local()
629 + topology->index_map(tdim - 1)->num_ghosts();
630 interprocess_marker.resize(num_facets, 0);
631 std::ranges::for_each(interprocess_facets,
632 [&interprocess_marker](auto f)
633 { interprocess_marker[f] = 1; });
634 }
635
636 for (int i = 0; i < num_integrals_type[interior_facet]; ++i)
637 {
638 const int id = ids[i];
639 ufcx_integral* integral
640 = ufcx_form.form_integrals[integral_offsets[interior_facet] + i];
641 assert(integral);
642 check_geometry_hash(*integral, form_idx);
643
644 std::vector<int> active_coeffs;
645 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
646 {
647 if (integral->enabled_coefficients[j])
648 active_coeffs.push_back(j);
649 }
650
651 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
652 assert(k);
653
654 // Build list of entities to assembler over
655 auto f_to_c = topology->connectivity(tdim - 1, tdim);
656 assert(f_to_c);
657 auto c_to_f = topology->connectivity(tdim, tdim - 1);
658 assert(c_to_f);
659 if (id == -1)
660 {
661 // Default kernel, operates on all (owned) interior facets
662 assert(topology->index_map(tdim - 1));
663 std::int32_t num_facets = topology->index_map(tdim - 1)->size_local();
664 default_facets_int.reserve(4 * num_facets);
665 for (std::int32_t f = 0; f < num_facets; ++f)
666 {
667 if (f_to_c->num_links(f) == 2)
668 {
669 std::array<std::int32_t, 4> pairs
670 = impl::get_cell_facet_pairs<2>(f, f_to_c->links(f), *c_to_f);
671 default_facets_int.insert(default_facets_int.end(), pairs.begin(),
672 pairs.end());
673 }
674 else if (interprocess_marker[f])
675 {
676 throw std::runtime_error(
677 "Cannot compute interior facet integral over interprocess "
678 "facet. Please use ghost mode shared facet when creating the "
679 "mesh");
680 }
681 }
682 integrals.insert({{IntegralType::interior_facet, i, form_idx},
683 {k, default_facets_int, active_coeffs}});
684 }
685 else if (sd != subdomains.end())
686 {
687 auto it = std::ranges::lower_bound(sd->second, id, std::less{},
688 [](auto& a) { return a.first; });
689 if (it != sd->second.end() and it->first == id)
690 {
691 integrals.insert({{IntegralType::interior_facet, i, form_idx},
692 {k,
693 std::vector<std::int32_t>(it->second.begin(),
694 it->second.end()),
695 active_coeffs}});
696 }
697 }
698
699 if (integral->needs_facet_permutations)
700 needs_facet_permutations = true;
701 }
702 }
703 }
704
705 // Attach exterior entity integrals
706 {
709 {
710 std::size_t dim;
711 switch (itg_type)
712 {
714 {
715 dim = tdim - 1;
716 break;
717 }
719 {
720 dim = tdim - 2;
721 break;
722 }
724 {
725 dim = 0;
726 break;
727 }
728 default:
729 throw std::runtime_error("Unsupported integral type");
730 }
731
732 const std::function<std::vector<std::int32_t>(const mesh::Topology&,
734 get_default_integration_entities
735 = [dim](const mesh::Topology& topology, IntegralType itg_type)
736 {
737 if (itg_type == IntegralType::exterior_facet)
738 {
739 // Integrate over all owned exterior facets
740 return mesh::exterior_facet_indices(topology);
741 }
742 else
743 {
744 // Integrate over all owned entities
745 std::int32_t num_entities = topology.index_map(dim)->size_local();
746 std::vector<std::int32_t> entities(num_entities);
747 std::iota(entities.begin(), entities.end(), 0);
748 return entities;
749 }
750 };
751
752 std::vector<std::int32_t> default_entities_ext;
753
754 std::span<const int> ids(ufcx_forms[0].get().form_integral_ids
755 + integral_offsets[(std::int8_t)itg_type],
756 num_integrals_type[(std::int8_t)itg_type]);
757 auto sd = subdomains.find(itg_type);
758 for (std::size_t form_idx = 0; form_idx < ufcx_forms.size(); ++form_idx)
759 {
760 const ufcx_form& ufcx_form = ufcx_forms[form_idx];
761 for (int i = 0; i < num_integrals_type[(std::int8_t)itg_type]; ++i)
762 {
763 const int id = ids[i];
764 ufcx_integral* integral
765 = ufcx_form.form_integrals[integral_offsets[(std::int8_t)itg_type]
766 + i];
767 assert(integral);
768 check_geometry_hash(*integral, form_idx);
769
770 std::vector<int> active_coeffs;
771 for (int j = 0; j < ufcx_form.num_coefficients; ++j)
772 {
773 if (integral->enabled_coefficients[j])
774 active_coeffs.push_back(j);
775 }
776
777 impl::kernel_t<T, U> k = impl::extract_kernel<T>(integral);
778
779 // Build list of entities to assembler over
780 auto e_to_c = topology->connectivity(dim, tdim);
781 assert(e_to_c);
782 auto c_to_e = topology->connectivity(tdim, dim);
783 assert(c_to_e);
784 if (id == -1)
785 {
786 std::vector default_entities
787 = get_default_integration_entities(*topology, itg_type);
788 // Default kernel
789 default_entities_ext.reserve(2 * default_entities.size());
790 for (std::int32_t e : default_entities)
791 {
792 // There will only be one pair for an exterior facet integral
793 std::array<std::int32_t, 2> pair = impl::get_cell_entity_pairs<1>(
794 e, e_to_c->links(e), *c_to_e);
795 default_entities_ext.insert(default_entities_ext.end(),
796 pair.begin(), pair.end());
797 }
798 integrals.insert({{itg_type, i, form_idx},
799 {k, default_entities_ext, active_coeffs}});
800 }
801 else if (sd != subdomains.end())
802 {
803 // NOTE: This requires that pairs are sorted
804 auto it = std::ranges::lower_bound(sd->second, id, std::less<>{},
805 [](auto& a) { return a.first; });
806 if (it != sd->second.end() and it->first == id)
807 {
808 integrals.insert({{itg_type, i, form_idx},
809 {k,
810 std::vector<std::int32_t>(it->second.begin(),
811 it->second.end()),
812 active_coeffs}});
813 }
814 }
815
816 if (integral->needs_facet_permutations)
817 needs_facet_permutations = true;
818 }
819 }
820 }
821 }
822
823 return Form<T, U>(spaces, std::move(integrals), mesh, coefficients, constants,
824 needs_facet_permutations, entity_maps);
825}
826
841template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
843 const ufcx_form& ufcx_form,
844 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
845 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
846 coefficients,
847 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
848 const std::map<
850 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
851 subdomains,
852 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
853 entity_maps,
854 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
855{
856 // Place coefficients in appropriate order
857 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
858 for (const std::string& name : get_coefficient_names(ufcx_form))
859 {
860 if (auto it = coefficients.find(name); it != coefficients.end())
861 coeff_map.push_back(it->second);
862 else
863 {
864 throw std::runtime_error("Form coefficient \"" + name
865 + "\" not provided.");
866 }
867 }
868
869 // Place constants in appropriate order
870 std::vector<std::shared_ptr<const Constant<T>>> const_map;
871 for (const std::string& name : get_constant_names(ufcx_form))
872 {
873 if (auto it = constants.find(name); it != constants.end())
874 const_map.push_back(it->second);
875 else
876 throw std::runtime_error("Form constant \"" + name + "\" not provided.");
877 }
878
879 return create_form_factory({ufcx_form}, spaces, coeff_map, const_map,
880 subdomains, entity_maps, mesh);
881}
882
901template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
903 ufcx_form* (*fptr)(),
904 const std::vector<std::shared_ptr<const FunctionSpace<U>>>& spaces,
905 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
906 coefficients,
907 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
908 const std::map<
910 std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>&
911 subdomains,
912 const std::vector<std::reference_wrapper<const mesh::EntityMap>>&
913 entity_maps,
914 std::shared_ptr<const mesh::Mesh<U>> mesh = nullptr)
915{
916 ufcx_form* form = fptr();
917 Form<T, U> L = create_form<T, U>(*form, spaces, coefficients, constants,
918 subdomains, entity_maps, mesh);
919 std::free(form);
920 return L;
921}
922
924template <std::floating_point T>
926 std::shared_ptr<mesh::Mesh<T>> mesh,
927 std::shared_ptr<const fem::FiniteElement<T>> e,
928 std::function<std::vector<int>(const graph::AdjacencyList<std::int32_t>&)>
929 reorder_fn = nullptr)
930{
931 // TODO: check cell type of e (need to add method to fem::FiniteElement)
932 assert(e);
933 assert(mesh);
934 assert(mesh->topology());
935 if (e->cell_type() != mesh->topology()->cell_type())
936 throw std::runtime_error("Cell type of element and mesh must match.");
937
938 // Create element dof layout
940
941 // Create a dofmap
942 std::function<void(std::span<std::int32_t>, std::uint32_t)> permute_inv
943 = e->needs_dof_permutations() ? e->dof_permutation_fn(true, true)
944 : nullptr;
945 auto dofmap = std::make_shared<const DofMap>(create_dofmap(
946 mesh->comm(), layout, *mesh->topology(), permute_inv, reorder_fn));
947
948 return FunctionSpace(mesh, e, dofmap);
949}
950
952template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
954 const ufcx_expression& e,
955 const std::vector<std::shared_ptr<const Function<T, U>>>& coefficients,
956 const std::vector<std::shared_ptr<const Constant<T>>>& constants,
957 std::shared_ptr<const FunctionSpace<U>> argument_space = nullptr)
958{
959 if (!coefficients.empty())
960 {
961 assert(coefficients.front());
962 assert(coefficients.front()->function_space());
963 std::shared_ptr<const mesh::Mesh<U>> mesh
964 = coefficients.front()->function_space()->mesh();
965 if (mesh->geometry().cmap().hash() != e.coordinate_element_hash)
966 {
967 throw std::runtime_error(
968 "Expression and mesh geometric maps do not match.");
969 }
970 }
971
972 if (e.rank > 0 and !argument_space)
973 {
974 throw std::runtime_error("Expression has Argument but no Argument "
975 "function space was provided.");
976 }
977
978 std::vector<U> X(e.points, e.points + e.num_points * e.entity_dimension);
979 std::array<std::size_t, 2> Xshape
980 = {static_cast<std::size_t>(e.num_points),
981 static_cast<std::size_t>(e.entity_dimension)};
982 std::vector<std::size_t> value_shape(e.value_shape,
983 e.value_shape + e.num_components);
984 std::function<void(T*, const T*, const T*, const scalar_value_t<T>*,
985 const int*, const std::uint8_t*, void*)>
986 tabulate_tensor = nullptr;
987 if constexpr (std::is_same_v<T, float>)
988 tabulate_tensor = e.tabulate_tensor_float32;
989#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
990 else if constexpr (std::is_same_v<T, std::complex<float>>)
991 {
992 tabulate_tensor = reinterpret_cast<void (*)(
993 T*, const T*, const T*, const scalar_value_t<T>*, const int*,
994 const unsigned char*, void*)>(e.tabulate_tensor_complex64);
995 }
996#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
997 else if constexpr (std::is_same_v<T, double>)
998 tabulate_tensor = e.tabulate_tensor_float64;
999#ifndef DOLFINX_NO_STDC_COMPLEX_KERNELS
1000 else if constexpr (std::is_same_v<T, std::complex<double>>)
1001 {
1002 tabulate_tensor = reinterpret_cast<void (*)(
1003 T*, const T*, const T*, const scalar_value_t<T>*, const int*,
1004 const unsigned char*, void*)>(e.tabulate_tensor_complex128);
1005 }
1006#endif // DOLFINX_NO_STDC_COMPLEX_KERNELS
1007 else
1008 throw std::runtime_error("Type not supported.");
1009
1010 assert(tabulate_tensor);
1011 return Expression(coefficients, constants, std::span<const U>(X), Xshape,
1012 tabulate_tensor, value_shape, argument_space);
1013}
1014
1017template <dolfinx::scalar T, std::floating_point U = scalar_value_t<T>>
1019 const ufcx_expression& e,
1020 const std::map<std::string, std::shared_ptr<const Function<T, U>>>&
1021 coefficients,
1022 const std::map<std::string, std::shared_ptr<const Constant<T>>>& constants,
1023 std::shared_ptr<const FunctionSpace<U>> argument_space = nullptr)
1024{
1025 // Place coefficients in appropriate order
1026 std::vector<std::shared_ptr<const Function<T, U>>> coeff_map;
1027 std::vector<std::string> coefficient_names;
1028 coefficient_names.reserve(e.num_coefficients);
1029 for (int i = 0; i < e.num_coefficients; ++i)
1030 coefficient_names.push_back(e.coefficient_names[i]);
1031
1032 for (const std::string& name : coefficient_names)
1033 {
1034 if (auto it = coefficients.find(name); it != coefficients.end())
1035 coeff_map.push_back(it->second);
1036 else
1037 {
1038 throw std::runtime_error("Expression coefficient \"" + name
1039 + "\" not provided.");
1040 }
1041 }
1042
1043 // Place constants in appropriate order
1044 std::vector<std::shared_ptr<const Constant<T>>> const_map;
1045 std::vector<std::string> constant_names;
1046 constant_names.reserve(e.num_constants);
1047 for (int i = 0; i < e.num_constants; ++i)
1048 constant_names.push_back(e.constant_names[i]);
1049
1050 for (const std::string& name : constant_names)
1051 {
1052 if (auto it = constants.find(name); it != constants.end())
1053 const_map.push_back(it->second);
1054 else
1055 {
1056 throw std::runtime_error("Expression constant \"" + name
1057 + "\" not provided.");
1058 }
1059 }
1060
1061 return create_expression(e, coeff_map, const_map, argument_space);
1062}
1063
1078template <std::floating_point T>
1080 std::shared_ptr<mesh::Mesh<T>> mesh, const CoordinateElement<T>& new_cmap,
1081 const std::function<std::vector<int>(
1082 const graph::AdjacencyList<std::int32_t>&)>& reorder_fn = nullptr)
1083{
1084 assert(mesh);
1085 const CoordinateElement<T>& old_cmap = mesh->geometry().cmap();
1086 if (new_cmap.cell_shape() != old_cmap.cell_shape())
1087 {
1088 throw std::runtime_error(
1089 "Cell shape of new coordinate element must match input mesh.");
1090 }
1091
1092 const int gdim = mesh->geometry().dim();
1093
1094 // Build a vector-valued Lagrange FiniteElement from the coordinate element.
1095 basix::FiniteElement<T> b_element = basix::create_element<T>(
1096 basix::element::family::P,
1097 mesh::cell_type_to_basix_type(new_cmap.cell_shape()), new_cmap.degree(),
1098 new_cmap.variant(), basix::element::dpc_variant::unset, false);
1099 auto element = std::make_shared<const FiniteElement<T>>(
1100 b_element, std::vector<std::size_t>{static_cast<std::size_t>(gdim)});
1101
1102 FunctionSpace<T> V = create_functionspace(mesh, element, reorder_fn);
1103
1104 // Tabulate physical coordinates of the new geometry dofs.
1105 std::vector<T> x_new = V.tabulate_dof_coordinates(false);
1106
1107 // Pull the geometry dofmap and index map from V.
1108 std::shared_ptr<const DofMap> dm = V.dofmap();
1109 assert(dm);
1110 std::shared_ptr<const common::IndexMap> new_imap = dm->index_map;
1111 assert(new_imap);
1112
1113 auto map_view = dm->map();
1114 std::vector<std::int32_t> dofmap_flat(
1115 map_view.data_handle(), map_view.data_handle() + map_view.size());
1116
1117 // Build input_global_indices as the local-to-global of the new geometry
1118 // dofs.
1119 const std::int32_t num_nodes
1120 = new_imap->size_local() + new_imap->num_ghosts();
1121 std::vector<std::int32_t> local(num_nodes);
1122 std::iota(local.begin(), local.end(), 0);
1123 std::vector<std::int64_t> igi(num_nodes);
1124 new_imap->local_to_global(local, igi);
1125
1127 new_imap, std::vector<std::vector<std::int32_t>>{std::move(dofmap_flat)},
1128 std::vector<CoordinateElement<T>>{new_cmap}, std::move(x_new), gdim,
1129 std::move(igi));
1130
1131 return mesh::Mesh<T>(mesh->comm(), mesh->topology(), std::move(geometry));
1132}
1133
1134} // 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
Definition CoordinateElement.h:38
basix::element::lagrange_variant variant() const
Variant of the element.
Definition CoordinateElement.cpp:212
mesh::CellType cell_shape() const
Cell shape.
Definition CoordinateElement.cpp:41
int degree() const
The polynomial degree of the element.
Definition CoordinateElement.cpp:198
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:117
int num_integrals(IntegralType type, int kernel_idx) const
Get number of integrals (kernels) for a given integral type and kernel index.
Definition Form.h:440
int rank() const
Rank of the form.
Definition Form.h:358
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
Common mesh for the form (the 'integration domain').
Definition Form.h:362
std::set< IntegralType > integral_types() const
Get types of integrals in the form.
Definition Form.h:396
const std::vector< std::shared_ptr< const FunctionSpace< geometry_type > > > & function_spaces() const
Function spaces for all arguments.
Definition Form.h:370
std::span< const std::int32_t > domain_arg(IntegralType type, int rank, int idx, int kernel_idx) const
Argument function mesh integration entity indices.
Definition Form.h:526
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:155
Definition SparsityPattern.h:26
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:34
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:49
std::shared_ptr< const common::IndexMap > index_map(int dim) const
Get the IndexMap that described the parallel distribution of the mesh entities.
Definition Topology.cpp:843
std::shared_ptr< const graph::AdjacencyList< std::int32_t > > connectivity(std::array< int, 2 > d0, std::array< int, 2 > d1) const
Get the connectivity from entities of topological dimension d0 to dimension d1.
Definition Topology.cpp:865
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:75
std::array< std::int32_t, 2 *num_cells > get_cell_entity_pairs(std::int32_t e, std::span< const std::int32_t > cells, const graph::AdjacencyList< std::int32_t > &c_to_e)
Definition utils.h:105
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:16
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
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:146
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:925
mesh::Mesh< T > interpolate_geometry(std::shared_ptr< mesh::Mesh< T > > mesh, const CoordinateElement< T > &new_cmap, const std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> &reorder_fn=nullptr)
Take an existing mesh and create a new mesh with its geometry interpolated into a new coordinate elem...
Definition utils.h:1079
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:414
std::vector< std::string > get_coefficient_names(const ufcx_form &ufcx_form)
Definition utils.cpp:139
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:173
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:953
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:154
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:842
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:318
IntegralType
Type of integral.
Definition Form.h:39
@ 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
la::SparsityPattern create_sparsity_pattern(const Form< T, U > &a)
Create a sparsity pattern for a given form.
Definition utils.h:198
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:78
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:226
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:22
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
basix::cell::type cell_type_to_basix_type(CellType celltype)
Convert a cell type to a Basix cell type.
Definition cell_types.cpp:163
Represents integral data, containing the kernel, and a list of entities to integrate over and the ind...
Definition Form.h:52