Deceleration Fuel Cut-off (DFCO) is a control strategy that stops fuel injection during qualifying deceleration events. In practical terms, when the vehicle is coasting down and conditions are met, the engine can keep rotating without consuming fuel.
This demo compares two runs on the same UDDS cycle:
A baseline conventional vehicle with DFCO disabled.
The same vehicle with DFCO enabled.
The comparison is designed to isolate DFCO’s effect on fuel use and show where that fuel reduction appears in the time-series signals.
import fastsim
import plotly.graph_objects as go
METERS_PER_MILE = 1609.34
MJ_PER_GGE = 125.0Modeling Approach¶
This notebook holds the vehicle and cycle constant, then changes only DFCO parameters.
set_dfco_params(...) controls three key pieces of behavior:
enabled: turns DFCO logic on or off,min_dfco_speed_m_per_s: minimum speed where DFCO may activate,max_accel_for_dfco_m_per_s2: acceleration threshold for activation (typically negative for deceleration).
cyc = fastsim.Cycle.from_resource("udds.csv")
# Baseline: DFCO disabled
veh = fastsim.Vehicle.from_resource("2012_Ford_Fusion.yaml")
veh.set_dfco_params(
enabled=False,
min_dfco_speed_m_per_s=0.0,
max_accel_for_dfco_m_per_s2=0.0,
)
sd = fastsim.SimDrive(veh, cyc)
sd.walk()
df = sd.to_dataframe()
# DFCO case: enabled above 25 mph and during moderate deceleration
veh_dfco = fastsim.Vehicle.from_resource("2012_Ford_Fusion.yaml")
veh_dfco.set_dfco_params(
enabled=True,
min_dfco_speed_m_per_s=11.176,
max_accel_for_dfco_m_per_s2=-0.2,
)
sd_dfco = fastsim.SimDrive(veh_dfco, cyc)
sd_dfco.walk()
df_dfco = sd_dfco.to_dataframe()Fuel Economy Comparison¶
Fuel economy is computed from total cycle distance and cumulative fuel energy. This gives a direct before/after DFCO comparison under identical drive conditions.
cyc_dict = cyc.to_pydict()
distance_m = cyc_dict["dist_meters"][-1]
distance_mi = distance_m / METERS_PER_MILE
fuel_mj = df["veh.pt_type.Conv.fc.history.energy_fuel_joules"].iloc[-1] / 1e6
fuel_dfco_mj = df_dfco["veh.pt_type.Conv.fc.history.energy_fuel_joules"].iloc[-1] / 1e6
gge_gal = fuel_mj / MJ_PER_GGE
gge_dfco_gal = fuel_dfco_mj / MJ_PER_GGE
fuel_economy_mpg = distance_mi / gge_gal
fuel_economy_dfco_mpg = distance_mi / gge_dfco_gal
percent_reduction_dfco = (fuel_mj - fuel_dfco_mj) * 100.0 / fuel_mj
print(f"Baseline Fuel Economy: {fuel_economy_mpg:.2f} mpg")
print(f"With DFCO Fuel Economy: {fuel_economy_dfco_mpg:.2f} mpg")
print(f"Fuel-Use Reduction (DFCO): {percent_reduction_dfco:.2f}%")Baseline Fuel Economy: 35.42 mpg
With DFCO Fuel Economy: 36.07 mpg
Fuel-Use Reduction (DFCO): 1.80%
Visualizing DFCO Behavior¶
The plot below combines fuel power and cycle speed into one shared-time figure.
What to look for:
Green shaded windows mark segments where DFCO is eligible (speed at or above the threshold and deceleration beyond the threshold).
In those windows, the DFCO run should show fuel power dropping toward zero compared with baseline.
Outside those windows, traces should be closer because both runs use the same vehicle and cycle.
A time-range slider is included so you can zoom into specific events and directly compare fuel behavior against the cycle segment.
from plotly.subplots import make_subplots
BASELINE_COLOR = "#0072B2"
DFCO_COLOR = "#D55E00"
TARGET_COLOR = "#333333"
dfco_speed_threshold = 11.176
dfco_accel_threshold = -0.2
time = df["cyc.time_seconds"]
speed_target = df["cyc.speed_meters_per_second"]
dt = time.diff().replace(0.0, float("nan"))
accel_target = (speed_target.diff() / dt).fillna(0.0)
dfco_eligible = (speed_target >= dfco_speed_threshold) & (accel_target <= dfco_accel_threshold)
starts = dfco_eligible & ~dfco_eligible.shift(1, fill_value=False)
ends = dfco_eligible & ~dfco_eligible.shift(-1, fill_value=False)
fig = make_subplots(
rows=2,
cols=1,
shared_xaxes=True,
vertical_spacing=0.08,
row_heights=[0.58, 0.42],
subplot_titles=(
"Fuel Converter Fuel Power: Baseline vs DFCO",
"Cycle Speed and Achieved Speed",
),
)
fig.add_trace(
go.Scatter(
x=df["cyc.time_seconds"],
y=df["veh.pt_type.Conv.fc.history.pwr_fuel_watts"] / 1e3,
name="Fuel Power (Baseline)",
line={"color": BASELINE_COLOR, "dash": "solid", "width": 2.4},
),
row=1,
col=1,
)
fig.add_trace(
go.Scatter(
x=df_dfco["cyc.time_seconds"],
y=df_dfco["veh.pt_type.Conv.fc.history.pwr_fuel_watts"] / 1e3,
name="Fuel Power (DFCO)",
line={"color": DFCO_COLOR, "dash": "dash", "width": 2.4},
),
row=1,
col=1,
)
fig.add_trace(
go.Scatter(
x=df["cyc.time_seconds"],
y=df["cyc.speed_meters_per_second"],
name="Target Speed",
line={"dash": "dash", "width": 3, "color": TARGET_COLOR},
),
row=2,
col=1,
)
fig.add_trace(
go.Scatter(
x=df["cyc.time_seconds"],
y=df["veh.history.speed_ach_meters_per_second"],
name="Achieved Speed (Baseline)",
line={"color": BASELINE_COLOR, "dash": "solid", "width": 2.2},
),
row=2,
col=1,
)
fig.add_trace(
go.Scatter(
x=df_dfco["cyc.time_seconds"],
y=df_dfco["veh.history.speed_ach_meters_per_second"],
name="Achieved Speed (DFCO)",
line={"color": DFCO_COLOR, "dash": "dash", "width": 2.2},
),
row=2,
col=1,
)
for x0, x1 in zip(time[starts], time[ends]):
fig.add_vrect(
x0=x0,
x1=x1,
fillcolor="rgba(120, 120, 120, 0.14)",
line_width=0,
layer="below",
row="all",
col=1,
)
fig.update_layout(
height=900,
title={"text": "DFCO Fuel Use vs Cycle Segments", "x": 0.5, "xanchor": "center", "y": 0.98, "yanchor": "top"},
legend={"orientation": "h", "yanchor": "bottom", "y": 1.06, "xanchor": "left", "x": 0.0},
margin={"t": 140},
hovermode="x unified",
)
fig.update_yaxes(title_text="Fuel Power [kW]", row=1, col=1)
fig.update_yaxes(title_text="Speed [m/s]", row=2, col=1)
fig.update_xaxes(title_text="Time [s]", row=2, col=1, rangeslider_visible=True)
fig.show()