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,157 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkdp87.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.rkdp87 import RKDP87
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 TestRKDP87(unittest.TestCase):
50
+ """
51
+ Test the implementation of the 'RKDP87' solver class
52
+ """
53
+
54
+ def test_init(self):
55
+
56
+ #test default initializtion
57
+ solver = RKDP87()
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 = RKDP87(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 = RKDP87()
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 = RKDP87()
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(-0.4, 0, 20)
117
+
118
+ for problem in reference_problems:
119
+
120
+ solver = RKDP87(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 8
135
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
136
+ self.assertGreater(p, 8)
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 = RKDP87(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
+ # RUN TESTS LOCALLY ====================================================================
155
+
156
+ if __name__ == '__main__':
157
+ unittest.main(verbosity=2)
@@ -0,0 +1,159 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkf45.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.rkf45 import RKF45
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 TestRKF45(unittest.TestCase):
50
+ """
51
+ Test the implementation of the 'RKF45' solver class
52
+ """
53
+
54
+ def test_init(self):
55
+
56
+ #test default initializtion
57
+ solver = RKF45()
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 = RKF45(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 = RKF45()
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 = RKF45()
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, -1, 20)
117
+
118
+ for problem in reference_problems:
119
+
120
+ solver = RKF45(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=1.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 4
135
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
136
+ self.assertEqual(np.round(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 = RKF45(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)
@@ -0,0 +1,160 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkf78.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.rkf78 import RKF78
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
+
49
+ # TESTS ================================================================================
50
+
51
+ class TestRKF78(unittest.TestCase):
52
+ """
53
+ Test the implementation of the 'RKF78' solver class
54
+ """
55
+
56
+ def test_init(self):
57
+
58
+ #test default initializtion
59
+ solver = RKF78()
60
+
61
+ self.assertTrue(callable(solver.func))
62
+ self.assertEqual(solver.jac, None)
63
+ self.assertEqual(solver.initial_value, 0)
64
+
65
+ self.assertEqual(solver.stage, 0)
66
+ self.assertTrue(solver.is_adaptive)
67
+ self.assertTrue(solver.is_explicit)
68
+ self.assertFalse(solver.is_implicit)
69
+
70
+ #test specific initialization
71
+ solver = RKF78(initial_value=1,
72
+ func=lambda x, u, t: -x,
73
+ jac=lambda x, u, t: -1,
74
+ tolerance_lte=1e-6)
75
+
76
+ self.assertEqual(solver.func(2, 0, 0), -2)
77
+ self.assertEqual(solver.jac(2, 0, 0), -1)
78
+ self.assertEqual(solver.initial_value, 1)
79
+ self.assertEqual(solver.tolerance_lte, 1e-6)
80
+
81
+
82
+ def test_stages(self):
83
+
84
+ solver = RKF78()
85
+
86
+ for i, t in enumerate(solver.stages(0, 1)):
87
+
88
+ #test the stage iterator
89
+ self.assertEqual(t, solver.eval_stages[i])
90
+
91
+
92
+ def test_step(self):
93
+
94
+ solver = RKF78()
95
+
96
+ for i, t in enumerate(solver.stages(0, 1)):
97
+
98
+ #test if stage incrementation works
99
+ self.assertEqual(solver.stage, i)
100
+
101
+ success, err, scale = solver.step(0.0, t, 1)
102
+
103
+ #test if expected return at intermediate stages
104
+ if i < len(solver.eval_stages)-1:
105
+ self.assertTrue(success)
106
+ self.assertEqual(err, 0.0)
107
+ self.assertEqual(scale, 1.0)
108
+
109
+ #test if expected return at final stage
110
+ self.assertNotEqual(err, 0.0)
111
+ self.assertNotEqual(scale, 1.0)
112
+
113
+
114
+ def test_integrate_fixed(self):
115
+
116
+ #integrate test problem and assess convergence order
117
+
118
+ timesteps = np.logspace(-1, 0, 20)
119
+
120
+ for problem in reference_problems:
121
+
122
+ solver = RKF78(problem.x0, problem.func, problem.jac)
123
+
124
+ errors = []
125
+
126
+ for dt in timesteps:
127
+
128
+ solver.reset()
129
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=1.0, dt=dt, adaptive=False)
130
+
131
+ errors.append(np.linalg.norm(numerical_solution - problem.solution(time)))
132
+
133
+ #test if errors are monotonically decreasing
134
+ self.assertTrue(np.all(np.diff(errors)>0))
135
+
136
+ #test convergence order, expected 7 or 8
137
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
138
+ self.assertGreater(p, 6.5)
139
+
140
+
141
+ def test_integrate_adaptive(self):
142
+
143
+ #test the error control for each reference problem
144
+
145
+ for problem in reference_problems:
146
+
147
+ solver = RKF78(problem.x0, problem.func, problem.jac, tolerance_lte=1e-6)
148
+
149
+ time, numerical_solution = solver.integrate(time_start=0.0, time_end=1.0, dt=1, adaptive=True)
150
+ error = np.linalg.norm(numerical_solution - problem.solution(time))
151
+
152
+ #test if error control was successful
153
+ self.assertLess(error, solver.tolerance_lte)
154
+
155
+
156
+
157
+ # RUN TESTS LOCALLY ====================================================================
158
+
159
+ if __name__ == '__main__':
160
+ unittest.main(verbosity=2)
@@ -0,0 +1,160 @@
1
+ ########################################################################################
2
+ ##
3
+ ## TESTS FOR
4
+ ## 'solvers/rkv65.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.rkv65 import RKV65
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 TestRKV65(unittest.TestCase):
50
+ """
51
+ Test the implementation of the 'RKV65' solver class
52
+ """
53
+
54
+ def test_init(self):
55
+
56
+ #test default initializtion
57
+ solver = RKV65()
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 = RKV65(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 = RKV65()
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 = RKV65()
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(-1, -0.2, 20)
117
+
118
+ for problem in reference_problems:
119
+
120
+ solver = RKV65(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=1.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 4
135
+ p, _ = np.polyfit(np.log10(timesteps), np.log10(errors), deg=1)
136
+ self.assertEqual(np.round(p), 6)
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 = RKV65(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
+
157
+ # RUN TESTS LOCALLY ====================================================================
158
+
159
+ if __name__ == '__main__':
160
+ unittest.main(verbosity=2)