← Physics-informed GNN course

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_grid specifies 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.

1 · Describebus, line, load, sgenTopology, impedances, demand, generation
2 · Constructpp.create_*()Equipment tables inside net
3 · Solverunpp / runpp_3phAC operating point
4 · Checknet.res_*Supply, voltage, current, losses

Follow the circuit into the modeling functions

Physical connections
buslineext_gridloadsgen
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.load
create_sgen() → net.sgen
net → runpp() / runpp_3ph() → net.res_*
Creation functions populate equipment tables. The solver constructs the network equations and writes res_* tables. Three-phase mode retains the connections but uses phase-specific asymmetric load and generator tables.

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 objectBalanced modelThree-phase modelResults to inspect
Bus / voltage basecreate_bus()Same, with line-to-line vn_kvres_bus / res_bus_3ph
Upstream sourcecreate_ext_grid()Add source sequence parametersres_ext_grid / res_ext_grid_3ph
Series branchcreate_line_from_parameters()Also provide zero-sequence parametersres_line / res_line_3ph
Constant-PQ demandcreate_load()create_asymmetric_load()Load result tables
Fixed-PQ invertercreate_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 lessonpandapower argument / unitExample
400 V line-to-linevn_kv, kV0.4, not 400
120 kW three-phase totalp_mw, MW in load0.120
90 kW on phase Ap_a_mw, MW in asymmetric_load0.090
600 A phase-current ratingmax_i_ka, kA0.600
0.012 Ω total series Rr_ohm_per_km × length_km0.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.

Keep the return-path assumptions visible. The preceding Unbalanced Power Flow chapter solves an explicit four-wire conductor model with finite downstream neutral impedance and no additional earth return. pandapower's three-phase solver uses an earth-return model, with wye neutral and earth treated together. Its results do not directly give that earlier model's neutral displacement. This chapter compares the same demands under a different return-path representation; it does not claim that the unbalanced numerical answers must match.

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

1–3 tie: normally openz₁₂z₂₃ 123 1∠0° puReference Load 120 kWPF = 0.95Load 180 kWDG 50 kWQɢ = 0

Swipe sideways to see the full diagram.

Solid branches form the radial 1 → 2 → 3 feeder; the dashed branch is the spare tie. Combine demand and generation at bus 3 as a net PQ injection. The experiment later changes generation and closes the tie.
BusEquipment and specified quantitiespandapower representation
1Upstream source, positive-sequence voltage 1∠0° puext_grid provides the voltage reference
2120 kW total load, PF = 0.95 lagging; three-phase mode uses 40 / 40 / 40 kWload or asymmetric_load
3180 kW total load, PF = 0.95 lagging; three-phase mode uses 90 / 55 / 35 kW; plus 50 kW solar generationLoad 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.

LineZ₁ (Ω)Z₀ (Ω), three-phase modeCurrent rating
1–20.012 + j0.0080.036 + j0.024600 A
2–30.008 + j0.0060.024 + j0.018600 A
1–3, initially open0.022 + j0.0140.066 + j0.042600 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:

ResultValue
Bus 2 / bus 3 voltage0.97559 / 0.96657 pu
Total line loss6.83889 kW
Source supply256.83889 kW
Line 1–2 current / loading399.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 resultABC
Voltage (pu)0.933820.976710.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 MVA

The 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.

Lowest supplied voltage—
Total line loss—
Source active power—
Highest line loading—
Highest VUF—
Unsupplied buses—

Bus-voltage profiles

Line current loading

Read res_bus / res_bus_3ph
BusV / A (pu)B (pu)C (pu)VUF (%)Supplied
Read res_line / res_line_3ph
LineP 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

  1. caseControls provide an input dictionary
  2. pandapower_implementation.pySource defines the model functions
  3. 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…
ParameterMeaning and units
mode / algorithmbalanced or three_phase; nr / bfsw applies to balanced runpp only.
p2_kw / p3_a_kw / p3_b_kw / p3_c_kwBus 2 total demand and bus 3 phase demands (kW); balanced mode sums bus 3 phases.
load_scale / pf / dg_kw / dg_phaseDemand multiplier, power factor, total PV kW and its phase connection.
vm_pu / z_scale / zero_ratioSource voltage pu, line impedance multiplier and zero-/positive-sequence impedance ratio.
limit_a / tie / trip12 / trip23 / trip13Current 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 sourceWhat 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.

Download solver

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.

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

Copy base case → remove one branch → solve again
1 · Supply connectivityDoes every bus retain a path to the reference source?
→
No path: record unsupplied busesTheir voltages are not valid solved-voltage labels.
2 · Solver outcomeCheck convergence for the supplied network.
→
No convergence: keep a separate outcomeSolver failure alone does not prove physical infeasibility.
3 · Operating limitsCheck voltage, line loading, and VUF in three-phase mode.
→
Record within limits or violationsThe teaching secure label requires supply, convergence, and all chosen limits.
Keep supply status, solver outcome, and limit checks separately. One net.converged flag cannot replace these three layers or establish dynamic stability.

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

  1. Compare nr and bfsw in balanced mode. Are the converged operating points equal within numerical tolerance?
  2. Set bus 3 to 60/60/60, then to 90/55/35 kW. Explain why runpp() is unchanged while runpp_3ph() responds.
  3. 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.
  4. Run the N-1 sweep with the tie closed, then repeat with it open. Distinguish a voltage violation, an unsupplied bus, and solver nonconvergence.
If a radial feeder's last line trips and net.converged is True, what should you conclude?

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.