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 <pup.h>
9 : #include <pup_stl.h>
10 : #include <sstream>
11 : #include <string>
12 : #include <type_traits>
13 : #include <utility>
14 : #include <vector>
15 :
16 : #include "IO/Observer/Helpers.hpp"
17 : #include "IO/Observer/ObservationId.hpp"
18 : #include "IO/Observer/ObserverComponent.hpp" // IWYU pragma: keep
19 : #include "IO/Observer/Protocols/ReductionDataFormatter.hpp"
20 : #include "IO/Observer/ReductionActions.hpp" // IWYU pragma: keep
21 : #include "IO/Observer/TypeOfObservation.hpp"
22 : #include "Options/String.hpp"
23 : #include "Parallel/ArrayComponentId.hpp"
24 : #include "Parallel/ArrayIndex.hpp"
25 : #include "Parallel/GlobalCache.hpp"
26 : #include "Parallel/Invoke.hpp"
27 : #include "Parallel/Local.hpp"
28 : #include "Parallel/Reduction.hpp"
29 : #include "ParallelAlgorithms/EventsAndTriggers/Event.hpp"
30 : #include "Time/Time.hpp"
31 : #include "Utilities/Functional.hpp"
32 : #include "Utilities/ProtocolHelpers.hpp"
33 : #include "Utilities/Serialization/CharmPupable.hpp"
34 : #include "Utilities/System/ParallelInfo.hpp"
35 : #include "Utilities/TMPL.hpp"
36 :
37 : /// \cond
38 : namespace Tags {
39 : struct TimeStep;
40 : } // namespace Tags
41 : /// \endcond
42 :
43 : namespace Events {
44 : namespace detail {
45 : using ObserveTimeStepReductionData = Parallel::ReductionData<
46 : Parallel::ReductionDatum<double, funcl::AssertEqual<>>,
47 : Parallel::ReductionDatum<size_t, funcl::Plus<>>,
48 : Parallel::ReductionDatum<double, funcl::AssertEqual<>>,
49 : Parallel::ReductionDatum<double, funcl::Min<>>,
50 : Parallel::ReductionDatum<double, funcl::Max<>>,
51 : Parallel::ReductionDatum<
52 : double, funcl::Plus<>,
53 : funcl::Divides<funcl::Literal<1, double>, funcl::Divides<>>,
54 : std::index_sequence<1>>,
55 : Parallel::ReductionDatum<double, funcl::Min<>>,
56 : Parallel::ReductionDatum<double, funcl::Max<>>>;
57 :
58 : struct FormatTimeOutput
59 : : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {
60 : using reduction_data = ObserveTimeStepReductionData;
61 : std::string operator()(const double time, const size_t /* num_points */,
62 : const double /* slab_size */,
63 : const double /* min_time_step */,
64 : const double /* max_time_step */,
65 : const double /* effective_time_step */,
66 : const double min_wall_time,
67 : const double max_wall_time) const {
68 : std::stringstream ss;
69 : ss << "Simulation time: " << std::to_string(time)
70 : << "\n Wall time: " << sys::pretty_wall_time(min_wall_time)
71 : << " (min) - " << sys::pretty_wall_time(max_wall_time) << " (max)";
72 : return ss.str();
73 : }
74 : // NOLINTNEXTLINE
75 : void pup(PUP::er& /*p*/) {}
76 : };
77 : } // namespace detail
78 :
79 : /*!
80 : * \brief %Observe the size of the time steps.
81 : *
82 : * Writes reduction quantities:
83 : * - `%Time`
84 : * - `NumberOfPoints`
85 : * - `%Slab size`
86 : * - `Minimum time step`
87 : * - `Maximum time step`
88 : * - `Effective time step`
89 : *
90 : * The effective time step is the step size of a global-time-stepping
91 : * method that would perform a similar amount of work. This is the
92 : * harmonic mean of the step size over all grid points:
93 : *
94 : * \f{equation}
95 : * (\Delta t)_{\text{eff}}^{-1} =
96 : * \frac{\sum_{i \in \text{points}} (\Delta t)_i^{-1}}{N_{\text{points}}}.
97 : * \f}
98 : *
99 : * This corresponds to averaging the number of steps per unit time
100 : * taken by all points.
101 : *
102 : * All values are reported as positive numbers, even for backwards
103 : * evolutions.
104 : */
105 : template <typename System>
106 1 : class ObserveTimeStep : public Event {
107 : private:
108 0 : using ReductionData = Events::detail::ObserveTimeStepReductionData;
109 :
110 : public:
111 : /// The name of the subfile inside the HDF5 file
112 1 : struct SubfileName {
113 0 : using type = std::string;
114 0 : static constexpr Options::String help = {
115 : "The name of the subfile inside the HDF5 file without an extension and "
116 : "without a preceding '/'."};
117 : };
118 :
119 0 : struct PrintTimeToTerminal {
120 0 : using type = bool;
121 0 : static constexpr Options::String help = {
122 : "Whether to print the time to screen."};
123 : };
124 :
125 0 : struct ObservePerCore {
126 0 : using type = bool;
127 0 : static constexpr Options::String help = {
128 : "Also write the data per-core in a file per-node."};
129 : };
130 :
131 : /// \cond
132 : explicit ObserveTimeStep(CkMigrateMessage* /*unused*/) {}
133 : using PUP::able::register_constructor;
134 : WRAPPED_PUPable_decl_template(ObserveTimeStep); // NOLINT
135 : /// \endcond
136 :
137 0 : using options = tmpl::list<SubfileName, PrintTimeToTerminal, ObservePerCore>;
138 0 : static constexpr Options::String help =
139 : "Observe the size of the time steps.\n"
140 : "\n"
141 : "Writes reduction quantities:\n"
142 : "- Time\n"
143 : "- NumberOfPoints\n"
144 : "- Slab size\n"
145 : "- Minimum time step\n"
146 : "- Maximum time step\n"
147 : "- Effective time step\n"
148 : "\n"
149 : "The effective time step is the step size of a global-time-stepping\n"
150 : "method that would perform a similar amount of work.\n"
151 : "\n"
152 : "All values are reported as positive numbers, even for backwards\n"
153 : "evolutions.";
154 :
155 0 : ObserveTimeStep() = default;
156 0 : explicit ObserveTimeStep(const std::string& subfile_name,
157 : const bool output_time, const bool observe_per_core);
158 :
159 0 : using observed_reduction_data_tags =
160 : observers::make_reduction_data_tags<tmpl::list<ReductionData>>;
161 :
162 0 : using compute_tags_for_observation_box = tmpl::list<>;
163 :
164 0 : using return_tags = tmpl::list<>;
165 : // We obtain the grid size from the variables, rather than the mesh,
166 : // so that this observer is not DG-specific.
167 0 : using argument_tags =
168 : tmpl::list<::Tags::TimeStep, typename System::variables_tag>;
169 :
170 : template <typename ArrayIndex, typename ParallelComponent,
171 : typename Metavariables>
172 0 : void operator()(const TimeDelta& time_step,
173 : const typename System::variables_tag::type& variables,
174 : Parallel::GlobalCache<Metavariables>& cache,
175 : const ArrayIndex& array_index,
176 : const ParallelComponent* const /*meta*/,
177 : const ObservationValue& observation_value) const {
178 : const size_t number_of_grid_points = variables.number_of_grid_points();
179 : const double slab_size = time_step.slab().duration().value();
180 : const double step_size = abs(time_step.value());
181 : const double wall_time = sys::wall_time();
182 :
183 : auto formatter =
184 : output_time_ ? std::make_optional(Events::detail::FormatTimeOutput{})
185 : : std::nullopt;
186 :
187 : auto& local_observer = *Parallel::local_branch(
188 : Parallel::get_parallel_component<
189 : tmpl::conditional_t<Parallel::is_nodegroup_v<ParallelComponent>,
190 : observers::ObserverWriter<Metavariables>,
191 : observers::Observer<Metavariables>>>(cache));
192 : Parallel::simple_action<tmpl::conditional_t<
193 : Parallel::is_nodegroup_v<ParallelComponent>,
194 : observers::ThreadedActions::CollectReductionDataOnNode,
195 : observers::Actions::ContributeReductionData>>(
196 : local_observer,
197 : observers::ObservationId(observation_value.value,
198 : subfile_path_ + ".dat"),
199 : Parallel::ArrayComponentId{
200 : std::add_pointer_t<ParallelComponent>{nullptr},
201 : Parallel::ArrayIndex<ArrayIndex>(array_index)},
202 : subfile_path_,
203 : std::vector<std::string>{observation_value.name, "NumberOfPoints",
204 : "Slab size", "Minimum time step",
205 : "Maximum time step", "Effective time step",
206 : "Minimum Walltime", "Maximum Walltime"},
207 : ReductionData{observation_value.value, number_of_grid_points, slab_size,
208 : step_size, step_size, number_of_grid_points / step_size,
209 : wall_time, wall_time},
210 : std::move(formatter), observe_per_core_);
211 : }
212 :
213 0 : using observation_registration_tags = tmpl::list<>;
214 : std::pair<observers::TypeOfObservation, observers::ObservationKey>
215 0 : get_observation_type_and_key_for_registration() const {
216 : return {observers::TypeOfObservation::Reduction,
217 : observers::ObservationKey{subfile_path_ + ".dat"}};
218 : }
219 :
220 0 : using is_ready_argument_tags = tmpl::list<>;
221 :
222 : template <typename Metavariables, typename ArrayIndex, typename Component>
223 0 : bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,
224 : const ArrayIndex& /*array_index*/,
225 : const Component* const /*meta*/) const {
226 : return true;
227 : }
228 :
229 1 : bool needs_evolved_variables() const override { return false; }
230 :
231 : // NOLINTNEXTLINE(google-runtime-references)
232 0 : void pup(PUP::er& p) override {
233 : Event::pup(p);
234 : p | subfile_path_;
235 : p | output_time_;
236 : p | observe_per_core_;
237 : }
238 :
239 : private:
240 0 : std::string subfile_path_;
241 0 : bool output_time_;
242 0 : bool observe_per_core_;
243 : };
244 :
245 : template <typename System>
246 : ObserveTimeStep<System>::ObserveTimeStep(const std::string& subfile_name,
247 : const bool output_time,
248 : const bool observe_per_core)
249 : : subfile_path_("/" + subfile_name),
250 : output_time_(output_time),
251 : observe_per_core_(observe_per_core) {}
252 :
253 : /// \cond
254 : template <typename System>
255 : PUP::able::PUP_ID ObserveTimeStep<System>::my_PUP_ID = 0; // NOLINT
256 : /// \endcond
257 : } // namespace Events
|