mcpField
F
fortran

fortran

github.com/mindrally開発28765 回インストール3 週間前に更新
リポジトリ

fortran について

科学計算や数値計算に使うモダンな Fortran の開発スキルです。モジュール設計、種別パラメーター、手続き、配列のメモリ安全性、ビルド、数値テストの指針を示します。

主な機能

  1. 01モダンなモジュール構成
  2. 02共有種別パラメーターと派生型
  3. 03明示的な手続きインターフェース
  4. 04割り付け配列の安全管理
  5. 05警告を有効にしたビルドとテスト
  6. 06許容誤差による数値検証

活用シーン

  1. 01科学計算用 Fortran モジュールの設計
  2. 02数値精度の一元的な定義
  3. 03動的配列の安全な管理
  4. 04ビルド環境ごとの数値結果のテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindrally/skills --skill fortran

インストール後、Claude Code で/fortranを入力して読み込まれたか確認します。

詳細

モダンな Fortran 開発

このスキルでは、科学技術計算および数値計算向けに、保守性の高いモダンな Fortran(2003/2008 以降)を記述する方法を扱います。モジュールの構成、kind パラメーター、プロシージャの設計、メモリー安全性、テストが含まれます。

モダンな Fortran モジュールを記述するワークフロー

  1. 共有 kind を最初に定義する — プロジェクト全体で使用する iso_fortran_env または selected_real_kind/selected_int_kind のパラメーターを含む kinds_mod(または同様の名前)モジュールを作成します。
  2. モジュールを設計する — 関連する派生型とプロシージャを、ファイルごとに焦点を絞った 1 つのモジュールにまとめます。先頭で implicit none を宣言します。
  3. プロシージャのインターフェースを記述する — すべての仮引数に明示的な intent(in)、intent(out)、または intent(inout) を指定します。use, only: を使って、必要なものだけをインポートします。
  4. 早い段階で検証してから実装する — 各プロシージャの先頭で前提条件(配列の境界、割り当て状態、有効範囲など)を確認し、深くネストするのではなく、早めに return/stop します。
  5. 配列を明示的に管理する — allocatable 配列を使用し、使用前に allocated() を確認します。ライフタイムが自然なスコープに収まらない場合は、割り当てを解除します。
  6. 警告を有効にしてビルドする — -Wall -Wextra -std=f2008(gfortran)または同等のオプションを使ってコンパイルし、新しい警告があれば CI を失敗させます。
  7. テストする — 個々のプロシージャにはユニットテストを、数値計算のワークフロー全体には統合テストを記述し、浮動小数点数の完全一致ではなく許容誤差を確認します。

基本原則

  • モダンな Fortran 規格(Fortran 2003、2008、またはそれ以降)を対象とし、コンパイラーが受け付けるからという理由だけで、レガシーな FORTRAN 77 形式で記述することは避けます。
  • すべてのプログラム単位(モジュール、プログラム、およびモジュールからの継承または明示的な文による各プロシージャ)に implicit none を記述し、宣言されていない変数のタイプミスを、誤った結果がひそかに出るのではなく、コンパイル時に検出できるようにします。
  • 外部サブプログラムではなく、プロシージャをモジュール内に配置します。モジュールプロシージャでは明示的なインターフェースが自動生成されるため、外部プロシージャでは検出できない引数の不一致をコンパイラーが検出できます。
  • モジュールは単一の関心事に絞り、主要なモジュールはそれぞれ独自のファイルに配置して、モジュール名と一致する名前を付けます(例:linear_solver.f90 内の module linear_solver)。
  • 言語の巧妙なテクニックよりも、明快で構造化されたコードを優先します。Fortran の数値計算コードは、記述される頻度より読まれる頻度のほうがはるかに高く、通常は元の作者以外が読みます。
  • 廃止された機能は避けます。COMMON ブロックはモジュールに置き換え、GOTO を多用した制御フローは構造化された if/do/select case に置き換えます(必要に応じて block も使用します)。また、ジャンプ先として使われる数値の文番号も避けます。

kind と型

  • 数値の kind パラメーターは共有モジュール(例:kind_mod または precision_mod)で定義し、コードベース全体の精度を 1 か所で変更できるようにします。
  • 浮動小数点値には real(kind=dp)(またはプロジェクトで承認された real kind)を使用します。実際の精度がコンパイラーやフラグに依存する、単独の real や double precision は使用しないでください。
  • 幅が重要な整数、特に C とのインターフェースやバイナリー I/O では、integer(kind=i4)(またはプロジェクトで承認された integer kind)を使用します。
  • pi などの定数は、切り詰めたリテラルではなく、明示的かつ正確に定義します(例:real(dp), parameter :: pi = 4.0_dp * atan(1.0_dp))。
  • 物理量を表す変数には、コメントで物理単位を記載します(real(dp) :: velocity ! m/s)。
  • プロシージャ呼び出しで多くの基本型の引数をばらばらに渡すのではなく、派生型を使って関連データをまとめます(例:位置、速度、質量のフィールドを持つ particle_t 型)。
例:kind モジュールと数値計算プロシージャ
module kinds_mod
  use iso_fortran_env, only: real64, int32
  implicit none
  private
  public :: dp, i4

  integer, parameter :: dp = real64
  integer, parameter :: i4 = int32
end module kinds_mod
module stats_mod
  use kinds_mod, only: dp
  implicit none
  private
  public :: mean, standard_deviation

contains

  pure function mean(x) result(m)
    real(dp), intent(in) :: x(:)
    real(dp) :: m

    m = sum(x) / real(size(x), dp)
  end function mean

  pure function standard_deviation(x) result(s)
    real(dp), intent(in) :: x(:)
    real(dp) :: s
    real(dp) :: m
    integer :: n

    n = size(x)
    if (n < 2) then
      s = 0.0_dp
      return
    end if

    m = mean(x)
    s = sqrt(sum((x - m)**2) / real(n - 1, dp))
  end function standard_deviation

end module stats_mod
program demo
  use kinds_mod, only: dp
  use stats_mod, only: mean, standard_deviation
  implicit none

  real(dp) :: samples(5)
  samples = [1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp, 5.0_dp]

  print '(A, F0.4)', 'mean = ', mean(samples)
  print '(A, F0.4)', 'stddev = ', standard_deviation(samples)
end program demo

命名とスタイル

  • 言語キーワードとほとんどの識別子には小文字を使用します。Fortran では大文字と小文字が区別されないため、一貫して小文字にすると、キーワードの大文字・小文字が混在することによる見た目の雑然さを避けられます。
  • 複数の単語からなる名前にはアンダースコアを使用します(particleVelocity や ParticleVelocity ではなく、particle_velocity)。
  • Fortran では同一の名前として扱われて混乱を招くため、大文字・小文字だけが異なる名前(Count と count など)は避けます。
  • 手続きや状態には説明的な名前を付けます(cr や calc2 ではなく、compute_residual)。
  • end 文の後には、手続き名またはモジュール名を繰り返し記述します(end module kinds_mod、end subroutine compute_residual)。長いプログラム単位でも、目視で簡単に確認できます。
  • do、if、select case、モジュールの各ブロック内では、インデントを一貫させます(プロジェクトに合わせて 2 つまたは 4 つのスペースを使用します)。

手続き

  • サブルーチンと関数は短く、単一の目的を持つようにします。残差を計算する手続きで、出力ファイルへの書き込みも行うべきではありません。
  • すべての仮引数に intent(in)、intent(out)、または intent(inout) を指定します。intent が指定されていない引数がある場合は、インターフェースの検討が不十分であることを示す警告サインです。
  • 可能な限り関数に副作用を持たせないようにします(該当する場合は pure または elemental を指定します)。状態を変更する処理や I/O を行う処理には subroutine を使用します。
  • 深くネストした if ブロックより、早い段階で検証して明確に戻る方法(if (n <= 0) then; ...; return; end if)を優先します。
  • モジュールから名前をインポートするときは、単なる use module_name ではなく use, only: name1, name2 を使用します。これにより、呼び出し箇所で何が取り込まれるのかが明確になり、名前の衝突も避けられます。

メモリと配列

  • ポインターの意味論(エイリアシング、連結構造など)が特に必要な場合を除き、ポインターより割り当て可能配列を優先します。割り当て可能配列は自動的に割り当て解除され、デフォルトでより安全です。
  • まだ割り当てられていない可能性のある配列を使用する前に、allocated() の状態と配列サイズ(size()、lbound()、ubound())を確認します。
  • 割り当て可能配列の寿命が自然にスコープ内に収まらない場合(たとえば、単一の手続き呼び出しより長く存続する派生型に保持されている場合)は、明示的に割り当て解除します。手続き内のローカル配列は、終了時に自動的に割り当て解除されます。
  • 配列境界が重要な場合は、明確に指定します。特に、デフォルト以外の下限値には注意します(real(dp) :: a(0:n))。
  • 反復回数の多いループ内で不要な動的割り当てを避けます。ループの外で一度だけ割り当ててバッファーを再利用するか、小さく固定サイズの一時配列には自動配列またはスタック配列を使用します。

テストとビルド

  • プロジェクトで標準化されているビルドシステム(CMake、fpm(Fortran Package Manager)、Make など)を一貫して使用し、複数を混在させないようにします。
  • 警告を有効にしてコンパイルし(開発ビルドでは gfortran -Wall -Wextra -std=f2008 -fcheck=all)、重要な警告は CI の失敗として扱います。
  • 公開手続きには単体テストを追加します(standard_deviation のような純粋関数には、既知の入力と出力を使った小規模で高速なテストを用意します)。数値計算のワークフロー全体には統合テストを追加します。
  • 境界条件(空の配列、要素が 1 つの配列、ゼロまたは負の入力)、無効な入力、および実際の問題領域から選んだ代表的な科学的ケースをテストします。
  • 浮動小数点数はコンパイラーや最適化レベルによって完全一致がほぼ保証されないため、厳密な一致に頼らず、許容誤差で数値結果を検証します(例:abs(actual - expected) < 1.0e-10_dp)。

よくある間違い

  • 実行可能な文の後に、block 構文で囲まず変数を宣言すること — Fortran では、スコープ単位ごとに実行コードより前にすべての宣言を記述する必要があります。
  • random_number が関数だと思い込むこと。これはサブルーチンです(x = random_number() ではなく、call random_number(x) とします)。
  • pure として宣言されたプロシージャから標準出力に書き込むこと(print、write(*,*)) — これは許可されておらず、コンパイルに失敗します。
  • 同じスコープ内で同じ変数を2回宣言すること。モジュールが大きくなると見落としやすくなります。
  • use で明示的にインポートまたは定義せずに、pi、dp、その他のプロジェクト標準の種別名や定数名がすでに存在すると想定すること。

コメント