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.
- elecsolver-2.0.1/.github/workflows/publish-pypi.yml +72 -0
- elecsolver-2.0.1/.github/workflows/test.yml +37 -0
- elecsolver-2.0.1/.gitignore +4 -0
- elecsolver-2.0.1/LICENSE.txt +21 -0
- elecsolver-2.0.1/PKG-INFO +404 -0
- elecsolver-2.0.1/README.md +379 -0
- elecsolver-2.0.1/env.yml +16 -0
- elecsolver-2.0.1/img/hydraulic.png +0 -0
- elecsolver-2.0.1/img/intensities_res.png +0 -0
- elecsolver-2.0.1/img/schema.png +0 -0
- elecsolver-2.0.1/img/schema2.png +0 -0
- elecsolver-2.0.1/img/schema3.png +0 -0
- elecsolver-2.0.1/pyproject.toml +50 -0
- elecsolver-2.0.1/setup.cfg +4 -0
- elecsolver-2.0.1/src/ElecSolver/FrequencySystemBuilder.py +323 -0
- elecsolver-2.0.1/src/ElecSolver/NetlistDumper.py +82 -0
- elecsolver-2.0.1/src/ElecSolver/NetlistGenerator.py +675 -0
- elecsolver-2.0.1/src/ElecSolver/NetlistParser.py +291 -0
- elecsolver-2.0.1/src/ElecSolver/TemporalSystemBuilder.py +490 -0
- elecsolver-2.0.1/src/ElecSolver/__init__.py +6 -0
- elecsolver-2.0.1/src/ElecSolver/utils.py +153 -0
- elecsolver-2.0.1/src/ElecSolver.egg-info/PKG-INFO +404 -0
- elecsolver-2.0.1/src/ElecSolver.egg-info/SOURCES.txt +29 -0
- elecsolver-2.0.1/src/ElecSolver.egg-info/dependency_links.txt +1 -0
- elecsolver-2.0.1/src/ElecSolver.egg-info/requires.txt +7 -0
- elecsolver-2.0.1/src/ElecSolver.egg-info/top_level.txt +1 -0
- elecsolver-2.0.1/tests/test_netlist_parser.py +163 -0
- elecsolver-2.0.1/tests/test_sysbuilder.py +156 -0
- elecsolver-2.0.1/tests/test_temporal_netlist_dumper.py +54 -0
- elecsolver-2.0.1/tests/test_temporal_system.py +420 -0
- 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,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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+
|