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.
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.
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
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.
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.
import clr, sys # assemblyFolderFilePath is the complete file path containing the DME Component Libraries .NET assemblies sys.path.append(assemblyFolderFilePath)
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.
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
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.
#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)
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.
#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))
Inevitably, you will have to do some debugging. This section will address some notes and hints that might be helpful.
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.
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:
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:
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)
Note |
---|
The example script described in this topic requires a license for the Segment Propagation Library to run. |
Python IDE, such as Spyder, installed
Sample Python file located in the Examples\Python directory of the DME Component Libraries install
Matplotlib
Numpy
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.