/**
@anchor Input_File
@page LEVEL2 Input File
The primary interface between the simulation executable and the user is the runstream
input file. The %Trick simulation input file syntax is Python. All Python syntax rules
apply
Rather than discuss an explicit syntax definition (which would probably be more
confusing than informative), each specific capability of the input processor, and
its associated input file syntax, will be discussed.
@section LEVEL3 Accessing Simulation Parameters
The parameter naming convention for ALL input parameters is the parameter’s actual
source code name. The following is a line from an input file :
ball.obj.state.output.position[0] = 1.0 ;
In this example, ball is the sim object name in the S_define file for a sim object
which contains the data structure named obj, where the obj data structure declaration
is as follows:
@code
class BallSimObject : Trick::SimObject {
...
Ball obj ;
...
}
BallSimObject ball ;
@endcode
state is a class found in the Ball class.
@code
class Ball {
...
public:
BallState state; /**< -- Ball state object. */
...
};
@endcode
output is a member of the BallState class, and finally position is
a member of the BallStateOutput class.
@code
class BallState {
...
public:
BallStateOutput output; /**< trick_units(--) User outputs. */
...
};
class BallStateOutput {
...
public:
double position[2]; /**< trick_units(m) X(horizontal), Y(vertical) position. */
...
};
@endcode
Arrays of simulation parameters may be read and written to with one Python statement.
@verbatim
"""
These variables are declared in the ball structure.
double da[3] ;
double daa[3][3] ;
"""
# Python lists are the equivalent of the C/C++ arrays.
ball.da = [ 1.0 , 2.0 , 3.0 ]
print ball.da
# [ 1.0 , 2.0 , 3.0 ] is printed
ball.daa = [[ 1.0 , 0.0 , 0.0 ] , [ 0.0 , 1.0 , 0.0 ] , [0.0 , 0.0 , 1.0]]
print ball.daa
# [[ 1.0 , 0.0 , 0.0 ] ,
# [ 0.0 , 1.0 , 0.0 ] ,
# [0.0 , 0.0 , 1.0]]
# is printed
@endverbatim
@section LEVEL3 Accessing Simulation Enumerated Types
Global Enumerations are available through the "trick" module.
@verbatim
# from sim_services/include/Flag.h
print trick.True trick.False trick.Yes trick.No
1 0 1 0
@endverbatim
@section LEVEL3 Accessing Simulation Functions and Object Member functions
Almost all functions and public object methods are available to call from the Python input file.
Arguments must be filled in just as they would be in C/C++ code. There is more information about what
%Trick simulation services routines are available later in this chapter.
@verbatim
# Trick simulation services routines are called by "trick.".
trick.exec_get_sim_time()
trick.checkpoint(100.0)
trick.stop(300.0)
# C User model functions are also called by "trick.".
trick.ball_print(ball.state)
# C++ User model class methods are called by referencing the full object path just like in C++
ball.obj.state.print_position()
@endverbatim
When calling functions, intrinsic typed simulation variables, e.g. int or double, will not work directly
as intrisic typed arguments.
@verbatim
// If we have a c function
void foo( double d) ;
// And a structure with a variable declared as this
double length ; /* (m) length */
@endverbatim
This call in the input will not work
@verbatim
# Will not work
foo(length)
@endverbatim
The reason is that in python space the variable length is an object that contains both the value of length
and the units. The built in python command float() will strip the units off leaving a double that can be
used in the function call.
@verbatim
# Works
foo(float(length))
@endverbatim
Structure and class variables do not carry around units, and therefore the units do not have to be removed.
@section LEVEL3 Creating New Objects and Allocating Memory
It is possible to create new objects and allocate new memory for structures directly in the Python
input file. There are at least two ways to allocate memory.
The first method is to call the Trick::MemoryManager routines to allocate memory. This is the preferred method.
There are 3 Trick::MemoryManager calls with varying arguments that can be used to allocate memory
@code
trick.TMM_declare_var_s("declaration")
trick.TMM_declare_var_1d("enh_type_spec", e_elems)
trick.alloc_type(e_elems , "enh_type_spec")
# Some examples using a c++ declaration
# double * foo ;
# All 3 of the following statments allocates the same amount of memory
foo = trick.TMM_declare_var_s("double[6]")
foo = trick.TMM_declare_var_1d("double", 6)
foo = trick.alloc_type(6 , "double")
# Some examples using a c++ declaration
# double ** food ;
# All 3 of the following statments allocates the same amount of memory
food = trick.TMM_declare_var_s("double *[3]")
food[0] = trick.TMM_declare_var_s("double [4]")
food[1] = trick.TMM_declare_var_s("double [5]")
food[2] = trick.TMM_declare_var_s("double [6]")
food = trick.TMM_declare_var_1d("double *", 3)
food[0] = trick.TMM_declare_var_1d("double", 4)
food[1] = trick.TMM_declare_var_1d("double", 5)
food[2] = trick.TMM_declare_var_1d("double", 6)
food = trick.alloc_type(3, "double *")
food[0] = trick.alloc_type(4, "double")
food[1] = trick.alloc_type(5, "double")
food[2] = trick.alloc_type(6, "double")
@endcode
Memory allocated using the above routines are tracked by the memory manager and is checkpointable and data recordable.
The second method is to call the wrapped constructor of the class directly. This is analogous to declaring local
variables in C/C++ routines. And like local variables in C/C++ if the python variable goes out of scope in the
input file, then python will try and free the memory associated with the local object. Memory allocated this
way is not checkpointable or data recordable.
For example if we are trying to instantiate a new C++ "Ball" object in the input file.
@verbatim
# The new_ball_1 instantiation is at the top level, new_ball_1 will not be freed.
new_ball_1 = trick.Ball() ;
# The new_ball_2 instantiation is in the function foo.
# When foo returns new_ball_2 will be freed by python
def foo():
new_ball_2 = trick.Ball() ;
@endverbatim
To stop python from freeing this memory we must tell python that it does not own the memory.
This can be done in two ways. 1) Tell Python it does not own the memory by modifying the
thisown flag. 2) Use a non-constructor routine that allocates memory and returns that
to the Python variable.
@verbatim
# In the above example, we can avoid new_ball_2 from being freed
# when foo returns by setting the thisown flag to 0 in the new_ball_2 object.
def foo():
new_ball_2 = trick.Ball() ;
new_ball_2.thisown = 0 ;
# Alternatively we could call a non-constructor C/C++ routine that returns a new Ball
# object to python. The python interpreter does not sense it allocated anything and
# will not free it.
"""
C++ code for get_new_ball_obj()
Ball * get_new_ball_obj() {
return(new Ball) ;
}
"""
def foo():
new_ball_2 = trick.get_new_ball_obj() ;
@endverbatim
@section LEVEL3 Comments
Comments in Python come in two forms.
@verbatim
# A single line comment starts with a '#' sign
"""
Multi line comments are enclosed in
three sets of double quotes.
"""
@endverbatim
@section LEVEL3 Nested File Inclusion
There are several ways to include files in Python.
@verbatim
# One way is to use the execfile command
execfile("Modified_data/data_record.py")
# Another way is to make the included file a module and import it.
# Import search paths may be added using the sys.path.append command.
sys.path.append("/my/python/dir") ;
import my_new_module
@endverbatim
@section LEVEL3 Local Python Variables
Local variables may be used anywhere in the Python input file. Local variables will follow normal
Python scoping rules. Shortcut variable names may be created to reference simulation variables.
@verbatim
my_position = ball.obj.state.output.position
my_position[0] = 4.5
my_postion [1] = 6.7
print ball.obj.state.output_position
# printout would read "4.5, 6.7"
@endverbatim
@section LEVEL3 Environment Variables
Environment Variables are available through the Python os.getenv call
@verbatim
print os.getenv("TRICK_CFLAGS")
@endverbatim
@section LEVEL3 Measurement Units
Every input parameter has associated measurement units specified in its corresponding data
structure definition file declaration. It specifies the units for the internal source code to
use for that parameter. However, %Trick users also have certain control over units specification
from the input file.
trick.attach_units() attaches a unit to a value or some Python objects.
@verbatim
"""
This variables is declared in the ball structure.
double position[3] ; /* (m) X,Y,Z postion */
"""
# Assign X position to 2m
ball.position[0] = trick.attach_units( "m" , 2.0 )
# Automatic units conversion is done if the attached unit is compatible with the variable.
# Assign Y position to 2ft
ball.position[1] = trick.attach_units( "ft" , 2.0 )
# Error is raised.
ball.position[2] = trick.attach_units( "ft/s" , 3.0 )
# Units may be attached to python lists and assigned to the array with one statement
# Automatic units conversion is done on the entire list.
ball.position = trick.attach_units( "ft" , [1.0 , 2.0, 3.0] )
# Lists may even include values of different units. Automatic units conversion is
# done element by element.
ball.position = [trick.attach_units( "ft" , 1.0 ) , trick.attach_units( "m" , 2.0 , trick.attach_units( "cm" , 3.0)] )
@endverbatim
Printing parameters in the Python script will include the attached units.
@verbatim
print ball.position
[1.0m , 2.0m , 3.0m]
@endverbatim
@section LEVEL3 Time Based Input Processing
The input processor allows pieces of the input file to be processed at a later simulation time.
To process code at a later time call trick.add_read(