Introduction to the Digital Twin, Part 3: Customizing a scenario.¶
In the previous notebooks, we looked at simple 2-aircraft scenarios on the I- and Y- sectors, and an example scenario in the Springfield airspace. In all these cases we used the "out-of-the-box" configuration - creating the Simulator just by passing the category and name of the scenario.
In this notebook, we will instantiate a "scenario manager", and use this to generate a Simulator. This way we have full control over the parameters of the scenario manager, such as the number of aircraft to be generated.
# Visualisation-related imports
import matplotlib.pyplot as plt
import os
import shutil
from IPython.display import SVG, Image
%matplotlib inline
Generating an Airspace and Routes¶
We will again use a simple "Artificial" airspace - this time the "X" sector, which is the next step up in complexity from the "Y" sector. Since there are four ways in our out of this sector, there is a defined set of "Routes" (i.e. the possible sequences of fixes that an aircraft might follow through the sector).
Import and instantiate the "Artificial" scenario manager
from bluebird_dt.airspace_generator.artificial_airspace import ArtificialAirspace
airspace, routes = ArtificialAirspace("x").generate_airspace()
Let's visualize this airspace:
from bluebird_dt.core import Pos2D
from bluebird_dt.render import Radar
view_centre = Pos2D.from_str("50.75N 3.5W")
view_width = 100.0 # [nmi] about 1 degree of longitude
aspect_ratio = 1.6
radar = Radar(view_centre, view_width, aspect_ratio)
radar.draw_airspace(airspace)
# print some of the possible Routes through the airspace
print([r.filed for r in routes[:4]])
[['SIREN', 'WITCH', 'ABYSS', 'DEMON', 'SANTA'], ['SIREN', 'WITCH', 'ABYSS', 'GATES', 'SIN'], ['SIREN', 'WITCH', 'ABYSS', 'HAUNT', 'LIMBO'], ['LIMBO', 'HAUNT', 'ABYSS', 'GATES', 'SIN']]
Now we can use the airway and routes to instantiate the "Tactical" scenario manager
from bluebird_dt.scenario_manager.tactical import Tactical
sm = Tactical(airspace=airspace, routes=routes, num_aircraft=4)
The in-line documentation for the scenario manager should help us see what else we can configure:
help(sm)
Help on Tactical in module bluebird_dt.scenario_manager.tactical object:
class Tactical(bluebird_dt.scenario_manager.scenario_manager.ScenarioManager)
| Tactical(
| num_aircraft: int,
| airspace: bluebird_dt.core.airspace.Airspace,
| routes: list[bluebird_dt.core.route.Route],
| balance: list[float] | None = None,
| speed_range: list[float] | None = None,
| time_entry_gap: float = 5,
| lateral_offset: tuple[int, int] | None = None,
| env_manager_class: type[bluebird_dt.manager.environment_manager.EnvironmentManager] | None = None,
| start_time: int = 0,
| vertical_buffer_distance: float | int = 500,
| lateral_buffer_distance: float | int = 20,
| initialise_with_event_handler: bool = True
| )
|
| Aircraft generator for simple tactical scenarios:
| - configurable number of Aircraft and balance of
| climbers/descenders/overfliers
| - randomly selected entry and exit Coordinations
| - randomly generated speeds
| - ensures that no two Aircraft have the same entry coordination
| (same Fix, Flight Level and time)
| - ability to randomize the start position of aircraft within an entry fix
| through a stochastic sample of lateral distance from the entry fix.
|
| Method resolution order:
| Tactical
| bluebird_dt.scenario_manager.scenario_manager.ScenarioManager
| abc.ABC
| typing.Generic
| builtins.object
|
| Methods defined here:
|
| __init__(
| self,
| num_aircraft: int,
| airspace: bluebird_dt.core.airspace.Airspace,
| routes: list[bluebird_dt.core.route.Route],
| balance: list[float] | None = None,
| speed_range: list[float] | None = None,
| time_entry_gap: float = 5,
| lateral_offset: tuple[int, int] | None = None,
| env_manager_class: type[bluebird_dt.manager.environment_manager.EnvironmentManager] | None = None,
| start_time: int = 0,
| vertical_buffer_distance: float | int = 500,
| lateral_buffer_distance: float | int = 20,
| initialise_with_event_handler: bool = True
| )
| Construct a new instance.
|
| Parameters
| ----------
| num_aircraft: float
| Number of Aircraft to generate.
| airspace: Airspace
| The Airspace the Aircraft are flying through. The generator expects it to have a single Sector
| composed of a single Volume. This is true for the I,X,Y Airspaces.
| routes: list[Route]
| The available Routes in the Airspace (choose one at random for each Aircraft Route).
| balance: list[float, float, float]
| Probabilities of any given Aircraft being one of climber/descender/overflier.
| The probabilities have to sum to 1 (Multinomial distribution parameter).
| speed_range: list[float, float]
| Optional range of [min,max] speeds from which to randomly generate Aircraft speed.
| If not provided, speed of all Aircraft is set to 400.
| time_entry_gap: float
| Optional amount of time in seconds that must be maintained between two
| Aircraft entry Coordinations if they are at the same Fix and FL.
| lateral_offset: tuple, optional
| if present, the range (low, high) from which to sample (uniform distribution)
| a lateral offset that is applied to randomize an aircraft start
| position (spawn point). Example sensible values are (0, 10)
| env_manager_class: type, optional
| if specified, use this class (maybe a subclass of BluebirdATC EventManager).
| start_time: int
| Start time in unix time (seconds)
| vertical_buffer_distance: int or float, default is 500
| Distance to expand airspace vertical boundary by - UoM: FL
| lateral_buffer_distance: int or float, default is 20
| Distance to expand airspace lateral boundary by - UoM: NMI
| initialise_with_event_handler: bool, default is True
| Initialise the environment with the EventHandler
|
| config(self) -> bluebird_dt.scenario_manager.tactical.TacticalScenarioManagerConfig
| Obtain the configuration this instance of the specific scenario manager.
|
| Returns
| -------
| TConfig
| Object reflecting the current configuration of the specific scenario manager, of the specific
| type of the scenario manager.
|
| create_env_manager(
| self,
| predictor: bluebird_dt.predictor.predictor.Predictor | None = None,
| log_filename: str | None = None
| ) -> bluebird_dt.manager.environment_manager.EnvironmentManager
| Create event_manager for the given Airspace.
|
| Parameters
| ----------
| predictor: Predictor, optional
| Aircraft Trajectory prediction used to evolve Aircraft. If None, then SimplePredictor will be created.
| log_filename: str, optional
| Name of file logs will be saved to. If None, defaults to datetime logger created.
|
| Returns
| ----------
| EnvironmentManager
| Environment Manager for Tactical scenario
|
| create_event_handler(self) -> bluebird_dt.events.event_handler.EventHandler
| Generate event_handler for the given Airspace.
|
| Returns
| ----------
| EventHandler
| The EventHandler specifying Aircraft with unique string identifiers (callsigns) to fly
| through the Airspace and when to add them to the Environment.
|
| set_up_lateral_start_points(self, airspace: bluebird_dt.core.airspace.Airspace) -> dict[str, tuple[float, float]]
| Create a dictionary of headings - two for each spawn point which can
| be used to offset the aircraft position on spawning.
|
| Parameters
| ----------
| airspace: Airspace
| The current airspace
|
| Returns
| ----------
| dictionary
| lateral_offset_headings: {str:[float, float]} fix name: list of
| two headings which would run parallel to the sector boundary.
|
| stochastic_start_pos(
| self,
| airspace: bluebird_dt.core.airspace.Airspace,
| route: bluebird_dt.core.route.Route
| ) -> bluebird_dt.core.pos2d.Pos2D
| Laterally offset the aircraft from their spawning point (start fix)
|
| Laterally offset the aircraft from their spawning point to minimise
| clashes and introduce stochasticity to aircraft start point in within
| a start fix.
|
| Parameters
| ----------
| airspace: Airspace
| The airspace to generate aircraft within offset start position.
| route: Route
| The route of the aircraft.
|
| Returns
| ----------
| Pos2D
| the sampled start position.
|
| to_simulator(
| self,
| scenario_name: str | None = None,
| category: str | None = None,
| use_wind: bool = True,
| use_forecast: bool = True,
| autosave: bool = True,
| attach_context_to_logger: bool = True,
| save_log_to_file: bool = True,
| log_filename: str | None = None,
| predictor: bluebird_dt.predictor.predictor.Predictor | None = None,
| simulated_sectors: Union[list[str], Literal['ALL']] = 'ALL'
| ) -> bluebird_dt.simulator.simulator.Simulator
| Create a Simulator instance for Tactical scenarios.
|
| Parameters
| ----------
| scenario_name : str | None, optional
| Name of the scenario. Default is None.
| category : str | None, optional
| Category of the simulation. Default is None.
| use_wind: bool
| Whether the wind, if available, is present in the scenario. Defaults to True.
| use_forecast: bool
| Whether the forecasted wind, if available, is present in the scenario. Defaults to True.
| autosave: bool
| The scenario will autosave every 5 minutes if True. Defaults to True.
| attach_context_to_logger: bool
| Adds the scenario name and scenario category as context to the active logger. This should be set to False if
| you are initialising multiple simulator classes in the same logger as then the context will be meaningless.
| Defaults to True.
| save_log_to_file: bool
| The log will be saved to file on exit if True. Defaults to True.
| log_filename: str, optional
| The name of the log directory. If None, then {category}_{scenario_name}_{the_datetime} is used.
| predictor: Predictor, optional
| The Predictor to use for the simulation. If None the default predictor for the
| scenario type will be used.
| simulated_sectors: list[str] | typing.Literal["ALL"], optional
| The sectors to be simulated. If "ALL", all sectors will be simulated. If a list, only the sectors names in
| the list will be simulated. Currently only applicable for real world scenarios. Defaults to "ALL".
|
| Returns
| -------
| Simulator
| A fully configured simulator instance
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| check_inputs_valid(
| num_aircraft: int,
| balance: list[float],
| speed_range: list[float],
| time_entry_gap: float
| ) -> None
| Check the inputs have valid types.
|
| Parameters
| ----------
| num_aircraft: float
| Number of Aircraft to generate.
| balance: list[float, float, float]
| Probabilities of any given Aircraft being one of climber/descender/overflier.
| The probabilities have to sum to 1 (Multinomial distribution parameter).
| speed_range: list[float, float]
| Optional range of [min,max] speeds from which to randomly generate Aircraft speed.
| If not provided, speed of all Aircraft is set to 400.
| time_entry_gap: float
| Optional amount of time in seconds that must be maintained between two
| Aircraft entry Coordinations if they are at the same Fix and FL.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __abstractmethods__ = frozenset()
|
| __annotations__ = {'airspace': <class 'bluebird_dt.core.airspace.Airsp...
|
| __orig_bases__ = (bluebird_dt.scenario_manager.scenario_manager.Sc...o...
|
| __parameters__ = ()
|
| event_handler_ignore_flags = EventHandler.IgnoreFlags(radar_if_simmed=...
|
| i_sector_pairs = (('EARTH', 'FIRE'), ('AIR', 'SPIRIT'))
|
| projection_centre = None
|
| x_sector_pairs = (('GATES', 'SIN'), ('DEMON', 'SANTA'), ('WITCH', 'SIR...
|
| y_sector_pairs = (('CANON', 'GOD'), ('BISHP', 'GHOST'), ('SON', 'DECAN...
|
| ----------------------------------------------------------------------
| Methods inherited from bluebird_dt.scenario_manager.scenario_manager.ScenarioManager:
|
| update(
| self,
| env_manager: bluebird_dt.manager.environment_manager.EnvironmentManager
| ) -> bluebird_dt.manager.environment_manager.EnvironmentManager
| Optionally update the environment or coordinations.
|
| Intended to allow scenario managers the option to dynamically update the environment depending on
| the state at any time.
|
|
| Parameters
| ----------
| env_manager: EnvironmentManager
| An environment manager containing the environment and coordinations
|
| Returns
| -------
| EnvironmentManager
|
| ----------------------------------------------------------------------
| Data descriptors inherited from bluebird_dt.scenario_manager.scenario_manager.ScenarioManager:
|
| __dict__
| dictionary for instance variables
|
| __weakref__
| list of weak references to the object
|
| ----------------------------------------------------------------------
| Class methods inherited from typing.Generic:
|
| __class_getitem__(...)
| Parameterizes a generic class.
|
| At least, parameterizing a generic class is the *main* thing this
| method does. For example, for some generic class `Foo`, this is called
| when we do `Foo[int]` - there, with `cls=Foo` and `params=int`.
|
| However, note that this method is also called when defining generic
| classes in the first place with `class Foo[T]: ...`.
|
| __init_subclass__(...)
| Function to initialize subclasses.
We have already provided the airspace, routes, and number of aircraft. Lets specify the speed range (in knots), and set the "balance" such that all the aircraft will be "climbers" (i.e. will exit the sector at a higher altitude than they enter it).
sm.speed_range = [500,600] # quite fast!!
sm.balance = [1,0,0]
We can now use the scenario manager to create a Simulator instance.
sim = sm.to_simulator()
Visualising the scenario¶
import IPython.display
# the radar is re-instantiated. no need to clear the screen.
radar = Radar(view_centre, view_width, aspect_ratio) # re-introduce the spines/axes
for _ in range(100):
sim.evolve(6.0)
figure, ax = radar.draw(sim.manager.environment)
IPython.display.display(figure) # or IPython.display.display(plt.gcf())
IPython.display.clear_output(wait=True)
The "Infinite" scenario manager¶
Up to now, the scenarios we have looked at have had a finite (and generally small) number of aircraft. There is another scenario manager in bluebird_dt that is able to add an unlimited number of aircraft, spawning randomly at the edge of the sector. Let's instantiate that now, looking at the "Xplus", which is a slightly asymmetric variation of the X-sector, with extra available routes.
# generate airspace and routes for xplus-sector
airspace, routes = ArtificialAirspace("xplus").generate_airspace()
# import and instantiate the Infinite scenario manager
from bluebird_dt.scenario_manager.infinite import Infinite
# instantiate the scenario manager
sm = Infinite(airspace=airspace, routes=routes)
Again, we can look at the source code, or use help to see what parameters are available to configure the scenario manager:
help(sm)
Help on Infinite in module bluebird_dt.scenario_manager.infinite object:
class Infinite(bluebird_dt.scenario_manager.scenario_manager.ScenarioManager, typing.Generic)
| Infinite(
| airspace: bluebird_dt.core.airspace.Airspace,
| routes: list[bluebird_dt.core.route.Route],
| initial_spawn_rate: float = 0.015,
| max_spawn_rate: float = 0.2,
| spawn_rate_increment: float = 0.0,
| spawn_rate_increase_interval: float = 0.0,
| spawn_distance_threshold: float = 10,
| spawn_distance_behind_fix: float = 10.0,
| random_seed: int | None = None,
| automatic_outcomm: bool = True,
| num_starter_aircraft: int = 2,
| speed_range: tuple[float, float] | None = None,
| aircraft_on_route: bool = False,
| typeof_environment_manager: type[~TEnvironmentManager] = <class 'bluebird_dt.manager.environment_manager.EnvironmentManager'>,
| typeof_event_handler: type[~TEventHandler] = <class 'bluebird_dt.events.event_handler.EventHandler'>,
| typeof_aircraft: type[~TAircraft] = <class 'bluebird_dt.core.aircraft.Aircraft'>,
| typeof_eventlogger: type[~TEventLogger] = <class 'bluebird_dt.events.event_logger.EventLogger'>,
| start_time: int = 0,
| max_spawn_attempts: int = 10,
| min_spawn_delta: float = 6.0,
| vertical_buffer_distance: float | int = 500,
| lateral_buffer_distance: float | int = 20
| )
|
| Generate aircraft at random points at set intervals, over an indefinite period of time.
| Optionally the rate at which aircraft spawn can increase over time.
| - start from randomly chosen seed point
| - choose random route to take it to one of the other exit points.
| - entry flight level chosen from restricted range.
| - current level matches entry level
| - exit level chosen from restricted range - limited by airspace.
|
| Method resolution order:
| Infinite
| bluebird_dt.scenario_manager.scenario_manager.ScenarioManager
| abc.ABC
| typing.Generic
| builtins.object
|
| Methods defined here:
|
| __init__(
| self,
| airspace: bluebird_dt.core.airspace.Airspace,
| routes: list[bluebird_dt.core.route.Route],
| initial_spawn_rate: float = 0.015,
| max_spawn_rate: float = 0.2,
| spawn_rate_increment: float = 0.0,
| spawn_rate_increase_interval: float = 0.0,
| spawn_distance_threshold: float = 10,
| spawn_distance_behind_fix: float = 10.0,
| random_seed: int | None = None,
| automatic_outcomm: bool = True,
| num_starter_aircraft: int = 2,
| speed_range: tuple[float, float] | None = None,
| aircraft_on_route: bool = False,
| typeof_environment_manager: type[~TEnvironmentManager] = <class 'bluebird_dt.manager.environment_manager.EnvironmentManager'>,
| typeof_event_handler: type[~TEventHandler] = <class 'bluebird_dt.events.event_handler.EventHandler'>,
| typeof_aircraft: type[~TAircraft] = <class 'bluebird_dt.core.aircraft.Aircraft'>,
| typeof_eventlogger: type[~TEventLogger] = <class 'bluebird_dt.events.event_logger.EventLogger'>,
| start_time: int = 0,
| max_spawn_attempts: int = 10,
| min_spawn_delta: float = 6.0,
| vertical_buffer_distance: float | int = 500,
| lateral_buffer_distance: float | int = 20
| )
| Construct a new instance.
|
| Parameters
| ----------
| airspace: Airspace
| The airspace to be used in the environment
| routes: list[Route]
| The available Routes in the Airspace
| initial_spawn_rate: float
| frequency (in 1/s) at which aircraft spawn at the start of the scenario
| max_spawn_rate: float
| maximum allowed frequency at which aircraft can spawn
| spawn_rate_increment: float
| if we choose to "ramp up" the spawn rate, this determines the step size
| spawn_rate_increase_interval: float
| if we "ramp up", this determines time in seconds between increasing the spawn rate by spawn_rate_increment.
| spawn_distance_threshold: float
| Minimum distance in nautical miles for spawning an aircraft behind an existing one
| spawn_distance_behind_fix: float
| Distance behind starting fix that an aircraft will spawn
| random_seed: int | None
| Optionally specify the seed for the random number generator
| automatic_outcomm: bool
| Specify if the scenario manager should automatically outcomm aircraft which leave the sector meeting exit
| coordination. Defaults to True
| num_starter_aircraft: int
| How many aircraft to spawn (at different fixes) at very start of the scenario.
| speed_range: tuple[float, float] | None
| Optional range of [min,max] speeds from which to randomly choose Aircraft speed. If not provided,
| speeds of all Aircraft are set to a range 350-450 knots
| aircraft_on_route: bool
| If True, spawned aircraft will not be laterally displaced from their starting fix,
| and will have "on_route" set to True. Default is False.
| start_time: int
| Start time of scenario, in unix time (seconds)
| max_spawn_attempts: int
| How many times to try and randomly spawn an aircraft that doesn't clash with existing aircraft
| min_spawn_delta: float
| Minimum allowed time (in seconds) between spawns. Default is 6.0.
| vertical_buffer_distance: int or float, default is 500
| Distance to expand airspace vertical boundary by - UoM: FL
| lateral_buffer_distance: int or float, default is 20
| Distance to expand airspace lateral boundary by - UoM: NMI
| typeof_environmentmanager: type[EnvironmentManager], optional
| If we want to use a derived class of env manager, specify here.
| typeof_aircraft: type[Aircraft], optional
| If we want to use a derived class for the aircraft class, specify here.
| typeof_eventlogger: type[EventLogger], optional
| If we want to use a derived class for the event logger, specify here.
| typeof_eventhandler: type[EventHandler], optional
| If we want to use a derived class for the Event Handler, specify here.
|
| add_starting_aircraft(self, event_handler: ~TEventHandler) -> ~TEventHandler
| Generate the aircraft that will be there at the start of the scenario and add them to Eventhandler
|
| Parameters
| ----------
| event_handler: TEventHandler
| newly created EventHandler instance
|
| Returns
| -------
| TEventHandler
| containing num_starter_aircraft aircraft and coordinations
|
| config(self) -> bluebird_dt.scenario_manager.infinite.InfiniteScenarioManagerConfig
| Obtain the configuration this instance of the specific scenario manager.
|
| Returns
| -------
| TConfig
| Object reflecting the current configuration of the specific scenario manager, of the specific
| type of the scenario manager.
|
| create_aircraft_with_coordinations(
| self,
| possible_routes: list[bluebird_dt.core.route.Route],
| callsign: str,
| spawn_distance_behind_fix: float
| ) -> tuple[~TAircraft, bluebird_dt.core.coordination.Coordination, bluebird_dt.core.coordination.Coordination]
| Generate an Aircraft instance with parameters in the allowed range
|
| Parameters
| airspace: Airspace
| possible_routes: list[Route]
| choice of routes for this aircraft, with given starting fix
| callsign: str
| the callsign of the created aircraft
| spawn_distance_behind_fix: float
| how far behind the first fix to spawn
|
| create_env_manager(
| self,
| log_filename: str | None = None,
| predictor: bluebird_dt.predictor.predictor.Predictor | None = None
| ) -> ~TEnvironmentManager
| Create event_manager for the given Airspace.
|
| Parameters
| ----------
| predictor: Predictor, optional
| Aircraft Trajectory prediction used to evolve Aircraft. If None, then SimplePredictor will be created.
| log_filename: str or None
| Name of file logs will be saved to. If None, defaults to datetime logger created.
|
| Returns
| ----------
| EnvironmentManager
| EnvironmentManager for Two Aircraft scenario
|
| create_event_handler(self) -> ~TEventHandler
| Generate Aircraft at random locations at regular intervals for the given Airspace.
|
| Returns
| ----------
| TEventHandler
| EventHandler instance for the chosen scenario
|
| setup_lateral_offset_headings(self) -> dict[str, tuple[float, float]]
| Create a dictionary of headings - two for each spawn point which can be used to offset the aircraft position
| on spawning.
|
| Returns
| ----------
| dictionary
| lateral_offset_headings: {str:[float, float]} fix name: list of two headings which would run
| perpendicular to the initial aircraft direction
|
| to_simulator(
| self,
| category: str | None = None,
| scenario_name: str | None = None,
| use_wind: bool = True,
| use_forecast: bool = True,
| autosave: bool = True,
| attach_context_to_logger: bool = True,
| save_log_to_file: bool = True,
| log_filename: str | None = None,
| predictor: bluebird_dt.predictor.predictor.Predictor | None = None,
| simulated_sectors: Union[list[str], Literal['ALL']] = 'ALL',
| typeof_simulator: type[~TSimulator] = <class 'bluebird_dt.simulator.simulator.Simulator'>
| ) -> ~TSimulator
| Create a Simulator instance for scenarios with infinitely spawning aircraft.
|
| Parameters
| ----------
| scenario_name : str | None, optional
| Name of the scenario. Default is None.
| category : str | None, optional
| Category of the simulation. Default is None.
| use_wind: bool
| Whether the wind, if available, is present in the scenario. Defaults to True.
| use_forecast: bool
| Whether the forecasted wind, if available, is present in the scenario. Defaults to True.
| autosave: bool
| The scenario will autosave every 5 minutes if True. Defaults to True.
| attach_context_to_logger: bool
| Adds the scenario name and scenario category as context to the active logger. This should be set to False if
| you are initialising multiple simulator classes in the same logger as then the context will be meaningless.
| Defaults to True.
| save_log_to_file: bool
| The log will be saved to file on exit if True. Defaults to True.
| log_filename: str, optional
| The name of the log directory. If None, then {category}_{scenario_name}_{the_datetime} is used.
| predictor: Predictor, optional
| The Predictor to use for the simulation. If None the default predictor for the
| scenario type will be used.
| simulated_sectors: list[str] | typing.Literal["ALL"], optional
| The sectors to be simulated. If "ALL", all sectors will be simulated. If a list, only the sectors names in
| the list will be simulated. Currently only applicable for real world scenarios. Defaults to "ALL".
|
| Returns
| -------
| Simulator
| A fully configured simulator instance
|
| update(self, env_manager: ~TEnvironmentManager) -> ~TEnvironmentManager
| If enough time has passed, spawn another aircraft
|
| Parameters
| ----------
| env_manager: TEnvironmentManager
| environment manager instance to update
|
| ----------------------------------------------------------------------
| Class methods defined here:
|
| setup(
| scenario_name: str,
| random_seed: int | None = None,
| num_starter_aircraft: int = 3,
| initial_spawn_rate: float = 0.01,
| spawn_rate_increment: float = 0,
| spawn_rate_increase_interval: float | None = None,
| max_spawn_rate: float = 0.1,
| speed_range: list[float] | None = None,
| spawn_distance_threshold: float = 10.0,
| use_wind: bool = True,
| use_forecast: bool = True,
| autosave: bool = True,
| attach_context_to_logger: bool = True,
| save_log_to_file: bool = True,
| log_filename: str | None = None,
| predictor: bluebird_dt.predictor.predictor.Predictor | None = None,
| simulated_sectors: Union[list[str], Literal['ALL']] = 'ALL',
| typeof_environment_manager: type[~TEnvironmentManager] = <class 'bluebird_dt.manager.environment_manager.EnvironmentManager'>,
| typeof_event_handler: type[~TEventHandler] = <class 'bluebird_dt.events.event_handler.EventHandler'>,
| typeof_aircraft: type[~TAircraft] = <class 'bluebird_dt.core.aircraft.Aircraft'>,
| typeof_eventlogger: type[~TEventLogger] = <class 'bluebird_dt.events.event_logger.EventLogger'>,
| typeof_simulator: type[~TSimulator] = <class 'bluebird_dt.simulator.simulator.Simulator'>
| ) -> ~TSimulator
| Setup artificial scenarios based on scenario name.
|
| Parameters
| ----------
| scenario_name: str
| The scenario name
| random_seed: int
| If specified, set the random seed for the generator
| num_starter_aircraft: int
| Number of aircraft to spawn at the start of the scenario
| initial_spawn_rate: float
| Average frequency (in Hz) of spawning new aircraft
| spawn_rate_increment: float
| Spawn rate will change by this number at set intervals
| spawn_rate_increase_interval: int
| If specified, time in seconds between spawn rate increases
| max_spawn_rate: float
| Spawn rate cannot exceed this value
| speed_range: list[float]
| Optional, if not set, aircraft speeds are set between 350 and 450 knots.
| spawn_distance_threshold: float
| Minimum spawn distance from nearest aircraft with overlapping fl range.
| use_wind: bool
| Whether the wind, if available, is present in the scenario. Defaults to True.
| use_forecast: bool
| Whether the forecasted wind, if available, is present in the scenario. Defaults to True.
| autosave: bool
| The scenario will autosave every 5 minutes if True. Defaults to True.
| attach_context_to_logger: bool
| Adds the scenario name and scenario category as context to the active logger. This should be set to False if
| you are initialising multiple simulator classes in the same logger as then the context will be meaningless.
| Defaults to True.
| save_log_to_file: bool
| The log will be saved to file on exit if True. Defaults to True.
| log_filename: str, optional
| The name of the log directory. If None, then {category}_{scenario_name}_{the_datetime} is used.
| predictor: Predictor, optional
| The Predictor to use for the simulation. If None the default predictor for the
| scenario type will be used.
| simulated_sectors: list[str] | typing.Literal["ALL"], default="ALL"
| The sectors to be simulated. If "ALL", all sectors will be simulated. If a list, only the sectors names in
| the list will be simulated. Currently only applicable for real world scenarios.
| env_manager_class: type, optional
| if specified, use this class (maybe a subclass of BluebirdATC EventManager).
| typeof_environmentmanager: type[EnvironmentManager], optional
| If we want to use a derived class of env manager, specify here.
| typeof_aircraft: type[Aircraft], optional
| If we want to use a derived class for the aircraft class, specify here.
| typeof_eventlogger: type[EventLogger], optional
| If we want to use a derived class for the event logger, specify here.
| typeof_eventhandler: type[EventHandler], optional
| If we want to use a derived class for the Event Handler, specify here.
| Returns
| -------
| Simulator
| A fully configured simulator instance
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| create_airspace(scenario_name: str) -> tuple[bluebird_dt.core.airspace.Airspace, list[bluebird_dt.core.route.Route]]
| Create specified airspace.
|
| Parameters
| ----------
| scenario_name: str
| This is used to identify the sector/airspace.
|
| Returns
| --------
| tuple[Airspace, list[Route]]
| tuple of the Airspace object and a list of allowed Routes
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __abstractmethods__ = frozenset()
|
| __annotations__ = {'aircraft_on_route': <class 'bool'>, 'airspace': <c...
|
| __orig_bases__ = (bluebird_dt.scenario_manager.scenario_manager.Sc...o...
|
| __parameters__ = (~TAircraft, ~TWindField, ~TForecastWindField, ~TEnvi...
|
| projection_centre = None
|
| ----------------------------------------------------------------------
| Data descriptors inherited from bluebird_dt.scenario_manager.scenario_manager.ScenarioManager:
|
| __dict__
| dictionary for instance variables
|
| __weakref__
| list of weak references to the object
|
| ----------------------------------------------------------------------
| Class methods inherited from typing.Generic:
|
| __class_getitem__(...)
| Parameterizes a generic class.
|
| At least, parameterizing a generic class is the *main* thing this
| method does. For example, for some generic class `Foo`, this is called
| when we do `Foo[int]` - there, with `cls=Foo` and `params=int`.
|
| However, note that this method is also called when defining generic
| classes in the first place with `class Foo[T]: ...`.
|
| __init_subclass__(...)
| Function to initialize subclasses.
By default, aircraft will spawn stochastically at a constant average frequency. It is possible however to ramp up the frequency by spawn_rate_increment every spawn_rate_increate_interval seconds, until it reaches max_spawn_rate. It is also possible to set the random_seed, if we want to reproduce the same set of aircraft in different runs.
Lets set the number of starting aircraft to 2, the initial spawn rate to 0.05 (i.e. on average one aircraft will spawn every 20 seconds) and specify the random seed.
sm.num_starter_aircraft=2
sm.initial_spawn_rate = 0.05
sm.random_seed = 1234
# instantiate the Simulator
sim = sm.to_simulator()
And let's run the scenario for 200 steps (though note that it could continue indefinitely!)
import IPython.display
# the radar is re-instantiated. no need to clear the screen.
radar = Radar(view_centre, view_width, aspect_ratio) # re-introduce the spines/axes
for _ in range(200):
sim.evolve(6.0)
figure, ax = radar.draw(sim.manager.environment)
IPython.display.display(figure) # or IPython.display.display(plt.gcf())
IPython.display.clear_output(wait=True)
Conclusions¶
In this notebook, we saw how to configure scenarios by constructing the scenario manager, and set its parameters. We also looked at the "Infinite" scenario manager, and a couple of new sectors ("X" and "Xplus").
In the next notebook we will take another peek under the hood of the digital twin, and look at "Predictors" - how the simulation evolves aircraft from one timestep to the next.