NetCDF错误处理辅助程序
使用NetCDF API时一定要加上函数返回码检查机制,这会给你节省很多调试时间
因为NetCDF库设计为每一个API函数返回一个整数值用于表明函数执行状态(成功或者失败,失败的原因有多种可能,其错误码也不同),但程序不会因为函数执行未成功便终止程序,而是会继续执行下去,但通过API函数获取的信息是错误的。
下面以读一个NetCDF文件获取其维数信息为例说明。有一个文件名为tos_O1_2001-2002.nc
文件,元数据如下:
dimensions:
lon = 180 ;
lat = 170 ;
time = UNLIMITED ; // (24 currently)
bnds = 2 ;
variables:
double lon(lon) ;
lon:standard_name = "longitude" ;
...
假如不小心敲错了文件名,如以下代码所示,同时调用netcdf api也没有检查函数返回值,你会发现程序会正常运行结束,输出lon/lat维数大小,但结果是不对的。
program main
use netcdf
implicit none
integer :: ncid,status
integer :: nlon,nlat
integer :: londimid,latdimid
character(len=128) :: file
file = 'tos_O1_2001-2002.nc1' !wrong filename
status = nf90_open(trim(file),NF90_NOWRITE,ncid)
status = nf90_inq_dimid(ncid, "lon", londimid)
status = nf90_inquire_dimension(ncid,londimid, len=nlon)
status = nf90_inq_dimid(ncid, "lat", latdimid)
status = nf90_inquire_dimension(ncid,latdimid, len=nlat)
print*, 'nlon = ',nlon, 'nlat = ',nlat
status = nf90_close(ncid)
end
我这里测试结果如下,坐标维大小完全不对。
$ ifort -I$NETCDF/include test.f90 -L$NETCDF/lib -lnetcdff -lnetcdf
$ ./a.out
nlon = 4 nlat = 4
但如果加上对NetCDF函数返回码检查机制,则可以在发现错误时输出错误原因并提前退出程序,官方建议handle_error处理机制:判断返回码是否等于NF90_NOERR
,不等于说明出错,利用NF90_STRERROR
函数将整数返回码转换成字符串,更加有意义,最后终止程序。
subroutine handle_err(status)
use netcdf
implicit none
integer :: status
if(status /= nf90_noerr) then
print*, nf90_strerror(status)
STOP 'Stopped'
endif
end subroutine handle_err
program main
use netcdf
implicit none
integer :: ncid,status
integer :: nlon,nlat
integer :: londimid,latdimid
character(len=128) :: file
file = 'tos_O1_2001-2002.nc1'
status = nf90_open(trim(file),NF90_NOWRITE,ncid)
call handle_err(status)
status = nf90_inq_dimid(ncid, "lon", londimid)
call handle_err(status)
status = nf90_inquire_dimension(ncid,londimid, len=nlon)
call handle_err(status)
status = nf90_inq_dimid(ncid, "lat", latdimid)
call handle_err(status)
status = nf90_inquire_dimension(ncid,latdimid, len=nlat)
call handle_err(status)
print*, 'nlon = ',nlon, 'nlat = ',nlat
status = nf90_close(ncid)
end
编译执行结果如下,报告没有找到文件,并终止程序。
No such file or directory
Stopped
如果遇到坐标名不对,会报NetCDF: Invalid dimension ID or name
等等信息,有助于排查。
handle_err子程序
官方文档里使用的handle_err函数如下(Fortran 90/77接口的):
- Fortran 90接口
subroutine handle_err(status)
use netcdf
implicit none
integer :: status
if(status /= nf90_noerr) then
print*, nf90_strerror(status)
STOP 'Stopped'
endif
end subroutine handle_err
- Fortran 77 接口
SUBROUTINE HANDLE_ERR(STATUS)
INCLUDE 'netcdf.inc'
INTEGER STATUS
IF (STATUS .NE. NF_NOERR) THEN
PRINT *, NF_STRERROR(STATUS)
STOP 'Stopped'
ENDIF
END
加强版错误处理子程序
参考ARWPOST使用的错误处理子程序,其利用C标准的预定义宏(__FILE__ , __LINE__
)变量获得出错源代码文件及行数。
子程序定义:
subroutine handle_nc_error(src_file,lineno,status)
use netcdf
implicit none
character(len=*),intent(in) :: src_file
integer,intent(in) :: lineno,status
if(status/=nf90_noerr)then
print*, src_file, '(', lineno, '): ',trim(nf90_strerror(status))
write(0,*) src_file, '(', lineno, '): ',trim(nf90_strerror(status))
stop "Stopped by netCDF"
endif
end subroutine handle_nc_error
子程序调用:
call handle_nc_error(__FILE__, __LINE__, status)
既然它们是宏定义,源代码就必须经过预处理,否则直接编译会出错,使用-cpp
进行预处理。
$ ifort -cpp -I$NETCDF/include test.f90 -L$NETCDF/lib -lnetcdff -lnetcdf
执行输出
$ ./a.out
test.f90( 35 ): No such file or directory
test.f90( 35 ): No such file or directory
Stopped by netCDF
小结
使用NetCDF API时一定要加上函数返回码检查机制,这会给你节省很多调试时间

