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 <limits> 8 : #include <memory> 9 : 10 : #include "ControlSystem/Tags/IsActiveMap.hpp" 11 : #include "ControlSystem/Tags/OptionTags.hpp" 12 : #include "DataStructures/DataBox/DataBox.hpp" 13 : #include "DataStructures/DataBox/Tag.hpp" 14 : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp" 15 : #include "Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp" 16 : #include "Domain/FunctionsOfTime/Tags.hpp" 17 : #include "Domain/Structure/ElementId.hpp" 18 : #include "Domain/Tags.hpp" 19 : #include "Parallel/AlgorithmExecution.hpp" 20 : #include "Parallel/GlobalCache.hpp" 21 : #include "ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp" 22 : #include "ParallelAlgorithms/EventsAndTriggers/Tags.hpp" 23 : #include "ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp" 24 : #include "Time/Tags/Time.hpp" 25 : #include "Time/Tags/TimeStepId.hpp" 26 : #include "Utilities/ConstantExpressions.hpp" 27 : #include "Utilities/ErrorHandling/Error.hpp" 28 : 29 : /// \cond 30 : namespace Parallel { 31 : template <typename Metavariables> 32 : class GlobalCache; 33 : } // namespace Parallel 34 : namespace Tags { 35 : struct TimeStep; 36 : struct TimeStepId; 37 : template <typename StepperInterface> 38 : struct TimeStepper; 39 : } // namespace Tags 40 : namespace tuples { 41 : template <class... Tags> 42 : class TaggedTuple; 43 : } // namespace tuples 44 : /// \endcond 45 : 46 1 : namespace control_system { 47 : /*! 48 : * \brief Holds options used to control when to start disabling the rotation 49 : * control system in a BNS simulation. 50 : */ 51 1 : struct DisableRotationWhen { 52 : /// The separation at which we start turning off rotation. 53 1 : struct DisableAtSeparation { 54 0 : using type = double; 55 0 : static type lower_bound() { return 2.0; } 56 0 : static constexpr Options::String help{ 57 : "The separation at which we start turning off rotation."}; 58 : }; 59 : 60 : /// The timescale in code units over which grid rotation is disabled. A 61 : /// reasonable value is 30M-60M. 62 1 : struct RotationDecayTimescale { 63 0 : using type = double; 64 0 : static type lower_bound() { return 30.0; } 65 0 : static type upper_bound() { return 200.0; } 66 0 : static constexpr Options::String help{ 67 : "The timescale in code units over which grid rotation is disabled. A " 68 : "reasonable value is 40M-60M."}; 69 : }; 70 : 71 0 : using options = tmpl::list<DisableAtSeparation, RotationDecayTimescale>; 72 : 73 0 : static constexpr Options::String help{ 74 : "Constrols the separation at which the rotation control system is " 75 : "disabled and the rotation function of time starts settling to a " 76 : "constant."}; 77 : 78 0 : void pup(PUP::er& p); 79 : 80 0 : double disable_at_separation{std::numeric_limits<double>::signaling_NaN()}; 81 0 : double rotation_decay_timescale{std::numeric_limits<double>::signaling_NaN()}; 82 : }; 83 : 84 1 : namespace OptionTags { 85 : /// Option tag for controlling when and how the rotation map is disabled. 86 1 : struct DisableRotationWhen { 87 0 : static constexpr Options::String help = 88 : "Options for controlling how the rotation control system stops as the " 89 : "two neutron stars merge."; 90 0 : using type = control_system::DisableRotationWhen; 91 0 : using group = control_system::OptionTags::ControlSystemGroup; 92 : }; 93 : } // namespace OptionTags 94 : 95 1 : namespace Tags { 96 : /// Tag for controlling when and how the rotation map is disabled. 97 1 : struct DisableRotationWhen : db::SimpleTag { 98 0 : using type = control_system::DisableRotationWhen; 99 0 : using option_tags = tmpl::list<OptionTags::DisableRotationWhen>; 100 : 101 0 : static constexpr bool pass_metavariables = false; 102 0 : static control_system::DisableRotationWhen create_from_options( 103 : const control_system::DisableRotationWhen& disable_rotation_when); 104 : }; 105 : } // namespace Tags 106 : 107 1 : namespace Actions { 108 : /*! 109 : * \brief Checks if the Rotation function of time has been updated because the 110 : * separation between the neutron star grid centers is small enough. 111 : * 112 : * \note This is an iterable action that is to be run on the elements. It is 113 : * only run on the element that satisfies `is_zeroth_element()`. 114 : * 115 : * \warning This action should only ever be run in the 116 : * `Parallel::Phase::DisableRotationControl` phase. 117 : * 118 : * The desired separation is controlled via the 119 : * control_systems::Tags::DisableRotationWhen tag. The main use for this 120 : * functionality is to disable the rotation control system in binary neutron 121 : * star mergers when the stars are sufficiently close because as the stars 122 : * merge, the dual control system starts to lose anything to lock on to and can 123 : * even start counter rotating. 124 : * 125 : * Tags used and modified: 126 : * - DataBox: 127 : * - `domain::Tags::Element<3>` 128 : * - `::Tags::TimeStepId` 129 : * - `::Tags::Time` 130 : * - MutableGlobalCache: 131 : * - Uses: 132 : * - `domain::Tags::FunctionsOfTime` ("GridCenters" and "Rotation") 133 : * - Modifies: 134 : * - `domain::Tags::FunctionsOfTime` ("Rotation") 135 : * - `control_system::Tags::IsActiveMap` ("Rotation") 136 : */ 137 1 : struct SwitchGridRotationToSettle { 138 : /// Invokable that changes the rotation function of time to a 139 : /// QuaternionSettleToConstant matching the current function of time values 140 : /// and derivatives for the rotation and decaying over a specified timescale. 141 : /// 142 : /// Note that the final orientation of the domain is arbitrary. If we want 143 : /// to rotate to a specific angle (e.g. aligning logical and inertial 144 : /// coordinate axes), we need a (new) QuaternionSettleToSpecifiedValue 145 : /// function of time. 146 1 : struct UpdateRotationToSettle { 147 0 : static void apply( 148 : gsl::not_null<std::unordered_map< 149 : std::string, 150 : std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*> 151 : f_of_t_list, 152 : const std::string& function_of_time_name, 153 : const std::array<DataVector, 3>& initial_func_and_derivs, 154 : double match_time, double decay_time); 155 : }; 156 : 157 : /// Invokable used to disable the rotation control system via a call to 158 : /// Parallel::mutate. 159 1 : struct DisableControlSystem { 160 0 : static void apply( 161 : gsl::not_null<std::unordered_map<std::string, bool>*> is_active_map, 162 : const std::string& control_system_name); 163 : }; 164 : 165 0 : using const_global_cache_tags = 166 : tmpl::list<control_system::Tags::DisableRotationWhen>; 167 : 168 : template <typename DbTags, typename... InboxTags, typename Metavariables, 169 : typename ActionList, typename ParallelComponent> 170 0 : static Parallel::iterable_action_return_t apply( 171 : db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/, 172 : Parallel::GlobalCache<Metavariables>& cache, 173 : const ElementId<3>& element_id, const ActionList /*meta*/, 174 : const ParallelComponent* const /*meta*/) { 175 : if (not is_zeroth_element(element_id)) { 176 : // Only one element needs to modify the control system and function of 177 : // time. 178 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 179 : } 180 : const auto& time_step_id = db::get<::Tags::TimeStepId>(box); 181 : if (not time_step_id.is_at_slab_boundary()) { 182 : ERROR( 183 : "Expected to be at a Slab boundary when changing the Rotation " 184 : "function of time to a SettleToConstant. Current TimeStepId is " 185 : << time_step_id); 186 : } 187 : 188 : const auto& bns_rotation_control = 189 : db::get<control_system::Tags::DisableRotationWhen>(box); 190 : 191 : const double time = db::get<::Tags::Time>(box); 192 : if (not get<domain::Tags::FunctionsOfTime>(cache).contains("GridCenters")) { 193 : ERROR( 194 : "There is no function of time named 'GridCenters', which is required " 195 : "when disabling the rotation control system since in a binary " 196 : "neutron star simulation we need to track the grid centers of the " 197 : "stars to decide when to disable rotation control."); 198 : } 199 : const domain::FunctionsOfTime::FunctionOfTime& grid_centers_fot = 200 : *get<domain::Tags::FunctionsOfTime>(cache).at("GridCenters"); 201 : const DataVector grid_centers = grid_centers_fot.func(time)[0]; 202 : const double separation = sqrt(square(grid_centers[0] - grid_centers[3]) + 203 : square(grid_centers[1] - grid_centers[4]) + 204 : square(grid_centers[2] - grid_centers[5])); 205 : if (separation > bns_rotation_control.disable_at_separation) { 206 : ERROR( 207 : "Disabling the rotation control system should happen when the " 208 : "separation is less than or equal to " 209 : << bns_rotation_control.disable_at_separation 210 : << " but the separation at time " << time << " is calculated to be " 211 : << separation); 212 : } 213 : 214 : if (not get<domain::Tags::FunctionsOfTime>(cache).contains("Rotation")) { 215 : ERROR( 216 : "There is no function of time named 'Rotation', which means that it " 217 : "cannot be disabled."); 218 : } 219 : const domain::FunctionsOfTime::FunctionOfTime& rotation_fot = 220 : *get<domain::Tags::FunctionsOfTime>(cache).at("Rotation"); 221 : const std::array<DataVector, 3> current_func_and_derivs = 222 : rotation_fot.func_and_2_derivs(time); 223 : Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateRotationToSettle>( 224 : cache, std::string{"Rotation"}, current_func_and_derivs, time, 225 : bns_rotation_control.rotation_decay_timescale); 226 : Parallel::mutate<control_system::Tags::IsActiveMap, DisableControlSystem>( 227 : cache, std::string{"Rotation"}); 228 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 229 : } 230 : }; 231 : } // namespace Actions 232 : } // namespace control_system