Fortranから使うPLplot入門 #1

1. Fortranから使うPLplot入門 #1 - インストールとHello World（この記事）
2. [Fortranから使うPLplot入門 #2](https://shinobuamasaki.github.io/items/introduction-to-plplot-using-fortran-002.html) - 直線・曲線・散布図を描く

## はじめに

現代のさまざまなプログラミング環境においては、グラフィカルな機能を備えるプログラミング言語が多いと思われます。例えばR言語、Pythonのmatplotlib、Julia言語のPlots.jlなどが例に挙げられます。数値計算アプリケーションを書くユーザーが、計算処理とデータ処理・グラフの作成をすべて同じ言語で行いたいと考えるのは自然な発想と言えるでしょう。

一方のFortranでは、標準で定められている機能にはそのようなものはありません。しかしながら、グラフや図の作成のためのFortranバインディングが提供されているフリーのライブラリはいくつか存在します。今回紹介する**PLplot**というソフトウェアはそのうちの一つです。

[PLplot](https://plplot.sourceforge.net/)は、科学技術の分野でグラフなどの図を作成するために作られたプロットツールのライブラリで、Fortranを含むさまざまなプログラミング言語から利用可能です。このツールはクロスプラットフォームに対応しているので、Unix系のOSだけでなくWindows上でも使うことができます。これはLGPLでライセンスされたフリーソフトウェアです。

これはFortranプログラムから他の言語を使わず直接に（PLplot自体はC言語で開発されていますが）図を描画できる数少ない選択肢の一つなのですが、残念ながら日本語で書かれた情報はほとんど存在しないようなので、今回から数回に分けて記事を書くことにしました。

初回となる本稿では、PLplotのインストールから2次元の簡単な図を作成するところまでをできるだけ詳細に解説します。

### 前提

[Fortran Package Manager](https://fpm.fortran-lang.org/)（`fpm`）とGNU Fortranコンパイラ（`gfortran`）がインストールされていて、ターミナルから利用可能であることを前提に話を進めます。

## インストール

ここでは、PLplotのインストール方法について簡単に解説します。

クロスプラットフォームに対応しているので、Windows、Mac、Linux（Ubuntu）でのインストール方法をそれぞれ述べます。インストール方法を確認した環境は次のとおりです。

- Windows 10 Pro 22H2
- macOS Sonoma 14.5 (MacBook Air, Apple M1, 2020)
- Ubuntu 22.04.5 LTS (Jammy Jellyfish)

このソフトウェアは、Fortranのモジュールファイル`plplot.mod`の配置される場所が少しわかりにくいので注意してください。

なお、ソースコードからビルドしてインストールする方法については本稿では扱いませんが、いずれ別の記事で書くかもしれません。

### Windows

Windows環境においては、簡単にインストールするにはMinGW-w64/Msys2を使うのが最も簡単です。以下ではIntelの64bitプロセッサで構成されたシステムを想定します。

1. Msys2のターミナルを開く。
2. `pacman -S mingw-w64-x86_64-plplot`を実行する。

これでインストールは完了します。

MSYS2のディレクトリがCドライブ直下の`C:\Msys2`にある場合、`plplot.mod`は`C:\Msys2\mingw64\lib\fortran\modules\plplot`ディレクトリに配置されます。MSYS2のターミナルで`fpm run`を実行する時に、`--flag "-I/mingw64/lib/fortran/modules/plplot"`を指定するとコンパイラが`plplot.mod`を見つけてくれます。もしくは環境変数`FPM_FFLAGS`と`FPM_LDFLAGS`に書き込んでもよいでしょう（`fpm help`を参照）。

なお、この方法では環境変数の設定の問題のため、このままではPowerShellなどのMsys2ではないターミナルからは利用することはできません。

### macOS

Macでは、MacPortsかHomebrewのどちらかのパッケージマネージャを使う方法が簡単です。

#### MacPorts

管理者権限で`sudo port install plplot`を実行してインストールします。

MacPortsで管理されるパッケージの標準のインストール先は`/opt/local`なので、共有ライブラリは`/opt/local/lib`ディレクトリに、モジュールファイルは`/opt/local/lib/fortran/modules/plplot`ディレクトリに配置されます。そのため、`fpm run`の実行時に`--flag "-I/opt/local/lib/fortran/modules/plplot"`と`--link-flag "-L/opt/local/lib"`を指定する必要があります。もしくは環境変数`FPM_FFLAGS`と`FPM_LDFLAGS`に書き込んでもよいでしょう（`fpm help`を参照）。

#### Homebrew

管理者権限で`sudo brew install plplot`を実行してインストールします。

HomebrewのパッケージはIntel CPUとApple siliconでインストール先が異なります。そのためインストール後は`locate plplot.mod`のコマンドなどでモジュールファイルの配置を取得し、`fpm run`の実行時には、MacPortsの場合と同様に`--flag`（必要であれば`--link-flag`も）を指定してください。

### Ubuntu

管理者権限で`apt`コマンドを次のように実行して、インストールします。

````bash
sudo apt install libplplot-dev libplplotfortran0 plplot-driver-cairo
````

この時、`plplot.mod`は`/usr/lib/x86_64-linux-gnu/fortran/modules/plplot`ディレクトリ下に配置されるので、`fpm run`の実行時には、`--flag "-I/usr/lib/x86_64-linux-gnu/fortran/modules/plplot"`を指定してください。もしくは環境変数`FPM_FFLAGS`に書き込んでもよいでしょう（`fpm help`を参照）。

## ビルド設定

FPMで管理されたプロジェクトの`fpm.toml`に、PLplotのモジュールを使うための指示を追加します。`[build]`セクションに以下の2行を追加してください。

```toml
external-modules = ["plplot"]
link = ["plplot", "plplotfortran"]
```

これらを設定することで、そのプロジェクトで管理されるのプログラム内においてPLplotのライブラリが利用可能になります。

## プログラムを書いてプロットを描く

### Hello World

まずは簡単な例から始めていきましょう。以下ようなのプログラムを書いたとします。これをコンパイルして実行しますが、`use plplot`文を`implicit none`の前に記述するのを忘れないでください。

```fortran
program main
   use plplot
   implicit none

   initialize: block
      call plinit
   end block initialize

   frame: block
      real(PLFLT), parameter :: xmin = 0d0, xmax = 1d0
      real(PLFLT), parameter :: ymin = -1d0, ymax = 1d0
      integer :: just, axis
      
      just = 0
      axis = 0
      call plenv( xmin, xmax, ymin, ymax, just, axis)
      call pllab("x", "y", "Title")
   end block frame

   plot: block
      real(PLFLT) :: cx, cy, dx, dy
      
      call plschr(12._plflt, 1._plflt)

      cx = 0.5d0
      cy = 0d0
      dx = 1d0
      dy = 0.5d0
      call plptex(cx, cy, dx, dy, 0.5_plflt, "Hello World!")
   end block plot

   finalize: block
      call plend
   end block finalize
 
end program main
```

このプログラムを実行すると、ターミナルには次のようなプロンプトが表示され、出力デバイス（ファイル形式など）とファイル名について問われます（以下はMacの場合の例です）。

```
Plotting Options:
 < 1> aqt        AquaTerm - Mac OS X
 < 2> xwin       X-Window (Xlib)
 < 3> ps         PostScript File (monochrome)
 < 4> psc        PostScript File (color)
 < 5> xfig       Xfig file
 < 6> null       Null device
 < 7> mem        User-supplied memory device
 < 8> psttf      PostScript File (monochrome)
 < 9> psttfc     PostScript File (color)
 <10> svg        Scalable Vector Graphics (SVG 1.1)
 <11> pdf        Portable Document Format PDF
 <12> xcairo     Cairo X Windows Driver
 <13> pdfcairo   Cairo PDF Driver
 <14> epscairo   Cairo EPS Driver
 <15> pscairo    Cairo PS Driver
 <16> svgcairo   Cairo SVG Driver
 <17> pngcairo   Cairo PNG Driver
 <18> memcairo   Cairo memory driver
 <19> extcairo   Cairo external context driver

Enter device number or keyword:
```

まずは出力先の一覧が表示されるので、その番号またはキーワードを指定します。ここでは出力デバイスに`pngcairo`を選択すると仮定して、次のように入力します。

```
Enter device number or keyword: 17
```

次にファイル名について聞かれますが、ここでは`hello.png`とすることとして次のように入力します。

```
Enter graphics output file name: hello.png
```

これらを終えると、カレントディレクトリに以下のような`hello.png`が作成されるでしょう。

![hello.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953366/6a627922-df61-faba-dcca-4e5f1a9d9d4d.png)


さて、次に上のFortranコードについて、ブロックごとに順番に見ていきます。

#### ` initalize`ブロック

最初に、`plinit`手続を呼び出しています。`plinit`は1つのプロットを初期化して開始します。                                                               

#### `frame`ブロック

2番目に`frame`ブロックでは、プロットする領域を4個の実数型変数を使って定義しています。この際に使用するのが`plenv`手続で、これにより描画するウィンドウとボックスが設定されます。`plenv`は6個の引数を取りますが、前の4つは描画する範囲を上下左右端の値を指定します。

```fortran
 frame: block
    real(PLFLT), parameter :: xmin = 0d0, xmax = 1d0
    real(PLFLT), parameter :: ymin = -1d0, ymax = 1d0
    integer :: just, axis

    just = 0
    axis = 0
    call plenv( xmin, xmax, ymin, ymax, just, axis)
    call pllab("x", "y", "Title")
 end block frame
```

宣言部で指定しているとおり、上の図の描画領域は$0 \le x \le 1$, $-1 \le y \le 1$の範囲にあることがわかります。

残り2つの引数`just`と`axis`は、0を指定しています。これはそれぞれ、`just`は座標軸がどのようにスケールされるかの指定された番号を選び、`axis` はプロットの装飾（ティックマークやグリッド）をどのようにするかの指定された番号を選択します。

- `just`
  - `-1`の場合、スケールは設定されないので、ユーザーが`plenv`を呼び出す前に`plvpa`や`plvasp`などを呼び出して指定する必要があります。
  - `0`の場合、できるだけスクリーンに一致するように、x軸とy軸は独立してスケールされます
  - `1`の場合、x軸とy軸のスケールは等しくなります。
  - `2`の場合、x軸とy軸の長さが等しくなり、ボックスは正方形になります。

`axis`の選択肢は30個ほどあるので、説明はここでは割愛しますが、`0`にすることでボックスと通常のティックマークと数値のラベルが描画されるようになります（詳細は[ドキュメンテーションのセクション17.23節](https://plplot.sourceforge.net/docbook-manual/plplot-html-5.15.0/plenv.html)を参照してください）。

このブロックの最後に`pllab`手続を呼び出しています。これの3つの引数にはそれぞれ、x軸のラベル、y軸のラベル、図のタイトルを文字列で指定します。

#### `plot`ブロック

次に`plot`ブロックですが、最初に実数型変数`cx, cy, dx, dy`を宣言していて、これらは`plptex`手続で文字列を置く位置を指定するために使用します。実行部の1行目では`plschr`手続を引数`(12._plflt, 1._plflt)`と共に呼び出し、プロットする文字の大きさを*12.0mm*でスケールを*1.0倍*とするように指定しています。

```fortran
 plot: block
    real(PLFLT) :: cx, cy, dx, dy

    call plschr(12._plflt, 1._plflt)

    cx = 0.5d0
    cy = 0d0
    dx = 1d0
    dy = 0.5d0
    call plptex(cx, cy, dx, dy, 0.5_plflt, "Hello World!")
 end block plot
```

後続の代入文と`plptex`手続では、文字列を置く座標を`cx = 0.5d0; cy = 0d0`とし、その傾きを原点と点` dx = 1d0; dy = 0.5d0`のなす直線に並行になるように指定しています。

`plptex`の第5引数は、`cx` と`cy`で指定した点に文字を置く際に、文字列のどの位置を基準点にするかを、0.0から1.0の数値(長さの割合)で指示しています。0.0の場合は左端に基準点が、1.0の場合は右端に基準点が置かれます。この例は0.5なので文字列の中央が基準点となっています。

`plptex`の最後の引数には、プロットする文字列`"Hello World!"`を指定しています。

#### `finalize`ブロック

最後に、`plend`手続を呼び出してプロットを完了します。

## まとめ

PLplotのインストール方法と初歩的なプロットの例について解説しました。FortranコードからPLplotライブラリを使うには、上で書いたように、基本的には次の手順に従います。

1. `use plplot`文を`implicit none`の前に書く
2. `plinit`を呼び出す
3. プロットする命令（`plptex`など）を呼び出す
4. `plend`を呼び出してプロットを終了する

今回はHello Worldをプロットして、PLplotのベーシックな使い方を見てきました。次回以降の記事ではより実用的なグラフを書く方法について述べたいと思います。

## 参考文献

1. [PLplot - The ultimate in cross-platform plotting](https://plplot.sourceforge.net/)
2. [PLplot - Examples](https://plplot.sourceforge.net/examples.php)
3. [Documentation of the PLplot plotting software - v5.15.0](https://plplot.sourceforge.net/docbook-manual/plplot-html-5.15.0/index.html)