use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use serde::{Deserialize, Serialize};
use crate::{
error::{EpbdError, Result},
types::{
CSubtype, CType, Carrier, Component, Dest, Factor, RenNrenCo2, Service, Source, Step,
SERVICES,
},
vecops::{veckmul, vecsum, vecvecdif, vecvecmin, vecvecmul, vecvecsum},
Components, Factors,
};
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Balance {
pub components: Components,
pub wfactors: Factors,
pub k_exp: f32,
pub arearef: f32,
pub balance_cr: HashMap<Carrier, BalanceForCarrier>,
pub balance: BalanceTotal,
pub balance_m2: BalanceTotal,
pub misc: Option<HashMap<String, String>>,
}
#[allow(non_snake_case)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BalanceTotal {
pub used_EPB_byuse: HashMap<Service, f32>,
pub A: RenNrenCo2,
pub A_byuse: HashMap<Service, RenNrenCo2>,
pub B: RenNrenCo2,
pub B_byuse: HashMap<Service, RenNrenCo2>,
pub we_del: RenNrenCo2,
pub we_exp_A: RenNrenCo2,
pub we_exp: RenNrenCo2,
}
#[allow(non_snake_case)]
pub fn energy_performance(
components: &Components,
wfactors: &Factors,
k_exp: f32,
arearef: f32,
) -> Result<Balance> {
if arearef < 1e-3 {
return Err(EpbdError::WrongInput(format!(
"El área de referencia no puede ser nula o casi nula y se encontró {}",
arearef
)));
};
let carriers: HashSet<_> = components.cdata.iter().map(|e| e.carrier).collect();
let mut balance_cr: HashMap<Carrier, BalanceForCarrier> = HashMap::new();
for &carrier in &carriers {
let components_cr: Vec<Component> = components
.cdata
.iter()
.filter(|e| e.carrier == carrier)
.cloned()
.collect();
let fp_cr: Vec<Factor> = wfactors
.wdata
.iter()
.filter(|e| e.carrier == carrier)
.cloned()
.collect();
let bal = balance_for_carrier(carrier, &components_cr, &fp_cr, k_exp)?;
balance_cr.insert(carrier, bal);
}
let balance: BalanceTotal = carriers
.iter()
.fold(BalanceTotal::default(), |mut acc, cr| {
acc.A += balance_cr[cr].we_an_A;
acc.B += balance_cr[cr].we_an;
acc.we_del += balance_cr[cr].we_delivered_an;
acc.we_exp_A += balance_cr[cr].we_exported_an_A;
acc.we_exp += balance_cr[cr].we_exported_an;
for &service in &SERVICES {
if let Some(value) = balance_cr[cr].used_EPB_an_byuse.get(&service) {
*acc.used_EPB_byuse.entry(service).or_default() += *value
}
if let Some(value) = balance_cr[cr].we_an_A_byuse.get(&service) {
*acc.A_byuse.entry(service).or_default() += *value
}
if let Some(value) = balance_cr[cr].we_an_byuse.get(&service) {
*acc.B_byuse.entry(service).or_default() += *value;
}
}
acc
});
let k_area = 1.0 / arearef;
let mut used_EPB_byuse = balance.used_EPB_byuse.clone();
used_EPB_byuse.values_mut().for_each(|v| *v *= k_area);
let mut A_byuse = balance.A_byuse.clone();
A_byuse.values_mut().for_each(|v| *v *= k_area);
let mut B_byuse = balance.B_byuse.clone();
B_byuse.values_mut().for_each(|v| *v *= k_area);
let balance_m2 = BalanceTotal {
used_EPB_byuse,
A: k_area * balance.A,
A_byuse,
B: k_area * balance.B,
B_byuse,
we_del: k_area * balance.we_del,
we_exp_A: k_area * balance.we_exp_A,
we_exp: k_area * balance.we_exp,
};
Ok(Balance {
components: components.clone(),
wfactors: wfactors.clone(),
k_exp,
arearef,
balance_cr,
balance,
balance_m2,
misc: None,
})
}
#[allow(non_snake_case)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BalanceForCarrier {
pub carrier: Carrier,
pub used_EPB: Vec<f32>,
pub used_EPB_an_byuse: HashMap<Service, f32>,
pub used_nEPB: Vec<f32>,
pub produced: Vec<f32>,
pub produced_an: f32,
pub produced_bygen: HashMap<CSubtype, Vec<f32>>,
pub produced_bygen_an: HashMap<CSubtype, f32>,
pub produced_used_EPus: Vec<f32>,
pub produced_used_EPus_bygen: HashMap<CSubtype, Vec<f32>>,
pub f_match: Vec<f32>,
pub exported: Vec<f32>, pub exported_an: f32,
pub exported_bygen: HashMap<CSubtype, Vec<f32>>, pub exported_bygen_an: HashMap<CSubtype, f32>, pub exported_grid: Vec<f32>,
pub exported_grid_an: f32,
pub exported_nEPB: Vec<f32>,
pub exported_nEPB_an: f32,
pub delivered_grid: Vec<f32>,
pub delivered_grid_an: f32,
pub we_delivered_grid_an: RenNrenCo2,
pub we_delivered_prod_an: RenNrenCo2,
pub we_delivered_an: RenNrenCo2,
pub we_exported_an_A: RenNrenCo2,
pub we_exported_nEPB_an_AB: RenNrenCo2,
pub we_exported_grid_an_AB: RenNrenCo2,
pub we_exported_an_AB: RenNrenCo2,
pub we_exported_an: RenNrenCo2,
pub we_an_A: RenNrenCo2,
pub we_an_A_byuse: HashMap<Service, RenNrenCo2>,
pub we_an: RenNrenCo2,
pub we_an_byuse: HashMap<Service, RenNrenCo2>,
}
#[allow(non_snake_case)]
fn balance_for_carrier(
carrier: Carrier,
cr_list: &[Component],
fp_cr: &[Factor],
k_exp: f32,
) -> Result<BalanceForCarrier> {
let num_steps = cr_list[0].values.len();
let E_EPus_cr_t = cr_list
.iter()
.filter(|e| e.ctype == CType::CONSUMO && e.csubtype == CSubtype::EPB)
.fold(vec![0.0; num_steps], |acc, e| vecvecsum(&acc, &e.values));
let E_nEPus_cr_t = cr_list
.iter()
.filter(|e| e.ctype == CType::CONSUMO && e.csubtype == CSubtype::NEPB)
.fold(vec![0.0; num_steps], |acc, e| vecvecsum(&acc, &e.values));
let mut E_pr_cr_i_t = HashMap::<CSubtype, Vec<f32>>::new();
for comp in cr_list
.iter()
.filter(|comp| comp.ctype == CType::PRODUCCION)
{
E_pr_cr_i_t
.entry(comp.csubtype)
.and_modify(|e| *e = vecvecsum(e, &comp.values))
.or_insert_with(|| comp.values.clone());
}
let pr_generators: Vec<CSubtype> = E_pr_cr_i_t.keys().cloned().collect();
let mut E_pr_cr_i_an = HashMap::<CSubtype, f32>::new();
for gen in &pr_generators {
E_pr_cr_i_an.insert(*gen, vecsum(&E_pr_cr_i_t[gen]));
}
let mut E_pr_cr_t = vec![0.0; num_steps];
for gen in &pr_generators {
E_pr_cr_t = vecvecsum(&E_pr_cr_t, &E_pr_cr_i_t[gen])
}
let E_pr_cr_an = vecsum(&E_pr_cr_t);
let f_match_t = vec![1.0; num_steps];
let E_pr_cr_used_EPus_t = vecvecmul(&f_match_t, &vecvecmin(&E_EPus_cr_t, &E_pr_cr_t));
let E_exp_cr_t = vecvecdif(&E_pr_cr_t, &E_pr_cr_used_EPus_t);
let E_exp_cr_used_nEPus_t = vecvecmin(&E_exp_cr_t, &E_nEPus_cr_t);
let E_exp_cr_used_nEPus_an = vecsum(&E_exp_cr_used_nEPus_t);
let E_exp_cr_grid_t = vecvecdif(&E_exp_cr_t, &E_exp_cr_used_nEPus_t);
let E_exp_cr_grid_an = vecsum(&E_exp_cr_grid_t);
let E_del_cr_t = vecvecdif(&E_EPus_cr_t, &E_pr_cr_used_EPus_t);
let E_del_cr_an = vecsum(&E_del_cr_t);
let mut f_pr_cr_i = HashMap::<CSubtype, f32>::new();
for gen in &pr_generators {
let f = if E_pr_cr_an > 1e-3 {
E_pr_cr_i_an[gen] / E_pr_cr_an
} else {
0.0
};
f_pr_cr_i.insert(*gen, f);
}
let mut E_pr_cr_i_used_EPus_t = HashMap::<CSubtype, Vec<f32>>::new();
for gen in &pr_generators {
E_pr_cr_i_used_EPus_t.insert(*gen, veckmul(&E_pr_cr_used_EPus_t, f_pr_cr_i[gen]));
}
let mut E_exp_cr_i_t = HashMap::<CSubtype, Vec<f32>>::new();
for gen in &pr_generators {
E_exp_cr_i_t.insert(
*gen,
vecvecdif(&E_pr_cr_i_t[gen], &E_pr_cr_i_used_EPus_t[gen]),
);
}
let mut E_exp_cr_i_an = HashMap::<CSubtype, f32>::new();
for gen in &pr_generators {
E_exp_cr_i_an.insert(*gen, vecsum(&E_exp_cr_i_t[gen]));
}
fn fp_find(fp_cr: &[Factor], source: Source, dest: Dest, step: Step) -> Result<&Factor> {
fp_cr
.iter()
.find(|fp| fp.source == source && fp.dest == dest && fp.step == step)
.ok_or_else(|| {
EpbdError::MissingFactor(format!(
"'{}, {}, {}, {}'",
fp_cr[0].carrier, source, dest, step
))
})
}
let fpA_grid = fp_find(fp_cr, Source::RED, Dest::SUMINISTRO, Step::A)?;
let E_we_del_cr_grid_an = E_del_cr_an * fpA_grid.factors();
let E_we_del_cr_onsite_an = E_pr_cr_i_an
.get(&CSubtype::INSITU)
.and_then(|E_pr_cr_i| {
fp_find(fp_cr, Source::INSITU, Dest::SUMINISTRO, Step::A)
.and_then(|fpA_pr_cr_i| Ok(E_pr_cr_i * fpA_pr_cr_i.factors()))
.ok()
})
.unwrap_or_default();
let E_we_del_cr_an = E_we_del_cr_grid_an + E_we_del_cr_onsite_an;
let mut E_we_exp_cr_an_A = RenNrenCo2::default();
let mut E_we_exp_cr_an_AB = RenNrenCo2::default();
let mut E_we_exp_cr_an = RenNrenCo2::default();
let mut E_we_exp_cr_used_nEPus_an_AB = RenNrenCo2::default();
let mut E_we_exp_cr_grid_an_AB = RenNrenCo2::default();
let E_exp_cr_an = E_exp_cr_used_nEPus_an + E_exp_cr_grid_an;
if E_exp_cr_an != 0.0 {
let mut f_pr_cr_i = HashMap::<CSubtype, f32>::new();
for gen in &pr_generators {
if E_exp_cr_i_an[gen] != 0.0 {
f_pr_cr_i.insert(*gen, vecsum(&E_exp_cr_i_t[gen]) / E_exp_cr_i_an[gen]);
}
}
let exp_generators: Vec<_> = f_pr_cr_i.keys().collect();
let f_we_exp_cr_stepA_nEPus: RenNrenCo2 = if E_exp_cr_used_nEPus_an == 0.0 {
RenNrenCo2::default() } else {
exp_generators.iter().fold(
Ok(RenNrenCo2::default()),
|acc: Result<RenNrenCo2>, &gen| {
let fp = fp_find(fp_cr, (*gen).try_into()?, Dest::A_NEPB, Step::A)?;
Ok(acc? + (fp.factors() * f_pr_cr_i[gen]))
},
)? };
let f_we_exp_cr_stepA_grid: RenNrenCo2 = if E_exp_cr_grid_an == 0.0 {
RenNrenCo2::default() } else {
exp_generators.iter().fold(
Ok(RenNrenCo2::default()),
|acc: Result<RenNrenCo2>, &gen| {
let fp = fp_find(fp_cr, (*gen).try_into()?, Dest::A_RED, Step::A)?;
Ok(acc? + (fp.factors() * f_pr_cr_i[gen]))
},
)? };
E_we_exp_cr_an_A = (E_exp_cr_used_nEPus_an * f_we_exp_cr_stepA_nEPus) + (E_exp_cr_grid_an * f_we_exp_cr_stepA_grid);
let f_we_exp_cr_used_nEPus = if E_exp_cr_used_nEPus_an == 0.0 {
RenNrenCo2::default() } else {
exp_generators.iter().fold(
Ok(RenNrenCo2::default()),
|acc: Result<RenNrenCo2>, &gen| {
let fp = fp_find(fp_cr, (*gen).try_into()?, Dest::A_NEPB, Step::B)?;
Ok(acc? + (fp.factors() * f_pr_cr_i[gen]))
},
)? };
let f_we_exp_cr_grid = if E_exp_cr_grid_an == 0.0 {
RenNrenCo2::default() } else {
exp_generators.iter().fold(
Ok(RenNrenCo2::default()),
|acc: Result<RenNrenCo2>, &gen| {
let fp = fp_find(fp_cr, (*gen).try_into()?, Dest::A_RED, Step::B)?;
Ok(acc? + (fp.factors() * f_pr_cr_i[gen]))
},
)? };
E_we_exp_cr_used_nEPus_an_AB =
E_exp_cr_used_nEPus_an * (f_we_exp_cr_used_nEPus - f_we_exp_cr_stepA_nEPus);
E_we_exp_cr_grid_an_AB = E_exp_cr_grid_an * (f_we_exp_cr_grid - f_we_exp_cr_stepA_grid);
E_we_exp_cr_an_AB = E_we_exp_cr_used_nEPus_an_AB + E_we_exp_cr_grid_an_AB;
E_we_exp_cr_an = E_we_exp_cr_an_A + (k_exp * E_we_exp_cr_an_AB); }
let E_we_cr_an_A: RenNrenCo2 = E_we_del_cr_an - E_we_exp_cr_an_A;
let E_we_cr_an: RenNrenCo2 = E_we_del_cr_an - E_we_exp_cr_an;
let f_us_cr = compute_factors_by_use_cr(cr_list);
let E_EPus_cr_an: f32 = E_EPus_cr_t.iter().sum();
let mut E_Epus_cr_an_byuse: HashMap<Service, f32> = HashMap::new();
let mut E_we_cr_an_A_byuse: HashMap<Service, RenNrenCo2> = HashMap::new();
let mut E_we_cr_an_byuse: HashMap<Service, RenNrenCo2> = HashMap::new();
for service in &SERVICES {
let f_us_k_cr = *f_us_cr.get(service).unwrap_or(&0.0f32);
if f_us_k_cr != 0.0 {
E_Epus_cr_an_byuse.insert(*service, E_EPus_cr_an * f_us_k_cr);
E_we_cr_an_A_byuse.insert(*service, E_we_cr_an_A * f_us_k_cr);
E_we_cr_an_byuse.insert(*service, E_we_cr_an * f_us_k_cr);
}
}
Ok(BalanceForCarrier {
carrier,
used_EPB: E_EPus_cr_t,
used_EPB_an_byuse: E_Epus_cr_an_byuse,
used_nEPB: E_nEPus_cr_t,
produced: E_pr_cr_t,
produced_an: E_pr_cr_an,
produced_bygen: E_pr_cr_i_t,
produced_bygen_an: E_pr_cr_i_an,
produced_used_EPus: E_pr_cr_used_EPus_t,
produced_used_EPus_bygen: E_pr_cr_i_used_EPus_t,
f_match: f_match_t, exported: E_exp_cr_t, exported_an: E_exp_cr_an,
exported_bygen: E_exp_cr_i_t,
exported_bygen_an: E_exp_cr_i_an,
exported_grid: E_exp_cr_grid_t,
exported_grid_an: E_exp_cr_grid_an,
exported_nEPB: E_exp_cr_used_nEPus_t,
exported_nEPB_an: E_exp_cr_used_nEPus_an,
delivered_grid: E_del_cr_t,
delivered_grid_an: E_del_cr_an,
we_delivered_grid_an: E_we_del_cr_grid_an,
we_delivered_prod_an: E_we_del_cr_onsite_an,
we_delivered_an: E_we_del_cr_an,
we_exported_an_A: E_we_exp_cr_an_A,
we_exported_nEPB_an_AB: E_we_exp_cr_used_nEPus_an_AB,
we_exported_grid_an_AB: E_we_exp_cr_grid_an_AB,
we_exported_an_AB: E_we_exp_cr_an_AB,
we_exported_an: E_we_exp_cr_an,
we_an_A: E_we_cr_an_A,
we_an_A_byuse: E_we_cr_an_A_byuse,
we_an: E_we_cr_an,
we_an_byuse: E_we_cr_an_byuse,
})
}
fn compute_factors_by_use_cr(cr_list: &[Component]) -> HashMap<Service, f32> {
let mut factors_us_k: HashMap<Service, f32> = HashMap::new();
let cr_use_list = cr_list
.iter()
.filter(|c| c.ctype == CType::CONSUMO && c.csubtype == CSubtype::EPB);
let q_us_all: f32 = cr_use_list
.clone()
.map(|c| c.values.iter().sum::<f32>())
.sum();
if q_us_all != 0.0 {
for us in SERVICES.iter().cloned() {
let q_us_k: f32 = cr_use_list
.clone()
.filter(|c| c.service == us)
.map(|c| c.values.iter().sum::<f32>())
.sum();
factors_us_k.insert(us, q_us_k / q_us_all);
}
}
factors_us_k
}