Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <boost/math/quaternion.hpp> 7 : #include <cstddef> 8 : #include <pup.h> 9 : 10 : #include "ControlSystem/ControlErrors/Expansion.hpp" 11 : #include "ControlSystem/ControlErrors/Rotation.hpp" 12 : #include "ControlSystem/Protocols/ControlError.hpp" 13 : #include "ControlSystem/Tags/QueueTags.hpp" 14 : #include "ControlSystem/Tags/SystemTags.hpp" 15 : #include "DataStructures/DataVector.hpp" 16 : #include "DataStructures/Matrix.hpp" 17 : #include "Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp" 18 : #include "Domain/Creators/Tags/ObjectCenter.hpp" 19 : #include "Domain/FunctionsOfTime/QuaternionHelpers.hpp" 20 : #include "Domain/Structure/ObjectLabel.hpp" 21 : #include "Options/String.hpp" 22 : #include "Parallel/GlobalCache.hpp" 23 : #include "Utilities/EqualWithinRoundoff.hpp" 24 : #include "Utilities/ErrorHandling/Assert.hpp" 25 : #include "Utilities/GetOutput.hpp" 26 : #include "Utilities/ProtocolHelpers.hpp" 27 : #include "Utilities/TMPL.hpp" 28 : #include "Utilities/TaggedTuple.hpp" 29 : 30 : /// \cond 31 : namespace domain::Tags { 32 : template <size_t Dim> 33 : struct Domain; 34 : struct FunctionsOfTime; 35 : } // namespace domain::Tags 36 : /// \endcond 37 : 38 : namespace control_system { 39 : namespace ControlErrors { 40 : /*! 41 : * \brief Control error in the 3D \link 42 : * domain::CoordinateMaps::TimeDependent::Translation Translation \endlink 43 : * coordinate map 44 : * 45 : * \details Computes the error in how much the system has translated. When there 46 : * are two excisions, it does this by using a modified version of Eq. (42) from 47 : * \cite Ossokine2013zga. The equation is 48 : * 49 : * \begin{equation} 50 : * \left(0, \delta\vec{T}\right) = a\mathbf{q}\left(\frac{1}{2}(\mathbf{x}_A 51 : * + \mathbf{x}_B - \frac{1}{2}(\mathbf{c}_A + \mathbf{c}_B)) - \mathbf{\delta 52 : * q}\wedge\frac{1}{2}(\mathbf{c}_A + \mathbf{c}_B) - \frac{\delta 53 : * a}{a}\frac{1}{2}(\mathbf{c}_A + \mathbf{c}_B) \right)\mathbf{q}^* 54 : * \end{equation} 55 : * 56 : * where object A is located on the positive x-axis in the grid frame, bold face 57 : * letters are quaternions, vectors are promoted to quaternions as \f$ 58 : * \mathbf{v} = (0, \vec{v}) \f$, \f$ \mathbf{q} \f$ is the quaternion from the 59 : * \link domain::CoordinateMaps::TimeDependent::Rotation Rotation \endlink map, 60 : * \f$ a \f$ is the function \f$ a(t) \f$ from the \link 61 : * domain::CoordinateMaps::TimeDependent::CubicScale CubicScale \endlink map, 62 : * \f$ \mathbf{\delta q}\wedge\mathbf{c}_A \equiv (0, \delta\vec{q} \times 63 : * \vec{c}_A) \f$, \f$ \delta\vec{q} \f$ is the \link 64 : * control_system::ControlErrors::Rotation Rotation \endlink control error, and 65 : * \f$ \delta a\f$ is the \link control_system::ControlErrors::Expansion 66 : * Expansion \endlink control error. 67 : * 68 : * When there is only a single excision, the control error assumes that the 69 : * center of the excision is at the origin. Thus, the control error is just 70 : * taken to be the current center of the horizon mapped through the expansion 71 : * and rotation maps if there are any. 72 : * 73 : * Requirements: 74 : * - This control error requires that there be either one or two objects in the 75 : * simulation 76 : * - Currently this control error can only be used with the \link 77 : * control_system::Systems::Translation Translation \endlink control system 78 : * - There must exist an expansion map and a quaternion rotation map in the 79 : * coordinate map with names "Expansion" and "Rotation", respectively if there 80 : * are two object. If there is a single object, then the "Expansion" and 81 : * "Rotation" maps may exist, but don't need to. 82 : */ 83 : template <size_t NumberOfObjects> 84 1 : struct Translation : tt::ConformsTo<protocols::ControlError> { 85 : static_assert(NumberOfObjects == 1 or NumberOfObjects == 2, 86 : "Translation control can only work with 1 or 2 objects."); 87 : 88 0 : using object_centers = tmpl::conditional_t< 89 : NumberOfObjects == 1, domain::object_list<domain::ObjectLabel::None>, 90 : domain::object_list<domain::ObjectLabel::A, domain::ObjectLabel::B>>; 91 : 92 0 : using options = tmpl::list<>; 93 0 : static constexpr Options::String help{ 94 : "Computes the control error for translation control. This should not " 95 : "take any options."}; 96 : 97 0 : void pup(PUP::er& /*p*/) {} 98 : 99 : template <typename Metavariables, typename... TupleTags> 100 0 : DataVector operator()(const ::TimescaleTuner<true>& /*unused*/, 101 : const Parallel::GlobalCache<Metavariables>& cache, 102 : const double time, 103 : const std::string& /*function_of_time_name*/, 104 : const tuples::TaggedTuple<TupleTags...>& measurements) { 105 : const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache); 106 : 107 : if constexpr (NumberOfObjects == 2) { 108 : using quat = boost::math::quaternion<double>; 109 : 110 : const quat quaternion = datavector_to_quaternion( 111 : functions_of_time.at("Rotation")->func(time)[0]); 112 : const double expansion_factor = 113 : functions_of_time.at("Expansion")->func(time)[0][0]; 114 : 115 : using center_A = 116 : control_system::QueueTags::Center<::domain::ObjectLabel::A>; 117 : using center_B = 118 : control_system::QueueTags::Center<::domain::ObjectLabel::B>; 119 : 120 : const tnsr::I<double, 3, Frame::Grid>& grid_position_of_A_tnsr = 121 : Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::A>>( 122 : cache); 123 : const DataVector grid_position_of_A{{grid_position_of_A_tnsr[0], 124 : grid_position_of_A_tnsr[1], 125 : grid_position_of_A_tnsr[2]}}; 126 : const DataVector& current_position_of_A = get<center_A>(measurements); 127 : 128 : const tnsr::I<double, 3, Frame::Grid>& grid_position_of_B_tnsr = 129 : Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::B>>( 130 : cache); 131 : const DataVector grid_position_of_B{{grid_position_of_B_tnsr[0], 132 : grid_position_of_B_tnsr[1], 133 : grid_position_of_B_tnsr[2]}}; 134 : const DataVector& current_position_of_B = get<center_B>(measurements); 135 : 136 : const DataVector grid_position_average = 137 : 0.5 * (grid_position_of_A + grid_position_of_B); 138 : const DataVector current_position_average = 139 : 0.5 * (current_position_of_A + current_position_of_B); 140 : 141 : const DataVector grid_separation = 142 : grid_position_of_A - grid_position_of_B; 143 : const DataVector current_separation = 144 : current_position_of_A - current_position_of_B; 145 : 146 : // These quantities come from the translation control implementation in 147 : // SpEC 148 : const double current_separation_dot_grid_separation = 149 : dot(current_separation, grid_separation); 150 : const double current_separation_dot_grid_average = 151 : dot(current_separation, grid_position_average); 152 : const double grid_separation_dot_grid_average = 153 : dot(grid_separation, grid_position_average); 154 : const double grid_separation_dot_grid_separation = 155 : dot(grid_separation, grid_separation); 156 : 157 : // From eq. 42 in 1304.3067 where the grid and current position are 158 : // swapped from only object A to the average grid and current positions of 159 : // both objects. 160 : const DataVector translation_control = 161 : expansion_factor * 162 : (grid_separation_dot_grid_separation * current_position_average - 163 : current_separation_dot_grid_separation * grid_position_average - 164 : grid_separation_dot_grid_average * current_separation + 165 : current_separation_dot_grid_average * grid_separation) / 166 : grid_separation_dot_grid_separation; 167 : const quat middle_expression = 168 : datavector_to_quaternion(translation_control); 169 : 170 : // Because we are converting from a quaternion to a DataVector, there will 171 : // be four components in the DataVector. However, translation control only 172 : // requires three (the latter three to be exact, because the first 173 : // component should be 0. We ASSERT this also.) 174 : const DataVector result_with_four_components = quaternion_to_datavector( 175 : quaternion * middle_expression * conj(quaternion)); 176 : ASSERT(equal_within_roundoff(result_with_four_components[0], 0.0), 177 : "Error in computing translation control error. First component of " 178 : "resulting quaternion should be 0.0, but is " + 179 : get_output(result_with_four_components[0]) + " instead."); 180 : 181 : return {result_with_four_components[1], result_with_four_components[2], 182 : result_with_four_components[3]}; 183 : } else { 184 : const DataVector& current_position = 185 : get<control_system::QueueTags::Center<::domain::ObjectLabel::None>>( 186 : measurements); 187 : DataVector result{3, 0.0}; 188 : 189 : double expansion_factor = 1.0; 190 : if (functions_of_time.count("Expansion") == 1) { 191 : expansion_factor = functions_of_time.at("Expansion")->func(time)[0][0]; 192 : } 193 : 194 : if (functions_of_time.count("Rotation") == 1) { 195 : const Matrix rot_matrix = 196 : rotation_matrix<3>(time, *functions_of_time.at("Rotation").get()); 197 : 198 : for (size_t i = 0; i < 3; i++) { 199 : for (size_t j = 0; j < 3; j++) { 200 : result[i] += rot_matrix(i, j) * current_position[j]; 201 : } 202 : } 203 : } else { 204 : result = current_position; 205 : } 206 : 207 : result *= expansion_factor; 208 : 209 : return result; 210 : } 211 : } 212 : }; 213 : } // namespace ControlErrors 214 : } // namespace control_system