pathsim 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pathsim/__init__.py +3 -0
- pathsim/blocks/__init__.py +14 -0
- pathsim/blocks/_block.py +209 -0
- pathsim/blocks/adder.py +30 -0
- pathsim/blocks/amplifier.py +34 -0
- pathsim/blocks/delay.py +70 -0
- pathsim/blocks/differentiator.py +70 -0
- pathsim/blocks/function.py +82 -0
- pathsim/blocks/integrator.py +66 -0
- pathsim/blocks/lti.py +155 -0
- pathsim/blocks/multiplier.py +30 -0
- pathsim/blocks/ode.py +86 -0
- pathsim/blocks/rf/__init__.py +4 -0
- pathsim/blocks/rf/filters.py +169 -0
- pathsim/blocks/rf/noise.py +218 -0
- pathsim/blocks/rf/sources.py +163 -0
- pathsim/blocks/rf/wienerhammerstein.py +338 -0
- pathsim/blocks/rng.py +57 -0
- pathsim/blocks/scope.py +224 -0
- pathsim/blocks/sources.py +71 -0
- pathsim/blocks/spectrum.py +316 -0
- pathsim/connection.py +112 -0
- pathsim/simulation.py +652 -0
- pathsim/solvers/__init__.py +25 -0
- pathsim/solvers/_solver.py +403 -0
- pathsim/solvers/bdf.py +240 -0
- pathsim/solvers/dirk2.py +101 -0
- pathsim/solvers/dirk3.py +86 -0
- pathsim/solvers/esdirk32.py +131 -0
- pathsim/solvers/esdirk4.py +99 -0
- pathsim/solvers/esdirk43.py +139 -0
- pathsim/solvers/esdirk54.py +141 -0
- pathsim/solvers/esdirk85.py +200 -0
- pathsim/solvers/euler.py +81 -0
- pathsim/solvers/rk4.py +61 -0
- pathsim/solvers/rkbs32.py +101 -0
- pathsim/solvers/rkck54.py +108 -0
- pathsim/solvers/rkdp54.py +111 -0
- pathsim/solvers/rkdp87.py +116 -0
- pathsim/solvers/rkf45.py +102 -0
- pathsim/solvers/rkf78.py +111 -0
- pathsim/solvers/rkv65.py +103 -0
- pathsim/solvers/ssprk22.py +62 -0
- pathsim/solvers/ssprk33.py +65 -0
- pathsim/solvers/ssprk34.py +74 -0
- pathsim/subsystem.py +267 -0
- pathsim/utils/__init__.py +0 -0
- pathsim/utils/adaptivebuffer.py +87 -0
- pathsim/utils/anderson.py +180 -0
- pathsim/utils/funcs.py +205 -0
- pathsim/utils/gilbert.py +110 -0
- pathsim/utils/progresstracker.py +90 -0
- pathsim/utils/realtimeplotter.py +230 -0
- pathsim/utils/statespacerealizations.py +116 -0
- pathsim/utils/waveforms.py +36 -0
- pathsim-0.2.0.dist-info/LICENSE.txt +21 -0
- pathsim-0.2.0.dist-info/METADATA +149 -0
- pathsim-0.2.0.dist-info/RECORD +109 -0
- pathsim-0.2.0.dist-info/WHEEL +5 -0
- pathsim-0.2.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/blocks/__init__.py +0 -0
- tests/blocks/test_adder.py +85 -0
- tests/blocks/test_amplifier.py +66 -0
- tests/blocks/test_block.py +138 -0
- tests/blocks/test_delay.py +122 -0
- tests/blocks/test_differentiator.py +102 -0
- tests/blocks/test_function.py +165 -0
- tests/blocks/test_integrator.py +92 -0
- tests/blocks/test_lti.py +162 -0
- tests/blocks/test_multiplier.py +87 -0
- tests/blocks/test_ode.py +125 -0
- tests/blocks/test_rng.py +109 -0
- tests/blocks/test_scope.py +196 -0
- tests/blocks/test_sources.py +119 -0
- tests/blocks/test_spectrum.py +119 -0
- tests/solvers/__init__.py +0 -0
- tests/solvers/test_bdf.py +364 -0
- tests/solvers/test_dirk2.py +138 -0
- tests/solvers/test_dirk3.py +137 -0
- tests/solvers/test_esdirk32.py +158 -0
- tests/solvers/test_esdirk4.py +138 -0
- tests/solvers/test_esdirk43.py +158 -0
- tests/solvers/test_esdirk54.py +160 -0
- tests/solvers/test_esdirk85.py +157 -0
- tests/solvers/test_euler.py +223 -0
- tests/solvers/test_rk4.py +138 -0
- tests/solvers/test_rkbs32.py +159 -0
- tests/solvers/test_rkck54.py +157 -0
- tests/solvers/test_rkdp54.py +159 -0
- tests/solvers/test_rkdp87.py +157 -0
- tests/solvers/test_rkf45.py +159 -0
- tests/solvers/test_rkf78.py +160 -0
- tests/solvers/test_rkv65.py +160 -0
- tests/solvers/test_solver.py +119 -0
- tests/solvers/test_ssprk22.py +136 -0
- tests/solvers/test_ssprk33.py +136 -0
- tests/solvers/test_ssprk34.py +136 -0
- tests/test_connection.py +176 -0
- tests/test_simulation.py +271 -0
- tests/test_subsystem.py +182 -0
- tests/utils/__init__.py +0 -0
- tests/utils/test_adaptivebuffer.py +111 -0
- tests/utils/test_anderson.py +142 -0
- tests/utils/test_funcs.py +143 -0
- tests/utils/test_gilbert.py +108 -0
- tests/utils/test_progresstracker.py +144 -0
- tests/utils/test_realtimeplotter.py +122 -0
- tests/utils/test_statespacerealizations.py +107 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT ADAPTIVE TIMESTEPPING RUNGE-KUTTA INTEGRATORS
|
|
4
|
+
## (solvers/rkf78.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from ._solver import ExplicitSolver
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SOLVERS ==============================================================================
|
|
18
|
+
|
|
19
|
+
class RKDP87(ExplicitSolver):
|
|
20
|
+
"""
|
|
21
|
+
13-stage 8-th order embedded Runge-Kutta method from Dormand and Prince
|
|
22
|
+
with embedded 7-th order method for 8-th order truncation error estimate
|
|
23
|
+
that can be used to adaptively control the timestep.
|
|
24
|
+
|
|
25
|
+
This solver is a great choice if extremely high accuracy is required.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
29
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
30
|
+
|
|
31
|
+
#counter for runge kutta stages
|
|
32
|
+
self.stage = 0
|
|
33
|
+
|
|
34
|
+
#flag adaptive timestep solver
|
|
35
|
+
self.is_adaptive = True
|
|
36
|
+
|
|
37
|
+
#slope coefficients for stages
|
|
38
|
+
self.Ks = {}
|
|
39
|
+
|
|
40
|
+
#intermediate evaluation times
|
|
41
|
+
self.eval_stages = [0.0, 1/18, 1/12, 1/8, 5/16, 3/8, 59/400, 93/200, 5490023248/9719169821, 13/20, 1201146811/1299019798, 1.0, 1.0]
|
|
42
|
+
|
|
43
|
+
#extended butcher table
|
|
44
|
+
self.BT = {0:[1/18],
|
|
45
|
+
1:[1/48, 1/16],
|
|
46
|
+
2:[1/32, 0, 3/32],
|
|
47
|
+
3:[5/16, 0, -75/64, 75/64],
|
|
48
|
+
4:[3/80, 0, 0, 3/16, 3/20],
|
|
49
|
+
5:[29443841/614563906, 0, 0, 77736538/692538347, -28693883/1125000000, 23124283/1800000000],
|
|
50
|
+
6:[16016141/946692911, 0, 0, 61564180/158732637, 22789713/633445777, 545815736/2771057229, -180193667/1043307555],
|
|
51
|
+
7:[39632708/573591083, 0, 0, -433636366/683701615, -421739975/2616292301, 100302831/723423059, 790204164/839813087, 800635310/3783071287],
|
|
52
|
+
8:[246121993/1340847787, 0, 0, -37695042795/15268766246, -309121744/1061227803, -12992083/490766935, 6005943493/2108947869, 393006217/1396673457, 123872331/1001029789],
|
|
53
|
+
9:[-1028468189/846180014, 0, 0, 8478235783/508512852, 1311729495/1432422823, -10304129995/1701304382, -48777925059/3047939560, 15336726248/1032824649, -45442868181/3398467696, 3065993473/597172653],
|
|
54
|
+
10:[185892177/718116043, 0, 0, -3185094517/667107341, -477755414/1098053517, -703635378/230739211, 5731566787/1027545527, 5232866602/850066563, -4093664535/808688257, 3962137247/1805957418, 65686358/487910083],
|
|
55
|
+
11:[403863854/491063109, 0, 0, -5068492393/434740067, -411421997/543043805, 652783627/914296604, 11173962825/925320556, -13158990841/6184727034, 3936647629/1978049680, -160528059/685178525, 248638103/1413531060, 0],
|
|
56
|
+
12:[14005451/335480064, 0, 0, 0, 0, -59238493/1068277825, 181606767/758867731, 561292985/797845732, -1041891430/1371343529, 760417239/1151165299, 118820643/751138087, -528747749/2220607170, 1/4]}
|
|
57
|
+
|
|
58
|
+
#coefficients for lower order solution evaluation
|
|
59
|
+
self.bh = [13451932/455176623, 0, 0, 0, 0, -808719846/976000145, 1757004468/5645159321, 656045339/265891186, -3867574721/1518517206, 465885868/322736535, 53011238/667516719, 2/45, 0]
|
|
60
|
+
|
|
61
|
+
#lower order solution
|
|
62
|
+
self.xh = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def error_controller(self, dt):
|
|
66
|
+
"""
|
|
67
|
+
compute scaling factor for adaptive timestep
|
|
68
|
+
based on local truncation error estimate and returns both
|
|
69
|
+
"""
|
|
70
|
+
if self.xh is None:
|
|
71
|
+
return True, 0.0, 1.0
|
|
72
|
+
|
|
73
|
+
#compute and clip truncation error
|
|
74
|
+
truncation_error = np.max(np.clip(abs(self.x-self.xh), 1e-18, None))
|
|
75
|
+
|
|
76
|
+
#compute error ratio
|
|
77
|
+
error_ratio = self.tolerance_lte / truncation_error
|
|
78
|
+
success = error_ratio >= 1.0
|
|
79
|
+
|
|
80
|
+
#compute timestep scale
|
|
81
|
+
timestep_rescale = 0.9 * (error_ratio)**(1/8)
|
|
82
|
+
|
|
83
|
+
return success, truncation_error, timestep_rescale
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def step(self, u, t, dt):
|
|
87
|
+
"""
|
|
88
|
+
performs the (explicit) timestep for (t+dt)
|
|
89
|
+
based on the state and input at (t)
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
#buffer intermediate slope
|
|
93
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
94
|
+
|
|
95
|
+
#update state at stage
|
|
96
|
+
slope = 0.0
|
|
97
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
98
|
+
slope += self.Ks[i] * b
|
|
99
|
+
self.x = dt * slope + self.x_0
|
|
100
|
+
|
|
101
|
+
#error and step size control
|
|
102
|
+
if self.stage < 12:
|
|
103
|
+
self.stage += 1
|
|
104
|
+
return True, 0.0, 1.0
|
|
105
|
+
|
|
106
|
+
else:
|
|
107
|
+
|
|
108
|
+
#lower order solution
|
|
109
|
+
slope = 0.0
|
|
110
|
+
for i, bh in enumerate( self.bh):
|
|
111
|
+
slope += self.Ks[i] * bh
|
|
112
|
+
self.xh = dt * slope + self.x_0
|
|
113
|
+
|
|
114
|
+
#reset stage counter
|
|
115
|
+
self.stage = 0
|
|
116
|
+
return self.error_controller(dt)
|
pathsim/solvers/rkf45.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT ADAPTIVE TIMESTEPPING RUNGE-KUTTA INTEGRATORS
|
|
4
|
+
## (solvers/rkf45.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from ._solver import ExplicitSolver
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SOLVERS ==============================================================================
|
|
18
|
+
|
|
19
|
+
class RKF45(ExplicitSolver):
|
|
20
|
+
"""
|
|
21
|
+
6-stage 4-th order embedded Runge-Kutta-Fehlberg method
|
|
22
|
+
with 5-th order truncation error estimate that can be used to
|
|
23
|
+
adaptively control the timestep.
|
|
24
|
+
|
|
25
|
+
Absolute classic but relatively slow.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
29
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
30
|
+
|
|
31
|
+
#counter for runge kutta stages
|
|
32
|
+
self.stage = 0
|
|
33
|
+
|
|
34
|
+
#flag adaptive timestep solver
|
|
35
|
+
self.is_adaptive = True
|
|
36
|
+
|
|
37
|
+
#slope coefficients for stages
|
|
38
|
+
self.Ks = {}
|
|
39
|
+
|
|
40
|
+
#intermediate evaluation times
|
|
41
|
+
self.eval_stages = [0.0, 1/4, 3/8, 12/13, 1, 1/2]
|
|
42
|
+
|
|
43
|
+
#extended butcher table
|
|
44
|
+
self.BT = {0:[ 1/4],
|
|
45
|
+
1:[ 3/32, 9/32],
|
|
46
|
+
2:[1932/2197, -7200/2197, 7296/2197],
|
|
47
|
+
3:[ 439/216, -8, 3680/513, -845/4104],
|
|
48
|
+
4:[ -8/27, 2, -3554/2565, 1859/4104, -11/40],
|
|
49
|
+
5:[ 25/216, 0, 1408/2565, 2197/4104, -1/5, 0]}
|
|
50
|
+
|
|
51
|
+
#coefficients for local truncation error estimate
|
|
52
|
+
self.TR = [1/360, 0, -128/4275, -2197/75240, 1/50, 2/55]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def error_controller(self, dt):
|
|
56
|
+
"""
|
|
57
|
+
compute scaling factor for adaptive timestep
|
|
58
|
+
based on local truncation error estimate and returns both
|
|
59
|
+
"""
|
|
60
|
+
if len(self.Ks)<len(self.TR):
|
|
61
|
+
return True, 0.0, 1.0
|
|
62
|
+
|
|
63
|
+
#compute local truncation error slope
|
|
64
|
+
slope = 0.0
|
|
65
|
+
for i, b in enumerate(self.TR):
|
|
66
|
+
slope += self.Ks[i] * b
|
|
67
|
+
|
|
68
|
+
#compute and clip truncation error
|
|
69
|
+
truncation_error = np.max(np.clip(abs(dt*slope), 1e-18, None))
|
|
70
|
+
|
|
71
|
+
#compute error ratio
|
|
72
|
+
error_ratio = self.tolerance_lte / truncation_error
|
|
73
|
+
success = error_ratio >= 1.0
|
|
74
|
+
|
|
75
|
+
#compute timestep scale
|
|
76
|
+
timestep_rescale = 0.9 * (error_ratio)**(1/5)
|
|
77
|
+
|
|
78
|
+
return success, truncation_error, timestep_rescale
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def step(self, u, t, dt):
|
|
82
|
+
"""
|
|
83
|
+
performs the (explicit) timestep for (t+dt)
|
|
84
|
+
based on the state and input at (t)
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
#buffer intermediate slope
|
|
88
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
89
|
+
|
|
90
|
+
#update state at stage
|
|
91
|
+
slope = 0.0
|
|
92
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
93
|
+
slope += self.Ks[i] * b
|
|
94
|
+
self.x = dt * slope + self.x_0
|
|
95
|
+
|
|
96
|
+
#error and step size control
|
|
97
|
+
if self.stage < 5:
|
|
98
|
+
self.stage += 1
|
|
99
|
+
return True, 0.0, 1.0
|
|
100
|
+
else:
|
|
101
|
+
self.stage = 0
|
|
102
|
+
return self.error_controller(dt)
|
pathsim/solvers/rkf78.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT ADAPTIVE TIMESTEPPING RUNGE-KUTTA INTEGRATORS
|
|
4
|
+
## (solvers/rkf78.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from ._solver import ExplicitSolver
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SOLVERS ==============================================================================
|
|
18
|
+
|
|
19
|
+
class RKF78(ExplicitSolver):
|
|
20
|
+
"""
|
|
21
|
+
13-stage 7-th order embedded Runge-Kutta-Fehlberg method
|
|
22
|
+
with 8-th order truncation error estimate that can be used to
|
|
23
|
+
adaptively control the timestep.
|
|
24
|
+
|
|
25
|
+
This solver is a great choice if extremely high accuracy is required.
|
|
26
|
+
It is also almost symplectic and therefore quite suitable for
|
|
27
|
+
conservation systems such as celestial dynamics, etc.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
31
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
32
|
+
|
|
33
|
+
#counter for runge kutta stages
|
|
34
|
+
self.stage = 0
|
|
35
|
+
|
|
36
|
+
#flag adaptive timestep solver
|
|
37
|
+
self.is_adaptive = True
|
|
38
|
+
|
|
39
|
+
#slope coefficients for stages
|
|
40
|
+
self.Ks = {}
|
|
41
|
+
|
|
42
|
+
#intermediate evaluation times
|
|
43
|
+
self.eval_stages = [0, 2/27, 1/9, 1/6, 5/12, 1/2, 5/6, 1/6, 2/3, 1/3, 1, 0, 1]
|
|
44
|
+
|
|
45
|
+
#extended butcher table
|
|
46
|
+
self.BT = {0: [ 2/27],
|
|
47
|
+
1: [ 1/36, 1/12],
|
|
48
|
+
2: [ 1/24, 0, 1/8],
|
|
49
|
+
3: [ 5/12, 0, -25/16, 25/16],
|
|
50
|
+
4: [ 1/20, 0, 0, 1/4, 1/5],
|
|
51
|
+
5: [ -25/108, 0, 0, 125/108, -65/27, 125/54],
|
|
52
|
+
6: [ 31/300, 0, 0, 0, 61/225, -2/9, 13/900],
|
|
53
|
+
7: [ 2, 0, 0, -53/6, 704/45, -107/9, 67/90, 3],
|
|
54
|
+
8: [ -91/108, 0, 0, 23/108, -976/135, 311/54, -19/60, 17/6, -1/12],
|
|
55
|
+
9: [ 2383/4100, 0, 0, -341/164, 4496/1025, -301/82, 2133/4100, 45/82, 45/164, 18/41],
|
|
56
|
+
10:[ 3/205, 0, 0, 0, 0, -6/41, -3/205, -3/41, 3/41, 6/41],
|
|
57
|
+
11:[-1777/4100, 0, 0, -341/164, 4496/1025, -289/82, 2193/4100, 51/82, 33/164, 12/41, 0, 1],
|
|
58
|
+
12:[ 41/840, 0, 0, 0, 0, 34/105, 9/35, 9/35, 9/280, 9/280, 41/840]}
|
|
59
|
+
|
|
60
|
+
#coefficients for local truncation error estimate
|
|
61
|
+
self.TR = [41/840, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41/840, -41/840, -41/840]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def error_controller(self, dt):
|
|
65
|
+
"""
|
|
66
|
+
compute scaling factor for adaptive timestep
|
|
67
|
+
based on local truncation error estimate and returns both
|
|
68
|
+
"""
|
|
69
|
+
if len(self.Ks)<len(self.TR):
|
|
70
|
+
return True, 0.0, 1.0
|
|
71
|
+
|
|
72
|
+
#compute local truncation error slope
|
|
73
|
+
slope = 0.0
|
|
74
|
+
for i, b in enumerate(self.TR):
|
|
75
|
+
slope += self.Ks[i] * b
|
|
76
|
+
|
|
77
|
+
#compute and clip truncation error
|
|
78
|
+
truncation_error = np.max(np.clip(abs(dt*slope), 1e-18, None))
|
|
79
|
+
|
|
80
|
+
#compute error ratio
|
|
81
|
+
error_ratio = self.tolerance_lte / truncation_error
|
|
82
|
+
success = error_ratio >= 1.0
|
|
83
|
+
|
|
84
|
+
#compute timestep scale
|
|
85
|
+
timestep_rescale = 0.9 * (error_ratio)**(1/8)
|
|
86
|
+
|
|
87
|
+
return success, truncation_error, timestep_rescale
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def step(self, u, t, dt):
|
|
91
|
+
"""
|
|
92
|
+
performs the (explicit) timestep for (t+dt)
|
|
93
|
+
based on the state and input at (t)
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
#buffer intermediate slope
|
|
97
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
98
|
+
|
|
99
|
+
#update state at stage
|
|
100
|
+
slope = 0.0
|
|
101
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
102
|
+
slope += self.Ks[i] * b
|
|
103
|
+
self.x = dt * slope + self.x_0
|
|
104
|
+
|
|
105
|
+
#error and step size control
|
|
106
|
+
if self.stage < 12:
|
|
107
|
+
self.stage += 1
|
|
108
|
+
return True, 0.0, 1.0
|
|
109
|
+
else:
|
|
110
|
+
self.stage = 0
|
|
111
|
+
return self.error_controller(dt)
|
pathsim/solvers/rkv65.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT ADAPTIVE TIMESTEPPING RUNGE-KUTTA INTEGRATORS
|
|
4
|
+
## (solvers/rkv65.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from ._solver import ExplicitSolver
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SOLVERS ==============================================================================
|
|
18
|
+
|
|
19
|
+
class RKV65(ExplicitSolver):
|
|
20
|
+
"""
|
|
21
|
+
9-stage 6-th order with embedded 5-th order Runge-Kutta method from Verner
|
|
22
|
+
with 6-th order truncation error estimate.
|
|
23
|
+
|
|
24
|
+
This is the 'most robust' 9, 6(5) pair of Jim Verner's Refuge for Runge-Kutta Pairs
|
|
25
|
+
URL: https://www.sfu.ca/~jverner/
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
29
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
30
|
+
|
|
31
|
+
#counter for runge kutta stages
|
|
32
|
+
self.stage = 0
|
|
33
|
+
|
|
34
|
+
#flag adaptive timestep solver
|
|
35
|
+
self.is_adaptive = True
|
|
36
|
+
|
|
37
|
+
#slope coefficients for stages
|
|
38
|
+
self.Ks = {}
|
|
39
|
+
|
|
40
|
+
#intermediate evaluation times
|
|
41
|
+
self.eval_stages = [0.0, 9/50, 1/6, 1/4, 53/100, 3/5, 4/5, 1.0, 1.0]
|
|
42
|
+
|
|
43
|
+
#extended butcher table
|
|
44
|
+
self.BT = {0:[ 9/50],
|
|
45
|
+
1:[ 29/324, 25/324],
|
|
46
|
+
2:[ 1/16, 0, 3/16],
|
|
47
|
+
3:[ 79129/250000, 0, -261237/250000, 19663/15625],
|
|
48
|
+
4:[ 1336883/4909125, 0, -25476/30875, 194159/185250, 8225/78546],
|
|
49
|
+
5:[-2459386/14727375, 0, 19504/30875, 2377474/13615875, -6157250/5773131, 902/735],
|
|
50
|
+
6:[ 2699/7410, 0, -252/1235, -1393253/3993990, 236875/72618, -135/49, 15/22],
|
|
51
|
+
7:[ 11/144, 0, 0, 256/693, 0, 125/504, 125/528, 5/72],
|
|
52
|
+
8:[ 28/477, 0, 0, 212/441, -312500/366177, 2125/1764, 0, -2105/35532, 2995/17766]}
|
|
53
|
+
|
|
54
|
+
#5-th order solution at stage 9
|
|
55
|
+
self.xh = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def error_controller(self, dt):
|
|
59
|
+
"""
|
|
60
|
+
compute scaling factor for adaptive timestep
|
|
61
|
+
based on local truncation error estimate and returns both
|
|
62
|
+
"""
|
|
63
|
+
if self.xh is None:
|
|
64
|
+
return True, 0.0, 1.0
|
|
65
|
+
|
|
66
|
+
#compute and clip truncation error
|
|
67
|
+
truncation_error = np.max(np.clip(abs(self.x-self.xh), 1e-18, None))
|
|
68
|
+
|
|
69
|
+
#compute error ratio
|
|
70
|
+
error_ratio = self.tolerance_lte / truncation_error
|
|
71
|
+
success = error_ratio >= 1.0
|
|
72
|
+
|
|
73
|
+
#compute timestep scale
|
|
74
|
+
timestep_rescale = 0.9 * (error_ratio)**(1/6)
|
|
75
|
+
|
|
76
|
+
return success, truncation_error, timestep_rescale
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def step(self, u, t, dt):
|
|
80
|
+
"""
|
|
81
|
+
performs the (explicit) timestep for (t+dt)
|
|
82
|
+
based on the state and input at (t)
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
#buffer intermediate slope
|
|
86
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
87
|
+
|
|
88
|
+
#compute slope at stage
|
|
89
|
+
slope = 0.0
|
|
90
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
91
|
+
slope += self.Ks[i] * b
|
|
92
|
+
|
|
93
|
+
#error and step size control
|
|
94
|
+
if self.stage < 8:
|
|
95
|
+
#stepping with 6-th order solution
|
|
96
|
+
self.x = dt * slope + self.x_0
|
|
97
|
+
self.stage += 1
|
|
98
|
+
return True, 0.0, 1.0
|
|
99
|
+
else:
|
|
100
|
+
#save 5-th order solution for error control at last stage
|
|
101
|
+
self.xh = dt * slope + self.x_0
|
|
102
|
+
self.stage = 0
|
|
103
|
+
return self.error_controller(dt)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT STRONG STABILITY PRESERVING RUNGE-KUTTA INTEGRATOR
|
|
4
|
+
## (solvers/ssprk22.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
from ._solver import ExplicitSolver
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# SOLVERS ==============================================================================
|
|
16
|
+
|
|
17
|
+
class SSPRK22(ExplicitSolver):
|
|
18
|
+
"""
|
|
19
|
+
Strong Stability Preserving (SSP) 2-nd order two stage (2,2) Runge-Kutta method,
|
|
20
|
+
also known as the 'Heun-Method'.
|
|
21
|
+
|
|
22
|
+
This integrator has a good trade off between speed, accuracy and stability.
|
|
23
|
+
Especially for non-stiff linear systems, this is probably a great choice.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
27
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
28
|
+
|
|
29
|
+
#counter for runge kutta stages
|
|
30
|
+
self.stage = 0
|
|
31
|
+
|
|
32
|
+
#slope coefficients for stages
|
|
33
|
+
self.Ks = {}
|
|
34
|
+
|
|
35
|
+
#intermediate evaluation times
|
|
36
|
+
self.eval_stages = [0.0, 1.0]
|
|
37
|
+
|
|
38
|
+
#butcher table
|
|
39
|
+
self.BT = {0:[1.0],
|
|
40
|
+
1:[1/2, 1/2]}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def step(self, u, t, dt):
|
|
44
|
+
"""
|
|
45
|
+
performs the (explicit) timestep for (t+dt)
|
|
46
|
+
based on the state and input at (t)
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
#buffer intermediate slope
|
|
50
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
51
|
+
|
|
52
|
+
#update state at stage
|
|
53
|
+
slope = 0.0
|
|
54
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
55
|
+
slope += self.Ks[i] * b
|
|
56
|
+
self.x = dt * slope + self.x_0
|
|
57
|
+
|
|
58
|
+
#wrap around stage counter
|
|
59
|
+
self.stage = (self.stage + 1) % 2
|
|
60
|
+
|
|
61
|
+
#no error estimate available
|
|
62
|
+
return True, 0.0, 1.0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT STRONG STABILITY PRESERVING RUNGE-KUTTA INTEGRATOR
|
|
4
|
+
## (solvers/ssprk33.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
from ._solver import ExplicitSolver
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# SOLVERS ==============================================================================
|
|
16
|
+
|
|
17
|
+
class SSPRK33(ExplicitSolver):
|
|
18
|
+
"""
|
|
19
|
+
Strong Stability Preserving (SSP) 3-rd order
|
|
20
|
+
three stage (3,3) Runge-Kutta method
|
|
21
|
+
|
|
22
|
+
This integrator is more accurate and stable then SSPRK22 but
|
|
23
|
+
also 50% more expensive due to 3 instead of 2 stages.
|
|
24
|
+
Originally designed for hyperbolic PDEs, this is also a great
|
|
25
|
+
choice if accuracy and stability and still good speed are important.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
29
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
30
|
+
|
|
31
|
+
#counter for runge kutta stages
|
|
32
|
+
self.stage = 0
|
|
33
|
+
|
|
34
|
+
#slope coefficients for stages
|
|
35
|
+
self.Ks = {}
|
|
36
|
+
|
|
37
|
+
#intermediate evaluation times
|
|
38
|
+
self.eval_stages = [0.0, 1.0, 0.5]
|
|
39
|
+
|
|
40
|
+
#butcher table
|
|
41
|
+
self.BT = {0:[1.0],
|
|
42
|
+
1:[1/4, 1/4],
|
|
43
|
+
2:[1/6, 1/6, 2/3]}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def step(self, u, t, dt):
|
|
47
|
+
"""
|
|
48
|
+
performs the (explicit) timestep for (t+dt)
|
|
49
|
+
based on the state and input at (t)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
#buffer intermediate slope
|
|
53
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
54
|
+
|
|
55
|
+
#update state at stage
|
|
56
|
+
slope = 0.0
|
|
57
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
58
|
+
slope += self.Ks[i] * b
|
|
59
|
+
self.x = dt * slope + self.x_0
|
|
60
|
+
|
|
61
|
+
#wrap around stage counter
|
|
62
|
+
self.stage = (self.stage + 1) % 3
|
|
63
|
+
|
|
64
|
+
#no error estimate available
|
|
65
|
+
return True, 0.0, 1.0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EXPLICIT STRONG STABILITY PRESERVING RUNGE-KUTTA INTEGRATOR
|
|
4
|
+
## (solvers/ssprk34.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
# import numpy as np
|
|
13
|
+
|
|
14
|
+
from ._solver import ExplicitSolver
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SOLVERS ==============================================================================
|
|
18
|
+
|
|
19
|
+
class SSPRK34(ExplicitSolver):
|
|
20
|
+
"""
|
|
21
|
+
Strong Stability Preserving (SSP) 3-rd order 4 stage
|
|
22
|
+
(3,4) Runge-Kutta method
|
|
23
|
+
|
|
24
|
+
This integrator has one stage more then SSPRK33 but is also
|
|
25
|
+
3-rd order. So in terms or accuracy, they are the same but
|
|
26
|
+
the 4-th stage gives quite a lot more stability.
|
|
27
|
+
The stability region includes the point -4 on the real axis
|
|
28
|
+
and is even more stable then the classical 'RK4' method in
|
|
29
|
+
this aspect. But again it is 33% more expensive then SSPRK33
|
|
30
|
+
due to the additional stage.
|
|
31
|
+
|
|
32
|
+
If super high stability is required, this might be a good
|
|
33
|
+
choice.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, initial_value=0, func=lambda x, u, t: u, jac=None, tolerance_lte=1e-6):
|
|
37
|
+
super().__init__(initial_value, func, jac, tolerance_lte)
|
|
38
|
+
|
|
39
|
+
#counter for runge kutta stages
|
|
40
|
+
self.stage = 0
|
|
41
|
+
|
|
42
|
+
#slope coefficients for stages
|
|
43
|
+
self.Ks = {}
|
|
44
|
+
|
|
45
|
+
#intermediate evaluation times
|
|
46
|
+
self.eval_stages = [0.0, 1/2, 1, 1/2]
|
|
47
|
+
|
|
48
|
+
#butcher table
|
|
49
|
+
self.BT = {0:[1/2],
|
|
50
|
+
1:[1/2, 1/2],
|
|
51
|
+
2:[1/6, 1/6, 1/6],
|
|
52
|
+
3:[1/6, 1/6, 1/6, 1/2]}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def step(self, u, t, dt):
|
|
56
|
+
"""
|
|
57
|
+
performs the (explicit) timestep for (t+dt)
|
|
58
|
+
based on the state and input at (t)
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
#buffer intermediate slope
|
|
62
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
63
|
+
|
|
64
|
+
#update state at stage
|
|
65
|
+
slope = 0.0
|
|
66
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
67
|
+
slope += self.Ks[i] * b
|
|
68
|
+
self.x = dt * slope + self.x_0
|
|
69
|
+
|
|
70
|
+
#wrap around stage counter
|
|
71
|
+
self.stage = (self.stage + 1) % 4
|
|
72
|
+
|
|
73
|
+
#no error estimate available
|
|
74
|
+
return True, 0.0, 1.0
|