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.