Skip to content

Commit

Permalink
Merge pull request #15 from gtri/13-write-docs-about-data-gathering
Browse files Browse the repository at this point in the history
Simulation data gathering
  • Loading branch information
JamesArruda authored Dec 24, 2024
2 parents a1fbb35 + 88ce8b1 commit 4b1e1a0
Show file tree
Hide file tree
Showing 26 changed files with 1,453 additions and 213 deletions.
28 changes: 14 additions & 14 deletions docs/source/class_refs.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@

.. _SimPy: https://simpy.readthedocs.io/

.. |Actor| replace:: :py:class:`~upstage.actor.Actor`
.. |State| replace:: :py:class:`~upstage.states.State`
.. |Task| replace:: :py:class:`~upstage.task.Task`
.. |TaskNetwork| replace:: :py:class:`~upstage.task_network.TaskNetwork`
.. |TaskNetworks| replace:: :py:class:`TaskNetworks <upstage.task_network.TaskNetwork>`
.. |EnvironmentContext| replace:: :py:class:`~upstage.base.EnvironmentContext`
.. |UpstageBase| replace:: :py:class:`~upstage.base.UpstageBase`
.. |NamedEntity| replace:: :py:class:`~upstage.base.NamedUpstageEntity`
.. |LinearChangingState| replace:: :py:class:`~upstage.states.LinearChangingState`
.. |CartesianLocation| replace:: :py:class:`~upstage.data_types.CartesianLocation`
.. |GeodeticLocationChangingState| replace:: :py:class:`~upstage.states.GeodeticLocationChangingState`
.. |ResourceState| replace:: :py:class:`~upstage.states.ResourceState`
.. |SelfMonitoringStore| replace:: :py:class:`~upstage.stores.SelfMonitoringStore`
.. |DecisionTask| replace:: :py:class:`~upstage.task.DecisionTask`
.. |Actor| replace:: :py:class:`~upstage_des.actor.Actor`
.. |State| replace:: :py:class:`~upstage_des.states.State`
.. |Task| replace:: :py:class:`~upstage_des.task.Task`
.. |TaskNetwork| replace:: :py:class:`~upstage_des.task_network.TaskNetwork`
.. |TaskNetworks| replace:: :py:class:`TaskNetworks <upstage_des.task_network.TaskNetwork>`
.. |EnvironmentContext| replace:: :py:class:`~upstage_des.base.EnvironmentContext`
.. |UpstageBase| replace:: :py:class:`~upstage_des.base.UpstageBase`
.. |NamedEntity| replace:: :py:class:`~upstage_des.base.NamedUpstageEntity`
.. |LinearChangingState| replace:: :py:class:`~upstage_des.states.LinearChangingState`
.. |CartesianLocation| replace:: :py:class:`~upstage_des.data_types.CartesianLocation`
.. |GeodeticLocationChangingState| replace:: :py:class:`~upstage_des.states.GeodeticLocationChangingState`
.. |ResourceState| replace:: :py:class:`~upstage_des.states.ResourceState`
.. |SelfMonitoringStore| replace:: :py:class:`~upstage_des.stores.SelfMonitoringStore`
.. |DecisionTask| replace:: :py:class:`~upstage_des.task.DecisionTask`
12 changes: 8 additions & 4 deletions docs/source/user_guide/how_tos/active_states.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
Active States
=============

Active States are an UPSTAGE feature where states are told how to update themselves when requested, while not having to modify or alter the timeout they are changing during.
Active States are an UPSTAGE feature where states are told how to update themselves when requested,
while not having to modify or alter the timeout they are changing during.

For example, a fuel depot may dispense fuel at a given rate for some amount of time. An employee may monitor that level at certain times. UPSTAGE allows the state to hold its own
For example, a fuel depot may dispense fuel at a given rate for some amount of time.
An employee may monitor that level at certain times. UPSTAGE allows the state to hold its own
update logic, rather than the employee code needing to know when the fuel started changing, at what rate, etc.

Active states are stopped and started with :py:meth:`~upstage_des.actor.Actor.activate_state` and :py:meth:`~upstage_des.actor.Actor.deactivate_state`.
Active states are stopped and started with :py:meth:`~upstage_des.actor.Actor.activate_state` and
:py:meth:`~upstage_des.actor.Actor.deactivate_state`.

Active states are automatically stopped when a Task is interrupted.

Expand Down Expand Up @@ -248,4 +251,5 @@ Another option is to make a subclass that hints for you:
>>> 220.0
Note that state activation doesn't require a task. It's just the best place to do it, because task interrupts automatically deactivate all states.
Note that state activation doesn't require a task. It's just the best place to do it,
because task interrupts automatically deactivate all states.
19 changes: 13 additions & 6 deletions docs/source/user_guide/how_tos/entity_naming.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
Named Entities
==============

Named entities are an :py:class:`~upstage_des.base.EnvironmentContext` and :py:class:`~upstage_des.base.NamedUpstageEntity` enabled feature where you can store instances in particular "entity groups" to gather
them from later. UPSTAGE's :py:class:`~upstage_des.actor.Actor` inherits from :py:class:`~upstage_des.base.NamedUpstageEntity`, giving all Actors the feature.
Named entities are an :py:class:`~upstage_des.base.EnvironmentContext` and
:py:class:`~upstage_des.base.NamedUpstageEntity` enabled feature where you
can store instances in particular "entity groups" to gather them from later.
UPSTAGE's :py:class:`~upstage_des.actor.Actor` inherits from :py:class:`~upstage_des.base.NamedUpstageEntity`,
giving all Actors the feature. Similarly, the ``SelfMonitoring<>`` resources
do the same to enable quick access to recorded simulation data.

All Actors are retrievable with the :py:meth:`~upstage_des.base.UpstageBase.get_actors` method if they inherit from Actor.
All Actors are retrievable with the :py:meth:`~upstage_des.base.UpstageBase.get_actors`
method if they inherit from Actor.

Entities are retrievable with :py:meth:`~upstage_des.base.UpstageBase.get_all_entity_groups` and :py:meth:`~upstage_des.base.UpstageBase.get_entity_group`.
Entities are retrievable with :py:meth:`~upstage_des.base.UpstageBase.get_all_entity_groups`
and :py:meth:`~upstage_des.base.UpstageBase.get_entity_group`.

Defining a named entity is done in the class definition:

Expand All @@ -29,7 +35,7 @@ Defining a named entity is done in the class definition:
...
Once you are in an environment context you get the actual instances.
Once you are in an environment context you can get the actual instances.

.. code-block:: python
Expand Down Expand Up @@ -59,5 +65,6 @@ Once you are in an environment context you get the actual instances.
print(different)
>>> [<__main__.Different object at 0x000001FFAB28BE10>]
Note that entity groups are inheritable, that you can inherit from ``NamedUpstageEntity`` and retrieve the instance without needing an Actor, and that it's simple to create an instance of
Note that entity groups are inheritable and that you can inherit from ``NamedUpstageEntity``
and retrieve the instance without needing an Actor. You may also create an instance of
``UpstageBase`` to get access to the required methods.
53 changes: 53 additions & 0 deletions docs/source/user_guide/how_tos/environment.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
===================
Environment Context
===================

UPSTAGE uses Python's [context variable](https://docs.python.org/3/library/contextvars.html)
capabilities to safely manage "global" state information while not polluting the module
level data with run-specific information.

The context manager accepts three arguments:

1. Simulation start time (passes through to ``simpy.Environment``)
2. A random seed for ``random.Random``
3. A random number generator object, if different than ``random.Random``

For more about the random numbers, see :doc:`Random Numbers </user_guide/how_tos/random_numbers>`.

.. note::

If you get a warning or error about not finding an environment, you have likely
tried to instantiate an actor, task, or other UPSTAGE object outside of an
environment context.


Creating Contexts
=================

Use the ``EnvironmentContext`` context manager:

.. code:: python
impoprt upstage_des.api as UP
with UP.EnvironmentContext() as env:
...
# everything in here can find that environment
...
env.run()
Or, create a context at the current scope:

.. code:: python
from upstage_des.base import create_top_context, clear_top_context
ctx = create_top_context()
env = ctx.env_ctx.get()
...
env.run()
clear_top_context(ctx)
This way is friendlier to Jupyter notebooks, where you might run a simulation and want to
explore the data without needing to remain in the context manager.
11 changes: 7 additions & 4 deletions docs/source/user_guide/how_tos/geography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,16 @@ Both distances are within .07% of UPSTAGE's calculations.
:py:class:`~upstage_des.states.GeodeticLocationChangingState`
-------------------------------------------------------------

This is a State that allows activation and movement along great-circle waypoints with altitude changing along the waypoints. When initializing, it accepts a ``GeodeticLocation`` object, and it returns those when you ask it for
This is a State that allows activation and movement along great-circle waypoints with altitude changing along the
waypoints. When initializing, it accepts a ``GeodeticLocation`` object, and it returns those when you ask it for
the state's value. Here is its basic usage:

.. code-block:: python
from upstage_des.utils import waypoint_time_and_dist
class Plane(UP.Actor):
location: UP.GeodeticLocation = UP.GeodeticLocationChangingState(recording=True)
location = UP.GeodeticLocationChangingState(recording=True)
speed = UP.State[float](valid_types=float, default=100.0)
class Fly(UP.Task):
Expand Down Expand Up @@ -147,7 +148,8 @@ the state's value. Here is its basic usage:
)
...
The :py:func:`~upstage_des.utils.waypoint_time_and_dist` function is a convenience function that gets the great circle distance and time over a set of waypoints to help schedule the arrival time.
The :py:func:`~upstage_des.utils.waypoint_time_and_dist` function is a convenience function that gets the great
circle distance and time over a set of waypoints to help schedule the arrival time.


Cartesian Locations
Expand Down Expand Up @@ -192,7 +194,8 @@ The distance is always implied to be in ``distance_units``, without setting it.
:py:class:`~upstage_des.states.CartesianLocationChangingState`
--------------------------------------------------------------

This active state works the exact same as the ``GeodeticLocationChangingState`` , except that it requires waypoints to be ``CartesianLocation`` s.
This active state works the exact same as the ``GeodeticLocationChangingState`` , except that it requires
waypoints to be ``CartesianLocation`` s.


Geography Sub-Module
Expand Down
12 changes: 8 additions & 4 deletions docs/source/user_guide/how_tos/random_numbers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ Random Numbers

Random numbers are not supplied by UPSTAGE, you are responsible for rolling dice on your own.

However, UPSTAGE does use them in one area, which is in :py:class:`~upstage_des.events.Wait`, in the :py:meth:`~upstage_des.events.Wait.from_random_uniform` method.
However, UPSTAGE does use them in one area, which is in :py:class:`~upstage_des.events.Wait`,
in the :py:meth:`~upstage_des.events.Wait.from_random_uniform` method.

The built-in python ``random`` module is used by default, and you can find it on ``stage.random``. It can be instantiated in a few ways:
The built-in python ``random`` module is used by default, and you can find it on
``stage.random``. It can be instantiated in a few ways:

.. code-block:: python
Expand All @@ -31,6 +33,8 @@ The built-in python ``random`` module is used by default, and you can find it on
print(num)
>>> 2.348057489610457
If you want to use your own random number generator, just supply it to the ``random_gen`` input, or as its own variable with ``UP.add_stage_variable``.
If you want to use your own random number generator, just supply it to the ``random_gen``
input, or as its own variable with ``UP.add_stage_variable``.

If you supply it as ``random_gen``, ensure that it has a ``uniform`` method so that the Wait event can use it.
If you supply it as ``random_gen``, ensure that it has a ``uniform`` method so that the
Wait event can use it.
83 changes: 83 additions & 0 deletions docs/source/user_guide/how_tos/resources.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
==============
Resource Types
==============

UPSTAGE comes with new resource types in addition to the SimPy resources:

1. :py:class:`~upstage_des.resources.container.ContinuousContainer`
2. :py:class:`~upstage_des.resources.monitoring.SelfMonitoringStore`
3. :py:class:`~upstage_des.resources.monitoring.SelfMonitoringFilterStore`
4. :py:class:`~upstage_des.resources.monitoring.SelfMonitoringContainer`
5. :py:class:`~upstage_des.resources.monitoring.SelfMonitoringContinuousContainer`
6. :py:class:`~upstage_des.resources.monitoring.SelfMonitoringSortedFilterStore`
7. :py:class:`~upstage_des.resources.monitoring.SelfMonitoringReserveContainer`
8. :py:class:`~upstage_des.resources.reserve.ReserveContainer`
9. :py:class:`~upstage_des.resources.sorted.SortedFilterStore`

The self-monitoring stores are discussed in :doc:`the simulation data section </user_guide/tutorials/data>`.
They enable recording of data within the store or container over time, with an optional input for a function to
evaluate when recording on the items in the stores.

ContinuousContainer
===================

This container accepts gets and puts that act continuously, requiring both a rate and time to get or pull at that rate:

.. code:: python
tank = UP.ContinuousContainer(env, capacity=10, init=0)
tank.put(rate=3.0, time=2.5)
env.run(until=3.0)
print(tank.level)
>>> 7.5
The gets and puts can be done simultaneously, and the container will determine the current level when asked for it. The
container will also, by default, raise errors when it has reach capacity or when it is empty.

SortedFilterStore
=================

This store behaves similar to the SimPy ``FilterStore``, except that it also accepts a function that prioritizes the
items in the store.

.. code:: python
with UP.EnvironmentContext() as env:
shelf = UP.SortedFilterStore(env)
# pre-load items
shelf.items.extend([(1, "a"), (2, "b"), (1, "b"), (1, "B")])
def _proc() -> tuple[float, str]:
ans = yield shelf.get(
filter=lambda x: x[1] == x[1].lower(),
sorter=lambda x: (x[1], -x[0]),
reverse=True,
)
return ans
p = env.process(_proc())
env.run()
print(p.value)
>>> (1, 'b')
In the above, we filter items to have lower-case letters. Then we sort by ascending alphabetical and
descending numerical. Note the use of ``reverse=True`` and the ``-x[0]`` to do this. That gives us the
tie-breaker between ``(1, "a")`` and ``(1, "b")`` that ignores ``(1, "B")``.

ReserveContainer
================

The reserve container is not a true Container, in that it doesn't hold on queues. It is used to hold first-come
reservations for something numeric. Those requests can be timed out, and then checked on later by the
requestor. This is useful if you want to reserve access to a limited resource, but don't want or need to
hold in a line to do so.

The public methods on the ``ReserveContainer`` are:

1. ``reserve(requestor, quantity, expiration=None)``: Hold an amount
2. ``cancel_request(requestor)``: Cancel a hold
3. ``take(requestor)``: Get the amount held - or fail if request expired
4. ``put(amount, capacity_increase=False)``: Put something in the container, optionally increasing capacity.

The workflow with this resource is to resever, take, then put back when done - if the resource represented isn't
consumable.
6 changes: 5 additions & 1 deletion docs/source/user_guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ tutorials/first_simulation
tutorials/interrupts
tutorials/rehearsal
tutorials/best_practices
tutorials/simpy_compare.rst
tutorials/data
tutorials/simpy_compare
```

It is also recommended that you familiarize yourself with how [SimPy runs by itself](https://simpy.readthedocs.io/en/latest/), since
Expand All @@ -41,6 +42,7 @@ These are complete examples for some of the above tutorials.
tutorials/first_sim_full.rst
tutorials/rehearsal_sim.rst
tutorials/complex_cashier.rst
tutorials/data_creation_example.rst
```

## How-to Guides
Expand All @@ -51,6 +53,8 @@ These pages detail the specific activities that can be accomplished using UPSTAG
:caption: How-Tos
:maxdepth: 1
how_tos/environment.rst
how_tos/resources.rst
how_tos/resource_states.rst
how_tos/active_states.rst
how_tos/mimic_states.rst
Expand Down
Loading

0 comments on commit 4b1e1a0

Please sign in to comment.