How to use Python
The easiest way to describe the basic python interface is by providing a number of examples of loops of different type.
Let's start with a loop over all of the nodes in a model_part, which prints the ID and the coordinates for all of the nodes
for node in model_part.Nodes: print node.Id , " ", node.X, " ",node.Y
This is very basic but already provides an idea of the interface.
Let's now enrich the example to print the same information but exclusively on the nodes with positive abscissa
for node in model_part.Nodes: if(node.X > 0.0): ##printing the ID of all of the nodes with positive X print node.Id , " ", node.X, " ",node.Y
Accessing to the Nodal Data Base
The Python interface provides full access to the nodal and elemental database. To make an example let's assume that we want to set the variable TEMPERATURE to the value of 100.0 on the nodes in our model_part. This is obtained immediately by typing
for it in model_part.Nodes: it.SetSolutionStepValue(TEMPERATURE,0,100.0)
in doing this we introduced the operator "SetSolutionStepValue" which provides us the possibility to write on the Nodal "solution step database"
The command above should be interpreted as: for the node pointed by iterator "it" assign to the variable TEMPERATURE at the current step (the current step is identified by 0) the value of 100.0.
In a similar way it is possible to access to the value that are already stored in the database, by using the operator "GetSolutionStepValue". to make an example the vector of DISPLACEMENTs on the nodes at at the current time step can be printed on all of the nodes in the model part by doing
for iii in model_part.Nodes: print iii.GetSolutionStepValue(DISPLACEMENT)
Take into account that the variable DISPLACEMENT might be exported to Python by a module which is loaded by 'import KratosMultiphysics'. Then, use
for iii in model_part.Nodes: print iii.GetSolutionStepValue(KratosMultiphysics.DISPLACEMENT)
The possibility exists to access to the values of a variable in the past ... at least as long as the time step of interest is still in the buffer (normally the buffer includes 1 or 2 steps in the past). To make an example
for iii in model_part.Nodes: print iii.GetSolutionStepValue(DISPLACEMENT,1) # prints the DISPLACEMENT at 1 step in the past print iii.GetSolutionStepValue(DISPLACEMENT,2) # prints DISPLACEMENTS 2 corresponding to 2 steps in the past
The user should note that "GetSolutionStepValue(DISPLACEMENT,0)" is identical to "GetSolutionStepValue(DISPLACEMENT)"
Fixing and Unfixing nodes
The Python interface provides the possibility of Fixing or Unfixing degrees of freedom on the nodes in a model part by the functions "Fix" and "Free". as an example let's consider the following code:
for node in model_part.Nodes: if(node.X**2 + node.Y**2 > 1.0): #at a distance of more than one from the origin node.Fix(DISPLACEMENT_X) else: node.Free(DISPLACEMENT_X)
this simple example fixes the DISPLACEMENT_X on all of the nodes outside of a circumference of radius 1 from the origin
The possibility exist to check if a given variable is fixed on a node. To make an example it is possible to print the Id of the nodes for which the TEMPERATURE is fixed by
for node in model_part.Nodes: if(node.IsFixed(TEMPERATURE) == True): print node.Id
An important feature is the possibility of the interface to create easily lists of nodes, which can be used to restrict the loops to some areas in the model. For example a list of all of the nodes with positive ascissa can be obtained as
new_list =  #here we create an empty list for node in model_part.Nodes: if(node.X > 0.0): new_list.append(node) #here we add to the new list the node
at this point we filled the "new list" with pointer to the nodes we wanted to identify. The number of nodes in the new list can be known by printing
which is equivalent to the "size" operator in C++. It is now possible to iterate on the "new_list" exactly as on the original, by simply writing
for it in new_list: it.(do something) ...
"Coloring Nodes" to create custom lists
Creating lists of Nodes depending on their coordinates may be cumbersome or impossible on complex geometries. A more practical possibility is "coloring" nodes in the pre-processing step and using this information for creating custom_lists inside the python. This could be done with every variable, but we will assume here that "FLAG_VARIABLE" is used for the purpose. For example one may assume that in the pre-processing step
- FLAG_VARIABLE = 1 was set on the nodes on one part of the boundary
- FLAG_VARIABLE = 2 was set somewhere else
the user may want to initialize the TEMPERATURE to 10 in the area identified by FLAG_VARIABLE=1 or to TEMPERATURE=sin(time) when FLAG_VARIABLE=2
a simple way of doing this is creating two lists identifying the nodes of one type or of the other
list1 =  list2 =  for node in model_part.Nodes: if(node.GetSolutionStepValue(FLAG_VARIABLE) == 1): list1.append(node) elif(node.GetSolutionStepValue(FLAG_VARIABLE) == 2): list2.append(node)
at this point we can loop on the two list and apply the conditions at wish, for example
for node in list1: node.SetSolutionStepValue(TEMPERATURE,0,10.0)
and, after importing python's mathematical library
the user can, at every time step, update the value of temperature on the second part
temp = math.sin(time); for node in list2: node.SetSolutionStepValue(TEMPERATURE,0,temp)
Accessing to non-historical database
We should highlight at this point that in Kratos the nodes store two databases:
- a database for which "historical data" are saved ( a buffer of historical data of course)
- a database for which no history is stored.
The first database is accessed by using Get/Set "SolutionStepValue", the second type of database can be accessed by "GetValue" and "SetValue".
The idea is quite simple: some variables are changing in time while others will be fixed (or at least updated independently on the time) during all of the analysis. For some variables we will need to have access to the past values while for others this has no importance. Variables for which we need history will be stored in the Historical Database, while the others will be saved in the other.
To make an example we may want to assign a "auxiliary index" to every node in a way that this is independent on time. to make an example
i = 1.0 for node in model_part.Nodes: node.SetValue(AUX_INDEX,i) i = i + 1.0
assigns a constitutitve auxiliary index to all of the nodes in the model_part.
the value of the aux index can then be accessed at wish as
for node in model_part.Nodes: print node.GetValue(AUX_INDEX)
It is sometimes useful to store the value of an "historical variable" (say TEMPERATURE) at a given point in the analysis so that it can be reused later. One way of doing this is by making a copy of the "historical variable" in the non-buffered database, so that it can be recalled when needed. This is easily achieved by doing
for node in model_part.Nodes: temp = node.GetSolutionStepValue(TEMPERATURE); node.SetValue(TEMPERATURE,temp);
the two databases are NOT syncronized meaning that the value of the Historical one (or of the other) can be changed freely without changing the value stored in the other database to make an example on a given "node"
node.SetSolutionStepValue(TEMPERATURE,0,10.0) print node.GetSolutionStepValue(TEMPERATURE) # outputs 10.0 temp = node.GetSolutionStepValue(TEMPERATURE); node.SetValue(TEMPERATURE,temp); print node.GetValue(TEMPERATURE) # outputs 10 node.SetSolutionStepValue(TEMPERATURE,0,123456.0) print node.GetSolutionStepValue(TEMPERATURE) # outputs 123456.0 print node.GetValue(TEMPERATURE) # still outputs 10