fastsim_core/vehicle/powertrain/
transmission.rs1use super::*;
2
3#[serde_api]
4#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, StateMethods, SetCumulative)]
5#[non_exhaustive]
6#[serde(deny_unknown_fields)]
7pub struct Transmission {
8 #[serde(default)]
10 pub(crate) mass: Option<si::Mass>,
11
12 pub eff_interp: InterpolatorEnumOwned<f64>,
15
16 pub save_interval: Option<usize>,
18 #[serde(default)]
20 pub state: TransmissionState,
21 #[serde(default)]
23 pub history: TransmissionStateHistoryVec,
24}
25
26impl Powertrain for Transmission {
27 fn set_curr_pwr_prop_out_max(
28 &mut self,
29 pwr_upstream: (si::Power, si::Power),
30 _pwr_aux: si::Power,
31 _dt: si::Time,
32 _veh_state: &VehicleState,
33 ) -> anyhow::Result<()> {
34 self.state.pwr_out_fwd_max.update(
35 pwr_upstream.0
36 * (self
37 .eff_interp
38 .interpolate(&[])
39 .with_context(|| format_dbg!())?
40 * uc::R),
41 || format_dbg!(),
42 )?;
43 self.state.pwr_out_regen_max.update(
44 pwr_upstream.1
45 * (self
46 .eff_interp
47 .interpolate(&[])
48 .with_context(|| format_dbg!())?
49 * uc::R),
50 || format_dbg!(),
51 )?;
52 Ok(())
53 }
54
55 fn get_curr_pwr_prop_out_max(&self) -> anyhow::Result<(si::Power, si::Power)> {
56 Ok((
57 *self.state.pwr_out_fwd_max.get_fresh(|| format_dbg!())?,
58 *self.state.pwr_out_regen_max.get_fresh(|| format_dbg!())?,
59 ))
60 }
61
62 fn solve(
63 &mut self,
64 pwr_out_req: si::Power,
65 _enabled: bool,
66 _dt: si::Time,
67 ) -> anyhow::Result<Option<si::Power>> {
68 let state = &mut self.state;
69 ensure!(
71 pwr_out_req <= *state.pwr_out_fwd_max.get_fresh(|| format_dbg!())?,
72 "{}\n`pwr_out_req` ({} kW) exceeds `state.pwr_out_fwd_max` ({})",
73 format_dbg!(),
74 pwr_out_req.get::<si::kilowatt>().format_eng(None),
75 state
76 .pwr_out_fwd_max
77 .get_fresh(|| format_dbg!())?
78 .get::<si::kilowatt>()
79 .format_eng(None)
80 );
81 let eff_pt: &[f64] = match self.eff_interp {
84 InterpolatorEnum::Interp0D(_) => &[],
85 _ => unimplemented!("Only Interp0D is currently implemented"),
86 };
87 state.eff.update(
88 self.eff_interp.interpolate(eff_pt)? * uc::R,
89 || format_dbg!(),
90 )?;
91 ensure!(
92 *state.eff.get_fresh(|| format_dbg!())? >= 0.0 * uc::R
93 && *state.eff.get_fresh(|| format_dbg!())? <= 1.0 * uc::R,
94 format!(
95 "{}\nTransmission efficiency ({}) must be between 0 and 1",
96 format_dbg!(
97 *state.eff.get_fresh(|| format_dbg!())? >= 0.0 * uc::R
98 && *state.eff.get_fresh(|| format_dbg!())? <= 1.0 * uc::R
99 ),
100 state.eff.get_fresh(|| format_dbg!())?.get::<si::ratio>()
101 )
102 );
103
104 state.pwr_out.update(pwr_out_req, || format_dbg!())?;
105 state.pwr_in.update(
106 if *state.pwr_out.get_fresh(|| format_dbg!())? > si::Power::ZERO {
107 *state.pwr_out.get_fresh(|| format_dbg!())?
108 / *state.eff.get_fresh(|| format_dbg!())?
109 } else {
110 *state.pwr_out.get_fresh(|| format_dbg!())?
111 * *state.eff.get_fresh(|| format_dbg!())?
112 },
113 || format_dbg!(),
114 )?;
115 state.pwr_loss.update(
116 (*state.pwr_in.get_fresh(|| format_dbg!())?
117 - *state.pwr_out.get_fresh(|| format_dbg!())?)
118 .abs(),
119 || format_dbg!(),
120 )?;
121
122 Ok(Some(*state.pwr_in.get_fresh(|| format_dbg!())?))
123 }
124
125 fn pwr_regen(&self) -> anyhow::Result<si::Power> {
126 Ok(-self
127 .state
128 .pwr_out
129 .get_fresh(|| format_dbg!())?
130 .max(si::Power::ZERO))
131 }
132}
133
134impl HistoryMethods for Transmission {
135 fn save_interval(&self) -> anyhow::Result<Option<usize>> {
136 Ok(self.save_interval)
137 }
138 fn set_save_interval(&mut self, save_interval: Option<usize>) -> anyhow::Result<()> {
139 self.save_interval = save_interval;
140 Ok(())
141 }
142 fn clear(&mut self) {
143 self.history.clear()
144 }
145}
146impl SerdeAPI for Transmission {}
147impl Init for Transmission {}
148
149impl Mass for Transmission {
150 fn mass(&self) -> anyhow::Result<Option<si::Mass>> {
151 Ok(self.mass)
152 }
153
154 fn set_mass(
156 &mut self,
157 new_mass: Option<si::Mass>,
158 _side_effect: MassSideEffect,
159 ) -> anyhow::Result<()> {
160 self.mass = new_mass;
161
162 Ok(())
163 }
164
165 fn derived_mass(&self) -> anyhow::Result<Option<si::Mass>> {
167 Ok(self.mass)
168 }
169
170 fn expunge_mass_fields(&mut self) {
171 self.mass = None;
172 }
173}
174
175impl TryFrom<fastsim_2::vehicle::RustVehicle> for Transmission {
176 type Error = anyhow::Error;
177 fn try_from(f2veh: fastsim_2::vehicle::RustVehicle) -> anyhow::Result<Transmission> {
178 let transmission = Transmission {
179 mass: None,
180 eff_interp: InterpolatorEnum::new_0d(f2veh.trans_eff),
181 save_interval: Some(1),
182 state: Default::default(),
183 history: Default::default(),
184 };
185 Ok(transmission)
186 }
187}
188#[serde_api]
189#[derive(
190 Clone,
191 Default,
192 Debug,
193 Deserialize,
194 Serialize,
195 PartialEq,
196 HistoryVec,
197 StateMethods,
198 SetCumulative,
199)]
200#[non_exhaustive]
201#[serde(default)]
202#[serde(deny_unknown_fields)]
203pub struct TransmissionState {
204 pub i: TrackedState<usize>,
206
207 pub pwr_out_fwd_max: TrackedState<si::Power>,
209
210 pub pwr_out_regen_max: TrackedState<si::Power>,
212
213 pub eff: TrackedState<si::Ratio>,
215
216 pub pwr_out: TrackedState<si::Power>,
219 pub energy_out: TrackedState<si::Energy>,
220 pub pwr_in: TrackedState<si::Power>,
223 pub energy_in: TrackedState<si::Energy>,
224
225 pub pwr_loss: TrackedState<si::Power>,
227 pub energy_loss: TrackedState<si::Energy>,
228}
229
230impl Init for TransmissionState {}
231impl SerdeAPI for TransmissionState {}