use powerio_core::ComponentId;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ScucDeviceKind {
Producer,
Consumer,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucStartupCostAdjustment {
pub cost: f64,
pub maximum_down_time: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucStartupLimit {
pub start_time: f64,
pub end_time: f64,
pub maximum_startups: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucEnergyRequirement {
pub start_time: f64,
pub end_time: f64,
pub energy: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucInitialCommitment {
pub accumulated_up_time: f64,
pub accumulated_down_time: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucRampLimits {
pub up: f64,
pub down: f64,
pub startup: f64,
pub shutdown: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucReserveLimits {
pub regulation_up: f64,
pub regulation_down: f64,
pub synchronized: f64,
pub nonsynchronized: f64,
pub ramping_up_online: f64,
pub ramping_down_online: f64,
pub ramping_up_offline: f64,
pub ramping_down_offline: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case", tag = "kind")]
#[non_exhaustive]
pub enum ScucReactiveCapability {
None,
Linear {
reactive_power_at_zero_active_power: f64,
slope: f64,
},
Bounded {
reactive_power_at_zero_active_power_min: f64,
reactive_power_at_zero_active_power_max: f64,
slope_min: f64,
slope_max: f64,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucEnergyCostBlock {
pub marginal_cost: f64,
pub block_size: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucReserveCosts {
pub regulation_up: f64,
pub regulation_down: f64,
pub synchronized: f64,
pub nonsynchronized: f64,
pub ramping_up_online: f64,
pub ramping_down_online: f64,
pub ramping_up_offline: f64,
pub ramping_down_offline: f64,
pub reactive_up: f64,
pub reactive_down: f64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucDevicePeriod {
pub on_status_min: bool,
pub on_status_max: bool,
pub active_power_min: f64,
pub active_power_max: f64,
pub reactive_power_min: f64,
pub reactive_power_max: f64,
pub energy_cost_blocks: Vec<ScucEnergyCostBlock>,
pub reserve_costs: ScucReserveCosts,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucDevice {
pub id: ComponentId,
pub kind: ScucDeviceKind,
pub on_cost: f64,
pub startup_cost: f64,
pub startup_cost_adjustments: Vec<ScucStartupCostAdjustment>,
pub shutdown_cost: f64,
pub startup_limits: Vec<ScucStartupLimit>,
pub energy_upper_bounds: Vec<ScucEnergyRequirement>,
pub energy_lower_bounds: Vec<ScucEnergyRequirement>,
pub minimum_up_time: f64,
pub minimum_down_time: f64,
pub ramp_limits: ScucRampLimits,
pub reserve_limits: ScucReserveLimits,
pub initial_on_status: bool,
pub initial_commitment: ScucInitialCommitment,
pub reactive_capability: ScucReactiveCapability,
pub periods: Vec<ScucDevicePeriod>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucShunt {
pub id: ComponentId,
pub conductance_per_step: f64,
pub susceptance_per_step: f64,
pub step_min: i64,
pub step_max: i64,
pub initial_step: i64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucBranchSwitchingCost {
pub id: ComponentId,
pub connection_cost: f64,
pub disconnection_cost: f64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucTransformerControl {
pub id: ComponentId,
pub tap_ratio_min: f64,
pub tap_ratio_max: f64,
pub phase_shift_min: f64,
pub phase_shift_max: f64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucActiveReserveZone {
pub id: ComponentId,
pub buses: Vec<ComponentId>,
pub regulation_up_requirement_fraction: f64,
pub regulation_down_requirement_fraction: f64,
pub synchronized_requirement_fraction: f64,
pub nonsynchronized_requirement_fraction: f64,
pub ramping_up_requirement: Vec<f64>,
pub ramping_down_requirement: Vec<f64>,
pub regulation_up_violation_cost: f64,
pub regulation_down_violation_cost: f64,
pub synchronized_violation_cost: f64,
pub nonsynchronized_violation_cost: f64,
pub ramping_up_violation_cost: f64,
pub ramping_down_violation_cost: f64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucReactiveReserveZone {
pub id: ComponentId,
pub buses: Vec<ComponentId>,
pub reactive_up_requirement: Vec<f64>,
pub reactive_down_requirement: Vec<f64>,
pub reactive_up_violation_cost: f64,
pub reactive_down_violation_cost: f64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucContingency {
pub id: ComponentId,
pub components: Vec<ComponentId>,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucViolationCosts {
pub active_power_balance: f64,
pub reactive_power_balance: f64,
pub branch_thermal_limit: f64,
pub energy_requirement: f64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub struct ScucInputs {
pub interval_durations: Vec<f64>,
pub devices: Vec<ScucDevice>,
pub shunts: Vec<ScucShunt>,
pub branch_switching_costs: Vec<ScucBranchSwitchingCost>,
pub transformer_controls: Vec<ScucTransformerControl>,
pub active_reserve_zones: Vec<ScucActiveReserveZone>,
pub reactive_reserve_zones: Vec<ScucReactiveReserveZone>,
pub contingencies: Vec<ScucContingency>,
pub violation_costs: ScucViolationCosts,
}
impl ScucInputs {
pub fn producers(&self) -> impl Iterator<Item = &ScucDevice> {
self.devices
.iter()
.filter(|device| device.kind == ScucDeviceKind::Producer)
}
pub fn consumers(&self) -> impl Iterator<Item = &ScucDevice> {
self.devices
.iter()
.filter(|device| device.kind == ScucDeviceKind::Consumer)
}
#[must_use]
pub fn device(&self, uid: &str) -> Option<&ScucDevice> {
self.devices
.iter()
.find(|device| device.id.local_id() == uid)
}
#[must_use]
pub fn shunt(&self, uid: &str) -> Option<&ScucShunt> {
self.shunts.iter().find(|shunt| shunt.id.local_id() == uid)
}
#[must_use]
pub fn contingency(&self, uid: &str) -> Option<&ScucContingency> {
self.contingencies
.iter()
.find(|contingency| contingency.id.local_id() == uid)
}
#[must_use]
pub fn interval_durations(&self) -> &[f64] {
&self.interval_durations
}
}