SimiLie
Loading...
Searching...
No Matches
reduction_and_reconstruction.hpp
1// SPDX-FileCopyrightText: 2026 Baptiste Legouix
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#include <array>
7
8#include <ddc/ddc.hpp>
9
10#include <similie/misc/clamp_to_domain.hpp>
11#include <similie/misc/factorial.hpp>
12#include <similie/misc/small_matrix.hpp>
13#include <similie/misc/specialization.hpp>
14#include <similie/misc/type_seq_conversion.hpp>
15#include <similie/tensor/antisymmetric_tensor.hpp>
16#include <similie/tensor/character.hpp>
17#include <similie/tensor/full_tensor.hpp>
18#include <similie/tensor/prime.hpp>
19#include <similie/tensor/tensor_impl.hpp>
20
21#include "volume.hpp"
22
23
24namespace sil {
25
26namespace exterior {
27
28namespace detail {
29
30template <class ElemType>
31struct FlatNaturalElemRank;
32
33template <class... Tags>
34struct FlatNaturalElemRank<ddc::DiscreteElement<Tags...>>
35{
36 static constexpr std::size_t value = (FlatNaturalElemRank<Tags>::value + ... + 0);
37};
38
39template <class Tag>
40struct FlatNaturalElemRank
41{
42 static constexpr std::size_t value = 1;
43};
44
45template <class ElemType, std::size_t N>
46KOKKOS_FUNCTION void copy_flat_natural_elem_ids(
47 std::array<std::size_t, N>& ids,
48 std::size_t& offset,
49 ElemType const& elem)
50{
51 auto const entries = ddc::detail::array(elem);
52 if constexpr (std::tuple_size_v<decltype(entries)> == 0) {
53 return;
54 } else {
55 using EntryType = std::remove_cvref_t<decltype(entries[0])>;
56 if constexpr (std::is_same_v<EntryType, std::size_t>) {
57 for (std::size_t i = 0; i < entries.size(); ++i) {
58 ids[offset++] = entries[i];
59 }
60 } else {
61 for (std::size_t i = 0; i < entries.size(); ++i) {
62 copy_flat_natural_elem_ids(ids, offset, entries[i]);
63 }
64 }
65 }
66}
67
68template <class ElemType>
69KOKKOS_FUNCTION auto flat_natural_elem_ids(ElemType const& elem)
70{
71 std::array<std::size_t, FlatNaturalElemRank<ElemType>::value> ids {};
72 std::size_t offset = 0;
73 copy_flat_natural_elem_ids(ids, offset, elem);
74 return ids;
75}
76
77template <class ElemType, std::size_t N>
78KOKKOS_FUNCTION void assign_flat_natural_elem_ids(
79 ElemType& elem,
80 std::array<std::size_t, N> const& ids,
81 std::size_t& offset)
82{
83 auto entries = ddc::detail::array(elem);
84 if constexpr (std::tuple_size_v<decltype(entries)> == 0) {
85 return;
86 } else {
87 using EntryType = std::remove_cvref_t<decltype(entries[0])>;
88 if constexpr (std::is_same_v<EntryType, std::size_t>) {
89 for (std::size_t i = 0; i < entries.size(); ++i) {
90 entries[i] = ids[offset++];
91 }
92 ddc::detail::array(elem) = entries;
93 } else {
94 for (std::size_t i = 0; i < entries.size(); ++i) {
95 assign_flat_natural_elem_ids(entries[i], ids, offset);
96 }
97 ddc::detail::array(elem) = entries;
98 }
99 }
100}
101
102template <class ElemType, std::size_t N>
103KOKKOS_FUNCTION ElemType natural_elem_from_flat_ids(std::array<std::size_t, N> const& ids)
104{
105 static_assert(
106 FlatNaturalElemRank<ElemType>::value == N,
107 "Flat natural element ids must match the element rank.");
108 ElemType elem;
109 std::size_t offset = 0;
110 assign_flat_natural_elem_ids(elem, ids, offset);
111 return elem;
112}
113
114template <misc::Specialization<ddc::detail::TypeSeq> Indices>
115struct ReductionTargetIndex;
116
117template <>
118struct ReductionTargetIndex<ddc::detail::TypeSeq<>>
119{
120 using type = tensor::Covariant<tensor::ScalarIndex>;
121};
122
123template <tensor::TensorNatIndex Index>
124struct ReductionTargetIndex<ddc::detail::TypeSeq<Index>>
125{
126 using type = Index;
127};
128
129template <tensor::TensorNatIndex... Index>
130 requires(sizeof...(Index) > 1)
131struct ReductionTargetIndex<ddc::detail::TypeSeq<Index...>>
132{
133 using type = tensor::TensorAntisymmetricIndex<Index...>;
134};
135
136template <std::size_t N>
137KOKKOS_FUNCTION bool has_unique_reduction_ids(std::array<std::size_t, N> const& ids)
138{
139 for (std::size_t i = 0; i < N; ++i) {
140 for (std::size_t j = i + 1; j < N; ++j) {
141 if (ids[i] == ids[j]) {
142 return false;
143 }
144 }
145 }
146 return true;
147}
148
149template <std::size_t N>
150KOKKOS_FUNCTION bool have_same_reduction_ids(
151 std::array<std::size_t, N> const& lhs,
152 std::array<std::size_t, N> const& rhs)
153{
154 for (std::size_t i = 0; i < N; ++i) {
155 if (lhs[i] != rhs[i]) {
156 return false;
157 }
158 }
159 return true;
160}
161
162template <std::size_t K>
163KOKKOS_FUNCTION double reduction_determinant(std::array<double, K * K> const& matrix)
164{
165 if constexpr (K == 0) {
166 return 1.;
167 } else if constexpr (K == 1) {
168 return matrix[0];
169 } else {
170 double det = 0.;
171 for (std::size_t col = 0; col < K; ++col) {
172 std::array<double, (K - 1) * (K - 1)> minor {};
173 for (std::size_t row = 1; row < K; ++row) {
174 std::size_t minor_col = 0;
175 for (std::size_t source_col = 0; source_col < K; ++source_col) {
176 if (source_col == col) {
177 continue;
178 }
179 minor[(row - 1) * (K - 1) + minor_col] = matrix[row * K + source_col];
180 ++minor_col;
181 }
182 }
183 double const sign = (col % 2 == 0) ? 1. : -1.;
184 det += sign * matrix[col] * reduction_determinant<K - 1>(minor);
185 }
186 return det;
187 }
188}
189
190template <std::size_t N, std::size_t K>
191KOKKOS_FUNCTION std::array<std::size_t, K> combination_from_rank(std::size_t rank)
192{
193 std::array<std::size_t, K> ids {};
194 if constexpr (K == 0) {
195 return ids;
196 } else {
197 std::size_t next_candidate = 0;
198 for (std::size_t i = 0; i < K; ++i) {
199 for (std::size_t candidate = next_candidate; candidate < N; ++candidate) {
200 std::size_t const remaining = K - i - 1;
201 std::size_t const remaining_space = N - candidate - 1;
202 std::size_t const count
203 = (remaining == 0) ? 1
204 : misc::binomial_coefficient(remaining_space, remaining);
205 if (rank < count) {
206 ids[i] = candidate;
207 next_candidate = candidate + 1;
208 break;
209 }
210 rank -= count;
211 }
212 }
213 return ids;
214 }
215}
216
217template <std::size_t N, std::size_t K>
218KOKKOS_FUNCTION std::size_t combination_rank(std::array<std::size_t, K> const& ids)
219{
220 if constexpr (K == 0) {
221 return 0;
222 } else {
223 std::size_t rank = 0;
224 std::size_t next_candidate = 0;
225 for (std::size_t i = 0; i < K; ++i) {
226 for (std::size_t candidate = next_candidate; candidate < ids[i]; ++candidate) {
227 std::size_t const remaining = K - i - 1;
228 std::size_t const remaining_space = N - candidate - 1;
229 rank += (remaining == 0) ? 1
230 : misc::binomial_coefficient(remaining_space, remaining);
231 }
232 next_candidate = ids[i] + 1;
233 }
234 return rank;
235 }
236}
237
238template <std::size_t N, std::size_t M>
239KOKKOS_FUNCTION bool hodge_has_unique_ids(std::array<std::size_t, M> const& ids)
240{
241 if constexpr (M > 0) {
242 for (std::size_t i = 0; i < M; ++i) {
243 if (ids[i] >= N) {
244 return false;
245 }
246 for (std::size_t j = i + 1; j < M; ++j) {
247 if (ids[i] == ids[j]) {
248 return false;
249 }
250 }
251 }
252 }
253 return true;
254}
255
256template <std::size_t N, std::size_t M1, std::size_t M2>
257KOKKOS_FUNCTION bool hodge_is_complete_permutation(
258 std::array<std::size_t, M1> const& source_ids,
259 std::array<std::size_t, M2> const& target_ids)
260{
261 std::array<int, N> counts {};
262 for (std::size_t id : source_ids) {
263 if (id >= N) {
264 return false;
265 }
266 counts[id]++;
267 }
268 for (std::size_t id : target_ids) {
269 if (id >= N) {
270 return false;
271 }
272 counts[id]++;
273 }
274 for (int count : counts) {
275 if (count != 1) {
276 return false;
277 }
278 }
279 return true;
280}
281
282template <std::size_t N, std::size_t M1, std::size_t M2>
283KOKKOS_FUNCTION int hodge_permutation_sign(
284 std::array<std::size_t, M1> const& source_ids,
285 std::array<std::size_t, M2> const& target_ids)
286{
287 std::array<std::size_t, N> permutation {};
288 for (std::size_t i = 0; i < M1; ++i) {
289 permutation[i] = source_ids[i];
290 }
291 if constexpr (M2 > 0) {
292 for (std::size_t i = 0; i < M2; ++i) {
293 permutation[M1 + i] = target_ids[i];
294 }
295 }
296
297 bool odd = false;
298 for (std::size_t i = 0; i < N; ++i) {
299 for (std::size_t j = i + 1; j < N; ++j) {
300 odd = (permutation[i] > permutation[j]) != odd;
301 }
302 }
303 return odd ? -1 : 1;
304}
305
306template <std::size_t N, std::size_t M>
307KOKKOS_FUNCTION std::array<std::size_t, N - M> hodge_complement_ids(
308 std::array<std::size_t, M> const& ids)
309{
310 std::array<std::size_t, N - M> complement {};
311 std::size_t complement_id = 0;
312 for (std::size_t i = 0; i < N; ++i) {
313 bool found = false;
314 for (std::size_t id : ids) {
315 found = found || (id == i);
316 }
317 if (!found) {
318 complement[complement_id++] = i;
319 }
320 }
321 return complement;
322}
323
324template <class PositionIndex, class PositionType, class BatchElem, std::size_t K>
325KOKKOS_FUNCTION double primal_reconstruction_diagonal(
326 PositionType position,
327 BatchElem elem,
328 std::array<std::size_t, K> const& ids)
329{
330 if constexpr (K == 0) {
331 return 1.;
332 } else {
333 std::array<double, K * K> jacobian_matrix {};
334 for (std::size_t row = 0; row < K; ++row) {
335 for (std::size_t col = 0; col < K; ++col) {
336 std::array<double, PositionIndex::size()> const edge
337 = sil::exterior::detail::edge_vector<
338 PositionIndex>(position, elem, ids[col]);
339 jacobian_matrix[row * K + col] = edge[ids[row]];
340 }
341 }
342 double const jacobian = reduction_determinant<K>(jacobian_matrix);
343 if (Kokkos::abs(jacobian) < 1e-14) {
344 return 0.;
345 }
346 return 1. / jacobian;
347 }
348}
349
350template <std::size_t N, std::size_t K, class MetricType, class BatchElem>
351KOKKOS_FUNCTION double continuous_hodge_value_from_ids(
352 MetricType metric,
353 BatchElem elem,
354 std::array<std::size_t, K> const& source_ids,
355 std::array<std::size_t, N - K> const& target_ids)
356{
357 if (!hodge_has_unique_ids<N>(source_ids) || !hodge_has_unique_ids<N>(target_ids)
358 || !hodge_is_complete_permutation<N>(source_ids, target_ids)) {
359 return 0.;
360 }
361
362 std::array<std::size_t, K> const complement = hodge_complement_ids<N>(target_ids);
363 using MetricIndex
364 = ddc::type_seq_element_t<0, ddc::to_type_seq_t<typename MetricType::indices_domain_t>>;
365 using MetricIndex1 = tensor::metric_index_1<MetricIndex>;
366 using MetricIndex2 = tensor::metric_index_2<MetricIndex>;
367 std::array<double, N * N> metric_alloc {};
368 std::array<double, N * N> determinant_metric_alloc {};
369 std::array<double, N * N> inverse_metric_alloc {};
370 std::array<double, N * N> workspace_alloc {};
371 std::array<double, K * K> submatrix_alloc {};
372 for (std::size_t i = 0; i < N; ++i) {
373 for (std::size_t j = 0; j < N; ++j) {
374 metric_alloc[i * N + j] = metric.get(metric.access_element(
375 elem,
376 ddc::DiscreteElement<MetricIndex1, MetricIndex2>(i, j)));
377 }
378 }
379 determinant_metric_alloc = metric_alloc;
380
381 auto determinant_metric_view = misc::math::matrix_view<
382 double,
383 typename MetricType::memory_space>(determinant_metric_alloc.data(), N, N);
384 auto metric_view = misc::math::
386 double const determinant = misc::math::determinant(determinant_metric_view);
387 auto inverse_metric_view = misc::math::matrix_view<
388 double,
389 typename MetricType::memory_space>(inverse_metric_alloc.data(), N, N);
390 auto workspace = misc::math::
392 if (!misc::math::invert(inverse_metric_view, metric_view, workspace)) {
393 return 0.;
394 }
395
396 return Kokkos::sqrt(Kokkos::abs(determinant))
397 * static_cast<double>(hodge_permutation_sign<N>(complement, target_ids))
399 K>(inverse_metric_view, source_ids, complement, submatrix_alloc)
400 / misc::factorial(K);
401}
402
403template <
404 CellComplex Complex,
405 std::size_t N,
406 std::size_t K,
407 class MetricType,
408 class PositionType,
409 class BatchElem>
410KOKKOS_FUNCTION double legacy_discrete_hodge_value_from_ids(
411 MetricType metric,
412 PositionType position,
413 BatchElem elem,
414 std::array<std::size_t, K> const& source_ids,
415 std::array<std::size_t, N - K> const& target_ids)
416{
417 if (!hodge_has_unique_ids<N>(source_ids) || !hodge_has_unique_ids<N>(target_ids)
418 || !hodge_is_complete_permutation<N>(source_ids, target_ids)) {
419 return 0.;
420 }
421 double const primal_volume
422 = SimplexVolume<CellComplex::Primal, N, MetricType, PositionType, BatchElem>::
423 template run<K>(metric, position, elem, source_ids);
424 if (primal_volume == 0.) {
425 return 0.;
426 }
427
428 return static_cast<double>(hodge_permutation_sign<N>(source_ids, target_ids))
429 * DualSimplexVolume<Complex, N, MetricType, PositionType, BatchElem>::template run<
430 K>(metric, position, elem, source_ids)
431 / (primal_volume * misc::factorial(K));
432}
433
434template <class ReductionNaturalElemType, std::size_t N1, std::size_t N2>
435KOKKOS_FUNCTION ReductionNaturalElemType merge_reduction_natural_elems(
436 std::array<std::size_t, N1> const& first_ids,
437 std::array<std::size_t, N2> const& second_ids)
438{
439 std::array<std::size_t, N1 + N2> reduction_ids {};
440 for (std::size_t i = 0; i < N1; ++i) {
441 reduction_ids[i] = first_ids[i];
442 }
443 for (std::size_t i = 0; i < N2; ++i) {
444 reduction_ids[N1 + i] = second_ids[i];
445 }
446 return natural_elem_from_flat_ids<ReductionNaturalElemType>(reduction_ids);
447}
448
449} // namespace detail
450
451template <misc::Specialization<ddc::detail::TypeSeq> Indices>
452using reduction_index_t = typename detail::ReductionTargetIndex<Indices>::type;
453
454template <misc::Specialization<ddc::detail::TypeSeq> Indices>
455using reduction_domain_t = ddc::detail::convert_type_seq_to_discrete_domain_t<ddc::type_seq_merge_t<
456 ddc::detail::TypeSeq<misc::convert_type_seq_to_t<
459 ddc::detail::TypeSeq<reduction_index_t<Indices>>>>;
460
461template <misc::Specialization<ddc::detail::TypeSeq> Indices>
463 = ddc::detail::convert_type_seq_to_discrete_domain_t<ddc::type_seq_merge_t<
464 ddc::detail::TypeSeq<reduction_index_t<Indices>>,
465 ddc::detail::TypeSeq<reduction_index_t<tensor::primes<Indices>>>>>;
466
467template <
470 class BatchElem,
472struct Reduction
482 using position_index_type = ddc::
483 type_seq_element_t<0, ddc::to_type_seq_t<typename PositionType::indices_domain_t>>;
484
485 template <
486 misc::Specialization<tensor::Tensor> ReductionTensorType,
488 KOKKOS_FUNCTION static void run(
489 ReductionTensorType reduced_tensor,
490 FormTensorType form_tensor,
491 PositionType position,
492 BatchElem elem)
493 {
494 using reduction_accessor_t
496 using reduction_natural_elem_type =
497 typename reduction_accessor_t::natural_domain_t::discrete_element_type;
498 using form_natural_elem_type =
499 typename FormTensorType::accessor_t::natural_domain_t::discrete_element_type;
500 if constexpr (target_index_type::rank() == 0) {
501 reduced_tensor.mem(ddc::DiscreteElement<target_index_type>(0))
502 = form_tensor.get(form_tensor.access_element(form_natural_elem_type()));
503 } else {
504 [[maybe_unused]] sil::tensor::TensorAccessor<source_index_type> source_accessor;
505 ddc::device_for_each(reduced_tensor.domain(), [&](auto target_mem_elem) {
506 auto const target_natural_elem
507 = reduced_tensor.canonical_natural_element(target_mem_elem);
508 double reduced_value = 0.;
509 ddc::device_for_each(
510 source_accessor.natural_domain(),
511 [&](auto source_natural_elem) {
512 reduction_natural_elem_type reduction_natural_elem;
513 auto const source_ids = ddc::detail::array(source_natural_elem);
514 auto const target_ids = ddc::detail::array(target_natural_elem);
515 reduction_natural_elem = detail::merge_reduction_natural_elems<
516 reduction_natural_elem_type>(source_ids, target_ids);
517
518 form_natural_elem_type form_natural_elem;
519 ddc::detail::array(form_natural_elem)
520 = ddc::detail::array(source_natural_elem);
521
522 reduced_value += value(position, elem, reduction_natural_elem)
523 * form_tensor.get(
524 form_tensor.access_element(form_natural_elem));
525 });
526 reduced_tensor.mem(target_mem_elem) = reduced_value;
527 });
528 }
529 }
530
531 template <
532 misc::Specialization<tensor::Tensor> ReductionTensorType,
535 KOKKOS_FUNCTION static void run(
536 ReductionTensorType reduced_tensor,
537 FormTensorType form_tensor,
538 MetricType metric,
539 PositionType position,
540 BatchElem elem)
541 {
542 if constexpr (Complex == CellComplex::Primal) {
543 run(reduced_tensor, form_tensor, position, elem);
544 } else {
545 static_assert(
547 "Metric-aware reduction is only implemented for the circumcentric dual "
548 "complex.");
549
550 constexpr std::size_t N = position_index_type::size();
551 constexpr std::size_t Q = target_index_type::rank();
552 constexpr std::size_t K = N - Q;
553 constexpr std::size_t NBASIS = misc::binomial_coefficient(N, Q);
554 static_assert(
555 NBASIS == misc::binomial_coefficient(N, K),
556 "Complementary basis sizes must match.");
557
558 std::array<double, NBASIS * NBASIS> coeff_from_primal_alloc {};
559 std::array<double, NBASIS * NBASIS> coeff_from_primal_inverse_alloc {};
560 std::array<double, NBASIS * NBASIS> old_hodge_alloc {};
561 std::array<double, NBASIS * NBASIS> dual_reduction_alloc {};
562
563 for (std::size_t primal_id = 0; primal_id < NBASIS; ++primal_id) {
564 std::array<std::size_t, K> const primal_ids
565 = detail::combination_from_rank<N, K>(primal_id);
566 double const reconstruction_diag = detail::primal_reconstruction_diagonal<
567 position_index_type>(position, elem, primal_ids);
568 for (std::size_t source_id = 0; source_id < NBASIS; ++source_id) {
569 std::array<std::size_t, Q> const source_ids
570 = detail::combination_from_rank<N, Q>(source_id);
571 coeff_from_primal_alloc[source_id * NBASIS + primal_id]
572 = detail::continuous_hodge_value_from_ids<
573 N,
574 K>(metric, elem, primal_ids, source_ids)
575 * reconstruction_diag;
576 }
577 for (std::size_t target_id = 0; target_id < NBASIS; ++target_id) {
578 std::array<std::size_t, Q> const target_ids
579 = detail::combination_from_rank<N, Q>(target_id);
580 old_hodge_alloc[target_id * NBASIS + primal_id]
581 = detail::legacy_discrete_hodge_value_from_ids<
582 Complex,
583 N,
584 K>(metric, position, elem, primal_ids, target_ids);
585 }
586 }
587
588 std::array<double, NBASIS * NBASIS> workspace_alloc {};
589 auto coeff_from_primal_view = misc::math::matrix_view<
590 double,
591 typename MetricType::
592 memory_space>(coeff_from_primal_alloc.data(), NBASIS, NBASIS);
593 auto coeff_from_primal_inverse_view = misc::math::matrix_view<
594 double,
595 typename MetricType::
596 memory_space>(coeff_from_primal_inverse_alloc.data(), NBASIS, NBASIS);
597 auto workspace = misc::math::vector_view<
598 double,
599 typename MetricType::memory_space>(workspace_alloc.data(), NBASIS * NBASIS);
600 bool const invertible = misc::math::
601 invert(coeff_from_primal_inverse_view, coeff_from_primal_view, workspace);
602
603 ddc::device_for_each(reduced_tensor.domain(), [&](auto target_mem_elem) {
604 auto const target_natural_elem
605 = reduced_tensor.canonical_natural_element(target_mem_elem);
606 std::size_t const target_id = [&]() {
607 if constexpr (Q == 0) {
608 return std::size_t(0);
609 } else {
610 std::array<std::size_t, Q> const target_ids
611 = ddc::detail::array(target_natural_elem);
612 return detail::combination_rank<N, Q>(target_ids);
613 }
614 }();
615
616 double reduced_value = 0.;
617 ddc::device_for_each(form_tensor.domain(), [&](auto source_mem_elem) {
618 auto const source_natural_elem
619 = form_tensor.canonical_natural_element(source_mem_elem);
620 std::size_t const source_id = [&]() {
621 if constexpr (Q == 0) {
622 return std::size_t(0);
623 } else {
624 std::array<std::size_t, Q> const source_ids
625 = ddc::detail::array(source_natural_elem);
626 return detail::combination_rank<N, Q>(source_ids);
627 }
628 }();
629 double dual_reduction_value = 0.;
630 if (invertible) {
631 for (std::size_t primal_id = 0; primal_id < NBASIS; ++primal_id) {
632 dual_reduction_value += old_hodge_alloc[target_id * NBASIS + primal_id]
633 * coeff_from_primal_inverse_alloc
634 [primal_id * NBASIS + source_id];
635 }
636 }
637
638 using form_natural_elem_type = typename FormTensorType::accessor_t::
639 natural_domain_t::discrete_element_type;
640 form_natural_elem_type form_natural_elem;
641 ddc::detail::array(form_natural_elem) = ddc::detail::array(source_natural_elem);
642 reduced_value += dual_reduction_value * form_tensor.mem(source_mem_elem);
643 });
644 reduced_tensor.mem(target_mem_elem) = reduced_value;
645 });
646 }
647 }
649 KOKKOS_FUNCTION static double value(PositionType position, BatchElem elem, auto natural_elem)
650 {
651 constexpr std::size_t K = target_index_type::rank();
652 if constexpr (K == 0) {
653 return 1.;
654 } else {
655 std::array const source_ids
656 = ddc::detail::array(source_natural_elem_type(natural_elem));
657 std::array const target_ids
658 = ddc::detail::array(target_natural_elem_type(natural_elem));
659 if (!detail::has_unique_reduction_ids<K>(source_ids)
660 || !detail::has_unique_reduction_ids<K>(target_ids)) {
661 return 0.;
662 }
663
664 std::array<double, K * K> jacobian_matrix {};
665 for (std::size_t row = 0; row < K; ++row) {
666 for (std::size_t col = 0; col < K; ++col) {
667 std::array<double, position_index_type::size()> const edge
668 = sil::exterior::detail::edge_vector<
669 position_index_type>(position, elem, target_ids[col]);
670 jacobian_matrix[row * K + col] = edge[source_ids[row]];
671 }
672 }
673 return detail::reduction_determinant<K>(jacobian_matrix)
674 * detail::complex_volume_factor<Complex, source_index_type::size()>(K)
675 / misc::factorial(K);
676 }
677 }
678
679 template <misc::Specialization<tensor::Tensor> MetricType>
680 KOKKOS_FUNCTION static double value(
681 MetricType metric,
682 PositionType position,
683 BatchElem elem,
684 auto natural_elem)
685 {
686 if constexpr (Complex == CellComplex::Primal) {
687 return value(position, elem, natural_elem);
688 } else {
689 [[maybe_unused]] sil::tensor::TensorAccessor<source_index_type> source_accessor;
690 std::array<double, source_index_type::access_size()> source_alloc {};
691 ddc::ChunkSpan<
692 double,
693 ddc::DiscreteDomain<source_index_type>,
694 Kokkos::layout_right,
695 typename MetricType::memory_space>
696 source_span(source_alloc.data(), source_accessor.domain());
697 sil::tensor::Tensor source_tensor(source_span);
698 ddc::device_for_each(source_tensor.domain(), [&](auto source_mem_elem) {
699 source_tensor.mem(source_mem_elem) = 0.;
700 });
701
702 [[maybe_unused]] sil::tensor::TensorAccessor<target_index_type> target_accessor;
703 std::array<double, target_index_type::access_size()> target_alloc {};
704 ddc::ChunkSpan<
705 double,
706 ddc::DiscreteDomain<target_index_type>,
707 Kokkos::layout_right,
708 typename MetricType::memory_space>
709 target_span(target_alloc.data(), target_accessor.domain());
710 sil::tensor::Tensor target_tensor(target_span);
711 ddc::device_for_each(target_tensor.domain(), [&](auto target_mem_elem) {
712 target_tensor.mem(target_mem_elem) = 0.;
713 });
714
715 source_natural_elem_type source_natural_elem;
716 ddc::detail::array(source_natural_elem)
717 = ddc::detail::array(source_natural_elem_type(natural_elem));
718 if constexpr (source_index_type::rank() > 1) {
719 if (!detail::has_unique_reduction_ids(ddc::detail::array(source_natural_elem))) {
720 return 0.;
721 }
722 }
723 source_tensor(source_tensor.accessor().access_element(source_natural_elem)) = 1.;
724
725 run(target_tensor, source_tensor, metric, position, elem);
726
727 if constexpr (target_index_type::rank() == 0) {
728 return target_tensor.get(
729 target_tensor.accessor().access_element(target_natural_elem_type()));
730 } else {
731 target_natural_elem_type target_natural_elem;
732 ddc::detail::array(target_natural_elem)
733 = ddc::detail::array(target_natural_elem_type(natural_elem));
734 return target_tensor.get(
735 target_tensor.accessor().access_element(target_natural_elem));
736 }
737 }
738 }
739};
740
741template <
743 misc::Specialization<tensor::Tensor> ReductionTensorType,
745 CellComplex Complex,
746 class BatchElem>
749 ReductionTensorType m_reduction_tensor;
750 PositionType m_position;
751 BatchElem m_elem;
752
753 template <class MemElem>
754 KOKKOS_FUNCTION void operator()(MemElem mem_elem) const
755 {
756 m_reduction_tensor.mem(
757 typename ReductionTensorType::discrete_element_type(m_elem, mem_elem))
759 value(m_position,
760 m_elem,
761 m_reduction_tensor.accessor().canonical_natural_element(mem_elem));
762 }
763};
764
765template <
767 misc::Specialization<tensor::Tensor> ReductionTensorType,
769 class ExecSpace,
770 CellComplex Complex = CellComplex::Primal>
771ReductionTensorType fill_reduction_operator(
772 ExecSpace const& exec_space,
773 ReductionTensorType reduction_tensor,
774 PositionType position)
775{
776 SIMILIE_DEBUG_LOG("similie_compute_reduction_operator");
777 ddc::parallel_for_each(
778 "similie_compute_reduction_operator",
779 exec_space,
780 reduction_tensor.non_indices_domain(),
781 KOKKOS_LAMBDA(
782 typename ReductionTensorType::non_indices_domain_t::discrete_element_type
783 elem) {
784 ddc::device_for_each(
785 reduction_tensor.accessor().domain(),
787 Indices,
788 ReductionTensorType,
789 PositionType,
790 Complex,
791 typename ReductionTensorType::non_indices_domain_t::
792 discrete_element_type> {reduction_tensor, position, elem});
793 });
794 return reduction_tensor;
795}
796
797template <
800 class BatchElem,
801 CellComplex Complex = CellComplex::Primal>
802struct Reconstruction
810
811 template <
812 misc::Specialization<tensor::Tensor> ReconstructedTensorType,
814 KOKKOS_FUNCTION static void run(
815 ReconstructedTensorType reconstructed_tensor,
816 CochainTensorType cochain_tensor,
817 PositionType position,
818 BatchElem elem)
819 {
820 using reconstruction_accessor_t
822 using reconstruction_natural_elem_type =
823 typename reconstruction_accessor_t::natural_domain_t::discrete_element_type;
824 using cochain_natural_elem_type =
825 typename CochainTensorType::accessor_t::natural_domain_t::discrete_element_type;
826 if constexpr (source_index_type::rank() == 0) {
827 reconstructed_tensor.mem(ddc::DiscreteElement<source_index_type>(0))
828 = cochain_tensor.get(
829 cochain_tensor.access_element(cochain_natural_elem_type()));
830 } else {
831 [[maybe_unused]] sil::tensor::TensorAccessor<source_index_type> source_accessor;
832 ddc::device_for_each(reconstructed_tensor.domain(), [&](auto target_mem_elem) {
833 auto const target_natural_elem
834 = reconstructed_tensor.canonical_natural_element(target_mem_elem);
835 double reconstructed_value = 0.;
836 ddc::device_for_each(source_accessor.domain(), [&](auto source_mem_elem) {
837 auto const source_natural_elem
838 = source_accessor.canonical_natural_element(source_mem_elem);
839 reconstruction_natural_elem_type reconstruction_natural_elem;
840 reconstruction_natural_elem = detail::merge_reduction_natural_elems<
841 reconstruction_natural_elem_type>(
842 ddc::detail::array(source_natural_elem),
843 ddc::detail::array(target_natural_elem));
844
845 cochain_natural_elem_type cochain_natural_elem;
846 ddc::detail::array(cochain_natural_elem)
847 = ddc::detail::array(source_natural_elem);
848
849 reconstructed_value += value(position, elem, reconstruction_natural_elem)
850 * cochain_tensor.get(cochain_tensor.access_element(
851 cochain_natural_elem));
852 });
853 reconstructed_tensor.mem(target_mem_elem) = reconstructed_value;
854 });
855 }
856 }
858 KOKKOS_FUNCTION static double value(PositionType position, BatchElem elem, auto natural_elem)
859 {
860 static_assert(
861 Complex != CellComplex::BarycentricDual,
862 "Reconstruction is not implemented for the barycentric dual complex.");
863 constexpr std::size_t K = source_index_type::rank();
864 if constexpr (K == 0) {
865 return 1.;
866 } else {
867 std::array const source_ids
868 = ddc::detail::array(source_natural_elem_type(natural_elem));
869 std::array const target_ids
870 = ddc::detail::array(target_natural_elem_type(natural_elem));
871 using reduction_accessor_t
873 using reduction_natural_elem_type =
874 typename reduction_accessor_t::natural_domain_t::discrete_element_type;
875 reduction_natural_elem_type reduction_natural_elem
876 = detail::merge_reduction_natural_elems<
877 reduction_natural_elem_type>(source_ids, target_ids);
878 double const reduction_value = Reduction<Indices, PositionType, BatchElem, Complex>::
879 value(position, elem, reduction_natural_elem);
880
881 if (!detail::have_same_reduction_ids<K>(source_ids, target_ids)) {
882 assert(Kokkos::abs(reduction_value) < 1e-14
883 && "Reconstruction assumes a diagonal local reduction operator.");
884 return 0.;
885 }
886
887 if (Kokkos::abs(reduction_value) < 1e-14) {
888 return 0.;
889 }
890 return 1. / (misc::factorial(K) * reduction_value);
891 }
892 }
893};
894
895template <
897 misc::Specialization<tensor::Tensor> ReconstructionTensorType,
899 CellComplex Complex,
900 class BatchElem>
903 ReconstructionTensorType m_reconstruction_tensor;
904 PositionType m_position;
905 BatchElem m_elem;
906
907 template <class MemElem>
908 KOKKOS_FUNCTION void operator()(MemElem mem_elem) const
909 {
910 m_reconstruction_tensor.mem(
911 typename ReconstructionTensorType::discrete_element_type(m_elem, mem_elem))
913 value(m_position,
914 m_elem,
915 m_reconstruction_tensor.accessor().canonical_natural_element(
916 mem_elem));
917 }
918};
919
920template <
922 misc::Specialization<tensor::Tensor> ReconstructionTensorType,
924 class ExecSpace,
925 CellComplex Complex = CellComplex::Primal>
926ReconstructionTensorType fill_reconstruction_operator(
927 ExecSpace const& exec_space,
928 ReconstructionTensorType reconstruction_tensor,
929 PositionType position)
930{
931 SIMILIE_DEBUG_LOG("similie_compute_reconstruction_operator");
932 ddc::parallel_for_each(
933 "similie_compute_reconstruction_operator",
934 exec_space,
935 reconstruction_tensor.non_indices_domain(),
936 KOKKOS_LAMBDA(
937 typename ReconstructionTensorType::non_indices_domain_t::discrete_element_type
938 elem) {
939 ddc::device_for_each(
940 reconstruction_tensor.accessor().domain(),
942 Indices,
943 ReconstructionTensorType,
944 PositionType,
945 Complex,
946 typename ReconstructionTensorType::non_indices_domain_t::
947 discrete_element_type> {
948 reconstruction_tensor,
949 position,
950 elem});
951 });
952 return reconstruction_tensor;
953}
954
955} // namespace exterior
956
957} // namespace sil
static constexpr discrete_domain_type domain()
ReconstructionTensorType fill_reconstruction_operator(ExecSpace const &exec_space, ReconstructionTensorType reconstruction_tensor, PositionType position)
ddc::detail::convert_type_seq_to_discrete_domain_t< ddc::type_seq_merge_t< ddc::detail::TypeSeq< reduction_index_t< Indices > >, ddc::detail::TypeSeq< reduction_index_t< tensor::primes< Indices > > > > > reconstruction_domain_t
typename detail::ReductionTargetIndex< Indices >::type reduction_index_t
ddc::detail::convert_type_seq_to_discrete_domain_t< ddc::type_seq_merge_t< ddc::detail::TypeSeq< misc::convert_type_seq_to_t< tensor::TensorFullIndex, tensor::primes< tensor::upper_t< Indices > > > >, ddc::detail::TypeSeq< reduction_index_t< Indices > > > > reduction_domain_t
ReductionTensorType fill_reduction_operator(ExecSpace const &exec_space, ReductionTensorType reduction_tensor, PositionType position)
KOKKOS_FUNCTION bool invert(InverseView inverse, MatrixView matrix, WorkspaceView workspace)
KOKKOS_FUNCTION unmanaged_vector_view_t< Scalar, MemorySpace > vector_view(Scalar *data, std::size_t n)
KOKKOS_FUNCTION double submatrix_determinant(MatrixView matrix, RowIds const &row_ids, ColIds const &col_ids, std::array< double, N *N > &submatrix_alloc)
KOKKOS_FUNCTION unmanaged_matrix_view_t< Scalar, MemorySpace > matrix_view(Scalar *data, std::size_t rows, std::size_t cols)
KOKKOS_FUNCTION MatrixView::non_const_value_type determinant(MatrixView matrix)
constexpr std::size_t factorial(std::size_t k) noexcept
Definition factorial.hpp:13
constexpr std::size_t binomial_coefficient(std::size_t n, std::size_t k) noexcept
typename detail::ConvertTypeSeqTo< T, Seq >::type convert_type_seq_to_t
Tensor(ddc::Chunk< ElementType, SupportType, Allocator >) -> Tensor< ElementType, SupportType, Kokkos::layout_right, typename Allocator::memory_space >
ddc::type_seq_element_t< 0, ddc::to_type_seq_t< typename MetricIndex::subindices_domain_t > > metric_index_1
Definition metric.hpp:33
detail::Primes< Indices, I >::type primes
Definition prime.hpp:56
ddc::type_seq_element_t< 1, ddc::to_type_seq_t< typename MetricIndex::subindices_domain_t > > metric_index_2
Definition metric.hpp:41
typename detail::NaturalDomainType< Index >::type natural_domain_t
detail::TensorAccessorForDomain< Dom >::type tensor_accessor_for_domain_t
The top-level namespace of SimiLie.
Definition csr.hpp:15
reduction_index_t< Indices > source_index_type
reduction_index_t< tensor::primes< Indices > > target_index_type
typename tensor::natural_domain_t< target_index_type >::discrete_element_type target_natural_elem_type
typename tensor::natural_domain_t< source_index_type >::discrete_element_type source_natural_elem_type
reduction_index_t< Indices > target_index_type
misc::convert_type_seq_to_t< tensor::TensorFullIndex, tensor::primes< tensor::upper_t< Indices > > > source_index_type
typename tensor::natural_domain_t< target_index_type >::discrete_element_type target_natural_elem_type
static KOKKOS_FUNCTION void run(ReductionTensorType reduced_tensor, FormTensorType form_tensor, PositionType position, BatchElem elem)
ddc:: type_seq_element_t< 0, ddc::to_type_seq_t< typename PositionType::indices_domain_t > > position_index_type
typename tensor::natural_domain_t< source_index_type >::discrete_element_type source_natural_elem_type