1. NAMELIST简介
NAMELIST(有名列表)是一种特殊的I/O方法,它将一组变量和数值封装在一起,进行输入/输出操作。其声明形式如下:
代码语言:javascript复制NAMELIST /nl_group_name/ var1[,var2,...]
其中nl_group_name是有名列表的名字,var1、var2等是列表中的变量。同一个NAMELIST中可以有不同类型的变量或数组。
NAMELIST的输出语句形式如下:
代码语言:javascript复制WRITE(UNIT=unit,NML=nl_group_name,[...])
其中unit是数据写入的I/O单元,nl_group_name是被写的有名列表的名字。当执行WRITE语句时,列表中的所有变量名都会和其值一起按照特定的顺序输出。输出的第一项是&,然后紧跟列表的名字,然后是一系列按照"Name=Value"格式的输出值。最后,列表以一个斜杠/结束。每个数值的输出格式是由编译器自行决定的。
例如,对有名列表
NAMELIST /MYLIST/ i,j
当i=1,j=2时
执行WRITE(UNIT=*,NML=MYLIST)后,屏幕会显示
&MYLIST
I = 1,
J = 2
/
有名列表的READ语句通用形式如下:
代码语言:javascript复制READ(UNIT=unit,NML=nl_group_name,[...])
其中unit是将要读取数据的I/O单元,nl_group_name是被读的有名列表的名字。当执行表控有名列表的READ语句时,程序搜索带有&nl_group_name的输入文件行,它表示有名列表的开始。然后读取有名列表中的所有数值,直到碰到字符"/"终止READ。列表变量可以不出现在输入文件的有名列表中,此时READ前后这些变量的值保持不变。而且输入文件中列表变量可以出现在&nl_group_name和字符"/"间的任意一行。
使用NAMELIST可以比较方便地在程序之间传递一系列数据,省去了很多文件读写格式上的麻烦。
2. 陷阱展示
2.1 操作环境
编译器:ifort 19.0.1.144
操作系统:Ubuntu 18.04.2
2.2 文件准备
现在我们先创建一个包含NAMELIST的MODULE,将其写在namelist.f90中:
代码语言:javascript复制module namelist_module
implicit none
integer :: i,j
namelist /int_list/ i
end module namelist_module
注意,此时namelist_module中有i和j两个值,但是int_list中目前只有一个i。
然后写一个主程序完成NAMELIST的READ和WRITE,将其写在Pitfall.f90中:
代码语言:javascript复制program main
use namelist_module
implicit none
integer :: io
open(newunit=io,file='Input')
read(unit=io,nml=int_list)
write(unit=*,nml=int_list)
end program
NAMELIST的输入是通过读取文件Input完成的,输出则是直接输出到屏幕上。Input的内容为:
& int_list
i=1
/
为了方便编译,需要写一个Makefile文件,内容如下:
代码语言:javascript复制namelist.o : namelist.f90
ifort namelist.f90 -c
Pitfall.o : Pitfall.f90
ifort Pitfall.f90 -c
Pitfall : namelist.o Pitfall.o
ifort namelist.o Pitfall.o -o Pitfall
clean :
rm -f *.o *.mod && rm -f Pitfall
2.3 开始操作
2.3.1 程序编译
命令行输入:
代码语言:javascript复制make Pitfall
屏幕输出:
代码语言:javascript复制ifort namelist.f90 -c
ifort Pitfall.f90 -c
ifort namelist.o Pitfall.o -o Pitfall
说明程序编译正常结束。
2.3.2 程序运行
命令行输入
代码语言:javascript复制./Pitfall
屏幕输出:
&INT_LIST
I = 1
/
NAMELIST输出正常。
2.3.3 修改源代码与Input文件
将namelist.f90中的第4行改写为
代码语言:javascript复制namelist /int_list/ i,j
即namelist中多出了一个变量j。
在Input中第3行插入
j=2
即在Input中读取变量j的值。
2.3.4 重新编译程序
命令行输入:
代码语言:javascript复制make Pitfall
屏幕输出:
代码语言:javascript复制ifort namelist.f90 -c
ifort namelist.o Pitfall.o -o Pitfall
说明程序编译正常结束。注意,由于我没有修改Pitfall.f90,Pitfall.f90没有被重新编译。
2.3.5 重新运行程序
命令行输入
代码语言:javascript复制./Pitfall
屏幕输出:
代码语言:javascript复制forrtl: severe (19): invalid reference to variable in NAMELIST input, unit -129, file /home/zxli/Documents/Pitfall-in-Fortran/namelist/Input, line 3, position 2
Image PC Routine Line Source
Pitfall 00000000004441CB Unknown Unknown Unknown
Pitfall 0000000000411446 Unknown Unknown Unknown
Pitfall 0000000000402C13 Unknown Unknown Unknown
Pitfall 0000000000402B02 Unknown Unknown Unknown
libc-2.27.so 00001552E1EE0B97 __libc_start_main Unknown Unknown
Pitfall 00000000004029EA Unknown Unknown Unknown
这说明重新编译过后,程序无法正常读取新的NAMELIST中的变量j。
2.3.6 再次修改并重新运行程序
将Input中的在步骤3中添加的内容删除,再次运行程序。
命令行输入
代码语言:javascript复制./Pitfall
屏幕输出:
&INT_LIST
I = 1
/
这说明主程序认为NAMELIST中只有变量i。这是因为步骤4中Pitfall.f90没有被重新编译。
2.3.7 再次重新编译并运行程序
命令行输入
代码语言:javascript复制make clean && make Pitfall
屏幕输出:
代码语言:javascript复制rm -f *.o *.mod && rm -f Pitfall
ifort namelist.f90 -c
ifort Pitfall.f90 -c
ifort namelist.o Pitfall.o -o Pitfall
重新在Input第3行插入
j=2
命令行输入
代码语言:javascript复制./Pitfall
屏幕输出:
&INT_LIST
I = 1,
J = 2
/
此时程序正常运行。
3. 总结
当NAMELIST写在MODULE中时,修改NAMELIST的内容,但不重新编译调用了这个MODULE的代码,就会出现错误。为了解决这个问题,可以在每次修改NAMELIST后重新编译所有文件,或者在Makefile中让调用了MODULE的文件依赖写有NAMELIST的MODULE文件。
参考资料:《Fortran 95/2003 程序设计 : 第3版》,p547-549