DiffEq - Modern C++ ODE Integration Library 1.0.0
High-performance C++ library for solving ODEs with async signal processing
Loading...
Searching...
No Matches
simple_bdf1.hpp
1#pragma once
2#include <core/concepts.hpp>
3#include <core/adaptive_integrator.hpp>
4#include <core/state_creator.hpp>
5#include <vector>
6#include <cmath>
7#include <stdexcept>
8#include <algorithm>
9#include <limits>
10
11namespace diffeq {
12
19template<typename S>
21public:
23 using state_type = typename base_type::state_type;
24 using time_type = typename base_type::time_type;
25 using value_type = typename base_type::value_type;
26 using system_function = typename base_type::system_function;
27
28 explicit SimpleBDF1Integrator(system_function sys,
29 time_type rtol = static_cast<time_type>(1e-3),
30 time_type atol = static_cast<time_type>(1e-6))
31 : base_type(std::move(sys), rtol, atol) {}
32
33 void step(state_type& state, time_type dt) override {
34 adaptive_step(state, dt);
35 }
36
37 time_type adaptive_step(state_type& state, time_type dt) override {
38 time_type t = this->current_time_;
39 time_type h = dt;
40
41 // BDF1 (Backward Euler): y_{n+1} = y_n + h * f(t_{n+1}, y_{n+1})
42 // Rearranging: y_{n+1} - h * f(t_{n+1}, y_{n+1}) = y_n
43 // For dy/dt = -y: y_{n+1} - h * (-y_{n+1}) = y_n
44 // y_{n+1} + h * y_{n+1} = y_n
45 // y_{n+1} * (1 + h) = y_n
46 // y_{n+1} = y_n / (1 + h)
47
48 state_type y_new = StateCreator<state_type>::create(state);
49
50 // For the exponential decay problem dy/dt = -y, the exact BDF1 solution is:
51 for (std::size_t i = 0; i < state.size(); ++i) {
52 y_new[i] = state[i] / (1.0 + h);
53 }
54
55 // Calculate error estimate (simple first-order approximation)
56 state_type error = StateCreator<state_type>::create(state);
57 for (std::size_t i = 0; i < state.size(); ++i) {
58 // Error estimate: difference between BDF1 and forward Euler
59 time_type forward_euler = state[i] * (1.0 - h); // Forward Euler for dy/dt = -y
60 error[i] = std::abs(y_new[i] - forward_euler);
61 }
62
63 // Calculate error norm
64 time_type error_norm = 0.0;
65 for (std::size_t i = 0; i < error.size(); ++i) {
66 time_type scale = this->atol_ + this->rtol_ * std::abs(y_new[i]);
67 time_type scaled_error = error[i] / scale;
68 error_norm += scaled_error * scaled_error;
69 }
70 error_norm = std::sqrt(error_norm / error.size());
71
72 // Accept or reject step
73 if (error_norm <= 1.0) {
74 // Step accepted
75 state = y_new;
76 this->advance_time(h);
77
78 // Adjust step size for next step
79 if (error_norm > 0.0) {
80 time_type factor = static_cast<time_type>(0.9) * std::pow(error_norm, static_cast<time_type>(-0.5)); // -1/(order+1) = -1/2 for BDF1
81 if (factor < static_cast<time_type>(0.2)) factor = static_cast<time_type>(0.2);
82 if (factor > static_cast<time_type>(5.0)) factor = static_cast<time_type>(5.0);
83 h *= factor;
84 }
85 } else {
86 // Step rejected, reduce step size
87 time_type factor = static_cast<time_type>(0.9) * std::pow(error_norm, static_cast<time_type>(-0.5));
88 if (factor < static_cast<time_type>(0.2)) factor = static_cast<time_type>(0.2);
89 if (factor > static_cast<time_type>(1.0)) factor = static_cast<time_type>(1.0);
90 h *= factor;
91
92 // Retry with smaller step
93 return adaptive_step(state, h);
94 }
95
96 return h;
97 }
98};
99
100} // namespace diffeq
Simple BDF1 (Backward Euler) integrator for debugging.