use std::collections::HashMap;
use std::ops::Range;
#[derive(Debug, Clone, Copy)]
pub struct EvaporationIndices {
pub evaporation_flow_col: usize,
pub f_evap_plus_col: usize,
pub f_evap_minus_col: usize,
pub evap_row: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct FphaRowRange {
pub start: usize,
pub planes_per_block: usize,
}
#[derive(Debug, Clone)]
pub struct StageIndexer {
pub storage: Range<usize>,
pub inflow_lags: Range<usize>,
pub storage_in: Range<usize>,
pub theta: usize,
pub n_state: usize,
pub storage_fixing: Range<usize>,
pub lag_fixing: Range<usize>,
pub anticipated_state: Range<usize>,
pub anticipated_state_fixing: Range<usize>,
pub n_anticipated: usize,
pub k_max: usize,
pub hydro_count: usize,
pub max_par_order: usize,
pub turbine: Range<usize>,
pub spillage: Range<usize>,
pub diversion: Range<usize>,
pub thermal: Range<usize>,
pub anticipated_decision: Range<usize>,
pub anticipated_state_out: Range<usize>,
pub anticipated_lead_stages: Vec<usize>,
pub anticipated_thermal_indices: Vec<usize>,
pub(crate) anticipated_local_by_sys_pos: HashMap<usize, usize>,
pub line_fwd: Range<usize>,
pub line_rev: Range<usize>,
pub deficit: Range<usize>,
pub max_deficit_segments: usize,
pub excess: Range<usize>,
pub n_blks: usize,
pub n_thermals: usize,
pub n_lines: usize,
pub n_buses: usize,
pub water_balance: Range<usize>,
pub load_balance: Range<usize>,
pub inflow_slack: Range<usize>,
pub inflow_slack_rows: Range<usize>,
pub has_inflow_penalty: bool,
pub generation: Range<usize>,
pub n_fpha_hydros: usize,
pub fpha_hydro_indices: Vec<usize>,
pub fpha_rows: Vec<FphaRowRange>,
pub n_evap_hydros: usize,
pub evap_hydro_indices: Vec<usize>,
pub evap_indices: Vec<EvaporationIndices>,
pub withdrawal_slack_neg: Range<usize>,
pub withdrawal_slack_pos: Range<usize>,
pub has_withdrawal: bool,
pub outflow_below_slack: Range<usize>,
pub outflow_above_slack: Range<usize>,
pub turbine_below_slack: Range<usize>,
pub generation_below_slack: Range<usize>,
pub min_outflow_rows: Range<usize>,
pub max_outflow_rows: Range<usize>,
pub min_turbine_rows: Range<usize>,
pub min_generation_rows: Range<usize>,
pub anticipated_fishing: Range<usize>,
pub anticipated_fishing_start: usize,
pub has_operational_violations: bool,
pub generic_constraint_rows: Range<usize>,
pub generic_constraint_slack: Range<usize>,
pub n_generic_constraints_active: usize,
pub ncs_generation: Range<usize>,
pub z_inflow: Range<usize>,
pub z_inflow_rows: Range<usize>,
pub z_inflow_row_start: usize,
pub nonzero_state_indices: Vec<usize>,
pub state_to_lp_column_map: Vec<usize>,
}
pub struct EquipmentCounts {
pub hydro_count: usize,
pub max_par_order: usize,
pub n_thermals: usize,
pub n_lines: usize,
pub n_buses: usize,
pub n_blks: usize,
pub has_inflow_penalty: bool,
pub max_deficit_segments: usize,
pub n_anticipated: usize,
pub k_max: usize,
pub anticipated_lead_stages: Vec<usize>,
pub anticipated_thermal_indices: Vec<usize>,
}
pub struct FphaColumnLayout {
pub hydro_indices: Vec<usize>,
pub planes_per_hydro: Vec<usize>,
}
pub struct EvapConfig {
pub hydro_indices: Vec<usize>,
}
impl StageIndexer {
#[must_use]
pub fn evap_indices(&self, local_idx: usize) -> &EvaporationIndices {
debug_assert!(
local_idx < self.n_evap_hydros,
"evap local index {local_idx} out of bounds (n_evap_hydros = {})",
self.n_evap_hydros
);
&self.evap_indices[local_idx]
}
#[must_use]
pub(crate) fn generation_col_start(&self) -> usize {
if self.has_inflow_penalty {
self.inflow_slack.end
} else {
self.excess.end
}
}
#[must_use]
pub(crate) fn evap_col_start(&self) -> usize {
self.generation_col_start() + self.n_fpha_hydros * self.n_blks
}
#[must_use]
pub(crate) fn fpha_rows_end(&self) -> usize {
let total_planes: usize = self.fpha_rows.iter().map(|r| r.planes_per_block).sum();
self.load_balance.end + self.n_blks * total_planes
}
}
const _: () = {
fn assert_send_sync<T: Send + Sync>() {}
fn check() {
assert_send_sync::<StageIndexer>();
}
let _ = check;
};
#[cfg(test)]
mod tests {
use super::{EvaporationIndices, FphaRowRange};
use crate::indexer::StageIndexer;
fn indexer_3_2() -> StageIndexer {
StageIndexer::new(3, 2)
}
#[test]
fn clone_and_debug() {
let idx = indexer_3_2();
let cloned = idx.clone();
assert_eq!(cloned.theta, idx.theta);
assert_eq!(cloned.n_state, idx.n_state);
let debug_str = format!("{idx:?}");
assert!(debug_str.contains("StageIndexer"));
}
#[test]
fn evap_indices_debug_clone_copy() {
let ei = EvaporationIndices {
evaporation_flow_col: 10,
f_evap_plus_col: 11,
f_evap_minus_col: 12,
evap_row: 5,
};
let cloned = ei;
assert_eq!(cloned.evaporation_flow_col, 10);
assert_eq!(cloned.evap_row, 5);
let debug_str = format!("{ei:?}");
assert!(debug_str.contains("EvaporationIndices"));
}
#[test]
fn fpha_row_range_debug_clone_copy() {
let r = FphaRowRange {
start: 42,
planes_per_block: 5,
};
let cloned = r;
assert_eq!(cloned.start, 42);
assert_eq!(cloned.planes_per_block, 5);
let debug_str = format!("{r:?}");
assert!(debug_str.contains("FphaRowRange"));
}
}