pathsim 0.4.2__tar.gz → 0.4.3__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.3}/PKG-INFO +1 -1
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/diff/value.py +21 -4
- pathsim-0.4.3/pathsim/solvers/_rungekutta.py +302 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/_solver.py +9 -24
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/bdf.py +15 -35
- pathsim-0.4.3/pathsim/solvers/dirk2.py +47 -0
- pathsim-0.4.3/pathsim/solvers/dirk3.py +40 -0
- pathsim-0.4.3/pathsim/solvers/esdirk32.py +47 -0
- pathsim-0.4.3/pathsim/solvers/esdirk4.py +42 -0
- pathsim-0.4.3/pathsim/solvers/esdirk43.py +58 -0
- pathsim-0.4.3/pathsim/solvers/esdirk54.py +59 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/esdirk85.py +12 -114
- pathsim-0.4.3/pathsim/solvers/rk4.py +38 -0
- pathsim-0.4.3/pathsim/solvers/rkbs32.py +50 -0
- pathsim-0.4.3/pathsim/solvers/rkck54.py +57 -0
- pathsim-0.4.3/pathsim/solvers/rkdp54.py +58 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/rkdp87.py +11 -76
- pathsim-0.4.3/pathsim/solvers/rkf45.py +51 -0
- pathsim-0.4.3/pathsim/solvers/rkf78.py +60 -0
- pathsim-0.4.3/pathsim/solvers/rkv65.py +56 -0
- pathsim-0.4.3/pathsim/solvers/ssprk22.py +40 -0
- pathsim-0.4.3/pathsim/solvers/ssprk33.py +43 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/ssprk34.py +9 -39
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim.egg-info/PKG-INFO +1 -1
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim.egg-info/SOURCES.txt +1 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/setup.py +1 -1
- 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.3}/LICENSE.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/README.md +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/_block.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/adder.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/amplifier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/delay.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/differentiator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/function.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/integrator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/lti.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/multiplier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/ode.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/rf/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/rf/filters.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/rf/noise.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/rf/sources.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/rf/wienerhammerstein.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/rng.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/scope.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/sources.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/blocks/spectrum.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/connection.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/diff/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/simulation.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/solvers/euler.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/subsystem.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/adaptivebuffer.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/anderson.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/funcs.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/gilbert.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/progresstracker.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim/utils/realtimeplotter.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim.egg-info/dependency_links.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim.egg-info/requires.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/pathsim.egg-info/top_level.txt +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/setup.cfg +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_adder.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_amplifier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_block.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_delay.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_differentiator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_function.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_integrator.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_lti.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_multiplier.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_ode.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_rng.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_scope.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_sources.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/blocks/test_spectrum.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/_referenceproblems.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_bdf.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_dirk2.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_dirk3.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_esdirk32.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_esdirk4.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_esdirk43.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_esdirk54.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_esdirk85.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_euler.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rk4.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkbs32.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkck54.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkdp54.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkdp87.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkf45.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkf78.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_rkv65.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_solver.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_ssprk22.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_ssprk33.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/solvers/test_ssprk34.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/test_connection.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/test_simulation.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/test_subsystem.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/utils/__init__.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/utils/test_adaptivebuffer.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/utils/test_anderson.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/utils/test_funcs.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/utils/test_gilbert.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/tests/utils/test_progresstracker.py +0 -0
- {pathsim-0.4.2 → pathsim-0.4.3}/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 of not enough slopes or no error estimate at all
|
|
64
|
+
if self.TR is None or len(self.Ks) < len(self.TR):
|
|
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 and update state at stage (this is 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,17 +20,11 @@ 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
30
|
self.K = {1:[1.0], 2:[-1/3, 4/3]}
|
|
@@ -103,17 +96,11 @@ class BDF3(ImplicitSolver):
|
|
|
103
96
|
with order ramp up for the initial steps.
|
|
104
97
|
"""
|
|
105
98
|
|
|
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)
|
|
99
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
100
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
101
|
+
|
|
102
|
+
#integration order (local)
|
|
103
|
+
self.n = 3
|
|
117
104
|
|
|
118
105
|
#bdf coefficients
|
|
119
106
|
self.K = {1:[1.0],
|
|
@@ -165,7 +152,6 @@ class BDF3(ImplicitSolver):
|
|
|
165
152
|
return err
|
|
166
153
|
|
|
167
154
|
|
|
168
|
-
|
|
169
155
|
def step(self, u, t, dt):
|
|
170
156
|
"""
|
|
171
157
|
Performs the timestep by buffereing the previous state.
|
|
@@ -188,17 +174,11 @@ class BDF4(ImplicitSolver):
|
|
|
188
174
|
with order ramp up for the initial steps.
|
|
189
175
|
"""
|
|
190
176
|
|
|
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)
|
|
177
|
+
def __init__(self, *solver_args, **solver_kwargs):
|
|
178
|
+
super().__init__(*solver_args, **solver_kwargs)
|
|
179
|
+
|
|
180
|
+
#integration order (local)
|
|
181
|
+
self.n = 4
|
|
202
182
|
|
|
203
183
|
#bdf coefficients
|
|
204
184
|
self.K = {1:[1.0],
|
|
@@ -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]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## DIAGONALLY IMPLICIT RUNGE KUTTA METHOD
|
|
4
|
+
## (solvers/esdirk4.py)
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
from ._rungekutta import DiagonallyImplicitRungeKutta
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# SOLVERS ==============================================================================
|
|
16
|
+
|
|
17
|
+
class ESDIRK4(DiagonallyImplicitRungeKutta):
|
|
18
|
+
"""
|
|
19
|
+
6-stage, 4th order Diagonally Implicit Runge–Kutta (DIRK) method
|
|
20
|
+
with explicit first stage that is specifically designed to handle
|
|
21
|
+
differential algebraic equations of indices up to two or three.
|
|
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 = 6
|
|
29
|
+
|
|
30
|
+
#order of scheme
|
|
31
|
+
self.n = 4
|
|
32
|
+
|
|
33
|
+
#intermediate evaluation times
|
|
34
|
+
self.eval_stages = [0.0, 1/2, 1/6, 37/40, 1/2, 1.0]
|
|
35
|
+
|
|
36
|
+
#butcher table
|
|
37
|
+
self.BT = {0:None, #explicit first stage
|
|
38
|
+
1:[1/4, 1/4],
|
|
39
|
+
2:[-1/36, -1/18, 1/4],
|
|
40
|
+
3:[-21283/32000, -5143/64000, 90909/64000, 1/4],
|
|
41
|
+
4:[46010759/749250000, -737693/40500000, 10931269/45500000, -1140071/34090875, 1/4],
|
|
42
|
+
5:[89/444, 89/804756, -27/364, -20000/171717, 843750/1140071, 1/4]}
|