#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HydroUnitGroupOverride {
pub min_turbined_m3s: Option<f64>,
pub max_turbined_m3s: Option<f64>,
pub min_generation_mw: Option<f64>,
pub max_generation_mw: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct HydroUnitGroupBoundsCountsSpec<'a> {
pub groups_per_plant: &'a [usize],
pub n_stages: usize,
pub max_blocks: usize,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ResolvedHydroUnitGroupBounds {
n_stages: usize,
max_blocks: usize,
plant_group_start: Vec<usize>,
stage: Vec<HydroUnitGroupOverride>,
block: Vec<HydroUnitGroupOverride>,
}
impl Default for ResolvedHydroUnitGroupBounds {
fn default() -> Self {
Self::empty()
}
}
impl ResolvedHydroUnitGroupBounds {
#[must_use]
pub fn empty() -> Self {
Self {
n_stages: 0,
max_blocks: 0,
plant_group_start: Vec::new(),
stage: Vec::new(),
block: Vec::new(),
}
}
#[must_use]
pub fn new(spec: &HydroUnitGroupBoundsCountsSpec<'_>) -> Self {
let mut plant_group_start = Vec::with_capacity(spec.groups_per_plant.len() + 1);
plant_group_start.push(0);
let mut total = 0usize;
for &n in spec.groups_per_plant {
total += n;
plant_group_start.push(total);
}
Self {
n_stages: spec.n_stages,
max_blocks: spec.max_blocks,
plant_group_start,
stage: Vec::new(),
block: Vec::new(),
}
}
fn total_groups(&self) -> usize {
self.plant_group_start.last().copied().unwrap_or(0)
}
fn group_slot(&self, hydro_idx: usize, group_pos: usize) -> Option<usize> {
let &start = self.plant_group_start.get(hydro_idx)?;
let &end = self.plant_group_start.get(hydro_idx + 1)?;
let slot = start + group_pos;
(slot < end).then_some(slot)
}
fn stage_flat_index(
&self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
) -> Option<usize> {
let slot = self.group_slot(hydro_idx, group_pos)?;
(stage_idx < self.n_stages).then_some(slot * self.n_stages + stage_idx)
}
fn block_flat_index(
&self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
block_idx: usize,
) -> Option<usize> {
let stage_flat = self.stage_flat_index(hydro_idx, group_pos, stage_idx)?;
(block_idx < self.max_blocks).then_some(stage_flat * self.max_blocks + block_idx)
}
#[inline]
#[must_use]
pub fn stage_override(
&self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
) -> HydroUnitGroupOverride {
self.stage_flat_index(hydro_idx, group_pos, stage_idx)
.and_then(|idx| self.stage.get(idx))
.copied()
.unwrap_or_default()
}
#[inline]
pub fn stage_override_mut(
&mut self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
) -> Option<&mut HydroUnitGroupOverride> {
let idx = self.stage_flat_index(hydro_idx, group_pos, stage_idx)?;
if self.stage.is_empty() {
self.stage =
vec![HydroUnitGroupOverride::default(); self.total_groups() * self.n_stages];
}
self.stage.get_mut(idx)
}
#[inline]
#[must_use]
pub fn block_override(
&self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
block_idx: usize,
) -> HydroUnitGroupOverride {
self.block_flat_index(hydro_idx, group_pos, stage_idx, block_idx)
.and_then(|idx| self.block.get(idx))
.copied()
.unwrap_or_default()
}
#[inline]
pub fn block_override_mut(
&mut self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
block_idx: usize,
) -> Option<&mut HydroUnitGroupOverride> {
let idx = self.block_flat_index(hydro_idx, group_pos, stage_idx, block_idx)?;
if self.block.is_empty() {
self.block = vec![
HydroUnitGroupOverride::default();
self.total_groups() * self.n_stages * self.max_blocks
];
}
self.block.get_mut(idx)
}
#[inline]
#[must_use]
pub fn override_at_block(
&self,
hydro_idx: usize,
group_pos: usize,
stage_idx: usize,
block_idx: usize,
) -> HydroUnitGroupOverride {
let stage = self.stage_override(hydro_idx, group_pos, stage_idx);
let block = self.block_override(hydro_idx, group_pos, stage_idx, block_idx);
HydroUnitGroupOverride {
min_turbined_m3s: block.min_turbined_m3s.or(stage.min_turbined_m3s),
max_turbined_m3s: block.max_turbined_m3s.or(stage.max_turbined_m3s),
min_generation_mw: block.min_generation_mw.or(stage.min_generation_mw),
max_generation_mw: block.max_generation_mw.or(stage.max_generation_mw),
}
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.stage.is_empty() && self.block.is_empty()
}
}