Source code for suboptimumg.yaml

from pathlib import Path

import numpy as np
import yaml
from pydantic import TypeAdapter

from .compsim import Competition
from .compsim.models import CompetitionScoring
from .track import (
    SampledTrackInput,
    SampledTrackReference,
    SampledTrackSource,
    Track,
    TrackConfig,
    from_corners,
    from_sampled,
)
from .vehicle import Car
from .vehicle.irl_vehicle import IrlCar
from .vehicle.vehicle_models import AnyVehicleModel, ExtendedVehicleModel

_TRACK_CONFIG_ADAPTER: TypeAdapter[TrackConfig] = TypeAdapter(TrackConfig)
_SAVED_TRACK_DECIMALS: int = 4


def _track_from_config(track_config: dict, distance_step: float, base_dir: Path) -> Track:
    """
    Build one event track from its ``tracks.<event>`` entry in a competition config.

    Parameters
    ----------
    track_config : dict
        Either a corner list (see CornerListInput) or a ``path`` to a sampled track YAML.
    distance_step : float
        Simulation distance step in meters, applied to every track.
    base_dir : Path
        Directory that a sampled track ``path`` is resolved against.

    Returns
    -------
    Track
    """
    track_config["distance_step"] = distance_step
    validated = _TRACK_CONFIG_ADAPTER.validate_python(track_config)

    if isinstance(validated, SampledTrackReference):
        with open(base_dir / validated.path, "r") as file:
            sampled = SampledTrackInput.model_validate(yaml.safe_load(file))
        return from_sampled(sampled, distance_step)

    return from_corners(validated)


def _competition_from_car_and_comp_dict(car: Car, comp_dict: dict, base_dir: Path) -> Competition:
    """
    Create a Competition object from a Car instance and competition configuration dictionary.

    Parameters
    ----------
    car : Car

    comp_dict : dict
        The competition configuration dictionary.

    base_dir : Path
        Directory that sampled track paths in ``comp_dict`` are resolved against.

    Returns
    -------
    Competition
    """
    distance_step = comp_dict["simulation"]["distance_step"]
    tracks = comp_dict["tracks"]

    return Competition(
        car,
        _track_from_config(tracks["accel"], distance_step, base_dir),
        _track_from_config(tracks["skidpad"], distance_step, base_dir),
        _track_from_config(tracks["autoX"], distance_step, base_dir),
        _track_from_config(tracks["endurance"], distance_step, base_dir),
        CompetitionScoring.model_validate(comp_dict["scoring"]),
    )


[docs] def save_track_to_yaml(track: Track, yaml_path: str, source: SampledTrackSource) -> None: """ Save a Track as uniformly spaced samples so a competition YAML can reference it. Parameters ---------- track : Track Track to save. Its radius, x and y samples are written. yaml_path : str Destination file path. source : SampledTrackSource Bookkeeping about which log and lap the track came from. Notes ----- Reference the saved file from ``parameters/competition.yaml`` with:: tracks: endurance: path: tracks/<file name>.yaml """ sampled = SampledTrackInput( distance_step=track.distance_step, source=source, radius=np.round(track.radius, _SAVED_TRACK_DECIMALS).tolist(), x_m=np.round(track.x_m, _SAVED_TRACK_DECIMALS).tolist(), y_m=np.round(track.y_m, _SAVED_TRACK_DECIMALS).tolist(), ) with open(yaml_path, "w") as file: yaml.safe_dump( sampled.model_dump(), file, default_flow_style=None, width=120, sort_keys=False )
[docs] def load_car_from_yaml(yaml_path: str) -> Car | IrlCar: """Load a vehicle configuration and return a Car or IrlCar instance. The YAML type is determined by the ``type`` field: - ``type: base`` (or omitted) → :class:`~suboptimumg.vehicle.vehicle.Car` - ``type: irl_setup`` → :class:`~suboptimumg.vehicle.complex_vehicle.IrlCar` Parameters ---------- yaml_path : str Path to the YAML configuration file. Returns ------- Car or IrlCar """ with open(yaml_path, "r") as file: config = yaml.safe_load(file) vehicle_model = AnyVehicleModel.validate_python(config) if isinstance(vehicle_model, ExtendedVehicleModel): return IrlCar(vehicle_model=vehicle_model) return Car(vehicle_model=vehicle_model)
[docs] def load_competition_from_yaml(car_yaml_path: str, comp_yaml_path: str) -> Competition: """ Create a Competition object from YAML configuration files. Parameters ---------- car_yaml_path : str The file path to the vehicle configuration YAML file. comp_yaml_path : str The file path to the competition configuration YAML file. Returns ------- Competition """ car = load_car_from_yaml(car_yaml_path) with open(comp_yaml_path, "r") as file: comp_config = yaml.safe_load(file) return _competition_from_car_and_comp_dict(car, comp_config, Path(comp_yaml_path).parent)
[docs] def load_competition_from_dicts(car_config: dict, comp_config: dict) -> Competition: """ Create a Competition object from configuration dictionaries. Parameters ---------- car_config : dict The vehicle configuration dictionary. comp_config : dict The competition configuration dictionary. Sampled track paths resolve against the current working directory. Returns ------- Competition """ vehicle_model = AnyVehicleModel.validate_python(car_config) car = ( IrlCar(vehicle_model=vehicle_model) if isinstance(vehicle_model, ExtendedVehicleModel) else Car(vehicle_model=vehicle_model) ) return _competition_from_car_and_comp_dict(car, comp_config, Path.cwd())