Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <array> 7 : #include <cmath> 8 : #include <limits> 9 : #include <tuple> 10 : 11 : #include "DataStructures/DataBox/DataBox.hpp" 12 : #include "IO/Observer/ObserverComponent.hpp" 13 : #include "IO/Observer/ReductionActions.hpp" 14 : #include "NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp" 15 : #include "NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp" 16 : #include "NumericalAlgorithms/SphericalHarmonics/Tags.hpp" 17 : #include "Parallel/GlobalCache.hpp" 18 : #include "Parallel/Invoke.hpp" 19 : #include "Parallel/ParallelComponentHelpers.hpp" 20 : #include "ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp" 21 : #include "ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp" 22 : #include "ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp" 23 : #include "PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp" 24 : #include "Utilities/Gsl.hpp" 25 : #include "Utilities/PrettyType.hpp" 26 : #include "Utilities/ProtocolHelpers.hpp" 27 : 28 : /// \cond 29 : namespace Frame { 30 : struct Grid; 31 : struct Distorted; 32 : struct Inertial; 33 : } // namespace Frame 34 : /// \endcond 35 : 36 : namespace ah::callbacks { 37 : /*! 38 : * \brief Writes the center of an apparent horizon to disk in both the 39 : * `Frame` template parameter frame and Frame::Inertial frame. Intended to be 40 : * used in the `horizon_find_callbacks` list of a 41 : * `ah::protocols::HorizonMetavars`. 42 : * 43 : * The centers will be written to a subfile with the name 44 : * `/ApparentHorizons/TargetName_Centers.dat` where `TargetName` is the 45 : * pretty_type::name of the \p HorizonMetavars template parameter. 46 : * 47 : * The columns of the dat file are: 48 : * - %Time 49 : * - GridCenter_x 50 : * - GridCenter_y 51 : * - GridCenter_z 52 : * - InertialCenter_x 53 : * - InertialCenter_y 54 : * - InertialCenter_z 55 : * 56 : * The `Frame` template parameter must be either `::Frame::Grid` or 57 : * `::Frame::Distorted`. Even though the template parameter can be 58 : * `::Frame::Distorted`, we still write `GridCenter_?` because the centers of 59 : * the objects are the same in the Grid and Distorted frames. 60 : * 61 : * \note Requires `ylm::Tags::Strahlkorper<Frame>` and 62 : * `ylm::Tags::CartesianCoords<Frame::Inertial>` and 63 : * `ylm::Tags::EuclideanAreaElement<Frame>` to be in the DataBox of the horizon 64 : * finder. 65 : */ 66 : template <typename HorizonMetavars> 67 1 : struct ObserveCenters : tt::ConformsTo<ah::protocols::Callback> { 68 : template <typename DbTags, typename Metavariables> 69 0 : static void apply(const db::DataBox<DbTags>& box, 70 : Parallel::GlobalCache<Metavariables>& cache, 71 : const FastFlow::Status /*status*/) { 72 : using frame = typename HorizonMetavars::frame; 73 : static_assert(std::is_same_v<frame, ::Frame::Grid> or 74 : std::is_same_v<frame, ::Frame::Distorted>, 75 : "Frame must be either Grid or Distorted."); 76 : using HorizonTag = ylm::Tags::Strahlkorper<frame>; 77 : using CoordsTag = ylm::Tags::CartesianCoords<::Frame::Inertial>; 78 : using EuclideanAreaElementTag = ylm::Tags::EuclideanAreaElement<frame>; 79 : static_assert(db::tag_is_retrievable_v<HorizonTag, db::DataBox<DbTags>>, 80 : "DataBox must contain ylm::Tags::Strahlkorper<Frame>"); 81 : static_assert(db::tag_is_retrievable_v<CoordsTag, db::DataBox<DbTags>>, 82 : "DataBox must contain " 83 : "ylm::Tags::CartesianCoords<Frame::Inertial>"); 84 : static_assert( 85 : db::tag_is_retrievable_v<EuclideanAreaElementTag, db::DataBox<DbTags>>, 86 : "DataBox must contain ylm::Tags::EuclideanAreaElement<Frame>"); 87 : 88 : // Only print the centers if we want to. 89 : if (not Parallel::get<ah::Tags::ObserveCenters>(cache)) { 90 : return; 91 : } 92 : 93 : const auto& grid_horizon = db::get<HorizonTag>(box); 94 : const std::array<double, 3> grid_center = grid_horizon.physical_center(); 95 : 96 : // computes the inertial center to be the average value of the 97 : // inertial coordinates over the Strahlkorper, where the average is 98 : // computed by a surface integral. 99 : // Note that we use the Euclidean area element here, since we are trying 100 : // to find a geometric center of a surface without regard to GR. 101 : const auto& inertial_coords = db::get<CoordsTag>(box); 102 : const auto& area_element = db::get<EuclideanAreaElementTag>(box); 103 : std::array<double, 3> inertial_center = 104 : make_array<3>(std::numeric_limits<double>::signaling_NaN()); 105 : auto integrand = make_with_value<Scalar<DataVector>>(get(area_element), 0.); 106 : const double denominator = grid_horizon.ylm_spherepack().definite_integral( 107 : get(area_element).data()); 108 : for (size_t i = 0; i < 3; ++i) { 109 : get(integrand) = get(area_element) * inertial_coords.get(i); 110 : gsl::at(inertial_center, i) = 111 : grid_horizon.ylm_spherepack().definite_integral( 112 : get(integrand).data()) / 113 : denominator; 114 : } 115 : 116 : // time, grid_x, grid_y, grid_z, inertial_x, inertial_y, inertial_z 117 : const auto& current_time = db::get<ah::Tags::CurrentTime>(box).value(); 118 : const auto center_tuple = std::make_tuple( 119 : current_time.id, grid_center[0], grid_center[1], grid_center[2], 120 : inertial_center[0], inertial_center[1], inertial_center[2]); 121 : 122 : auto& observer_writer_proxy = Parallel::get_parallel_component< 123 : observers::ObserverWriter<Metavariables>>(cache); 124 : 125 : const std::string subfile_path{"/ApparentHorizons/" + 126 : HorizonMetavars::name() + "_Centers"}; 127 : 128 : Parallel::threaded_action< 129 : observers::ThreadedActions::WriteReductionDataRow>( 130 : // Node 0 is always the writer 131 : observer_writer_proxy[0], subfile_path, legend_, center_tuple); 132 : } 133 : 134 : private: 135 0 : const static inline std::vector<std::string> legend_{ 136 : {"Time", "GridCenter_x", "GridCenter_y", "GridCenter_z", 137 : "InertialCenter_x", "InertialCenter_y", "InertialCenter_z"}}; 138 : }; 139 : } // namespace ah::callbacks