Quickstart¶
This page walks through a minimal end-to-end calculation: two substations connected by a single medium-voltage cable, a fault at the remote bus and a current source at the feeding substation. The example covers every stage of a typical workflow — network construction, path generation, solve and result access.
1. Import and create a network¶
Every object in groundinsight lives inside a Network container. Start by
importing the package and creating an empty network with the frequencies of
interest (the 50 Hz fundamental plus a few harmonics):
import groundinsight as gi
net = gi.create_network(
name="QuickstartNet",
frequencies=[50, 250, 350, 450, 550],
)
2. Define bus and branch types¶
BusType and BranchType hold the formula strings for the grounding
impedance, self impedance and mutual impedance. The symbols rho, f and l
refer to the specific earth resistance \(\rho_E\), frequency and line length;
j denotes the imaginary unit.
bus_type = gi.BusType(
name="SubstationBus",
description="Lumped substation grounding grid",
system_type="Substation",
voltage_level=20,
impedance_formula="rho * 0.01 + j * f * 1/50 * 0.1",
)
cable_type = gi.BranchType(
name="MSCable",
description="20 kV single-core cable with shield",
grounding_conductor=True,
self_impedance_formula="(0.25 + j * f * 0.012) * l",
mutual_impedance_formula="(0.0 + j * f * 0.012) * l",
)
3. Add buses and branches¶
gi.create_bus(
name="bus_source",
type=bus_type,
network=net,
specific_earth_resistance=100.0,
)
gi.create_bus(
name="bus_fault",
type=bus_type,
network=net,
specific_earth_resistance=100.0,
)
gi.create_branch(
name="cable_1",
type=cable_type,
from_bus="bus_source",
to_bus="bus_fault",
length=5.0,
specific_earth_resistance=100.0,
network=net,
)
4. Add the source and the fault¶
gi.create_source(
name="substation_infeed",
bus="bus_source",
values={50: 1000.0, 250: 200.0, 350: 100.0, 450: 50.0, 550: 25.0},
network=net,
)
gi.create_fault(
name="fault_at_remote_bus",
bus="bus_fault",
description="Single-phase-to-ground fault at bus_fault",
scalings={50: 1.0},
network=net,
)
5. Create paths and solve¶
Path generation discovers every route from each source to the active fault and
is used to inject the mutual-coupling Norton currents with the correct sign.
If you skip create_paths, run_fault calls it implicitly.
6. Inspect the results¶
The results are attached to the Network object and exposed as Polars
DataFrames through convenience methods:
import polars as pl
buses = net.res_buses(fault="fault_at_remote_bus")
branches = net.res_branches(fault="fault_at_remote_bus")
print(buses.filter(pl.col("bus_name") == "bus_fault"))
print(branches.filter(pl.col("branch_name") == "cable_1"))
The res_all_impedances() method summarises the grounding impedance \(Z_G\) and
the reduction factor \(r\) of every configured fault:
7. Plot¶
For a quick visual check use the bar-plot helpers:
result = net.results["fault_at_remote_bus"]
gi.plot_bus_voltages(result=result, title="EPR — RMS values")
gi.plot_branch_currents(result=result, title="Branch currents — RMS values")
gi.plot_bus_currents(result=result, title="Bus currents — RMS values")
8. Save and load¶
Networks can be persisted either to a SQLite database or to a JSON file:
# --- JSON ---
gi.save_network_to_json(network=net, path="quickstart.json")
loaded = gi.load_network_from_json(path="quickstart.json")
# --- SQLite ---
gi.start_dbsession(sqlite_path="quickstart.db")
gi.save_network_to_db(network=net, overwrite=True)
restored = gi.load_network_from_db(name="QuickstartNet")
gi.close_dbsession()
That is the full workflow. The Concepts page explains the model behind the scenes; the Examples section contains runnable notebooks covering more realistic network topologies.