LoginSignup
4
4

More than 3 years have passed since last update.

Windows10でFortranのsubroutineをJuliaから呼び出す

Last updated at Posted at 2020-08-08

はじめに

Juliaを用いて数値計算をする際に、速度の向上などを目的として、一部の処理をFortranに行わせたい場合があります。Juliaには、Fortranのsubroutineを呼び出すための関数がデフォルトで用意されていて、このようなことは簡単に実行できるとされています。しかしながら、Windows10上でやろうとしたときに思わぬ落とし穴があったので、ここにまとめておきたいと思います。

症状

問題が発生したのは以下の環境です。

  • OS: Windows10 (64-bit)
  • Juliaのバージョン: 1.3.1
  • fortranコンパイラ: gfortran 8.1.0 (build by MinGW-W64, i686-posix-dwarf-rev0)

MinGW-W64をインストール後、gfortranのパスを通し終わっていると仮定します。以下のfortranコードを考えます。

greet.f90
module test_module
    implicit none    

contains

    subroutine greet
        write(*, *) "Hello."
    end subroutine

end module test_module

このモジュール自身に問題がないことを確認するために、別のfortranプログラムからsubroutineを呼び出してみます。

main.f90
program main
    use test_module
    implicit none

    call greet
end program main

このプログラムは

>gfortran greet.f90 main.f90

によってコンパイルできます。これを実行すると

>a.exe 
Hello.

のようになり、意図した通りの結果が得られます。

次に、このsubroutineをJuliaから呼び出すことを試みます。
そのためにまず、greet.f90から共有ライブラリを作成します。

>gfortran greet.f90 -fPIC -shared -o greet.dll

生成されたgreet.dllの中身をnmコマンドで見てみます。

>nm greet.dll > log

logを開き、subroutine名であるgreetで検索をかけると、___test_module_MOD_greetという文字列がヒットします。これがいわば「正式な」subroutine名で、Julia側から呼ぶためにはこれを指定する必要があります。ちなみにこの文字列は処理系に依存するようですので、必ずしもこの例のようにはならないかもしれません。(これを解決する方法はありますが、本筋から離れるので後で説明します。)

次にJuliaのコードとして、以下のようなものを用意します。

greet.jl
function greet()
    product = ccall((:___test_module_MOD_greet, "./greet.dll"),
                Nothing,
                ())
end

greet()

これを実行すると

>julia greet.jl
ERROR: LoadError: error compiling greet: could not load library "./greet.dll"
%1 is not a valid Win32 application.

Stacktrace:
(省略)

というエラーが発生します。not a valid Win32 applicationという何やら闇の深そうなメッセージが現れてしまいました。

(追記)同様の問題を解説している記事がありました。

原因

上のエラーは、gccが32bit版であることが原因でした。gccの選択が重要であることは、この方が指摘されています。(明示的には書かれていませんが、同じ問題に直面したのではないかと思います。)今一度gccのバージョンを確認してみると

>gcc --version
gcc (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

と表示されます。i686とあるので、確かにこれは32bit版であることが分かります。(そもそもコンピューターに詳しくないので、gccにも色々種類があるとか、i686が32bitを意味するとか知るよしもありませんでした。)

(注)could not load libraryと出ていることから、一見greet.dllが(何らかの理由で)Juliaから見えないことが原因かのようにも思われますが、カレントディレクトリをLOAD_PATHに追加するなどしても結果は変わりません。従って、not a valid Win32 applicationというメッセージから原因を探る他ないのですが、情報量が少なすぎて困ってしまいます。同様の現象はどうやら早くから知られていたようで、このページで議論された後、ad hocな解決策らしきものが提案されていました。しかしながらここで述べられていることは、fortranコードをコンパイルしてライブラリを作成する工程をJuliaで自動化しているに過ぎず、本質的な解決になっていないように思われます。(少なくとも私の問題は解決しませんでした。)

Win10上でFortranコードを呼び出すまでの手順

以下では、Windows10上にJuliaがインストールされていて、その他はまっさらな状態を仮定します。

gccのインストール

ここで述べる手順は、このページに書かれている方法とまったく同じです。

  • MingW-W64-buildsをダウンロードし、インストーラーを実行する。
  • Settings: Specify setup settings. と聞かれるので、Architectureとしてx86-64を選択する。その他はデフォルトのままでOK.
  • あとはnextを連打してインストールを完了させる。
  • 環境変数にgfortran.exeがあるディレクトリ(例:C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin)を加える。
  • システムを再起動する。

ターミナルでgccのバージョンを確認して、

>gcc --version
GNU Fortran (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

と表示されれば成功です。

Fortranコードのコンパイル

公式ドキュメントにある通り、-fPIC-sharedオプションをつけてコンパイルします。

>gfortran greet.f90 -fPIC -shared -o greet.dll

生成されたgreet.dllの中身をnmコマンドで見てみます。

>nm greet.dll > log

logを開き、subroutine名であるgreetで検索をかけると、__test_module_MOD_greetという文字列がヒットします。先程のときと比べて`test'の前のアンダースコアの数が一つ減っていました。

Juliaから呼び出す

subroutine名以外に変更点はありませんが、念の為コードサンプルを示します。

greet_for_64bitgcc.jl
function greet()
    product = ccall((:__test_module_MOD_greet, "./greet.dll"),
                Nothing,
                ())
end

greet()

これを実行して

>julia greet_for_64bitgcc.jl
Hello.

となれば成功です。お疲れさまでした。

補足1: 共有ライブラリの拡張子

Windowsなので共有ライブラリのファイル名を何となくgreet.dllにしましたが、別にgreet.soでも構いません。

補足2: subroutineの名称

上記のやり方では、subroutine名が__test_module_MOD_greetのように長ったらしいものになる上、処理系にも依存してしまいます。これを回避する方法として、subroutineにBIND(C)属性を指定する方法があります。これについては以下のページで解説されています。

-JuliaからFortranのsubroutineを呼び出す(Qiita)
-JuliaからFortranのsubroutine を呼び出す

今の例では、fortran, juliaのコードはそれぞれ

greet_cbinded.f90
module test_module
    implicit none    

contains

    subroutine greet() bind(c, name = 'greet')
        write(*, *) "Hello."
    end subroutine

end module test_module
greet_cbinded.jl
function greet()
    product = ccall((:greet, "./greet.dll"),
                Nothing,
                ())
end

greet()

のようにすれば良くなります。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4