use rust_decimal::Decimal;
use rust_decimal_macros::dec;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindStandort {
pub guetegrad: Decimal,
pub korrekturfaktor: Decimal,
pub grundverguetungsperiode_aktiv: bool,
pub standortklasse: WindStandortklasse,
}
impl WindStandort {
#[must_use]
pub fn effective_aw(&self, base_aw_ct_kwh: Decimal) -> Decimal {
(base_aw_ct_kwh * self.korrekturfaktor).round_dp(5)
}
#[must_use]
pub fn approximate_from_guetegrad(guetegrad: Decimal) -> Self {
let korrekturfaktor = if guetegrad < dec!(0.80) {
dec!(0.0) } else if guetegrad < Decimal::ONE {
(dec!(1.25) - dec!(0.25) * guetegrad).round_dp(4)
} else if guetegrad <= dec!(1.50) {
(dec!(0.95) - dec!(0.10) * (guetegrad - Decimal::ONE)).round_dp(4)
} else {
dec!(0.70) };
let standortklasse = WindStandortklasse::from_guetegrad(guetegrad);
let grundverguetungsperiode_aktiv = guetegrad < Decimal::ONE;
Self {
guetegrad,
korrekturfaktor,
grundverguetungsperiode_aktiv,
standortklasse,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
pub enum WindStandortklasse {
Excellent,
AboveReference,
Reference,
BelowReference,
Marginal,
}
impl WindStandortklasse {
#[must_use]
pub fn from_guetegrad(guetegrad: Decimal) -> Self {
if guetegrad >= dec!(1.50) {
Self::Excellent
} else if guetegrad >= dec!(1.10) {
Self::AboveReference
} else if guetegrad >= dec!(0.90) {
Self::Reference
} else if guetegrad >= dec!(0.80) {
Self::BelowReference
} else {
Self::Marginal
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standortklasse_from_guetegrad() {
assert_eq!(
WindStandortklasse::from_guetegrad(dec!(1.60)),
WindStandortklasse::Excellent
);
assert_eq!(
WindStandortklasse::from_guetegrad(dec!(1.20)),
WindStandortklasse::AboveReference
);
assert_eq!(
WindStandortklasse::from_guetegrad(dec!(1.00)),
WindStandortklasse::Reference
);
assert_eq!(
WindStandortklasse::from_guetegrad(dec!(0.85)),
WindStandortklasse::BelowReference
);
assert_eq!(
WindStandortklasse::from_guetegrad(dec!(0.70)),
WindStandortklasse::Marginal
);
}
#[test]
fn effective_aw_applies_korrekturfaktor() {
let standort = WindStandort {
guetegrad: dec!(0.85),
korrekturfaktor: dec!(1.08),
grundverguetungsperiode_aktiv: true,
standortklasse: WindStandortklasse::BelowReference,
};
let effective = standort.effective_aw(dec!(7.35));
assert_eq!(effective, dec!(7.938));
}
#[test]
fn reference_site_korrekturfaktor_near_one() {
let standort = WindStandort::approximate_from_guetegrad(dec!(1.00));
assert!(standort.korrekturfaktor > dec!(0.90) && standort.korrekturfaktor < dec!(1.10));
assert!(!standort.grundverguetungsperiode_aktiv);
}
#[test]
fn below_reference_triggers_grundverguetungsperiode() {
let standort = WindStandort::approximate_from_guetegrad(dec!(0.85));
assert!(standort.grundverguetungsperiode_aktiv);
assert!(
standort.korrekturfaktor > Decimal::ONE,
"below-reference => higher AW"
);
}
}