Penalty System
Este conteúdo não está disponível em sua língua ainda.
Purpose
Section titled “Purpose”This spec defines the unified penalty system that ensures LP feasibility across all scenarios while correctly pricing operational costs and constraint violations. See LP Formulation for how penalties enter the objective function.
1. Cascade Resolution
Section titled “1. Cascade Resolution”The LP must always be feasible. Several physical and operational constraints may be impossible to satisfy in extreme scenarios (droughts, equipment failures, etc.). The penalty system provides slack variables with graduated costs to maintain feasibility while signaling the severity of violations.
Cobre’s penalty cascade has three levels of specificity: a stage override on a (entity, stage, penalty) triple is most specific, an entity override defined per entity is next, and a global default at the case level is the fallback. The most specific value present wins.
| Query | Stage Override? | Entity Override? | Result |
|---|---|---|---|
| Hydro 0, Stage 30, spillage | No | Yes (0.005) | 0.005 (entity) |
| Hydro 0, Stage 60, spillage | Yes (0.02) | Yes (0.005) | 0.02 (stage) |
| Hydro 1, Stage 30, spillage | No | No | 0.01 (global default) |
Penalties cascade through three tiers (global default, per-entity, per-stage); defaults live in case-level files.
2. Penalty Categories
Section titled “2. Penalty Categories”Penalties serve three distinct purposes in the LP formulation. Understanding these categories is important for setting appropriate cost magnitudes and interpreting results.
Category 1: Recourse Slacks (LP Feasibility)
Section titled “Category 1: Recourse Slacks (LP Feasibility)”These penalties ensure that the SDDP algorithm has relatively complete recourse — every subproblem must be feasible regardless of the scenario realization. Without these slacks, the LP would be infeasible when generation cannot meet demand or when excess uncontrollable generation cannot be absorbed.
| Penalty | Units | Applied To | Purpose | Typical Range |
|---|---|---|---|---|
deficit_segments | $/MWh | Unmet load per bus | Piecewise cost of load shedding | 1,000–10,000 $/MWh |
excess_cost | $/MWh | Excess generation per bus | Absorb uncontrollable surplus | 0.001–100 $/MWh |
Deficit and excess are conceptually slack variables on the load balance constraint, but they have special names because of their importance in the hydrothermal dispatch application. Deficit represents the value of lost load; excess is a regularization-level cost to eliminate spurious slack generation.
Category 2: Constraint Violation Penalties (Policy Shaping)
Section titled “Category 2: Constraint Violation Penalties (Policy Shaping)”These penalties provide slack for physical or operational constraints that may be impossible to satisfy under extreme conditions (e.g., drought, environmental directives from the system operator). Their cost must be high enough to affect the value function in earlier stages, signaling that the system should avoid states that lead to these violations.
| Penalty | Units | Applied To | Purpose | Typical Range |
|---|---|---|---|---|
storage_violation_below_cost | $/hm3 | Storage < min (dead volume) | Reservoir below dead volume — near-physical limit | above deficit |
filling_target_violation_cost | $/hm3 | Per-stage filling floor missed | Commissioning fill schedule — below deficit | below deficit |
turbined_violation_below_cost | $/(m3/s·h) | Turbined flow < min | Equipment limits / ecological flow | 500–1,000 $/unit |
outflow_violation_below_cost | $/(m3/s·h) | Outflow < min | Environmental minimum flow (operator/regulatory) | 500–1,000 $/unit |
outflow_violation_above_cost | $/(m3/s·h) | Outflow > max | Downstream flooding prevention | 500–1,000 $/unit |
generation_violation_below_cost | $/MWh | Generation < min | Contractual or environmental minimum generation | 1,000–2,000 $/unit |
evaporation_violation_cost | $/(m3/s·h) | Evaporation constraint | Physical constraint (bidirectional, see Section 5) | 5,000+ $/unit |
water_withdrawal_violation_cost | $/(m3/s·h) | Unmet water withdrawal | Human consumption / irrigation commitments | 1,000–5,000 $/unit |
generic_violation_cost | varies | Generic constraint violations | User-defined physical or operational constraints | User-defined |
These penalties create an artificial cost in the objective function that propagates backward through the value function, telling earlier stages to store more water (or dispatch differently) to avoid reaching states where violations are necessary.
Category 3: Regularization Costs (Solution Guidance)
Section titled “Category 3: Regularization Costs (Solution Guidance)”These are small costs inserted into the objective function to guide the solver toward physically preferred solutions when the LP would otherwise be indifferent. They do not represent real costs and should be orders of magnitude smaller than any economic cost to avoid distorting the optimal policy.
| Penalty | Units | Applied To | Purpose | Typical Range |
|---|---|---|---|---|
spillage_cost | $/(m3/s·h) | Water spilled | Prefer turbining over spilling when solver is indifferent | 0.001–0.01 $/unit |
turbined_cost | $/(m3/s·h) | Turbined flow (every hydro) | Prevent interior FPHA solutions; must be > spillage_cost per plant (see below) | 0.01–0.1 $/unit |
diversion_cost | $/(m3/s·h) | Water diverted | Prefer main channel flow; higher than spillage (water leaves cascade) | 0.01–0.1 $/unit |
curtailment_cost | $/MWh | Curtailed non-controllable gen | Prioritize using available non-controllable generation over curtailing it | 0.001–0.01 $/unit |
exchange_cost | $/MWh | Power flow on lines | Prefer local supply; avoid unnecessary inter-bus power flows | 0.01–1.0 $/unit |
Penalty Priority Ordering
Section titled “Penalty Priority Ordering”When setting penalty magnitudes, the following ordering must be maintained:
The storage violation below cost is the highest penalty in the system: it must exceed deficit cost, because keeping a reservoir above its dead volume is more critical than serving load (operating below dead volume risks dam safety and equipment damage). Deficit, in turn, exceeds the operational-constraint, resource, and regularization tiers.
The filling-target penalty sits on a separate rung, below deficit:
A commissioning fill schedule must not be defended as hard as load shedding — when nature physically cannot both serve load and keep a filling reservoir on its accumulation schedule, the solver should serve load and let the filling floor slip. The filling-target penalty’s position relative to the operational-constraint tier (minimum turbined flow, outflow, generation, evaporation, withdrawal) is left to study calibration: it is only pinned below deficit, not against those operational slacks.
Turbined-cost validation rule: For every hydro, turbined_cost > spillage_cost must hold. The rule has two motivations. For hydros using the fpha model, the concave FPHA geometry allows interior LP solutions where the objective is met with less turbined flow than the physical production function would require — the turbined-flow penalty makes every unit of turbined flow carry a small additional cost, which collapses the degenerate interior region. For hydros using constant_productivity, the same penalty applies uniformly so that two solutions with the same generation but different (turbined, spillage) decompositions are tie-broken deterministically; without it, constant-productivity plants paid nothing on the turbine column and the (turbined, spillage) split was left under-determined.
Penalty Ordering Validation
Section titled “Penalty Ordering Validation”The ordering above is enforced by five validation checks. Four follow the main chain (storage → deficit → constraints → resource positivity); a fifth pins the filling-target penalty below deficit as a side branch. All five produce warnings, not errors — violating the ordering degrades policy quality but does not break algorithmic correctness. Two separate rules are hard errors:
- the turbined-cost rule (
turbined_cost > spillage_cost), because it affects LP solution correctness for FPHA plants (interior FPHA solutions), not merely policy quality; - the filling-sufficiency check (see below), because a fill schedule that can never reach the dead volume is a data error, not a calibration choice.
Validation runs on post-resolution penalty values — after the three-tier cascade (global defaults, entity overrides, stage overrides) has been applied. Each (entity, stage) pair is checked independently, so a stage override that inverts the ordering for a specific entity will trigger a warning for that entity at that stage.
| Check | Higher Priority | Lower Priority | Comparison Scope | Severity |
|---|---|---|---|---|
| 1 | storage_violation_below_cost | max(deficit_segments[-1].cost) (last deficit segment on the bus) | Per hydro vs. bus | Warning |
| 2 | max(deficit_segments[-1].cost) | filling_target_violation_cost | Per bus vs. hydro | Warning |
| 3 | max(deficit_segments[-1].cost) | max(constraint_violation_costs) (see below) | Per hydro vs. bus | Warning |
| 4 | min(constraint_violation_costs) | max(resource_costs) (thermal generation costs) | Per bus | Warning |
| 5 | min(resource_costs) | 0 (resource costs must be strictly positive) | System-wide | Warning |
Note that checks 1 and 2 together encode storage_violation_below_cost > deficit > filling_target_violation_cost: the filling-target penalty is compared against deficit only, never against the storage or operational tiers, so its placement among the operational slacks is unconstrained.
Constraint violation costs (used in checks 3 and 4): the set water_withdrawal_violation_cost for the hydro being checked.
Resource costs (used in checks 4 and 5): thermal generation costs (cost_per_mwh) for thermals connected to the bus being checked.
Regularization costs are the Category-3 set exchange_cost; they should stay orders of magnitude below the resource tier (the validator enforces only that resource costs are positive, not a numeric gap to regularization).
Filling sufficiency (hard error): independent of the ordering, a one-sided feasibility check confirms each filling hydro can reach its dead volume on schedule. It requires the cumulative minimum accumulation over the filling window to cover the gap from the seed storage to the dead volume:
where converts m³/s over the stage duration into hm³. A schedule that falls short is rejected as a BusinessRuleViolation — the fill could never complete, so the case is malformed.
Warning aggregation: To avoid excessive output when many (entity, stage) pairs violate the same check, warnings are aggregated: one warning per violated check across all entities and stages. The warning message reports the total violation count and the most extreme example (the pair with the largest inversion magnitude).
3. Penalty Defaults and Overrides
Section titled “3. Penalty Defaults and Overrides”Default penalty values are set at the case level for buses, lines, hydros, and non-controllable sources. Per-entity overrides may appear in the entity registry; per-stage overrides may appear as sparse tables.
Five additional hydro penalty fields support directional and inflow-specific penalties. All are optional and default to the symmetric value when unset:
water_withdrawal_violation_pos_cost— penalty per m3/s of over-withdrawal (withdrew more than target); defaults towater_withdrawal_violation_costwater_withdrawal_violation_neg_cost— penalty per m3/s of under-withdrawal (withdrew less than target); defaults towater_withdrawal_violation_costevaporation_violation_pos_cost— penalty per m³/s of positive deviation of the signed net evaporation flow from the linearised target; defaults toevaporation_violation_costevaporation_violation_neg_cost— penalty per m³/s of negative deviation of the signed net evaporation flow from the linearised target; defaults toevaporation_violation_costinflow_nonnegativity_cost— penalty per m3/s of inflow non-negativity slack activation (method ="penalty"); defaults to 1000.0
The fallback target for the four directional costs is the entity’s resolved symmetric cost. If an entity overrides only its symmetric water_withdrawal_violation_cost (or evaporation_violation_cost), its directional *_pos_cost / *_neg_cost inherit that entity-level override — not the global symmetric default. The resolution order per directional field is therefore: entity directional override → entity symmetric override → global symmetric default; the global directional default is never used as a fallback for a per-entity directional cost.
See Inflow Non-Negativity for details on the penalty method.
Piecewise Deficit
Section titled “Piecewise Deficit”Deficit is modeled as piecewise linear segments. Each segment specifies a depth (MW of unmet demand) and cost. Segments are cumulative: first depth_mw MW at first cost, next at second cost, etc. The last segment MUST have depth_mw: null to ensure LP feasibility (unbounded extension).
Deficit segments can be overridden per bus in the entity registry, but cannot be stage-varying (piecewise structure is too complex for per-stage override).
4. Constraint Violation Coverage
Section titled “4. Constraint Violation Coverage”This section enumerates all constraints in the LP that use slack variables, organized by the system element they belong to. For the full LP constraint formulations, see LP Formulation.
System-Level (Bus)
Section titled “System-Level (Bus)”| Constraint Type | Slack Variable | Direction | Penalty | Category |
|---|---|---|---|---|
| Load balance | deficit | Lower (unmet demand) | deficit_segments | Recourse |
| Load balance | excess | Upper (surplus generation) | excess_cost | Recourse |
Hydro — Flow and Generation Constraints
Section titled “Hydro — Flow and Generation Constraints”| Constraint Type | Slack Variable | Direction | Penalty | Category |
|---|---|---|---|---|
| Minimum storage | storage_violation_below | Lower bound | storage_violation_below_cost | Constraint violation |
| Filling target | filling_target_violation | Lower bound | filling_target_violation_cost | Constraint violation |
| Minimum turbined | turbined_violation_below | Lower bound | turbined_violation_below_cost | Constraint violation |
| Minimum outflow | outflow_violation_below | Lower bound | outflow_violation_below_cost | Constraint violation |
| Maximum outflow | outflow_violation_above | Upper bound | outflow_violation_above_cost | Constraint violation |
| Minimum generation | generation_violation_below | Lower bound | generation_violation_below_cost | Constraint violation |
| Evaporation | evaporation_violation_pos | Upper slack | evaporation_violation_pos_cost | Constraint violation |
| Evaporation | evaporation_violation_neg | Lower slack | evaporation_violation_neg_cost | Constraint violation |
| Water withdrawal | water_withdrawal_violation_pos | Upper slack | water_withdrawal_violation_pos_cost | Constraint violation |
| Water withdrawal | water_withdrawal_violation_neg | Lower slack | water_withdrawal_violation_neg_cost | Constraint violation |
Hydro — Storage Bounds
Section titled “Hydro — Storage Bounds”Minimum storage (min_storage_hm3) uses a slack variable (storage_violation_below) with a very high penalty cost — above deficit cost in the penalty hierarchy. Operating below dead volume risks dam safety and equipment damage, so this is treated as a near-physical violation. The slack ensures LP feasibility in extreme scenarios (severe drought, or the transition from filling to operating when the reservoir did not fully reach min_storage_hm3).
Maximum storage (max_storage_hm3) is a hard physical limit (reservoir capacity). If storage would exceed the maximum, emergency spillage handles the excess. No slack variable is used.
Filling floors: While a plant is filling ([start_stage_id, entry_stage_id)), each stage carries a per-stage minimum end-of-stage storage V_target[t] that rises toward min_storage_hm3, reaching the dead volume at the last filling stage. Each floor is soft, v_h + filling_target_violation >= V_target[t], penalized by filling_target_violation_cost. This penalty sits below deficit (a fill schedule is not defended as hard as load serving); the slack absorbs the gap whenever nature physically did not supply enough water to stay on the accumulation schedule. See §6 Dead-Volume Filling Specifics for the floor trajectory.
Exchange bounds are hard variable bounds on the direct and reverse flow variables. No slack variables. The exchange_cost is a regularization term on the flow variables themselves, not a violation penalty.
Thermals
Section titled “Thermals”Thermal bounds (min_generation, max_generation) are hard constraints. No slack variables. Thermal dispatch is directly controllable (unlike hydro, which depends on exogenous inflows), so if bounds cannot be met, this indicates a data error.
Generic Constraints
Section titled “Generic Constraints”User-defined generic constraints can optionally have slack variables with user-specified penalty costs. These are typically used for physical or operational directives from the system operator, and fall into the constraint violation category.
Non-Controllable Sources
Section titled “Non-Controllable Sources”Non-controllable generation bounds: Generation is bounded by [0, available_generation] where available_generation comes from the scenario pipeline. Both bounds are hard constraints — no slack variables. The curtailment_cost is a regularization term (Category 3) applied to the difference between available and dispatched generation, penalizing curtailment to prioritize using “free” non-controllable energy. Analogous to hydro spillage_cost — curtailment discards available energy.
5. Bidirectional Slacks and Directional Overrides
Section titled “5. Bidirectional Slacks and Directional Overrides”Signed Net Evaporation
Section titled “Signed Net Evaporation”The reservoir-surface evaporation flow is a signed net flux: positive values represent net evaporative loss, negative values represent net rainfall input on the lake surface (precipitation on the reservoir exceeds open-water evaporation). On tropical and subtropical basins, the per-stage net coefficient is routinely negative for 5–7 months per year, so the negative case is the wet-season norm rather than an exceptional condition. Other situations that can yield a negative value include condensation in humid climates and linearisation artefacts at certain volume/coefficient combinations.
The evaporation column is bounded symmetrically, , where is the maximum-magnitude linearised target across stages multiplied by a safety margin. The symmetric bound lets the same column absorb both directions without forcing the over-evaporation slack to fire on every wet-month stage. A negative enters the water-balance equation with the same coefficient as a positive value; the sign of itself controls whether the term subtracts (loss) or adds (rainfall input) from storage.
The evaporation equality row uses bidirectional slack variables to absorb deviations from the linearised target:
Q_evaporated - evap_slack_positive + evap_slack_negative = EvapCoef x Area(V_avg)
where: evap_slack_positive >= 0 (Q_evaporated above the linearised target) evap_slack_negative >= 0 (Q_evaporated below the linearised target)The slacks themselves remain one-sided (); the sign of the target is carried by and EvapCoef. Each slack receives its own penalty: evaporation_violation_pos_cost and evaporation_violation_neg_cost. When the directional costs are unset, both default to the entity’s resolved symmetric evaporation_violation_cost — an entity that overrides only the symmetric cost has that override inherited by its directional costs, rather than reverting to the global default.
Stage-varying overrides follow the same pattern: directional costs may be overridden independently per (entity, stage). Penalty values may vary by stage. Stage-varying overrides are sparse — only entries that differ from defaults are recorded.
6. Hydro Variable Bounds Summary
Section titled “6. Hydro Variable Bounds Summary”| Variable | Lower Bound | Upper Bound | Lower Slack | Upper Slack |
|---|---|---|---|---|
storage | min_storage_hm3 | max_storage_hm3 | With penalty | Emergency spill |
turbined_flow | min_turbined_m3s | max_turbined_m3s | With penalty | Hard |
spillage | 0 | Inf | Hard | — |
outflow | min_outflow_m3s | max_outflow_m3s | With penalty | With penalty |
generation | min_generation_mw | max_generation_mw | With penalty | Hard |
evaporation | With penalty | With penalty | ||
withdrawal | water_withdrawal | water_withdrawal | With penalty | With penalty |
Relationship: outflow = turbined_flow + spillage, generation = f(turbined_flow, storage) (depends on production model)
Dead-Volume Filling Specifics
Section titled “Dead-Volume Filling Specifics”During the filling period ([start_stage_id, entry_stage_id)), the hydro is in commissioning state: the reservoir accumulates water toward min_storage_hm3 (dead volume) before it can begin generating. This is Cobre’s own dead-volume filling model (enchimento de volume morto), whose per-stage accumulation schedule is specified below.
Filling is governed by a minimum accumulation rate filling_min_rate_m3s rather than a single end-of-period target. The rate is not an applied inflow and not a cap on natural inflow — it is the floor below which the reservoir is not allowed to lag the fill schedule. From it, Cobre derives a per-stage minimum end-of-stage storage that rises stage by stage and reaches the dead volume exactly at the last filling stage :
where is the per-stage filling_min_rate_m3s (stage overrides allowed) and converts a flow rate sustained over the stage duration into hm³. Equivalently, the floor at the end of stage is the dead volume minus the accumulation still owed after — a ramp from the seed storage up to . The cumulative climb must cover the gap from the initial storage to the dead volume, a feasibility condition checked up front (the filling-sufficiency hard error in §2).
Operational constraints during filling:
- No generation:
turbined_flow = 0,generation = 0(hard constraint — turbines not installed/operational, so the plant is excluded from the production-function constraints) - Outflow via spillage only: During filling, turbines are not operational. Outflow is limited to spillage once water reaches the spillway crest. Bottom discharge outlets are a simulation-only feature.
- Min outflow requirement: Environmental flow must be met via spillage. If spillage is not physically possible (reservoir level below spillway crest), the
outflow_violation_belowslack absorbs the shortfall. - Natural inflow flows freely: incremental inflow and upstream cascade releases enter the reservoir through the ordinary water balance — there is no retention or impound cap that diverts inflow to storage. Whatever nature provides is what fills the reservoir.
- Storage floor is the fill schedule:
min_storage_hm3does not apply as a bound during filling — storage may sit anywhere in[0, max_storage_hm3]. The active floor is the per-stage soft target below.
Per-stage filling floors: At every filling stage the LP enforces
v_h + filling_target_violation >= V_target[t]with filling_target_violation >= 0 priced at filling_target_violation_cost. This penalty sits below deficit in the hierarchy — when nature cannot both serve load and keep the reservoir on schedule, the solver lets the filling floor slip rather than shed load. The slack activates only when the inflow physically did not supply enough water to clear that stage’s floor. (This replaces the earlier model’s single terminal constraint at entry_stage_id - 1; the floor is now enforced at every filling stage.)
Transition to operating: At entry_stage_id, the hydro becomes operational. The storage at the end of the last filling stage becomes the initial storage for the first operating stage, and the ordinary soft minimum-storage floor (storage_violation_below / storage_violation_below_cost) takes over. If the fill fell short (filling_target_violation > 0 at ), that operating-stage floor slack absorbs the shortfall — both LPs remain feasible.
Penalties during filling: The same outflow violation cost applies, and spillage during filling still incurs spillage_cost. The storage_violation_below slack is not active during filling — the per-stage filling_target_violation slack is the one that fires, and storage is allowed below min_storage_hm3 by design.
7. LP Objective Function Impact
Section titled “7. LP Objective Function Impact”For each stage t, block b, scenario s:
minimize: // Resource costs + Sigma_thermal (generation x cost_per_mwh) + Sigma_contract (import x import_price - export x export_price)
// Recourse slacks (LP feasibility) + Sigma_bus Sigma_segment (deficit_segment x segment_cost) + Sigma_bus (excess x excess_cost)
// Constraint violation penalties (policy shaping) + Sigma_hydro (storage_violation_below x storage_violation_below_cost) + Sigma_hydro_filling (filling_target_violation x filling_target_violation_cost) + Sigma_hydro (turbined_violation_below x turbined_violation_below_cost) + Sigma_hydro (outflow_violation_below x outflow_violation_below_cost) + Sigma_hydro (outflow_violation_above x outflow_violation_above_cost) + Sigma_hydro (generation_violation_below x generation_violation_below_cost) + Sigma_hydro (evaporation_violation_pos x evaporation_violation_pos_cost) + Sigma_hydro (evaporation_violation_neg x evaporation_violation_neg_cost) + Sigma_hydro (water_withdrawal_violation_pos x water_withdrawal_violation_pos_cost) + Sigma_hydro (water_withdrawal_violation_neg x water_withdrawal_violation_neg_cost)
// Regularization costs (solution guidance) + Sigma_hydro (spillage x spillage_cost) + Sigma_hydro (turbined_flow x turbined_cost) + Sigma_hydro (diversion x diversion_cost) + Sigma_ncs (curtailment x curtailment_cost) + Sigma_line (direct_flow x exchange_cost + reverse_flow x exchange_cost)
// Future cost function + theta // Cut approximation (future cost)Implementation in Cobre
Section titled “Implementation in Cobre”The methodology above defines the penalty taxonomy and priority ordering; the tabs below cover how Cobre’s software surface configures and reports the penalty costs that implement it.
Cobre’s penalties.json file supplies the case-wide penalty defaults that the
methodology above prices into the objective. Entity-level and stage-level
overrides layer on top of these defaults through the resolution cascade
described in §1 Cascade Resolution and
§3 Penalty Defaults and Overrides. This
tab is the field-level configuration reference; the penalty-priority ordering
itself stays in the methodology body above, not here.
penalties.json — Global Penalty Defaults
Section titled “penalties.json — Global Penalty Defaults”penalties.json is required in every case directory. All four top-level
sections — bus, line, hydro, non_controllable_source — are required,
and every scalar cost must be strictly positive.
{ "$schema": "https://docs.cobre-rs.dev/schemas/penalties.schema.json", "bus": { "deficit_segments": [ { "depth_mw": 500.0, "cost": 7000.0 }, { "depth_mw": null, "cost": 7500.0 } ], "excess_cost": 100.0 }, "line": { "exchange_cost": 2.0 }, "hydro": { "spillage_cost": 0.01, "turbined_cost": 0.05, "diversion_cost": 0.1, "storage_violation_below_cost": 10000.0, "filling_target_violation_cost": 6000.0, "turbined_violation_below_cost": 500.0, "outflow_violation_below_cost": 500.0, "outflow_violation_above_cost": 500.0, "generation_violation_below_cost": 1000.0, "evaporation_violation_cost": 5000.0, "water_withdrawal_violation_cost": 1000.0 }, "non_controllable_source": { "curtailment_cost": 0.005 }}Bus Section
Section titled “Bus Section”| Field | Type | Description |
|---|---|---|
deficit_segments | array | Piecewise-linear deficit cost tiers — see Bus deficit_segments below. |
excess_cost | number | Cost per MWh of excess (surplus) generation. |
Line Section
Section titled “Line Section”| Field | Type | Description |
|---|---|---|
exchange_cost | number | Cost per MWh of inter-bus exchange flow, applied to every transmission line in both flow directions. |
Hydro Section
Section titled “Hydro Section”| Field | Type | Description |
|---|---|---|
spillage_cost | number | Spillage regularization cost. |
turbined_cost | number | Turbined-flow regularization cost, applied to every hydro’s turbine column. |
diversion_cost | number | Diversion-flow regularization cost. |
storage_violation_below_cost | number | Storage-below-minimum violation cost. |
filling_target_violation_cost | number | Filling-target violation cost. |
turbined_violation_below_cost | number | Turbined-flow-below-minimum violation cost. |
outflow_violation_below_cost | number | Total-outflow-below-minimum violation cost. |
outflow_violation_above_cost | number | Total-outflow-above-maximum violation cost. |
generation_violation_below_cost | number | Generation-below-minimum violation cost. |
evaporation_violation_cost | number | Symmetric evaporation-violation cost (both directions, when the two fields below are absent). |
evaporation_violation_pos_cost | number or null | Optional over-evaporation override. Absent = the resolved symmetric evaporation_violation_cost. |
evaporation_violation_neg_cost | number or null | Optional under-evaporation override. Absent = the resolved symmetric evaporation_violation_cost. |
water_withdrawal_violation_cost | number | Symmetric water-withdrawal-violation cost. |
water_withdrawal_violation_pos_cost | number or null | Optional over-withdrawal override. Absent = the resolved symmetric water_withdrawal_violation_cost. |
water_withdrawal_violation_neg_cost | number or null | Optional under-withdrawal override. Absent = the resolved symmetric water_withdrawal_violation_cost. |
inflow_nonnegativity_cost | number or null | Optional inflow non-negativity slack cost. Absent defaults to 1000.0. |
The entity-level hydros[].penalties block that overrides these defaults per
plant — together with the directional-cost fallback order and the
evaporation / filling config blocks these particular costs price — is
documented in the
Hydro Production Models Configure tab.
This chapter does not repeat that field list.
Non-Controllable Source Section
Section titled “Non-Controllable Source Section”| Field | Type | Description |
|---|---|---|
curtailment_cost | number | Cost per MWh of curtailed non-controllable generation. |
Bus deficit_segments — Two-Tier Resolution
Section titled “Bus deficit_segments — Two-Tier Resolution”Deficit is priced through a piecewise-linear cost curve rather than a single
flat value. Each segment is a { depth_mw, cost } pair; segments are
cumulative — the first depth_mw MW of unmet demand cost the first segment’s
cost, the next depth_mw MW cost the second segment’s cost, and so on:
"deficit_segments": [ { "depth_mw": 500.0, "cost": 1000.0 }, { "depth_mw": null, "cost": 5000.0 }]| Field | Type | Description |
|---|---|---|
depth_mw | number or null | MW of deficit covered by this segment. The last segment must be null — unbounded, which guarantees the LP always has a feasible solution regardless of the shortfall size. |
cost | number | Cost per MWh of deficit in this segment. Must be positive; segments must be listed in ascending cost order. |
deficit_segments resolves from exactly two sources and does not support
stage-level overrides:
- Bus-level override — the
deficit_segmentsarray on the bus object insystem/buses.json. - Global default — the
bus.deficit_segmentsarray inpenalties.json.
excess_cost resolves through a different pair of tiers: there is no
bus-level excess_cost override, but the global default may be refined per
stage via constraints/penalty_overrides_bus.parquet (see the Inputs &
Outputs tab).
Line and Non-Controllable-Source Overrides
Section titled “Line and Non-Controllable-Source Overrides”exchange_cost (line) and curtailment_cost (non-controllable source) each
fall back to their penalties.json global default when no override applies
at a more specific tier:
| Field | Entity-level override | Stage-level override |
|---|---|---|
line.exchange_cost | lines[].exchange_cost in system/lines.json | constraints/penalty_overrides_line.parquet |
non_controllable_source.curtailment_cost | not supported | constraints/penalty_overrides_ncs.parquet |
Resolution Cascade at a Glance
Section titled “Resolution Cascade at a Glance”The table below consolidates the tiers each penalty field supports — the tiers differ per field, so this is a lookup table, not a single uniform rule:
| Penalty field | Stage-level override | Entity-level override | Global default |
|---|---|---|---|
bus.deficit_segments | not supported | buses[].deficit_segments | penalties.json → bus.deficit_segments |
bus.excess_cost | penalty_overrides_bus.parquet | not supported | penalties.json → bus.excess_cost |
line.exchange_cost | penalty_overrides_line.parquet | lines[].exchange_cost | penalties.json → line.exchange_cost |
hydro.* (11 cost fields) | penalty_overrides_hydro.parquet (subset) | hydros[].penalties — see the Hydro Configure tab | penalties.json → hydro.* |
non_controllable_source.curtailment_cost | penalty_overrides_ncs.parquet | not supported | penalties.json → non_controllable_source.curtailment_cost |
This is a topic-scoped index of the files the penalty system touches — it names each file and its role, it does not repeat their field-by-field schemas. The exhaustive, field-by-field case-directory and output reference is owned by the Reference corpus (Case Directory Format and Output Format pages).
Inputs
Section titled “Inputs”| File | Role |
|---|---|
penalties.json | Case-wide penalty defaults — required in every case; the four sections (bus, line, hydro, non_controllable_source) are covered in the Configure tab. |
constraints/penalty_overrides_bus.parquet | Sparse per-(bus_id, stage_id) override of excess_cost. Deficit segments are not stage-varying, so they have no row here. |
constraints/penalty_overrides_line.parquet | Sparse per-(line_id, stage_id) override of exchange_cost. |
constraints/penalty_overrides_hydro.parquet | Sparse per-(hydro_id, stage_id) override of the hydro penalty fields (a subset of the entity-level penalties block fields — the symmetric costs, not their directional variants). |
constraints/penalty_overrides_ncs.parquet | Sparse per-(source_id, stage_id) override of curtailment_cost. |
system/buses.json (deficit_segments) | Bus entity registry; only its deficit_segments field is penalty configuration (covered in the Configure tab) — the rest of the bus schema belongs to the network topology chapter. |
system/lines.json (exchange_cost) | Transmission line registry; only its exchange_cost field is penalty configuration (covered in the Configure tab) — the rest of the line schema belongs to the network topology chapter. |
system/hydros.json (penalties block) | Hydro plant registry; the entity-level penalties block is documented in the Hydro Production Models Configure tab, not repeated here. |
All penalty-override parquet files use sparse storage: only rows for
(entity_id, stage_id) pairs where a cost differs from the entity-level or
global default are required, and every value present must be strictly
positive and finite.
For the complete field-by-field schema of every file above, see the Case Directory Format reference page in the Reference corpus.
Outputs
Section titled “Outputs”| File / column group | Role |
|---|---|
simulation/costs/ | Per-(stage, block) monetary cost breakdown (27 columns), including one column per penalty category — deficit_cost, excess_cost, storage_violation_cost, filling_target_cost, hydro_violation_cost, the outflow/turbined/generation/evaporation/withdrawal violation costs, inflow_penalty_cost, and generic_violation_cost — alongside the resource-cost columns. |
simulation/violations/generic/ | One row per (stage, block, constraint) where a generic constraint’s slack fired, with the violation magnitude and its monetary cost. |
| Per-hydro violation columns | Slack magnitudes (e.g. storage_violation_below_hm3, filling_target_violation_hm3, the evaporation and water-withdrawal directional slacks) reported alongside every other hydro dispatch column — see the Hydro Production Models Inputs & Outputs tab for the hydro output file this belongs to. |
For the complete output schema (columns, types, file layout), see the Output Format reference page in the Reference corpus.
Cross-References
Section titled “Cross-References”- LP Formulation — Cost taxonomy (§1) and penalty terms (§9)
- Inflow Non-Negativity — Penalty method for inflow non-negativity