Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <algorithm>
7 : #include <array>
8 : #include <complex>
9 : #include <cstddef>
10 :
11 : #include "DataStructures/BoostMultiArray.hpp"
12 : #include "DataStructures/ComplexDataVector.hpp"
13 : #include "DataStructures/ComplexModalVector.hpp"
14 : #include "DataStructures/DataVector.hpp"
15 : #include "DataStructures/ModalVector.hpp"
16 : #include "DataStructures/VectorImpl.hpp"
17 :
18 : // Boost MultiArray is used internally in odeint, so odeint must be included
19 : // later
20 : #include <boost/numeric/odeint.hpp>
21 :
22 : namespace detail {
23 : struct vector_impl_algebra : boost::numeric::odeint::vector_space_algebra {
24 : template <typename VectorType>
25 : static double norm_inf(const VectorType& vector) {
26 : return max(abs(vector));
27 : }
28 : };
29 :
30 : struct vector_impl_array_algebra : boost::numeric::odeint::array_algebra {
31 : template <typename VectorType, size_t Size>
32 : static double norm_inf(const std::array<VectorType, Size>& vector_array) {
33 : return std::accumulate(
34 : vector_array.begin(), vector_array.end(), 0.0,
35 : [](const double maximum_so_far, const VectorType& element) {
36 : return std::max(maximum_so_far, max(abs(element)));
37 : });
38 : }
39 : };
40 : } // namespace detail
41 :
42 : namespace boost {
43 : namespace numeric {
44 : namespace odeint {
45 :
46 : namespace detail {
47 : template <typename V>
48 : struct set_unit_value_impl<ComplexDataVector, V, void> {
49 : static void set_value(ComplexDataVector& t, const V& v) { // NOLINT
50 : // this ensures that the blaze expression template resolve the result to a
51 : // complex vector
52 : t = std::complex<double>(1.0, 0.0) * v;
53 : }
54 : };
55 : template <typename V>
56 : struct set_unit_value_impl<ComplexModalVector, V, void> {
57 : static void set_value(ComplexModalVector& t, const V& v) { // NOLINT
58 : // this ensures that the blaze expression template resolve the result to a
59 : // complex vector
60 : t = std::complex<double>(1.0, 0.0) * v;
61 : }
62 : };
63 : } // namespace detail
64 :
65 : // In some integration contexts, boost requires the ability to resize the
66 : // integration arguments in preparation for writing to output buffers passed by
67 : // reference. These specializations make the resize work correctly with spectre
68 : // vector types.
69 : template <class VectorType1, class VectorType2>
70 0 : struct resize_impl_sfinae<VectorType1, VectorType2,
71 : typename boost::enable_if_c<
72 : is_derived_of_vector_impl_v<VectorType1> and
73 : is_derived_of_vector_impl_v<VectorType2>>::type> {
74 0 : static void resize(VectorType1& x1, const VectorType2& x2) {
75 : x1.destructive_resize(x2.size());
76 : }
77 : };
78 :
79 : template <class VectorType>
80 0 : struct is_resizeable_sfinae<
81 : VectorType,
82 : typename boost::enable_if_c<is_derived_of_vector_impl_v<VectorType>>::type>
83 : :
84 : #if BOOST_VERSION >= 108600
85 : std::true_type
86 : #else
87 : boost::true_type
88 : #endif
89 : {
90 : };
91 :
92 : template <>
93 0 : struct algebra_dispatcher<DataVector> {
94 0 : using algebra_type = ::detail::vector_impl_algebra;
95 : };
96 :
97 : template <>
98 0 : struct algebra_dispatcher<ComplexDataVector> {
99 0 : using algebra_type = ::detail::vector_impl_algebra;
100 : };
101 :
102 : template <>
103 0 : struct algebra_dispatcher<ModalVector> {
104 0 : using algebra_type = ::detail::vector_impl_algebra;
105 : };
106 :
107 : template <>
108 0 : struct algebra_dispatcher<ComplexModalVector> {
109 0 : using algebra_type = ::detail::vector_impl_algebra;
110 : };
111 :
112 : template <size_t Size>
113 : struct algebra_dispatcher<std::array<DataVector, Size>> {
114 : using algebra_type = ::detail::vector_impl_array_algebra;
115 : };
116 :
117 : template <size_t Size>
118 : struct algebra_dispatcher<std::array<ComplexDataVector, Size>> {
119 : using algebra_type = ::detail::vector_impl_array_algebra;
120 : };
121 :
122 : template <size_t Size>
123 : struct algebra_dispatcher<std::array<ModalVector, Size>> {
124 : using algebra_type = ::detail::vector_impl_array_algebra;
125 : };
126 :
127 : template <size_t Size>
128 : struct algebra_dispatcher<std::array<ComplexModalVector, Size>> {
129 : using algebra_type = ::detail::vector_impl_array_algebra;
130 : };
131 : } // namespace odeint
132 : } // namespace numeric
133 : } // namespace boost
134 :
135 : /*!
136 : * \ingroup NumericalAlgorithmsGroup
137 : * \brief For ODE integration, we suggest using the boost libraries whenever
138 : * possible.
139 : *
140 : * \details Here we describe briefly the suggested setup to use a boost ODE
141 : * integrator in SpECTRE. The boost utilities have a number of more elaborate
142 : * features, including features that may result in simpler function calls than
143 : * the specific recipes below. For full documentation, see the Boost odeint
144 : * documentation:
145 : * https://www.boost.org/doc/libs/1_72_0/libs/numeric/odeint/doc/html/boost_numeric_odeint/
146 : *
147 : * The integration methods can be used largely as in the boost documentation. We
148 : * summarize the salient points necessary to get an example running. The main
149 : * steps in using a boost integrator are:
150 : * - define the system
151 : * - construct the stepper
152 : * - initialize (might be performed implicitly, depending on the stepper)
153 : * - perform steps
154 : * - (compute dense output, for dense-output steppers)
155 : *
156 : * For most cases the Dormand-Prince fifth order controlled, dense-output
157 : * stepper is recommended. That stepper is used in the section "SpECTRE vectors
158 : * or `std::array` thereof in boost integrators" below.
159 : *
160 : * #### Fundamental types or `std::array` thereof in fixed-step integrators
161 : *
162 : * Let us consider a simple oscillator system, which we'll declare as a lambda:
163 : *
164 : * \snippet Test_OdeIntegration.cpp explicit_fundamental_array_system
165 : *
166 : * Note that the first argument is a `const` lvalue reference to the state type,
167 : * `std::array<double, 2>`, the second argument is an lvalue reference for the
168 : * time derivatives that is written to by the system function, and the final
169 : * argument is the current time.
170 : *
171 : * The construction and initialization of the stepper is simple:
172 : *
173 : * \snippet Test_OdeIntegration.cpp explicit_fundamental_stepper_construction
174 : *
175 : * Finally, we can perform the steps and examine the output,
176 : *
177 : * \snippet Test_OdeIntegration.cpp explicit_fundamental_stepper_use
178 : *
179 : * #### Fundamental types or `std::array` thereof in Dense-output integrators
180 : *
181 : * The dense-output and controlled-step-size ODE integrators in boost comply
182 : * with a somewhat different interface, as significantly more state information
183 : * must be managed. The result is a somewhat simpler, but more opaque, interface
184 : * to the user code.
185 : *
186 : * Once again, we start by constructing the system we'd like to integrate,
187 : *
188 : * \snippet Test_OdeIntegration.cpp dense_output_fundamental_system
189 : *
190 : * The constructor for dense steppers takes optional arguments for tolerance,
191 : * and dense steppers need to be initialized.
192 : *
193 : * \snippet Test_OdeIntegration.cpp dense_output_fundamental_construction
194 : *
195 : * It is important to take note of the tolerance arguments, as the defaults are
196 : * `1.0e-6`, which is often looser than we want for calculations in SpECTRE.
197 : *
198 : * We then perform the step supplying the system function, and can retrieve the
199 : * dense output state with `calc_state`, which returns by reference by the
200 : * second argument.
201 : *
202 : * \snippet Test_OdeIntegration.cpp dense_output_fundamental_stepper_use
203 : *
204 : * #### SpECTRE vectors or `std::array` thereof in boost integrators
205 : *
206 : * The additional template specializations present in the `OdeIntegration.hpp`
207 : * file ensure that the boost ODE methods work transparently for SpECTRE vectors
208 : * as well. We'll run through a brief example to emphasize the functionality.
209 : *
210 : * \snippet Test_OdeIntegration.cpp dense_output_vector_stepper
211 : */
212 1 : namespace OdeIntegration {}
|