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