彭国伦Fortran95学习笔记(一)第一至七章

作为一只CFD小白,Fortran是无论如何都绕不开的语言,无数已有的程序甚至小规模的商用软件都是Fortran写的。但是这些古老的代码存在goto过多,format使用过多,大量的矩阵操作都是用do循环堆砌,以及变量命名过度简化,fixed格式导致排版乱七八糟等等问题。总之就是可读性差,复用困难。正好疫情期间躲在家,在改写已有的组内代码的同时,也将原来的.f程序升级成为.f90程序。为此学习fortran语言的书《Fortran 95 程序设计》 彭国伦著。

因为已经使用过一年左右的Fortran语言,主要关注点是之前不怎么使用的语法和功能,尤其是从F77升级到F90和F95之后的新增功能。对于已经比较熟悉的部分就会略过,只挑觉得重要,或写高性能计算程序时有用的部分说。作为这段时间改写程序的一个记录。

OK!那我们开始吧!

第一章 计算机概论

略,高性能计算和CPU运算的一些细节确实相关,不过最好参考更加专门的书籍,比如说《计算机体系结构》胡伟武

第二章 编译器的使用

win下的编译器比较常用的是Visual Fortran和VS之下的Fortran组件。笔者主要使用的是linux的编译器。Linux下的GNU会包含F77的编译器G77,编译方式如下

g77 hello.f
./a.out

F90则使用另外一个编译命令:

f90 hello.f90

而F95则使用:

F hello.f95

呃,不过现在我在用的是gfortran,书中并未介绍。正常编译.f90是可以通过的,等到遇到问题再回到这里补充吧。

第三章 Fortran程序设计基础

Fixed 和 Free 格式

在Fortran77的时候,使用的是Fixed Format固定格式。而在Fortran90以及95,则使用Free Format自由格式。两者的主要规则罗列如下:
Fixed Format:

  1. 每行第7到72个字符有效,会被编译。前六个字符一般为空格
  2. 每行第5个字符如果是空格或者0之外的字符,就是续航符
  3. 每行前几个字符可以用来给定行标,方便format,goto等等命令使用
  4. 使用小写字母c注释
    Free Format:
  5. 行首的六个空格被取消,可编写字符扩展到132个
  6. 续航符为每行尾或者行首的&符号
  7. 行标写在每行最前面即可
  8. 注释用!而不是c

Fortran的数据类型和数学表达式

相对F77没有新知识

第四章 输入输出声明

输出

输出使用如下两种:

write(*,*) "String"
print  *,"Hello"

其中F90使用双引号,F77使用单引号。输出特殊字符的细节此处不说明,write的左右*星号分别代表输出到那个端口,以及输出格式。而print只能控制输出格式,输出一定是输出到终端的。

变量的声明与kind

基础的变量声明没什么好说的,有一个新知识是声明使用的变量的bytes数

integer*4 a             !F77
integer(kind=4) a       !F90
real*8 b                !F77
real(kind=8) b          !F90

其中kind的值代表存储变量使用的bytes数量,上述分别代表长整型和双精度。而F90的声明可以向下兼容。其中变量某个值所需的kind值,可以通过fortran命令给出,具体需要查阅书中Page67

变量的赋值精度

为什么专门强调这个问题,在科学计算里面,有一些基础参数的设定一定要尽可能的精确。比如说测试数值格式精度,误差会下降到1e-10的情况下,如果计算区域长度的设置本来是real*8 :: a=1.0实际赋值之后的变量为a=1.000008,即单浮点数的精度之后,出现了误差,那么整体精度就不可能达到1e-10。

书中给出,给单精度浮点数赋值时,使用的是a=1.23E3。而给双精度浮点数赋值时,使用的是a=1.23D3。部分编译器即使是给双精度的变量赋值,也一定要带D才能有足够精度的赋值。

输入

就是read命令,这里介绍的都很基础,略。

format格式

基础用法如下:

write(*,100) a
100 format(I4)

要用到行号,书中给出了格式详细的参数说明,不过经常使用的是I,F,E,A,X这几个。详细的说明可以直接翻书或者百度,这里只简单说明罗列:

!整数I
write(*,"(I5)") 100  !5个字符宽的输出
write(*,"(I5.3)") 10 !5个字符宽的输出,最少输出3个数字
!浮点数E和D分别为单双精度
write(*,"(E15.7)") 123.45 !占15个字符,小数占7位
write(*,"(E9.2E3)") 12.34 !占9个字符,小数占2位,指数部分输出三个数字
!字符串A
write(*,"(A10)") "Hello" !10个字符宽
!移位X
write(*,"(5X,I3)") 100 !输出3个字符宽,同时先填5个空格

implicit命令

常用的就两个

implicit real*8 (a-h,o-z)
implicit none

不写的话,fortran也会默认第一行的情形,不过是单精度。不过比较推荐第二种,不然师兄的程序传给师弟的时候,师弟看得头发都快掉没了。

parameter参数

使用方法如下:

real,parameter :: a=1.0d0

real a
parameter (a=1.0d0)

赋初值与双冒号

声明的同时赋初值的话,需要使用双冒号::,即

real :: a=1.0d0

也可以用Data进行赋初值,不过这里并不方便,不说明

等价声明

新知识,类似C语言里面的&a=b,这里对应

integer a,b
equivalence(a,b)

声明后,a和b使用同一个内存空间。主要作用是调用一些高维数组中的单个元素,因为数组下标索引时会带来额外的运算量。

equivalence (a(1,1,5),b)

声明在程序中的作用

这部分对于高性能计算还是比较重要的。首先对于编程来说,声明本身一定要出现在数值计算之后。对于数值计算来说,声明的部分在代码编译之后,编译器会预留空间给程序使用

Fortran中的结构体type功能

给一个实例:

!结构体定义
type: person
   integer :: age
   integer :: length
   integer :: weight
end type person

!结构体声明和赋初值
type(person) :: a=person(20,170,60)

!结构体的使用
a%age = 12

第五章 流程控制与逻辑运算

就是ifselect case命令。其中判断语句如果对字符串使用,字符串会转换成数字进行比较,例如"abc"<"abcd"。然后给出一个select case的例子

select case(score)
case(60)
   a=1
case(:59)
   a=0
case(61:100)
   a=2
case(101:)
   a=3
case default
   a=4
end select

pause,continue,stop

pause代表暂停,直到用户按下Enter键。continue功能就是继续向下执行程序,只是方便阅读。stop是终止程序运行。

第六章 循环

do循环的循环体有多种写法:

do i = 1,10
    a=a+i
end do

do 100 i=1,10
100    a=a+i

do 100 i=1,10
    a = a+i
100 continue

cycle和exit

前者为直接进行下一次循环,后者为跳出当前循环

循环的署名

新知识,例子如下:

outter: do i = 1,10
   inner: do j = 1,10
        ....
   end do inner
end do outter

第七章 数组

基本知识不提了,这里Fortran有一个其他语言中数组没有的功能,尤其是CFD中需要使用ghost point时非常方便的。

real a(-3:8)

即,下标起始位置可以人为规定,当然默认是1。

数组赋初值有以下几种技巧,其中隐含式循环为之前没用过的新知识:

integer A(5)
DATA A /1,2,3,4,5/

integer b(5)
DATA (B(I),I=2,4) /2,3,4/

integer c(5) = (/1,(2,I=2,4),5/)

对整个数组的操作

假设有三个维数相同的数组a,b,c,那么数组的操作和Matlab中的数组操作会非常类似。这是F90之后的新功能,也是Fortran适合科学计算的一个原因。

a = 5   !全部赋值为5
a=/(1,2,3)/   !数组前三个元素分别赋值1,2,3
a=b   !b的元素分别赋值给a
a=b+c
a=b-c
a=b*c
a=b/c 
a=sin(b)   !对数组的元素分别进行四则运算或者函数运算

甚至还可以像matlab一样非常灵活的只对数组中其中一部分元素进行操作。

a(3:5)=5
a(3:)=5
a(3:5)=(/3,4,5/)
a(1:3) = b(4:6)
a(1:5:2) = 3
a(1:10) = a(10:1:-1)

a(:) = b(:,2)
a(:,:) = b(:,:,1)

除此之外,F95还提供了WHERE功能和FORALL功能,能够自动检索并进行多元素操作。和matlab中功能也类似

where (a<3)
  b = a
end where

forall (i=1:size,j=1:size,i>j) a(i,j) = 1
forall (i=1:size,j=1:size,i==j) a(i,j) = 2
forall (i=1:size,j=1:size,i<j) a(i,j) = 3

数组的保存规则

Fortran中数组的保存规则和C语言恰好是相反的比如

integer a(2,3,4,5)

Fortran为下标从左向右,先跑最左侧下标。而C语言为从右向左。另外,当fortran运行时,索引数组中的一个元素,是从首地址开始,根据下标计算元素所在地址。所以维数很高的数组的元素调用会有较大的额外运算量。

另外,CPU在运行时,在地址相邻的位置调用元素可以大大提高cache的命中率,从而提高CPU运行速度。这就要求我们能够对数组下标的顺序进行调整。例如

do i = 1,5
    sum = sum + a(1,1,1,1,i)
end do

do i = 1,n
do j = 1,m
   a(i,j) = ...
end do
end do

就不如

do i = 1,5
    sum = sum + a(i,1,1,1,1)
end do

do i = 1,n
do j = 1,m
   a(j,i) = ...
end do
end do

可变大小数组

先给出用法

integer,allocatable :: a(:,:)

if (.not. allocated(a)) then
allocate(a(-1:5,5),stat=error)
end if

a(1,1) = 1

deallocate(a)

这里说明分配内存是动态的,会损失一部分运行速度,但是并不会损失特别多,是比较推荐的一种写法。而stat=error是用来反馈是否分配内存成功,可以不写。此时动态分配的内存和C类似,是必须释放的。而if条件可以判断a是否已经被分配内存。

  • 5
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值