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 = tmpl::list<
238 : Tags::Psi0Compute, Tags::Psi1Compute, Tags::Psi2Compute,
239 : Tags::SwshDerivativeCompute<Tags::BondiJ, Spectral::Swsh::Tags::Eth>,
240 : Tags::SwshDerivativeCompute<Tags::BondiW, Spectral::Swsh::Tags::Eth>,
241 : Tags::NewmanPenroseAlphaCompute, Tags::NewmanPenroseBetaCompute,
242 : Tags::NewmanPenroseGammaCompute, Tags::NewmanPenroseEpsilonCompute,
243 : // Tags::NewmanPenroseKappaCompute,
244 : // in our choice of tetrad, \kappa=0
245 : Tags::NewmanPenroseTauCompute, Tags::NewmanPenroseSigmaCompute,
246 : Tags::NewmanPenroseRhoCompute, Tags::NewmanPenrosePiCompute,
247 : Tags::NewmanPenroseNuCompute, Tags::NewmanPenroseMuCompute,
248 : Tags::NewmanPenroseLambdaCompute,
249 : Tags::SwshDerivativeCompute<Tags::NewmanPenrosePi,
250 : Spectral::Swsh::Tags::Eth>,
251 : Tags::SwshDerivativeCompute<Tags::NewmanPenrosePi,
252 : Spectral::Swsh::Tags::Ethbar>,
253 : Tags::DyCompute<Tags::NewmanPenrosePi>,
254 : Tags::DyCompute<Tags::NewmanPenroseMu>,
255 : // H is the final hypersurface quantity, so unlike the other
256 : // Bondi variables its radial derivative is not needed by (and
257 : // is not computed during) the hypersurface integration steps.
258 : // It is only of interest as volume output, so it is computed
259 : // on demand here rather than stored in the evolution DataBox.
260 : Tags::DyCompute<Tags::BondiH>>;
261 :
262 0 : using return_tags = tmpl::list<>;
263 0 : using argument_tags = tmpl::list<::Tags::ObservationBox>;
264 :
265 : template <typename DataBoxType, typename ComputeTagsList,
266 : typename Metavariables, typename ArrayIndex,
267 : typename ParallelComponent>
268 0 : void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,
269 : Parallel::GlobalCache<Metavariables>& cache,
270 : const ArrayIndex& /*array_index*/,
271 : const ParallelComponent* const /*component*/,
272 : const ObservationValue& /*observation_value*/) const {
273 : const bool write_synchronously =
274 : Parallel::number_of_procs<size_t>(cache) == 1;
275 :
276 : // Number of points
277 : const size_t l_max = get<Tags::LMax>(box);
278 : const size_t l_max_plus_one_squared = square(l_max + 1);
279 : const size_t number_of_angular_points =
280 : Spectral::Swsh::number_of_swsh_collocation_points(l_max);
281 : const size_t number_of_radial_grid_points =
282 : get<Tags::NumberOfRadialPoints>(box);
283 :
284 : // Time
285 : const double time = get<::Tags::Time>(box);
286 :
287 : // Observer writer
288 : auto observer_proxy = Parallel::get_parallel_component<
289 : ::observers::ObserverWriter<Metavariables>>(cache)[0];
290 :
291 : ////////////////////////////////////////////////////////////
292 : // The inertial retarded time is special because it's stored as a
293 : // Scalar<DataVector> because it's only real and only has one set of angular
294 : // points worth of data to write. However, all the machinery is for a
295 : // SpinWeighted<ComplexDataVector>. Luckily there is a
296 : // ComplexInertialRetardedTime where the real part is the
297 : // InertialRetardedTime and the imaginary part is 0, so we use that instead,
298 : // swapping the names where necessary.
299 : // Put into volume subfile named /<SubgroupName>/InertialRetardedTime.vol
300 : const std::string inertial_retarded_time_name =
301 : detail::name<Tags::ComplexInertialRetardedTime>();
302 : if (variables_to_observe_.count(inertial_retarded_time_name) == 1) {
303 : const std::string subfile_name = subgroup_path_
304 : + "/" + inertial_retarded_time_name;
305 : const observers::ObservationId observation_id{time,
306 : subfile_name + ".vol"};
307 : const std::vector<size_t> extents_vector{{l_max, l_max}};
308 : const std::vector<Spectral::Basis> bases_vector{
309 : {Spectral::Basis::SphericalHarmonic,
310 : Spectral::Basis::SphericalHarmonic}};
311 : const std::vector<Spectral::Quadrature> quadratures_vector{
312 : {Spectral::Quadrature::Gauss,
313 : Spectral::Quadrature::Equiangular}};
314 :
315 : const SpinWeighted<ComplexDataVector, 0>& complex_inertial_retarded_time =
316 : get(get<Tags::ComplexInertialRetardedTime>(box));
317 :
318 : // Allocate a buffer to receive the transformed data, since
319 : // WriteVolumeData only understands DataVectors, not
320 : // ComplexDataVectors.
321 : DataVector goldberg_modes_interleaved_dv(2 * l_max_plus_one_squared);
322 :
323 : // A non-owning view of goldberg_modes_interleaved_dv,
324 : // with the correct spin
325 : SpinWeighted<ComplexModalVector, 0> goldberg_mode_view;
326 : goldberg_mode_view.set_data_ref(
327 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
328 : reinterpret_cast<std::complex<double>*>(
329 : goldberg_modes_interleaved_dv.data()),
330 : l_max_plus_one_squared);
331 :
332 : Spectral::Swsh::libsharp_to_goldberg_modes(
333 : make_not_null(&goldberg_mode_view),
334 : Spectral::Swsh::swsh_transform(
335 : l_max, 1, complex_inertial_retarded_time),
336 : l_max);
337 :
338 : const std::vector<TensorComponent> tensor_components{
339 : {inertial_retarded_time_name, goldberg_modes_interleaved_dv}};
340 :
341 : if (write_synchronously) {
342 : Parallel::local_synchronous_action<
343 : observers::ThreadedActions::WriteVolumeData>(
344 : observer_proxy, cache,
345 : Parallel::get<observers::Tags::VolumeFileName>(cache),
346 : subfile_name, observation_id,
347 : std::vector<ElementVolumeData>{
348 : {inertial_retarded_time_name, tensor_components,
349 : extents_vector, bases_vector,
350 : quadratures_vector}});
351 : } else {
352 : // Send to observer writer
353 : Parallel::threaded_action<
354 : observers::ThreadedActions::WriteVolumeData>(
355 : observer_proxy,
356 : Parallel::get<observers::Tags::VolumeFileName>(cache),
357 : subfile_name, observation_id,
358 : std::vector<ElementVolumeData>{
359 : {inertial_retarded_time_name, tensor_components,
360 : extents_vector, bases_vector,
361 : quadratures_vector}});
362 : }
363 : }
364 :
365 : ////////////////////////////////////////////////////////////
366 : // One minus y is also special because every angular grid point for a given
367 : // radius holds the same value. Thus we only need to write one double per
368 : // radial grid point corresponding to 1 - y. Put into volume subfile named
369 : // /<SubgroupName>/OneMinusY.vol
370 : const std::string one_minus_y_name = detail::name<Tags::OneMinusY>();
371 : if (variables_to_observe_.count(one_minus_y_name) == 1) {
372 : const std::string subfile_name = subgroup_path_ + "/" + one_minus_y_name;
373 : const observers::ObservationId observation_id{time,
374 : subfile_name + ".vol"};
375 : const std::vector<size_t> extents_vector{number_of_radial_grid_points};
376 : const std::vector<Spectral::Basis> bases_vector{
377 : Spectral::Basis::Legendre};
378 : const std::vector<Spectral::Quadrature> quadratures_vector{
379 : Spectral::Quadrature::GaussLobatto};
380 :
381 : const ComplexDataVector& one_minus_y =
382 : get(get<Tags::OneMinusY>(box)).data();
383 :
384 : DataVector one_minus_y_to_write(number_of_radial_grid_points);
385 :
386 : for (size_t radial_index = 0; radial_index < number_of_radial_grid_points;
387 : radial_index++) {
388 : one_minus_y_to_write[radial_index] =
389 : real(one_minus_y[radial_index * number_of_angular_points]);
390 : }
391 :
392 : const std::vector<TensorComponent> tensor_components{
393 : {one_minus_y_name, one_minus_y_to_write}};
394 :
395 : if (write_synchronously) {
396 : Parallel::local_synchronous_action<
397 : observers::ThreadedActions::WriteVolumeData>(
398 : observer_proxy, cache,
399 : Parallel::get<observers::Tags::VolumeFileName>(cache),
400 : subfile_name, observation_id,
401 : std::vector<ElementVolumeData>{
402 : {one_minus_y_name, tensor_components,
403 : extents_vector, bases_vector,
404 : quadratures_vector}});
405 : } else {
406 : // Send to observer writer
407 : Parallel::threaded_action<
408 : observers::ThreadedActions::WriteVolumeData>(
409 : observer_proxy,
410 : Parallel::get<observers::Tags::VolumeFileName>(cache),
411 : subfile_name, observation_id,
412 : std::vector<ElementVolumeData>{
413 : {one_minus_y_name, tensor_components,
414 : extents_vector, bases_vector,
415 : quadratures_vector}});
416 : }
417 : }
418 :
419 : ////////////////////////////////////////////////////////////
420 : // Everything else gets written together into the volume subfile named
421 : // /<SubgroupName>/VolumeData.vol
422 :
423 : // Field-independent info for writing into volume data file
424 : const std::string subfile_name = subgroup_path_ + "/VolumeData";
425 : const observers::ObservationId observation_id{time,
426 : subfile_name + ".vol"};
427 : const std::vector<size_t> extents_vector{
428 : {number_of_radial_grid_points, l_max, l_max}};
429 : const std::vector<Spectral::Basis> bases_vector{
430 : {Spectral::Basis::Legendre,
431 : Spectral::Basis::SphericalHarmonic,
432 : Spectral::Basis::SphericalHarmonic}};
433 : const std::vector<Spectral::Quadrature> quadratures_vector{
434 : {Spectral::Quadrature::GaussLobatto,
435 : Spectral::Quadrature::Gauss,
436 : Spectral::Quadrature::Equiangular}};
437 :
438 : // Create tensor_components by looping over all available spin
439 : // weighted tags and checking if we are observing this tag.
440 : std::vector<TensorComponent> tensor_components;
441 : tmpl::for_each<spin_weighted_tags_to_observe>([&](auto tag_v) {
442 : using tag = tmpl::type_from<decltype(tag_v)>;
443 : constexpr int spin = tag::type::type::spin;
444 : const std::string name = detail::name<tag>();
445 :
446 : // If we aren't observing this tag, then skip it
447 : if (not variables_to_observe_.contains(name)) {
448 : return;
449 : }
450 :
451 : const SpinWeighted<ComplexDataVector, spin>& field =
452 : get(get<tag>(box));
453 :
454 : // Allocate a buffer to receive the transformed data, since
455 : // WriteVolumeData only understands DataVectors, not
456 : // ComplexDataVectors.
457 : DataVector goldberg_modes_interleaved_dv(2 *
458 : l_max_plus_one_squared * number_of_radial_grid_points);
459 :
460 : // A non-owning view of goldberg_modes_interleaved_dv,
461 : // with the correct spin
462 : SpinWeighted<ComplexModalVector, spin> goldberg_mode_view;
463 : goldberg_mode_view.set_data_ref(
464 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
465 : reinterpret_cast<std::complex<double>*>(
466 : goldberg_modes_interleaved_dv.data()),
467 : l_max_plus_one_squared * number_of_radial_grid_points);
468 :
469 : Spectral::Swsh::libsharp_to_goldberg_modes(
470 : make_not_null(&goldberg_mode_view),
471 : Spectral::Swsh::swsh_transform(l_max,
472 : number_of_radial_grid_points, field), l_max);
473 :
474 : tensor_components.emplace_back(
475 : name, std::move(goldberg_modes_interleaved_dv));
476 :
477 : });
478 :
479 : if (write_synchronously) {
480 : Parallel::local_synchronous_action<
481 : observers::ThreadedActions::WriteVolumeData>(
482 : observer_proxy, cache,
483 : Parallel::get<observers::Tags::VolumeFileName>(cache),
484 : subfile_name, observation_id,
485 : std::vector<ElementVolumeData>{
486 : {"VolumeData", tensor_components,
487 : extents_vector, bases_vector,
488 : quadratures_vector}});
489 : } else {
490 : // Send to observer writer
491 : Parallel::threaded_action<
492 : observers::ThreadedActions::WriteVolumeData>(
493 : observer_proxy,
494 : Parallel::get<observers::Tags::VolumeFileName>(cache),
495 : subfile_name, observation_id,
496 : std::vector<ElementVolumeData>{
497 : {"VolumeData", tensor_components,
498 : extents_vector, bases_vector,
499 : quadratures_vector}});
500 : }
501 : }
502 :
503 0 : using is_ready_argument_tags = tmpl::list<>;
504 :
505 : template <typename Metavariables, typename ArrayIndex, typename Component>
506 0 : bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,
507 : const ArrayIndex& /*array_index*/,
508 : const Component* const /*meta*/) const {
509 : return true;
510 : }
511 :
512 1 : bool needs_evolved_variables() const override { return true; }
513 :
514 : // NOLINTNEXTLINE(google-runtime-references)
515 0 : void pup(PUP::er& p) override {
516 : Event::pup(p);
517 : p | subgroup_path_;
518 : p | variables_to_observe_;
519 : }
520 :
521 : private:
522 0 : std::string subgroup_path_;
523 0 : std::unordered_set<std::string> variables_to_observe_;
524 : };
525 :
526 : ObserveFields::ObserveFields(
527 : const std::string& subgroup_name,
528 : const std::vector<std::string>& variables_to_observe,
529 : const Options::Context& context)
530 : : subgroup_path_("/" + subgroup_name),
531 : variables_to_observe_([&context, &variables_to_observe]() {
532 : std::unordered_set<std::string> result{};
533 : for (const auto& tensor : variables_to_observe) {
534 : if (result.contains(tensor)) {
535 : PARSE_ERROR(
536 : context,
537 : "Listed variable '"
538 : << tensor
539 : << "' more than once in list of variables to observe.");
540 : }
541 : result.insert(tensor);
542 : }
543 : return result;
544 : }()) {
545 : std::unordered_set<std::string> valid_tensors{};
546 : tmpl::for_each<available_tags_to_observe>([&valid_tensors](auto tag_v) {
547 : using tag = tmpl::type_from<decltype(tag_v)>;
548 : valid_tensors.insert(detail::name<tag>());
549 : });
550 :
551 : for (const auto& name : variables_to_observe_) {
552 : if (not valid_tensors.contains(name)) {
553 : PARSE_ERROR(
554 : context,
555 : name << " is not an available variable. Available variables:\n"
556 : << valid_tensors);
557 : }
558 : }
559 : }
560 :
561 : /// \cond
562 : PUP::able::PUP_ID ObserveFields::my_PUP_ID = 0; // NOLINT
563 : /// \endcond
564 : } // namespace Cce::Events
|