GNATS simulation¶
para-atm includes capabilities to facilitate running the GNATS simulation from within Python. These capabilities are provided by the paraatm.io.gnats
module. The following functionality is provided:
- Boilerplate code to automatically start and stop the Java virtual machine, and to prevent it from being started multiple times
- Behind-the-scenes path handling, so that the GNATS simulation does not need to be run from within the GNATS installation directory
- A utility function to retrieve GNATS constants from the Java environment
- Return trajectory results directly as a pandas DataFrame
- Both high-level and low-level interfaces for defining simulations
The functions for interfacing with GNATS are subject to change. Currently, the code has been tested with GNATS beta1.10 on Ubuntu Linux.
Two mechanisms are provided for interfacing with the GNATS simulation. The first is GNATS basic interface, which provides a simple interface for running a simulation from given TRX and MFL files without having to write any of the code to drive the simulation. The second option is to use GNATS wrapper interface, which provides more control over the simulation but requires writing more code.
GNATS basic interface¶
The GNATS basic interface makes it possible to run a GNATS simulation without having to write any of the GNATS driver code. The simulation is specified by providing the TRX and MFL files. The trajectory results are returned as a simulation output in the form of a DataFrame
.
The basic interface is implemented through the GnatsBasicSimulation
class. The following is a complete example, which recreates the DEMO_Gate_To_Gate_Simulation_SFO_PHX_beta1.9.py from the GNATS samples directory.
1 2 3 4 5 6 7 8 9 | import os
from paraatm.io.gnats import GnatsBasicSimulation, GnatsEnvironment
GnatsEnvironment.start_jvm()
trx_file = os.path.join(GnatsEnvironment.share_dir, "tg/trx/TRX_DEMO_SFO_PHX_GateToGate_geo.trx")
mfl_file = os.path.join(GnatsEnvironment.share_dir, "tg/trx/TRX_DEMO_SFO_PHX_mfl.trx")
simulation = GnatsBasicSimulation(trx_file, mfl_file, 22000, 30)
df = simulation()['trajectory']
|
Lines 5 and 6 specify the locations of the TRX and MFL files that will be used. In this example, these files are referenced within the GNATS installation directory, by accessing the GnatsEnvironment.share_dir
variable, which stores the location of the “share
” directory based on GNATS_HOME
. Of course, the user is free to specify TRX and MFL files that are not located within the GNATS installation as well. Note that line 4, which manually starts the JVM, is necessary so that GnatsEnvironment.share_dir
is available for use on lines 5 and 6.
Line 8 creates an instance of the simulation class by specifying the input files as well as the simulation propagation time and time step. Line 9 executes the simulation and stores the trajectory results in the variable df
. More information about the simulation results is given below in Running the GNATS simulation.
If more control over the simulation is needed, the user can use the GNATS wrapper interface (the basic simulation interface itself is implemented in terms of the wrapper interface).
GNATS wrapper interface¶
The GNATS wrapper interface is provided for users that need more control over the simulation (e.g., to customize the simulation inputs or to pause the simulation as it runs). para-atm provides a base class that the user can derive from, which automates some of the steps of interfacing with GNATS.
Creating a GNATS simulation¶
The GNATS wrapper interface is used by writing a class that derives from the GnatsSimulationWrapper
class. This is best understood through an example. The complete code for the following example is available at tests/gnats_gate_to_gate.py, and it is based on DEMO_Gate_To_Gate_Simulation_SFO_PHX_beta1.9.py from the GNATS samples directory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from paraatm.io.gnats import GnatsSimulationWrapper, GnatsEnvironment
class GateToGate(GnatsSimulationWrapper):
def simulation(self):
GNATS_SIMULATION_STATUS_PAUSE = GnatsEnvironment.get_gnats_constant('GNATS_SIMULATION_STATUS_PAUSE')
GNATS_SIMULATION_STATUS_ENDED = GnatsEnvironment.get_gnats_constant('GNATS_SIMULATION_STATUS_ENDED')
DIR_share = GnatsEnvironment.share_dir
simulationInterface = GnatsEnvironment.simulationInterface
environmentInterface = GnatsEnvironment.environmentInterface
aircraftInterface = GnatsEnvironment.aircraftInterface
# ...
|
In this example, Line 3 defines the GateToGate
class as a subclass of GnatsSimulationWrapper
class. Then, the simulation()
method is defined. This is where the user’s code for setting up and running the GNATS simulation should go. Notice that lines 6 and 7 use the get_gnats_constant()
method to retrieve specific constants from the Java environment, which are used later in the simulation code.
Line 9 gets a reference to the location of the “share” directory used by GNATS. Lines 11-13 retrieve references to the interface objects, which are available through GnatsEnvironment
. The bulk of the remaining code follows the example file that is included with GNATS.
As compared to the GNATS sample file, some key differences in this implementation are:
from GNATS_Python_Header_standalone import *
is not used (in general,import *
is not advisable)- Cleanup calls for
gnatsStandalone.stop()
andshutdownJVM()
are not needed, as they are automatically handled - GNATS constants are retrieved using the utility function
get_gnats_constant()
, as opposed to importing the constants from GNATS_Python_Header_standalone.py, where each constant is manually defined
Running the GNATS simulation¶
Once the user-defined class deriving from GnatsSimulationWrapper
has been created, the simulation is executed by creating an instance of the class and calling its __call__()
method. This method will handle various setup behind the scenes, such as starting the JVM, creating the GNATSStandalone
instance, and preparing the current working directory. Once the simulation is prepared, the user’s simulation()
method is called automatically. The output file is automatically created by communicating with the user-defined write_output()
method, and the trajectory results are stored as a DataFrame in the 'trajectory'
key of the returned dictionary.
For example, the GateToGate
simulation class defined above could be invoked as:
1 2 | g2g_sim = GateToGate()
df = g2g_sim()['trajectory']
|
Here, line 1 creates an instance of the GateToGate
class. Line 2 executes the simulation, passing no arguments (note that the ()
operator invokes the __call__
method). The return value of g2g_sim()
is a dictionary, and we retrieve the value of the 'trajectory'
key, which is a DataFrame that stores the resulting trajectory data. Note that line 2 is just shorthand for:
results = g2g_sim()
df = results['trajectory']
Additional keyword arguments provided to __call__()
are passed on to simulation()
. This makes it possible to create a simulation instance that accepts parameter values. For example:
1 2 3 4 5 6 7 | class MySim(GnatsSimulationWrapper):
def simulation(self, my_parameter):
# .. Perform simulation using the value of my_parameter
my_sim = MySim()
df1 = my_sim(my_parameter=1)['trajectory']
df2 = my_sim(my_parameter=2)['trajectory']
|
Here, the user-defined simulation()
method on line 2 is defined to accept an argument, my_parameter
. Once the simulation class is instantiated, repeated calls can be made using different parameter values, as shown on lines 6 and 7.
If the simulation method itself returns values, __call__()
stores these in the 'sim_results'
key of the dictionary that it returns. For example:
1 2 3 4 5 6 7 | class MySimWithReturnVals(GnatsSimulationWrapper):
def simulation(self):
# .. Perform simulation
return some_data
my_sim = MySimWithReturnVals()
some_data = my_sim(return_df=False)['sim_results']
|
In this example, the call to my_sim()
on line 7 uses the return_df=False
option to suppress storing the trajectory results. However, this is not required, and both trajectory results and custom return values can be returned if needed.
The API¶
-
class
paraatm.io.gnats.
GnatsBasicSimulation
(trx_file, mfl_file, propagation_time, time_step)¶ Simple interface for running a GNATS simulation from TRX and MFL files
If more control is needed, create a subclass of
GnatsSimulationWrapper
-
__init__
(trx_file, mfl_file, propagation_time, time_step)¶ Define basic simulation
Parameters: - trx_file (str) –
- mfl_file (str) –
- propagation_time (int) – Total flight propagation time in seconds
- time_step (int) – Time step in seconds
-
__call__
(output_file=None, return_df=True, **kwargs)¶ Execute GNATS simulation and write output to specified file
Parameters: - output_file (str) – Output file to write to. If not provided, a temporary file is used
- return_df (bool) – Whether to read the output into a DataFrame and return it
- **kwargs – Extra keyword arguments to pass to simulation call
Returns: - A dictionary with the following keys:
- ’trajectory’ (if return_df==True)
DataFrame with trajectory results
- ’sim_results’
Return value from child simulation method
Return type: dict
-
-
class
paraatm.io.gnats.
GnatsSimulationWrapper
¶ Parent class for creating a GNATS simulation instance
Users should implement the following methods in the derived class:
- simulation
- This method runs the actual GNATS simulation. If the simulation
code needs to access data files relative to the original working
directory, use the
GnatsEnvironment.build_path()
method, which will produce an appropriate path to work around the fact that GNATS simulation occurs in the GNATS_HOME directory. - write_output
- This method writes output to the specified filename.
- cleanup
- Cleanup code that will be called after simulation and write_output. Having cleanup code in a separate method makes it possible for cleanup to occur after write_output. The cleanup code should not stop the GNATS standalone server or the JVM, as this is handled by the GnatsEnvironment class.
Once an instance of the class is created, the simulation is run by calling the instance as a function, which will go to the
__call__()
method. This will call the user’s simulation method, with additional pre- and post-processing steps. The JVM will be started automatically if it is not already running.-
simulation
()¶ Users must implement this method in the derived class
Assume that the jvm is already started and that it will be shutdown by the parent class.
The function may accept parameter values, which must be provided as keyword arguments when invoking
__call__()
.
-
write_output
(filename)¶ Users must implement this method in the derived class
It will be called after the simulation method and should issue the commands necessary to write the output to the specified file.
-
__call__
(output_file=None, return_df=True, **kwargs)¶ Execute GNATS simulation and write output to specified file
Parameters: - output_file (str) – Output file to write to. If not provided, a temporary file is used
- return_df (bool) – Whether to read the output into a DataFrame and return it
- **kwargs – Extra keyword arguments to pass to simulation call
Returns: - A dictionary with the following keys:
- ’trajectory’ (if return_df==True)
DataFrame with trajectory results
- ’sim_results’
Return value from child simulation method
Return type: dict
-
class
paraatm.io.gnats.
GnatsEnvironment
¶ Class that provides static methods to start and stop the JVM for GNATS
-
classmethod
start_jvm
(gnats_home=None)¶ Start java virtual machine and GNATS standalone server
This function is called automatically by
GnatsSimulationWrapper
, so normally there is no need for the user to call it directly.If the JVM is already running, this will do nothing. If the JVM has already been stopped, this will raise an error, since it cannot be restarted.
This function takes care of setting the Java classpath, changing directories, starting the JVM, and starting the GNATS standalone server.
References to gnatsStandalone as well as other interface objects, which are normally available via the GNATS header file, are stored as attributes of the class.
Path issues with GNATS are handled behind the scenes by setting the classpath and changing directories prior to starting the JVM. The original directory is remembered, and it is restored after the JVM is stopped.
Parameters: gnats_home (str, optional) – Path to GNATS home directory. If not provided, the GNATS_HOME environment variable will be used.
-
classmethod
stop_jvm
()¶ Stop java virtual machine and GNATS server
This also moves back to the original directory that was set prior to starting the JVM
If this function is not called manually, it will be called automatically at exit to make sure that the JVM is properly shutdown. Multiple calls are OK.
-
classmethod
get_gnats_standalone
()¶ Retrieve reference to GNATSStandalone class instance
-
classmethod
get_gnats_constant
(name, classname='Constants')¶ Return the variable that stores the named GNATS constant
Parameters: - name (str) – Name of GNATS constant to retrieve
- classname (str) – Name of the Java class under which the constant is defined (refer to the GNATS Python header file)
-
classmethod
build_path
(filename)¶ Return a path to filename that behaves as if original directory is current working directory
This will internally convert relative paths to be relative to the original working directory (otherwise, GNATS considers GNATS_HOME to be the working directory).
-
classmethod