upstage package#
Subpackages#
- upstage.communications package
- upstage.geography package
- Submodules
- upstage.geography.conversions module
- upstage.geography.intersections module
- upstage.geography.spherical module
Spherical
Spherical.EARTH_RADIUS
Spherical.bearing()
Spherical.cross_track_distance()
Spherical.cross_track_point()
Spherical.distance()
Spherical.distance_and_bearing()
Spherical.ecef_and_geo_linspace()
Spherical.ecef_linspace()
Spherical.geo_circle()
Spherical.geo_linspace()
Spherical.geo_linspace_with_ecef()
Spherical.point_along()
Spherical.point_from_bearing_dist()
- upstage.geography.wgs84 module
- Module contents
- upstage.motion package
- upstage.resources package
- upstage.units package
Submodules#
upstage.actor module#
This file contains the fundamental Actor class for UPSTAGE.
- class Actor(*, name: str, debug_log: bool = True, **states: Any)#
Bases:
SettableEnv
,NamedUpstageEntity
Actors perform tasks and are composed of states.
You can subclass, but do not overwrite __init_subclass__.
Always super().__init__().
- Parameters:
name (str) – The name of the actor. This is a required attribute.
debug_log (bool, optional) – Run the debug logger which captures runtime information about the actor.
**states – Keyword arguments to set the values of the actor’s states.
Raises
------
ValueError – If the keys of the states passed as keyword arguments do not match the names of the actor’s states.
- activate_linear_state(*, state: str, rate: float, task: Task) None #
Shortcut for activating a LinearChangingState.
- Parameters:
state (str) – The name of the LinearChangingState to set as active.
rate (float) – The rate of the change
task (Task) – The task that is activating
- activate_location_state(*, state: str, speed: float, waypoints: list[GeodeticLocation] | list[CartesianLocation], task: Task) None #
Shortcut for activating a (Cartesian|Geodetic)LocationChangingState.
- Parameters:
state (str) – State name
speed (float) – The speed to move at
waypoints (LOC_LIST) – Waypoints to move over
task (Task) – The task that the state is activated during.
- activate_mimic_state(*, self_state: str, mimic_state: str, mimic_actor: Actor, task: Task) None #
Activate a state to mimic a state on another actor.
- activate_state(*, state: str, task: Task, **kwargs: Any) None #
Set a state as active.
Note
This method is used by the tasks for activating states they use/modify.
TODO: on init, create activate_<statename> methods that type-hint the inputs
- add_task_network(network: TaskNetwork) None #
Add a task network to the actor.
- Parameters:
network (TaskNetwork) – The task network to add to the actor.
- clear_knowledge(name: str, caller: str | None = None) None #
Clear a knowledge value.
Raises an error if the knowledge does not exist.
- Parameters:
name (str) – The name of the knowledge item to clear.
caller (str) – The name of the Task that called the method. Used for debug logging purposes.
- clear_task_queue(network_name: str) None #
Empty the actor’s task queue.
This will cause the task network to be used for task flow.
- Parameters:
network_name (str) – The name of the network to clear the task queue.
- clone(new_env: MockEnvironment | None = None, knowledge: dict[str, Any] | None = None, **new_states: Any) Self #
Clones an actor and assigns it a new environment.
Note
This function is useful when testing if an actor can accomplish a task.
In general, cloned actor are referred to as
understudy
to keep with the theater analogy.The clones’ names are appended with the label
'[CLONE #]'
where'#'
indicates the number of clones of the actor.- Parameters:
new_env (Optional[MockEnvironment], optional) – Environment for cloning. Defaults to None.
knowledge (Optional[dict[str, Any]], optional) – Knowledge for the clone. Defaults to None.
new_states (Any) – New states to add to the actor when cloning.
- Returns:
The cloned actor of the same type
- Return type:
- create_knowledge_event(*, name: str, rehearsal_time_to_complete: float = 0.0) Event #
Create an event and store it in knowledge.
Useful for creating simple hold points in Tasks that can be succeeded by other processes.
Example
>>> def task(self, actor): >>> evt = actor.create_knowledge_event(name="hold") >>> yield evt >>> ... # do things ... >>> def other_task(self, actor): >>> if condition: >>> actor.succeed_knowledge_event(name="hold")
- Parameters:
name (str) – Name of the knowledge slot to store the event in.
rehearsal_time_to_complete (float, optional) – The event’s expected time to complete. Defaults to 0.0.
- Returns:
The event to yield on
- Return type:
- deactivate_all_mimic_states(*, task: Task) None #
Deactivate all mimicking states in the actor for a given task.
- Parameters:
task (Task) – The task where states are mimicking others.
- deactivate_all_states(*, task: Task) None #
Deactivate all states in the actor for a given task.
- Parameters:
task (Task) – The task that is deactivating the states.
- deactivate_mimic_state(*, self_state: str, task: Task) None #
Deactivate a mimicking state.
- Parameters:
self_state (str) – State name
task (Task) – Task it’s running in.
- deactivate_state(*, state: str, task: Task) None #
Deactivate a specific state.
- Parameters:
state (str) – The name of the state to deactivate.
task (Task) – The task that is deactivating the state.
- deactivate_states(*, states: str | Iterable[str], task: Task) None #
Set a list of active states to not active.
- Parameters:
states (str | Iterable[str]) – The names of the states to deactivate.
task (Task) – The task that is deactivating the states.
- delete_task_network(network_id: Any) None #
Deletes a task network reference.
Be careful, the network may still be running!
Do any interruptions on your own.
- Parameters:
network_id (Any) – Typically a string for the network name.
- get_active_state_data(state_name: str, without_update: bool = False) dict[str, Any] #
Get the data for a specific state.
- Parameters:
state_name (str) – The name of the state for which to retrieve the data.
without_update (bool) – Whether or not to update the state to the current sim time. Defaults to True
- Returns:
The state data.
- Return type:
dict[str, Any]
- get_all_task_queues() dict[str, list[str]] #
Get the task queues for all running networks.
- Returns:
Task names, keyed on task network name.
- Return type:
dict[str, list[str]]
- get_knowledge(name: str, must_exist: bool = False) Any #
Get a knowledge value from the actor.
- Parameters:
name (str) – The name of the knowledge
must_exist (bool) – Raise an error if the knowledge isn’t present. Defaults to false.
- Returns:
The knowledge value. None if the name doesn’t exist.
- Return type:
- get_log() list[str] #
Get the debug log.
- Returns:
List of log messages.
- Return type:
list[str]
- get_next_task(network_name: str) None | str #
Return the next task the actor has been told if there is one.
This does not clear the task, it’s information only.
- Parameters:
network_name (str) – The name of the network
- Returns:
The name of the next task, None if no next task.
- Return type:
None | str
- get_nucleus() TaskNetworkNucleus #
Return the actor’s nucleus.
- Returns:
The nucleus on the actor.
- Return type:
- get_remaining_waypoints(location_state: str) list[GeodeticLocation] | list[CartesianLocation] #
Convenience method for interacting with LocationChangingStates.
Primary use case is when restarting a Task that has a motion element to allow updating waypoint knowledge easily.
- Parameters:
location_state (str) – The name of the <LocationChangingState>
- Returns:
List of waypoints yet to be reached
- Return type:
list[Location]
- get_running_task(network_name: str) TaskData | None #
Return name and process reference of a task on this Actor’s task network.
Useful for finding a process to call interrupt() on.
- Parameters:
network_name (str) – Network name.
- Returns:
- Dataclass of name and process for the current task.
{“name”: Name, “process”: the Process simpy is holding.}
- Return type:
- get_running_tasks() dict[str, TaskData] #
Get all running task data.
- Returns:
- Dictionary of all running tasks.
Keyed on network name, then {“name”: Name, “process”: …}
- Return type:
dict[str, dict[str, TaskData]]
- get_task_queue(network_name: str) list[str] #
Get the actor’s task queue on a single network.
- Parameters:
network_name (str) – The network name
- Returns:
List of task names in the queue
- Return type:
list[str]
- has_task_network(network_id: Any) bool #
Test if a network id exists.
- Parameters:
network_id (Any) – Typically a string for the network name.
- Returns:
If the task network is on this actor.
- Return type:
bool
- interrupt_network(network_name: str, **interrupt_kwargs: Any) None #
Interrupt a running task network.
- Parameters:
network_name (str) – The name of the network.
interrupt_kwargs (Any) – kwargs to pass to the interrupt.
- log(msg: str | None = None) list[str] | None #
Add to the log or return it.
Only adds to log if debug_logging is True.
- Parameters:
msg (str, Optional) – The message to log.
- Returns:
The log if no message is given. None otherwise.
- Return type:
list[str] | None
- rehearse_network(network_name: str, task_name_list: list[str], knowledge: dict[str, Any] | None = None, end_task: str | None = None) Self #
Rehearse a network on this actor.
Supply the network name, the tasks to rehearse from this state, and any knowledge to apply to the cloned actor.
- Parameters:
network_name (str) – Network name
task_name_list (list[str]) – Tasks to rehearse on the network.
knowledge (dict[str, Any], optional) – knowledge to give to the cloned actor. Defaults to None.
end_task (str, optional) – A task to end on once reached.
- Returns:
The cloned actor after rehearsing the network.
- Return type:
- set_knowledge(name: str, value: Any, overwrite: bool = False, caller: str | None = None) None #
Set a knowledge value.
Raises an error if the knowledge exists and overwrite is False.
- Parameters:
name (str) – The name of the knowledge item.
value (Any) – The value to store for the knowledge.
overwrite (bool, Optional) – Allow the knowledge to be changed if it exits. Defaults to False.
caller (str, Optional) – The name of the object that called the method.
- set_task_queue(network_name: str, task_list: list[str]) None #
Initialize an actor’s empty task queue.
- Parameters:
network_name (str) – Task Network name
task_list (list[str]) – List of task names to queue.
- Raises:
SimulationError – _description_
- start_network_loop(network_name: str, init_task_name: str | None = None) None #
Start a task network looping/running on an actor.
If no task name is given, it will default to following the queue.
- Parameters:
network_name (str) – Network name.
init_task_name (str, optional) – Task to start with. Defaults to None.
- property state_values: dict[str, Any]#
Get the state names and values.
- Returns:
State name:value pairs.
- Return type:
dict[str, Any]
- property states: tuple[str, ...]#
Get the names of the actor’s states.
- Returns:
State names
- Return type:
tuple[str]
- succeed_knowledge_event(*, name: str, **kwargs: Any) None #
Succeed and clear an event stored in the actor’s knowledge.
See “create_knowledge_event” for usage example.
- Parameters:
name (str) – Event knowledge name.
**kwargs (Any) – Any payload to send to the event. Defaults to None
- suggest_network_name(factory: TaskNetworkFactory) str #
Deconflict names of task networks by suggesting a new name.
Used for creating multiple parallel task networks.
- Parameters:
factory (TaskNetworkFactory) – The factory from which you will create the network.
- Returns:
The network name to use
- Return type:
str
upstage.api module#
The elements in the UPSTAGE Application Programmable Interface.
upstage.base module#
Base classes and exceptions for UPSTAGE.
- class EarthProtocol(*args, **kwargs)#
Bases:
Protocol
Protocol for defining an earth model interface.
- distance(loc1: tuple[float, float], loc2: tuple[float, float], units: str) float #
Get the distance between two lat/lon (degrees) points.
- distance_and_bearing(loc1: tuple[float, float], loc2: tuple[float, float], units: str) tuple[float, float] #
Get the distance between two lat/lon (degrees) points.
- geo_linspace(start: tuple[float, float], end: tuple[float, float], num_segments: int) list[tuple[float, float]] #
Get evenly spaced coordinates between lat/lon pairs.
- lla2ecef(locs: list[tuple[float, float, float]]) list[tuple[float, float, float]] #
Get ECEF coordinates from lat lon alt.
- point_from_bearing_dist(point: tuple[float, float], bearing: float, distance: float, distance_units: str = 'nmi') tuple[float, float] #
Get a lat/lon in degrees from a point, bearing, and distance.
- class EnvironmentContext(initial_time: float = 0.0, random_seed: int | None = None, random_gen: Any | None = None)#
Bases:
object
A context manager to create a safe, globally (in context) referenceable environment and data.
The environment created is of type simpy.Environment
This also sets context variables for actors, entities, and the stage.
- Usage:
>>> with EnvironmentContext(initial_time=0.0) as env: >>> env.run(until=3.0)
This context manager is meant to be paired with inheritors of UpstageBase.
that provides access to the context variables created in this manager.
>>> class SimData(UpstageBase): >>> ... >>> >>> with EnvironmentContext(initial_time=0.0) as env: >>> data = SimData() >>> assert data.env is env
You may also provide a random seed, and a default Random() will be created with that seed.
>>> with EnvironmentContext(random_seed=1234986) as env: >>> UpstageBase().stage.random.uniform(1, 3) ... 2.348057489610457
Or your own RNG:
>>> rng = Random(1234986) >>> with EnvironmentContext(random_gen=rng) as env: >>> UpstageBase().stage.random.uniform(1, 3) ... 2.348057489610457
- class MockEnvironment(now: float)#
Bases:
object
A fake environment that holds the
now
property and all-caps attributes.- classmethod mock(env: Environment | MockEnvironment) MockEnvironment #
Create a mock environment from another environment.
- Parameters:
env (SimpyEnv | MockedEnvironment) – The simpy environments
- Returns:
The mocked environment (time only)
- Return type:
- classmethod run(until: float | int) None #
Method stub for playing nice with rehearsal.
- Parameters:
until (float | int) – Placeholder
- exception MotionAndDetectionError(message: str, time: float | None = None)#
Bases:
SimulationError
A simulation error raised during motion detection.
- class NamedUpstageEntity#
Bases:
UpstageBase
A base class for naming entities, and retrieving them.
This creates a record of every instance of a subclass of this class.
Example
>>> class RocketCar(NamedUpstageEntity, entity_groups=["car", "fast"]) >>> ... >>> rc = RocketCar() >>> assert rc in rc.get_entity_group("car")
- exception RulesError#
Bases:
UpstageError
Raised by the user when a simulation rule is violated.
- class SettableEnv(*args: Any, **kwargs: Any)#
Bases:
UpstageBase
A mixin class for allowing the instance’s environment to change.
- property env: Environment | MockEnvironment#
Get the relevant environment.
- Returns:
Real or mocked environment.
- Return type:
SimpyEnv | MockEnvironment
- exception SimulationError(message: str, time: float | None = None)#
Bases:
UpstageError
Raised when a simulation error occurs.
- class StageProtocol(*args, **kwargs)#
Bases:
Protocol
Protocol for typing the minimum entries in the Stage.
- property altitude_units: str#
Units of altitude.
- property distance_units: str#
Units of distance.
- property intersection_model: Callable[[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float], float, str, EarthProtocol, float | None, list[int] | None], list[CrossingCondition]]#
Callable for geodetic intersections.
- property random: Random#
Random number generator.
- property stage_model: EarthProtocol#
Model for geodetics.
- property time_unit: str#
Time unit, Treated as ‘hr’ if not set.
- class UpstageBase#
Bases:
object
A base mixin class for everyone.
Provides access to all context variables created by EnvironmentContext.
>>> with EnvironmentContext(initial_time=0.0) as env: >>> data = UpstageBase() >>> assert data.env is env
- property env: Environment#
Return the environment.
- Returns:
SimPy environment.
- Return type:
SimpyEnv
- get_actors() list[NamedUpstageEntity] #
Return all actors that the director knows.
- Returns:
List of actors in the simulation.
- Return type:
list[NamedUpstageEntity]
- get_all_entity_groups() dict[str, list[NamedUpstageEntity]] #
Get all entity groups.
- Returns:
- Entity group names and associated
entities.
- Return type:
dict[str, list[NamedUpstageEntity]]
- get_entity_group(group_name: str) list[NamedUpstageEntity] #
Get a single entity group by name.
- Parameters:
group_name (str) – The name of the entity group.
- Returns:
List of entities in the group.
- Return type:
list[NamedUpstageEntity]
- property pretty_now: str#
A well-formatted string of the sim time.
- Returns:
The sim time
- Return type:
str
- property stage: StageProtocol#
Return the stage context variable.
- Returns:
The stage, as defined in context.
- Return type:
- exception UpstageError#
Bases:
Exception
Raised when an UPSTAGE error happens or expectation is not met.
- add_stage_variable(varname: str, value: Any) None #
Add a variable to the stage.
Will fail if it already exists.
- Parameters:
varname (str) – Name of the variable
value (Any) – Value to set it as
- class dotdict#
Bases:
dict
A dictionary that supports dot notation as well as dictionary access notation.
Usage: d = dotdict({‘val1’:’first’}) set attributes: d.val2 = ‘second’ or d[‘val2’] = ‘second’ get attributes: d.val2 or d[‘val2’] would both produce ‘second’
upstage.constants module#
The constant values used by UPSTAGE.
upstage.data_types module#
Data types for common operations. Currently just locations.
- class CartesianLocation(x: float, y: float, z: float = 0.0, *, use_altitude_units: bool = False)#
Bases:
Location
A location that can be mapped to a 3-dimensional cartesian space.
- copy() CartesianLocation #
Return a copy of the location.
- Returns:
CartesianLocation
- straight_line_distance(other: object) float #
Get the straight line distance between this and another location.
- Parameters:
other (object) – The other CartesianLocation point.
- class GeodeticLocation(lat: float, lon: float, alt: float = 0.0, *, in_radians: bool = False)#
Bases:
Location
A Location that can be mapped to the surface of a spheroid (a.k.a. ellipsoid).
More specifically, a Location representing somewhere on an ellipsoid, with Latitude, Longitude, and Altitude that uses the geodetic datum (or geodetic system). Can be used to define a location on Earth or other planetary bodies.
Units for the horizontal datum (i.e., lat and lon) can be Decimal Degrees or Radians, depending on the value of units, the vertical datum (i.e., alt) is assumed to be in meters.
Subtraction represents a great circle distance, NOT a true 3D straight-line distance.
Speeds used for this location type will represent ground speed, which allows the class to ignore solving the exact altitude change in the path.
Units are an input to the location type.
The ellipsoid model must have a .distance(Location1, Location2) method that looks for .lat and .lon.
altitude is 0.0 by default.
- lat (latitude) and lon (longitude) are in degrees by default.
if using radians, set in_radians to True.
- copy() GeodeticLocation #
Copy the location.
- Returns:
GeodeticLocation
- dist_with_altitude(other: GeodeticLocation) float #
Get the distance between two points with an altitude component.
- Parameters:
other (GeodeticLocation) – The other point
- Returns:
Distance - pythagorean of great-circle and altitude
- Return type:
float
- latlon() tuple[float, float] #
Return a tuple of just lat/lon as degrees.
- Returns:
Latitude and longitude in degrees.
- Return type:
tuple[float, float]
- straight_line_distance(other: object) float #
Straight-line distance, using ECEF.
This won’t account for horizon.
- Parameters:
other (GeodeticLocation) – The other point
- Returns:
Distance
- Return type:
float
- to_degrees() GeodeticLocation #
Convert to degrees, if already in degrees, return self.
- Returns:
GeodeticLocation
- to_radians() GeodeticLocation #
Convert to radians, if already in radians, return self.
- Returns:
GeodeticLocation
upstage.events module#
Classes for UPSTAGE events that feed to simpy.
- class All(*events: BaseEvent | Process)#
Bases:
MultiEvent
An event that requires all events to succeed before succeeding.
- static aggregation_function(times: list[float]) float #
Aggregate event times for rehearsal.
- Parameters:
times (list[float]) – List of rehearsing times.
- Returns:
Aggregated (maximum) time.
- Return type:
float
- static simpy_equivalent(env: Environment, events: list[Event]) Event #
Return the SimPy version of the UPSTAGE All event.
- Parameters:
env (SIM.Environment) – SimPy Environment.
events (list[SIM.Event]) – List of events.
- Returns:
A simpy AllOf event.
- Return type:
SIM.Event
- class Any(*events: BaseEvent | Process)#
Bases:
MultiEvent
An event that requires one event to succeed before succeeding.
- static aggregation_function(times: list[float]) float #
Aggregation function for rehearsal time.
- Parameters:
times (list[float]) – List of rehearsal times
- Returns:
Aggregated time (the minimum)
- Return type:
float
- static simpy_equivalent(env: Environment, events: list[Event]) Event #
Return the SimPy version of the UPSTAGE Any event.
- Parameters:
env (SIM.Environment) – SimPy Environment.
events (list[SIM.Event]) – List of events.
- Returns:
A simpy AnyOf event.
- Return type:
SIM.Event
- class BaseEvent(*, rehearsal_time_to_complete: float = 0.0)#
Bases:
UpstageBase
Base class for framework events.
- as_event() Event #
Convert UPSTAGE event to a simpy Event.
- Returns:
The upstage event as a simpy event.
- Return type:
SIM.Event
- calculate_time_to_complete() float #
Calculate the time elapsed until the event is triggered.
- Returns:
The time until the event triggers.
- Return type:
float
- cancel() None #
Cancel an event.
- property done_rehearsing: bool#
If the event is done rehearsing.
- Returns:
bool
- is_complete() bool #
Is the event complete?
- Returns:
If it’s complete or not.
- Return type:
bool
- property now: float#
Current sim time.
- Returns:
sim time
- Return type:
float
- rehearse() tuple[float, Any | None] #
Run the event in ‘rehearsal’ mode without changing the real environment.
This is used by the task rehearsal functions.
- Returns:
The time to complete and the event’s response.
- Return type:
tuple[float, Any | None]
- property rehearsing: bool#
If the event is rehearsing.
- Returns:
bool
- class BaseRequestEvent(rehearsal_time_to_complete: float = 0.0)#
Bases:
BaseEvent
Base class for Request Events.
Requests are things like Get and Put that wait in a queue.
- cancel() None #
Cancel the Request.
- is_complete() bool #
Test if the request is finished.
- Returns:
bool
- class Event(rehearsal_time_to_complete: float = 0.0, auto_reset: bool = True)#
Bases:
BaseEvent
An UPSTAGE version of the standard SimPy Event.
Returns a planning factor object on rehearsal for user testing against in rehearsals, in case.
When the event is succeeded, a payload can be added through kwargs.
This Event assumes that it might be long-lasting, and will auto-reset when yielded on.
- as_event() Event #
Get the Event as a simpy type.
This resets the event if allowed.
- Returns:
SIM.Event
- calculate_time_to_complete() float #
Return the time to complete.
- Returns:
Time to complete estimate.
- Return type:
float
- cancel() None #
Cancel the event.
Cancelling doesn’t mean much, since it’s still going to be yielded on.
- get_payload() dict[str, Any] #
Get any payload from the call to succeed().
- Returns:
The payload left by the succeed() caller.
- Return type:
dict[str, Any]
- is_complete() bool #
Is the event done?
- Returns:
bool
- rehearse() tuple[float, Any] #
Run the event in ‘trial’ mode without changing the real environment.
- Returns:
The time to complete and the event’s response.
- Return type:
tuple[float, Any]
- reset() None #
Reset the event to allow it to be held again.
- class FilterGet(get_location: FilterStore, filter: Callable[[Any], bool], rehearsal_time_to_complete: float = 0.0)#
Bases:
Get
A Get for a FilterStore.
- class Get(get_location: Store | Container, *get_args: Any, rehearsal_time_to_complete: float = 0.0, **get_kwargs: Any)#
Bases:
BaseRequestEvent
Wrap the
simpy
Get event.Event that gets an object from a
simpy
store or gets an amount from a container.- as_event() ContainerGet | StoreGet #
Convert get to a
simpy
Event.- Returns:
ContainerGet | StoreGet
- calculate_time_to_complete() float #
Calculate time elapsed until the event is triggered.
- Returns:
Estimated time until the event triggers.
- Return type:
float
- get_value() Any #
Get the value returned when the request is complete.
- Returns:
The amount or item requested.
- Return type:
- rehearse() tuple[float, Any] #
Mock the event to test if it is feasible.
Note
The function does not fully test the conditions to satisfy the get request, but this method can be called as part of a more complex rehearse run.
- Returns:
The time it took to do the request Any: The value of the request.
- Return type:
float
- class MultiEvent(*events: BaseEvent | Process)#
Bases:
BaseEvent
A base class for evaluating multiple events.
Note
- Subclasses of MultiEvent must define these methods:
aggregation_function: Callable[[list[float]], float]
simpy_equivalent: simpy.Event
- static aggregation_function(times: list[float]) float #
Aggregate event times to one single time.
- Parameters:
times (list[float]) – Event rehearsal times
- Returns:
The aggregated time
- Return type:
float
- as_event() Event #
Convert the UPSTAGE event to simpy.
- Returns:
typically an Any or All
- Return type:
SIM.Event
- calc_time_to_complete_with_sub() tuple[float, dict[BaseEvent, float]] #
Compute time required for MultiEvent and get sub-event times.
- Returns:
Aggregate and individual times.
- Return type:
tuple[float, dict[BaseEvent, float]]
- calculate_time_to_complete() float #
Compute time required to complete the multi-event.
- Parameters:
return_sub_events (bool, Optional) – Whether to return all times or not. Defaults to False.
- cancel() None #
Cancel the multi event and propagate it to the sub-events.
- class Put(put_location: Container | Store, put_object: float | int | Any, rehearsal_time_to_complete: float = 0.0)#
Bases:
BaseRequestEvent
Wrap the
simpy
Put event.This is an event that puts an object into a
simpy
store or puts an amount into a container.
- class ResourceHold(resource: Resource, *resource_args: Any, rehearsal_time_to_complete: float = 0.0, **resource_kwargs: Any)#
Bases:
BaseRequestEvent
Wrap the
simpy
request resource event.This manages getting and giving back all in one object.
Example
>>> resource = simpy.Resource(env, capacity=1) >>> hold = ResourceHold(resource) >>> # yield on the hold to get it >>> yield hold >>> # now that you have it, do things.. >>> # give it back >>> yield hold >>> ...
- as_event() Request | Release #
Create the simpy event for the right state of Resource usage.
- Returns:
The simpy event.
- Return type:
Request | Release
- calculate_time_to_complete() float #
Time to complete, based on waiting for getting or giving back.
- Returns:
Time
- Return type:
float
- class Wait(timeout: float | int, rehearsal_time_to_complete: float | int | None = None)#
Bases:
BaseEvent
Wait a specified or random uniformly distributed amount of time.
Return a timeout. If time is a list of length 2, choose a random time between the interval given.
Rehearsal time is given by the maximum time of the interval, if given.
- Parameters:
timeout (int, float, list, tuple) – Amount of time to wait. If it is a list or a tuple of length 2, a random uniform value between the two values will be used.
- as_event() Timeout #
Cast Wait event as a simpy Timeout event.
- Returns:
SIM.Timeout
- cancel() None #
Cancel the timeout.
There’s no real meaning to cancelling a timeout. It sits in simpy’s queue either way.
- classmethod from_random_uniform(low: float | int, high: float | int, rehearsal_time_to_complete: float | int | None = None) Wait #
Create a wait from a random uniform time.
- Parameters:
low (float) – Lower bounds of random draw
high (float) – Upper bounds of random draw
rehearsal_time_to_complete (float | int, optional) – The rehearsal time to complete. Defaults to None - meaning the random value drawn.
- Returns:
The timeout event
- Return type:
upstage.math_utils module#
This module contains math utility functions to avoid numpy.
upstage.nucleus module#
The file contains the Nucleus features of UPSTAGE.
- class NucleusInterrupt(name: str, value: Any)#
Bases:
object
A data container for interrupting nucleus events.
- class TaskNetworkNucleus(*, actor: Actor)#
Bases:
object
The nucleus, for state-based task network signaling.
- add_network(network_name: str | TaskNetwork, watch_states: list[str]) None #
Add a network to the nucleus for state management.
- Parameters:
network_name (str | TaskNetwork) – A task network that works on this nucleus/actor
watch_states (list[str]) – States that - when changed - cause the network to change.
- remove_network(network_name: str | TaskNetwork) None #
Remove a network from nucleus.
- Parameters:
network_name (str | TaskNetwork) – A task network that works on this nucleus/actor
upstage.state_sharing module#
States that enable sharing between tasks.
Bases:
ActiveState
[float
]A state whose value changes linearly over time.
Allows for multiple users of the state, keyed on actor and task.
Assumes it’s a non-frozen, floating-point value.
Still activated in the usual way:
>>> class Example(Actor): >>> fuel = SharedLinearChangingState() ... >>> example.activate_state( >>> state="fuel", >>> task=self, >>> rate=actor.fuel_burn, >>> )
upstage.states module#
A state defines the conditions of an actor over time.
- class ActiveState(*, default: ST | None = None, frozen: bool = False, valid_types: type | tuple[type, ...] | None = None, recording: bool = False, default_factory: Callable[[], ST] | None = None)#
Bases:
State
,Generic
[ST
]Base class for states that change over time according to some rules.
This class must be subclasses with an implemented active method.
- class CartesianLocationChangingState(*, recording: bool = False)#
Bases:
ActiveState
[CartesianLocation
]A state that contains the location in 3-dimensional Cartesian space.
Movement is along straight lines in that space.
- For activating:
>>> actor.activate_state( >>> state=<state name>, >>> task=self, # usually >>> speed=<speed>, >>> waypoints=[ >>> List of CartesianLocation >>> ] >>> )
- class CommunicationStore(*, mode: str, default: type | None = None, valid_types: type | tuple[type, ...] | None = None)#
Bases:
ResourceState
[Store
]A State class for communications inputs.
Used for automated finding of communication inputs on Actors by the CommsTransfer code.
Follows the same rules for defaults as ResourceState, except this defaults to a SelfMonitoringStore without any user input.
Only resources inheriting from simpy.Store will work for this state. Capacities are assumed infinite.
- The input an Actor needs to receive for a CommunicationStore is a dictionary of:
>>> { >>> 'kind': <class> (optional) >>> 'mode': <string> (required) >>> }
Example
>>> class Worker(Actor): >>> walkie = CommunicationStore(mode="UHF") >>> intercom = CommunicationStore(mode="loudspeaker") >>> >>> worker = Worker( >>> name='Billy', >>> walkie={'kind': SelfMonitoringStore}, >>> )
- class DetectabilityState(*, default: bool = False, recording: bool = False)#
Bases:
State
[bool
]A state whose purpose is to indicate True or False.
For consideration in the motion manager’s <>LocationChangingState checks.
- class GeodeticLocationChangingState(*, recording: bool = False)#
Bases:
ActiveState
[GeodeticLocation
]A state that contains a location around an ellipsoid that follows great-circle paths.
Requires a distance model class that implements: 1. distance_and_bearing 2. point_from_bearing_dist and outputs objects with .lat and .lon attributes
For activating:
>>> actor.activate_state( >>> state=<state name>, >>> task=self, # usually >>> speed=<speed>, >>> waypoints=[ >>> List of CartesianLocation >>> ] >>> )
- class LinearChangingState(*, default: ST | None = None, frozen: bool = False, valid_types: type | tuple[type, ...] | None = None, recording: bool = False, default_factory: Callable[[], ST] | None = None)#
Bases:
ActiveState
[float
]A state whose value changes linearly over time.
When activating:
>>> class Lin(Actor): >>> x = LinearChangingState() >>> >>> def task(self, actor: Lin): >>> actor.activate_state( >>> name="x", >>> task=self, >>> rate=3.2, >>> )
- class ResourceState(*, default: Any | None = None, valid_types: type | tuple[type, ...] | None = None)#
Bases:
State
,Generic
[T
]A State class for States that are meant to be Stores or Containers.
This should enable easier initialization of Actors with stores/containers or similar objects as states.
No input is needed for the state if you define a default resource class in the class definition and do not wish to modify the default inputs of that class.
The input an Actor needs to receive for a ResourceState is a dictionary of: * ‘kind’: <class> (optional if you provided a default) * ‘capacity’: <numeric> (optional, works on stores and containers) * ‘init’: <numeric> (optional, works on containers) * key:value for any other input expected as a keyword argument by the resource class
Note that the resource class given must accept the environment as the first positional argument. This is to maintain compatibility with simpy.
Example
>>> class Warehouse(Actor): >>> shelf = ResourceState[Store](default=Store) >>> bucket = ResourceState[Container]( >>> default=Container, >>> valid_types=(Container, SelfMonitoringContainer), >>> ) >>> >>> wh = Warehouse( >>> name='Depot', >>> shelf={'capacity': 10}, >>> bucket={'kind': SelfMonitoringContainer, 'init': 30}, >>> )
- class State(*, default: ST | None = None, frozen: bool = False, valid_types: type | tuple[type, ...] | None = None, recording: bool = False, default_factory: Callable[[], ST] | None = None)#
Bases:
Generic
[ST
]The particular condition that something is in at a specific time.
The states are implemented as Descriptors which are associated to
upstage.actor.Actor
.Note
The classes that use this descriptor must contain an
env
attribute.States are aware
- has_default() bool #
Check if a default exists.
- Returns:
bool
- property is_recording: bool#
Check if the state is recording.
- Returns:
bool
upstage.task module#
Tasks constitute the actions that Actors can perform.
- class DecisionTask#
Bases:
Task
A task used for decision processes.
- make_decision(*, actor: Any) None #
Define the process this task follows.
- rehearse(*, actor: REH_ACTOR, knowledge: dict[str, Any] | None = None, cloned_actor: bool = False, **kwargs: Any) REH_ACTOR #
Rehearse the task to evaluate its feasibility.
- Parameters:
- Returns:
Cloned actor after rehearsing this task.
- Return type:
- rehearse_decision(*, actor: Any) None #
Define the process this task follows.
- class InterruptStates(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)#
Bases:
IntFlag
Class that describes how to behave after an interrupt.
- END = 0#
- IGNORE = 1#
- RESTART = 2#
- class Task#
Bases:
SettableEnv
A Task is an action that can be performed by an Actor.
- INTERRUPT#
alias of
InterruptStates
- clear_actor_knowledge(actor: Actor, name: str) None #
Clear knowledge from an actor.
Convenience method for passing in the name of task for actor logging.
- Parameters:
actor (Actor) – The actor to clear knowledge from
name (str) – The name of the knowledge
- clear_actor_task_queue(actor: Actor) None #
Clear out the task queue on the network.
- Parameters:
actor (Actor) – The actor whose queue will be cleared
- clear_marker() None #
Clear the marker and set that an interrupt ends the task.
- static get_actor_knowledge(actor: Actor, name: str, must_exist: bool = False) Any #
Get knowledge from the actor.
- get_actor_next_task(actor: Actor) str | None #
Get the next queued task.
- Parameters:
actor (Actor) – The actor to get the next task from
- Returns:
The next task name (or None if no task)
- Return type:
str | None
- get_actor_task_queue(actor: Actor) list[str] #
Get the task queue on the actor.
- Parameters:
actor (Actor) – The actor to modify the task queue of
- get_marker() str | None #
Get the current marker.
- Returns:
Marker (or None if cleared)
- Return type:
str | None
- get_marker_time() float | None #
The time the current marker was set.
- Returns:
Marker set time (or None if cleared)
- Return type:
float | None
- on_interrupt(*, actor: Any, cause: Any) InterruptStates #
Define any actions to take on the actor if this task is interrupted.
Note
Custom Tasks can overwrite this method so they can handle being interrupted with a custom procedure. By default, interrupt ends the task.
- rehearse(*, actor: REH_ACTOR, knowledge: dict[str, Any] | None = None, cloned_actor: bool = False, **kwargs: Any) REH_ACTOR #
Rehearse the task to evaluate its feasibility.
- Parameters:
- Returns:
The cloned actor with a state reflecting the task flow.
- Return type:
- run(*, actor: Actor) Generator[Event | Process, Any, None] #
Execute the task.
- Parameters:
actor (Actor) – The actor using the task
- Returns:
Generator[SimpyEvent, Any, None]
- set_actor_knowledge(actor: Actor, name: str, value: Any, overwrite: bool = False) None #
Set knowledge on the actor.
Convenience method for passing in the name of task for actor logging.
- set_actor_task_queue(actor: Actor, task_list: list[str]) None #
Set the task queue on the actor.
This assumes an empty queue.
- Parameters:
actor (Actor) – The actor to modify the task queue of
task_list (list[str]) – The list of task names to queue.
- set_marker(marker: str, interrupt_action: ~upstage.task.InterruptStates = <InterruptStates.END: 0>) None #
Set a marker to help with inspection of interrupts.
The interrupt_action is set for when no on_interrupt is implemented.
- Parameters:
marker (str) – String for the marker.
interrupt_action (InterruptStates, optional) – Action to take on interrupt.
InterruptStates.END. (Defaults to)
- class TerminalTask#
Bases:
Task
A rehearsal-safe task that cannot exit, i.e., it is terminal.
Note
The user can re-implement the log_message method to return a custom message that will be appended to the actor’s log through its log method.
- log_message(*, actor: Actor) str #
A message to save to a log when this task is reached.
- Parameters:
actor (Actor) – The actor using this task.
- Returns:
A log message
- Return type:
str
- on_interrupt(*, actor: Actor, cause: Any) InterruptStates #
Special case interrupt for terminal task.
- process(func: Callable[[...], Generator[Event, Any, None]]) Callable[[...], Process] #
Decorate a
simpy
process to schedule it as a callable.Allows users to decorate a generator, and when they want to schedule them as a
simpy
process, they can simply call it, e.g., instead of calling:Usage:
>>> from upstage.api import process, Wait ... >>> @process >>> def generator(wait_period=1.0, msg="Finished Waiting"): >>> # A simple process that periodically prints a statement >>> while True: >>> yield Wait(wait_period).as_event() >>> print(msg) ... >>> @process >>> def another_process(): >>> # Some other process that calls the first one >>> generator()
- Parameters:
func (Callable[..., Generator[BaseEvent, None, None]]) – The process function that is a
events. (generator of simpy)
- Returns:
The generator as a
simpy
process.- Return type:
Process
Note
The value of this decorator is that it reduces the chance of a user forgetting to call the generator as a process, which tends to produce behaviors that are difficult to troubleshoot because the code will build and can run, but the simulation will not work schedule the process defined by the generator.
upstage.task_network module#
The task network class, and factory classes.
- class TaskLinks(default: str | None, allowed: Sequence[str])#
Bases:
object
Type hinting for task link dictionaries.
- allowed: Sequence[str]#
- default: str | None#
- class TaskNetwork(name: str, task_classes: Mapping[str, type[Task]], task_links: Mapping[str, TaskLinks])#
Bases:
object
A means to represent, execute, and rehearse interdependent tasks.
- is_feasible(curr: str, new: str) bool #
Determine if a task can follow another one.
- Parameters:
curr (str) – Current task name
new (str) – Potential next task name
- Returns:
If the new task can follow the current.
- Return type:
bool
- loop(*, actor: Actor, init_task_name: str | None = None) Generator[Process, None, None] #
Start a task network running its loop.
If no initial task name is given, it will default to following the queue.
- Parameters:
actor (Actor) – The actor to run the loop on.
init_task_name (Optional[str], optional) – Optional task to start running.
None. (Defaults to)
- rehearse_network(*, actor: REH_ACTOR, task_name_list: list[str], knowledge: dict[str, Any] | None = None, end_task: str | None = None) REH_ACTOR #
Rehearse a path through the task network.
- Parameters:
- Returns:
A copy of the original actor with state changes associated with the network.
- Return type:
- class TaskNetworkFactory(name: str, task_classes: Mapping[str, type[Task]], task_links: Mapping[str, TaskLinks])#
Bases:
object
A factory for creating task network instances.
- classmethod from_ordered_loop(name: str, task_classes: list[type[Task]]) TaskNetworkFactory #
Create a network factory from a list of tasks that loops.
- Parameters:
name (str) – Network name
task_classes (list[Task]) – The tasks to run in order.
- Returns:
The factory for the ordered network.
- Return type:
- classmethod from_ordered_terminating(name: str, task_classes: list[type[Task]]) TaskNetworkFactory #
Create a network factory from a list of tasks that terminates.
- Parameters:
name (str) – Network name
task_classes (list[Task]) – The tasks to run in order.
- Returns:
The factory for the ordered network.
- Return type:
- classmethod from_single_looping(name: str, task_class: type[Task]) TaskNetworkFactory #
Create a network factory from a single task that loops.
- Parameters:
name (str) – Network name
task_class (Task) – The single task to loop
- Returns:
The factory for the single looping network.
- Return type:
- classmethod from_single_terminating(name: str, task_class: type[Task]) TaskNetworkFactory #
Create a network factory from a single task that terminates.
- Parameters:
name (str) – Network name
task_class (Task) – The single task to terminate after
- Returns:
The factory for the single terminating network.
- Return type:
- make_network(other_name: str | None = None) TaskNetwork #
Create an instance of the task network.
By default, this uses the name defined on instantiation.
- Parameters:
other_name (str, optional) – Another name for the network. Defaults to None.
- Returns:
TaskNetwork
upstage.utils module#
This module contains utility functions.
Note
Some of the functions included in this module directly support UPSTAGE’s other modules, and some are there for the user’s convenience.
- debug_assert(test: bool, msg: str = '') None #
Coalesces breakpoints for any failing assert to a single line.
Coalesces all potential lines where the code may fail into one single line that can be marked as a breakpoint.
- Parameters:
test (bool) – The boolean statement to test, i.e., must evaluate to
true
orfalse
.msg (str) – The message to display if the test is false.
Note
This is necessary because
pdb
sometimes does not work well when runningsimpy
due to the way thatsimpy
handles exceptions.This is also helpful when debugging complex behaviors that run the same code multiple times. Instead of manually writing a
try/except
statement, you can usedebug_assert
to do that for you, and ignore all of them from a single control point.Example
>>> # 1. Add a ``debug_assert`` statement in your code, e.g.: >>> from upstage.utils import debug_assert >>> ... >>> bar, foo = 0, 1 # <<< change foo to be less than bar to raise >>> debug_assert(foo > bar, "foo is not greater than bar") >>> # 2. Put a break point on the ``raise error`` line to see why the assert failed.
- debug_pause(test: bool | None = None) None #
Call function to pause IDE on debug mode with single breakpoint.
A helper function to pause the execution of the interpreter when running the code from an IDE (e.g., PyCharm).
- Parameters:
test (bool, optional) – A boolean statement to pause on when it evaluates to
true
.
Note
This is necessary because
pdb
sometimes does not work well when runningsimpy
due to the way thatsimpy
handles exceptions.Note
Put a break point on the
pass
line to pause the IDE.
- get_caller_info(caller_level: int = 1) str #
Get information from the object that called the function.
- Parameters:
caller_level (str, optional) – The number of frames to go back in the call stack.
- get_caller_object(caller_level: int = 2) Any #
Inspect the stack to see who called you.
- Parameters:
caller_level (int, optional) – Number of hops up in the stack. Defaults to 2.
- Returns:
The task object
- Return type:
- iterable_convert(item: T | list[T] | tuple[T, ...]) list[T] #
Convert single objects or tuples into a list.
- Parameters:
item (T | list[T] | tuple[T,...]) – Object, list, or tuple to convert.
- Returns:
The list version of the input.
- Return type:
list[T]
Module contents#
A framework for modeling and simulating complex systems of systems.
UPSTAGE (i.e., the Universal Platform for Simulating Tasks and Actors with Graphs and Events) is built atop of SimPy, with the intent of simplifying the development process for complex simulations.