Grid environments¶
Public API for TransGridEnv and shared transmission infrastructure (DC / AC power flow, OPF helpers). For physics, see Physics → Transmission. For radial distribution envs, see API → Distribution. For the SCUC env used by the TSO benchmark, see API → Unit commitment.
TransGridEnv¶
Effective modes¶
physics |
solver_mode |
Dispatch source | Network | Safety |
|---|---|---|---|---|
0 |
0 |
agent | DC PF | MW line limits |
1 |
0 |
agent | Newton-Raphson AC PF | apparent-power thermal + voltage bounds |
0 |
1 |
DCOPF | DC PF | MW line limits |
1 |
1 |
DCOPF | DCOPF + AC PF ex-post check | apparent-power thermal + voltage bounds |
0 or 1 |
2 |
ACOPF | ACOPF | apparent-power thermal + voltage bounds |
Step contract¶
reward = -reward_scale * gen_cost.costs = [cost_thermal_overload, cost_voltage_violation, cost_power_balance, cost_resource].info["cost_sum"] = sum(costs)is the aggregate diagnostic.- DC observation:
[line_flow / cap, load / total_cap, unit_p / p_max, sin(t), cos(t), <bundle_obs>]. - AC observation:
[|S| / cap, vm, load / total_cap, unit_p / p_max, sin(t), cos(t), <bundle_obs>].
powerzoojax.envs.grid.trans.TransGridEnv
¶
Bases: Environment
Transmission grid environment — DC / AC Power Flow mode.
The agent dispatches generators every time step. The environment computes power flow (DC or AC), checks safety, and returns an observation of normalised flows, loads, and unit outputs.
reset(key, params)
¶
step(key, state, action, params)
¶
Run one environment step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
PRNGKey
|
PRNG key. |
required |
state
|
TransGridState
|
Current grid state. |
required |
action
|
Array
|
When |
required |
params
|
TransGridParams
|
Environment parameters. |
required |
observation_space(params)
¶
action_space(params)
¶
powerzoojax.envs.grid.trans.TransGridState
¶
Bases: GridState
TransGridEnv state (extends GridState) — pure grid quantities only.
Resource state (battery SOC, etc.) is NOT stored here; it lives in the
caller's scan carry. The grid env only sees external_injection_mw
passed through step().
powerzoojax.envs.grid.trans.TransGridParams
¶
Bases: GridParams
TransGridEnv parameters (extends GridParams).
Attributes:
| Name | Type | Description |
|---|---|---|
case |
CaseData
|
CaseData with PTDF, limits, cost coefficients, etc. |
reward_scale |
float
|
Scaling factor for generation cost in reward. |
acpf_setup |
ACPFSetup
|
Precomputed ACPF data (None for DC-only mode). |
dcopf_setup |
DCOPFSetup
|
Precomputed DCOPF data (None unless solver_mode=1). |
acopf_setup |
ACOPFSetup
|
Precomputed ACOPF data (None unless solver_mode=2). |
resources |
tuple
|
Tuple of ResourceBundle instances for grid attachment.
Each bundle's |
powerzoojax.envs.grid.trans.make_trans_params(case, load_profiles=None, max_steps=48, delta_t_hours=0.5, steps_per_day=48, cost_thermal_weight=1.0, reward_scale=0.01, physics=0, solver_mode=0, resources=(), include_unit_dispatch=True)
¶
Create TransGridParams from a CaseData and optional load profiles.
If load_profiles is None, a flat profile at (d_max + d_min) / 2 is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
case
|
CaseData
|
CaseData instance. |
required |
load_profiles
|
Array
|
(T, n_loads) load demand array [MW]. |
None
|
max_steps
|
int
|
Maximum steps per episode. |
48
|
delta_t_hours
|
float
|
Time step duration in hours. |
0.5
|
steps_per_day
|
int
|
Steps per day. |
48
|
cost_thermal_weight
|
float
|
Weight for thermal overload cost. |
1.0
|
reward_scale
|
float
|
Multiplier for generation cost in reward. |
0.01
|
physics
|
int
|
0=DC, 1=AC. |
0
|
solver_mode
|
int
|
0=PF, 1=DCOPF, 2=ACOPF. |
0
|
resources
|
tuple
|
Tuple of ResourceBundle instances (e.g. BatteryBundle).
Use |
()
|
Shared state containers¶
powerzoojax.envs.grid.base.GridState
¶
State for grid environments (immutable pytree).
Attributes:
| Name | Type | Description |
|---|---|---|
time_step |
Array
|
Current simulation step (int32). |
done |
Array
|
Whether the episode has terminated. |
unit_power_mw |
Array
|
Active power of each unit [MW] (n_units,). |
line_flow_mw |
Array
|
Active power flow on each line [MW] (n_lines,). |
node_injection_mw |
Array
|
Net injection at each node [MW] (n_nodes,). |
is_safe |
Array
|
True if all line flows are within limits. |
n_violations |
Array
|
Number of line-limit violations. |
total_cost |
Array
|
Cumulative generation cost this episode [$]. |
vm |
Array
|
Voltage magnitudes (n_nodes,) [p.u.] — AC mode only (ones in DC). |
va |
Array
|
Voltage angles (n_nodes,) [rad] — AC mode only (zeros in DC). |
q_gen |
Array
|
Reactive power output (n_units,) [MVAr] — AC mode only (zeros in DC). |
line_flow_q_mw |
Array
|
Reactive line flows (n_lines,) [MVAr] — AC mode only (zeros in DC). |
powerzoojax.envs.grid.base.GridParams
¶
Parameters for grid environments (immutable pytree).
Attributes:
| Name | Type | Description |
|---|---|---|
load_profiles |
Array
|
Load profiles (T, n_loads) [MW].
Row t gives each load's demand at time step t.
Access should always use RL note: the default modulo indexing produces a strictly deterministic cycle. Agents can overfit to this exact period. For generalization benchmarks, consider adding random start-offset sampling or Gaussian noise to the profiles before passing them in. |
max_steps |
int
|
Maximum steps per episode. |
delta_t_hours |
float
|
Time step duration in hours (default 0.5 = 30 min). |
steps_per_day |
int
|
Steps in one day (e.g. 48 for 30-min resolution). |
cost_thermal_weight |
float
|
Per-MW penalty weight for line overload in cost channel. |
physics |
int
|
0=DC, 1=AC (determines which solver to use). |
solver_mode |
int
|
0=PF, 1=OPF (determines action→dispatch mapping). |
max_steps, delta_t_hours, steps_per_day, physics, and
solver_mode are marked pytree_node=False so they become static
under JIT. This prevents ConcretizationTypeError when they are used
in control-flow decisions and ensures no wasted tracing.
DC power flow¶
powerzoojax.envs.grid.power_flow.dc_power_flow(case, unit_power_mw, node_load_mw)
¶
Compute DC power flow.
The slack bus absorbs the power imbalance. The returned
actual_unit_power_mw reflects the true generation including slack
adjustment — use it for cost computation and constraint checking so
that RL agents cannot exploit "free" slack energy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
case
|
CaseData
|
CaseData with PTDF, nodes_units_map, slack_bus_idx. |
required |
unit_power_mw
|
Array
|
Generator outputs (n_units,) [MW]. |
required |
node_load_mw
|
Array
|
Nodal load demand (n_nodes,) [MW]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
line_flow_mw |
Array
|
Line active power flows (n_lines,) [MW]. |
node_injection_mw |
Array
|
Net nodal injection (n_nodes,) [MW]. Imbalance is absorbed by adjusting slack-bus load (load-side adjustment); numerically equivalent to adjusting slack-bus generation. |
actual_unit_power_mw |
Array
|
Generator outputs with slack adjustment (n_units,) [MW]. Slack-bus units are treated as an infinite-capacity reference; p_min/p_max are not clamped here. Enforce slack limits in the env or agent if needed. |
powerzoojax.envs.grid.power_flow.dc_power_flow_with_check(case, unit_power_mw, node_load_mw)
¶
DC power flow followed by safety check.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
case
|
CaseData
|
CaseData. |
required |
unit_power_mw
|
Array
|
Generator outputs (n_units,) [MW]. |
required |
node_load_mw
|
Array
|
Nodal loads (n_nodes,) [MW]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
line_flow_mw |
Array
|
Line active power flows (n_lines,) [MW]. |
node_injection_mw |
Array
|
Net nodal injection (n_nodes,) [MW]. |
actual_unit_power_mw |
Array
|
Generator outputs with slack adjustment (n_units,) [MW].
Use this (not the raw |
is_safe |
Array
|
True if all lines within limits (bool scalar). |
n_violations |
Array
|
Number of violated lines (int32 scalar). |
cost_thermal |
Array
|
Active-power overload [MW] (DC MW-limit semantics); see
:func: |
powerzoojax.envs.grid.power_flow.safety_check(line_flow_mw, line_cap, line_floor)
¶
Check line flow safety and compute thermal overload cost.
Uses active-power (MW) limit semantics for DC power flow. cost_thermal is in MW, not currency; when folding into reward, apply a penalty weight in the env layer (units: $/MW).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
line_flow_mw
|
Array
|
Line flows (n_lines,) [MW]. |
required |
line_cap
|
Array
|
Upper capacity limits (n_lines,) [MW]. |
required |
line_floor
|
Array
|
Lower capacity limits (n_lines,) [MW]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
is_safe |
Array
|
True if all lines within limits (bool scalar). |
n_violations |
Array
|
Number of violated lines (int32 scalar). |
cost_thermal |
Array
|
Total active-power violation [MW] (sum of overrun above cap and underrun below floor); DC active-power limits, not AC MVA thermal limits. |
Note
For AC power flow with MVA or current thermal limits, do not reuse this function; implement a separate check to avoid semantic confusion.
powerzoojax.envs.grid.power_flow.compute_generation_cost(unit_power_mw, cost_a, cost_b, cost_c)
¶
Compute total generation cost by integrating the marginal cost curve.
Marginal cost: MC(p) = cost_a * p^2 + cost_b * p + cost_c Total cost: TC(p) = (cost_a/3) * p^3 + (cost_b/2) * p^2 + cost_c * p
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
unit_power_mw
|
Array
|
Generator outputs (n_units,) [MW]. |
required |
cost_a
|
Array
|
Quadratic coefficient in MC(p), i.e. coefficient of p^2 (n_units,). These are marginal-cost (MC) polynomial coefficients, not MATPOWER-style total-cost (TC) coefficients. MATPOWER uses TC = a·p² + b·p + c and MC = 2a·p + b (linear); here MC(p) = cost_a·p² + cost_b·p + cost_c (quadratic) and TC = (a/3)p³ + (b/2)p² + c·p. Convert when importing from MATPOWER. |
required |
cost_b
|
Array
|
Linear coefficient of MC(p) (n_units,). |
required |
cost_c
|
Array
|
Constant term of MC(p) [$/MWh], i.e. marginal cost at zero output (n_units,). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
total_cost |
Array
|
Scalar generation cost [\(/h] (assuming a 1-hour dispatch interval; cost_c [\)/MWh] × p [MW] = [$/h]). |
powerzoojax.envs.grid.power_flow.proportional_dispatch(total_load_mw, p_min, p_max)
¶
Dispatch units proportionally to their capacity.
Each unit first produces p_min, then the remaining demand is allocated proportionally to each unit's headroom (p_max - p_min).
If total_load_mw exceeds the total generation capacity
(sum(p_max)), all units are clipped to p_max — no error
is raised, as this must stay JIT-safe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
total_load_mw
|
Array
|
Total system load [MW] (scalar). |
required |
p_min
|
Array
|
Minimum output per unit (n_units,) [MW]. |
required |
p_max
|
Array
|
Maximum output per unit (n_units,) [MW]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
unit_power_mw |
Array
|
Dispatched power (n_units,) [MW], clipped to [p_min, p_max]. |
AC power flow¶
powerzoojax.envs.grid.ac_power_flow.prepare_acpf(case)
¶
Prepare all precomputed data needed for AC power flow.
Extracts bus classification, generator schedules, and builds Y_bus. Called once at env construction time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
case
|
CaseData
|
CaseData with AC fields populated. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ACPFSetup |
ACPFSetup
|
frozen struct ready for JIT-compiled NR solver. |
powerzoojax.envs.grid.ac_power_flow.ac_power_flow(setup, max_iter=30, tol=1e-05)
¶
Newton-Raphson AC power flow solver (pure JAX, JIT-compilable).
Uses lax.while_loop for the NR iteration.
Q-limit enforcement: PV buses whose calculated Q exceeds Qg_min/Qg_max
(stored in setup) are switched to PQ mid-iteration (bus_is_pq mask).
The Jacobian stays fixed at (2*n_pvpq, 2*n_pvpq) regardless of how many
buses switch, satisfying the static-shape requirement of lax.while_loop.
PV→PQ switching is one-way within a single solve (no PQ→PV recovery).
If Qg_min_bus/Qg_max_bus are ±inf (default when Q data absent),
switching never triggers and the solver behaves identically to before.
Note: Under jax.vmap, all batch elements run for max_iter iterations
(already-converged elements still execute but do not update). Each
iteration contains jnp.linalg.solve(J, mismatch) which is O(n³).
For large networks with large batch sizes, consider lax.fori_loop
with a fixed iteration count as a performance-tuning alternative.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
setup
|
ACPFSetup
|
Precomputed ACPFSetup from prepare_acpf(). |
required |
max_iter
|
int
|
Maximum NR iterations. |
30
|
tol
|
float
|
Convergence tolerance on max |mismatch| in p.u. Default 1e-5 (float32 precision floor is ~2e-6). |
1e-05
|
Returns:
| Type | Description |
|---|---|
ACPFResult
|
ACPFResult with voltage, power, branch flow, and convergence info. |
powerzoojax.envs.grid.ac_power_flow.ACPFSetup
¶
Precomputed data for AC power flow (built once at env construction).
powerzoojax.envs.grid.ac_power_flow.ACPFResult
¶
Output of AC power flow solver.
OPF modules¶
DCOPF¶
powerzoojax.envs.grid.dc_opf.prepare_dcopf(case, rho=1.0)
¶
Prepare DCOPF data from CaseData.
powerzoojax.envs.grid.dc_opf.dc_opf(setup, node_load_mw, commitment=None, max_iter=100, tol=0.001)
¶
Solve DC-OPF via ADMM (Alternating Direction Method of Multipliers).
Primal objective: mc_c @ p (linear), matching PowerZoo HiGHS LP.
The ADMM consensus decomposition introduces auxiliary variables z1
(box projection) and z2 (line-flow projection) so that each update
step is cheap and JAX-native:
- p-update: exact KKT solve (n_units+1 linear system) — balance is enforced exactly at every iteration.
- z1-update: element-wise clip to
[p_min, p_max]. - z2-update: element-wise clip to
[line_floor, line_cap].
This handles the "degenerate LP optimal" case (e.g. congested line where the LP optimum requires two generators at non-bound values simultaneously) which merit-order / subgradient methods cannot represent exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
setup
|
DCOPFSetup
|
Precomputed DCOPFSetup. |
required |
node_load_mw
|
Array
|
(n_nodes,) load [MW]. |
required |
commitment
|
Array
|
(n_units,) binary mask. None = all on. |
None
|
max_iter
|
int
|
Max ADMM iterations (100 suffices for most cases; use 200 for heavily congested systems). |
100
|
tol
|
float
|
Convergence tolerance for primal residuals [MW]. |
0.001
|
Returns:
| Type | Description |
|---|---|
DCOPFResult
|
DCOPFResult. |
powerzoojax.envs.grid.dc_opf.DCOPFSetup
¶
Precomputed data for DCOPF.
Cost convention: mc_a, mc_b, mc_c are marginal cost coefficients.
MC(p) = mc_a * p² + mc_b * p + mc_c [\(/MWh]
TC(p) = (mc_a/3)*p³ + (mc_b/2)*p² + mc_c*p [\)]
The dispatch objective uses only mc_c (linear), matching the
PowerZoo LP. mc_a and mc_b are retained for total-cost reporting.
A_aug_inv is the precomputed inverse of the (n_units+1) × (n_units+1)
ADMM KKT matrix (constant across all ADMM iterations and all env steps).
Storing the inverse replaces an O(n³) solve with an O(n²) matmul per
ADMM iteration, giving a large speedup for vmap'd training rollouts.
powerzoojax.envs.grid.dc_opf.DCOPFResult
¶
Output of DC OPF solver.
ACOPF¶
powerzoojax.envs.grid.ac_opf.ac_opf(setup, node_load_mw=None, node_load_mvar=None, commitment=None, max_iter=200, tol=0.001, warm_start_unit_power_mw=None, alm_outer_steps=12)
¶
Solve AC-OPF via reduced-space L-BFGS-B with IFT-differentiable NR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
setup
|
ACOPFSetup
|
ACOPFSetup from |
required |
node_load_mw
|
Array
|
(n_bus,) active load [MW]. |
None
|
node_load_mvar
|
Array
|
(n_bus,) reactive load [MVAr]. |
None
|
commitment
|
Array
|
(n_gen,) binary. |
None
|
max_iter
|
int
|
Max L-BFGS-B outer iterations (default 200; typically converges in 20–60 iterations for case5). |
200
|
tol
|
float
|
L-BFGS-B convergence tolerance. |
0.001
|
warm_start_unit_power_mw
|
Array
|
Optional (n_gen,) dispatch guess [MW] (e.g. DC-OPF result). Used to initialise Pg_ctrl. |
None
|
alm_outer_steps
|
int
|
Number of augmented-Lagrangian outer iterations
( |
12
|
Returns:
| Type | Description |
|---|---|
ACOPFResult
|
ACOPFResult with identical field layout to the previous AL-based solver. |
powerzoojax.envs.grid.ac_opf.ACOPFSetup
¶
Precomputed AC-OPF data including bus-type classification.