Including Dynamic Link Library (*.dll) Files

 

Athough PSCAD comes complete with a wide range of built-in component models, there may be times when you need to use your own custom components/models, written in Fortran or C/C++. To distribute these components without revealing the source code, you can create dynamic link libraries (*.dll) or static libraries (*.lib) that contain the compiled (and thereby hidden) code of your components and load it into PSCAD.

 

The purpose of this topic is to explain how to use external libraries in PSCAD, using explicit linking and implicit linking. Explicit linking is used when you only have the *.dll file, while implicit linking is used when you have both the import library file (*.lib) in addition to the *.dll. Implicit linking is easier to implement: It may not work with all the compilers if there is no corresponding import library for that compiler. On the other hand, explicit linking is likely to work with many compilers. One thing to keep in mind is that 32-bit *.dll files will work only with 32-bit compilers and 64-bit *.dll files with 64-bit compilers.

Using only Dynamic Libraries: Explicit Linking

When you may want to use external libraries, but you only have the *.dll file, you can use explicit linking to load the *.dll file and call the functions inside it.

 

First, although there is no need to add *.dll files in the resources folder of your project, it is a good idea to add it as a resource. This will be helpful if the workspace containing the project needs to be consolidated for the purpose of sharing with others. After including the files, you will see them in the resources folder of the project as shown below. For more information, see the Resources Branch topic.

 

 

After that, right-click on the *.dll file, select Settings, you may want to choose the To temporary folder option from the Copy File dropdown menu as shown below. This action will copy the *.dll file to the project temporary folder when you run the simulation. This is only needed if you want to use a shorter path name to the *.dll file in the GET_DLL_HANDLE subroutine that will be explained later.

 

 

The next step to use the functions defined in the *.dll file is to create a custom Fortran module in which you will:

Now, we will explain each step in detail.

  1. Load the required modules. ISO_C_BINDING and PSCAD_DLL_INTERFACE can be loaded in Fortran by doing USE, INTRINSIC :: ISO_C_BINDING and USE PSCAD_DLL_INTERFACE. The ISO_C_BINDING module is used to define the variable types that will be used in the functions, while the PSCAD_DLL_INTERFACE module helps to load the DLL and the functions inside it. ISO_C_BINDING provides access to necessary variable types such as c_int, c_double, and c_bool among others.

  2. Create an interface to the functions inside the *.dll. In Fortran, interfaces are defined within an interface block. For example, if we have a *.dll with two functions, function1 and function2, where function1 has no input or output arguments and function2 has two arguments (variable1 and variable2), the interface can be defined as follows:

    module myExplicitDLL1
        ! Load the required modules
        implicit none
        ! Define the interface to functions
        interface
            subroutine function1Interface() bind(C, name="function1")
                use iso_c_binding
                implicit none
            end subroutine function1Interface
            subroutine function2Interface(variable1, variable2) bind(C, name="function2")
                use iso_c_binding
                implicit none
                real(c_double), intent(in) :: variable1
                real(c_double), intent(out) :: variable2
            end subroutine function2Interface
        end interface
        ! Create a handler for the DLL
        ! Create function pointers
        ! Create procedure pointers to the interface functions
    end module myExplicitDLL1
    

    Note: The name of the C/C++ functions is declared using the name attribute in the bind statement. The argument name is case-sensitive, so it is important to use the same name that is in the C/C++ code.

  3. Create a handler of the DLL. The module PSCAD_DLL_INTERFACE contains a struct type DLL_HANDLE that is used to handle the *.dll. To create a handler, you can use the following code: type (DLL_HANDLE) :: handlerName.

  4. Create function pointers. Function pointers are used to store the address of the functions inside the *.dll. To create a function pointer, you can use the following code: type(FUNCTION_POINTER) :: function1Ptr.

  5. Create procedure pointers to the interface functions. Procedure pointers are used to call the functions inside the *.dll. To create a procedure pointer, you can use the following code: procedure(function1Interface) :: function1. Note that the name of the procedure pointer must match the name of the interface function.

The complete module will look like this:

module myExplicitDLL1
    ! Load the required modules
    use, intrinsic :: iso_c_binding
    use pscad_dll_interface
    implicit none
    ! Define the interface to functions
    interface
        subroutine function1Interface() bind(C, name="function1")
            use iso_c_binding
            implicit none
        end subroutine function1Interface
        subroutine function2Interface(variable1, variable2) bind(C, name="function2")
            use iso_c_binding
            implicit none
            real(c_double), intent(in) :: variable1
            real(c_double), intent(out) :: variable2
        end subroutine function2Interface
    end interface
    ! Create a handler for the DLL
    type (DLL_HANDLE) :: handlerName
    ! Create function pointers
    type(FUNCTION_POINTER) :: function1Ptr
    type(FUNCTION_POINTER) :: function2Ptr
    ! Create procedure pointers to the interface functions
    procedure(function1Interface) :: function1
    procedure(function2Interface) :: function2
end module myExplicitDLL1

After creating the module, you still need to load the *.dll and assign the function pointers to the functions inside the *.dll. This can be done using the subroutines GET_DLL_HANDLE, GET_DLL_FUNCTION_ADDRESS, and ASSOCIATE_FUNCTION_POINTER from the PSCAD_DLL_INTERFACE module.

 

GET_DLL_HANDLE is used to load the *.dll and get a handler to it. GET_DLL_FUNCTION_ADDRESS is used to get the address of the functions inside the *.dll. ASSOCIATE_FUNCTION_POINTER is used to associate the function pointers with the functions inside the *.dll.

 

GET_DLL_HANDLE has two arguments: the handler (e.g. handlerName) and the *.dll path + name. This allows to load a *.dll in any directory. GET_DLL_FUNCTION_ADDRESS has three arguments: the handler, the function pointer, and the name of the function in C/C++. ASSOCIATE_FUNCTION_POINTER has two arguments: the function pointer and the procedure pointer.

 

Normally, the *.dll will be loaded at the beginning of the program. Therefore, it is recommended to create a subroutine to load the *.dll at the beginning of the program. The following code shows an example of how to load the *.dll and assign the function pointers using a subroutine:

subroutine initialize_DLL()
    use myExplicitDLL1
    
    ! Load the DLL from a different directory
    ! call GET_DLL_HANDLE(handlerName, "pathToMyDLL\myfunctions.dll")
    ! or Load the DLL considering that it is copied to the same directory as the executable
    ! call GET_DLL_HANDLE(handlerName, ".\myfunctions.dll")
    call GET_DLL_HANDLE(handlerName, "..\Resources\myfunctions.dll")
    ! Get the address of the functions
    call GET_DLL_FUNCTION_ADDRESS(handlerName, function1Ptr, "function1")
    call GET_DLL_FUNCTION_ADDRESS(handlerName, function2Ptr, "function2")
    ! Associate the function pointers with the functions
    call ASSOCIATE_FUNCTION_POINTER(function1Ptr, function1)
    call ASSOCIATE_FUNCTION_POINTER(function2Ptr, function2)
end subroutine initialize_DLL

After loading the *.dll and assigning the function pointers, you can call the functions inside the *.dll using the procedure pointers in a custom component. The following code shows an example of how to call the functions, considering that variable1 and variable2 are real variables defined in the custom component:


! initialize the DLL if it is the first timestep
if (FIRSTSTEP) then
    call initialize_DLL()
end if
! Call the functions
call function1()
call function2($variable1, $variable2)

Complete example of Explicit Linking

Suppose that you created a *.dll file named myfunctions.dll from the following C++ file:

// myfunctions.cpp
#include <iostream>
#include <cstdbool> 
extern "C" {
    __declspec(dllexport) int addFromDll(int a, int b) {
        return  a + b;
    }
    __declspec(dllexport) double multiplyFromDLL(double a, double b) {
        return  a * b;
    }
    __declspec(dllexport) double hello() {
        std::cout << "Hello from DLL" << std::endl;
        return 3.1416;
    }
    // Receive a logical element and prints it
    __declspec(dllexport) int printLogical(bool value) {
        if (value) {
            std::cout << "T" << std::endl;
        } else {
            std::cout << "F" << std::endl;
        }
        return 1;
    }
    // Receive an array of logical elements and print them
    __declspec(dllexport) int printLogicalArray(bool* array, int size) {
        for (int i = 0; i < size; i++) {
            if (array[i]) {
                std::cout << "T ";
            } else {
                std::cout << "F ";
            }
        }
        std::cout << std::endl;
        return 1;
    }
}

Note: For more information on how to create a *.dll from a C++ file, you could see the Mirosoft Walkthrough: Create and use your own Dynamic Link Library (C++) topic.

Now, we will create a Fortran file named explicitModule.f90 that will define the interface to the functions inside the *.dll and the helper subroutines to load and use the required functions. The file will look like this:

module myExplicitDLL1
    use, intrinsic :: ISO_C_BINDING
    use pscad_dll_interface
    implicit none
    ! Define the interface for the functions in the DLL
    interface
        function addFromDllInterface(a, b) bind(C, name="addFromDll")
            import :: C_INT
            integer(C_INT) :: addFromDllInterface
            integer(C_INT), value :: a, b
        end function addFromDllInterface
        function multiplyFromDLLInterface(a, b) bind(C, name="multiplyFromDLL")
            import :: C_DOUBLE
            real(C_DOUBLE) :: multiplyFromDLLInterface
            real(C_DOUBLE), value :: a, b
        end function multiplyFromDLLInterface
        function printLogicalValueInterface(val) bind(C, name="printLogical")
            import :: C_BOOL, C_INT
            integer(C_INT) :: printLogicalValueInterface
            logical(C_BOOL), intent(in), value :: val
        end function printLogicalValueInterface
        function printLogicalArrayInterface(arr, n) bind(C, name="printLogicalArray")
            import :: C_BOOL, C_INT
            integer(C_INT) :: printLogicalArrayInterface
            logical(C_BOOL), intent(in) :: arr(n)
            integer(C_INT), value :: n
        end function printLogicalArrayInterface
    end interface
    ! Create the DLL handler
    type (DLL_HANDLE) :: MyHandle
    ! Create function pointers
    type (FUNCTION_POINTER) :: addFromDllPtr, multiplyFromDLLPtr, printLogicalValuePtr, printLogicalArrayPtr
    ! Create procedure pointers to the interface functions
    procedure(addFromDllInterface), pointer :: addFromDll
    procedure(multiplyFromDLLInterface), pointer :: multiplyFromDLL
    procedure(printLogicalValueInterface), pointer :: printLogicalValue
    procedure(printLogicalArrayInterface), pointer :: printLogicalArray
end module myExplicitDLL1
subroutine initializeExplicitDLL()
    use myExplicitDLL1
    implicit none
    ! Load the DLL considering that it is copied to the same directory as the executable
    call GET_DLL_HANDLE(MyHandle, ".\myfunctions.dll")
    ! Load the DLL from a different directory
    ! call GET_DLL_HANDLE(MyHandle, "..\myfunctions.dll")

    ! Get the address of the functions
    call GET_DLL_FUNCTION_ADDRESS(MyHandle, addFromDllPtr, "addFromDll")
    call GET_DLL_FUNCTION_ADDRESS(MyHandle, multiplyFromDLLPtr, "multiplyFromDLL")
    call GET_DLL_FUNCTION_ADDRESS(MyHandle, printLogicalValuePtr, "printLogical")
    call GET_DLL_FUNCTION_ADDRESS(MyHandle, printLogicalArrayPtr, "printLogicalArray")
    ! Associate the function pointers with the functions
    call ASSOCIATE_FUNCTION_POINTER(addFromDllPtr, addFromDll)
    call ASSOCIATE_FUNCTION_POINTER(multiplyFromDLLPtr, multiplyFromDLL)
    call ASSOCIATE_FUNCTION_POINTER(printLogicalValuePtr, printLogicalValue)
    call ASSOCIATE_FUNCTION_POINTER(printLogicalArrayPtr, printLogicalArray)
end subroutine initializeExplicitDLL
subroutine testExplicitDLL()
    use myExplicitDLL1
    implicit none
    
    integer(c_int) :: a, b
    real(c_double) :: c, d
    logical(c_bool) :: e
    integer, parameter :: n = 5
    logical(c_bool) :: arr(n)
    a = 5
    b = 10
    c = 5.0
    d = 10.0
    e = .true.
    arr = [.true., .false., .true., .false., .true.]
    print *, "Starting testExplicitDLL"
    print *, "addFromDll(5, 10) = ", addFromDll(a, b)
    print *, "multiplyFromDLL(5.0, 10.0) = ", multiplyFromDLL(c, d)
    print *, "printLogical(.true.) = ", printLogicalValue(e)
    print *, "printLogicalArray([.true., .false., .true., .false., .true.], 5) = ", printLogicalArray(arr, n)
end subroutine testExplicitDLL

Afterwards, we will create a custom component in PSCAD with a Fortran Segment in which we will load the *.dll at the first time step, and then call the testExplicitDLL subroutine in the next time step. The code will look like this:

    if (timezero) then
        call initializeExplicitDLL()
    end if
    if ( abs(time - delt) .le. 1e-12 ) then
        call testExplicitDLL()
    end if

Finally, include the files myfunctions.dll and explicitModule.f90 in the resources folder of the project. Ensure that the *.dll settings are configured to copy the *.dll to the current folder since it is loaded from the current working directory in the example.

 

Using Dynamic Libraries with Associated Import Libraries: Implicit Linking

Using a combination of dynamic libraries with associted import libraries in PSCAD is simpler than using only dynamic libraries. However, *.lib files are not always distributed along the *.dll. The main difference in this approach is that you do not need to load the *.dll using the PSCAD_DLL_INTERFACE module. Instead, you can directly define the interface to the functions and use them in your custom component.

 

Additionally, you need both the import library file (*.lib) and the dynamic library file (*.dll) to be in the resources folder of your project as shown below. Also, remember to set the *.dll settings to be copied to the current folder as shown below:

 

 

The following code shows an example of how to define the interface to the functions inside a dynamic library:

module myImplicitDLL1
    ! Load the required module
    use, intrinsic :: iso_c_binding
    implicit none
    ! Define the interface to functions
    interface
        subroutine function1Interface() bind(C, name="function1")
            use iso_c_binding
            implicit none
        end subroutine function1Interface
        subroutine function2Interface(variable1, variable2) bind(C, name="function2")
            use iso_c_binding
            implicit none
            real(c_double), intent(in) :: variable1
            real(c_double), intent(out) :: variable2
        end subroutine function2Interface
    end interface
end module myImplicitDLL1

After having the module, you can create subroutines to call the functions inside the dynamic library. The following code shows an example of how to call the functions:

subroutine function1()
    use myImplicitDLL1
    call function1Interface()
end subroutine function1
subroutine function2(variable1, variable2)
    use myImplicitDLL1
    real(c_double), intent(in) :: variable1
    real(c_double), intent(out) :: variable2
    call function2Interface(variable1, variable2)
end subroutine function2

As in the previous example, you can call the functions inside the dynamic library using the subroutines in a custom component.

Workaround when the import library is not available

When you only have the *.dll file but want to use implicit linking in PSCAD, you can generate an import library from the *.dll file.

 

To generate an import library from a *.dll, first create a Module Definition *.def file that contains the names of the functions exported from the *.dll. This can be done using the Visual Studio tool dumpbin as follows:

dumpbin /exports dllName.dll > dllName.def

This file need to be edited to satisfy the syntax rules for *.def files. After creating the *.def file, use the lib tool that comes with Visual Studio to generate the import library:

lib /def:dllName.def /out:dllName.lib /machine:machineArchitecture

Replace machineArchitecture with the appropriate architecture (e.g., x86, x64). For example, to create an import library for a 64-bit compiler, use:

lib /def:dllName.def /out:dllName.lib /machine:x64

This will create an import library file named dllName.lib and a .exp file containing the exported functions from the *.dll. Include the *.lib file in the resources folder of your project and use implicit linking as explained in the previous section.

Note: Ensure you have Visual Studio installed and either use the Intel OneAPI command prompt or set the environment variables to the paths where the Visual Studio tools are located.

Complete example of Implicit Linking

As in the previous example, suppose that you created a *.dll file named myfunctions.dll and an import library file named myfunctions.lib from the same C++ file.

 

Now, we will create a fortran file named implicitModule.f90 that will define the interface to the functions inside the *.dll and the helper subroutines to load and use the required functions. The file will look like this:

module myImplicitDLL1
    use, intrinsic :: iso_c_binding
    implicit none
    interface
        function addFromDll(a, b) bind(C, name="addFromDll")
            import :: C_INT
            integer(C_INT) :: addFromDll
            integer(C_INT), value :: a, b
        end function addFromDll
        function multiplyFromDLL(a, b) bind(C, name="multiplyFromDLL")
            import :: C_DOUBLE
            real(C_DOUBLE) :: multiplyFromDLL
            real(C_DOUBLE), value :: a, b
        end function multiplyFromDLL
        function printLogicalValue(val) bind(C, name="printLogical")
            import :: C_BOOL, C_INT
            integer(C_INT) :: printLogicalValue
            logical(C_BOOL), intent(in), value :: val
        end function printLogicalValue
        function printLogicalArray(arr, n) bind(C, name="printLogicalArray")
            import :: C_BOOL, C_INT
            integer(C_INT) :: printLogicalArray
            logical(C_BOOL), intent(in) :: arr(n)
            integer(C_INT), value :: n
        end function printLogicalArray
    end interface
end module myImplicitDLL1
subroutine testImplicitDLL()
    use myImplicitDLL1
    implicit none
    integer(c_int) :: a, b
    real(c_double) :: c, d
    logical(c_bool) :: e
    integer, parameter :: n = 5
    logical(c_bool) :: arr(n)
    a = 5
    b = 10
    c = 5.0
    d = 10.0
    e = .true.
    arr = [.true., .false., .true., .false., .true.]
    print *, "Starting testImplicitDLL"
    print *, "addFromDll(5, 10) = ", addFromDll(a, b)
    print *, "multiplyFromDLL(5.0, 10.0) = ", multiplyFromDLL(c, d)
    print *, "printLogical(.true.) = ", printLogicalValue(e)
    print *, "printLogicalArray([.true., .false., .true., .false., .true.], 5) = ", printLogicalArray(arr, n)
end subroutine testImplicitDLL

Note that in this case, we do not need to load the *.dll using the PSCAD_DLL_INTERFACE module, retrieve the function addresses, or associate the functions. Instead, we can directly define the interface to the functions and use them in the testImplicitDLL subroutine. This method is simpler than using dynamic libraries with explicit linking, but it requires including both the import library file and the dynamic library file in the resources folder of the project.

Additional Examples and Resources