Thrust and Aerodynamic Orientation - Code Refactor
This page discusses the non-backwards compatible changes that were introduced in Tudatpy from version 0.7.
The framework in Tudat to calculate inertial thrust direction and aerodynamic guidance was overhauled in mid-2022. The reasons for this were:
Uninformative error warnings for certain models (primarily: custom thrust directions)
Inconsistent ways in which a vehicle’s orientation is defined (implicitly by both aerodynamic guidance and thrust guidance)
Unclear interaction between thrust and aerodynamics: both can define a body’s orientation, so what happens if you use both?
The refactor has addressed these issues, as well as a number of related points. Most of the changes in functionality have been made ‘under the hood’, and are not visble to users. However, two important aspects of the interfaces have been modified in a manner which is not backwards compatible. If a user accesses the ‘old’ function, they will be redirected to this page. Below is an overview of what has changes, why, and how to convert your old code to new code.
Specifically, you are likely here because one of two pieces of code is no longer working: the definition of a thrust acceleration, or the definition of aerodynamic guidance. Below, we describe how to modify your old code to the new setup of Tudat, while retaining the identical functionality.
Main modification - vehicle rotation
The driver behind the lack of backwards compatibility is the following new paradigm:
All methods in which to define a vehicle’s orientation (with the exception of the numerical propagation of the vehicle’s rotational dynamics) have to happen by explicitly setting the vehicle’s rotation model. This addresses the issues listed above concerning the old setup, where it was often not clear which setting overrode/interacted with which other model. Currently, a user will know exactly which rotation model is used: they have to define it explicitly. This means that
The definition of thrust direction settings, which implicitly define a body-fixed frame, through thrust acceleration settings
The definition of an
AerodynamicGuidance-derived class (which could only be created and added after the aerodynamic acceleration was created….)
are no long supported. Below, we provide a guide on how to convert code that utilizes this to the new setup.
In the older version of Tudat, the explicit definition of the aerodynamic angles of attack, sideslip angle, and bank angle (\(\alpha\), \(\beta\) and \(\sigma\)) had to be done through a user-defined class derived from the
AerodynamicGuidance-derived class. This class should then set
currentAngleOfAttack_ (and similar for other angles) values in the
updateGuidance function, which was called at each time step.
An example of the application of this is in the example application (old version). Specifically, a class was created as follows:
class STSAerodynamicGuidance(propagation.AerodynamicGuidance): def __init__(self, bodies: environment.SystemOfBodies): # Call the base class constructor propagation.AerodynamicGuidance.__init__(self) ... # Function that is called at each simulation time step to update the ideal bank angle of the vehicle def updateGuidance(self, current_time: float): self.angle_of_attack = ... self.bank_angle = ...
this class had a number of specific requirements in terms of variable/function naming, inheritance. It was linked to the environment as follows (here, linked to the body named “STS”):
# Create the aerodynamic guidance object guidance_object = STSAerodynamicGuidance(bodies) # Set aerodynamic guidance (this line links the STSAerodynamicGuidance settings with the propagation) environment_setup.set_aerodynamic_guidance(guidance_object, bodies.get_body("STS"))
which could only be done after the creation of
FlightConditions of the body “STS” (typically after an aerodynamic acceleration acting on it was created).
Not only was the above structure specific for aerodynamic orientation, it also had a number of specific ad arbitrary requirements that made it somewhat unwieldy.
set_body_orientation_angle_functions of the
tudatpy.numerical_simulation.environment.AerodynamicAngleCalculator could be called the directly to set either constant, or time-variable aerodynamic angles. This interface is no longer supported, as the
tudatpy.numerical_simulation.environment.AerodynamicAngleCalculator class is no longer responsible for calculating these angles, it only extracts them from where the are calculated: in the vehicle’s rotation model.
In the new version of the code, the definition of a body’s orientation through aerodynamic angles is allowed in one way: through the creation of a rotation model for the body. Details are discussed on a dedicated page. The above example can be ‘translated’ to the new setup as follows
class STSAerodynamicGuidance: def __init__(self, bodies: environment.SystemOfBodies): ... # Function that is called at each simulation time step to update the ideal bank angle of the vehicle def getAerodynamicAngles(self, current_time: float): self.angle_of_attack = ... self.bank_angle = ... return np.array([self.angle_of_attack, 0.0, self.bank_angle])
The main requirement on this new guidance class is the following: it should have a function that takes time as input, and returns a vector containing [\(\alpha\), \(\beta\), \(\sigma\)]
It is then linked to the environment as follows:
# Create the aerodynamic guidance object aerodynamic_guidance_object = STSAerodynamicGuidance(bodies) # Link getAerodynamicAngles function of aerodynamic_guidance_object to rotation model settings rotation_model_settings = environment_setup.rotation_model.aerodynamic_angle_based( 'Earth', '', 'STS_Fixed', aerodynamic_guidance_object.getAerodynamicAngles ) # Create rotation model from rotation model settings environment_setup.add_rotation_model( bodies, 'STS', rotation_model_settings )
The function computing the aerodynamic angles in the guidance object (
aerodynamic_guidance_object.getAerodynamicAngles) is linked to the specific rotation model settings
aerodynamic_angle_based`() which defines the rotation model we want.
The above example is discussed in detail on the entry example page. Note that in the above code snippet, we have used a slightly more simplified guidance object than in the example application. They are functionally equivalent, but the code in the example application lends itself better to incorporation into more complex guidance classes. The type of guidance class used in the code snippet and the example application are of the type Custom model (single), class member and Custom model (multiple), class member, respectively.
You may continue to use a very similar guidance class as before, but the class now requires a function that provides [\(\alpha\),:math:beta,:math:sigma], with \(t\) as a input. This function is now linked to the environment not through an aerodynamics-specific function, but through the definition of a rotation model. An example of Python code using the old and new setup is found on our examples repository here and here, respectively.
The changes to the interfaces for defining a thrust acceleration have changed significantly. However, all underlying functionality is intact, and your code can be easily changed from the old to the new version. In both versions, the definition of thrust happens through (typically) separate objects for thrust direction and thrust magnitude. What is different, is where these two pieces of information are provided. In the old version:
Both thrust direction and thrust magnitude settings were passed to the thrust acceleration settings using the
tudatpy.numerical_simulation.propagation_setup.acceleration.thrust_from_direction_and_magnitude()function. The guide below discusses how to modify your code to the new version if you are using this.
Directly defining the thrust vector using the
tudatpy.numerical_simulation.propagation_setup.acceleration.thrust_and_isp_from_custom_function()function. A new interface for this is under development.
In the new version:
Thrust magnitude settings are stored in an
EngineModelthat is assigned to the vehicle. This same object also defines the body-fixed direction of the thrust
The thrust direction is obtained by rotating the body-fixed thrust direction (see above line) to the inertial frame using a rotation model assigned to the vehicle. Every option that was previously provided as a “thrust direction settings” has been refactored into a rotation model. Below, we provide a guide with a one-to-one correspondence between each of the old thrust direction settings, and the new rotation model settings.
A detailed guide on using thrust in the new version can be found on a dedicated page on Use of thrust models. The thrust guide of the old version is also retained under a dedicated page on Use of thrust models - Legacy. Below, we provide a brief on how to convert old code to new code. n example of Python code using the old and new setup is found on our examples repository here and here, respectively.
Converting thrust code
In the old code, you typically defined a thrust acceleration through:
# Define settings for inertial thrust direction thrust_direction_settings = ... # Define settings for thrust magnitude thrust_magnitude_settings = ... # Create acceleration model settings acceleration_on_vehicle = dict( Vehicle=[ # Define the thrust acceleration from its direction and magnitude propagation_setup.acceleration.thrust_from_direction_and_magnitude( thrust_direction_settings=thrust_direction_settings, thrust_magnitude_settings=thrust_magnitude_settings, ) ], ...
In the new code, the equivalent functionality is provided through:
# Create body rotation settings and create rotation model rotation_model_settings = ... environment_setup.add_rotation_model( system_of_bodies, 'Vehicle', rotation_model_settings ) ... # Define settings for thrust magnitude thrust_magnitude_settings = ... # Create engine model with given thrust magnitude environment_setup.add_engine_model( 'Vehicle', 'MainEngine', thrust_magnitude_settings, system_of_bodies ) ... # Create acceleration model settings acceleration_on_vehicle = dict( Vehicle=[ propagation_setup.acceleration.thrust_from_engine( 'MainEngine') ],
Here, we have chosen to define the rotation model settings, and create the rotation model, after creating the
SystemOfBodies, as discussed here.
Details on the functioning of the two pieces of code can be found on the dedicated thrust pages of the new code, and the legacy code (see above). The steps to take:
Convert your thrust direction settings to rotation model settings. See below for a one-to-one correspondence betwee individual models
Using the same
thrust_magnitude_settingsas before, create an engine model using the
add_engine_model()function. In addition to containing information on the thrust magnitude, the engine object holds information on:
The name of the engine (used to link it to the acceleration, see below)
The body-fixed thrust direction (for a time-variable body-fixed thrust direction, use the
Create the thrust acceleration, by specifying which engine(s) should be used in the calculation of the thrust acceleration. Here, the name provided to the engine model is used.
Converting thrust direction settings
Each of the old thrust direction settings (in the
propagation_setup.thrust submodule) is now reformulated as a rotation model setting. In addition to the inputs from the corresponding thrust direction settings, the rotation model settings require the
base_frame (typically J2000 or ECLIPJ2000), and the
target_frame to be defined. The
target_frame is simply the identifier that the code uses for the body-fixed frame of the vehicle, and may be selected at will by the user.
propagation_setup.thrust.thrust_from_existing_body_orientation This option is now moot, by definition all thrust models now use the existing body orientation. If you were using this option, now additional action is needed.
propagation_setup.thrust.custom_thrust_orientation This option is now replaced with the equivalent
custom_rotation_model() option, with the addition of the frame identiers specified above. It defines the body-fixed orientation in a fully user-defined manner.
propagation_setup.thrust.custom_thrust_direction This option is now replaced with the equivalent
custom_inertial_direction_based() option, with the addition of the frame identiers specified above. This rotation model is very typical for the use of thrust: it imposes that a body-fixed axis (now limited to the body-fixed x-axis) is pointed along an arbitrary user-defined inertial direction. As an extension of the old code, this model now allows he full rotation to be defined, but defining the free rotation about the body-fixed x-axis (thrust) axis.
propagation_setup.thrust.thrust_direction_from_state_guidance This option is replaced by the
orbital_state_direction_based(). The inputs are equivalent, with the addition of the frame identiers specified above. This rotation model is a typical example for a basic use of thrust: it imposes that a body-fixed axis (now limited to the body-fixed x-axis) is pointed along its inertial position or velocity vector. As an extension of the old code, this model now allows he full rotation to be defined, but defining the free rotation about the body-fixed x-axis (thrust) axis.