European Standards For Writing and Documenting Exchangeable Fortran 90 Code --- Version 1.1 の日本語訳

原著者 : Phillip Andrews (UKMO), Gerard Cats (KNMI/HIRLAM), David Dent (ECMWF), Michael Gertz (DWD), Jean Louis Ricard (Meteo France)

訳者 : 保坂征宏、吉村裕正(いずれも気象研気候)

注意。

目次

1 はじめに

この文書の目的は、 ヨーロッパ気象組織での Fortran 90 の使用のための枠組を与えること、 組織内でのコードの交換を容易にすること、 である。 この目標に向けて、 コードの書き方の標準を作ると同時に、 コード解説文書のための標準を作る。 この文書は各コードについて、 外との関係の解説と、コードそのものの解説をするものである。 コードの書き方の標準は、 可読性と維持しやすさを高め、 移植性や計算効率を可能な限り確実にするようにデザインされている。

2 文書

文書は 2 つのカテゴリに分けられる。 一つは[コードとは別に存在する]コード外解説であり、 もう一つは[コードの中にコメントとして書かれる]コード内解説である。 これらを 2.1 および 2.2 節に記す。 文書が便利であるようにするために、 コードが作られた組織以外の組織でも 文書は最新のものに更新され、かつ読みやすい必要がある。 他の機関が輸入したコードを変更するために、 全ての文書はコード外解説とコード内解説のいずれも、 英語で用意されなければならない。

2-1 コード外解説

大抵の場合、個々のサブルーチンではなく、パッケージについて記される。 以下のことを含まねばならない。
  1. 科学的記述 [ あるいは科学的背景の解説 ]
    これは、そのパッケージで解かれるべき問題と、採用された解法に関す る科学的な根拠とを記述する。 この文書はコード自体とは独立である。

  2. implementation の記述
    科学的記述において書かれた解法の implimentation を記す。 パッケージ内の全てのルーチン(サブルーチン、関数、モジュール等) をリストアップし、 各々の簡単な記述をする。 ツリー構造も示さねばならない。

  3. ユーザガイド
    パッケージへの全ての入力を記す。 パッケージに対するサブルーチン引数、 パッケージ内のスイッチやチューニング用変数を含まねばならない。 適切なデフォルト値や値の有効範囲等も記すこと。 読み込む全てのファイルやネームリストも詳述すること。

2-2 コード内解説

これは個々のルーチンに適用される。 4 つのタイプのコード内解説はすべてなければならない。
  1. 手続きヘッダ
    サブルーチン・関数・モジュール等ごとにヘッダを持たねばならない。 その一つの目的は、 ルーチンの機能を記すことにあり、 それはおそらくコード外解説を参照しながらということになるだろう。 そしてもう一つの目的は ルーチン内で使われる変数を記述することにある。 ルーチンで使われる全ての変数はヘッダで宣言され、 目的についてコメントされなければならない。 付録 A [ B の誤り ] でリストされたヘッダが使われていることが必要である。 各機関は、もし望むならば、ヘッダに特別な節を加えてもよい。

  2. 節コメント
    節コメントは、コードをナンバリングされた論理節に分けるものである。 コード外解説を参照してもよい。 これらのコメントは 独自に行を確保して、節の冒頭部に置かれる。 推奨される書式は
    
    !--------------------------------------------------------------
    
    !<節の数字><節のタイトル>
    
    !--------------------------------------------------------------
    
    
    である。
    <>
    中に適切に書くこと。

  3. その他のコメント
    コードを読むプログラマー向けのものであり、 理解を助けになるものである。 これらのコメントは コードの直前又は同じ行に置かれなければならない。 推奨されるフォーマットは
    
    !  <コメント>
    
    
    である。
    <>
    中に適切に書くこと。

  4. 意味のある名前
    コードは、変数名やサブプログラム名を構成するために 意味のある名前が使われているとより読みやすい。
全てのコード内解説は英語で書かれていることが推奨される。 しかしそれは常に可能でないかも知れないので、 英語訳のコメントを伴う母国語のコメントのルールを用意する。
  1. 意味のある名前は母国語で書かれていてもよい。

  2. 母国語で書かれた節コメントには 英語訳を添えねばならない。

  3. 母国語で書かれたその他のコメントには 出来れば英語訳を添える方がよい。

  4. 母国語で書かれた、ヘッダ中の記述と方法の節には 英語訳を添えねばならない。

  5. ヘッダ節で宣言された変数を記述するコメントは 母国語で書かれていてもよい。 (パッケージの全ての英語訳の宣言を含む) 英語で書かれた宣言は別のファイルにあるべきである。

  6. 別の目的のために同じ変数名を繰り返し使うことは禁止されているので、 母国語の宣言を英語の宣言に置き換えるための簡単なツールが ソースファイル中に用意されていてもよい?

3 パッケージのコーディングルール

これらの規則はおおむね、 Kalnay ら(1989) のプラグ互換性規則に基づいている。 この文書中の他で書かれている規則については、 この節では繰り返さない。
  1. パッケージは、 独自のモジュールとサブプログラムと、 Fortran 90 標準に含まれる内部ルーチンのみを参照する。

    NAGライブラリなどのこの文書の中で指定されているライブラリを パッケージに附属させることができるように、 制限を緩める必要があるかもしれない。 また、multigrid p.d.e.ソルバや飽和水蒸気圧を計算するルーチンを含む 『統合気象計算ユーティリティライブラリ』のようなものを パッケージの中に入れてもよいだろう。

  2. パッケージは、 各々がエントリポイントを一つもつような、 準備手続きと実行手続きとをもってもよい。 静的データの初期化は準備手続きでなされる。 実行手続きではこのデータを変更してはならない。 [ 保坂注。準備手続き (set up procedure) とは、 よく IF (FIRST) ... ENDIF 等で行なうような、 定数の設定部分にあたる。 実行手続き(running procedure)とは その定数などを使う部分にあたる。]

  3. パッケージと外部との連絡は以下を通して行なってよい。

    1. パッケージエントリと準備ルーチンの引数リスト。

    2. NAMELIST ファイル読み込み

    3. 標準ルーチンで読まれる環境変数。 環境変数は文書化され、 使用する OS にスクリプトで設定されなければならない。 (unix に対しては posix スクリプト)

      我々はまた、この文書で定義された標準モジュールの使用を許す。 グリッドタイプや解像度等の情報、エラー対処等。。。


  4. パッケージは内部的に Module を使ってもよい。 たとえば準備手続きと実行手続きはこの方法で連絡してもよい。

  5. 引用仕様宣言(Interface Blocks)は 準備手続きと実行手続きのために 与えられなければならない(できるかぎり module を通して)。 このことにより、 コンパイラーによる実際のまたはダミーの引数の型が合致(match) することの検査を可能にするだけではなく、 形状引継ぎ配列(assumed shape arrays)や 省略可能引数(optional arguments)等の使用も可能になる。 もし、これらの外部引数リスト(external argument lists)中の変数が 構造型であれば、 これらの構造型がパッケージを呼び出すルーチンで利用可能にするのに 必要となる型定義文を含むモジュールが与えられなければならない。

  6. パッケージはプログラムの実行を止めるべきではない。 パッケージ内でエラーが起こった場合は exit し、引数リスト中の 整数変数を通してエラーを報告すべきである。 パッケージは、 フォートラン入出力を使い、 パッケージの準備手続きで選択可能なエラー出力装置番号に対して、 エラーを記述する診断メッセージを書くべきである。

    パッケージの開始点が、 FortranプログラムではなくUNIXのスクリプトである場合、 exitするルーチンは、 STOP文を使ってスクリプトのレベルへ制御を戻すようにすること。
    [訳注。 FortranのロードモジュールがFPEなどで異常終了した場合、 その場でプロセスが死んでしまうのではなく、 一旦上のレベルのシェルスクリプトに制御を返してから、 最終的にスクリプトがエラーコードを吐き出すようにする、ということ。] [ 素人質問。実際にはどうやればいいの? ]

  7. パッケージは可能な限り解像度によらないように書かれるべきである。 解像度はパッケージの準備手続きで設定可能でなければならない。

  8. プリコンパイラについて。 例えば、コードのうちのコンパイルされる部分を設定するために用いられる。 コードのポータビリティーを簡単にするためには、 我々は同じプリコンパイラを使う必要があり、 どの機関もこれを利用可能でなければならない。 C プリコンパイラは、unix を使っているすべてのマシンにのっているので、 おそらく最善のオプションである。

    標準プリコンパイラとしてこれを採用することは、 さまざまな機関が現在さまざまなプリコンパイラを使用しているので、 何らかの問題を持つであろう。 しかし各機関が現在のプリコンパイラから C プリコンパイラに 変換することはそれほどたいした作業ではないかもしれない。

  9. すべての unix スクリプトは posix shell を使って書かれなければならない。 これは標準化された shell であり、 すべての POSIX compliant unix system で利用可能である。

  10. 各プログラム単位は別々のファイルに書かれなければならない。

4 動的メモリの使い方のガイダンス

動的メモリの使用は望ましい。 なぜなら、動的メモリを使うことによって、 コンパイルされたコードのセットが いかなる解像度に対しても(少なくともハードウエアのメモリが許す限りは) 使えることになり、 ワークメモリを効率的に減らすことができるからである。

しかし、 特に並列化のコードにおいて、 非効率的なメモリ使用になる可能性があるような場合は 注意が必要である。 たとえば、 下層レベルのルーチンによってメモリ空間がアロケートされ 上層のルーチンに戻るまえに解放されないならば、 heap fragmentation [?]が起こるかも知れない。

Fortran 90 での動的メモリの与え方には 3 つの方法がある。
  1. 自動配列 (Automatic arrays)
    サブプログラム内で初期に宣言される配列。 その extent は実行時にわかる変数、 つまり引数でサブプログラムに渡される変数、 に依存する。
    [ 保坂注。たとえば
      
    
          PROGRAM MAIN
    
          PARAMETER ( IMAX = 100 )
    
          ..
    
          CALL SUB ( A , IMAX )
    
          ..
    
          STOP
    
          END
    
    C-----------------------
    
          SUBROUTINE SUB ( A , IMAX )
    
          DIMENSION DYNAMIC ( IMAX )    ! 自動配列
    
          ... 
    
          RETURN
    
          END
    
    
    であろう。]

  2. ポインタ配列 ( Pointer arrays )
    POINTER属性で宣言した配列変数は、ALLOCATEコマンドを使用することによって 実行時にメモリ空間に割付けられる。

  3. アロケータブル配列 ( Allocatable arrays )
    ALLOCATE属性で宣言した配列変数は、 ALLOCATEコマンドを使用することによって 実行時にメモリ空間に割付けられる。 しかし、ポインターと違って、 割付け配列は、構造型(derived data types)の中では許されない。 [ 以下の訳の方がいいのかも知れない。 保坂にはどちらにしても理解できず、 よしあしの判断ができないので併記しておく。 「アロケータブル配列、つまり ALLOCATABLE 属性を指定して宣言された 配列変数は、実行時に ALLOCATE コマンドにより確保される メモリ空間である。しかしポインタとは異なり、 派生データ型のメンバに対するアロケータブル配列は許されない。」]

5ルーチンのコーディング規則

ルーチンとは、 サブルーチン・関数・モジュール・プログラムのような Fortran プログラム単位を意味する。 ここで示す規則は、 共通のスタイルのルールをつくることによって、 うまく構造化されたプログラミングの実践を促進し、 管理労力を簡単にし、 交換するコードの可読性を高めるためのものである。

5-1禁止条項

以下の項のうちのあるものは、 Fortran90 では使わないほうがよい機能・冗長な機能である。 また他の幾つかの項では、コードの維持管理を困難にする ようなプログラミング上の悪習と考えられる機能を禁止している。

  1. COMMON ブロックは禁止。
    かわりに Modules を使え。

  2. EQUIVALENCE 文は禁止。
    かわりに POINTERS や derived data types を使え。

  3. Assigned and computed GO TOs は禁止。
    かわりに CASE construct を使え。

  4. 算術 IF 文は禁止。
    ブロック IF 文を使え。

  5. ラベルは基本的に禁止。
    許される使用法は一つのみ。
    • ラベルつき DO は禁止。
      かわりに End DO を使え。

    • I/O ルーチンでの End = と ERR = は禁止。
      かわりに IOSTAT を使え。

    • FORMAT 文は禁止。
      Character パラメタを使うか、または READ 文と WRITE 文中で 陽にフォーマットを指定せよ。

    • GO TO 文は基本的に禁止。
      GO TO の唯一の認められる使用法は、 エラーを検知した際にルーチンの最後にある エラー取り扱いセクションにジャンプするためである。 CONTINUE 文に飛ばねばならず、 使われるラベルは 9999 でなければならない。 この実践すら、避けることが推奨される。

    上記以外の GOTO 文の使用はおそらく IF,CASE,DO WHILE,EXIT,CYCLE文を使うことで 回避することができる。 GOTO の使用が避けられない場合は、 プログラムの流れを説明するコメントを明示し、 同様のコメントが付されたCONTINUE文へジャンプするようにせよ。

  6. PAUSE 文は禁止。

  7. ENTRY 文は禁止。
    副プログラムはただ一つの入口しかもたない。

  8. 副作用を持つ関数、 つまり (1) 引数や、関数で使われるモジュールの変数を変更してしまう関数、 (2) I/O を行なう関数、 は禁止。

    これは C プログラミングではよく行なわれることだが、 混乱を招く。 また、 その関数は副作用を持たないと知っていれば、 コンパイラが最適化をするかも知れない。 分散並列計算機向けにデザインされた Fortran 90 の変形である HPF ( High Performance Fortran ) は このような実装[副作用がない場合に、より最適化をすることか?]を 認めている。

  9. サブルーチンに渡された時に、配列の形状を変更すること。

    サブルーチンに引数を渡す際、暗黙的に配列の形状を変更することは禁止。 実際には標準で禁じられているにもかかわらず、 FORTRAN 77 では、n 次元配列をサブルーチンに渡し、 サブルーチン内では例えば 1 次元配列として扱うことは 慣習としてよく行なわれていた。 この慣習は Fortran 90 では禁止だが、INTERFACE ブロックを伴わない 外部ルーチンの場合は依然可能である。 このような引渡し方は、メモリ上でのデータ配置を仮定することで はじめて有効になるので、超並列計算機上では機能しない可能性が大きい。 そこでこれを行なうことを禁止する。

5-2スタイルルール

基本的な精神は、 読みやすく維持しやすい、 移植しやすいコードを書くことである。 コードは、予見できない変形を見込んで、 できる限り一般的な方法で書かれているべきである。 実際にはこのことは、 コーディングの時間と手間はやや長くなることを意味する。 しかし、この余分な労力のおかげで、 そのソフトが生き続ける間、維持コストは減じるであろう。

  1. フリーフォーマットを使え。

  2. 1 行の上限は 80 文字である。 Fortran 90 ではラインの長さの上限は 132 文字であるが、 やや古い端末で見る時や A4 の紙に出力する時に問題がでる。

  3. Implicit none を使え。 これによって全ての変数は陽に宣言されなければならす、 したがって文書化されねばならない 。 また、コンパイラは変数名のタイプミスを検知できる。

  4. 意味のある変数名を使え、できれば英語がよい。 変数名があまりに長くならないための適度の省略は構わない。

  5. Fortran文は大文字だけを使うか、 もしくは最初の文字を大文字にして残りを小文字に しなければならない。 変数やパラメータやサブルーチンなどの名前は、 大部分を小文字にして大文字を混ぜてもよい。

  6. 読みやすさのために、 DO; DO WHILE; block IF; CASE; Interface; などの中では コードを 2 文字分字下げせよ。

  7. 継続行は字下げせよ。 たとえば複数行にわたる式の部分は読みやすいように一列に並ばせよ。

  8. 字下げが独立行[訳注、継続行ではない行]で発生する場合には、 コードの構造を反映させて コード内解説の第三項のタイプのコード内コメントも字下げする。 コードの字下げが2文字分であるのに対し、 これより少ない1文字分で字下げをすれば、 コメント行とコードを明確に分離させることができ、 しかもコードの構造を壊さずにすむ。 [訳注。例
    
        ! distribute input data
    
        if(myid==0) then
    
         ! node 0 sends data to all other nodes.    <= コメント行1文字字下げ
    
          call MPI_SEND(...)                        <=   コード行2文字字下げ
    
        else
    
         ! all nodes except 0 receive data.         <= コメント行1文字字下げ
    
          call MPI_RECV(...)                        <=   コード行2文字字下げ
    
        end if
    
    
    ] [ 実際問題、上の例のコメントの字下げの仕方って、わかりやすいかなあ? ]

  9. 可読性を高めるために横にも縦にも空白を使え。 特に、変数と演算子の間には空白をいれよ。 また対応する部分は一列に並べるように努めよ。

    たとえば

    
    ! Initialize Variables
    
    x=1
    
    MEANINGFUL_NAME=3.0
    
    SILLY_NAME=2.0
    
    
    のかわりに、
    
    ! Initialize variables
    
     x              = 1
    
     MeaningfulName = 3.0
    
     SillyName      = 2.0
    
    
    とせよ。

    同様に、 式を式として認識しやすく読みやすいように努めよ。 継続行では、 前の行の最後に作用素を書くのではなく、 適当に字下げされた作用素ではじめるようにする方が読みやすい。

  10. コード中にタブを使うな。 タブを使うと移植した時にコードの見かけが変わるかも知れない。

  11. IO 文での出力のフォーマット情報を、出力の情報から離すこと。 すなわち、I/O文の括弧内にテキストをおいてはならない。

  12. 使われていないヘッダ成分は消去せよ。

  13. この文書の著者の多くは、 変数に推奨される名前の申し合わせをすることを強く望んでいる。 しかし、Fortran 77 での申し合わせは不適当であること、 適当な申し合わせを決める前に Fortran 90 についてより多くの経験が必要とされることが 決定された。 そのような申し合わせはこの文書の改定版で含まれることになるだろう。

5-3Fortran 90 の特色の使用

Fortran 90 で導入された新たな特色の使い方に愚かなやり方があるのは 避けられない。 当然我々はそのような使い方は禁止したいし、 それ以外の実践は推奨したい。 しかし そのような推奨の完璧なリストを作るには Fortran 90 についてある程度の経験を持たねばならないだろう。 以下のルールはそのような経験のもとで修正・拡張されなければならないだろう。

  1. モジュールで定義された変数や型定義(type definitions)などのどれが、 使用するルーチン(Using routine)で利用されることになるかを 明示するために、 Use, ONLYの使用を推奨する。

  2. 引用仕様宣言(Interface Blocks)の使用についての議論:

    はじめに

    もし、省略可能引数やキーワード引数(optional or keyword arguments)を 使用するなら、f90のルーチン間で、明示的引用仕様宣言 (Explicit interface blocks)が必要になる。 それにより、また、CALLで指定された(specified)引数の型や形状や 数がサブプログラム自身で指定されたものと同じであるかをコンパイラーが チェックすることが可能になる。 付け加えて、コンパイラーのいくつか(例えばCray f90のコンパイラー)は、 呼び出されたサブプログラムがf90で書かれているかどうか(このことにより どのように配列の情報がサブルーチンに引き渡されるかが変わる)を決定する ために、引用仕様宣言の存在を使用する。 このように、一般的にはf90のルーチン間で明示的引用仕様宣言を与えるのが 望ましい。 そうするにはいくつかの方法があり、それらの各々は、プログラム設計 (program design)やコード管理(code management)や構成の制御(configuration contro l)さえも関連(implications)をもっている。 以下のセクションでは、3つの主要な選択肢(options)について議論する。

    選択肢(option) I: 明示的にコードされた引用仕様宣言(Explicitly Coded Interface Blocks)

    引用仕様宣言は、特に呼び出されるルーチンから引数リストの宣言部分を コピーすることにより、呼び出すルーチンに明示的に書かれる。 この直接的なアプローチは、しかし、いくつかの欠点がある。 まず初めに、もし呼び出されるルーチンの引数リストが変わると引用仕様宣言は CALL文とともに更新しなければならないので、呼び出すルーチンを維持管理するのに 必要な作業が増大し、望ましくない。 更に、呼び出すルーチン内の引用仕様宣言が実際に更新されていて呼び出される ルーチンの実際の引用仕様(interface)と同じになっているかどうかの保証がない。

    選択肢 II: モジュール(Module)内の明示的にコードされた引用仕様宣言

    あるパッケージのすべてのルーチンの引用仕様宣言がモジュールの中に明示的に 書かれ、このモジュールがパッケージのすべてのルーチンに使用(use)される。 これは、すべてのルーチンの引用仕様の記述(the interface specification)を見つけ る のに、一つのルーチンを調べればよいという長所がある。これは、呼び出される すべてのルーチンのソースコードを個別に調べるより簡単である。 しかし、ルーチン自身やそれへのCALL文に加えて、引用仕様宣言をまだ維持管理 しなければならない。引用仕様宣言を含むモジュールを自動的に作り出すように、 プログラムや例えばunix scriptを書くことはできるけれども。

    選択肢 III: 自動引用仕様宣言(Automatic Interface Blocks)

    Fortran 90コンパイラは、Contains文に続くルーチンの間で、自動的に明示的に 引用仕様宣言を供給することができる。 引用仕様宣言はまた、モジュールを使用(use)することによって、どのルーチンにも 供給することができる。 このように、引用仕様宣言を実際には書かずに、コンパイラーによって全ての ルーチン間に明示的引用仕様宣言が供給されるようなシステムを設計することが可能で ある。 このようにする一つの方法は、 f90のモジュールのレベルで コードを「モジュール化(modularise)」することである。 すなわち、 関連するコードを一つのモジュール内のContains文以降に 一緒に置くことである。 モジュールBの中のルーチンbを呼び出すモジュールAの中のルーチンaは、ルーチンb への明示的引用仕様を自動的に供給されるようにするために、ただ、モジュールBを Useしなければならない。 明らかに、もしルーチンbが代わりにモジュールAのなかにあれば、Use文は必要ない。 このアプローチの一つの結果は、ひとつのモジュールとそれに含まれるすべての ルーチンは一つのコンパイルユニットを構成するということである。 もし、モジュールが大きいか、もしくは一つのパッケージ内の各々のモジュールが パッケージ内の他の多くのモジュールをUseするルーチンを含んでいるなら (その場合には、一つのモジュール内の一つのルーチンを変更することにより 実質的にパッケージ全体の再コンパイルが必要になる)、これは欠点になるだろう。 他方で、コンパイルユニットの数が非常に減れば、コンパイルや構成(configuration) の コントロールシステムが単純になる。

    結論

    選択肢 II) や III) は両方、 明示的引用仕様宣言の問題に実行可能な解決法を与える。 選択肢 III) は、コンパイラーが引用仕様宣言を与える仕事を行い、 プログラミングのオーバーヘッド(overheads)が減少し、 同時に使用される引用仕様宣言が正しいことが 保証されるので、おそらくより望ましい。 どの選択肢が選択されても、 プログラム設計と同様にコード管理や構成の制御 (configuration control)に重大な影響を与える。
  3. 配列表記(array notation)は、可能な時は使用すべきである。 これは、最適化を助け、必要なコードの行数を少なくする。 読みやすくするために、括弧の中に配列の形状を示す事。
     
    
         1dArrayA(:) = 1dArrayB(:) + 1dArrayC(:)
    
         2dArray(:, :) = scalar * Another2dArray(:, :)
    
    

  4. 例えば差分方程式のように、 配列の一部分(subsections)を参照(access)する際には、 配列全体の三つ組表記(the triplet notation)を使用すること。 [訳者注:三つ組表記とは、例えばA(1,10:1:-1)のような、下限:上限:刻み幅という  表記法のこと。]

    
            2dArray(:, 2:len2) = scalar                         &
    
                               * ( Another2dArray(:, 1:len2 -1) &
    
                                 - Another2dArray(:, 2:len2)    &
    
                                 )
    
    
  5. いつでも'program units'の名前を付けること。 またいつでもthe End program; End subroutine;End interface; End module等の 構造構文(constructs)を使用し、 その後に'program unit'の名前を記述すること。

  6. 論理比較(logical comparisons)をする際には、 .gt., .ge., .eq., .lt., .le., .ne. のかわりに>, >=, ==, <, <=, /= を使用すること。 標準の数学表記に近いこの新しい構文は、意味がより分かりやすい。

  7. 一行に複数の文を書かないこと。これは、コードの読みやすさをそこなう。

  8. 変数の宣言。 Fortran 90は、同じ結果を得るために多くの異なった構文を提供するが、 もし変数を宣言する際に皆が同じ命名既約を採用するなら、 理解がしやすくなる。
    • DIMENSION文やDIMENSION属性(attribute)を使用しないこと。 宣言文の変数名の後の括弧の中に配列の型やサイズを宣言すること。

    • もし属性(attributes)がなくても、いつも::表記(notation)を使用すること。

    • 文字変数の長さを(len = )構文を用いて宣言すること。
  9. 効率の面から再帰ルーチンの使用は避ける事。 (CPUやメモリーの使用に関して効率が悪くなりがちである。)

  10. 現存する演算子を多重定義(overload)するより、 新しい演算子を定義することを推奨する。 このことにより、何が行われているかがよりはっきりと記述され、 またコードの読みやすさと維持しやすさの低下を避けることができる。

  11. 32bitと64bitのプラットフォームの間の 移植性(portability)を改善するため、 必要な数値精度と範囲(numerical precision and range)を得るために 種別(kind)を使用する事は非常に便利である。 各々の必要な種別(kind)と対応するパラメーターを定義するために module が使われるべきであり、例えば、
    
    Integer, parameter       :: single            & ! single precision kind.
    
                              = selected_real_kind(6,50) 
    
    Integer, parameter       :: double            & ! double precision kind.
    
                              = selected_real_kind(12,150)
    
    

    このモジュールは、すべての変数が適切な種別(kind)で宣言されるように、 すべてのルーチンで使用(USE)することができる。例えば、

     
    
    Real(single), pointer   :: geopotential(:,:,:) ! Geopotential height fields
    
    

6標準の実施

これらの標準が守られること、 特に文書がソフトウエアと同時に更新され続けることと ソフトウエアができるかぎり移植可能な形で書かれること、 を保証することは重要である。 もしこれらの標準が守られないならば、 コードの交換は難しくなるだろう。 QA Fortran [QA Fortran って何?]のような これらの標準に対する従順さをテストするための ツールを作らねばならないかも知れない。 これは今後の課題である。

一つの選択肢は コード交換とコード外解説の中心データベースを 立ち上げることであろう。 受け入れるかどうかという判断基準は、 この文書に書かれている標準を満たすことに置くことになろう。 これはもちろん、 データベース維持のためのハードウエア、ソフトウエア、人を確保するための 資金を必要とする。

その反対の状態として、 各々の機関が独自の強制戦略を採用し。 他機関に提供可能なソフトウエアのリストを示す、 という選択肢もある。

最善の解は Mosaic とインターネットを使う分散システムをデザインすること かもしれない。

参考文献

Kalnay et al. (1989) Rules for Interchange of Physical Parametrizations, Bull. A.M.S., 70 No. 6, p 620.

付録 A : 変更履歴

23/3/94: Draft Version 0.1 Phillip Andrews (UKMO)
22/4/94: Draft Version 0.2 Phillip Andrews (UKMO)
7/6/94: Draft Version 0.3 Phillip Andrews (UKMO)
31/7/94: Draft Version 0.4 Phillip Andrews (UKMO)
29/9/94: Draft Version 0.5 Phillip Andrews (UKMO)
14/10/94: Draft Version 0.6 Phillip Andrews (UKMO)

Renumbered Version 1.0

20/6/95: Version 1.1 Phillip Andrews (UKMO)

付録 B: 標準ヘッダ

標準ヘッダはこの付録で与えられる。 テンプレートとして書かれている。 テキスト中の < > ブラケットは ユーザが適当な文に置き換えること。

B-1プログラムヘッダ

!+ <A one line description of this program> 

! 

Program <NameOfProgram> 

 

! Description: 

!   <Say what this program does> 

! 

! Method: 

!   <Say how it does it: include references to external documentation> 

!   <If this routine is divided into sections, be brief here,  

!        and put Method comments at the start of each section> 

! 

! Input files: 

!   <Describe these, and say in which routine they are read> 

 

! Output files: 

!   <Describe these, and say in which routine they are written> 

 

! Current Code Owner: <Name of person responsible for this code> 

 

! History: 

! Version   Date     Comment 

! -------   ----     ------- 

! <version> <date>   Original code. <Your name> 

 

! Code Description: 

!   Language:           Fortran 90. 

!   Software Standards: "European Standards for Writing and  

!     Documenting Exchangeable Fortran 90 Code". 

 

! Declarations: 

 

! Modules used: 

 

Use, Only : & 

! Imported Type Definitions: 

 

! Imported Parameters: 

 

! Imported Scalar Variables with intent (in): 

 

! Imported Scalar Variables with intent (out): 

 

! Imported Array Variables with intent (in): 

 

! Imported Array Variables with intent (out): 

 

! Imported Routines: 

 

! <Repeat from Use for each module...> 

  

Implicit None 

 

! Include statements  

! Declarations must be of the form: 

! <type>   <VariableName>      ! Description/ purpose of variable 

 

! Local parameters: 

                                 

! Local scalars: 

 

! Local arrays: 

 

!- End of header --------------------------------------------------------------- 

B-2サブルーチンヘッダ

!+ <A one line description of this subroutine> 

! 

Subroutine <SubroutineName> & 

! 

  (<InputArguments, inoutArguments, OutputArguments>) 

 

! Description: 

!   <Say what this routine does> 

! 

! Method: 

!   <Say how it does it: include references to external documentation> 

!   <If this routine is divided into sections, be brief here,  

!        and put Method comments at the start of each section> 

! 

! Current Code Owner: <Name of person responsible for this code> 

! 

! History: 

! Version   Date     Comment 

! -------   ----     ------- 

! <version> <date>   Original code. <Your name> 

! 

! Code Description: 

!   Language:           Fortran 90. 

!   Software Standards: "European Standards for Writing and  

!     Documenting Exchangeable Fortran 90 Code". 

! 

! Declarations: 

! Modules used: 

  

Use, Only : & 

! Imported Type Definitions: 

 

! Imported Parameters: 

 

! Imported Scalar Variables with intent (in): 

 

! Imported Scalar Variables with intent (out): 

 

! Imported Array Variables with intent (in): 

 

! Imported Array Variables with intent (out): 

 

! Imported Routines: 

 

! <Repeat from Use for each module...> 

 

Implicit None 

 

! Include statements: 

! Declarations must be of the form: 

! <type>   <VariableName>      ! Description/ purpose of variable 

 

! Subroutine arguments 

! Scalar arguments with intent(in): 

 

! Array  arguments with intent(in): 

 

! Scalar arguments with intent(inout): 

 

! Array  arguments with intent(inout): 

 

! Scalar arguments with intent(out): 

 

! Array  arguments with intent(out): 

 

! Local parameters: 

 

! Local scalars: 

 

! Local arrays: 

 

!- End of header --------------------------------------------------------------- 

B-3関数ヘッダ

!+ <A one line description of this function> 

! 

Function <FunctionName> & 

  (<InputArguments>)    & 

Result (<ResultName>) ! The use of result is recommended 

                            ! but is not compulsory. 

 

! Description: 

!   <Say what this function does> 

! 

! Method: 

!   <Say how it does it: include references to external documentation> 

!   <If this routine is divided into sections, be brief here,  

!        and put Method comments at the start of each section> 

! 

! Current Code Owner: <Name of person responsible for this code> 

! 

! History: 

! Version   Date     Comment 

! -------   ----     ------- 

! <version> <date>   Original code. <Your name> 

! 

! Code Description: 

!   Language:           Fortran 90. 

!   Software Standards: "European Standards for Writing and  

!     Documenting Exchangeable Fortran 90 Code". 

! 

! Declarations: 

! Modules used: 

 

Use, Only : & 

! Imported Type Definitions: 

 

! Imported Parameters: 

 

! Imported Scalar Variables with intent (in): 

 

! Imported Scalar Variables with intent (out): 

 

! Imported Array Variables with intent (in): 

 

! Imported Array Variables with intent (out): 

 

! Imported Routines: 

 

! <Repeat from Use for each module...> 

 

Implicit None 

 

! Declarations must be of the form: 

! <type>   <VariableName>      ! Description/ purpose of variable 

 

! Include statements: 

 

! Function arguments 

! Scalar arguments with intent(in): 

 

! Array  arguments with intent(in): 

 

! Local parameters: 

 

! Local scalars: 

 

! Local arrays: 

 

!- End of header ------------------------------------------------------------ 

B-4モジュールヘッダ

!+ <A one line description of this module> 

! 

Module <ModuleName> 

 

! 

! Description: 

!   <Say what this module is for> 

! 

! Current Code Owner: <Name of person responsible for this code> 

! 

! History: 

!  

! Version   Date     Comment 

! -------   ----     ------- 

! <version> <date>   Original code. <Your name> 

! 

! Code Description: 

!   Language:           Fortran 90. 

!   Software Standards: "European Standards for Writing and  

!     Documenting Exchangeable Fortran 90 Code". 

! 

! Modules used: 

! 

Use, only : & 

! Imported Type Definitions: 

 

! Imported Parameters: 

 

! Imported Scalar Variables with intent (in): 

 

! Imported Scalar Variables with intent (out): 

 

! Imported Array Variables with intent (in): 

 

! Imported Array Variables with intent (out): 

 

! Imported Routines: 

 

! <Repeat from Use for each module...> 

 

! Declarations must be of the form: 

! <type>   <VariableName>      ! Description/ purpose of variable 

 

 Imlicit none 

! Global (i.e. public) Declarations: 

! Global Type Definitions: 

 

! Global Parameters: 

 

! Global Scalars: 

 

! Global Arrays: 

 

! Local (i.e. private) Declarations: 

! Local Type Definitions: 

 

! Local Parameters: 

 

! Local Scalars: 

 

! Local Arrays: 

 

! Operator definitions: 

!   Define new operators or overload existing ones. 

 

Contains 

! Define procedures contained in this module. 

 

End module <ModuleName> 

 

!- End of module header

付録 C: 例

C-1変数サブルーチン

This example subroutine has been taken from the UKMO's variational assimilation code. Some additional style rules have been applied such as placing one argument per line and commenting the argument with its intent.

!+ Allocates and calculates the LS field Vtheta

Subroutine Var_LSVtheta &

 ( LocalFS,             & ! in

   LS )                   ! inout

 

! Description:

!   Allocates and calculates the linearization state derived field Vtheta:

!   i.e. the virtual potential temperature.

!

! Method:

!   See UMDP 101 section 3.1.3.

!

! Owner: Phil Andrews

!

! History:

! Version   Date     Comment

! -------   ----     -------

! 0.1     01/09/94   Original code. Phil Andrews

! 0.2     26/10/94   Changed references from theta to thetaL and corrected

! 0.2                the calculation of Vtheta. Mike Thurlow.

! 0.3     07/02/95   Test of FieldStatus added.  Phil Andrews.

! 1.4     11/05/95   Tracing added. JB

!

! Code Description:

!   Language:           Fortran 90.

!   Software Standards: "European Standards for Writing and

!     Documenting Exchangeable Fortran 90 Code".

!

! Parent module: VarMod_LS

!

! Declarations:



! Modules used:

Use VarMod_PFInfo,       only :         &

! Imported routines:

 Var_DeallocateModel,                   &



! Imported Type Definitions:

 ModelDump_type,                        & ! Stores LS PF or Adj states.      

 ModelDumpHeader_type,                  & ! Structures use within 

 Header_type,                           & ! ModelDump_type 

 XYgrid_type,                           & ! ditto

 Zgrid_type,                            & ! ditto

        

! Imported Parameters:

 FieldStatus_absent                       ! FieldStatus code for absent

      

Use VarMod_Constants, only :            &

! Imported Parameters:

 InvGasRatioMinusOne



Use VarMod_Trace, only :                &

! Imported routines:

 Var_TraceEntry,                        &

 Var_Trace,                             &

 Var_TraceExit,                         &



! Imported scalars:

 UseTrace,                              &

 TraceNameLen   



Implicit none



!* Subroutine arguments

! Scalar arguments with intent(in):

 Integer, intent(in)  :: LocalFS          ! value to use for FieldStatus



! Scalar arguments with intent(inout):

 Type (ModelDump_type), intent(inout)   & ! linearization state

                      :: LS 



!* End of Subroutine arguments



! Local parameters:

 Character (len=TraceNameLen), parameter &

                      :: RoutineName = "Var_LSVtheta"



! Local scalars:

 Integer              :: qlevels          ! Number of q levels

 Integer              :: Tlevels          ! Number of temperature levels



!- End of header --------------------------------------------------------------



 If (LS % header % Vtheta % FieldStatus == FieldStatus_absent) then





  !-----------------------------------------------------------------------------

  ![1.0] Initialize: allocate space, calculate required fields etc...

  !-----------------------------------------------------------------------------

   If (UseTrace) call Var_TraceEntry(RoutineName)



   qlevels = LS % header % qT % z % TopLev

   Tlevels = LS % header % thetaL % z % TopLev



  ! Allocate LS % Vtheta:

   Allocate (LS % Vtheta                           &

              (1:LS % header % thetaL % x % len,   &

               1:LS % header % thetaL % y % len,   &

               1:LS % header % thetaL % z % TopLev &

            ) )



  ! Set header values for LS % Vtheta:

   LS % header % Vtheta               = LS % header % thetaL

   LS % header % Vtheta % FieldStatus = LocalFS

 

  ! Obtain derived LS fields as necessary:

   Call Var_LSqc         &

    ( LocalFS +1,        & ! in

      LS )                 ! inout



   Call Var_LStheta      &

    ( LocalFS +1,        & ! in

      LS )                 ! inout





  !-----------------------------------------------------------------------------

  ![2.0] Calculate LS % Vtheta:

  !-----------------------------------------------------------------------------

  ! lowest to top wet level:

   LS % Vtheta(:,:,1:qlevels) = LS % theta(:,:,1:qlevels)                      &

                              * ( 1.0                                          &

                                + ( InvGasRatioMinusOne                        &

                                  * ( LS % qT(:,:,:) - LS % qc(:,:,:) )        &

                                ) )

 

  ! Top wet level +1 to top dry level:

   LS % Vtheta(:,:,qlevels+1:Tlevels) = LS % thetaL(:,:,qlevels +1:Tlevels)



  ! Tidy up:

   Call Var_DeallocateModel &

    ( LocalFS +1,           & ! in

      LS )                    ! inout

 

  ! That's it!

   If (UseTrace) call Var_TraceExit(RoutineName)



 End if

End subroutine Var_LSVtheta


C-2作用素

The following program demonstrates how new operators can be defined in Fortran 90. It also shows how to extend the existing operators and the assignment operation.

!+ Extend assignment and + operators, & define two new ops

!

Module Operators

!

! Description:

!

! Module to extend the assignment operation so that it will

! convert characters to an integer, extend the + operator to

! add logical values, and define two new operators.

!

! Current Code Owner: A N Other

!

! History:

! Version   Date   Comment

! -------  ------  -------

!   1.0    5/5/95  Original code. (A N Other)

!

! Code Description:

! Language: Fortran 90.

!

! The procedure named must be a subroutine with one

! intent(OUT) argument and one intent(in) argument.



  Implicit none



! Override operators:

  Interface assignment(=)

    Module procedure Chars_to_Integer



  End interface



! Similarly the next interface will arrange for Add_Logicals

! to be called if two logical values are added together. The

! procedure named must be a function with two intent(in)

! arguments. The result of the addition is the result of the

! function.



  Interface operator(+)

    Module procedure Add_Logicals



  End interface



! Define new operators:



! This creates a new operator .Half. It is a unary operator,

! because Divide_By_Two only takes one argument.



  Interface operator(.Half.)

    Module procedure Divide_By_Two



  End interface







! This creates a new operator .JPlusKSq. It is a binary

! operator, because J_Plus_K_Squared takes two arguments.



  Interface operator(.JPlusKSq.)

    Module procedure J_Plus_K_Squared



  End interface



Contains



! Define procedures contained in this module



  subroutine Chars_to_Integer(int, int_as_chars)

  

  ! Subroutine to convert a character string containing

  ! digits to an integer.

  

    Character(len=5), intent(in)  :: int_as_chars

    Integer, intent(OUT)          :: int



    Read (int_as_chars, FMT = '(I5)') int

    

  End subroutine Chars_to_Integer



  Function Add_Logicals(a, b) result(c)

  

  ! Description:

  ! Function to implement addition of logical values as an

  ! OR operation.

  

    Logical, intent(in)  :: a

    Logical, intent(in)  :: b

    Logical              :: c

    

    c = a .OR. b

    

  End function Add_Logicals



  Function Divide_By_Two(a) result(b)

    Integer, intent(in)  :: a

    Integer              :: b

    

    b = a / 2

    

  End function Divide_By_Two



  Function J_Plus_K_Squared(j, k) result(l)

    Integer, intent(in)  :: j

    Integer, intent(in)  :: k

    Integer              :: l

    

    l = (j + k) * (j + k)

    

  End function J_Plus_K_Squared

End module Operators



!- End of module header

!+ Test program for operator module

!

Program Try_Operators



! Description:

! Program to test the operators defined in the module

! Operators

!

! Current Code Owner: A N Other

! History:

! Version   Date   Comment

! -------  ------  -------

!   1.0    5/5/95  Original code. (A N Other)

!

! Code Description:

! Language: Fortran 90.



! Modules used:



Use Operators



Implicit none



! Local scalars:

Character(len=5)  :: string5 = '-1234'   ! Char. to integer



Integer           :: i_value = 99999     ! and half operator 

Integer           :: half_value          ! test variables.

Integer           :: n                   ! Test variables  

Integer           :: l = 4               ! for add & square

Integer           :: m = 8               ! operation.



Logical           :: x = .false.         ! Test variables     

Logical           :: y = .true.          ! for logical    

Logical           :: z = .false.         ! addition ops.



!- End of header



  i_value = string5     ! Convert from a string to an integer.

  z       = x + y       ! Add together some logicals.



  Print *, string5, i_value, x, y, z



  half_value = .Half. i_value   ! Use user def unary operator

  n          = l .JPlusKSq. m   ! Use user def binary operator



  Print *, i_value, half_value, l, m, n



End program Try_Operators


C-3関数

This program shows how to define a generic function (one which is defined for arguments of more than one type). It also illustrates how explicit interfaces may be required when using subroutines or functions, and how these are provided automatically if the subroutines or functions are placed in a module.

!+ Generic function to return a cube of a number

!

Module Generic_Cube

!

! Description:

! Module to define a generic function called Cube which

! returns the cube of a number. The function is defined for

! both integer and real arguments so, for example, Cube(2)

! and Cube(4.263) will both work.

!

! Current Code Owner: A N Other

!

! History:

! Version   Date   Comment

! -------  ------  -------

!   1.0    5/5/95  Original code. (A N Other)

!

! Code Description:

! Language: Fortran 90.



  Implicit none



! The interface says that when the generic function Cube is

! used the compiler should either call R_Cube or I_Cube

! depending on the type of the argument.



  Interface Cube

    Module procedure R_Cube, I_Cube



  End interface



Contains



  Function R_Cube(value) result(res)! Compiler knows to call

    Real  :: value                  ! this with real args

    Real  :: res                    ! because 'value' is



    a = value * value * value       ! declared as Real. 



  End function R_Cube               



  Function I_Cube(value) result(res)! Compiler knows to call 

    Integer  :: value               ! this with integer args

    Integer  :: res                 ! because 'value' is



    a = value * value * value       ! declared as Integer.



  End function I_Cube              

End module Generic_Cube



!- End of module header









!+ Print the cube roots numbers.

!

Program Cubes



! Description:

! Program to print the cubes and cube roots of some numbers.

!

! Current Code Owner: A N Other

!

! History:

! Version   Date   Comment

! -------  ------  -------

!   1.0    5/5/95  Original code. (A N Other)

!

! Code Description:

! Language: Fortran 90.





Use Generic_Cube              ! Make the generic function Cube

                              ! available in this program

Implicit none



! Local scalars:

  Integer  :: i = 8           ! Integer test value



  Real     :: a = 8.01        ! Real test value



! Interface to define a generic function Cube_Root which

! returns the cube root of an integer or real number.

! Because the functions R_Cube_Root and I_Cube_Root are not

! in a module, the compiler doesn't know the types of their

! arguments when it compiles the main program. This means we

! have to give them explicitly in the interface, which is

! laborious and error prone. The moral of the tale is - use

! a module!



Interface Cube_Root

  Function R_Cube_Root(value) result(res)

    Real  :: value

    Real  :: res



  End function R_Cube_Root



  Function I_Cube_Root(value) result(res)

    Integer  :: value

    Integer  :: res



  End function I_Cube_Root

End interface



 ! Print the cubes and cube roots of some numbers using the

 ! Cube and Cube_Root generic functions.



  Write (*, "(3(A, I9.3))") "i: ", i, " Cube: ", Cube(i), &

                            " Cube root: ", Cube_Root(i)

  Write (*, "(3(A, F9.4))") "a: ", a, " Cube: ", Cube(a), &

                            " Cube root: ", Cube_Root(a)  

                    

End program Cubes





!+ Return the cube root of a real number

!

Function R_Cube_Root(value) result(res)



! Description:

! Function returning the cube root of a real number.

!

! Current Code Owner: A N Other

!

! History:

! Version   Date   Comment

! -------  ------  -------

!   1.0    5/5/95  Original code. (A N Other)

!

! Code Description:

! Language: Fortran 90.



  Implicit none



! Scalar arguments with intent(in)

  Real  :: value            ! Input value



! Local scalars

  Real  :: res              ! Result



!- End of header



  res = value ** (1.0 / 3.0)



End function R_Cube_Root





!+ Return the cube root of an integer

!

Function I_Cube_Root(value) result(res)



! Description.

! Function returning the integer part of the cube root of

! its integer argument.

!

! Current Code Owner: A N Other

!

! History:

! Version   Date   Comment

! -------  ------  -------

!   1.0    5/5/95  Original code. (A N Other)

!

! Code Description:

! Language: Fortran 90.



  Implicit none



! Scalar arguments with intent(in)

  Integer  :: value          ! Input value



! Local scalars

  Integer  :: res            ! Result



!- End of header



  res = int(Real(value) ** (1.0 / 3.0))



End function I_Cube_Root

Last updated: 23 October 1996 by cpjones@meto.gov.uk | © Crown Copyright 1996