diff --git a/docs/scenario_inputs.md b/docs/scenario_inputs.md new file mode 100644 index 0000000..339633e --- /dev/null +++ b/docs/scenario_inputs.md @@ -0,0 +1,174 @@ +# Pre-Race Simulation Scenario Inputs + +This document separates inputs provided by a user from the configuration parameters that define a scenario. + +## User-Defined Inputs + +| User input | How it is provided | Maps to | +|---|---|---| +| Car parameters | `car_params.json` | `CarConfig` | +| Track | `--location` or prompt | `TrackConfig` | +| Strategy | `--strategy` or prompt | `RaceConfig.strategy` | +| Aggressiveness | `--aggressiveness` | `RaceConfig.aggressiveness` | +| Energy safety scale | Interactive prompt | `RaceConfig.energy_safety_scale` | +| Fixed-speed sweep | `--speed-sweep` | Planning settings | +| Planning study | `--plan` | Planning settings | +| Weather source | Currently set in code | Weather settings | +| Race times, SOC, speed limits, timestep | Currently set in code | `RaceConfig` | + +*For Car Parameters, the program automatically searches in `car_params.json` in the project root and pulls the configs from there.* +## Config/Scenario Parameters + +These are the fields a future scenario configuration should contain. Values marked optional can use the listed default. + +### Car Configuration + +Source: `car_params.json` + +| Parameter | Units | Required | Affects | +|---|---:|---|---| +| `mass` | kg | Yes | Rolling resistance, Regen energy, total energy use | +| `battery_capacity` | Wh | Yes | Available battery energy, SOC changes | +| `solar_panel_area` | m² | Yes | Solar power collected | +| `solar_panel_efficiency` | fraction | Yes | Solar power collected | +| `electrical_efficiency` | fraction | Yes | Driving power consumption | +| `C_dA` | m² | Yes | Aerodynamic drag, energy use | +| `tire_pressure` | pressure units | Yes | Rolling resistance, energy use | +| `regen_efficiency` | fraction | Yes | Regen energy | +| `rho` | kg/m³ | Yes | Aerodynamic drag | +| `g` | m/s² | Yes | Rolling resistance | + +### Track Configuration + +| Parameter | Units | Required | Affects | +|---|---:|---|---| +| `name` | text | Yes | Report and plot labels | +| `file_path_name` | text | Yes | Track identifier and output filenames | +| `location` | text | Yes | Human-readable location | +| `latitude`, `longitude` | degrees | Yes | Weather API location | +| `lap_distance_km` | km | Yes | Distance and lap count | +| `timezone` | IANA timezone | Yes | Local race and weather times | + +Available track IDs: `shenandoah_speedway`, `virginia_international_raceway`, `brainerd_international_raceway`. + +### Race and Battery Conditions + +| Parameter | Units | Required | Default | Affects | +|---|---:|---|---:|---| +| `start_time_hour` | local hour | Yes | `10.0` | Race duration, weather window | +| `end_time_hour` | local hour | Yes | `18.0` | Race duration, weather window | +| `start_soc` | fraction | Yes | `1.0` | Starting energy, achievable distance | +| `target_soc` | fraction | No | `0.10` | Planned energy budget, SOC trajectory | +| `min_soc` | fraction | No | `0.10` | Early-stop safety threshold | +| `energy_safety_scale` | fraction | No | `1.0` | Energy reserve | + +### Weather Configuration + +| Parameter | Type | Required | Default | Affects | +|---|---|---|---|---| +| `source` | `api` / `synthetic` | Yes | API | Weather data used | +| `race_date` | date | No | Auto-selected | Forecast date | +| `random_seed` | integer | No | `7` in sweeps | Synthetic-weather generation | +| `GHI`, `cloud_cover` | weather data | Generated or API-provided | None | Solar charging, SOC trajectory | + +The current API uses Open-Meteo shortwave radiation and cloud cover. API failure falls back to synthetic weather. + +### Strategy and Speed + +| Parameter | Units / values | Required | Default | Affects | +|---|---|---|---|---| +| `strategy` | `pi`, `stepped`, `interval-hold`, `fixed` | Yes | `pi` | Speed strategy | +| `aggressiveness` | scalar | No | Strategy-specific | Controller response strength | +| `fixed_speed_mps` | m/s | No | `15.0` | Fixed-strategy speed | +| `min_speed_mps` | m/s | No | `4.0` | Lower speed limit | +| `max_speed_mps` | m/s | No | `35.0` | Upper speed limit | +| `initial_speed_mps` | m/s | No | `20.0` | *Currently unused* | + +Planning aggressiveness defaults: `pi=1.5`, `stepped=1.2`, `interval-hold=1.0`, `fixed=1.0`. + +### Simulation and Planning + +| Parameter | Type | Required | Default | Affects | +|---|---|---|---|---| +| `time_step_minutes` | minutes | No | `1.0` | Simulation update frequency | +| `use_api_weather` | boolean | No | `True` | Weather data source | +| `planning_enabled` | boolean | No | `False` | Single run versus planning study | +| `strategies` | list | No | All strategies | Strategies compared | +| `fixed_speed_values_mph` | list | No | `10`-`46`, step `2` | Speeds compared | + +## Expected Planning Outputs + +- Best fixed speed and strategy recommendation +- Total laps and distance +- Final and minimum SOC +- Average, minimum, and maximum speed +- Full-window completion status +- Time and laps when minimum SOC was reached + +### Example Scenario Outlines +The following are examples of format files that can be implemented as YAML. + +1. Scenario 1: + - Track: Shenandoah Raceway + - Strategy: fixed-speed sweep strategy comparison + +``` +scenario: + name: Shenandoah fixed-speed sweep + car_params: car_params.json + track: shenandoah_speedway + + mode: sweep + + race: + start_time_hour: 10.0 + end_time_hour: 18.0 + start_soc: 1.0 + target_soc: 0.10 + min_soc: 0.10 + + weather: + source: api + + strategy: + name: fixed + aggressiveness: 1.0 + + planning: + fixed_speed_values_mph: [18, 22, 26, 30, 34, 38] + + simulation: + time_step_minutes: 1.0 +``` + +2. Scenario 2: + - Track: Brainerd + - Strategy: PI with aggressiveness = 0.9. + +``` +scenario: + name: Brainerd PI controller run + car_params: car_params.json + track: brainerd_international_raceway + + mode: strategy + + race: + start_time_hour: 10.0 + end_time_hour: 18.0 + start_soc: 1.0 + target_soc: 0.10 + min_soc: 0.10 + + strategy: + name: pi + aggressiveness: 0.9 + min_speed_mps: 4.0 + max_speed_mps: 35.0 + + weather: + source: api + + simulation: + time_step_minutes: 1.0 +``` \ No newline at end of file diff --git a/sim/config.py b/sim/config.py index c93f582..3c2c737 100644 --- a/sim/config.py +++ b/sim/config.py @@ -8,6 +8,7 @@ @dataclass class TrackConfig: name: str + file_path_name: str location: str latitude: float longitude: float @@ -143,6 +144,7 @@ def get_available_tracks() -> List[TrackConfig]: return [ TrackConfig( name="Shenandoah Speedway", + file_path_name="shenandoah_speedway", location="Page County, Virginia", latitude=38.5110, longitude=-78.6359, @@ -151,6 +153,7 @@ def get_available_tracks() -> List[TrackConfig]: ), TrackConfig( name= "Virginia International Raceway (Patriot Course)", + file_path_name="virginia_international_raceway", location="Alton, Virginia", latitude=36.5666, longitude=-79.2058, @@ -159,6 +162,7 @@ def get_available_tracks() -> List[TrackConfig]: ), TrackConfig( name= "Brainerd International Raceway (Donnybrooke Course)", + file_path_name="brainerd_international_raceway", location="Brainerd, Minnesota", latitude=46.4176, longitude=-94.2853, diff --git a/sim/main.py b/sim/main.py index c6c0d00..aff101a 100644 --- a/sim/main.py +++ b/sim/main.py @@ -24,8 +24,8 @@ def parse_args(): parser = argparse.ArgumentParser(description="Modular laps race simulator") parser.add_argument( "--location", - choices=["shenandoah"], - default="shenandoah", + choices=["shenandoah_speedway", "virginia_international_raceway", "brainerd_international_raceway"], + default=None, help="Race location selection (currently only Shenandoah)", ) parser.add_argument( @@ -126,22 +126,26 @@ def main(): car = CarConfig() print("Using default car parameters") - print("Choose a track to test on:") - for i in range(len(tracks)): - print(f"({i+1}) {tracks[i].name}") - num = 1 - while True: - inp = input() - if inp.isdigit(): - num = int(inp) - if num <= 0 or num > len(tracks): - print("Invalid int") - else: + if args.location is not None: + track = next(track for track in tracks + if track.file_path_name == args.location) + else: + print("Choose a track to test on:") + for i, available_track in enumerate(tracks, start=1): + print(f"({i}) {available_track.name}") + + num = 1 + while True: + inp = input() + if inp.isdigit(): + num = int(inp) + if num <= 0 or num > len(tracks): + print("Invalid int") + else: + break + elif inp == "": break - elif inp == "": - break - - track = tracks[num-1] + track = tracks[num-1] if args.plan: base_race = RaceConfig( @@ -231,7 +235,7 @@ def main(): reporter.print_summary() output_dir = os.path.join("plots", "laps_modular") - prefix = f"shenandoah_{selected_strategy}" + prefix = f"{track.file_path_name}_{selected_strategy}" plotter = SimulationPlotter(results, track) saved = save_all_plots(plotter, output_dir, prefix)