pathsim 0.2.0__tar.gz → 0.4.2__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 (117) hide show
  1. {pathsim-0.2.0 → pathsim-0.4.2}/LICENSE.txt +20 -20
  2. pathsim-0.4.2/PKG-INFO +261 -0
  3. pathsim-0.4.2/README.md +244 -0
  4. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/__init__.py +2 -2
  5. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/__init__.py +14 -14
  6. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/_block.py +213 -209
  7. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/adder.py +30 -30
  8. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/amplifier.py +34 -34
  9. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/delay.py +69 -69
  10. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/differentiator.py +69 -69
  11. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/function.py +82 -82
  12. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/integrator.py +65 -65
  13. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/lti.py +154 -154
  14. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/multiplier.py +30 -30
  15. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/ode.py +85 -85
  16. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/rf/__init__.py +3 -3
  17. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/rf/filters.py +168 -168
  18. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/rf/noise.py +218 -218
  19. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/rf/sources.py +163 -163
  20. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/rf/wienerhammerstein.py +337 -337
  21. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/rng.py +57 -57
  22. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/scope.py +224 -224
  23. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/sources.py +71 -70
  24. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/blocks/spectrum.py +316 -316
  25. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/connection.py +121 -111
  26. pathsim-0.4.2/pathsim/diff/__init__.py +1 -0
  27. pathsim-0.4.2/pathsim/diff/value.py +353 -0
  28. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/simulation.py +669 -651
  29. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/__init__.py +25 -25
  30. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/_solver.py +435 -402
  31. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/bdf.py +267 -240
  32. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/dirk2.py +111 -101
  33. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/dirk3.py +96 -86
  34. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/esdirk32.py +147 -130
  35. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/esdirk4.py +107 -99
  36. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/esdirk43.py +155 -138
  37. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/esdirk54.py +157 -140
  38. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/esdirk85.py +216 -199
  39. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/euler.py +81 -81
  40. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rk4.py +67 -61
  41. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkbs32.py +113 -100
  42. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkck54.py +120 -107
  43. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkdp54.py +123 -110
  44. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkdp87.py +125 -115
  45. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkf45.py +114 -101
  46. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkf78.py +123 -110
  47. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/rkv65.py +120 -102
  48. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/ssprk22.py +68 -62
  49. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/ssprk33.py +71 -65
  50. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/solvers/ssprk34.py +80 -74
  51. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/subsystem.py +266 -267
  52. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/adaptivebuffer.py +87 -87
  53. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/anderson.py +180 -180
  54. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/funcs.py +205 -205
  55. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/gilbert.py +110 -110
  56. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/progresstracker.py +90 -90
  57. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/realtimeplotter.py +229 -229
  58. pathsim-0.4.2/pathsim.egg-info/PKG-INFO +261 -0
  59. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim.egg-info/SOURCES.txt +3 -0
  60. {pathsim-0.2.0 → pathsim-0.4.2}/setup.cfg +4 -4
  61. {pathsim-0.2.0 → pathsim-0.4.2}/setup.py +26 -26
  62. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_adder.py +84 -84
  63. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_amplifier.py +65 -65
  64. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_block.py +137 -137
  65. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_delay.py +121 -121
  66. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_differentiator.py +102 -102
  67. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_function.py +164 -164
  68. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_integrator.py +94 -92
  69. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_lti.py +162 -162
  70. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_multiplier.py +86 -86
  71. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_ode.py +125 -125
  72. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_rng.py +108 -108
  73. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_scope.py +195 -195
  74. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_sources.py +118 -118
  75. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/test_spectrum.py +118 -118
  76. pathsim-0.4.2/tests/solvers/_referenceproblems.py +40 -0
  77. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_bdf.py +333 -364
  78. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_dirk2.py +108 -138
  79. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_dirk3.py +108 -137
  80. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_esdirk32.py +129 -158
  81. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_esdirk4.py +108 -138
  82. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_esdirk43.py +128 -158
  83. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_esdirk54.py +128 -160
  84. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_esdirk85.py +129 -157
  85. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_euler.py +196 -223
  86. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rk4.py +107 -138
  87. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkbs32.py +127 -159
  88. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkck54.py +128 -157
  89. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkdp54.py +127 -159
  90. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkdp87.py +127 -157
  91. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkf45.py +128 -159
  92. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkf78.py +127 -160
  93. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_rkv65.py +128 -160
  94. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_solver.py +119 -118
  95. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_ssprk22.py +107 -136
  96. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_ssprk33.py +107 -136
  97. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/test_ssprk34.py +108 -136
  98. {pathsim-0.2.0 → pathsim-0.4.2}/tests/test_connection.py +175 -175
  99. {pathsim-0.2.0 → pathsim-0.4.2}/tests/test_simulation.py +270 -270
  100. {pathsim-0.2.0 → pathsim-0.4.2}/tests/test_subsystem.py +182 -182
  101. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/test_adaptivebuffer.py +110 -110
  102. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/test_anderson.py +141 -141
  103. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/test_funcs.py +142 -142
  104. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/test_gilbert.py +107 -107
  105. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/test_progresstracker.py +143 -143
  106. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/test_realtimeplotter.py +121 -121
  107. pathsim-0.2.0/PKG-INFO +0 -149
  108. pathsim-0.2.0/README.md +0 -132
  109. pathsim-0.2.0/pathsim.egg-info/PKG-INFO +0 -149
  110. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim/utils/__init__.py +0 -0
  111. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim.egg-info/dependency_links.txt +0 -0
  112. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim.egg-info/requires.txt +0 -0
  113. {pathsim-0.2.0 → pathsim-0.4.2}/pathsim.egg-info/top_level.txt +0 -0
  114. {pathsim-0.2.0 → pathsim-0.4.2}/tests/__init__.py +0 -0
  115. {pathsim-0.2.0 → pathsim-0.4.2}/tests/blocks/__init__.py +0 -0
  116. {pathsim-0.2.0 → pathsim-0.4.2}/tests/solvers/__init__.py +0 -0
  117. {pathsim-0.2.0 → pathsim-0.4.2}/tests/utils/__init__.py +0 -0
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Milan Rother
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Milan Rother
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
pathsim-0.4.2/PKG-INFO ADDED
@@ -0,0 +1,261 @@
1
+ Metadata-Version: 2.1
2
+ Name: pathsim
3
+ Version: 0.4.2
4
+ Summary: A block based time domain system simulation framework.
5
+ Home-page: https://github.com/milanofthe/pathsim
6
+ Author: Milan Rother
7
+ Author-email: milan.rother@gmx.de
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.txt
14
+ Requires-Dist: numpy
15
+ Requires-Dist: matplotlib
16
+ Requires-Dist: scipy
17
+
18
+ # PathSim: A Time-Domain System Simulation Framework
19
+
20
+
21
+ ## Overview
22
+
23
+ PathSim is a minimalistic and flexible block-based time-domain system simulation framework in Python with basic automatic differentiation capabilities. It provides a modular and intuitive approach to modeling and simulating complex interconnected dynamical systems. It is similar to Matlab Simulink in spirit but works very differently under the hood.
24
+
25
+ Key features of PathSim include:
26
+
27
+ - Decentralized architecture where each dynamical block has their own numerical integration engine.
28
+ - The system is solved directly on the computational graph instead of compiling a unified differential algebraic system.
29
+ - This has some advantages such as hot-swappable blocks during simulation and reading simulation results directly from the scopes.
30
+ - The block execution is decoupled from the data transfer, which enables parallelization (future) and linear computational complexity scaling for sparsely connected systems.
31
+ - Support for MIMO (Multiple Input, Multiple Output) blocks, enabling the creation of complex interconnected system topologies.
32
+ - Fixed-point iteration approach with path length estimation to efficiently resolve algebraic loops.
33
+ - Wide range of numerical solvers, including implicit and explicit multi-stage, and adaptive Runge-Kutta methods such as `RKDP54` or `ESDIRK54`.
34
+ - Modular and hierarchical modeling with (nested) subsystems.
35
+ - Automatic differentiation for differentiable system simulations.
36
+ - Library of pre-defined blocks, including mathematical operations, integrators, delays, transfer functions, and more.
37
+ - Easy extensibility, allowing users to define custom blocks by subclassing the base `Block` class and implementing just a handful of methods.
38
+
39
+ ## Installation
40
+
41
+ The latest release version of pathsim is installable via pip:
42
+
43
+ ```console
44
+ $ pip install pathsim
45
+ ```
46
+
47
+ ## Example - Harmonic Oscillator
48
+
49
+ Here's an example that demonstrates how to create a basic simulation. The main components of the package are:
50
+
51
+ - `Simulation`: The main class that handles the blocks, connections, and the simulation loop.
52
+ - `Connection`: The class that defines the connections between blocks.
53
+ - Various block classes from the `blocks` module, such as `Integrator`, `Amplifier`, `Adder`, `Scope`, etc.
54
+
55
+ In this example, we create a simulation of the harmonic oscillator (a spring mass damper 2nd order system) initial value problem. The ODE that defines it is give by
56
+
57
+ $$
58
+ \ddot{x} + \frac{c}{m} \dot{x} + \frac{k}{m} x = 0
59
+ $$
60
+
61
+ where $c$ is the damping, $k$ the spring constant and $m$ the mass. And initial conditions $x_0$ and $v_0$ for position and velocity.
62
+
63
+ The ODE above can be translated to a block diagram using integrators, amplifiers and adders in the following way:
64
+
65
+ ![png](README_files/harmonic_oscillator_blockdiagram.png)
66
+
67
+ The topology of the block diagram above can be directly defined as blocks and connections in the `PathSim` framework. First we initialize the blocks needed to represent the dynamical systems with their respective arguments such as initial conditions and gain values, then the blocks are connected using `Connection` objects, forming two feedback loops. The `Simulation` instance manages the blocks and connections and advances the system in time with the timestep (`dt`). The `log` flag for logging the simulation progress is also set. Finally, we run the simulation for some number of seconds and plot the results using the `plot()` method of the scope block.
68
+
69
+
70
+
71
+ ```python
72
+ from pathsim import Simulation
73
+ from pathsim import Connection
74
+ from pathsim.blocks import Integrator, Amplifier, Adder, Scope
75
+ from pathsim.solvers import SSPRK22 # 2nd order fixed timestep, this is also the default
76
+
77
+ #initial position and velocity
78
+ x0, v0 = 2, 5
79
+
80
+ #parameters (mass, damping, spring constant)
81
+ m, c, k = 0.8, 0.2, 1.5
82
+
83
+ # Create blocks
84
+ I1 = Integrator(v0) # integrator for velocity
85
+ I2 = Integrator(x0) # integrator for position
86
+ A1 = Amplifier(-c/m)
87
+ A2 = Amplifier(-k/m)
88
+ P1 = Adder()
89
+ Sc = Scope(labels=["v(t)", "x(t)"])
90
+
91
+ blocks = [I1, I2, A1, A2, P1, Sc]
92
+
93
+ # Create connections
94
+ connections = [
95
+ Connection(I1, I2, A1, Sc), # one to many connection
96
+ Connection(I2, A2, Sc[1]),
97
+ Connection(A1, P1), # default connection to port 0
98
+ Connection(A2, P1[1]), # specific connection to port 1
99
+ Connection(P1, I1)
100
+ ]
101
+
102
+ # Create a simulation instance from the blocks and connections
103
+ Sim = Simulation(blocks, connections, dt=0.05, log=True, Solver=SSPRK22)
104
+
105
+ # Run the simulation for 50 seconds
106
+ Sim.run(duration=50.0)
107
+
108
+ # Plot the results directly from the scope
109
+ Sc.plot()
110
+
111
+ # Read the results from the scope for further processing
112
+ time, data = Sc.read()
113
+ ```
114
+
115
+ 2024-10-23 15:38:58,943 - INFO - LOGGING enabled
116
+ 2024-10-23 15:38:58,943 - INFO - SOLVER SSPRK22 adaptive=False implicit=False
117
+ 2024-10-23 15:38:58,944 - INFO - PATH LENGTH ESTIMATE 2, 'iterations_min' set to 2
118
+ 2024-10-23 15:38:58,944 - INFO - RESET
119
+ 2024-10-23 15:38:58,944 - INFO - RUN duration=50.0
120
+ 2024-10-23 15:38:58,945 - INFO - STARTING progress tracker
121
+ 2024-10-23 15:38:58,945 - INFO - progress=0%
122
+ 2024-10-23 15:38:58,963 - INFO - progress=10%
123
+ 2024-10-23 15:38:58,982 - INFO - progress=20%
124
+ 2024-10-23 15:38:59,000 - INFO - progress=30%
125
+ 2024-10-23 15:38:59,019 - INFO - progress=40%
126
+ 2024-10-23 15:38:59,037 - INFO - progress=50%
127
+ 2024-10-23 15:38:59,055 - INFO - progress=60%
128
+ 2024-10-23 15:38:59,073 - INFO - progress=70%
129
+ 2024-10-23 15:38:59,092 - INFO - progress=80%
130
+ 2024-10-23 15:38:59,110 - INFO - progress=90%
131
+ 2024-10-23 15:38:59,129 - INFO - progress=100%
132
+ 2024-10-23 15:38:59,129 - INFO - FINISHED steps(total)=1001(1001) runtime=184.78ms
133
+
134
+
135
+
136
+
137
+ ![png](README_files/README_4_1.png)
138
+
139
+
140
+
141
+ ## Example - Differentiable Simulation
142
+
143
+ PathSim also includes a rudimentary automatic differentiation framework based on a dual number system with overloaded operators. This makes the system simulation fully differentiable with respect to a predefined set of parameters. For now it only works with the explicit integrators. To demonstrate this lets consider the following linear feedback system.
144
+
145
+ ![png](README_files/linear_feedback_blockdiagram.png)
146
+
147
+
148
+ The source term is a scaled unit step function (scaled by $A$). The parameters we want to differentiate the time domain response by are the feedback term $a$, the initial condition $x_0$ and the amplitude of the source term $A$.
149
+
150
+
151
+ ```python
152
+ from pathsim import Simulation, Connection
153
+ from pathsim.blocks import Source, Integrator, Amplifier, Adder, Scope
154
+
155
+ #AD module
156
+ from pathsim.diff import Parameter
157
+
158
+ #parameters
159
+ A = Parameter(1)
160
+ a = Parameter(-1)
161
+ x0 = Parameter(2)
162
+
163
+ #simulation timestep
164
+ dt = 0.01
165
+
166
+ #step function
167
+ tau = 3
168
+ def s(t):
169
+ return A*int(t>tau)
170
+
171
+ #blocks that define the system
172
+ Src = Source(s)
173
+ Int = Integrator(x0)
174
+ Amp = Amplifier(a)
175
+ Add = Adder()
176
+ Sco = Scope(labels=["step", "response"])
177
+
178
+ blocks = [Src, Int, Amp, Add, Sco]
179
+
180
+ #the connections between the blocks
181
+ connections = [
182
+ Connection(Src, Add[0], Sco[0]),
183
+ Connection(Amp, Add[1]),
184
+ Connection(Add, Int),
185
+ Connection(Int, Amp, Sco[1])
186
+ ]
187
+
188
+ #initialize simulation with the blocks, connections, timestep and logging enabled
189
+ Sim = Simulation(blocks, connections, dt=dt, log=True)
190
+
191
+ #run the simulation for some time
192
+ Sim.run(4*tau)
193
+
194
+ Sco.plot()
195
+ ```
196
+
197
+ 2024-10-23 15:39:02,206 - INFO - LOGGING enabled
198
+ 2024-10-23 15:39:02,207 - INFO - SOLVER SSPRK22 adaptive=False implicit=False
199
+ 2024-10-23 15:39:02,207 - INFO - PATH LENGTH ESTIMATE 2, 'iterations_min' set to 2
200
+ 2024-10-23 15:39:02,208 - INFO - RESET
201
+ 2024-10-23 15:39:02,209 - INFO - RUN duration=12
202
+ 2024-10-23 15:39:02,209 - INFO - STARTING progress tracker
203
+ 2024-10-23 15:39:02,210 - INFO - progress=0%
204
+ 2024-10-23 15:39:02,263 - INFO - progress=10%
205
+ 2024-10-23 15:39:02,316 - INFO - progress=20%
206
+ 2024-10-23 15:39:02,368 - INFO - progress=30%
207
+ 2024-10-23 15:39:02,420 - INFO - progress=40%
208
+ 2024-10-23 15:39:02,471 - INFO - progress=50%
209
+ 2024-10-23 15:39:02,523 - INFO - progress=60%
210
+ 2024-10-23 15:39:02,575 - INFO - progress=70%
211
+ 2024-10-23 15:39:02,627 - INFO - progress=80%
212
+ 2024-10-23 15:39:02,678 - INFO - progress=90%
213
+ 2024-10-23 15:39:02,730 - INFO - progress=100%
214
+ 2024-10-23 15:39:02,731 - INFO - FINISHED steps(total)=1201(1201) runtime=520.65ms
215
+
216
+
217
+
218
+
219
+ ![png](README_files/README_6_1.png)
220
+
221
+
222
+
223
+ Now the recorded data is of type `Parameter` and we can evaluate the automatically computed partial derivatives at each timestep. For example
224
+ $\partial x(t) / \partial a$ the response with respect to the linear feedback parameter.
225
+
226
+
227
+ ```python
228
+ import matplotlib.pyplot as plt
229
+
230
+ #read data from the scope
231
+ time, [step, data] = Sco.read()
232
+
233
+ #evaluate partial derivatives
234
+ dxda = list(map(lambda x: x.d(a), data)) # w.r.t. feedback
235
+ dxdx0 = list(map(lambda x: x.d(x0), data)) # w.r.t. initial condition
236
+ dxdA = list(map(lambda x: x.d(A), data)) # w.r.t. source amplitude
237
+
238
+ fig, ax = plt.subplots(nrows=1, tight_layout=True, figsize=(8, 4), dpi=120)
239
+
240
+ ax.plot(time, dxda, label="$dx/da$")
241
+ ax.plot(time, dxdx0, label="$dx/dx_0$")
242
+ ax.plot(time, dxdA, label="$dx/dA$")
243
+
244
+ ax.set_xlabel("time [s]")
245
+ ax.grid(True)
246
+ ax.legend(fancybox=False);
247
+ ```
248
+
249
+
250
+
251
+ ![png](README_files/README_8_0.png)
252
+
253
+
254
+
255
+ ## More Examples
256
+ There are many examples of dynamical system simulations in the `examples` directory. They cover almost all the blocks currently available in `PathSim` as well as different numerical integrators / solvers to experiment with.
257
+
258
+
259
+ ```python
260
+
261
+ ```
@@ -0,0 +1,244 @@
1
+ # PathSim: A Time-Domain System Simulation Framework
2
+
3
+
4
+ ## Overview
5
+
6
+ PathSim is a minimalistic and flexible block-based time-domain system simulation framework in Python with basic automatic differentiation capabilities. It provides a modular and intuitive approach to modeling and simulating complex interconnected dynamical systems. It is similar to Matlab Simulink in spirit but works very differently under the hood.
7
+
8
+ Key features of PathSim include:
9
+
10
+ - Decentralized architecture where each dynamical block has their own numerical integration engine.
11
+ - The system is solved directly on the computational graph instead of compiling a unified differential algebraic system.
12
+ - This has some advantages such as hot-swappable blocks during simulation and reading simulation results directly from the scopes.
13
+ - The block execution is decoupled from the data transfer, which enables parallelization (future) and linear computational complexity scaling for sparsely connected systems.
14
+ - Support for MIMO (Multiple Input, Multiple Output) blocks, enabling the creation of complex interconnected system topologies.
15
+ - Fixed-point iteration approach with path length estimation to efficiently resolve algebraic loops.
16
+ - Wide range of numerical solvers, including implicit and explicit multi-stage, and adaptive Runge-Kutta methods such as `RKDP54` or `ESDIRK54`.
17
+ - Modular and hierarchical modeling with (nested) subsystems.
18
+ - Automatic differentiation for differentiable system simulations.
19
+ - Library of pre-defined blocks, including mathematical operations, integrators, delays, transfer functions, and more.
20
+ - Easy extensibility, allowing users to define custom blocks by subclassing the base `Block` class and implementing just a handful of methods.
21
+
22
+ ## Installation
23
+
24
+ The latest release version of pathsim is installable via pip:
25
+
26
+ ```console
27
+ $ pip install pathsim
28
+ ```
29
+
30
+ ## Example - Harmonic Oscillator
31
+
32
+ Here's an example that demonstrates how to create a basic simulation. The main components of the package are:
33
+
34
+ - `Simulation`: The main class that handles the blocks, connections, and the simulation loop.
35
+ - `Connection`: The class that defines the connections between blocks.
36
+ - Various block classes from the `blocks` module, such as `Integrator`, `Amplifier`, `Adder`, `Scope`, etc.
37
+
38
+ In this example, we create a simulation of the harmonic oscillator (a spring mass damper 2nd order system) initial value problem. The ODE that defines it is give by
39
+
40
+ $$
41
+ \ddot{x} + \frac{c}{m} \dot{x} + \frac{k}{m} x = 0
42
+ $$
43
+
44
+ where $c$ is the damping, $k$ the spring constant and $m$ the mass. And initial conditions $x_0$ and $v_0$ for position and velocity.
45
+
46
+ The ODE above can be translated to a block diagram using integrators, amplifiers and adders in the following way:
47
+
48
+ ![png](README_files/harmonic_oscillator_blockdiagram.png)
49
+
50
+ The topology of the block diagram above can be directly defined as blocks and connections in the `PathSim` framework. First we initialize the blocks needed to represent the dynamical systems with their respective arguments such as initial conditions and gain values, then the blocks are connected using `Connection` objects, forming two feedback loops. The `Simulation` instance manages the blocks and connections and advances the system in time with the timestep (`dt`). The `log` flag for logging the simulation progress is also set. Finally, we run the simulation for some number of seconds and plot the results using the `plot()` method of the scope block.
51
+
52
+
53
+
54
+ ```python
55
+ from pathsim import Simulation
56
+ from pathsim import Connection
57
+ from pathsim.blocks import Integrator, Amplifier, Adder, Scope
58
+ from pathsim.solvers import SSPRK22 # 2nd order fixed timestep, this is also the default
59
+
60
+ #initial position and velocity
61
+ x0, v0 = 2, 5
62
+
63
+ #parameters (mass, damping, spring constant)
64
+ m, c, k = 0.8, 0.2, 1.5
65
+
66
+ # Create blocks
67
+ I1 = Integrator(v0) # integrator for velocity
68
+ I2 = Integrator(x0) # integrator for position
69
+ A1 = Amplifier(-c/m)
70
+ A2 = Amplifier(-k/m)
71
+ P1 = Adder()
72
+ Sc = Scope(labels=["v(t)", "x(t)"])
73
+
74
+ blocks = [I1, I2, A1, A2, P1, Sc]
75
+
76
+ # Create connections
77
+ connections = [
78
+ Connection(I1, I2, A1, Sc), # one to many connection
79
+ Connection(I2, A2, Sc[1]),
80
+ Connection(A1, P1), # default connection to port 0
81
+ Connection(A2, P1[1]), # specific connection to port 1
82
+ Connection(P1, I1)
83
+ ]
84
+
85
+ # Create a simulation instance from the blocks and connections
86
+ Sim = Simulation(blocks, connections, dt=0.05, log=True, Solver=SSPRK22)
87
+
88
+ # Run the simulation for 50 seconds
89
+ Sim.run(duration=50.0)
90
+
91
+ # Plot the results directly from the scope
92
+ Sc.plot()
93
+
94
+ # Read the results from the scope for further processing
95
+ time, data = Sc.read()
96
+ ```
97
+
98
+ 2024-10-23 15:38:58,943 - INFO - LOGGING enabled
99
+ 2024-10-23 15:38:58,943 - INFO - SOLVER SSPRK22 adaptive=False implicit=False
100
+ 2024-10-23 15:38:58,944 - INFO - PATH LENGTH ESTIMATE 2, 'iterations_min' set to 2
101
+ 2024-10-23 15:38:58,944 - INFO - RESET
102
+ 2024-10-23 15:38:58,944 - INFO - RUN duration=50.0
103
+ 2024-10-23 15:38:58,945 - INFO - STARTING progress tracker
104
+ 2024-10-23 15:38:58,945 - INFO - progress=0%
105
+ 2024-10-23 15:38:58,963 - INFO - progress=10%
106
+ 2024-10-23 15:38:58,982 - INFO - progress=20%
107
+ 2024-10-23 15:38:59,000 - INFO - progress=30%
108
+ 2024-10-23 15:38:59,019 - INFO - progress=40%
109
+ 2024-10-23 15:38:59,037 - INFO - progress=50%
110
+ 2024-10-23 15:38:59,055 - INFO - progress=60%
111
+ 2024-10-23 15:38:59,073 - INFO - progress=70%
112
+ 2024-10-23 15:38:59,092 - INFO - progress=80%
113
+ 2024-10-23 15:38:59,110 - INFO - progress=90%
114
+ 2024-10-23 15:38:59,129 - INFO - progress=100%
115
+ 2024-10-23 15:38:59,129 - INFO - FINISHED steps(total)=1001(1001) runtime=184.78ms
116
+
117
+
118
+
119
+
120
+ ![png](README_files/README_4_1.png)
121
+
122
+
123
+
124
+ ## Example - Differentiable Simulation
125
+
126
+ PathSim also includes a rudimentary automatic differentiation framework based on a dual number system with overloaded operators. This makes the system simulation fully differentiable with respect to a predefined set of parameters. For now it only works with the explicit integrators. To demonstrate this lets consider the following linear feedback system.
127
+
128
+ ![png](README_files/linear_feedback_blockdiagram.png)
129
+
130
+
131
+ The source term is a scaled unit step function (scaled by $A$). The parameters we want to differentiate the time domain response by are the feedback term $a$, the initial condition $x_0$ and the amplitude of the source term $A$.
132
+
133
+
134
+ ```python
135
+ from pathsim import Simulation, Connection
136
+ from pathsim.blocks import Source, Integrator, Amplifier, Adder, Scope
137
+
138
+ #AD module
139
+ from pathsim.diff import Parameter
140
+
141
+ #parameters
142
+ A = Parameter(1)
143
+ a = Parameter(-1)
144
+ x0 = Parameter(2)
145
+
146
+ #simulation timestep
147
+ dt = 0.01
148
+
149
+ #step function
150
+ tau = 3
151
+ def s(t):
152
+ return A*int(t>tau)
153
+
154
+ #blocks that define the system
155
+ Src = Source(s)
156
+ Int = Integrator(x0)
157
+ Amp = Amplifier(a)
158
+ Add = Adder()
159
+ Sco = Scope(labels=["step", "response"])
160
+
161
+ blocks = [Src, Int, Amp, Add, Sco]
162
+
163
+ #the connections between the blocks
164
+ connections = [
165
+ Connection(Src, Add[0], Sco[0]),
166
+ Connection(Amp, Add[1]),
167
+ Connection(Add, Int),
168
+ Connection(Int, Amp, Sco[1])
169
+ ]
170
+
171
+ #initialize simulation with the blocks, connections, timestep and logging enabled
172
+ Sim = Simulation(blocks, connections, dt=dt, log=True)
173
+
174
+ #run the simulation for some time
175
+ Sim.run(4*tau)
176
+
177
+ Sco.plot()
178
+ ```
179
+
180
+ 2024-10-23 15:39:02,206 - INFO - LOGGING enabled
181
+ 2024-10-23 15:39:02,207 - INFO - SOLVER SSPRK22 adaptive=False implicit=False
182
+ 2024-10-23 15:39:02,207 - INFO - PATH LENGTH ESTIMATE 2, 'iterations_min' set to 2
183
+ 2024-10-23 15:39:02,208 - INFO - RESET
184
+ 2024-10-23 15:39:02,209 - INFO - RUN duration=12
185
+ 2024-10-23 15:39:02,209 - INFO - STARTING progress tracker
186
+ 2024-10-23 15:39:02,210 - INFO - progress=0%
187
+ 2024-10-23 15:39:02,263 - INFO - progress=10%
188
+ 2024-10-23 15:39:02,316 - INFO - progress=20%
189
+ 2024-10-23 15:39:02,368 - INFO - progress=30%
190
+ 2024-10-23 15:39:02,420 - INFO - progress=40%
191
+ 2024-10-23 15:39:02,471 - INFO - progress=50%
192
+ 2024-10-23 15:39:02,523 - INFO - progress=60%
193
+ 2024-10-23 15:39:02,575 - INFO - progress=70%
194
+ 2024-10-23 15:39:02,627 - INFO - progress=80%
195
+ 2024-10-23 15:39:02,678 - INFO - progress=90%
196
+ 2024-10-23 15:39:02,730 - INFO - progress=100%
197
+ 2024-10-23 15:39:02,731 - INFO - FINISHED steps(total)=1201(1201) runtime=520.65ms
198
+
199
+
200
+
201
+
202
+ ![png](README_files/README_6_1.png)
203
+
204
+
205
+
206
+ Now the recorded data is of type `Parameter` and we can evaluate the automatically computed partial derivatives at each timestep. For example
207
+ $\partial x(t) / \partial a$ the response with respect to the linear feedback parameter.
208
+
209
+
210
+ ```python
211
+ import matplotlib.pyplot as plt
212
+
213
+ #read data from the scope
214
+ time, [step, data] = Sco.read()
215
+
216
+ #evaluate partial derivatives
217
+ dxda = list(map(lambda x: x.d(a), data)) # w.r.t. feedback
218
+ dxdx0 = list(map(lambda x: x.d(x0), data)) # w.r.t. initial condition
219
+ dxdA = list(map(lambda x: x.d(A), data)) # w.r.t. source amplitude
220
+
221
+ fig, ax = plt.subplots(nrows=1, tight_layout=True, figsize=(8, 4), dpi=120)
222
+
223
+ ax.plot(time, dxda, label="$dx/da$")
224
+ ax.plot(time, dxdx0, label="$dx/dx_0$")
225
+ ax.plot(time, dxdA, label="$dx/dA$")
226
+
227
+ ax.set_xlabel("time [s]")
228
+ ax.grid(True)
229
+ ax.legend(fancybox=False);
230
+ ```
231
+
232
+
233
+
234
+ ![png](README_files/README_8_0.png)
235
+
236
+
237
+
238
+ ## More Examples
239
+ There are many examples of dynamical system simulations in the `examples` directory. They cover almost all the blocks currently available in `PathSim` as well as different numerical integrators / solvers to experiment with.
240
+
241
+
242
+ ```python
243
+
244
+ ```
@@ -1,3 +1,3 @@
1
- from .simulation import Simulation
2
- from .connection import Connection
1
+ from .simulation import Simulation
2
+ from .connection import Connection
3
3
  from .subsystem import Subsystem, Interface
@@ -1,14 +1,14 @@
1
- from .differentiator import *
2
- from .integrator import *
3
- from .amplifier import *
4
- from .function import *
5
- from .spectrum import *
6
- from .sources import *
7
- from .multiplier import *
8
- from .adder import*
9
- from .scope import *
10
- from .delay import *
11
- from .lti import *
12
- from .ode import *
13
- from .rng import *
14
-
1
+ from .differentiator import *
2
+ from .integrator import *
3
+ from .amplifier import *
4
+ from .function import *
5
+ from .spectrum import *
6
+ from .sources import *
7
+ from .multiplier import *
8
+ from .adder import*
9
+ from .scope import *
10
+ from .delay import *
11
+ from .lti import *
12
+ from .ode import *
13
+ from .rng import *
14
+