pathsim 0.4.2__tar.gz → 0.4.4__tar.gz
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-0.4.2 → pathsim-0.4.4}/PKG-INFO +1 -1
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/diff/value.py +21 -4
- pathsim-0.4.4/pathsim/solvers/_rungekutta.py +302 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/_solver.py +9 -24
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/bdf.py +18 -37
- pathsim-0.4.4/pathsim/solvers/dirk2.py +47 -0
- pathsim-0.4.4/pathsim/solvers/dirk3.py +40 -0
- pathsim-0.4.4/pathsim/solvers/esdirk32.py +47 -0
- pathsim-0.4.4/pathsim/solvers/esdirk4.py +42 -0
- pathsim-0.4.4/pathsim/solvers/esdirk43.py +58 -0
- pathsim-0.4.4/pathsim/solvers/esdirk54.py +59 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/esdirk85.py +12 -114
- pathsim-0.4.4/pathsim/solvers/rk4.py +38 -0
- pathsim-0.4.4/pathsim/solvers/rkbs32.py +50 -0
- pathsim-0.4.4/pathsim/solvers/rkck54.py +57 -0
- pathsim-0.4.4/pathsim/solvers/rkdp54.py +59 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/rkdp87.py +11 -76
- pathsim-0.4.4/pathsim/solvers/rkf45.py +51 -0
- pathsim-0.4.4/pathsim/solvers/rkf78.py +60 -0
- pathsim-0.4.4/pathsim/solvers/rkv65.py +56 -0
- pathsim-0.4.4/pathsim/solvers/ssprk22.py +40 -0
- pathsim-0.4.4/pathsim/solvers/ssprk33.py +43 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/ssprk34.py +9 -39
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/gilbert.py +1 -1
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/PKG-INFO +1 -1
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/SOURCES.txt +3 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/setup.py +1 -1
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_differentiator.py +6 -4
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_lti.py +7 -5
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_ode.py +6 -4
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_spectrum.py +6 -4
- pathsim-0.4.4/tests/diff/test_value.py +342 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/test_simulation.py +9 -5
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/test_subsystem.py +4 -4
- pathsim-0.4.4/tests/utils/__init__.py +0 -0
- pathsim-0.4.2/pathsim/solvers/dirk2.py +0 -111
- pathsim-0.4.2/pathsim/solvers/dirk3.py +0 -96
- pathsim-0.4.2/pathsim/solvers/esdirk32.py +0 -148
- pathsim-0.4.2/pathsim/solvers/esdirk4.py +0 -107
- pathsim-0.4.2/pathsim/solvers/esdirk43.py +0 -156
- pathsim-0.4.2/pathsim/solvers/esdirk54.py +0 -158
- pathsim-0.4.2/pathsim/solvers/rk4.py +0 -67
- pathsim-0.4.2/pathsim/solvers/rkbs32.py +0 -114
- pathsim-0.4.2/pathsim/solvers/rkck54.py +0 -121
- pathsim-0.4.2/pathsim/solvers/rkdp54.py +0 -124
- pathsim-0.4.2/pathsim/solvers/rkf45.py +0 -115
- pathsim-0.4.2/pathsim/solvers/rkf78.py +0 -124
- pathsim-0.4.2/pathsim/solvers/rkv65.py +0 -121
- pathsim-0.4.2/pathsim/solvers/ssprk22.py +0 -68
- pathsim-0.4.2/pathsim/solvers/ssprk33.py +0 -71
- {pathsim-0.4.2 → pathsim-0.4.4}/LICENSE.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/README.md +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/_block.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/adder.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/amplifier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/delay.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/differentiator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/function.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/integrator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/lti.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/multiplier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/ode.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/filters.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/noise.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/sources.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/wienerhammerstein.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rng.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/scope.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/sources.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/spectrum.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/connection.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/diff/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/simulation.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/euler.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/subsystem.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/adaptivebuffer.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/anderson.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/funcs.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/progresstracker.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/realtimeplotter.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/dependency_links.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/requires.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/top_level.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/setup.cfg +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_adder.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_amplifier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_block.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_delay.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_function.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_integrator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_multiplier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_rng.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_scope.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_sources.py +0 -0
- {pathsim-0.4.2/tests/solvers → pathsim-0.4.4/tests/diff}/__init__.py +0 -0
- {pathsim-0.4.2/tests/utils → pathsim-0.4.4/tests/solvers}/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/_referenceproblems.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_bdf.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_dirk2.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_dirk3.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk32.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk4.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk43.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk54.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk85.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_euler.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rk4.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkbs32.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkck54.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkdp54.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkdp87.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkf45.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkf78.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkv65.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_solver.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_ssprk22.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_ssprk33.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_ssprk34.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/test_connection.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_adaptivebuffer.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_anderson.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_funcs.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_gilbert.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_progresstracker.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_realtimeplotter.py +0 -0
|
@@ -46,8 +46,6 @@ FUNC_GRAD = {
|
|
|
46
46
|
np.cbrt : lambda x: 1/(3*np.cbrt(x)**2),
|
|
47
47
|
|
|
48
48
|
# Complex functions
|
|
49
|
-
np.real : lambda x: np.real(x),
|
|
50
|
-
np.imag : lambda x: np.imag(x),
|
|
51
49
|
np.conj : lambda x: np.conj(x),
|
|
52
50
|
np.abs : lambda x: x/np.abs(x),
|
|
53
51
|
np.angle : lambda x: -1j/x,
|
|
@@ -118,6 +116,18 @@ class Value:
|
|
|
118
116
|
return self.grad.get(other, 0.0)
|
|
119
117
|
|
|
120
118
|
|
|
119
|
+
@property
|
|
120
|
+
def real(self):
|
|
121
|
+
return Value(val=np.real(self.val),
|
|
122
|
+
grad={k: np.real(v) for k, v in self.grad.items()})
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def imag(self):
|
|
127
|
+
return Value(val=np.imag(self.val),
|
|
128
|
+
grad={k: np.imag(v) for k, v in self.grad.items()})
|
|
129
|
+
|
|
130
|
+
|
|
121
131
|
def __hash__(self):
|
|
122
132
|
return id(self)
|
|
123
133
|
|
|
@@ -332,6 +342,9 @@ if __name__ == "__main__":
|
|
|
332
342
|
|
|
333
343
|
A = np.array([x, Value(3), Value(0.5)])
|
|
334
344
|
|
|
345
|
+
C = np.array([[x, Value(3), Value(0.5)],
|
|
346
|
+
[Value(-1.09), y, Value(2.3)]])
|
|
347
|
+
|
|
335
348
|
B = A**2 - y
|
|
336
349
|
print(B[0].d(x))
|
|
337
350
|
|
|
@@ -347,7 +360,11 @@ if __name__ == "__main__":
|
|
|
347
360
|
b = np.linalg.norm(B)
|
|
348
361
|
print(b.d(x), b.d(y))
|
|
349
362
|
|
|
350
|
-
|
|
363
|
+
print(np.real(b))
|
|
364
|
+
|
|
365
|
+
c = np.dot(C, A)
|
|
366
|
+
print(c[0].d(x))
|
|
351
367
|
|
|
352
|
-
|
|
368
|
+
c = x + C
|
|
369
|
+
print(c[0, 0].d(x))
|
|
353
370
|
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## BASE CLASS FOR RUNGE-KUTTA INTEGRATORS
|
|
4
|
+
## (solvers/_rungekutta.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from ._solver import ExplicitSolver, ImplicitSolver
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# SOLVERS ==============================================================================
|
|
18
|
+
|
|
19
|
+
class ExplicitRungeKutta(ExplicitSolver):
|
|
20
|
+
"""
|
|
21
|
+
Base class for explicit Runge-Kutta integrators which implements
|
|
22
|
+
the timestepping at intermediate stages and the error control if
|
|
23
|
+
the coefficients for the local truncation error estimate are defined.
|
|
24
|
+
|
|
25
|
+
NOTE:
|
|
26
|
+
This class is not intended to be used directly!!!
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
30
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
31
|
+
|
|
32
|
+
#order of the integration scheme and embedded method (if available)
|
|
33
|
+
self.n = 0
|
|
34
|
+
self.m = 0
|
|
35
|
+
|
|
36
|
+
#number of stages in RK scheme
|
|
37
|
+
self.s = 0
|
|
38
|
+
|
|
39
|
+
#safety factor for error controller (if available)
|
|
40
|
+
self.beta = 0.9
|
|
41
|
+
|
|
42
|
+
#slope coefficients for stages
|
|
43
|
+
self.Ks = {}
|
|
44
|
+
|
|
45
|
+
#extended butcher tableau
|
|
46
|
+
self.BT = None
|
|
47
|
+
|
|
48
|
+
#coefficients for local truncation error estimate
|
|
49
|
+
self.TR = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def error_controller(self, dt):
|
|
53
|
+
"""
|
|
54
|
+
compute scaling factor for adaptive timestep based on
|
|
55
|
+
absolute and relative local truncation error estimate,
|
|
56
|
+
also checks if the error tolerance is achieved and returns
|
|
57
|
+
a success metric.
|
|
58
|
+
|
|
59
|
+
INPUTS:
|
|
60
|
+
dt : (float) integration timestep
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
#early exit if no error estimate avaliable
|
|
64
|
+
if self.TR is None:
|
|
65
|
+
return True, 0.0, 0.0, 1.0
|
|
66
|
+
|
|
67
|
+
#compute local truncation error slope (this is faster then 'sum' comprehension)
|
|
68
|
+
slope = 0.0
|
|
69
|
+
for i, b in enumerate(self.TR):
|
|
70
|
+
slope = slope + self.Ks[i] * b
|
|
71
|
+
tr = dt * slope
|
|
72
|
+
|
|
73
|
+
#compute and clip truncation error, error ratio abs
|
|
74
|
+
truncation_error_abs = float(np.max(np.clip(abs(tr), 1e-18, None)))
|
|
75
|
+
error_ratio_abs = self.tolerance_lte_abs / truncation_error_abs
|
|
76
|
+
|
|
77
|
+
#compute and clip truncation error, error ratio rel
|
|
78
|
+
if np.any(self.x == 0.0):
|
|
79
|
+
truncation_error_rel = 1.0
|
|
80
|
+
error_ratio_rel = 0.0
|
|
81
|
+
else:
|
|
82
|
+
truncation_error_rel = float(np.max(np.clip(abs(tr/self.x), 1e-18, None)))
|
|
83
|
+
error_ratio_rel = self.tolerance_lte_rel / truncation_error_rel
|
|
84
|
+
|
|
85
|
+
#compute error ratio and success check
|
|
86
|
+
error_ratio = max(error_ratio_abs, error_ratio_rel)
|
|
87
|
+
success = error_ratio >= 1.0
|
|
88
|
+
|
|
89
|
+
#compute timestep scale factor using accuracy order of truncation error
|
|
90
|
+
timestep_rescale = self.beta * error_ratio**(1/(min(self.m, self.n) + 1))
|
|
91
|
+
|
|
92
|
+
return success, truncation_error_abs, truncation_error_rel, timestep_rescale
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def step(self, u, t, dt):
|
|
96
|
+
"""
|
|
97
|
+
performs the (explicit) timestep at the intermediate RK stages
|
|
98
|
+
for (t+dt) based on the state and input at (t)
|
|
99
|
+
|
|
100
|
+
INPUTS:
|
|
101
|
+
u : (float) non-autonomous external component for integration
|
|
102
|
+
t : (float) evaluation time for right-hand-side function
|
|
103
|
+
dt : (float) integration timestep
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
#buffer intermediate slope
|
|
107
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
108
|
+
|
|
109
|
+
#compute slope at stage, faster then 'sum' comprehension
|
|
110
|
+
slope = 0.0
|
|
111
|
+
for i, b in enumerate(self.BT[self.stage]):
|
|
112
|
+
slope = slope + self.Ks[i] * b
|
|
113
|
+
self.x = self.x_0 + dt * slope
|
|
114
|
+
|
|
115
|
+
#error and step size control
|
|
116
|
+
if self.stage < self.s - 1:
|
|
117
|
+
|
|
118
|
+
#increment stage counter
|
|
119
|
+
self.stage += 1
|
|
120
|
+
|
|
121
|
+
#no error control for intermediate stages
|
|
122
|
+
return True, 0.0, 0.0, 1.0
|
|
123
|
+
|
|
124
|
+
else:
|
|
125
|
+
|
|
126
|
+
#reset stage counter
|
|
127
|
+
self.stage = 0
|
|
128
|
+
|
|
129
|
+
#compute truncation error estimate in final stage
|
|
130
|
+
return self.error_controller(dt)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class DiagonallyImplicitRungeKutta(ImplicitSolver):
|
|
135
|
+
"""
|
|
136
|
+
Base class for diagonally implicit Runge-Kutta (DIRK) integrators
|
|
137
|
+
which implements the timestepping at intermediate stages, involving
|
|
138
|
+
the numerical solution of the implicit update equation and the
|
|
139
|
+
error control if the coefficients for the local truncation error
|
|
140
|
+
estimate are defined.
|
|
141
|
+
|
|
142
|
+
Extensions and checks to also handle explicit first stages (ESDIRK)
|
|
143
|
+
and additional final evaluation coefficients (not stiffly accurate)
|
|
144
|
+
|
|
145
|
+
NOTE:
|
|
146
|
+
This class is not intended to be used directly!!!
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
150
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
151
|
+
|
|
152
|
+
#order of the integration scheme and embedded method (if available)
|
|
153
|
+
self.n = 0
|
|
154
|
+
self.m = 0
|
|
155
|
+
|
|
156
|
+
#number of stages in RK scheme
|
|
157
|
+
self.s = 0
|
|
158
|
+
|
|
159
|
+
#safety factor for error controller (if available)
|
|
160
|
+
self.beta = 0.9
|
|
161
|
+
|
|
162
|
+
#slope coefficients for stages
|
|
163
|
+
self.Ks = {}
|
|
164
|
+
|
|
165
|
+
#extended butcher tableau
|
|
166
|
+
self.BT = None
|
|
167
|
+
|
|
168
|
+
#final evaluation (if not stiffly accurate)
|
|
169
|
+
self.A = None
|
|
170
|
+
|
|
171
|
+
#coefficients for local truncation error estimate
|
|
172
|
+
self.TR = None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def error_controller(self, dt):
|
|
176
|
+
"""
|
|
177
|
+
compute scaling factor for adaptive timestep based on
|
|
178
|
+
absolute and relative local truncation error estimate,
|
|
179
|
+
also checks if the error tolerance is achieved and returns
|
|
180
|
+
a success metric.
|
|
181
|
+
|
|
182
|
+
INPUTS:
|
|
183
|
+
dt : (float) integration timestep
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
#early exit of not enough slopes or no error estimate at all
|
|
187
|
+
if self.TR is None or len(self.Ks) < len(self.TR):
|
|
188
|
+
return True, 0.0, 0.0, 1.0
|
|
189
|
+
|
|
190
|
+
#compute local truncation error slope (this is faster then 'sum' comprehension)
|
|
191
|
+
slope = 0.0
|
|
192
|
+
for i, b in enumerate(self.TR):
|
|
193
|
+
slope = slope + self.Ks[i] * b
|
|
194
|
+
tr = dt * slope
|
|
195
|
+
|
|
196
|
+
#compute and clip truncation error, error ratio abs
|
|
197
|
+
truncation_error_abs = float(np.max(np.clip(abs(tr), 1e-18, None)))
|
|
198
|
+
error_ratio_abs = self.tolerance_lte_abs / truncation_error_abs
|
|
199
|
+
|
|
200
|
+
#compute and clip truncation error, error ratio rel
|
|
201
|
+
if np.any(self.x == 0.0):
|
|
202
|
+
truncation_error_rel = 1.0
|
|
203
|
+
error_ratio_rel = 0.0
|
|
204
|
+
else:
|
|
205
|
+
truncation_error_rel = float(np.max(np.clip(abs(tr/self.x), 1e-18, None)))
|
|
206
|
+
error_ratio_rel = self.tolerance_lte_rel / truncation_error_rel
|
|
207
|
+
|
|
208
|
+
#compute error ratio and success check
|
|
209
|
+
error_ratio = max(error_ratio_abs, error_ratio_rel)
|
|
210
|
+
success = error_ratio >= 1.0
|
|
211
|
+
|
|
212
|
+
#compute timestep scale factor using accuracy order of truncation error
|
|
213
|
+
timestep_rescale = self.beta * error_ratio**(1/(min(self.m, self.n) + 1))
|
|
214
|
+
|
|
215
|
+
return success, truncation_error_abs, truncation_error_rel, timestep_rescale
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def solve(self, u, t, dt):
|
|
219
|
+
"""
|
|
220
|
+
Solves the implicit update equation via anderson acceleration.
|
|
221
|
+
|
|
222
|
+
INPUTS:
|
|
223
|
+
u : (float) non-autonomous external component for integration
|
|
224
|
+
t : (float) evaluation time for right-hand-side function
|
|
225
|
+
dt : (float) integration timestep
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
#first stage is explicit -> ESDIRK -> early exit
|
|
229
|
+
if self.stage == 0 and self.BT[self.stage] is None:
|
|
230
|
+
return 0.0
|
|
231
|
+
|
|
232
|
+
#update timestep weighted slope
|
|
233
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
234
|
+
|
|
235
|
+
#compute slope (this is faster then 'sum' comprehension)
|
|
236
|
+
slope = 0.0
|
|
237
|
+
for i, a in enumerate(self.BT[self.stage]):
|
|
238
|
+
slope = slope + self.Ks[i] * a
|
|
239
|
+
|
|
240
|
+
#use the jacobian
|
|
241
|
+
if self.jac is not None:
|
|
242
|
+
|
|
243
|
+
#most recent butcher coefficient
|
|
244
|
+
b = self.BT[self.stage][self.stage]
|
|
245
|
+
|
|
246
|
+
#compute jacobian of fixed-point equation
|
|
247
|
+
jac_g = dt * b * self.jac(self.x, u, t)
|
|
248
|
+
|
|
249
|
+
#anderson acceleration step with local newton
|
|
250
|
+
self.x, err = self.acc.step(self.x, dt*slope + self.x_0, jac_g)
|
|
251
|
+
|
|
252
|
+
else:
|
|
253
|
+
#anderson acceleration step (pure)
|
|
254
|
+
self.x, err = self.acc.step(self.x, dt*slope + self.x_0, None)
|
|
255
|
+
|
|
256
|
+
#return the fixed-point residual
|
|
257
|
+
return err
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def step(self, u, t, dt):
|
|
261
|
+
"""
|
|
262
|
+
performs the (explicit) timestep at the intermediate RK stages
|
|
263
|
+
for (t+dt) based on the state and input at (t)
|
|
264
|
+
|
|
265
|
+
INPUTS:
|
|
266
|
+
u : (float) non-autonomous external component for integration
|
|
267
|
+
t : (float) evaluation time for right-hand-side function
|
|
268
|
+
dt : (float) integration timestep
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
#first stage is explicit -> ESDIRK
|
|
272
|
+
if self.stage == 0 and self.BT[self.stage] is None:
|
|
273
|
+
self.Ks[self.stage] = self.func(self.x, u, t)
|
|
274
|
+
|
|
275
|
+
#restart anderson accelerator
|
|
276
|
+
self.acc.reset()
|
|
277
|
+
|
|
278
|
+
#error and step size control
|
|
279
|
+
if self.stage < self.s - 1:
|
|
280
|
+
|
|
281
|
+
#increment stage counter
|
|
282
|
+
self.stage += 1
|
|
283
|
+
|
|
284
|
+
#no error estimate for intermediate stages
|
|
285
|
+
return True, 0.0, 0.0, 1.0
|
|
286
|
+
|
|
287
|
+
else:
|
|
288
|
+
|
|
289
|
+
#compute final output if not stiffly accurate
|
|
290
|
+
if self.A is not None:
|
|
291
|
+
|
|
292
|
+
#compute slope (this is faster then 'sum' comprehension)
|
|
293
|
+
slope = 0.0
|
|
294
|
+
for i, a in enumerate(self.A):
|
|
295
|
+
slope = slope + self.Ks[i] * a
|
|
296
|
+
self.x = self.x_0 + dt * slope
|
|
297
|
+
|
|
298
|
+
#reset stage counter
|
|
299
|
+
self.stage = 0
|
|
300
|
+
|
|
301
|
+
#compute truncation error estimate in final stage
|
|
302
|
+
return self.error_controller(dt)
|
|
@@ -56,6 +56,9 @@ class Solver:
|
|
|
56
56
|
#flag to identify adaptive/fixed timestep solvers
|
|
57
57
|
self.is_adaptive = False
|
|
58
58
|
|
|
59
|
+
#order of the integration scheme
|
|
60
|
+
self.n = 1
|
|
61
|
+
|
|
59
62
|
#current evaluation stage for multistage solvers
|
|
60
63
|
self.stage = 0
|
|
61
64
|
|
|
@@ -186,7 +189,7 @@ class ExplicitSolver(Solver):
|
|
|
186
189
|
"""
|
|
187
190
|
Base class for explicit solver definition.
|
|
188
191
|
|
|
189
|
-
INPUTS :
|
|
192
|
+
INPUTS (*solver_args, **solver_kwargs) :
|
|
190
193
|
initial_value : (float or array) initial condition / integration constant
|
|
191
194
|
func : (callable) function to integrate with state 'x', input 'u' and time 't' dependency
|
|
192
195
|
jac : (callable or None) jacobian of 'func' with respect to 'x', depending on 'x', 'u' and 't', if 'None', no jacobian is used
|
|
@@ -194,17 +197,8 @@ class ExplicitSolver(Solver):
|
|
|
194
197
|
tolerance_lte_rel : (float) relative tolerance for local truncation error (for solvers with error estimate)
|
|
195
198
|
"""
|
|
196
199
|
|
|
197
|
-
def __init__(self,
|
|
198
|
-
|
|
199
|
-
func=lambda x, u, t: u,
|
|
200
|
-
jac=None,
|
|
201
|
-
tolerance_lte_abs=1e-6,
|
|
202
|
-
tolerance_lte_rel=1e-3):
|
|
203
|
-
super().__init__(initial_value,
|
|
204
|
-
func,
|
|
205
|
-
jac,
|
|
206
|
-
tolerance_lte_abs,
|
|
207
|
-
tolerance_lte_rel)
|
|
200
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
201
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
208
202
|
|
|
209
203
|
#flag to identify implicit/explicit solvers
|
|
210
204
|
self.is_explicit = True
|
|
@@ -292,7 +286,7 @@ class ImplicitSolver(Solver):
|
|
|
292
286
|
"""
|
|
293
287
|
Base class for implicit solver definition.
|
|
294
288
|
|
|
295
|
-
INPUTS :
|
|
289
|
+
INPUTS (*solver_args, **solver_kwargs) :
|
|
296
290
|
initial_value : (float or array) initial condition / integration constant
|
|
297
291
|
func : (callable) function to integrate with state 'x', input 'u' and time 't' dependency
|
|
298
292
|
jac : (callable or None) jacobian of 'func' with respect to 'x', depending on 'x', 'u' and 't', if 'None', no jacobian is used
|
|
@@ -300,17 +294,8 @@ class ImplicitSolver(Solver):
|
|
|
300
294
|
tolerance_lte_rel : (float) relative tolerance for local truncation error (for solvers with error estimate)
|
|
301
295
|
"""
|
|
302
296
|
|
|
303
|
-
def __init__(self,
|
|
304
|
-
|
|
305
|
-
func=lambda x, u, t: u,
|
|
306
|
-
jac=None,
|
|
307
|
-
tolerance_lte_abs=1e-6,
|
|
308
|
-
tolerance_lte_rel=1e-3):
|
|
309
|
-
super().__init__(initial_value,
|
|
310
|
-
func,
|
|
311
|
-
jac,
|
|
312
|
-
tolerance_lte_abs,
|
|
313
|
-
tolerance_lte_rel)
|
|
297
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
298
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
314
299
|
|
|
315
300
|
#flag to identify implicit/explicit solvers
|
|
316
301
|
self.is_explicit = False
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
# IMPORTS ==============================================================================
|
|
11
11
|
|
|
12
12
|
from ._solver import ImplicitSolver
|
|
13
|
-
from ..utils.funcs import numerical_jacobian
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
# SOLVERS ==============================================================================
|
|
@@ -21,20 +20,15 @@ class BDF2(ImplicitSolver):
|
|
|
21
20
|
with order ramp up for the initial steps.
|
|
22
21
|
"""
|
|
23
22
|
|
|
24
|
-
def __init__(self,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
tolerance_lte_rel=1e-3):
|
|
30
|
-
super().__init__(initial_value,
|
|
31
|
-
func,
|
|
32
|
-
jac,
|
|
33
|
-
tolerance_lte_abs,
|
|
34
|
-
tolerance_lte_rel)
|
|
23
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
24
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
25
|
+
|
|
26
|
+
#integration order (local)
|
|
27
|
+
self.n = 2
|
|
35
28
|
|
|
36
29
|
#bdf coefficients
|
|
37
|
-
self.K = {1:[1.0],
|
|
30
|
+
self.K = {1:[1.0],
|
|
31
|
+
2:[-1/3, 4/3]}
|
|
38
32
|
self.F = {1:1.0, 2:2/3}
|
|
39
33
|
|
|
40
34
|
#bdf solution buffer
|
|
@@ -103,17 +97,11 @@ class BDF3(ImplicitSolver):
|
|
|
103
97
|
with order ramp up for the initial steps.
|
|
104
98
|
"""
|
|
105
99
|
|
|
106
|
-
def __init__(self,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
tolerance_lte_rel=1e-3):
|
|
112
|
-
super().__init__(initial_value,
|
|
113
|
-
func,
|
|
114
|
-
jac,
|
|
115
|
-
tolerance_lte_abs,
|
|
116
|
-
tolerance_lte_rel)
|
|
100
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
101
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
102
|
+
|
|
103
|
+
#integration order (local)
|
|
104
|
+
self.n = 3
|
|
117
105
|
|
|
118
106
|
#bdf coefficients
|
|
119
107
|
self.K = {1:[1.0],
|
|
@@ -165,7 +153,6 @@ class BDF3(ImplicitSolver):
|
|
|
165
153
|
return err
|
|
166
154
|
|
|
167
155
|
|
|
168
|
-
|
|
169
156
|
def step(self, u, t, dt):
|
|
170
157
|
"""
|
|
171
158
|
Performs the timestep by buffereing the previous state.
|
|
@@ -188,17 +175,11 @@ class BDF4(ImplicitSolver):
|
|
|
188
175
|
with order ramp up for the initial steps.
|
|
189
176
|
"""
|
|
190
177
|
|
|
191
|
-
def __init__(self,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
tolerance_lte_rel=1e-3):
|
|
197
|
-
super().__init__(initial_value,
|
|
198
|
-
func,
|
|
199
|
-
jac,
|
|
200
|
-
tolerance_lte_abs,
|
|
201
|
-
tolerance_lte_rel)
|
|
178
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
179
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
180
|
+
|
|
181
|
+
#integration order (local)
|
|
182
|
+
self.n = 4
|
|
202
183
|
|
|
203
184
|
#bdf coefficients
|
|
204
185
|
self.K = {1:[1.0],
|
|
@@ -237,7 +218,7 @@ class BDF4(ImplicitSolver):
|
|
|
237
218
|
#use the jacobian
|
|
238
219
|
if self.jac is not None:
|
|
239
220
|
|
|
240
|
-
#compute jacobian
|
|
221
|
+
#compute jacobian of implicit update equation
|
|
241
222
|
jac_g = self.F[n] * dt * self.jac(self.x, u, t)
|
|
242
223
|
|
|
243
224
|
#anderson acceleration step with local newton
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## DIAGONALLY IMPLICIT RUNGE KUTTA METHOD
|
|
4
|
+
## (solvers/dirk2.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
from ._rungekutta import DiagonallyImplicitRungeKutta
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# SOLVERS ==============================================================================
|
|
16
|
+
|
|
17
|
+
class DIRK2(DiagonallyImplicitRungeKutta):
|
|
18
|
+
"""
|
|
19
|
+
The 2-stage SSP-optimal Diagonally Implicit Runge–Kutta (DIRK) method
|
|
20
|
+
of second order, namely the second order RK with the largest radius
|
|
21
|
+
of absolute monotonicity.
|
|
22
|
+
It is also symplectic and the optimal 2-stage second order implicit RK.
|
|
23
|
+
|
|
24
|
+
FROM :
|
|
25
|
+
L. Ferracina and M.N. Spijker.
|
|
26
|
+
Strong stability of singlydiagonally-implicit Runge-Kutta methods.
|
|
27
|
+
Applied Numerical Mathematics, 58:1675–1686, 2008.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
31
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
32
|
+
|
|
33
|
+
#number of stages in RK scheme
|
|
34
|
+
self.s = 2
|
|
35
|
+
|
|
36
|
+
#order of scheme
|
|
37
|
+
self.n = 2
|
|
38
|
+
|
|
39
|
+
#intermediate evaluation times
|
|
40
|
+
self.eval_stages = [1/4, 3/4]
|
|
41
|
+
|
|
42
|
+
#butcher table
|
|
43
|
+
self.BT = {0:[1/4],
|
|
44
|
+
1:[1/2, 1/4]}
|
|
45
|
+
|
|
46
|
+
#final evaluation
|
|
47
|
+
self.A = [1/2, 1/2]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## DIAGONALLY IMPLICIT RUNGE KUTTA METHOD
|
|
4
|
+
## (solvers/dirk3.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
from ._rungekutta import DiagonallyImplicitRungeKutta
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# SOLVERS ==============================================================================
|
|
16
|
+
|
|
17
|
+
class DIRK3(DiagonallyImplicitRungeKutta):
|
|
18
|
+
"""
|
|
19
|
+
Four-stage, 3rd order, L-stable Diagonally Implicit Runge–Kutta (DIRK) method.
|
|
20
|
+
|
|
21
|
+
(from Wikipedia)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
25
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
26
|
+
|
|
27
|
+
#number of stages in RK scheme
|
|
28
|
+
self.s = 4
|
|
29
|
+
|
|
30
|
+
#order of scheme
|
|
31
|
+
self.n = 3
|
|
32
|
+
|
|
33
|
+
#intermediate evaluation times
|
|
34
|
+
self.eval_stages = [1/2, 2/3, 1/2, 1.0]
|
|
35
|
+
|
|
36
|
+
#butcher table
|
|
37
|
+
self.BT = {0:[1/2],
|
|
38
|
+
1:[1/6, 1/2],
|
|
39
|
+
2:[-1/2, 1/2, 1/2],
|
|
40
|
+
3:[3/2, -3/2, 1/2, 1/2]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## EMBEDDED DIAGONALLY IMPLICIT RUNGE KUTTA METHOD
|
|
4
|
+
## (solvers/esdirk32.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
from ._rungekutta import DiagonallyImplicitRungeKutta
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# SOLVERS ==============================================================================
|
|
16
|
+
|
|
17
|
+
class ESDIRK32(DiagonallyImplicitRungeKutta):
|
|
18
|
+
"""
|
|
19
|
+
Williams et.al constructed an ESDIRK method of order 3 with four stages.
|
|
20
|
+
This method is constructed such that it is applicable to index-2
|
|
21
|
+
differential algebraic systems.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
25
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
26
|
+
|
|
27
|
+
#number of stages in RK scheme
|
|
28
|
+
self.s = 4
|
|
29
|
+
|
|
30
|
+
#order of scheme and embedded method
|
|
31
|
+
self.n = 3
|
|
32
|
+
self.m = 2
|
|
33
|
+
|
|
34
|
+
#flag adaptive timestep solver
|
|
35
|
+
self.is_adaptive = True
|
|
36
|
+
|
|
37
|
+
#intermediate evaluation times
|
|
38
|
+
self.eval_stages = [0.0, 1.0, 3/2, 1.0]
|
|
39
|
+
|
|
40
|
+
#butcher table
|
|
41
|
+
self.BT = {0:None, #explicit first stage
|
|
42
|
+
1:[1/2, 1/2],
|
|
43
|
+
2:[5/8, 3/8, 1/2],
|
|
44
|
+
3:[7/18, 1/3, -2/9, 1/2]}
|
|
45
|
+
|
|
46
|
+
#coefficients for truncation error estimate
|
|
47
|
+
self.TR = [-1/9, -1/6, -2/9, 1/2]
|