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.
Files changed (132) hide show
  1. {pathsim-0.4.2 → pathsim-0.4.4}/PKG-INFO +1 -1
  2. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/diff/value.py +21 -4
  3. pathsim-0.4.4/pathsim/solvers/_rungekutta.py +302 -0
  4. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/_solver.py +9 -24
  5. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/bdf.py +18 -37
  6. pathsim-0.4.4/pathsim/solvers/dirk2.py +47 -0
  7. pathsim-0.4.4/pathsim/solvers/dirk3.py +40 -0
  8. pathsim-0.4.4/pathsim/solvers/esdirk32.py +47 -0
  9. pathsim-0.4.4/pathsim/solvers/esdirk4.py +42 -0
  10. pathsim-0.4.4/pathsim/solvers/esdirk43.py +58 -0
  11. pathsim-0.4.4/pathsim/solvers/esdirk54.py +59 -0
  12. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/esdirk85.py +12 -114
  13. pathsim-0.4.4/pathsim/solvers/rk4.py +38 -0
  14. pathsim-0.4.4/pathsim/solvers/rkbs32.py +50 -0
  15. pathsim-0.4.4/pathsim/solvers/rkck54.py +57 -0
  16. pathsim-0.4.4/pathsim/solvers/rkdp54.py +59 -0
  17. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/rkdp87.py +11 -76
  18. pathsim-0.4.4/pathsim/solvers/rkf45.py +51 -0
  19. pathsim-0.4.4/pathsim/solvers/rkf78.py +60 -0
  20. pathsim-0.4.4/pathsim/solvers/rkv65.py +56 -0
  21. pathsim-0.4.4/pathsim/solvers/ssprk22.py +40 -0
  22. pathsim-0.4.4/pathsim/solvers/ssprk33.py +43 -0
  23. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/ssprk34.py +9 -39
  24. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/gilbert.py +1 -1
  25. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/PKG-INFO +1 -1
  26. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/SOURCES.txt +3 -0
  27. {pathsim-0.4.2 → pathsim-0.4.4}/setup.py +1 -1
  28. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_differentiator.py +6 -4
  29. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_lti.py +7 -5
  30. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_ode.py +6 -4
  31. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_spectrum.py +6 -4
  32. pathsim-0.4.4/tests/diff/test_value.py +342 -0
  33. {pathsim-0.4.2 → pathsim-0.4.4}/tests/test_simulation.py +9 -5
  34. {pathsim-0.4.2 → pathsim-0.4.4}/tests/test_subsystem.py +4 -4
  35. pathsim-0.4.4/tests/utils/__init__.py +0 -0
  36. pathsim-0.4.2/pathsim/solvers/dirk2.py +0 -111
  37. pathsim-0.4.2/pathsim/solvers/dirk3.py +0 -96
  38. pathsim-0.4.2/pathsim/solvers/esdirk32.py +0 -148
  39. pathsim-0.4.2/pathsim/solvers/esdirk4.py +0 -107
  40. pathsim-0.4.2/pathsim/solvers/esdirk43.py +0 -156
  41. pathsim-0.4.2/pathsim/solvers/esdirk54.py +0 -158
  42. pathsim-0.4.2/pathsim/solvers/rk4.py +0 -67
  43. pathsim-0.4.2/pathsim/solvers/rkbs32.py +0 -114
  44. pathsim-0.4.2/pathsim/solvers/rkck54.py +0 -121
  45. pathsim-0.4.2/pathsim/solvers/rkdp54.py +0 -124
  46. pathsim-0.4.2/pathsim/solvers/rkf45.py +0 -115
  47. pathsim-0.4.2/pathsim/solvers/rkf78.py +0 -124
  48. pathsim-0.4.2/pathsim/solvers/rkv65.py +0 -121
  49. pathsim-0.4.2/pathsim/solvers/ssprk22.py +0 -68
  50. pathsim-0.4.2/pathsim/solvers/ssprk33.py +0 -71
  51. {pathsim-0.4.2 → pathsim-0.4.4}/LICENSE.txt +0 -0
  52. {pathsim-0.4.2 → pathsim-0.4.4}/README.md +0 -0
  53. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/__init__.py +0 -0
  54. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/__init__.py +0 -0
  55. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/_block.py +0 -0
  56. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/adder.py +0 -0
  57. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/amplifier.py +0 -0
  58. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/delay.py +0 -0
  59. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/differentiator.py +0 -0
  60. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/function.py +0 -0
  61. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/integrator.py +0 -0
  62. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/lti.py +0 -0
  63. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/multiplier.py +0 -0
  64. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/ode.py +0 -0
  65. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/__init__.py +0 -0
  66. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/filters.py +0 -0
  67. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/noise.py +0 -0
  68. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/sources.py +0 -0
  69. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rf/wienerhammerstein.py +0 -0
  70. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/rng.py +0 -0
  71. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/scope.py +0 -0
  72. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/sources.py +0 -0
  73. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/blocks/spectrum.py +0 -0
  74. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/connection.py +0 -0
  75. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/diff/__init__.py +0 -0
  76. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/simulation.py +0 -0
  77. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/__init__.py +0 -0
  78. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/solvers/euler.py +0 -0
  79. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/subsystem.py +0 -0
  80. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/__init__.py +0 -0
  81. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/adaptivebuffer.py +0 -0
  82. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/anderson.py +0 -0
  83. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/funcs.py +0 -0
  84. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/progresstracker.py +0 -0
  85. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim/utils/realtimeplotter.py +0 -0
  86. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/dependency_links.txt +0 -0
  87. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/requires.txt +0 -0
  88. {pathsim-0.4.2 → pathsim-0.4.4}/pathsim.egg-info/top_level.txt +0 -0
  89. {pathsim-0.4.2 → pathsim-0.4.4}/setup.cfg +0 -0
  90. {pathsim-0.4.2 → pathsim-0.4.4}/tests/__init__.py +0 -0
  91. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/__init__.py +0 -0
  92. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_adder.py +0 -0
  93. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_amplifier.py +0 -0
  94. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_block.py +0 -0
  95. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_delay.py +0 -0
  96. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_function.py +0 -0
  97. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_integrator.py +0 -0
  98. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_multiplier.py +0 -0
  99. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_rng.py +0 -0
  100. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_scope.py +0 -0
  101. {pathsim-0.4.2 → pathsim-0.4.4}/tests/blocks/test_sources.py +0 -0
  102. {pathsim-0.4.2/tests/solvers → pathsim-0.4.4/tests/diff}/__init__.py +0 -0
  103. {pathsim-0.4.2/tests/utils → pathsim-0.4.4/tests/solvers}/__init__.py +0 -0
  104. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/_referenceproblems.py +0 -0
  105. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_bdf.py +0 -0
  106. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_dirk2.py +0 -0
  107. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_dirk3.py +0 -0
  108. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk32.py +0 -0
  109. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk4.py +0 -0
  110. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk43.py +0 -0
  111. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk54.py +0 -0
  112. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_esdirk85.py +0 -0
  113. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_euler.py +0 -0
  114. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rk4.py +0 -0
  115. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkbs32.py +0 -0
  116. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkck54.py +0 -0
  117. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkdp54.py +0 -0
  118. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkdp87.py +0 -0
  119. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkf45.py +0 -0
  120. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkf78.py +0 -0
  121. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_rkv65.py +0 -0
  122. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_solver.py +0 -0
  123. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_ssprk22.py +0 -0
  124. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_ssprk33.py +0 -0
  125. {pathsim-0.4.2 → pathsim-0.4.4}/tests/solvers/test_ssprk34.py +0 -0
  126. {pathsim-0.4.2 → pathsim-0.4.4}/tests/test_connection.py +0 -0
  127. {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_adaptivebuffer.py +0 -0
  128. {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_anderson.py +0 -0
  129. {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_funcs.py +0 -0
  130. {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_gilbert.py +0 -0
  131. {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_progresstracker.py +0 -0
  132. {pathsim-0.4.2 → pathsim-0.4.4}/tests/utils/test_realtimeplotter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pathsim
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: A block based time domain system simulation framework.
5
5
  Home-page: https://github.com/milanofthe/pathsim
6
6
  Author: Milan Rother
@@ -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
- d = np.sign(np.sin(2*np.pi*x))
363
+ print(np.real(b))
364
+
365
+ c = np.dot(C, A)
366
+ print(c[0].d(x))
351
367
 
352
- print(np.sign(d))
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
- initial_value=0,
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
- initial_value=0,
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
- initial_value=0,
26
- func=lambda x, u, t: u,
27
- jac=None,
28
- tolerance_lte_abs=1e-6,
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], 2:[-1/3, 4/3]}
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
- initial_value=0,
108
- func=lambda x, u, t: u,
109
- jac=None,
110
- tolerance_lte_abs=1e-6,
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
- initial_value=0,
193
- func=lambda x, u, t: u,
194
- jac=None,
195
- tolerance_lte_abs=1e-6,
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]