DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
interpolate.h
1// Copyright (C) 2020-2024 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
50template <std::floating_point T>
51std::vector<T> interpolation_coords(const fem::FiniteElement<T>& element,
53 std::span<const std::int32_t> cells)
54{
55 // Find CoordinateElement appropriate to element
56 const std::vector<CoordinateElement<T>>& cmaps = geometry.cmaps();
57 mesh::CellType cell_type = element.cell_type();
58 auto it
59 = std::find_if(cmaps.begin(), cmaps.end(), [&cell_type](const auto& cm)
60 { return cell_type == cm.cell_shape(); });
61 if (it == cmaps.end())
62 throw std::runtime_error("Cannot find CoordinateElement for FiniteElement");
63 int index = std::distance(cmaps.begin(), it);
64
65 // Get geometry data and the element coordinate map
66 const std::size_t gdim = geometry.dim();
67 auto x_dofmap = geometry.dofmap(index);
68 std::span<const T> x_g = geometry.x();
69
70 const CoordinateElement<T>& cmap = cmaps.at(index);
71 const std::size_t num_dofs_g = cmap.dim();
72
73 // Get the interpolation points on the reference cells
74 const auto [X, Xshape] = element.interpolation_points();
75
76 // Evaluate coordinate element basis at reference points
77 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(0, Xshape[0]);
78 std::vector<T> phi_b(
79 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
80 md::mdspan<const T, md::extents<std::size_t, 1, md::dynamic_extent,
81 md::dynamic_extent, 1>>
82 phi_full(phi_b.data(), phi_shape);
83 cmap.tabulate(0, X, Xshape, phi_b);
84 auto phi = md::submdspan(phi_full, 0, md::full_extent, md::full_extent, 0);
85
86 // Push reference coordinates (X) forward to the physical coordinates
87 // (x) for each cell
88 std::vector<T> coordinate_dofs(num_dofs_g * gdim, 0);
89 std::vector<T> x(3 * (cells.size() * Xshape[0]), 0);
90 for (std::size_t c = 0; c < cells.size(); ++c)
91 {
92 // Get geometry data for current cell
93 auto x_dofs = md::submdspan(x_dofmap, cells[c], md::full_extent);
94 for (std::size_t i = 0; i < x_dofs.size(); ++i)
95 {
96 std::copy_n(std::next(x_g.begin(), 3 * x_dofs[i]), gdim,
97 std::next(coordinate_dofs.begin(), i * gdim));
98 }
99
100 // Push forward coordinates (X -> x)
101 for (std::size_t p = 0; p < Xshape[0]; ++p)
102 {
103 for (std::size_t j = 0; j < gdim; ++j)
104 {
105 T acc = 0;
106 for (std::size_t k = 0; k < num_dofs_g; ++k)
107 acc += phi(p, k) * coordinate_dofs[k * gdim + j];
108 x[j * (cells.size() * Xshape[0]) + c * Xshape[0] + p] = acc;
109 }
110 }
111 }
112
113 return x;
114}
115
132template <dolfinx::scalar T, std::floating_point U>
133void interpolate(Function<T, U>& u, std::span<const T> f,
134 std::array<std::size_t, 2> fshape,
135 std::span<const std::int32_t> cells);
136
137namespace impl
138{
140template <typename T, std::size_t D>
141using mdspan_t = md::mdspan<T, md::dextents<std::size_t, D>>;
142
162template <dolfinx::scalar T>
163void scatter_values(MPI_Comm comm, std::span<const std::int32_t> src_ranks,
164 std::span<const std::int32_t> dest_ranks,
165 mdspan_t<const T, 2> send_values, std::span<T> recv_values)
166{
167 const std::size_t block_size = send_values.extent(1);
168 assert(src_ranks.size() * block_size == send_values.size());
169 assert(recv_values.size() == dest_ranks.size() * block_size);
170
171 // Build unique set of the sorted src_ranks
172 std::vector<std::int32_t> out_ranks(src_ranks.size());
173 out_ranks.assign(src_ranks.begin(), src_ranks.end());
174 auto [unique_end, range_end] = std::ranges::unique(out_ranks);
175 out_ranks.erase(unique_end, range_end);
176 out_ranks.reserve(out_ranks.size() + 1);
177
178 // Remove negative entries from dest_ranks
179 std::vector<std::int32_t> in_ranks;
180 in_ranks.reserve(dest_ranks.size());
181 std::copy_if(dest_ranks.begin(), dest_ranks.end(),
182 std::back_inserter(in_ranks),
183 [](auto rank) { return rank >= 0; });
184
185 // Create unique set of sorted in-ranks
186 {
187 std::ranges::sort(in_ranks);
188 auto [unique_end, range_end] = std::ranges::unique(in_ranks);
189 in_ranks.erase(unique_end, range_end);
190 }
191 in_ranks.reserve(in_ranks.size() + 1);
192
193 // Create neighborhood communicator
194 MPI_Comm reverse_comm;
195 MPI_Dist_graph_create_adjacent(
196 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
197 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL, false, &reverse_comm);
198
199 std::vector<std::int32_t> comm_to_output;
200 std::vector<std::int32_t> recv_sizes(in_ranks.size());
201 recv_sizes.reserve(1);
202 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
203 {
204 // Build map from parent to neighborhood communicator ranks
205 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
206 rank_to_neighbor.reserve(in_ranks.size());
207 for (std::size_t i = 0; i < in_ranks.size(); i++)
208 rank_to_neighbor.push_back({in_ranks[i], i});
209 std::ranges::sort(rank_to_neighbor);
210
211 // Compute receive sizes
212 std::ranges::for_each(
213 dest_ranks,
214 [&rank_to_neighbor, &recv_sizes, block_size](auto rank)
215 {
216 if (rank >= 0)
217 {
218 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
219 std::ranges::less(),
220 [](auto e) { return e.first; });
221 assert(it != rank_to_neighbor.end() and it->first == rank);
222 recv_sizes[it->second] += block_size;
223 }
224 });
225
226 // Compute receiving offsets
227 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
228 std::next(recv_offsets.begin(), 1));
229
230 // Compute map from receiving values to position in recv_values
231 comm_to_output.resize(recv_offsets.back() / block_size);
232 std::vector<std::int32_t> recv_counter(recv_sizes.size(), 0);
233 for (std::size_t i = 0; i < dest_ranks.size(); ++i)
234 {
235 if (const std::int32_t rank = dest_ranks[i]; rank >= 0)
236 {
237 auto it = std::ranges::lower_bound(rank_to_neighbor, rank,
238 std::ranges::less(),
239 [](auto e) { return e.first; });
240 assert(it != rank_to_neighbor.end() and it->first == rank);
241 int insert_pos = recv_offsets[it->second] + recv_counter[it->second];
242 comm_to_output[insert_pos / block_size] = i * block_size;
243 recv_counter[it->second] += block_size;
244 }
245 }
246 }
247
248 std::vector<std::int32_t> send_sizes(out_ranks.size());
249 send_sizes.reserve(1);
250 {
251 // Compute map from parent MPI rank to neighbor rank for outgoing
252 // data. `out_ranks` is sorted, so rank_to_neighbor will be sorted
253 // too.
254 std::vector<std::pair<std::int32_t, std::int32_t>> rank_to_neighbor;
255 rank_to_neighbor.reserve(out_ranks.size());
256 for (std::size_t i = 0; i < out_ranks.size(); i++)
257 rank_to_neighbor.push_back({out_ranks[i], i});
258
259 // Compute send sizes. As `src_ranks` is sorted, we can move 'start'
260 // in search forward.
261 auto start = rank_to_neighbor.begin();
262 std::ranges::for_each(
263 src_ranks,
264 [&rank_to_neighbor, &send_sizes, block_size, &start](auto rank)
265 {
266 auto it = std::ranges::lower_bound(start, rank_to_neighbor.end(),
267 rank, std::ranges::less(),
268 [](auto e) { return e.first; });
269 assert(it != rank_to_neighbor.end() and it->first == rank);
270 send_sizes[it->second] += block_size;
271 start = it;
272 });
273 }
274
275 // Compute sending offsets
276 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
277 std::partial_sum(send_sizes.begin(), send_sizes.end(),
278 std::next(send_offsets.begin(), 1));
279
280 // Send values to dest ranks
281 std::vector<T> values(recv_offsets.back());
282 values.reserve(1);
283 MPI_Neighbor_alltoallv(send_values.data_handle(), send_sizes.data(),
284 send_offsets.data(), dolfinx::MPI::mpi_t<T>,
285 values.data(), recv_sizes.data(), recv_offsets.data(),
286 dolfinx::MPI::mpi_t<T>, reverse_comm);
287 MPI_Comm_free(&reverse_comm);
288
289 // Insert values received from neighborhood communicator in output
290 // span
291 std::ranges::fill(recv_values, T(0));
292 for (std::size_t i = 0; i < comm_to_output.size(); i++)
293 {
294 auto vals = std::next(recv_values.begin(), comm_to_output[i]);
295 auto vals_from = std::next(values.begin(), i * block_size);
296 std::copy_n(vals_from, block_size, vals);
297 }
298};
299
308template <MDSpan U, MDSpan V, dolfinx::scalar T>
309void interpolation_apply(U&& Pi, V&& data, std::span<T> coeffs, int bs)
310{
311 using X = typename dolfinx::scalar_value_t<T>;
312
313 // Compute coefficients = Pi * x (matrix-vector multiply)
314 if (bs == 1)
315 {
316 assert(data.extent(0) * data.extent(1) == Pi.extent(1));
317 for (std::size_t i = 0; i < Pi.extent(0); ++i)
318 {
319 coeffs[i] = 0.0;
320 for (std::size_t k = 0; k < data.extent(1); ++k)
321 for (std::size_t j = 0; j < data.extent(0); ++j)
322 coeffs[i]
323 += static_cast<X>(Pi(i, k * data.extent(0) + j)) * data(j, k);
324 }
325 }
326 else
327 {
328 const std::size_t cols = Pi.extent(1);
329 assert(data.extent(0) == Pi.extent(1));
330 assert(data.extent(1) == bs);
331 for (int k = 0; k < bs; ++k)
332 {
333 for (std::size_t i = 0; i < Pi.extent(0); ++i)
334 {
335 T acc = 0;
336 for (std::size_t j = 0; j < cols; ++j)
337 acc += static_cast<X>(Pi(i, j)) * data(j, k);
338 coeffs[bs * i + k] = acc;
339 }
340 }
341 }
342}
343
363template <dolfinx::scalar T, std::floating_point U>
364void interpolate_same_map(Function<T, U>& u1, const Function<T, U>& u0,
365 std::span<const std::int32_t> cells1,
366 std::span<const std::int32_t> cells0)
367{
368 auto V0 = u0.function_space();
369 assert(V0);
370 auto V1 = u1.function_space();
371 assert(V1);
372 auto mesh0 = V0->mesh();
373 assert(mesh0);
374
375 auto mesh1 = V1->mesh();
376 assert(mesh1);
377
378 auto element0 = V0->element();
379 assert(element0);
380 auto element1 = V1->element();
381 assert(element1);
382
383 assert(mesh0->topology()->dim());
384 const int tdim = mesh0->topology()->dim();
385 auto map = mesh0->topology()->index_map(tdim);
386 assert(map);
387 std::span<T> u1_array = u1.x()->mutable_array();
388 std::span<const T> u0_array = u0.x()->array();
389
390 std::span<const std::uint32_t> cell_info0;
391 std::span<const std::uint32_t> cell_info1;
392 if (element1->needs_dof_transformations()
393 or element0->needs_dof_transformations())
394 {
395 mesh0->topology_mutable()->create_entity_permutations();
396 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
397 mesh1->topology_mutable()->create_entity_permutations();
398 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
399 }
400
401 // Get dofmaps
402 auto dofmap1 = V1->dofmap();
403 auto dofmap0 = V0->dofmap();
404
405 // Get block sizes and dof transformation operators
406 const int bs1 = dofmap1->bs();
407 const int bs0 = dofmap0->bs();
408 auto apply_dof_transformation = element0->template dof_transformation_fn<T>(
410 auto apply_inverse_dof_transform
411 = element1->template dof_transformation_fn<T>(
413
414 // Create working array
415 std::vector<T> local0(element0->space_dimension());
416 std::vector<T> local1(element1->space_dimension());
417
418 // Create interpolation operator
419 const auto [i_m, im_shape]
420 = element1->create_interpolation_operator(*element0);
421
422 // Iterate over mesh and interpolate on each cell
423 using X = typename dolfinx::scalar_value_t<T>;
424 for (std::size_t c = 0; c < cells0.size(); c++)
425 {
426 // Pack and transform cell dofs to reference ordering
427 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(cells0[c]);
428 for (std::size_t i = 0; i < dofs0.size(); ++i)
429 for (int k = 0; k < bs0; ++k)
430 local0[bs0 * i + k] = u0_array[bs0 * dofs0[i] + k];
431
432 apply_dof_transformation(local0, cell_info0, cells0[c], 1);
433
434 // FIXME: Get compile-time ranges from Basix
435 // Apply interpolation operator
436 std::ranges::fill(local1, 0);
437 for (std::size_t i = 0; i < im_shape[0]; ++i)
438 for (std::size_t j = 0; j < im_shape[1]; ++j)
439 local1[i] += static_cast<X>(i_m[im_shape[1] * i + j]) * local0[j];
440
441 apply_inverse_dof_transform(local1, cell_info1, cells1[c], 1);
442 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(cells1[c]);
443 for (std::size_t i = 0; i < dofs1.size(); ++i)
444 for (int k = 0; k < bs1; ++k)
445 u1_array[bs1 * dofs1[i] + k] = local1[bs1 * i + k];
446 }
447}
448
463template <dolfinx::scalar T, std::floating_point U>
464void interpolate_nonmatching_maps(Function<T, U>& u1,
465 std::span<const std::int32_t> cells1,
466 const Function<T, U>& u0,
467 std::span<const std::int32_t> cells0)
468{
469 // Get mesh
470 auto V0 = u0.function_space();
471 assert(V0);
472 auto mesh0 = V0->mesh();
473 assert(mesh0);
474
475 // Mesh dims
476 const int tdim = mesh0->topology()->dim();
477 const int gdim = mesh0->geometry().dim();
478
479 // Get elements
480 auto V1 = u1.function_space();
481 assert(V1);
482 auto mesh1 = V1->mesh();
483 assert(mesh1);
484 auto element0 = V0->element();
485 assert(element0);
486 auto element1 = V1->element();
487 assert(element1);
488
489 std::span<const std::uint32_t> cell_info0;
490 std::span<const std::uint32_t> cell_info1;
491 if (element1->needs_dof_transformations()
492 or element0->needs_dof_transformations())
493 {
494 mesh0->topology_mutable()->create_entity_permutations();
495 cell_info0 = std::span(mesh0->topology()->get_cell_permutation_info());
496 mesh1->topology_mutable()->create_entity_permutations();
497 cell_info1 = std::span(mesh1->topology()->get_cell_permutation_info());
498 }
499
500 // Get dofmaps
501 auto dofmap0 = V0->dofmap();
502 auto dofmap1 = V1->dofmap();
503
504 const auto [X, Xshape] = element1->interpolation_points();
505
506 // Get block sizes and dof transformation operators
507 const int bs0 = element0->block_size();
508 const int bs1 = element1->block_size();
509 auto apply_dof_transformation0 = element0->template dof_transformation_fn<U>(
511 auto apply_inverse_dof_transform1
512 = element1->template dof_transformation_fn<T>(
514
515 // Get sizes of elements
516 const std::size_t dim0 = element0->space_dimension() / bs0;
517 const std::size_t value_size_ref0 = element0->reference_value_size();
518 const std::size_t value_size0 = V0->element()->reference_value_size();
519
520 const CoordinateElement<U>& cmap = mesh0->geometry().cmap();
521 auto x_dofmap = mesh0->geometry().dofmap();
522 const std::size_t num_dofs_g = cmap.dim();
523 std::span<const U> x_g = mesh0->geometry().x();
524
525 // (0) is derivative index, (1) is the point index, (2) is the basis
526 // function index and (3) is the basis function component.
527
528 // Evaluate coordinate map basis at reference interpolation points
529 const std::array<std::size_t, 4> phi_shape
530 = cmap.tabulate_shape(1, Xshape[0]);
531 std::vector<U> phi_b(
532 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
533 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
534 md::dynamic_extent, md::dynamic_extent, 1>>
535 phi(phi_b.data(), phi_shape);
536 cmap.tabulate(1, X, Xshape, phi_b);
537
538 // Evaluate v basis functions at reference interpolation points
539 const auto [_basis_derivatives_reference0, b0shape]
540 = element0->tabulate(X, Xshape, 0);
541 md::mdspan<const U, std::extents<std::size_t, 1, md::dynamic_extent,
542 md::dynamic_extent, md::dynamic_extent>>
543 basis_derivatives_reference0(_basis_derivatives_reference0.data(),
544 b0shape);
545
546 // Create working arrays
547 std::vector<T> local1(element1->space_dimension());
548 std::vector<T> coeffs0(element0->space_dimension());
549
550 std::vector<U> basis0_b(Xshape[0] * dim0 * value_size0);
551 md::mdspan<U, std::dextents<std::size_t, 3>> basis0(
552 basis0_b.data(), Xshape[0], dim0, value_size0);
553
554 std::vector<U> basis_reference0_b(Xshape[0] * dim0 * value_size_ref0);
555 md::mdspan<U, std::dextents<std::size_t, 3>> basis_reference0(
556 basis_reference0_b.data(), Xshape[0], dim0, value_size_ref0);
557
558 std::vector<T> values0_b(Xshape[0] * 1 * V1->element()->value_size());
559 md::mdspan<
560 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
561 values0(values0_b.data(), Xshape[0], 1, V1->element()->value_size());
562
563 std::vector<T> mapped_values_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 mapped_values0(mapped_values_b.data(), Xshape[0], 1,
567 V1->element()->value_size());
568
569 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
570 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
571 num_dofs_g, gdim);
572
573 std::vector<U> J_b(Xshape[0] * gdim * tdim);
574 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
575 tdim);
576 std::vector<U> K_b(Xshape[0] * tdim * gdim);
577 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
578 gdim);
579 std::vector<U> detJ(Xshape[0]);
580 std::vector<U> det_scratch(2 * gdim * tdim);
581
582 // Get interpolation operator
583 const auto [_Pi_1, pi_shape] = element1->interpolation_operator();
584 impl::mdspan_t<const U, 2> Pi_1(_Pi_1.data(), pi_shape);
585
586 using u_t = md::mdspan<U, std::dextents<std::size_t, 2>>;
587 using U_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
588 using J_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
589 using K_t = md::mdspan<const U, std::dextents<std::size_t, 2>>;
590 auto push_forward_fn0
591 = element0->basix_element().template map_fn<u_t, U_t, J_t, K_t>();
592
593 using v_t = md::mdspan<const T, std::dextents<std::size_t, 2>>;
594 using V_t = decltype(md::submdspan(mapped_values0, 0, md::full_extent,
595 md::full_extent));
596 auto pull_back_fn1
597 = element1->basix_element().template map_fn<V_t, v_t, K_t, J_t>();
598
599 // Iterate over mesh and interpolate on each cell
600 std::span<const T> array0 = u0.x()->array();
601 std::span<T> array1 = u1.x()->mutable_array();
602 for (std::size_t c = 0; c < cells0.size(); c++)
603 {
604 // Get cell geometry (coordinate dofs)
605 auto x_dofs = md::submdspan(x_dofmap, cells0[c], md::full_extent);
606 for (std::size_t i = 0; i < num_dofs_g; ++i)
607 {
608 const int pos = 3 * x_dofs[i];
609 for (int j = 0; j < gdim; ++j)
610 coord_dofs(i, j) = x_g[pos + j];
611 }
612
613 // Compute Jacobians and reference points for current cell
614 std::ranges::fill(J_b, 0);
615 for (std::size_t p = 0; p < Xshape[0]; ++p)
616 {
617 auto dphi
618 = md::submdspan(phi, std::pair(1, tdim + 1), p, md::full_extent, 0);
619 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
620 cmap.compute_jacobian(dphi, coord_dofs, _J);
621 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
622 cmap.compute_jacobian_inverse(_J, _K);
623 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
624 }
625
626 // Copy evaluated basis on reference, apply DOF transformations, and
627 // push forward to physical element
628 for (std::size_t k0 = 0; k0 < basis_reference0.extent(0); ++k0)
629 for (std::size_t k1 = 0; k1 < basis_reference0.extent(1); ++k1)
630 for (std::size_t k2 = 0; k2 < basis_reference0.extent(2); ++k2)
631 basis_reference0(k0, k1, k2)
632 = basis_derivatives_reference0(0, k0, k1, k2);
633
634 for (std::size_t p = 0; p < Xshape[0]; ++p)
635 {
636 apply_dof_transformation0(
637 std::span(basis_reference0_b.data() + p * dim0 * value_size_ref0,
638 dim0 * value_size_ref0),
639 cell_info0, cells0[c], value_size_ref0);
640 }
641
642 for (std::size_t i = 0; i < basis0.extent(0); ++i)
643 {
644 auto _u = md::submdspan(basis0, i, md::full_extent, md::full_extent);
645 auto _U = md::submdspan(basis_reference0, i, md::full_extent,
646 md::full_extent);
647 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
648 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
649 push_forward_fn0(_u, _U, _J, detJ[i], _K);
650 }
651
652 // Copy expansion coefficients for v into local array
653 const int dof_bs0 = dofmap0->bs();
654 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(cells0[c]);
655 for (std::size_t i = 0; i < dofs0.size(); ++i)
656 for (int k = 0; k < dof_bs0; ++k)
657 coeffs0[dof_bs0 * i + k] = array0[dof_bs0 * dofs0[i] + k];
658
659 // Evaluate v at the interpolation points (physical space values)
660 using X = typename dolfinx::scalar_value_t<T>;
661 for (std::size_t p = 0; p < Xshape[0]; ++p)
662 {
663 for (int k = 0; k < bs0; ++k)
664 {
665 for (std::size_t j = 0; j < value_size0; ++j)
666 {
667 T acc = 0;
668 for (std::size_t i = 0; i < dim0; ++i)
669 acc += coeffs0[bs0 * i + k] * static_cast<X>(basis0(p, i, j));
670 values0(p, 0, j * bs0 + k) = acc;
671 }
672 }
673 }
674
675 // Pull back the physical values to the u reference
676 for (std::size_t i = 0; i < values0.extent(0); ++i)
677 {
678 auto _u = md::submdspan(values0, i, md::full_extent, md::full_extent);
679 auto _U
680 = md::submdspan(mapped_values0, i, md::full_extent, md::full_extent);
681 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
682 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
683 pull_back_fn1(_U, _u, _K, 1.0 / detJ[i], _J);
684 }
685
686 auto values
687 = md::submdspan(mapped_values0, md::full_extent, 0, md::full_extent);
688 interpolation_apply(Pi_1, values, std::span(local1), bs1);
689 apply_inverse_dof_transform1(local1, cell_info1, cells1[c], 1);
690
691 // Copy local coefficients to the correct position in u dof array
692 const int dof_bs1 = dofmap1->bs();
693 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(c);
694 for (std::size_t i = 0; i < dofs1.size(); ++i)
695 for (int k = 0; k < dof_bs1; ++k)
696 array1[dof_bs1 * dofs1[i] + k] = local1[dof_bs1 * i + k];
697 }
698}
699
710template <dolfinx::scalar T, std::floating_point U>
711void point_evaluation(const FiniteElement<U>& element, bool symmetric,
712 const DofMap& dofmap, std::span<const std::int32_t> cells,
713 std::span<const std::uint32_t> cell_info,
714 std::span<const T> f, std::array<std::size_t, 2> fshape,
715 std::span<T> coeffs)
716{
717 // Point evaluation element *and* the geometric map is the identity,
718 // e.g. not Piola mapped
719
720 const int element_bs = element.block_size();
721 const int num_scalar_dofs = element.space_dimension() / element_bs;
722 const int dofmap_bs = dofmap.bs();
723
724 std::vector<T> _coeffs(num_scalar_dofs);
725
726 auto apply_inv_transpose_dof_transformation
727 = element.template dof_transformation_fn<T>(
729
730 if (symmetric)
731 {
732 std::size_t matrix_size = 0;
733 while (matrix_size * matrix_size < fshape[0])
734 ++matrix_size;
735
736 spdlog::info("Loop over cells");
737 // Loop over cells
738 for (std::size_t c = 0; c < cells.size(); ++c)
739 {
740 // The entries of a symmetric matrix are numbered (for an
741 // example 4x4 element):
742 // 0 * * *
743 // 1 2 * *
744 // 3 4 5 *
745 // 6 7 8 9
746 // The loop extracts these elements. In this loop, row is the
747 // row of this matrix, and (k - rowstart) is the column
748 std::size_t row = 0;
749 std::size_t rowstart = 0;
750 const std::int32_t cell = cells[c];
751 std::span<const std::int32_t> dofs = dofmap.cell_dofs(cell);
752 for (int k = 0; k < element_bs; ++k)
753 {
754 if (k - rowstart > row)
755 {
756 ++row;
757 rowstart = k;
758 }
759
760 // num_scalar_dofs is the number of interpolation points per
761 // cell in this case (interpolation matrix is identity)
762 std::copy_n(
763 std::next(f.begin(), (row * matrix_size + k - rowstart) * fshape[1]
764 + c * num_scalar_dofs),
765 num_scalar_dofs, _coeffs.begin());
766 apply_inv_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
767 for (int i = 0; i < num_scalar_dofs; ++i)
768 {
769 const int dof = i * element_bs + k;
770 std::div_t pos = std::div(dof, dofmap_bs);
771 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
772 }
773 }
774 }
775 }
776 else
777 {
778 // Loop over cells
779 for (std::size_t c = 0; c < cells.size(); ++c)
780 {
781 const std::int32_t cell = cells[c];
782 std::span<const std::int32_t> dofs = dofmap.cell_dofs(cell);
783 for (int k = 0; k < element_bs; ++k)
784 {
785 // num_scalar_dofs is the number of interpolation points per
786 // cell in this case (interpolation matrix is identity)
787 std::copy_n(std::next(f.begin(), k * fshape[1] + c * num_scalar_dofs),
788 num_scalar_dofs, _coeffs.begin());
789 apply_inv_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
790 for (int i = 0; i < num_scalar_dofs; ++i)
791 {
792 const int dof = i * element_bs + k;
793 std::div_t pos = std::div(dof, dofmap_bs);
794 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
795 }
796 }
797 }
798 }
799}
800
811template <dolfinx::scalar T, std::floating_point U>
812void identity_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
813 const DofMap& dofmap,
814 std::span<const std::int32_t> cells,
815 std::span<const std::uint32_t> cell_info,
816 std::span<const T> f,
817 std::array<std::size_t, 2> fshape,
818 std::span<T> coeffs)
819{
820 // Not a point evaluation, but the geometric map is the identity,
821 // e.g. not Piola mapped
822
823 const int element_bs = element.block_size();
824 const int num_scalar_dofs = element.space_dimension() / element_bs;
825 const int dofmap_bs = dofmap.bs();
826
827 std::vector<T> _coeffs(num_scalar_dofs);
828
829 if (symmetric)
830 {
831 throw std::runtime_error("Interpolation into this element not supported.");
832 }
833
834 const int element_vs = element.reference_value_size();
835
836 if (element_vs > 1 and element_bs > 1)
837 {
838 throw std::runtime_error("Interpolation into this element not supported.");
839 }
840
841 // Get interpolation operator
842 const auto [_Pi, pi_shape] = element.interpolation_operator();
843 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
844 const std::size_t num_interp_points = Pi.extent(1);
845 assert(Pi.extent(0) == num_scalar_dofs);
846
847 auto apply_inv_transpose_dof_transformation
848 = element.template dof_transformation_fn<T>(
850
851 // Loop over cells
852 std::vector<T> ref_data_b(num_interp_points);
853 md::mdspan<T, md::extents<std::size_t, md::dynamic_extent, 1>> ref_data(
854 ref_data_b.data(), num_interp_points, 1);
855 for (std::size_t c = 0; c < cells.size(); ++c)
856 {
857 const std::int32_t cell = cells[c];
858 std::span<const std::int32_t> dofs = dofmap.cell_dofs(cell);
859 for (int k = 0; k < element_bs; ++k)
860 {
861 for (int i = 0; i < element_vs; ++i)
862 {
863 std::copy_n(
864 std::next(f.begin(),
865 (i + k) * fshape[1] + c * num_interp_points / element_vs),
866 num_interp_points / element_vs,
867 std::next(ref_data_b.begin(), i * num_interp_points / element_vs));
868 }
869 impl::interpolation_apply(Pi, ref_data, std::span(_coeffs), 1);
870 apply_inv_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
871 for (int i = 0; i < num_scalar_dofs; ++i)
872 {
873 const int dof = i * element_bs + k;
874 std::div_t pos = std::div(dof, dofmap_bs);
875 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
876 }
877 }
878 }
879}
880
892template <dolfinx::scalar T, std::floating_point U>
893void piola_mapped_evaluation(const FiniteElement<U>& element, bool symmetric,
894 const DofMap& dofmap,
895 std::span<const std::int32_t> cells,
896 std::span<const std::uint32_t> cell_info,
897 std::span<const T> f,
898 std::array<std::size_t, 2> fshape,
899 const mesh::Mesh<U>& mesh, std::span<T> coeffs)
900{
901
902 const int gdim = mesh.geometry().dim();
903 const int tdim = mesh.topology()->dim();
904
905 const int element_bs = element.block_size();
906 const int num_scalar_dofs = element.space_dimension() / element_bs;
907 const int value_size = element.reference_value_size();
908 const int dofmap_bs = dofmap.bs();
909
910 std::vector<T> _coeffs(num_scalar_dofs);
911 md::mdspan<const T, md::dextents<std::size_t, 2>> _f(f.data(), fshape);
912
913 if (symmetric)
914 {
915 throw std::runtime_error("Interpolation into this element not supported.");
916 }
917 // Get the interpolation points on the reference cells
918 const auto [X, Xshape] = element.interpolation_points();
919 if (X.empty())
920 {
921 throw std::runtime_error(
922 "Interpolation into this space is not yet supported.");
923 }
924
925 if (_f.extent(1) != cells.size() * Xshape[0])
926 throw std::runtime_error("Interpolation data has the wrong shape.");
927
928 // Get coordinate map
929 const CoordinateElement<U>& cmap = mesh.geometry().cmap();
930
931 // Get geometry data
932 auto x_dofmap = mesh.geometry().dofmap();
933 const int num_dofs_g = cmap.dim();
934 std::span<const U> x_g = mesh.geometry().x();
935
936 // Create data structures for Jacobian info
937 std::vector<U> J_b(Xshape[0] * gdim * tdim);
938 md::mdspan<U, std::dextents<std::size_t, 3>> J(J_b.data(), Xshape[0], gdim,
939 tdim);
940 std::vector<U> K_b(Xshape[0] * tdim * gdim);
941 md::mdspan<U, std::dextents<std::size_t, 3>> K(K_b.data(), Xshape[0], tdim,
942 gdim);
943 std::vector<U> detJ(Xshape[0]);
944 std::vector<U> det_scratch(2 * gdim * tdim);
945
946 std::vector<U> coord_dofs_b(num_dofs_g * gdim);
947 md::mdspan<U, std::dextents<std::size_t, 2>> coord_dofs(coord_dofs_b.data(),
948 num_dofs_g, gdim);
949 const std::size_t value_size_ref = element.reference_value_size();
950 std::vector<T> ref_data_b(Xshape[0] * 1 * value_size_ref);
951 md::mdspan<
952 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
953 ref_data(ref_data_b.data(), Xshape[0], 1, value_size_ref);
954
955 std::vector<T> _vals_b(Xshape[0] * 1 * value_size);
956 md::mdspan<
957 T, md::extents<std::size_t, md::dynamic_extent, 1, md::dynamic_extent>>
958 _vals(_vals_b.data(), Xshape[0], 1, value_size);
959
960 // Tabulate 1st derivative of shape functions at interpolation
961 // coords
962 std::array<std::size_t, 4> phi_shape = cmap.tabulate_shape(1, Xshape[0]);
963 std::vector<U> phi_b(
964 std::reduce(phi_shape.begin(), phi_shape.end(), 1, std::multiplies{}));
965 md::mdspan<const U, md::extents<std::size_t, md::dynamic_extent,
966 md::dynamic_extent, md::dynamic_extent, 1>>
967 phi(phi_b.data(), phi_shape);
968 cmap.tabulate(1, X, Xshape, phi_b);
969 auto dphi = md::submdspan(phi, std::pair(1, tdim + 1), md::full_extent,
970 md::full_extent, 0);
971
972 const std::function<void(std::span<T>, std::span<const std::uint32_t>,
973 std::int32_t, int)>
974 apply_inverse_transpose_dof_transformation
975 = element.template dof_transformation_fn<T>(
977
978 // Get interpolation operator
979 const auto [_Pi, pi_shape] = element.interpolation_operator();
980 md::mdspan<const U, std::dextents<std::size_t, 2>> Pi(_Pi.data(), pi_shape);
981
982 using u_t = md::mdspan<const T, md::dextents<std::size_t, 2>>;
983 using U_t
984 = decltype(md::submdspan(ref_data, 0, md::full_extent, md::full_extent));
985 using J_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
986 using K_t = md::mdspan<const U, md::dextents<std::size_t, 2>>;
987 auto pull_back_fn
988 = element.basix_element().template map_fn<U_t, u_t, J_t, K_t>();
989
990 for (std::size_t c = 0; c < cells.size(); ++c)
991 {
992 const std::int32_t cell = cells[c];
993 auto x_dofs = md::submdspan(x_dofmap, cell, md::full_extent);
994 for (int i = 0; i < num_dofs_g; ++i)
995 {
996 const int pos = 3 * x_dofs[i];
997 for (int j = 0; j < gdim; ++j)
998 coord_dofs(i, j) = x_g[pos + j];
999 }
1000
1001 // Compute J, detJ and K
1002 std::ranges::fill(J_b, 0);
1003 for (std::size_t p = 0; p < Xshape[0]; ++p)
1004 {
1005 auto _dphi = md::submdspan(dphi, md::full_extent, p, md::full_extent);
1006 auto _J = md::submdspan(J, p, md::full_extent, md::full_extent);
1007 cmap.compute_jacobian(_dphi, coord_dofs, _J);
1008 auto _K = md::submdspan(K, p, md::full_extent, md::full_extent);
1009 cmap.compute_jacobian_inverse(_J, _K);
1010 detJ[p] = cmap.compute_jacobian_determinant(_J, det_scratch);
1011 }
1012
1013 std::span<const std::int32_t> dofs = dofmap.cell_dofs(cell);
1014 for (int k = 0; k < element_bs; ++k)
1015 {
1016 // Extract computed expression values for element block k
1017 for (int m = 0; m < value_size; ++m)
1018 {
1019 for (std::size_t k0 = 0; k0 < Xshape[0]; ++k0)
1020 {
1021 _vals(k0, 0, m)
1022 = f[fshape[1] * (k * value_size + m) + c * Xshape[0] + k0];
1023 }
1024 }
1025
1026 // Get element degrees of freedom for block
1027 for (std::size_t i = 0; i < Xshape[0]; ++i)
1028 {
1029 auto _u = md::submdspan(_vals, i, md::full_extent, md::full_extent);
1030 auto _U = md::submdspan(ref_data, i, md::full_extent, md::full_extent);
1031 auto _K = md::submdspan(K, i, md::full_extent, md::full_extent);
1032 auto _J = md::submdspan(J, i, md::full_extent, md::full_extent);
1033 pull_back_fn(_U, _u, _K, 1.0 / detJ[i], _J);
1034 }
1035
1036 auto ref = md::submdspan(ref_data, md::full_extent, 0, md::full_extent);
1037 impl::interpolation_apply(Pi, ref, std::span(_coeffs), element_bs);
1038 apply_inverse_transpose_dof_transformation(_coeffs, cell_info, cell, 1);
1039
1040 // Copy interpolation dofs into coefficient vector
1041 assert(_coeffs.size() == num_scalar_dofs);
1042 for (int i = 0; i < num_scalar_dofs; ++i)
1043 {
1044 const int dof = i * element_bs + k;
1045 std::div_t pos = std::div(dof, dofmap_bs);
1046 coeffs[dofmap_bs * dofs[pos.quot] + pos.rem] = _coeffs[i];
1047 }
1048 }
1049 }
1050}
1051
1052//----------------------------------------------------------------------------
1053} // namespace impl
1054
1055template <dolfinx::scalar T, std::floating_point U>
1056void interpolate(Function<T, U>& u, std::span<const T> f,
1057 std::array<std::size_t, 2> fshape,
1058 std::span<const std::int32_t> cells)
1059{
1060 // TODO: Index for mixed-topology, zero for now
1061 const int index = 0;
1062 auto element = u.function_space()->elements(index);
1063 assert(element);
1064 const int element_bs = element->block_size();
1065 if (int num_sub = element->num_sub_elements();
1066 num_sub > 0 and num_sub != element_bs)
1067 {
1068 throw std::runtime_error("Cannot directly interpolate a mixed space. "
1069 "Interpolate into subspaces.");
1070 }
1071
1072 // Get mesh
1073 assert(u.function_space());
1074 auto mesh = u.function_space()->mesh();
1075 assert(mesh);
1076
1077 if (fshape[0]
1078 != (std::size_t)u.function_space()->elements(index)->value_size()
1079 or f.size() != fshape[0] * fshape[1])
1080 throw std::runtime_error("Interpolation data has the wrong shape/size.");
1081
1082 spdlog::debug("Check for dof transformation");
1083 std::span<const std::uint32_t> cell_info;
1084 if (element->needs_dof_transformations())
1085 {
1086 mesh->topology_mutable()->create_entity_permutations();
1087 cell_info = std::span(mesh->topology()->get_cell_permutation_info());
1088 }
1089
1090 spdlog::debug("Interpolate: get dofmap");
1091 // Get dofmap
1092 const auto dofmap = u.function_space()->dofmaps(index);
1093 assert(dofmap);
1094
1095 // Result will be stored to coeffs
1096 std::span<T> coeffs = u.x()->mutable_array();
1097
1098 const bool symmetric = u.function_space()->symmetric();
1099 if (element->map_ident() && element->interpolation_ident())
1100 {
1101 spdlog::debug("Interpolate: point evaluation");
1102 // This assumes that any element with an identity interpolation matrix
1103 // is a point evaluation
1104 impl::point_evaluation(*element, symmetric, *dofmap, cells, cell_info, f,
1105 fshape, coeffs);
1106 }
1107 else if (element->map_ident())
1108 {
1109 spdlog::debug("Interpolate: identity-mapped evaluation");
1110 impl::identity_mapped_evaluation(*element, symmetric, *dofmap, cells,
1111 cell_info, f, fshape, coeffs);
1112 }
1113 else
1114 {
1115 spdlog::debug("Interpolate: Piola-mapped evaluation");
1116 impl::piola_mapped_evaluation(*element, symmetric, *dofmap, cells,
1117 cell_info, f, fshape, *mesh, coeffs);
1118 }
1119}
1120
1139template <std::floating_point T>
1141 const mesh::Geometry<T>& geometry0, const FiniteElement<T>& element0,
1142 const mesh::Mesh<T>& mesh1, std::span<const std::int32_t> cells, T padding)
1143{
1144 // Collect all the points at which values are needed to define the
1145 // interpolating function
1146 std::vector<T> coords = interpolation_coords(element0, geometry0, cells);
1147
1148 // Transpose interpolation coords
1149 std::vector<T> x(coords.size());
1150 std::size_t num_points = coords.size() / 3;
1151 for (std::size_t i = 0; i < num_points; ++i)
1152 for (std::size_t j = 0; j < 3; ++j)
1153 x[3 * i + j] = coords[i + j * num_points];
1154
1155 // Determine ownership of each point
1156 return geometry::determine_point_ownership<T>(mesh1, x, padding);
1157}
1158
1171template <dolfinx::scalar T, std::floating_point U>
1173 std::span<const std::int32_t> cells,
1174 const geometry::PointOwnershipData<U>& interpolation_data)
1175{
1176 auto mesh = u.function_space()->mesh();
1177 assert(mesh);
1178 MPI_Comm comm = mesh->comm();
1179 {
1180 auto mesh_v = v.function_space()->mesh();
1181 assert(mesh_v);
1182 int result;
1183 MPI_Comm_compare(comm, mesh_v->comm(), &result);
1184 if (result == MPI_UNEQUAL)
1185 {
1186 throw std::runtime_error("Interpolation on different meshes is only "
1187 "supported on the same communicator.");
1188 }
1189 }
1190
1191 assert(mesh->topology());
1192 auto cell_map = mesh->topology()->index_map(mesh->topology()->dim());
1193 assert(cell_map);
1194 auto element_u = u.function_space()->element();
1195 assert(element_u);
1196 const std::size_t value_size = u.function_space()->element()->value_size();
1197
1198 const std::vector<int>& dest_ranks = interpolation_data.src_owner;
1199 const std::vector<int>& src_ranks = interpolation_data.dest_owners;
1200 const std::vector<U>& recv_points = interpolation_data.dest_points;
1201 const std::vector<std::int32_t>& evaluation_cells
1202 = interpolation_data.dest_cells;
1203
1204 // Evaluate the interpolating function where possible
1205 std::vector<T> send_values(recv_points.size() / 3 * value_size);
1206 v.eval(recv_points, {recv_points.size() / 3, (std::size_t)3},
1207 evaluation_cells, send_values, {recv_points.size() / 3, value_size});
1208
1209 // Send values back to owning process
1210 std::vector<T> values_b(dest_ranks.size() * value_size);
1211 md::mdspan<const T, md::dextents<std::size_t, 2>> _send_values(
1212 send_values.data(), src_ranks.size(), value_size);
1213 impl::scatter_values(comm, src_ranks, dest_ranks, _send_values,
1214 std::span(values_b));
1215
1216 // Transpose received data
1217 md::mdspan<const T, md::dextents<std::size_t, 2>> values(
1218 values_b.data(), dest_ranks.size(), value_size);
1219 std::vector<T> valuesT_b(value_size * dest_ranks.size());
1220 md::mdspan<T, md::dextents<std::size_t, 2>> valuesT(
1221 valuesT_b.data(), value_size, dest_ranks.size());
1222 for (std::size_t i = 0; i < values.extent(0); ++i)
1223 for (std::size_t j = 0; j < values.extent(1); ++j)
1224 valuesT(j, i) = values(i, j);
1225
1226 // Call local interpolation operator
1227 fem::interpolate<T>(u, valuesT_b, {valuesT.extent(0), valuesT.extent(1)},
1228 cells);
1229}
1230
1246template <dolfinx::scalar T, std::floating_point U>
1247void interpolate(Function<T, U>& u1, std::span<const std::int32_t> cells1,
1248 const Function<T, U>& u0, std::span<const std::int32_t> cells0)
1249{
1250 if (cells0.size() != cells1.size())
1251 throw std::runtime_error("Length of cell lists do not match.");
1252
1253 assert(u1.function_space());
1254 assert(u0.function_space());
1255 auto mesh = u1.function_space()->mesh();
1256 assert(mesh);
1257 assert(cells0.size() == cells1.size());
1258
1259 auto cell_map0 = mesh->topology()->index_map(mesh->topology()->dim());
1260 assert(cell_map0);
1261 std::size_t num_cells0 = cell_map0->size_local() + cell_map0->num_ghosts();
1262 if (u1.function_space() == u0.function_space()
1263 and cells1.size() == num_cells0)
1264 {
1265 // Same function spaces and on whole mesh
1266 std::span<T> u1_array = u1.x()->mutable_array();
1267 std::span<const T> u0_array = u0.x()->array();
1268 std::ranges::copy(u0_array, u1_array.begin());
1269 }
1270 else
1271 {
1272 // Get elements and check value shape
1273 auto fs0 = u0.function_space();
1274 auto element0 = fs0->element();
1275 assert(element0);
1276 auto fs1 = u1.function_space();
1277 auto element1 = fs1->element();
1278 assert(element1);
1279 if (!std::ranges::equal(fs0->element()->value_shape(),
1280 fs1->element()->value_shape()))
1281 {
1282 throw std::runtime_error(
1283 "Interpolation: elements have different value dimensions");
1284 }
1285
1286 if (element1 == element0 or *element1 == *element0)
1287 {
1288 // Same element, different dofmaps (or just a subset of cells)
1289 const int tdim = mesh->topology()->dim();
1290 auto cell_map1 = mesh->topology()->index_map(tdim);
1291 assert(cell_map1);
1292 assert(element1->block_size() == element0->block_size());
1293
1294 // Get dofmaps
1295 std::shared_ptr<const DofMap> dofmap0 = u0.function_space()->dofmap();
1296 assert(dofmap0);
1297 std::shared_ptr<const DofMap> dofmap1 = u1.function_space()->dofmap();
1298 assert(dofmap1);
1299
1300 std::span<T> u1_array = u1.x()->mutable_array();
1301 std::span<const T> u0_array = u0.x()->array();
1302
1303 // Iterate over mesh and interpolate on each cell
1304 const int bs0 = dofmap0->bs();
1305 const int bs1 = dofmap1->bs();
1306 for (std::size_t c = 0; c < cells1.size(); ++c)
1307 {
1308 std::span<const std::int32_t> dofs0 = dofmap0->cell_dofs(cells0[c]);
1309 std::span<const std::int32_t> dofs1 = dofmap1->cell_dofs(cells1[c]);
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 (element1->map_type() == element0->map_type())
1324 {
1325 // Different elements, same basis function map type
1326 impl::interpolate_same_map(u1, u0, cells1, 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}
1335} // 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:144
std::shared_ptr< const la::Vector< value_type > > x() const
Underlying vector (const version).
Definition Function.h:150
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
Definition interpolate.h:33
MPI_Datatype mpi_t
Retrieves the MPI data type associated to the provided type.
Definition MPI.h:280
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64
void cells(la::SparsityPattern &pattern, std::array< std::span< const std::int32_t >, 2 > cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.cpp:16
Finite element method functionality.
Definition assemble_expression_impl.h:23
geometry::PointOwnershipData< T > create_interpolation_data(const mesh::Geometry< T > &geometry0, const FiniteElement< T > &element0, const mesh::Mesh< T > &mesh1, std::span< const std::int32_t > cells, T padding)
Generate data needed to interpolate finite element fem::Function's across different meshes.
Definition interpolate.h:1140
void interpolate(Function< T, U > &u, std::span< const T > f, std::array< std::size_t, 2 > fshape, std::span< const std::int32_t > cells)
Interpolate an evaluated expression f(x) in a finite element space.
Definition interpolate.h:1056
@ transpose
Transpose.
Definition FiniteElement.h:28
@ inverse_transpose
Transpose inverse.
Definition FiniteElement.h:30
@ standard
Standard.
Definition FiniteElement.h:27
@ cell
Cell.
Definition Form.h:40
std::vector< T > interpolation_coords(const fem::FiniteElement< T > &element, const mesh::Geometry< T > &geometry, std::span< const std::int32_t > cells)
Compute the evaluation points in the physical space at which an expression should be computed to inte...
Definition interpolate.h:51
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:22
PointOwnershipData< T > determine_point_ownership(const mesh::Mesh< T > &mesh, std::span< const T > points, T padding)
Given a set of points, determine which process is colliding, using the GJK algorithm on cells to dete...
Definition utils.h:686
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