Python external functions

To use an external function written in Python you need to have Python installed on your machine. You also need the OrcaFlex Python interface, OrcFxAPI.py, installed. For details see Python interface: Installation.

Introduction and Examples

An overview of using Python external functions, with examples, is included in the OrcaFlex installation. These files are on the OrcaFlex disk, and are installed in the ExternalFunctionExamples/Python sub-directory of the OrcaFlex installation directory on your machine; they are also available on our website.

Python external functions are fully supported under both 32- and 64-bit version of OrcaFlex. The bitness of OrcaFlex and Python must match: you must have a 64-bit version of Python to use Python external functions from 64-bit OrcaFlex, and you must have 32-bit version of Python to use Python external functions from 32-bit OrcaFlex. Multiple Python versions can be installed on a single machine so it is perfectly possible to use Python external functions from both 32- and 64-bit versions of OrcaFlex on a single machine (both versions of OrcaFlex are installed by default).

External Function Module

To write an external function in Python, you must write a Python module containing an external function class with a specific set of methods – see Python External Function Class below. This Python file is the external function module. It can also define other classes and code, but OrcaFlex will only call the methods described below, for the external function class you specify. The external function module can contain more than one external function class and also other functions and non-external function classes. It can also import standard or third party Python libraries.

To use the external function, create an external function data source (on the Variable Data form in OrcaFlex) and set its File Name to the name of your Python module. The Function Name data item will then display a list of classes contained in the module, so that you can select the external function class to use. The list will also include any other classes in the module that are not external functions, although of course they are not suitable for selecting.

When your model is run, OrcaFlex sets up a Python environment for your Python code, and then imports both the OrcaFlex Python interface (OrcFxAPI) and your external function module into that environment. OrcaFlex then creates an instance of your external function class for each use of the external function in the OrcaFlex model, and calls methods of those objects, as described below.

Note: Once imported, Python does not enable a module to be re-imported into the same Python instance. So if functions or classes are renamed, added or removed then OrcaFlex will have to be restarted to ensure these changes are imported into the Python environment. Changes to code within an existing function does not require this step, since it will be re-interpreted each time the simulation is run.

OrcFxAPI module

You must not explicitly import the OrcFxAPI module in external function code. This import is performed implicitly by the host OrcaFlex process. The import needs to be performed by the host OrcaFlex process to ensure that the imported OrcFxAPI module correctly references the host OrcaFlex process. The external function code can access the implicitly imported OrcFxAPI module in the usual way. For example:

def Initialise(self, info):

info.period = OrcFxAPI.PeriodNum.InstantaneousValue

Python External Function Class

OrcaFlex looks for the following methods in your class: Initialise, Calculate, StoreState, and Finalise which are described below. All of these methods are optional. If none of these methods are defined then the value of the variable data item will be the initial value of the external function set on the Variable Data Form. If you want to calculate custom results in your external function then you also need to implement the following methods in your class: RegisterResults, LogResult, and DeriveResult. In addition, if using a variable data source for line type stiffness, a tracking external function can be used which must implement a TrackCalculation method.

All the methods receive a parameter called info. This contains some information attributes that the function can read but should not modify, and some attributes that the function can set. The most important of the modifiable attributes is the Value attribute, which returns the externally calculated value. For detailed information on the attributes of this info parameter see ExternalFunctionInfo.

Your code can call OrcaFlex back, using OrcFxAPI, to interrogate the model's data items and instantaneous state. Your external function class can extend another base class and may also have additional methods supporting your code, but only the methods described below will be called directly by OrcaFlex.

Initialise

def Initialise(self, info):

# Your initialisation code here

info.Value = initialValue

If this method is defined in your class then OrcaFlex will call it once per instance of your external function, before calculating statics. This method allows you to set up and initialise working data, check for the presence of stored state data (see StoreState below) and modify the initial value of the external function.

To begin with, the info.Value attribute contains the initial value defined for the external function in the Variable Data form, but this can be overridden in this method by assigning a new value. If your class also has a Python constructor __init__ then this will be called automatically before the Initialise method.

External functions for vessels, 6D buoys, wings and line applied loads can elect to be updated during statics by setting the info.UpdateDuringStatics attribute to True. If the external function does this then the Calculate method is called during the static analysis as well as during dynamics.

If possible, external functions should be coded to support resuming partially run simulation files. Typically this will involve saving state in the StoreState method, and restoring that state in the Initialise method. However, this is not always possible, for example when the state is held by an external DLL that does not provide access to it. If it is not possible for the external function to resume simulations, this should be indicated by setting the info.CanResumeSimulation attribute to False.

RegisterResults

def RegisterResults(self, info):

info.ExternalResults = [{"ID": result_id, "Name": resultName, "Units": resultUnits}, ...]

This method must be defined if your class provides custom results. OrcaFlex will call this method once, before calling the Initialise method and always before calling Calculate. When called, this method must return a Python array of one or more dictionaries containing the results registration data. This array is returned by setting the info.ExternalResults attribute. See ExternalFunctionInfo for a description of the expected registration data items.

Calculate

def Calculate(self, info):

# Your calculation code here

info.Value = modifiedValue

If defined, this method should calculate the external function value and return it to OrcaFlex by assigning it to the info.Value attribute, or in the case of externally calculated primary motion by assigning it to the info.StructValue attribute (see ExternallyCalculatedImposedMotion).

When the simulation is using implicit integration then this method will usually be called more than once for the same time step. OrcaFlex will indicate when a new timestep is starting by setting the info.NewTimeStep parameter attribute to True.

The info.Value and info.StructValue attributes are preserved between calls to the Calculate method, so they do not need to be re-assigned if the same value is wanted. For externally calculated primary motion, the info.StructValue attribute returned must not change during a single time step, so that attribute should only be assigned when info.NewTimeStep is True.

The external function can query the current model (info.Model) and the model object that owns the external function (info.ModelObject), to obtain the value of other data items and results variables. Results variables are queried by calling the Python interface OrcaFlexObject.TimeHistory method, but the only Period allowed is PeriodNum.InstantaneousValue. Some model objects provide information about their instantaneous state (e.g. position, orientation, velocity etc.) through the info.InstantaneousCalculationData attribute – see Instantaneous Calculation Data.

TrackCalculation

def TrackCalculation(self, info):

# Code to track Node Instantaneous Calculation data if required.

When using a Bend Stiffness Variable Data Source it is possible to set up an external function to track this data item in order to calculate custom results for lines. This external function must implement a TrackCalculation method which is called before the LogResult method, twice for each node of the line type (for each half segment). The info.InstantaneousCalculationData attribute will be populated with the node's InstantaneousCalculationData so the external function can update working data and prepare for the subsequent call to LogResult.

LogResult

def LogResult(self, info):

info.LogData = logDataString

This method allows an external function to store the data it needs for calculating custom results to the OrcaFlex simulation file. When running the simulation OrcaFlex will call this method at every log interval and after calling Calculate or TrackCalculation. The data to be stored is returned to OrcaFlex as a string by setting the info.LogData attribute. To encode the custom result data as a string we recommend using the built in Python module json.

DeriveResult

def DeriveResult(self, info):

if info.ResultID == MY_RESULTID:

logData = info.LogData # extract the logged data string

....

queryData = info.ObjectExtra.ExternalResultText # Additional query parameters

....

info.Value = customResultValue

When a custom result is requested, OrcaFlex will repeatedly call this method for each log sample in the query period. OrcaFlex supplies the id of the custom result in the info.ResultID attribute, previously registered in the RegisterResults method. Any data saved in the simulation file for this log sample by the LogResult method is supplied in the info.LogData attribute. Additional user defined parameters for the query may be supplied as text in the info.ObjectExtra.ExternalResultText attribute. The custom result value is returned by setting info.Value.

StoreState

def StoreState(self, info):

info.StateData = externalFunctionState

This method is only required if an external function maintains working data that is dependent on data from an earlier time step, and so needs to be preserved in case a saved completed simulation is extended, or a saved paused simulation is later resumed.

When a simulation is saved, OrcaFlex will call this method to give the external function a chance to save its state to the simulation file. This state data should be in the form of Python string or byte data assigned to the info.StateData attribute. When the simulation is reloaded, the Initialise method can check this attribute and restore the state. We recommend using Python's json module to create the state data. If multiple instances of an external function are sharing state data then only one instance should take responsibility for saving and restoring it.

Finalise

def Finalise(self, info):

# Your finalisation code here

This method is optional. If present it will be called when the simulation is reset. In most cases this method will not be required since Python (and OrcaFlex) will tidy up memory for you. However, if you have references to objects outside of the Python environment (for example an open file) then these resources should be closed here.

Output and Error Handling

Any output from your code to stdout and stderr, e.g. from print statements and Python error messages, will be redirected to the External Function Output window in OrcaFlex, which can be opened using the Window menu in OrcaFlex.

Errors raised in the Python code, and not handled by that code, will stop the simulation and the error will be reported in OrcaFlex. If the error occurred after the external function module has been sucessfully imported, then the Python details of the error, and any output from print statements in your code, will be shown in the External Function Output window.

Errors that occur while importing the external function module will be reported, but the error detail will not viewable in OrcaFlex, because the import stage occurs before Python output has been redirected to OrcaFlex. We therefore recommend trying to import your module directly in a Python console first, to expose any syntax errors when developing.

See also

ExternalFunctionInfo, ExternallyCalculatedImposedMotion and Instantaneous Calculation Data.