Topology pre-processing#

merge_zero_z(case, threshold, *, xfmr_threshold=None)#

Consolidate electrically equivalent network nodes in place.

The merged case stays a valid case: record ids that collide at a representative bus are renamed. The lowering calls merge_topology instead, because it keys nothing on merged identities.

Parameters:
  • case -- Parsed case to update in place.

  • threshold -- Branch impedance-magnitude limit in per unit on the system base. A nonpositive value disables branch consolidation.

  • xfmr_threshold -- Optional separate impedance-magnitude limit for two-winding transformers. Zero disables transformer consolidation.

Returns:

Counts of affected buses, branches, transformers, and device records.

class gpf.MergeZeroZStats#

Summarize the network records affected by bus consolidation.

scale_loads(case, *, areas=None, zones=None, owners=None, buses=None, bus_type=None, interruptible='both', target_load_mw=None, multiplier=None, percent=None, increment_mw=None, q_mode='scale', q_target_mvar=None, q_percent=None, q_target_pf=None, q_increment_mvar=None)#

Scale selected in-service loads in place.

Supply exactly one active-power method. All supplied selectors intersect. The function does not change out-of-service loads. Loads with SCALE=0 and loads excluded by interruptible remain fixed, but their demand counts toward target_load_mw.

Parameters:
  • case -- Case to modify.

  • areas -- Optional area numbers.

  • zones -- Optional zone numbers.

  • owners -- Optional owner numbers.

  • buses -- Optional bus numbers.

  • bus_type -- None selects all bus types. "pq" selects type 1. "pv_slack" selects types 2 and 3.

  • interruptible -- "both" selects all loads. "non_interruptible" or "interruptible" selects one class.

  • target_load_mw -- Target total active load in MW for the selected group.

  • multiplier -- Active-load multiplier. For example, 1.05 adds 5 percent.

  • percent -- Active-load percentage change. For example, 5 adds 5 percent.

  • increment_mw -- Active-load change in MW for the selected group.

  • q_mode -- Reactive-load treatment. "scale" uses the active-load multiplier. "preserve" keeps reactive load unchanged. "zero" sets it to zero. "target", "percent", "pf", and "increment" use their matching argument.

  • q_target_mvar -- Target total reactive load in Mvar for "target".

  • q_percent -- Reactive-load percentage change for "percent".

  • q_target_pf -- Target power-factor magnitude in (0, 1] for "pf". The original reactive-load sign determines leading or lagging sign.

  • q_increment_mvar -- Reactive-load change in Mvar for "increment".

Returns:

The multiplier that the function applied to active load.

Raises:

ValueError -- The caller supplies an invalid option combination, a selector matches no load, or the requested change is undefined.

Examples

Increase active and reactive load in area 2 by 5 percent:

>>> alpha = gpf.scale_loads(case, areas=[2], percent=5.0)

Set active load at two buses to 500 MW and preserve reactive load:

>>> alpha = gpf.scale_loads(
...     case,
...     buses=[101, 205],
...     target_load_mw=500.0,
...     q_mode="preserve",
... )
curtail_loads(case, *, loads)#

Curtail named constant-power loads in place.

Parameters:
  • case -- Case to modify.

  • loads -- (bus, load_id, curtailment_mw) rows. The function limits each curtailment to the load's positive constant-power PL.

Returns:

Total active load curtailed in MW.

Raises:

ValueError -- A named load is absent, out of service, or has no positive constant-power active demand.

Notes

The function reduces PL without crossing zero and scales QL by the same factor. It does not change current or admittance load terms.

dispatch_generators(case, *, machines, delta_mw, enforce_pt_pb=False)#

Shift active generation across named machines in place.

Parameters:
  • case -- Case to modify.

  • machines -- (bus, generator_id, participation_factor) rows. The function normalizes the factors by their sum.

  • delta_mw -- Total active-generation change in MW. A positive value raises output. A negative value lowers output.

  • enforce_pt_pb -- Clamp each resulting output to [PB, PT] when true.

Returns:

The total MW change that the function applied. This value can differ from delta_mw when generator limits bind.

Raises:

ValueError -- A named generator is absent or out of service, or the participation factors sum to zero.

Examples

Share a 100 MW increase between two generators in a 60:40 ratio:

>>> applied_mw = gpf.dispatch_generators(
...     case,
...     machines=[(101, "1", 0.6), (205, "2", 0.4)],
...     delta_mw=100.0,
...     enforce_pt_pb=True,
... )
scale_generators(case, *, areas=None, zones=None, owners=None, buses=None, target_pg_mw=None, multiplier=None, enforce_pt_pb=False)#

Scale selected in-service generator active output in place.

Supply exactly one of target_pg_mw and multiplier. All supplied selectors intersect. The function does not change reactive output or reactive limits.

Parameters:
  • case -- Case to modify.

  • areas -- Optional bus-area numbers.

  • zones -- Optional bus-zone numbers.

  • owners -- Optional generator-owner numbers.

  • buses -- Optional generator bus numbers.

  • target_pg_mw -- Target total active generation in MW for the selected machines.

  • multiplier -- Active-output multiplier for each selected machine.

  • enforce_pt_pb -- Enforce each non-slack generator's PB and PT limits when true. This option requires target_pg_mw. Selected slack generators receive the remaining target in proportion to their original active output.

Returns:

The active-output multiplier calculated from the target or supplied by the caller.

Raises:

ValueError -- The caller does not supply exactly one scaling method, a selector matches no in-service generator, or limit enforcement is requested without a target.

Examples

Set total in-service generation in area 2 to 1,500 MW:

>>> alpha = gpf.scale_generators(
...     case, areas=[2], target_pg_mw=1500.0, enforce_pt_pb=True
... )
scale_shunts(case, *, areas=None, zones=None, owners=None, buses=None, target_g_mw=None, multiplier_g=None, target_cap_mvar=None, target_reactor_mvar=None)#

Scale in-service FIXED SHUNT records in place.

All supplied selectors intersect. The function does not change out-of-service shunts.

Parameters:
  • case -- Case to modify.

  • areas -- Optional bus-area numbers.

  • zones -- Optional bus-zone numbers.

  • owners -- Optional bus-owner numbers.

  • buses -- Optional shunt bus numbers.

  • target_g_mw -- Target total active shunt conductance in MW. Do not supply this argument with multiplier_g.

  • multiplier_g -- Multiplier for active shunt conductance.

  • target_cap_mvar -- Capacitor target parameter in Mvar. Supply this argument together with target_reactor_mvar.

  • target_reactor_mvar -- Reactor target parameter in Mvar. Supply this argument together with target_cap_mvar.

Returns:

None.

Raises:

ValueError -- A selector matches no in-service fixed shunt, active-shunt scaling is ambiguous, only one reactive target is supplied, or a requested target has no applicable shunt records.

Notes

Active-shunt scaling applies one common multiplier. Reactive-shunt scaling preserves each record's share within its original capacitor or reactor group. Let ct and rt be the two target parameters. Let S_c and S_r be the sums of the original positive and absolute negative BL values. A capacitor receives ct * ct * BL / (S_c * (ct + rt)). A reactor receives ct * rt * abs(BL) / (S_r * (ct + rt)) and becomes nonnegative. A zero BL remains unchanged.

start_generators(case, *, machines)#

Bring named out-of-service generators online in place.

Parameters:
  • case -- Case to modify.

  • machines -- (bus, generator_id, output_mw) rows. The function clamps each requested output to [0, PT].

Returns:

Total active output in MW assigned to the started generators.

Raises:

ValueError -- A named generator is absent or already in service.

Notes

The function sets QG, QT, and QB to zero. The started generators therefore supply no reactive-power support. The function can start a generator below PB because it does not perform unit commitment.

apply_substation_topology(case)#

Apply node-breaker topology to a copy of a case.

Parameters:

case -- Case with substation topology records.

Returns:

The updated case and a report of topology changes.

class TopologyApplyReport(splits=<factory>, boundary_branches=<factory>, deenergized=<factory>, unmoved=<factory>)#

Describe changes from applying substation topology.

splits#

Substation, source bus, new bus, and node numbers for each split.

Type:

list[tuple[int, int, int, tuple[int, ...]]]

boundary_branches#

End buses, circuit identifier, and switch state for each generated boundary branch.

Type:

list[tuple[int, int, str, bool]]

deenergized#

Bus numbers that became isolated.

Type:

list[int]

unmoved#

Equipment types and identifiers that could not be relocated.

Type:

list[tuple[str, str]]

is_noop()#

Return true when topology application changed nothing.

summary()#

Return a one-line summary of the topology changes.