use crate::EntityId;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ThermalCostSegment {
pub capacity_mw: f64,
pub cost_per_mwh: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GnlConfig {
pub lag_stages: i32,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Thermal {
pub id: EntityId,
pub name: String,
pub bus_id: EntityId,
pub entry_stage_id: Option<i32>,
pub exit_stage_id: Option<i32>,
pub cost_segments: Vec<ThermalCostSegment>,
pub min_generation_mw: f64,
pub max_generation_mw: f64,
pub gnl_config: Option<GnlConfig>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_thermal_cost_segment() {
let segment = ThermalCostSegment {
capacity_mw: 200.0,
cost_per_mwh: 75.0,
};
assert!((segment.capacity_mw - 200.0).abs() < f64::EPSILON);
assert!((segment.cost_per_mwh - 75.0).abs() < f64::EPSILON);
}
#[test]
fn test_thermal_construction() {
let thermal = Thermal {
id: EntityId::from(1),
name: "Angra 1".to_string(),
bus_id: EntityId::from(10),
entry_stage_id: None,
exit_stage_id: None,
cost_segments: vec![
ThermalCostSegment {
capacity_mw: 300.0,
cost_per_mwh: 50.0,
},
ThermalCostSegment {
capacity_mw: 357.0,
cost_per_mwh: 80.0,
},
],
min_generation_mw: 0.0,
max_generation_mw: 657.0,
gnl_config: None,
};
assert_eq!(thermal.id, EntityId::from(1));
assert_eq!(thermal.name, "Angra 1");
assert_eq!(thermal.bus_id, EntityId::from(10));
assert_eq!(thermal.entry_stage_id, None);
assert_eq!(thermal.exit_stage_id, None);
assert_eq!(thermal.cost_segments.len(), 2);
assert!((thermal.min_generation_mw - 0.0).abs() < f64::EPSILON);
assert!((thermal.max_generation_mw - 657.0).abs() < f64::EPSILON);
assert_eq!(thermal.gnl_config, None);
}
#[test]
fn test_thermal_with_gnl() {
let thermal = Thermal {
id: EntityId::from(2),
name: "Pecém I".to_string(),
bus_id: EntityId::from(20),
entry_stage_id: Some(1),
exit_stage_id: Some(120),
cost_segments: vec![ThermalCostSegment {
capacity_mw: 360.0,
cost_per_mwh: 120.0,
}],
min_generation_mw: 100.0,
max_generation_mw: 360.0,
gnl_config: Some(GnlConfig { lag_stages: 2 }),
};
assert_eq!(thermal.gnl_config, Some(GnlConfig { lag_stages: 2 }));
assert_eq!(thermal.entry_stage_id, Some(1));
assert_eq!(thermal.exit_stage_id, Some(120));
}
#[test]
fn test_thermal_without_gnl() {
let thermal = Thermal {
id: EntityId::from(3),
name: "Candiota".to_string(),
bus_id: EntityId::from(5),
entry_stage_id: None,
exit_stage_id: None,
cost_segments: vec![ThermalCostSegment {
capacity_mw: 446.0,
cost_per_mwh: 60.0,
}],
min_generation_mw: 0.0,
max_generation_mw: 446.0,
gnl_config: None,
};
assert_eq!(thermal.gnl_config, None);
}
#[cfg(feature = "serde")]
#[test]
fn test_thermal_serde_roundtrip() {
let thermal = Thermal {
id: EntityId::from(2),
name: "Pecém I".to_string(),
bus_id: EntityId::from(20),
entry_stage_id: Some(1),
exit_stage_id: Some(120),
cost_segments: vec![
ThermalCostSegment {
capacity_mw: 200.0,
cost_per_mwh: 80.0,
},
ThermalCostSegment {
capacity_mw: 160.0,
cost_per_mwh: 120.0,
},
],
min_generation_mw: 100.0,
max_generation_mw: 360.0,
gnl_config: Some(GnlConfig { lag_stages: 2 }),
};
let json = serde_json::to_string(&thermal).unwrap();
let deserialized: Thermal = serde_json::from_str(&json).unwrap();
assert_eq!(thermal, deserialized);
}
}