Adding Basilisk Modules

Understanding now what a Basilisk process (Task Group) and a task is, we can now move forward to adding Basilisk modules to a task. Module: cModuleTemplate and Module: cppModuleTemplate will be the basic module C and C++ BSK modules that we use in this discussion. These simple modules are used to illustrate how to make a prototypical Basilisk module. The functionality and messages used are the same across both modules. These modules are convenient for this discussion as they are set up as a tutorial module and print out the module ID value when the module is executed. This makes it simple in the simulation below to see that the desired module execution order is achieved.

../../_images/qs-bsk-2.svg

The following simulation creates a single process called dynamicsProcess and single 0.2Hz task called dynamicsTask. As illustrated above, two copies of Module: cModuleTemplate and one of Module: cppModuleTemplate are created where they are called cModule1, cModule3 and cppModule2 respectively. However, note that in this example we seek to execute modules 2 and 3 first and 1 last.

 1
 2from Basilisk.utilities import SimulationBaseClass
 3from Basilisk.utilities import macros
 4from Basilisk.moduleTemplates import cModuleTemplate
 5from Basilisk.moduleTemplates import cppModuleTemplate
 6
 7def run():
 8    """
 9    Illustration of adding Basilisk modules to a task
10    """
11
12    #  Create a sim module as an empty container
13    scSim = SimulationBaseClass.SimBaseClass()
14
15    #  create the simulation process
16    dynProcess = scSim.CreateNewProcess("dynamicsProcess")
17
18    # create the dynamics task and specify the integration update time
19    dynProcess.addTask(scSim.CreateNewTask("dynamicsTask", macros.sec2nano(5.)))
20
21    # create copies of the Basilisk modules
22    mod1 = cModuleTemplate.cModuleTemplateConfig()
23    mod1Wrap = scSim.setModelDataWrap(mod1)
24    mod1Wrap.ModelTag = "cModule1"
25
26    mod2 = cppModuleTemplate.CppModuleTemplate()
27    mod2.ModelTag = "cppModule2"
28
29    mod3 = cModuleTemplate.cModuleTemplateConfig()
30    mod3Wrap = scSim.setModelDataWrap(mod3)
31    mod3Wrap.ModelTag = "cModule3"
32
33    scSim.AddModelToTask("dynamicsTask", mod1Wrap, mod1)
34    scSim.AddModelToTask("dynamicsTask", mod2, None, 10)
35    scSim.AddModelToTask("dynamicsTask", mod3Wrap, mod3, 5)
36
37    #  initialize Simulation:
38    scSim.InitializeSimulation()
39    print("InitializeSimulation() completed...")
40
41    #   configure a simulation stop time and execute the simulation run
42    scSim.ConfigureStopTime(macros.sec2nano(5.0))
43    scSim.ExecuteSimulation()
44
45    return
46
47
48if __name__ == "__main__":
49    run()

The resulting demonstration code is shown above. Note that the C-based Module: cModuleTemplate must be imported from Basilisk.fswAlgorithm at the top of the file.

Next we create a copy of the C-based Basilisk module. Let’s discuss this process considering a generic module name someCModule. As C code doesn’t know about class objects, in Basilisk we have to recreate some of the basic features of an object oriented language like C++. Instead of having class variables, the data of the C module is stored in the someCModuleConfig data structure. For Module: cModuleTemplate you find this module configuration structure listed at the bottom of that web page, or you can see the structure definition in the someCModule.h header file. Thus, to create a python copy of this module configuration structure use:

moduleData = someCModule.someCModuleConfig()

In the code example above this step is done on lines 22 and 29.

Next, for Basilisk to be able to execute this module, we need to wrap this module data structure with a C++ interface. These steps make it possible to have multiple C module copies be created that don’t conflict with each other. The module wrapped is done with:

moduleWrap = scSim.setModelDataWrap(moduleData)

Each Basilisk module should have a unique name. This is set using:

moduleWrap.ModelTag = "someModuleName"

In the code above you see these steps repeated two times to create two distinct copies of Module: cModuleTemplate. The next step is to add these modules to the task list to execute them in the desired order. The general command to add a C-based module to a task is:

scSim.AddModelToTask("taskName", moduleWrap, moduleData, priority)

The first argument is the name of the task to which you are adding the module. The 2nd and 3rd arguments are the module wrapper and module data variables. The last argument is the optional integer priority argument.

Warning

If priority argument is not provided, then the priority defaults to -1 and the modules are executed in the order that they are added, but after modules with priority have been executed. This is the same behavior as what we saw with processes and tasks earlier.

If the BSK module being added is a C++ module, then the above steps are simplified to the following. Let the module be called someCppModule. As this is a C++ class, we don’t need to create a data structure and wrap it as we do with a C module. Rather, we can get an instance of the C++ module using:

mod = someCppModule.SomeCppModule()
mod.ModelTag = "moduleName"

To add this to a task use:

scSim.AddModelToTask("taskName", mod, None, priority)

The 3rd argument is None as the module data is already contained in the module class. To add a module without specifying the priority you can use this shorter version as well:

scSim.AddModelToTask("taskName", mod)

In the above python script, the tutorial C++ Module: cppModuleTemplate is imported from Basilisk.simulation. Next, while the 1st and 3rd module are instances of the C module, the 2nd module is an instance of the equivalent C++ module.

Note

Basilisk assigns unique positive ID numbers to C/C++ modules upon their creation. Thus, in the above simulation code the modules 1, 2 and 3 will have the corresponding ID numbers 1, 2 and 3 because that is the order in which they are created.

Looking at the above simulation code, note that Module1 is added to the task list without any priority specified. In contrasts, Module2 and Module3 have the priorities 10 and 5 assigned. The higher the module priority, the earlier it is evaluated.

Note

The task list can contain both C and C++ based Basilisk modules at the same time.

If you execute this python code you should see the following terminal output:

source/codeSamples % python3 bsk-2.py
BSK_INFORMATION: Variable dummy set to 0.000000 in reset.
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 2 ran Update at 0.000000s
BSK_INFORMATION: C Module ID 3 ran Update at 0.000000s
BSK_INFORMATION: C Module ID 1 ran Update at 0.000000s
BSK_INFORMATION: C++ Module ID 2 ran Update at 5.000000s
BSK_INFORMATION: C Module ID 3 ran Update at 5.000000s
BSK_INFORMATION: C Module ID 1 ran Update at 5.000000s

Module: cModuleTemplate and Module: cppModuleTemplate log in the Reset() method that a variable has been set to 0. As we have three such modules, notice that this reset statement is seen three times. This reset step occurs when we run scSim.InitializeSimulation().

After the initialization, Basilisk starts the time loop evaluating the modules at the specified rate. The Update() routine in both Module: cModuleTemplate and Module: cppModuleTemplate print out the module ID and the simulation time where the module is called. Note that thanks to the module evaluation priorities we set, the desired module execution order is achieved.