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