Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <array>
7 : #include <cstddef>
8 : #include <memory>
9 : #include <optional>
10 : #include <string>
11 : #include <tuple>
12 : #include <vector>
13 :
14 : #include "ControlSystem/Component.hpp"
15 : #include "DataStructures/DataVector.hpp"
16 : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
17 : #include "Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp"
18 : #include "IO/Observer/ReductionActions.hpp"
19 : #include "Parallel/GlobalCache.hpp"
20 : #include "Parallel/Info.hpp"
21 : #include "Parallel/Invoke.hpp"
22 :
23 : namespace control_system {
24 : /*!
25 : * \ingroup ControlSystemGroup
26 : * \brief Writes all components of a function of time to disk at a specific time
27 : * from a control system after it updates the functions of time.
28 : *
29 : * \details The columns of data written are:
30 : * - %Time
31 : * - FunctionOfTime
32 : * - dtFunctionOfTime
33 : * - d2tFunctionOfTime
34 : * - ControlError
35 : * - dtControlError
36 : * - DampingTimescale
37 : *
38 : * Data will be stored in the reduction file. All subfiles for the control
39 : * system within the H5 file will be under the group "/ControlSystems".
40 : * Within this group, there will be one group for each control system. The name
41 : * of each group will be the result of the `name()` function from each control
42 : * system. An example would look like
43 : *
44 : * - /ControlSystems/SystemA
45 : * - /ControlSystems/SystemB
46 : * - /ControlSystems/SystemC
47 : *
48 : * Then, within each system group, there will be one subfile for each component
49 : * of the function of time that is being controlled. The name of this subfile is
50 : * the name of the component. The name of each component will be the result of
51 : * the `component_name(i)` function from the control system, where `i` is the
52 : * index of the component. For example, if "SystemA" has 3 components with names
53 : * "X", "Y", and "Z", then the subfiles would look like
54 : *
55 : * - /ControlSystems/SystemA/X.dat
56 : * - /ControlSystems/SystemA/Y.dat
57 : * - /ControlSystems/SystemA/Z.dat
58 : */
59 : template <typename ControlSystem, typename Metavariables>
60 1 : void write_components_to_disk(
61 : const double time, Parallel::GlobalCache<Metavariables>& cache,
62 : const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>&
63 : function_of_time,
64 : const std::array<DataVector, 2>& q_and_dtq, const DataVector& timescales) {
65 : auto& observer_writer_proxy = Parallel::get_parallel_component<
66 : observers::ObserverWriter<Metavariables>>(cache);
67 :
68 : constexpr size_t deriv_order = ControlSystem::deriv_order;
69 : std::array<DataVector, 3> function_at_current_time{};
70 : const auto* const quat_func_of_time = dynamic_cast<
71 : const domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>*>(
72 : function_of_time.get());
73 : if (quat_func_of_time == nullptr) {
74 : // Just call the usual `func_and_2_derivs` member.
75 : function_at_current_time = function_of_time->func_and_2_derivs(time);
76 : } else {
77 : // If we are working with a QuaternionFunctionOfTime, we aren't actually
78 : // controlling a quaternion. We are controlling a small change in angle
79 : // associated with the angular velocity in each direction. Because of this,
80 : // we want to write the component data of the thing we are actually
81 : // controlling. This is accessed by the `angle_func_and_2_derivs` member of
82 : // a QuaternionFunctionOfTime. Since `angle_func_and_2_derivs` is not a
83 : // virtual function of the FunctionOfTime base class, we need to down cast
84 : // the original function to a QuaternionFunctionOfTime.
85 : function_at_current_time = quat_func_of_time->angle_func_and_2_derivs(time);
86 : }
87 :
88 : // Each control system is its own group under the overarching `ControlSystems`
89 : // group. There is a different subfile for each component, so loop over them.
90 : // Eg. translation files will look like
91 : //
92 : // ControlSystems/Translation/X.dat
93 : // ControlSystems/Translation/Y.dat
94 : // ControlSystems/Translation/Z.dat
95 : const size_t num_components = function_at_current_time[0].size();
96 : for (size_t i = 0; i < num_components; ++i) {
97 : const std::optional<std::string> component_name_opt =
98 : ControlSystem::component_name(i, num_components);
99 : if (not component_name_opt) {
100 : continue;
101 : }
102 : // Currently all reduction data is written to the reduction file so preface
103 : // everything with ControlSystems/
104 : const std::string subfile_name{"/ControlSystems/" + ControlSystem::name() +
105 : "/" + *component_name_opt};
106 : std::vector<std::string> legend{"Time",
107 : "FunctionOfTime",
108 : "dtFunctionOfTime",
109 : "d2tFunctionOfTime",
110 : "ControlError",
111 : "dtControlError",
112 : "DampingTimescale"};
113 :
114 : Parallel::threaded_action<
115 : observers::ThreadedActions::WriteReductionDataRow>(
116 : // Node 0 is always the writer
117 : observer_writer_proxy[0], subfile_name, std::move(legend),
118 : std::make_tuple(
119 : // clang-format off
120 : time,
121 : function_at_current_time[0][i],
122 : function_at_current_time[1][i],
123 : function_at_current_time[2][i],
124 : q_and_dtq[0][i],
125 : q_and_dtq[1][i],
126 : timescales[i])
127 : // clang-format on
128 : );
129 : }
130 : }
131 : } // namespace control_system
|