彭國倫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是否已經被分配內存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章