use std::collections::HashSet;
use std::fmt;
use std::str;
use serde::{Deserialize, Serialize};
use crate::{
error::EpbdError,
types::{CSubtype, Carrier, Dest, Factor, Meta, MetaVec, RenNrenCo2, Source, Step},
Components,
};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Factors {
pub wmeta: Vec<Meta>,
pub wdata: Vec<Factor>,
}
impl Factors {
pub fn strip_nepb(&mut self) {
self.wdata.retain(|e| e.dest != Dest::A_NEPB);
}
pub fn update_wfactor(
&mut self,
carrier: Carrier,
source: Source,
dest: Dest,
step: Step,
values: RenNrenCo2,
comment: &str,
) {
if let Some(factor) = self.wdata.iter_mut().find(|f| {
f.carrier == carrier && f.source == source && f.step == step && f.dest == dest
}) {
factor.set_values(&values);
} else {
self.wdata
.push(Factor::new(carrier, source, dest, step, values, comment));
};
}
pub fn ensure_wfactor(
&mut self,
carrier: Carrier,
source: Source,
dest: Dest,
step: Step,
values: RenNrenCo2,
comment: &str,
) {
if !self
.wdata
.iter()
.any(|f| f.carrier == carrier && f.source == source && f.step == step && f.dest == dest)
{
self.wdata
.push(Factor::new(carrier, source, dest, step, values, comment));
};
}
pub fn set_user_wfactors(mut self, user: UserWF<Option<RenNrenCo2>>) -> Self {
use Carrier::{ELECTRICIDAD, RED1, RED2};
use Dest::{A_NEPB, A_RED, SUMINISTRO};
use Source::{COGENERACION, RED};
use Step::A;
[
(
ELECTRICIDAD,
COGENERACION,
A_RED,
A,
user.cogen_to_grid,
"Factor de usuario",
),
(
ELECTRICIDAD,
COGENERACION,
A_NEPB,
A,
user.cogen_to_nepb,
"Factor de usuario",
),
(RED1, RED, SUMINISTRO, A, user.red1, "Factor de usuario"),
(RED2, RED, SUMINISTRO, A, user.red2, "Factor de usuario"),
]
.iter()
.for_each(|(carrier, source, dest, step, uservalue, comment)| {
if let Some(value) = *uservalue {
self.update_wfactor(*carrier, *source, *dest, *step, value, comment)
}
});
self
}
pub fn normalize(mut self, defaults: &UserWF<RenNrenCo2>) -> Result<Self, EpbdError> {
use Carrier::*;
use Dest::*;
use Source::*;
use Step::*;
let wf_carriers: HashSet<_> = self.wdata.iter().map(|f| f.carrier).collect();
self.update_wfactor(
MEDIOAMBIENTE,
INSITU,
SUMINISTRO,
A,
RenNrenCo2::new(1.0, 0.0, 0.0),
"Recursos usados para obtener energía térmica del medioambiente",
);
self.update_wfactor(
MEDIOAMBIENTE,
RED,
SUMINISTRO,
A,
RenNrenCo2::new(1.0, 0.0, 0.0),
"Recursos usados para obtener energía térmica del medioambiente (red ficticia)",
);
if wf_carriers.contains(&ELECTRICIDAD) {
self.update_wfactor(
ELECTRICIDAD,
INSITU,
SUMINISTRO,
A,
RenNrenCo2::new(1.0, 0.0, 0.0),
"Recursos usados para generar electricidad in situ",
);
}
let has_grid_factors_for_all_carriers = wf_carriers.iter().all(|&c| {
self.wdata.iter().any(|f| {
f.carrier == c
&& f.source == Source::RED
&& f.dest == Dest::SUMINISTRO
&& f.step == Step::A
})
});
if !has_grid_factors_for_all_carriers {
return Err(EpbdError::MissingFactor(
"Factores de red VECTOR, INSITU, SUMINISTRO, A, fren?, fnren?".into(),
));
}
self.update_wfactor(
ELECTRICIDAD,
COGENERACION,
SUMINISTRO,
A,
RenNrenCo2::new(0.0, 0.0, 0.0),
"Factor de paso generado (el impacto de la cogeneración se tiene en cuenta en el vector de suministro)",
);
let exp_carriers = [
(Carrier::ELECTRICIDAD, Source::INSITU),
(Carrier::ELECTRICIDAD, Source::COGENERACION),
(Carrier::MEDIOAMBIENTE, Source::INSITU),
];
for (c, s) in &exp_carriers {
if *s != Source::COGENERACION {
let fp_a_input = self
.wdata
.iter()
.find(|f| {
f.carrier == *c
&& f.source == *s
&& f.step == Step::A
&& f.dest == Dest::SUMINISTRO
})
.map(|f| f.factors());
if let Some(factors) = fp_a_input {
self.ensure_wfactor(
*c,
*s,
A_RED,
A,
factors,
"Recursos usados para producir la energía exportada a la red",
);
self.ensure_wfactor(
*c,
*s,
A_NEPB,
A,
factors,
"Recursos usados para producir la energía exportada a usos no EPB",
);
}
} else {
self.ensure_wfactor(
ELECTRICIDAD,
COGENERACION,
A_RED,
A,
defaults.cogen_to_grid,
"Recursos usados para producir la energía exportada a la red. Valor predefinido",
);
self.ensure_wfactor(
ELECTRICIDAD,
COGENERACION,
A_NEPB,
A,
defaults.cogen_to_nepb,
"Recursos usados para producir la energía exportada a usos no EPB. Valor predefinido",
);
}
let fp_a_red_input = self
.wdata
.iter()
.find(|f| {
f.carrier == *c
&& f.source == Source::RED
&& f.dest == Dest::SUMINISTRO
&& f.step == Step::A
})
.map(|f| f.factors());
if let Some(factors) = fp_a_red_input {
self.ensure_wfactor(
*c,
*s,
A_RED,
B,
factors,
"Recursos ahorrados a la red por la energía producida in situ y exportada a la red",
);
self.ensure_wfactor(
*c,
*s,
A_NEPB,
B,
factors,
"Recursos ahorrados a la red por la energía producida in situ y exportada a usos no EPB",
);
} else {
return Err(EpbdError::MissingFactor(format!("{}, SUMINISTRO, A", c)));
}
}
self.ensure_wfactor(
RED1,
RED,
SUMINISTRO,
A,
defaults.red1,
"Recursos usados para suministrar energía de la red de distrito 1 (definible por el usuario)",
);
self.ensure_wfactor(
RED2,
RED,
SUMINISTRO,
A,
defaults.red2,
"Recursos usados para suministrar energía de la red de distrito 2 (definible por el usuario)",
);
Ok(self)
}
pub fn strip(mut self, components: &Components) -> Self {
let wf_carriers: HashSet<_> = components.cdata.iter().map(|c| c.carrier).collect();
self.wdata.retain(|f| wf_carriers.contains(&f.carrier));
let has_cogen = components
.cdata
.iter()
.any(|c| c.csubtype == CSubtype::COGENERACION);
self.wdata
.retain(|f| f.source != Source::COGENERACION || has_cogen);
let has_nepb = components
.cdata
.iter()
.any(|c| c.csubtype == CSubtype::NEPB);
self.wdata.retain(|f| f.dest != Dest::A_NEPB || has_nepb);
let has_elec_insitu = components
.cdata
.iter()
.any(|c| c.carrier == Carrier::ELECTRICIDAD && c.csubtype == CSubtype::INSITU);
self.wdata.retain(|f| {
f.carrier != Carrier::ELECTRICIDAD || f.source != Source::INSITU || has_elec_insitu
});
self
}
}
impl MetaVec for Factors {
fn get_metavec(&self) -> &Vec<Meta> {
&self.wmeta
}
fn get_mut_metavec(&mut self) -> &mut Vec<Meta> {
&mut self.wmeta
}
}
impl fmt::Display for Factors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let metalines = self
.wmeta
.iter()
.map(|v| format!("{}", v))
.collect::<Vec<_>>()
.join("\n");
let datalines = self
.wdata
.iter()
.map(|v| format!("{}", v))
.collect::<Vec<_>>()
.join("\n");
write!(f, "{}\n{}", metalines, datalines)
}
}
impl str::FromStr for Factors {
type Err = EpbdError;
fn from_str(s: &str) -> Result<Factors, Self::Err> {
let lines: Vec<&str> = s.lines().map(str::trim).collect();
let metalines = lines
.iter()
.filter(|l| l.starts_with("#META") || l.starts_with("#CTE_"));
let datalines = lines
.iter()
.filter(|l| !(l.starts_with('#') || l.starts_with("vector,") || l.is_empty()));
let wmeta = metalines
.map(|e| e.parse())
.collect::<Result<Vec<Meta>, _>>()?;
let wdata = datalines
.map(|e| e.parse())
.collect::<Result<Vec<Factor>, _>>()?;
Ok(Factors { wmeta, wdata })
}
}
#[derive(Debug, Copy, Clone)]
pub struct UserWF<T = RenNrenCo2> {
pub red1: T,
pub red2: T,
pub cogen_to_grid: T,
pub cogen_to_nepb: T,
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn tfactors() {
let tfactors1 = "#META CTE_FUENTE: RITE2014
#META CTE_FUENTE_COMENTARIO: Factores de paso del documento reconocido del IDAE de 20/07/2014
ELECTRICIDAD, RED, SUMINISTRO, A, 0.414, 1.954, 0.331 # Recursos usados para suministrar electricidad (peninsular) desde la red
ELECTRICIDAD, INSITU, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para producir electricidad in situ";
assert_eq!(tfactors1.parse::<Factors>().unwrap().to_string(), tfactors1);
}
#[test]
fn set_user_factors() {
let tfactors1 = "#META CTE_FUENTE: RITE2014
#META CTE_FUENTE_COMENTARIO: Factores de paso del documento reconocido del IDAE de 20/07/2014
ELECTRICIDAD, RED, SUMINISTRO, A, 0.414, 1.954, 0.331 # Recursos usados para suministrar electricidad (peninsular) desde la red
ELECTRICIDAD, INSITU, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para producir electricidad in situ
".parse::<Factors>().unwrap();
let tfactorsres = "#META CTE_FUENTE: RITE2014
#META CTE_FUENTE_COMENTARIO: Factores de paso del documento reconocido del IDAE de 20/07/2014
ELECTRICIDAD, RED, SUMINISTRO, A, 0.414, 1.954, 0.331 # Recursos usados para suministrar electricidad (peninsular) desde la red
ELECTRICIDAD, INSITU, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para producir electricidad in situ
ELECTRICIDAD, COGENERACION, A_RED, A, 0.125, 0.500, 1.000 # Factor de usuario
ELECTRICIDAD, COGENERACION, A_NEPB, A, 0.500, 0.125, 2.000 # Factor de usuario
RED1, RED, SUMINISTRO, A, 0.100, 0.125, 0.500 # Factor de usuario
RED2, RED, SUMINISTRO, A, 0.125, 0.100, 0.500 # Factor de usuario";
assert_eq!(
tfactors1
.set_user_wfactors(UserWF {
red1: Some(RenNrenCo2::new(0.1, 0.125, 0.5)),
red2: Some(RenNrenCo2::new(0.125, 0.1, 0.5)),
cogen_to_grid: Some(RenNrenCo2::new(0.125, 0.5, 1.0)),
cogen_to_nepb: Some(RenNrenCo2::new(0.5, 0.125, 2.0)),
})
.to_string(),
tfactorsres
);
}
#[test]
fn normalize_and_strip() {
let tfactors = "#META CTE_FUENTE: RITE2014
#META CTE_FUENTE_COMENTARIO: Factores de paso del documento reconocido del IDAE de 20/07/2014
ELECTRICIDAD, RED, SUMINISTRO, A, 0.414, 1.954, 0.331 # Recursos usados para suministrar electricidad (peninsular) desde la red
ELECTRICIDAD, INSITU, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para producir electricidad in situ
".parse::<Factors>().unwrap();
let tfactors_normalized_str = "#META CTE_FUENTE: RITE2014
#META CTE_FUENTE_COMENTARIO: Factores de paso del documento reconocido del IDAE de 20/07/2014
ELECTRICIDAD, RED, SUMINISTRO, A, 0.414, 1.954, 0.331 # Recursos usados para suministrar electricidad (peninsular) desde la red
ELECTRICIDAD, INSITU, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para producir electricidad in situ
MEDIOAMBIENTE, INSITU, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para obtener energía térmica del medioambiente
MEDIOAMBIENTE, RED, SUMINISTRO, A, 1.000, 0.000, 0.000 # Recursos usados para obtener energía térmica del medioambiente (red ficticia)
ELECTRICIDAD, COGENERACION, SUMINISTRO, A, 0.000, 0.000, 0.000 # Factor de paso generado (el impacto de la cogeneración se tiene en cuenta en el vector de suministro)
ELECTRICIDAD, INSITU, A_RED, A, 1.000, 0.000, 0.000 # Recursos usados para producir la energía exportada a la red\nELECTRICIDAD, INSITU, A_NEPB, A, 1.000, 0.000, 0.000 # Recursos usados para producir la energía exportada a usos no EPB
ELECTRICIDAD, INSITU, A_RED, B, 0.414, 1.954, 0.331 # Recursos ahorrados a la red por la energía producida in situ y exportada a la red
ELECTRICIDAD, INSITU, A_NEPB, B, 0.414, 1.954, 0.331 # Recursos ahorrados a la red por la energía producida in situ y exportada a usos no EPB
ELECTRICIDAD, COGENERACION, A_RED, A, 0.000, 2.500, 0.300 # Recursos usados para producir la energía exportada a la red. Valor predefinido
ELECTRICIDAD, COGENERACION, A_NEPB, A, 0.000, 2.500, 0.300 # Recursos usados para producir la energía exportada a usos no EPB. Valor predefinido
ELECTRICIDAD, COGENERACION, A_RED, B, 0.414, 1.954, 0.331 # Recursos ahorrados a la red por la energía producida in situ y exportada a la red
ELECTRICIDAD, COGENERACION, A_NEPB, B, 0.414, 1.954, 0.331 # Recursos ahorrados a la red por la energía producida in situ y exportada a usos no EPB
MEDIOAMBIENTE, INSITU, A_RED, A, 1.000, 0.000, 0.000 # Recursos usados para producir la energía exportada a la red
MEDIOAMBIENTE, INSITU, A_NEPB, A, 1.000, 0.000, 0.000 # Recursos usados para producir la energía exportada a usos no EPB
MEDIOAMBIENTE, INSITU, A_RED, B, 1.000, 0.000, 0.000 # Recursos ahorrados a la red por la energía producida in situ y exportada a la red
MEDIOAMBIENTE, INSITU, A_NEPB, B, 1.000, 0.000, 0.000 # Recursos ahorrados a la red por la energía producida in situ y exportada a usos no EPB
RED1, RED, SUMINISTRO, A, 0.000, 1.300, 0.300 # Recursos usados para suministrar energía de la red de distrito 1 (definible por el usuario)
RED2, RED, SUMINISTRO, A, 0.000, 1.300, 0.300 # Recursos usados para suministrar energía de la red de distrito 2 (definible por el usuario)";
let tcomps = "ELECTRICIDAD, CONSUMO, EPB, NDEF, 1 # Solo consume electricidad de red"
.parse::<Components>()
.unwrap();
let tfactors_normalized_stripped_str = "#META CTE_FUENTE: RITE2014
#META CTE_FUENTE_COMENTARIO: Factores de paso del documento reconocido del IDAE de 20/07/2014
ELECTRICIDAD, RED, SUMINISTRO, A, 0.414, 1.954, 0.331 # Recursos usados para suministrar electricidad (peninsular) desde la red";
let tfactors_normalized = tfactors
.normalize(&UserWF {
red1: RenNrenCo2 {
ren: 0.0,
nren: 1.3,
co2: 0.3,
},
red2: RenNrenCo2 {
ren: 0.0,
nren: 1.3,
co2: 0.3,
},
cogen_to_grid: RenNrenCo2 {
ren: 0.0,
nren: 2.5,
co2: 0.3,
},
cogen_to_nepb: RenNrenCo2 {
ren: 0.0,
nren: 2.5,
co2: 0.3,
},
})
.unwrap();
let tfactors_normalized_stripped = tfactors_normalized.clone().strip(&tcomps);
assert_eq!(tfactors_normalized.to_string(), tfactors_normalized_str);
assert_eq!(
tfactors_normalized_stripped.to_string(),
tfactors_normalized_stripped_str
);
}
}