Skip to content

Commit

Permalink
Adding top-level context creation. Docs for it, and some minor docs c…
Browse files Browse the repository at this point in the history
…hanges for qol.
  • Loading branch information
JamesArruda committed Dec 11, 2024
1 parent ecd7b53 commit 3651b5a
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ celerybeat-schedule
.env
.report.json
.venv
.scripts

# VSCode
.vscode
Expand All @@ -123,3 +124,7 @@ tmp/
# Temporary Reporting Artifacts
classes.png
packages.png


# Built autodocs
docs/source/auto
2 changes: 1 addition & 1 deletion docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,5 @@ The API documentation is auto-generated.
:caption: API
:maxdepth: 2
modules.rst
auto/modules.rst
```
7 changes: 0 additions & 7 deletions docs/source/modules.rst

This file was deleted.

14 changes: 7 additions & 7 deletions docs/source/user_guide/how_tos/geography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Geographic Data Types and State
These are the built-in features that use geography:

:py:class:`~upstage_des.data_types.GeodeticLocation`
------------------------------------------------
----------------------------------------------------

This data type stores a Latitude / Longitude / Altitude (optional) for a point around the globe.

Expand Down Expand Up @@ -108,7 +108,7 @@ 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
the state's value. Here is its basic usage:
Expand Down Expand Up @@ -156,7 +156,7 @@ Cartesian Locations
These aren't geographic, but they serve the same purpose, so we include them here.

:py:class:`~upstage_des.data_types.CartesianLocation`
-------------------------------------------------
-----------------------------------------------------

This data type stores an X / Y / Z (optional) location in 2 or 3D space (z is set to zero if not included).

Expand Down Expand Up @@ -190,7 +190,7 @@ 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.

Expand All @@ -201,7 +201,7 @@ Geography Sub-Module
The :py:mod:`upstage_des.geography` module contains:

:py:class:`~upstage_des.geography.spherical.Spherical`
--------------------------------------------------
------------------------------------------------------

This class contains methods for finding distances, positions, and for segmenting great-circle paths on the assumption of a spherical earth.

Expand All @@ -214,7 +214,7 @@ The most useful methods, besides distance, may be:
#. :py:meth:`~upstage_des.geography.spherical.Spherical.point_from_bearing_dist`, which gives you a point relative to a base location at some distance and bearing.

:py:class:`~upstage_des.geography.wgs84.WGS84`
-------------------------------------------
----------------------------------------------

This class contains methods for finding distances, positions, and for segmenting great-circle paths on the assumption of a WGS84 ellipsoid. These methods take longer to run than the Spherical version,
so be sure the extra accuracy is worth it.
Expand All @@ -228,7 +228,7 @@ The most useful methods, besides distance, may be:
#. :py:meth:`~upstage_des.geography.spherical.WGS84.point_from_bearing_dist`, which gives you a point relative to a base location at some distance and bearing.

:py:mod:`upstage_des.geography.intersections`
-------------------------------------------
---------------------------------------------

The :py:func:`~upstage_des.geography.intersections.get_intersection_locations` function calculates an intersection between a great circle path and a sphere. It can be passed an instance of ``Spherical`` or ``WGS84``
to do distance calculations with.
Expand Down
29 changes: 27 additions & 2 deletions docs/source/user_guide/how_tos/stage_variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ SimPy or UPSTAGE tasks, states, or processes.
Expected Stage Variables
===========================
=========================

Some variables are expected to exist on the stage for some features. These are found in the :py:class:`~upstage_des.base.StageProtocol` protocol,
and are listed below:
Expand Down Expand Up @@ -69,7 +69,7 @@ actors and tasks. The following snippets shows how you might use it for pure Sim
Accessing Stage through upstage_des.api
===================================
=======================================

For convenience, you can also do the following:

Expand All @@ -84,3 +84,28 @@ For convenience, you can also do the following:
assert stage.altitude_units == "centimeters"
altitude_units = UP.get_stage_variable("altitude_units")
assert altitude_units == "centimeters"
Accessing Stage outside of the EnvironmentContext
=================================================

There are some times when you may want the Stage to exist outside of the EnvironmentContext. When doing plotting of
geographic entities, for example, having access to the ``stage_model`` is useful. This is also helpful when visualizing
or doing analysis in Jupyter Notebooks, where you don't want to sit inside a context manager.

For this situation, UPSTAGE provides a way to operate the context manager without needing to be inside the context.

.. code-block:: python
import upstage_des.api as UP
from upstage_des.base import create_top_context, clear_top_context
ctx = create_top_context()
add_stage_variable("example", 1.234)
assert get_stage_variable("example") == 1.234
clear_top_context(ctx)
The two functions are just wrappers around the context manager's ``__enter__`` and ``__exit__`` methods, but they provide a clearer
idea of what's being done and why.
26 changes: 26 additions & 0 deletions src/upstage_des/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,29 @@ def get_stage() -> StageProtocol:
except LookupError:
raise ValueError("Stage should have been set.")
return stage


def create_top_context(
initial_time: float = 0.0,
random_seed: int | None = None,
random_gen: Any | None = None,
) -> EnvironmentContext:
"""Create a stage at this level of context.
Makes your current level the same as the context manager.
Returns:
EnvironmentContext: The context
"""
ctx = EnvironmentContext(initial_time, random_seed, random_gen)
ctx.__enter__()
return ctx


def clear_top_context(ctx: EnvironmentContext) -> None:
"""Clear the context.
Args:
ctx (EnvironmentContext): The object made from create_stage()
"""
ctx.__exit__()
25 changes: 25 additions & 0 deletions src/upstage_des/test/test_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
UpstageBase,
UpstageError,
add_stage_variable,
get_stage_variable,
)
from upstage_des.base import clear_top_context, create_top_context


class Stager(UpstageBase): ...
Expand Down Expand Up @@ -38,3 +40,26 @@ def test_stage() -> None:
# After the context, it should not exists
with pytest.raises(UpstageError):
source.stage


def test_contextless_stage() -> None:
ctx = create_top_context()
add_stage_variable("example", 1.234)

assert get_stage_variable("example") == 1.234

# dropping into a new context ignores the above
with EnvironmentContext():
add_stage_variable("example", 8.675)
assert get_stage_variable("example") == 8.675

assert get_stage_variable("example") == 1.234

clear_top_context(ctx)

with pytest.raises(ValueError, match="Stage should have been set."):
get_stage_variable("example")


if __name__ == "__main__":
test_contextless_stage()

0 comments on commit 3651b5a

Please sign in to comment.