I wrote a Fortran code and make it TestDLL.dll and TestDLL.lib file. I want use that in OpenModelica. But the Modelica say that
could not find library TestDLL in either of:~~~
I put .dll and .lib file in E:\MODELICAEXAM (my modelica work space) or E:\MODELICAEXAM\MyPackage\Resouces, but it doesn't work.
I read the ModelicaSpec34 document(12.9.4) which describe about "annotation", but I really confuse about what is "modelica://".
So I have two question about that:
can I use fortran dll in the modelica?
If first question is ok, where should I put my lib and dll file to make modelica find these files? or I misunderstood the question?
The code as below
function SUB(a,b)
!DEC$ ATTRIBUTES DLLEXPORT::SUB
implicit none
real :: a,b
real :: SUB
SUB = a+b
return
end
modelica part
model test
function sub
input Real a;
input Real b;
output Real Result;
external "C" Result = sub(a, b);
annotation(
Library = "TestDLL",
LibraryDirectory =
"modelica://MyPackage/Resouces");
end sub;
Real result;
parameter Real a = 1;
parameter Real b = 2;
equation
result = sub(a, b);
end test;!
Thanks for your answers. I have solved this question. Fortran make the variable to capital form automatically. When I use this function in modelica, I must use capital form to call the function. The "modelica://" means the file folder where the. mo file located.
Related
I need to create and use dynamic-link library (DLL) for Fortran application using Compaq Visual Fortran 6.6. The following code works just fine:
PROGRAM AMAIN1
IMPLICIT NONE
REAL(8):: A,B,S
A = 1D0
B = 2D0
CALL SUBRO1(A,B,S)
PRINT*, 'S = ', S
END PROGRAM AMAIN1
SUBROUTINE SUBRO1(A,B,S)
!DEC$ ATTRIBUTES DLLEXPORT :: SUBRO1
IMPLICIT NONE
REAL(8):: A,B,S
S = A + B
RETURN
END SUBROUTINE SUBRO1
The result is correct:
S = 3.00000000000000
Press any key to continue
However, if I implement the same algorithm using the module, I get inconsistent result (i.e. zero):
PROGRAM AMAIN2
USE MODUL2
A = 1D0
B = 2D0
CALL SUBRO2
PRINT*, 'S = ', S
END PROGRAM AMAIN2
MODULE MODUL2
IMPLICIT NONE
REAL(8):: A,B,S
END MODULE MODUL2
SUBROUTINE SUBRO2
!DEC$ ATTRIBUTES DLLEXPORT :: SUBRO2
USE MODUL2
S = A + B
RETURN
END SUBROUTINE SUBRO2
The result is incorrect:
S = 0.000000000000000E+000
Press any key to continue
As can be seen above, DLL contains only subprogram in both cases (SUBRO1 and SUBRO2, respectively). I have built DLL and LIB files from the visual development environment. The second case (with the use of module) represents the structure of my large source-code so I need to resolve this issue. Any advice would be greatly appreciated.
BTW, the same algorithm without using the DLL works well and gives correct result:
PROGRAM AMAIN3
USE MODUL3
A = 1D0
B = 2D0
CALL SUBRO3
PRINT*, 'S = ', S
END PROGRAM AMAIN3
MODULE MODUL3
IMPLICIT NONE
REAL(8):: A,B,S
END MODULE MODUL3
SUBROUTINE SUBRO3
USE MODUL3
S = A + B
RETURN
END SUBROUTINE SUBRO3
The result is correct:
S = 3.00000000000000
Press any key to continue
You need to add:
!DEC$ ATTRIBUTES DLLEXPORT :: A,B,S
to the module, so that the main program can see the module variables from the DLL. Otherwise A, B and S are local variables.
Edit: January 16, 2019
I was able to log in to Bakhbergen's PC and eventually figured out the problem.
In CVF 6.6C (and the later Intel compilers), when you USE a module that has a DLLEXPORT directive, that turns into a DLLIMPORT, hence my advice above. But it wasn't always this way, and the version he has doesn't have that behavior. Before that change (which my memory says I lobbied for), you had to supply a separately compiled .mod where the source had DLLIMPORT instead of DLLEXPORT. When I did this, the program worked. I don't remember exactly which update had this change.
So what he needs to do is have two versions of MODUL2.f90, one with DLLEXPORT and one with DLLIMPORT. The DLLEXPORT version gets built into the DLL. The DLLIMPORT version would just be compiled (/c) and only the .mod used, not the ,obj, when linking the main program. Messy, I know, which is why we changed it.
I am new to Fortran and trying to understand if the following is possible. My idea to structure the program is to declare the precision and variable types in one module. Then make use of those variables without declaring again the type in other modules or the main program.
module pre
implicit none
INTEGER, PARAMETER :: sp=SELECTED_REAL_KIND(6,37)
INTEGER, PARAMETER :: dp=SELECTED_REAL_KIND(15,307)
INTEGER, PARAMETER :: qp=SELECTED_REAL_KIND(33,4931)
REAL(dp), PARAMETER :: pi = 4.*ATAN(1.)
REAL(dp) :: H
REAL(dp) :: M
REAL(dp) :: KR
end module pre
Now I want to make use of all the variables in another module that contains one or more functions, such as:
module hon
use pre
implicit none
contains
function KE(H,M) result(KR)
KR = 2*PI/H/M
end function KE
end module hon
Then I use gfortran in this order:
gfortran -c mod_pre.f90
gfortran -c mod_hon.f90
Since 'module pre' is part of 'module hon' I compile in order, but gfortran shows an error.
With the code above I understand the variable types and parameters should have been included by USE; But the message I get from gfortran is that none of my variables have IMPLICIT type when I try to compile 'module hon'.
Could somebody clarify the problem or suggest a solution? I would like to avoid having my variables scattered in multiple modules.
Thanks!
In the function statement, the result(kr) says that the function result has name kr. This function result is not the same thing as the module variable kr. In particular, this function result makes inaccessible the module variable.
The function result is specific to the function itself and its properties must be declared within the function subprogram.
Similarly, the dummy arguments of the function, H and M, are distinct from the module variables and need to be declared in the function subprogram.
Beyond that, you perhaps have similar concerns to this other question.
To be clear, it isn't possible to say something like "all function results called kr and all dummy arguments called H or M have these characteristics". Each individual object must be given the properties.
However, although I don't recommend this, this is a situation where literal text inclusion (using a preprocessor or include file) could help you:
function ke(H, M) result (kr)
include 'resdummydecls'
...
end function
where the file has the declarations.
I have a module which holds global variables. To declare some global variables, I need to use HDF5. I am also using a library, so I also need to include a header file. So the preamble of global_variable.F90 looks like this.
module global_variables
use HDF5
#include "finclude/petscsys.h"
#include "finclude/petscvec.h"
integer(HID_T) id_file
integer(HID_T) id_plist
Vec M, C, K
...
end module
Vec is a data type defined in the header file and HID_T is a data type defined in HDF5 module.
Now, I have a file which holds subroutines for I/O. This file also uses HDF5 and the same library used in global_variables.F90. So IO.F90 looks like this.
module io
use global_varibles
contains
subroutine read_input_file( vector )
Vec vector
integer HDF5err
call H5open_f( HDF5err )
...
end subroutine
end module
Question 1: compiler returns error when compiling IO.F90, saying that Vec is undefined data type. But it does not complain about HID_T. I thought global_variables module already contains both HDF5 modules and header files, using global_variables module in IO.F90 will handle every data type declaration but it seems not. Could you please help me understand what I am understanding wrong?
Question 2: Is there a way to restrict the effect of #include to the module where it is declared?
PS. If I include #include "finclude/petscvec.h" in IO.F90, which declares Vec, then it compiles well.
The syntax
Vec vector
is completely alien to Fortran. It works only because Vec is a C pre-processor (CPP) macro defined in the header file "finclude/petscvec.h" as
#define Vec PetscFortranAddr
That means that you must include the header file in every Fortran file in which you use the above syntax with Vec. The macro cannot be inherited using the Fortran use because it is not a part of Fortran.
The PetscFortranAddr is in the end defined in "finclude/petscdef.h" as an integer with 4 or 8 bytes depending on your system.
There is probably nothing you can do except reverse engineering what it in the end the pre-processor makes to be, but I wouldn't go that way, it may be unportable.
Modelica modeling language supports calling functions from external FORTRAN DLLs, however, MapleSim only includes the support of calling functions from C DLLs even though it says it has full support for the Modelica language, and I have no choice but to use this FORTRAN DLL (I can't reprogram it in C nor I can use other Modelica evironments than MapleSim), so I'm not in the undocumented features realm.
I tried forcing MapleSim to include the FORTRAN DLL and created a custom component but its not working. This is the code I got when I opened the custom component: [relevant portion only]
model ExternalCode
function GETPSAT
input Real TC;
output Real PC;
external "C" PC = GETPSAT(TC)
annotation (
Library = "C:/Path/To/My/DLL/FORTRAN.dll", __Maplesoft_callconv = "stdcall");
end GETPSAT;
equation
(PC) = GETPSAT(TC);
annotation (
experiment(__Maplesoft_engine = 2));
end ExternalCode;
I know I should change external "C" to external "FORTRAN", but what should I should I do with __Maplesoft_callconv = "stdcall"? ie: what is the call convention for FORTRAN functions? (I know nothing on the subject of calling conventions).
Note the parent evnironment of MapleSim 6.1 (Maple 17) supports importing external functions from FORTRAN so I think there is a possibility MapleSim will support it despite it being undocumented.
edit: By the way, the DLL was compiled with Compaq visual fortran (I don't remember the version)
edit2: The function in the FORTRAN DLL is exported as follows:
FUNCTION GETPSAT(TC)
!DEC$ ATTRIBUTES ALIAS:'GETPSAT' :: GETPSAT
!DEC$ ATTRIBUTES DLLEXPORT :: GETPSAT
!DEC$ ATTRIBUTES VALUE :: TC
GETPSAT=PSAT11(TC)
RETURN
END
edit3: I don't know it this helps, but the same function can be called from C# like this:
[DllImport("C:\\Path\\To\\My\\DLL\\FORTRAN.dll")]
static extern float GETPSAT(float T);
This particular Fortran function should behave like a completely normal C function because of the attributes it has. Do not add any external(FORTRAN) or similar. Its name should be GETPSAT and it accepts 1 float TC by value.
Use the same "stdcal" attribute, it is default for the DEC, Compaq and Intel Fortran https://software.intel.com/sites/products/documentation/hpc/mkl/mkl_userguide_win/GUID-E74229B0-7389-46A6-9FCF-91CD6CD5B0E4.htm
I'm currently working on being able to import a DLL written in Fortran into Visual Basic. I've got all the basics down, so now I'm trying to take it a step further. The title basically says it all, but I'll explain what it is I'm trying to do anyways.
For kicks and giggles, let's just assume I want to pass an object that has three double values in it, possibly representing a point in space in three dimensions. In my Fortran method, I want to take that object, print out the x value, then change the x value to 7.5. Here's my Fortran code that does just that.
module test
type Point
double precision x, y, z
end type Point
end module test
function ex1(ThreeDubs)
use test
type (Point) :: ThreeDubs
print *, ThreeDubs%x
ex1 = 1
return
end function
And this code works great!...For structures only. In other words, Let's assume I have the following structure and class in VB
Public Structure StructurePoint
Public x As Double
Public y As Double
Public z As Double
End Structure
Public Class ObjectPoint
Public x As Double
Public y As Double
Public z As Double
End Class
Creating an instance of StructurePoint yields perfect results: the Fortran method prints out the x value, and then modifies the value of x. Perfect. Now the problem. When I pass an instance of ObjectPoint, the program prints out a value similar to 1.523E-306. Basically, telling me that the location in which it thinks the x value is located is not the x value. So, herein lies my question. Is it even possible to pass an Object to a Fortran DLL and access it correctly, and if so, how would I go about doing so?
The Solution
Modifying the Class declaration is the ONLY thing that has to be done in order to pass this object to Fortran.
<StructLayout(LayoutKind.Sequential)> _
Public Class CustomPoint3d
Public x As Double
Public y As Double
Public z As Double
End Class
<DllImport("passPoint3d.dll")> _
Public Shared Function PrintX(ByVal point As CustomPoint3d) As Boolean
End Function
This might be difficult, and I don't think there's any benefit, so I advise you not to bother!
Here goes anyway! I think the VB.Net object will be marshalled as a pointer. There is some support for pointers in Fortran 90. It might work if you add pointer to the Fortran declaration of ThreeDubs.
function ex1(ThreeDubs)
use test
type (Point), pointer :: ThreeDubs ! Note additional pointer keyword
print *, ThreeDubs%x
ex1 = 1
return
end function
I doubt you would ever be able to call methods in the ThreeDubs object from the Fortran, so I'm not sure of the benefit of passing an object.
Here are two articles on PInvoke: PInvoke is the .Net name for calling oldschool "unmanaged" DLLs like your Fortran. The articles explain how .Net arguments are "marshalled" (translated) into the Fortran DLL. You have some control over the marshalling using attributes: the articles explain more. They tend to use C and C# for examples :(
I don't know about Visual Basic, but I've shared user-defined types between Fortran & C. This is supported by the ISO C Binding of Fortran 2003, which is already widely supported by Fortran 95 compilers. You declare a Fortran user-defined type and give it the bind C attribute and a bind name. As long as all of the sub-components of the type are inter-operable with C, then the user-defined type is also inter-operable with C. And this method is standard and portable. I've done this by passing the information with the Fortran user-defined type variable being a module variable, and the C structure variable being in global memory via being declared at the top of a file. Perhaps you can use the ISO C Binding on the Fortran side, and somehow tell Visual Basic to use a C-like interface?