SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Systems/Cce/Callbacks - DumpBondiSachsOnWorldtube.hpp Hit Total Coverage
Commit: 1c32b58340e006addc79befb2cdaa7547247e09c Lines: 3 14 21.4 %
Date: 2024-04-19 07:30:15
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <cmath>
       7             : #include <cstddef>
       8             : #include <iomanip>
       9             : #include <iterator>
      10             : #include <mutex>
      11             : #include <string>
      12             : #include <utility>
      13             : #include <vector>
      14             : 
      15             : #include "DataStructures/DataBox/DataBox.hpp"
      16             : #include "DataStructures/DataBox/TagName.hpp"
      17             : #include "DataStructures/Tensor/TypeAliases.hpp"
      18             : #include "DataStructures/Variables.hpp"
      19             : #include "DataStructures/VariablesTag.hpp"
      20             : #include "Evolution/Systems/Cce/BoundaryData.hpp"
      21             : #include "Evolution/Systems/Cce/OptionTags.hpp"
      22             : #include "Evolution/Systems/Cce/Tags.hpp"
      23             : #include "Evolution/Systems/GeneralizedHarmonic/Tags.hpp"
      24             : #include "IO/Observer/Actions/GetLockPointer.hpp"
      25             : #include "IO/Observer/ObserverComponent.hpp"
      26             : #include "IO/Observer/ReductionActions.hpp"
      27             : #include "NumericalAlgorithms/Spectral/SwshCoefficients.hpp"
      28             : #include "NumericalAlgorithms/Spectral/SwshCollocation.hpp"
      29             : #include "Parallel/GlobalCache.hpp"
      30             : #include "Parallel/Invoke.hpp"
      31             : #include "ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp"
      32             : #include "ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp"
      33             : #include "ParallelAlgorithms/Interpolation/Targets/AngularOrdering.hpp"
      34             : #include "ParallelAlgorithms/Interpolation/Targets/Sphere.hpp"
      35             : #include "PointwiseFunctions/GeneralRelativity/Tags.hpp"
      36             : #include "Utilities/ConstantExpressions.hpp"
      37             : #include "Utilities/ErrorHandling/Assert.hpp"
      38             : #include "Utilities/ErrorHandling/Error.hpp"
      39             : #include "Utilities/Gsl.hpp"
      40             : #include "Utilities/MakeString.hpp"
      41             : #include "Utilities/PrettyType.hpp"
      42             : #include "Utilities/ProtocolHelpers.hpp"
      43             : #include "Utilities/TMPL.hpp"
      44             : 
      45           1 : namespace intrp {
      46           1 : namespace callbacks {
      47             : /*!
      48             :  * \brief Post interpolation callback that dumps metric data in Bondi-Sachs form
      49             :  * on a number of extraction radii given by the `intrp::TargetPoints::Sphere`
      50             :  * target.
      51             :  *
      52             :  * To use this callback, the target must be the `intrp::TargetPoints::Sphere`
      53             :  * target in the inertial frame. This callback also expects that the GH source
      54             :  * vars on each of the target spheres are:
      55             :  *
      56             :  * - `gr::Tags::SpacetimeMetric`
      57             :  * - `gh::Tags::Pi`
      58             :  * - `gh::Tags::Phi`
      59             :  *
      60             :  * This callback will write a new `H5` file for each extraction radius in the
      61             :  * Sphere target. The name of this file will be a file prefix specified by the
      62             :  * Cce::Tags::FilePrefix prepended onto `CceRXXXX.h5` where the XXXX is the
      63             :  * zero-padded extraction radius rounded to the nearest integer. The quantities
      64             :  * that will be written are
      65             :  *
      66             :  * - `Cce::Tags::BondiBeta`
      67             :  * - `Cce::Tags::Dr<Cce::Tags::BondiJ>`
      68             :  * - `Cce::Tags::Du<Cce::Tags::BondiR>`
      69             :  * - `Cce::Tags::BondiH`
      70             :  * - `Cce::Tags::BondiJ`
      71             :  * - `Cce::Tags::BondiQ`
      72             :  * - `Cce::Tags::BondiR`
      73             :  * - `Cce::Tags::BondiU`
      74             :  * - `Cce::Tags::BondiW`
      75             :  *
      76             :  * \note For all real quantities (Beta, DuR, R, W) we omit writing the
      77             :  * negative m modes, and the imaginary part of the m = 0 mode.
      78             :  */
      79             : template <typename InterpolationTargetTag>
      80           1 : struct DumpBondiSachsOnWorldtube
      81             :     : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {
      82           0 :   static constexpr double fill_invalid_points_with =
      83             :       std::numeric_limits<double>::quiet_NaN();
      84             : 
      85           0 :   using const_global_cache_tags = tmpl::list<Cce::Tags::FilePrefix>;
      86             : 
      87           0 :   using cce_boundary_tags = Cce::Tags::characteristic_worldtube_boundary_tags<
      88             :       Cce::Tags::BoundaryValue>;
      89             : 
      90           0 :   using cce_tags_to_dump = db::wrap_tags_in<
      91             :       Cce::Tags::BoundaryValue,
      92             :       tmpl::list<Cce::Tags::BondiBeta, Cce::Tags::Dr<Cce::Tags::BondiJ>,
      93             :                  Cce::Tags::Du<Cce::Tags::BondiR>, Cce::Tags::BondiH,
      94             :                  Cce::Tags::BondiJ, Cce::Tags::BondiQ, Cce::Tags::BondiR,
      95             :                  Cce::Tags::BondiU, Cce::Tags::BondiW>>;
      96             : 
      97           0 :   using gh_source_vars_for_cce =
      98             :       tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,
      99             :                  gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>;
     100             : 
     101           0 :   using gh_source_vars_from_interpolation =
     102             :       typename InterpolationTargetTag::vars_to_interpolate_to_target;
     103             : 
     104             :   static_assert(
     105             :       std::is_same_v<tmpl::list_difference<cce_tags_to_dump, cce_boundary_tags>,
     106             :                      tmpl::list<>>,
     107             :       "Cce tags to dump are not in the boundary tags.");
     108             : 
     109             :   static_assert(
     110             :       tmpl::and_<
     111             :           std::is_same<tmpl::list_difference<gh_source_vars_from_interpolation,
     112             :                                              gh_source_vars_for_cce>,
     113             :                        tmpl::list<>>,
     114             :           std::is_same<tmpl::list_difference<gh_source_vars_for_cce,
     115             :                                              gh_source_vars_from_interpolation>,
     116             :                        tmpl::list<>>>::type::value,
     117             :       "To use DumpBondiSachsOnWorldtube, the GH source variables must be the "
     118             :       "spacetime metric, pi, and phi.");
     119             : 
     120             :   static_assert(
     121             :       std::is_same_v<typename InterpolationTargetTag::compute_target_points,
     122             :                      intrp::TargetPoints::Sphere<InterpolationTargetTag,
     123             :                                                  ::Frame::Inertial>>,
     124             :       "To use the DumpBondiSachsOnWorltube post interpolation callback, you "
     125             :       "must use the intrp::TargetPoints::Sphere target in the inertial "
     126             :       "frame");
     127             : 
     128             :   template <typename DbTags, typename Metavariables, typename TemporalId>
     129           0 :   static void apply(const db::DataBox<DbTags>& box,
     130             :                     Parallel::GlobalCache<Metavariables>& cache,
     131             :                     const TemporalId& temporal_id) {
     132             :     const auto& sphere =
     133             :         Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache);
     134             :     const auto& filename_prefix = Parallel::get<Cce::Tags::FilePrefix>(cache);
     135             : 
     136             :     if (sphere.angular_ordering != intrp::AngularOrdering::Cce) {
     137             :       ERROR(
     138             :           "To use the DumpBondiSachsOnWorldtube post interpolation callback, "
     139             :           "the angular ordering of the Spheres must be Cce, not "
     140             :           << sphere.angular_ordering);
     141             :     }
     142             : 
     143             :     const auto& radii = sphere.radii;
     144             :     const size_t l_max = sphere.l_max;
     145             :     const size_t num_points_single_sphere =
     146             :         Spectral::Swsh::number_of_swsh_collocation_points(l_max);
     147             : 
     148             :     const auto& all_gh_vars =
     149             :         db::get<::Tags::Variables<gh_source_vars_from_interpolation>>(box);
     150             : 
     151             :     const auto& all_spacetime_metric =
     152             :         get<gr::Tags::SpacetimeMetric<DataVector, 3>>(all_gh_vars);
     153             :     const auto& all_pi = get<gh::Tags::Pi<DataVector, 3>>(all_gh_vars);
     154             :     const auto& all_phi = get<gh::Tags::Phi<DataVector, 3>>(all_gh_vars);
     155             : 
     156             :     const tnsr::aa<DataVector, 3, ::Frame::Inertial> spacetime_metric;
     157             :     const tnsr::aa<DataVector, 3, ::Frame::Inertial> pi;
     158             :     const tnsr::iaa<DataVector, 3, ::Frame::Inertial> phi;
     159             : 
     160             :     // Bondi data
     161             :     Variables<cce_boundary_tags> bondi_boundary_data{num_points_single_sphere};
     162             :     ComplexModalVector goldberg_mode_buffer{square(l_max + 1)};
     163             :     const std::vector<std::string> all_legend = build_legend(l_max, false);
     164             :     const std::vector<std::string> real_legend = build_legend(l_max, true);
     165             : 
     166             :     size_t offset = 0;
     167             :     for (const auto& radius : radii) {
     168             :       // Set data references so we don't copy data unnecessarily
     169             :       for (size_t a = 0; a < 4; a++) {
     170             :         for (size_t b = 0; b < 4; b++) {
     171             :           make_const_view(make_not_null(&spacetime_metric.get(a, b)),
     172             :                           all_spacetime_metric.get(a, b), offset,
     173             :                           num_points_single_sphere);
     174             :           make_const_view(make_not_null(&pi.get(a, b)), all_pi.get(a, b),
     175             :                           offset, num_points_single_sphere);
     176             :           for (size_t i = 0; i < 3; i++) {
     177             :             make_const_view(make_not_null(&phi.get(i, a, b)),
     178             :                             all_phi.get(i, a, b), offset,
     179             :                             num_points_single_sphere);
     180             :           }
     181             :         }
     182             :       }
     183             : 
     184             :       offset += num_points_single_sphere;
     185             : 
     186             :       Cce::create_bondi_boundary_data(make_not_null(&bondi_boundary_data), phi,
     187             :                                       pi, spacetime_metric, radius, l_max);
     188             : 
     189             :       const std::string filename =
     190             :           MakeString{} << filename_prefix << "CceR" << std::setfill('0')
     191             :                        << std::setw(4) << std::lround(radius);
     192             : 
     193             :       tmpl::for_each<cce_tags_to_dump>(
     194             :           [&temporal_id, &l_max, &all_legend, &real_legend, &filename,
     195             :            &bondi_boundary_data, &goldberg_mode_buffer, &cache](auto tag_v) {
     196             :             using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;
     197             :             constexpr int spin = tag::tag::type::type::spin;
     198             :             // Spin = 0 does not imply a quantity is real. However, all the tags
     199             :             // we want to print out that have spin = 0 happen to be real, so we
     200             :             // use this as an indicator.
     201             :             constexpr bool is_real = spin == 0;
     202             : 
     203             :             const auto& legend = is_real ? real_legend : all_legend;
     204             : 
     205             :             // `tag` is a BoundaryValue. We want the actual tag name
     206             :             const std::string subfile_name{
     207             :                 "/" + replace_name(db::tag_name<typename tag::tag>())};
     208             : 
     209             :             const auto& bondi_data = get(get<tag>(bondi_boundary_data));
     210             : 
     211             :             // Convert our modal data to goldberg modes
     212             :             SpinWeighted<ComplexModalVector, spin> goldberg_modes;
     213             :             goldberg_modes.set_data_ref(make_not_null(&goldberg_mode_buffer));
     214             :             Spectral::Swsh::libsharp_to_goldberg_modes(
     215             :                 make_not_null(&goldberg_modes),
     216             :                 Spectral::Swsh::swsh_transform(l_max, 1, bondi_data), l_max);
     217             : 
     218             :             std::vector<double> data_to_write_buffer;
     219             :             data_to_write_buffer.reserve(number_of_components(l_max, is_real));
     220             :             data_to_write_buffer.emplace_back(
     221             :                 intrp::InterpolationTarget_detail::get_temporal_id_value(
     222             :                     temporal_id));
     223             : 
     224             :             // We loop over ell and m rather than just the total number of modes
     225             :             // because we don't print negative m or the imaginary part of m=0
     226             :             // for real quantities.
     227             :             for (size_t ell = 0; ell <= l_max; ell++) {
     228             :               for (int m = is_real ? 0 : -static_cast<int>(ell);
     229             :                    m <= static_cast<int>(ell); m++) {
     230             :                 const size_t goldberg_index =
     231             :                     Spectral::Swsh::goldberg_mode_index(l_max, ell, m);
     232             :                 data_to_write_buffer.push_back(
     233             :                     real(goldberg_modes.data()[goldberg_index]));
     234             :                 if (not is_real or m != 0) {
     235             :                   data_to_write_buffer.push_back(
     236             :                       imag(goldberg_modes.data()[goldberg_index]));
     237             :                 }
     238             :               }
     239             :             }
     240             : 
     241             :             ASSERT(legend.size() == data_to_write_buffer.size(),
     242             :                    "Legend (" << legend.size()
     243             :                               << ") does not have the same number of "
     244             :                                  "components as data to write ("
     245             :                               << data_to_write_buffer.size() << ") for tag "
     246             :                               << db::tag_name<typename tag::tag>());
     247             : 
     248             :             // Even though no other cores should be writing to this file, we
     249             :             // still need to get the h5 file lock so the system hdf5 doesn't get
     250             :             // upset
     251             :             auto* hdf5_lock =
     252             :                 Parallel::local_branch(
     253             :                     Parallel::get_parallel_component<
     254             :                         observers::ObserverWriter<Metavariables>>(cache))
     255             :                     ->template local_synchronous_action<
     256             :                         observers::Actions::GetLockPointer<
     257             :                             observers::Tags::H5FileLock>>();
     258             : 
     259             :             {
     260             :               const std::lock_guard lock(*hdf5_lock);
     261             :               observers::ThreadedActions::ReductionActions_detail::write_data(
     262             :                   subfile_name, observers::input_source_from_cache(cache),
     263             :                   legend, std::make_tuple(data_to_write_buffer), filename,
     264             :                   std::index_sequence<0>{});
     265             :             }
     266             :           });
     267             :     }
     268             :   }
     269             : 
     270             :  private:
     271             :   // There are exactly half the number of modes for spin = 0 quantities as their
     272             :   // are for spin != 0
     273           0 :   static size_t number_of_components(const size_t l_max, const bool is_real) {
     274             :     return 1 + square(l_max + 1) * (is_real ? 1 : 2);
     275             :   }
     276             : 
     277           0 :   static std::vector<std::string> build_legend(const size_t l_max,
     278             :                                                const bool is_real) {
     279             :     std::vector<std::string> legend;
     280             :     legend.reserve(number_of_components(l_max, is_real));
     281             :     legend.emplace_back("Time");
     282             :     for (int ell = 0; ell <= static_cast<int>(l_max); ++ell) {
     283             :       for (int m = is_real ? 0 : -ell; m <= ell; ++m) {
     284             :         legend.push_back(MakeString{} << "Re(" << ell << "," << m << ")");
     285             :         // For real quantities, don't include the imaginary m=0
     286             :         if (not is_real or m != 0) {
     287             :           legend.push_back(MakeString{} << "Im(" << ell << "," << m << ")");
     288             :         }
     289             :       }
     290             :     }
     291             :     return legend;
     292             :   }
     293             : 
     294             :   // These match names that CCE executable expects
     295           0 :   static std::string replace_name(const std::string& db_tag_name) {
     296             :     if (db_tag_name == "BondiBeta") {
     297             :       return "Beta";
     298             :     } else if (db_tag_name == "Dr(J)") {
     299             :       return "DrJ";
     300             :     } else if (db_tag_name == "Du(R)") {
     301             :       return "DuR";
     302             :     } else {
     303             :       return db_tag_name;
     304             :     }
     305             :   }
     306             : };
     307             : }  // namespace callbacks
     308             : }  // namespace intrp

Generated by: LCOV version 1.14