Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/doc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Documentation
on:
push:
branches:
- master
- main
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/configure-pages@v5
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: pip install zensical
- run: zensical build --clean
- uses: actions/upload-pages-artifact@v4
with:
path: site
- uses: actions/deploy-pages@v4
id: deployment
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ For now this package is distributed on pypi and can be installed using pip and c
```
pip install ElecSolver
```
or
or
```
conda install elecsolver
```
Expand Down Expand Up @@ -82,7 +82,7 @@ This class handles **frequency-domain** analysis of linear electric systems.
#### Example

We would like to study the following system:
![Multiple system](img/schema.png)
![Multiple system](docs/img/schema.png)

this can simply be defined in the following manner (We took R=1, L=1 and M=2):
```python
Expand Down Expand Up @@ -128,7 +128,7 @@ print(frequencial_response.potentials[3]-frequencial_response.potentials[4])
```
#### Adding a Parallel Resistance
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)
![Parallel system](img/schema3.png)
![Parallel system](docs/img/schema3.png)

In python, simply add the resistance to the list of impedence in the very first lines of the script:

Expand Down Expand Up @@ -165,7 +165,7 @@ This class models **time-dependent** systems using resistors, capacitors, coils,


We would like to study the following system:
![Temporal system](img/schema2.png)
![Temporal system](docs/img/schema2.png)

with R=1, L=0.1, C=2 this gives:

Expand Down Expand Up @@ -228,7 +228,7 @@ plt.savefig("intensities_res.png")
```

This outputs the following graph that displays the intensity passing through the resistances
![Temporal system](img/intensities_res.png)
![Temporal system](docs/img/intensities_res.png)


## Solver suggestions
Expand All @@ -247,7 +247,7 @@ This repository can be used as is in order to model the mass flow or thermal flu

We are considering the following hydraulic problem:

![Hydraulic system](img/hydraulic.png)
![Hydraulic system](docs/img/hydraulic.png)

Taking R=1 this gives

Expand Down
89 changes: 89 additions & 0 deletions docs/components/frequency-system-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# FrequencySystemBuilder

This class handles **frequency-domain** analysis of linear electric systems.

## Features

- Supports tension and intensity sources
- Models inductive and resistive mutuals
- Detects and couples multiple subsystems
- Accepts arbitrary complex impedances and mutuals
- Constructs sparse linear systems in COO format

!!! tip

Some solvers do not support complex-valued systems. Use `cast_complex_system_in_real_system` from `utils.py` to convert an `n`-dimensional complex system into a `2n`-dimensional real system.

## Example

We would like to study the following system:

![Multiple system](../img/schema.png)

This can be defined in the following manner. We took `R=1`, `L=1` and `M=2`.

```python
import numpy as np
from scipy.sparse.linalg import spsolve
from ElecSolver import FrequencySystemBuilder


# Complex and sparse impedance matrix
# notice coil impedence between points 0 and 2, and coil impedence between 3 and 4
impedence_coords = np.array([[0, 0, 1, 3], [1, 2, 2, 4]], dtype=int)
impedence_data = np.array([1, 1j, 1, 1j], dtype=complex)

# Mutual inductance or coupling
# The indexes here are the impedence indexes in impedence_data
# The coupling is inductive
mutuals_coords = np.array([[1], [3]], dtype=int)
mutuals_data = np.array([2.0j], dtype=complex)

electric_sys = FrequencySystemBuilder(
impedence_coords,
impedence_data,
mutuals_coords,
mutuals_data,
)

# Add source (current source here)
electric_sys.add_current_source(intensity=10, input_node=2, output_node=0)

# Set ground
# 2 values because one for each subsystem
electric_sys.set_ground(0, 3)

# Build system
electric_sys.build_system()

# Get and solve the system
sys, b = electric_sys.get_system()
sol = spsolve(sys.tocsr(), b)
frequencial_response = electric_sys.build_intensity_and_voltage_from_vector(sol)

# We see a tension appearing on the lonely coil (between node 3 and 4)
print(frequencial_response.potentials[3] - frequencial_response.potentials[4])
```

## Adding a Parallel Resistance

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.

![Parallel system](../img/schema3.png)

In Python, simply add the resistance to the list of impedances in the first lines of the script:

```python
import numpy as np
from scipy.sparse.linalg import spsolve
from ElecSolver import FrequencySystemBuilder


# We add an additional resistance between 0 and 2
impedence_coords = np.array([[0, 0, 1, 3, 0], [1, 2, 2, 4, 2]], dtype=int)
impedence_data = np.array([1, 1j, 1, 1j, 1], dtype=complex)

# No need to change the couplings since indexes of the coils did not change
mutuals_coords = np.array([[1], [3]], dtype=int)
mutuals_data = np.array([2.0j], dtype=complex)
```
97 changes: 97 additions & 0 deletions docs/components/temporal-system-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# TemporalSystemBuilder

This class models **time-dependent** systems using resistors, capacitors, coils, and mutuals.

## Features

- Supports tension and intensity sources
- Models inductive and resistive mutuals
- Detects and couples multiple subsystems
- Accepts resistances, capacities, and coils
- Constructs sparse linear systems in COO format

## Example

We would like to study the following system:

![Temporal system](../img/schema2.png)

With `R=1`, `L=0.1`, `C=2` this gives:

```python
import matplotlib.pyplot as plt
import numpy as np
from scipy.sparse.linalg import spsolve
from ElecSolver import TemporalSystemBuilder

## Defining resistances
res_coords = np.array([[0, 2], [1, 3]], dtype=int)
res_data = np.array([1, 1], dtype=float)

## Defining coils
coil_coords = np.array([[1, 0], [3, 2]], dtype=int)
coil_data = np.array([0.1, 0.1], dtype=float)

## Defining capacities
capa_coords = np.array([[1, 3], [2, 0]], dtype=int)
capa_data = np.array([2, 2], dtype=float)

## Defining empty mutuals here
mutuals_coords = np.array([[], []], dtype=int)
mutuals_data = np.array([], dtype=float)

res_mutuals_coords = np.array([[], []], dtype=int)
res_mutuals_data = np.array([], dtype=float)

## Initializing system
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,
)

## Add source
elec_sys.add_current_source(10, 1, 0)

## Setting ground at point 0
elec_sys.set_ground(0)

## Build system
elec_sys.build_system()

# Getting initial condition system
S_i, b = elec_sys.get_init_system()
sol = spsolve(S_i.tocsr(), b)

# Get system (S1 is real part, S2 derivative part)
S1, S2, rhs = elec_sys.get_system()

## Solving using implicit Euler scheme
dt = 0.08
vals_res1 = []
vals_res2 = []

for _ in range(50):
temporal_response = elec_sys.build_intensity_and_voltage_from_vector(sol)
vals_res1.append(temporal_response.intensities_res[1])
vals_res2.append(temporal_response.intensities_res[0])
sol = spsolve(S2 + dt * S1, b * dt + S2 @ sol)

plt.xlabel("Time")
plt.ylabel("Intensity")
plt.plot(vals_res1, label="intensity res 1")
plt.plot(vals_res2, label="intensity res 2")
plt.legend()
plt.savefig("intensities_res.png")
```

This outputs the following graph that displays the intensity passing through the resistances:

![Intensity through resistances](../img/intensities_res.png)
62 changes: 62 additions & 0 deletions docs/extra-uses-hydraulic-or-thermal-system-modeling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Extra uses: Hydraulic or Thermal system modeling

ElecSolver can also be used to model mass flow or thermal flux in hydraulic and thermal networks where a pressure or temperature difference can be assimilated to a tension source.

Since electric potentials are always computed relative to the ground node, you may need to rescale the resulting potentials.

We consider the following hydraulic problem:

![Hydraulic system](img/hydraulic.png)

Taking `R=1` gives:

```python
import numpy as np
from scipy.sparse.linalg import spsolve
from ElecSolver import TemporalSystemBuilder

## Defining resistances
R = 1
res_coords = np.array([[0, 2, 1, 0, 1, 3], [1, 3, 3, 2, 2, 0]], dtype=int)
res_data = R * np.array([2, 3, 1, 1, 1, 1], dtype=float)

## Here we are not using coils, capacities or mutuals
coil_coords = np.array([[], []], dtype=int)
coil_data = np.array([], dtype=float)
capa_coords = np.array([[], []], dtype=int)
capa_data = np.array([], dtype=float)
mutuals_coords = np.array([[], []], dtype=int)
mutuals_data = np.array([], dtype=float)
res_mutuals_coords = np.array([[], []], dtype=int)
res_mutuals_data = np.array([], dtype=float)

## Initializing system
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,
)

## Enforcing a pressure delta of 10 Pa
hydraulic_sys.add_voltage_source(10, 1, 0)
hydraulic_sys.set_ground(0)
hydraulic_sys.build_system()

S1, S2, rhs = hydraulic_sys.get_system()
sol = spsolve(S1.tocsr(), rhs)
solution = hydraulic_sys.build_intensity_and_voltage_from_vector(sol)

pressure_input = 10000
pressure_node = 0
potentials = solution.potentials - solution.potentials[pressure_node] + pressure_input

print("Pressures in the system:", potentials)
print("Debit through the system", solution.intensities_sources[0])
```
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
59 changes: 59 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# ElecSolver

## Overview

**ElecSolver** formalizes electric systems as linear problems, suitable for both **temporal** and **frequency-domain** studies.
It focuses on constructing the linear system representation, leaving the actual numerical resolution to the user.

This repository is **not** a general-purpose electrical system solver. Instead, it acts as a **bridge** between:

- The graph-based description of an electric network
- The corresponding sparse linear system to solve

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.

!!! warning

ElecSolver has been designed with the following specifications in mind:

- The time needed for building the linear system must be negligible compared to the time needed for solving it.
- Handle natively inductive mutuals and resistive mutuals.
- Handle as many coupled electric systems as needed.
- Deal with lonely nodes and lonely edges in the electric graph when the problem is still well posed.

!!! note

Non-linear components are not supported. You must manage event detection and system updates yourself.

## How to install

ElecSolver is distributed on PyPI and can be installed with `pip` or with `conda`/`mamba`.

```bash
pip install ElecSolver
```

or

```bash
conda install elecsolver
```

For solving the linear systems, `python-mumps` is recommended when it is available on your platform.

```bash
conda install python-mumps
```

## Components

The documentation is organized around the same component split as the README:

- [FrequencySystemBuilder](components/frequency-system-builder.md)
- [TemporalSystemBuilder](components/temporal-system-builder.md)

Then follow the same supporting sections:

- [Solver suggestions](solver-suggestions.md)
- [Extra uses: Hydraulic or Thermal system modeling](extra-uses-hydraulic-or-thermal-system-modeling.md)
- [Netlist import feature](netlist-import-feature.md)
Loading
Loading