Skip to content

Fuel Economy module

t3co.objectives.fueleconomy

Module containing functions for calculating fuel economy objectives.

CD = 0 module-attribute

CS = 1 module-attribute

PHEV = 365 module-attribute

cd_elec_kwh_out_ach = round(sum(np.array(sim_drives[CD].ess_kw_out_ach) / 3600), 1) module-attribute

cd_elec_mpgge = round(sum(sim_drives[CD].dist_mi) / cd_elec_kwh_out_ach / gl.get_kwh_per_gge(), 1) module-attribute

cd_fuel_kwh_out_ach = round(sum(sim_drives[CD].fs_kwh_out_ach), 1) module-attribute

cs_elec_kwh_out_ach = round(sum(np.array(sim_drives[CS].ess_kw_out_ach) / 3600), 1) module-attribute

cs_fuel_kwh_out_ach = round(sum(sim_drives[CS].fs_kwh_out_ach), 1) module-attribute

cyc_file_path = gl.OPTIMIZATION_DRIVE_CYCLES / 'regional_haul.csv' module-attribute

cycle = run_scenario.load_design_cycle_from_path(cyc_file_path) module-attribute

fc_kwh_cummultive = np.cumsum(np.array(sim_drives[0].fc_kw_out_ach)) / 3600 module-attribute

fc_kwh_out = np.array(sim_drives[0].fc_kw_out_ach) / 3600 module-attribute

mc_kwh_cummultive = np.cumsum(np.array(sim_drives[0].mc_mech_kw_out_ach)) / 3600 module-attribute

mk_kwh_out = np.array(sim_drives[0].mc_mech_kw_out_ach) / 3600 module-attribute

scenario_inputs_path = Path(gl.T3CO_INPUTS_DIR / 'tda_example/TDA_FY22_scenario_assumptions.csv').resolve() module-attribute

v = run_scenario.get_vehicle(PHEV, vehicle_input_path) module-attribute

vehicle_input_path = Path(gl.T3CO_INPUTS_DIR / 'tda_example/TDA_FY22_vehicle_model_assumptions.csv').resolve() module-attribute

x = range(0, len(cycle.time_s)) module-attribute

get_mpgge(eff_range_cyc: fastsim.cycle.Cycle | List[Tuple[fastsim.cycle.Cycle, float]], v: fastsim.vehicle.Vehicle, scenario, diagnostic=False) -> Tuple[dict, List[fastsim.simdrive.SimDrive], List[dict]]

This helper method gets the composite mpgge fuel efficiency of vehicle for each efficiency_range Drive Cycle and weight. It runs the vehicle using efficiency range cycle(s) and returns mpgge based on the powertrain type

Method computes a composite mpgge from multiple drive cycles and weights for each cycle. If the user passes in a single Drive Cycle rather than a list of tuples, the base case of a composite mpgge from a single Drive Cycle and a single weight, 1, is computed.

Also updates the vehicle's corresponding scenario object

Parameters:

Name Type Description Default
eff_range_cyc fastsim.cycle.Cycle | List[Tuple[fastsim.cycle.Cycle, float]]

efficiency range cycle

required
v fastsim.vehicle.Vehicle

FASTSim vehicle object for analysis vehicle

required
scenario run_scenario.Scenario

Scenario object for current selection

required
diagnostic bool

if True, returns all mpgge dicts. Defaults to False.

False

Raises:

Type Description
ValueError

unknown vehicle powertrain type

Returns:

Name Type Description
mpgge_comp dict

Dictionary containing MPGGE breakdowns

sim_drives List[fastsim.simdrive.SimDrive]

List of simdrives for charge depleting and charge sustaining cycles

mpgges (List[dict], optional)

if diagnostic==True, returns additional

Source code in t3co/objectives/fueleconomy.py
Python
def get_mpgge(
    eff_range_cyc: fastsim.cycle.Cycle | List[Tuple[fastsim.cycle.Cycle, float]],
    v: fastsim.vehicle.Vehicle,
    scenario,
    diagnostic=False,
) -> Tuple[dict, List[fastsim.simdrive.SimDrive], List[dict]]:
    """
    This helper method gets the composite mpgge fuel efficiency of vehicle for each efficiency_range Drive Cycle and weight.
    It runs the vehicle using efficiency range cycle(s) and returns mpgge based on the powertrain type

    Method computes a composite mpgge from multiple drive cycles and weights for each cycle.
    If the user passes in a single Drive Cycle rather than a list of tuples, the base case of
    a composite mpgge from a single Drive Cycle and a single weight, 1, is computed.

    Also updates the vehicle's corresponding scenario object

    Args:
        eff_range_cyc (fastsim.cycle.Cycle | List[Tuple[fastsim.cycle.Cycle, float]]): efficiency range cycle
        v (fastsim.vehicle.Vehicle): FASTSim vehicle object for analysis vehicle
        scenario (run_scenario.Scenario): Scenario object for current selection
        diagnostic (bool, optional): if True, returns all mpgge dicts. Defaults to False.

    Raises:
        ValueError: unknown vehicle powertrain type

    Returns:
        mpgge_comp (dict): Dictionary containing MPGGE breakdowns
        sim_drives (List[fastsim.simdrive.SimDrive]): List of simdrives for charge depleting and charge sustaining cycles
        mpgges (List[dict], optional): if diagnostic==True, returns additional

    """

    if not isinstance(eff_range_cyc, list):
        eff_range_cyc = [(eff_range_cyc, 1)]

    if v.veh_pt_type == gl.BEV:
        assert (
            v.mc_max_kw > 0
        ), "motor size is 0 kw in BEV - will lead to faulty results"
        assert (
            v.fs_kwh == 0 and v.fc_max_kw == 0
        ), "Error! BEV vehicle has non-zero ICE attributes - vehicle mass calculation may be off"
    elif v.veh_pt_type == gl.CONV:
        assert (
            v.fc_max_kw > 0
        ), "engine size is 0 kw in CONV - will lead to faulty results"
        assert (
            v.ess_max_kwh == 0 and v.mc_max_kw == 0
        ), "Error! CONV vehicle has non-zero BEV attributes - vehicle mass calculation may be off"
    elif v.veh_pt_type == gl.PHEV:
        assert (
            v.fc_max_kw > 0
        ), "engine size is 0 kw in CONV - will lead to faulty results"
        assert (
            v.mc_max_kw > 0
        ), "motor size is 0 kw in BEV - will lead to faulty results"
    elif v.veh_pt_type == gl.HEV:
        pass
    else:
        raise ValueError(f"unknown vehicle powertrain type {v.veh_pt_type}")

    mpgges = []
    weights = []
    sim_drives = []
    for cycle_weight in eff_range_cyc:
        # efficiency-range cycle, weight
        erc, w = cycle_weight

        if v.veh_pt_type == gl.PHEV:
            # get two copies of cycle, rename
            cd_erc = erc.copy()
            cs_erc = erc.copy()
            cd_erc.name = "CD_" + erc.name

            #
            # Charge Depleting Cycle first
            #
            ess_max_kwh_orig = v.ess_max_kwh
            ess_max_kw_orig = v.ess_max_kw
            veh_kg_orig = v.veh_kg
            # infinite battery hack (with no extra weight) to simulate charge depleting behavior
            v.veh_override_kg = veh_kg_orig
            run_scenario.set_max_battery_kwh(v, 50e3)
            run_scenario.set_max_battery_power_kw(v, 1000)
            # don't want extra weight from huge battery
            sim_drive_cd = get_sim_drive(cd_erc, v, scenario)
            assert (
                sim_drive_cd.veh.veh_kg == veh_kg_orig
            ), f"sim_drive.veh.veh_kg == veh_kg_orig / {round(sim_drive_cd.veh.veh_kg)} == {round(veh_kg_orig)}"
            assert (
                sim_drive_cd.veh.ess_max_kwh == v.ess_max_kwh
            ), f"sim_drive.veh.ess_max_kwh == v.ess_max_kwh / {round(sim_drive_cd.veh.ess_max_kwh)} == {round(v.ess_max_kwh)}"
            sim_drive_cd.sim_drive(init_soc=v.max_soc)
            v.veh_override_kg = None

            #
            # Charge Sustaining Cycle second
            #
            # reset battery to inputs for charge sustaining behavior
            run_scenario.set_max_battery_kwh(v, ess_max_kwh_orig)
            run_scenario.set_max_battery_power_kw(v, ess_max_kw_orig)
            # we're supposed to run this as a "hybrid" so set
            # veh_pt_type to gl.HEV so that FASTSim does SOC balancing, run as an HEV, basically
            v.veh_pt_type = gl.HEV
            sim_drive_cs = get_sim_drive(cs_erc, v, scenario)
            sim_drive_cs.sim_drive()

            # CS calcs
            assert (
                sim_drive_cs.props.kwh_per_gge == gl.get_kwh_per_gge()
            ), "fuel LHV not consistent between T3CO and FASTSim"
            cs_fuel_kwh_out_ach = np.sum(sim_drive_cs.fs_kwh_out_ach)
            cs_fuel_kwh__mi = cs_fuel_kwh_out_ach / np.sum(sim_drive_cs.dist_mi)

            # CD calcs
            # sim_drive_cd.fs_kwh_out_ach[i] = sim_drive_cd.fs_kw_out_ach[i] * sim_drive_cd.cyc.dt_s[i] * (1 / 3.6e3)
            assert (
                sim_drive_cd.props.kwh_per_gge == gl.get_kwh_per_gge()
            ), "fuel LHV not consistent between T3CO and FASTSim"
            cd_fuel_kwh_out_ach = np.sum(sim_drive_cd.fs_kwh_out_ach)
            cd_elec_kwh_out_ach = np.sum(
                np.array(sim_drive_cd.ess_kw_out_ach)
                * np.array(sim_drive_cd.cyc.dt_s)
                / 3.6e3
            )
            cd_elec_mpgge = np.sum(sim_drive_cd.dist_mi) / (
                cd_elec_kwh_out_ach / gl.get_kwh_per_gge()
            )
            cd_electric_kwh__mi = cd_elec_kwh_out_ach / np.sum(sim_drive_cd.dist_mi)
            cd_fuel_kwh__mi = cd_fuel_kwh_out_ach / np.sum(sim_drive_cd.dist_mi)

            v.veh_pt_type = gl.PHEV
            v.set_derived()
            assert v.veh_kg == veh_kg_orig
            assert v.ess_max_kwh == ess_max_kwh_orig
            assert v.ess_max_kw == ess_max_kw_orig
            assert v.veh_pt_type == gl.PHEV

            # TODO: some CS sd's have sum(fs_kwh_out_ach) == 0 ... how?
            # These are probably bad designs and should be skipped

            # TODO: these kinds of outputs could be turned into a dataclass to set
            # desired outputs and have stricter checks
            mpgge = {
                #### Charge depleting ####
                "cd_electric_mpgge": cd_elec_mpgge,
                "cd_grid_electric_mpgge": cd_elec_mpgge
                * v.chg_eff,  # can be used for TCO CD mpgge electric
                "cd_fuel_mpgge": sim_drive_cd.mpgge,
                "cd_fuel_mpgde": sim_drive_cd.mpgge / gl.DieselGalPerGasGal,
                "cd_electric_kwh__mi": cd_electric_kwh__mi,
                "cd_fuel_kwh__mi": cd_fuel_kwh__mi,
                "cd_fuel_used_kwh": cd_fuel_kwh_out_ach,
                "cd_elec_used_kwh": cd_elec_kwh_out_ach,
                #### Charge sustaining ####
                "cs_fuel_mpgge": sim_drive_cs.mpgge,
                "cs_fuel_mpgde": sim_drive_cs.mpgge / gl.DieselGalPerGasGal,
                "cs_fuel_kwh__mi": cs_fuel_kwh__mi,
            }
            sim_drives.append(sim_drive_cd)
            sim_drives.append(sim_drive_cs)
        else:
            sim_drive = get_sim_drive(erc, v, scenario)
            sim_drive.sim_drive()

            # FASTSim BEV and PHEV have two different fuel efficiencies, the mpgge from battery and the mpgge from grid
            # grid_mpgge is really just mpgge multiplied by the charger efficiency fraction
            # grid_mpgge is used for fuel cost calculations
            # TCO fuel cost = vmt / mpgge * $/gge
            # for CONV and HEV
            # this is on an energy basis (mpgge has no electric component if the vehicle is CONV)
            # if vehicle is HEV mpgge = mpgge_from_fs_kwh_used + mpgge_from_battery_kwh_used
            if v.veh_pt_type in [gl.CONV, gl.HEV]:
                mpgge_both = sim_drive.mpgge
            else:
                mpgge_both = (
                    sim_drive.mpgge
                    + (1 / sim_drive.electric_kwh_per_mi) * gl.get_kwh_per_gge()
                )
            mpgge = {
                # combine ICE and electric PT components for overall mpgge
                "mpgge": mpgge_both,
                "grid_mpgge": mpgge_both * sim_drive.veh.chg_eff,
                "mpgde": mpgge_both / gl.DieselGalPerGasGal,
                # TODO, have this checked
                "kwh_per_mi": (
                    sum(sim_drive.fs_kwh_out_ach)
                    + ((sim_drive.roadway_chg_kj + sim_drive.ess_dischg_kj) / 3.6e3)
                )
                / sum(sim_drive.dist_mi),
            }
            sim_drives.append(sim_drive)

        mpgges.append(mpgge)
        weights.append(w)

    if v.veh_pt_type == gl.PHEV:
        cs_fuel_mpgge_i = np.array([m["cs_fuel_mpgge"] for m in mpgges])
        cs_fuel_mpgde_i = np.array([m["cs_fuel_mpgde"] for m in mpgges])
        cs_fuel_kwh__mi_i = np.array([m["cs_fuel_kwh__mi"] for m in mpgges])

        cd_fuel_used_kwh_i = [m["cd_fuel_used_kwh"] for m in mpgges]
        cd_battery_used_kwh_i = [m["cd_elec_used_kwh"] for m in mpgges]
        cd_fuel_mpgge_i = np.array([m["cd_fuel_mpgge"] for m in mpgges])
        cd_fuel_mpgde_i = np.array([m["cd_fuel_mpgde"] for m in mpgges])
        cd_electric_mpgge_i = np.array([m["cd_electric_mpgge"] for m in mpgges])
        cd_grid_electric_mpgge_i = np.array(
            [m["cd_grid_electric_mpgge"] for m in mpgges]
        )
        cd_electric_kwh__mi_i = np.array([m["cd_electric_kwh__mi"] for m in mpgges])
        cd_fuel_kwh__mi_i = np.array([m["cd_fuel_kwh__mi"] for m in mpgges])

        mpgge_comp = {
            # Charge sustaining
            "cs_fuel_mpgge": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cs_fuel_mpgge_i,
                    out=np.zeros_like(cs_fuel_mpgge_i),
                    where=cs_fuel_mpgge_i != 0,
                    casting="unsafe",
                )
            ),
            "cs_fuel_mpgde": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cs_fuel_mpgde_i,
                    out=np.zeros_like(cs_fuel_mpgde_i),
                    where=cs_fuel_mpgde_i != 0,
                    casting="unsafe",
                )
            ),
            "cs_fuel_kwh__mi": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cs_fuel_kwh__mi_i,
                    out=np.zeros_like(cs_fuel_kwh__mi_i),
                    where=cs_fuel_kwh__mi_i != 0,
                    casting="unsafe",
                )
            ),
            # Charge depleting
            "cd_fuel_used_kwh_total": sum(cd_fuel_used_kwh_i),
            "cd_battery_used_kwh": sum(cd_battery_used_kwh_i),
            "cd_fuel_mpgge": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cd_fuel_mpgge_i,
                    out=np.zeros_like(cd_fuel_mpgge_i),
                    where=cd_fuel_mpgge_i != 0,
                    casting="unsafe",
                )
            ),
            "cd_fuel_mpgde": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cd_fuel_mpgde_i,
                    out=np.zeros_like(cd_fuel_mpgde_i),
                    where=cd_fuel_mpgde_i != 0,
                    casting="unsafe",
                )
            ),
            "cd_electric_mpgge": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cd_electric_mpgge_i,
                    out=np.zeros_like(cd_electric_mpgge_i),
                    where=cd_electric_mpgge_i != 0,
                    casting="unsafe",
                )
            ),
            "cd_grid_electric_mpgge": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cd_grid_electric_mpgge_i,
                    out=np.zeros_like(cd_grid_electric_mpgge_i),
                    where=cd_grid_electric_mpgge_i != 0,
                    casting="unsafe",
                )
            ),
            "cd_electric_kwh__mi": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cd_electric_kwh__mi_i,
                    out=np.zeros_like(cd_electric_kwh__mi_i),
                    where=cd_electric_kwh__mi_i != 0,
                    casting="unsafe",
                )
            ),
            "cd_fuel_kwh__mi": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    cd_fuel_kwh__mi_i,
                    out=np.zeros_like(cd_fuel_kwh__mi_i),
                    where=cd_fuel_kwh__mi_i != 0,
                    casting="unsafe",
                )
            ),
        }
        # also report: AveCombinedkWhperMile = UF*(CDelectricityKWhperMile + CDfuelKWhpermile) + (1-UF)*CSfuelKWhperMile
        uf = run_scenario.get_phev_util_factor(scenario, v, mpgge)
        mpgge_comp["ave_combined_kwh__mile"] = (
            uf * (mpgge_comp["cd_electric_kwh__mi"] + mpgge_comp["cd_fuel_kwh__mi"])
            + (1 - uf) * mpgge_comp["cs_fuel_kwh__mi"]
        )
    else:
        mpgge_i = np.array([m["mpgge"] for m in mpgges])
        grid_mpgge_i = np.array([m["grid_mpgge"] for m in mpgges])
        mpgde_i = np.array([m["mpgde"] for m in mpgges])
        kwh_per_m_i = np.array([m["kwh_per_mi"] for m in mpgges])
        mpgge_comp = {
            "mpgge": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    mpgge_i,
                    out=np.zeros_like(mpgge_i),
                    where=mpgge_i != 0,
                    casting="unsafe",
                )
            ),
            "grid_mpgge": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    grid_mpgge_i,
                    out=np.zeros_like(grid_mpgge_i),
                    where=grid_mpgge_i != 0,
                    casting="unsafe",
                )
            ),
            "mpgde": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    mpgde_i,
                    out=np.zeros_like(mpgde_i),
                    where=mpgde_i != 0,
                    casting="unsafe",
                )
            ),
            # energy consumption kWh/mi is wt-averaged normally (doesn't require inverting / harmonic averaging)
            "kwh_per_mi": np.sum(weights)
            / np.sum(
                np.divide(
                    weights,
                    kwh_per_m_i,
                    out=np.zeros_like(kwh_per_m_i),
                    where=kwh_per_m_i != 0,
                    casting="unsafe",
                )
            ),
        }

    if diagnostic:
        return mpgge_comp, sim_drives, mpgges
    return mpgge_comp, sim_drives

get_range_mi(mpgge_info: dict, vehicle: fastsim.vehicle.Vehicle, scenario: run_scenario.Scenario) -> dict

This funcion computes range [miles] from mpgge using vehicle powertrain type and energy (or fuel) store size.

Considerations: - at some point each vehicle powertrain type could employ the concept of a "first fuel" or "primary fuel" - so return a primary fuel-based range for all powertrains. - PHEVs have two fuels (generally diesel and electricity). So return two ranges: -- One for determining range during optimization i.e. the CD range that PHEVs are commonly specified with (e.g. PHEV-50 = PHEV with 50 mi AER "All-Electric Range" ~= CD range) -- One that represents the "true" total PHEV range (CD + CS using both ESS and FS)

Parameters:

Name Type Description Default
mpgge_info dict

Dictionary containing MPGGE breakdown

required
vehicle fastsim.vehicle.Vehicle

FASTSim vehicle object of analysis vehicle

required
scenario run_scenario.Scenario

Scenario object of current selection

required

Returns:

Name Type Description
range_dict dict

Dictionary containing different range results

Source code in t3co/objectives/fueleconomy.py
Python
def get_range_mi(
    mpgge_info: dict, vehicle: fastsim.vehicle.Vehicle, scenario: run_scenario.Scenario
) -> dict:
    """
    This funcion computes range [miles] from mpgge using vehicle powertrain type and energy (or fuel) store size.

    Considerations:
    - at some point each vehicle powertrain type could employ the concept
    of a "first fuel" or "primary fuel" - so return a primary fuel-based range for all
    powertrains.
    - PHEVs have two fuels (generally diesel and electricity). So return two ranges:
    -- One for determining range during optimization
        i.e. the CD range that PHEVs are commonly specified with
        (e.g. PHEV-50 = PHEV with 50 mi AER "All-Electric Range" ~= CD range)
    -- One that represents the "true" total PHEV range (CD + CS using both ESS and FS)

    Args:
        mpgge_info (dict): Dictionary containing MPGGE breakdown
        vehicle (fastsim.vehicle.Vehicle): FASTSim vehicle object of analysis vehicle
        scenario (run_scenario.Scenario): Scenario object of current selection

    Returns:
        range_dict (dict): Dictionary containing different range results
    """
    range_dict = {}
    kwh_per_gge = gl.get_kwh_per_gge()
    if vehicle.veh_pt_type == gl.BEV:
        assert (
            vehicle.fs_kwh == 0 and vehicle.fc_max_kw == 0
        ), "Error! BEV vehicle has non-zero ICE attributes - vehicle mass calculation may be off"
        mpgge = mpgge_info["mpgge"]  # use fuel efficiency from battery
        range_mi = (
            vehicle.ess_max_kwh
            * (vehicle.max_soc - vehicle.min_soc)
            * mpgge
            / kwh_per_gge
        )
        range_dict["primary_fuel_range_mi"] = range_mi
    elif vehicle.veh_pt_type == gl.CONV:
        assert (
            vehicle.ess_max_kwh == 0 and vehicle.mc_max_kw == 0
        ), "Error! CONV vehicle has non-zero BEV attributes - vehicle mass calculation may be off"
        mpgge = mpgge_info["mpgge"]
        range_mi = (vehicle.fs_kwh / kwh_per_gge) * mpgge
        range_dict["primary_fuel_range_mi"] = range_mi
    elif vehicle.veh_pt_type == gl.HEV:
        mpgge = mpgge_info["mpgge"]
        elec_range_mi = (
            vehicle.ess_max_kwh
            * (vehicle.max_soc - vehicle.min_soc)
            * mpgge
            / kwh_per_gge
        )
        conv_range_mi = (vehicle.fs_kwh / kwh_per_gge) * mpgge
        range_mi = elec_range_mi + conv_range_mi
        range_dict["primary_fuel_range_mi"] = range_mi
    elif vehicle.veh_pt_type == gl.PHEV:
        # https://github.nrel.gov/AVCI/FASTSim_TCO_Truck/issues/24#issuecomment-39958
        # charge depleting range [miles]
        # CDrangeMiles = MIN( ESSmaxKWh*(CDmaxSOC- CDminSOC)/ CDelectricityKWhperMile , maxFuelStorKWh/33.7/ CDfuelGGEperMile )
        cd_range_mi = min(
            vehicle.ess_max_kwh
            * (vehicle.max_soc - vehicle.min_soc)
            / mpgge_info["cd_electric_kwh__mi"],
            vehicle.fs_kwh / kwh_per_gge * mpgge_info["cd_fuel_mpgge"],
        )
        # charge sustaining range [miles]
        # note, CS range in this way of thinking, is essentially what range is *left over* after you've exhausted the battery to min SOC
        # and switch into CS mode from CD mode, thus we subtract cd_gge_used from the GGE fuel stores of the vehicle
        cd_gge_used = cd_range_mi / mpgge_info["cd_fuel_mpgge"]
        gge_capacity = vehicle.fs_kwh / kwh_per_gge
        cs_range_mi = (gge_capacity - cd_gge_used) * mpgge_info["cs_fuel_mpgge"]
        true_range_mi = cd_range_mi + cs_range_mi
        range_dict = {
            "cd_aer_phev_range_mi": cd_range_mi,
            "cs_phev_range_mi": cs_range_mi,
            "true_phev_range_mi": true_range_mi,
            "primary_fuel_range_mi": cd_range_mi,
        }
    # if sim_drive:
    #     range_dict["cycle_distance_mi"] = sum(sim_drive.cyc.mps * np.diff(sim_drive.cyc.time_s)[0]) * gl.m_to_mi
    #     range_dict["mean_cyc_speed_mph"] = sum(sim_drive.cyc.mps)/max(sim_drive.cyc.time_s) * gl.mps_to_mph
    # elif range_cyc:
    #     range_dict["cycle_distance_mi"] = sum(range_cyc.mps * np.diff(range_cyc.time_s)[0]) * gl.m_to_mi
    #     range_dict["mean_cyc_speed_mph"] = sum(range_cyc.mps)/max(range_cyc.time_s) * gl.mps_to_mph
    return range_dict

get_sim_drive(erc, v, scenario)

This helper method returns a FASTSim SimDrive object using the vehicle, drive cycle and scenario

Parameters:

Name Type Description Default
erc fastsim.cycle.Cycle| List[Tuple[fastsim.cycle.Cycle, float

FASTSim range cycle object or list of tuples of cycles

required
v fastsim.vehicle.Vehicle

FASTSim vehicle object for analysis vehicle

required
scenario run_scenario.Scenario

Scenario object for current selection

required

Returns:

Name Type Description
sim_drive fastsim.simdrive.SimDrive

FASTSim SimDrive object

Source code in t3co/objectives/fueleconomy.py
Python
def get_sim_drive(erc, v, scenario):
    """
    This helper method returns a FASTSim SimDrive object using the vehicle, drive cycle and scenario

    Args:
        erc (fastsim.cycle.Cycle| List[Tuple[fastsim.cycle.Cycle, float): FASTSim range cycle object or list of tuples of cycles
        v (fastsim.vehicle.Vehicle): FASTSim vehicle object for analysis vehicle
        scenario (run_scenario.Scenario): Scenario object for current selection

    Returns:
        sim_drive (fastsim.simdrive.SimDrive): FASTSim SimDrive object
    """
    sim_drive = fastsim.simdrive.SimDrive(erc, v)
    sim_drive = sim_drive.to_rust()

    sim_params = sim_drive.sim_params
    sim_params.reset_orphaned()
    # sim_params.verbose = False
    if scenario.missed_trace_correction:
        sim_params.missed_trace_correction = True
        sim_params.max_time_dilation = scenario.max_time_dilation
        sim_params.min_time_dilation = scenario.min_time_dilation
        sim_params.time_dilation_tol = scenario.time_dilation_tol
    sim_drive.sim_params = sim_params

    props = sim_drive.props
    props.reset_orphaned()  # see if this is needed
    props.kwh_per_gge = gl.get_kwh_per_gge()
    sim_drive.props = props

    return sim_drive