本記事では、FortranでNetCDFファイルを読み込む方法を紹介します。
NetCDFファイルとは
また、NetCDFファイルは、ncdumpコマンドを使うことで、どのようなデータが格納されているのか調べることができます。
使用するデータ
さっそくコードをNetCDFファイルを読み込むコードを見ていきます。
例では、このような4次元データを読み込んでいます。
netcdf PRES_0020.pe000000 {
dimensions:
z = 75 ;
x = 512 ;
y = 512 ;
time = UNLIMITED ; // (21 currently)
variables:
float PRES(time, z, y, x) ;
PRES:long_name = "pressure" ;
PRES:units = "Pa" ;
PRES:standard_name = "air_pressure" ;
PRES:_FillValue = -9.9999e+30f ;
PRES:missing_value = -9.9999e+30f ;
PRES:coordinates = "lon lat height time" ;
PRES:cell_measures = "volume: cell_volume" ;
PRES:grid = "grid" ;
PRES:location = "face" ;
上記の結果は、ncdumpコマンドを用いています。
コード
program netcdf_read
use netcdf
implicit none
!!!!read
character(10),parameter::var_pres4D="PRES"
character(len=*),parameter::FILE_pres4D = "./"//trim(var_pres4D)//"_0020.pe000000.nc"
integer::ncid
integer,parameter::ndims = 4
integer::xx_dim,yy_dim,zz_dim,tt_dim
real,allocatable::xx(:),yy(:),zz(:),tt(:)
integer::var_pres
real,allocatable::press(:,:,:)
integer::start4D(ndims),count4D(ndims)
integer::x,y,z,t
real::var_mid
integer::err
call check( nf90_open(FILE_pres4D,nf90_nowrite,ncid) )
call get_dimension(ncid,"x",xx_dim,xx)
call get_dimension(ncid,"y",yy_dim,yy)
call get_dimension(ncid,"z",zz_dim,zz)
call get_dimension(ncid,"time",tt_dim,tt)
allocate(press(xx_dim,yy_dim,zz_dim))
call check( nf90_inq_varid( ncid, var_pres4D, pres ) )
count3D = (/ xx_dim, yy_dim, zz_dim, 1 /)
start3D = (/ 1, 1, 1, 1 /)
do t = 1, tt_dim
start4D(4) = t
call check( nf90_get_var( ncid, var_pres, press, start = start4D, count = count4D ))
do z = 1, zz_dim
do y = 1, yy_dim
do x = 1, xx_dim
!!!処理
enddo
enddo
enddo
enddo
call check( nf90_close(ncid) )
contains
subroutine get_dimension( ncid, name, dim, dims )
integer, intent( in)::ncid
character(len=*),intent( in)::name
integer, intent(out)::dim
real,allocatable, intent(out)::dims(:)
integer::varid,dimid
call check( nf90_inq_dimid( ncid, name, dimid ) )
call check( nf90_inquire_dimension( ncid, dimid, len=dim ) )
allocate(dims(dim), stat=error)
if ( error /= 0 ) print *,name,":Allocation request denied"
call check( nf90_inq_varid( ncid, name, varid ))
call check( nf90_get_var( ncid, varid, dims ))
end subroutine get_dimension
subroutine check(status)
integer,intent(in):: status
if ( status /= nf90_noerr ) then
print *, trim(nf90_strerror(status))
stop "Stopped"
endif
end subroutine check
end program netcdf_read
FortranでNetCDFファイルを読み込むと、書くコード量が多くなります。
解説
program netcdf_read
use netcdf
implicit none
use netcdf
でnetcdfライブラリを使います。
call check( nf90_open(FILE_pres4D,nf90_nowrite,ncid) )
call get_dimension(ncid_pres4D,"x",xx_dim,xx)
call get_dimension(ncid_pres4D,"y",yy_dim,yy)
call get_dimension(ncid_pres4D,"z",zz_dim,yy)
call get_dimension(ncid_pres4D,"time",tt_dim,tt)
nf90_open
関数で NetCDF ファイルを開きます。
nf90_nowrite
としているの読み込み専用を意味します。
これでファイルが開かれ、ncid
にファイルと対応した識別子が代入されます。
get_dimension
関数でNetCDFファイルの各次元の長さを抽出しています。
”x”
は、今回のNetCDFファイルでは、512ですので、xx_dim
に512が代入されます。
”y”
は、今回のNetCDFファイルでは、512ですので、yy_dim
に512が代入されます。
"z"
は、今回のNetCDFファイルでは、75ですので、zz_dim
に75が代入されます。
”time”
は、今回のNetCDFファイルでは、21ですので、tt_dim
に21が代入されます。
allocate(press(xx_dim,yy_dim,zz_dim))
press
の配列に先ほど取得したx方向y方向z方向の長さを代入しています。
call check( nf90_inq_varid( ncid, var_pres4D, var_pres ) )
nf90_inq_varid
関数で変数の識別子を取得します。
count4D = (/ xx_dim, yy_dim, zz_dim, 1 /)
start4D = (/ 1, 1, 1, 1 /)
start4D
とcount4D
で変数の次元毎に読み込む開始と一回に読み込む量を取得します。
今回は、x=1,y=1,z=1,t=1から開始し、一回のループでx方向に512,y方向に512,z方向に75のデータを読み込みます。
do t = 1, tt_dim
start4D(4) = t
call check( nf90_get_var( ncid, var_pres, sfc_press, start = start4D, count = count4D ))
do z = 1, zz_dim
do y = 1, yy_dim
do x = 1, xx_dim
!!!処理
enddo
enddo
enddo
enddo
一気にデータを読み込むとメモリを多量に使うことから、次元time
で繰り返し処理をして、値を取得しています。
start4D
の4つ目の要素をtにすることで、その時刻のデータを取り出すことができます。
実行
このまま、gfortranでコンパイルすることはできません。
use netcdf
1
Fatal Error: Can't open module file 'netcdf.mod' for reading at (1): No such file or directory
このようなエラーメッセージが出てしまいます。
netcdf.modとlibnetcdff.aをインストールする必要があります。
この方法は、こちらのサイトにて詳しく書かれています。
$ gfortran ファイル名.f90 -I INCLUDE -L LIBROOT -lnetcdff
netcdf.modがある場所をINCLUDE
、libnetcdff.aがある場所をLIBROOT
とします。
これで実行できます。
また、毎回、このコマンドを打つのはしんどいので、シェルスクリプトを書いて、そのファイルをコマンドとして使いましょう。
#!/bin/bash
LIBROOT=-L/usr/local/lib64
INCLUDE=-I/usr/local/include
LIBS=-lnetcdff
echo gfortran $@ $INCLUDE $LIBROOT $LIBS
gfortran $@ $INCLUDE $LIBROOT $LIBS
LIBROOT
とINCLUDE
は、環境に合わせて変えてください。
このファイルをgfortncとすると、
$ gfortnc ファイル名.f90
のみで実行できます。
自作コマンドについては、詳しくはこちら