添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 大坑,定义的变量在计算前要手动赋值初始化,重复掉用此函数,即使在定义变量时初始化,有时候变量里面也有值,累加大忌 (今天又遇到了这个大坑, 更多坑 )
  • DO i=start,end循环结束后,i=end+1
  • 语句 => 程序单元(主程序program,子例程序subroutine,函数function ) => 模块(module) => 程序

    语句规则和特点

  • 将Module写成一个文件,调用时要加 USE ModuleName
  • 将subroutine写成一个文件,可以直接调用
  • real(dp) 中的 dp 必须是 parameter 类型,不然会报错
  • 程序单元可以写在不同的文件内进行编译,最后把分别编译的Obj链接成一个可执行文件与把他们写在一个文件里面编译等价

  • 写在不同的文件内,可以提高编译速度,修改部分代码,仅需编译修改代码所在文件
  • 主程序program,有且仅有一个,作为程序入口
    子例程序subroutine,没有返回值的函数

    一组程序单元及一组相关联的变量,可组成模块(module)

    默认ijklmn开头的变量为为整型,其他为实型,通过下面命令取消此默认规定

    Implicit None 
    

    常用:类型( 属性 ) , 形容词 , 形容词 … :: 变量名(数组外形)= 值 , 变量名2(数组外形)= 值

    定义多个变量用,隔开,用空格不行

  • 整型(Integer)
  • 实型(Real)
  • 复数Complex 类型(本质上是real)
  • 字符型(Character)
  • 派生(type)类型
  • Real(Kind=8) , parameter , private :: rVar = 20.0d0
    Character(Len=32) , Intent( IN ) :: cStr(5,8)
    Integer , save :: n = 30 , m = 40
    Integer m
    complex :: com
    #如果( , )构造复数报错,可以使用cmplx(real,image)函数
    yc(i)=cmplx(yr(i),yi(i)) 
    

    数值型kind

    用于解决不同编译器默认表达范围不一致问题
    整形最大可表示i位示例

    ! k = Selected_Int_Kind( i )  可以用这个函数来选择能满足要求的Kind
    ! i 表示需要最大的十进制位数
    ! k 表示返回的能满足范围的最小的Kind值	
    ! Selected_Int_Kind( i )函数可以放在声明语句里
    integer , parameter :: KI=selected_int_kind(10)
    integer(kind=KI) :: i1 i2 i3
    
    ! k = Selected_Real_Kind( r , p )  可以用这个函数来选择能满足要求的Kind
    ! r 表示需要最大的十进制位数 , p 表示最小的有效位数 p位*E^r
    ! k 表示返回的能满足范围的最小的Kind值
    integer , parameter :: DP=selected_real_kind(r=50,p=14)
    real(kind=DP) :: r1 r2
    
    双精准度 - 使用两个双精准浮点数 
    complex(kind=8) a ! F90新增作法 
    complex(8)      a ! 
    COMPLEX*16      a ! F77传统作法
    

    在数值后面加kind数值,表明数值类型,如siesta

    ! Initialize some variables(Double precision:dp=8)
          DUext = 0.0_dp
          Eharrs = 0.0_dp
          Eharrs1 = 0.0_dp
    

    若一个函数要求输入双精度实数,要传递xxx.xx_dp给这个函数,否则会结果异常,如

    call Pdgemm("N","N",2,8,2,1.0_dp,A,1,1,DESCA,B,1,1,DESCB,0.0_dp,C,1,1,DESCC)

    平方根函数要求sqrt()输入为实数或复数,因此sqrt(2)会报错,而sqrt(2.0)才是正确的 complex与实数kind一样

    字符型len

    kind默认为1(ASCII码格式),也可通过Selected_Char_Kind( ‘ASCII’ )确定
    len表示长度

    character(len=32) :: str
    

    parameter 常量

        syslab="  ---abc==="
        write(*,*) "adjustl"//adjustl(syslab)//"end"
        write(*,*) "adjustr"//adjustr(syslab)//"end"
        write(*,*) "trim"//trim(syslab)//"end"
        write(*,*) "trim(adjustl)"//trim(adjustl(syslab))//"end"
    
     #     |<                            >!
     adjustl---abc===                     end
     adjustr                     ---abc===end
     trim  ---abc===end
     trim(adjustl)---abc===end
    

    INDEX示例

    str="hello,world"
    WRITE(*,*) INDEX(str,"llo") !返回结果为3,即str中第3个元素及之后是llo
    
    类型,形容词 :: 数组名(n1,n2,n3...维度)
    类型,形容词 :: 数组名(n0:n1,m0:m1,...) 也可以,n0,m0也可从负数开始
    

    加上allocatable形容词

    类型,allocatable,其他形容词 :: 数组名(:,:,:....) !用:
    !分配大小
    allocate(array(N))
    !释放空间
    deallocate(array)
    

    ALLOCATE分配的数据边界可以是正数,负数,0
    参考数组赋值与运算
    可用在倒格失的定义当中如qe的FFTXlib/stick_base.f90文件

    ALLOCATE(array(lb:ub))
    
    ALLOCATE(b(-4:4))
    DO i = -4,4
    b(i) = i
    ENDDO
    WRITE(*,*) "b(-2:0)",b(-2:0)
    
    b(-2:0)          -2          -1           0
    

    内存中的存储顺序为array(1,1,1)->array(2,1,1)->array(3,1,1)->array(1,2,1)
    所以do r=1,n1 sum=sum+array(r,1,1) do r=1,n1 sum=sum+array(1,1,r) 的语法就是最快的
    同理,不建议高维数组

    data只能用来赋初值,已赋值后,调用无效

    data的顺序

      data((A(i,j),i=1,2),j=1,3) /1,2,3,4,5,6/
       1.00000000       3.00000000       5.00000000
       2.00000000       4.00000000       6.00000000
    integer :: array(5),A(2,3)
    !使用data赋值,后面的值要和前面的一一对应
    array(1)=1
    array(2:5)=3
    data array /1,2,3,4,5/
    data array /5*5/   !*表重复 /5,5,5,5,5/
    data((A(i,j),i=1,2),j=1,3) /1,2,3,4,5,6/
    !(f(I),i=1,5)就代表一组循环,i从1到5,输出f(i),例如
    A=(/(I*I,I=1,6)/)
    
    !可直接进行调用或赋值
    array(n,m)
    array(n,:)
    array(:)
    A(:,:)
    

    array(:,n)array(n,:)都视为一行矩阵,可以互相赋值,即列和行可以互相赋值

    +-*/function() 都是对应元素操作,不是数学矩阵操作

    > <返回逻辑值,也是对应元素比较

    可以 b=2*a等价于b(i)=2*a(i),加减乘除都行

    也可以a*b不过是matlab中的点乘

    2.1.15 数组查询函数
    default return value of ubound and lbound

    MAXVAL

    WRITE(*,"(A,I1,A,I4)") "max is a(",MAXLOC(a),"), which is",MAXVAL(a)
    WRITE(*,"(A,I1,A,I4)") "min is a(",MINLOC(a),"), which is",MINVAL(a)
    

    注意MAXLOC返回的是数组,即使a是一维的,a(MAXLOC(a))只有一个数,也要用数组变量去接受最大值

    ANY判断数组元素

    RESULT = ANY(MASK [, DIM])
    ANY( (/.true., .false., .true./) )
    

    可以返回是否有成立值,应该不仅可以用来判断整数,逻辑数组都可以,如

    IF( ANY( a .EQ. 0 ) ) WRITE(*,*) "At least one 0 in a"
    IF( .NOT. ANY( a > 90 ) ) WRITE(*,*) "No one > 90 in a"
    

    结构体 TYPE

    结构体,type内只有变量
    type内含有方法(函数)时,就是类了->面向对象编程了

    定义结构体类型

    TYPE ,形容词 :: 结构体名
    变量表(声明语句)
    END TYPE
    

    将结构体实体化,也可实体化为数组

    TYPE(结构体名) :: 变量名
    
          type :: student
                  character :: nickname,address
                  integer :: num,score
          end type
          type(student) :: xiaoming
    
    结构体名%成员名 !优先使用
    结构体名.成员名 !gfortran好像不支持
    xiaoming%nickname
    xiaoming.num
    
    TYPE(student)::xiaoming=student (“xiaoming","china",1,90), S2, S3
    xiaoming.score=95
    

    类是在结构体的基础上,面向对象的拓展

  • 指针POINTER属性
  • 被指针指向的变量TARGET属性
  • 指向变量p1=>t1,检查是否指向TARGET变量ASSOCIATED(POINTER,[TARGET]),示例

    PROGRAM pointer_
    IMPLICIT NONE
    INTEGER,POINTER :: p1
    INTEGER,TARGET :: i1,i2
    NULLIFY(p1)
    i1=1; i2=2
    p1=>i1 ; WRITE(*,*) p1,ASSOCIATED(p1),ASSOCIATED(p1,i1),ASSOCIATED(p1,i2)
    p1=>i1 ; WRITE(*,*) p1,ASSOCIATED(p1),ASSOCIATED(p1,i1),ASSOCIATED(p1,i2)
    NULLIFY(p1) ; WRITE(*,*) p1,ASSOCIATED(p1),ASSOCIATED(p1,i1),ASSOCIATED(p1,i2)
    END PROGRAM
    
    cndaqiang@girl:~/code/test$ gfortran point.f90 ; ./a.out 
               1 T T F
               1 T T F
               0 F F F
    

    数据类型转换

    !1,数字转字符
    write(str1,"(i4.4)") num ! 如有需要,不足四位前面补零
    print*,str1
    !2,字符转数字
    read(str1,"(i2)") num
    print*,str1
    

    流程控制 IF SELECT

    IF(条件) 执行语句
    
    IF (条件) THEN
    ELSE IF (条件) THEN
    ELSE IF (条件) THEN
    END IF
    !ELSE IF与ELSE可不写
    

    SELECT

    SELECT case (表达式)
    case(A)
    case(B)
    END SELECT
    !好像表达式结果只能为整数或字符串,A,B...也要对应
    

    CYCLE 进入下一循环

    EXIT 结束循环

    DO i=start,end [,step]
    !步长step默认为1,可设置为正,负
    !不写i=1,end 无穷循环
    END DO
    

    结束后i=end+1

    DO WHILE()

    DO WHILE(条件)
    END DO
    

    FORALL屏蔽赋值

    数组赋值与运算

    FORALL是F95的新增功能。它是数组屏蔽赋值(WHERE语句和构造)功能的一对一元素的推广,其方式有FORALL语句和FORALL构造。
    FORALL语句的一般形式为:FORALL(循环三元下标[,循环三元下标]…[,屏蔽表达式]) 赋值语句
    FORALL构造的一般形式为:

    [构造名:] FORALL(循环三元下标[,循环三元下标]…[,屏蔽表达式])
    END FORALL [构造名]
      

    屏蔽表达式是数组逻辑表达式,缺省值为.TRUE.。块是赋值语句,或为WHERE语句或构造,或为FORALL语句或构造。
    循环三元下标的形式为:循环变量=下界:上界[:步长]。循环变量是整型的。步长不能为0,缺省值为1。

    PROGRAM forallT
    INTEGER :: a(10),b(10)
    INTEGER :: i
    DO i=1,10
        a(i) = i
    ENDDO
    b=0
    FORALL(i=1:10,a(i)>5) b(i)=6
    WRITE(*,*) b
    END PROGRAM
    
    cndaqiang@girl:~/code/test$ gfortran forall.f90 ; ./a.out 
               0           0           0           0           0
               6           6           6           6           6
    

    WHERE

    可以找到数组中符合条件的项,然后操作另一数组中相同的项,如

    PROGRAM whereT
    INTEGER :: a(10),b(10)
    INTEGER :: i
    DO i=1,10
        a(i) = i
    ENDDO
    b=0
    WHERE(a>5)
        b=6
    ELSEWHERE(a>3)
        b=4
    ELSEWHERE
        b=1
    ENDWHERE
    WRITE(*,*) b
    WHERE(a>5) a=0
    WRITE(*,*) a
    END PROGRAM
    
    cndaqiang@girl:~/code/test$ gfortran where.f90 ; ./a.out 
               1           1           1           4           4
               6           6           6           6           6
               1           2           3           4           5
               0           0           0           0           0
    

    格式化输入输出

    READ(unit=设备号/字符串名称,fmt=格式或行号) 格式化输入

    读取一行数据,且存到矩阵

         read(unit,*) ika, ((eig(ib,is), ib = 1, nband), is = 1, nspin)
                            #此处先进行ib循环,再进行is循环
    

    WRITE(unit=设备号/字符串名称,fmt=格式) 格式化输出

  • 默认设备是键盘,用5*表示,还可以是外部文件(open文件时指定设备号)
  • 输入输出为字符串时,等价于将字符串视为文件,称为内部文件
    实现整数/实数<=>字符串
  • 格式适合一次控制,"格式内容"
  • 行号可以在很多次输出都采用统一格式时,减少书写量
  • READ(设备号,格式行号)也可以
  • READ(*,*) *分别表示默认键盘和不指定格式
  • READ(*,*) a,b,c 一次读取多个数据赋值给a,b,c
  • 格式化输出控制

    默认WRITE后会换行,设置advance="no"取消换行,如
    注意:ifort有bug,还要再加上一个FLUSH,不然也会出现随机换行事件

    WRITE(funit,9035,advance="no") 
    FLUSH(funit) #ifort要用这个确保不会换行
    

    FMT=* 读写

  • WRITE(unit,FMT=*) var时, 会根据var的内容进行写,有些编译器(ifort)会写成多行,gfortran一般写成一行
    如果写到了第n行, 再次调用WRITE时, 从n+1行开始写
  • READ(unit,FMT=*) var时, 会根据var的内容进行读, 如果当前行的数据不够, 会读取下面几行
    如果读到了第n行, 再次调用READ时,从n+1行开始读
  • 针对这些特点, 我们读写的数据如果不是给人看的, 按照数组下标,或者直接将一个数组WRITE(unit,FMT=*)即可, 而且不同编译器之间都可以互相读写
    给人看的时候才要考虑格式化输出

    输出未知数目的数时

    先计算出FMT并保存到字符串中,再用这个字符串作为FMT输出

    real :: a(10)
    write(s,fmt="(a3,i3,a6)") "(a,",UBOUND(a,1),"f12.6)"
    write(*,fmt=s) a
    

    当变量比格式多时,会重复格式进行输入,如

    REAL(8) :: b(10)
    b=1.0
    write(*,10) b
    10 FORMAT(1X,2E20.8)
    write(*,"(1X,3E20.8)") b
    

    b会按照格式每行输出2/3个,直到输出完毕为止

  • 函数与主程序program并列
  • 主程序和函数无先后次序
  • function 与subroutine除了返回值,和声明方式,没有区别
  • 函数都是传址调用
  • function

    [形容词][,返回类型] function 名称([虚参列表])
    	[虚参声明]
    	[局部变量定义]
    	名称=返回值
    	[return]
    End [function [名称]]
    

    用前要声明

  • external
  • interface
  • contains包含在程序单元内
  • module引入
  • 返回变量=名称([实参列表])
    

    调用外部函数function,要在变量定义区指明,子程序subroutine不用

    CHARACTER(len=6), EXTERNAL :: int_to_char
    
    Error: Function ‘int_to_char’ at (1) has no IMPLICIT type
    td_analysis.f90:90:89:
    

    subroutine

    [形容词] subroutine 名称([虚参列表])
    	[虚参声明][局部变量定义]
    	名称=返回值
    	[return]
    End [subroutine [名称]]
    

    直接call

    call 名称([实参列表])
    

    使用前的声明

    使用function前用external声明,或使用interface声明

    program externalOrinterface
          implicit none
          real :: x,y
          real ,external :: fun !函数要声明
          x=1.0
          y=fun(x)
          write(*,*) y
          call sub(x)
    end program externalOrinterface
    real function fun(x)
          real :: x
          fun=x*x+1.0
    end function fun
    subroutine sub(x)
            real :: x,y
            y=x*x+1.0
            write(*,*) y
    end subroutine sub
    

    interface

    fun也可使用interface 代替external,有些特殊用法需要用interface声明

          interface 
                 real function  fun(x)
                         real :: x
                 end function fun
          end interface
    

    使用以下用法时,必须使用 interface:

    • 函数返回值是数组、指针

    • 参数为假定形状数组

    • 参数具有 intent、value 属性

    • 参数有可选参数、改变参数顺序

    以下用法时,虽然不强制要求,但也推荐使用 interface

    • 函数名作为虚参和虚参

    实际上,我们建议在任何函数调用时,都使用接口!

    每个程序单元调用其他函数时都要interface,用module可以减少书写那么多interface

    interface同名函数重构
    使用interface module procedure 定义同名函数,会根据函数输入变量类型自动匹配

    (python27) ~/code/TDQE/Fortran/module_interface $ cat main.f90 
    program main
        use m_interface
        integer :: aa(5),bb(5)
        call my_sum(aa(1),bb(1))
        call my_sum(aa,bb)
    end program(python27) ~/code/TDQE/Fortran/module_interface $ cat module.f90 
    module m_interface
        interface my_sum
         module procedure my_sum_integer
         module procedure my_sum_array
        end interface
    contains
        subroutine my_sum_integer(a,b)
            INTEGER :: a,b
            write(*,*) "my_sum_integer:",a+b
        end subroutine
        subroutine my_sum_array(a,b)
            INTEGER ::a(:),b(:)
            write(*,*) "my_sum_array",a+b
        end subroutine
    end module
    
    mpif90 -c -g -O2 -ffree-line-length-none     module.f90
    mpif90 -c -g -O2 -ffree-line-length-none     main.f90
    mpif90  -g -O2 -ffree-line-length-none  -o interface  main.o module.o
    ./interface
     my_sum_integer:           3
     my_sum_array           3           3           3           3           3
    

    contains

    在程序单元(主程序,function,subroutine)内使用contains,contains后面跟的函数,仅可以被此程序单元调用,且不用声明

    module内部的函数也用containsuse module_name后,可以调用

    contains后面的函数可以直接调用程序单元和module里的变量

    real function fun(x)
          implicit none
          real ::x,y
          y=23.0
          fun=x*3.0
          fun=two(fun)
    contains      !使用contains
          real function two(x)
            real ::x
            two=x*2.0+y !可以直接调用程序单元或module里的变量
        end function two
    end function fun 
    

    module 内部函数互相调用不用声明

    参数传递:在函数内解释参数

    传递字符串

    声明时len=*,siesta得代码中常见

    program strsub
          implicit none
          character(len=20)::str
          str="hello,world!"
          call sub(str)
          contains
                  subroutine sub(strr)
                          implicit none
                          character(len=*) :: strr
                          write(*,*) trim(strr)
                  end subroutine sub
    end program strsub
    

    传递数组-数组地址维度大小都会传过来

    program pro
          implicit none
          real :: array(10),total
          integer I
          array=(/(I,I=1,10)/)
          total=sum(array(2:3))
          write(*,*) total
    end program pro
    real  function sum(a)
         real::a(:)   !多维时a(:,:),传入参数的下标变为1,2,3...,size(a,dim=n),上限自动计算,下线也可指定
         !real::a(n)取出传过来的数组的前n个元素作为一个数组
         integer :: i
         sum=0
         do i=1,size(a,dim=1)
          sum=sum+a(i)
         end do
    end function sum
    

    也可以把数组维度作为参数传入

    也有用real :: a(*),老程序中有,会将输入数组变为1维,适合于不同维度的矩阵运算,如不同维度矩阵相加

    module m_matradd
        implicit none
        contains
        subroutine matradd(m,n,A,B,C,lds)
        implicit none
        integer :: m,n,i,j,lds
        real:: A(*),B(*),C(*)
        !其实fortran直接 C=A+B 就可以
        !C(1:m*n)=A(1:m*n)+B(1:m*n)
        DO j=0,n-1
            DO i=1,m
                C(i+j*lds)=A(i+j*lds)+B(i+j*lds)
            end DO
        end DO
        end subroutine matradd
    end module m_matrad
    

    传递结构体-用module

    主程序和子程序需使用同一个结构体定义,使用module

    modele里面include mpif.h后,调用该module的程序不用include了** **module里面inplicit none`后,对调用该module的程序无影响

    module typedef
          implicit none
          type :: student
                  character(len=10) :: nickname
                  integer :: num,score
          end type
    end module typedef
    program cndaqiang
          use typedef
    !use module名 要在其他语句之前
          implicit none
          type(student)::xiaoming
          xiaoming%nickname="xiaoming"
          xiaoming%num=1
          xiaoming%score=95
          call sub(xiaoming)
    end program
    subroutine sub(who)
            use typedef
            implicit none
            type(student)::who
            write(*,*) who%nickname(:),who%num
    end subroutine
    

    在函数体内用interface声明传入参数的类型

    module mod_fun
        !real :: x,y
    contains
    real function fun(test,x)
          implicit none
          interface !函数作为参数时,要用interface声明(解释)函数类型
          real function test(x)
          real ::x
          end function
          end interface
          real :: x
          fun=test(x)+x
    end function fun      
    real function test(x)
          implicit none
          real :: x
          test=x*x+x
    end function test
    end module mod_fun
    program inter
        use mod_fun   !使用module导入的函数,不用声明
        implicit none
        real :: x,y
        x=3.0
        y=fun(test,x)
        write(*,*) y
    end program inter
    

    save属性

    函数执行完后,空间不释放,下次执行该函数时作为初值

    Integer , save :: var
    Integer :: var = 0  !// 虽然没有书写 save ,但定义时初始化值,有具有 save 属性
    

    虚参的 Intent 属性(需要 Interface)

    明确指定虚参的目的:输入参数、输出参数、中性参数

    !//输入参数,在子程序内部不允许改变  
    Integer , Intent( IN ) :: input_arg   
    !//输出参数,子程序返回前必须改变(对应实参不能是常数,也不能是表达式)
    Integer , Intent( OUT ) :: output_arg 
    !//中性参数
    Integer , Intent( INOUT ) :: neuter_arg
    Integer :: neuter_arg !// 未指定 Intent 则为中性
    请注意:Intent 的检查是在编译时进行,而非运行时检查
    建议对每一个虚参都指定 Intent 属性!
    

    虚参的 value 属性(需要 Interface

    指定该参数为传值参数,而非传址参数。

    !//传值参数,只能作为输入参数。改变后不影响实参。  
    Integer , value :: by_value_arg
    请注意:除混编之外,一般不使用 value 属性
    

    可选参数optional(需要 Interface

    虚参可传可不传

    program op
          call opop(1)
          call opop()
    contains
    subroutine opop(a)
          integer,optional ::a   !a为可选参数
          if (present(a)) then
                  write(*,*) "a=",a
                  write(*,*) "none"
          end if
    end subroutine opop
    end program op
    

    更改参数顺序(需要 Interface)

    更改参数顺序:即,函数的实参、虚参可以不按照顺序对应。

    call writeresult( Data = var , file = "res.txt" , size = 1000 ) 
    call writeresult( var , 1000 , "res.txt")