pathsim 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. pathsim/__init__.py +3 -0
  2. pathsim/blocks/__init__.py +14 -0
  3. pathsim/blocks/_block.py +209 -0
  4. pathsim/blocks/adder.py +30 -0
  5. pathsim/blocks/amplifier.py +34 -0
  6. pathsim/blocks/delay.py +70 -0
  7. pathsim/blocks/differentiator.py +70 -0
  8. pathsim/blocks/function.py +82 -0
  9. pathsim/blocks/integrator.py +66 -0
  10. pathsim/blocks/lti.py +155 -0
  11. pathsim/blocks/multiplier.py +30 -0
  12. pathsim/blocks/ode.py +86 -0
  13. pathsim/blocks/rf/__init__.py +4 -0
  14. pathsim/blocks/rf/filters.py +169 -0
  15. pathsim/blocks/rf/noise.py +218 -0
  16. pathsim/blocks/rf/sources.py +163 -0
  17. pathsim/blocks/rf/wienerhammerstein.py +338 -0
  18. pathsim/blocks/rng.py +57 -0
  19. pathsim/blocks/scope.py +224 -0
  20. pathsim/blocks/sources.py +71 -0
  21. pathsim/blocks/spectrum.py +316 -0
  22. pathsim/connection.py +112 -0
  23. pathsim/simulation.py +652 -0
  24. pathsim/solvers/__init__.py +25 -0
  25. pathsim/solvers/_solver.py +403 -0
  26. pathsim/solvers/bdf.py +240 -0
  27. pathsim/solvers/dirk2.py +101 -0
  28. pathsim/solvers/dirk3.py +86 -0
  29. pathsim/solvers/esdirk32.py +131 -0
  30. pathsim/solvers/esdirk4.py +99 -0
  31. pathsim/solvers/esdirk43.py +139 -0
  32. pathsim/solvers/esdirk54.py +141 -0
  33. pathsim/solvers/esdirk85.py +200 -0
  34. pathsim/solvers/euler.py +81 -0
  35. pathsim/solvers/rk4.py +61 -0
  36. pathsim/solvers/rkbs32.py +101 -0
  37. pathsim/solvers/rkck54.py +108 -0
  38. pathsim/solvers/rkdp54.py +111 -0
  39. pathsim/solvers/rkdp87.py +116 -0
  40. pathsim/solvers/rkf45.py +102 -0
  41. pathsim/solvers/rkf78.py +111 -0
  42. pathsim/solvers/rkv65.py +103 -0
  43. pathsim/solvers/ssprk22.py +62 -0
  44. pathsim/solvers/ssprk33.py +65 -0
  45. pathsim/solvers/ssprk34.py +74 -0
  46. pathsim/subsystem.py +267 -0
  47. pathsim/utils/__init__.py +0 -0
  48. pathsim/utils/adaptivebuffer.py +87 -0
  49. pathsim/utils/anderson.py +180 -0
  50. pathsim/utils/funcs.py +205 -0
  51. pathsim/utils/gilbert.py +110 -0
  52. pathsim/utils/progresstracker.py +90 -0
  53. pathsim/utils/realtimeplotter.py +230 -0
  54. pathsim/utils/statespacerealizations.py +116 -0
  55. pathsim/utils/waveforms.py +36 -0
  56. pathsim-0.2.0.dist-info/LICENSE.txt +21 -0
  57. pathsim-0.2.0.dist-info/METADATA +149 -0
  58. pathsim-0.2.0.dist-info/RECORD +109 -0
  59. pathsim-0.2.0.dist-info/WHEEL +5 -0
  60. pathsim-0.2.0.dist-info/top_level.txt +2 -0
  61. tests/__init__.py +0 -0
  62. tests/blocks/__init__.py +0 -0
  63. tests/blocks/test_adder.py +85 -0
  64. tests/blocks/test_amplifier.py +66 -0
  65. tests/blocks/test_block.py +138 -0
  66. tests/blocks/test_delay.py +122 -0
  67. tests/blocks/test_differentiator.py +102 -0
  68. tests/blocks/test_function.py +165 -0
  69. tests/blocks/test_integrator.py +92 -0
  70. tests/blocks/test_lti.py +162 -0
  71. tests/blocks/test_multiplier.py +87 -0
  72. tests/blocks/test_ode.py +125 -0
  73. tests/blocks/test_rng.py +109 -0
  74. tests/blocks/test_scope.py +196 -0
  75. tests/blocks/test_sources.py +119 -0
  76. tests/blocks/test_spectrum.py +119 -0
  77. tests/solvers/__init__.py +0 -0
  78. tests/solvers/test_bdf.py +364 -0
  79. tests/solvers/test_dirk2.py +138 -0
  80. tests/solvers/test_dirk3.py +137 -0
  81. tests/solvers/test_esdirk32.py +158 -0
  82. tests/solvers/test_esdirk4.py +138 -0
  83. tests/solvers/test_esdirk43.py +158 -0
  84. tests/solvers/test_esdirk54.py +160 -0
  85. tests/solvers/test_esdirk85.py +157 -0
  86. tests/solvers/test_euler.py +223 -0
  87. tests/solvers/test_rk4.py +138 -0
  88. tests/solvers/test_rkbs32.py +159 -0
  89. tests/solvers/test_rkck54.py +157 -0
  90. tests/solvers/test_rkdp54.py +159 -0
  91. tests/solvers/test_rkdp87.py +157 -0
  92. tests/solvers/test_rkf45.py +159 -0
  93. tests/solvers/test_rkf78.py +160 -0
  94. tests/solvers/test_rkv65.py +160 -0
  95. tests/solvers/test_solver.py +119 -0
  96. tests/solvers/test_ssprk22.py +136 -0
  97. tests/solvers/test_ssprk33.py +136 -0
  98. tests/solvers/test_ssprk34.py +136 -0
  99. tests/test_connection.py +176 -0
  100. tests/test_simulation.py +271 -0
  101. tests/test_subsystem.py +182 -0
  102. tests/utils/__init__.py +0 -0
  103. tests/utils/test_adaptivebuffer.py +111 -0
  104. tests/utils/test_anderson.py +142 -0
  105. tests/utils/test_funcs.py +143 -0
  106. tests/utils/test_gilbert.py +108 -0
  107. tests/utils/test_progresstracker.py +144 -0
  108. tests/utils/test_realtimeplotter.py +122 -0
  109. tests/utils/test_statespacerealizations.py +107 -0
@@ -0,0 +1,138 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rk4.py'
5
+ ##
6
+ ## Milan Rother 2024
7
+ ##
8
+ ########################################################################################
9
+
10
+ # IMPORTS ==============================================================================
11
+
12
+ import unittest
13
+ import numpy as np
14
+
15
+ from pathsim.solvers.rk4 import RK4
16
+
17
+
18
+
19
+ # TEST PROBLEMS ========================================================================
20
+
21
+ class Problem:
22
+ def __init__(self, name, func, jac, x0, solution):
23
+ self.name = name
24
+ self.func = func
25
+ self.jac = jac
26
+ self.x0 = x0
27
+ self.solution = solution
28
+
29
+
30
+ #create some reference problems for testing
31
+ reference_problems = [
32
+ Problem(name="linear_feedback",
33
+ func=lambda x, u, t: -x,
34
+ jac=lambda x, u, t: -1,
35
+ x0=1.0,
36
+ solution=lambda t: np.exp(-t)
37
+ ),
38
+ Problem(name="logistic",
39
+ func=lambda x, u, t: x*(1-x),
40
+ jac=lambda x, u, t: 1-2*x,
41
+ x0=0.5,
42
+ solution=lambda t: 1/(1 + np.exp(-t))
43
+ )
44
+ ]
45
+
46
+
47
+ # TESTS ================================================================================
48
+
49
+ class TestRK4(unittest.TestCase):
50
+ """
51
+ Test the implementation of the 'RK4' solver class
52
+ """
53
+
54
+ def test_init(self):
55
+
56
+ #test default initializtion
57
+ solver = RK4()
58
+
59
+ self.assertTrue(callable(solver.func))
60
+ self.assertEqual(solver.jac, None)
61
+ self.assertEqual(solver.initial_value, 0)
62
+
63
+ self.assertEqual(solver.stage, 0)
64
+ self.assertFalse(solver.is_adaptive)
65
+ self.assertTrue(solver.is_explicit)
66
+ self.assertFalse(solver.is_implicit)
67
+
68
+ #test specific initialization
69
+ solver = RK4(initial_value=1,
70
+ func=lambda x, u, t: -x,
71
+ jac=lambda x, u, t: -1,
72
+ tolerance_lte=1e-6)
73
+
74
+ self.assertEqual(solver.func(2, 0, 0), -2)
75
+ self.assertEqual(solver.jac(2, 0, 0), -1)
76
+ self.assertEqual(solver.initial_value, 1)
77
+ self.assertEqual(solver.tolerance_lte, 1e-6)
78
+
79
+
80
+ def test_stages(self):
81
+
82
+ solver = RK4()
83
+
84
+ for i, t in enumerate(solver.stages(0, 1)):
85
+
86
+ #test the stage iterator
87
+ self.assertEqual(t, solver.eval_stages[i])
88
+
89
+
90
+ def test_step(self):
91
+
92
+ solver = RK4()
93
+
94
+ for i, t in enumerate(solver.stages(0, 1)):
95
+
96
+ #test if stage incrementation works
97
+ self.assertEqual(solver.stage, i)
98
+
99
+ success, err, scale = solver.step(0.0, t, 1)
100
+
101
+ #test if expected return at intermediate stages
102
+ self.assertTrue(success)
103
+ self.assertEqual(err, 0.0)
104
+ self.assertEqual(scale, 1.0)
105
+
106
+
107
+ def test_integrate_fixed(self):
108
+
109
+ #integrate test problem and assess convergence order
110
+
111
+ timesteps = np.logspace(-0.4, 0, 20)
112
+
113
+ for problem in reference_problems:
114
+
115
+ solver = RK4(problem.x0, problem.func, problem.jac)
116
+
117
+ errors = []
118
+
119
+ for dt in timesteps:
120
+
121
+ solver.reset()
122
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=3.0, dt=dt, adaptive=False)
123
+
124
+ errors.append(np.linalg.norm(numerical_solution - problem.solution(time)))
125
+
126
+ #test if errors are monotonically decreasing
127
+ self.assertTrue(np.all(np.diff(errors)>0))
128
+
129
+ #test convergence order, expected 4
130
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
131
+ self.assertEqual(round(p), 4)
132
+
133
+
134
+
135
+ # RUN TESTS LOCALLY ====================================================================
136
+
137
+ if __name__ == '__main__':
138
+ unittest.main(verbosity=2)
@@ -0,0 +1,159 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkbs32.py'
5
+ ##
6
+ ## Milan Rother 2024
7
+ ##
8
+ ########################################################################################
9
+
10
+ # IMPORTS ==============================================================================
11
+
12
+ import unittest
13
+ import numpy as np
14
+
15
+ from pathsim.solvers.rkbs32 import RKBS32
16
+
17
+
18
+ # TEST PROBLEMS ========================================================================
19
+
20
+ class Problem:
21
+ def __init__(self, name, func, jac, x0, solution):
22
+ self.name = name
23
+ self.func = func
24
+ self.jac = jac
25
+ self.x0 = x0
26
+ self.solution = solution
27
+
28
+
29
+ #create some reference problems for testing
30
+ reference_problems = [
31
+ Problem(name="linear_feedback",
32
+ func=lambda x, u, t: -x,
33
+ jac=lambda x, u, t: -1,
34
+ x0=1.0,
35
+ solution=lambda t: np.exp(-t)
36
+ ),
37
+ Problem(name="logistic",
38
+ func=lambda x, u, t: x*(1-x),
39
+ jac=lambda x, u, t: 1-2*x,
40
+ x0=0.5,
41
+ solution=lambda t: 1/(1 + np.exp(-t))
42
+ )
43
+ ]
44
+
45
+
46
+
47
+
48
+ # TESTS ================================================================================
49
+
50
+ class TestRKBS32(unittest.TestCase):
51
+ """
52
+ Test the implementation of the 'RKBS32' solver class
53
+ """
54
+
55
+ def test_init(self):
56
+
57
+ #test default initializtion
58
+ solver = RKBS32()
59
+
60
+ self.assertTrue(callable(solver.func))
61
+ self.assertEqual(solver.jac, None)
62
+ self.assertEqual(solver.initial_value, 0)
63
+
64
+ self.assertEqual(solver.stage, 0)
65
+ self.assertTrue(solver.is_adaptive)
66
+ self.assertTrue(solver.is_explicit)
67
+ self.assertFalse(solver.is_implicit)
68
+
69
+ #test specific initialization
70
+ solver = RKBS32(initial_value=1,
71
+ func=lambda x, u, t: -x,
72
+ jac=lambda x, u, t: -1,
73
+ tolerance_lte=1e-6)
74
+
75
+ self.assertEqual(solver.func(2, 0, 0), -2)
76
+ self.assertEqual(solver.jac(2, 0, 0), -1)
77
+ self.assertEqual(solver.initial_value, 1)
78
+ self.assertEqual(solver.tolerance_lte, 1e-6)
79
+
80
+
81
+ def test_stages(self):
82
+
83
+ solver = RKBS32()
84
+
85
+ for i, t in enumerate(solver.stages(0, 1)):
86
+
87
+ #test the stage iterator
88
+ self.assertEqual(t, solver.eval_stages[i])
89
+
90
+
91
+ def test_step(self):
92
+
93
+ solver = RKBS32()
94
+
95
+ for i, t in enumerate(solver.stages(0, 1)):
96
+
97
+ #test if stage incrementation works
98
+ self.assertEqual(solver.stage, i)
99
+
100
+ success, err, scale = solver.step(0.0, t, 1)
101
+
102
+ #test if expected return at intermediate stages
103
+ if i < len(solver.eval_stages)-1:
104
+ self.assertTrue(success)
105
+ self.assertEqual(err, 0.0)
106
+ self.assertEqual(scale, 1.0)
107
+
108
+ #test if expected return at final stage
109
+ self.assertNotEqual(err, 0.0)
110
+ self.assertNotEqual(scale, 1.0)
111
+
112
+
113
+ def test_integrate_fixed(self):
114
+
115
+ #integrate test problem and assess convergence order
116
+
117
+ timesteps = np.logspace(-0.4, 0, 20)
118
+
119
+ for problem in reference_problems:
120
+
121
+ solver = RKBS32(problem.x0, problem.func, problem.jac)
122
+
123
+ errors = []
124
+
125
+ for dt in timesteps:
126
+
127
+ solver.reset()
128
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=3.0, dt=dt, adaptive=False)
129
+
130
+ errors.append(np.linalg.norm(numerical_solution - problem.solution(time)))
131
+
132
+ #test if errors are monotonically decreasing
133
+ self.assertTrue(np.all(np.diff(errors)>0))
134
+
135
+ #test convergence order, expected 3
136
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
137
+ self.assertEqual(round(p), 3)
138
+
139
+
140
+ def test_integrate_adaptive(self):
141
+
142
+ #test the error control for each reference problem
143
+
144
+ for problem in reference_problems:
145
+
146
+ solver = RKBS32(problem.x0, problem.func, problem.jac, tolerance_lte=1e-6)
147
+
148
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=2.0, dt=0.1, adaptive=True)
149
+ error = np.linalg.norm(numerical_solution - problem.solution(time))
150
+
151
+ #test if error control was successful (same OOM for global error -> < 1e-5)
152
+ self.assertLess(error, solver.tolerance_lte*10)
153
+
154
+
155
+
156
+ # RUN TESTS LOCALLY ====================================================================
157
+
158
+ if __name__ == '__main__':
159
+ unittest.main(verbosity=2)
@@ -0,0 +1,157 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkck54.py'
5
+ ##
6
+ ## Milan Rother 2024
7
+ ##
8
+ ########################################################################################
9
+
10
+ # IMPORTS ==============================================================================
11
+
12
+ import unittest
13
+ import numpy as np
14
+
15
+ from pathsim.solvers.rkck54 import RKCK54
16
+
17
+
18
+ # TEST PROBLEMS ========================================================================
19
+
20
+ class Problem:
21
+ def __init__(self, name, func, jac, x0, solution):
22
+ self.name = name
23
+ self.func = func
24
+ self.jac = jac
25
+ self.x0 = x0
26
+ self.solution = solution
27
+
28
+
29
+ #create some reference problems for testing
30
+ reference_problems = [
31
+ Problem(name="linear_feedback",
32
+ func=lambda x, u, t: -x,
33
+ jac=lambda x, u, t: -1,
34
+ x0=1.0,
35
+ solution=lambda t: np.exp(-t)
36
+ ),
37
+ Problem(name="logistic",
38
+ func=lambda x, u, t: x*(1-x),
39
+ jac=lambda x, u, t: 1-2*x,
40
+ x0=0.5,
41
+ solution=lambda t: 1/(1 + np.exp(-t))
42
+ )
43
+ ]
44
+
45
+
46
+ # TESTS ================================================================================
47
+
48
+ class TestRKCK54(unittest.TestCase):
49
+ """
50
+ Test the implementation of the 'RKCK54' solver class
51
+ """
52
+
53
+ def test_init(self):
54
+
55
+ #test default initializtion
56
+ solver = RKCK54()
57
+
58
+ self.assertTrue(callable(solver.func))
59
+ self.assertEqual(solver.jac, None)
60
+ self.assertEqual(solver.initial_value, 0)
61
+
62
+ self.assertEqual(solver.stage, 0)
63
+ self.assertTrue(solver.is_adaptive)
64
+ self.assertTrue(solver.is_explicit)
65
+ self.assertFalse(solver.is_implicit)
66
+
67
+ #test specific initialization
68
+ solver = RKCK54(initial_value=1,
69
+ func=lambda x, u, t: -x,
70
+ jac=lambda x, u, t: -1,
71
+ tolerance_lte=1e-6)
72
+
73
+ self.assertEqual(solver.func(2, 0, 0), -2)
74
+ self.assertEqual(solver.jac(2, 0, 0), -1)
75
+ self.assertEqual(solver.initial_value, 1)
76
+ self.assertEqual(solver.tolerance_lte, 1e-6)
77
+
78
+
79
+ def test_stages(self):
80
+
81
+ solver = RKCK54()
82
+
83
+ for i, t in enumerate(solver.stages(0, 1)):
84
+
85
+ #test the stage iterator
86
+ self.assertEqual(t, solver.eval_stages[i])
87
+
88
+
89
+ def test_step(self):
90
+
91
+ solver = RKCK54()
92
+
93
+ for i, t in enumerate(solver.stages(0, 1)):
94
+
95
+ #test if stage incrementation works
96
+ self.assertEqual(solver.stage, i)
97
+
98
+ success, err, scale = solver.step(0.0, t, 1)
99
+
100
+ #test if expected return at intermediate stages
101
+ if i < len(solver.eval_stages)-1:
102
+ self.assertTrue(success)
103
+ self.assertEqual(err, 0.0)
104
+ self.assertEqual(scale, 1.0)
105
+
106
+ #test if expected return at final stage
107
+ self.assertNotEqual(err, 0.0)
108
+ self.assertNotEqual(scale, 1.0)
109
+
110
+
111
+ def test_integrate_fixed(self):
112
+
113
+ #integrate test problem and assess convergence order
114
+
115
+ timesteps = np.logspace(-2, -1, 20)
116
+
117
+ for problem in reference_problems:
118
+
119
+ solver = RKCK54(problem.x0, problem.func, problem.jac)
120
+
121
+ errors = []
122
+
123
+ for dt in timesteps:
124
+
125
+ solver.reset()
126
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=1.0, dt=dt, adaptive=False)
127
+
128
+ errors.append(np.linalg.norm(numerical_solution - problem.solution(time)))
129
+
130
+ #test if errors are monotonically decreasing
131
+ self.assertTrue(np.all(np.diff(errors)>0))
132
+
133
+ #test convergence order, expected 5
134
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
135
+ self.assertEqual(np.round(p), 5)
136
+
137
+
138
+ def test_integrate_adaptive(self):
139
+
140
+ #test the error control for each reference problem
141
+
142
+ for problem in reference_problems:
143
+
144
+ solver = RKCK54(problem.x0, problem.func, problem.jac, tolerance_lte=1e-6)
145
+
146
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=1.0, dt=1, adaptive=True)
147
+ error = np.linalg.norm(numerical_solution - problem.solution(time))
148
+
149
+ #test if error control was successful
150
+ self.assertLess(error, solver.tolerance_lte)
151
+
152
+
153
+
154
+ # RUN TESTS LOCALLY ====================================================================
155
+
156
+ if __name__ == '__main__':
157
+ unittest.main(verbosity=2)
@@ -0,0 +1,159 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkdp54.py'
5
+ ##
6
+ ## Milan Rother 2024
7
+ ##
8
+ ########################################################################################
9
+
10
+ # IMPORTS ==============================================================================
11
+
12
+ import unittest
13
+ import numpy as np
14
+
15
+ from pathsim.solvers.rkdp54 import RKDP54
16
+
17
+
18
+ # TEST PROBLEMS ========================================================================
19
+
20
+ class Problem:
21
+ def __init__(self, name, func, jac, x0, solution):
22
+ self.name = name
23
+ self.func = func
24
+ self.jac = jac
25
+ self.x0 = x0
26
+ self.solution = solution
27
+
28
+
29
+ #create some reference problems for testing
30
+ reference_problems = [
31
+ Problem(name="linear_feedback",
32
+ func=lambda x, u, t: -x,
33
+ jac=lambda x, u, t: -1,
34
+ x0=1.0,
35
+ solution=lambda t: np.exp(-t)
36
+ ),
37
+ Problem(name="logistic",
38
+ func=lambda x, u, t: x*(1-x),
39
+ jac=lambda x, u, t: 1-2*x,
40
+ x0=0.5,
41
+ solution=lambda t: 1/(1 + np.exp(-t))
42
+ )
43
+ ]
44
+
45
+
46
+
47
+ # TESTS ================================================================================
48
+
49
+ class TestRKDP54(unittest.TestCase):
50
+ """
51
+ Test the implementation of the 'RKDP54' solver class
52
+ """
53
+
54
+ def test_init(self):
55
+
56
+ #test default initializtion
57
+ solver = RKDP54()
58
+
59
+ self.assertTrue(callable(solver.func))
60
+ self.assertEqual(solver.jac, None)
61
+ self.assertEqual(solver.initial_value, 0)
62
+
63
+ self.assertEqual(solver.stage, 0)
64
+ self.assertTrue(solver.is_adaptive)
65
+ self.assertTrue(solver.is_explicit)
66
+ self.assertFalse(solver.is_implicit)
67
+
68
+ #test specific initialization
69
+ solver = RKDP54(initial_value=1,
70
+ func=lambda x, u, t: -x,
71
+ jac=lambda x, u, t: -1,
72
+ tolerance_lte=1e-6)
73
+
74
+ self.assertEqual(solver.func(2, 0, 0), -2)
75
+ self.assertEqual(solver.jac(2, 0, 0), -1)
76
+ self.assertEqual(solver.initial_value, 1)
77
+ self.assertEqual(solver.tolerance_lte, 1e-6)
78
+
79
+
80
+ def test_stages(self):
81
+
82
+ solver = RKDP54()
83
+
84
+ for i, t in enumerate(solver.stages(0, 1)):
85
+
86
+ #test the stage iterator
87
+ self.assertEqual(t, solver.eval_stages[i])
88
+
89
+
90
+ def test_step(self):
91
+
92
+ solver = RKDP54()
93
+
94
+ for i, t in enumerate(solver.stages(0, 1)):
95
+
96
+ #test if stage incrementation works
97
+ self.assertEqual(solver.stage, i)
98
+
99
+ success, err, scale = solver.step(0.0, t, 1)
100
+
101
+ #test if expected return at intermediate stages
102
+ if i < len(solver.eval_stages)-1:
103
+ self.assertTrue(success)
104
+ self.assertEqual(err, 0.0)
105
+ self.assertEqual(scale, 1.0)
106
+
107
+ #test if expected return at final stage
108
+ self.assertNotEqual(err, 0.0)
109
+ self.assertNotEqual(scale, 1.0)
110
+
111
+
112
+ def test_integrate_fixed(self):
113
+
114
+ #integrate test problem and assess convergence order
115
+
116
+ timesteps = np.logspace(-2, -0.2, 20)
117
+
118
+ for problem in reference_problems:
119
+
120
+ solver = RKDP54(problem.x0, problem.func, problem.jac)
121
+
122
+ errors = []
123
+
124
+ for dt in timesteps:
125
+
126
+ solver.reset()
127
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=3.0, dt=dt, adaptive=False)
128
+
129
+ errors.append(np.linalg.norm(numerical_solution - problem.solution(time)))
130
+
131
+ #test if errors are monotonically decreasing
132
+ self.assertTrue(np.all(np.diff(errors)>0))
133
+
134
+ #test convergence order, expected 5
135
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
136
+ self.assertGreater(p, 4)
137
+
138
+
139
+ def test_integrate_adaptive(self):
140
+
141
+ #test the error control for each reference problem
142
+
143
+ for problem in reference_problems:
144
+
145
+ solver = RKDP54(problem.x0, problem.func, problem.jac, tolerance_lte=1e-6)
146
+
147
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=1.0, dt=1, adaptive=True)
148
+ error = np.linalg.norm(numerical_solution - problem.solution(time))
149
+
150
+ #test if error control was successful
151
+ self.assertLess(error, solver.tolerance_lte)
152
+
153
+
154
+
155
+
156
+ # RUN TESTS LOCALLY ====================================================================
157
+
158
+ if __name__ == '__main__':
159
+ unittest.main(verbosity=2)