1#![cfg(feature = "simdrivelabel")]
3
4use ndarray::Array;
5use serde::Serialize;
6use std::collections::HashMap;
7
8use crate::cycle::RustCycle;
10use crate::imports::*;
11use crate::params::*;
12use crate::proc_macros::add_pyo3_api;
13use crate::proc_macros::ApproxEq;
14
15#[cfg(feature = "pyo3")]
16use crate::pyo3imports::*;
17
18use crate::simdrive::{RustSimDrive, RustSimDriveParams};
19use crate::vehicle;
20
21#[add_pyo3_api]
22#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, ApproxEq)]
23pub struct LabelFe {
24 pub veh: vehicle::RustVehicle,
25 pub adj_params: AdjCoef,
26 pub lab_udds_mpgge: f64,
27 pub lab_hwy_mpgge: f64,
28 pub lab_comb_mpgge: f64,
29 pub lab_udds_kwh_per_mi: f64,
30 pub lab_hwy_kwh_per_mi: f64,
31 pub lab_comb_kwh_per_mi: f64,
32 pub adj_udds_mpgge: f64,
33 pub adj_hwy_mpgge: f64,
34 pub adj_comb_mpgge: f64,
35 pub adj_udds_kwh_per_mi: f64,
36 pub adj_hwy_kwh_per_mi: f64,
37 pub adj_comb_kwh_per_mi: f64,
38 pub adj_udds_ess_kwh_per_mi: f64,
39 pub adj_hwy_ess_kwh_per_mi: f64,
40 pub adj_comb_ess_kwh_per_mi: f64,
41 pub net_range_miles: f64,
42 pub uf: f64,
43 pub net_accel: f64,
44 pub res_found: String,
45 pub phev_calcs: Option<LabelFePHEV>,
46 pub adj_cs_comb_mpgge: Option<f64>,
47 pub adj_cd_comb_mpgge: Option<f64>,
48 pub net_phev_cd_miles: Option<f64>,
49 pub trace_miss_speed_mph: f64,
50}
51
52impl SerdeAPI for LabelFe {}
53
54#[add_pyo3_api]
55#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, ApproxEq)]
56pub struct LabelFePHEV {
58 pub regen_soc_buffer: f64,
59 pub udds: PHEVCycleCalc,
60 pub hwy: PHEVCycleCalc,
61}
62
63impl SerdeAPI for LabelFePHEV {}
64
65#[add_pyo3_api]
66#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, ApproxEq)]
67pub struct PHEVCycleCalc {
69 pub cd_ess_kwh: f64,
71 pub cd_ess_kwh_per_mi: f64,
72 pub cd_fs_gal: f64,
74 pub cd_fs_kwh: f64,
75 pub cd_mpg: f64,
76 pub cd_cycs: f64,
78 pub cd_miles: f64,
79 pub cd_lab_mpg: f64,
80 pub cd_adj_mpg: f64,
81 pub cd_frac_in_trans: f64,
83 pub trans_init_soc: f64,
85 pub trans_ess_kwh: f64,
87 pub trans_ess_kwh_per_mi: f64,
88 pub trans_fs_gal: f64,
89 pub trans_fs_kwh: f64,
90 pub cs_ess_kwh: f64,
92 pub cs_ess_kwh_per_mi: f64,
93 pub cs_fs_gal: f64,
95 pub cs_fs_kwh: f64,
96 pub cs_mpg: f64,
97 pub lab_mpgge: f64,
98 pub lab_kwh_per_mi: f64,
99 pub lab_uf: f64,
100 pub lab_uf_gpm: Array1<f64>,
101 pub lab_iter_uf: Array1<f64>,
102 pub lab_iter_uf_kwh_per_mi: Array1<f64>,
103 pub lab_iter_kwh_per_mi: Array1<f64>,
104 pub adj_iter_mpgge: Array1<f64>,
105 pub adj_iter_kwh_per_mi: Array1<f64>,
106 pub adj_iter_cd_miles: Array1<f64>,
107 pub adj_iter_uf: Array1<f64>,
108 pub adj_iter_uf_gpm: Vec<f64>,
109 pub adj_iter_uf_kwh_per_mi: Array1<f64>,
110 pub adj_cd_miles: f64,
111 pub adj_cd_mpgge: f64,
112 pub adj_cs_mpgge: f64,
113 pub adj_uf: f64,
114 pub adj_mpgge: f64,
115 pub adj_kwh_per_mi: f64,
116 pub adj_ess_kwh_per_mi: f64,
117 pub delta_soc: f64,
118 pub total_cd_miles: f64,
120}
121
122impl SerdeAPI for PHEVCycleCalc {}
123
124pub fn make_accel_trace() -> RustCycle {
125 let accel_cyc_secs = Array::range(0., 300., 0.1);
126 let cyc_len = accel_cyc_secs.len();
127 let mut accel_cyc_mps = Array::ones(cyc_len) * 90.0 / MPH_PER_MPS;
128 accel_cyc_mps[0] = 0.0;
129
130 RustCycle {
131 time_s: accel_cyc_secs,
132 mps: accel_cyc_mps,
133 grade: Array::zeros(cyc_len),
134 road_type: Array::zeros(cyc_len),
135 name: String::from("accel"),
136 orphaned: false,
137 }
138}
139
140#[cfg(feature = "pyo3")]
141#[pyfunction(name = "make_accel_trace")]
142pub fn make_accel_trace_py() -> RustCycle {
144 make_accel_trace()
145}
146
147pub fn get_net_accel(sd_accel: &mut RustSimDrive, scenario_name: &String) -> anyhow::Result<f64> {
148 #[cfg(feature = "logging")]
149 log::debug!("running `sim_drive_accel`");
150 sd_accel.sim_drive_accel(None, None)?;
151 if sd_accel.mph_ach.iter().any(|&x| x >= 60.) {
152 Ok(
153 interpolate(&60., &sd_accel.mph_ach, &sd_accel.cyc0.time_s, false)
154 .with_context(|| format_dbg!())?,
155 )
156 } else {
157 #[cfg(feature = "logging")]
158 log::warn!("vehicle '{}' never achieves 60 mph", scenario_name);
159 Ok(1e3)
160 }
161}
162
163#[cfg(feature = "pyo3")]
164#[pyfunction(name = "get_net_accel")]
165pub fn get_net_accel_py(sd_accel: &mut RustSimDrive, scenario_name: &str) -> anyhow::Result<f64> {
167 let result = get_net_accel(sd_accel, &scenario_name.to_string())?;
168 Ok(result)
169}
170
171pub fn get_label_fe(
172 veh: &vehicle::RustVehicle,
173 full_detail: Option<bool>,
174 verbose: Option<bool>,
175) -> anyhow::Result<(LabelFe, Option<HashMap<&str, RustSimDrive>>)> {
176 let sim_params = RustSimDriveParams::default();
190 let props = RustPhysicalProperties::default();
191 let long_params = RustLongParams::default();
192
193 let mut cyc = HashMap::new();
194 let mut sd = HashMap::new();
195 let mut out = LabelFe::default();
196 let mut max_trace_miss_in_mph = 0.0;
197
198 out.veh = veh.clone();
199
200 cyc.insert("accel", make_accel_trace());
202
203 cyc.insert("udds", RustCycle::from_resource("udds.csv", false)?);
204 cyc.insert("hwy", RustCycle::from_resource("hwfet.csv", false)?);
205
206 sd.insert("udds", RustSimDrive::new(cyc["udds"].clone(), veh.clone()));
208 sd.insert("hwy", RustSimDrive::new(cyc["hwy"].clone(), veh.clone()));
209
210 for (k, val) in sd.iter_mut() {
211 val.sim_drive(None, None)?;
212 let key = k;
213 let trace_miss_speed_mph = val.trace_miss_speed_mps * MPH_PER_MPS;
214 if (key == &"udds" || key == &"hwy") && trace_miss_speed_mph > max_trace_miss_in_mph {
215 max_trace_miss_in_mph = trace_miss_speed_mph;
216 }
217 }
218 out.trace_miss_speed_mph = max_trace_miss_in_mph;
219
220 let adj_params = if veh.veh_year < 2017 {
222 &long_params.ld_fe_adj_coef.adj_coef_map["2008"]
223 } else {
224 &long_params.ld_fe_adj_coef.adj_coef_map["2017"]
226 };
227 out.adj_params = adj_params.clone();
228
229 if veh.veh_pt_type != vehicle::PHEV {
231 if veh.veh_pt_type != vehicle::BEV {
232 out.lab_udds_mpgge = sd["udds"].mpgge;
234 out.lab_hwy_mpgge = sd["hwy"].mpgge;
236 out.lab_comb_mpgge = 1. / (0.55 / sd["udds"].mpgge + 0.45 / sd["hwy"].mpgge);
237 } else {
238 out.lab_udds_mpgge = 0.;
239 out.lab_hwy_mpgge = 0.;
240 out.lab_comb_mpgge = 0.;
241 }
242
243 if veh.veh_pt_type == vehicle::BEV {
244 out.lab_udds_kwh_per_mi = sd["udds"].battery_kwh_per_mi;
245 out.lab_hwy_kwh_per_mi = sd["hwy"].battery_kwh_per_mi;
246 out.lab_comb_kwh_per_mi =
247 0.55 * sd["udds"].battery_kwh_per_mi + 0.45 * sd["hwy"].battery_kwh_per_mi;
248 } else {
249 out.lab_udds_kwh_per_mi = 0.;
250 out.lab_hwy_kwh_per_mi = 0.;
251 out.lab_comb_kwh_per_mi = 0.;
252 }
253
254 if veh.veh_pt_type != vehicle::BEV {
256 out.adj_udds_mpgge =
260 1. / (adj_params.city_intercept + adj_params.city_slope / sd["udds"].mpgge);
261 out.adj_hwy_mpgge =
263 1. / (adj_params.hwy_intercept + adj_params.hwy_slope / sd["hwy"].mpgge);
264 out.adj_comb_mpgge = 1. / (0.55 / out.adj_udds_mpgge + 0.45 / out.adj_hwy_mpgge);
265 } else {
266 out.adj_udds_mpgge = 0.;
269 out.adj_hwy_mpgge = 0.;
270 out.adj_comb_mpgge = 0.;
271 }
272
273 if veh.veh_pt_type == vehicle::BEV {
275 out.adj_udds_kwh_per_mi =
277 (1. / max(
278 1. / (adj_params.city_intercept
279 + (adj_params.city_slope
280 / ((1. / out.lab_udds_kwh_per_mi) * props.kwh_per_gge))),
281 (1. / out.lab_udds_kwh_per_mi)
282 * props.kwh_per_gge
283 * (1. - sim_params.max_epa_adj),
284 )) * props.kwh_per_gge
285 / CHG_EFF;
286 out.adj_hwy_kwh_per_mi =
287 (1. / max(
288 1. / (adj_params.hwy_intercept
289 + (adj_params.hwy_slope
290 / ((1. / out.lab_hwy_kwh_per_mi) * props.kwh_per_gge))),
291 (1. / out.lab_hwy_kwh_per_mi)
292 * props.kwh_per_gge
293 * (1. - sim_params.max_epa_adj),
294 )) * props.kwh_per_gge
295 / CHG_EFF;
296 out.adj_comb_kwh_per_mi =
297 0.55 * out.adj_udds_kwh_per_mi + 0.45 * out.adj_hwy_kwh_per_mi;
298
299 out.adj_udds_ess_kwh_per_mi = out.adj_udds_kwh_per_mi * CHG_EFF;
300 out.adj_hwy_ess_kwh_per_mi = out.adj_hwy_kwh_per_mi * CHG_EFF;
301 out.adj_comb_ess_kwh_per_mi = out.adj_comb_kwh_per_mi * CHG_EFF;
302
303 out.net_range_miles = veh.ess_max_kwh / out.adj_comb_ess_kwh_per_mi;
305 }
306
307 out.uf = 0.;
309 } else {
310 let phev_calcs =
312 get_label_fe_phev(veh, &mut sd, &long_params, adj_params, &sim_params, &props)?;
313 out.phev_calcs = Some(phev_calcs.clone());
314
315 out.lab_udds_mpgge = phev_calcs.udds.lab_mpgge;
318 out.lab_hwy_mpgge = phev_calcs.hwy.lab_mpgge;
319 out.lab_comb_mpgge =
320 1.0 / (0.55 / phev_calcs.udds.lab_mpgge + 0.45 / phev_calcs.hwy.lab_mpgge);
321
322 out.lab_udds_kwh_per_mi = phev_calcs.udds.lab_kwh_per_mi;
323 out.lab_hwy_kwh_per_mi = phev_calcs.hwy.lab_kwh_per_mi;
324 out.lab_comb_kwh_per_mi =
325 0.55 * phev_calcs.udds.lab_kwh_per_mi + 0.45 * phev_calcs.hwy.lab_kwh_per_mi;
326
327 out.adj_udds_mpgge = phev_calcs.udds.adj_mpgge;
329 out.adj_hwy_mpgge = phev_calcs.hwy.adj_mpgge;
330 out.adj_comb_mpgge =
331 1.0 / (0.55 / phev_calcs.udds.adj_mpgge + 0.45 / phev_calcs.hwy.adj_mpgge);
332
333 out.adj_cs_comb_mpgge =
334 Some(1.0 / (0.55 / phev_calcs.udds.adj_cs_mpgge + 0.45 / phev_calcs.hwy.adj_cs_mpgge));
335 out.adj_cd_comb_mpgge =
336 Some(1.0 / (0.55 / phev_calcs.udds.adj_cd_mpgge + 0.45 / phev_calcs.hwy.adj_cd_mpgge));
337
338 out.adj_udds_kwh_per_mi = phev_calcs.udds.adj_kwh_per_mi;
339 out.adj_hwy_kwh_per_mi = phev_calcs.hwy.adj_kwh_per_mi;
340 out.adj_comb_kwh_per_mi =
341 0.55 * phev_calcs.udds.adj_kwh_per_mi + 0.45 * phev_calcs.hwy.adj_kwh_per_mi;
342
343 out.adj_udds_ess_kwh_per_mi = phev_calcs.udds.adj_ess_kwh_per_mi;
344 out.adj_hwy_ess_kwh_per_mi = phev_calcs.hwy.adj_ess_kwh_per_mi;
345 out.adj_comb_ess_kwh_per_mi =
346 0.55 * phev_calcs.udds.adj_ess_kwh_per_mi + 0.45 * phev_calcs.hwy.adj_ess_kwh_per_mi;
347
348 out.uf = long_params.uf_array[first_grtr(
351 &long_params.rechg_freq_miles,
352 0.55 * phev_calcs.udds.adj_cd_miles + 0.45 * phev_calcs.hwy.adj_cd_miles,
353 )
354 .unwrap()
355 - 1];
356
357 out.net_phev_cd_miles =
358 Some(0.55 * phev_calcs.udds.adj_cd_miles + 0.45 * phev_calcs.hwy.adj_cd_miles);
359
360 out.net_range_miles = (veh.fs_kwh / props.kwh_per_gge
361 - out.net_phev_cd_miles.unwrap() / out.adj_cd_comb_mpgge.unwrap())
362 * out.adj_cs_comb_mpgge.unwrap()
363 + out.net_phev_cd_miles.unwrap();
364 }
365
366 let mut sd_accel = RustSimDrive::new(cyc["accel"].clone(), veh.clone());
368 out.net_accel = get_net_accel(&mut sd_accel, &veh.scenario_name)?;
369 sd.insert("accel", sd_accel);
370
371 out.res_found = String::from("model needs to be implemented for this"); if full_detail.unwrap_or(false) && verbose.unwrap_or(false) {
375 println!("{:#?}", out);
376 Ok((out, Some(sd)))
377 } else if full_detail.unwrap_or(false) {
378 Ok((out, Some(sd)))
379 } else if verbose.unwrap_or(false) {
380 println!("{:#?}", out);
381 Ok((out, None))
382 } else {
383 Ok((out, None))
384 }
385}
386
387#[cfg(feature = "pyo3")]
388#[pyfunction(name = "get_label_fe")]
389#[cfg_attr(feature = "pyo3", pyo3(signature = (veh, full_detail=None, verbose=None)))]
390pub fn get_label_fe_py(
392 veh: &vehicle::RustVehicle,
393 full_detail: Option<bool>,
394 verbose: Option<bool>,
395) -> anyhow::Result<(LabelFe, Option<HashMap<&str, RustSimDrive>>)> {
396 let result = get_label_fe(veh, full_detail, verbose)?;
397 Ok(result)
398}
399
400pub fn get_label_fe_phev(
413 veh: &vehicle::RustVehicle,
414 sd: &mut HashMap<&str, RustSimDrive>,
415 long_params: &RustLongParams,
416 adj_params: &AdjCoef,
417 sim_params: &RustSimDriveParams,
418 props: &RustPhysicalProperties,
419) -> anyhow::Result<LabelFePHEV> {
420 let mut phev_calcs = LabelFePHEV {
421 regen_soc_buffer: min(
422 ((0.5 * veh.veh_kg * ((60. * (1. / MPH_PER_MPS)).powi(2)))
423 * (1. / 3600.)
424 * (1. / 1000.)
425 * veh.max_regen
426 * veh.mc_peak_eff())
427 / veh.ess_max_kwh,
428 (veh.max_soc - veh.min_soc) / 2.0,
429 ),
430 ..Default::default()
431 };
432
433 for (key, sd_val) in sd.iter_mut() {
435 sd_val.sim_drive(Some(veh.max_soc), None)?;
440 let mut phev_calc = PHEVCycleCalc::default();
441
442 phev_calc.cd_ess_kwh = (veh.max_soc - veh.min_soc) * veh.ess_max_kwh;
445
446 phev_calc.delta_soc = sd_val.soc[0] - sd_val.soc.last().unwrap();
448 phev_calc.total_cd_miles =
450 (veh.max_soc - veh.min_soc) * sd_val.veh.ess_max_kwh / sd_val.battery_kwh_per_mi;
451 phev_calc.cd_cycs = phev_calc.total_cd_miles / sd_val.dist_mi.sum();
453 phev_calc.cd_frac_in_trans = phev_calc.cd_cycs % phev_calc.cd_cycs.floor();
455
456 phev_calc.cd_fs_gal = sd_val.fs_kwh_out_ach.sum() / props.kwh_per_gge;
458 phev_calc.cd_fs_kwh = sd_val.fs_kwh_out_ach.sum();
459 phev_calc.cd_ess_kwh_per_mi = sd_val.battery_kwh_per_mi;
460 phev_calc.cd_mpg = sd_val.mpgge;
461
462 let interp_x_vals =
465 Array::range(0.0, phev_calc.cd_cycs.ceil() + 1.0, 1.0) * sd_val.dist_mi.sum();
466 phev_calc.lab_iter_uf = interp_x_vals
467 .iter()
468 .map(|x: &f64| -> f64 {
469 long_params.uf_array[first_grtr(&long_params.rechg_freq_miles, *x).unwrap() - 1]
470 })
471 .collect();
472
473 phev_calc.trans_init_soc = veh.max_soc - phev_calc.cd_cycs.floor() * phev_calc.delta_soc;
475
476 sd_val.sim_drive(Some(phev_calc.trans_init_soc), None)?;
478 phev_calc.trans_ess_kwh =
480 phev_calc.cd_ess_kwh_per_mi * sd_val.dist_mi.sum() * phev_calc.cd_frac_in_trans;
481 phev_calc.trans_ess_kwh_per_mi = phev_calc.cd_ess_kwh_per_mi * phev_calc.cd_frac_in_trans;
482
483 let init_soc = sd_val.veh.min_soc + 0.01;
486 sd_val.sim_drive(Some(init_soc), None)?;
487 phev_calc.cs_fs_gal = sd_val.fs_kwh_out_ach.sum() / props.kwh_per_gge;
489 phev_calc.trans_fs_gal = phev_calc.cs_fs_gal * (1.0 - phev_calc.cd_frac_in_trans);
491 phev_calc.cs_fs_kwh = sd_val.fs_kwh_out_ach.sum();
492 phev_calc.trans_fs_kwh = phev_calc.cs_fs_kwh * (1.0 - phev_calc.cd_frac_in_trans);
493 phev_calc.cs_ess_kwh = sd_val.ess_dischg_kj;
495 phev_calc.cs_ess_kwh_per_mi = sd_val.battery_kwh_per_mi;
496
497 let lab_iter_uf_diff = diff(&phev_calc.lab_iter_uf);
498 phev_calc.lab_uf_gpm = Array::from_vec(vec![
499 phev_calc.trans_fs_gal * lab_iter_uf_diff.last().unwrap(),
500 phev_calc.cs_fs_gal * (1.0 - phev_calc.lab_iter_uf.last().unwrap()),
501 ]) / sd_val.dist_mi.sum();
502
503 phev_calc.cd_mpg = sd_val.mpgge;
504
505 phev_calc.cd_miles =
507 if (veh.max_soc - phev_calcs.regen_soc_buffer - sd_val.soc.min()?) < 0.01 {
508 1000.0
509 } else {
510 phev_calc.cd_cycs.ceil() * sd_val.dist_mi.sum()
511 };
512 phev_calc.cd_lab_mpg =
513 phev_calc.lab_iter_uf.last().unwrap() / (phev_calc.trans_fs_gal / sd_val.dist_mi.sum());
514
515 phev_calc.cs_mpg = sd_val.dist_mi.sum() / phev_calc.cs_fs_gal;
517
518 phev_calc.lab_uf = long_params.uf_array
519 [first_grtr(&long_params.rechg_freq_miles, phev_calc.cd_miles).unwrap() - 1];
520
521 phev_calc.cd_adj_mpg =
523 phev_calc.lab_iter_uf.max()? / phev_calc.lab_uf_gpm[phev_calc.lab_uf_gpm.len() - 2];
524
525 phev_calc.lab_mpgge = 1.0
526 / (phev_calc.lab_uf / phev_calc.cd_adj_mpg
527 + (1.0 - phev_calc.lab_uf) / phev_calc.cs_mpg);
528
529 let mut lab_iter_kwh_per_mi_vals = Vec::new();
530 lab_iter_kwh_per_mi_vals.push(0.0);
531 lab_iter_kwh_per_mi_vals
532 .extend(vec![phev_calc.cd_ess_kwh_per_mi; phev_calc.cd_cycs.floor() as usize].iter());
533 lab_iter_kwh_per_mi_vals.push(phev_calc.trans_ess_kwh_per_mi);
534 lab_iter_kwh_per_mi_vals.push(0.0);
535 phev_calc.lab_iter_kwh_per_mi = Array::from_vec(lab_iter_kwh_per_mi_vals);
536 let mut vals = Vec::new();
537 vals.push(0.0);
538 vals.extend(
539 (&phev_calc
540 .lab_iter_kwh_per_mi
541 .slice(s![1..phev_calc.lab_iter_kwh_per_mi.len() - 1])
542 * &diff(&phev_calc.lab_iter_uf).slice(s![1..]))
543 .iter(),
544 );
545 vals.push(0.0);
546 phev_calc.lab_iter_uf_kwh_per_mi = Array::from_vec(vals);
547
548 phev_calc.lab_kwh_per_mi =
549 phev_calc.lab_iter_uf_kwh_per_mi.sum() / phev_calc.lab_iter_uf.max()?;
550
551 let mut adj_iter_mpgge_vals = vec![0.0; phev_calc.cd_cycs.floor() as usize];
552 let mut adj_iter_kwh_per_mi_vals = vec![0.0; phev_calc.lab_iter_kwh_per_mi.len()];
553 if *key == "udds" {
554 adj_iter_mpgge_vals.push(max(
555 1.0 / (adj_params.city_intercept
556 + (adj_params.city_slope
557 / (sd_val.dist_mi.sum() / (phev_calc.trans_fs_kwh / props.kwh_per_gge)))),
558 sd_val.dist_mi.sum() / (phev_calc.trans_fs_kwh / props.kwh_per_gge)
559 * (1.0 - sim_params.max_epa_adj),
560 ));
561 adj_iter_mpgge_vals.push(max(
562 1.0 / (adj_params.city_intercept
563 + (adj_params.city_slope
564 / (sd_val.dist_mi.sum() / (phev_calc.cs_fs_kwh / props.kwh_per_gge)))),
565 sd_val.dist_mi.sum() / (phev_calc.cs_fs_kwh / props.kwh_per_gge)
566 * (1.0 - sim_params.max_epa_adj),
567 ));
568
569 for (c, _) in phev_calc.lab_iter_kwh_per_mi.iter().enumerate() {
570 if phev_calc.lab_iter_kwh_per_mi[c] == 0.0 {
571 adj_iter_kwh_per_mi_vals[c] = 0.0;
572 } else {
573 adj_iter_kwh_per_mi_vals[c] =
574 (1.0 / max(
575 1.0 / (adj_params.city_intercept
576 + (adj_params.city_slope
577 / ((1.0 / phev_calc.lab_iter_kwh_per_mi[c])
578 * props.kwh_per_gge))),
579 (1.0 - sim_params.max_epa_adj)
580 * ((1.0 / phev_calc.lab_iter_kwh_per_mi[c]) * props.kwh_per_gge),
581 )) * props.kwh_per_gge;
582 }
583 }
584 } else {
585 adj_iter_mpgge_vals.push(max(
586 1.0 / (adj_params.hwy_intercept
587 + (adj_params.hwy_slope
588 / (sd_val.dist_mi.sum() / (phev_calc.trans_fs_kwh / props.kwh_per_gge)))),
589 sd_val.dist_mi.sum() / (phev_calc.trans_fs_kwh / props.kwh_per_gge)
590 * (1.0 - sim_params.max_epa_adj),
591 ));
592 adj_iter_mpgge_vals.push(max(
593 1.0 / (adj_params.hwy_intercept
594 + (adj_params.hwy_slope
595 / (sd_val.dist_mi.sum() / (phev_calc.cs_fs_kwh / props.kwh_per_gge)))),
596 sd_val.dist_mi.sum() / (phev_calc.cs_fs_kwh / props.kwh_per_gge)
597 * (1.0 - sim_params.max_epa_adj),
598 ));
599
600 for (c, _) in phev_calc.lab_iter_kwh_per_mi.iter().enumerate() {
601 if phev_calc.lab_iter_kwh_per_mi[c] == 0.0 {
602 adj_iter_kwh_per_mi_vals[c] = 0.0;
603 } else {
604 adj_iter_kwh_per_mi_vals[c] =
605 (1.0 / max(
606 1.0 / (adj_params.hwy_intercept
607 + (adj_params.hwy_slope
608 / ((1.0 / phev_calc.lab_iter_kwh_per_mi[c])
609 * props.kwh_per_gge))),
610 (1.0 - sim_params.max_epa_adj)
611 * ((1.0 / phev_calc.lab_iter_kwh_per_mi[c]) * props.kwh_per_gge),
612 )) * props.kwh_per_gge;
613 }
614 }
615 }
616 phev_calc.adj_iter_mpgge = Array::from(adj_iter_mpgge_vals);
617 phev_calc.adj_iter_kwh_per_mi = Array::from(adj_iter_kwh_per_mi_vals);
618
619 phev_calc.adj_iter_cd_miles =
620 Array::from_vec(vec![0.0; phev_calc.cd_cycs.ceil() as usize + 2]);
621 for c in 0..phev_calc.adj_iter_cd_miles.len() {
622 if c == 0 {
623 phev_calc.adj_iter_cd_miles[c] = 0.0;
624 } else if c <= phev_calc.cd_cycs.floor() as usize {
625 phev_calc.adj_iter_cd_miles[c] = phev_calc.adj_iter_cd_miles[c - 1]
626 + phev_calc.cd_ess_kwh_per_mi * sd_val.dist_mi.sum()
627 / phev_calc.adj_iter_kwh_per_mi[c];
628 } else if c == phev_calc.cd_cycs.floor() as usize + 1 {
629 phev_calc.adj_iter_cd_miles[c] = phev_calc.adj_iter_cd_miles[c - 1]
630 + phev_calc.trans_ess_kwh_per_mi * sd_val.dist_mi.sum()
631 / phev_calc.adj_iter_kwh_per_mi[c];
632 } else {
633 phev_calc.adj_iter_cd_miles[c] = 0.0;
634 }
635 }
636
637 phev_calc.adj_cd_miles =
638 if veh.max_soc - phev_calcs.regen_soc_buffer - sd_val.soc.min()? < 0.01 {
639 1000.0
640 } else {
641 *phev_calc.adj_iter_cd_miles.max()?
642 };
643
644 phev_calc.adj_iter_uf = phev_calc
647 .adj_iter_cd_miles
648 .iter()
649 .map(|x: &f64| -> f64 {
650 long_params.uf_array[first_grtr(&long_params.rechg_freq_miles, *x).unwrap() - 1]
651 })
652 .collect();
653
654 let adj_iter_uf_diff = diff(&phev_calc.adj_iter_uf);
655 phev_calc.adj_iter_uf_gpm = vec![0.0; phev_calc.cd_cycs.floor() as usize];
656 phev_calc.adj_iter_uf_gpm.push(
657 (1.0 / phev_calc.adj_iter_mpgge[phev_calc.adj_iter_mpgge.len() - 2])
658 * adj_iter_uf_diff[adj_iter_uf_diff.len() - 2],
659 );
660 phev_calc.adj_iter_uf_gpm.push(
661 (1.0 / phev_calc.adj_iter_mpgge.last().unwrap())
662 * (1.0 - phev_calc.adj_iter_uf[phev_calc.adj_iter_uf.len() - 2]),
663 );
664
665 phev_calc.adj_iter_uf_kwh_per_mi =
666 &phev_calc.adj_iter_kwh_per_mi * &diff(&phev_calc.adj_iter_uf);
667
668 phev_calc.adj_cd_mpgge = 1.0
669 / phev_calc.adj_iter_uf_gpm[phev_calc.adj_iter_uf_gpm.len() - 2]
670 * phev_calc.adj_iter_uf.max()?;
671 phev_calc.adj_cs_mpgge = 1.0 / phev_calc.adj_iter_uf_gpm.last().unwrap()
672 * (1.0 - phev_calc.adj_iter_uf.max()?);
673
674 phev_calc.adj_uf = long_params.uf_array
675 [first_grtr(&long_params.rechg_freq_miles, phev_calc.adj_cd_miles).unwrap() - 1];
676
677 phev_calc.adj_mpgge = 1.0
678 / (phev_calc.adj_uf / phev_calc.adj_cd_mpgge
679 + (1.0 - phev_calc.adj_uf) / phev_calc.adj_cs_mpgge);
680
681 phev_calc.adj_kwh_per_mi =
682 phev_calc.adj_iter_uf_kwh_per_mi.sum() / phev_calc.adj_iter_uf.max()? / veh.chg_eff;
683
684 phev_calc.adj_ess_kwh_per_mi =
685 phev_calc.adj_iter_uf_kwh_per_mi.sum() / phev_calc.adj_iter_uf.max()?;
686
687 match *key {
688 "udds" => phev_calcs.udds = phev_calc.clone(),
689 "hwy" => phev_calcs.hwy = phev_calc.clone(),
690 &_ => bail!("No field for cycle {}", key),
691 };
692 }
693
694 Ok(phev_calcs)
695}
696
697#[cfg(feature = "pyo3")]
698#[pyfunction(name = "get_label_fe_phev")]
699pub fn get_label_fe_phev_py(
701 veh: &vehicle::RustVehicle,
702 sd_dict: Bound<PyDict>,
703 adj_params: AdjCoef,
704 long_params: RustLongParams,
705 sim_params: &RustSimDriveParams,
706 props: RustPhysicalProperties,
707) -> anyhow::Result<LabelFePHEV> {
708 let mut sd_mut: HashMap<String, RustSimDrive> = HashMap::new();
709 for (key, value) in sd_dict.keys().into_iter().zip(sd_dict.values().into_iter()) {
710 let key_extracted = key
711 .extract::<String>()
712 .with_context(|| format!("{}\nFailed to extract key", format_dbg!()))?;
713 let value_extracted = value
714 .extract()
715 .with_context(|| format!("{}\nFailed to extract value", format_dbg!()))?;
716 sd_mut.insert(key_extracted, value_extracted);
717 }
718
719 let mut sd_mut =
721 HashMap::from_iter(sd_mut.iter().map(|item| (item.0.as_str(), item.1.clone())));
722
723 get_label_fe_phev(
724 veh,
725 &mut sd_mut,
726 &long_params,
727 &adj_params,
728 sim_params,
729 &props,
730 )
731}
732
733#[cfg(test)]
734mod simdrivelabel_tests {
735 use super::*;
736
737 #[test]
738 fn test_get_label_fe_conv() {
739 let veh = vehicle::RustVehicle::mock_vehicle();
740 let (mut label_fe, _) = get_label_fe(&veh, None, None).unwrap();
741 let ref_veh = vehicle::RustVehicle::default();
745 label_fe.veh = ref_veh.clone();
746 let label_fe_truth = LabelFe {
749 veh: ref_veh,
750 adj_params: RustLongParams::default().ld_fe_adj_coef.adj_coef_map["2008"].clone(),
751 lab_udds_mpgge: 32.47503766676829,
752 lab_hwy_mpgge: 42.265348793379445,
753 lab_comb_mpgge: 36.25407690819302,
754 lab_udds_kwh_per_mi: 0.,
755 lab_hwy_kwh_per_mi: 0.,
756 lab_comb_kwh_per_mi: 0.,
757 adj_udds_mpgge: 25.246151811422468,
758 adj_hwy_mpgge: 30.08729992782952,
759 adj_comb_mpgge: 27.21682755127691,
760 adj_udds_kwh_per_mi: 0.,
761 adj_hwy_kwh_per_mi: 0.,
762 adj_comb_kwh_per_mi: 0.,
763 adj_udds_ess_kwh_per_mi: 0.,
764 adj_hwy_ess_kwh_per_mi: 0.,
765 adj_comb_ess_kwh_per_mi: 0.,
766 net_range_miles: 0.,
767 uf: 0.,
768 net_accel: 9.451683946821882,
769 res_found: String::from("model needs to be implemented for this"),
770 phev_calcs: None,
771 adj_cs_comb_mpgge: None,
772 adj_cd_comb_mpgge: None,
773 net_phev_cd_miles: None,
774 trace_miss_speed_mph: 0.0,
775 };
776
777 assert!(
783 label_fe.approx_eq(&label_fe_truth, 1e-10),
784 "label_fe:\n{}\n\nlabel_fe_truth:\n{}",
785 label_fe.to_json().unwrap(),
786 label_fe_truth.to_json().unwrap(),
787 );
788 }
789
790 #[test]
791 fn test_get_label_fe_phev() {
792 let mut veh = vehicle::RustVehicle {
793 props: RustPhysicalProperties {
794 air_density_kg_per_m3: 1.2,
795 a_grav_mps2: 9.81,
796 kwh_per_gge: 33.7,
797 fuel_rho_kg__L: 0.75,
798 fuel_afr_stoich: 14.7,
799 orphaned: false,
800 },
801 veh_kg: Default::default(),
802 scenario_name: "2016 Chevrolet Volt".into(),
803 selection: 13,
804 veh_year: 2016,
805 veh_pt_type: "PHEV".into(),
806 drag_coef: 0.3,
807 frontal_area_m2: 2.565,
808 glider_kg: 950.564,
809 veh_cg_m: 0.53,
810 drive_axle_weight_frac: 0.59,
811 wheel_base_m: 2.6,
812 cargo_kg: 136.0,
813 veh_override_kg: None,
814 comp_mass_multiplier: 1.4,
815 fs_max_kw: 2000.0,
816 fs_secs_to_peak_pwr: 1.0,
817 fs_kwh: 297.0,
818 fs_kwh_per_kg: 9.89,
819 fc_max_kw: 75.0,
820 fc_pwr_out_perc: Array1::from(vec![
821 0.0, 0.005, 0.015, 0.04, 0.06, 0.1, 0.14, 0.2, 0.4, 0.6, 0.8, 1.0,
822 ]),
823 fc_eff_map: Array1::from(vec![
824 0.1, 0.12, 0.16, 0.22, 0.28, 0.33, 0.35, 0.36, 0.35, 0.34, 0.32, 0.3,
825 ]),
826 fc_eff_type: "SI".into(),
827 fc_sec_to_peak_pwr: 6.0,
828 fc_base_kg: 61.0,
829 fc_kw_per_kg: 2.13,
830 min_fc_time_on: 30.0,
831 idle_fc_kw: 1.5,
832 mc_max_kw: 111.0,
833 mc_pwr_out_perc: Array1::from(vec![
834 0.0, 0.02, 0.04, 0.06, 0.08, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0,
835 ]),
836 mc_eff_map: Array1::from(vec![
837 0.84, 0.86, 0.88, 0.9, 0.91, 0.92, 0.94, 0.95, 0.95, 0.94, 0.93,
838 ]),
839 mc_sec_to_peak_pwr: 3.0,
840 mc_pe_kg_per_kw: 0.833,
841 mc_pe_base_kg: 21.6,
842 ess_max_kw: 115.0,
843 ess_max_kwh: 18.4,
844 ess_kg_per_kwh: 8.0,
845 ess_base_kg: 75.0,
846 ess_round_trip_eff: 0.97,
847 ess_life_coef_a: 110.0,
848 ess_life_coef_b: -0.6811,
849 min_soc: 0.15,
850 max_soc: 0.9,
851 ess_dischg_to_fc_max_eff_perc: 1.0,
852 ess_chg_to_fc_max_eff_perc: 0.0,
853 wheel_inertia_kg_m2: 0.815,
854 num_wheels: 4.0,
855 wheel_rr_coef: 0.007,
856 wheel_radius_m: 0.336,
857 wheel_coef_of_fric: 0.7,
858 max_accel_buffer_mph: 60.0,
859 max_accel_buffer_perc_of_useable_soc: 0.2,
860 perc_high_acc_buf: 0.0,
861 mph_fc_on: 85.0,
862 kw_demand_fc_on: 120.0,
863 max_regen: 0.98,
864 stop_start: false,
865 force_aux_on_fc: false,
866 alt_eff: 1.0,
867 chg_eff: 0.86,
868 aux_kw: 0.3,
869 trans_kg: 114.0,
870 trans_eff: 0.98,
871 ess_to_fuel_ok_error: 0.005,
872 small_motor_power_kw: 7.5,
873 large_motor_power_kw: 75.0,
874 fc_perc_out_array: FC_PERC_OUT_ARRAY.into(),
875 mc_kw_out_array: Default::default(),
876 mc_max_elec_in_kw: Default::default(),
877 mc_full_eff_array: Default::default(),
878 max_trac_mps2: Default::default(),
879 ess_mass_kg: Default::default(),
880 mc_mass_kg: Default::default(),
881 fc_mass_kg: Default::default(),
882 fs_mass_kg: Default::default(),
883 mc_perc_out_array: Default::default(),
884 regen_a: 500.0,
885 regen_b: 0.99,
886 charging_on: false,
887 no_elec_sys: false,
888 no_elec_aux: false,
889 max_roadway_chg_kw: Array1::from(vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
890 input_kw_out_array: Array1::from(vec![
891 0.0,
892 0.375,
893 1.125,
894 3.0,
895 4.5,
896 7.5,
897 10.500000000000002,
898 15.0,
899 30.0,
900 45.0,
901 60.0,
902 75.0,
903 ]),
904 fc_kw_out_array: Default::default(),
905 fc_eff_array: Default::default(),
906 modern_max: 0.95,
907 mc_eff_array: Array1::from(vec![
908 0.84, 0.86, 0.88, 0.9, 0.91, 0.92, 0.94, 0.95, 0.95, 0.94, 0.93,
909 ]),
910 mc_kw_in_array: Default::default(),
911 val_udds_mpgge: f64::NAN,
912 val_hwy_mpgge: f64::NAN,
913 val_comb_mpgge: 42.0,
914 val_udds_kwh_per_mile: f64::NAN,
915 val_hwy_kwh_per_mile: f64::NAN,
916 val_comb_kwh_per_mile: 0.31,
917 val_cd_range_mi: 53.0,
918 val_const65_mph_kwh_per_mile: f64::NAN,
919 val_const60_mph_kwh_per_mile: f64::NAN,
920 val_const55_mph_kwh_per_mile: f64::NAN,
921 val_const45_mph_kwh_per_mile: f64::NAN,
922 val_unadj_udds_kwh_per_mile: f64::NAN,
923 val_unadj_hwy_kwh_per_mile: f64::NAN,
924 val0_to60_mph: 8.4,
925 val_ess_life_miles: 120000.0,
926 val_range_miles: f64::NAN,
927 val_veh_base_cost: 17000.0,
928 val_msrp: 33170.0,
929 fc_peak_eff_override: None,
930 mc_peak_eff_override: None,
931 orphaned: false,
932 ..Default::default()
933 };
934 veh.set_derived().unwrap();
935
936 let (mut label_fe, _) = get_label_fe(&veh, None, None).unwrap();
937 label_fe.veh = vehicle::RustVehicle::default();
940 println!("Calculated net accel: {}", label_fe.net_accel);
942 println!(
943 "Percent diff to Python calc: {:.3}%",
944 100. * (9.451683946821882 - label_fe.net_accel) / 9.451683946821882
945 );
946 label_fe.net_accel = 1000.;
947
948 let udds = PHEVCycleCalc {
949 cd_ess_kwh: 13.799999999999999,
950 cd_ess_kwh_per_mi: 0.1670807863534209,
951 cd_fs_gal: 0.0,
952 cd_fs_kwh: 0.0,
953 cd_mpg: 65.0128437991813,
954 cd_cycs: 11.083418864860784,
955 cd_miles: 89.42523198551896,
956 cd_lab_mpg: 59.77814990568397,
957 cd_adj_mpg: 2968.1305812156647,
958 cd_frac_in_trans: 0.08341886486078387,
959 trans_init_soc: 0.15564484203010176,
960 trans_ess_kwh: 0.10386509335387073,
961 trans_ess_kwh_per_mi: 0.013937689537649522,
962 trans_fs_gal: 0.105063189381161,
963 trans_fs_kwh: 3.5406294821451265,
964 cs_ess_kwh: -27.842875966770062,
965 cs_ess_kwh_per_mi: -0.001037845633667792,
966 cs_fs_gal: 0.11462508375235472,
967 cs_fs_kwh: 3.8628653224543545,
968 cs_mpg: 65.01284379918131,
969 lab_mpgge: 370.06411942132064,
970 lab_kwh_per_mi: 0.16342111007981494,
971 lab_uf: 0.8427800000000001,
972 lab_uf_gpm: Array::from_vec(vec![0.00028394, 0.00241829]),
973 lab_iter_uf: Array::from_vec(vec![
974 0., 0.16268, 0.28152, 0.41188, 0.51506, 0.59611, 0.64532, 0.69897, 0.74176,
975 0.77648, 0.79825, 0.82264, 0.84278,
976 ]),
977 lab_iter_uf_kwh_per_mi: Array::from_vec(vec![
978 0., 0.0271807, 0.01985588, 0.02178065, 0.0172394, 0.0135419, 0.00822205,
979 0.00896388, 0.00714939, 0.00580104, 0.00363735, 0.0040751, 0.00028071, 0.,
980 ]),
981 lab_iter_kwh_per_mi: Array::from_vec(vec![
982 0., 0.16708079, 0.16708079, 0.16708079, 0.16708079, 0.16708079, 0.16708079,
983 0.16708079, 0.16708079, 0.16708079, 0.16708079, 0.16708079, 0.01393769, 0.,
984 ]),
985 adj_iter_mpgge: Array::from_vec(vec![
986 0.,
987 0.,
988 0.,
989 0.,
990 0.,
991 0.,
992 0.,
993 0.,
994 0.,
995 0.,
996 0.,
997 50.2456134,
998 46.69198818,
999 ]),
1000 adj_iter_kwh_per_mi: Array::from_vec(vec![
1001 0., 0.23868684, 0.23868684, 0.23868684, 0.23868684, 0.23868684, 0.23868684,
1002 0.23868684, 0.23868684, 0.23868684, 0.23868684, 0.23868684, 0.01991099, 0.,
1003 ]),
1004 adj_iter_cd_miles: Array::from_vec(vec![
1005 0.,
1006 5.21647187,
1007 10.43294373,
1008 15.6494156,
1009 20.86588746,
1010 26.08235933,
1011 31.29883119,
1012 36.51530306,
1013 41.73177493,
1014 46.94824679,
1015 52.16471866,
1016 57.38119052,
1017 62.59766239,
1018 0.,
1019 ]),
1020 adj_iter_uf: Array::from_vec(vec![
1021 0., 0.11878, 0.2044, 0.31698, 0.38194, 0.46652, 0.53737, 0.57771, 0.62998, 0.6599,
1022 0.69897, 0.73185, 0.75126, 0.,
1023 ]),
1024 adj_iter_uf_gpm: vec![
1025 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.0003863, 0.00532725,
1026 ],
1027 adj_iter_uf_kwh_per_mi: Array::from_vec(vec![
1028 0., 0.02835122, 0.02043637, 0.02687136, 0.0155051, 0.02018813, 0.01691096,
1029 0.00962863, 0.01247616, 0.00714151, 0.00932549, 0.00784802, 0.00038647, 0.,
1030 ]),
1031 adj_cd_miles: 62.59766238986325,
1032 adj_cd_mpgge: 1944.7459827561047,
1033 adj_cs_mpgge: 46.69198818435928,
1034 adj_uf: 0.75126,
1035 adj_mpgge: 175.0223917643415,
1036 adj_kwh_per_mi: 0.27097024959679444,
1037 adj_ess_kwh_per_mi: 0.23303441465324323,
1038 delta_soc: 0.0676686507245362,
1039 total_cd_miles: 82.59477526523773,
1040 };
1041
1042 let hwy = PHEVCycleCalc {
1043 cd_ess_kwh: 13.799999999999999,
1044 cd_ess_kwh_per_mi: 0.19912462736394723,
1045 cd_fs_gal: 0.0,
1046 cd_fs_kwh: 0.0,
1047 cd_mpg: 61.75832757157714,
1048 cd_cycs: 6.75533367335913,
1049 cd_miles: 71.81337619240335,
1050 cd_lab_mpg: 199.76659107309018,
1051 cd_adj_mpg: 4975.506626976092,
1052 cd_frac_in_trans: 0.7553336733591296,
1053 trans_init_soc: 0.23385969996618272,
1054 trans_ess_kwh: 1.5430184793777608,
1055 trans_ess_kwh_per_mi: 0.15040553624307812,
1056 trans_fs_gal: 0.040643020828268026,
1057 trans_fs_kwh: 1.3696698019126325,
1058 cs_ess_kwh: -27.84287564320177,
1059 cs_ess_kwh_per_mi: -0.0007538835761840731,
1060 cs_fs_gal: 0.1661161198039534,
1061 cs_fs_kwh: 5.59811323739323,
1062 cs_mpg: 61.75832757157714,
1063 lab_mpgge: 282.75893721314793,
1064 lab_kwh_per_mi: 0.19665299886733625,
1065 lab_uf: 0.7914100000000001,
1066 lab_uf_gpm: Array::from_vec(vec![0.00015906, 0.00337752]),
1067 lab_iter_uf: Array::from_vec(vec![
1068 0., 0.2044, 0.38194, 0.51506, 0.62998, 0.69897, 0.75126, 0.79141,
1069 ]),
1070 lab_iter_uf_kwh_per_mi: Array::from_vec(vec![
1071 0., 0.04070107, 0.03535259, 0.02650747, 0.0228834, 0.01373761, 0.01041223,
1072 0.00603878, 0.,
1073 ]),
1074 lab_iter_kwh_per_mi: Array::from_vec(vec![
1075 0., 0.19912463, 0.19912463, 0.19912463, 0.19912463, 0.19912463, 0.19912463,
1076 0.15040554, 0.,
1077 ]),
1078 adj_iter_mpgge: Array::from_vec(vec![0., 0., 0., 0., 0., 0., 176.69300837, 43.2308293]),
1079 adj_iter_kwh_per_mi: Array::from_vec(vec![
1080 0., 0.28446375, 0.28446375, 0.28446375, 0.28446375, 0.28446375, 0.28446375,
1081 0.21486505, 0.,
1082 ]),
1083 adj_iter_cd_miles: Array::from_vec(vec![
1084 0.,
1085 7.18133762,
1086 14.36267524,
1087 21.54401286,
1088 28.72535048,
1089 35.9066881,
1090 43.08802572,
1091 50.26936333,
1092 0.,
1093 ]),
1094 adj_iter_uf: Array::from_vec(vec![
1095 0., 0.16268, 0.28152, 0.41188, 0.49148, 0.57771, 0.64532, 0.68662, 0.,
1096 ]),
1097 adj_iter_uf_gpm: vec![0., 0., 0., 0., 0., 0., 0.00023374, 0.00724899],
1098 adj_iter_uf_kwh_per_mi: Array::from_vec(vec![
1099 0., 0.04627656, 0.03380567, 0.03708269, 0.02264331, 0.02452931, 0.01923259,
1100 0.00887393, 0.,
1101 ]),
1102 adj_cd_miles: 50.26936333468235,
1103 adj_cd_mpgge: 2937.5533511975764,
1104 adj_cs_mpgge: 43.230829300104,
1105 adj_uf: 0.68662,
1106 adj_mpgge: 133.64102451254365,
1107 adj_kwh_per_mi: 0.3259039663244739,
1108 adj_ess_kwh_per_mi: 0.2802774110390475,
1109 delta_soc: 0.11102338333896955,
1110 total_cd_miles: 69.30333119859274,
1111 };
1112
1113 let phev_calcs = LabelFePHEV {
1114 regen_soc_buffer: 0.00957443430586049,
1115 udds,
1116 hwy,
1117 };
1118
1119 let label_fe_truth = LabelFe {
1120 veh: vehicle::RustVehicle::default(),
1121 adj_params: RustLongParams::default().ld_fe_adj_coef.adj_coef_map["2008"].clone(),
1122 lab_udds_mpgge: 370.06411942132064,
1123 lab_hwy_mpgge: 282.75893721314793,
1124 lab_comb_mpgge: 324.91895455274005,
1125 lab_udds_kwh_per_mi: 0.16342111007981494,
1126 lab_hwy_kwh_per_mi: 0.19665299886733625,
1127 lab_comb_kwh_per_mi: 0.17837546003419952,
1128 adj_udds_mpgge: 175.0223917643415,
1129 adj_hwy_mpgge: 133.64102451254365,
1130 adj_comb_mpgge: 153.61727461480555,
1131 adj_udds_kwh_per_mi: 0.27097024959679444,
1132 adj_hwy_kwh_per_mi: 0.3259039663244739,
1133 adj_comb_kwh_per_mi: 0.29569042212425023,
1134 adj_udds_ess_kwh_per_mi: 0.23303441465324323,
1135 adj_hwy_ess_kwh_per_mi: 0.2802774110390475,
1136 adj_comb_ess_kwh_per_mi: 0.25429376302685514,
1137 net_range_miles: 453.1180867180584,
1138 uf: 0.73185,
1139 net_accel: 1000.,
1141 res_found: String::from("model needs to be implemented for this"),
1142 phev_calcs: Some(phev_calcs),
1143 adj_cs_comb_mpgge: Some(45.06826741586106),
1144 adj_cd_comb_mpgge: Some(2293.5675017498143),
1145 net_phev_cd_miles: Some(57.04992781503185),
1146 trace_miss_speed_mph: 0.0,
1147 };
1148
1149 let tol = 1e-8;
1150 assert!(label_fe.veh.approx_eq(&label_fe_truth.veh, tol));
1151 assert!(
1152 label_fe
1153 .phev_calcs
1154 .approx_eq(&label_fe_truth.phev_calcs, tol),
1155 "label_fe.phev_calcs: {:?}",
1156 &label_fe.phev_calcs
1157 );
1158 assert!(label_fe.approx_eq(&label_fe_truth, tol));
1159 }
1160}