This commit is contained in:
2024-11-08 10:46:21 -06:00
parent 02b3a309e1
commit e628f1d67a

View File

@@ -5,6 +5,7 @@
#include <cassert>
#include <cmath>
#include <iterator>
#include <ranges>
#include <span>
namespace mini_odeint {
@@ -63,96 +64,47 @@ template <typename T> struct DormandPrince {
90730570.0 / 29380423.0, -8293050.0 / 29380423.0}}};
};
template <typename E> struct OdeVector {
template <typename E> struct Vec3 {
using value_type = E;
E value;
E x, y, z;
constexpr OdeVector() = default;
constexpr explicit OdeVector(E value) : value(std::move(value)) {}
constexpr Vec3() = default;
constexpr Vec3(E x, E y, E z) : x(x), y(y), z(z) {}
constexpr operator E() const { return value; }
constexpr explicit Vec3(std::array<E, 3> v) : x(v[0]), y(v[1]), z(v[2]) {}
friend constexpr OdeVector operator+(const OdeVector &lhs,
const OdeVector &rhs) {
return OdeVector{lhs.value + rhs.value};
friend constexpr Vec3 operator+(const Vec3 &lhs, const Vec3 &rhs) {
return Vec3{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z};
}
friend constexpr OdeVector operator*(const OdeVector &lhs,
const value_type &rhs) {
return OdeVector{lhs.value * rhs};
friend constexpr Vec3 operator*(const Vec3 &lhs, const value_type &rhs) {
return Vec3{lhs.x * rhs, lhs.y * rhs, lhs.z * rhs};
}
friend constexpr OdeVector operator*(const value_type &lhs,
const OdeVector &rhs) {
return OdeVector{lhs * rhs.value};
friend constexpr Vec3 operator*(const value_type &lhs, const Vec3 &rhs) {
return Vec3{lhs * rhs.x, lhs * rhs.y, lhs * rhs.z};
}
constexpr OdeVector &operator+=(const OdeVector &rhs) {
value += rhs.value;
constexpr Vec3 &operator+=(const Vec3 &rhs) {
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
constexpr OdeVector &operator+=(const value_type &rhs) {
value += rhs;
return *this;
}
value_type inf_norm() const { return std::abs(value); }
};
template <typename T, std::size_t N> struct OdeVector<std::array<T, N>> {
using array_type = std::array<T, N>;
using value_type = T;
std::array<T, N> value;
constexpr OdeVector() = default;
constexpr explicit OdeVector(std::array<T, N> value)
: value(std::move(value)) {}
operator std::array<T, N>() const { return value; }
friend constexpr OdeVector operator+(const OdeVector &lhs,
const OdeVector &rhs) {
OdeVector result{lhs};
for (std::size_t i = 0; i < N; ++i) {
result.value[i] += rhs.value[i];
}
return result;
}
friend constexpr OdeVector operator*(const OdeVector &lhs,
const value_type &rhs) {
OdeVector result{lhs};
for (std::size_t i = 0; i < N; ++i) {
result.value[i] *= rhs;
}
return result;
}
friend constexpr OdeVector operator*(const value_type &lhs,
const OdeVector &rhs) {
OdeVector result{rhs};
for (std::size_t i = 0; i < N; ++i) {
result.value[i] *= lhs;
}
return result;
}
constexpr OdeVector &operator+=(const OdeVector &rhs) {
for (std::size_t i = 0; i < N; ++i) {
value[i] += rhs.value[i];
}
return *this;
namespace alg {
template <typename T> inline auto inf_norm(const T &v) {
std::ranges::max_element(v, {}, [](auto n) { return std::abs(n); });
}
constexpr OdeVector &operator+=(const value_type &rhs) {
for (std::size_t i = 0; i < N; ++i) {
value[i] += rhs;
}
return *this;
inline float inf_norm(float v) { return std::abs(v); }
inline double inf_norm(double v) { return std::abs(v); }
template <typename E> inline E inf_norm(const Vec3<E> &v) {
return std::max({std::abs(v.x), std::abs(v.y), std::abs(v.z)});
}
value_type inf_norm() const {
auto max = std::abs(value[0]);
for (std::size_t i = 1; i < N; ++i) {
max = std::max(max, std::abs(value[i]));
}
return max;
}
};
} // namespace alg
template <typename Vector, typename Scalar = std::iter_value_t<Vector>,
typename Tableau = DormandPrince<Scalar>>
@@ -202,7 +154,7 @@ inline std::size_t explicitRungeKutta(std::span<Vector> ys,
const auto last_k_store = k[stages - 1];
k[0] = k[stages - 1];
for (std::size_t i = 1; i < stages; ++i) {
OdeVector<Vector> sum_ak{};
Vector sum_ak{};
for (std::size_t j = 0; j < i; ++j) {
sum_ak += a[i][j] * k[j];
}
@@ -210,8 +162,8 @@ inline std::size_t explicitRungeKutta(std::span<Vector> ys,
}
// calculate final value and error
OdeVector<Vector> error{};
OdeVector<Vector> sum_bk{};
Vector error{};
Vector sum_bk{};
for (std::size_t i = 0; i < stages; ++i) {
sum_bk += b_hat[i] * k[i];
error += (b_hat[i] - b[i]) * k[i];
@@ -219,13 +171,13 @@ inline std::size_t explicitRungeKutta(std::span<Vector> ys,
const auto y_hat_np1 = y_hat_n + h_n * sum_bk;
// check if step is successful, ie error is within tolerance
const auto E_hp1 = (h_n * error).inf_norm();
const auto E_hp1 = alg::inf_norm(h_n * error);
if (E_hp1 < tol) {
// if moved over any requested times then interpolate their values
const auto t_np1 = t_n + h_n;
while (it < N && t_np1 >= ts[it]) {
const auto sigma = (ts[it] - t_n) / h_n;
OdeVector<Vector> Phi{};
Vector Phi{};
for (std::size_t i = 0; i < stages; ++i) {
auto term = sigma;
auto b_i = term * p[i][0];
@@ -266,22 +218,21 @@ inline std::size_t explicitRungeKutta(std::span<Vector> ys,
int main() {
using namespace mini_odeint;
// make vector of doubles from 0.0 to 1.0 by 0.001
// make vector of floats from 0.0 to 1.0 by 0.001
std::vector<float> times;
times.reserve(1001);
for (int i = 0; i < 1000; ++i) {
times.push_back(i / 1000.0);
}
std::vector<std::array<float, 2>> ys(times.size());
std::vector<Vec3<float>> ys(times.size());
explicitRungeKutta(
std::span(ys), std::span<const float>(times),
std::array<float, 2>{1.0, 2.0}, float(1e-6),
[](auto y, auto t) { return std::array<float, 2>{-y[0], -y[1]}; });
std::span(ys), std::span<const float>(times), Vec3<float>{1.0, 1.0, 1.0},
float(1e-6), [](auto y, auto t) { return Vec3<float>{-y.x, 0.0, 0.0}; });
for (const auto &v : ys) {
std::cout << v[0] << v[1] << '\n';
std::cout << v.x << '\n';
}
}