Critical Clearing Time Mapping#
The critical clearing time (CCT) is defined as the maximum duration for which a fault can persist before the system loses synchronism. It is a fundamental metric in transient stability assessment, where a shorter CCT indicates a more vulnerable operating condition. By mapping the CCT across all buses in a network, the spatial distribution of stability margins can be revealed.
In this notebook, the CCT is computed for every bus in the Kundur two-area system, which consists of four GENROU generators equipped with AVR and governor controls. The procedure is as follows:
A three-phase fault is applied at each bus, and the clearing time is swept over a range of values.
Each scenario is classified as stable, unstable, or non-convergent using the built-in rotor angle separation criterion.
The stability boundary is visualized through a heatmap of maximum angle separation, a network topology diagram colored by CCT, and rotor angle trajectories at the stability boundary.
The TDS.reinit() API is utilized to reset the simulation to its
post-initialization state between scenarios, thereby avoiding the overhead
of reloading the system for each clearing time.
System Setup#
The Kundur two-area system consists of two symmetrical areas connected by
a weak tie line, with two GENROU generators in each area. The built-in
case includes a line-trip event (Toggle) at \(t = 2\) s, which is disabled
here so that the fault remains the sole disturbance.
Each bus is classified by its role in the network: generator buses (G) host synchronous machines, tie-line buses (T) form the inter-area corridor, and load buses (L) represent intermediate connection points. This classification is used to annotate the visualizations that follow.
case_path = andes.get_case("kundur/kundur_full.xlsx")
ss_ref = andes.load(case_path, no_output=True)
ss_ref.Toggle.u.v[0] = 0 # disable built-in line trip
ss_ref.PFlow.run()
bus_idxs = list(ss_ref.Bus.idx.v)
gen_idxs = list(ss_ref.GENROU.idx.v)
gen_buses = list(ss_ref.GENROU.bus.v)
# Classify buses by role
bus_type = {}
for b in bus_idxs:
if b in gen_buses:
bus_type[b] = "gen"
elif b in [7, 8, 9]: # tie-line buses
bus_type[b] = "tie"
else:
bus_type[b] = "load"
gen_info = pd.DataFrame({
"gen_idx": gen_idxs,
"bus": gen_buses,
"M": ss_ref.GENROU.M.v,
})
gen_info
| gen_idx | bus | M | |
|---|---|---|---|
| 0 | 1 | 1 | 117.00 |
| 1 | 2 | 2 | 117.00 |
| 2 | 3 | 3 | 111.15 |
| 3 | 4 | 4 | 111.15 |
CCT Sweep#
A three-phase fault with reactance \(x_f = 10^{-4}\) p.u. is applied at
\(t = 1\) s on each bus. The clearing time is swept from 25 ms to 675 ms
in 25 ms increments. For each fault bus, the system is loaded once, and
TDS.reinit() is called between clearing-time values to restore the
post-initialization state without reloading.
Each scenario is classified into one of three outcomes:
Stable: the simulation completes and all rotor angle separations remain below 180 degrees.
Unstable: the built-in
deltadeltacriterion detects a rotor angle separation exceeding 180 degrees, and the simulation is terminated early.Non-convergent: the Newton solver fails to converge and the time step is reduced to zero.
The maximum rotor angle separation (in degrees) is recorded for each scenario to produce a continuous stability map.
tc_values = np.arange(0.025, 0.70, 0.025)
n_bus = len(bus_idxs)
n_tc = len(tc_values)
outcome = np.full((n_bus, n_tc), -1, dtype=int)
max_sep_deg = np.full((n_bus, n_tc), np.nan)
for i, fbus in enumerate(bus_idxs):
ss = andes.load(case_path, setup=False, no_output=True)
ss.Toggle.u.v[0] = 0
ss.add("Fault", bus=fbus, tf=1.0, tc=1.0 + tc_values[0], xf=1e-4)
ss.setup()
ss.config.warn_abnormal = 0
ss.PFlow.run()
ss.TDS.config.tf = 8.0
ss.TDS.config.no_tqdm = 1
ss.TDS.init()
for j, tc in enumerate(tc_values):
ss.TDS.reinit()
ss.Fault.tc.v[0] = 1.0 + tc
ss.switch_dict.clear()
ss.store_switch_times(ss.exist.pflow_tds)
ss.TDS.config.tf = 8.0
ss.TDS.run(no_summary=True)
# Extract max rotor angle separation
delta = ss.dae.ts.x[:, ss.GENROU.delta.a]
sep = np.rad2deg(np.max(delta, axis=1) - np.min(delta, axis=1))
max_sep_deg[i, j] = sep.max()
if not ss.TDS.busted:
outcome[i, j] = 0
elif "criteria" in ss.TDS.err_msg.lower():
outcome[i, j] = 1
else:
outcome[i, j] = 2
Stability Boundary#
The heatmap below displays the maximum rotor angle separation for each (bus, clearing time) pair. Buses are sorted by their CCT values, with the most vulnerable buses at the top. Green regions correspond to small angle excursions (stable operation), while red regions indicate separations approaching or exceeding the 180-degree threshold. The white dashed contour marks the 90-degree separation level, and white dots indicate the CCT boundary for each bus.
Bus labels are color-coded by type: generator (G, red), tie-line (T, blue), and load (L, green).
Spatial Distribution of CCT#
The network topology diagram below maps the CCT values onto the physical layout of the Kundur two-area system. Each bus is colored according to its CCT, with the value annotated below the node. Generator buses are represented by squares, and non-generator buses by circles.
The results reveal a consistent ordering: tie-line midpoint buses (7, 8) have the longest CCTs, followed by interior generator and load buses (2, 3, 5, 10), then boundary generator buses (1, 4), and finally the Area 1 border bus (6) with the shortest CCT at 25 ms. A notable asymmetry exists between the two area border buses: bus 6 and bus 9 occupy topologically symmetric positions, yet their CCTs differ by an order of magnitude. The source of this asymmetry is not immediately apparent from the topology alone and likely involves the pre-fault tie-line power flow direction, specific line impedances, and load distribution.
Rotor Angle Trajectories at the Stability Boundary#
To illustrate the transition from stable to unstable behavior, rotor angle trajectories are plotted for a fault at bus 8 at four clearing times that straddle the CCT. The angles are expressed relative to the center of inertia (COI), which removes the common-mode swing and isolates the inter-machine separation.
For the final (unstable) case, the stability criterion is disabled so that the simulation continues beyond the point of instability, thereby capturing the full divergent trajectory.
The four panels confirm the expected transition from stable to unstable behavior. For clearing times well below the CCT, the rotor angles undergo damped oscillations relative to the COI and converge to a new equilibrium. As the clearing time approaches the CCT, the oscillation amplitude increases and the return to equilibrium becomes slower. Beyond the CCT, the generators separate irreversibly, indicating loss of synchronism.
Extensions#
Several directions can be pursued to extend this example:
Fault severity: the fault reactance \(x_f\) can be increased to model less severe faults, and the resulting shift of the CCT boundary can be observed.
Line trip protection: the built-in
Toggleevent can be re-enabled to simulate a fault cleared by tripping a line, which is the standard protection scheme in practice.Load level sweep: all loads can be scaled by a common factor, and the CCT map can be recomputed to study how loading affects transient stability margins.
Machine learning classifier: the (bus, clearing time, outcome) data can be used as labeled training samples for a classifier that predicts stability from pre-fault features alone.