Repository navigation
Add NetlistCircuit, importing circuit into SparseLinearDAE #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
29644f6
Added NetlistCircuit class for sparse spice-like circuit simulation
Pimss 971dedd
Added tests for NetlistCircuit
Pimss fe6f4bb
Add ElecSolver dependency version 2.2.1
Pimss 1aa1736
Add sparse netlist circuit example notebook
Pimss 0e34e37
Add NetlistCircuit class to README
Pimss d6c3a0e
Add transformer example with thermal model
Pimss 1dabcc8
Remove ambient temperature definition from example
Pimss File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,224 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "9949413d", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# Sparse Circuit from a Netlist\n", | ||
| "\n", | ||
| "This example simulates a pulse-driven LC low-pass filter with a resistive load using the `NetlistCircuit` block. [ElecSolver](https://github.com/williampiat3/ElecSolver) parses the SPICE-like netlist; PathSim then integrates the circuit as a sparse linear differential-algebraic equation (DAE).\n", | ||
| "\n", | ||
| "The block behaves like a state space model of the netlist:\n", | ||
| "\n", | ||
| "- the **inputs** are the independent sources, named like in the netlist (`V1`, `Bload`, ...)\n", | ||
| "- the **outputs** are all node voltages `V(node)` and all element currents `I(name)`\n", | ||
| "\n", | ||
| "so the circuit is wired to other blocks by name.\n", | ||
| "\n", | ||
| "**Before running:** select a Python environment with `pathsim`, `pathsim_rf`, `matplotlib`, and the optional dependency `ElecSolver` (`pip install ElecSolver`). If you use local source checkouts, keep their directories on `PYTHONPATH`. Run the cells from top to bottom: inspect the available ports, select the measurements, connect the blocks, and run the simulation." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "7f789358", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import matplotlib.pyplot as plt\n", | ||
| "\n", | ||
| "from pathsim import Simulation, Connection\n", | ||
| "from pathsim.blocks import PulseSource, Amplifier, Scope\n", | ||
| "from pathsim.solvers import BDF2\n", | ||
| "from pathsim_rf import NetlistCircuit" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "cd74bc6b", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Netlist\n", | ||
| "\n", | ||
| "The source `V1` drives node `in` through a 0.5 mH series inductor (`Lfilter`). A 50 µF capacitor (`Cfilter`) connects the output node `n1` to ground, node `0`.\n", | ||
| "\n", | ||
| "The 50 Ω load is represented by a behavioral current source, `Bload`. Its expression `I=V(n1)/50` describes the intended load, but the block does **not** evaluate it: the current is an input supplied by the PathSim feedback connection below.\n", | ||
| "\n", | ||
| "Likewise, the `PULSE(...)` waveform and `.tran` directive do not control the PathSim run. We set the matching pulse parameters in `PulseSource` and the timestep and duration in the simulation cells." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "0d28cb83", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "netlist = \"\"\"\n", | ||
| "*validation netlist for pathsim\n", | ||
| ".param FREQ = 9k\n", | ||
| ".param DUTY = 0.5\n", | ||
| ".param V_LOW = 0\n", | ||
| ".param V_HIGH = 1\n", | ||
| ".param T_PER = {1/FREQ}\n", | ||
| ".param T_ON = {T_PER * DUTY}\n", | ||
| ".param T_RISE = 10n\n", | ||
| ".param T_FALL = 10n\n", | ||
| "\n", | ||
| "V1 in 0 PULSE({V_LOW} {V_HIGH} 0 {T_RISE} {T_FALL} {T_ON} {T_PER})\n", | ||
| "Bload n1 0 I=V(n1)/50\n", | ||
| "Lfilter in n1 0.5e-3\n", | ||
| "Cfilter n1 0 50e-6\n", | ||
| ".tran 0.5e-6 40e-3 0 0.00001\n", | ||
| ".end\n", | ||
| "\"\"\"" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "2bf01163", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Circuit block\n", | ||
| "\n", | ||
| "First, create a block with all outputs exposed and print the available port names. Inputs list voltage sources first, then current sources. Outputs list node voltages relative to ground (ground itself is excluded), then element currents. A current is positive from the first node to the second node in that element's netlist declaration.\n", | ||
| "\n", | ||
| "The default initial state is zero: the capacitor is uncharged and the inductor carries no current. You can set these quantities by name with `initial_value`, for example `{\"V(n1)\": 1.0, \"I(Lfilter)\": 0.5}` (volts and amperes)." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "023c710b", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "circuit = NetlistCircuit(netlist)\n", | ||
| "\n", | ||
| "print(\"inputs :\", circuit.input_names)\n", | ||
| "print(\"outputs:\", circuit.output_names)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "77692372", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "Now replace the inspection block with one exposing only the two measurements used below: `V(n1)` for the load feedback and plot, and `I(Lfilter)` for the plot. The selection lists take bare node and element names, not the formatted port labels.\n", | ||
| "\n", | ||
| "This reduces the rows of the sparse output matrices `C` and `D` inherited from `SparseLinearDAE`; it does not remove components or reduce the internal circuit state. Selecting outputs without direct feedthrough can also avoid unnecessary algebraic dependencies, but output selection alone does not guarantee that a feedback loop disappears." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "3a03c84e", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "circuit = NetlistCircuit(netlist, output_voltages=[\"n1\"], output_currents=[\"Lfilter\"])\n", | ||
| "\n", | ||
| "print(\"inputs :\", circuit.input_names)\n", | ||
| "print(\"outputs:\", circuit.output_names)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "6c82fba0", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Blocks and connections\n", | ||
| "\n", | ||
| "The load feedback is implemented outside the circuit: `V(n1)` passes through a gain of `1/50` A/V and returns to the input `Bload`. This implements Ohm's law, drawing current from `n1` to ground just like a 50 Ω resistor.\n", | ||
| "\n", | ||
| "`PulseSource` drives `V1` with a 0-to-1 V pulse at 9 kHz and 50% duty cycle, matching the netlist. The scope records the source voltage, filtered output voltage, and inductor current. Named circuit ports make these connections independent of their numeric port indices." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "7682fbde", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "Src = PulseSource(T=1/9000, amplitude=1, duty=0.5, t_rise=10e-9, t_fall=10e-9)\n", | ||
| "Load = Amplifier(1/50)\n", | ||
| "Sco = Scope(labels=[\"pulse V1\", \"V(n1)\", \"I(Lfilter)\"])\n", | ||
| "\n", | ||
| "blocks = [Src, circuit, Load, Sco]\n", | ||
| "\n", | ||
| "connections = [\n", | ||
| " Connection(Src, circuit[\"V1\"], Sco[0]),\n", | ||
| " Connection(circuit[\"V(n1)\"], Load, Sco[1]),\n", | ||
| " Connection(Load, circuit[\"Bload\"]),\n", | ||
| " Connection(circuit[\"I(Lfilter)\"], Sco[2]),\n", | ||
| " ]" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "ccf4cd9a", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Simulation\n", | ||
| "\n", | ||
| "Use the implicit BDF2 solver to integrate the circuit DAE, with an initial timestep of 0.5 µs. The simulation starts from the zero initial state described above; `log=True` enables progress logging. The next cell constructs the simulation; the final cell advances it for 40 ms." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "ddf8d3bb", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "Sim = Simulation(blocks, connections, Solver=BDF2, dt=0.5e-6, log=True)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "20aaee67", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Results\n", | ||
| "\n", | ||
| "Run the simulation and plot the three recorded signals. `pulse V1` and `V(n1)` are in volts, while `I(Lfilter)` is in amperes, so their plotted magnitudes have different units.\n", | ||
| "\n", | ||
| "Look for the LC startup oscillation and the smoothing of the input pulses at `V(n1)`. With a 0-to-1 V input and 50% duty cycle, the long-term mean output should be near 0.5 V; zoom in on the end of the trace to inspect the remaining switching ripple." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "Sim.run(40e-3)\n", | ||
| "Sco.plot(lw=1.5)\n", | ||
| "plt.show()" | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "language_info": { | ||
| "codemirror_mode": { | ||
| "name": "ipython", | ||
| "version": 3 | ||
| }, | ||
| "file_extension": ".py", | ||
| "mimetype": "text/x-python", | ||
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.x" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 5 | ||
| } |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,27 @@ | ||
| """ | ||
| PathSim-RF: RF Engineering toolbox for PathSim | ||
|
|
||
| A toolbox providing specialized blocks for RF and microwave engineering | ||
| simulations in the PathSim framework. | ||
| """ | ||
|
|
||
| try: | ||
| from ._version import version as __version__ | ||
| except ImportError: | ||
| __version__ = "unknown" | ||
|
|
||
| __all__ = ["__version__"] | ||
|
|
||
| from .transmission_line import * | ||
| from .amplifier import * | ||
| from .mixer import * | ||
| from .netlist_to_statespace import NetlistStateSpace | ||
|
|
||
| try: | ||
| from .network import * | ||
| except ImportError: | ||
| pass | ||
| """ | ||
| PathSim-RF: RF Engineering toolbox for PathSim | ||
|
|
||
| A toolbox providing specialized blocks for RF and microwave engineering | ||
| simulations in the PathSim framework. | ||
| """ | ||
|
|
||
| try: | ||
| from ._version import version as __version__ | ||
| except ImportError: | ||
| __version__ = "unknown" | ||
|
|
||
| __all__ = ["__version__"] | ||
|
|
||
| from .transmission_line import * | ||
| from .amplifier import * | ||
| from .mixer import * | ||
| from .netlist_to_statespace import NetlistStateSpace | ||
|
|
||
| try: # test if scikit-rf is present in env (pip install scikit-rf) | ||
| from .network import * | ||
| except ImportError: | ||
| pass | ||
| try: # test if Elecsolver is present in env (pip install ElecSolver>=2.2.1) | ||
| from .circuit import * | ||
| except ImportError: | ||
| pass |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@milanofthe ElecSolver>=2.2.1 is a python lightweight sparse linear system builder for electric systems (or anything that can be expressed in a similar circuit form), it only needs numpy and scipy (I removed networkx in the 2.2.0 release to make it minimal).
In case you were worried about adding deps to pathSim, this one does not add extra packages (except itself)