Optimal transmission switching
Optimal Transmission Switching decides the on/off state of network elements so as to minimize generation cost. Counter-intuitively, removing a line can reduce cost: it redistributes power flows away from congested corridors, letting cheaper generation dispatch that would otherwise be constrained off.
Each switchable element carries a binary variable equal to 1 when energized and 0 when disconnected. The package provides three problem specifications, differing only in which elements get one.
Problem specifications
run_acdcots_AC(data, model_type, solver; kwargs...)
run_acdcots_DC(data, model_type, solver; kwargs...)
run_acdcots_AC_DC(data, model_type, solver; kwargs...)| Function | AC branches | DC branches | AC/DC converters |
|---|---|---|---|
run_acdcots_AC | ✅ | — | — |
run_acdcots_DC | — | ✅ | ✅ |
run_acdcots_AC_DC | ✅ | ✅ | ✅ |
# from a Dict (preferred — you usually want to inspect or modify the data first)
data = _PM.parse_file("case5_acdc.m")
_PMACDC.process_additional_data!(data)
result = _PMTP.run_acdcots_AC(data, ACPPowerModel, juniper; setting = s)
# from a path (parses and calls process_additional_data! for you)
result = _PMTP.run_acdcots_AC("case5_acdc.m", ACPPowerModel, juniper; setting = s)What each model builds
run_acdcots_AC
Adds _PM.variable_branch_indicator — one binary z_ac per AC branch — and swaps the AC branch constraints for their on/off counterparts:
- Ohm's law from/to (
constraint_ohms_yt_*_on_off) - voltage angle difference (
constraint_voltage_angle_difference_on_off) - thermal limits from/to (
constraint_thermal_limit_*_on_off)
Bus voltages use variable_bus_voltage_on_off, which relaxes voltage bounds on buses that may become islanded. The DC grid is modelled exactly as in a standard AC/DC OPF.
run_acdcots_DC
Adds variable_dc_branch_indicator (z_dc, one per DC branch), variable_dc_conv_indicator (z_cv, one per converter), and a voltage slack variable. The corresponding constraints are all package-local:
| Constraint | Purpose |
|---|---|
constraint_ohms_ots_dc_branch | DC Ohm's law, deactivated when z_dc = 0 |
constraint_branch_limit_on_off_dc_ots | forces DC branch flow to zero when open |
constraint_converter_losses_dc_ots | converter loss curve, zeroed when z_cv = 0 |
constraint_converter_current_ots | converter current, zeroed when z_cv = 0 |
constraint_conv_transformer_dc_ots | converter transformer |
constraint_conv_reactor_dc_ots | converter phase reactor |
constraint_converter_limit_on_off_dc_ots | zeroes AC- and DC-side converter power |
The pairing of the loss and current constraints is what guarantees that a de-energized converter contributes exactly zero losses, rather than the constant term a_cv of its loss curve. This matters: without it the model would pay standby losses for equipment it has switched off.
run_acdcots_AC_DC
The union of the two. Binaries on AC branches, DC branches, and converters, all optimized in one problem.
Reading the results
Switching decisions are reported into the standard PowerModels solution dictionary:
| Element | Path | Key |
|---|---|---|
| AC branch | result["solution"]["branch"][id] | br_status |
| DC branch | result["solution"]["branchdc"][id] | br_status |
| Converter | result["solution"]["convdc"][id] | conv_status |
opened = [id for (id, br) in result["solution"]["branch"] if br["br_status"] < 0.1]
println("de-energized AC branches: ", opened)Restricting which elements may switch
The models make every branch and converter switchable by default. This is convenient for small studies and untenable for anything else — binary count is often what drives the computational complexity of the problem and its solve time, and in a real network most lines cannot be arbitrarily de-energized anyway.
There is no built-in API for selecting a subset. A practical workaround is to fix the binaries you do not want free, via PowerModels' variable start/bound machinery, or by pre-filtering the data. Fixing all the binaries reduces the problem to a conventional OPF for hybrid AC/DC grids, which is a useful sanity check that your setup is correct.
Automatic selection of the most promising lines is listed as future work in the reference paper. The literature it points to for line-ranking heuristics, e.g. LMP-difference screening, sensitivity analysis, congestion-zone identification, is a reasonable starting point if you need to build this yourself.
Formulation support
OTS is implemented for the exact non-convex formulation only. Relaxations and linear approximations are available for busbar splitting, not for OTS.
result = run_acdcots_AC_DC(data, ACPPowerModel, juniper; setting = s) # ✅
result = run_acdcots_AC_DC(data, LPACCPowerModel, gurobi; setting = s) # ✗ not supportedThe rationale is that the paper's contribution is the BuS model; the same relaxation strategy would apply to OTS, but it has not been implemented or validated here.
Caveat: protection
The model tells you that a topology is cheaper. It does not tell you that it is safe to operate. Coordination between the elements selected for switching and the grid's protection strategy has to be verified separately, at the planning stage, before any of these actions would be used in practice during grid operations.