Source code for pybtls.traffic.vehicle_generator

"""
The methods and classes that are not defined in Python are defined in C++ py_main.cpp. 
"""

from ..lib.BTLS import (
    _ConfigData,
    _LaneFlowComposition,
    _VehicleGenNominal,
    _VehModelDataNominal,
    _VehicleGenGrave,
    _VehModelDataGrave,
    _VehicleGenGarage,
    _VehModelDataGarage,
    _VehClassPattern,
    _VehClassAxle,
    Vehicle,
    _Vehicle,
)
from .lfc import LaneFlowComposition
from ..garage.read import read_garage_file
from typing import Literal, Union
from pathlib import Path
import importlib.resources as pkg_resources

__all__ = ["VehicleGenNominal", "VehicleGenGrave", "VehicleGenGarage"]


[docs] class VehicleGenNominal: def __init__(self, nominal_vehicle: Vehicle, COV_list: list[float], **kwargs): """ The VehicleGenNominal instance in Python stores the data for creating a CVehicleGenNominal instance in C++.\n All heavy vehicles will be generated based on the intput nominal vehicle. Parameters ---------- nominal_vehicle : Vehicle\n Nominal vehicle. COV_list : list[float]\n List of COV for axle spacing and axle weight. \n COV_list = [COV_AS, COV_AW]. Keyword Arguments ----------------- classifier_type : str\n Vehicle classifier type. "axle" or "pattern". Default is "pattern". lane_eccentricity_std : float\n Standard deviation of lane eccentricity. Default is 0.0. kernel_type : int\n Kernel type. 0 (Normal) or 1 (Triangle). Default is 1 (Triangle). """ self._tag = "Nominal" self._config = _ConfigData() if len(COV_list) != 2: raise ValueError("Invalid COV list for nominal vehicle generator.") self._nominal_vehicle = nominal_vehicle self._COV_list = COV_list # COV_list = [COV_AS, COV_AW] self._set_config(**kwargs) def __getstate__(self): attribute_dict = {} attribute_dict["tag"] = self._tag attribute_dict["config"] = self._config attribute_dict["nominal_vehicle"] = self._nominal_vehicle attribute_dict["COV_list"] = self._COV_list return attribute_dict def __setstate__(self, attribute_dict): self._tag = attribute_dict["tag"] self._config = attribute_dict["config"] self._nominal_vehicle = attribute_dict["nominal_vehicle"] self._COV_list = attribute_dict["COV_list"] @property def tag(self) -> str: return self._tag def _get_VC(self): return self._config._Traffic.CLASSIFICATION def _set_config(self, **kwargs): classifier_type = 0 if kwargs.get("classifier_type") == "axle" else 1 lane_eccentricity_std = kwargs.get("lane_eccentricity_std", 0.0) kernel_type = kwargs.get("kernel_type", 1) self._config.set_veh_gen_nominal( vehicle_classifier=classifier_type, lane_eccentricity_std=lane_eccentricity_std, # in cm kernel_type=kernel_type, ) def _check_lfc(self, lfc: LaneFlowComposition): if not lfc.flow_assigned: raise ValueError( "Flow data is not included in the LaneFlowComposition instance." ) def _get_generator( self, lfc: _LaneFlowComposition ) -> tuple[_VehicleGenNominal, _VehModelDataNominal]: """ Get a CVehicleGenNominal instance and a CVehModelDataNominal instance (generator and its model data). """ if self._config._Traffic.CLASSIFICATION == 1: vehicle_classifier = _VehClassPattern() else: vehicle_classifier = _VehClassAxle() model_data = _VehModelDataNominal( self._config, vehicle_classifier, lfc, self._nominal_vehicle, self._COV_list ) return _VehicleGenNominal(model_data), model_data
[docs] class VehicleGenGrave: def __init__( self, traffic_site: Literal[ "A196", "A296", "Angers", "Auxerre", "B224", "Samaris-D", "Samaris-D1", "Samaris-D2", "Samaris-D3", "Samaris-S", "Samaris-S1", "Samaris-S2", "Samaris-S3", ], truck_track_width: float = 190.0, **kwargs, ): """ The VehicleGenGrave instance in Python stores the data for creating a CVehicleGenGrave instance in C++.\n All heavy vehicles will be generated by sampling from the pre-studied distributions of vehicle properties. Parameters ---------- traffic_site : Literal['A196','A296','Angers','Auxerre','B224','Samaris-D','Samaris-D1','Samaris-D2','Samaris-D3','Samaris-S','Samaris-S1','Samaris-S2','Samaris-S3']\n Traffic site. These sites are all in Europe. truck_track_width : float optional\n Truck track width. Default is 190.0. Keyword Arguments ----------------- classifier_type : str\n Vehicle classifier type. "axle" or "pattern". Default is "pattern". lane_eccentricity_std : float\n Standard deviation of lane eccentricity. Default is 0.0. """ self._tag = "Grave" self._config = _ConfigData() if traffic_site not in [ "A196", "A296", "Angers", "Auxerre", "B224", "Samaris-D", "Samaris-D1", "Samaris-D2", "Samaris-D3", "Samaris-S", "Samaris-S1", "Samaris-S2", "Samaris-S3", ]: raise ValueError("Unrecorded traffic site for Grave model.") self._traffic_site = traffic_site self._truck_track_width = truck_track_width self._set_config(**kwargs) def __getstate__(self): attribute_dict = {} attribute_dict["tag"] = self._tag attribute_dict["config"] = self._config return attribute_dict def __setstate__(self, attribute_dict): self._tag = attribute_dict["tag"] self._config = attribute_dict["config"] @property def tag(self) -> str: return self._tag def _get_VC(self): return self._config._Traffic.CLASSIFICATION def _set_config(self, **kwargs): classifier_type = 0 if kwargs.get("classifier_type") == "axle" else 1 lane_eccentricity_std = kwargs.get("lane_eccentricity_std", 0.0) traffic_folder = str( pkg_resources.files("pybtls").joinpath( "data/GraveParameters/" + self._traffic_site ) ) self._config.set_veh_gen_grave( vehicle_classifier=classifier_type, traffic_folder=traffic_folder, truck_track_width=self._truck_track_width, lane_eccentricity_std=lane_eccentricity_std, # in cm ) def _check_lfc(self, lfc: LaneFlowComposition): if not lfc.flow_assigned: raise ValueError( "Flow data is not included in the LaneFlowComposition instance." ) if not lfc.truck_composition_assigned: raise ValueError( "Truck composition data is not included in the LaneFlowComposition instance." ) def _get_generator( self, lfc: _LaneFlowComposition ) -> tuple[_VehicleGenGrave, _VehModelDataGrave]: """ Get a CVehicleGenGrave instance and a CVehModelDataGrave instance (generator and its model data). """ if self._config._Traffic.CLASSIFICATION == 1: vehicle_classifier = _VehClassPattern() else: vehicle_classifier = _VehClassAxle() model_data = _VehModelDataGrave(self._config, vehicle_classifier, lfc) return _VehicleGenGrave(model_data), model_data
[docs] class VehicleGenGarage: def __init__( self, garage: Union[Path, list[_Vehicle]], kernel: list[list[float]], garage_format: Literal[1, 2, 3, 4] = None, **kwargs, ): """ The VehicleGenGarage instance in Python stores the data for creating a CVehicleGenGarage instance in C++.\n This generation is a bootstrapping process. Parameters ---------- garage : Union[Path, list[_Vehicle]]\n The path to the garage file, or a list of Vehicle objects. kernel : list[list[float]]\n The kernel is to ensure variation between the generated vehicles and garages. \n kernel = [\n [Mean_GVW, Std_GVW],\n [Mean_AxleWeight, Std_AxleWeight],\n [Mean_AxleSpacing, Std_AxleSpacing]\n ]. garage_format : Literal[1,2,3,4], optional\n The format of the .txt garage file.\n 1: CASTOR format.\n 2: BEDIT format.\n 3: DITIS format.\n 4: MON format. Keyword Arguments ----------------- classifier_type : str\n Vehicle classifier type. "axle" or "pattern". Default is "pattern". lane_eccentricity_std : float\n Standard deviation of lane eccentricity. Default is 0.0. kernel_type : int\n Kernel type. 0 (Normal) or 1 (Triangle). Default is 1 (Triangle). """ self._tag = "Garage" self._config = _ConfigData() if len(kernel) != 3 and not all(len(sublist) == 2 for sublist in kernel): raise ValueError("Invalid kernel data for garage vehicle generator.") if isinstance(garage, (Path, str)): if garage_format is None: raise ValueError("Garage format is not specified.") self._garage = ( Path(garage).resolve() if not isinstance(garage, Path) else garage.resolve() ) self._garage_format = garage_format elif isinstance(garage, list): if all(isinstance(vehicle, (Vehicle, _Vehicle)) for vehicle in garage): self._garage = garage else: raise ValueError("Existing non-Vehicle object in the garage.") else: raise ValueError("Invalid garage data for garage vehicle generator.") self._kernel = kernel # kernel = [[Mean_GVW, Std_GVW], [Mean_AW, Std_AW], [Mean_AS, Std_AS]] self._set_config(**kwargs) def __getstate__(self): attribute_dict = {} attribute_dict["tag"] = self._tag attribute_dict["config"] = self._config attribute_dict["garage"] = self._garage attribute_dict["garage_format"] = self._garage_format attribute_dict["kernel"] = self._kernel return attribute_dict def __setstate__(self, attribute_dict): self._tag = attribute_dict["tag"] self._config = attribute_dict["config"] self._garage = attribute_dict["garage"] self._garage_format = attribute_dict["garage_format"] self._kernel = attribute_dict["kernel"] @property def tag(self) -> str: return self._tag def _get_VC(self): return self._config._Traffic.CLASSIFICATION def _set_config(self, **kwargs): classifier_type = 0 if kwargs.get("classifier_type") == "axle" else 1 lane_eccentricity_std = kwargs.get("lane_eccentricity_std", 0.0) kernel_type = kwargs.get("kernel_type", 1) self._config.set_veh_gen_garage( vehicle_classifier=classifier_type, lane_eccentricity_std=lane_eccentricity_std, # in cm kernel_type=kernel_type, ) def _check_lfc(self, lfc: LaneFlowComposition): if not lfc.flow_assigned: raise ValueError( "Flow data is not included in the LaneFlowComposition instance." ) def _get_generator( self, lfc: _LaneFlowComposition ) -> tuple[_VehicleGenGarage, _VehModelDataGarage]: """ Get a CVehicleGenGarage instance and a CVehModelDataGarage instance (generator and its model data). """ if self._config._Traffic.CLASSIFICATION == 1: vehicle_classifier = _VehClassPattern() else: vehicle_classifier = _VehClassAxle() vehicle_list = ( read_garage_file(self._garage, self._garage_format) if isinstance(self._garage, Path) else self._garage ) model_data = _VehModelDataGarage( self._config, vehicle_classifier, lfc, vehicle_list, self._kernel ) return _VehicleGenGarage(model_data), model_data