-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added documentation for the new Python modules
- Loading branch information
1 parent
03e72e9
commit 3ceca94
Showing
2 changed files
with
211 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,101 @@ | ||
Making Python Modules | ||
===================== | ||
|
||
To learn how to program a Python module, see :ref:`scenarioAttitudePointingPy`. The module is defined purely in a python script. As such there is no header, definition or swig interface file. | ||
.. sidebar:: Source Code | ||
|
||
.. note:: | ||
The Python code shown below can be downloaded :download:`here </../../docs/source/codeSamples/making-pyModules.py>`. | ||
|
||
Python modules run much slower than C or C++ modules. They are very convenient when prototyping a module behavior, or having a simple task to perform that is not called very often. | ||
Python modules are are a good alternative to C and C++ modules for quick prototyping. | ||
They are defined entirely in a Python script, which means that there is no need | ||
for a header (``.h``), definition (``.cpp``), or SWIG interface file (``.i``). However, they | ||
are much slower than C or C++ modules, which will significantly slow down your simulation. | ||
|
||
.. warning:: | ||
Python modules are implemented by subclassing ``SysModel`` from ``Basilisk.architecture.sysModel``. | ||
Then, one can implement the ``__init__``, | ||
``Reset``, and ``UpdateState`` methods in the same way that one would | ||
implement these methods in C++. Remember to always call ``__init__`` of | ||
the parent class ``SysModel`` if you are implementing your own ``__init__``. | ||
|
||
The python process(es) will always be executed after the regular C/C++ processes. | ||
All Python modules have a logger stored in ``bskLogger`` (although it will | ||
not be available until the module has been added to a simulation). Additionally, | ||
you may declare any other variables, methods, messages... within your Python module. | ||
|
||
The script below expands on the code shown in :ref:`bskPrinciples-2` to include | ||
a Python module. | ||
|
||
.. literalinclude:: ../../codeSamples/making-pyModules.py | ||
:language: python | ||
:linenos: | ||
:lines: 18- | ||
|
||
Running the above code prints: | ||
|
||
.. code-block:: | ||
(.venv) source/codeSamples % python making-pyModules.py | ||
BSK_INFORMATION: Variable dummy set to 0.000000 in reset. | ||
BSK_INFORMATION: Reset in TestPythonModule | ||
BSK_INFORMATION: Variable dummy set to 0.000000 in reset. | ||
BSK_INFORMATION: Variable dummy set to 0.000000 in reset. | ||
InitializeSimulation() completed... | ||
BSK_INFORMATION: C Module ID 3 ran Update at 0.000000s | ||
BSK_INFORMATION: Python Module ID 4 ran Update at 0.0s | ||
BSK_INFORMATION: C++ Module ID 2 ran Update at 0.000000s | ||
BSK_INFORMATION: C Module ID 1 ran Update at 0.000000s | ||
BSK_INFORMATION: C Module ID 3 ran Update at 5.000000s | ||
BSK_INFORMATION: Python Module ID 4 ran Update at 5.0s | ||
BSK_INFORMATION: C++ Module ID 2 ran Update at 5.000000s | ||
BSK_INFORMATION: C Module ID 1 ran Update at 5.000000s | ||
Recorded mod2.dataOutMsg.dataVector: [[2. 1. 0.] | ||
[5. 2. 0.]] | ||
Note how the Python module made use of ``bskLogger``, the ``Reset`` | ||
and ``UpdateState`` were called, how the priority of the Python | ||
module was respected, and how messaging happened between a C++ | ||
and Python module. | ||
|
||
The scenario :ref:`scenarioAttitudePointingPy` further shows how to define Python modules. | ||
|
||
Deprecated way of creating Python modules | ||
----------------------------------------- | ||
.. warning:: | ||
|
||
This section discusses the deprecated way of setting up Python modules. | ||
Users should refer to the previous section when setting up new simulation | ||
scripts using Python modules. | ||
|
||
Apart from the way to shown above, there exist an older way to create | ||
Python modules which has now been deprecated. This section briefly | ||
discusses this older method, its disadvantages, and how to update to the | ||
new system. Note that this deprecated method is pending for removal; | ||
users are advised to update to the new system. | ||
|
||
Before the new system, Python modules had to be added to separate | ||
Python processes, which could not have C/C++ modules. Moreover, this | ||
Python processes always had to have a lower priority than C++/C | ||
processes, which effectively meant that all Python modules would run | ||
after the C++/C modules. This severely limited users' control of the | ||
execution order of their simulation. | ||
|
||
Moreover, the syntax for creating these modules was slightly different | ||
than for C++ modules, which was unintuitive: | ||
- The class inherited from ``PythonModelClass`` instead of ``SysModel`` | ||
- The ``ModelTag`` had to be passed to the constructor of the class | ||
- One had to overload ``reset`` and ``updateState``, instead of ``Reset`` and ``UpdateState`` | ||
|
||
In order to update code that uses the deprecated system to the | ||
new system, one needs to: | ||
- Replace ``CreateNewPythonProcess`` by ``CreateNewProcess``, as the new Python modules can be added to regular processes. | ||
- Make the Python module class inherit from ``SysModel``, and not from ``PythonModelClass`` (note that you must import ``SysModel`` from ``Basilisk.architecture.sysModel``). | ||
- Instead of passing the module tag and priority in the constructor, set the tag by setting the ``ModuleTag`` attribute (similar to C++ modules), and set the priority on the ``addTask`` method. | ||
- Rename ``selfInit``, ``reset``, and ``updateState`` to ``SelftInit``, ``Reset``, and ``UpdateState``. | ||
|
||
It is possible that you may not even need a separate process | ||
for your Python modules, so consider adding the Python modules directly | ||
to other existing processes (always with a lower priority if you want | ||
to retain the older behaviour). | ||
|
||
Be aware that in the new system, C++, C, and Python modules all have a | ||
unique positive module IDs. This means that updating your simulation script | ||
might change the ID of your modules compared to previous versions. | ||
|
||
The scenario :ref:`scenarioAttitudePointingPyDEPRECATED` shows both the | ||
deprecated way of creating a Python module. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# | ||
# ISC License | ||
# | ||
# Copyright (c) 2021, Autonomous Vehicle Systems Lab, University of Colorado at Boulder | ||
# | ||
# Permission to use, copy, modify, and/or distribute this software for any | ||
# purpose with or without fee is hereby granted, provided that the above | ||
# copyright notice and this permission notice appear in all copies. | ||
# | ||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
# | ||
|
||
from Basilisk.utilities import SimulationBaseClass | ||
from Basilisk.utilities import macros | ||
from Basilisk.moduleTemplates import cModuleTemplate | ||
from Basilisk.moduleTemplates import cppModuleTemplate | ||
from Basilisk.architecture import sysModel | ||
from Basilisk.architecture import bskLogging | ||
from Basilisk.architecture import messaging | ||
|
||
import numpy as np | ||
|
||
def run(): | ||
""" | ||
Illustration of adding Basilisk Python modules to a task | ||
""" | ||
|
||
# Create a sim module as an empty container | ||
scSim = SimulationBaseClass.SimBaseClass() | ||
|
||
# create the simulation process | ||
dynProcess = scSim.CreateNewProcess("dynamicsProcess") | ||
|
||
# create the dynamics task and specify the integration update time | ||
dynProcess.addTask(scSim.CreateNewTask("dynamicsTask", macros.sec2nano(5.))) | ||
|
||
# create copies of the Basilisk modules | ||
mod1 = cModuleTemplate.cModuleTemplateConfig() | ||
mod1Wrap = scSim.setModelDataWrap(mod1) | ||
mod1Wrap.ModelTag = "cModule1" | ||
scSim.AddModelToTask("dynamicsTask", mod1Wrap, mod1, 0) | ||
|
||
mod2 = cppModuleTemplate.CppModuleTemplate() | ||
mod2.ModelTag = "cppModule2" | ||
scSim.AddModelToTask("dynamicsTask", mod2, None, 5) | ||
|
||
mod3 = cModuleTemplate.cModuleTemplateConfig() | ||
mod3Wrap = scSim.setModelDataWrap(mod3) | ||
mod3Wrap.ModelTag = "cModule3" | ||
scSim.AddModelToTask("dynamicsTask", mod3Wrap, mod3, 15) | ||
|
||
# The following is a Python module, which has a higher priority | ||
# then some of the C++/C modules. Observe in the script output | ||
# how the Python module is called in the order that respects | ||
# its priority with respect to the rest of the modules. | ||
mod4 = TestPythonModule() | ||
mod4.ModelTag = "pythonModule4" | ||
scSim.AddModelToTask("dynamicsTask", mod4, None, 10) | ||
|
||
mod2.dataInMsg.subscribeTo(mod4.dataOutMsg) | ||
mod4.dataInMsg.subscribeTo(mod3.dataOutMsg) | ||
|
||
# Set up recording | ||
mod2MsgRecorder = mod2.dataOutMsg.recorder() | ||
scSim.AddModelToTask("dynamicsTask", mod2MsgRecorder) | ||
|
||
# initialize Simulation: | ||
scSim.InitializeSimulation() | ||
print("InitializeSimulation() completed...") | ||
|
||
# configure a simulation stop time and execute the simulation run | ||
scSim.ConfigureStopTime(macros.sec2nano(5.0)) | ||
scSim.ExecuteSimulation() | ||
|
||
print("Recorded mod2.dataOutMsg.dataVector: ", mod2MsgRecorder.dataVector) | ||
|
||
return | ||
|
||
class TestPythonModule(sysModel.SysModel): | ||
|
||
def __init__(self, *args): | ||
super().__init__(*args) | ||
self.dataInMsg = messaging.CModuleTemplateMsgReader() | ||
self.dataOutMsg = messaging.CModuleTemplateMsg() | ||
|
||
def Reset(self, CurrentSimNanos): | ||
# Ensure that self.dataInMsg is linked | ||
if not self.dataInMsg.isLinked(): | ||
self.bskLogger.bskLog(bskLogging.BSK_ERROR, "TestPythonModule.dataInMsg is not linked.") | ||
|
||
# Initialiazing self.dataOutMsg | ||
payload = self.dataOutMsg.zeroMsgPayload | ||
payload.dataVector = np.array([0,0,0]) | ||
self.dataOutMsg.write(payload, CurrentSimNanos, self.moduleID) | ||
|
||
self.bskLogger.bskLog(bskLogging.BSK_INFORMATION, "Reset in TestPythonModule") | ||
|
||
def UpdateState(self, CurrentSimNanos): | ||
# Read input message | ||
inPayload = self.dataInMsg() | ||
inputVector = inPayload.dataVector | ||
|
||
# Set output message | ||
payload = self.dataOutMsg.zeroMsgPayload | ||
payload.dataVector = self.dataOutMsg.read().dataVector + np.array([0,1,0]) + inputVector | ||
self.dataOutMsg.write(payload, CurrentSimNanos, self.moduleID) | ||
|
||
self.bskLogger.bskLog(bskLogging.BSK_INFORMATION, f"Python Module ID {self.moduleID} ran Update at {CurrentSimNanos*1e-9}s") | ||
|
||
if __name__ == "__main__": | ||
run() |