Making Python Modules

Python modules 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.

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 ModelTag value of these python BSK modules will be a unique positive number, same as with C/C++ BSK modules.

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, etc. within your Python module.

The script below expands on the code shown in Adding Basilisk Modules to include a Python module.

  1
  2from Basilisk.utilities import SimulationBaseClass
  3from Basilisk.utilities import macros
  4from Basilisk.moduleTemplates import cModuleTemplate
  5from Basilisk.moduleTemplates import cppModuleTemplate
  6from Basilisk.architecture import sysModel
  7from Basilisk.architecture import bskLogging
  8from Basilisk.architecture import messaging
  9
 10import numpy as np
 11
 12
 13def run():
 14    """
 15    Illustration of adding Basilisk Python modules to a task
 16    """
 17
 18    #  Create a sim module as an empty container
 19    scSim = SimulationBaseClass.SimBaseClass()
 20
 21    #  create the simulation process
 22    dynProcess = scSim.CreateNewProcess("dynamicsProcess")
 23
 24    # create the dynamics task and specify the integration update time
 25    dynProcess.addTask(scSim.CreateNewTask("dynamicsTask", macros.sec2nano(5.0)))
 26
 27    # create copies of the Basilisk modules
 28    mod1 = cModuleTemplate.cModuleTemplate()
 29    mod1.ModelTag = "cModule1"
 30    scSim.AddModelToTask("dynamicsTask", mod1, 0)
 31
 32    mod2 = cppModuleTemplate.CppModuleTemplate()
 33    mod2.ModelTag = "cppModule2"
 34    scSim.AddModelToTask("dynamicsTask", mod2, 5)
 35
 36    mod3 = cModuleTemplate.cModuleTemplate()
 37    mod3.ModelTag = "cModule3"
 38    scSim.AddModelToTask("dynamicsTask", mod3, 15)
 39
 40    # The following is a Python module, which has a higher priority
 41    # then some of the C++/C modules. Observe in the script output
 42    # how the Python module is called in the order that respects
 43    # its priority with respect to the rest of the modules.
 44    mod4 = TestPythonModule()
 45    mod4.ModelTag = "pythonModule4"
 46    scSim.AddModelToTask("dynamicsTask", mod4, 10)
 47
 48    mod2.dataInMsg.subscribeTo(mod4.dataOutMsg)
 49    mod4.dataInMsg.subscribeTo(mod3.dataOutMsg)
 50
 51    # Set up recording
 52    mod2MsgRecorder = mod2.dataOutMsg.recorder()
 53    scSim.AddModelToTask("dynamicsTask", mod2MsgRecorder)
 54
 55    #  initialize Simulation:
 56    scSim.InitializeSimulation()
 57    print("InitializeSimulation() completed...")
 58
 59    #   configure a simulation stop time and execute the simulation run
 60    scSim.ConfigureStopTime(macros.sec2nano(5.0))
 61    scSim.ExecuteSimulation()
 62
 63    print("Recorded mod2.dataOutMsg.dataVector: ", mod2MsgRecorder.dataVector)
 64
 65    return
 66
 67
 68class TestPythonModule(sysModel.SysModel):
 69    def __init__(self, *args):
 70        super().__init__(*args)
 71        self.dataInMsg = messaging.CModuleTemplateMsgReader()
 72        self.dataOutMsg = messaging.CModuleTemplateMsg()
 73
 74    def Reset(self, CurrentSimNanos):
 75        # Ensure that self.dataInMsg is linked
 76        if not self.dataInMsg.isLinked():
 77            self.bskLogger.bskLog(
 78                bskLogging.BSK_ERROR, "TestPythonModule.dataInMsg is not linked."
 79            )
 80
 81        # Initialiazing self.dataOutMsg
 82        payload = self.dataOutMsg.zeroMsgPayload
 83        payload.dataVector = np.array([0, 0, 0])
 84        self.dataOutMsg.write(payload, CurrentSimNanos, self.moduleID)
 85
 86        self.bskLogger.bskLog(bskLogging.BSK_INFORMATION, "Reset in TestPythonModule")
 87
 88    def UpdateState(self, CurrentSimNanos):
 89        # Read input message
 90        inPayload = self.dataInMsg()
 91        inputVector = inPayload.dataVector
 92
 93        # Set output message
 94        payload = self.dataOutMsg.zeroMsgPayload
 95        payload.dataVector = (
 96            self.dataOutMsg.read().dataVector + np.array([0, 1, 0]) + inputVector
 97        )
 98        self.dataOutMsg.write(payload, CurrentSimNanos, self.moduleID)
 99
100        self.bskLogger.bskLog(
101            bskLogging.BSK_INFORMATION,
102            f"Python Module ID {self.moduleID} ran Update at {CurrentSimNanos*1e-9}s",
103        )
104
105
106if __name__ == "__main__":
107    run()

Running the above code prints:

(.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 scenarioAttitudePointingPy further shows how to define Python modules.