Source code for suboptimumg.compsim.models
import csv
from pathlib import Path
import numpy as np
from numpy.typing import NDArray
from pydantic import BaseModel, ConfigDict, Field, computed_field
from ..track.models import TrackData
from ..vehicle.vehicle_models import VehicleModel
[docs]
class EventScoring(BaseModel):
event_best_time: float = Field(description="Best time recorded for this event")
worst_time_ratio: float = Field(
description="The maximum race time for this event, expressed as a ratio of the track_best_time"
)
min_points: float = Field(description="The minimum amount of points awarded for this event")
max_point_gain: float = Field(
description="The maximum amount of point a car can gain on this event, if they meet or exceed track_best_time"
)
scale_time_ratio: float = Field(
description="Exponentiate event time ratios for point calculations (1 = no effect)"
)
[docs]
class EfficiencyScoring(BaseModel):
num_endurance_laps: int = Field(
description="Number of laps around the track for endurance event"
)
max_eff_points: float = Field(description="Maximum amount of points awarded for efficiency")
co2_scaling: float = Field(
description="(kg CO2 / kWh) ratio to convert EV to IC (from FSAE Rules)"
)
fastest_lap_time: float = Field(description="Average lap time for fastest endurance finisher")
co2_min: float = Field(
description="Minimum co2 usage out of teams that were eligible for endurance score"
)
eff_max: float = Field(description="Best efficiency factor out of all teams")
max_time_scale: float = Field(
description="Rules-defined baseline laptime (1.45 = 145% of fastest endurance finisher), used as constant in score calculations"
)
eff_min_co2_your: float = Field(
description="Rules-defined baseline CO2 value ((20.02 kg CO2 / 100km) * 22km), used as constant in score calculations"
)
[docs]
class CompetitionScoring(BaseModel):
accel: EventScoring
skidpad: EventScoring
autoX: EventScoring
endurance: EventScoring
efficiency: EfficiencyScoring
[docs]
class InternalDataSeedInfo(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
v_proposal: NDArray[np.float64] = Field(description="Proposed velocity profile")
acc_proposal: NDArray[np.float64] = Field(description="Proposed acceleration profile")
grown_forward_mask: NDArray[np.bool] = Field(
description="Mask of indices grown forward from the seed"
)
slower_mask: NDArray[np.bool] = Field(
description="Mask of indices where the proposal is slower than the current max"
)
v_max_post: NDArray[np.float64] = Field(
description="Velocity profile after merging this seed's proposal"
)
acc_max_post: NDArray[np.float64] = Field(
description="Acceleration profile after merging this seed's proposal"
)
p_proposal: NDArray[np.float64] = Field(description="Proposed power profile")
[docs]
class InternalData(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
initial_velocity: float = Field(description="Initial velocity at start of track (m/s)")
v_max_profile: NDArray[np.float64] = Field(
description="Initial maximum velocity profile for the track (m/s)"
)
seed_idx_list: NDArray[np.integer] = Field(
description="List of seed indices used in the simulation, sorted by increasing max velocity"
)
cumulative_dist: NDArray[np.float64] = Field(
description="Cumulative distance along the track (m)"
)
# NOTE: was not included before
seed_v_list: NDArray[np.float64] = Field(
description="List of seed velocities, corresponding to seed_idx_list"
)
per_seed: list[InternalDataSeedInfo] = Field(
description="List of per-seed internal data, corresponding to seed_idx_list"
)
[docs]
class LapsimResults(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
lap_t: NDArray[np.float64] = Field(description="List of lap time (s)")
lap_dxs: NDArray[np.float64] = Field(description="List of distances (m)")
lap_vels: NDArray[np.float64] = Field(description="List of velocities (m/s)")
lap_accs: NDArray[np.float64] = Field(description="List of accelerations (m/s^2)")
lap_powers: NDArray[np.float64] = Field(description="List of powers (W)")
lap_eff_motor_torques: NDArray[np.float64] = Field(
description="List of effective motor torques (N⋅m)"
)
internal_data: InternalData | None = Field(
default=None, description="Optional internal data from the simulation"
)
[docs]
def write_csv(self, filepath: str | Path) -> None:
"""Write the lap simulation time series to a CSV file.
Parameters
----------
filepath : str or Path
Destination CSV file path.
"""
columns = {
"Time (s)": self.lap_t,
"Distance (m)": self.lap_dxs,
"Velocity (m/s)": self.lap_vels,
"Acceleration (m/s^2)": self.lap_accs,
"Power Draw (W)": self.lap_powers,
"Effective Motor Torque (N⋅m)": self.lap_eff_motor_torques,
}
with open(filepath, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(columns.keys())
writer.writerows(zip(*columns.values()))
[docs]
class EventResults(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
points: float = Field(description="Points scored in the event")
tyour: float = Field(description="Your time (s)")
lapsim_results: LapsimResults = Field(description="Results from the lapsim")
[docs]
class CompetitionResults(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
accel: EventResults = Field(description="Acceleration event results")
skidpad: EventResults = Field(description="Skidpad event results")
autoX: EventResults = Field(description="Autocross event results")
endurance: EventResults = Field(description="Endurance event results")
efficiency_points: float = Field(description="Efficiency points scored in the endurance event")
endu_consumed_energy: float = Field(description="Energy consumed in the endurance event (kWh)")
@computed_field # type: ignore[prop-decorator]
# See vehicle_models.py: valid Pydantic V2 pattern, mypy plugin currently
# incompatible with the installed mypy version.
@property
def total_points(self) -> float:
return (
self.accel.points
+ self.skidpad.points
+ self.autoX.points
+ self.endurance.points
+ self.efficiency_points
)
[docs]
class CompetitionData(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
vehicle_model: VehicleModel = Field(description="Vehicle configuration")
accel: TrackData = Field(description="Acceleration track data")
skidpad: TrackData = Field(description="Skidpad track data")
autoX: TrackData = Field(description="Autocross track data")
endurance: TrackData = Field(description="Endurance track data")
scoring: CompetitionScoring = Field(description="Competition scoring configuration")