Table of contents
Table of Contents |
---|
1. Introduction
The document describes how to generate a LinkableComponent on Linux:
- how to compile a shared library for a Fortran 90 engine named <engine>
- how to port a C# wrapper for this shared library from Windows to Linux. The wrapper contains a class for accessing the Fortran
...
- dll <engine>DllAccess and two outer classes <engine>DotNetAccess and <engine>Wrapper
...
2. Technical prerequisites
2.1. Machine and OS of the test system
- workstation with an Intel Xeon processor
- 64bit Suse Linux Edition Desktop SLED 10.3 on the machine with the Fortran compiler
- 64bit openSUSE 11.0 on the machine with the C# compiler
2.2. Mono for multi platform C#
- Mono v. 1.9.1. for openSUSE 11.0 in 64 bit mode. V. 1.9.1. is the concrete term, but it is also referred to as Mono 2.0.
- compiler gmcs 2.0.
2.3. Fortran 90 Compiler
- Intel 64 bit Fortran Compiler v. 9.1.051
...
3. How to generate a shared library for a Fortran engine
...
3.1. General
During runtime the C# wrapper could refers only reference one Fortran shared library. All Fortran sources were compiled to one shared library with the following compiler options <fortranEngine>.so: Compilation:
Panel | ||||
---|---|---|---|---|
| ||||
ifort -c -fPIC -convert big_endian -fpp *.f90 |
...
Panel | ||||
---|---|---|---|---|
| ||||
ifort -shared *.o -o <sharedLibrary<fortranEngine.so>*.o |
During runtime some fortran libraries must be accessible via the environment variable LD_LIBRARY_PATH. In the following examples they are provided by the ifort compiler:.
Panel | ||||
---|---|---|---|---|
| ||||
export LD_LIBRARY_PATH=/opt/intel/fce/9.1.051/lib:. // on 64bit systems |
...
3.2. Ifort parameter bug
The ifort compiler v. 9.1.051 has a bug with public character parameters in modules.
Example:
Panel | ||||||
---|---|---|---|---|---|---|
| ifort -shared *.o -o <sharedLibrary.so>||||||
CHARACTER (LEN=40), PUBLIC, PARAMETER :: c_att_name(2)= & |
The parameter c_att_name can not easily be accessed from outside the modulean external Fortran method. But if a Mono C# application calls a Fortran method, that accesses c_att_name, it will crash without error message. The solution is a Fortran function, that exports the parameter as a return value. Now the values can be accessed from outsideC#.
Panel | ||||
---|---|---|---|---|
| ||||
PUBLIC FUNCTION get_c_att_name ( idx ) & |
3.3. Fortran interface functions
The <fortranEngine>.so interface functions are the same as in Windows Fortran. The two example functions are part of the module gei_ui and will be accessed from C# in 4.2.1.. The first one returns the integer comp_id_len.
Panel | ||||
---|---|---|---|---|
| ||||
FUNCTION gei_component_id_len ( comp_id_len ) RESULT( ok ) |
The second example returns the character string comp_id.
Panel | ||||
---|---|---|---|---|
| ||||
FUNCTION gei_component_id ( comp_idx, comp_id ) RESULT( ok ) |
4. How to port a C# wrapper from Windows to Linux
...
4.1. General
The Mono Guidelines Interop with Native Libaries gives general information about the interface between managed and unmanaged code.
The wrapper has three layers:
- <engine>DllAccess.cs is the inner
...
- class for accessing the Fortran dll;
- <engine>DotNetAccess.cs references <engine>DllAccess.cs;
- <engine>Wrapper.cs is a LinkableComponent and the outer layer.
Compilation of the two inner layers:
Panel | ||||
---|---|---|---|---|
| ||||
gmcs -target:library -out: <engine>DotNetAccess.dll <engine>DllAccess. |
...
cs <engine>DotNetAccess.cs |
Compilation of the outer layer:
Panel | ||||
---|---|---|---|---|
| ||||
gmcs -target:library -out:<engine>Wrapper.dll -pkg:baw-geidotnet.pc, |
...
openmi-backbone.pc,openmi-devsupport.pc,openmi-spatial.pc, |
...
openmi-standard.pc,openmi-wrapper.pc *.cs |
4.2. How to port the individual layers
Fortunately, the original Windows C# code can nearly remain as it is.
4.2.1. <engine>DllAccess.cs
<engine>DllAccess offers access to the Fortran shared library, e.g. @"gei.xe.so". The most of the Windows C# code remains unchanged and is displayed in black colour in the following example. The Fortran module gei_ui, method gei_component_id_len returns the integer comp_id_len.
Panel | ||||
---|---|---|---|---|
| ||||
[DllImport(
|
Some more adjustments are necessary, if a Fortran method returns the character string quantId:
Panel | ||||
---|---|---|---|---|
| ||||
[ DllImport( |
[Out] byte[ ] quantId:
Several guidelines recommend to marshall a variable of type StringBuilder in order to return a Fortran character string:
Panel | ||||
---|---|---|---|---|
| ||||
[MarshalAs(UnmanagedType.LPStr)] StringBuilder quantId |
But on Linux systems StringBuilder can very rarely lead to variables with undefined return values. It is not clear why this happens. The Mono guidelines display a slightly different case, where the Mono garbage collector frees memory before the character string is returned, s. paragraph "GC-Safe P/Invoke code".
However, the variables of type [Out] byte[ ] were always returned correctly and it is recommended to use them on Linux. The StringBuilder remains the better solution for Windows .NET. Developers are invited to find a solution that works on Windows as well as on Linux, e.g. an additional C / C++ wrapper or a SWIG generated wrapper.
4.2.2. <engine>DotNetAccess.cs
Methods accessing int variables, e.g. ComponentIdLen(), remain unchanged from Windows:
Panel | ||||
---|---|---|---|---|
| ||||
public int ComponentIdLen() return compIdLen; |
This example displays that the variable of type byte[ ] from 4.2.1. is externally encoded to the string str:
Panel | ||||
---|---|---|---|---|
| ||||
public string OutputExchangeQuantityId(int compIdx, int outputExchangeN) } |
Furthermore, the Initialize method of this class is a good place for a check, whether the dll is running on Mono or not:
Panel | ||||
---|---|---|---|---|
| ||||
if ( Type.GetType ("Mono.Runtime") == null ) |
4.2.3. <engine>Wrapper.cs
There are no changes compared to Windows C#.
5. Use of the LinkableComponent
Before using the LinkableComponent its location must be known by Linux. It is recommended to put all shared libraries <fortranEngine>.so, <engine>DotNetAccess.dll and <engine>Wrapper.dll in one directory <componentDir>.
Panel | ||||
---|---|---|---|---|
| ||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<componentDir> |
Now the generated LinkableComponent can be connected to any other LinkableComponent. Adding it as a model to the Linux version of the ConfigurationEditor is an easy test, s. How to port the OpenMI from Windows to Linux.