DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
interpolate.h
1// Copyright (C) 2020-2026 Garth N. Wells, Igor A. Baratta, Massimiliano Leoni
2// and Jørgen S.Dokken
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 "CoordinateElement.h"
11#include "DofMap.h"
12#include "FiniteElement.h"
13#include "FunctionSpace.h"
14#include <algorithm>
15#include <basix/mdspan.hpp>
16#include <concepts>
17#include <dolfinx/common/IndexMap.h>
18#include <dolfinx/common/types.h>
19#include <dolfinx/geometry/utils.h>
20#include <dolfinx/mesh/Mesh.h>
21#include <functional>
22#include <numeric>
23#include <ranges>
24#include <span>
25#include <vector>
26
27namespace dolfinx::fem
28{
29template <dolfinx::scalar T, std::floating_point U>
30class Function;
31
32template <typename T>
33concept MDSpan = requires(T x, std::size_t idx) {
34 x(idx, idx);
35 { x.extent(0) } -> std::integral;
36 { x.extent(1) } -> std::integral;
37};
38
49template <std::floating_point T>
50std::vector<T> interpolation_coords(const fem::FiniteElement<T>& element,
52 mesh::CellRange auto&& cells)
53{
54 // Find CoordinateElement appropriate to element
55 auto cmap_index = [&geometry](mesh::CellType cell_type)
56 {
57 for (std::size_t i = 0; i < geometry.cmaps().size(); ++i)
58 {
59 if (geometry.cmaps().at(i).cell_shape() == cell_type)
60 return i;
61 }
62 throw std::runtime_error("Cannot find CoordinateElement for FiniteElement");
63 };
64 int index = cmap_index(element.cell_type());
65
66 // Get geometry data and the element coordinate map
67 const std::size_t gdim = geometry.dim();
68 auto x_dofmap = geometry.dofmaps().at(index);
69 std::span<const T> x_g = geometry.x();
70
71 const CoordinateElement<T>& cmap = geometry.cmaps().at(index);
72 const std::size_t num_dofs_g = cmap.dim();
73
74 // Get the interpolation points on the reference cells
75 const auto [X, Xshape] = element.interpolation_points();
76
77 // Evaluate coordinate element basis at reference points
78 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(0, Xshape[0]);
79 std::vector<T> phi_b(
80 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
81 md::mdspan<const T, md::extents<std::size_t, 1, md::dynamic_extent,
82 md::dynamic_extent, 1>>
83 phi_full(phi_b.data(), phi_shape);
84 cmap.tabulate(0, X, Xshape, phi_b);
85 auto phi = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
86
87 // Push reference coordinates (X) forward to the physical coordinates
88 // (x) for each cell
89 std::vector<T> coordinate_dofs(num_dofs_g * gdim, 0);
90 std::vector<T> x(3 * (cells.size() * Xshape[0]), 0);
91 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
92 {
93 // Get geometry data for current cell
94 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
95 for (std::size_t i = 0; i < x_dofs.size(); ++i)
96 {
97 std::copy_n(std::next(x_g.begin(), 3 * x_dofs[i]), gdim,
98 std::next(coordinate_dofs.begin(), i * gdim));
99 }
100
101 // Push forward coordinates (X -> x)
102 std::size_t offset = std::distance(cells.begin(), cell_it);
103 for (std::size_t p = 0; p < Xshape[0]; ++p)
104 {
105 for (std::size_t j = 0; j < gdim; ++j)
106 {
107 T acc = 0;
108 for (std::size_t k = 0; k < num_dofs_g; ++k)
109 acc += phi(p, k) * coordinate_dofs[k * gdim + j];
110 x[j * (cells.size() * Xshape[0]) + offset * Xshape[0] + p] = acc;
111 }
112 }
113 }
114
115 return x;
116}
117
134template <dolfinx::scalar T, std::floating_point U>
135void interpolate(Function<T, U>& u, std::span<const T> f,
136 std::array<std::size_t, 2> fshape,
137 mesh::CellRange auto&& cells);
138
139namespace impl
140{
142template <typename T, std::size_t D>
143using mdspan_t = md::mdspan<T, md::dextents<std::size_t, D>>;
144
164template <dolfinx::scalar T>
165void scatter_values(MPI_Comm comm, std::span<const std::int32_t> src_ranks,
166 std::span<const std::int32_t> dest_ranks,
167 mdspan_t<const T, 2> send_values, std::span<T> recv_values)
168{
169 const std::size_t block_size = send_values.extent(1);
170 assert(src_ranks.size() * block_size == send_values.size());
171 assert(recv_values.size() == dest_ranks.size() * block_size);
172
173 // Build unique set of the sorted src_ranks
174 std::vector<std::int32_t> out_ranks(src_ranks.size());
175 out_ranks.assign(src_ranks.begin(), src_ranks.end());
176 auto [unique_end, range_end] = std::ranges::unique(out_ranks);
177 out_ranks.erase(unique_end, range_end);
178 out_ranks.reserve(out_ranks.size() + 1);
179
180 // Remove negative entries from dest_ranks
181 std::vector<std::int32_t> in_ranks;
182 in_ranks.reserve(dest_ranks.size());
183 std::copy_if(dest_ranks.begin(), dest_ranks.end(),
184 std::back_inserter(in_ranks),
185 [](auto rank) { return rank >= 0; });
186
187 // Create unique set of sorted in-ranks
188 {
189 std::ranges::sort(in_ranks);
190 auto [unique_end, range_end] = std::ranges::unique(in_ranks);
191 in_ranks.erase(unique_end, range_end);
192 }
193 in_ranks.reserve(in_ranks.size() + 1);
194
195 // Create neighborhood communicator
196 MPI_Comm reverse_comm;
197 MPI_Dist_graph_create_adjacent(
198 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
199 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL, false, &reverse_comm);
200
201 std::vector<std::int32_t> comm_to_output;
202 std::vector<std::int32_t> recv_sizes(in_ranks.size());
203 recv_sizes.reserve(1);
204 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
205 {
206 // Build map from parent to neighborhood communicator ranks
207 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
208 rank_to_neighbor.reserve(in_ranks.size());
209 for (std::size_t i = 0; i < in_ranks.size(); i++)
210 rank_to_neighbor.push_back({in_ranks[i], i});
211 std::ranges::sort(rank_to_neighbor);
212
213 // Compute receive sizes
214 std::ranges::for_each(
215 dest_ranks,
216 [&rank_to_neighbor, &recv_sizes, block_size](auto rank)
217 {
218 if (rank >= 0)
219 {
220 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
221 std::ranges::less(),
222 [](auto e) { return e.first; });
223 assert(it != rank_to_neighbor.end() and it->first == rank);
224 recv_sizes[it->second] += block_size;
225 }
226 });
227
228 // Compute receiving offsets
229 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
230 std::next(recv_offsets.begin(), 1));
231
232 // Compute map from receiving values to position in recv_values
233 comm_to_output.resize(recv_offsets.back() / block_size);
234 std::vector<std::int32_t> recv_counter(recv_sizes.size(), 0);
235 for (std::size_t i = 0; i < dest_ranks.size(); ++i)
236 {
237 if (const std::int32_t rank = dest_ranks[i]; rank >= 0)
238 {
239 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
240 std::ranges::less(),
241 [](auto e) { return e.first; });
242 assert(it != rank_to_neighbor.end() and it->first == rank);
243 int insert_pos = recv_offsets[it->second] + recv_counter[it->second];
244 comm_to_output[insert_pos / block_size] = i * block_size;
245 recv_counter[it->second] += block_size;
246 }
247 }
248 }
249
250 std::vector<std::int32_t> send_sizes(out_ranks.size());
251 send_sizes.reserve(1);
252 {
253 // Compute map from parent MPI rank to neighbor rank for outgoing
254 // data. `out_ranks` is sorted, so rank_to_neighbor will be sorted
255 // too.
256 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
257 rank_to_neighbor.reserve(out_ranks.size());
258 for (std::size_t i = 0; i < out_ranks.size(); i++)
259 rank_to_neighbor.push_back({out_ranks[i], i});
260
261 // Compute send sizes. As `src_ranks` is sorted, we can move 'start'
262 // in search forward.
263 auto start = rank_to_neighbor.begin();
264 std::ranges::for_each(
265 src_ranks,
266 [&rank_to_neighbor, &send_sizes, block_size, &start](auto rank)
267 {
268 auto it = std::ranges::lower_bound(start, rank_to_neighbor.end(),
269 rank, std::ranges::less(),
270 [](auto e) { return e.first; });
271 assert(it != rank_to_neighbor.end() and it->first == rank);
272 send_sizes[it->second] += block_size;
273 start = it;
274 });
275 }
276
277 // Compute sending offsets
278 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
279 std::partial_sum(send_sizes.begin(), send_sizes.end(),
280 std::next(send_offsets.begin(), 1));
281
282 // Send values to dest ranks
283 std::vector<T> values(recv_offsets.back());
284 values.reserve(1);
285 MPI_Neighbor_alltoallv(send_values.data_handle(), send_sizes.data(),
286 send_offsets.data(), dolfinx::MPI::mpi_t<T>,
287 values.data(), recv_sizes.data(), recv_offsets.data(),
288 dolfinx::MPI::mpi_t<T>, reverse_comm);
289 MPI_Comm_free(&reverse_comm);
290
291 // Insert values received from neighborhood communicator in output
292 // span
293 std::ranges::fill(recv_values, T(0));
294 for (std::size_t i = 0; i < comm_to_output.size(); i++)
295 {
296 auto vals = std::next(recv_values.begin(), comm_to_output[i]);
297 auto vals_from = std::next(values.begin(), i * block_size);
298 std::copy_n(vals_from, block_size, vals);
299 }
300};
301
310template <MDSpan U, MDSpan V, dolfinx::scalar T>
311void interpolation_apply(U&& Pi, V&& data, std::span<T> coeffs, int bs)
312{
313 // Geometry (real) scalar type, taken from the interpolation operator Pi
314 // rather than scalar_value_t<T> so it is independent of the value scalar T.
315 using X = typename std::remove_cvref_t<U>::value_type;
316
317 // Compute coefficients = Pi * x (matrix-vector multiply)
318 if (bs == 1)
319 {
320 assert(data.extent(0) * data.extent(1) == Pi.extent(1));
321 for (std::size_t i = 0; i < Pi.extent(0); ++i)
322 {
323 coeffs[i] = 0.0;
324 for (std::size_t k = 0; k < data.extent(1); ++k)
325 for (std::size_t j = 0; j < data.extent(0); ++j)
326 coeffs[i]
327 += static_cast<X>(Pi(i, k * data.extent(0) + j)) * data(j, k);
328 }
329 }
330 else
331 {
332 assert(data.extent(0) == Pi.extent(1));
333 assert(static_cast<int>(data.extent(1)) == bs);
334 std::size_t cols = Pi.extent(1);
335 for (int k = 0; k < bs; ++k)
336 {
337 for (std::size_t i = 0; i < Pi.extent(0); ++i)
338 {
339 T acc = 0;
340 for (std::size_t j = 0; j < cols; ++j)
341 acc += static_cast<X>(Pi(i, j)) * data(j, k);
342 coeffs[bs * i + k] = acc;
343 }
344 }
345 }
346}
347
367template <dolfinx::scalar T, std::floating_point U>
368void interpolate_same_map(Function<T, U>& u1, mesh::CellRange auto&& cells1,
369 const Function<T, U>& u0,
370 mesh::CellRange auto&& cells0)
371{
372 auto V0 = u0.function_space();
373 assert(V0);
374 auto V1 = u1.function_space();
375 assert(V1);
376 auto mesh0 = V0->mesh();
377 assert(mesh0);
378
379 auto mesh1 = V1->mesh();
380 assert(mesh1);
381
382 auto element0 = V0->element();
383 assert(element0);
384 auto element1 = V1->element();
385 assert(element1);
386
387 assert(mesh0->topology()->dim());
388 const int tdim = mesh0->topology()->dim();
389 auto map = mesh0->topology()->index_map(tdim);
390 assert(map);
391 std::span<T> u1_array = u1.x()->array();
392 std::span<const T> u0_array = u0.x()->array();
393
394 std::span<const std::uint32_t> cell_info0;
395 std::span<const std::uint32_t> cell_info1;
396 if (element1->needs_dof_transformations()
397 or element0->needs_dof_transformations())
398 {
399 mesh0->topology_mutable()->create_entity_permutations();
400 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
401 mesh1->topology_mutable()->create_entity_permutations();
402 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
403 }
404
405 // Get dofmaps
406 auto dofmap1 = V1->dofmap();
407 auto dofmap0 = V0->dofmap();
408
409 // Get block sizes and dof transformation operators
410 const int bs1 = dofmap1->bs();
411 const int bs0 = dofmap0->bs();
412 auto apply_dof_transformation = element0->template dof_transformation_fn<T>(
414 auto apply_inverse_dof_transform
415 = element1->template dof_transformation_fn<T>(
417
418 // Create working array
419 std::vector<T> local0(element0->space_dimension());
420 std::vector<T> local1(element1->space_dimension());
421
422 // Create interpolation operator
423 auto [i_m, im_shape] = element1->create_interpolation_operator(*element0);
424
425 // Iterate over mesh and interpolate on each cell
426 using X = U; // geometry (real) type, independent of the value scalar T
427 if (cells0.size() != cells1.size())
428 throw std::runtime_error("Length of cells0 and cells1 must match.");
429 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
430 cell0_it != cells0.end() and cell1_it != cells1.end();
431 ++cell0_it, ++cell1_it)
432 {
433 // Pack and transform cell dofs to reference ordering
434 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
435 for (std::size_t i = 0; i < dofs0.size(); ++i)
436 for (int k = 0; k < bs0; ++k)
437 local0[bs0 * i + k] = u0_array[bs0 * dofs0[i] + k];
438
439 apply_dof_transformation(local0, cell_info0, *cell0_it, 1);
440
441 // FIXME: Get compile-time ranges from Basix
442 // Apply interpolation operator
443 std::ranges::fill(local1, 0);
444 for (std::size_t i = 0; i < im_shape[0]; ++i)
445 for (std::size_t j = 0; j < im_shape[1]; ++j)
446 local1[i] += static_cast<X>(i_m[im_shape[1] * i + j]) * local0[j];
447
448 apply_inverse_dof_transform(local1, cell_info1, *cell1_it, 1);
449 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
450 for (std::size_t i = 0; i < dofs1.size(); ++i)
451 for (int k = 0; k < bs1; ++k)
452 u1_array[bs1 * dofs1[i] + k] = local1[bs1 * i + k];
453 }
454}
455
470template <dolfinx::scalar T, std::floating_point U>
471void interpolate_nonmatching_maps(Function<T, U>& u1,
472 mesh::CellRange auto&& cells1,
473 const Function<T, U>& u0,
474 mesh::CellRange auto&& cells0)
475{
476 // Get mesh
477 auto V0 = u0.function_space();
478 assert(V0);
479 auto mesh0 = V0->mesh();
480 assert(mesh0);
481
482 // Mesh dims
483 const int tdim = mesh0->topology()->dim();
484 const int gdim = mesh0->geometry().dim();
485
486 // Get elements
487 auto V1 = u1.function_space();
488 assert(V1);
489 auto mesh1 = V1->mesh();
490 assert(mesh1);
491 auto element0 = V0->element();
492 assert(element0);
493 auto element1 = V1->element();
494 assert(element1);
495
496 std::span<const std::uint32_t> cell_info0;
497 std::span<const std::uint32_t> cell_info1;
498 if (element1->needs_dof_transformations()
499 or element0->needs_dof_transformations())
500 {
501 mesh0->topology_mutable()->create_entity_permutations();
502 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
503 mesh1->topology_mutable()->create_entity_permutations();
504 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
505 }
506
507 // Get dofmaps
508 auto dofmap0 = V0->dofmap();
509 auto dofmap1 = V1->dofmap();
510
511 const auto [X, Xshape] = element1->interpolation_points();
512
513 // Get block sizes and dof transformation operators
514 const int bs0 = element0->block_size();
515 const int bs1 = element1->block_size();
516 auto apply_dof_transformation0 = element0->template dof_transformation_fn<U>(
518 auto apply_inv_dof_transform1 = element1->template dof_transformation_fn<T>(
520
521 // Get sizes of elements
522 const std::size_t dim0 = element0->space_dimension() / bs0;
523 const std::size_t value_size_ref0 = element0->reference_value_size();
524 const std::size_t value_size0 = V0->element()->reference_value_size();
525
526 const CoordinateElement<U>& cmap = mesh0->geometry().cmaps().front();
527 auto x_dofmap = mesh0->geometry().dofmaps().front();
528 std::span<const U> x_g = mesh0->geometry().x();
529
530 // (0) is derivative index, (1) is the point index, (2) is the basis
531 // function index and (3) is the basis function component.
532
533 // Evaluate coordinate map basis at reference interpolation points
534 const std::array<std::size_t, 4> phi_shape
535 = cmap.tabulate_shape(1, Xshape[0]);
536 std::vector<U> phi_b(
537 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
538 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
539 md::dynamic_extent, md::dynamic_extent, 1>>
540 phi(phi_b.data(), phi_shape);
541 cmap.tabulate(1, X, Xshape, phi_b);
542
543 // Evaluate v basis functions at reference interpolation points
544 const auto [_basis_derivatives_reference0, b0shape]
545 = element0->tabulate(X, Xshape, 0);
546 md::mdspan<const U, std::extents<std::size_t, 1, md::dynamic_extent,
547 md::dynamic_extent, md::dynamic_extent>>
548 basis_derivatives_reference0(_basis_derivatives_reference0.data(),
549 b0shape);
550
551 // Create working arrays
552 std::vector<T> local1(element1->space_dimension());
553 std::vector<T> coeffs0(element0->space_dimension());
554
555 std::vector<U> basis0_b(Xshape[0] * dim0 * value_size0);
556 md::mdspan<U, std::dextents<std::size_t, 3>> basis0(
557 basis0_b.data(), Xshape[0], dim0, value_size0);
558
559 std::vector<U> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
560 md::mdspan<U, std::dextents<std::size_t, 3>> basis_reference0(
561 basis_reference0_b.data(), Xshape[0], dim0, value_size_ref0);
562
563 std::vector<T> values0_b(Xshape[0] * 1 * V1->element()->value_size());
564 md::mdspan<
565 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
566 values0(values0_b.data(), Xshape[0], 1, V1->element()->value_size());
567
568 std::vector<T> mapped_values_b(Xshape[0] * 1 * V1->element()->value_size());
569 md::mdspan<
570 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
571 mapped_values0(mapped_values_b.data(), Xshape[0], 1,
572 V1->element()->value_size());
573
574 const std::size_t num_dofs_g = cmap.dim();
575 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
576 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
577 num_dofs_g, gdim);
578
579 std::vector<U> J_b(Xshape[0] * gdim * tdim);
580 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
581 tdim);
582 std::vector<U> K_b(Xshape[0] * tdim * gdim);
583 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
584 gdim);
585 std::vector<U> detJ(Xshape[0]);
586 std::vector<U> det_scratch(2 * gdim * tdim);
587
588 // Get interpolation operator
589 const auto [_Pi_1, pi_shape] = element1->interpolation_operator();
590 impl::mdspan_t<const U, 2> Pi_1(_Pi_1.data(), pi_shape);
591
592 using u_t = md::mdspan<U, std::dextents<std::size_t, 2>>;
593 using U_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
594 using J_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
595 using K_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
596 auto push_forward_fn0
597 = element0->basix_element().template map_fn<u_t, U_t, J_t, K_t>();
598
599 using v_t = md::mdspan<const T, std::dextents<std::size_t, 2>>;
600 using V_t = decltype(md::submdspan(mapped_values0, 0, md::full_extent,
601 md::full_extent));
602 auto pull_back_fn1
603 = element1->basix_element().template map_fn<V_t, v_t, K_t, J_t>();
604
605 // Iterate over mesh and interpolate on each cell
606 std::span<const T> array0 = u0.x()->array();
607 std::span<T> array1 = u1.x()->array();
608 if (cells0.size() != cells1.size())
609 throw std::runtime_error("Length of cells0 and cells1 must match.");
610 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
611 cell0_it != cells0.end() and cell1_it != cells1.end();
612 ++cell0_it, ++cell1_it)
613 {
614 // Get cell geometry (coordinate dofs)
615 auto x_dofs = md::submdspan(x_dofmap, *cell0_it, md::full_extent);
616 for (std::size_t i = 0; i < num_dofs_g; ++i)
617 {
618 const int pos = 3 * x_dofs[i];
619 for (int j = 0; j < gdim; ++j)
620 coord_dofs(i, j) = x_g[pos + j];
621 }
622
623 // Compute Jacobians and reference points for current cell
624 std::ranges::fill(J_b, 0);
625 for (std::size_t p = 0; p < Xshape[0]; ++p)
626 {
627 auto dphi
628 = md::submdspan(phi, std::pair(1, tdim + 1), p, md::full_extent, 0);
629 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
630 cmap.compute_jacobian(dphi, coord_dofs, _J);
631 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
632 cmap.compute_jacobian_inverse(_J, _K);
633 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
634 }
635
636 // Copy evaluated basis on reference, apply DOF transformations, and
637 // push forward to physical element
638 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
639 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
640 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
641 basis_reference0(k0, k1, k2)
642 = basis_derivatives_reference0(0, k0, k1, k2);
643
644 for (std::size_t p = 0; p < Xshape[0]; ++p)
645 {
646 apply_dof_transformation0(
647 std::span(basis_reference0_b.data() + p * dim0 * value_size_ref0,
648 dim0 * value_size_ref0),
649 cell_info0, *cell0_it, value_size_ref0);
650 }
651
652 for (std::size_t i = 0; i < basis0.extent(0); ++i)
653 {
654 auto _u = md::submdspan(basis0, i, md::full_extent, md::full_extent);
655 auto _U = md::submdspan(basis_reference0, i, md::full_extent,
656 md::full_extent);
657 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
658 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
659 push_forward_fn0(_u, _U, _J, detJ[i], _K);
660 }
661
662 // Copy expansion coefficients for v into local array
663 const int dof_bs0 = dofmap0->bs();
664 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
665 for (std::size_t i = 0; i < dofs0.size(); ++i)
666 for (int k = 0; k < dof_bs0; ++k)
667 coeffs0[dof_bs0 * i + k] = array0[dof_bs0 * dofs0[i] + k];
668
669 // Evaluate v at the interpolation points (physical space values)
670 using X = U; // geometry (real) type, independent of the value scalar T
671 for (std::size_t p = 0; p < Xshape[0]; ++p)
672 {
673 for (int k = 0; k < bs0; ++k)
674 {
675 for (std::size_t j = 0; j < value_size0; ++j)
676 {
677 T acc = 0;
678 for (std::size_t i = 0; i < dim0; ++i)
679 acc += coeffs0[bs0 * i + k] * static_cast<X>(basis0(p, i, j));
680 values0(p, 0, j * bs0 + k) = acc;
681 }
682 }
683 }
684
685 // Pull back the physical values to the u reference
686 for (std::size_t i = 0; i < values0.extent(0); ++i)
687 {
688 auto _u = md::submdspan(values0, i, md::full_extent, md::full_extent);
689 auto _U
690 = md::submdspan(mapped_values0, i, md::full_extent, md::full_extent);
691 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
692 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
693 pull_back_fn1(_U, _u, _K, 1.0 / detJ[i], _J);
694 }
695
696 auto values
697 = md::submdspan(mapped_values0, md::full_extent, 0, md::full_extent);
698 interpolation_apply(Pi_1, values, std::span(local1), bs1);
699 apply_inv_dof_transform1(local1, cell_info1, *cell1_it, 1);
700
701 // Copy local coefficients to the correct position in u dof array
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];
707 }
708}
709
721template <dolfinx::scalar T, std::floating_point 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,
726 std::span<T> coeffs)
727{
728 // Point evaluation element *and* the geometric map is the identity,
729 // e.g. not Piola mapped
730
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();
734
735 auto apply_inv_transpose_dof_transformation
736 = element.template dof_transformation_fn<T>(
738 std::vector<T> coeffs_b(num_scalar_dofs);
739 if (symmetric)
740 {
741 std::size_t matrix_size = 0;
742 while (matrix_size * matrix_size < fshape[0])
743 ++matrix_size;
744
745 // Loop over cells
746 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
747 {
748 // The entries of a symmetric matrix are numbered (for an
749 // example 4x4 element):
750 // 0 * * *
751 // 1 2 * *
752 // 3 4 5 *
753 // 6 7 8 9
754 // The loop extracts these elements. In this loop, row is the
755 // row of this matrix, and (k - rowstart) is the column
756 std::size_t row = 0;
757 std::size_t rowstart = 0;
758 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
759 std::size_t offset = std::distance(cells.begin(), cell_it);
760 for (int k = 0; k < element_bs; ++k)
761 {
762 if (k - rowstart > row)
763 {
764 ++row;
765 rowstart = k;
766 }
767
768 // num_scalar_dofs is the number of interpolation points per
769 // cell in this case (interpolation matrix is identity)
770 std::copy_n(
771 std::next(f.begin(), (row * matrix_size + k - rowstart) * fshape[1]
772 + offset * num_scalar_dofs),
773 num_scalar_dofs, coeffs_b.data());
774 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
775 1);
776 for (int i = 0; i < num_scalar_dofs; ++i)
777 {
778 const int dof = i * element_bs + k;
779 std::div_t pos = std::div(dof, dofmap_bs);
780 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
781 }
782 }
783 }
784 }
785 else
786 {
787 // Loop over cells
788 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
789 {
790 std::size_t offset = std::distance(cells.begin(), cell_it);
791 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
792 for (int k = 0; k < element_bs; ++k)
793 {
794 // num_scalar_dofs is the number of interpolation points per
795 // cell in this case (interpolation matrix is identity)
796 std::copy_n(
797 std::next(f.begin(), k * fshape[1] + offset * num_scalar_dofs),
798 num_scalar_dofs, coeffs_b.data());
799 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it,
800 1);
801 for (int i = 0; i < num_scalar_dofs; ++i)
802 {
803 const int dof = i * element_bs + k;
804 std::div_t pos = std::div(dof, dofmap_bs);
805 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
806 }
807 }
808 }
809 }
810}
811
823template <dolfinx::scalar T, std::floating_point U>
824void identity_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
825 const DofMap& dofmap,
826 mesh::CellRange auto&& cells,
827 std::span<const std::uint32_t> cell_info,
828 std::span<const T> f,
829 std::array<std::size_t, 2> fshape,
830 std::span<T> coeffs)
831{
832 // Not a point evaluation, but the geometric map is the identity,
833 // e.g. not Piola mapped
834
835 if (symmetric)
836 throw std::runtime_error("Interpolation into this element not supported.");
837
838 const int element_bs = element.block_size();
839 const int num_scalar_dofs = element.space_dimension() / element_bs;
840 const int dofmap_bs = dofmap.bs();
841
842 const int element_vs = element.reference_value_size();
843 if (element_vs > 1 and element_bs > 1)
844 throw std::runtime_error("Interpolation into this element not supported.");
845
846 // Get interpolation operator
847 const auto [_Pi, pi_shape] = element.interpolation_operator();
848 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
849 const std::size_t num_interp_points = Pi.extent(1);
850 assert(static_cast<int>(Pi.extent(0)) == num_scalar_dofs);
851
852 auto apply_inv_transpose_dof_transformation
853 = element.template dof_transformation_fn<T>(
855
856 // Loop over cells
857 std::vector<T> ref_data_b(num_interp_points);
858 md::mdspan<T, md::extents<std::size_t, md::dynamic_extent, 1>> ref_data(
859 ref_data_b.data(), num_interp_points, 1);
860 std::vector<T> coeffs_b(num_scalar_dofs);
861 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
862 {
863 std::size_t offset = std::distance(cells.begin(), cell_it);
864 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
865 for (int k = 0; k < element_bs; ++k)
866 {
867 for (int i = 0; i < element_vs; ++i)
868 {
869 std::copy_n(
870 std::next(f.begin(), (i + k) * fshape[1]
871 + offset * num_interp_points / element_vs),
872 num_interp_points / element_vs,
873 std::next(ref_data_b.begin(), i * num_interp_points / element_vs));
874 }
875
876 impl::interpolation_apply(Pi, ref_data, std::span(coeffs_b), 1);
877 apply_inv_transpose_dof_transformation(coeffs_b, cell_info, *cell_it, 1);
878 for (int i = 0; i < num_scalar_dofs; ++i)
879 {
880 const int dof = i * element_bs + k;
881 std::div_t pos = std::div(dof, dofmap_bs);
882 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
883 }
884 }
885 }
886}
887
900template <dolfinx::scalar T, std::floating_point U>
901void piola_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
902 const DofMap& dofmap, mesh::CellRange auto&& cells,
903 std::span<const std::uint32_t> cell_info,
904 std::span<const T> f,
905 std::array<std::size_t, 2> fshape,
906 const mesh::Mesh<U>& mesh, std::span<T> coeffs)
907{
908 if (symmetric)
909 throw std::runtime_error("Interpolation into this element not supported.");
910
911 const int gdim = mesh.geometry().dim();
912 assert(mesh.topology());
913 const int tdim = mesh.topology()->dim();
914
915 const int element_bs = element.block_size();
916 const int num_scalar_dofs = element.space_dimension() / element_bs;
917 const int value_size = element.reference_value_size();
918 const int dofmap_bs = dofmap.bs();
919
920 md::mdspan<const T, md::dextents<std::size_t, 2>> _f(f.data(), fshape);
921
922 // Get the interpolation points on the reference cells
923 const auto [X, Xshape] = element.interpolation_points();
924 if (X.empty())
925 {
926 throw std::runtime_error(
927 "Interpolation into this space is not yet supported.");
928 }
929
930 if (_f.extent(1) != cells.size() * Xshape[0])
931 throw std::runtime_error("Interpolation data has the wrong shape.");
932
933 // Get coordinate map
934 const CoordinateElement<U>& cmap = mesh.geometry().cmaps().front();
935
936 // Get geometry data
937 auto x_dofmap = mesh.geometry().dofmaps().front();
938 const int num_dofs_g = cmap.dim();
939 std::span<const U> x_g = mesh.geometry().x();
940
941 // Create data structures for Jacobian info
942 std::vector<U> J_b(Xshape[0] * gdim * tdim);
943 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
944 tdim);
945 std::vector<U> K_b(Xshape[0] * tdim * gdim);
946 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
947 gdim);
948 std::vector<U> detJ(Xshape[0]);
949 std::vector<U> det_scratch(2 * gdim * tdim);
950
951 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
952 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
953 num_dofs_g, gdim);
954 const std::size_t value_size_ref = element.reference_value_size();
955 std::vector<T> ref_data_b(Xshape[0] * 1 * value_size_ref);
956 md::mdspan<
957 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
958 ref_data(ref_data_b.data(), Xshape[0], 1, value_size_ref);
959
960 std::vector<T> _vals_b(Xshape[0] * 1 * value_size);
961 md::mdspan<
962 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
963 _vals(_vals_b.data(), Xshape[0], 1, value_size);
964
965 // Tabulate 1st derivative of shape functions at interpolation
966 // coords
967 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
968 std::vector<U> phi_b(
969 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
970 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
971 md::dynamic_extent, md::dynamic_extent, 1>>
972 phi(phi_b.data(), phi_shape);
973 cmap.tabulate(1, X, Xshape, phi_b);
974 auto dphi = md::submdspan(phi, std::pair(1, tdim + 1), md::full_extent,
975 md::full_extent, 0);
976
977 std::function<void(std::span<T>, std::span<const std::uint32_t>, std::int32_t,
978 int)>
979 apply_inv_trans_dof_transformation
980 = element.template dof_transformation_fn<T>(
982
983 // Get interpolation operator
984 const auto [_Pi, pi_shape] = element.interpolation_operator();
985 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
986
987 using u_t = md::mdspan<const T, md::dextents<std::size_t, 2>>;
988 using U_t
989 = decltype(md::submdspan(ref_data, 0, md::full_extent, md::full_extent));
990 using J_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
991 using K_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
992 auto pull_back_fn
993 = element.basix_element().template map_fn<U_t, u_t, J_t, K_t>();
994
995 std::vector<T> coeffs_b(num_scalar_dofs);
996 for (auto cell_it = cells.begin(); cell_it != cells.end(); ++cell_it)
997 {
998 auto x_dofs = md::submdspan(x_dofmap, *cell_it, md::full_extent);
999 for (int i = 0; i < num_dofs_g; ++i)
1000 {
1001 const int pos = 3 * x_dofs[i];
1002 for (int j = 0; j < gdim; ++j)
1003 coord_dofs(i, j) = x_g[pos + j];
1004 }
1005
1006 // Compute J, detJ and K
1007 std::ranges::fill(J_b, 0);
1008 for (std::size_t p = 0; p < Xshape[0]; ++p)
1009 {
1010 auto _dphi = md::submdspan(dphi, md::full_extent, p, md::full_extent);
1011 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
1012 cmap.compute_jacobian(_dphi, coord_dofs, _J);
1013 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
1014 cmap.compute_jacobian_inverse(_J, _K);
1015 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
1016 }
1017
1018 const std::size_t offset = std::distance(cells.begin(), cell_it);
1019 std::span<const std::int32_t> dofs = dofmap.cell_dofs(*cell_it);
1020 for (int k = 0; k < element_bs; ++k)
1021 {
1022 // Extract computed expression values for element block k
1023 for (int m = 0; m < value_size; ++m)
1024 {
1025 for (std::size_t k0 = 0; k0 < Xshape[0]; ++k0)
1026 {
1027 _vals(k0, 0, m)
1028 = f[fshape[1] * (k * value_size + m) + offset * Xshape[0] + k0];
1029 }
1030 }
1031
1032 // Get element degrees of freedom for block
1033 for (std::size_t i = 0; i < Xshape[0]; ++i)
1034 {
1035 auto _u = md::submdspan(_vals, i, md::full_extent, md::full_extent);
1036 auto _U = md::submdspan(ref_data, i, md::full_extent, md::full_extent);
1037 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
1038 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
1039 pull_back_fn(_U, _u, _K, 1.0 / detJ[i], _J);
1040 }
1041
1042 auto ref = md::submdspan(ref_data, md::full_extent, 0, md::full_extent);
1043 impl::interpolation_apply(Pi, ref, std::span(coeffs_b), element_bs);
1044 apply_inv_trans_dof_transformation(coeffs_b, cell_info, *cell_it, 1);
1045
1046 // Copy interpolation dofs into coefficient vector
1047 assert(coeffs_b.size() == static_cast<std::size_t>(num_scalar_dofs));
1048 for (int i = 0; i < num_scalar_dofs; ++i)
1049 {
1050 const int dof = i * element_bs + k;
1051 std::div_t pos = std::div(dof, dofmap_bs);
1052 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = coeffs_b[i];
1053 }
1054 }
1055 }
1056}
1057
1058//----------------------------------------------------------------------------
1059} // namespace impl
1060
1079template <std::floating_point T>
1081 const mesh::Geometry<T>& geometry0, const FiniteElement<T>& element0,
1082 const mesh::Mesh<T>& mesh1, mesh::CellRange auto&& cells, T padding)
1083{
1084 // Collect all the points at which values are needed to define the
1085 // interpolating function
1086 std::vector<T> coords = interpolation_coords(element0, geometry0, cells);
1087
1088 // Transpose interpolation coords
1089 std::vector<T> x(coords.size());
1090 std::size_t num_points = coords.size() / 3;
1091 for (std::size_t i = 0; i < num_points; ++i)
1092 for (std::size_t j = 0; j < 3; ++j)
1093 x[3 * i + j] = coords[i + j * num_points];
1094
1095 // Determine ownership of each point
1096 return geometry::determine_point_ownership<T>(mesh1, x, padding,
1097 std::nullopt);
1098}
1099
1100template <dolfinx::scalar T, std::floating_point U>
1101void interpolate(Function<T, U>& u, std::span<const T> f,
1102 std::array<std::size_t, 2> fshape,
1103 mesh::CellRange auto&& cells)
1104{
1105 // TODO: Index for mixed-topology, zero for now
1106 const int index = 0;
1107 auto element = u.function_space()->elements(index);
1108 assert(element);
1109 const int element_bs = element->block_size();
1110 if (int num_sub = element->num_sub_elements();
1111 num_sub > 0 and num_sub != element_bs)
1112 {
1113 throw std::runtime_error("Cannot directly interpolate a mixed space. "
1114 "Interpolate into subspaces.");
1115 }
1116
1117 // Get mesh
1118 assert(u.function_space());
1119 auto mesh = u.function_space()->mesh();
1120 assert(mesh);
1121
1122 if (fshape[0]
1123 != (std::size_t)u.function_space()->elements(index)->value_size()
1124 or f.size() != fshape[0] * fshape[1])
1125 {
1126 throw std::runtime_error("Interpolation data has the wrong shape/size.");
1127 }
1128
1129 spdlog::debug("Check for dof transformation");
1130 std::span<const std::uint32_t> cell_info;
1131 if (element->needs_dof_transformations())
1132 {
1133 mesh->topology_mutable()->create_entity_permutations();
1134 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
1135 }
1136
1137 // Get dofmap
1138 spdlog::debug("Interpolate: get dofmap");
1139 const auto dofmap = u.function_space()->dofmaps().at(index);
1140 assert(dofmap);
1141
1142 // Result will be stored to coeffs
1143 std::span<T> coeffs = u.x()->array();
1144
1145 if (bool symmetric = u.function_space()->symmetric();
1146 element->map_ident() and element->interpolation_ident())
1147 {
1148 // This assumes that any element with an identity interpolation
1149 // matrix is a point evaluation
1150 spdlog::debug("Interpolate: point evaluation");
1151 impl::point_evaluation(*element, symmetric, *dofmap, cells, cell_info, f,
1152 fshape, coeffs);
1153 }
1154 else if (element->map_ident())
1155 {
1156 spdlog::debug("Interpolate: identity-mapped evaluation");
1157 impl::identity_mapped_evaluation(*element, symmetric, *dofmap, cells,
1158 cell_info, f, fshape, coeffs);
1159 }
1160 else
1161 {
1162 spdlog::debug("Interpolate: Piola-mapped evaluation");
1163 impl::piola_mapped_evaluation(*element, symmetric, *dofmap, cells,
1164 cell_info, f, fshape, *mesh, coeffs);
1165 }
1166}
1167
1184template <dolfinx::scalar T, std::floating_point U>
1186 mesh::CellRange auto&& cells, double tol, int maxit,
1187 const geometry::PointOwnershipData<U>& interpolation_data)
1188{
1189 auto mesh1 = u1.function_space()->mesh();
1190 assert(mesh1);
1191 MPI_Comm comm = mesh1->comm();
1192 {
1193 assert(u0.function_space());
1194 auto mesh0 = u0.function_space()->mesh();
1195 assert(mesh0);
1196 int result;
1197 MPI_Comm_compare(comm, mesh0->comm(), &result);
1198 if (result == MPI_UNEQUAL)
1199 {
1200 throw std::runtime_error("Interpolation on different meshes is only "
1201 "supported on the same communicator.");
1202 }
1203 }
1204
1205 assert(mesh1->topology());
1206 auto cell_map = mesh1->topology()->index_map(mesh1->topology()->dim());
1207 assert(cell_map);
1208 auto element1 = u1.function_space()->element();
1209 assert(element1);
1210 const std::size_t value_size = element1->value_size();
1211
1212 const std::vector<int>& dest_ranks = interpolation_data.src_owner;
1213 const std::vector<int>& src_ranks = interpolation_data.dest_owners;
1214 const std::vector<U>& recv_points = interpolation_data.dest_points;
1215 const std::vector<std::int32_t>& evaluation_cells
1216 = interpolation_data.dest_cells;
1217
1218 // Evaluate the interpolating function where possible
1219 std::vector<T> send_values(recv_points.size() / 3 * value_size);
1220 u0.eval(recv_points, {recv_points.size() / 3, (std::size_t)3},
1221 evaluation_cells, send_values, {recv_points.size() / 3, value_size},
1222 tol, maxit);
1223
1224 // Send values back to owning process
1225 std::vector<T> values_b(dest_ranks.size() * value_size);
1226 md::mdspan<const T, md::dextents<std::size_t, 2>> _send_values(
1227 send_values.data(), src_ranks.size(), value_size);
1228 impl::scatter_values(comm, src_ranks, dest_ranks, _send_values,
1229 std::span(values_b));
1230
1231 // Transpose received data
1232 md::mdspan<const T, md::dextents<std::size_t, 2>> values(
1233 values_b.data(), dest_ranks.size(), value_size);
1234 std::vector<T> valuesT_b(value_size * dest_ranks.size());
1235 md::mdspan<T, md::dextents<std::size_t, 2>> valuesT(
1236 valuesT_b.data(), value_size, dest_ranks.size());
1237 for (std::size_t i = 0; i < values.extent(0); ++i)
1238 for (std::size_t j = 0; j < values.extent(1); ++j)
1239 valuesT(j, i) = values(i, j);
1240
1241 // Call local interpolation operator
1242 fem::interpolate<T>(u1, valuesT_b, {valuesT.extent(0), valuesT.extent(1)},
1243 cells);
1244}
1245
1262template <dolfinx::scalar T, std::floating_point U>
1264 const Function<T, U>& u0, mesh::CellRange auto&& cells0)
1265{
1266 if (cells0.size() != cells1.size())
1267 throw std::runtime_error("Length of cell lists do not match.");
1268
1269 auto V1 = u1.function_space();
1270 assert(V1);
1271 auto V0 = u0.function_space();
1272 assert(V0);
1273
1274 // Get elements and check value shape
1275 auto e0 = V0->element();
1276 assert(e0);
1277 auto e1 = V1->element();
1278 assert(e1);
1279 if (!std::ranges::equal(e0->value_shape(), e1->value_shape()))
1280 {
1281 throw std::runtime_error(
1282 "Interpolation: elements have different value dimensions");
1283 }
1284
1285 if (V1->mesh() == V0->mesh() and (e1 == e0 or *e1 == *e0))
1286 {
1287 // Same element and same mesh
1288 if (e1->block_size() != e0->block_size())
1289 throw std::runtime_error("Mismatch in element block size.");
1290
1291 // Get dofmaps
1292 std::shared_ptr<const DofMap> dofmap0 = V0->dofmap();
1293 assert(dofmap0);
1294 std::shared_ptr<const DofMap> dofmap1 = V1->dofmap();
1295 assert(dofmap1);
1296
1297 // Iterate over mesh and interpolate on each cell
1298 const int bs0 = dofmap0->bs();
1299 const int bs1 = dofmap1->bs();
1300 std::span<T> u1_array = u1.x()->array();
1301 std::span<const T> u0_array = u0.x()->array();
1302 assert(cells0.size() == cells1.size());
1303 for (auto cell0_it = cells0.begin(), cell1_it = cells1.begin();
1304 cell0_it != cells0.end() and cell1_it != cells1.end();
1305 ++cell0_it, ++cell1_it)
1306
1307 {
1308 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(*cell0_it);
1309 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(*cell1_it);
1310 assert(bs0 * dofs0.size() == bs1 * dofs1.size());
1311 for (std::size_t i = 0; i < dofs0.size(); ++i)
1312 {
1313 for (int k = 0; k < bs0; ++k)
1314 {
1315 int index = bs0 * i + k;
1316 std::div_t dv1 = std::div(index, bs1);
1317 u1_array[bs1 * dofs1[dv1.quot] + dv1.rem]
1318 = u0_array[bs0 * dofs0[i] + k];
1319 }
1320 }
1321 }
1322 }
1323 else if (e1->map_type() == e0->map_type())
1324 {
1325 // Different elements, same basis function map type
1326 impl::interpolate_same_map(u1, cells1, u0, cells0);
1327 }
1328 else
1329 {
1330 // Different elements with different maps for basis functions
1331 impl::interpolate_nonmatching_maps(u1, cells1, u0, cells0);
1332 }
1333}
1334
1344template <dolfinx::scalar T, std::floating_point U>
1346 std::ranges::input_range auto&& cells)
1347{
1348 assert(u1.function_space());
1349 assert(u0.function_space());
1350 if (u1.function_space()->mesh() == u0.function_space()->mesh())
1351 interpolate<T, U>(u1, cells, u0, cells);
1352 else
1353 throw std::runtime_error("Meshes do no match.");
1354}
1355
1365template <dolfinx::scalar T, std::floating_point U>
1367{
1368 assert(u1.function_space());
1369 assert(u0.function_space());
1370 if (auto V1 = u1.function_space(); V1 == u0.function_space())
1371 std::ranges::copy(u0.x()->array(), u1.x()->array().begin());
1372 else
1373 {
1374 auto mesh = V1->mesh();
1375 assert(mesh);
1376 assert(mesh->topology());
1377 auto map = mesh->topology()->index_map(mesh->topology()->dim());
1378 assert(map);
1379 std::int32_t num_cells = map->size_local() + map->num_ghosts();
1380 interpolate<T, U>(u1, u0, std::ranges::views::iota(0, num_cells));
1381 }
1382}
1383} // namespace dolfinx::fem
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:55
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:48
int dim() const
The dimension of the coordinate element space.
Definition CoordinateElement.cpp:205
Model of a finite element.
Definition FiniteElement.h:57
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:464
mesh::CellType cell_type() const noexcept
Cell shape that the element is defined on.
Definition FiniteElement.cpp:279
Definition Function.h:47
std::shared_ptr< const FunctionSpace< geometry_type > > function_space() const
Access the function space.
Definition Function.h:147
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:457
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:153
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
Definition interpolate.h:33
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:257
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64
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
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:1101
@ transpose
Transpose.
Definition FiniteElement.h:28
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:30
@ standard
Standard.
Definition FiniteElement.h:27
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:50
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)
Generate data needed to interpolate finite element fem::Function's across different meshes.
Definition interpolate.h:1080
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)
Given a set of points, determine which process is colliding, using the GJK algorithm on cells to dete...
Definition utils.h:683
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:30
std::vector< T > dest_points
Points that are owned by current process.
Definition utils.h:35
std::vector< std::int32_t > dest_cells
Definition utils.h:37
std::vector< int > dest_owners
Ranks that sent dest_points to current process.
Definition utils.h:34
std::vector< int > src_owner
Definition utils.h:31