FMOPSolver Dynamic Linked Library

Selected oSP3D features can be exported to a dynamic linked library with the C Application Programming Interface (API).

The C API documentation for the FMOPSolver dynamic linked library is available on the Ansys API Documentation site. With this C API, you can only query and evaluate an already created field-MOP. Hence, reference mesh definition, data input, and field-MOP creation all must be done within the oSP3D main executable. Once created, the oSP3D database can be exported and used within this dynamic linked library.

Installation Directories

On Linux, the FMOPSolver integration gets unpacked automatically during installation. The Windows installation is optional and must be selected explicitly during setup.

If successfully installed, you can find the following FMOPSolver subdirectories within the oSP3D installation directory:

FMOPSolver/share/

Demonstration examples in C, C++, python, and MATLAB languages as well as developer documentation in HTML and PDF format. To read the documentation in HTML format, open share/doc/html/index.html.

FMOPSolver/lib/

The dynamic link library itself, without any dependency.

FMOPSolver/include/

The C/C++-API header file.

Linkage to Other Shared Libraries and Compatibility Notes

The installation subdirectory share contains some demonstration examples. These examples gets compiled and tested in the oSP3D build and test environments on a regular basis, using tools of the GNU Compiler Collection (GCC) for programming languages and python 2.7.x for the scripting example.

To use the C and C++ examples in a custom environment, compile the respective source code unit and link to the FMOPSolver dynamic linked library. Because the FMOPSolver library depends on several other libraries, you must further ensure that the loader executable finds them. Because a typical oSP3D installation is not located in any default search paths, you must add the ${SoS_install_dir}/sos directory to the list of search paths.

On most Linux systems, you can do this either by using the LD_LIBRARY_PATH environment variable or by adding the search path to the /etc/ld.so.conf file. Windows follows different strategies with different search orders, depending on operating system settings. In most cases, the directory from which the application was loaded, the current directory, and the directories that are listed in the PATH environment variable are scanned. For more information, refer to the manual for your operating system.

The python example is written for version 2.7.x. It uses the ctypes module to access the FMOPSolver dynamic linked library API. Due to its dependencies to other libraries, you mus ensure that the loader executable can find these dependencies. Refer to the previous paragraph for help.

The next two sections introduce the FMOPSolver API using a general workflow example and a Python example.

General Workflow Example

A typical FMOPSolver dynamic linked library workflow executes API calls in the following order:

  1. Add an additional license search path if your license file is not placed at a default location. Default locations are listed in the developer documentation. For more information, search the developer documentation for FMOP_appendLicenseSearchPath.

  2. Acquire a license. The library cannot be used without a valid license.

  3. Load the oSP3D database containing the field-MOP of interest.

  4. Get a handle to the field-MOP in question.

  5. Get the parameters and evaluate the field-MOP, repeating this step as needed

  6. Release the field-MOP handle.

  7. Release the database handle.

  8. Release all licenses. You cannot release acquired licenses until all fmop_handle_*t handles are successfully released.

On the Ansys API Documentation site, the C API documentation for the FMOPSolver dynamic linked library provides demonstration examples that show this workflow for C, C++, and Python code.


Note:
  • Be sure to load oSP3D databases only once. Because a database file is usually quite large, loading it requires a significant amount of time and resources. Therefore, you should keep the database file handler open as long as one of its field-MOP models is needed. If you only need one field-MOP handle from a specific database, you can release this database handle immediately after loading it. However, because this field-MOP handle stores a reference on its parent object, resources are not released until the last child object is released.

  • Be sure to release your license if no longer needed. Licenses are not released automatically. Licenses are guaranteed to be returned only at program or process termination.


Python Example

The installed version 2.7.x Python example follows in principal the suggestions of the general workflow example. To use the C-API, import the ctypes module:

import ctypes

In the Variables section, you must define the system environment, which is where the FMOPSolver dynamic linked library can be found: CDLL_lib_path = C:\your\path\FMOPSolver0.dll. The python example assumes that library dependencies are located in one of the default search paths, next to the FMOPSolver dynamic linked library. Furthermore, you must define the path to the oSP3D field-MOP demo or any other oSP3D field-MOP database, the number of field-MOP input parameters, and some parameter values set within their DOE (Design of Experiments) boundaries.

Using the following code, the current working directory gets set to the path of the FMOPSolver library and loaded into the oSP3D handle:

os.chdir ( os.path.dirname( CDLL_lib_path ) )
sos = ctypes.CDLL ( os.path.basename( CDLL_lib_path ) )

Because the ctypes module assumes that int data is to be returned by default, the subsequent line declares some other return types:

sos.FMOP_getLastLogString.restype = ctypes.c_char_p

Enum variable definitions are not known by python and are therefore redefined for convenience. Furthermore, the function CheckError and the class LicManager are also for convenience only. The first one checks the FMOPSolver API returned error code and throws an error. The latter one closes all open handles and returns the license on error.

To acquire a license:

sos.FMOP_appendLicenseSearchPath ( user_lic_path )
sos.FMOP_acquireLicenses ( FMOP_MESH_ALL )

To load the database of interest into the database handle:

database = ctypes.c_void_p()
sos.FMOP_loadDbFile ( ctypes.byref( database ), db_path )

As noted in the general workflow example, you should keep the database handle in scope as long as its field-MOP models get used. You should not load and unload the oSP3D database for each FMOP handle.

To get a list of known field-MOP identifiers:

  1. Call sos.FMOP_getModelIdents ( database, FMOP_NODE_DATA, ctypes.byref( fmop_idents ), ctypes.byref( num_idents ) ).

  2. Print the fmop_idents array:

    fmop = ctypes.c_void_p()
    sos.FMOP_getModel ( database, FMOP_ELEMENT_DATA, fmop_ident, ctypes.byref( fmop ) )

An element field-MOP handle with the given fmop_ident is loaded into your prepared fmop variable.

At this point, you can query some interesting properties, like the FCOP[total] value or the number of active parameters and the model dimension. The latter two parameters are of special interest because they are needed to prepare input arrays for a field-MOP evaluation call. For example, the following code queries the needed field-MOP input array sizes and evaluates the fmop handle at the given param_values point:

param_idents = ctypes.POINTER( ctypes.c_char_p )()
num_idents = ctypes.c_ulonglong(0)
sos.FMOP_getModelParamIdents ( fmop, ctypes.byref( param_idents ), ctypes.byref ( num_idents ) )

num_mesh_items = ctypes.c_ulonglong(0)
sos.FMOP_getModelDim ( fmop, ctypes.byref ( num_mesh_items ) )

param_values = ( ctypes.c_double * num_idents ) ( 1., 2., 3., 4., 5., 6. )
approx_field = ( ctypes.c_double * num_mesh_items.value ) ()
sos.FMOP_approxField ( fmop, param_values, ctypes.byref( approx_field ) )

This can be done as often as a different param_values combination of interest exists and these parameters are within their DOE boundaries.

Finally, to release all handles and licenses:

sos.FMOP_releaseModel ( ctypes.byref ( fmop ) )
sos.FMOP_releaseDb ( ctypes.byref ( database ) )
sos.FMOP_releaseLicenses ()

MATLAB Example

The FMOPSolver shared library can be used inside MATLAB™. The provided example was tested on Microsoft Windows using MATLAB R2015b with MinGW64/GCC4.9 as the MEX compiler.

MATLAB provides a simple mechanism for binding external libraries. It parses the API and links against the dynamic library by declaring the API through the ANSI C header file. The command loadlibrary is used to import the library:

loadlibrary ( CDLL_lib_file ,'fmop_solver.h')

You can call functions in the dynamic library:

calllib ( CDLL_lib_file, function_name, parameter_1, parameter_2, ... )

To acquire a license:

calllib ( CDLL_lib_file, 'FMOP_appendLicenseSearchPath', user_lic_path );
errno = calllib ( CDLL_lib_file, 'FMOP_acquireLicenses', feature );
FMOPSolver_checkError ( errno, 'Error acquiring licenses', calllib( CDLL_lib_file, 'FMOP_getLastErrorString' ) );

The function FMOPSolver_checkError is a MATLAB function provided in a separate example file for convenience.

To load the database of interest into the database handle:

database = libpointer( 'voidPtr' );
errno = calllib ( CDLL_lib_file, 'FMOP_loadDbFile', database, db_path );

You should keep the database handle in scope as long as its field-MOP models get used. You should not load and unload the oSP3D database for each field-MOP handle. To get a list of known field-MOP identifiers:

num_idents = uint64 (0);
[errno, ~, num_idents] = calllib ( CDLL_lib_file, 'FMOP_getModDim', database, 'fmop_node_data', num_idents );

To get the number of identifiers and then access the string identifier of each field-MOP:

ident = calllib ( CDLL_lib_file, 'FMOP_getModelIdent', database, 'fmop_element_data', uint64(i) );

fmop = libpointer( 'voidPtr' );
calllib ( CDLL_lib_file, 'FMOP_getModel', database, 'fmop_element_data', fmop_ident, fmop )

An element field-MOP handle is loaded with the given fmop_ident into your prepared fmop variable. At this point, you can query some interesting properties, like the FCoP[Total] value or the number of active parameters and the model dimension. The latter two parameters are of special interest because they are needed to prepare input arrays for a field-MOP evaluation call.

For example, the following code queries the needed field-MOP input array sizes and evaluates the fmop handle at the given param_values point:

[errno, ~, num_idents] = calllib ( CDLL_lib_file, 'FMOP_getModelParamIdentsDim', fmop, num_idents );
for i = 1 : num_idents-1
    param_ident = calllib ( CDLL_lib_file, 'FMOP_getModelParamIdent', fmop, uint64(i) );
    ...
end
num_mesh_items = uint64 (0);
[errno, ~, num_mesh_items] = calllib ( CDLL_lib_file, 'FMOP_getModelDim', fmop, num_mesh_items );
param_values = [ 43341.092 134.12064 0.093418892 0.042747076 2.7525392e-09 58.798039 ];
approx_field = libpointer( 'doublePtr', zeros ( num_mesh_items, 1 ) );
calllib ( CDLL_lib_file, 'FMOP_approxField', fmop, param_values, approx_field );
% approx_field.Value ( 1:10, 1 )

This can be done as often as a different param_values combination of interest exists and these parameters are within their DOE boundaries.

Finally, to release all handles and licenses:

calllib ( CDLL_lib_file, 'FMOP_releaseModel', fmop );
calllib ( CDLL_lib_file, 'FMOP_releaseDb', database );
calllib ( CDLL_lib_file, 'FMOP_releaseLicenses' );