Physics-informed GNN · Microgrids & distribution systems
PandaPower-based Implementation
The previous chapters explained the equations. Now turn a feeder diagram into a reproducible computational experiment: create buses and equipment, solve the operating point, inspect the results, and ask what happens when a line goes out of service. This chapter executes real pandapower 3.2.1 in your browser and connects those results to the first steps of security-data generation.
1. Translate the physical model into tables
Prerequisites: Balanced Power Flow and Unbalanced Power Flow. First connect the physical assumptions to equipment tables and solvers; the worked example then introduces the feeder, bus numbers, and power settings.
Modeling assumptions
- Sinusoidal steady-state AC: use fundamental-frequency RMS phasors to solve a static operating point.
- Series lines: retain resistance and reactance, with shunt capacitance set to zero. This example contains lines, without transformers or regulators.
- Constant-PQ devices: load P and Q remain specified during the solve. Solar generation uses fixed PQ, with Q = 0 in this example and no voltage regulation.
- Choose the phase model explicitly: balanced mode uses a symmetric three-phase equivalent. Three-phase mode specifies wye load and generation powers per phase, using sequence impedances and earth return; it does not solve a separate finite-impedance neutral voltage.
- One external-grid reference:
ext_gridspecifies positive-sequence voltage magnitude and angle; three-phase mode also needs source sequence impedances. The worked example lists these parameters.
See the official three-phase solver notes for the return-path convention. The workflow below uses generic equipment; the worked example defines bus numbers and equipment settings.
bus, line, load, sgenTopology, impedances, demand, generationpp.create_*()Equipment tables inside netrunpp / runpp_3phAC operating pointnet.res_*Supply, voltage, current, lossesFollow the circuit into the modeling functions
Physical connections
Equipment → input tables
- Buses and voltage bases
create_bus() → net.bus- Reference source
create_ext_grid() → net.ext_grid- Branches and impedances
create_line_from_parameters() → net.line- Demand / fixed-PQ generation
create_load() → net.loadcreate_sgen() → net.sgen
net contains pandas DataFrames. Input tables describe equipment; res_* tables contain the latest solved operating point. Changing an input cell does not automatically update the results: run the solver again.
| Physical object | Balanced model | Three-phase model | Results to inspect |
|---|---|---|---|
| Bus / voltage base | create_bus() | Same, with line-to-line vn_kv | res_bus / res_bus_3ph |
| Upstream source | create_ext_grid() | Add source sequence parameters | res_ext_grid / res_ext_grid_3ph |
| Series branch | create_line_from_parameters() | Also provide zero-sequence parameters | res_line / res_line_3ph |
| Constant-PQ demand | create_load() | create_asymmetric_load() | Load result tables |
| Fixed-PQ inverter | create_sgen() | create_asymmetric_sgen() | Generator result tables |
Fixed-PQ generation enters sgen in balanced mode or asymmetric_sgen in three-phase mode. In PV inverter, PV means photovoltaic; a power-flow PV bus instead specifies active power and voltage magnitude. This lesson uses the fixed-PQ assumption above, and a disconnected solar inverter does not automatically become a grid-forming source.
2. From equations to API arguments
A. Choose bases and convert units
Enter physical quantities rather than hand-converting every input to pu. For this network, the three-phase base is 1 MVA and the line-to-line voltage base is 0.4 kV:
The line model converts physical impedance to pu internally. For a single equivalent line:
Here length_km=1 with r_ohm_per_km=0.012 gives 0.012 Ω total resistance. This is an equivalent teaching parameterization, not a specified one-kilometer cable. Shunt capacitance is zero in the example. See the line model and parameter definitions.
| Quantity in this lesson | pandapower argument / unit | Example |
|---|---|---|
| 400 V line-to-line | vn_kv, kV | 0.4, not 400 |
| 120 kW three-phase total | p_mw, MW in load | 0.120 |
| 90 kW on phase A | p_a_mw, MW in asymmetric_load | 0.090 |
| 600 A phase-current rating | max_i_ka, kA | 0.600 |
| 0.012 Ω total series R | r_ohm_per_km × length_km | 0.012 × 1 |
Positive load power means consumption; positive sgen and ext_grid power means generation or supply. Balanced load power is a three-phase aggregate; asymmetric load power is entered phase by phase. Refer to the unit and sign conventions.
B. Derive Q from demand and power factor
For lagging constant-PQ loads, reactive demand is positive. The inverter supplies active power independently of the load’s power factor:
For 120 kW at PF = 0.95, Q ≈ 39.442 kvar. In code, that is p_mw=0.120 and q_mvar=0.039442. This lesson uses constant-PQ loads and disables voltage-dependent load behavior. The AC equations include line resistance and reactance and solve for voltage magnitudes and angles.
C. Choose balanced or three-phase equations deliberately
runpp(net, algorithm="nr") solves the balanced AC equations using Newton–Raphson. The experiment also offers bfsw to compare a backward/forward sweep. See the balanced solver options.
Switching nr / bfsw compares numerical methods within the same balanced model. Switching runpp() / runpp_3ph() changes the phase model; distinguish these two choices. The preceding chapters’ reasons for using NR and four-wire sweeps are explained in the solver comparison.
For a symmetric sequence-impedance line, the phase-domain impedance follows:
In this example Z₂ = Z₁ and Z₀ = κZ₁. κ is the adjustable zero/positive sequence ratio; it is an illustrative modeling parameter. It cannot simply replace the preceding chapter’s independently specified neutral impedance.
runpp_3ph() couples phase-specific constant-PQ injections through sequence networks: positive sequence uses Newton–Raphson, and zero/negative sequence use current-injection calculations. It needs zero-sequence line data and source sequence impedance information. See the three-phase solver and external-grid parameters.
D. Read results and verify balance
Line power is positive into each end of the branch. Therefore active loss is the sum of the two endpoint injections:
Balanced res_line.pl_mw is already total three-phase loss. For res_line_3ph, sum pl_a_mw + pl_b_mw + pl_c_mw. Never multiply an aggregate result by three again. A negative p_from_mw describes reverse flow, not negative dissipation.
3. Worked example: reproduce, then change the model
Introduce the feeder and bus settings
Consider a three-bus, 400 V distribution feeder. Bus 1 is the upstream source, connected to bus 2 through line 1–2. Line 2–3 then supplies downstream bus 3. Tie line 1–3 is initially open and can be closed in the later N-1 experiment. Both solver modes use the same bus and branch connections.
Example network: a reference source, two PQ buses, and a tie
Swipe sideways to see the full diagram.
| Bus | Equipment and specified quantities | pandapower representation |
|---|---|---|
| 1 | Upstream source, positive-sequence voltage 1∠0° pu | ext_grid provides the voltage reference |
| 2 | 120 kW total load, PF = 0.95 lagging; three-phase mode uses 40 / 40 / 40 kW | load or asymmetric_load |
| 3 | 180 kW total load, PF = 0.95 lagging; three-phase mode uses 90 / 55 / 35 kW; plus 50 kW solar generation | Load table plus sgen or asymmetric_sgen |
The inverter at bus 3 is a specified P, Q injection: 50 kW total active power and Q = 0, with active power equally distributed in the three-phase baseline. It does not regulate voltage or turn the bus into a voltage-controlled PV bus. Combine the load and generation to obtain the bus’s net injection.
Set line and source parameters
Use a 1 MVA three-phase base and a 0.4 kV line-to-line voltage base. The total positive- and zero-sequence line impedances and current ratings are below; the initial ratio is κ = Z₀/Z₁ = 3.
| Line | Z₁ (Ω) | Z₀ (Ω), three-phase mode | Current rating |
|---|---|---|---|
| 1–2 | 0.012 + j0.008 | 0.036 + j0.024 | 600 A |
| 2–3 | 0.008 + j0.006 | 0.024 + j0.018 | 600 A |
| 1–3, initially open | 0.022 + j0.014 | 0.066 + j0.042 | 600 A |
The three-phase source uses Ssc,max = Ssc,min = 1000 MVA, R/X = 0.1, X₀/X = 1, and R₀/X₀ = 0.1. These parameters supply the source zero/negative-sequence representation; they are teaching assumptions rather than measured feeder data.
Solve and check the results
Step 1 — Balanced solve. Bus 3’s three phase-demand controls are summed to 180 kW. runpp() gives:
| Result | Value |
|---|---|
| Bus 2 / bus 3 voltage | 0.97559 / 0.96657 pu |
| Total line loss | 6.83889 kW |
| Source supply | 256.83889 kW |
| Line 1–2 current / loading | 399.55 A / 66.592% |
This reproduces the first chapter’s balanced baseline. Check 256.83889 + 50 − 300 = 6.83889 kW.
Step 2 — Keep totals, expose unequal phases. Switch to three-phase mode. Bus 2 still has 40 / 40 / 40 kW, while bus 3 has 90 / 55 / 35 kW. PV remains equally distributed. With κ = 3:
| Bus 3 result | A | B | C |
|---|---|---|---|
| Voltage (pu) | 0.93382 | 0.97671 | 0.98850 |
| Angle (°) | −0.6311 | −121.2352 | +120.5968 |
Total line loss becomes 8.51027 kW, line 1–2’s highest phase current becomes 557.07 A, and bus 3 VUF is 0.91098%. Phase A falls below the teaching voltage band even though total demand is unchanged. The source’s phase voltages can differ slightly because its zero/negative-sequence impedances are finite; its positive-sequence voltage is fixed.
Step 3 — Balance the phases. Set bus 3 to 60 / 60 / 60 kW. The symmetric three-phase solution recovers the balanced voltage magnitudes and losses to numerical tolerance. Matching this special case checks units, phase-power allocation, and result interpretation.
4. Experiment with a real library
Start the runtime once, then move the sliders. Compare source voltage, phase allocation, line impedance, PV placement, and current rating. Open the generated construction code to see exactly which arguments each control changes. The code panel below has its own worker, so a code experiment can be stopped separately.
From parameters to real pandapower results
pandapower 3.2.1 · 400 V · 1 MVAThe first start downloads the scientific runtime; allow a minute or longer. Once loaded, sliders automatically rebuild the network, solve power flow, and update the figures. Computation runs in your browser.
Parameters are ready. Start the lab to calculate.
Bus-voltage profiles
Line current loading
Read res_bus / res_bus_3ph
| Bus | V / A (pu) | B (pu) | C (pu) | VUF (%) | Supplied |
|---|
Read res_line / res_line_3ph
| Line | P from (kW) | Loss (kW) | I / A (A) | B (A) | C (A) | Loading (%) |
|---|
pandapower construction code for these parameters
This code updates with the controls and can be copied into the editor below. It uses this chapter's collect_results() result reader.
Runtime output and error details
Try these observations: changing only the current rating changes loading percentages without changing the unconstrained power-flow solution. Closing the tie redistributes flows. Tripping 2–3 in the radial feeder leaves bus 3 unsupplied; a remaining supplied subnetwork may still converge. Missing bus voltages are shown as “—”, never as valid zero-voltage operating points.
5. Edit tables, rerun, and inspect the answer
The panel separates inputs, model source and the experiment entry program. main() copies case, calls build_network(), run_network() and collect_results() to build, solve and read, then returns the answer for plotting. Expand the source for function definitions and dependencies. solve(case) wraps the same workflow and is useful for parameter sweeps.
How the code fits together
- caseControls provide an input dictionary
- pandapower_implementation.pySource defines the model functions
- main(case) → resultMain computes, prints and returns a result to plot
When you press Run, the page copies the controls into case, executes the folded model source, then executes your editable main program. This makes the source's functions available directly to main(), without a separate import statement. Read the three parts in that order.
1 · Inputs: expand the current case and parameter meanings
These are the inputs for the next run and update with the controls. Copying them into parameters inside main() lets you test another case without changing the controls.
Reading parameters…
| Parameter | Meaning and units |
|---|---|
mode / algorithm | balanced or three_phase; nr / bfsw applies to balanced runpp only. |
p2_kw / p3_a_kw / p3_b_kw / p3_c_kw | Bus 2 total demand and bus 3 phase demands (kW); balanced mode sums bus 3 phases. |
load_scale / pf / dg_kw / dg_phase | Demand multiplier, power factor, total PV kW and its phase connection. |
vm_pu / z_scale / zero_ratio | Source voltage pu, line impedance multiplier and zero-/positive-sequence impedance ratio. |
limit_a / tie / trip12 / trip23 / trip13 | Current rating in A; tie enables line 1–3; True trips the corresponding line. |
2 · Model source: pandapower_implementation.py (expand to inspect / edit)
Dependencies: The source imports json, math and pandapower as pp. The browser loads pinned pandapower 3.2.1 with NumPy, SciPy, pandas and its dependencies; local installation uses the downloadable requirements file.
This is the source actually executed by Python. Use the table to connect its functions to this chapter's equations. Edits take effect on the next main-program run. The parameter lab keeps the original pandapower model as a reference.
| Function in the source | What it does |
|---|---|
settings(case) | Merges defaults and validates input settings. |
build_network(case) | Settings → net.bus / line / load / sgen tables; converts kW to MW and A to kA. |
supplied_buses(net) | Finds buses connected to the external grid in this line-only feeder. |
run_network(net, case) | Calls pp.runpp or pp.runpp_3ph and fills the corresponding res_* tables. |
collect_results(net, case) | res_* tables → the dictionary used by the lesson's plots and supply/limit checks. |
solve(case) | Convenience wrapper for build → run → collect; useful for parameter sweeps. |
3 · Main program: what does main() do?
The main program copies / changes inputs → calls the model → checks the solution → prints → returns the result. solved is the result dictionary inside the function; the final result = main(case) passes it to the page's plot. print() writes to the output panel and does not draw the plot.
First run the example unchanged and compare it with the worked example. Then edit net.load after build_network() and before run_network(), then inspect the new result tables.
The first run downloads Pyodide, NumPy, SciPy, pandas, and pandapower 3.2.1; allow a minute or longer. Code runs in a separate browser worker and can be stopped. A page reload needs a new initialization.
Ready to run.
How to read the output
net is the editable pandapower network; res_bus/res_line (or *_3ph) are its solved tables. result is collect_results' dictionary for plotting. ok means convergence; secure additionally checks supply and teaching limits. Unsupplied bus indices are zero-based.
Download experiment includes the current inputs, model source and main program in one runnable .py file. Install the requirements below before running locally.
Output appears here.
Code result: voltage profiles
For a direct table edit in balanced mode, insert these two lines inside main(), before run_network(net, parameters) (keep four spaces of indentation):
net.load.loc[net.load.bus == 2, "p_mw"] = 0.240
net.load.loc[net.load.bus == 2, "q_mvar"] = 0.240 * math.tan(math.acos(parameters["pf"]))
The diagram numbers buses 1/2/3; pandas indices here are 0/1/2. Retain the integer IDs returned by create_bus() when building a larger network, rather than assuming that bus labels equal row indices. The result reader in this example handles the line-only feeder; extend its connectivity and power-balance checks when adding transformers, switches, or other equipment.
For a local environment, save the Python model and requirements in one directory, create a Python 3.12 virtual environment, then install and run:
python -m pip install -r requirements-pandapower.txt
python pandapower_implementation.py
The Notebook embeds the model source, so it is self-contained. It includes installation, DataFrame inspection, balanced/three-phase comparison, and an N-1 sweep. The browser pins pandapower 3.2.1 with Pyodide 0.28.3 and disables optional Numba acceleration. First initialization requires internet access; local downloads let you keep a reproducible course experiment.
6. From operating points to N-1 labels
An N-1 screen solves the base operating point and each selected single-component outage. A label should include supply status, not only net.converged. In this teaching network:
These are chosen teaching limits. This static screen does not assess protection, transient stability, harmonics, or islanded control. Solver failure is a separate outcome; it does not establish physical infeasibility.
Three checks for each outage scenario
Paste this into the Python editor. A closed tie gives an alternate supply path; each trial begins from the same base case:
import pandas as pd
base = dict(case, tie=True, trip12=False, trip23=False, trip13=False)
rows = []
for outage in (None, "trip12", "trip23", "trip13"):
trial = dict(base)
if outage:
trial[outage] = True
solved = solve(trial)
rows.append({
"outage": outage or "base",
"converged": solved["ok"],
"unsupplied": solved.get("unsupplied", []),
"min_v_pu": solved.get("min_voltage"),
"max_loading_pct": solved.get("max_loading"),
"secure": solved.get("secure", False),
})
if solved["ok"]:
result = solved
print(pd.DataFrame(rows).to_string(index=False))
For a future GNN dataset, retain bus demand/generation and voltage bases as node features; impedance, rating, and outage status as edge features. Save solved voltages, loading, unsupplied flags, and solver outcome separately as labels or metadata. Keep bus IDs and phase order consistent across cases. Split evaluation by operating scenario or topology so that closely related contingency cases do not leak across training and test sets.
7. Predict, test, explain
- Compare
nrandbfswin balanced mode. Are the converged operating points equal within numerical tolerance? - Set bus 3 to 60/60/60, then to 90/55/35 kW. Explain why
runpp()is unchanged whilerunpp_3ph()responds. - Reduce the line rating from 600 to 300 A. Explain why overload appears without automatic curtailment: ordinary power flow does not enforce the thermal limit.
- Run the N-1 sweep with the tie closed, then repeat with it open. Distinguish a voltage violation, an unsupplied bus, and solver nonconvergence.
Use the expandable Python source to connect each API call to its equation, then keep the Notebook as the starting point for your own feeder and course examples.