−Polcher氏作プログラムの解説と使い方−(under construction)
ISLSCP−なんだか妙に発音しにくそうな略語です.これはInternational Satellite Land-Surface Climatology Projectの略で,少なくとも筆者の周りでは「いするすきぷ」と発音されています.
ISLSCPについての詳しい説明は省略しまして(というか筆者も勉強不足),そのプロジェクトの成果物について紹介します.それはISLSCP Initiative data setsというものです.
このデータセットは,世界中を緯度経度1度ずつに区切ったグリッドの各点(つまり東西360点×南北180点)について,1987年と1988年の2年間のいろいろな気象データの6時間ごとの値を示したものです.あっさり「いろいろな気象データ」と言ってしまいましたが,その内容は実に膨大なもので,CD-ROM版では5枚分となっています.
このデータセットは,ALMAにとっても大変重要なものです.というのは,過去に行われた各種LSM相互比較プロジェクトでは,LSMへの「共通の」入力データとして,このデータセットが使われたことがあるのです(例:GSWP).つまり,いくつかのモデルに対して,ISLSCP Iという全く同じ境界条件を与えたときの挙動の差が解析されたのです.ある意味でモデル入力のグローバルスタンダードというわけで,使用事例が豊富なのです.ALMAは別にISLSCP Iに依存しているわけではありませんが,まずその第一歩としては入力にISLSCP Iを使って過去の結果と比較してみるという作業が不可欠だと考えられるのです.
ISLSCP Iのデータフォーマットは単純なテキストです.というわけで,ALMAにあわせたLSMにこのデータを食わせるには,とりあえずこれをnetCDF化しなければなりません.これを実現するのがALMA関係者のPolcher氏が作ったiscscp2ncというプログラム・コマンド類です.
Polcher氏の作ったプログラムのWWW資料はhttp://www.ipsl.jussieu.fr/~ssipsl/data/data_islscp.htmlに用意されています.そこの中に,ソースファイルへのリンクが用意されています.まずはそれをとってきましょう.ファイル名はislscp2nc.tar.Zです.バージョンはよく分かりません(研究用プログラムでは,これは結構多い)が,更新日時は2000年5月15日と書いてあります.ALMA規約準拠のnetCDFを生成すると書いてあるので,ちょっと楽しみです.
さて,まずはファイルを解凍しましょう.なお,ここではサブディレクトリを作る必要はありません.後のtarの実行の時にサブディレクトリislscp2ncが自動的に作られるからです:
% uncompress islscp2nc.tar.Z %
ここでディレクトリの内容を見てみましょう.islscp2nc.tarというファイルができていればここまではOKです.次にこのtarファイルの内容を確認します:
% ls -alF (中略) -rw-r--r-- 1 foo group1 61440 Jul 15 19:31 islscp2nc.tar (略) % tar -tvf islscp2nc.tar drwxr-xr-x 6072/3000 0 May 29 16:50 2000 ./islscp2nc/ -rw-r--r-- 6072/3000 17262 May 15 21:16 2000 ./islscp2nc/calendar.F90 -rwxr-xr-x 6072/3000 687 May 15 21:16 2000 ./islscp2nc/do_allfiles -rwxr-xr-x 6072/3000 395 May 15 21:16 2000 ./islscp2nc/do_Europe -rwxr-xr-x 6072/3000 386 May 15 21:16 2000 ./islscp2nc/do_Scan -rw-r--r-- 6072/3000 1568 May 15 21:16 2000 ./islscp2nc/errioipsl.F90 -rw-r--r-- 6072/3000 26500 May 29 16:44 2000 ./islscp2nc/islscp2nc.F90 -rw-r--r-- 6072/3000 794 May 15 21:51 2000 ./islscp2nc/Makefile -rw-r--r-- 6072/3000 3802 May 15 21:17 2000 ./islscp2nc/stringop.F90
ここまでOKですか?では実際にファイルを取り出してみましょう.なお,このときカレントディレクトリの下にislscp2ncというファイルが自動的に作られます:
% tar -xvf islscp2nc.tar x ./islscp2nc, 0 bytes, 0 tape blocks x ./islscp2nc/calendar.F90, 17262 bytes, 34 tape blocks x ./islscp2nc/do_allfiles, 687 bytes, 2 tape blocks x ./islscp2nc/do_Europe, 395 bytes, 1 tape blocks x ./islscp2nc/do_Scan, 386 bytes, 1 tape blocks x ./islscp2nc/errioipsl.F90, 1568 bytes, 4 tape blocks x ./islscp2nc/islscp2nc.F90, 26500 bytes, 52 tape blocks x ./islscp2nc/Makefile, 794 bytes, 2 tape blocks x ./islscp2nc/stringop.F90, 3802 bytes, 8 tape blocks
これで解凍は終了です.といってもまだ前途は多難ですが…
次にメイクを行います.普通はここでconfigureを行なうところなのですが,どうやらこのプロダクトは直接設定ファイルを書き換えさせる仕様のようです.まずは,上記のファイル解凍でできたディレクトリに移り,インストール法を説明したドキュメントを探してみましょう:
% cd islscp2nc % ls -alF (略)
出てくるのはMakefileと*.F90,それにいくつかのシェルスクリプト類だけのようです.つまり説明文書はありません.説明文書は,むしろ上記のWWW資料が中心のようです.そこを探すと,ありました.do_allfilesというシェルスクリプトを動かせとあります.このシェルスクリプトの内容を覗いてみると,冒頭が
# !/bin/csh # # make islscp2nc echo 'COMPILED' /bin/rm -f CD_* *.log
なんていうことになっていて,つまりいきなりmakeを立ち上げている模様です.うーん大丈夫なのでしょうか.とりあえずMakefileの中を見てみます.幸いなことに,わずか32行です.チェックしたところ,アヤシイところはなさそうです.というわけで,早速makeを走らせ,帰ってきたメッセージに対処する作戦にでます.
% make clean (エラーがいくつか出るが気にしない) % make islscp2nd /bin/cp errioipsl.F90 errioipsl.f90 pgf90 -c errioipsl.f90 sh: pgf90: not found *** Error code 1 make: Fatal error: Command failed for target `errioipsl.o'
このエラーメッセージは気にしないわけには行きません.つまり,pgf90なんて命令は知らんよというメッセージです.これは名前からすると,Portland Group社製のFORTRAN90コンパイラです(プログラミング言語マニアならご存知かと思いますが,HPFですね).筆者のテスト環境(言い忘れましたが,SUN SPARC上のSolaris7,FORTRANコンパイラはSUN Workshop Compilers 4.2のf90です)ではpgf90なんてインストールされておらず,当然エラーとなるわけです.というわけでMakefile内の以下を書き換えます(しかしどうしてconfigureスクリプトが入っていないのだ?):
#F90 = pgf90 F90 = f90 RM = /bin/rm CP = /bin/cp (以下略)
ただし,rmやcpがそれぞれ/bin/rm,/bin/cpに入っていない場合は上記の2行目や3行目も書き換える必要があります(やれやれ).では書き換えた後にもう一度挑戦です:
% make islscp2nd /bin/cp errioipsl.F90 errioipsl.f90 f90 -c errioipsl.f90 /bin/rm errioipsl.f90 /bin/cp stringop.F90 stringop.f90 f90 -c stringop.f90 /bin/rm stringop.f90
お,何か上手くいっているようです.しかし,結局またダメになってしまうのでした:
INCLUDE "netcdf.inc" cf90-63 f90comp: ERROR ISLSCP2NC, File = islscp2nc.f90, Line = 58 Cannot open INCLUDE file "netcdf.inc". (中略) f90: SunSoft F90 Version 1.0.1.0 (21229283) Tue Jul 18, 2000 14:56:47 f90: COMPILE TIME 0.620000 SECONDS f90: MAXIMUM FIELD LENGTH 2590320 DECIMAL WORDS f90: 856 SOURCE LINES f90: 16 ERRORS, 1 WARNINGS, 0 OTHER MESSAGES, 0 ANSI f90: CODE: 1216 WORDS, DATA: 25 WORDS *** Error code 1 make: Fatal error: Command failed for target `islscp2nc.o'
大量のエラーが出てきましたが,こういうときに見るべきなのは最初に発見されたエラーです(最初の不具合をつぶすと,後に続くエラーメッセージが皆消えてしまうというのはよくあることです).それはCannot open INCLUDE fileというメッセージですから,インクルードファイルが見当たらないという意味だとわかります.そのファイル名はnetcdf.inc.明らかにnetCDF用のインクルードファイルです.FORTRANコンパイラが,このファイルの存在を最初から知っているわけがありませんから,これはユーザが明示的に位置を教えてやる必要があります.これには環境変数を用いる方法もありますが,ここでは直接Makefileを書き換える手段にでます.
netCDFのライブラリやインクルードファイルがどこに入っているのか(それ以前に,そもそも入っているのか)はホストの管理者に聞いてみてください.以下ではその位置(ディレクトリ)を/usr/local/netcdfと仮定します.この場合,netCDF用のインクルードファイルは/usr/local/netcdf/includeに入ります.
FORTRANコンパイラ,というかコンパイラ一般にいえることですが,インクルードファイルを探す場所の指定には-Iオプションを使うのが普通です.つまり,f90命令に,-I/usr/local/netcdf/includeオプションをつけるわけです.そういう風にMakefileを書き換えます:
NETCDFHOME = /usr/local/netcdf F90 = f90 RM = /bin/rm CP = /bin/cp FLAGS = -I$(NETCDFHOME)/include F_LOAD = -Wl,-Bstatic (後略)
Makefileでは,後に「F90」(現在の例の場合,f90)の指定に「$(FLAGS)」という変数を含めています.普通は,このFLAGSという変数に,「F90」に与えるオプションを書き入れます.それが上記の「FLAGS = ...
」という行です.ただしFLAGSの定義に直接/usr/local/netcdf/includeを書き込まずに,ちょっと間接的に,別に定義したnetCDFホームディレクトリ用変数NETCDFHOME(=/usr/local/netcdf)を用いています.これはNETCDFHOMEは別のところでも使う可能性があり(実際,後で出てきます),netCDFのホームディレクトリをそのたびに書くのは面倒だし,後での変更も大変になるからです.
それではもうメイク可能になるでしょうか?
% make islscp2nc (略) f90 -o islscp2nc errioipsl.o stringop.o calendar.o islscp2nc.o -Wl,-Bstatic -L/distrib/local/netcdf/pgf/lib/ -lnetcdf f90: Warning: Option -Wl,-Bstatic passed to ld, if ld is invoked, ignored otherwise /usr/ccs/bin/ld: illegal option -- W /usr/ccs/bin/ld: illegal option -- W (中略) *** Error code 1 make: Fatal error: Command failed for target `islscp2nc'
ハイ残念でした.しかし一歩前進です.というのは,細かい点は省略しますが,コンパイルはすべて終わり,リンクの段階に入っていることがわかるからです(リンクの段階といえば,もう実行プログラムの完成一歩手前です).で,上記のメッセージは,f90というコマンドが「-Wl,-Bstatic」なんてコマンドを知らないので別のコマンドld(これはリンクを行うコマンドです)にそのオプションを横流しすると言っています.そして,ldのほうは,オレだってそんなオプションは知らんとエラーを返してきているのです.なお,Makefile内では,リンクに使うオプションは変数F_LOADに入っています.
「-Wl」はコンパイラ依存が強いオプション(-Bstaticという文字列をldに横流しする指令と思われますが未確認)で,f90では用意されていません.一応は削除します.「-Bstatic」はごく普通なので残します.これでうまくいくでしょうか.実はダメなのです.
NETCDFHOME = /usr/local/netcdf F90 = f90 RM = /bin/rm CP = /bin/cp FLAGS = -I$(NETCDFHOME)/include F_LOAD = -Bstatic
% make islscp2nc f90 -o islscp2nc errioipsl.o stringop.o calendar.o islscp2nc.o -Bstatic -L/distrib/local/netcdf/pgf/lib/ -lnetcdf ld: fatal: library -lnetcdf: not found ld: fatal: File processing errors. No output written to islscp2nc *** Error code 1 make: Fatal error: Command failed for target `islscp2nc'
何が起こったのでしょうか.これは上の強調表示部分に示した「-L/distrib/local/netcdf/pgf/lib/ -lnetcdf」の部分,特に後半が問題なのです.「-L」オプションはリンクの際に組み込むライブラリの場所(ディレクトリ),「-l」オプションはライブラリの名前を指定するものです(上記ではnetCDF関係のライブラリを組み込もうとしていることは明白でしょう).しかし,上記でしていている-L/distrib/local/netcdf/pgf/lib/なる場所には探しているnetCDFライブラリなどないのです(そもそもこんなディレクトリはこちらの環境にはありません).「-L」オプションをこちらの環境に合わせて書き換える必要があります.
また,「-L」にせよ「-l」にせよ,リンクのオプションは変数F_LOADに入れる構造になっていると見るのが普通です.以上を考えると,Makefileは次のような構造になるはずです.
NETCDFHOME = /usr/local/netcdf F90 = f90 RM = /bin/rm CP = /bin/cp FLAGS = -I$(NETCDFHOME)/include F_LOAD = -Bstatic -L$(NETCDFHOME)/lib -lnetcdf islscp2nc : errioipsl.o stringop.o calendar.o islscp2nc.o $(F90) -o islscp2nc errioipsl.o stringop.o \ calendar.o islscp2nc.o $(F_LOAD) (後略)
% make islscp2nc f90 -o islscp2nc errioipsl.o stringop.o calendar.o islscp2nc.\ -Bstatic -L/usr/local/netcdf/lib -lnetcdf % ls -alF islscp2nc -rwxr-xr-x 1 foo group1 7708872 Jul 18 15:43 islscp2nc* %
ちゃんとmakeは終わったようです.islscp2ncという実行ファイルもできています.お茶でも飲んで一息入れましょうか.
もしヒマがあればMakefileについて,次のような手直しをすると精神衛生上いいかもしれません.現行のMakefileで問題ないと思う方は別にいいのですが…
以上の変更を加えたMakefileを以下に示します:
# # Makefile for islscp2nc # Originally made by Dr. Polcher # http://www.ipsl.jussieu.fr/~ssipsl/data/data_islscp.html # 2000-May-15 version # # Revised by AGATASHI # # 2000-Jul-18 version 1.1 NETCDFHOME = /usr/local/netcdf F90 = f90 RM = /bin/rm CP = /bin/cp FLAGS = -I$(NETCDFHOME)/include F_LOAD = -Bstatic -L$(NETCDFHOME)/lib -lnetcdf CLEANFILES = *.f90 *.M *.o islscp2nc .SUFFIXES : .F90 $(SUFFIXES) .F90.o: $(CP) $< $*.f90 $(f90) $(flags) -c $*.f90 $(rm) $*.f90 all : islscp2nc islscp2nc : errioipsl.o stringop.o calendar.o islscp2nc.o $(F90) -o islscp2nc errioipsl.o stringop.o calendar.o islscp2nc.o $(F_LOAD) clean : $(RM) -f $(CLEANFILES)
各*.F90ファイルについてのルールを一つ一つ書いているのではないことに注目してください.
注記1:とりあえず今はオプティマイズについては触れないでおきます.
注記2:実は上記のメイクファイルは完全ではありません.各*.oと*.F90との間にも微妙な依存関係があるからです.gmakeにおける-jオプションやSUNのdmakeコマンドのように並列メイクを行なう場合に問題が起きることがあります.しかし並列メイクのような凝ったことをするほどのサイズのプログラムではありませんから,そのままにしておきます.
では今や実行可能となったislscp2ncコマンドを動かしてみましょう.するとWWW資料にあるようにいろいろ聞いてきますので,とりあえず答えてみます:
% ./islscp2nc Month to start the transfer (in yymm) (Starting the first day of the month) : 8701 Month to end the transfer (in yymm) (Ending the last day of the month) : 8701 Path in which the ISLSCP files are to be found : /export/islscp Path to which the netCDF files should be written : . Should the data be stored only over land points [y/n] ? n File name for the netCDF file to be created : firsttest Longitude interval to be written to the file (lon_min, lon_max): 0 180 Latitude interval to be written to the file (lat_min, lat_max): -90 90 The netCDF that will be created :./firsttest.nc BEGIN : yy, mm, dd :1987, 1, 1 END : yy, mm, dd :1987, 1, 31 >>>>>>>>>>>>> ibeg, imr :180, 180 >>>>>>>>>>>>> jbeg, jmr :1, 179 DOING :87010100 cp: cannot access /export/islscp/cdrom3/y87m01/temp_2m/t7010100.z cp: cannot access /export/islscp/cdrom3/y87m01/dwpnt_2m/d7010100.z cp: cannot access /export/islscp/cdrom4/y87m01/mwnd_10m/w7010100.z cp: cannot access /export/islscp/cdrom4/y87m01/sur_prsr/p7010100.z cp: cannot access /export/islscp/cdrom5/y87m01/ecm_lang/hyb_lwdn/l7010100.z cp: cannot access /export/islscp/cdrom5/y87m01/ecm_lang/hyb_swdn/s7010100.z cp: cannot access /export/islscp/cdrom5/y87m01/nmc_gpcp/totl_prc/o7010100.z chmod: No match. uncompress: No match. lib-1001 : UNRECOVERABLE library error Encountered during a list-directed READ from unit 11 Fortran unit 11 is connected to a sequential formatted text file: "CD_dwpnt_2m" Abort
はい見事にコケました.これは次のようなことが原因です:
今回は最初の方だけで引っかかりましたが,これを回避しても2,3番目でまた引っかかるのは明白です.この対象となるコード部分はislscp2nc.F90の217〜236行,ならびに566〜649行にあります:
! ! ! Get cdrom files and uncompress them ! ! WRITE(str_yy,'(I2.2)') year-1900 WRITE(str_mm,'(I2.2)') month WRITE(str_dd,'(I2.2)') day WRITE(str_hh,'(I2.2)') hour ! WRITE(*,*) 'DOING :', str_yy,str_mm, str_dd, str_hh ! temp_2m ! K command='cp '//in_path(1:LEN_TRIM(in_path))//'cdrom3/y'// str_yy//'m'//str_mm//& & '/temp_2m/t'//str_yy(2:2)//str_mm// str_dd//str_hh//'.z CD_temp_2m.Z' iout = system(command) (中略) ! Get the right permissions iout = system('chmod 644 CD_*.Z') ! Uncompress iout = system('uncompress CD_*.Z') (中略) OPEN (11, file='CD_dwpnt_2m') READ(11,*) dewt CLOSE(11)
さて,これに対する対策はいくつか考えられます.まずはパスおよびファイル名に関する方策を挙げてみましょう:
(ここまで,2000/07/28までに記)
で結局採用した「仕様」は以下のとおりです:
大文字小文字の変換は,FORTRANでは標準で用意されてはいないですから,自分で作るか人のものを借りてくるかしかありません.ちょうど,このプログラムのアーカイブの中にあるstringop.F90に文字列関係の関数がたくさん用意されているのでそれを流用できそうです.探してみると,ありました.こんなソースです:
SUBROUTINE struppercase(str) ! ! Transforms a string into uppercase ! IMPLICIT NONE ! CHARACTER*(*) :: str ! INTEGER :: i,ic ! DO i=1,LEN_TRIM(str) ic = IACHAR(str(i:i)) IF ( ic .GE. 97 .AND. ic .LE. 122 ) THEN str(i:i) = CHAR(ic-32) ENDIF ENDDO ! END SUBROUTINE struppercase
ところがこれは移植性に問題アリです.文字コードがASCIIならいいけどEBCDICならどうなのでしょう?結局次のようなルーチンを自作しました.動作は遅いでしょうがしょうがないです:
! ! UCASE : change lower letters to upper ones ! CHARACTER*(*) FUNCTION UCASE(str_org) CHARACTER*(*) str_org CHARACTER (LEN=26) :: upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' CHARACTER (LEN=26) :: lower = 'abcdefghijklmnopqrstuvwxyz' INTEGER :: pos INTRINSIC LEN_TRIM,INDEX UCASE = str_org DO i=1,LEN_TRIM(UCASE) pos=INDEX( lower , UCASE(i:i)) IF (pos > 0) UCASE(i:i) = upper(pos:pos) END DO END FUNCTION UCASE
FORTRANを使うのは久しぶりなので何か妙な言い回しが多いかもしれないですがご容赦ください.
(ここまで2000/08/04記)
(以下続く.結局I/O部分を全面的に書き換え,SUNのFORTRAN90 1.2(Worksho4.2)の信じがたいほどオバカなバグに悩まされながらも現在は動作に成功.最適化・並列化なしですと,1年分全球のデータを作るのに約2時間,ファイルアクセスをチューニングして1時間30分弱,さらに最適化オプションを調整してして約53分といったところです.もうすこし速くしたいですね.時間を見つけて細かい調整日記を書くので請うご期待!)