use super::funejson::Value; use super::math_round_div::round_div; // watts x Kk x SF (hundredths) / (1000 W/kW x 100) is kWh. const DIVISOR: i64 = 100_000; fn check(what: &str, value: i64, max: i64) { if value < 0 || value > max { panic!("{} must be a whole number from 0 to {}, received {}", what, max, value); } } /// Annual AC generation by the MCS method, kWp x Kk x SF, rounded half-up to /// a whole kWh for each array; the total is the sum of the arrays. /// /// # Panics /// Panics on no arrays, or a rating, Kk or shade factor out of range. pub fn solar_generation(arrays: &[PvArray]) -> SolarEstimate { if arrays.is_empty() { panic!("a solar estimate needs at least one array"); } let kwh: Vec = arrays .iter() .map(|a| { check("wattsPeak", a.watts_peak, 100_000_000); check("kk", a.kk, 5000); check("shadeFactor", a.shade_factor, 100); round_div(a.watts_peak * a.kk * a.shade_factor, DIVISOR, "half-up") }) .collect(); let total = kwh.iter().sum(); SolarEstimate { arrays: kwh, total } } fn whole(what: &str, max: i64, v: &Value) -> i64 { if let Value::Float(f) = v { panic!("{} must be a whole number from 0 to {}, received {}", what, max, f); } v.as_i64() } pub fn pv_array_from_value(v: &Value) -> PvArray { PvArray { watts_peak: whole("wattsPeak", 100_000_000, v.get("wattsPeak")), kk: whole("kk", 5000, v.get("kk")), shade_factor: whole("shadeFactor", 100, v.get("shadeFactor")), } } pub fn solar_estimate_to_value(e: &SolarEstimate) -> Value { Value::obj(vec![ ("arrays", Value::Arr(e.arrays.iter().map(|k| Value::Int(*k)).collect())), ("total", Value::Int(e.total)), ]) } pub fn fune_vector(args: &[Value]) -> Value { let arrays: Vec = args[0].as_arr().iter().map(pv_array_from_value).collect(); solar_estimate_to_value(&solar_generation(&arrays)) }