The Python API zendir
can be used for creating dynamic simulations using cloud infrastructure. There are specific endpoints for creating components that are exposed via the Python module. This document will cover how to create objects in the simulation, including children, behaviors and models. This uses the runner
system from the Creating Simulations guide, assuming that all code is found within an async function.
Adding Objects
There are four kinds of components in Zendir’s simulations. They are the Universe Object, the Universe Behaviour, the Universe Model and the Universe System. Each class available within Zendir’s architecture inherits from one of the four different types.
- Universe Objects: Typical components that have a visual transform (position and rotation) as well as as mass. These include spacecraft, ground stations, thrusters, solar panels and so on.
- Universe Behaviour: Typically just logic that can run in the simulation, but doesn’t have a physical presence. These include flight software, as they can’t be represented in 3D space.
- Universe Model: These are extensions to the universe objects that add additional functionality. These include atmosphere models and error models for objects.
- Universe System: Typically reserved but a singleton object that always exists in the simulation, such as the solar system or the telemetry system for handling data connections.
To add an object to the simulation, such as a Spacecraft, the add_object
function can be called on the simulation.
spacecraft: Object = await simulation.add_object("Spacecraft")
The first parameter in the add_object
function is the Zendir class name. For example, to create a Ground Station, the code would be:
ground_station: Object = await simulation.add_object("GroundStation")
Any objects added to the simulation directly are top-level objects with no parents. This is usually reserved for the planets, spacecraft, ground stations and ground objects. Following the class name, which is the only required parameter, additional parameters can be optionally provided into the function call. For example, in the ground station, the following could be added on initialization:
ground_station: Object = await simulation.add_object(
"GroundStation", Latitude=10.0, Longitude=170.0
)
These parameters for each associated class can be found inside of the API Reference documentation pages. For example, the ground station properties can be found in the Ground Station API Reference document, just as all other available classes can be found.
Adding Children
Just as objects can be added to the simulation using the add_object
method without a parent, using a object, children can be added. This will create a new object of a specified class as a child object to the chosen object, using the add_child
reference. This will ensure that the positions of those objects are directly moved with the parent object when changed, as though it is attached. By default, the child object is attached to the origin for its rotation and position relative to the parent object.
spacecraft: Object = await simulation.add_object("Spacecraft")
battery: Object = await spacecraft.add_child("Battery")
Similar to the root object function, any number of parameters can be added to the children objects based on the API Reference documentation. For example, the battery properties can be found in the Battery API Reference page.
battery: Object = await spacecraft.add_child(
"Battery", ChargeFraction=0.5, NominalCapacity=100.0
)
There is no limit to how far down the hierarchy children can be added. For example, the following is valid:
child_a = await spacecraft.add_child("PhysicalObject")
child_b = await child_a.add_child("PhysicalObject")
child_c = await child_b.add_child("PhysicalObject")
Adding Behaviors
Typically, the most common use-cases of behaviors are for flight software. Universe behaviors are components that have no 3D or visual representation and cannot have children or objects attached to them. They can be attached to either the root of the simulation or, more commonly, objects. For example, adding some flight software such as a Sun Safe Pointing Software could be done using the add_behaviour
method on the Computer object.
computer: Object = await spacecraft.add_child("Computer")
software: Behaviour = await computer.add_behaviour("SunSafePointingSoftware")
Just like with objects, adding behaviors also enable initializing parameters associated with that component. These are also found in the API Reference documentation on what properties are available with that class.
software: Behaviour = await computer.add_behaviour(
"SunSafePointingSoftware", SmallAngle=0.01, SunBodyVector=[0, 1, 0]
)
NOTE
Behaviors cannot have children, behaviors or models attached to them. Treat behaviors as logic that exists on an object but does not have a 3D structure or representation.
Adding Models
Models provide extension functionality to existing Universe Object classes, such as error model or functionality for interfacing with the universe. They cannot be added to behaviors or have any objects, behaviors or models as children. As an example, there exists a Solar Panel Degradation Error Model which slowly degrades the solar panel by a fixed percentage per year in simulation time. This could be used to model a degraded solar panel. To add this model, the get_model
function can be called on the object.
solar_panel: Object = await spacecraft.add_child("SolarPanel")
error_model: Model = await solar_panel.get_model("SolarPanelDegradationErrorModel")
Unlike objects which can have multiple children of the same class as objects, it is not possible to add multiple of the same model class to the same object instance. Calling get_model
with the same class name on the same object will return back the same model instance.
model_a = await solar_panel.get_model("SolarPanelDegradationErrorModel")
model_b = await solar_panel.get_model("SolarPanelDegradationErrorModel")
model_a == model_b
>> True
Adding Systems
A universe system is an object that can only exist once per simulation. It always exists and is reserved for classes that act as singleton structures, in that any object within the simulation can access them. An example is the Solar System, which can be used for fetching planets and celestial data. To fetch the systems, this can be done on the simulation object using the get_system
function.
solar_system: System = await simulation.get_system("SolarSystem")
A common use case is to initialize the solar system with an epoch as a datetime. This can be done using the Epoch
variable, which takes in a date-time structure.
from datetime import datetime
solar_system: System = await simulation.get_system(
"SolarSystem", Epoch=datetime(2025, 8, 19)
)