Parallel evaluation of Mosel user functions
It is possible to use parallel evaluations of simple Mosel functions that return a single real value. These functions may take an arbitrary array of nlctr expressions as input. It is the modeler's responsibility to ensure that the user functions to be called in parallel are thread-safe (i.e., they do not depend upon shared resources). Assuming the name of the user function is MyFunc, the user function before enabling the parallel version is expected to be declared as usefuncMosel('MyFunc').
In order for mmxnlp to be able to utilize parallel user function evaluations, the user function must be implemented as a public function in a Mosel package. Any initialization necessary to enable the evaluation of the user function should be performed as part of the package initialization (which is the code in in the main body of the package).
To enable parallel evaluations, a parallel enabled version of the user function needs to be generated using the mmxnlp procedure generateUFparallel, which takes two arguments: the compiled package .bim name implementing the user function and the name of the user function within the package. It is good practice to use a separate Mosel model to perform this generation, keeping it separate from the main model. Multiple generated parallel user functions may be used within a single model.
The generator will produce a single Mosel file, the Mosel package MyFunc_master. This package also includes the worker model which will be responsible for the user function evaluations and will be resident in memory during the execution. The package also implements the parallel version of the user function, called MyFunc_parallel.
After compiling and including the master package into your model, it is this function that should be used in the actual model as userfuncMosel('MyFunc_parallel',XSLP_DELTAS). In most cases, no other modifications are necessary, as the parallel function will detect the number of threads in the system and will start that many worker threads automatically. These will be shut down when your model finishes. Each worker's initialization code is performed only once, at the time of its first execution.
It may be necessary to explicitly start the worker threads, either to control the number of threads used, or to pass specific parameter settings to the user function package. This can be done by the procedure MyFunc_StartWorkers( ThreadCount : integer, UfPackageParameters : string ). In case it is necessary to stop the workers, the procedure MyFunc_StopWorkers may be used.
In case the user functions are computationally very expensive, by modifying the connection string in the generated module it is possible to utilize distributed/cloud-based computation of the user functions.
The worker model will only be compiled into memory during execution, but may be modified as necessary within the master model. For debugging purposes, it may be practical to redirect the worker to a file.