Skip to main content

powerio_prob/scopf/
types.rs

1use powerio::BusId;
2use serde::{Deserialize, Serialize};
3
4/// One bus row: `(i, uid, v_min, v_max)`.
5#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
6#[non_exhaustive]
7pub struct ScopfBusRow {
8    pub i: BusId,
9    pub uid: String,
10    pub v_min: f64,
11    pub v_max: f64,
12}
13
14/// One shunt row.
15#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
16#[non_exhaustive]
17pub struct ScopfShuntRow {
18    pub j_sh: usize,
19    pub uid: String,
20    pub bus: BusId,
21    pub g_sh: f64,
22    pub b_sh: f64,
23}
24
25/// One AC line row.
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27#[non_exhaustive]
28pub struct ScopfAcLineRow {
29    pub j_ln: usize,
30    pub uid: String,
31    pub to_bus: BusId,
32    pub fr_bus: BusId,
33    pub c_su: f64,
34    pub c_sd: f64,
35    pub s_max: f64,
36    pub g_sr: f64,
37    pub b_sr: f64,
38    pub b_ch: f64,
39    pub g_fr: f64,
40    pub g_to: f64,
41    pub b_fr: f64,
42    pub b_to: f64,
43}
44
45/// One two winding transformer row.
46#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
47#[non_exhaustive]
48pub struct ScopfTransformerRow {
49    pub j_xf: usize,
50    pub uid: String,
51    pub to_bus: BusId,
52    pub fr_bus: BusId,
53    pub c_su: f64,
54    pub c_sd: f64,
55    pub s_max: f64,
56    pub g_sr: f64,
57    pub b_sr: f64,
58    pub b_ch: f64,
59    pub g_fr: f64,
60    pub g_to: f64,
61    pub b_fr: f64,
62    pub b_to: f64,
63}
64
65/// One DC line row.
66#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
67#[non_exhaustive]
68pub struct ScopfDcLineRow {
69    pub j_dc: usize,
70    pub uid: String,
71    pub pdc_max: f64,
72    pub qdc_fr_min: f64,
73    pub qdc_to_min: f64,
74    pub qdc_fr_max: f64,
75    pub qdc_to_max: f64,
76    pub to_bus: BusId,
77    pub fr_bus: BusId,
78}
79
80/// A transformer with a variable phase-shift control range (`vpd`).
81#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
82#[non_exhaustive]
83pub struct ScopfVariablePhaseRow {
84    pub j_xf: usize,
85    pub phi_min: f64,
86    pub phi_max: f64,
87}
88
89/// A transformer with a fixed phase shift (`fpd`).
90#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
91#[non_exhaustive]
92pub struct ScopfFixedPhaseRow {
93    pub j_xf: usize,
94    pub phi_o: f64,
95}
96
97/// A transformer with a variable winding ratio control range (`vwr`).
98#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
99#[non_exhaustive]
100pub struct ScopfVariableRatioRow {
101    pub j_xf: usize,
102    pub tau_min: f64,
103    pub tau_max: f64,
104}
105
106/// A transformer with a fixed winding ratio (`fwr`).
107#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
108#[non_exhaustive]
109pub struct ScopfFixedRatioRow {
110    pub j_xf: usize,
111    pub tau_o: f64,
112}
113
114/// One simple dispatchable device row.
115///
116/// Producers and consumers share this layout. Vector fields use the internal
117/// zero based period order.
118///
119/// The reactive capability block is the `q_bound_cap`/`q_linear_cap` pair and
120/// the parameters of the mode each one selects. The two modes are mutually
121/// exclusive and a device can select neither. A parameter of a mode the device
122/// did not select is `None`. Read the two flags before the parameters.
123#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
124#[non_exhaustive]
125pub struct ScopfDeviceRow {
126    pub bus: BusId,
127    pub uid: String,
128    pub c_on: f64,
129    pub c_su: f64,
130    pub c_sd: f64,
131    pub p_ru: f64,
132    pub p_rd: f64,
133    pub p_ru_su: f64,
134    pub p_rd_sd: f64,
135    pub c_rgu: Vec<f64>,
136    pub c_rgd: Vec<f64>,
137    pub c_scr: Vec<f64>,
138    pub c_nsc: Vec<f64>,
139    pub c_rru_on: Vec<f64>,
140    pub c_rru_off: Vec<f64>,
141    pub c_rrd_on: Vec<f64>,
142    pub c_rrd_off: Vec<f64>,
143    pub c_qru: Vec<f64>,
144    pub c_qrd: Vec<f64>,
145    pub p_rgu_max: f64,
146    pub p_rgd_max: f64,
147    pub p_scr_max: f64,
148    pub p_nsc_max: f64,
149    pub p_rru_on_max: f64,
150    pub p_rru_off_max: f64,
151    pub p_rrd_on_max: f64,
152    pub p_rrd_off_max: f64,
153    pub p_0: f64,
154    pub q_0: f64,
155    pub p_max: Vec<f64>,
156    pub p_min: Vec<f64>,
157    pub q_max: Vec<f64>,
158    pub q_min: Vec<f64>,
159    pub sus: Vec<Vec<f64>>,
160    pub q_bound_cap: i64,
161    pub q_linear_cap: i64,
162    pub beta_ub: Option<f64>,
163    pub beta_lb: Option<f64>,
164    pub q_0_ub: Option<f64>,
165    pub q_0_lb: Option<f64>,
166    pub beta: Option<f64>,
167    /// The intercept of the linear capability line. The GOC3 document calls
168    /// this `q_0`, which this row already uses for `initial_status.q`.
169    pub q_p0: Option<f64>,
170}
171
172/// One active power zonal reserve row.
173#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
174#[non_exhaustive]
175pub struct ScopfActiveReserveRow {
176    pub n_p: usize,
177    pub uid: String,
178    pub c_rgu: f64,
179    pub c_rgd: f64,
180    pub c_scr: f64,
181    pub c_nsc: f64,
182    pub c_rru: f64,
183    pub c_rrd: f64,
184    pub sigma_rgu: f64,
185    pub sigma_rgd: f64,
186    pub sigma_scr: f64,
187    pub sigma_nsc: f64,
188    pub p_rru_min: Vec<f64>,
189    pub p_rrd_min: Vec<f64>,
190}
191
192/// One reactive (reactive-power) zonal reserve row.
193#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
194#[non_exhaustive]
195pub struct ScopfReactiveReserveRow {
196    pub n_q: usize,
197    pub uid: String,
198    pub c_qru: f64,
199    pub c_qrd: f64,
200    pub q_qru_min: Vec<f64>,
201    pub q_qrd_min: Vec<f64>,
202}
203
204/// One (bus, active reserve zone, device) membership row.
205#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
206#[non_exhaustive]
207pub struct ScopfActiveReserveSetRow {
208    pub i: BusId,
209    pub n_p: usize,
210    pub uid: String,
211}
212
213/// One (bus, reactive reserve zone, device) membership row.
214#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
215#[non_exhaustive]
216pub struct ScopfReactiveReserveSetRow {
217    pub i: BusId,
218    pub n_q: usize,
219    pub uid: String,
220}
221
222/// Set sizes for each indexed device class.
223#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
224#[non_exhaustive]
225pub struct ScopfLengths {
226    pub l_j_xf: usize,
227    pub l_j_ln: usize,
228    pub l_j_ac: usize,
229    pub l_j_dc: usize,
230    pub l_j_br: usize,
231    pub l_j_cs: usize,
232    pub l_j_pr: usize,
233    pub l_j_cspr: usize,
234    pub l_j_sh: usize,
235    /// Bus count.
236    pub i: usize,
237    pub l_t: usize,
238    pub l_n_p: usize,
239    pub l_n_q: usize,
240    /// Contingency count.
241    pub k: usize,
242}
243
244/// Static buses, branches, devices, controls, reserves, and memberships.
245#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
246#[non_exhaustive]
247pub struct ScopfStaticData {
248    pub bus: Vec<ScopfBusRow>,
249    pub shunt: Vec<ScopfShuntRow>,
250    pub acl_branch: Vec<ScopfAcLineRow>,
251    pub acx_branch: Vec<ScopfTransformerRow>,
252    pub vpd: Vec<ScopfVariablePhaseRow>,
253    pub fpd: Vec<ScopfFixedPhaseRow>,
254    pub vwr: Vec<ScopfVariableRatioRow>,
255    pub fwr: Vec<ScopfFixedRatioRow>,
256    pub dc_branch: Vec<ScopfDcLineRow>,
257    pub prod: Vec<ScopfDeviceRow>,
258    pub cons: Vec<ScopfDeviceRow>,
259    pub active_reserve: Vec<ScopfActiveReserveRow>,
260    pub reactive_reserve: Vec<ScopfReactiveReserveRow>,
261    pub active_reserve_set_pr: Vec<ScopfActiveReserveSetRow>,
262    pub active_reserve_set_cs: Vec<ScopfActiveReserveSetRow>,
263    pub reactive_reserve_set_pr: Vec<ScopfReactiveReserveSetRow>,
264    pub reactive_reserve_set_cs: Vec<ScopfReactiveReserveSetRow>,
265}
266
267/// One device energy cost curve. `cost[t][m]` is `[c_en, p_max]` for price
268/// block `m` in period `t`.
269#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
270pub(super) struct ScopfCostRow {
271    pub(super) bus: BusId,
272    pub(super) uid: String,
273    pub(super) cost: Vec<Vec<[f64; 2]>>,
274}
275
276/// Static index sets and the cost vectors used by the price block projection.
277#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
278#[non_exhaustive]
279pub(super) struct ScopfStaticDataProjection {
280    pub(super) static_data: ScopfStaticData,
281    pub(super) lengths: ScopfLengths,
282    pub(super) cost_vector_pr: Vec<ScopfCostRow>,
283    pub(super) cost_vector_cs: Vec<ScopfCostRow>,
284}
285
286macro_rules! energy_window_row {
287    ($name:ident, $ind_field:ident, $start_field:ident, $end_field:ident, $bound_field:ident) => {
288        #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
289        pub struct $name {
290            pub $ind_field: usize,
291            pub uid: String,
292            pub $start_field: f64,
293            pub $end_field: f64,
294            pub $bound_field: f64,
295        }
296    };
297}
298
299energy_window_row!(
300    ScopfEnergyWindowMaxPrRow,
301    w_en_max_pr_ind,
302    a_en_max_start,
303    a_en_max_end,
304    e_max
305);
306energy_window_row!(
307    ScopfEnergyWindowMaxCsRow,
308    w_en_max_cs_ind,
309    a_en_max_start,
310    a_en_max_end,
311    e_max
312);
313energy_window_row!(
314    ScopfEnergyWindowMinPrRow,
315    w_en_min_pr_ind,
316    a_en_min_start,
317    a_en_min_end,
318    e_min
319);
320energy_window_row!(
321    ScopfEnergyWindowMinCsRow,
322    w_en_min_cs_ind,
323    a_en_min_start,
324    a_en_min_end,
325    e_min
326);
327
328macro_rules! energy_window_period_row {
329    ($name:ident, $ind_field:ident) => {
330        /// Period membership of one energy window: the period belongs when
331        /// its midpoint falls within the window's `(start, end]` interval.
332        #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
333        pub struct $name {
334            pub $ind_field: usize,
335            pub uid: String,
336            pub t: usize,
337            pub dt: f64,
338        }
339    };
340}
341
342energy_window_period_row!(ScopfEnergyWindowPeriodMaxPrRow, w_en_max_pr_ind);
343energy_window_period_row!(ScopfEnergyWindowPeriodMaxCsRow, w_en_max_cs_ind);
344energy_window_period_row!(ScopfEnergyWindowPeriodMinPrRow, w_en_min_pr_ind);
345energy_window_period_row!(ScopfEnergyWindowPeriodMinCsRow, w_en_min_cs_ind);
346
347/// Energy requirement windows and their period memberships.
348#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
349#[non_exhaustive]
350pub struct ScopfEnergyWindows {
351    pub w_en_max_pr: Vec<ScopfEnergyWindowMaxPrRow>,
352    pub w_en_max_cs: Vec<ScopfEnergyWindowMaxCsRow>,
353    pub w_en_min_pr: Vec<ScopfEnergyWindowMinPrRow>,
354    pub w_en_min_cs: Vec<ScopfEnergyWindowMinCsRow>,
355    pub t_w_en_max_pr: Vec<ScopfEnergyWindowPeriodMaxPrRow>,
356    pub t_w_en_max_cs: Vec<ScopfEnergyWindowPeriodMaxCsRow>,
357    pub t_w_en_min_pr: Vec<ScopfEnergyWindowPeriodMinPrRow>,
358    pub t_w_en_min_cs: Vec<ScopfEnergyWindowPeriodMinCsRow>,
359}
360
361/// One flattened device, period, and price block row.
362#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
363#[non_exhaustive]
364pub struct ScopfPriceBlockRow {
365    pub flat_k: usize,
366    pub uid: String,
367    pub t: usize,
368    pub m: usize,
369    pub c_en: f64,
370    pub p_max: f64,
371}
372
373/// Flattened producer and consumer price blocks.
374#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
375#[non_exhaustive]
376pub struct ScopfPriceBlocks {
377    pub producer: Vec<ScopfPriceBlockRow>,
378    pub consumer: Vec<ScopfPriceBlockRow>,
379}
380
381/// One AC line surviving a contingency.
382#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
383#[non_exhaustive]
384pub struct ScopfAcLineSurvivorRow {
385    pub ctg: usize,
386    pub j_ln: usize,
387    pub uid: String,
388    pub to_bus: BusId,
389    pub fr_bus: BusId,
390    pub b_sr: f64,
391    pub s_max_ctg: f64,
392}
393
394/// One transformer surviving a contingency.
395#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
396#[non_exhaustive]
397pub struct ScopfTransformerSurvivorRow {
398    pub ctg: usize,
399    pub j_xf: usize,
400    pub uid: String,
401    pub to_bus: BusId,
402    pub fr_bus: BusId,
403    pub b_sr: f64,
404    pub s_max_ctg: f64,
405}
406
407/// Per-contingency surviving AC lines and transformers, one group per
408/// contingency in `reliability.contingency` document order.
409#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
410#[non_exhaustive]
411pub struct ScopfAcContingencySurvivors {
412    pub ln: Vec<Vec<ScopfAcLineSurvivorRow>>,
413    pub xf: Vec<Vec<ScopfTransformerSurvivorRow>>,
414}
415
416/// One surviving DC line in one contingency and period.
417#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
418#[non_exhaustive]
419pub struct ScopfDcContingencyFlowRow {
420    pub flat_jtk_dc: usize,
421    pub ctg: usize,
422    pub j_dc: usize,
423    pub to_bus: BusId,
424    pub fr_bus: BusId,
425    pub t: usize,
426    pub dt: f64,
427}
428
429/// The case's four violation prices. The GOC3 document spells them
430/// `p_bus_vio_cost`, `q_bus_vio_cost`, `s_vio_cost`, and `e_vio_cost` under
431/// `network.violation_cost`. Any one of them can be absent and is then `None`:
432/// GOCompetition's 14 bus validation case has no `e_vio_cost`.
433#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
434#[non_exhaustive]
435pub struct ScopfViolationCost {
436    pub p_bus: Option<f64>,
437    pub q_bus: Option<f64>,
438    pub s: Option<f64>,
439    pub e: Option<f64>,
440}
441
442/// Matrix free SCOPF input data.
443///
444/// Internal class, period, contingency, window, and flattened row indices are
445/// zero based and follow the source document order of the section that owns
446/// them. Source UIDs and external bus IDs remain separate fields.
447#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
448#[non_exhaustive]
449pub struct ScopfInstance {
450    /// Buses, branches, devices, controls, reserves, and memberships.
451    pub static_data: ScopfStaticData,
452    pub lengths: ScopfLengths,
453    pub energy_windows: ScopfEnergyWindows,
454    pub price_blocks: ScopfPriceBlocks,
455    pub ac_contingency_survivors: ScopfAcContingencySurvivors,
456    pub dc_contingency_flows: Vec<ScopfDcContingencyFlowRow>,
457    pub violation_cost: ScopfViolationCost,
458    pub device_class_layout: ScopfDeviceClassLayout,
459}
460
461/// How the two device classes sit in the `simple_dispatchable_device` section.
462///
463/// A model that stacks producers and consumers into one variable vector
464/// addresses a device by a per class offset, which is a bijection only when
465/// each class owns one unbroken run. Reading the order without that
466/// precondition is the one mistake this type makes unavailable.
467#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
468#[serde(rename_all = "snake_case", tag = "kind")]
469#[non_exhaustive]
470pub enum ScopfDeviceClassLayout {
471    /// Each class is one unbroken run. `producers_first` says which run
472    /// starts first.
473    Contiguous { producers_first: bool },
474    /// The two classes interleave, so no per class offset addresses a device.
475    Interleaved,
476}
477
478impl ScopfDeviceClassLayout {
479    /// Whether the producer run starts before the consumer run, or `None`
480    /// when the classes interleave and no offset scheme holds.
481    #[must_use]
482    pub fn producers_first(self) -> Option<bool> {
483        match self {
484            Self::Contiguous { producers_first } => Some(producers_first),
485            Self::Interleaved => None,
486        }
487    }
488}