use super::*;
pub trait LocoTrait {
fn set_cur_pwr_max_out(
&mut self,
pwr_aux: Option<si::Power>,
dt: si::Time,
) -> anyhow::Result<()>;
fn save_state(&mut self) {
unimplemented!();
}
fn step(&mut self) {
unimplemented!();
}
fn get_energy_loss(&self) -> si::Energy;
}
#[altrios_api]
#[derive(Default, Serialize, Deserialize, Clone, PartialEq, SerdeAPI)]
pub struct Pyo3VecLocoWrapper(pub Vec<Locomotive>);
pub trait SolvePower {
fn solve_positive_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>>;
fn solve_negative_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>>;
}
#[derive(PartialEq, Eq, Clone, Deserialize, Serialize, Debug, SerdeAPI)]
pub struct RESGreedy;
impl SolvePower for RESGreedy {
fn solve_positive_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
let loco_pwr_out_vec: Vec<si::Power> = if state.pwr_out_deficit == si::Power::ZERO {
loco_vec
.iter()
.map(|loco| match &loco.loco_type {
PowertrainType::ConventionalLoco(_) => si::Power::ZERO,
PowertrainType::HybridLoco(_) => {
loco.state.pwr_out_max / state.pwr_out_max_reves * state.pwr_out_req
}
PowertrainType::BatteryElectricLoco(_) => {
loco.state.pwr_out_max / state.pwr_out_max_reves * state.pwr_out_req
}
PowertrainType::DummyLoco(_) => state.pwr_out_req,
})
.collect()
} else {
loco_vec
.iter()
.map(|loco| match &loco.loco_type {
PowertrainType::ConventionalLoco(_) => {
loco.state.pwr_out_max / state.pwr_out_max_non_reves
* state.pwr_out_deficit
}
PowertrainType::HybridLoco(_) => loco.state.pwr_out_max,
PowertrainType::BatteryElectricLoco(_) => loco.state.pwr_out_max,
PowertrainType::DummyLoco(_) => {
si::Power::ZERO
}
})
.collect()
};
utils::assert_almost_eq_uom(
&loco_pwr_out_vec.iter().copied().sum(),
&state.pwr_out_req,
None,
);
Ok(loco_pwr_out_vec)
}
fn solve_negative_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
solve_negative_traction(loco_vec, state)
}
}
fn get_pwr_regen_vec(loco_vec: &[Locomotive], regen_frac: si::Ratio) -> Vec<si::Power> {
loco_vec
.iter()
.map(|loco| match &loco.loco_type {
PowertrainType::ConventionalLoco(_) => si::Power::ZERO,
PowertrainType::HybridLoco(_) => loco.state.pwr_regen_max * regen_frac,
PowertrainType::BatteryElectricLoco(_) => loco.state.pwr_regen_max * regen_frac,
PowertrainType::DummyLoco(_) => si::Power::ZERO,
})
.collect()
}
fn solve_negative_traction(
loco_vec: &[Locomotive],
consist_state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
let pwr_brake_req = -consist_state.pwr_out_req;
let regen_frac = if consist_state.pwr_regen_max == si::Power::ZERO {
si::Ratio::ZERO
} else {
(pwr_brake_req / consist_state.pwr_regen_max).min(uc::R * 1.)
};
let pwr_out_vec: Vec<si::Power> = if consist_state.pwr_regen_deficit == si::Power::ZERO {
get_pwr_regen_vec(loco_vec, regen_frac)
} else {
let pwr_regen_vec = get_pwr_regen_vec(loco_vec, regen_frac);
let pwr_surplus_vec: Vec<si::Power> = loco_vec
.iter()
.zip(&pwr_regen_vec)
.map(|(loco, pwr_regen)| loco.electric_drivetrain().unwrap().pwr_out_max - *pwr_regen)
.collect();
let pwr_surplus_sum = pwr_surplus_vec
.iter()
.fold(0.0 * uc::W, |acc, &curr| acc + curr);
let surplus_frac = consist_state.pwr_regen_deficit / pwr_surplus_sum;
ensure!(
surplus_frac >= si::Ratio::ZERO && surplus_frac <= uc::R,
format_dbg!(surplus_frac),
);
let pwr_dyn_brake_vec: Vec<si::Power> = pwr_surplus_vec
.iter()
.zip(pwr_regen_vec)
.map(|(pwr_surplus, pwr_regen)| *pwr_surplus * surplus_frac + pwr_regen)
.collect();
pwr_dyn_brake_vec
};
let pwr_out_vec: Vec<si::Power> = pwr_out_vec.iter().map(|x| -*x).collect();
Ok(pwr_out_vec)
}
#[derive(PartialEq, Eq, Clone, Deserialize, Serialize, Debug, SerdeAPI)]
pub struct Proportional;
impl SolvePower for Proportional {
fn solve_positive_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
Ok(loco_vec
.iter()
.map(|loco| {
loco.state.pwr_out_max / state.pwr_out_max * state.pwr_out_req
})
.collect())
}
fn solve_negative_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
solve_negative_traction(loco_vec, state)
}
}
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug, SerdeAPI)]
pub struct GoldenSectionSearch {
pub fuel_res_ratio: f64,
pub gss_interval: usize,
}
impl SolvePower for GoldenSectionSearch {
fn solve_positive_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
if state.i == 1 || state.i % self.gss_interval == 0 {
todo!() } else {
Ok(loco_vec.iter().map(|loco| loco.state.pwr_out).collect())
}
}
fn solve_negative_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
if state.i == 1 || state.i % self.gss_interval == 0 {
todo!() } else {
Ok(loco_vec.iter().map(|loco| loco.state.pwr_out).collect())
}
}
}
#[derive(PartialEq, Eq, Clone, Deserialize, Serialize, Debug)]
pub struct FrontAndBack;
impl SerdeAPI for FrontAndBack {}
impl SolvePower for FrontAndBack {
fn solve_positive_traction(
&mut self,
_loco_vec: &[Locomotive],
_state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
todo!() }
fn solve_negative_traction(
&mut self,
_loco_vec: &[Locomotive],
_state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
todo!() }
}
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug, SerdeAPI)]
pub enum PowerDistributionControlType {
RESGreedy(RESGreedy),
Proportional(Proportional),
GoldenSectionSearch(GoldenSectionSearch),
FrontAndBack(FrontAndBack),
}
impl SolvePower for PowerDistributionControlType {
fn solve_negative_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
match self {
Self::RESGreedy(res_greedy) => res_greedy.solve_negative_traction(loco_vec, state),
Self::Proportional(prop) => prop.solve_negative_traction(loco_vec, state),
Self::GoldenSectionSearch(gss) => gss.solve_negative_traction(loco_vec, state),
Self::FrontAndBack(fab) => fab.solve_negative_traction(loco_vec, state),
}
}
fn solve_positive_traction(
&mut self,
loco_vec: &[Locomotive],
state: &ConsistState,
) -> anyhow::Result<Vec<si::Power>> {
match self {
Self::RESGreedy(res_greedy) => res_greedy.solve_positive_traction(loco_vec, state),
Self::Proportional(prop) => prop.solve_positive_traction(loco_vec, state),
Self::GoldenSectionSearch(gss) => gss.solve_positive_traction(loco_vec, state),
Self::FrontAndBack(fab) => fab.solve_positive_traction(loco_vec, state),
}
}
}
impl Default for PowerDistributionControlType {
fn default() -> Self {
Self::RESGreedy(RESGreedy)
}
}