|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include "../Metric.hpp" |
| 4 | +#include "BSSNDerivatives.hpp" |
| 5 | +#include "../../Core/Tensor.hpp" |
| 6 | + |
| 7 | +namespace tensorium { |
| 8 | + template<typename T> |
| 9 | + struct TensorTraits; |
| 10 | + |
| 11 | + template<typename T, size_t Rank> |
| 12 | + struct TensorTraits<Tensor<T, Rank>> { |
| 13 | + static constexpr size_t rank = Rank; |
| 14 | + using value_type = T; |
| 15 | + }; |
| 16 | + |
| 17 | + template<typename T> |
| 18 | + struct TensorTraits { |
| 19 | + static constexpr size_t rank = 0; |
| 20 | + using value_type = T; |
| 21 | + }; |
| 22 | + |
| 23 | + template<typename T> |
| 24 | + struct TensorTraits<Vector<T>> { |
| 25 | + static constexpr size_t rank = 1; |
| 26 | + using value_type = T; |
| 27 | + }; |
| 28 | +} |
| 29 | + |
| 30 | +namespace tensorium_RG { |
| 31 | + |
| 32 | + enum class DiffMode { PARTIAL, COV, COV2 }; |
| 33 | + |
| 34 | + // ===================== RANK 0 (SCALAR) ===================== |
| 35 | + // Return type : Tensor<T,1> (i.e. un vecteur gradient) |
| 36 | + template<typename T, typename ScalarFunc> |
| 37 | + tensorium::Tensor<T, 1> autodiff_rank0( |
| 38 | + const tensorium::Vector<T>& X, |
| 39 | + T dx, T dy, T dz, |
| 40 | + ScalarFunc&& func, |
| 41 | + DiffMode mode, |
| 42 | + const tensorium::Tensor<T, 3>& christoffel) |
| 43 | + { |
| 44 | + using namespace tensorium; |
| 45 | + if (mode == DiffMode::PARTIAL) { |
| 46 | + return partial_scalar(X, dx, dy, dz, std::forward<ScalarFunc>(func)); |
| 47 | + } |
| 48 | + else if (mode == DiffMode::COV2) { |
| 49 | + return covariant_scalar_second(X, dx, dy, dz, std::forward<ScalarFunc>(func), christoffel); |
| 50 | + } |
| 51 | + else { |
| 52 | + throw std::invalid_argument("autodiff_rank0: uniquement PARTIAL ou COV2 autorisés pour un champ scalaire"); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + // ===================== RANK 1 (VECTOR) ===================== |
| 57 | + // Return type : Tensor<T,2> (i.e. une matrice dérivée covariante ou jacobienne) |
| 58 | + template<typename T, typename VectorFunc> |
| 59 | + tensorium::Tensor<T, 2> autodiff_rank1( |
| 60 | + const tensorium::Vector<T>& X, |
| 61 | + T dx, T dy, T dz, |
| 62 | + VectorFunc&& func, |
| 63 | + DiffMode mode, |
| 64 | + const tensorium::Tensor<T, 3>& christoffel) |
| 65 | + { |
| 66 | + using namespace tensorium; |
| 67 | + if (mode == DiffMode::PARTIAL) { |
| 68 | + return partial_vector(X, dx, dy, dz, std::forward<VectorFunc>(func)); |
| 69 | + } |
| 70 | + else if (mode == DiffMode::COV) { |
| 71 | + return covariant_vector(X, dx, dy, dz, std::forward<VectorFunc>(func), christoffel); |
| 72 | + } |
| 73 | + else { |
| 74 | + throw std::invalid_argument("autodiff_rank1: uniquement PARTIAL ou COV autorisés pour un champ vectoriel"); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + // ============== RANK 2 (TENSOR 2D) : PREMIÈRE DÉRIVÉE SEULEMENT ============== |
| 79 | + // Nous n'implémentons ici que PARTIAL et COV. Si on demande COV2, on lève une exception. |
| 80 | + // Return type : Tensor<T,3> (i.e. un tenseur à 3 indices : ∂_k T_{ij} ou ∇_k T_{ij}) |
| 81 | + template<typename T, typename TensorFunc> |
| 82 | + tensorium::Tensor<T, 3> autodiff_rank2_first( |
| 83 | + const tensorium::Vector<T>& X, |
| 84 | + T dx, T dy, T dz, |
| 85 | + TensorFunc&& func, |
| 86 | + DiffMode mode, |
| 87 | + const tensorium::Tensor<T, 3>& christoffel) |
| 88 | + { |
| 89 | + using namespace tensorium; |
| 90 | + if (mode == DiffMode::PARTIAL) { |
| 91 | + return partial_tensor2(X, dx, dy, dz, std::forward<TensorFunc>(func)); |
| 92 | + } |
| 93 | + else if (mode == DiffMode::COV) { |
| 94 | + return covariant_tensor2(X, dx, dy, dz, std::forward<TensorFunc>(func), christoffel); |
| 95 | + } |
| 96 | + else { |
| 97 | + throw std::invalid_argument("autodiff_rank2_first: uniquement PARTIAL ou COV autorisés pour un tenseur d'ordre 2"); |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + // ===================== WRAPPER GÉNÉRIQUE ===================== |
| 102 | + template<typename T, typename FieldFunc> |
| 103 | + auto autodiff( |
| 104 | + const tensorium::Vector<T>& X, |
| 105 | + T dx, T dy, T dz, |
| 106 | + FieldFunc&& func, |
| 107 | + DiffMode mode, |
| 108 | + const tensorium::Tensor<T, 3>& christoffel = {}) |
| 109 | + { |
| 110 | + using namespace tensorium; |
| 111 | + auto sample = func(X); |
| 112 | + constexpr size_t rank = TensorTraits<std::decay_t<decltype(sample)>>::rank; |
| 113 | + |
| 114 | + if constexpr (rank == 0) { |
| 115 | + return autodiff_rank0(X, dx, dy, dz, std::forward<FieldFunc>(func), mode, christoffel); |
| 116 | + } |
| 117 | + else if constexpr (rank == 1) { |
| 118 | + return autodiff_rank1(X, dx, dy, dz, std::forward<FieldFunc>(func), mode, christoffel); |
| 119 | + } |
| 120 | + else if constexpr (rank == 2) { |
| 121 | + // On autorise ici PARTIAL et COV uniquement pour un champ « Tensor, rank2 ». |
| 122 | + // Si on veut une dérivée covariante seconde d'un tenseur 2D, l'utilisateur |
| 123 | + // devra appeler explicitement la fonction covariant_tensor2_second(...) par lui-même. |
| 124 | + return autodiff_rank2_first(X, dx, dy, dz, std::forward<FieldFunc>(func), mode, christoffel); |
| 125 | + } |
| 126 | + else { |
| 127 | + static_assert(rank <= 2, "autodiff: seuls les rangs 0 (scalaire), 1 (vecteur) et 2 (tenseur2) sont supportés."); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | +} // namespace tensorium_RG |
0 commit comments