| [Home](/trick) → [Documentation Home](../Documentation-Home) → [Running a Simulation](Running-a-Simulation) → 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. ## 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 : ```python 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: ```cpp class BallSimObject : Trick::SimObject { ... Ball obj ; ... } BallSimObject ball ; ``` `state` is a class member found in the `Ball` class. ```cpp class Ball { ... public: BallState state; /**< -- Ball state object. */ ... }; ``` `output` is a member of the `BallState` class, and finally `position` is a member of the `BallStateOutput` class. ```cpp class BallState { ... public: BallStateOutput output; /**< trick_units(--) User outputs. */ ... }; class BallStateOutput { ... public: double position[2]; /**< trick_units(m) X(horizontal), Y(vertical) position. */ ... }; ``` Arrays of simulation parameters may be read and written to with one Python statement. ```python """ 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 ``` ## Accessing Simulation Enumerated Types Global Enumerations are available through the `trick` module. ```python # from sim_services/include/Flag.h print trick.True trick.False trick.Yes trick.No 1 0 1 0 ``` ## 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. ```python # 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() ``` When calling functions, intrinsic typed simulation variables, e.g. int or double, will not work directly as intrisic typed arguments. ```cpp // If we have a c function void foo( double d) ; // And a structure with a variable declared as this double length ; /* (m) length */ ``` This call in the input will not work ```python # Will not work foo(length) ``` 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. ```python # Works foo(float(length)) ``` Structure and class variables do not carry around units, and therefore the units do not have to be removed. ## 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 ```python 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") ``` 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. ```python # 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() ; ``` 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. ```python # 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() ; ``` ## Comments Comments in Python come in two forms. ```python # A single line comment starts with a '#' sign """ Multi line comments are enclosed in three sets of double quotes. """ ``` ## Nested File Inclusion There are several ways to include files in Python. ```python # One way is to use the execfile command exec(open("Modified_data/data_record.py").read()) # 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 ``` ## 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. ```python my_position = ball.obj.state.output.position my_position[0] = 4.5 my_position[1] = 6.7 print ball.obj.state.output_position # printout would read "4.5, 6.7" ``` ## Environment Variables Environment Variables are available through the Python `os.getenv` call ```python print os.getenv("TRICK_CFLAGS") ``` ## 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. ```python """ This variables is declared in the ball structure. double position[3] ; /* (m) X,Y,Z position */ """ # 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 )] ``` Printing parameters in the Python script will include the attached units. ```pycon >>> print ball.position [1.0m , 2.0m , 3.0m] ``` ## 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(