use std::sync::Arc;
use powerio_core::Error;
use crate::diagnostics::codes;
use crate::instance::AcScucInstance;
use crate::solution::{Producer, Residuals, Termination};
#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct ScucNetworkOutputs {
pub bus_vm: Vec<Vec<f64>>,
pub bus_va: Vec<Vec<f64>>,
pub shunt_step: Vec<Vec<f64>>,
pub ac_line_on_status: Vec<Vec<f64>>,
pub transformer_tm: Vec<Vec<f64>>,
pub transformer_ta: Vec<Vec<f64>>,
pub transformer_on_status: Vec<Vec<f64>>,
pub dc_line_pdc_fr: Vec<Vec<f64>>,
pub dc_line_qdc_fr: Vec<Vec<f64>>,
pub dc_line_qdc_to: Vec<Vec<f64>>,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct ScucDeviceOutputs {
pub on_status: Vec<Vec<f64>>,
pub p_on: Vec<Vec<f64>>,
pub q: Vec<Vec<f64>>,
pub p_reg_res_up: Vec<Vec<f64>>,
pub p_reg_res_down: Vec<Vec<f64>>,
pub p_syn_res: Vec<Vec<f64>>,
pub p_nsyn_res: Vec<Vec<f64>>,
pub p_ramp_res_up_online: Vec<Vec<f64>>,
pub p_ramp_res_down_online: Vec<Vec<f64>>,
pub q_res_up: Vec<Vec<f64>>,
pub q_res_down: Vec<Vec<f64>>,
}
pub const SCUC_NETWORK_OUTPUT_SERIES: [&str; 10] = [
"bus_vm",
"bus_va",
"shunt_step",
"ac_line_on_status",
"transformer_tm",
"transformer_ta",
"transformer_on_status",
"dc_line_pdc_fr",
"dc_line_qdc_fr",
"dc_line_qdc_to",
];
pub const SCUC_DEVICE_OUTPUT_SERIES: [&str; 11] = [
"on_status",
"p_on",
"q",
"p_reg_res_up",
"p_reg_res_down",
"p_syn_res",
"p_nsyn_res",
"p_ramp_res_up_online",
"p_ramp_res_down_online",
"q_res_up",
"q_res_down",
];
#[derive(Clone, Debug)]
pub struct AcScucSolution {
instance: Arc<AcScucInstance>,
termination: Termination,
residuals: Residuals,
producer: Producer,
network_outputs: ScucNetworkOutputs,
device_outputs: ScucDeviceOutputs,
objective: Option<f64>,
}
impl AcScucSolution {
pub fn new(
instance: Arc<AcScucInstance>,
termination: Termination,
network_outputs: ScucNetworkOutputs,
device_outputs: ScucDeviceOutputs,
objective: Option<f64>,
) -> Result<Self, Error> {
let periods = instance.inputs().dt.len();
let check = |what: &'static str, series: &Vec<Vec<f64>>| -> Result<(), Error> {
if series.is_empty() || series.len() == periods {
Ok(())
} else {
Err(Error::new(
&codes::BUILD_SOLUTION_SHAPE_MISMATCH,
format!(
"{what} carries {} time rows; the instance states {periods} intervals",
series.len()
),
))
}
};
check("bus vm", &network_outputs.bus_vm)?;
check("bus va", &network_outputs.bus_va)?;
check("shunt step", &network_outputs.shunt_step)?;
check("ac line on status", &network_outputs.ac_line_on_status)?;
check("transformer tm", &network_outputs.transformer_tm)?;
check("transformer ta", &network_outputs.transformer_ta)?;
check(
"transformer on status",
&network_outputs.transformer_on_status,
)?;
check("dc line pdc_fr", &network_outputs.dc_line_pdc_fr)?;
check("dc line qdc_fr", &network_outputs.dc_line_qdc_fr)?;
check("dc line qdc_to", &network_outputs.dc_line_qdc_to)?;
check("device on status", &device_outputs.on_status)?;
check("device p_on", &device_outputs.p_on)?;
check("device q", &device_outputs.q)?;
check("device p_reg_res_up", &device_outputs.p_reg_res_up)?;
check("device p_reg_res_down", &device_outputs.p_reg_res_down)?;
check("device p_syn_res", &device_outputs.p_syn_res)?;
check("device p_nsyn_res", &device_outputs.p_nsyn_res)?;
check(
"device p_ramp_res_up_online",
&device_outputs.p_ramp_res_up_online,
)?;
check(
"device p_ramp_res_down_online",
&device_outputs.p_ramp_res_down_online,
)?;
check("device q_res_up", &device_outputs.q_res_up)?;
check("device q_res_down", &device_outputs.q_res_down)?;
Ok(Self {
instance,
termination,
residuals: Residuals::default(),
producer: None,
network_outputs,
device_outputs,
objective,
})
}
#[must_use]
pub fn instance(&self) -> &AcScucInstance {
&self.instance
}
#[must_use]
pub fn shared_instance(&self) -> Arc<AcScucInstance> {
Arc::clone(&self.instance)
}
#[must_use]
pub const fn termination(&self) -> &Termination {
&self.termination
}
#[must_use]
pub const fn residuals(&self) -> &Residuals {
&self.residuals
}
#[must_use]
pub fn producer(&self) -> Option<&str> {
self.producer.as_deref()
}
#[must_use]
pub fn with_producer(mut self, producer: impl Into<String>) -> Self {
self.producer = Some(producer.into());
self
}
#[must_use]
pub fn with_residuals(mut self, residuals: Residuals) -> Self {
self.residuals = residuals;
self
}
#[must_use]
pub const fn network_outputs(&self) -> &ScucNetworkOutputs {
&self.network_outputs
}
#[must_use]
pub const fn device_outputs(&self) -> &ScucDeviceOutputs {
&self.device_outputs
}
#[must_use]
pub const fn objective(&self) -> Option<f64> {
self.objective
}
}
#[cfg(test)]
mod series_vocabulary_tests {
use super::*;
#[test]
fn every_output_field_is_named_in_the_series_constants() {
let ScucNetworkOutputs {
bus_vm: _,
bus_va: _,
shunt_step: _,
ac_line_on_status: _,
transformer_tm: _,
transformer_ta: _,
transformer_on_status: _,
dc_line_pdc_fr: _,
dc_line_qdc_fr: _,
dc_line_qdc_to: _,
} = ScucNetworkOutputs::default();
assert_eq!(SCUC_NETWORK_OUTPUT_SERIES.len(), 10);
let ScucDeviceOutputs {
on_status: _,
p_on: _,
q: _,
p_reg_res_up: _,
p_reg_res_down: _,
p_syn_res: _,
p_nsyn_res: _,
p_ramp_res_up_online: _,
p_ramp_res_down_online: _,
q_res_up: _,
q_res_down: _,
} = ScucDeviceOutputs::default();
assert_eq!(SCUC_DEVICE_OUTPUT_SERIES.len(), 11);
}
}