Using STK Components with Python |
The Python package Pythonnet supports integrating Python with .NET libraries. This means that STK Components can be run using Python. This topic will show you how to setup and use STK Components from a Python IDE.
By integrating STK Components with Python, you have full access to .NET libraries, STK Components 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 frameworks. This means that you will be able to use all the capabilities of STK Components with the data analysis and visualization tools native to Python.
You must have the following prerequisites met to integrate STK Components with Python:
A valid STK Components 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 STK Components license before starting your analysis. This section will show you the basics of setting up and using Pythonnet with STK Components.
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 STK Components .NET assemblies sys.path.append(assemblyFolderFilePath)
To first load individual .NET assemblies (from something like STK Components), we need to utilize the clr.AddReference() function in the clr module. You can then essentially treat Components 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 STK Components 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 complete file path of STK Components license with open(licensePath) as f: licenseFile = f.read() Licensing.ActivateLicense(licenseFile)
There are multiple classes throughout the STK Components 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 STK Components.
#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 STK Components 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)
![]() |
---|
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 STK Components install
Matplotlib
Numpy
We have created a tutorial on how to use STK Components with Python, which is located at Examples\Python\HohmannTransfer.py in your STK Components 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.