use anyhow::Result;
use csv::StringRecord;
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use validator::Validate;
use crate::{parse_optional_record, parse_record};
#[cfg(target_arch = "wasm32")]
use tsify::Tsify;
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
#[cfg(feature = "dataset")]
use soa_derive::StructOfArray;
#[derive(Serialize, Deserialize, Validate, Clone, Debug, Builder, PartialEq)]
#[cfg_attr(
target_arch = "wasm32",
derive(Tsify),
tsify(into_wasm_abi, from_wasm_abi)
)]
#[builder(setter(into))]
#[validate(schema(function = "crate::validate::validate_gen"))]
#[cfg_attr(feature = "pyo3", pyclass)]
#[cfg_attr(
feature = "dataset",
derive(StructOfArray),
soa_derive(Serialize, Deserialize)
)]
pub struct Gen {
#[builder(setter(custom))]
#[validate(range(min = 1))]
pub gen_bus: usize,
#[builder(default)]
pub pg: f64,
#[builder(default)]
pub qg: f64,
#[builder(default = "f64::INFINITY")]
pub qmax: f64,
#[builder(default = "f64::NEG_INFINITY")]
pub qmin: f64,
#[builder(default = "1.0")]
pub vg: f64,
#[builder(default)]
pub mbase: f64,
#[builder(setter(into = false), default = "1")]
#[validate(range(min = 0, max = 1))]
pub gen_status: usize,
#[builder(default = "f64::INFINITY")]
pub pmax: f64,
#[builder(default = "f64::NEG_INFINITY")]
pub pmin: f64,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub pc1: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub pc2: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub qc1min: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub qc1max: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub qc2min: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub qc2max: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub ramp_agc: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub ramp_10: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub ramp_30: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub ramp_q: Option<f64>,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub apf: Option<f64>,
#[builder(setter(custom), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub mu_pmax: Option<f64>,
#[builder(setter(custom), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub mu_pmin: Option<f64>,
#[builder(setter(custom), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub mu_qmax: Option<f64>,
#[builder(setter(custom), default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub mu_qmin: Option<f64>,
}
impl Gen {
pub fn new(gen_bus: usize) -> GenBuilder {
GenBuilder {
gen_bus: Some(gen_bus),
..Default::default()
}
}
}
#[cfg_attr(feature = "pyo3", pymethods)]
impl Gen {
pub fn is_on(&self) -> bool {
self.gen_status != 0
}
pub fn is_off(&self) -> bool {
self.gen_status == 0
}
pub fn is_load(&self) -> bool {
self.pmin < 0.0 && self.pmax == 0.0
}
pub fn is_version_1(&self) -> bool {
self.pc1.is_none()
&& self.pc2.is_none()
&& self.qc1min.is_none()
&& self.qc1max.is_none()
&& self.qc2min.is_none()
&& self.qc2max.is_none()
&& self.ramp_agc.is_none()
&& self.ramp_10.is_none()
&& self.ramp_30.is_none()
&& self.ramp_q.is_none()
&& self.apf.is_none()
}
pub fn is_opf(&self) -> bool {
self.mu_pmax.is_some()
&& self.mu_pmin.is_some()
&& self.mu_qmax.is_some()
&& self.mu_qmin.is_some()
}
}
impl Gen {
pub(crate) fn to_string_record(&self, is_version_1: bool, is_opf: bool) -> StringRecord {
let mut record = StringRecord::new();
record.push_field(&format!("{}", self.gen_bus));
record.push_field(&format!("{}", self.pg));
record.push_field(&format!("{}", self.qg));
record.push_field(&format!("{}", self.qmax));
record.push_field(&format!("{}", self.qmin));
record.push_field(&format!("{}", self.vg));
record.push_field(&format!("{}", self.mbase));
record.push_field(&format!("{}", self.gen_status));
record.push_field(&format!("{}", self.pmax));
record.push_field(&format!("{}", self.pmin));
if !is_version_1 {
record.push_field(&format!("{}", self.pc1.unwrap()));
record.push_field(&format!("{}", self.pc2.unwrap()));
record.push_field(&format!("{}", self.qc1min.unwrap()));
record.push_field(&format!("{}", self.qc1max.unwrap()));
record.push_field(&format!("{}", self.qc2min.unwrap()));
record.push_field(&format!("{}", self.qc2max.unwrap()));
record.push_field(&format!("{}", self.ramp_agc.unwrap()));
record.push_field(&format!("{}", self.ramp_10.unwrap()));
record.push_field(&format!("{}", self.ramp_30.unwrap()));
record.push_field(&format!("{}", self.ramp_q.unwrap()));
record.push_field(&format!("{}", self.apf.unwrap()));
}
if is_opf {
record.push_field(&format!("{}", self.mu_pmax.unwrap()));
record.push_field(&format!("{}", self.mu_pmin.unwrap()));
record.push_field(&format!("{}", self.mu_qmax.unwrap()));
record.push_field(&format!("{}", self.mu_qmin.unwrap()));
}
record
}
pub(crate) fn from_string_record(record: StringRecord) -> Result<Self> {
let mut iter = record.iter();
Ok(Self {
gen_bus: parse_record!(iter, usize),
pg: parse_record!(iter, f64),
qg: parse_record!(iter, f64),
qmax: parse_record!(iter, f64),
qmin: parse_record!(iter, f64),
vg: parse_record!(iter, f64),
mbase: parse_record!(iter, f64),
gen_status: parse_record!(iter, usize),
pmax: parse_record!(iter, f64),
pmin: parse_record!(iter, f64),
pc1: parse_optional_record!(iter, f64),
pc2: parse_optional_record!(iter, f64),
qc1min: parse_optional_record!(iter, f64),
qc1max: parse_optional_record!(iter, f64),
qc2min: parse_optional_record!(iter, f64),
qc2max: parse_optional_record!(iter, f64),
ramp_agc: parse_optional_record!(iter, f64),
ramp_10: parse_optional_record!(iter, f64),
ramp_30: parse_optional_record!(iter, f64),
ramp_q: parse_optional_record!(iter, f64),
apf: parse_optional_record!(iter, f64),
mu_pmax: parse_optional_record!(iter, f64),
mu_pmin: parse_optional_record!(iter, f64),
mu_qmax: parse_optional_record!(iter, f64),
mu_qmin: parse_optional_record!(iter, f64),
})
}
}
impl GenBuilder {
pub fn in_service(&mut self) -> &mut Self {
self.gen_status = Some(1);
self
}
pub fn out_of_service(&mut self) -> &mut Self {
self.gen_status = Some(0);
self
}
}