Trace miss occurs when a vehicle cannot achieve the prescribed speed of the drive cycle. For vehicles, this may be due to high vehicle mass, sluggish acceleration limits, etc. For cycles, this may be due to unrealistically aggressive jumps in speed or road grade.
Trace miss handling and tolerances are configured by setting simulation parameters. For detailed information, see Editing Simulation Parameters.
import fastsim
import numpy as np
import plotly.graph_objects as go
Building a Challenging Cycle¶
This cycle has a sharp acceleration from 0 to 8 m/s (~18 mph) in just one second, which a typical vehicle cannot fully achieve:
# Create a custom cycle with sharp acceleration
cyc_dict = {
"time_seconds": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
"speed_meters_per_second": [0.0, 8.0, 8.0, 8.0, 8.0, 8.0, 4.0, 0.0, 0.0],
}
cyc = fastsim.Cycle.from_pydict(cyc_dict)
# Load vehicle
veh = fastsim.Vehicle.from_resource("2012_Ford_Fusion.yaml")
Trace Miss Options¶
TraceMissOptions controls what happens when the vehicle cannot match the
target speed. The default is "Error" (raise an exception), but four options
are available:
| Option | Behavior |
|---|---|
"Error" | (default) Throw an error when trace miss occurs |
"Allow" | Silently allow trace miss without correction or validation |
"AllowChecked" | Allow trace miss only if it stays within specified tolerances |
"Correct" | Correct the trace by adjusting the driver model to catch up with the cycle |
Option 1: Error (default)¶
# By default, SimParams uses "Error" for trace miss
sd_error = fastsim.SimDrive(veh, cyc)
try:
sd_error.walk()
except Exception as e:
print(f"Expected error: {type(e).__name__}")
print(f"Message (truncated): {str(e)[:100]}...")
Expected error: RuntimeError
Message (truncated): solver step failed at line [fastsim-core/src/simdrive/mod.rs:293]
time step: 1
with originating err...
Option 2: Allow¶
# Use "Allow" to silently permit the vehicle to fall behind
params = fastsim.SimParams.default().to_pydict()
params["trace_miss_opts"] = "Allow"
veh_allow = fastsim.Vehicle.from_resource("2012_Ford_Fusion.yaml")
sd_allow = fastsim.SimDrive(veh_allow, cyc, fastsim.SimParams.from_pydict(params))
sd_allow.walk()
df_allow = sd_allow.to_dataframe()
print(f"Simulation completed without error. {len(df_allow)} time steps recorded.")
Simulation completed without error. 9 time steps recorded.
Option 3: AllowChecked¶
The "AllowChecked" option allows trace miss only if it stays within specified
tolerances. This provides a middle ground between permissive and strict behavior.
# Use "AllowChecked" with custom tolerances
params = fastsim.SimParams.default().to_pydict()
params["trace_miss_opts"] = "AllowChecked"
# Very relaxed tolerances for this extremely challenging cycle
params["trace_miss_tol"] = {
"tol_dist": 5000.0,
"tol_speed": 10.0,
"tol_dist_frac": 1.0,
"tol_speed_frac": 0.75
}
veh_checked = fastsim.Vehicle.from_resource("2012_Ford_Fusion.yaml")
sd_checked = fastsim.SimDrive(veh_checked, cyc, fastsim.SimParams.from_pydict(params))
sd_checked.walk()
df_checked = sd_checked.to_dataframe()
print(f"Simulation with AllowChecked completed. {len(df_checked)} time steps recorded.")
Simulation with AllowChecked completed. 9 time steps recorded.
Option 4: Correct¶
The "Correct" option uses a driver model to adjust the acceleration/deceleration
and catch back up to the cycle target. The trace_miss_correct_max_steps
parameter controls the maximum number of steps to re-rendezvous.
# Use "Correct" to automatically adjust the driver to catch back up
params = fastsim.SimParams.default().to_pydict()
params["trace_miss_opts"] = "Correct"
params["trace_miss_correct_max_steps"] = 6
veh_correct = fastsim.Vehicle.from_resource("2012_Ford_Fusion.yaml")
sd_correct = fastsim.SimDrive(veh_correct, cyc, fastsim.SimParams.from_pydict(params))
sd_correct.walk()
df_correct = sd_correct.to_dataframe()
print(f"Simulation with correction completed. {len(df_correct)} time steps recorded.")
Simulation with correction completed. 9 time steps recorded.
Comparing Results¶
Compare the target cycle against the achieved speed with each option:
fig = go.Figure()
TRACE_COLORS = {
"Target": "#4D4D4D",
"Allow": "#56B4E9",
"AllowChecked": "#0072B2",
"Correct": "#009E73",
}
# Get target distance from cycle (convert time/speed to distance)
cyc_dict_py = cyc.to_pydict()
cyc_time = np.array(cyc_dict_py["time_seconds"])
cyc_speed = np.array(cyc_dict_py["speed_meters_per_second"])
cyc_dist = np.cumsum(cyc_speed * np.diff(cyc_time, prepend=cyc_time[0]))
# Target cycle
fig.add_trace(go.Scatter(
x=cyc_time, y=cyc_speed,
name="Target", mode="lines",
line={"dash": "dash", "width": 2, "color": TRACE_COLORS["Target"]}
))
# Allow (permissive)
fig.add_trace(go.Scatter(
x=df_allow["cyc.time_seconds"],
y=df_allow["veh.history.speed_ach_meters_per_second"],
name="Allow", mode="lines",
line={"color": TRACE_COLORS["Allow"]}
))
# AllowChecked (with tolerances)
fig.add_trace(go.Scatter(
x=df_checked["cyc.time_seconds"],
y=df_checked["veh.history.speed_ach_meters_per_second"],
name="AllowChecked", mode="lines",
line={"color": TRACE_COLORS["AllowChecked"]}
))
# Correct (adaptive)
fig.add_trace(go.Scatter(
x=df_correct["cyc.time_seconds"],
y=df_correct["veh.history.speed_ach_meters_per_second"],
name="Correct", mode="lines",
line={"color": TRACE_COLORS["Correct"]}
))
fig.update_layout(
title="Speed vs Time for Each Trace Miss Option",
xaxis_title="Time [s]",
yaxis_title="Speed [m/s]",
hovermode="x unified",
)
fig.show()
Why does the trace miss correction option overshoot the prescribed speed? Let’s evaluate the achieved simulation distance to find out:
fig_time = go.Figure()
# Target cycle
fig_time.add_trace(go.Scatter(
x=cyc_time, y=cyc_dist,
name="Target", mode="lines",
line={"dash": "dash", "width": 2, "color": TRACE_COLORS["Target"]}
))
# Allow (permissive)
fig_time.add_trace(go.Scatter(
x=df_allow["cyc.time_seconds"],
y=df_allow["veh.history.dist_meters"],
name="Allow", mode="lines",
line={"color": TRACE_COLORS["Allow"]}
))
# AllowChecked (with tolerances)
fig_time.add_trace(go.Scatter(
x=df_checked["cyc.time_seconds"],
y=df_checked["veh.history.dist_meters"],
name="AllowChecked", mode="lines",
line={"color": TRACE_COLORS["AllowChecked"]}
))
# Correct (adaptive)
fig_time.add_trace(go.Scatter(
x=df_correct["cyc.time_seconds"],
y=df_correct["veh.history.dist_meters"],
name="Correct", mode="lines",
line={"color": TRACE_COLORS["Correct"]}
))
fig_time.update_layout(
title="Distance vs Time for Each Trace Miss Option",
xaxis_title="Time [s]",
yaxis_title="Distance [m]",
hovermode="x unified",
)
fig_time.show()
Here we can see that only the trace miss correction simulation makes up for the missed distance in the cycle, whereas the others fall short.
print(f"Prescribed cycle distance: {cyc.to_pydict()['dist_meters'][-1]} m")
print(f"Achieved cycle distance:")
print(f" trace_miss_opts = 'Allow': {df_allow['veh.history.dist_meters'].iloc[-1]:.1f} m")
print(f" trace_miss_opts = 'AllowChecked': {df_checked['veh.history.dist_meters'].iloc[-1]:.1f} m")
print(f" trace_miss_opts = 'Correct': {df_correct['veh.history.dist_meters'].iloc[-1]:.1f} m")Prescribed cycle distance: 44.0 m
Achieved cycle distance:
trace_miss_opts = 'Allow': 38.5 m
trace_miss_opts = 'AllowChecked': 38.5 m
trace_miss_opts = 'Correct': 44.0 m
Summary¶
“Error” (default): Raises an exception if trace miss occurs. Forces you to acknowledge the issue and choose a resolution strategy.
“Allow”: Permits the vehicle to fall behind without correction. Useful for exploratory analyses but may produce unrealistic behavior.
“AllowChecked”: Allows trace miss but validates it against specified tolerances (distance and speed). Provides a middle ground between permissive and strict.
“Correct”: Adjusts the driver model to catch back up to the target cycle.
For most use cases, “Correct” is the recommended choice when trace miss is expected, as it ensures the vehicle re-synchronizes with the target cycle while maintaining realistic dynamics.