SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Systems/Cce/Events - ObserveFields.hpp Hit Total Coverage
Commit: 6c07040310c093b5fbed1dd2b7e9a5199069d22a Lines: 3 26 11.5 %
Date: 2025-11-12 14:32:51
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 <cstddef>
       7             : #include <pup.h>
       8             : #include <string>
       9             : #include <tuple>
      10             : #include <type_traits>
      11             : #include <unordered_set>
      12             : #include <vector>
      13             : 
      14             : #include "DataStructures/ComplexDataVector.hpp"
      15             : #include "DataStructures/ComplexModalVector.hpp"
      16             : #include "DataStructures/DataBox/DataBox.hpp"
      17             : #include "DataStructures/DataBox/DataBoxTag.hpp"
      18             : #include "DataStructures/DataBox/ObservationBox.hpp"
      19             : #include "DataStructures/DataBox/Prefixes.hpp"
      20             : #include "Domain/Structure/ElementId.hpp"
      21             : #include "Evolution/Systems/Cce/LinearOperators.hpp"
      22             : #include "Evolution/Systems/Cce/NewmanPenrose.hpp"
      23             : #include "Evolution/Systems/Cce/OptionTags.hpp"
      24             : #include "Evolution/Systems/Cce/SwshDerivatives.hpp"
      25             : #include "Evolution/Systems/Cce/Tags.hpp"
      26             : #include "IO/Observer/ObserverComponent.hpp"
      27             : #include "IO/Observer/ReductionActions.hpp"
      28             : #include "IO/Observer/VolumeActions.hpp"
      29             : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp"
      30             : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp"
      31             : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp"
      32             : #include "Options/Context.hpp"
      33             : #include "Options/ParseError.hpp"
      34             : #include "Options/String.hpp"
      35             : #include "Parallel/GlobalCache.hpp"
      36             : #include "Parallel/Info.hpp"
      37             : #include "Parallel/Invoke.hpp"
      38             : #include "ParallelAlgorithms/EventsAndTriggers/Event.hpp"
      39             : #include "Time/Tags/Time.hpp"
      40             : #include "Utilities/Gsl.hpp"
      41             : #include "Utilities/MakeString.hpp"
      42             : #include "Utilities/TMPL.hpp"
      43             : #include "Utilities/TypeTraits/IsA.hpp"
      44             : 
      45             : /// \cond
      46             : template <size_t Dim>
      47             : class Mesh;
      48             : namespace Frame {
      49             : struct Inertial;
      50             : }  // namespace Frame
      51             : /// \endcond
      52             : 
      53             : namespace Cce::Events {
      54             : namespace detail {
      55             : template <typename Tag>
      56             : std::string name() {
      57             :   if constexpr (std::is_same_v<Tag, Tags::ComplexInertialRetardedTime>) {
      58             :     return db::tag_name<Tags::InertialRetardedTime>();
      59             :   } else {
      60             :     return db::tag_name<Tag>();
      61             :   }
      62             : }
      63             : }  // namespace detail
      64             : 
      65             : /*!
      66             :  * \brief Event to observe fields/variables in a characteristic evolution.
      67             :  *
      68             :  * \details Similar to `dg::Events::ObserveFields`, this event will write volume
      69             :  * data from the characteristic domain to disk when triggered. However, there
      70             :  * are several differences which are important to highlight.
      71             :  *
      72             :  * First is the fields themselves. The DG event takes the fields to observe as
      73             :  * template parameters because the event must work with many evolution systems.
      74             :  * However, since this event is specific to the characteristic evolution system,
      75             :  * we can hardcode the list of fields that are available to observe. The fields
      76             :  * available to observe are the following tags along with their first and second
      77             :  * `Cce::Tags::Dy` derivatives (see `Cce::Tags::Dy` for a definition of `y`):
      78             :  *
      79             :  * - `Cce::Tags::BondiBeta`
      80             :  * - `Cce::Tags::BondiU`
      81             :  * - `Cce::Tags::BondiQ`
      82             :  * - `Cce::Tags::BondiW`
      83             :  * - `Cce::Tags::BondiH` (no second derivative)
      84             :  * - `Cce::Tags::BondiJ`
      85             :  * - `Cce::Tags::Du<Cce::Tags::BondiJ>`
      86             :  *
      87             :  * Some more fields to observe are:
      88             :  *
      89             :  * - `Cce::Tags::Psi0`
      90             :  * - `Cce::Tags::Psi1`
      91             :  * - `Cce::Tags::Psi2`
      92             :  * - `Cce::Tags::ComplexInertialRetardedTime`
      93             :  * - `Cce::Tags::OneMinusY`
      94             :  * - `Cce::Tags::BondiR`
      95             :  * - `Cce::Tags::EthRDividedByR`
      96             :  * - `Cce::Tags::DuRDividedByR`
      97             :  *
      98             :  * The main reason that this event is separate from the DG one is because this
      99             :  * event writes modal data over the sphere for every radial grid point, while
     100             :  * the DG event writes nodal data. Every tag above is a
     101             :  * `Scalar<SpinWeighted<ComplexDataVector, Spin>>` for some `Spin`. While this
     102             :  * data itself is in nodal form, it is more convenient to transform to modal
     103             :  * data and decompose in spherical harmonics before writing. This means
     104             :  * our typical way of writing/storing volume data won't work.
     105             :  *
     106             :  * All data will be written into the `observers::OptionTags::VolumeFileName`
     107             :  * file. If CCE is run on a single core, then this will write the volume data
     108             :  * immediately (synchronously) instead of sending it to the ObserverWriter to be
     109             :  * written asynchronously.  The option `SubgroupName` controls the name of the
     110             :  * H5 group where this volume data is written. For example, if `SubgroupName` is
     111             :  * "CceVolumeData", then the volume file will contain
     112             :  * `/CceVolumeData/VolumeData.vol` for most fields; it would contain
     113             :  * `/CceVolumeData/InertialRetardedTime.vol` for the inertial retarded time; and
     114             :  * it would contain `/CceVolumeData/OneMinusY.vol` for the compactified radial
     115             :  * coordinate. The structure of the .vol subfiles is the same as that for DG
     116             :  * volume data. However, the extents of the three different volume files are all
     117             :  * different, and for modal directions, they take the values l_max in both of
     118             :  * the two extent slots corresponding to angular modes. This  also means that
     119             :  * complex modal data (which has real/imag parts interleaved, as described
     120             :  * below) has twice as much data as the products of the extents suggests.
     121             :  *
     122             :  * The formats for `Cce::Tags::ComplexInertialRetardedTime` and
     123             :  * `Cce::Tags::OneMinusY` are special and are described below. Every other field
     124             :  * follows the same format. Each ObservationId contains one time slice. Within
     125             :  * an ObservationId, each field is an unraveled vector of complex modal
     126             :  * coefficients at compactified radial slices (the compactified coordinate is $y
     127             :  * = 1 - 2R/r$ where $r$ is your coordinate radius and $R$ is the coordinate
     128             :  * radius of your worldtube; it is recommended to always dump the quantity
     129             :  * `Cce::Tags::OneMinusY` so the values of the compactified coordinates are
     130             :  * available as well). The ordering of the coefficients for a field $f$ at
     131             :  * constant observation event are, for example:
     132             :  * Re f_{0,0}(1-y_0), Im f_{0,0}(1-y_0), Re f_{1,-1}(1-y_0), Im f_{1,-1}(1-y_0),
     133             :  * Re f_{1,0}(1-y_0), Im f_{1,0}(1-y_0), ...
     134             :  * Re f_{l_{max},l_{max}}(1-y_0), Im f_{l_{max},l_{max}}(1-y_0),
     135             :  * Re f_{0,0}(1-y_1), Im f_{0,0}(1-y_1), ...
     136             :  * Re f_{l_{max},l_{max}}(1-y_{max)), Im f_{l_{max},l_{max}}(1-y_{max}).
     137             :  * That is, the radial slice indexes the slowest, followed by the $\ell$ number
     138             :  * (always starting at 0, even for s≠0), followed by the azimuthal m number
     139             :  * (from $-\ell$ to $+\ell$ inclusive), followed by interleaving real and
     140             :  * imaginary parts of the complex field.
     141             :  *
     142             :  * There are two notable exceptions to this format. One is
     143             :  * `Cce::Tags::ComplexInertialRetardedTime`. The quantity we are actually
     144             :  * interested in is `Cce::Tags::InertialRetardedTime` which is real and only
     145             :  * defined once for every direction $\theta,\phi$ (meaning it does not have
     146             :  * different values at the different radial grid points). However, we use
     147             :  * `Cce::Tags::ComplexInertialRetardedTime` because it has the same data type as
     148             :  * the other tags which makes the internals of the class simpler. This quantity
     149             :  * is stored in `/<SubgroupName>/InertialRetardedTime.vol`.
     150             :  *
     151             :  * The second is `Cce::Tags::OneMinusY`. Even though this quantity is stored as
     152             :  * a `Scalar<SpinWeighted<ComplexDataVector, 0>>` like the others, there is only
     153             :  * one meaningful value per radial grid point. All angular grid points for a
     154             :  * given radius are set to this value, namely $1-y$. Thus we only need to write
     155             :  * this value once for each radial grid point. We do this in a volume subfile
     156             :  * `/<SubgroupName>/OneMinusY.vol` with the elements in the same order as the
     157             :  * radial index order for the spin weighted quantities above.
     158             :  */
     159           1 : class ObserveFields : public Event {
     160             :   template <typename Tag, bool IncludeSecondDeriv = true>
     161             :   // clang-format off
     162           0 :   using zero_one_two_radial_derivs = tmpl::flatten<tmpl::list<
     163             :       Tag,
     164             :       Tags::Dy<Tag>,
     165             :       tmpl::conditional_t<IncludeSecondDeriv,
     166             :                           Tags::Dy<Tags::Dy<Tag>>,
     167             :                           tmpl::list<>>>>;
     168           0 :   using spin_weighted_tags_to_observe = tmpl::flatten<
     169             :       tmpl::list<zero_one_two_radial_derivs<Tags::BondiBeta>,
     170             :                  zero_one_two_radial_derivs<Tags::BondiU>,
     171             :                  zero_one_two_radial_derivs<Tags::BondiQ>,
     172             :                  zero_one_two_radial_derivs<Tags::BondiW>,
     173             :                  zero_one_two_radial_derivs<Tags::BondiH, false>,
     174             :                  zero_one_two_radial_derivs<Tags::BondiJ>,
     175             :                  zero_one_two_radial_derivs<Tags::Du<Tags::BondiJ>>,
     176             :                  Tags::BondiR,
     177             :                  Tags::Psi0,
     178             :                  Tags::Psi1,
     179             :                  Tags::Psi2,
     180             :                  Tags::NewmanPenroseAlpha,
     181             :                  Tags::NewmanPenroseBeta,
     182             :                  Tags::NewmanPenroseGamma,
     183             :                  Tags::NewmanPenroseEpsilon,
     184             :                  // Tags::NewmanPenroseKappa,
     185             :                  // in our choice of tetrad, \kappa=0
     186             :                  Tags::NewmanPenroseTau,
     187             :                  Tags::NewmanPenroseSigma,
     188             :                  Tags::NewmanPenroseRho,
     189             :                  Tags::NewmanPenrosePi,
     190             :                  Tags::NewmanPenroseNu,
     191             :                  Tags::NewmanPenroseMu,
     192             :                  Tags::NewmanPenroseLambda,
     193             :                  Tags::EthRDividedByR,
     194             :                  Tags::DuRDividedByR>>;
     195             :   // clang-format on
     196             : 
     197             :  public:
     198           0 :   using available_tags_to_observe =
     199             :       tmpl::push_back<spin_weighted_tags_to_observe,
     200             :                       Tags::ComplexInertialRetardedTime, Tags::OneMinusY>;
     201             : 
     202             :   /// \cond
     203             :   explicit ObserveFields(CkMigrateMessage* /*unused*/) {}
     204             :   using PUP::able::register_constructor;
     205             :   WRAPPED_PUPable_decl_template(ObserveFields);  // NOLINT
     206             :   /// \endcond
     207             : 
     208             :   /// The name of the subgroup inside the HDF5 file
     209           1 :   struct SubgroupName {
     210           0 :     using type = std::string;
     211           0 :     static constexpr Options::String help = {
     212             :       "The name of the subgroup inside the HDF5 file without an extension and "
     213             :       "without a preceding '/'."};
     214             :   };
     215             : 
     216           0 :   struct VariablesToObserve {
     217           0 :     static constexpr Options::String help = "Subset of variables to observe";
     218           0 :     using type = std::vector<std::string>;
     219           0 :     static size_t lower_bound_on_size() { return 1; }
     220             :   };
     221             : 
     222           0 :   using options = tmpl::list<SubgroupName, VariablesToObserve>;
     223             : 
     224           0 :   static constexpr Options::String help =
     225             :       "Observe volume tensor fields on the characteristic grid. Writes volume "
     226             :       "quantities from the tensors listed in the 'VariablesToObserve' "
     227             :       "option to volume subfiles in the subgroup named by the option "
     228             :       "'SubgroupName', into the volume h5 file named by the option "
     229             :       "'VolumeFileName' of Observers.\n";
     230             : 
     231           0 :   ObserveFields() = default;
     232             : 
     233           0 :   ObserveFields(const std::string& subgroup_name,
     234             :                 const std::vector<std::string>& variables_to_observe,
     235             :                 const Options::Context& context = {});
     236             : 
     237           0 :   using compute_tags_for_observation_box =
     238             :     tmpl::list<Tags::Psi0Compute, Tags::Psi1Compute, Tags::Psi2Compute,
     239             :                Tags::SwshDerivativeCompute<Tags::BondiJ,
     240             :                                                   Spectral::Swsh::Tags::Eth>,
     241             :                Tags::SwshDerivativeCompute<Tags::BondiW,
     242             :                                                   Spectral::Swsh::Tags::Eth>,
     243             :                Tags::NewmanPenroseAlphaCompute, Tags::NewmanPenroseBetaCompute,
     244             :                Tags::NewmanPenroseGammaCompute,
     245             :                Tags::NewmanPenroseEpsilonCompute,
     246             :                // Tags::NewmanPenroseKappaCompute,
     247             :                // in our choice of tetrad, \kappa=0
     248             :                Tags::NewmanPenroseTauCompute,
     249             :                Tags::NewmanPenroseSigmaCompute, Tags::NewmanPenroseRhoCompute,
     250             :                Tags::NewmanPenrosePiCompute, Tags::NewmanPenroseNuCompute,
     251             :                Tags::NewmanPenroseMuCompute, Tags::NewmanPenroseLambdaCompute,
     252             :                Tags::SwshDerivativeCompute<Tags::NewmanPenrosePi,
     253             :                                                  Spectral::Swsh::Tags::Eth>,
     254             :                Tags::SwshDerivativeCompute<Tags::NewmanPenrosePi,
     255             :                                                  Spectral::Swsh::Tags::Ethbar>,
     256             :                Tags::DyCompute<Tags::NewmanPenrosePi>,
     257             :                Tags::DyCompute<Tags::NewmanPenroseMu>
     258             :       >;
     259             : 
     260           0 :   using return_tags = tmpl::list<>;
     261           0 :   using argument_tags = tmpl::list<::Tags::ObservationBox>;
     262             : 
     263             :   template <typename DataBoxType, typename ComputeTagsList,
     264             :             typename Metavariables, typename ArrayIndex,
     265             :             typename ParallelComponent>
     266           0 :   void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,
     267             :                   Parallel::GlobalCache<Metavariables>& cache,
     268             :                   const ArrayIndex& /*array_index*/,
     269             :                   const ParallelComponent* const /*component*/,
     270             :                   const ObservationValue& /*observation_value*/) const {
     271             :     const bool write_synchronously =
     272             :         Parallel::number_of_procs<size_t>(cache) == 1;
     273             : 
     274             :     // Number of points
     275             :     const size_t l_max = get<Tags::LMax>(box);
     276             :     const size_t l_max_plus_one_squared = square(l_max + 1);
     277             :     const size_t number_of_angular_points =
     278             :         Spectral::Swsh::number_of_swsh_collocation_points(l_max);
     279             :     const size_t number_of_radial_grid_points =
     280             :         get<Tags::NumberOfRadialPoints>(box);
     281             : 
     282             :     // Time
     283             :     const double time = get<::Tags::Time>(box);
     284             : 
     285             :     // Observer writer
     286             :     auto observer_proxy = Parallel::get_parallel_component<
     287             :         ::observers::ObserverWriter<Metavariables>>(cache)[0];
     288             : 
     289             :     ////////////////////////////////////////////////////////////
     290             :     // The inertial retarded time is special because it's stored as a
     291             :     // Scalar<DataVector> because it's only real and only has one set of angular
     292             :     // points worth of data to write. However, all the machinery is for a
     293             :     // SpinWeighted<ComplexDataVector>. Luckily there is a
     294             :     // ComplexInertialRetardedTime where the real part is the
     295             :     // InertialRetardedTime and the imaginary part is 0, so we use that instead,
     296             :     // swapping the names where necessary.
     297             :     // Put into volume subfile named /<SubgroupName>/InertialRetardedTime.vol
     298             :     const std::string inertial_retarded_time_name =
     299             :         detail::name<Tags::ComplexInertialRetardedTime>();
     300             :     if (variables_to_observe_.count(inertial_retarded_time_name) == 1) {
     301             :       const std::string subfile_name = subgroup_path_
     302             :                                        + "/" + inertial_retarded_time_name;
     303             :       const observers::ObservationId observation_id{time,
     304             :         subfile_name + ".vol"};
     305             :       const std::vector<size_t> extents_vector{{l_max, l_max}};
     306             :       const std::vector<Spectral::Basis> bases_vector{
     307             :         {Spectral::Basis::SphericalHarmonic,
     308             :          Spectral::Basis::SphericalHarmonic}};
     309             :       const std::vector<Spectral::Quadrature> quadratures_vector{
     310             :         {Spectral::Quadrature::Gauss,
     311             :          Spectral::Quadrature::Equiangular}};
     312             : 
     313             :       const SpinWeighted<ComplexDataVector, 0>& complex_inertial_retarded_time =
     314             :           get(get<Tags::ComplexInertialRetardedTime>(box));
     315             : 
     316             :       // Allocate a buffer to receive the transformed data, since
     317             :       // WriteVolumeData only understands DataVectors, not
     318             :       // ComplexDataVectors.
     319             :       DataVector goldberg_modes_interleaved_dv(2 * l_max_plus_one_squared);
     320             : 
     321             :       // A non-owning view of goldberg_modes_interleaved_dv,
     322             :       // with the correct spin
     323             :       SpinWeighted<ComplexModalVector, 0> goldberg_mode_view;
     324             :       goldberg_mode_view.set_data_ref(
     325             :         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     326             :         reinterpret_cast<std::complex<double>*>(
     327             :           goldberg_modes_interleaved_dv.data()),
     328             :         l_max_plus_one_squared);
     329             : 
     330             :       Spectral::Swsh::libsharp_to_goldberg_modes(
     331             :           make_not_null(&goldberg_mode_view),
     332             :           Spectral::Swsh::swsh_transform(
     333             :               l_max, 1, complex_inertial_retarded_time),
     334             :           l_max);
     335             : 
     336             :       const std::vector<TensorComponent> tensor_components{
     337             :         {inertial_retarded_time_name, goldberg_modes_interleaved_dv}};
     338             : 
     339             :       if (write_synchronously) {
     340             :         Parallel::local_synchronous_action<
     341             :           observers::ThreadedActions::WriteVolumeData>(
     342             :               observer_proxy, cache,
     343             :               Parallel::get<observers::Tags::VolumeFileName>(cache),
     344             :               subfile_name, observation_id,
     345             :               std::vector<ElementVolumeData>{
     346             :                 {inertial_retarded_time_name, tensor_components,
     347             :                  extents_vector, bases_vector,
     348             :                  quadratures_vector}});
     349             :       } else {
     350             :         // Send to observer writer
     351             :         Parallel::threaded_action<
     352             :           observers::ThreadedActions::WriteVolumeData>(
     353             :               observer_proxy,
     354             :               Parallel::get<observers::Tags::VolumeFileName>(cache),
     355             :               subfile_name, observation_id,
     356             :               std::vector<ElementVolumeData>{
     357             :                 {inertial_retarded_time_name, tensor_components,
     358             :                  extents_vector, bases_vector,
     359             :                  quadratures_vector}});
     360             :       }
     361             :     }
     362             : 
     363             :     ////////////////////////////////////////////////////////////
     364             :     // One minus y is also special because every angular grid point for a given
     365             :     // radius holds the same value. Thus we only need to write one double per
     366             :     // radial grid point corresponding to 1 - y. Put into volume subfile named
     367             :     // /<SubgroupName>/OneMinusY.vol
     368             :     const std::string one_minus_y_name = detail::name<Tags::OneMinusY>();
     369             :     if (variables_to_observe_.count(one_minus_y_name) == 1) {
     370             :       const std::string subfile_name = subgroup_path_ + "/" + one_minus_y_name;
     371             :       const observers::ObservationId observation_id{time,
     372             :         subfile_name + ".vol"};
     373             :       const std::vector<size_t> extents_vector{number_of_radial_grid_points};
     374             :       const std::vector<Spectral::Basis> bases_vector{
     375             :         Spectral::Basis::Legendre};
     376             :       const std::vector<Spectral::Quadrature> quadratures_vector{
     377             :         Spectral::Quadrature::GaussLobatto};
     378             : 
     379             :       const ComplexDataVector& one_minus_y =
     380             :           get(get<Tags::OneMinusY>(box)).data();
     381             : 
     382             :       DataVector one_minus_y_to_write(number_of_radial_grid_points);
     383             : 
     384             :       for (size_t radial_index = 0; radial_index < number_of_radial_grid_points;
     385             :            radial_index++) {
     386             :         one_minus_y_to_write[radial_index] =
     387             :             real(one_minus_y[radial_index * number_of_angular_points]);
     388             :       }
     389             : 
     390             :       const std::vector<TensorComponent> tensor_components{
     391             :         {one_minus_y_name, one_minus_y_to_write}};
     392             : 
     393             :       if (write_synchronously) {
     394             :         Parallel::local_synchronous_action<
     395             :           observers::ThreadedActions::WriteVolumeData>(
     396             :               observer_proxy, cache,
     397             :               Parallel::get<observers::Tags::VolumeFileName>(cache),
     398             :               subfile_name, observation_id,
     399             :               std::vector<ElementVolumeData>{
     400             :                 {one_minus_y_name, tensor_components,
     401             :                  extents_vector, bases_vector,
     402             :                  quadratures_vector}});
     403             :       } else {
     404             :         // Send to observer writer
     405             :         Parallel::threaded_action<
     406             :           observers::ThreadedActions::WriteVolumeData>(
     407             :               observer_proxy,
     408             :               Parallel::get<observers::Tags::VolumeFileName>(cache),
     409             :               subfile_name, observation_id,
     410             :               std::vector<ElementVolumeData>{
     411             :                 {one_minus_y_name, tensor_components,
     412             :                  extents_vector, bases_vector,
     413             :                  quadratures_vector}});
     414             :       }
     415             :     }
     416             : 
     417             :     ////////////////////////////////////////////////////////////
     418             :     // Everything else gets written together into the volume subfile named
     419             :     // /<SubgroupName>/VolumeData.vol
     420             : 
     421             :     // Field-independent info for writing into volume data file
     422             :     const std::string subfile_name = subgroup_path_ + "/VolumeData";
     423             :     const observers::ObservationId observation_id{time,
     424             :       subfile_name + ".vol"};
     425             :     const std::vector<size_t> extents_vector{
     426             :       {number_of_radial_grid_points, l_max, l_max}};
     427             :     const std::vector<Spectral::Basis> bases_vector{
     428             :       {Spectral::Basis::Legendre,
     429             :        Spectral::Basis::SphericalHarmonic,
     430             :        Spectral::Basis::SphericalHarmonic}};
     431             :     const std::vector<Spectral::Quadrature> quadratures_vector{
     432             :       {Spectral::Quadrature::GaussLobatto,
     433             :        Spectral::Quadrature::Gauss,
     434             :        Spectral::Quadrature::Equiangular}};
     435             : 
     436             :     // Create tensor_components by looping over all available spin
     437             :     // weighted tags and checking if we are observing this tag.
     438             :     std::vector<TensorComponent> tensor_components;
     439             :     tmpl::for_each<spin_weighted_tags_to_observe>([&](auto tag_v) {
     440             :       using tag = tmpl::type_from<decltype(tag_v)>;
     441             :       constexpr int spin = tag::type::type::spin;
     442             :       const std::string name = detail::name<tag>();
     443             : 
     444             :       // If we aren't observing this tag, then skip it
     445             :       if (not variables_to_observe_.contains(name)) {
     446             :         return;
     447             :       }
     448             : 
     449             :       const SpinWeighted<ComplexDataVector, spin>& field =
     450             :         get(get<tag>(box));
     451             : 
     452             :       // Allocate a buffer to receive the transformed data, since
     453             :       // WriteVolumeData only understands DataVectors, not
     454             :       // ComplexDataVectors.
     455             :       DataVector goldberg_modes_interleaved_dv(2 *
     456             :         l_max_plus_one_squared * number_of_radial_grid_points);
     457             : 
     458             :       // A non-owning view of goldberg_modes_interleaved_dv,
     459             :       // with the correct spin
     460             :       SpinWeighted<ComplexModalVector, spin> goldberg_mode_view;
     461             :       goldberg_mode_view.set_data_ref(
     462             :         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     463             :         reinterpret_cast<std::complex<double>*>(
     464             :           goldberg_modes_interleaved_dv.data()),
     465             :         l_max_plus_one_squared * number_of_radial_grid_points);
     466             : 
     467             :       Spectral::Swsh::libsharp_to_goldberg_modes(
     468             :         make_not_null(&goldberg_mode_view),
     469             :         Spectral::Swsh::swsh_transform(l_max,
     470             :           number_of_radial_grid_points, field), l_max);
     471             : 
     472             :       tensor_components.emplace_back(
     473             :         name, std::move(goldberg_modes_interleaved_dv));
     474             : 
     475             :     });
     476             : 
     477             :     if (write_synchronously) {
     478             :       Parallel::local_synchronous_action<
     479             :         observers::ThreadedActions::WriteVolumeData>(
     480             :         observer_proxy, cache,
     481             :         Parallel::get<observers::Tags::VolumeFileName>(cache),
     482             :         subfile_name, observation_id,
     483             :         std::vector<ElementVolumeData>{
     484             :           {"VolumeData", tensor_components,
     485             :            extents_vector, bases_vector,
     486             :            quadratures_vector}});
     487             :     } else {
     488             :       // Send to observer writer
     489             :       Parallel::threaded_action<
     490             :         observers::ThreadedActions::WriteVolumeData>(
     491             :         observer_proxy,
     492             :         Parallel::get<observers::Tags::VolumeFileName>(cache),
     493             :         subfile_name, observation_id,
     494             :         std::vector<ElementVolumeData>{
     495             :           {"VolumeData", tensor_components,
     496             :            extents_vector, bases_vector,
     497             :            quadratures_vector}});
     498             :     }
     499             :   }
     500             : 
     501           0 :   using is_ready_argument_tags = tmpl::list<>;
     502             : 
     503             :   template <typename Metavariables, typename ArrayIndex, typename Component>
     504           0 :   bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,
     505             :                 const ArrayIndex& /*array_index*/,
     506             :                 const Component* const /*meta*/) const {
     507             :     return true;
     508             :   }
     509             : 
     510           1 :   bool needs_evolved_variables() const override { return true; }
     511             : 
     512             :   // NOLINTNEXTLINE(google-runtime-references)
     513           0 :   void pup(PUP::er& p) override {
     514             :     Event::pup(p);
     515             :     p | subgroup_path_;
     516             :     p | variables_to_observe_;
     517             :   }
     518             : 
     519             :  private:
     520           0 :   std::string subgroup_path_;
     521           0 :   std::unordered_set<std::string> variables_to_observe_;
     522             : };
     523             : 
     524             : ObserveFields::ObserveFields(
     525             :     const std::string& subgroup_name,
     526             :     const std::vector<std::string>& variables_to_observe,
     527             :     const Options::Context& context)
     528             :     : subgroup_path_("/" + subgroup_name),
     529             :       variables_to_observe_([&context, &variables_to_observe]() {
     530             :         std::unordered_set<std::string> result{};
     531             :         for (const auto& tensor : variables_to_observe) {
     532             :           if (result.contains(tensor)) {
     533             :             PARSE_ERROR(
     534             :                 context,
     535             :                 "Listed variable '"
     536             :                     << tensor
     537             :                     << "' more than once in list of variables to observe.");
     538             :           }
     539             :           result.insert(tensor);
     540             :         }
     541             :         return result;
     542             :       }()) {
     543             :   std::unordered_set<std::string> valid_tensors{};
     544             :   tmpl::for_each<available_tags_to_observe>([&valid_tensors](auto tag_v) {
     545             :     using tag = tmpl::type_from<decltype(tag_v)>;
     546             :     valid_tensors.insert(detail::name<tag>());
     547             :   });
     548             : 
     549             :   for (const auto& name : variables_to_observe_) {
     550             :     if (not valid_tensors.contains(name)) {
     551             :       PARSE_ERROR(
     552             :           context,
     553             :           name << " is not an available variable. Available variables:\n"
     554             :                << valid_tensors);
     555             :     }
     556             :   }
     557             : }
     558             : 
     559             : /// \cond
     560             : PUP::able::PUP_ID ObserveFields::my_PUP_ID = 0;  // NOLINT
     561             : /// \endcond
     562             : }  // namespace Cce::Events

Generated by: LCOV version 1.14