ElecSolver 2.0.1__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 (31) hide show
  1. elecsolver-2.0.1/.github/workflows/publish-pypi.yml +72 -0
  2. elecsolver-2.0.1/.github/workflows/test.yml +37 -0
  3. elecsolver-2.0.1/.gitignore +4 -0
  4. elecsolver-2.0.1/LICENSE.txt +21 -0
  5. elecsolver-2.0.1/PKG-INFO +404 -0
  6. elecsolver-2.0.1/README.md +379 -0
  7. elecsolver-2.0.1/env.yml +16 -0
  8. elecsolver-2.0.1/img/hydraulic.png +0 -0
  9. elecsolver-2.0.1/img/intensities_res.png +0 -0
  10. elecsolver-2.0.1/img/schema.png +0 -0
  11. elecsolver-2.0.1/img/schema2.png +0 -0
  12. elecsolver-2.0.1/img/schema3.png +0 -0
  13. elecsolver-2.0.1/pyproject.toml +50 -0
  14. elecsolver-2.0.1/setup.cfg +4 -0
  15. elecsolver-2.0.1/src/ElecSolver/FrequencySystemBuilder.py +323 -0
  16. elecsolver-2.0.1/src/ElecSolver/NetlistDumper.py +82 -0
  17. elecsolver-2.0.1/src/ElecSolver/NetlistGenerator.py +675 -0
  18. elecsolver-2.0.1/src/ElecSolver/NetlistParser.py +291 -0
  19. elecsolver-2.0.1/src/ElecSolver/TemporalSystemBuilder.py +490 -0
  20. elecsolver-2.0.1/src/ElecSolver/__init__.py +6 -0
  21. elecsolver-2.0.1/src/ElecSolver/utils.py +153 -0
  22. elecsolver-2.0.1/src/ElecSolver.egg-info/PKG-INFO +404 -0
  23. elecsolver-2.0.1/src/ElecSolver.egg-info/SOURCES.txt +29 -0
  24. elecsolver-2.0.1/src/ElecSolver.egg-info/dependency_links.txt +1 -0
  25. elecsolver-2.0.1/src/ElecSolver.egg-info/requires.txt +7 -0
  26. elecsolver-2.0.1/src/ElecSolver.egg-info/top_level.txt +1 -0
  27. elecsolver-2.0.1/tests/test_netlist_parser.py +163 -0
  28. elecsolver-2.0.1/tests/test_sysbuilder.py +156 -0
  29. elecsolver-2.0.1/tests/test_temporal_netlist_dumper.py +54 -0
  30. elecsolver-2.0.1/tests/test_temporal_system.py +420 -0
  31. elecsolver-2.0.1/tests/test_utils.py +7 -0
@@ -0,0 +1,72 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build distributions
10
+ permissions:
11
+ contents: read
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+
17
+ - name: Install dependencies
18
+ run: |
19
+ sudo apt-get update
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v4
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - name: Install build tools
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install build
30
+
31
+ - name: Check version consistency
32
+ run: |
33
+ pip install .
34
+ RELEASE_VER=${GITHUB_REF#refs/*/}
35
+ PACKAGE_VER="$(python -c 'import ElecSolver; print(ElecSolver.__version__)')"
36
+ if [ "$RELEASE_VER" != "$PACKAGE_VER" ]; then
37
+ echo "Package version ($PACKAGE_VER) doesn't match tag ($RELEASE_VER)"
38
+ exit 1
39
+ fi
40
+
41
+ - name: Build package
42
+ run: |
43
+ python -m build --sdist --wheel --outdir dist/
44
+
45
+ - uses: actions/upload-artifact@v4
46
+ with:
47
+ name: dists-3.12
48
+ path: dist/*
49
+
50
+ publish:
51
+ name: Publish to PyPI
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ environment: pypi
55
+ permissions:
56
+ id-token: write
57
+ contents: read
58
+
59
+ steps:
60
+ - uses: actions/checkout@v4
61
+
62
+ - name: Download all distribution artifacts
63
+ uses: actions/download-artifact@v4
64
+ with:
65
+ pattern: dists-*
66
+ path: dist
67
+ merge-multiple: true
68
+
69
+ - name: Publish to PyPI
70
+ uses: pypa/gh-action-pypi-publish@release/v1
71
+ with:
72
+ packages-dir: dist
@@ -0,0 +1,37 @@
1
+ name: Test Python Program
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ defaults:
12
+ run:
13
+ shell: bash -l {0}
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout code
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Set up Miniconda
21
+ uses: conda-incubator/setup-miniconda@v3
22
+ with:
23
+ activate-environment: ElecSolver
24
+ environment-file: env.yml
25
+ auto-activate-base: false
26
+ conda-remove-defaults: "true"
27
+
28
+ - name: Verify environment
29
+ run: |
30
+ conda info
31
+ conda list
32
+ - name: Set PythonPath
33
+ run: |
34
+ echo "PYTHONPATH=${{ github.workspace }}/src" >> $GITHUB_ENV
35
+ - name: Run tests
36
+ run: |
37
+ pytest ${{ github.workspace }}/tests/
@@ -0,0 +1,4 @@
1
+ **__pycache__**
2
+ tests/*.png
3
+ dist
4
+ src/ElecSolver.egg-info
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 William PIAT
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
+ SOFTWARE.
@@ -0,0 +1,404 @@
1
+ Metadata-Version: 2.4
2
+ Name: ElecSolver
3
+ Version: 2.0.1
4
+ Summary: Formalizes electric systems as linear problems for temporal and frequency-domain studies.
5
+ Author-email: William Piat <william.piat3@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/williampiat3/ElecSolver
8
+ Platform: Linux
9
+ Platform: Unix
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: POSIX
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Scientific/Engineering :: Physics
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: numpy
19
+ Requires-Dist: scipy
20
+ Requires-Dist: networkx
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: python-mumps; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ # ElecSolver
27
+
28
+ ## Overview
29
+
30
+ **ElecSolver** formalizes electric systems as linear problems, suitable for both **temporal** and **frequency-domain** studies.
31
+ It focuses on constructing the linear system representation, leaving the actual numerical resolution to the user.
32
+
33
+ This repository is **not** a general-purpose electrical system solver. Instead, it acts as a **bridge** between:
34
+
35
+ - The graph-based description of an electric network
36
+ - The corresponding sparse linear system to solve
37
+
38
+
39
+ Its main goal is to provide a friendly Python interface for simulating analog electric systems. While suitable for small circuit simulations, its strength lies in its scalability: it is able to build linear systems with millions of nodes and components.
40
+
41
+
42
+ > [!IMPORTANT]
43
+ > ElecSolver has been designed with the following specifications in mind:
44
+ > - The time needed for building the linear system must be negligible compared to the time needed for solving it.
45
+ > - Handle natively inductive mutuals and resistive mutuals
46
+ > - Handle as many coupled electric systems that one wants.
47
+ > - Deal with lonely nodes and lonely edges in the electric graph: the problem can be well posed and thus solved.
48
+
49
+
50
+
51
+ > [!NOTE]
52
+ > Non-linear components are not supported. You must manage event detection and system updates yourself.
53
+
54
+
55
+ ## Table of content
56
+
57
+ - [ElecSolver](#elecsolver)
58
+ - [Overview](#overview)
59
+ - [Table of content](#table-of-content)
60
+ - [How to install](#how-to-install)
61
+ - [Components](#components)
62
+ - [FrequencySystemBuilder](#frequencysystembuilder)
63
+ - [Features](#features)
64
+ - [Example](#example)
65
+ - [Adding a Parallel Resistance](#adding-a-parallel-resistance)
66
+ - [TemporalSystemBuilder](#temporalsystembuilder)
67
+ - [Features](#features-1)
68
+ - [Example](#example-1)
69
+ - [Solver suggestions](#solver-suggestions)
70
+ - [Extra uses: Hydraulic or Thermal system modeling](#extra-uses-hydraulic-or-thermal-system-modeling)
71
+ - [Netlist import feature](#netlist-import-feature)
72
+
73
+ ## How to install
74
+ For now this package is distributed on pypi and can be installed using pip and conda/mamba
75
+ ```
76
+ pip install ElecSolver
77
+ ```
78
+ or
79
+ ```
80
+ conda install elecsolver
81
+ ```
82
+ For solving the linear systems we advise using MUMPS through `python-mumps` available for linux, macOS and Windows. It can be installed via conda:
83
+ ```
84
+ conda install python-mumps
85
+ ```
86
+
87
+
88
+
89
+ ## Components
90
+
91
+ ### FrequencySystemBuilder
92
+
93
+ This class handles **frequency-domain** analysis of linear electric systems.
94
+
95
+ #### Features
96
+
97
+ - Supports tension and intensity sources
98
+ - Models inductive and resistive mutuals
99
+ - Detects and couples multiple subsystems
100
+ - Accepts arbitrary complex impedances and mutuals
101
+ - Constructs sparse linear systems (COO format)
102
+
103
+
104
+ > [!TIP]
105
+ > Some solvers do not support complex-valued systems. Use the utility function `cast_complex_system_in_real_system` in `utils.py` to convert an `n`-dimensional complex system into a `2n`-dimensional real system.
106
+
107
+ #### Example
108
+
109
+ We would like to study the following system:
110
+ ![Multiple system](img/schema.png)
111
+
112
+ this can simply be defined in the following manner (We took R=1, L=1 and M=2):
113
+ ```python
114
+ import numpy as np
115
+ from scipy.sparse.linalg import spsolve
116
+ from ElecSolver import FrequencySystemBuilder
117
+
118
+
119
+ # Complex and sparse impedance matrix
120
+ # notice coil impedence between points 0 and 2, and coil impedence between 3 and 4
121
+ impedence_coords = np.array([[0,0,1,3],[1,2,2,4]], dtype=int)
122
+ impedence_data = np.array([1, 1j, 1, 1j], dtype=complex)
123
+
124
+ # Mutual inductance or coupling
125
+ # The indexes here are the impedence indexes in impedence_data
126
+ # The coupling is inductive
127
+ mutuals_coords = np.array([[1],[3]], dtype=int)
128
+ mutuals_data = np.array([2.j], dtype=complex)
129
+
130
+ electric_sys = FrequencySystemBuilder(
131
+ impedence_coords,
132
+ impedence_data,
133
+ mutuals_coords,
134
+ mutuals_data
135
+ )
136
+
137
+ # Add source (Current source here)
138
+ electric_sys.add_current_source(intensity=10, input_node=2, output_node=0)
139
+ # Set ground
140
+ # 2 values because one for each subsystem
141
+ electric_sys.set_ground(0, 3)
142
+ # Building system
143
+ electric_sys.build_system()
144
+
145
+
146
+ # Get and solve the system
147
+ sys, b = electric_sys.get_system()
148
+ sol = spsolve(sys.tocsr(), b)
149
+ frequencial_response = electric_sys.build_intensity_and_voltage_from_vector(sol)
150
+
151
+ ## We see a tension appearing on the lonely coil (between node 3 and 4)
152
+ print(frequencial_response.potentials[3]-frequencial_response.potentials[4])
153
+ ```
154
+ #### Adding a Parallel Resistance
155
+ We want to add components in parallel with existing components for instance inserting a resistor in parallel with the first inductance (between nodes 0 and 2)
156
+ ![Parallel system](img/schema3.png)
157
+
158
+ In python, simply add the resistance to the list of impedence in the very first lines of the script:
159
+
160
+ ```python
161
+ import numpy as np
162
+ from scipy.sparse.linalg import spsolve
163
+ from ElecSolver import FrequencySystemBuilder
164
+
165
+
166
+ # We add an additionnal resistance between 0 and 2
167
+ impedence_coords = np.array([[0,0,1,3,0],[1,2,2,4,2]], dtype=int)
168
+ impedence_data = np.array([1, 1j,1, 1j,1], dtype=complex)
169
+
170
+ # No need to change the couplings since indexes of the coils did not change
171
+ mutuals_coords = np.array([[1],[3]], dtype=int)
172
+ mutuals_data = np.array([2.j], dtype=complex)
173
+
174
+ ```
175
+
176
+
177
+ ### TemporalSystemBuilder
178
+
179
+ This class models **time-dependent** systems using resistors, capacitors, coils, and mutuals.
180
+
181
+ #### Features
182
+
183
+ - Supports tension and intensity sources
184
+ - Models inductive and resistive mutuals
185
+ - Detects and couples multiple subsystems
186
+ - Accepts 3 dipole types: resistances, capacities and coils
187
+ - Constructs sparse linear systems (COO format)
188
+
189
+ #### Example
190
+
191
+
192
+ We would like to study the following system:
193
+ ![Temporal system](img/schema2.png)
194
+
195
+ with R=1, L=0.1, C=2 this gives:
196
+
197
+ ```python
198
+ import numpy as np
199
+ from scipy.sparse.linalg import spsolve
200
+ from ElecSolver import TemporalSystemBuilder
201
+
202
+ ## Defining resistances
203
+ res_coords = np.array([[0,2],[1,3]],dtype=int)
204
+ res_data = np.array([1,1],dtype=float)
205
+ ## Defining coils
206
+ coil_coords = np.array([[1,0],[3,2]],dtype=int)
207
+ coil_data = np.array([0.1,0.1],dtype=float)
208
+ ## Defining capacities
209
+ capa_coords = np.array([[1,3],[2,0]],dtype=int)
210
+ capa_data = np.array([2,2],dtype=float)
211
+
212
+ ## Defining empty mutuals here
213
+ mutuals_coords=np.array([[],[]],dtype=int)
214
+ mutuals_data = np.array([],dtype=float)
215
+
216
+
217
+ res_mutuals_coords=np.array([[],[]],dtype=int)
218
+ res_mutuals_data = np.array([],dtype=float)
219
+
220
+ ## initializing system
221
+ elec_sys = TemporalSystemBuilder(coil_coords,coil_data,res_coords,res_data,capa_coords,capa_data,mutuals_coords,mutuals_data,res_mutuals_coords,res_mutuals_data)
222
+ ## Add source
223
+ elec_sys.add_current_source(10,1,0)
224
+ ## Setting ground at point 0
225
+ elec_sys.set_ground(0)
226
+ ## Build second member
227
+ elec_sys.build_system()
228
+
229
+ # getting initial condition system
230
+ S_i,b = elec_sys.get_init_system()
231
+ # initial condition
232
+ sol = spsolve(S_i.tocsr(),b)
233
+ # get system (S1 is real part, S2 derivative part)
234
+ S1,S2,rhs = elec_sys.get_system()
235
+
236
+ ## Solving using euler implicit scheme
237
+ dt=0.08
238
+ vals_res1 = []
239
+ vals_res2 = []
240
+ for i in range(50):
241
+ temporal_response = elec_sys.build_intensity_and_voltage_from_vector(sol)
242
+ vals_res1.append(temporal_response.intensities_res[1])
243
+ vals_res2.append(temporal_response.intensities_res[0])
244
+ ## implicit euler time iterations
245
+ sol = spsolve(S2+dt*S1,b*dt+S2@sol)
246
+ import matplotlib.pyplot as plt
247
+ plt.xlabel("Time")
248
+ plt.ylabel("Intensity")
249
+ plt.plot(vals_res1,label="intensity res 1")
250
+ plt.plot(vals_res2,label="intensity res 2")
251
+ plt.legend()
252
+ plt.savefig("intensities_res.png")
253
+ ```
254
+
255
+ This outputs the following graph that displays the intensity passing through the resistances
256
+ ![Temporal system](img/intensities_res.png)
257
+
258
+
259
+ ## Solver suggestions
260
+
261
+ - For **small or moderately sized systems**, the built-in `scipy.sparse.linalg.spsolve` is effective.
262
+ - For **large-scale temporal problems**, consider using **MUMPS** (via `python-mumps`).
263
+ MUMPS is more efficient when only the second member (`b`) changes during time-stepping.
264
+
265
+ > [!TIP]
266
+ > See example `tests.test_temporal_system` in the tests on how to use `python-mumps` for solving the resulting system efficiently.
267
+
268
+
269
+ ## Extra uses: Hydraulic or Thermal system modeling
270
+
271
+ This repository can be used as is in order to model the mass flow or thermal flux in respectively Hydraulic networks or Thermal networks where a difference of pressure or a difference of temperature can be assimilated to a tension source. Since electric potentials are always computed relatively to the ground node you might need to rescale the resulting potentials:
272
+
273
+ We are considering the following hydraulic problem:
274
+
275
+ ![Hydraulic system](img/hydraulic.png)
276
+
277
+ Taking R=1 this gives
278
+
279
+ ```python
280
+ import numpy as np
281
+ from scipy.sparse.linalg import spsolve
282
+ from ElecSolver import TemporalSystemBuilder
283
+
284
+ ## Defining resistances
285
+ R = 1
286
+ res_coords = np.array([[0,2,1,0,1,3],[1,3,3,2,2,0]],dtype=int)
287
+ res_data = R*np.array([2,3,1,1,1,1],dtype=float)
288
+
289
+ ## Here we are not using coils, capacities or mutuals we defined them as empty
290
+ ## Defining 0 coil
291
+ coil_coords = np.array([[],[]],dtype=int)
292
+ coil_data = np.array([],dtype=float)
293
+ ## Defining 0 capacity
294
+ capa_coords = np.array([[],[]],dtype=int)
295
+ capa_data = np.array([],dtype=float)
296
+
297
+ ## Defining no mutual
298
+ mutuals_coords=np.array([[],[]],dtype=int)
299
+ mutuals_data = np.array([],dtype=float)
300
+
301
+
302
+ res_mutuals_coords=np.array([[],[]],dtype=int)
303
+ res_mutuals_data = np.array([],dtype=float)
304
+
305
+ ## initializing system
306
+ hydraulic_sys = TemporalSystemBuilder(coil_coords,coil_data,res_coords,res_data,capa_coords,capa_data,mutuals_coords,mutuals_data,res_mutuals_coords,res_mutuals_data)
307
+ ## Enforcing a pressure delta of 10 Pa
308
+ hydraulic_sys.add_voltage_source(10,1,0)
309
+ ## Setting ground at point 0
310
+ hydraulic_sys.set_ground(0)
311
+ ## Build second member
312
+ hydraulic_sys.build_system()
313
+
314
+ # get system (S1 is real part, S2 derivative part)
315
+ # the problem is only resitive thus S2 =0
316
+ S1,S2,rhs = hydraulic_sys.get_system()
317
+
318
+ sol = spsolve(S1.tocsr(),rhs)
319
+ solution = hydraulic_sys.build_intensity_and_voltage_from_vector(sol)
320
+ # After you computed the solution of the system
321
+
322
+ pressure_input=10000
323
+ pressure_node=0
324
+ # Rescaling the potential to the new reference
325
+ potentials = solution.potentials - solution.potentials[pressure_node] + pressure_input
326
+ print("Pressures in the system:", potentials)
327
+ ## get the flux passing through the system
328
+ print("Debit through the system",solution.intensities_sources[0])
329
+ ```
330
+ ## Netlist import feature
331
+
332
+ A new class, named NetlistParser allows importing passive netlist and building a TemporalSystem instance.
333
+ Solving the system can then be performed like any other example above.
334
+
335
+ ```python
336
+ """
337
+ *test netlist for python solver square.net
338
+ Iin 0 1 PWL(0 0 0.000000001 10)
339
+ L0 1 3 0.1
340
+ L1 2 0 0.1
341
+ R1 1 0 1
342
+ R2 2 3 1
343
+ c2 1 2 2
344
+ c3 0 3 2
345
+ .tran 0 4 0 0.08
346
+ .end
347
+ """
348
+ from scipy.sparse.linalg import spsolve
349
+ import matplotlib.pyplot as plt
350
+ from ElecSolver import NetlistParser
351
+
352
+ parser = NetlistParser("square.net")
353
+ parser.map_netlist()
354
+ node_zero = parser.node_map["0"]
355
+ node_one = parser.node_map["1"]
356
+
357
+ elec_sys=parser.generate_temporal_system()
358
+ # Set 10 A injection entering in node 1 and exiting in node 0
359
+ elec_sys.add_current_source(10, node_one, node_zero)
360
+ ## Setting ground at point 0
361
+ elec_sys.set_ground(node_zero)
362
+ ## Build second member
363
+ elec_sys.build_system()
364
+ # getting initial condition system
365
+ S_i,b = elec_sys.get_init_system()
366
+ # initial condition
367
+ sol = spsolve(S_i.tocsr(),b)
368
+ # get system (S1 is real part, S2 derivative part)
369
+ S1,S2,rhs = elec_sys.get_system()
370
+
371
+ ## Solving using euler implicit scheme
372
+ dt=0.08
373
+ steps = 50
374
+ vals_res1 = []
375
+ vals_res2 = []
376
+ vals_L1 = []
377
+ voltage_src = []
378
+
379
+ R1_index = list(parser.resistors.keys()).index("R1")
380
+ R2_index = list(parser.resistors.keys()).index("R2")
381
+ L1_index = list(parser.inductors.keys()).index("L1")
382
+
383
+
384
+ for i in range(steps):
385
+ temporal_response = elec_sys.build_intensity_and_voltage_from_vector(sol)
386
+ vals_res1.append(temporal_response.intensities_res[R1_index])
387
+ vals_res2.append(temporal_response.intensities_res[R2_index])
388
+ vals_L1.append(temporal_response.intensities_coil[L1_index])
389
+ voltage_src.append(temporal_response.potentials[node_one]-temporal_response.potentials[node_zero])
390
+ ## implicit euler time iterations
391
+ sol = spsolve(S2+dt*S1,b*dt+S2@sol)
392
+
393
+
394
+ plt.xlabel("Time")
395
+ plt.ylabel("Intensity")
396
+ plt.plot(arange(steps, dtype=float)*dt, vals_res1, label="intensity res 1")
397
+ plt.plot(arange(steps, dtype=float)*dt, vals_res2, label="intensity res 2")
398
+ plt.plot(arange(steps, dtype=float)*dt, vals_L1, label="intensity L1")
399
+ plt.plot(arange(steps, dtype=float)*dt, voltage_src, label="V(1-0)") # equal to I(R1)
400
+
401
+ plt.legend()
402
+ plt.savefig("intensities_res.png")
403
+ ```
404
+