10#include "CoordinateElement.h"
12#include "FiniteElement.h"
13#include "FunctionSpace.h"
15#include <basix/mdspan.hpp>
17#include <dolfinx/common/IndexMap.h>
18#include <dolfinx/common/types.h>
19#include <dolfinx/geometry/utils.h>
20#include <dolfinx/mesh/Mesh.h>
30template <dolfinx::scalar T, std::
floating_po
int U>
43template <std::
floating_po
int T>
51 for (std::size_t i = 0; i <
geometry.cmaps().size(); ++i)
53 if (
geometry.cmaps().at(i).cell_shape() == cell_type)
56 throw std::runtime_error(
"Cannot find CoordinateElement for FiniteElement");
58 int index = cmap_index(element.
cell_type());
61 const std::size_t gdim =
geometry.dim();
62 auto x_dofmap =
geometry.dofmaps().at(index);
63 std::span<const T> x_g =
geometry.x();
66 const std::size_t num_dofs_g = cmap.
dim();
72 std::array<std::size_t, 4> phi_shape = cmap.
tabulate_shape(0, Xshape[0]);
74 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
75 md::mdspan<
const T, md::extents<std::size_t, 1, md::dynamic_extent,
76 md::dynamic_extent, 1>>
77 phi_full(phi_b.data(), phi_shape);
79 auto phi = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
83 std::vector<T> coordinate_dofs(num_dofs_g * gdim, 0);
84 std::vector<T> x(3 * (cells.size() * Xshape[0]), 0);
85 for (
auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
88 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
89 for (std::size_t i = 0; i < x_dofs.size(); ++i)
91 std::copy_n(std::next(x_g.begin(), 3 * x_dofs[i]), gdim,
92 std::next(coordinate_dofs.begin(), i * gdim));
96 std::size_t offset = std::ranges::distance(cells.begin(), cell_it);
97 for (std::size_t p = 0; p < Xshape[0]; ++p)
99 for (std::size_t j = 0; j < gdim; ++j)
102 for (std::size_t k = 0; k < num_dofs_g; ++k)
103 acc += phi(p, k) * coordinate_dofs[k * gdim + j];
104 x[j * (cells.size() * Xshape[0]) + offset * Xshape[0] + p] = acc;
128template <dolfinx::scalar T, std::
floating_po
int U>
129void interpolate(Function<T, U>& u, std::span<const T> f,
130 std::array<std::size_t, 2> fshape,
136template <
typename T, std::
size_t D>
137using mdspan_t = md::mdspan<T, md::dextents<std::size_t, D>>;
158template <dolfinx::scalar T>
159void scatter_values(MPI_Comm comm, std::span<const std::int32_t> src_ranks,
160 std::span<const std::int32_t> dest_ranks,
161 mdspan_t<const T, 2> send_values, std::span<T> recv_values)
163 const std::size_t block_size = send_values.extent(1);
164 assert(src_ranks.size() * block_size == send_values.size());
165 assert(recv_values.size() == dest_ranks.size() * block_size);
168 std::vector<std::int32_t> out_ranks(src_ranks.size());
169 out_ranks.assign(src_ranks.begin(), src_ranks.end());
170 auto [unique_end, range_end] = std::ranges::unique(out_ranks);
171 out_ranks.erase(unique_end, range_end);
172 out_ranks.reserve(out_ranks.size() + 1);
175 std::vector<std::int32_t> in_ranks;
176 in_ranks.reserve(dest_ranks.size());
177 std::copy_if(dest_ranks.begin(), dest_ranks.end(),
178 std::back_inserter(in_ranks),
179 [](
auto rank) { return rank >= 0; });
183 std::ranges::sort(in_ranks);
184 auto [unique_end, range_end] = std::ranges::unique(in_ranks);
185 in_ranks.erase(unique_end, range_end);
187 in_ranks.reserve(in_ranks.size() + 1);
190 MPI_Comm reverse_comm;
191 MPI_Dist_graph_create_adjacent(
192 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
193 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL,
false, &reverse_comm);
195 std::vector<std::int32_t> comm_to_output;
196 std::vector<std::int32_t> recv_sizes(in_ranks.size());
197 recv_sizes.reserve(1);
198 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
201 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
202 rank_to_neighbor.reserve(in_ranks.size());
203 for (std::size_t i = 0; i < in_ranks.size(); i++)
204 rank_to_neighbor.push_back({in_ranks[i], i});
205 std::ranges::sort(rank_to_neighbor);
208 std::ranges::for_each(
210 [&rank_to_neighbor, &recv_sizes, block_size](
auto rank)
214 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
216 [](
auto e) {
return e.first; });
217 assert(it != rank_to_neighbor.end() and it->first == rank);
218 recv_sizes[it->second] += block_size;
223 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
224 std::next(recv_offsets.begin(), 1));
227 comm_to_output.resize(recv_offsets.back() / block_size);
228 std::vector<std::int32_t> recv_counter(recv_sizes.size(), 0);
229 for (std::size_t i = 0; i < dest_ranks.size(); ++i)
231 if (
const std::int32_t rank = dest_ranks[i];
rank >= 0)
233 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
235 [](
auto e) {
return e.first; });
236 assert(it != rank_to_neighbor.end() and it->first == rank);
237 int insert_pos = recv_offsets[it->second] + recv_counter[it->second];
238 comm_to_output[insert_pos / block_size] = i * block_size;
239 recv_counter[it->second] += block_size;
244 std::vector<std::int32_t> send_sizes(out_ranks.size());
245 send_sizes.reserve(1);
250 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
251 rank_to_neighbor.reserve(out_ranks.size());
252 for (std::size_t i = 0; i < out_ranks.size(); i++)
253 rank_to_neighbor.push_back({out_ranks[i], i});
257 auto start = rank_to_neighbor.begin();
258 std::ranges::for_each(
260 [&rank_to_neighbor, &send_sizes, block_size, &start](
auto rank)
262 auto it = std::ranges::lower_bound(start, rank_to_neighbor.end(),
263 rank, std::ranges::less(),
264 [](
auto e) { return e.first; });
265 assert(it != rank_to_neighbor.end() and it->first == rank);
266 send_sizes[it->second] += block_size;
272 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
273 std::partial_sum(send_sizes.begin(), send_sizes.end(),
274 std::next(send_offsets.begin(), 1));
277 std::vector<T> values(recv_offsets.back());
279 MPI_Neighbor_alltoallv(send_values.data_handle(), send_sizes.data(),
281 values.data(), recv_sizes.data(), recv_offsets.data(),
283 MPI_Comm_free(&reverse_comm);
287 std::ranges::fill(recv_values, T{0});
288 for (std::size_t i = 0; i < comm_to_output.size(); i++)
290 auto vals = std::next(recv_values.begin(), comm_to_output[i]);
291 auto vals_from = std::next(values.begin(), i * block_size);
292 std::copy_n(vals_from, block_size, vals);
304template <dolfinx::MDSpanRank2 U, dolfinx::MDSpanRank2 V, dolfinx::scalar T>
305void interpolation_apply(U&& Pi, V&& data, std::span<T> coeffs,
int bs)
309 using X =
typename std::remove_cvref_t<U>::value_type;
314 assert(data.extent(0) * data.extent(1) == Pi.extent(1));
315 for (std::size_t i = 0; i < Pi.extent(0); ++i)
318 for (std::size_t k = 0; k < data.extent(1); ++k)
319 for (std::size_t j = 0; j < data.extent(0); ++j)
321 +=
static_cast<X
>(Pi(i, k * data.extent(0) + j)) * data(j, k);
326 assert(data.extent(0) == Pi.extent(1));
327 assert(
static_cast<int>(data.extent(1)) == bs);
328 std::size_t cols = Pi.extent(1);
329 for (
int k = 0; k < bs; ++k)
331 for (std::size_t i = 0; i < Pi.extent(0); ++i)
334 for (std::size_t j = 0; j < cols; ++j)
335 acc +=
static_cast<X
>(Pi(i, j)) * data(j, k);
336 coeffs[bs * i + k] = acc;
361template <dolfinx::scalar T, std::
floating_po
int U>
362void interpolate_same_map(Function<T, U>& u1, mesh::CellRange
auto&& cells1,
363 const Function<T, U>& u0,
364 mesh::CellRange
auto&& cells0)
366 auto V0 = u0.function_space();
368 auto V1 = u1.function_space();
370 auto mesh0 = V0->mesh();
373 auto mesh1 = V1->mesh();
376 auto element0 = V0->element();
378 auto element1 = V1->element();
381 assert(mesh0->topology()->dim());
382 const int tdim = mesh0->topology()->dim();
383 auto map = mesh0->topology()->index_map(tdim);
385 std::span<T> u1_array = u1.x()->array();
386 std::span<const T> u0_array = u0.x()->array();
388 std::span<const std::uint32_t> cell_info0;
389 std::span<const std::uint32_t> cell_info1;
390 if (element1->needs_dof_transformations()
391 or element0->needs_dof_transformations())
393 mesh0->topology_mutable()->create_entity_permutations();
394 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
395 mesh1->topology_mutable()->create_entity_permutations();
396 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
400 auto dofmap1 = V1->dofmap();
401 auto dofmap0 = V0->dofmap();
404 const int bs1 = dofmap1->bs();
405 const int bs0 = dofmap0->bs();
406 auto apply_dof_transformation = element0->template dof_transformation_fn<T>(
408 auto apply_inverse_dof_transform
409 = element1->template dof_transformation_fn<T>(
413 std::vector<T> local0(element0->space_dimension());
414 std::vector<T> local1(element1->space_dimension());
417 auto [i_m, im_shape] = element1->create_interpolation_operator(*element0);
421 if (cells0.size() != cells1.size())
422 throw std::invalid_argument(
"Length of cells0 and cells1 must match.");
423 for (
auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
424 cell0_it != cells0.end() and cell1_it != cells1.end();
425 ++cell0_it, ++cell1_it)
428 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
429 for (std::size_t i = 0; i < dofs0.size(); ++i)
430 for (
int k = 0; k < bs0; ++k)
431 local0[bs0 * i + k] = u0_array[bs0 * dofs0[i] + k];
433 if (apply_dof_transformation)
434 apply_dof_transformation(local0, cell_info0, *cell0_it, 1);
438 std::ranges::fill(local1, 0);
439 for (std::size_t i = 0; i < im_shape[0]; ++i)
440 for (std::size_t j = 0; j < im_shape[1]; ++j)
441 local1[i] +=
static_cast<X
>(i_m[im_shape[1] * i + j]) * local0[j];
443 if (apply_inverse_dof_transform)
444 apply_inverse_dof_transform(local1, cell_info1, *cell1_it, 1);
445 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
446 for (std::size_t i = 0; i < dofs1.size(); ++i)
447 for (
int k = 0; k < bs1; ++k)
448 u1_array[bs1 * dofs1[i] + k] = local1[bs1 * i + k];
466template <dolfinx::scalar T, std::
floating_po
int U>
467void interpolate_nonmatching_maps(Function<T, U>& u1,
468 mesh::CellRange
auto&& cells1,
469 const Function<T, U>& u0,
470 mesh::CellRange
auto&& cells0)
473 auto V0 = u0.function_space();
475 auto mesh0 = V0->mesh();
479 const int tdim = mesh0->topology()->dim();
480 const int gdim = mesh0->geometry().dim();
483 auto V1 = u1.function_space();
485 auto mesh1 = V1->mesh();
487 auto element0 = V0->element();
489 auto element1 = V1->element();
492 std::span<const std::uint32_t> cell_info0;
493 std::span<const std::uint32_t> cell_info1;
494 if (element1->needs_dof_transformations()
495 or element0->needs_dof_transformations())
497 mesh0->topology_mutable()->create_entity_permutations();
498 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
499 mesh1->topology_mutable()->create_entity_permutations();
500 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
504 auto dofmap0 = V0->dofmap();
505 auto dofmap1 = V1->dofmap();
507 const auto [X, Xshape] = element1->interpolation_points();
510 const int bs0 = element0->block_size();
511 const int bs1 = element1->block_size();
512 auto apply_dof_transformation0 = element0->template dof_transformation_fn<U>(
514 auto apply_inv_dof_transform1 = element1->template dof_transformation_fn<T>(
518 const std::size_t dim0 = element0->space_dimension() / bs0;
519 const std::size_t value_size_ref0 = element0->reference_value_size();
520 const std::size_t value_size0 = V0->element()->reference_value_size();
522 const CoordinateElement<U>& cmap = mesh0->geometry().cmaps().front();
523 auto x_dofmap = mesh0->geometry().dofmaps().front();
524 std::span<const U> x_g = mesh0->geometry().x();
530 const std::array<std::size_t, 4> phi_shape
531 = cmap.tabulate_shape(1, Xshape[0]);
532 std::vector<U> phi_b(
533 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
534 md::mdspan<
const U, md::extents<std::size_t, md::dynamic_extent,
535 md::dynamic_extent, md::dynamic_extent, 1>>
536 phi(phi_b.data(), phi_shape);
537 cmap.tabulate(1, X, Xshape, phi_b);
540 const auto [_basis_derivatives_reference0, b0shape]
541 = element0->tabulate(X, Xshape, 0);
542 md::mdspan<
const U, std::extents<std::size_t, 1, md::dynamic_extent,
543 md::dynamic_extent, md::dynamic_extent>>
544 basis_derivatives_reference0(_basis_derivatives_reference0.data(),
548 std::vector<T> local1(element1->space_dimension());
549 std::vector<T> coeffs0(element0->space_dimension());
551 std::vector<U> basis0_b(Xshape[0] * dim0 * value_size0);
552 md::mdspan<U, std::dextents<std::size_t, 3>> basis0(
553 basis0_b.data(), Xshape[0], dim0, value_size0);
555 std::vector<U> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
556 md::mdspan<U, std::dextents<std::size_t, 3>> basis_reference0(
557 basis_reference0_b.data(), Xshape[0], dim0, value_size_ref0);
559 std::vector<T> values0_b(Xshape[0] * 1 * V1->element()->value_size());
561 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
562 values0(values0_b.data(), Xshape[0], 1, V1->element()->value_size());
564 std::vector<T> mapped_values_b(Xshape[0] * 1 * V1->element()->value_size());
566 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
567 mapped_values0(mapped_values_b.data(), Xshape[0], 1,
568 V1->element()->value_size());
570 const std::size_t num_dofs_g = cmap.dim();
571 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
572 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
575 std::vector<U> J_b(Xshape[0] * gdim * tdim);
576 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
578 std::vector<U> K_b(Xshape[0] * tdim * gdim);
579 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
581 std::vector<U> detJ(Xshape[0]);
582 std::vector<U> det_scratch(2 * gdim * tdim);
585 const auto [_Pi_1, pi_shape] = element1->interpolation_operator();
586 impl::mdspan_t<const U, 2> Pi_1(_Pi_1.data(), pi_shape);
588 using u_t = md::mdspan<U, std::dextents<std::size_t, 2>>;
589 using U_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
590 using J_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
591 using K_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
592 auto push_forward_fn0
593 = element0->basix_element().template map_fn<u_t, U_t, J_t, K_t>();
595 using v_t = md::mdspan<const T, std::dextents<std::size_t, 2>>;
596 using V_t =
decltype(md::submdspan(mapped_values0, 0, md::full_extent,
599 = element1->basix_element().template map_fn<V_t, v_t, K_t, J_t>();
602 std::span<const T> array0 = u0.x()->array();
603 std::span<T> array1 = u1.x()->array();
604 if (cells0.size() != cells1.size())
605 throw std::invalid_argument(
"Length of cells0 and cells1 must match.");
606 for (
auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
607 cell0_it != cells0.end() and cell1_it != cells1.end();
608 ++cell0_it, ++cell1_it)
611 auto x_dofs = md::submdspan(x_dofmap, *cell0_it, md::full_extent);
612 for (std::size_t i = 0; i < num_dofs_g; ++i)
614 const int pos = 3 * x_dofs[i];
615 for (
int j = 0; j < gdim; ++j)
616 coord_dofs(i, j) = x_g[pos + j];
620 std::ranges::fill(J_b, 0);
621 for (std::size_t p = 0; p < Xshape[0]; ++p)
624 = md::submdspan(phi, std::pair(1, tdim + 1), p, md::full_extent, 0);
625 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
626 cmap.compute_jacobian(dphi, coord_dofs, _J);
627 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
628 cmap.compute_jacobian_inverse(_J, _K);
629 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
634 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
635 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
636 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
637 basis_reference0(k0, k1, k2)
638 = basis_derivatives_reference0(0, k0, k1, k2);
640 if (apply_dof_transformation0)
642 for (std::size_t p = 0; p < Xshape[0]; ++p)
644 apply_dof_transformation0(
645 std::span(basis_reference0_b.data() + p * dim0 * value_size_ref0,
646 dim0 * value_size_ref0),
647 cell_info0, *cell0_it, value_size_ref0);
651 for (std::size_t i = 0; i < basis0.extent(0); ++i)
653 auto _u = md::submdspan(basis0, i, md::full_extent, md::full_extent);
654 auto _U = md::submdspan(basis_reference0, i, md::full_extent,
656 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
657 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
658 push_forward_fn0(_u, _U, _J, detJ[i], _K);
662 const int dof_bs0 = dofmap0->bs();
663 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
664 for (std::size_t i = 0; i < dofs0.size(); ++i)
665 for (
int k = 0; k < dof_bs0; ++k)
666 coeffs0[dof_bs0 * i + k] = array0[dof_bs0 * dofs0[i] + k];
670 for (std::size_t p = 0; p < Xshape[0]; ++p)
672 for (
int k = 0; k < bs0; ++k)
674 for (std::size_t j = 0; j < value_size0; ++j)
677 for (std::size_t i = 0; i < dim0; ++i)
678 acc += coeffs0[bs0 * i + k] *
static_cast<X
>(basis0(p, i, j));
679 values0(p, 0, j * bs0 + k) = acc;
685 for (std::size_t i = 0; i < values0.extent(0); ++i)
687 auto _u = md::submdspan(values0, i, md::full_extent, md::full_extent);
689 = md::submdspan(mapped_values0, i, md::full_extent, md::full_extent);
690 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
691 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
692 pull_back_fn1(_U, _u, _K, 1.0 / detJ[i], _J);
696 = md::submdspan(mapped_values0, md::full_extent, 0, md::full_extent);
697 interpolation_apply(Pi_1, values, std::span(local1), bs1);
698 if (apply_inv_dof_transform1)
699 apply_inv_dof_transform1(local1, cell_info1, *cell1_it, 1);
702 const int dof_bs1 = dofmap1->bs();
703 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
704 for (std::size_t i = 0; i < dofs1.size(); ++i)
705 for (
int k = 0; k < dof_bs1; ++k)
706 array1[dof_bs1 * dofs1[i] + k] = local1[dof_bs1 * i + k];
721template <dolfinx::scalar T, std::
floating_po
int U>
722void point_evaluation(
const FiniteElement<U>& element,
bool symmetric,
723 const DofMap& dofmap, mesh::CellRange
auto&& cells,
724 std::span<const std::uint32_t> cell_info,
725 std::span<const T> f, std::array<std::size_t, 2> fshape,
731 const int element_bs = element.block_size();
732 const int num_scalar_dofs = element.space_dimension() / element_bs;
733 const int dofmap_bs = dofmap.bs();
735 auto apply_inv_transpose_dof_transformation
736 = element.template dof_transformation_fn<T>(
738 std::vector<T> coeffs_b(num_scalar_dofs);
741 const bool same_bs = (dofmap_bs == element_bs);
745 std::size_t matrix_size = 0;
746 while (matrix_size * matrix_size < fshape[0])
750 for (
auto cell_it =
cells.begin(); cell_it !=
cells.end(); ++cell_it)
761 std::size_t rowstart = 0;
762 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
763 std::size_t offset = std::ranges::distance(
cells.begin(), cell_it);
764 for (
int k = 0; k < element_bs; ++k)
766 if (k - rowstart > row)
775 std::next(f.begin(), (row * matrix_size + k - rowstart) * fshape[1]
776 + offset * num_scalar_dofs),
777 num_scalar_dofs, coeffs_b.data());
778 if (apply_inv_transpose_dof_transformation)
780 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
785 for (
int i = 0; i < num_scalar_dofs; ++i)
786 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
790 for (
int i = 0; i < num_scalar_dofs; ++i)
792 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
793 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
802 for (
auto cell_it =
cells.begin(); cell_it !=
cells.end(); ++cell_it)
804 std::size_t offset = std::ranges::distance(
cells.begin(), cell_it);
805 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
806 for (
int k = 0; k < element_bs; ++k)
811 std::next(f.begin(), k * fshape[1] + offset * num_scalar_dofs),
812 num_scalar_dofs, coeffs_b.data());
813 if (apply_inv_transpose_dof_transformation)
815 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
820 for (
int i = 0; i < num_scalar_dofs; ++i)
821 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
825 for (
int i = 0; i < num_scalar_dofs; ++i)
827 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
828 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
847template <dolfinx::scalar T, std::
floating_po
int U>
848void identity_mapped_evaluation(
const FiniteElement<U>& element,
bool symmetric,
849 const DofMap& dofmap,
850 mesh::CellRange
auto&& cells,
851 std::span<const std::uint32_t> cell_info,
852 std::span<const T> f,
853 std::array<std::size_t, 2> fshape,
860 throw std::invalid_argument(
861 "Interpolation into this element not supported.");
863 const int element_bs = element.block_size();
864 const int num_scalar_dofs = element.space_dimension() / element_bs;
865 const int dofmap_bs = dofmap.bs();
867 const int element_vs = element.reference_value_size();
868 if (element_vs > 1 and element_bs > 1)
869 throw std::runtime_error(
"Interpolation into this element not supported.");
872 const auto [_Pi, pi_shape] = element.interpolation_operator();
873 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
874 const std::size_t num_interp_points = Pi.extent(1);
875 assert(
static_cast<int>(Pi.extent(0)) == num_scalar_dofs);
877 auto apply_inv_transpose_dof_transformation
878 = element.template dof_transformation_fn<T>(
882 const bool same_bs = (dofmap_bs == element_bs);
885 std::vector<T> ref_data_b(num_interp_points);
886 md::mdspan<T, md::extents<std::size_t, md::dynamic_extent, 1>> ref_data(
887 ref_data_b.data(), num_interp_points, 1);
888 std::vector<T> coeffs_b(num_scalar_dofs);
889 for (
auto cell_it =
cells.begin(); cell_it !=
cells.end(); ++cell_it)
891 std::size_t offset = std::ranges::distance(
cells.begin(), cell_it);
892 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
893 for (
int k = 0; k < element_bs; ++k)
895 for (
int i = 0; i < element_vs; ++i)
898 std::next(f.begin(), (i + k) * fshape[1]
899 + offset * num_interp_points / element_vs),
900 num_interp_points / element_vs,
901 std::next(ref_data_b.begin(), i * num_interp_points / element_vs));
904 impl::interpolation_apply(Pi, ref_data, std::span(coeffs_b), 1);
905 if (apply_inv_transpose_dof_transformation)
907 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
912 for (
int i = 0; i < num_scalar_dofs; ++i)
913 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
917 for (
int i = 0; i < num_scalar_dofs; ++i)
919 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
920 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
939template <dolfinx::scalar T, std::
floating_po
int U>
940void piola_mapped_evaluation(
const FiniteElement<U>& element,
bool symmetric,
941 const DofMap& dofmap, mesh::CellRange
auto&& cells,
942 std::span<const std::uint32_t> cell_info,
943 std::span<const T> f,
944 std::array<std::size_t, 2> fshape,
945 const mesh::Mesh<U>& mesh, std::span<T> coeffs)
948 throw std::invalid_argument(
949 "Interpolation into this element not supported.");
951 const int gdim = mesh.geometry().dim();
952 assert(mesh.topology());
953 const int tdim = mesh.topology()->dim();
955 const int element_bs = element.block_size();
956 const int num_scalar_dofs = element.space_dimension() / element_bs;
957 const int value_size = element.reference_value_size();
958 const int dofmap_bs = dofmap.bs();
961 const bool same_bs = (dofmap_bs == element_bs);
963 md::mdspan<const T, md::dextents<std::size_t, 2>> _f(f.data(), fshape);
966 const auto [X, Xshape] = element.interpolation_points();
969 throw std::invalid_argument(
970 "Interpolation into this space is not yet supported.");
973 if (_f.extent(1) !=
cells.size() * Xshape[0])
974 throw std::invalid_argument(
"Interpolation data has the wrong shape.");
977 const CoordinateElement<U>& cmap = mesh.geometry().cmaps().front();
980 auto x_dofmap = mesh.geometry().dofmaps().front();
981 const int num_dofs_g = cmap.dim();
982 std::span<const U> x_g = mesh.geometry().x();
985 std::vector<U> J_b(Xshape[0] * gdim * tdim);
986 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
988 std::vector<U> K_b(Xshape[0] * tdim * gdim);
989 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
991 std::vector<U> detJ(Xshape[0]);
992 std::vector<U> det_scratch(2 * gdim * tdim);
994 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
995 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
997 const std::size_t value_size_ref = element.reference_value_size();
998 std::vector<T> ref_data_b(Xshape[0] * 1 * value_size_ref);
1000 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
1001 ref_data(ref_data_b.data(), Xshape[0], 1, value_size_ref);
1003 std::vector<T> _vals_b(Xshape[0] * 1 * value_size);
1005 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
1006 _vals(_vals_b.data(), Xshape[0], 1, value_size);
1010 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
1011 std::vector<U> phi_b(
1012 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
1013 md::mdspan<
const U, md::extents<std::size_t, md::dynamic_extent,
1014 md::dynamic_extent, md::dynamic_extent, 1>>
1015 phi(phi_b.data(), phi_shape);
1016 cmap.tabulate(1, X, Xshape, phi_b);
1017 auto dphi = md::submdspan(phi, std::pair(1, tdim + 1), md::full_extent,
1018 md::full_extent, 0);
1020 std::function<void(std::span<T>, std::span<const std::uint32_t>, std::int32_t,
1022 apply_inv_trans_dof_transformation
1023 = element.template dof_transformation_fn<T>(
1027 const auto [_Pi, pi_shape] = element.interpolation_operator();
1028 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
1030 using u_t = md::mdspan<const T, md::dextents<std::size_t, 2>>;
1032 =
decltype(md::submdspan(ref_data, 0, md::full_extent, md::full_extent));
1033 using J_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
1034 using K_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
1036 = element.basix_element().template map_fn<U_t, u_t, J_t, K_t>();
1038 std::vector<T> coeffs_b(num_scalar_dofs);
1039 for (
auto cell_it =
cells.begin(); cell_it !=
cells.end(); ++cell_it)
1041 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
1042 for (
int i = 0; i < num_dofs_g; ++i)
1044 const int pos = 3 * x_dofs[i];
1045 for (
int j = 0; j < gdim; ++j)
1046 coord_dofs(i, j) = x_g[pos + j];
1050 std::ranges::fill(J_b, 0);
1051 for (std::size_t p = 0; p < Xshape[0]; ++p)
1053 auto _dphi = md::submdspan(dphi, md::full_extent, p, md::full_extent);
1054 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
1055 cmap.compute_jacobian(_dphi, coord_dofs, _J);
1056 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
1057 cmap.compute_jacobian_inverse(_J, _K);
1058 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
1061 const std::size_t offset = std::ranges::distance(
cells.begin(), cell_it);
1062 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
1063 for (
int k = 0; k < element_bs; ++k)
1066 for (
int m = 0; m < value_size; ++m)
1068 for (std::size_t k0 = 0; k0 < Xshape[0]; ++k0)
1071 = f[fshape[1] * (k * value_size + m) + offset * Xshape[0] + k0];
1076 for (std::size_t i = 0; i < Xshape[0]; ++i)
1078 auto _u = md::submdspan(_vals, i, md::full_extent, md::full_extent);
1079 auto _U = md::submdspan(ref_data, i, md::full_extent, md::full_extent);
1080 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
1081 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
1082 pull_back_fn(_U, _u, _K, 1.0 / detJ[i], _J);
1085 auto ref = md::submdspan(ref_data, md::full_extent, 0, md::full_extent);
1086 impl::interpolation_apply(Pi, ref, std::span(coeffs_b), element_bs);
1087 if (apply_inv_trans_dof_transformation)
1088 apply_inv_trans_dof_transformation(coeffs_b, cell_info, *cell_it, 1);
1091 assert(coeffs_b.size() ==
static_cast<std::size_t
>(num_scalar_dofs));
1094 for (
int i = 0; i < num_scalar_dofs; ++i)
1095 coeffs[dofmap_bs * dofs[i] + k] = coeffs_b[i];
1099 for (
int i = 0; i < num_scalar_dofs; ++i)
1101 std::div_t pos = std::div(i * element_bs + k, dofmap_bs);
1102 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
1137template <std::
floating_po
int T>
1141 bool allow_extrapolation =
true)
1148 std::vector<T> x(coords.size());
1149 std::size_t num_points = coords.size() / 3;
1150 for (std::size_t i = 0; i < num_points; ++i)
1151 for (std::size_t j = 0; j < 3; ++j)
1152 x[3 * i + j] = coords[i + j * num_points];
1156 allow_extrapolation);
1159template <dolfinx::scalar T, std::
floating_po
int U>
1161 std::array<std::size_t, 2> fshape,
1165 const int index = 0;
1168 const int element_bs = element->block_size();
1169 if (
int num_sub = element->num_sub_elements();
1170 num_sub > 0 and num_sub != element_bs)
1172 throw std::invalid_argument(
"Cannot directly interpolate a mixed space. "
1173 "Interpolate into subspaces.");
1182 != (std::size_t)u.
function_space()->elements(index)->value_size()
1183 or f.size() != fshape[0] * fshape[1])
1185 throw std::invalid_argument(
"Interpolation data has the wrong shape/size.");
1188 spdlog::debug(
"Check for dof transformation");
1189 std::span<const std::uint32_t> cell_info;
1190 if (element->needs_dof_transformations())
1192 mesh->topology_mutable()->create_entity_permutations();
1193 cell_info = std::span(
mesh->topology()->get_cell_permutation_info());
1197 spdlog::debug(
"Interpolate: get dofmap");
1202 std::span<T> coeffs = u.
x()->array();
1205 element->map_ident() and element->interpolation_ident())
1209 spdlog::debug(
"Interpolate: point evaluation");
1210 impl::point_evaluation(*element, symmetric, *dofmap, cells, cell_info, f,
1213 else if (element->map_ident())
1215 spdlog::debug(
"Interpolate: identity-mapped evaluation");
1216 impl::identity_mapped_evaluation(*element, symmetric, *dofmap, cells,
1217 cell_info, f, fshape, coeffs);
1221 spdlog::debug(
"Interpolate: Piola-mapped evaluation");
1222 impl::piola_mapped_evaluation(*element, symmetric, *dofmap, cells,
1223 cell_info, f, fshape, *
mesh, coeffs);
1243template <dolfinx::scalar T, std::
floating_po
int U>
1250 MPI_Comm comm = mesh1->comm();
1256 MPI_Comm_compare(comm, mesh0->comm(), &result);
1257 if (result == MPI_UNEQUAL)
1259 throw std::invalid_argument(
"Interpolation on different meshes is only "
1260 "supported on the same communicator.");
1264 assert(mesh1->topology());
1265 auto cell_map = mesh1->topology()->index_map(mesh1->topology()->dim());
1269 const std::size_t value_size = element1->value_size();
1271 const std::vector<int>& dest_ranks = interpolation_data.
src_owner;
1272 const std::vector<int>& src_ranks = interpolation_data.
dest_owners;
1273 const std::vector<U>& recv_points = interpolation_data.
dest_points;
1274 const std::vector<std::int32_t>& evaluation_cells
1278 std::vector<T> send_values(recv_points.size() / 3 * value_size);
1279 u0.
eval(recv_points, {recv_points.size() / 3, (std::size_t)3},
1280 evaluation_cells, send_values, {recv_points.size() / 3, value_size},
1284 std::vector<T> values_b(dest_ranks.size() * value_size);
1285 md::mdspan<const T, md::dextents<std::size_t, 2>> _send_values(
1286 send_values.data(), src_ranks.size(), value_size);
1287 impl::scatter_values(comm, src_ranks, dest_ranks, _send_values,
1288 std::span(values_b));
1291 md::mdspan<const T, md::dextents<std::size_t, 2>> values(
1292 values_b.data(), dest_ranks.size(), value_size);
1293 std::vector<T> valuesT_b(value_size * dest_ranks.size());
1294 md::mdspan<T, md::dextents<std::size_t, 2>> valuesT(
1295 valuesT_b.data(), value_size, dest_ranks.size());
1296 for (std::size_t i = 0; i < values.extent(0); ++i)
1297 for (std::size_t j = 0; j < values.extent(1); ++j)
1298 valuesT(j, i) = values(i, j);
1321template <dolfinx::scalar T, std::
floating_po
int U>
1325 if (cells0.size() != cells1.size())
1326 throw std::invalid_argument(
"Length of cell lists do not match.");
1334 auto e0 = V0->element();
1336 auto e1 = V1->element();
1338 if (!std::ranges::equal(e0->value_shape(), e1->value_shape()))
1340 throw std::invalid_argument(
1341 "Interpolation: elements have different value dimensions");
1344 if (V1->mesh() == V0->mesh() and (e1 == e0 or *e1 == *e0))
1347 if (e1->block_size() != e0->block_size())
1348 throw std::invalid_argument(
"Mismatch in element block size.");
1351 std::shared_ptr<const DofMap> dofmap0 = V0->dofmap();
1353 std::shared_ptr<const DofMap> dofmap1 = V1->dofmap();
1357 const int bs0 = dofmap0->bs();
1358 const int bs1 = dofmap1->bs();
1359 std::span<T> u1_array = u1.
x()->array();
1360 std::span<const T> u0_array = u0.
x()->array();
1361 assert(cells0.size() == cells1.size());
1362 for (
auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
1363 cell0_it != cells0.end() and cell1_it != cells1.end();
1364 ++cell0_it, ++cell1_it)
1367 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
1368 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
1369 assert(bs0 * dofs0.size() == bs1 * dofs1.size());
1370 for (std::size_t i = 0; i < dofs0.size(); ++i)
1372 for (
int k = 0; k < bs0; ++k)
1374 int index = bs0 * i + k;
1375 std::div_t dv1 = std::div(index, bs1);
1376 u1_array[bs1 * dofs1[dv1.quot] + dv1.rem]
1377 = u0_array[bs0 * dofs0[i] + k];
1382 else if (e1->map_type() == e0->map_type())
1385 impl::interpolate_same_map(u1, cells1, u0, cells0);
1390 impl::interpolate_nonmatching_maps(u1, cells1, u0, cells0);
1403template <dolfinx::scalar T, std::
floating_po
int U>
1405 std::ranges::input_range
auto&& cells)
1412 throw std::invalid_argument(
"Meshes do no match.");
1424template <dolfinx::scalar T, std::
floating_po
int U>
1430 std::ranges::copy(u0.
x()->array(), u1.
x()->array().begin());
1433 auto mesh = V1->mesh();
1435 assert(
mesh->topology());
1436 auto map =
mesh->topology()->index_map(
mesh->topology()->dim());
1438 std::int32_t num_cells = map->size_local() + map->num_ghosts();
Degree-of-freedom map representations and tools.
Definition CoordinateElement.h:38
void tabulate(int nd, std::span< const T > X, std::array< std::size_t, 2 > shape, std::span< T > basis) const
Evaluate basis values and derivatives at set of points.
Definition CoordinateElement.cpp:59
std::array< std::size_t, 4 > tabulate_shape(std::size_t nd, std::size_t num_points) const
Shape of array to fill when calling tabulate.
Definition CoordinateElement.cpp:52
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:222
Model of a finite element.
Definition FiniteElement.h:62
std::pair< std::vector< geometry_type >, std::array< std::size_t, 2 > > interpolation_points() const
Points on the reference cell at which an expression needs to be evaluated in order to interpolate the...
Definition FiniteElement.cpp:468
mesh::CellType cell_type() const noexcept
Cell shape that the element is defined on.
Definition FiniteElement.cpp:283
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:149
void eval(std::span< const geometry_type > x, std::array< std::size_t, 2 > xshape, mesh::CellRange auto &&cells, std::span< value_type > u, std::array< std::size_t, 2 > ushape, double tol, int maxit) const
Evaluate the Function at points.
Definition Function.h:462
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:155
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:37
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Requirement on range of cell indices.
Definition Topology.h:32
MPI_Datatype mpi_t
Retrieves the MPI data type associated to the provided type.
Definition MPI.h:320
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:73
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:24
void interpolate(Function< T, U > &u, std::span< const T > f, std::array< std::size_t, 2 > fshape, mesh::CellRange auto &&cells)
Interpolate an evaluated expression f(x) in a finite element space.
Definition interpolate.h:1160
geometry::PointOwnershipData< T > create_interpolation_data(const mesh::Geometry< T > &geometry0, const FiniteElement< T > &element0, const mesh::Mesh< T > &mesh1, mesh::CellRange auto &&cells, T padding, bool allow_extrapolation=true)
Generate data needed to interpolate finite element fem::Function's across different meshes.
Definition interpolate.h:1138
@ transpose
Transpose.
Definition FiniteElement.h:30
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:32
@ standard
Standard.
Definition FiniteElement.h:29
std::vector< T > interpolation_coords(const fem::FiniteElement< T > &element, const mesh::Geometry< T > &geometry, mesh::CellRange auto &&cells)
Compute the evaluation points in the physical space at which an expression should be computed to inte...
Definition interpolate.h:44
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:24
PointOwnershipData< T > determine_point_ownership(const mesh::Mesh< T > &mesh, std::span< const T > points, T padding, std::optional< std::span< const std::int32_t > > cells, bool find_closest_cell=true)
Determine, for a set of points, the owning process of the cell (if any) that contains each point.
Definition utils.h:714
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
CellType
Cell type identifier.
Definition cell_types.h:22
Information on the ownership of points distributed across processes.
Definition utils.h:34
std::vector< T > dest_points
Points that are owned by current process.
Definition utils.h:39
std::vector< std::int32_t > dest_cells
Definition utils.h:41
std::vector< int > dest_owners
Ranks that sent dest_points to current process.
Definition utils.h:38
std::vector< int > src_owner
Definition utils.h:35