Universe dynamics model evaluation example

In this tutorial we will set up different dynamics model and evaluate them on a spacecraft.

First, load all the godot modules we will need:

In [1]:
import numpy as np

from godot.core import tempo, util, autodif as ad
from godot.model import common
from godot import cosmos

Load the universe configuration and create the universe object.

In [2]:
# avoid verbose output
util.suppressLogger()

uni_config = cosmos.util.load_yaml("universe.yml")
uni = cosmos.Universe(uni_config)

We need to add a model that will store the spacecraft (or test point) state vector w.r.t. an arbitrary point in the frame system. We can change the value of the model as it is constant, and do evaluations.

In [3]:
translation_model = common.ConstantVectorTimeEvaluable(np.zeros(6))
print(translation_model.eval(tempo.Epoch()))

translation_model.set([1, 2, 3, 4, 5, 6])
print(translation_model.eval(tempo.Epoch()))
[0. 0. 0. 0. 0. 0.]
[1. 2. 3. 4. 5. 6.]

We can use this model to add a new point in the frame system that is translated from Earth along the ICRF axes.

In [4]:
uni.frames.addPoint("Spacecraft", tempo.TimeScale.TDB)
uni.frames.addTimeEvaluableTranslation("Spacecraft", "Earth", "ICRF", translation_model)

Similarly to before, we can use the frame system to evaluate the state vector w.r.t. any frame system point and along an arbitrary axes.

In [5]:
print(uni.frames.vector6("Earth", "Spacecraft", "ICRF", tempo.Epoch()))
translation_model.set([6, 5, 4, 3, 2, 1])
print(uni.frames.vector6("Earth", "Spacecraft", "ICRF", tempo.Epoch()))
[1. 2. 3. 4. 5. 6.]
[6. 5. 4. 3. 2. 1.]

We can retrieve the dynamics models from the universe, but evaluation is not psosible at the moment because the dynamics model does not know about the test point the evaluation should use.

In [6]:
dyn = uni.dynamics.get("EarthPointMass")
try:
    dyn.acc.eval(tempo.Epoch())
except RuntimeError as e:
    print(e)
Exception raised in GODOT: 
What : Post-condition check failed: err == 0. Help message: Could not connect Point EarthPointMass_point to Point Earth at 2000-01-01T00:00:00.000000 TDB
Func : setupPath
Src  : /builds/godot/godotpy/godot/godot/model/frames/Frames.h
Line : 692

We need to alias the spacecraft point such that the dynamics model will use that as test point (with zero mass).

In [7]:
uni.frames.setAlias(dyn.point, uni.frames.pointId("Spacecraft"))
print(dyn.acc.eval(tempo.Epoch()))
translation_model.set([7000.0, 0, 0, 0, 0, 0])
print(dyn.acc.eval(tempo.Epoch()))
[-3539.58977138 -2949.65814282 -2359.72651425]
[-0.0081347  0.         0.       ]

We can similarly interate over the available dynamics models and evaluate different ones after making sure the frame point aliasing is done before each model evaluation. Here we also need to alias the evaluation axes for the combined dynamics model.

In [8]:
model_names = ["EarthPointMass", "EarthJ2", "EMS", "EMS_and_SRP"]
for name in model_names:
    dyn = uni.dynamics.get(name)
    uni.frames.setAlias(dyn.point, uni.frames.pointId("Spacecraft"))
    uni.frames.setAlias(dyn.axes, uni.frames.axesId("ICRF"))
    print(name, dyn.acc.eval(tempo.Epoch()))
EarthPointMass [-0.0081347  0.         0.       ]
EarthJ2 [-8.14567027e-03 -1.66055412e-14  5.93966596e-10]
EMS [-8.14567007e-03  5.93556096e-10  7.28451175e-10]
EMS_and_SRP [-8.14567008e-03  6.36184600e-10  7.46932679e-10]

In case the user needs to compute the partials derivatives w.r.t the state of the spacecraft, it can be done by using the eval method with an XEpoch.

In [9]:
print(translation_model.eval(tempo.XEpoch()))
         Value
  7.000000e+03
  0.000000e+00
  0.000000e+00
  0.000000e+00
  0.000000e+00
  0.000000e+00

To actually get partial derivatives, the model needs to be set to use autodif variable.

In [10]:
state = [7000.0, 0, 0, 0, 0, 0]
x = ad.Vector(state, "x")
translation_model.set(x)
print(translation_model.eval(tempo.XEpoch()))
         Value |          x[0]          x[1]          x[2]          x[3]          x[4]          x[5]
  7.000000e+03 |  1.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00  1.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00  0.000000e+00  1.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00  0.000000e+00  0.000000e+00  1.000000e+00  0.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  1.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  1.000000e+00

Then, we can use the dynamics model the same way as before, with XEpoch and get the partials.

In [11]:
dyn = uni.dynamics.get("EarthPointMass")
uni.frames.setAlias(dyn.point, uni.frames.pointId("Spacecraft"))
a = dyn.acc.eval(tempo.XEpoch())
print(a)
         Value |          x[0]          x[1]          x[2]          x[3]          x[4]          x[5]
 -8.134703e-03 |  2.324201e-06  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00 -1.162100e-06  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00
  0.000000e+00 |  0.000000e+00  0.000000e+00 -1.162100e-06  0.000000e+00  0.000000e+00  0.000000e+00

Gradient matrix can be retrieved to get a numpy array.

In [12]:
np.set_printoptions(linewidth=100)
print(a.gradient()["x"])
[[ 2.32420079e-06  0.00000000e+00  0.00000000e+00  0.00000000e+00  0.00000000e+00  0.00000000e+00]
 [ 0.00000000e+00 -1.16210039e-06  0.00000000e+00  0.00000000e+00  0.00000000e+00  0.00000000e+00]
 [ 0.00000000e+00  0.00000000e+00 -1.16210039e-06  0.00000000e+00  0.00000000e+00  0.00000000e+00]]
In [ ]: