Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <boost/math/special_functions/spherical_harmonic.hpp> 7 : #include <cstddef> 8 : #include <optional> 9 : #include <vector> 10 : 11 : #include "DataStructures/DataBox/Tag.hpp" 12 : #include "DataStructures/DataVector.hpp" 13 : #include "DataStructures/Tags/TempTensor.hpp" 14 : #include "DataStructures/Tensor/EagerMath/DotProduct.hpp" 15 : #include "DataStructures/Tensor/Slice.hpp" 16 : #include "DataStructures/Variables.hpp" 17 : #include "Domain/AreaElement.hpp" 18 : #include "Domain/ExcisionSphere.hpp" 19 : #include "Domain/Structure/Direction.hpp" 20 : #include "Domain/Structure/IndexToSliceAt.hpp" 21 : #include "Domain/Tags.hpp" 22 : #include "Domain/TagsTimeDependent.hpp" 23 : #include "Evolution/Systems/CurvedScalarWave/System.hpp" 24 : #include "Evolution/Systems/CurvedScalarWave/Tags.hpp" 25 : #include "Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp" 26 : #include "Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp" 27 : #include "Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp" 28 : #include "Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp" 29 : #include "NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp" 30 : #include "NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp" 31 : #include "NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp" 32 : #include "Parallel/AlgorithmExecution.hpp" 33 : #include "Parallel/GlobalCache.hpp" 34 : #include "Parallel/Invoke.hpp" 35 : #include "PointwiseFunctions/GeneralRelativity/Tags.hpp" 36 : #include "Utilities/ErrorHandling/Assert.hpp" 37 : #include "Utilities/Gsl.hpp" 38 : #include "Utilities/TMPL.hpp" 39 : 40 : /// \cond 41 : namespace Tags { 42 : struct TimeStepId; 43 : } // namespace Tags 44 : /// \endcond 45 : 46 : namespace CurvedScalarWave::Worldtube::Actions { 47 : /*! 48 : * \brief Projects the regular field \f$\Psi^R\f$ and its time derivative 49 : * \f$\partial_t \Psi^R\f$ onto real spherical harmonics and sends the result to 50 : * the worldtube. 51 : * 52 : * \details The regular field is obtained by subtracting the singular/puncture 53 : * field from the numerical DG field. 54 : * All spherical harmonics are computed for \f$l <= n\f$, where \f$n\f$ is the 55 : * worldtube expansion order. The projection is done by integrating over the DG 56 : * grid of the element face using \ref definite_integral with the euclidean area 57 : * element. The worldtube adds up all integrals from the different elements to 58 : * obtain the integral over the entire sphere. 59 : * 60 : * DataBox: 61 : * - Uses: 62 : * - `tags_to_slice_on_face` 63 : * - `Worldtube::Tags::ExpansionOrder` 64 : * - `Worldtube::Tags::FaceCoordinates<Dim, Frame::Inertial, true>` 65 : * - `Worldtube::Tags::PunctureField` 66 : * - `Worldtube::Tags::ExcisionSphere` 67 : * - `Tags::TimeStepId` 68 : */ 69 1 : struct SendToWorldtube { 70 0 : static constexpr size_t Dim = 3; 71 0 : using tags_to_send = tmpl::list<CurvedScalarWave::Tags::Psi, 72 : ::Tags::dt<CurvedScalarWave::Tags::Psi>>; 73 0 : using tags_to_slice_to_face = 74 : tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi, 75 : CurvedScalarWave::Tags::Phi<Dim>, 76 : gr::Tags::Shift<DataVector, Dim>, gr::Tags::Lapse<DataVector>, 77 : domain::Tags::InverseJacobian<Dim, Frame::ElementLogical, 78 : Frame::Inertial>>; 79 : 80 : template <typename DbTagsList, typename... InboxTags, typename Metavariables, 81 : typename ArrayIndex, typename ActionList, 82 : typename ParallelComponent> 83 0 : static Parallel::iterable_action_return_t apply( 84 : db::DataBox<DbTagsList>& box, 85 : tuples::TaggedTuple<InboxTags...>& /*inboxes*/, 86 : Parallel::GlobalCache<Metavariables>& cache, 87 : const ArrayIndex& /*array_index*/, const ActionList /*meta*/, 88 : const ParallelComponent* const /*meta*/) { 89 : const auto& element_id = db::get<domain::Tags::Element<Dim>>(box).id(); 90 : const auto& excision_sphere = db::get<Tags::ExcisionSphere<Dim>>(box); 91 : const auto direction = excision_sphere.abutting_direction(element_id); 92 : if (not direction.has_value()) { 93 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 94 : } 95 : const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box); 96 : const auto face_mesh = mesh.slice_away(direction->dimension()); 97 : const size_t face_size = face_mesh.number_of_grid_points(); 98 : 99 : Variables<tmpl::list< 100 : CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>, 101 : ::Tags::TempScalar<0>, ::Tags::TempScalar<1>, ::Tags::TempScalar<2>>> 102 : temporaries(face_size); 103 : auto& psi_regular_times_det = 104 : get(get<CurvedScalarWave::Tags::Psi>(temporaries)); 105 : auto& dt_psi_regular_times_det = 106 : get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(temporaries)); 107 : auto& theta = get(get<::Tags::TempScalar<0>>(temporaries)); 108 : auto& phi = get(get<::Tags::TempScalar<1>>(temporaries)); 109 : auto& spherical_harmonic = get(get<::Tags::TempScalar<2>>(temporaries)); 110 : const auto& face_quantities = db::get<Tags::FaceQuantities>(box).value(); 111 : const auto& psi_numerical_face = 112 : get<CurvedScalarWave::Tags::Psi>(face_quantities); 113 : const auto& dt_psi_numerical_face = 114 : get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(face_quantities); 115 : const auto& area_element = 116 : get<gr::surfaces::Tags::AreaElement<DataVector>>(face_quantities); 117 : const auto& puncture_field = 118 : db::get<Tags::CurrentIteration>(box) > 0 119 : ? db::get<Tags::IteratedPunctureField<Dim>>(box).value() 120 : : db::get<Tags::PunctureField<Dim>>(box).value(); 121 : const auto& psi_puncture = get<CurvedScalarWave::Tags::Psi>(puncture_field); 122 : const auto& dt_psi_puncture = 123 : get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(puncture_field); 124 : 125 : psi_regular_times_det = 126 : (get(psi_numerical_face) - get(psi_puncture)) * get(area_element); 127 : dt_psi_regular_times_det = 128 : (get(dt_psi_numerical_face) - get(dt_psi_puncture)) * get(area_element); 129 : const auto& centered_face_coords = 130 : db::get<Tags::FaceCoordinates<Dim, Frame::Inertial, true>>(box); 131 : ASSERT(centered_face_coords.has_value(), 132 : "Should be an abutting element here, but face coords are not " 133 : "calculated!"); 134 : const auto& x = get<0>(centered_face_coords.value()); 135 : const auto& y = get<1>(centered_face_coords.value()); 136 : const auto& z = get<2>(centered_face_coords.value()); 137 : 138 : const size_t order = db::get<Worldtube::Tags::ExpansionOrder>(box); 139 : const size_t num_modes = (order + 1) * (order + 1); 140 : Variables<tags_to_send> Ylm_coefs(num_modes); 141 : theta = atan2(hypot(x, y), z); 142 : phi = atan2(y, x); 143 : size_t index = 0; 144 : // project onto spherical harmonics 145 : for (size_t l = 0; l <= order; ++l) { 146 : // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) 147 : for (int m = -l; m <= static_cast<int>(l); ++m, ++index) { 148 : spherical_harmonic = ylm::real_spherical_harmonic(theta, phi, l, m); 149 : get(get<CurvedScalarWave::Tags::Psi>(Ylm_coefs)).at(index) = 150 : definite_integral(psi_regular_times_det * spherical_harmonic, 151 : face_mesh); 152 : get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(Ylm_coefs)).at(index) = 153 : definite_integral(dt_psi_regular_times_det * spherical_harmonic, 154 : face_mesh); 155 : } 156 : } 157 : ASSERT(index == num_modes, "Internal indexing error. " 158 : << num_modes 159 : << " modes should have been calculated but " 160 : << index << " modes were computed."); 161 : 162 : auto& worldtube_component = Parallel::get_parallel_component< 163 : Worldtube::WorldtubeSingleton<Metavariables>>(cache); 164 : Parallel::receive_data<Worldtube::Tags::SphericalHarmonicsInbox<Dim>>( 165 : worldtube_component, db::get<::Tags::TimeStepId>(box), 166 : std::make_pair(element_id, std::move(Ylm_coefs))); 167 : if (db::get<Tags::CurrentIteration>(box) + 1 < 168 : db::get<Tags::MaxIterations>(box) ) { 169 : db::mutate<Tags::CurrentIteration>( 170 : [](const gsl::not_null<size_t*> current_iteration) { 171 : *current_iteration += 1; 172 : }, 173 : make_not_null(&box)); 174 : // still iterating, go to `IteratePunctureField` 175 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 176 : 177 : } else { 178 : db::mutate<Tags::CurrentIteration>( 179 : [](const gsl::not_null<size_t*> current_iteration) { 180 : *current_iteration = 0; 181 : }, 182 : make_not_null(&box)); 183 : // done iterating, get data for BCs 184 : return {Parallel::AlgorithmExecution::Continue, 185 : tmpl::index_of<ActionList, ReceiveWorldtubeData>::value}; 186 : } 187 : } 188 : }; 189 : } // namespace CurvedScalarWave::Worldtube::Actions