Click or drag to resize

Using DME Component Libraries with Python

The Python package Pythonnet supports integrating Python with .NET libraries. This means that DME Component Libraries can be run using Python. This topic will show you how to setup and use DME Component Libraries from a Python IDE.

Why Use Python with DME Component Libraries?

By integrating DME Component Libraries with Python, you have full access to .NET libraries, DME Component Libraries and Python modules within one program. Python is a readable, easy to use language, which translates when using Pythonnet to interface with .NET languages (in our case, C#). Pythonnet makes Python look like C# and vice versa, so programmers who are experienced in either language won't have a large learning curve. Since interfacing with C# classes is simplified, the Python code still looks like C#. Pythonnet interfaces both the .NET and Python virtual machines at a native level which allows direct memory access between Python and .NET. This means that you will be able to use all the capabilities of DME Component Libraries with the data analysis and visualization tools native to Python.

Prerequisites

You must have the following prerequisites met to integrate DME Component Libraries with Python:

  • A valid DME Component Libraries license

  • Pythonnet, version 3.0.0 or later, installed

  • Python, version 3.10.2 or later, installed

Getting Started

When you start your Python program, you will need to add a system path to your .NET assemblies, load appropriate classes, and load your DME Component Libraries license before starting your analysis. This section will show you the basics of setting up and using Pythonnet with DME Component Libraries.

Appending Path to .NET Assemblies

The following code snippet demonstrates how to import the right packages for Pythonnet and to append the path to access .NET assemblies. First import both the Pythonnet clr package (which we will show how to use in the next subsection) and the sys module, which allows us to manipulate different parts of the Python runtime environment. We then can utilize the append() function to add the folder containing the .NET assemblies (including all .dll files) to our sys.path, which will allow us to then load these assemblies and their namespaces and classes, shown in next section.

Python
import clr, sys

# assemblyFolderFilePath is the complete file path containing the DME Component Libraries .NET assemblies

sys.path.append(assemblyFolderFilePath)

Importing Packages and Classes

To first load individual .NET assemblies (from something like DME Component Libraries), we need to utilize the clr.AddReference() function in the clr module. You can then essentially treat the DME Component Libraries namespaces (belonging to a respective assembly) as packages and import classes from these namespaces. This process can be followed using the general format of from [.NET package/namespace] import [.NET class]. For a given class, the package will be listed on its information page. For example, the Cartesian class is in the agi.foundation.coordinates package/namespace. The following code provides an example of using these tools to create a Duration object and store its Days component in a variable.

Python
clr.AddReference("AGI.Foundation.Core")

#AGI.Foundation.Time is part of the AGI.Foundation.Core assembly

from AGI.Foundation.Time import *

#Both 'Duration' and 'TimeStandard' are in the AGI.Foundation.Time namespace

duration = Duration(5, 0.0, TimeStandard.CoordinatedUniversalTime)
days = duration.Days

Configuring the DME Component Libraries License

You also need to load the DME Component Libraries license prior to doing your analysis. The following code snippet shows you how to add your license to your Python project.

Python
#We need to load the AGI.Foundation.Core assembly, then import Licensing from AGI.Foundation namespace

clr.AddReference("AGI.Foundation.Core")
from AGI.Foundation import Licensing


#licensePath is the complete file path of the DME Component Libraries license

with open(licensePath) as f:
    licenseFile = f.read()

Licensing.ActivateLicense(licenseFile)
Creating Delegates

There are multiple classes throughout DME Component Libraries that make use of creating a Delegate. While programming in C#, you would be able to create/declare a delegate using the delegate keyword, then passing in a method with the same signature to create an instance of the delegate.

In Python, Pythonnet has made it simple to use delegates (simply create a Python method and pass it into the delegate). The following code snippet demonstrates how to do so, using the SetVariableCallBack delegate found in DME Component Libraries.

Python
#Creating method to pass into delegate
def setX(currentValue, configurationToModify):
    configurationToModify.Maneuver.X = configurationToModify.Maneuver.X + currentValue

#Passing method into delegate
deltaV1VelocityXVariable = deltaV1.CreateVariable(200.0, 0.1, SetVariableCallback[ImpulsiveManeuverSegmentConfiguration](setX))
Debugging

Inevitably, you will have to do some debugging. This section will address some notes and hints that might be helpful.

Function Errors in Python

There can be instances where we run into situations where the return type of method(s) in DME Component Libraries does not correspond or simply work with types in Python, which could lead to some unexpected errors. In the code fragment below, we create a variable called fuel that holds an 'IList' of floats. This collection of objects varies from the typical 'List' in Python, and we can see how it leads to a 'ValueError' later on when using other packages to plot data.

Python
fuelStateOverMission = missionResults.GetDateMotionCollectionOfOverallTrajectory[float](fuelMassName)
fuel = fuelStateOverMission.Values # [kg]

#later in code

ax[1, 0].plot(timeSinceStart, fuel)

This line will throw the following error:

Value Error
  ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_15096\3855576218.py in <module>
     14
     15 # Fuel vs Time
---> 16 ax[1, 0].plot(timeSinceStart, fuel)
     17 ax[1, 0].set_xlabel('Time [hours]')
     18 ax[1, 0].set_ylabel('Fuel [kg]')

ValueError: x and y must have same first dimension, but have shapes (626,) and (1,)

We can see how the dimension/shape of the fuel argument is incorrect, due to the type (it should also be 626). It is a good example of how you may need to understand some discrepancies with return types in C#/.NET vs. Python, and to build a work-around for it. In this case, we will need to iterate through the IList and add each item to a Python List, to resolve the error. The following line of code demonstrates how to fix this line:

Python
fuelStateOverMission = missionResults.GetDateMotionCollectionOfOverallTrajectory[float](fuelMassName)
fuel = fuelStateOverMission.Values # [kg]
fuelList = []
for x in fuel:
    fuelList.append(x)

#later in code

ax[1, 0].plot(timeSinceStart, fuelList)
Tutorial
Note Note

The example script described in this topic requires a license for the Segment Propagation Library to run.

Additional Requirements for this Tutorial

  • Python IDE, such as Spyder, installed

  • Sample Python file located in the Examples\Python directory of the DME Component Libraries install

  • Matplotlib

  • Numpy

Description

We have created a tutorial on how to use DME Component Libraries with Python, which is located at Examples\Python\HohmannTransfer.py in your DME Component Libraries install. It goes through how to set up a Hohmann Transfer from an initial orbit with a apogee of 10,000 km and perigee of 7071 km to a final orbit with an eccentricity of 0.1 and apogee of 42,000 km. The script then goes into analyzing the generated ephemeris and creating graphs for fuel over time, altitude over time, speed over time and both a static and animated ground track.

Python Plots