tchlux / fmodpy

A lightweight, efficient, highly automated, fortran wrapper for python.
MIT License
70 stars 5 forks source link

Compilation of fortran file fails: cannot open shared object file #5

Closed koshMer closed 3 years ago

koshMer commented 3 years ago

Hi, its me again :)

I tried to put several functions in one file test2.f90 and use it in python. However, the compilation of this small program fails. The code in question:


pure function add(val1, val2) result(val3)

    use iso_fortran_env, only: real32
    implicit none

    real(real32), intent(in)    :: val1,val2
    real(real32)            :: val3

    val3 = val1+val2

end function add

pure function triple_add(val1,val2) result(val3)

    use iso_fortran_env, only: real32
    implicit none

    real(real32), intent(in)    :: val1,val2
    real(real32)            :: val3

    val3 = add(val1,val2)*3.0

end function triple_add 

The python code i use to run it:

import fmodpy 
import numpy as np
code = fmodpy.fimport("test.f90",verbose = True)

val1 = np.float32(1)
val2 = np.float32(2)

code.add(1,2)

code.triple_add(1,2)

And the error output:

______________________________________________________________________
fimport

fmodpy configuration:
  autocompile = True
  blas = False
  config_file = '.fmodpy.py'
  debug_line_numbers = False
  end_is_named = True
  f_compiler = 'gfortran'
  f_compiler_args = ['-fPIC', '-shared', '-O3']
  home_directory = '/home/johannes'
  implicit_typing = False
  lapack = False
  link_blas = ['-lblas']
  link_lapack = ['-lblas', '-llapack']
  link_omp = ['-fopenmp']
  log_file = <ipykernel.iostream.OutStream object at 0x7f6acd706bb0>
  omp = False
  rebuild = False
  show_warnings = True
  verbose = True
  wrap = True

=================================================================================
Input file directory:  /home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test
Input file name:       test2.f90
Base module name:      test2
Using build dir:       temporary directory
  fortran wrapper:     test2_c_wrapper.f90
  python wrapper:      test2_python_wrapper.py
Output module path:    /home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test
=================================================================================

Using temporary build directory at '/tmp/tmp2t6pmdpk'.
Build directory is different from source directory..
 sym-linking '/home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test2.f90' to '/tmp/tmp2t6pmdpk/test2.f90'
 sym-linking '/home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test.txt' to '/tmp/tmp2t6pmdpk/test.txt'
 sym-linking '/home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test' to '/tmp/tmp2t6pmdpk/test'

Attempting to autocompile..

 reading 'test2.f90' to check if it can be autocompiled.. yes.
 skipping '/tmp/tmp2t6pmdpk/test.txt'
 skipping '/tmp/tmp2t6pmdpk/test'

Compiling 'test2.f90'.. 
 gfortran -fPIC -shared -O3 -o ./fmodpy_get_size test2.f90
  failed.
----------------------------------------------------------------------
STANDARD ERROR:
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Return type mismatch of function ‘add’ at (1) (UNKNOWN/REAL(4))
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Function ‘add’ at (1) has no IMPLICIT type
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Reference to impure function ‘add’ at (1) within a PURE procedure

----------------------------------------------------------------------
Compiling 'test2.f90'.. 
 gfortran -fPIC -shared -O3 -o ./fmodpy_get_size test2.f90
  failed.
----------------------------------------------------------------------
STANDARD ERROR:
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Return type mismatch of function ‘add’ at (1) (UNKNOWN/REAL(4))
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Function ‘add’ at (1) has no IMPLICIT type
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Reference to impure function ‘add’ at (1) within a PURE procedure

----------------------------------------------------------------------
Failed to compile 'test2.f90'.
Failed to compile 'test2.f90'.

File.parse 
Function.parse ADD
 Function.parse done.
Function.parse TRIPLE_ADD
 Function.parse done.
 File.parse done.
----------------------------------------------------------------------
FILE 
  FUNCTION ADD(VAL1, VAL2) RESULT(VAL3)
    USE ISO_FORTRAN_ENV , ONLY : REAL32
    IMPLICIT NONE
    REAL(KIND=REAL32), INTENT(IN) :: VAL1
    REAL(KIND=REAL32), INTENT(IN) :: VAL2
    REAL(KIND=REAL32) :: VAL3
  END FUNCTION ADD
  FUNCTION TRIPLE_ADD(VAL1, VAL2) RESULT(VAL3)
    USE ISO_FORTRAN_ENV , ONLY : REAL32
    IMPLICIT NONE
    REAL(KIND=REAL32), INTENT(IN) :: VAL1
    REAL(KIND=REAL32), INTENT(IN) :: VAL2
    REAL(KIND=REAL32) :: VAL3
  END FUNCTION TRIPLE_ADD

----------------------------------------------------------------------

Making symlink from '__init__.py' to 'test2_python_wrapper.py'

Moving from:
  /tmp/tmp2t6pmdpk
to
  /home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test2

Finished making module 'test2'.

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Running system command with arguments
   gfortran -fPIC -shared -O3 -o test2.so test2.f90 test2_c_wrapper.f90
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Return type mismatch of function ‘add’ at (1) (UNKNOWN/REAL(4))
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Function ‘add’ at (1) has no IMPLICIT type
test2.f90:26:11:

   26 |  val3 = add(val1,val2)*3.0
      |           1
Error: Reference to impure function ‘add’ at (1) within a PURE procedure
Traceback (most recent call last):

  File "/home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test2/__init__.py", line 28, in <module>
    clib = ctypes.CDLL(_path_to_lib)

  File "/home/johannes/anaconda3/lib/python3.8/ctypes/__init__.py", line 381, in __init__
    self._handle = _dlopen(self._name, mode)

OSError: /home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test2/test2.so: cannot open shared object file: No such file or directory

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File "/home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/python_test_f90.py", line 11, in <module>
    code = fmodpy.fimport("test2.f90",verbose = True)

  File "/home/johannes/anaconda3/lib/python3.8/site-packages/fmodpy/fmodpy.py", line 240, in fimport
    module = importlib.import_module(name)

  File "/home/johannes/anaconda3/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)

  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import

  File "<frozen importlib._bootstrap>", line 991, in _find_and_load

  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked

  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked

  File "<frozen importlib._bootstrap_external>", line 783, in exec_module

  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed

  File "/home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test2/__init__.py", line 42, in <module>
    clib = ctypes.CDLL(_path_to_lib)

  File "/home/johannes/anaconda3/lib/python3.8/ctypes/__init__.py", line 381, in __init__
    self._handle = _dlopen(self._name, mode)

OSError: /home/johannes/Dokumente/Masterarbeit/Fortran/TMM/tmm_test/test2/test2.so: cannot open shared object file: No such file or directory

I am not really sure why I am getting any of those errors when compiling the fortran code. It seems to me that both functions are pure and i state the type for all variables and only use real32....

It works well if I use only the function add(val1,val2). Do I have to structure programs differently? Should I define functions in a fortran module?

tchlux commented 3 years ago

This is an issue because your functions don't "know" about each other. This is a quirk of Fortran, where unlike C it does not automatically know to reference a function declared in the same flat file when you make the call to add from within triple_add. There are three ways to solve this problem. 1) Encapsulate both functions within a module, so that they implicitly have access to each other's interfaces. 2) Explicitly define an interface for the function add within the function triple_add. 3) If you don't need external access to add then you can place it inside of triple_add after a contains keyword.

I think (1) and (3) are self explanatory, but here's an example of what option (2) should look like:

pure function add(val1, val2) result(val3)
  use iso_fortran_env, only: real32
  implicit none
  real(real32), intent(in) :: val1,val2
  real(real32) :: val3
  val3 = val1+val2
end function add

pure function triple_add(val1,val2) result(val3)
  use iso_fortran_env, only: real32
  implicit none
  real(real32), intent(in) :: val1,val2
  real(real32) :: val3
  interface
     pure function add(val1, val2) result(val3)
       use iso_fortran_env, only: real32
       real(real32), intent(in) :: val1,val2
       real(real32) :: val3
     end function add
  end interface
  val3 = add(val1,val2)*3.0
end function triple_add

☝️ this code makes everything compile and run successfully for me.

If you go with option (1), then your calls in python will need to look like code.<module_name>.add and code.<module_name>.triple_add.

tchlux commented 3 years ago

Sorry for any confusion, I just edited my original incorrect response. The above should solve your problem!

koshMer commented 3 years ago

Oh, something I hadn't encountered yet. Up until now I always defined my functions inside modules. Thank you for the small lesson on Fortran! :)

With code.<module_name>.<function_name> and using your suggestion 1) I can access all functions from python without any errors! :)