Fortran中的陷阱-NAMELIST

2020-07-27 14:53:40 浏览数 (1)

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

0 人点赞