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
|