科学网

 找回密码
  注册
科学网 标签 Fortran 相关日志

tag 标签: Fortran

相关日志

怎么在子程序1中调用主程序中定义的变量m?
h2457528767 2019-5-22 16:45
如果变量是一维的,即一个数,那么不用定义其维度为“A(1)”,直接定义为双精度变量Double Precision A,即可在之后的运算中调用,否则需要在之后的调用中输入“A(1)”而不是“A”。 For example: program main implicit none dimension x(1),y(1),z(1),M(1),Omega(1),gm(1) double precision x,y,z,M,L,Omega,gm x=1 y=2 Omega=1 call equation2(x,y,z,M,Omega,gm) open(file='result.dat',unit=1) write(1,'(1X, 4E20.10)') z,M,gm,0.5374E-98 close(1) end program main subroutine equation1(Omega,gm) implicit none dimension Omega(1),gm(1) double precision Omega,gm gm=Omega*80 return end subroutine equation1 subroutine equation2(x,y,z,M,Omega,gm) implicit none dimension x(1),y(1),z(1),M(1),Omega(1),gm(1) double precision x,y,z,M,Omega,gm call equation1(Omega,gm) z=x+y+gm return end subroutine equation2
个人分类: 软件学习|3099 次阅读|0 个评论
Intel Fortran 怎么显示行号?
h2457528767 2019-4-17 21:33
工具--选项--文本编辑器--Fortran--显示--行号
个人分类: 软件学习|9959 次阅读|0 个评论
Fortran动态链接库生成和使用(Linux, gfortran)
blazing216 2018-11-15 10:14
Fortran 90增加了Module的功能,可用来替代common data,打包子程序。因此,一个方便使用的Fortran程序包应该尽量采用module的形式。那么如何使用已经写好的module呢,一个比较好的方式是将module编译为动态链接库。本文介绍在Linux环境下如何使用gfortran生成fortran的动态链接库,以及如何使用。 如何生成动态链接库: 假如已经有了若干module,写在不同的文件中。例如t_mod.f90(自定义类型,文件中定义module t),t_mod_sub.f90(相关子程序,文件中定义module t_sub),可按如下形式编译 gfortran -shared -fPIC -o libt.so t_mod.f90 t_mod_sub.f90 编译成功后会生成三个文件,libt.so,t.mod, t_sub.mod。其中libt.so即是动态链接库,t.mod, t_sub.mod是头文件。 如何使用动态链接库: 假如有一个t_main.f90,里面使用module t中的自定义类型和module t_sub中的子程序。 方式1:一步 gfortran -o t_main t_main.f90 (指定动态链接库) (指定头文件) 方式2:两步 gfortran -o t_main.o -c t_main.f90 (指定头文件) gfortran -o t_main t_main.o (指定动态链接库) 如何指定动态链接库 方法1:显式指定文件(不推荐) path_to_lib/libt.so 方法2:指定动态链接库所在路径 这时要使用命令行参数-lt,然后通过下面三种方式之一指定路径 方法2.1 显式(推荐) 使用命令行参数-Lpath_to_lib 方法2.2 隐式(推荐) 加入到环境变量LIBRARY_PATH中,如export LIBRARY_PATH=$LIBRARY_PATH:path_to_lib。推荐使用绝对路径 方法2.3 使用当前文件夹(不推荐) 当前文件夹默认为动态链接库搜索的路径,可将.so文件拷贝到当前文件夹。 如何指定头文件 方法:指定头文件路径 方法1 显式(推荐) 使用命令行参数-Ipath_to_lib 方法2 使用当前文件夹(不推荐) 将头文件拷贝到当前文件夹 运行前要保证系统可以找到动态链接库。 方法:指定动态链接库所在路径 方法1:使用环境变量LD_LIBRARY_PATH。用法同上(推荐) 方法2:使用当前文件夹(不推荐) 总结: 推荐的方式为 先编译为动态链接库 gfortran -shared -fPIC -o libt.so t_mod.f90 t_mod_sub.f90 将生成的.so文件和.mod文件分别拷贝到文件夹A和B下,将文件夹A加入到LIBRARY_PATH和LD_LIBRARY_PATH下。 然后使用动态链接库时 用-I文件夹B 指定头文件所在文件夹,用-llibname指定所用的动态链接库。 Note: Linux下,-llibname对应的动态链接库应为liblibname.so。比如-lt寻找的是libt.so,而不是t.so。
个人分类: Fortran|6740 次阅读|0 个评论
[转载]转载:Fortran基本用法小结
zhengfan 2018-5-8 10:14
本文转自刘翰林的博客:原网址 http://blog.sina.com.cn/s/blog_478559630100p020.html 目录: 一、说明 二、概述 三、数据类型及基本输入输出 四、流程控制 五、循环 六、数组 七、函数 八、文件 一、说明 本文多数内容是我读彭国伦《Fortran 95 程序设计》的笔记。只读到第九章,主要是3~9 章,都是最基本的用法(原书共16章)。这里主要摘录了我看书过程中总结的一些Fortran和C不 同的地方,主要是语法方面。希望这份笔记能够给学过C但没有接触过Fortran的同学带去一些帮 助。要想得更清楚些,推荐看一下原书,觉得作者真的写得很好,很清楚;如果有C语言的基础, 看完前九应该很快的,花一两天就行了。觉得如果耐心看完本文,基本功能应该也可以顺利用起 来了。外,由于我之前没有用过Fortran,这次为了赶文档看书又看得很粗浅,大多数东西看过 之后都没得及仔细想,只是按着作者的意思去理解。所以这份笔记还处于纸上谈兵的层次。如果 有不妥的方,希望大家指正。谢谢! 文中 蓝色 的部分是程序代码, !后面的内容为注释 。 二、概述 1、名词解释 Fortran= For mula Tran slator/Translation 一看就知道有什么特色了:可以把接近数学语言的文本翻译成机械语言。的确,从一开始 ,IBM设计的时候就是为了方便数值计算和科学数据处理。设计强大的数组操作就是为了实现这一 目标。Fortran奠定了高级语言发展的基础。现在Fortran在科研和机械方面应用很广。 2、Fortran的主要版本及差别 按其发展历史,Fortran编译器的版本其实很多。现在在广泛使用的是Fortran 77和Fortr an90。Fortran 90在Fortran 77基础上添加了不少使用的功能,并且改良了77编程的版面格式, 所以编程时推荐使用90。鉴于很多现成的程序只有77版本,有必要知道77的一些基本常识,至少保 证能够看77程序。以下是77和90的一些格式上的区别。 Fortran 77: 固定格式(fixed format),程序代码扩展名:.f或.for (1)若某行以C,c或*开头,则该行被当成注释; (2)每行前六个字符不能写程序代码,可空着,或者1~5字符以数字表明行代码(用作格 式化输入出等);7~72为程序代码编写区;73往后被忽略; (3)太长的话可以续行,所续行的第六个字符必须是0以外的任何字符。 Fortran 90: 自由格式(free format), 扩展名:.f90 (1)以!引导注释; (2)每行可132字符,行代码放在每行最前面; (3)以续行,放在该行末或下行初。 以下都是讨论Fortran 90。 3、Fortran的一些特点,和C的一些不同 其实很多,在下面涉及具体方面时可以看到。这里只是大致提一些。 (1)不分大小写 (2)每句末尾不必要写分号 (3)程序代码命令间的空格没有意义 (4)不像C,Fortran不使用{ } (5)数据类型多出了复数和逻辑判断类型。比如复数类型 complex :: a !声明复数的方法。复数显然方便了科学计算,满足了工程方面需求 a=(1.0,2.0) ! a=1+i (6)多出了乘幂运算(**)。乘幂除了整数还可以是实数形式。如开方,开立方 a=4.0**0.5,a=8.0**(1.0/3.0) 。 (7)数组有一些整体操作的功能;可以方便的对部分元素进行操作 (8)有些情况下可以声明大小待定的数组,很实用的功能 4、Fortran的基本程序结构 先看一看所谓的Hello Fortran程序。 program main !程序开始,main是program的名字,完全自定义 write(*,*) Hello !主程序 stop !终止程序 end ] !end用于封装代码,表示代码编写完毕。 中的内容可省略,下同。 再看一段实用一些的程序,好有点感性认识。程序用于计算圆柱的表面积,要求输入底面 半径和。其中展示了Fortran的一些特色用法。程序摘自维基。其实是一个叫 www.answers.com 的网上引的维基的网页。推荐去看看!能查到不少有意思的东西。 program cylinder !给主函数起个名字 ! Calculate the area of a cylinder. ! Declare variables and constants. ! constants=pi ! variables=radius squared and height implicit none ! Require all variables to be explicitly declared !这个一般都是要写上的。下面会进一步说明。 integer :: ierr character :: yn real :: radius, height, area real, parameter :: pi = 3.1415926536 !这是常量的声明方法 interactive_loop: do !do循环,Fortran中的循环可以加标签,如d前面的 !interactive_loop就是标签 ! Prompt the user for radius and height ! and read them. write (*,*) 'Enter radius and height.' !屏幕输出 read (*,*,iostat=ierr) radius,height !键盘输入。isotat的值用判断输入成功否。 ! If radius and height could not be read from input, ! then cycle through the loop. if (ierr /= 0) then write(*,*) 'Error, invalid input.' cycle interactive_loop !cycle 相当于C里的continue end if ! Compute area. The ** means raise to a power. area = 2 * pi * (radius**2 + radius*height) ! 指数运算比C方便 ! Write the input variables (radius, height) ! and output (area) to the screen. write (*,'(1x,a7,f6.2,5x,a7,f6.2,5x,a5,f6.2)') !表示续行。这里还显示了格式化输出 'radius=',radius,'height=',height,'area=',area yn = ' ' yn_loop: do !内嵌的另一个do循环 write(*,*) 'Perform another calculation? y ' read(*,'(a1)') yn if (yn=='y' .or. yn=='Y') exit yn_loop if (yn=='n' .or. yn=='N' .or. yn==' ') exit interactive_loop end do yn_loop !结束内嵌do循环 end do interactive_loop end program cylinder Fortran程序的主要结构就是这样了。一般还会有些module的部分在主函数前,函数在主函 数后。 三、数据类型及基本输入输出 1、数据类型,声明及赋初值 (1)integer: 短整型kind=2, 长整型kind=4 integer( 2) :: a=3 如果声明成integer:: a,则默认为长整型。 !:: 在声明并同时赋初值时必须要写上;类型名后面有形容词时也必须保留::;其他情况可略去 !所谓形容词,可以看一下这个。比如声明常数 real,parameter :: pi=3.1415926 。parameter就是形容词。 (2)real:单精度kind=4(默认),双精度kind=8 real( 8) :: a=3.0 还有指数的形式,如1E10为单精度,1D10为双精度 (3)complex 单精度和双精度 complex( 4) b (4)character character( 10) c !len为最大长度 (5)logical logical*2 :: d=.ture. (等价于 logical(2)::d=.ture. ) (6)自定义类型type:类似于C中的struct Fortran 77中给变量赋初值常用DATA命令,可同时给多个变量赋初值 data a,b,string /1, 2.0, 'fortran'/ 与C不同的是,Fortran中变量不声明也能使用,即有默认类型(跟implicit命令有关)。按 照默认的定,以i,j,k,l,m,n开头的变量被定义为integer,其余为real。取消该设置需在程序声明 部分之前implicit none。彭国伦建议一般都使用该语句。 另一点关于声明的不同是Fortran有等价声明: integer a,b equivalence(a,b) 使得a,b使用同一块内存。这样可以节省内存;有时可精简代码。如:equivalence(很长名 字的变量如三维数组的某个元素,a),之后使用a来编写程序就简洁多了。 2、基本输入输出 输入: read(*,*) a !从键盘读入 输出: write(*,*) text !在屏幕上输出。Fortran 77用' text'。Fortan 90中一般 和' '都可 print *,text !只能用于屏幕输出 (*,*)完整写为(unit=*,fmt=*)。其中unit为输入/输出位置,如屏幕,文件等;fmt为 格式。如这两项都写成*,则按默认的方式进行,即上面描述的。print后面的*表示按默认格式输 出。 四、流程控制 1、运算符 (1)逻辑运算符 == /= = = !Fortran 90用法 .EQ. .NE. .GT. .GE. .LT. .LE. !Fortran 77用法 (2)涉及相互关系的集合运算符 .AND. .OR. .NOT. .EQV. .NEQV. ! 仅.NOT.连接一个表达式,其余左右两边都要有表达式(可以是logical类型的变量) !.EQV.:当两边逻辑运算值相同时为真, .NEQV.:当两边逻辑运算值不同时为真 2、IF (1) 基本 : if(逻辑判断式) then …… end if 如果then后面只有一句,可写为 if(逻辑判断式) …… !then和end if可省略 (2) 多重判断: if(条件1) then …… else if(条件2)then …… else if (条件3)then …… else …… end if (3) 嵌套: if(逻辑判断式) then if(逻辑判断式) then if(逻辑判断式) then else if(逻辑判断式) then …… else …… end if end if end if (4) 算术判断: program example implicit none real c write (*,*) input a number read (*,*) c if(c) 10,20,30 !10,20和30为行代码,根据c小于/等于/大于0,执行10/20/30行的程 10 write (*,*) A goto 40 !goto可实现跳到任意前面或后面的行代码处,但用多了破坏程序结 20 write (*,*) B goto 40 30 write (*,*) C goto 40 40 stop end 3、SELECT CASE 类似于C的switch语句 select case(变量) case(数值1) ! 比如case(1:5)代表1=变量=5会执行该模块 …… !case(1,3,5)代表变量等于1或3或5会执行该模块 case(数值2) !括号中数值只能是integer,character或logical型常量,不能real型 … case default …… end case 4、PAUSE, CONTINUE pause暂停程序执行,按enter可继续执行 continue貌似没什么用处,可用作封装程序的标志 五、循环 1、DO do counter=初值, 终值, 增/减量 !counter的值从初值到终值按增/减量变, …… !counter每取一个值对应着一次循环。增/减量不写则认为1 …… …… !循环主体也没有必要用{} …… end do Fortran 77中不是用end do来终止,而是下面这样子: do 循环最后一行的行代码 counter=初值, 终值, 增/减量 …… 行代码 …… !这是do的最后一行 2、DO WHILE do while(逻辑运算) …… …… end do 类似于C中的while(逻辑运算) {……}。 一开始那个计算圆柱表面积的程序中,应该也算是这一类。不过它是通过内部的if语句来 控制循。看来也是可以的,不过在这本书上没看到这样写。其实应该也可以归于下面这种。 3、没看到和C里面的do{……}while(逻辑运算); 相对应的循环语句,不过可以这样,保证 至少做一循环: do while(.ture.) …… …… if(逻辑运算) exit !exit就好比C里面的break。C里的continue在Fortran里是cycle end do 4、Fortran的一个特色:带署名的循环 可以这样,不易出错: outer: do i=1,3 inner: do j=1,3 …… end do inner end do outer 还可以这样,很方便: loop 1: do i=1,3 loop2: do j=1,3 if(i==3) exit loop1 !exit终止整个循环loop1 if(j==2) cycle loop2 !cycle跳出loop2的本次循环,进行loop2的下次循环 write(*,*) i,j end do loop2 end do loop1 还有一些循环主要用于Fortran中的数组运算,为Fortran特有,很实用。 六、数组 1、数组的声明 和C不同的是,Fortran中的数组元素的索引值写在()内,且高维的也只用一个(),如 integer a(5) !声明一个整型一维数组 real :: b(3,6) !声明一个实型二维数组 类型可以是integer, real, character, logical或type。最高可以到7维。 数组大小必须为常数。但是和C语言不同,Fortran也有办法使用大小可变的数组,方法如: integer, allocatable :: a(:) !声明小可变经过某个途径得知所需数组大小size之后,用下面的语句: allocate(a(size)) !配置内存空间 之后该数组和通过一般方法声明的数组完全相同。 与C不同,Fortran索引值默认为从1开始,而且可以在声明时改变该规则: integer a(-3:1) ! 索引值为-3,-2,-1,0,1 integer b(2:3,-1:3) !b(2~3,-1~3)为可使用的元素 2、数组在内存中的存放 和C不同,Fortran中的数组比如a(2,2)在内存中存放顺序为a(1,1),a(2,1),a(1,2),a(2,2 )。原则是放低维的元素,再放高维的元素。此规则称为column major。 3、赋初值 (1)最普通的做法: integer a(5) data a /1,2,3,4,5/ 或 integer :: a(5)=(/1,2,3,4,5/) 若 integer :: a(5)=5 ,则5个元素均为5 对于 integer :: a(2,2)=(/1,2,3,4/) 根据数组元素在内存中存放的方式,等价于赋值 a(1,1)=1,a(2,1)=2,a(1,2)=3,a(2,2)=4 (2)利用Fortran的特色:隐含式循环。看例子就明白了。 integer a(5) integer i data (a(i),i=2,4)/2,3,4/ !(a(i),i=2,4)表示i从2到4循环,增量为默认值1 还可以这样: integer i integer :: a(5)=(/1,(2,i=2,4),5/) !五个元素分别赋值为1,2,2,2,5 integer :: b(5)=(/i, i=1,5/) !五个元素分别赋值为1,2,3,4, 还可以嵌套 data ((a(i,j),i=1,2),j=1,2)=/1,2,3,4/ !a(1,1)=1,1(2,1)=2,a(1,2)=3,a(2,2)=4 4、操作整个数组 设a,b为相同类型、维数和大小的数组 a=5 !所有元素赋值为5 a=(/1,2,3/) !这里假设a为一维,a(1)=1,a(2)=2,a(3)=3 a=b !对应元素赋值,要求a,b,c维数和大小相同,下同 a=b+c a=b-c a=b*c a=b/c a=sin(b) !内部函数都可以这样用 5、操作部分数组元素 a为一维数组 a(3:5)=(/3,4,5/) !a(3)=3,a(4)=4,a(5)=5 a(1:5:2)=3 !a(1)=3,a(3)=3,a(5)=3 a(3:)=5 !a(3)以及之后的所有元素赋值为5 a(1:3)=b(4:6) !类似于这种的要求左右数组元素个数相同 a(:)=b(:,2) !a(1)=b(1,2),a(2)=b(2,2),以此类推 6、WHERE where形式上类似于if,但只用于设置数组。设有两个同样类型、维数和大小的数组a,b where(a3) b=a !a中小于3的元素赋值给b对应位置的元素 end where 再如: where(a(1:3)/=0) c=a !略去了end where,因为只跟了一行where可嵌,也 !可类似do循环有署名标签。 7、FORALL 有点像C中的for循环: forall(triplet1 ],mask) 其中triplet形如i=2:6:2,表示循环,最后一个数字省略则增量为1 例如: forall(i=1:5,j=1:5,a(i,j)10) a(i,j)=1 end forall 又如: forall(i=1:5,j=1:5,a(i,j)/=0) a(i,j)=1/a(i,j) forall也可以嵌套使用,好比C中for循环的嵌套。 七、函数 Fortran中函数分两类:子程序(subroutine)和自定义函数(function)。自定义函数本 质上就是学上的函数,一般要传递自变量给自定义函数,返回函数值。子程序不一定是这样,可 以没有返值。传递参数要注意类型的对应,这跟C是一样的。 1、子程序 目的:把某一段经常使用的有特定功能的程序独立出来,可以方便调用。 习惯上一般都把子程序放在主程序结束之后。 形式: subroutine name (parameter1, parameter2) !给子程序起一个有意义的名字。可以传递参数,子程序无返回值与自定义函数不同之处。括号内也可以 空着,代不传递参数。 implicit none integer:: parameter1, parameter2 !需要定义一下接收参数的类型。 …… !接下来的程序编写跟主程序没有任何别。 …… return !跟C不同,这里表示子程序执行后回到调用它的地方继续执行下面的程序。不一定放 !在最后。可以放在子程序的其他位置,作用相同;子程序中return之后的部分不执行。还可省略不写 end 调用:使用call命令直接使用,不需要声明。在调用处写: call subroutine name(parameter1,parameter2) 注意点: a.子程序之间也可相互调用。直接调用就是了,像在主程序中调用子程序一样。 b.传递参数的原理和C中不同。Fortran里是传址调用(call by address/reference),就是 传递时用参数和子程序中接收时用的参数使用同一个地址,尽管命名可以不同。这样如果子程序 的执行改子程序中接收参数的值,所传递的参数也相应发生变化。 c.子程序各自内部定义的变量具有独立性,类似于C。各自的行代码也具有独立性。因此各 个子程序主程序中有相同的变量名、行代码号,并不会相互影响。 2、自定义函数 和子程序的明显不同在于:需要在主程序中声明之后才能使用。调用方式也有差别。另外 按照惯例用函数不去改变自变量的值。如果要改变传递参数的值,习惯上用子程序来做。 声明方式: real, external :: function_name 一般自定义函数也是放在主程序之后。 形式: function function_name(parameter1, parameter2) implicit none real:: parameter1, parameter2 !声明函数参数类型,这是必需的 real::function_name !声明函数返回值类型,这是必需的 …… …… function_name=…. !返回值的表达式,以函数名返回 return !跟C不同,这里表示子程序执行后回到调用它的地方继续执行下面的程序。可以不写 end 也可以这样直接声明返回值类型,简洁些: real function function_name(parameter1, parameter2) implicit none real:: parameter1, parameter2 !这个还是必需的 …… …… function_name=…. !返回值表达式 return end 调用: function_name(parameter1,parameter2) 不需要call命令。 自定义函数可以相互调用。调用时也需要事先声明。 总之,调用自定义函数前需要做声明,调用子程序则不需要。 3、关于函数中的变量 (1)注意类型的对应。Fortran中甚至可以传递数值常量,但只有跟函数定义的参数类型 对应才会到想要的结果。如call ShowReal(1.0)就必须用1.0而不是1。 (2)传递数组参数,也跟C一样是传地址,不过不一定是数组首地址,而可以是数组某个 指定元素地址。比如有数组a(5),调用call function(a)则传递a(1)的地址,调用call functio n(a(3))则递a(3)的地址。 (3)多维数组作为函数参数,跟C相反的是,最后一维的大小可以不写,其他维大小必须 写。这决于Fortran中数组元素column major的存放方式。 (4)在函数中,如果数组是接收用的参数,则在声明时可以用变量赋值它的大小,甚至可 以不指定大小。例如: subroutine Array(num,size) implicit none integer:: size integer num(size) !可以定义一个数组,其大小是通过传递过来的参数决定的。这很实用。注意这里与在主程序中声明数组时,数组大小需要用常数来规定大小的规则不太一样,而这里也可以用参数变量赋值其数组大小。 …… …… return end (5)save命令:将函数中的变量值在调用之后保留下来,下次调用此函数时该变量的值就 是上次保的值。只要在定义时加上save就行: integer, save :: a=1 (6)传递函数(包括自定义函数、库函数、子程序都是可以的)。类似于C中的函数指针需要在 主程序和调用函数的函数中都声明作为参数传递的函数。如 real, external :: function !自定义函数 real, intrinsic :: sin !库函数 external sub !子程序 (7)函数使用接口(interface):一段程序模块。以下情况必需: a.函数返回值为数组 b.指定参数位置来传递参数时 c.所调用的函数参数个数不固定 d.输入指标参数时 e.函数返回值为指针时。 具体用法结合例子容易看懂。例子都很长。看书吧。 4、全局变量 功能就不用说了。原理:根据声明时的相对位置关系而取用,不同与C中根据变量名使用。 如果在主程序中定义: integer :: a,b common a,b !就是这样定义全局变量的 在子程序或自定义函数中定义: integer :: c,d common c,d 则a和c共用相同内存,b和d共用相同内存。 全局变量太多时会很麻烦。可以把它们人为归类,只需在定义时在common后面加上区间名 。如 common /groupe1/ a, common /group2/ b 。这样使用时就不必把所有全局变量 都列出来,再声明 common /groupe1/ c 就可以用a、c全局变量了。 可以使用block data程序模块。在主程序和函数中不能直接使用前面提到的data命令给全 局变量赋初值。可以给它们各自赋初值;如果要使用data命令必须要这样: block data implicit none integer a,b,c real d,e common a b c common /group1/ d,e data a,b,c,d,e /1,2,3,4.0,5.0/ end ] 5、Module Module不是函数。它用于封装程序模块,一般是把具有相关功能的函数及变量封装在一起 。用法很单,但能提供很多方便,使程序变得简洁,比如使用全局变量不必每次都声明一长串, 写在odule里调用就行了。Module一般写在主程序开始之前。 形式: module module_name …… …… end ] 使用:在主程序或函数中使用时,需要在声明之前先写上一行:use module_name. Module中有函数时必须在contains命令之后(即在某一行写上contains然后下 面开始写数,多所有函数都写在这个contains之后)。并且module中定义过的变量在module里的 函数中可直接使用,函数之间也可以直接相互调用,连module中的自定义函数在被调用时也不用 先声明。 6、include放在需要的任何地方,插入另外的文件(必须在同一目录下)。如: include 'funcion.f90' 八、文件 1、文本文件 Fortran里有两种读取文件的方式,对应于两种文件 顺序读取:用于文本文件 直接读取:用于二进制文件 这里只摘录关于文本文件的读取。一般模式如下。 character(len=20)::filenamein=in.txt, filenameout=out.txt !文件名 logical alive integer::fileidin=10,fileidout=20 !10,20是给文件编的号,除1,2,5,6的正整数都可,因为2、6是默认的输出位置(屏幕 ),1、5是默认的输入位置(键盘) integer::error real::in,out !下面这一段用于确认指定名字的文件是否存在 inquire(file=filenamein, exist=alive) !如果存在,alive赋值为0 if(.NOT. alive) then write(*,*) trim(filenamein), doesn't exist. !trim用于删去filenamein中字串 !后面的stop多余空格,输出时好看些 end if open( fileidin, file=filenamein, status=old) open( fileidout,file=filenameout ) !unit指定输入/输出的位置。打开已有文件一定要用status=old;打开新文件用status=new; !不指定status,则默认status=unknown,覆盖已有文件或打开新文件…… read( fileidin, 100,iostat=error )in !error=0表示正确读入数据。 100 format(1X,F6.3) !按一定格式输入输出,格式可以另外写并指定行代码,也可以直接写在read/write中 write(( fileidout, (1X,F6.3))out close(fileidin) close(fileidout) !1X代表一个空格。F6.3代表real型数据用占6个字符(含小数点),其中小数点后三位。 !常用的还有I3,用于整型数据,共占三个字符;A8,字符型,占8个字符。换行用 / 二进制文件的读取有所不同。不再列举。 2、内部文件 另一个很实用的读写功能是内部文件(internal file)。看看这个例子就明白了。 integer::a=1,b=2 character(len=20)::string write(unit=string,fmt=(I2,'+',I2,'=',I2))a,b,a+b write(*,*)string 则结果输出1+2=3。反过来也是可以的: integer a character(len=20)::string=123 read(string,*)a write(*,*)a 则输出123。 !全文结束。
个人分类: Fortran学习笔记|976 次阅读|0 个评论
Fortran编程相关资源推荐
PengChen2016 2018-3-2 17:38
由于要学习、使用一些已有的Fortran代码,近期找到了一些不错的资源,推荐给有需要的同学。如果大家有相关资源推荐或者觉得本文有bug,还望不吝赐教。 对于缺乏编程基础的新人,可以先看fcode网站的相关介绍。 1. Fortran编程的基础知识 http://v.fcode.cn/ 上有一套Fortran的视频教程,强烈推荐先阅读基础篇第一节的ppt,了解开发工具等基础知识。该ppt可以搭配教程网页 http://fcode.cn/guide-29-1.html 一起看,略有不同。 fcode提供的编译器选择工具 http://choose.fcode.cn ,适合新手上路。而 http://fcode.cn/content-6-28-1.html 有详细的编译器信息对比。 2. 学习教材 Fcode推荐新手阅读彭国伦的《Fortran95程序设计》和Stephen J.Chapman的书,请见条目1中的教程网页第六点。 Fcode也提供了视频教程,百度云可以直接下载全集。已经看过教材的新手,建议再看一下基础篇的前三节ppt,个人感觉有所收获。 3. VS 我是windows环境开发windows程序,使用的开发工具是VS+IVF,。 VS可到MS官网下2017 community版,免费的。官网下到的是下载器,提供联网安装服务。如果需要离线安装包,中文网页 https://www.ithome.com/html/win10/297093.htm 简介了如何操作,更详细的指令说明参见MS的网页 https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio 。如果要下载离线安装包,建议看下后者,根据需求下载workload,否则全文件下载有近百G。 我主要下载了C++ for windows和phython相关workload,还有一些我感觉要用的组件。操作步骤如下: 管理员身份运行cmd,然后切换到下载器所在文件夹。@寂靜·櫻花雨给出了win7/8下这一步的操作,但win10下可能资源管理器-文件菜单中没有打开cmd的选项。通用的方法是:开始菜单-运行或搜索cmd,想办法管理员身份打开,然后cmd一般会处于C盘,直接打D: 换盘符,然后打cd 下载器所在文件夹的绝对路径。 然后运行以下指令(第一个是下载器的文件名,请参照自己的下载器名字做修改;各组件的ID见MS的那个网页。) vs_community.exe --layout D:\\download\\vs2017community --lang en-US zh-CN --add Microsoft.VisualStudio.Workload.CoreEditor --add Microsoft.VisualStudio.Workload.NativeDesktop –add Microsoft.VisualStudio.Workload.Data –add Microsoft.VisualStudio.Workload.DataScience –add Microsoft.VisualStudio.Workload.NativeCrossPlat –add Microsoft.VisualStudio.Workload.Python –add Microsoft.VisualStudio.Workload.VisualStudioExtension –add Component.GitHub.VisualStudio –add Microsoft.Component.HelpViewer –add Microsoft.VisualStudio.Component.DependencyValidation.Community –add Microsoft.VisualStudio.Component.GraphDocument –add Microsoft.VisualStudio.Component.TestTools.Core –add Microsoft.VisualStudio.Component.VC.Tools.14.11 –add Microsoft.VisualStudio.Component.VC.Tools.ARM64 –add Microsoft.VisualStudio.Component.Windows10SDK.16299.Desktop.arm 然后按照@寂靜·櫻花雨教程操作。 4. IVF IVF现在是作为Intel Parallel Studio XE的一个组件存在,可到Intel官网下载Intel Parallel Studio XE 2018 for Windows Update1,学生用户可通过学校邮箱获得cluster版本的一年期使用权,然后可续期~。 这里面还有一些需要考虑的内容,比如综合考虑程序运行的目标电脑和程序开发使用的电脑配置来选择开发工具。另外,IVF安装的相关问题,fcode等网站有教程 http://fcode.cn/guide-30-1.html ,个人更推荐直接看Intel的官方指南:VS与IVF版本搭配 https://software.intel.com/en-us/intel-parallel-studio-xe-compilers-required-microsoft-visual-studio 和安装出错 https://software.intel.com/en-us/articles/troubleshooting-fortran-integration-issues-with-visual-studio/ 。 可以通过help,查看intel fortran compiler的文档。下载的pdf中详细讲解了在VS中应如何操作,并且说明了一些很有用的功能,如编辑器增强。 5. 其他工具 VS+IVF的Fortran IDE原生功能很一般,inteliSense不支持Fortran语言,说实话让习惯了VS C++编程的我很失望。intel fortran compiler编辑器增强功能中转到定义等命令,在VS快捷键设置中都找不到ID,无法分配快捷键,只能通过右键菜单访问。因此推荐一个VS的扩展,请到VS工具-扩展-联网中搜索fortran codenav,该插件对于VS下fortran编程很有帮助,如ctrl+Q“跳转到定义”。要注意的是,VS采用中文界面时codenav的快捷键可能不可用,请切换到英文界面。快捷键见codenav的about信息。 当然,既然VS+IVF不够好,也可以考虑其他IDE或者换个编辑器,我用过的是code bocks和notepad++。我目前缺乏能力,也无意对比各工具的优劣。 6. 其他资源 国内的论坛 http://fcode.cn fcode论坛人气还可以,尤其是qq群里许多热心的大佬愿意为萌新答疑解惑,很有帮助。 http://bbs.pfan.cn/forum/16.html Intel提供的用户论坛 https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows stackoverflow、CSDN以及各种程序员聚集的地方,建议google一下自己的问题,英语资源要丰富的多,而且可能会找到该软件开发者等权威人士的回答。 Last but not least, 非常感谢以上资源的提供者们,还有我手边正在看的这份Fortran源代码的开发者们。说实话,我对fortran的印象是OA资源远不如C++丰富(可能是因为我不是Fortran出身2333);而所有OA甚至OS资源的提供者们,帮助了我这样的水货新人快速进入这一领域,十分感谢。
6546 次阅读|0 个评论
Fortran派生结构体数组的动态空间分配
hejianhui 2018-1-18 16:28
1、一般派生结构体数组 a、一般派生类型定义 TYPE A integer,allocatable,dimension(:,:,:)::data real:: ... ENDTYPE b、一般派生类型申明,结构体或结构体数组 TYPE(A)::B 或 TYPE(A),dimension(ndim)::B c、一般派生类型动态分配 DO i=1,ndim ALLOCATE(B(i)%data(n1,n2,n3)) ENDDO 2、嵌套派生结构体数组 a、嵌套派生类型定义 TYPE A real,allocatable,dimension(:,:,:)::data ENDTYPE TYPE B TYPE(A),dimension(ndim1)::A1 ENDTYPE TYPE(B),dimension(ndim2)::B1 .... 依此类推 b,嵌套派生结构体动态空间分配 DO i=1,ndim2 DO j=1,ndim1 ALLOCATE(B1(j)%A1(i)%data(n1,n2,n3)) ENDDO ENDDO
个人分类: Fortran学习心得|5578 次阅读|0 个评论
[转载]Fortran常见错误
xiaoxiaoxingzi 2018-1-17 15:21
本文从编译错误,链接错误,运行时错误,计算结果错误等四个方面介绍了常见的错误及解决思路。适合初学者阅读。 首先应该明确:错误有哪几种?我们当前遇到的是何种错误? 阐述这些问题前,我们先讨论一下常规的应用程序开发的过程: 1编写代码,使用一个或多个源代码文件。 2对第一步的每一个源代码文件执行编译操作。得到一个或若干个目标代码。 3将目标代码,运行时库(Run-time Library)和其他使用到的函数库链接起来。得到一个可执行文件(EXE 或其他) 4编写程序的说明书,必要的(输入)数据文件 5将上述得到的结果发布给用户。(发布的方式可以是刻录成光盘,销售,放在网站上供别人下载,或者其他) 6用户得到程序后,运行,输入数据,得到计算结果。 对于很多 Fortran 程序员来说,可能用户就是自己,也可能仅仅是自己教研室的同事同学。所以第4,5,6步骤很多时候不明显。而如果使用集成开发环境(IDE)进行开发,第1,2,3步骤又可以一键完成。因此,很多初学者就认为,写程序就是:输入代码,运行,得到结果。这样的理解太狭义。 不管我们面对什么使用者来写代码,程序开发应该是上述的过程。我们的编译器,编译环境,也是为这个过程而设计的。 于是,我们将错误分为四种: 一. 编译错误(发生在第2步) 编译错误,一般是源代码书写格式不正确,不符合语法要求。 二. 链接错误(发生在第3步) 链接错误,一般是源代码结构不完整,运行时库或函数库使用不合理。 三. 运行时错误(发生在第6步) 运行时错误,一般是执行代码时,遇到了事先未料及的错误。比如内存不足了,磁盘空间不够了,输入文件格式不对了,输出文件写入失败了等等。 四. 计算结果不符合预期(程序代码不规范,或不符合你的设想) 计算结果不符合预期,可能性就很多了。语法与你的想法不一致,超出函数库的适用范围,执行流程控制不当等等。 这四种错误,其排查难度依次增大。也就是,编译错误最容易排查和修改,而计算结果不正确,最让人头疼。 不管是阅读本文,还是在网络上求助,抑或是与其他程序员交流。首先一定要弄清楚,自己遇到的,是哪一种错误。如你使用的是 IDE 方式开发,具体不知道是第一种还是第二种错误,应该尝试手动编译代码,手动链接程序,看错误发生在哪一步。使用一键 Build 可能会让初学者搞不清楚。 对于前三种错误,一定要找到错误提示,这对于你解决问题至关重要! 另外需要说明的是,对于同样的错误,不同的编译器给出的提示可能也是不一样的。 下面都以Compaq Visual Fortran 和 Intel Visual Fortran 举例。其他编译器提示应该类似。 一. 编译错误 编译错误经常让初学者感到恐怖,因为动辄几十几百乃至上千个错误。然而实际上,错误可能并没有那么多。这是因为编译器对代码进行构析,遇到错误后,会严重影响接下来的分析,导致将本来正确的写法认为是错误。 所以,对于编译错误,一定先解决第一个。然后重新编译,然后再解决新的第一个。直到顺利编译为止。 编译错误,在 IDE 环境下一般可通过双击切换到错误所在行。在命令行下也会显示错误所在的行数。 Q0001:【CVF】Severe: Invalid argument , Error executing df.exe 编译错误提示类似于: f90: Severe: Invalid argument ... file is 'C:\\?D??\\fcode.f90' Error executing df.exe 原因:这是由于 Compaq Visual Fortran 不支持中文路径下的源代码文件。源代码文件名作为参数(argument)传递给编译器(df.exe)被认为是非法的(Invalid 因为含有中文)。 解决:将源代码文件放入英文路径下重新编译。 Q0002:【CVF / IVF 】Error spawning df.exe 或 The Fortran compiler (ifort.exe) cannot be found. 原因:这多半是由于编译器安装不合适造成的,或者安装时未设置合理的环境变量。执行编译器( df.exe 或 ifort.exe ) 失败或根本找不到。 解决:重新安装编译器(win7/win8 下请不要使用 CVF),注意安装时如选择是否设置环境变量,选Yes。 Q0003:【CVF / IVF 】Unrecognized token '?' skipped 原因:代码中出现了编译器无法识别的字符。 解决:首先检查自己的源代码文件是否为 ANSI 编码,而不是Unicode编码或其他。然后检查错误行,是否存在特殊字符?包括除字符串常量和注释行之外的中文。尤其注意括号,逗号,冒号是否是半角? Q0004:【CVF / IVF 】Syntax error 原因:语法错误 解决:语法错误的可能性很多,但凡违反语法规定的写法,都可能出这个错误。认真检查错误行,某些编译器可能会给出更详细的错误提示,翻译一下会更便于查找错误。 Q0005:【CVF / IVF 】Unbalanced parentheses 原因:括号不成对出现,例如 a(3)) 解决:检查错误行的括号,确保成对出现。 Q0006:【CVF / IVF 】An unterminated block exists. 原因:If 或 Do 等区块没有结束。 解决:检查 If 是否有 EndIf 对应,Do 是否有 End Do 对应 Q0007:【CVF / IVF 】A specification statement cannot appear in the executable section. 原因:声明语句出现在了执行语句中 解决:所有声明语句必须在所有执行语句前面,即先声明所有变量,然后才有执行语句。调整这些语句的顺序既可。 Q0008:【CVF / IVF 】The type of the actual argument differs from the type of the dummy argument. . 原因:SOME作为实参,它的的类型与对应的虚参不一致 解决:我们建议所有实参与对应的虚参具有相同的类型,精度,大小和数组上下限。检查一下被提示的变量,其类型精度是否与虚参一致?尤其是没有使用 Implicit None 的程序单元,默认 IN 规则情况下,容易发生类型不一致。 二. 链接错误 Q1001: unresolved external symbol _SOME / 无法解析的外部符号 _SOME,该符号在函数 _MAIN__ 中被引用 原因:找不到某些函数或子程序。如果这些函数或子程序在函数库中,没有正确被使用。如果 SOME 是数组,可能没有定义为数组,而被编译器误认为是函数。 解决:找到这个函数并添加到工程内,或正确设置函数库并使用,或定义该数组。 Q1002: unresolved external symbol _MAIN__ / 无法解析的外部符号 _MAIN__,该符号在函数 _main 中被引用 原因:找不到主程序。 解决:补充主程序。 Q1003:unresolved external symbol _WinMain@16 / 无法解析的外部符号 _WinMain@16__,该符号在函数 _main 中被引用 原因:找不到 WinMain 主程序。WinMain 是 WinGUI 界面工程所需的函数。 解决:如果你不是刻意的开发windows界面程序,请重新选择 console 工程类型。否则,请补充 WinMain 函数。 Q1004: _MAIN__ already defined in main.obj / _MAIN__ 已经在 main.obj 中定义 原因:MAIN 函数已经在 main.obj 文件中定义过。即,有多个文件中书写了相同名称的函数或主函数。 解决:删除多余的同名函数,或者检查是否重复添加了相同的文件在工程中。 三. 运行时错误 请注意, 某些编译器编译后的程序,在发生运行时错误时,会弹出VC++ Runtime Library 的警告框。这是因为这些编译器的 Runtime Library 部分使用了 VC++ 的运行时库。对于这个警告框,基本上得不到有用的信息。我们应该查看黑色 console 窗口上的错误提示 。 另外,几乎没有程序员可以保证自己的程序一定没有运行时错误。因为运行时错误可能发生在某些特定的情况之下,而不一定是每次都会发生的。 例如:一个程序大多数时候跑得没有问题,突然有一天遇到内存占用很大,一块数组申请失败;或者忽然有一天,用户的磁盘满了;再或者,用户把一个具有只读属性的文件指定为输出文件;还有杀毒软件的一些拦截行为也可能导致某些操作意外失败。就计算程序来看,程序可能认为某个曲线是单调的,或者光滑可导的,某个数组的数据一点是大于 0 的,某个方程一定是有解的,但实际上用户输入的算例并不一定满足这些条件。 因为用户运行程序,是在未知的计算机环境,未知的算例进行的。因此,很多意外的运行时错误我们很难预料。即便是非常商业化的程序,如 QQ,迅雷,Internet Explorer,Visual Studio,甚至我们使用的编译器自身,出现运行时错误意外终止都不是新鲜事。 我们需要做的,恐怕只是尽量的避免运行时错误,使程序在绝大多数情况下可以正常运行计算出结果。此外,不是所有的运行时错误都需要修改代码,有些还需要调整输入文件,或者改变其他运行环境。(当然了,改变代码使得程序能更宽泛的适应多种特殊情况更好) Q2001: floating invalid / math error / DOMAIN error / Integer divide by zero 原因:这是计算时最容易发生的错误,他表示浮点数错误,数学函数错误(如超出数学函数的定义域,负数开方,分母为零等等)。 解决:对数据进行合理控制,判断是否在定义域内,如每个算例均出现,应进行 Debug 调试。 Q2002: Program Exception - array bounds exceeded / Subscript #1 of the array A has value 101 which is greater than the upper bound of 100 原因:这是数组越界,即,数组引用的元素超出了定义它的范围。比如定义 a(50:100),如引用 a(49) 或 a(101) 则会越界。很多时候,这是循环对数组操作时,没控制好,比如 Do i = 50 , 100 然后引用了 a(i+1),当i=100时,i+1=101,就会越界。Intel Fortran 的数组越界会给出很详细的错误提示,包括具体越界的数组名,定义范围和引用角标。 解决:检查越界数组,根据情况修改代码。 Q2003: End-of-file during read 原因:这是读取文件时遇到了文件的结束。例如,代码要求数据有3行数据,而实际输入文件只有2行。在某些时候,甚至输入文件根本不存在或是空白文件。此外,数据文件缺失某些行某些列也是较大的可能。当然,也可能是循环读取文件时没控制住。比如想读100行,结果写错了,读了1000行。 解决:补足数据文件,或者修改代码使得读取的数据与实际数据文件一致。 Q2004: File not found 原因:这个就简单了,文件找不到。最大的可能是文件名写错了,路径(文件夹)放置不正确。 解决:增加应有的文件,或放置到合适的路径下。 Q2005: Attempt to access non-existent record 原因:一般针对直接读取文件,意思是读写了一个不存在的记录。例如文件只有2个记录,却视图读取第3个。也可能是记录长度的字节数设置不正确,使得应该在第2记录的字节超出了文件的字节。 解决:修改代码或修改文件,使得记录长度与个数相匹配。 Q2006: Insufficient virtual memory 原因:程序试图访问一个受保护或不存在的内存地址。多数为可分配数组,指针等动态内存引发的错误。 解决:确保数组已经经过分配后才访问,确保指针指向可用的内容。 Q2007: Format syntax error at or near x x 原因:在 x x 位置或附近的格式符 x 错误。因为使用了错误的输入输出格式符。 解决:修改源代码中对应的格式符,或输入正确的可识别的格式符。 Q2008: List-directed I/O syntax error 原因:输入数据不正确。例如从文件或字符串中读取整型或浮点数数据,而遇到非数字的符号,比如“abc” 解决:这个问题多数需要修改输入文件。 Q2009: Stack overflow 原因:堆栈溢出。可能性较多:堆栈不够;程序内局部变量太大或太多;递归调用终止失控。 解决:首先尝试改大堆栈,在不同编译器上具体操作不同。VS 下可设置工程属性,如图: 命令行下增加链接开关 /STACK:1000000000,1000000000 如果还是不足,可将大的局部数组改为可分配数组。如有递归调用函数,检查其终止条件是否设置合理。 Q2010: Program Exception - access violation 原因:这个问题可能性很多,属于比较麻烦的运行时错误。表示程序尝试读写一个非法的内存地址。常见于可分配数组尚未分配就传入子程序使用,子程序中修改了虚参但对应的实参为常数。等等。 解决:Debug 调试,检查错误所在位置。 Q2011:Formatted I/O to unit open for unformatted transfers / Unformatted I/O to unit open for formatted transfers 原因:使用无格式打开的文件,但使用了有格式的输入输出。或反之。 解决:使用匹配的格式打开和输入输出。 Q2012:Sequential-access I/O to unit open for direct access 原因:使用直接读写方式打开的文件,但使用了顺序读取的输入输出。 解决:使用匹配的读写方式打开和输入输出。 IVF 的运行时错误官方有专门的文档,大约有700多个。这里无法一一列举。如果你遇到运行时错误,请先尝试翻译它。然后尝试去解决。CVF和其他编译器的运行时错误与这些类似但不完全相同。 四. 计算结果不符合预期 这是最麻烦的错误了。编译器和操作系统已经无法帮助我们来检查和预知错误。需要我们自己进行 Debug 调试,检查计算结果从哪里开始与我们的预期开始不同了。 如果你的代码在其他编译器或其他操作系统上可以正常使用,但更换编译器以后计算结果不正确,也不要觉得奇怪。语法只是规定了一部分规则,还有很多语法中尚未规定的事情,不同编译器的处理就不一样,导致这种结果。这恰恰说明代码还有隐含的错误。 一段严谨的代码,会尽量避免上述情况。我们在书写代码时,也应该如此。不要让代码离开你的计算机就变成一堆垃圾。 如果你遇到了计算结果不符合预期的情况,耐心的检查吧,以下可能会是突破点: 1.检查变量是否全部定义并给定了类型和精度。程序单元都使用了 Implicit None 语句。 2.检查变量是否有尚未初始化就使用的情况? 3.函数调用过程中,是否存在虚参实参的类型差异。 4.变量的 Kind 值在不同编译器上的含义是否有差别? 5.直接读取文件的 RecL 的值在不同编译器上含义是否一致? 6.是否使用了依赖编译器的函数库,在更换系统后同样替代的函数,适用范围是否一样? 最后,强调一下,如果你使用的编译器不是 Compaq / Intel Visual Fortran 系列,他们的错误提示与本文的可能不同。请你首先尝试翻译错误提示,大多数情况错误提示会把问题描述得比较清楚。
个人分类: 编程相关|4393 次阅读|0 个评论
关于程序的编译和链接等概念
xiaoxiaoxingzi 2018-1-15 23:19
今天,在调试fortran程序时碰到一些错误,又上网看了一下Fcode网站上的视频 基础篇第三节《常见问题的检测和排查》,原来程序的执行过程分为编译和链接两个部分,可以先检查程序在编译阶段的错误,然后再把链接阶段的错误一起检查一下! 编译和运行的区别也可参考CSDN博文《 什么叫编译时和运行时 》 以前经常听说编译时和运行时的概念.但没太搞明白具体代表啥意思.后面花了点时间研究了下.总算知道个大概意思了. 编译时 编译时顾名思义就是正在编译的时候 . 那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码 .(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字节码,C#中只有CLR能识别的MSIL.另外还有啥链接器.汇编器.为了了便于理解我们可以统称为编译器) 那 编译时就是简单的作一些翻译工作 ,比如检查老兄你有没有粗心写错啥关键字了啊.有啥词法分析,语法分析之类的过程. 就像个老师检查学生的作文中有没有错别字和病句一样 .如果发现啥错误编译器就告诉你.如果你用微软的VS的话,点下build.那就开始编译,如果下面有errors或者warning信息,那都是编译器检查出来的.所谓这时的错误就叫编译时错误,这个过程中做的啥类型检查也就叫编译时类型检查,或静态类型检查(所谓静态嘛就是没把真把代码放内存中运行起来,而只是把代码当作文本来扫描下). 所以有时一些人说编译时还分配内存啥的肯定是错误的说法. 运行时 所谓运行时就是代码跑起来了.被装载到内存中去了 .(你的代码保存在磁盘上没装入内存之前是个死家伙.只有跑到内存中才变成活的).而运行时类型检查就与前面讲的编译时类型检查(或者静态类型检查)不一样.不是简单的扫描代码.而是在内存中做些操作,做些判断. 举例说明 可能光讲概念你还是迷糊.还分别用C++和C#来举个简单点的例子.数组越界检查的例子(开发工具用的微软的VS) C++中 int arr ; coutresultendl; 上面的代码你一瞧你知道是错误的代码,数组越界了.但用 编译器一编译,一点错都没 .可见编译器其实还是挺笨的,还没你脑瓜子那么聪明啊.然后开始运行,Start Dubugging.于是报错了,于是你想虽然编译器笨了点,但运行起来时发现了错误也还不算太坏. 但实际上运行时做数组的越界检查不是C++里面支持的特性,这里你dubug是VS中的一些工具给你做的检查 .你如果点运行时选的是release而不是dubug的话会发现一切正常运行,但得到的结果不确定的.(因为你不知道arr 所指的内存里具有有啥数据.反正所以东东在内存中都是0101串嘛,你找到连续4个字节的一串0101来然后当成int数据处理.)我一运行得到个吓人的数字,数了下貌似是十亿多.要是银行计算我的账户中有多少钱时也这样来个数组越界,搞个十多亿那我可发了啊.哎显然是想多了,还是老实敲代码吧. C#中 int ; Console.WriteLine(result); 一编译还是正常通过.但一运行就报错了啊.C #与C++中不同,它有与运行时类型检查.会检查数组是否越界不. 如果越界了不会给你返回个错误的结果,而是直接报错.你如果没有异常处理语句处理的话整个软件就挂掉了啊. 为啥C++不在运行时做数组越界检查呢? 这应该 主要是考虑到性能问题吧 .C++设计之初为了达到与C差不多的效率.就尽量不会在运行时多做些额外的检查.因为这样无疑会降低性能的. 但有些地方却是必须得做运行时类型检查的.比如多态,你不在运行时做类型检查就没法搞定啊.举个简单例子吧.假如有父类Father,继承自Father的子类Son.这两个类中都有虚函数Fun. Father fa; Son so; fa = so; fa.Fun(); //在编译时,实际上是把Fun当作Father类中的Fun看待. //但在运行时实际上这里的Fun是调用的Son中的函数Fun.所以不做运行时类型检查是没法确定的啊.
个人分类: 编程相关|4 次阅读|0 个评论
[转载][讨论]调用IMSL时出现internal error,用不带“_int”的库时出现
xiaoxiaoxingzi 2018-1-12 10:19
环境 Win7 x86 SP1,Intel Fortran XE2011,IMSL 7.0 x86 运行如下测试代码时没有问题 program main INCLUDE 'link_fnl_shared.h' !USE IMSL_INT USE EIG_INT USE RAND_INT implicit none real B(32,32) real :: a(3,3)=(/ 1,0,0, 0,2,0, 0,0,3 /) real eigenvalue(3),eigenvector(3,3) integer i B=rand(B) eigenvalue=eig(A, v=eigenvector) do i=1,2 write(*,('eigenvalue=',f5.2))eigenvalue(i) write(*,('eigenvector= '))eigenvector(:,i) enddo end program main !------------------------------------------------------ 为了测试.i.运算符但是运行如下代码的时候出现问题(代码来自cgl_lgs大大的一个帖子) program exammm include 'link_fnl_shared.h' use imsl_libraries implicit none real:: v=2.3 integer i,j real h00(6,6) real h0(6,6),h(6,6) h00=0.0 do i=1,6 if(mod(i,2)==0) then h00(i,i-1)=v else h00(i,i+1)=v end if end do h0=.i.h00 h=h0.x.h00 write(*,'(6f5.2)') h !检验结果是否为单位矩阵 end program !---------------------------------- 提示如下 1------ 已启动生成: 项目: fnltest, 配置: Debug Win32 ------ 1Compiling with Intel(R) Visual Fortran Compiler XE 12.1.3.300 ... 1Source2.f90 1D:\\Users\\liulinsl\\Documents\\Visual Studio 2010\\Projects\\fnltest\\fnltest\\Source2.f90(115): internal error: Please visit 'http://www.intel.com/software/products/support' for assistance. 1 h00=0.0 1^ 1 1compilation aborted for D:\\Users\\liulinsl\\Documents\\Visual Studio 2010\\Projects\\fnltest\\fnltest\\Source2.f90 (code 1) 1 1Build log written to file://D:\\Users\\liulinsl\\Documents\\Visual Studio 2010\\Projects\\fnltest\\fnltest\\Debug\\BuildLog.htm 1fnltest - 1 error(s), 0 warning(s) ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ========== 运行IMSL的范例.xt. program xt use linear_operators implicit none integer, parameter :: n=32 real(kind(1d0)) :: one=1d0, zero=0d0 real(kind(1d0)) A(n,n), P(n,n), Q(n,n), S_D(n), U_D(n,n), V_D(n,n) ! Generate a random matrix. A = rand(A) ! Compute the singular value decomposition. S_D = SVD(A, U=U_D, V=V_D) ! Compute the (left) orthogonal factor. P = U_D .xt. V_D ! Compute the (right) self-adjoint factor. Q = V_D .x. diag(S_D) .xt. V_D ! Check the results. if (norm( EYE(n) - (P .xt. P)) = sqrt(epsilon(one))) then if (norm(A - (P .x. Q))/norm(A) = sqrt(epsilon(one))) then write (*,*) 'Example 2 for LIN_SOL_SVD (operators) is correct.' end if end if end 同样会出错 1Compiling with Intel(R) Visual Fortran Compiler XE 12.1.3.300 ... 1Source2.f90 1D:\\Users\\liulinsl\\Documents\\Visual Studio 2010\\Projects\\fnltest\\fnltest\\Source2.f90(114): internal error: Please visit 'http://www.intel.com/software/products/support' for assistance. 1 A = rand(A) 1^ 1 1compilation aborted for D:\\Users\\liulinsl\\Documents\\Visual Studio 2010\\Projects\\fnltest\\fnltest\\Source2.f90 (code 1) 1 1Build log written to file://D:\\Users\\liulinsl\\Documents\\Visual Studio 2010\\Projects\\fnltest\\fnltest\\Debug\\BuildLog.htm 1fnltest - 1 error(s), 0 warning(s) ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ========== 改成use rand_int之后 其他地方,例如.xt.处还是会出现internal error 请大大们指教 然后,有人回答: 是这样的,imsl7.0中用use linear_operators这句话是会出错的,当时intel论坛上也有人问,后来intel给出了个X64 imsl的补丁,但是32位的没有。 要使用包含在linear_operators这个库中的函数时,要用use+原函数 例如:上面那个例子把use linear_operators改成use operation_xt即可 另外,imsl7.0引用函数和以前版本不一样的 补充一点: linear_operators这个文件是这样子的(一看就知道原因了): module linear_operators use cond_int use det_int use diag_int use diagonals_int use fft_int use ifft_int use eye_int use lin_eig_self_int use lin_sol_self_int use norm_int use operation_i use operation_ix use operation_t use operation_h use operation_tx use operation_hx use operation_x use operation_xi use operation_xt use operation_xh use orth_int use rand_int use rank_int use svd_int use unit_int use eig_int use chol_int use isnan_int end module
个人分类: 编程相关|2 次阅读|0 个评论
[转载]Debug单步调试 (Debug模式和Release模式有什么区别?)
xiaoxiaoxingzi 2018-1-12 10:03
本文介绍了 IVF+VS 和 Compaq Visual Fortran 编译器的单步调试操作方法,其他编译器可以类比参考。适合初学者阅读。 第一,Debug 调试是什么? 据传,世界上最早一批程序员中的一位,葛丽丝·霍普,在调试程序时出现故障,拆开继电器后,发现有只飞蛾被夹扁在触点中间,从而“卡”住了机器的运行。于是,霍波诙谐的把程序故障统称为“臭虫(BUG)”,把排除程序故障叫DeBug。后来,Debug 就成为了一个专业的计算机术语。 Debug 是一个程序员的基本功。它有很多层含义,比如源码级Debug,汇编级Debug,系统级Debug等等。Fortran 程序员,通常只会接触到源码级Debug。本文也只描述源码级Debug,如您对汇编和反汇编有一定的了解,还可尝试汇编级Debug。 源码级 Debug,意思是,让程序逐行逐行的运行,运行在中途时,可暂停运行,并将此时的若干状态呈现给程序员查看,以便程序员分析,此时各变量各过程及程序流程,是否符合自己的预期。同时,调试者可随时改变这些状态,例如变量的值,然后继续运行,以便测试在不同情况下程序的反应。 目前的计算机运行速度已经很快了,往往很长的代码运行完毕也就一眨眼的功夫。如果运行过程中出现错误,或者计算结果不正确。往往程序员不知道问题发生在哪里。而 Debug 则可以让程序在可疑的程序段暂停下来,程序员可以查看此时是否符合自己的预期?从而为查找问题提供更多的依据。 源码级 Debug,可便于程序员查找 运行时错误(Run-time Error),计算结果不正确 ,这两种问题。( 编译链接错误不能通过Debug查找 ) 第二,Debug 的前提 Debug 实际上是一种运行程序的方式。所以,首要前提是, 程序已经可以正常链接,获得可执行文件 。对于不单独运行的程序,例如 DLL,LIB 等,需要额外的 Loader 来加载它,并进行调试(本文暂不涉及,如您有疑问,可于本站论坛提出)。如果您的代码编译链接还有错误,那么请先解决编译链接问题。 其次, Debug 需要调试器 (Debugger),这是一种软件。一般商业编译器都会附带调试器。免费开源编译器也会有附属的开源调试器。如果你的编译器产品需要选择组件安装,请确保自己勾选了相应的调试器并进行了安装。 最后,很多编译器允许两种编译链接方式: Debug模式 和 Release模式 。 这两种链接方式的区别主要是: 1.Debug 模式:程序几乎不进行优化。产生的可执行程序具有调试信息,执行效率低,文件尺寸大。 2.Release 模式:程序进行合理优化。产生的可执行程序不具有调试信息,执行效率高,文件尺寸小。 (实际上,Debug模式 和 Release模式只是编译器 预设的两种方式 ,我们可以通过调节编译链接参数来获得更自由的搭配,产生介于Debug和Release之间的编译方式) 想要进行 Debug 调试,我们需要程序中存在调试信息,需采用 Debug 模式 编译链接程序才可以。 第三,Debug 的操作 调试器的操作,因不同调试器而不同,这里以集成在 Visual Studio 中的 Intel 调试器为例。Intel Visual Fortran 会默认安装这个调试器。 其他的调试器,如果也是集成在IDE中的,则操作方式大同小异。如果是单独的命令行程序,则需要通过命令来进行调试(本文暂不涉及) 首先我们来看一个示范代码: 01 Program www_fcode_cn 02 !// 本程序用于演示 Intel 调试器的使用 03 Implicit None 04 Integer , parameter :: N = 10 05 Real :: a ( N ) , b ( N ) 06 integer :: i 07 Do i = 1 , N 08 a ( i ) = i 09 b ( i ) = 100 - i 10 End Do 11 !a(5) = 0.0 12 Do i = 1 , N 13 write ( * , * ) b ( i ) / a ( i ) 14 End Do 15 End Program www_fcode_cn 程序第11行,是故意将a(5) 设定为 0.0 的。如果不执行它,则程序应该输出: 99.00000 49.00000 32.33333 24.00000 19.00000 15.66667 13.28571 11.50000 10.11111 9.000000 如果执行了11行,则可能会输出 Infinity ,也可能会出现运行时错误 forrtl: error (73): floating divide by zero (根据不同编译器或编译参数中,浮点数的设置而不同) 假设我们程序执行时,遇到了错误,分母为零了。我们来看看如何通过 Debug 发现这个错误。 首先第一步 ,我们要在可疑的位置 插入断点 (Insert breakpoint),调试器会让程序运行到断点的位置并暂停下来。如果可疑的位置有多处,可以分别插入断点。 集成开发环境里,在编辑代码时,右键既可插入断点。(在某些环境下,直接点击某行最前方也可插入断点) 请注意,如果你修改了代码,则需要保存,然后编译链接,才能够插入断点。另外,断点不可位于注释语句上,比如上例,想在11行插入断点,就需要取消11行的注释感叹号(然后保存重新编译链接) 需要注意的是,断点需要执行到它的位置,只能位于执行语句(定义语句并不执行)。 如上图,我们插入了两个断点。存在断点的行,一般会有红色的实心圆。 第二步,启动调试程序 。可通过菜单栏,工具栏上的按钮进行。在不同的环境下,具体菜单栏和按钮位置不同。你或许需要自定义一下工具栏。 之后,我们可以看到,程序执行到它遇到的第一个断点处。 此时,黄色的箭头指向的行,即为当前程序执行到的位置。(一般来说,一开始都在断点上,所以是红色圆内一个黄色箭头) 此时,程序就在一开始的位置等待我们。我们可以查看到各种状态,例如变量的值。在 局部变量 窗口我们会看到 I B A 三个变量(或数组),他们的值很乱。因为此时还没有对他们进行初始化。 如果你无法看到 局部变量 窗口,可能是被隐藏起来了,你或许需要在角落里找一下它,或者通过某些菜单把他“召唤”出来。(由于VS版本的不同,具体位置和图标外观,菜单名称可能稍有不同) 在局部变量里,我们已经可以修改 i 或者 a b 数组的值了,但是目前修改,还没有太大的意义。 我们按下逐语句按钮: 黄色箭头就会下移到下一条语句,这表示程序又向前执行了一行。 同时,局部变量里,i 的值变成了 1 ,这表示循环变量 i 有了值,并且当前是 1。 红色,表示此时与上一步相比值发生了改变 。相比而言,B 和 A 数组未改变,因此不是红色。 再次点击逐语句按钮(或快捷键F11),黄色箭头继续下移,且 A(1) 变为红色。这表示数组 a 的第一个元素发生了改变。 多次点击逐语句按钮。会发现,黄色箭头开始在循环体内来回移动,i 的值从1开始变大,a 数组慢慢变为 1-10 之间的数。这是完全符合我们的代码预期的。 如果循环较多,比如1000次循环,点击逐语句就需要点击几千次。此时,我们可以在循环外插入第二个断点,例如在本例的第11行。然后点击继续按钮(表示直接运行到下一个断点处,而不是一步一步执行了)。 此时,可发现黄色箭头已到了下一个断点的位置,而且 A 数组和 B 数组已经赋值完毕。均符合我们的预期。 (假设我们此时无法确定第11行导致了 a(5) = 0.0) 再次点击“继续”按钮。编译器一直运行,直到发生了错误为止,会弹出错误: 我们点击“中断”,会发现程序虽然没有碰到断点,但依然暂停了。黄色箭头停留在第13行。我们把鼠标移动到 i 变量上,看到他的值是 5,说明第5次循环出现了错误。 分别移动鼠标到 b 和 a 上,查看 b(5) 和 a(5) 的值。会发现 a(5) 的值为 0.000, 这就是导致错误的原因 。 在某些情况下,某些编译器,或者因为设置原因,并不会抛出错误,也无法中断,编译器会让 b(5) / 0.0 = Infinity。 此时,就需要更细致的断点,跟踪,检查各变量的值是否符合预期,然后再来确定原因了。(当然也可以通过设置让编译器抛出浮点数错误),例如 IVF 如此设置: 关于调试,还有很多问题。下面简单陈述,具体效果请读者自行测试,很容易理解。 上述的逐语句按钮,在遇到函数调用时,会进入函数内部。如果不希望进入函数内部,可使用逐过程按钮(或跟踪步过),如果已进入函数内,可使用跳出当前函数执行到返回。 读者可以自行拿出自己以前的100行以内有函数调用的代码来测试一下,点击这些按钮,观察黄色箭头的位置,这些概念并不难理解。 最后,有一个比较常用的东西,叫做“ 条件断点 ”,它会在满足某些条件时才会触发这个断点。例如,一个10000次的循环,循环到 1234 次出现了错误。我们不能点1234次逐语句吧?此时,我们可设置断点的触发条件为 i=1234。 或者说,循环到不知道多少次的时候,某个数 ff 变为 0 了,我们可设置断点的触发条件为 ff 0.00001。 首先插入常规断点,然后在断点处右键: 调试还有很多可利用的工具,例如调用堆栈可查看程序调用函数的全部路线,对某些变量数组添加监视,查看内存中的数据,寄存器的值,windows返回的错误,调试字符串等等。 请读者朋友们根据现有的提示自行琢磨。祝大家的代码都调通!
个人分类: 编程相关|2574 次阅读|0 个评论
[转载]win7 系统下,IMSL在IVF中的安装和应用
xiaoxiaoxingzi 2018-1-11 17:28
首先做个铺垫,之前用fortran都是在compaq visual studio(以下简称CVF)下编译使用的。后来随着2005年intel公司将其收购,并在此基础上开发了intel visual studio(以下简称IVF)之后,主流计算机win7系统下,CVF就很难再使用了,因为CVF不支持64位系统,在32位下编译也常常出错。 现在的新电脑上都是预装了64位的正版win7系统,于是我就装了microsoft visual studio2010 和intel visual fortran2011, 全新的界面和运算速度确实感受不一样。 最近在工作中,遇到了一个要求方程组解的计算问题,突然想到自己以前编过的一个小程序可以拿来用用,就找了出来。不用还好,这一用,一下子就粗大事了。 在CVF中,一些简单的求方程解或者逆矩阵之类的,是可以调用imsl函数库直接求解的,这点用过CVF的人应该都知道,而现在,在IVF中却编译不了了。哥一下子就蒙了,总会显示如下一段话: error #7002: Error in opening the compiled module file. Check INCLUDE paths. 。但凡懂点英语的也能猜个大概其,程序没问题,是在打开imsl时出错了,调用路径出了问题。对于我这种电脑盲来说,一出现问题就等于束手就擒,一下子没了头绪。重装了一遍,还是有错,把程序简化,弄了个只有8行的小程序,还是来调用imsl,还是出错,这下明确了,软件安装和程序本身都没问题,应该是函数库本身的问题。于是开始求教百度和编程爱好者论坛,不得不插一句,像哥这种电脑盲,点开编程爱好者论坛的时候,心里都发慌,回头还瞅了瞅,生怕被别人看见给鄙视了。 不搜不知道,一搜才发现,原来和我同样问题的人很多。浏览了大概半天,终于理清了思路。 大概是这样的,IVF 后来自己开发了 MKL 函数库,于是不合 IMSL 合作了。因此新版本都没有捆绑 IMSL 函数库。那怎么办?还好可以自己手动安装,IMSL函数库地址如下: x86_64 ftp://ftp.vni.com/pub/imsl/fnldownload/fnl60_10594.zip x32 ftp://ftp.vni.com/pub/imsl/fnldownload/fnl60_10593.zip 安装过程中会提示你输入的序列号:201111 然后对编译器进行配置,工具--选项 Libraries:C:\\Program Files\\VNI\\imsl\\fnl600\\IA32\\lib Includes :C:\\Program Files\\VNI\\imsl\\fnl600\\IA32\\include\\dll 然后就各种装,各种下载,都弄完了想着万事大吉了,结果没想到还是不行,我擦! 继续研究,发现原来是调用语句错了,以前那种 USE IMSL的语句早就过时了,哎!!真心out了,现在都是 INCLUDE 'link_fnl_static.h' use lin_sol_gen_int use rand_gen_int use error_option_packet 这类语句,具体啥意思详见imsl 安装路径下的help文件夹中有帮助文档,最后说CVF或是IVF本身的HELP文档是非常好的帮助,仔细研读可以学到很多有用的东西。只是有一点,其help不够翔实,例子也不够多,所以有些东西理解起来比较费劲,所以很多人选择了放弃。可以多读一些英文的fortran书籍
个人分类: 编程相关|2677 次阅读|0 个评论
[转载]从Compaq Visual Fortran到Intel Visual Fortran
xiaoxiaoxingzi 2018-1-11 10:47
现在64位的计算机已经越来越普遍了,Intel公司的CORE处理器也从酷睿双核升级为i3、i5直到现在的主流i7,微软的Windows 7系列的64位系统用起来感觉也挺不错的。但正是这64位,最近给我但来了些许的纠结。本文结合今天自己学习Intel Visual Fortran(以下简称IVF)的内容,分析了IVF的优势、相关软件的安装要求、从Compaq Visual Fortran(以下简称CVF)向IVF的转换以及IVF下简单Fortran计算程序的编译执行的操作过程,以期能对碰到类似情况者提供点思路。 IVF较之CVF优势: 我的戴尔Inspiron系列机子是64位,Windows 7系统也是64位的。在我的硕士研究生阶段学习中需要用到Fortran程序设计语言,以前我习惯使用CVF来编写、调试和运行Fortran程序,但现在看来不行了。CVF不支持64位,因为2005年以前Fortran 90/95 的Win32 开发环境多采用Compaq Visual Fortran 6.x,但在2005年CVF 开发团队加盟到Intel 公司,HP 宣布其CVF6.6 截至2005年12月31日,IVF9.0 将作为其新一代后继编译器。 IVF是在CVF的基础上开发的Fortran编译器,它将CVF 前端与英特尔处理器后端相结合,拥有CVF 丰富的语言功能和英特尔处理器的代码生成及优化功能,使运行在英特尔平台上的程序能得到大幅度提高。而且随着计算规模的增大,对计算内存的要求必须采用64位的程序才能够满足我们的要求,但CVF没有提供64位系统的编译平台。另外程序并行功能的实现可以用Intel的MKL所提供的函数库,这些函数库采用了共享内存并行计算的OpenMP,但以前的CVF没有提供对OpenMP的支持,所以无法调用MKL的并行函数库。所以,从CVF6.x转移到IVF9.0已是势在必行,选用IVF来Fortran编译程序也是很有必要的。 IVF相关软件的安装说明: 在使用CVF6.5/6.6编写运行Fortran程序时,只需要安装Compaq Visual Fortran6.5/6.6即可。这是因为在这个安装源程序中,Visual Fortran已经被组合(集成)在Microsoft Visual Studio(以下简称VS)的图形接口开发环境中了,所以可以直接安装使用。这与VC/VC++类似,故用户看到的CVF程序编写界面与VC/VC++的基本是一致的。 而采用IVF来编写运行Fortran程序则稍微麻烦些。网上下载到的Intel Visual Fortran Compiler10/11版本都只是编译器,它需要放到集成开发环境(Integrated Development Environment,以下简称IDE)中去才能使用。故而IVF Complier还需要VS6.6以上版本的IDE的支持,所以必须事先安装好VS后,才能安装编译器。我就是在安装VS2005后再安装IVF10的。具体安装方法参见本文后的链接部分说明。 IVF与CVF的兼容性: IVF 编译器支持所有的CVF 语法,包括Digital Equipment Corporation(DEC) Fortran和Microsoft Fortran PowerStation 4.0 的语言扩展;支持所有的CVF库例程,包括可移植库,QuickWin以及所有的系统接口模块。 IVF 不支持的CVF特征有: (1)Compaq Extended Math Library(CXML)。可以使用Intel Math Kernel Library 或IMSL、NAG 等第三方数学库来替换。 (2)跨引用的源代码浏览器。 (3)COM服务器向导。假如要转换由CVF的COM服务器向导创建的组件工程,需要在IV下重新构建;假如要调整组件接口,比如增加新的接口,则需要在CVF下进行。 从CVF向IVF的转换: 由CVF 转换到IVF,所有的Fortran源文件都需要在IVF下重新编译。IVF编译器不能直接使用CVF编译的对象、模块和静态链接库;但只要不在两个环境下共享输入/输出单元,IVF编译的应用程序可以使用CVF 建造的动态链接库。 大多数情况下无须改变Fortran源文件,只在IVF下重新建造现存的CVF工程即可;但有些工程需要改变小量源代码,有些则需要对建造方法做出适当调整。 IVF编译器提供有工程转换向导,以方便CVF向IVF的转换。转换可以分两步进行: (1)打开CVF项目空间。可以直接双击CVF项目空间(*.dsw)打开之(如图1所示文件)。 图1 在随后出现消息框,如图2,点击“全是”按钮,将CVF 项目空间下的每一项工程转换为IVF 解决方案下对应的Visual C++.NET 工程。 图2 (2)提取Fortran 工程项目。在上一步执行完毕,会展示Microsoft Visual Studio.NET开发环境,待转换的CVF 工程作为Microsoft Visual C++ 工程出现在解决方案资源管理器中,如图3所示。在每一个工程名上点击右键,从弹出的上下文菜单中选择执行“ExtractCompaq Visual Fortran Project Items”,至此工程才算转换完毕。 图3 由于Microsoft Visual Studio.NET的一个工程只能使用一种语言,所以,若CVF工程中包含Fortran和C/C++两种源文件,必须将CVF混合语言工程转换成两个单一语言工程:一个为Visual C++工程;另一个为Intel Fortran工程。而且,IVF向导将其中的一个工程改造成主工程。具体由哪一种语言建造主工程,由开发人员来决定,如图4 所示。 图4 在IVF下新建Fortran工程: 首先新建一个Fortran工程项目,在VS2005下执行菜单命令“文件—新建—项目” (如图5)即可。 图5 在弹出的对话框中选择“Intel(R)Fortran—Console Application—Empty Project”(如图6),点击确定即可创建一个新的Fortran工程项目。 图6 向导生成的在解决方案中的工程项目,可以从IDE“项目”菜单或在解决方案资源管理器窗口点击鼠标右键弹出的上下文菜单中,选择“添加新项”对话框,选择“Source”,从而创建要插入工程的文件。于是一个扩展名为.f90为文件被创建,详见图7~图9。 图7 图8 图9 在IVF下运行Fortran程序: 在IVF下链接、编译、调试和运行Fortran程序都与CVF下有所不同。首先需要生成exe文件,点击菜单中的“生成—生成***”,见图10,编译器即开始链接编译Fortran程序。 图10 若链接编译没有错误,及表示exe文件已经生成,如图11。 图11 此时,查看运行结果或者输入相关信息,点击菜单下“调试—开始执行(不调试)”见图12,于是即可见到如同CVF运行时的界面,如图13。 图12 图13 相关链接: 1、关于Microsoft Visual Studio 2005、Intel Visual Fortran和MKL(Math Kernel Library,是Intel为科学和工程计算设计的数学库)的详细安装步骤,请参见河海大学牛志伟老师QQ空间《VS2005+IVF+MKL安装及编译环境配置方法》,地址如下:http://user.qzone.qq.com/793915?ptlang=2052 2、集成开发环境(Integrated Development Environment简称IDE)软件是用于程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面工具。就是集成了代码编写功能、分析功能、编译功能、debug功能等一体化的开发软件套。所有具备这一特性的软件或者软件套(组)都可以叫做IDE。如微软的VisualStudio系列,Borland的C++Builder,Delphi系列等。该程序可以独立运行,也可以和其它程序并用。例如,BASIC语言在微软办公软件中可以使用,可以在微软Word文档中编写WordBasic程序。IDE为用户使用VisualBasic、Java和PowerBuilder等现代编程语言提供了方便。不同的技术体系有不同的IDE。比如可以称为C++、VB、C#等语言的集成开发环境,所以可以叫做IDE。同样,Borland的JBuilder也是一个IDE,它是Java的IDE。zendstudio、editplus、ultraedit这些,每一个都具备基本的编码、调试功能,所以每一个都可以称作IDE。
个人分类: 编程相关|2265 次阅读|0 个评论
Fortran语言初探及Win7 64位下Fortran开发环境配置
GISdsq 2017-5-21 16:44
笔者作为一只游走在生态、遥感、GIS与计算机的学生狗,最近终于因缘际会各种巧合下开始学习Fortran。还记得遥感物理课上牛柳两位老师(真是一个折磨萌萌哒台湾腔南方银口音的老师组合)的辐射传输方程、几何光学模型时时出现Fortran的身影。 好了,扯淡完毕,首先先来简介下Fortran语言。 Fortran源自于“公式翻译”(英语:FormulaTranslation)的缩写,是一种编程语言。它是世界上最早出现的计算机高级程序设计语言,广泛应用于科学和工程计算领域。FORTRAN语言以其特有的功能在数值、科学和工程计算领域发挥着重要作用。Fortran 90之前的版本是人们所知晓的FORTRAN(全部字母大写),从Fortran 90以及以后的版本都写成Fortran(仅有第一个字母大写)(ps,来自度娘百科)。 可以说Fortran是属于计算机编程语言中的老古董了,但是另一个重要特点就是在科学和工程计算领域应用广泛,主要是其编程语言本身在数组计算上的一些优点决定的。从TIOBE 2017年1月的编程语言排行榜来看 Fortran排在第28位,仍居前30之列,说明该语言仍旧具有广泛适用人群。 那么Fortran在地理学、生态学与遥感方面的应用典型有哪些呢? 事实上,在地理学、生态学与遥感领域,Fortran可以说有大量的学者使用并建立开发了大量的模型。比如遥感方面,大气辐射传输6S模型、MODTRAN辐射传输模型;生态学方面,WOFOST作物生长模型、DSSAT作物生长模型、景观中性模型模拟软件RULE等。 同时Fortran对数组处理的优势使得它能在遥感数据的处理方面担当举足轻重的角色(类比语言IDL、Matlab、Python的numpy),这也是笔者学习的初衷。 当然,正如前面提到了,Fortran是个典型的老古董语言,应用广泛的相关模型基于的Fortran版本的编译器在Win 7及以上系统中基本无法正常安装,故Win 7 64位系统如何配置Fortran开发环境是Fortran语言学习的第一步。 由于传统的Visual Fortran 6.6.0及以下版本在Win 7 64位无法兼容,网上虽有帖子提出了相关解决法方法,但笔者亲自尝试的结果是hello world无法运行,故这边介绍其他方法。 这里有两种配置方法是可以的: 第一种,安装Visual Studio。作为微软主推的IDE,VS在诸多IDE中确实功能突出,优点颇多,作为商业软件,简单的开发环境配置方法也是一大优势。只需勾选Fortran相关编译器安装,即可配置成功。 第二种,安装其他IDE,由于VS的简便性导致将其分为一类,其他IDE只需有Fortran编译器即可。VS在简便性上确实很优秀,但是相对而言,VS是个典型的重量级IDE。相对而言,笔者最近喜欢轻量级IDE,故搜索了其他IDE,以Code::Blocks为例,偏爱它的另一个原因就是因为它是免费开源软件(开源大法好)。 1.首先下载带有Fortran编译器的Code::Blocks软件。 http://www.codeblocks.org/ 选择最后一个 2.直接安装即可,确认安装所有部分 3.安装完毕后,打开IDE 在菜单栏中找到“Setting”→“Compiler” 复制一个编译器,自定义名字 接着点“Toolchain executables” 将画框部分的文件全部改成gfortran.exe 点击ok即可。 4.Hello World 编写 在菜单栏找到”File”→”New”→”Project”,建立一个Fortran工程文件。 工程命名 选择自定义的编译器 添加hello world项目的Fortran文件 编写如下的hello world进行测试。 program helloworld implicit none write(*,*) 'Hello world' end program 5.生成exe文件无法打开的处理方法 某些时候生成的exe文件打开会报错。类似“找不到 * .dll”“这个应用程序安装/配置不正确,重新安装…”这样的错误。 这样的情况下,只需在系统变量里面PATH加上对应的路径即可。
个人分类: 技术干货|2100 次阅读|0 个评论
Win10安装gfortran
dabing 2016-12-10 19:56
在这个网站 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62049 找到下载链接: http://users.humboldt.edu/finneyb/gfortran-windows-20140629.exe 下载好后解压安装。 另外这个网站提供了几个不同系统的安装版本: https://gcc.gnu.org/wiki/GFortranBinaries 这里有详细的使用说明: https://gcc.gnu.org/wiki/GFortranBinariesWindows ------------------------------------------------------------------ 软件安装完后,编译自己的fortran文件 在cmd窗口中进入fortran文件所在的目录,如: C:\ cd /D F:\MacCopy\VTri\6_WD\02lcdc2015 F:\MacCopy\VTri\6_WD\02lcdc2015 gfortran dc22may2015flop.f -o dctest 生成 dctest.exe ,点击即可运行fortran程序。 2016-12-10@BNU
个人分类: Win_soft|23181 次阅读|0 个评论
Matlab与Fortran的混合编程,通过DLL实现
热度 2 MTIWorkstation 2016-8-25 16:09
最近在研究 Matlab 与 Fortran 的混合编程,简单的教程或者个人的使用经验与大家分享一下。以前对于 C++ 和 Fortran 的混合编程比较熟悉,网上关于 C 和 Fortran 混编的教程也很多,但是关于 matlab 和 fortran 的教程却比较少,而且以 mex 混编为主,很少有关于以动态链接库( dll )混编的教程,自己从网上找了些教程,加上自己摸索的。 matlab 与 fortran 的混编有两种主要的方式,一种方式是在 matlab 里面以 mex 命令进行调用,这也是应用比较多的一种方式,但是在实际使用过程中,发现有一些问题,主要集中在编译器的问题或者其他的一些错误,如在改写 fortran 时需要加入头文件等,对于编译器也有一定的要求,因为 mex 混编需要两个编译器同时运行或者同步进行,所以作者经过一顿时间的捣鼓失败后,只能遗憾放弃这种方式。但是这种方式却也有一定的优势,如调用比较简单,就跟 matlab 调用自家函数一样。 另一种方式就是动态链接库了,也就是 DLL ,作者对此比较熟悉,这种的好处是不受编译器的限制,只要完成正确的 dll ,后续就交给 matlab 或者其他调用语言了,在其他语言混编中非常常用,也是微软非常喜欢的一种方式,个人也比较喜欢这种方式进行编程,尤其是进行软件的开发。 言归正传,如何实现 matlab 与 fortran 的动态链接库混编呢,其实很简单,首先就是 fortran 程序的编写或者改写,与通用的 dll 改写时一样的,首先在编译器中新建一个动态链接库工程,然后就是代码的编写。以求和为例详细说明: 1. 声明函数: subroutine test ( a,b,c ) 2. 定义输出函数,表明该函数是输出的,其它语言可以调用: !DEC$ATTRIBUTES C,DLLEXPORT :: test 3. 返回函数值,就是需要其完成的功能后输出到 matlab 的数据,在此有一点与 c++ 的混编不同,就是不需要声明输入变量,因为 c++ 调用 fortran dll 时同时需要声明输入变量,即 a,b 。声明方式是一样的。 !DEC$ATTRIBUTES REFERENCE ::z ! 4. 正常的 fortran 编码: integer a,b,c a=1 b=2 c=a+b 5. end subroutine 以上就是一个简单的例子,当然,如果要完成复杂的程序是一样的,不管有多少个函数多少个变量,完全与 fortran 的编码一样。 其次就是 matlab 的调用了,与 c++ 类似,需要 loadlibrary 来加载 dll ,但是其需要一个 .h 的头文件,头文件很简单,就是生命一下前面的 test 函数,因为我们是通过 C 的标准生成的输出函数,所以需要一个 c 的头文件,里面就一条命令, void test(int,int,int*); matlab 中的调用如下: loadlibrary('test.dll','test.h');// 加载 dll 文件和头文件,将这两个文件与 m 文件放在一个目录下,当然也可以通过路径指定 c=0; W=calllib('test','test',1,2,c); 函数调用命令 , 第一个 test 就是 dll 文件名,第二个 test 是头文件中的函数名,当然可以不相同,为了区分尽量可以取不一样的名。 1,2,c 就是输入输出变量。 unloadlibrarytest // 释放 dll ,同 c++ 以上就是一个简单的 matlab 与 fortran 基于动态链接库调用的例子,如有错误或者不当之处欢迎指正交流。
个人分类: 编程|4018 次阅读|2 个评论
0012:Fortran 95程序设计_彭国伦(个人笔记)
cwhe10 2016-8-2 12:12
《Fortran 95程序设计_彭国伦编著》一书学习用时5天,对Fortran 语言编程具有一定的理解,以下是个人笔记: !------------------------------------------------------------------ 1.以*.F90位扩展名的文件,就是以Free Format来编写的fortran程序; 2.Fortran语言不区分大小写; 3.Free Fortran格式说明:1)叹号!后面是代码注释;2)每行可以编写132个字符 ;3)行号放在每行程序的最前面; 4)一行代码最后是号具有连接下一行代码的 作用,同样,若代码开头具有号也是连 接上一行的作用; 4.write(*,*)括号中两个星号都没有各自的意义,第一个星号代表输出的位置使 用默认值,即屏幕;第二个星号则代表不特别输出格式; 5.write(UNIT=*,FMT=*)与write(*,*)等价; 6.print *,“Hello” !星号限定输出格式; 7.Fortran 4种基本类型:整数integer、浮点数real、字符character、布尔变量 logical; 8.声明方法:integer(kind=2) a integer *2 b integer (2) c 9.等价声明方法:integer :: a 和 integer a 10.3.0/2.0=1.5 3/2=1 !------------------------------------------------------------------------- 11.连续使用两个*号是要做乘幂计算; 12.复数类型 Complex 13.add = first // second !经过两个连续的除号可以连接两个字符串; 14.INDEX(String,key) !返回key这个‘子字符串’在母字符串中第一次出现的 位置; 15.char(65) !输出ASCII码65所代表的字符,就是A; 16.ichar(‘A') !输出字符A的ASCII码; 17.a = .true. !设置真值,注意两个点号; 18.Fortran系统会默认第一个字母变量为I/J/k/L/M/N为整数变量类型,其它变量会 被当成浮点数使用; 19.Implicit none!取消默认类型功能,所有变量都要事先声明,其次,该命令最好 放在program的下一行; 20.real,parameter :: pi=3.14159!定义常变量,实数类型; !------------------------------------------------------------------ 21.DATA A,B,C,str /1,2.0,(1.0,2.3),'Fortran 77'/ !data命令会一会走啊顺序 设置初值,即A=1,B=2.0,C=(1.0,2.3),str='Fortran 77' 22.equivalence(a,b)!声明a,b两个变量使用同一块内存空间; 23.Type具有组合类型功能,结构: type :: person -----end type person 例如:a=person(peter,20,170,60,Guilin,China) !person是type类的一个 名字 24.integer (kind=1) !-128 - 127 integer (kind=2)!-32768 - 32767 integer (kind=4)!-2147483648 - 2147483647 real (kind=4)!±1.18*10^(-38) - ±3.40*10^(38) 25.Fortran 90逻辑运算符号说明: == 相等 、 /=不相等 、 .EQ.等于 、 .NE. 不等于 、 .GT.大于 、 .GE.大于等于 、 .LE.小于等于 26..AND. 交集并且两者均成立、 .OR. 并集,只要有一个成立、 .NOT.逻辑反向、 .EQV.两边表达式要相等、 .NEQV. 两边结果不相同 27.字符串的大小比较是根据字母顺序来进行的; 28.Fortran中的select case类似matlab中的switch功能: select case (score) case (90:100) grade='A' case (80:89) grade='B' ... case default grade='?' !其它情况 end select 29.select case有些限制:1)只能使用整数(integer),字符(character),及 逻辑(logical),不能使用浮点类型(real); 2)每个case里的数值必须是固定的常量,不能使用变量; 30.使用浮点数时,不能使用select-case做多重判断,只能使用if-else if的做法; !------------------------------------------------------------------ 31.不建议使用goto语句; 32.pause 暂停执行 、continue 无意义(与matlab中的功能不同),继续向下执行、 stop 停止一切程序; 33.IAND 二进制的AND运算 、 IOR 二进制的OR运算; 34.推荐好书 《Modern Fortran》 35.Fortran编译器: GCC、intel、PGI、NAG、Absoft 36.Fortran:比较底层,各种算法功能需要自己实现; 37. 推荐教材书 《Modern Fortran Explained》,这是最好 的一本! 38.有用的网址推荐: http://sourceforge.net!很多开源项目 http://www.fcode.cn !国内fortran论坛,内有视频与书籍免费下载 http://gcc.gnu.org/wiki/GFortran!windows !上安装GFortran编译器 39.windows系统可以采用IVF+VS组合编译器 40.do couter=1 , lines , 1 !计数器=1,终止数值=lines,增值=1 !------------------------------------------------------------------ 41.Do while() end do 42.声明变量过程中最好先赋予一个初值; 43.Cycle循环: 在循环中药略过目前的循环程序模块,直接进入下一个循环是,就 可以使用cycle命令了; 44.Exit: 可以直接跳出一个DO循环或Do while循环; 45.outter : do i=1,3,1 !循环取名为:outter 46.fortran数组声明:Datatype name(size) !类型 数组名(大小) 47.type :: person real :: height,weight end type type (person) :: a(10) !用person这个新类型来声明数组 --- a(2)%height = 180.0 a(2)%weight = 70 48.integer a(5) !可以使用a(1-5)五个角标数据; 49.integer a(-1:4) !可以使用a(-1) a(0) a(1) a(2) a(3) a(4)共6个数据 50.integer A(5) Data A /5*3/ !A为5个3的数组 !------------------------------------------------------------------ 51.数组a=b*c !a(i,j)=b(i,j)*c(i,j),其它a=b/c类似 52.integer,allocatable :: a(:) !声明一个可变大小的一维数组 53.allocate (a(10)) !配置数组内存空间 54.allocate (a(10),stat=error)!若error为0,则分配内存成功;否则分配内存失败 ; 55.子程序与主程序之间的变量互异,互不相同; 56.主程序中的变量传递到子程序中后,两者共同占用同一块内存; 57.Fortran:传址调用 C语言:传值调用 58.External:声明不是一个可以使用的变量,而是一个可以调用的函数 real add !add是一个real变量 external add !add是一个函数 59.function add(a,b) 与 real function add(a,b) 等价; 60.全局变量 Common 声明的时候,主程序中的位置与子程序声明的共同占用同样的 内存; !------------------------------------------------------------------ 61.BLOCK Data可以设置全局变量的初值! 62.参数传递过程中需要类型一致,否则报错; 63.第178页的例子需要手动敲进去测试一下,以便加深印象; 64.数组在函数传递参数过程中,均传递第一个数据的地址; 65.例如:character (len=20) :: str=Hello,Fortran 95 call showstring (str)!送出字符串开头的地址; call showstring (str(8:))!送出字符串第8个字符的地址; 66.external是外部函数 intrinsic是Fortran的库存函数 67.Fortran 90的intent命令用来设置参数属性; 68.intent(inout) !可读又可写的属性,跟什么都没有是一样的 69.Fortran 90中,可以用optional命令来表示某些参数是‘可以省略’的——P191 70.递归调用函数格式 resursive integer function funt(n) result(ans) 递归 返回类型 函数 返回值 !------------------------------------------------------------------ 71.Pure并行计算作用 ——P202 72.学习网址: http://choose.fcode.cn/!个人电脑与喜好来选择编译器 http://pan.fcode.cn/!资料盘 http://v.fcode.cn/!视频 73.Module 封装程序模块 74.在module声明中指定要save的变量,功能上也等于全局变量; 75.Visual Fortran中所提供一些扩充函数库就用这个方法来归类,像数值函数库 IMSL就放在modual IMSL中, 3D绘图程序库openGL的函数就放在modual OpenGL 中,使用它们均首先要 use IMSL和 use OpenGL下; 76.封装在同一个module中的函数会自动互相认识; 77.*.LIB的文件内容经过编译,无法从这个文件中读到初始程序代码; 78.第222页的程序模块要敲下代码试试; 79.open (unit=10,file='Hello.txt')!10是文件的指定代码,充当文件的别名作用; 'Hello.txt'是文件名称 write (10,*) Hello!Hello被写入10代码的文件'Hello.txt'中 80.上面的unit的赋值最好避开1-2-5-6四个值; !------------------------------------------------------------------ 81.Rewind(10) !10是文件代码标号,rewind作用是将文件读写位置移动到最前面 或者最开始的地方; 82.inquire 文件查询 backspace 文件读写位置返回 endfile 读写位置变成文件结尾 rewind 返回开头 close 文件关闭 Trim 删除文件后面的多余空格 83.read (fileid,(A200),iostat=error) buffer !这行 代码是啥意思呢? 84.read(string,*) a !从字符串string中读出一个数字 85.namelist /n1_name/var1,var2,...!这个namelist是什么东 西? 86.指针作用:1)取出指针中所保存的内存位置;2)到这个内存中读写数据; 87.指针pointer一般与目标target配合使用,尤其是在type自定义类型中; 88. integer,pointer :: P allocate (P) !分配内存给P 89.Fortran提供Associated函数,用来检查指针是否已经设置指向; 90.interger,pointer :: P=null()!把指针初值指向一个不能使用的内存地址, 相当于给指针赋予初值,是有一定好处的; !------------------------------------------------------------------ 91.type (datalink),pointer :: next !可以这么写 type (datalink) :: next !不可以这么写 92.use A,aa=Va !把module A中的变量Va改名为aa使用 93.use A, only : Vc!只使用module A中的变量Vc 94.interface show!虚拟函数show module procedure show_int module procedure show_character end interface 则有, call show_int(1) 等价于 call show(1) call show_character(Fortran 95) 等价于 call show(Fortran 95) !------------------------------------------------------------------ 何成文 桂林电子科技大学 2016年8月2日 12:10 附件: Fortran 95程序设计_何成文笔记.txt
个人分类: 科学研究|8536 次阅读|0 个评论
fortran编译的flag设置可提示数组越界(segmentation fault)
zhoufcumt 2015-12-2 05:26
ifort -O2 -g -traceback -check all -fp-stack-check -mcmodel medium -shared-intel test.f90 -o test
个人分类: Fortran|5301 次阅读|0 个评论
[转载]MKL程序编译与连接:Lapack篇
besphed 2015-11-25 16:46
原帖转自我的空间:http://hi.baidu.com/coolrainbow/blog/index/1 经验表明,第一次做MKL程序编译时,大多数人都会走很多弯路,编译几个小时也不成功。其实这个原因很简单,就是:懒于阅读技术文档!!!!!!事实上,本人当时就是这样,看到3000多页的MKL手册头都大了,于是在编译时就“跟着感觉走”,弄得找不到库或者函数错误。其实,花一个小时读下文档,绝对比自己瞎折腾要强的多,这里把编译MKL的一些经验与大家分享下,作为快速入门。真正深入的话,还是那句:读文档! 1 MKL的环境变量 安装好MKL后,需要设置一些环境变量,这样才能找到所需要的库,这可以通过/opt/intel /Compiler/11.1/064/mkl/lib/tools/enviroments/mklvars{your-architecutre}. {sh|csh}实现。如果需要的话,可以加入到/etc/profile或你的.bashrc中。在程序编译或运行时,如果发生can not find libXXX之类,记得导出相应的LD_LIBRARY_PATH。如: can not find libmkl_intel_thread: cannot open shared object file... 如果你的这个库位于/opt/intel/Compiler/11.1/064/mkl/lib/em64t,那么: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/intel/Compiler/11.1/064/mkl/lib/em64t 即可。 2 函数选择:Lapack MKL提供了3000多页的技术文档,对每一个函数进行了详细的介绍。这里仅对Lapack系列做一些介绍。 初学Lapack的人可能会问:我怎么知道哪个函数是做向量点积的,哪个函数是做奇异值分解的。问的最多的是:有没有什么书可以参考的?答案:至少我没见过哪个书像讲MFC或PHP那样详细的把Lapack的每一个函数讲解的书。那么唯一的途径就是阅读技术文档。不要头疼,那个文档有英文版和日语版(shit)的,没有中文版的。好好学英文吧。 Lapack中的函数的名称都是XYYZZZ形式(BLAS类似)的, X 精度。 如 s 单精度(对应于Fortran中的real(kink = 4),C中的float) d 双精度(对应于Fortran中的real(kink = 8),C中的double) c 单精度复数 z 双精度复数 YY 对应的矩阵类型和存储方式 ge 一般的矩阵,存储方式full 所谓full就是说,矩阵A的元素aij以数组a(i, j)或a的形式存储于计算机中。这是最简单的方法,但是对稀疏矩阵或元素有一定相关性的矩阵效率较低 sy 对称矩阵,以full方式存储 sp 对称矩阵,以pack方式存储 pack方式对symmetric matrix采用了一维数组优化存储的方法,可以减小内存的使用。有两种存储方法,在Lapack程序中分别用'U' 'L'表示,对于'U',简单的说: aij --- a(i+j*(j-1/2)) where i j 'V‘类似。 还有其他的类型,请参考技术文档。 ZZZ 任务类型 就是你要干什么。如dot表示点积,trd表示三对角化等等 知道了这个命名规则后,找函数就方便多了。 3 函数参数:Lapack Lapack中的每一个函数都有很长的参数,常常把初学者吓倒。并不是说对矩阵A,B求乘法,只要简单的输入A,B就可以了。还有一些辅助空间,任务类型之类的,都要输入,如矩阵乘法: call dgemm('n','n',N,M,K,a,x,N,y,K,b,z,N) 执行矩阵乘法z=x*y,但还有一些character*1 的'n','n'以及integer的N,K等等,这些含义一定要仔细弄清,不然可能会使任务执行错误! 4 编译连接 这是最头痛的问题。MKL的连接选项比较长,最好使用make工具,我常用的一个模板如下 mkllib=/opt/intel/Compiler/11.1/064/mkl/lib/em64t mklinc=/opt/intel/Compiler/11.1/064/mkl/include foo: foo.o ifort -o foo foo.o -I$(mklinc) -L$(mkllib) -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -lmkl_lapack95_lp64 -liomp5 -lpthread 在连接MKL程序时,要在每一个“LAYER”中选择一个库, 1 interface layer:提供程序接口,即libmkl_intel{lp64},其中lp64是对em64t或ia64版本。 2 threading layer:线程接口。一般都是libmkl_intel_thread。注意,有了它后,一定要加上iomp5和pthread库,不然会出现连接错误。 3 computational layer,计算接口,即libmkl_core和你所需要的,如lapack的libmkl_lapack95_lp64或者线性方程组的libmkl_solver_lp64等等。 4 RTL接口,即pthread等等。 这样才可以连接成功。 实例: 现在以一个实例完成。想算一个矩阵乘法,矩阵用双精度,一般存储模式,则选用dge???函数,查文档得知?gemm做矩阵乘法,于是选用dgemm,这个算矩阵乘法的程序代码: program main integer,parameter::N=3000 integer,parameter::M=2000 integer,parameter::K=4000 real(kind=8) a,b real(kind=8),pointer::x(:,:) real(kind=8),pointer::y(:,:) real(kind=8),pointer::z(:,:) a=1.0 b=0.0 allocate(x(N,K)) allocate(y(K,M)) allocate(z(N,M)) x=10.0 y=20.0 z=0.0 write (*,*) Performing matrix multiplication... call dgemm('n','n',N,M,K,a,x,N,y,K,b,z,N) write (*,*) DONE! deallocate(x) deallocate(y) deallocate(z) end 命名为dge.f90,写Makefile: mkllib=/opt/intel/Compiler/11.1/064/mkl/lib/em64t mklinc=/opt/intel/Compiler/11.1/064/mkl/include FCCFLAG= -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -lmkl_lapack95_lp64 -liomp5 -lpthread FCC = ifort dge: dge.o $FCC -o dge dge.o -I$(mklinc) -L$(mkllib) $(FCCFLAG) -g dge.o: dge.f90 $FCC dge.f90 -c -g 然后执行make,好,一个叫dge的可执行程序出现了,在我的机器上运行时间为1.457s。用我自己随手写的一个矩阵乘法程序一算:11min34s,汗颜....
个人分类: Fortran学习|3 次阅读|0 个评论
[转载]利用 makefile 编译 Fortran 程序的例子
changq0726 2015-5-16 17:14
 假设有四个文件,分别是: main.f90 ; global.f90; function1.f90; subroutine1.f90 其中: main.f90:包括主程序。它调用函数和子程序; global.f90:用module定义一些全局变量(过程),这个module被main.f90以及 function1.f90中的function1 函数调用; function1.f90:中包括一个函数function1,它被main.f90调用; subroutine1.f90:中包括一个过程subroutine1,它也被main.f90调用; 1.直接生成可执行文件 假如我们手头上有一个intel的fortran编译器进行编译,就要采用如下命令: ifort global.f90 main.f90 function1.f90 subrouine1.f90 这样会自动生成文件a.out,这是一个可执行文件。因为编译器将上述四个文件编译(compile)后生成目标文件(***.o),并将这几个目标文件自动连接起来(link)。同时我们会发现当前目录下多了如下几个文件: global.o global.mod main.o function1.o subroutine1.o 如果想让生成的可执行程序有一个名字,将命令改成 ifort -o execname global.f90 main.f90 function1.f90 subrouine1.f90 即可。 输入命令 ./execname 即可执行该程序。 2.分步进行 Build=Compile+Link。所以我们也可以不直接生成程序,而是先编译文件以产生目标文件(即不连接)。例: ifort -c function1.f90 这个命令会生成与源程序(Source)function.f90相对应的目标文件(Object file)。 如果我们想要编译一个调用某module的程序,就必须先将module文件编译,顺序不能改变。例: ifort -c global.f90 ifort -c main.f90 第一个命令生成两个文件:global.mod和global.o;在第二条命令,也就是在main.f90的编译过程中,需要用到global.mod文件。所以,这两条命令的顺序不能改变! 将四个文件编译后,即可连接: ifort global.o main.o function1.o subroutine1.o 或者直接 ifort *.o Tip: NOTE:(1)若是gfortran编译器,将以上所有ifort替换成gfortran即可 (2)若所有的源文件都在一个文件夹中,生成可执行文件最快的方式是 ifort -c *.f90 ifort -o exename *.o 3.采用makefile 这是一个简单的makefile文件 # This is an commentary line in a makefile # Start of the makefile execname: global.o main.o function1.o subroutine1.o ifort -o execname global.o function1.o subroutine1.o main.o global.mod: global.o global.f90 ifort -c global.f90 global.o: global.f90 ifort -c global.f90 main.o: global.mod main.f90 ifort -c main.f90 function1.o: global.mod function1.f90 ifort -c function1.f90 subroutine1.o: subroutine1.f90 ifort -c subroutine1.f90 clean: rm global.mod global.o main.o function1.o subroutine1.o execname # End of the makefile 说明: makefile文件应命名为makefile,并将其防止在与源程序同一目录下。过长的句子可用 \ 做换行符, # 后的句子为注释。 执行时,直接输入: make 执行清除时,输入: make clean 我的理解是这样的。举个例子,比如这两行: execname: global.o main.o function1.o subroutine1.o ifort -o execname global.o function1.o subroutine1.o main.o3 其中,execname称为目标文件(target);冒号后的文件是产生这个文件的一些prerequisites(前提)。第二行是命令(command),跟在target和prerequisites之后,在其前加入Tab字符,以示区别。这些共同组成了规则(rule)。其实makefile所”make“的所有事情都是rule中具体化的命令。 clean并不是一个文件,而是一个行为(action)。默认不执行。它不包含任何前提(prerequisites)。我们称之为phony target(虚假目标)。 Tip NOTE: (1)第一行应定义可执行文件,并附加上它所需要的所有前提文件。 (2)定义目标文件时,应附加其所有mod文件,以及该mod文件所对应的object文件(***.o)。 以上两条规则应时刻最遵守!
个人分类: Fortran|10807 次阅读|0 个评论
NumericalRecipes 【Fortran Codes】
leeyp 2015-5-8 21:19
Numerical Recipes Codes2.10.rar 最近找到了Numerical Recipes的Fortran codes.
个人分类: 编程笔记|67 次阅读|0 个评论
Fortran与C#混合编程的一些思考
T573029173 2014-11-6 13:10
Fortran 与 C# 混合编程 Fortran 语言是世界上最早出现的、比较底层的计算机数值计算高级程序设计语言,较高层的程序设计语言,它具有实现精度高、计算速度快的功能特点,且其自身就拥有非常强大的程序集和计算类的数据结构,因此广泛应用于数值、科学和工程计算领域。但是 Fortran 语言的图形界面开发功能较弱,不适合开发独立的桌面或 Web 应用程序。 C# 是最近几年微软推出的新一代面向对象的界面开发语言,它以强大的 .NET 底层框架为基础,拥有十分强大的图形化界面开发平台。 C# 程序设计以 C 和 C++ 入门,在保留 C++ 设计灵活性的基础上,加入了 VB 的快速开发,因此十分适合图形界面的交互系统快速开发。然而,由于 .NET 采用的是托管机制,使得 C# 语言的计算速度、精度以及执行效率相对来说不是很理想。 因此,本文设想:能否在开发一个系统的时候将系统的工程计算部分和项目组织部分分开实现?将计算部分用数据精度高、计算速度快、执行效率高的 Fortran 语言实现,将项目组织部分用快速、高效的 C# 图形化用户设计语言。如此,在开发交互式解释系统软件时,可以采用 C# 与 Fortran 混合编程,这样既可以发挥 C# 的高效开发特点,又使得现有的经典 Fortran 计算程序可以得到充分利用。本文主要从混合编程的实现方面阐述了一些常用的方法和要点。 混合编程,是指利用两种或两种以上的程序设计语言组合起来的程序设计开发,彼此互相调用,传递参数,共享数据结构或数据信息,从而形成一个统一的程序系统的过程。利用 Fortran 语言和 C# 语言进行混合编程是为了能够更充分地发挥两种语言各自的特点,其基本思路如下: 运用 Fortran 语言编写动态链接库 ( DLL ) ,在 DLL 中提供计算的函数接口,然后在 C# 中调用该 DLL 中计算部分的函数,实现计算过程。这里需要注意的是,由于我们使用的是 Fortran 编译器,生成的 DLL 属于第三方非托管 DLL ,因此无法直接在程序中添加 DLL 的引用。 1. 混合编程的基本步骤 1.1 创建 Fortran 动态链接库 DLL STEP1 : 进入到 FORTRAN 集成开发环境下,依次打开 File | New | Fortran Dynamic Link Library ,为新的动态库命名如 : FORTRAN.DLL STEP2 : 使用 Ctrl + N 快捷方式,添加文件 Free Format 的 *.f90 子程序作为当前工作空间。 STEP3 :Fortran 建立动态链接库是使用子函数形式的,我们推荐是用 SUBROUTINE 而避免使用 FUNCTION. 同时要记得使用编译为 DLL 的注释性命名 : !DEC$ ATTRIBUTES DLLEXPORT :: FORDLL STEP4 : 当完成 DLL 的程序设计以后,编译生成 DLL 文件。进入工程文件中的 Bin/Debug 目录,便能找到该 DLL 库文件。 1.2 C# 调用 Fortran DLL 的过程 准备工作 : (1) 将 Fortran 生成的 DLL 文件拷贝至 C# 工程文件的 bin/Debug 目录下,目的是确保可执行程序 (exe 文件 ) 与库文件 (dll 文件 ) 在同一文件夹中,便于 Windows 自动查找 DLL 文件。当然这里除了手动拷贝 DLL 到 C# 的工程目录下外,还可以使用 脚本程序 让开发工具来完成。 (2) 在进行 Dll Import 连接之前,需要在 C# 中 增加 对动态连接库操作的类的 引用 : Using System.Runtime.Interopservices; 完成相关的连接设置就可以在 C# 程序中使用 Fortran DLL 中的子例程了。 C# 调用动态链接库的参数设置是使用 Dll Import 属性 来实现。然后使用一个 实例来装载传递过来的子例程和相应的参数 。 Example: // 通过 DLL Import 属性来设置调用 DLL 文件的参数。 public staticextern void FORDLL() // 使用一个具体的实例来装载从 Fortran 编译的 DLL 文件传递过来的子例程和相应的参数。 DllImport 的命名参数 : l CallingConvention 指示入口点的调用约定。如果未有指定 , 则使用默认值 : CallingConvention.Winapi l CharSet 指示用在入口点中字符集,默认为 CharSet.Auto l EntryPoint 给出 DLL 中入口点的函数名称。若未指定,则使用方法本身的名称。 l ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配。默认为 False l PreserveSig 指示方法的签名被保留还是被转换。当签名被转换时,它被转换为一个具有 HRESULT 返回值和该返回该值的一个名为 retval 的附加输出参数的签名。默认值为 True 。 l SetLastError 指示方法是否保留 Win32 上一错误 ,默认为 False 。 FORDLL 修饰符说明 : l public 用来说明这个函数是公用的,可以在程序中的其它地方访问它 ; l static 则表示这个函数是静态的,即 C# 在调用的时候不会传递参数外的其它信息 ; l extern 则表示这个函数由程序以外的模块实现 ; l void 代表函数的返回类型,这个视情况而定。 2. 混合编程的关键技术 混合编程时,要注意调用程序与被调用程序遵守相同的规则,包括语言约定的一致性和数据处理的相容性等接口问题。 2.1 语言约定一致 1 函数名命名约定。 相互匹配的标识符在编程过程中应处理成一致的,因为不同语言的编译器智能机械的按本语言的命名约定进行处理。 C# 语言的符号名是区分大小写的, Fortran 语言中不存在符合名大小写的问题,在 Fortran 编译器中,默认的导出函数名是纯大写形式,两者如果处理不一致则导致程序连接失败。 而在 C# 中调用 Fortran Dll 时,必须指定与 Fortran 中的函数名一致。在 Fortran 代码中的解决办法是: ① Fortran 的缺省方式使符号名在 OBJ 文件中变成大写,如果在 C# 中调用一个使用 Fortran 缺省的子例程时,在 C# 中需用一个纯大写的函数名称来调用; DOUBLE PRECISION FUNCTION ADD (A, B) !DEC$ ATTRIBUTES DLLEXPORT:: ADD DOUBLE PRECISION A, B ADD =A + B END 纯大写 —— 对应的 C# 声明为: private static extern double ADD ( double A, double B); ② 如果想在 C# 中使用小写声明 Fortran 中的子例程,则应该在 Fortran 程序中需用 C 和 STDCALL 属性将所有名称转换为纯小写的形式 ; ③ 若是在 C# 中调用一个例程以大小写混合形式出现的时候,需要使用 Fortran 的 ALIAS 属性来解决混合形式之间的命名冲突。 Double Precision Function ADD (A, B) ! DEC$ATTRIBUTES DLLEXPORT :: ADD ! DEC$ATTRIBUTES ALIAS: 'Add' :: Add Double Precision A, B Add = A + B End 大小写混合 —— 对应的 C# 声明为: private static extern double Add ( double A, double B); ④ C# 中提供的解决方案是,通过使用 Dlllmport 的 EntryPoint 属性,指定 Fortran 的导出函数名。 例如: DOUBLE PRECISION FUNCTION ADD (A, B) !DEC$ATTRIBUTES DLLEXPORT :: ADD DOUBLE PRECISION A, B ADD = A + B END 对应的 C# 声明为: private static extern double ADD( double A, double B); 2 堆栈管理约定。 C# 语言在 windows 平台上的调用模式默认为 StdCall 模式,既由被调用方清理堆栈。而 Fortran 语言则默认由调用方清除。 所有涉及堆栈这样一种数据结构的参数,调用例程 (C#) 和被调用例程 (Fortran) 必须统一调用双方的堆栈清除方式才能保证 2 种语言间的正常函数调用 。因此, 堆栈管理约定包括 : 在调用过程中子例程接受参数的数目和顺序,调用完成后由哪一方来清理堆栈等 。这一约定在 Fortran 语言或 C# 语言中均可以采取措施进行统一。 1 ) 在 Fortran 语言中可以通过编译指令 “ !DEC$ ” 后的可选项 “ C ” 或 “ STDCALL ” 参数来实现: l STDCALL 模式 指定由被调用方清除堆栈。 l C 模式 声明由主调用函数清除堆栈( 但在传递数组和字符串参数时不能用此方法 )。 2 ) 如果在 C# 语言内做改动,则需要在 DllImport 属性中设置 CallingConvention 字段的值为 Cdecl (表示由主调用方清理堆栈)或 StdCall (表示由被调用方清理堆栈)。 堆栈清理方式 程序语言 调用方清理 被调用方清理 C# Cdecl 模式 StdCall 模式 (C# 默认 ) FORTRAN C 模式 STDCALL 模式 3 参数传递约定。 只有以同样的方式发送和接收参数,才能获得正确的数据传送和正确的程序结果。常见的参数传递为 值传递和引用传递 两种。 C# 默认的是值传递,而 Fortran 默认的是引用传递, 因此在混合编程过程中应注意保持传递方式的一致性。 1) 若统一为引用传递,在 Fortran 中无需做任何修改,因为 Fortran 默认的就是引用传递类型;而只需将 C# 的参数类型定义为引用类型,此时需 使用 ref 关键字 。 public static extern voidFORDLL(ref int m); // 通过 ref 将整形参数 m 被定义为引用传递 2) 若统一为值传递类型,在 C# 中无需做任何修改,因为 C# 中默认的就是值传递类型;而只需将 Fortran 的参数定义为值类型,此时 使用 VALUE 关键字。 !DEC$ ATTRIBUTES VALUE:: m ! 使用 VALUE 将 m 定义为值传递方式 若传递过程中既有值类型,也有引用类型,则为上面的两种综合使用, Fortran 中使用引用为 REFERENCE 。 !DEC$ ATTRIBUTES REFERENCE:: m ! 使用 REFERENCEE 将 m 定义为引用传递方式 2.2 数据类型一致 在 Fortran 中常用的数据参数类型有: REAL , INTEGER , DOUBLEPRECISION 。 REAL: 表示浮点数据类型,即小数,等价于 C# 的 float 。 INTEGER: 表示整数类型,相当于 C# 的 int 数据类型。 DOUBLEPRECISION: 表示双精度数据类型,相当于 C# 的 double 数据类型。 在 C# 调用 Fortran DLL 是必须保证参数的一致性,例如在 Fortran 中变量定义的是 REAL 类型,而我们传入的是 Double, 那么就会出现计算错误。 数据传递也就是涉及基本的数值传递和字符串的传递,其中 数值传递又包括单值型数据和数组型的数据。 1 数值传递。 满足好引用类型就基本可以实现,但是在传递数组的时候要特别注意: C# 的数组是从 0 开始记录的,而 Fortran 则是从 1 开始记录; 另外, C# 采用的是按行存放,而 Fortran 则为按列存放。在传递时应详细考虑数组问题。 例如在 Fortran 中的 A 数组,在 C# 中就应为 A 。 并且数组传递还应注意,只需要传递数组的首地址即可, DLL 需要的是数组的起始位置。 int ; FORDLL(ref bb ); 2 字符串传递。 传递和返回字符串是混合编程中最为复杂,也是需要考虑问题最多的部分。 C# 和 Fortran 的字符串传递同样麻烦,首先, C# 中的字符串是双字节的 Unicode 类型,而 Fortran 中默认的是单字节的 Ansi 类型。 这样产生的问题是 C# 会自动把 Fortran 的每两个字符合并为一个字符, Fortran 中的 128 个字符的字符串变成了 C# 中的 64 个字符的垃圾。另外, C# 和 Fortran 关于字符串的表示方式也有所不同: C# 的字符串表示方式与 C 语言相同,使用 \0 表示字符串的结束; Fortran 中则采用在最右端添加空格表示,并在最右端使用一个隐藏的参数表示实际的长度, 因此要正确的传递字符串,应该解决如何正确表达字符串长度的问题。 解决方案 1: 原理很简单,就是避免直接的进行不同类型的字符串传递,换用整型数组传递。 C# 中的字符串传向 FORTRAN 的具体操作流程如下: step1: 在 C# 中将字符串分割为字符数组; step2: 再将字符数组转为 ASCII 码数组 (0~128 的整数 ) ,然后传递给 FORTRAN ; step3: 在 FORTRAN 中利用 CHAR() 函数将 ASCII 码还原为字符串即可。 NOTE: FORTRAN 传向 C# 也是可以采用相同的思路进行 --------------------------------------------------------------------- // C# 中的字符串分割,并转存为 ASCII 码数组 String c = abcdefg ; ASCIIEncoding ascii = new ASCIIEncoding (); int ; for ( int i = 0; i c.Length; i++) { num = ( int )ascii.GetBytes(c) ; // 通过 ASCIIEncoding 类的对象调用 GetBytes 方法将字符串转变成字符,然后将字符转变成 8 位的 Byte 类型,再将 Byte 类型转变成 int 类型。 } int m = c.Length; S( ref num , ref m); // 在 C# 中使用 ref 关键字,表示参数传递时使用引用类型 --------------------------------------------------------------------- //C# 调用 DLL 声明及操作 //DLL 程序在 C# 代码中的入口点 public static extern void S( ref int ka, ref int m); // 使用一个具体实例 S 来装载传递过来的子例程和相应的参数。 // 参数说明 :ka 为 ASCII 码数组名 ; m 为 ka 数组大小 ( 即字符串长度 ) ---------------------------------------------------------------------- !FORTRAN 中还原字符串的操作 SUBROUTINE S(ka, m) !DEC$ ATTRIBUTESDLLEXPORT :: S CHARACTER(m)::str DIMENSION ka(m) INTEGER i INTEGER x, y DO i=1, m str(i : i) = char(ka(i)) END DO END ----------------------------------------------------------------------- 解决方案 2: 根据 Compaq Visual Fortran Version 6.6 随机帮助文件中的 Programmer's Guide - Creating Fortran DLLs - Programming withMixed Languages 和 Language Reference - A to Z Reference - A toB - ATTRIBUTES 等相关部分的内容做出的一个测试例程,用来说明 C# 调用 Fortran DLL 过程中参数传入 Fortran 的实现(只涉及到了数值类型和字符及字符串类型,其它类型读者可以继续到帮助中阅读相关资料)。 ! FortranDLL.f90 文件的内容如下: ! 用来建立 DLL 项目 ! 函数 WTSTR(Str) 用来建立 Str 文件并在该文件里记录 Str 字符串的内容 SUBROUTINE writestr(str) !DEC$ ATTRIBUTESDLLEXPORT,ALIAS:'WTSTR':: Writestr Character*(*) str Open(1, file = str) Write(1,*) str END SUBROUTINE writestr ! 函数 Iadd2(A,B) 用来就算两个整型数 A 、 B 的和并将结果输出到屏幕 SUBROUTINE iadd2(a, b) !DEC$ ATTRIBUTES C, DLLEXPORT, ALIAS: 'Iadd2' :: Iadd2 INTEGER :: a, b INTEGER :: sum sum = a + b WRITE(*,*) The sum:,sum, in DLL iadd2 END SUBROUTINE iadd2 ! 上述 Iadd2 函数中用来 C 属性字段,是用来说明参数是按值来传送的,而在 WTSTR 函数中因为是 ! 用来传递字符类型的,所以不能用 “C” 。( If C or STDCALL is specified for a subprogram, !arguments(exceptfor array sand characters)are passed by value. ) //C# 中的调用代码如下: // 这部分代码将编译生成 .EXE 可执行程序,调用上面的 DLL using System; using System.Runtime.InteropServices; namespace CCallDll { class Program { //WTSTR 函数说明部分,注意此处字符串参数的传递 public static extern voidWTSTR(string str,int strlength); //Iadd2 函数说明部分 // 两种类型的参数传递涉及到在 Fortran 程序中的处理也不一样 public static extern void Iadd2(inta, int b); // 主程序入口 static void Main(string[] args) { //Delcare the String Variable. Notice: it'sUnicode which occupies 2 Bytes string unicodeString=This.txt; Console.WriteLine(Originalstring is:{0},unicodeString); Console.WriteLine(The lengthof the string:{0},unicodeString.Length); //Call the Function of WTSTR(,) 此处增加了一个表示字符串长度的参数是因为 //Fortran 、和 C #存贮字符串的方法不一样 ( 详情查阅相关资料,有很多介绍 ) WTSTR(unicodeString, unicodeString.Length); Iadd2(1000,10); } } }
8098 次阅读|0 个评论
[转载]Random walk in 2 dimensions( Fortran code)
motivatedgirl 2014-9-12 09:24
! Random walk in 2 dimensions ! Program to accompany "Computational Physics" by N. Giordano and H. Nakanishi ! Copyright Prentice Hall 1997, 2006 program walk_2d option nolet library "sgfunc.trc" library "sglib.trc" randomize dim x2ave(1000) ! keep r^2 averages here call initialize(x2ave,n_walks,n_steps) ymax = sqr(n_steps) open #1: screen 0,1,0,0.93 set window -2*ymax,2*ymax,-2*ymax,2*ymax set background color "white" clear set color "black" plot area: -0.1,-0.1;-0.1,0.1;0.1,0.1;0.1,-0.1;-0.1,-0.1 plot 0,0; call calculate(x2ave,n_walks,n_steps) call display(x2ave) close #1 end ! initialize variables ! n_walks = number of walkers n_steps = number of steps taken by each walker sub initialize(x2ave(),n_walks,n_steps) input prompt "number of steps per walk = ": n_steps input prompt "number of walks = ": n_walks print "Hit p to pause, anything else to stop plotting." mat redim x2ave(n_steps) end sub ! do the calculation here ! x2ave(n) contains the average of r^2 at step n ! n_walks = total number of walkers ! n_steps = number of steps taken by each walker sub calculate(x2ave(),n_walks,n_steps) randomize cross_flag = 1 plot_flag = 1 for i = 1 to n_walks x = 0 ! current location of the walker y = 0 for j = 1 to n_steps r = rnd if r = 0.25 then x = x + 1 else if r = 0.5 then x = x - 1 else if r = 0.75 then y = y + 1 else y = y - 1 end if if plot_flag = 1 and jn_steps then plot x,y; ! plot each walk until a keystroke is hit else if plot_flag = 1 then plot x,y end if if cross_flag=1 and key input then get key z c$ = chr$(z) if c$="s" or c$="p" then get key z else cross_flag=0 plot_flag=0 end if end if x2ave(j) = x2ave(j) + x^2 + y^2 next j clear next i for i = 1 to n_steps ! normalize x2ave when finished x2ave(i) = log(x2ave(i) / n_walks) next i end sub ! display results now sub display(x2ave()) dim t(0) ! dummy array for plotting call setcanvas("white") set color "black" n = size(x2ave) mat redim t(n) for i = 1 to n t(i) = log(i) next i call settitle("Random Walks") call sethlabel("ln N") call setvlabel("ln R^2") call datagraph(t,x2ave,1,0,"black") call addlsgraph(t,x2ave,1,"red") ! compute and plot least squares fit call fitline(t,x2ave,m,b) set cursor 4,20 print "slope = ";m;", steps =";n get key z end sub
1978 次阅读|0 个评论
备忘:安装完毕 Intel Fortran Composer 之后的设置
mac81 2014-8-11 18:36
在.bashrc中加入: source /opt/intel/bin/ifortvars.sh intel64 alias ifortmkl='ifort -I/opt/intel/mkl/include/intel64/lp64 -L/opt/intel/mkl/lib/intel64 -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -lmkl_blas95_lp64 -lmkl_lapack95_lp64 -liomp5' 之后即可使用ifortmkl命令编译含有mkl子程序的代码了。
2409 次阅读|0 个评论
fortran2003 函数作为参数传递的问题
joeqwang 2014-5-3 22:14
今天在在编写一个自定义函数,它的目的是获得整型或者实型的所占位数,以便用于实现自动生成格式描述符。 这个自定义函数不仅可以识别不同类型(见红色字体代码),还可以将函数的计算结果作为参数传递(见蓝色字体代码)。 同样是传递内部函数,第一个例子不报错,第二个却报错 ,WHY?? ========================================================= 第一个例子, numspace ()能传递内部函数 ========================================================= moduletest_numspace implicit none interfacenumspace moduleprocedure numspace_i, numspace_r !module procedure numspace_f ! 如果不注释会报错,为什么?? end interfacenumspace contains integerfunction numspace_i(int_number) implicitnone integer,intent(in)::int_number character(len=128)::fmt write(fmt,'(I)')int_number numspace_i=len_trim(adjustl(fmt)) endfunction numspace_i !BL integerfunction numspace_r(real_number) implicitnone real,intent(in)::real_number character(len=128)::fmt write(fmt,'(F)')real_number numspace_r=len_trim(adjustl(fmt)) endfunction numspace_r !BL integerfunction numspace_f(from_function) implicitnone interface functionfrom_function() endfunction endinterface real::real_number character(len=128)::fmt real_number=from_function() write(fmt,'(F)')real_number numspace_f=len_trim(adjustl(fmt)) endfunction numspace_f end moduletest_numspace program main usetest_numspace integer::i1=123,i2=-221 real::r1=12.3,r2=33.2 character(len=128)::fmt1 write(fmt1,'(F)')r1 write(*,*)r1,len_trim(adjustl(fmt1)) write(*,*)i1,numspace(i1) write(*,*)r2,numspace(r2) write(*,*)numspace(min(i1,i2)) write(*,*)numspace(max(r1,r2)) end program ========================================================= 第二个例子, numspace ()完全都不能传递内部函数,外部函数没有试过。 ========================================================= program read_stream use module1 implicit none character (len=128)::sgyfile= 'xc_wtie1.sgy' integer (kind=8), parameter :: filesize=5835496 !byte integer (kind=4), parameter :: bytesPerSample=4 character (len=128)::iom= 'OK' integer :: ios,mark_pos=1,i,j,k,status,imin,imax real :: rmin,rmax integer (kind=1), dimension (3200):: textheader ! EBCDIC 字符 integer (kind=2), dimension (80,40):: textheader1 ! EBCDIC 字符 integer (kind=2), dimension (40,80):: textheader2 ! EBCDIC 字符 integer (kind=4), dimension (3):: binheader1 ! integer (kind=2), dimension (24):: binheader2 ! integer (kind=2), dimension (170):: binheader3 ! real (kind=4)::sampint integer (kind=4)::numsamps integer (kind=4)::numtraces real (kind=4), allocatable , dimension (:,:)::dataout integer (kind=2), allocatable , dimension (:,:)::traceheaders character (:), allocatable :: fmt !============================================================================== ! 打开 SEG-Y 文件 !============================================================================== open (7,file=sgyfile, access= 'stream' , ! 流访问 , 即按字节访问文件 form= 'unformatted' , CONVERT= 'BIG_ENDIAN' , !IEEE Big Endian iostat=ios, iomsg=iom) inquire (7,pos=mark_pos) print *, 'Stream Position:' ,mark_pos !============================================================================== ! step 1: 读卷头 !============================================================================== ! 读 EBCDIC 字符,个字节 read (7)textheader textheader1= reshape (textheader,(/80,40/)) ! 将有符号整数转化为无符号整数 ! 转换规则参考《深入理解计算机系统》第二章第二节 forall (i=1:80,j=1:40,textheader1(i,j) 0 ) textheader1(i,j)= textheader1(i,j) + 256 ! 因为有符号整数默认是用补码表示的 end forall textheader2= transpose (textheader1) ! 输出卷头 open (8,file= 'textheader_ebcdic.dat' ) open (9,file= 'textheader_ascii.dat' ) open (10,file= 'textheader.dat' ) do i=1,40 write (8, '(80i4)' )(textheader2(i,j),j=1,80) write (9, '(80i4)' )(E2A(textheader2(i,j)),j=1,80) write (10, '(80a)' )( achar (E2A(textheader2(i,j))),j=1,80) enddo close (8) close (9) close (10) inquire (7,pos=mark_pos) print *, 'Stream Position:' ,mark_pos !============================================================================== ! step 2: 读字节的二进制文件头 (老格式?新格式有变化) !============================================================================== ! 读个字节长度的二进制数据 , 只有前面字节的信息有用 read (7)binheader1 !write(*,*)(binheader1(i),i=1,3) read (7)binheader2 ! 含有采样间隔和采样点数的信息 !write(*,*)(binheader2(i),i=1,24) sampint = binheader2(3) / 1e6 numsamps = binheader2(5) numtraces =(filesize-3600)/(240+bytesPerSample*numsamps) write (*,9)numsamps,numtraces 9 format ( 'Number of Samples:' ,I6,/, 'Numberof traces:' ,I6) allocate (dataout(numsamps,numtraces),stat=status) !write(*,10)allocated(dataout),status !10 format('Allocated Status:',L2,I3) allocate (traceheaders(120,numtraces),stat=status) !write(*,10)allocated(traceheaders),status read (7)binheader3 ! withinusless information inquire (7,pos=mark_pos) print *, 'Stream Position:' ,mark_pos !============================================================================== ! step 3: 读每一道的数据 !============================================================================== ! 每一道都有一个道头 (240 字节 ) 和数据(采样点 *4 个字节)两部分组成 do i=1,numtraces read (7)traceheaders(:,i) !inquire(7,pos=mark_pos) !write(*,11)i,mark_pos !11 format('Trace ',I4,', Stream Position:',I10) read (7)dataout(:,i) enddo close (7) !============================================================================== ! step 4: 输出数据 !============================================================================== open (7,file= 'traceheader.dat' ) open (8,file= 'amplitude.dat' ) imin= minval (traceheaders) imax= maxval (traceheaders) !write(*,*)numspace(maxval(traceheaders)) ! 出错了, WHY??? do i=1, size (traceheaders,1) write (7,12)(traceheaders(i,j),j=1,numtraces) 12 format ( size (traceheaders,1)I max (numspace(imin),numspace(imax))+1) enddo deallocate (traceheaders) rmin= minval (dataout) rmax= maxval (dataout) do i=1,numsamps write (8,13)(dataout(i,j),j=1,numtraces) 13 format (numsampsF max (numspace(rmin),numspace(rmax)).3) enddo deallocate (dataout) close (7) close (8) end program read_stream module module1 implicit none integer , dimension (255):: E2A=(/ 1, 2, 3,156,9,134,127,151,141,142, 11, 12, 13, 14, 15, 16, 17, 18,19,157,133, 8,135, 24, 25,146,143, 28, 29, 30, 31,128,129,130,131,132,10,23, 27,136,137,138, 139,140, 5, 6,7, 144,145, 22,147,148,149,150, 4,152, 153,154,155, 20,21,158, 26, 32,160,161,162,163,164, 165,166,167,168,91, 46, 60, 40, 43, 33, 38,169,170, 171,172,173,174,175,176,177,93, 36, 42, 41, 59, 94, 45,47,178,179,180,181,182,183,184,185,124, 44, 37, 95, 62, 63,186,187,188,189,190,191,192,193,194, 96, 58, 35, 64, 39,61, 34, 195, 97, 98, 99,100,101,102, 103,104,105,196,197,198,199,200,201,202,106,107,108, 109,110,111,112,113,114,203,204,205,206,207, 208,209, 126,115,116,117,118,119,120,121,122,210,211,212,213, 214,215,216,217,218,219,220,221,222,223,224,225,226, 227,228,229,230,231,123, 65, 66, 67, 68, 69, 70,71, 72,73,232,233,234,235,236,237, 125, 74, 75, 76, 77, 78,79,80,81,82,238,239,240,241,242,243, 92,159, 83, 84, 85, 86, 87,88, 89, 90,244,245,246,247,248,249, 48,49, 50, 51,52, 53, 54, 55, 56, 57,250,251,252,253,254,255/) interface numspace module procedure numspace_i, numspace_r ! module procedure numspace_f end interface numspace contains subroutine printASCII() implicit none integer :: i do i=1,256 write (*,10)i,E2A(i), char (E2A(i)), achar (i-1) 10 format ( E2A( ,i3, )= ,i3, = ,A, ' , ACHAR=' ,A) enddo end subroutine printASCII integer function numspace_i(int_number) implicit none integer , intent (in)::int_number character (len=128):: fmt write (fmt, '(I)' )int_number numspace_i= len_trim ( adjustl (fmt)) end function numspace_i !BL integer function numspace_r(real_number) implicit none real , intent (in)::real_number character (len=128):: fmt write (fmt, '(F)' )real_number numspace_r= len_trim ( adjustl (fmt)) end function numspace_r !BL integer function numspace_f(from_function) implicit none interface function from_function() end function end interface real ::real_number character (len=128):: fmt real_number= from_function() write (fmt, '(F)' )real_number numspace_f= len_trim ( adjustl (fmt)) end function numspace_f ! character function numspace_i(int_number) ! implicit none ! integer,intent(in)::int_number ! character(len=128):: fmt ! write(fmt,'(I)')int_number ! write(numspace_i,'')=len_trim(adjustl(fmt)) ! end function numspace_i !!BL ! character function numspace_r(real_number) ! implicit none ! real,intent(in)::real_number ! character(len=128):: fmt ! write(fmt,'(F)')real_number ! numspace_r=len_trim(adjustl(fmt)) ! end function numspace_r end module module1
4184 次阅读|1 个评论
FORTRAN学习笔记(5): FORTRAN数组类型和存储结构
peluo 2014-3-16 16:01
FORTRAN中的数组声明可以有如下几种类型:Explicit-shape Array, Assumed-shape Array, Assumed-size Array, and Deferred-size Array Explicit-shape array: An explicit-shape array is declared with explicit values for the bounds in each dimension of the array(显式指定数组各维的上下界) . http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-1071BCFF-5A84-4CA9-B296-CB1BD5571777.htm#GUID-1071BCFF-5A84-4CA9-B296-CB1BD5571777 Both automatic array and adjustable array are explicit-shape arrays. Automatic Array: an automatic array is an explicit shape array that is a local variable. Automaticarrays are only allowed in function and subroutine subprograms, and are declared in the specification part of the subprogram. At least one bound of an automatic array must be a nonconstant specification expression. The bounds are determined when the subprogram is called. (自动数组是子程序的局地变量,在子程序内部声明,数组维数中至少一维由子程序的参数传递获得,其他维可在子程序中指定) Adjustable Array: a n adjustable array is an explicit shape array that is a dummy argument to a subprogram . At least one bound of an adjustable array must be a nonconstant specification expression. The bounds are determined when the subprogram is called. (局地可调数组是子程序的传递参数之一,同时可调数组维数中至少一维由子程序参数传递获得) Assumed-shape array : a n assumed-shape array is a dummy argument array that assumes the shape of its associated actual argument array(假定形状数组通常在子程序内部声明,指定维数/rank,但不指定维的大小 ) . http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-70941978-B44B-46CE-9156-67A02F23768E.htm#GUID-70941978-B44B-46CE-9156-67A02F23768E Assumed-size array : a n assumed-size array is a dummy argument array that assumes the size (only) of its associated actual argument array; the rank and extents can differ for the actual and dummy arrays(假定大小数组通常在子程序中声明,可以指定1维或多维的大小). http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-E094C9ED-8A70-4220-AA83-CBF957F7223C.htm#GUID-E094C9ED-8A70-4220-AA83-CBF957F7223C Deferred-shape array : a deferred-shape array is an array pointer or an allocatable array(递延形状数组指数组指针或可分配数组,数组的维数和各维大小在指针赋值时或使用allocate语句时获得). http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-885E4216-44C6-47B5-84D7-9F40CBF0008E.htm#GUID-885E4216-44C6-47B5-84D7-9F40CBF0008E 不同类型数组的存储结构不一样,比如Local Adjustable Array是stack array(栈型数组),而Deferred-shape array就是heap array (堆型数组)。stack的大小具有平台依赖性,因此程序中出现较大的局部数组时,可能会出现“segmentation fault”(即所谓的栈溢出,stack overflow),此时要么人为增加系统栈大小,要么将数组存储结构更改为堆结构。同时,两种数据结构在存取效率等多方面具有显著差异。 可参考: http://metman.info/blog/2013/08/12/fortransheng-ming-shu-zu-guo-da-wen-ti-jie-jue-ban-fa/ http://metman.info/blog/2013/05/31/segmentation-faultchan-sheng-gen-ben-yuan-yin/ http://bbs.csdn.net/topics/390147637
个人分类: Fortran|8389 次阅读|0 个评论
[转载]Arithmetic IF Statement
zhoufcumt 2013-11-19 09:48
转载自: http://blog.163.com/jey_df/blog/static/182550161201222132825185/ 这两天看Hwang的程序中看到这样一个语法: IF (3-N) 30,30,40 30 T2=T2+SQROOT(N-1)*SQROOT(N-2)*PBAR(INDXP(N-2,1))/SQROOT(2*N-3) 40 PBAR(INDXP(N,1))=SQROOT(2*N+1)*T2/(SQROOT(N)*SQROOT(N+1)) 这个IF语句完全不明白是什么意思,然后去询问了高手SnoopyZhao,给我了有建设意义的帮助。这个形式的语法叫Arithmetic IF Statement,三个标号,按顺序分别对应负、零和正的条件 Arithmetic IF Statement It is a three way branch statement of the form, IF( expression ) label1 , label2 , label3 Here expression is any expression producing a result of type INTEGER, REAL or DOUBLE PRECISION, and the three labels are statement labels of executable statements. If the value of the expression is negative, execution transfers to the statement labelled label1 . If the expression is zero, transfer is to the statement labelled label2 , and a positive result causes transfer to label3 . The same label can be repeated. This relic of the original Fortran has been redundant since the early 1960s when the logical IF and computed GOTO were introduced and it should be replaced by an equivalent CASEor IF construct.
个人分类: Fortran|1763 次阅读|0 个评论
[转载]Fortran文件函数:Open close write read
zhoufcumt 2013-11-18 21:54
转载地址: http://blog.163.com/jey_df/blog/static/182550161201221511219329/ http://blog.sciencenet.cn/blog-51026-622584.html 1.文件读取的概念: 读取:“顺序读取”和“直接读取”。 保存:“文本文件”和“二进制文件”。 二进制文件:读取快,因为保存格式和数据在内存中的保存方法一样,同时也节省空间。 ---------------------------------- 2. 文件的操作: ---------------------------------- open的使用 : 使用open命令打开文件之后,就可以对文件来做输入输出。 example: program ex0901 impicit none open(unit=10, file='hello.txt') ! 打开hello.txt文件, unit指定文件代码,file指定文件名称。 write(10, *) hello !在代码为10的文件中写入hello stop end program ex0901 open中有很多参数可以使用,详细如下: OPEN(UNIT=number, FILE='filename', FORM='...', STATUS='...', ACCESS='...', RECL=length, ERR=label, IOSTAT=iostat, BLANK='...', POSITION='...', ACTION=action, PAD='...', DELIM='...') UNIT= 'number': number必须是一个正整数,它可以使用变量或是常量来赋值。number最好避开1,2,5,6。因为2,6是默认的输出位置,也就是屏幕。1,5则是默认的输入位置,键盘。 FILE= 'filename': 指定要打开的文件名称,文件名要符合系统规定。windows下不区分大小写,unix下则会区分大小写,最好不要使用中文文件名。 FORM ='FORMATTED' OR 'UNFORMATTED' FORM字段只有两个值可以设置: FORM='FORMATTED' “文本文件”格式来保存 FORM='UNFORMATTED' “二进制文件”格式保存 这一栏不给定时候的默认值是: FORM='FORMATTED' STATUS ='NEW' or 'OLD' or 'SCRATCH' or 'UNKNOWN' 用来说明打开一个新的文件或已经存在的旧文件。 STATUS='NEW' 打开一个原本不存在的新文件 STATUS='OLD' 打开一个原来已经存在的文件 STATUS='REPLACE' 若文件已经存在则重新创建一次,原来的内容消失;若不存在则会创建新文件。 STATUS='SCRATCH' 表示要打开一个暂存文盘,这个时候可以不需要指定文件名称,也就是FILE这个一栏可以忽略。因为程序本身会自动取一个文件名,至于文件名是啥也不重要,因为暂存盘会在程序结束后自动删除。 STATUS='UNKNOWN' 由各编译器自定义。通常会同REPLACE的效果。 !这一栏不给定时,默认为STATUS='UNKNOWN'。 ACCESS ='SEQUENTIAL' or 'DIRECT' 设置读写文件的方法: ACCESS='SEQUENTIAL' 读写文件的操作会以“顺序”的方法来做读写,“顺序读取文件”。 ACCESS='DIRET' 读写文件的操作可以任意指定位置,“直接读取文件”。 !不赋值时候,默认为: ACCESS='SEQUENTIAL'。 RECL =length 在顺序读取文件中,RECL字段值用来设置一次可以读取多大容量的数据。 打开“直接读取文件”,RECL=length的length值是用来设置文件中每一个模块单元的分区长度。 length的单位在文本根式下为1个字符,也就是1 byte。在二进制格式下则由编译器自行决定,一般可能为1 byte (G77) 或 4 byte (Visual Fortran)。 ERR=LABEL 这个字段用来设置当文件打开发生错误时,程序会跳跃到LABEL所指定的行代码处来继续执行程序。 IOSTAT=var 这个字段会设置一个整数值给后面的整型变量,这是用来说明文件打开的状态,数值会有下面三种情况: var0 表示读取操作错误 var=0 表示读取操作正常 var0 表示文件终了 判断文件是否读取结束的方法: F90/95 大概这样 do read(..., iostat=ios,...) if (ios /= 0) exit end do F77 大概是这样 read(...,end=10,...) ... 10 .... 实例:读取如下文件 Fortran源码: ! ReadFileTOend.f90 ! ! FUNCTIONS: ! ReadFileTOend - Entry point of console application. ! program ReadFileTOend implicit none ! Variables real*8 longitude,latitude real*8 x,y,z integer*4 ios open(10,file='ins_error.dat',status='old') write(*,*)'Open file Success !' write(*,*)The first Method: do read(10,*,iostat=ios) longitude,latitude,x,y,z if(ios/=0) exit !0 表示正常读取文件 0 读取出错 0 读取结束 write(*,99)longitude,latitude,x,y,z enddo 99 format(5f15.8) rewind(10) write(*,*)The second Method: do read(10,*,end=100)longitude,latitude,x,y,z write(*,(5f15.8))longitude,latitude,x,y,z end do 100 continue ! Body of ReadFileTOend print *, 'Read file to the end!' end program ReadFileTOend 运行结果: 源代码下载: ReadFileTOend.rar BLANK ='NULL' or 'ZERO' 用来设置输入数字时,当所设置的格式字段中有空格存在时所代表的意义。 BLANK='NULL'时,空格代表没有东西。BLANK='ZERO'时,空格部分会自动以0代入。 以下是Fortran 90添加的功能: POSITION ='ASIS' or 'REWIND' or 'APPEND' 设置文件打开时候的读写位置: POSITION='ASIS' 表示文件打开时的读取的位置不特别指定,通常就是在文件的开头。是默认值。 POSITION='REWIND' 表示文件打开时的读取位置移到文件的开头。 POSITION='APPEND' 表示文件打开时的读取位置移到文件的结尾。 ACTION ='READ' or 'WRITE' or 'READWRITE' 设置打开文件的读写权限: ACTION='READWRITE' 表示所打开的文件可以用来读取及写入,这是默认值。 ACTION='READ' 表示所打开的文件只能用来读取数据。 ACTION='WRITE' 表示所打开的文件只能用来写入数据。 PAD ='YES' or 'NO' PAD='YES' 在格式化输入时,最前面的不足字段会自动以空格填满,默认值是PAD='YES'。 PAD='NO' 在格式化输入时,不足的字段不会自动以空格填满。 DELIM ='APOSTEROPHE' or 'QUOTE' or 'NONE' DELIM='NONE' 纯粹输出字符串内容 DELIM='QUOTE' 输出字符串内容会在前后加上 双引号 DELIM='APOSTEROPHE' 输出字符串内容会在前后加上单引号 ----------------------------------------- WRITE READ 的使用(详细): WRITE/READ(UNIT=number, FMT=format, NML=namelist, REC=record, IOSTAT=stat, ERR=errlabel, END=endlabel, ADVANCE=advance, SIZE=size) UNIT=number 指定read/write所使用的输入输出的位置。 FMT=format 指定输入输出格式的使用。 NML=namelist 指定读写某个NAMELIST的内容(后续介绍)。 REC=record 在直接读取文件中,设置所要读写的文件的模块位置。 IOSTAT=stat 会设置一个数值给在它后面的变量,用来说明文件的读写状态。 stat0 表示读取操作发生错误。 stat=0 表示读取操作正常。 stat0 表示文件终了。 ERR=errlabel 指定在读写过程中发生错误时,会转移到某个行代码来继续执行程序。 END=endlabel 指定在读写到文件末尾时,要转移到某个行代码来继续执行程序。 以下是fortran 90添加功能: ADVANCE='YES' or 'NO' 设置在文本格式下的顺序文件中,每一次的READ,WRITE命令完成后, 读写 位置会不会自动想下移动一行。 ADVANCE='YES' 是默认的状态,每读写一次会向下移动一行。 ADVANCE='NO' 会暂停自动换行的操作。 !使用这个字段时候一定要设置输出入格式,在屏幕输出时可以使用这个设置来控制write命令是否会自动换行。 SIZE=count 在ADVANCE='NO'时,才可以使用这个字段。它会把这一次输出入的字符数目设置给后面的整型变量。 ---------------------------------- 查询文件的状态INQUIRE: 在使用open打开文件的前后,都可以通过inquire命令来查询文件目前的情况,inquire命令中的各个字段和第一小节中open的字段很类似。 example: !检查某个程序是否存在 program ex0903 implicit none character(len=20) :: filename = ex0903.f90 logical alive inquire(file=filename, exist=alive) if(alive) then write (*, *) filename, exist. else write (*, *) filename, doesn't exist. end if stop edn program ex0903 详细介绍inquire的使用方法: INQUIRE(UNIT=number, FILE=filename, IOSTAT=stat, ERR=label, EXIST=exist, OPENED=opened, NUMBER=number, NAMED=named, ACCESS=access, SEQUENTIAL=sequential, DIRECT=direct, FORM=form, FORMATTED=formatted, UNFORMATTED=unformatted, RECL=recl) UNIT=number 文件代号 FILE=filename 文件名 IOSTAT=stat 查询文件读取情况,会设置一个整数给后面的变量: stat0 文件读取操作错误 stat=0 文件读取操作正常 stat0 文件终了 ERR=errlabel 发生错误时会转移到复制的代码行继续执行程序。 EXIST=exist 检查文件是否存在,返回布尔变量,真表示存在,假值表示不存在。 OPEND=opened 检查文件是否用已经用open打开,返回布尔变量,真表示已经打开,假表示尚未打开。 NUMBER=number 用文件名来查询这个文件所给定的代码。 NAMED=named 查询文件是否取了名字,也就是检查文件是否为临时保存盘,返回值为逻辑数。 ACCESS=access 检查文件的读取格式,返回一个字符串,可以是: 'SEQUENTIAL' 代表文件使用顺序读取格式 'DIRECT' 代表文件使用直接读取格式 'UNDEFINED' 代表没有定义 SEQUENTIAL=sequential 查看文件是否使用顺序格式,会返回一个字符串,可以是: 'YES' 代表文件是顺序读取文件 'NO' 代表文件不是顺序读取文件 'UNKNOWN' 代表不知道 DIRECT=direct 查看文件是否使用直接格式,会返回一个字符串,可以是: 'YES' 文件是直接读取文件 'NO' 文件是非直接读取文件 'UNKNOWN' 代表不知道 FORM=form 查看文件的保存方法,返回字符串,可以是: 'FORMATTED' 打开的是文本文件 'UNFORMATTED' 打开的是二进制文件 'UNDEFINED' 没有定义 FORMATTED=fmt 查看文件是否是文本文件,返回字符串,可以是: 'YES' 本文件是文本文件 'NO' 本文件非文本文件 'UNDEFINED' 无法判断 UNFORMATTED=fmt 查看文件是否是二进制文件,返回字符串,可以是: 'YES' 本文件是二进制文件 'NO' 本文件非二进制文件 'UNKNOWN' 无法判断 RECL=length 返回open文件时recl栏的设置值。 NEXTREC=nr 返回下一次文件读写的位置。 BLANK=blank 返回值是字符串,用来查看open文件时的blank参数所给定的字符串值。 以下是fortran 90的添加功能: POSITION=position 返回打开文件时position字段所给定的字符串, 可能是'REWIND', 'APPEND', 'ASIS', 'UNDEFINED' ACTION=action 返回打开文件时action 字段所赋值的字符串,可能是'READ', 'WRITE', 'READWRITE'。 READ=read 返回字符串,检查文件是否为只读文件: 'YES' 文件是只读的 'NO' 文件不是只读的 'UNKNOWN' 无法判断 WRITE=write 返回一个字符串,检查文件是否可写入: 'YES' 文件可以写入 'NO' 文件不可以写入 'UNKNOWN' 无法判定 READWRITE=readwrite 返回一个字符串,检查文件是否可以同时读及写: 'YES' 文件可以同时读写 'NO' 文件不可以同时读写 'UNKNOWN' 无法判定 DELIM=delim 返回打开文件时,DELIM字段所设置的字符串,返回值可以是: 'APOSTROPHE', 'QUOTE', 'NONE', 'UNDEFINED' PAD=pad 返回打开文件时PAD字段所设置的字符串,返回值可以是:'YES', 'NO'。 其他文件运行命令: 1.BACKSPACE (UNIT=number, ERR=errlabel, IOSTAT=iostat) 把文件读写位置退回一步。 2.ENDFILE (UNNIT=number, ERR=errlabel, IOSTAT=iostat)使用这个命令会把目前文件的读写位置变成文件的结尾。 3.REWIND (UNIT=number, ERR=errlabel, IOSTAT=iostat)把文件的读写位置倒回到文件开头。 4.CLOSE (UNIT=number, STATUS=string, ERR=errlabel, IOSTAT=) 把文件关闭,不要进行读写操作。 5.STAT ='KEEP' 会在文件关闭后,保留这个文件。是默认状态。 6.STAT ='DELETE' 在文件关闭后,消除这个文件。 7.顺序文件 (SEQUENTIAL): 在读写时,不能任意赋值到文件的某个位置读写数据,只能从开头开始一步步向下进行。在改变文件读写位置时,只能一步步地退,或是直接移回到文件开头。 直接访问文件:把文件的空间,内容,事先分区成好几个同样大小的小模块,这些模块会自动安顺序编号。读写文件时,要先赋值文件读写位置在第几个模块,再进行读写的工作。直接访问文件可以任意到文件的任何一个地方来读写。在使用直接访问文件时,要小心使用endfile命令,使用这个命令会把目前所在的文件位置之后的数据都清除掉。 8.二进制文件 的操作 :使用二进制文件来做直接读取时,open命令中的recl字段所设置的整数n值所代表的大小会随编译器不同而改变。每个编译器应该都可以经过设置来改变recl字段的单位大小。二进制文件没有必要在数据之间用区分符号来增加文件的可读性,因为二进制文件本身就没有可读性。二进制文件是直接把内存的二进制数据写入文件,就没有所谓的格式化输入/出存在。存放“精确”及“大量”的数据时,使用二进制文件是比较好的选择。二进制文件也可以使用顺序格式来操作,顺序格式下显示来的二进制文件,每个数据的前后都会被编译器补上一些额外的信息,所生成的文件不太容易被其他程序读取。 关于以上文件操作详细见《fortran 95程序设计》9-3~9-5。 ------------------------------------------- !程序结束时候会自动关闭文件,不过最好要养成自己关闭文件的习惯。 !在读文件的时候要懂得略掉一些没有必要的数据,如文件中的注释行。 !自由格式的数据文件读取(可以先读入前面的判断字符,结合select case或其他方法判断读入的数据) !在open,read,write时使用不同的unit值,就可以打开多个文件。最好不要同时打开很多个文件。 Internal File (内部文件) 使用写入文件的方法,把数据写到一个字符串变量中。 example: a=2 b=3 character (len=20) :: string write (unit=string, fmt=(I2,'+',I2,'=',I2)) a, b, a+b !把字符串变量当作输出的目的。 write(*, *) string 结果: 2+ 3= 5 还可以经过read命令从字符串读入数据: integer :: a character (len=20) :: string=123 read(string, *) a write(*, *) a 在某些情况下需要使用内部文件来设置数据: 使用read命令从键盘输入数据时,如果用户输入错误的数据,会导致死机。如需要输入整数时却输入英文字母,就可能会死机。比较好的处理办法是,程序先暂时把数据当作字符串读入,检查字符串中是否含有不合理的字符,如果字符串中都是0~9的数字字符,就把字符串转成整数,不然就请用户在输入一次。 内部文件还可应用在动态改变输出格式,输出格式可以事先存放在字符串中,程序进行时,动态改变字符串内容就可以改变输出格式。(见书P263) NAMELIST: NAMELIST是很特殊的输入/输出方法,收录在f90标准当中,f90中有统一NAMELIST的格式。 NAMELIST可以把一组相关变量封装在一起,输入/出这一组变量时,只要在write/read中的NML字段赋值使用哪一个NAMELIST就行了。 example: program ex0918 implicit none integer :: a = 1, b = 2, c= 3 namelist /na/ a,b,c write(*,nml=na) stop end program ex0918 NA A = 1, B = 2, C = 3, / 程序中把a,b,c这三个变量放在名字叫做na的namelist中。namelist也算是声明的一部分,必须编写在程序执行命令前面。 NAMELIST的语法 很类似COMMON,不过使用namelist时一定要取名字: namelist /nl_name/ var1, var2, ... !后面的变量会放在nl_name这个namelist中。 封装好namelist后,在write的NML字段中指名要输出哪一个namelist,就可以把namelist中的变量全部输出。 write(*,nml=na) !输出na这个namelist 输出namelist时候不能赋值输出格式,f90标准中规定输出namelist时首先会输出符号,后面紧接着这个namelist的名字。接着会根据顺序输出变量的名称,等号以及内容,变量之间用空格或逗号来做分隔,最后使用除号来作结束。 至于每个数值内容会使用何种格式输出由编译器自行决定。 NAMELIST也可以用来输入数据,不过通常都会用来读取文件, 不会用在键盘输入。输入格式需要按照前面的格式。na ....../ 不需要按照变量顺序输入,程序会自动按照变量名称来设置数值。变量甚至可以重复输入,不过变量会得到最后一次设置的数值。 namelist通常使用在文本文件的输入/输出中,使用read从文件中读取数据时,会自动从目前的位置向下寻找存放namelist的地方。 example: program ex0920 implicit none integer :: a(3) namelist /na/ a open(10, file=ex0920.txt) read(10, nml=na) write(*, (3I2)) a stop end program 输入文件的内容如下: happy birthday na a = 1,2,3/ 程序打开时,读写位置在文件的开头,read命令会自动向下寻找na这个namelist的存放位置来读取数据,这边可以看到namelist处理数组的方法,它会在等号后面根据顺序显示数组内容。
个人分类: Fortran|6440 次阅读|0 个评论
Fortran 格式化输出矩阵
zhoufcumt 2013-11-9 22:30
效果图: 写代码的同学都对矩阵格式化输出有点头大,不论是Fortran、 C++、C或者其它语言,本程序提供一个实例,提供方便通用的矩阵格式化输出,本实例是fortran 90语言书写,其它语言可参考本实例改写对应的模版。 代码和验证例子附件: test.f90 wrtmat.f90
个人分类: Fortran|15484 次阅读|0 个评论
[转载]fortran中输出不换行
zhoufcumt 2013-11-9 22:20
转载自: http://blog.sciencenet.cn/blog-47991-411109.html 三种方法: write(3,'(1x,f8.2 ,$)’) x write(3,'(1x,f8.2\)')x write(*,'(1x,f12.5)',advance='no') x
个人分类: Fortran|6967 次阅读|0 个评论
Fortran 90 实现 Vondrak 滤波
zhoufcumt 2013-11-2 22:43
捷克天文学家J. Vondrak提出了一种能够在未知拟合函数形式的情况下,对测量资料进行合理平滑的方法,有时也称之为最优平滑法。这种方法还具有较好的滤波性能,目前已在国内外天文地球动力学等领域得到了广泛有效的应用。 源码在Linux64和Win64下编译通过,验证实际数据通过,对Vondrak滤波感兴趣,但苦于无参考的同学可以发邮件从zhouforme@cgu.org.cn获取源码,下一步将实现交叉证认法(Cross-validation)选取平滑因子,同时改善Vondrak滤波的端部效应问题。
个人分类: Fortran|4530 次阅读|0 个评论
[转载]Fortran中的index函数
zhoufcumt 2013-10-27 22:28
http://wangshunxi1986.blog.163.com/blog/static/234055520095188458361/ 目的:返回substring在string中出现的位置参数类型和属性: STRING:必须是字符型 SUBSTRING:必须是字符型 BACK:可选的,逻辑型 结果类型和属性: 默认是整数型返回结果 如果没有出现BACK选项或者BACK的值是.FALSE.,结果是I的最小值,其中I满足下式STRING (I : I + LEN (SUBSTRING) - 1) = SUBSTRING 如果I不存在则返回零值。如果LEN (STRING) LEN (SUBSTRING)也返回零值。如果LEN (SUBSTRING) = 0.返回1. 如果BACK的值为 .TRUE., 结果是I的最大值,其中I满足STRING (I : I + LEN (SUBSTRING) - 1) = SUBSTRING ,I不存在时返回0。如果LEN (STRING) LEN (SUBSTRING)返回0 ,如果 LEN (SUBSTRING) = 0返回LEN (STRING) + 1 例子: INDEX ('FORTRAN', 'R', BACK = .TRUE.) 返回 5. INDEX ('FORTRAN', 'R') 返回 3
个人分类: Fortran|13643 次阅读|0 个评论
Linux下 Fortran 90 不同目录的modules和主程序的编译链接
热度 2 zhoufcumt 2013-10-11 07:59
昨天在Linux下编译fortran90的modules的时候出现问题,比如我在main目录下有mod和src两个目录,mod目录下test1.f90 这个module,src目录下有test2.f90这个module,当然多个modules也类似(下面的过程就需要写makefile来实现,更方便快捷),src有个test_src目录,里面有test.f90主程序,正确编译链接除了要加 use xx (xx是module名)。刚开始我的编译链接流程是这样的: 1. 进入mod目录: ifort -free -c test1.f90,ar rv libtest1.a test1.o 2. 进入src目录:ifort -free -I../mod/ -c test2.f90,ar rv libtest2.a test2.o (test2要用到test1 module) 前两步除了生成库文件,object文件(.o),还会生成 .mod文件。 3. 进入test_src目录:ifort -free test_src.f90 -I../ -I../../mod/ -o test_src (-I带相对路径是指定 .mod所在目录) 这样的编译结果提示 undefined reference to...... 网上的解决方案有个是把.mod拷贝到和主程序一个目录,其实和我的做法是一样的,就是确定好路径而已。 当用ifort -free test_src.f90 ../libtest2.a ../../mod/libtest1.a -o test_src,提示 Error in opening the compiled module file, check INCLUDE paths 最后的解决方案是:ifort -free test_src.f90 ../libtest2.a ../../mod/libtest1.a -I../ -I../../mod/ -o test_src,既要指定.mod所在的目录,又要指定对应的静态库,刚开始理解上有个误区,以为有了.mod就不用静态库了,其实不然,mod目录下的modules还要生成静态库,而且主程序链接生成可执行模块的时候还需要这个静态库。 NOTE: -I../ -I../../mod/ 这两个的顺序可以互换,但是 ../libtest2.a ../../mod/libtest1.a不能互换,否则出错,因为libtest2.a依赖于libtest1.a。
个人分类: Fortran|14120 次阅读|2 个评论
[转载]fortran读文本文件—无法估计数据数目问题
zhoufcumt 2013-10-10 08:23
转载自: http://blog.csdn.net/dajuan1989/article/details/11797081
个人分类: Fortran|1510 次阅读|0 个评论
Fortran学习笔记(4): 伪随机数生成方法
peluo 2013-10-7 18:42
rand, ran, irand, srand 这四个函数是GNU Fortran 95为了与GNU Fortran 77相兼容而内嵌的与伪随机数生成相关的函数,都是function。 rand(x): 按照均匀分布,生成0到1之间的伪随机数或伪随机数组。参数x为real或者real型的数组;即x~U(0, 1) ran(x): 是rand的别名函数 irand(): 按照均匀分布,生成0到系统最大整数之间的伪随机数。 srand(): 随机数生成器的初始化函数 Ref: http://gcc.gnu.org/onlinedocs/gfortran/RAND.html#RAND http://gcc.gnu.org/onlinedocs/gfortran/RAN.html#RAN http://gcc.gnu.org/onlinedocs/gfortran/IRAND.html#IRAND http://gcc.gnu.org/onlinedocs/gfortran/SRAND.html#SRAND random_number, random_seed 这两个函数是GNU Fortran 95及其后新标准中内嵌的与伪随机数生成相关的函数,都是subroutine。 random_number(x):与rand和ran的功能相似,按照均匀分布生成0到1之间的伪随机数或伪随机数组。语法为call random_number(x) random_seed():语法为call random_seed( ),该函数的三个参数是可选参数,当直接调用random_seed()是将随机数生成器初始化为默认状态;size和get为输出参数(intent(out)), put为输入参数(intent(in))。 Ref: http://gcc.gnu.org/onlinedocs/gfortran/RANDOM_005fNUMBER.html#RANDOM_005fNUMBER http://gcc.gnu.org/onlinedocs/gfortran/RANDOM_005fSEED.html#RANDOM_005fSEED 由U(0, 1)随机数产生N(0, 1)和N(μ, σ 2 )随机数 假设X 1 ~U(0, 1), X 2 ~U(0, 1), 且X 1 和X 2 相互独立,则根据Box-Muller变换有如下关系: ~N(0, 1) ~N(0, 1) 即Y 1 和Y 2 都属于标准正态分布,且相互独立。 由标准正态分布N(0, 1)产生任意正态分布N(μ, σ 2 )就很简单了,只需要指定均值μ和标准差σ两个参数就可以了。 Fortran程序示例: ************************************** call random_seed() call random_number(x1) call random_seed() call random_number(x2) y=sqrt(-2*log(x1))*cos(2*3.141593*x2) **************************************** Ref: Box-Muller变换 http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform 侯文, 2005. 常用概率分布间的关系 http://wenku.baidu.com/view/37ccbacf0508763231121279 陶会强, 2011. 常 用 概 率 分 布 之 间 的 关 系 及 应 用 研 究 http://wenku.baidu.com/view/ab62251c0b4e767f5acfce3c.html
个人分类: Fortran|15567 次阅读|0 个评论
[转载]GLONASS Frequency Channels
zhoufcumt 2013-10-7 09:13
对应的C++、C、Fortran、Python、Matlab源码可从 http://geodesy.cn/glonass/index.xml 处下载。 GLONASS Frequency Channels (GFC) are generated using data from official information of GLONASS. This file constains all latest data in the form of a set of date and frequency channels of 24 satellites. All satellites' frequency channels remain unchanged until the date in next set. It's not available if a satellite frequency channel is 99.
个人分类: 空间大地测量相关软件|2169 次阅读|0 个评论
Install Intel Fortran on Ubuntu system
热度 1 shanbin 2013-7-10 14:55
Step 1. Google the 'non-commerical intel fortran compiler' tofind the install package of Intel fortran. The download link on thewebsit ( http://software.intel.com/en-us/non-commercial-software-development ). Step 2. Fillyour information to obtain anemail which includes the serial number and filedownload link. Download the files. Step 3. unpack the install filewith the command tar-xzvf ....tar.gz Step 4. installseveral probably necessary sources for intalling fortran.( During my install process, g++ isasked .) sudo apt-get install build-essential sudo apt-get install libstdc++5 apt-get install gcc apt-get install g++ if you want install 32-bit compiler on 64-bit PC, ohter sources maybe needed. Atfirst: apt-get install ia32-libs Then you can install others sources: apt-get install libstdc++5 apt-getinstall lib32stdc++6 apt-get install libc6-dev-i386 apt-get install gcc-multilib apt-get install g++-multilib Moreover,the new version of ubuntu may not include the libstdc++5. We canuse the source of debian. http://packages.debian.org/stable/base/libstdc++5 Download package of deb in the version of i386 andamd64. Come to the directory which include the package of deb.Install package of amd6 4. dpkg -i libstdc++5_xxxxx_amd64.deb Itis more complex for the package of i386, because it may recover thelibrary of 64-bit. So you sould better extract the packagemanually. dpkg –extract libstdc++_xxxxx_i386.deb ./ Then copy thefile 'libstdc++.so.5.xxx' in the directory of lib to the 32-bitlibrary, and link it with libstdc++.so.5. cd usr/lib cp libstdc++.so.5.xxx /usr/lib32 cd/usr/lib32 ln -s libstdc++.so.5.xxx libstdc++.so.5 Step 5. run the file install.sh to install intelfortran. In this process, you must input the serial numer. Look theinformation carefully during this process. sudo ./install.sh Step 6. Source the variables with bash command which will be listedon the screen. Until here,the install process is finished.You can check the install directory with command 'which ifort',check the verison of intel fortran with 'ifort-v'. for64-bit source /opt/intel/Compiler/11.x/xxx/bin/ifortvars.shintel64 ifort –help for 32-bit source /opt/intel/Compiler/11.x/xxx/bin/ifortvars.shia32 ifort –help (/opt/intel/ may be changed to your installdirectory) Step 7. If you do not want to set the enviroment variableseverytime when you open a new terminal, you should better modifythe file .bashrc in your home directory. You need to open the.bashrc file, and all the below command in the last line. source / install-directory /intel/Compiler/11.x/xxx/bin/ifortvars.shintel64 (the command in step 6). Actually, during my install process, I open the .bashrc file athome directory, and add a path below the lastline PATH="$HOME/intel/bin:$PATH" After that you can compile your fortran programwith the most simple command: ifort -o execute-file language.f Sometimes, there will be an unpredicted error (e.g. segmentation fault...) Since my desktop is a 64 bit PC, I don't install the 32 bit library. But actually, I sould better also intall package listed below apt-get install ia32-libs (only needed if you install the 32bit compiler) apt-get install openjdk-6-jre-headless
个人分类: Linux学习|5699 次阅读|2 个评论
Fortran学习笔记(1)
热度 1 peluo 2013-6-8 10:16
===============================注================================= 大学期间一直没有接触过fortran,研究生阶段开始零散的写过一些fortran小程序,主要是为了完成自己的工作,但从来还没有完整地阅读一本有关fortran程序设计的参考书。日前,决定系统的学习fortran程序设计,先读由彭国伦编著的《Fortran 95程序设计》,并将其中的重要内容整理成学习笔记,每篇10个要点。 ================================================================== 1、fortran程序书写格式 fortran程序有两种书写格式,在fortran 90之前采用比较老一些的格式,叫做固定格式(fixed format),在fortran 90之后采用新的更加人性化的格式,叫做自由格式(free format)。 固定格式如下: 123456*******************(后省略至72字符) 其中第1-5个字符有特殊的意义,有3中情况(1)第1个字符为C,就表示改行为注释(comment);(2)第1-5个字符如果是数字,就表示为从该行开始的程序段分配的程序段编码;(3)如果前两种case都不是,那这5个字符只能是空格。第6个字符也有特殊的用途,专门用来作为上下行之间的连接符的,在该字符位置上只要是除0和空格之外的任何字符,就表示上下行之间是连接关系,编译器当成一行程序来处理。第7到第72个字符是程序代码的编写区域,超过72个字符后面的所有都将被编译器忽略,甚至造成编译器报错。这固定格式真是让人汗颜啊,所有的位置都规定的死死的。 自由格式真的就自由多了,没有再规定这些位置了,每行可以编写132个字符,代码的起始位置可以从这132个字符的任意位置开始,自由格式中用!表示注释,上下行的连接用来完成(即在上一行的末尾处用,或者在上一行末尾和下一行开头同时使用,如此就表示行连接)。 2、fortran程序基本构成 program test *** end program test 3、fortran数据类型 包括integer,real,complex,character和logical。 4、frotran数学运算符 包括:加(+),减(-),乘(*),除(/),幂(**)以及表示运算优先级的括号。 5、frotran变量申明 基本格式为:integer::a, b, c 6、frotran输入 用read函数,基本格式为:read(unit=*,fmt=*,access=*……) 7、fortran输出 用write函数,基本格式为:write(unit=*,fmt=*)输出内容。可以省略unit=以及fmt=这些字符。 8、fortran格式化输出 定义格式化输出的格式需要用(),常用的格式化输出格式有I(整型),F(浮点型),E(科学计数法),A(字符串),L(逻辑型),G(所有类型的数据),X(移位) 用法为: (1)(Iw ):表示以w宽度输出整型数据,如果指定.m,就表示至少输出m个数字; (2) (Fw.d): 表示以w宽度输出浮点型数据,且小数部分占d位; (3) (Ew.d ): 表示以科学计数法输出浮点型数据,小数部分占d位,如果指定Ee,就表示指数部分至少输出e位; (4) (Aw): 表示以w宽度输出字符串 (5) (Lw): 表示以w宽度输出逻辑型数据,比如.true. (6) (Gw.d ): 可以输出包括字符串、整型、浮点型和逻辑型等多种类型数据,d不能省略,可以随意指定。 换行用/,不换行用\或者advance='no' 9、IMPLICIT fortran标准中可以隐式地的进行变量申明,即使不显式地申明变量,编译器也能自动根据变量名的首字符来确定变量类型(首字符为I、J、K、L、M、N的变量自动识别为整型变量,其他均自动识别为浮点型变量)并通过编译。但如果在程序PROGRAM的下一行(并且只能在这一行)中使用IMPLICIT NONE,来指定必须显式地申明变量,编译器就不会自动识别变量,可以避免很多bug的发生。 10、PARAMETER常数申明 parameter(pi=3.14159) real, parameter::pi=3.14159
个人分类: Fortran|5225 次阅读|1 个评论
Sublime编译C/C++、Fortran、Python、Makefile、Matlab等的效
热度 4 zhoufcumt 2013-6-1 11:14
Sublime的Ctrl+B快捷键是编译过程,Ctrl+Shilt+B是运行过程。个人感觉科研必须要编程,因此选择一门好的编程语言,一个好的IDE,另外加上一个好的键鼠套装,外加一个好的显示器,这样的配置后的编程会是一个很愉悦的体验过程,大家在路上喜欢看美女,编程上当然也需要好的视觉感受,那么请选择Sublime!举几个小例子提升下大家用这个IDE的心情。 编译运行python效果图: 编译运行C效果图: 编译运行C++效果图: 编译运行fortran效果图: 编译运行Makefile效果图: Matlab效果图: 当然了无所不能的sublime还可以写C#,由于本人用的不多,所以留感兴趣的各位体验吧。 Wish you have a good experience with Sublime Text 2. Good Luck. Feng Zhou do the work above.
个人分类: LINUX|14402 次阅读|4 个评论
Abaqus 6.12下Fortran编译器的配置(VS 2010)
热度 3 epistemer 2013-5-10 18:28
目前 Abaqus 的最新版本已经是 6.12-1,Intel Fortran 编译器的最新版本也已经到了 IntelParallel Studio XE 2013 Fortran Compiler, visual studio 的版本也有 2012 了。 但是根据: http://www.hiyip.com/?p=321 的介绍, VS2012 不支持 Abaqus6.12-1 ,这也难怪, VS 2012 发布的时间要比 Abaqus6.12 的晚, Abaqus 支持的最高 VS 版本是 VS2010 。 目前流行的搭配是: Abaqus 6.9+VS2005+Intel Fortran 9.1/10.0/10.1 Abaqus 6.10/6.11/6.12+VS2008+Intel Fortran 10.1 本文想要实现的是 将 Abaqus6.12-1 与 Intel Fortran Compiler XE 2013 以及 visualstudio 2010 连接起来,以实现在 Abaqus6.12-1 中调用 subroutine 子程序。 操作系统: win 7 x64 ; CPUintel i3 Abaqus 版本: Abaqus6.12-1 x64 Fortran 版本: Intel Parallel Studio XE 2013 Intel CompilerXE VS 版本: Visual studio 2010 ultimate en ( 1 ) Fortran Intel Fortran Compiler XE 2013 的 ifortvars.bat 批处理文件与 10.1 有较大的不同,在调用 ifortvars.bat 时,需要设定两个参数: 1-arch ; 2-vs ,第一个参数为系统架构 ,第二个参数为 vs 的版本。 格式为: ifortvas.bat arch 在我的电脑上,提供两种模式: C:\Windows\SysWOW64\cmd.exe /E:ON /V:ON /K C:\Program Files (x86)\Intel\Composer XE 2013\bin\ipsxe-comp-vars.bat ia32 vs2010 C:\Windows\SysWOW64\cmd.exe /E:ON /V:ON /K C:\Program Files (x86)\Intel\Composer XE 2013\bin\ipsxe-comp-vars.bat intel64 vs2010 可以查看 开始菜单 - 所有程序 -IntelParallel Studio XE 2013 -CommandPrompt-Parallel Studio XE with Intel Compiler XE v13.0-{IA-32mode ; intel 64 mode ; } 鼠标右击查看属性,从目标中可以看到以上调用方式。 在本系统下,选择 64 位系统 archtechture ,调用方式为: ifortvas.bat intel64 vs2010 即系统架构为 intel64 , vs 版本为 vs2010 。。。 ( 2 ) VS 2010 VS2010 的 vcvarsall.bat 批处理文件在调用时,也需要指定参数 同样,通过 commandprompt 中可以看到有如下几种参数调用方式。。。。 %comspec% /k C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat x86 %comspec% /k C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat x86_ia64 %comspec% /k C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat x86_amd64 %comspec% /k C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat amd64 本文选择第二种,即 x86_ia64 ( 3 ) Abaqus6.12-1 的设置 上面废话了那么多,现在才是正题 记事本打开批处理文件: abq6121.bat 我的电脑上默认位置为: C:\SIMULIA\Abaqus\Commands\abq6121.bat 将 abq6121.bat 中的内容由 @echo off C:\SIMULIA\Abaqus\6.12-1\code\bin\abq6121.exe %* 修改为 call C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat x86_ia64 call C:\Program Files (x86)\Intel\Composer XE 2013\bin\ifortvars.bat intel64 vs2010 @echo off C:\SIMULIA\Abaqus\6.12-1\code\bin\abq6121.exe %* 然后通过 : 程序 -SIMULIA FLEXnet Licensing-Licensing utilities 打开 lmtools , 启动 Abaqus 的 License 再运行 AbaqusVerification 查看 verify.log 文件 Abaqus Product Install Verification... Fri May 10 18:08:08 2013 Running system requirement checks. Requirement: Windows Server 2003, Windows Server 2008,Windows Server 2008 R2, Windows HPC Server2008, Windows XP, Windows Vista, or Windows 7 Product: All Abaqus Products Status: Pass - Found Windows 7 UltimateService Pack 1 (Build 7601) (x64). Requirement: Microsoft Visual C++ 9.0 (2008), or 10.0(2010) Product: Abaqus make utility with C++ and Abaquswith user subroutines Status: Pass - Found Microsoft Visual C++10.0.30319.1. Requirement: Intel Fortran Compiler 10.1 or 11.1 Product: Abaqus make utility with Fortran andAbaqus with user subroutines Status: Warning - Found Intel Fortran Compiler13.0, which is newer than the published Abaqusrequirement at the time this version was released. Later minor releases of a published requirement are generallycompatible with Abaqus. Please check the system requirementsweb page for the latest information on supportedconfigurations. Requirement: MS-MPI 2.0, 3.0 or greater Product: Abaqus analyses using MPI-basedparallelization and Abaqus/CFD Status: Pass - Found MS-MPI 3.0.2369.0. Requirement: Internet Explorer 8.0 or 9.0 or greater,Firefox 3.0 or 3.5 or greater Product: Abaqus Documentation Status: Pass - Found Internet Explorer8.0.7601.17514 WARNING: Verification for all selectedproducts will be attempted, but some products may fail because of the indicated system deficiencies. Found C:\D\AbaqusTemp\verify. All verification files will reside in this directory. 上述方法为解决方案之一,我试过是行得通的 , 也可以不用改 abq6121.bat 批处理文件,只需在启动目标中加入 fortran 和 vs 的 bat ,不过同样要传递参数。 有什么问题大家可以留言,一起讨论看该怎么解决。 ------------------------------------------------------------------------ 参考内容: 关于 ABAQUS 和 User Subroutine 的若干问题 http://www.hiyip.com/?p=321 succeed in installing ABAQUS subroutine http://polymerfem.com/showthread.php?910-succeed-in-installing-ABAQUS-subroutine How to link the fortran compiler with Abaqus? http://imechanica.org/node/11219 abaqus verification------link abaqus with fortran http://blog.renren.com/share/1393531270/7448777700 abaqus 子程序安装过程详解 http://hi.baidu.com/xiaomingbing/item/5de5a5f9f8e5540885d27800 ABAQUS 子程序验证时 Microsoft Visual C++ 无法通过问题的解决【原创,已测试】 http://blog.163.com/scut_yumin/blog/static/1404680092010102974120563/ Abaqus user subroutine in c++ running on 64 bit machine http://polymerfem.com/showthread.php?2168-Abaqus-user-subroutine-in-c-running-on-64-bit-machine
23508 次阅读|3 个评论
[转载]Fortran教程
热度 1 lingtingjiangla 2013-5-8 10:58
http://micro.ustc.edu.cn/fortran/ZJDing/
个人分类: 编程|6567 次阅读|2 个评论
谁有pgi fortran 编译器的破解文件啊
热度 1 smalljasmine 2013-5-2 12:11
谁有pgi fortran 编译器的破解文件啊,发我邮箱 814774381@qq.com ,不胜感激 ps:我已经有了,10.3的
5874 次阅读|1 个评论
[转载]Fortran语言中contains的妙用
runningabc 2013-4-18 11:20
contains是Fortran 90中新增的一个语法功能,在module或者subroutine中可以用contains来包含其他的module或者subroutine。这里我们来介绍一个contains的一种用法,用它来对subroutine进行“包装”,从而实现我们的一些特殊目的。 现在,假设我们通过某种方式得到一段Fortran源代码,我们要用到这个源代码中的一个subroutine,但是这个subroutine的输入参数中包含有一个外部的subroutine(或function),而我们想传入的subroutine的形参和该外部subroutine所应该具有的形参不同,此时就可以用contains来对我们自己的subroutine进行一个“包装”,使得包装后的subroutine调用方式满足要求。 例如,下面是一段用数值方法求函数导数的Fortran代码,其中fx是传入的外部子程序,且调用方式为call fx(real*8 x,real*8 y)。 !***************************************************************** subroutine cal_dydx(fx,x,det,dydx) implicit none real(8) :: x,det,dydx,y0,y1 external :: fx !---------------------------------------------------------------- ! purpose ! 这是一个向前差分方法求导数的子程序 ! in ! fx: ! 引入的外部子程序,调用方式如下: ! call fx(x,y) ! x: ! 自变量 ! det: ! 差分步长(一般取为sqrt(1D-15~1D-16)) ! out ! dydx: ! 返回的差分导数 !---------------------------------------------------------------- call fx(x,y0) call fx(x+det,y1) dydx = (y1-y0)/det return end subroutine !***************************************************************** 现在,我们想用这个cal_dydx来计算二元函数f(x1,x2)=sqrt(x1*x1+x2*x2)关于x1在x1=1,x2=1时的偏导数值,计算f(x1,x2)的代码为: !***************************************************************** subroutine fx1(x1,x2,fx) implicit none real(8) :: x1,x2,fx !---------------------------------------------------------------- ! 这是一个二元函数,x1,x2是自变量,fx是返回的函数值 !---------------------------------------------------------------- fx = sqrt(x1*x1+x2*x2) return end subroutine !***************************************************************** 显然,以上fx1的调用方式并不满足cal_dydx中传入的外部子程序fx的调用方式,但是,在主程序中通过contains的方法可以让我们对fx1进行“包装”,完整程序如下: !***************************************************************** program test_contains implicit none real(8) :: x1,x2,dydx !---------------------------------------------------------------- x1 = 1D0 x2 = 1D0 !调用cal_dydx来计算差分导数(事实上求的是偏导数) call cal_dydx(fx,x1,1D-7,dydx) write(*,*) df/dx =,dydx stop contains subroutine fx(x,y) implicit none real(8) :: x,y !-------------------------- !利用contains来实现对fx1(x1,x2,fx)子程序的包装使得其形式 !上能够满足cal_dydx的调用 !-------------------------- call fx1(x,x2,y) return end subroutine end program !***************************************************************** subroutine fx1(x1,x2,fx) implicit none real(8) :: x1,x2,fx !---------------------------------------------------------------- ! 这是一个二元函数,x1,x2是自变量,fx是返回的函数值 !---------------------------------------------------------------- fx = sqrt(x1*x1+x2*x2) return end subroutine !***************************************************************** subroutine cal_dydx(fx,x,det,dydx) implicit none real(8) :: x,det,dydx,y0,y1 external :: fx !---------------------------------------------------------------- ! purpose ! 这是一个向前差分方法求导数的子程序 ! in ! fx: ! 引入的外部子程序,调用方式如下: ! call fx(x,y) ! x: ! 自变量 ! det: ! 差分步长(一般取为sqrt(1D-15~1D-16)) ! out ! dydx: ! 返回的差分导数 !---------------------------------------------------------------- call fx(x,y0) call fx(x+det,y1) dydx = (y1-y0)/det return end subroutine !***************************************************************** 读者也可以通过 点击链接下载 编译打包好的源程序。 因此,我们在使用其他人写的Fortran程序时,如果别人程序中的传入子程序/函数的调用参数列表与我们已经写好的子程序/函数不同,这时,就可以用contains来解决这个问题了。 转载自 科算网 http://www.scicalweb.com/kes_talk/mod/talk_0103.html
16140 次阅读|0 个评论
Fortran指针应用——环状双向串行
yangjj2005 2013-4-17 12:44
一、module部分: 1.自定义数据类型、变量及同名重载接口 2.openfile子程序 3.output1子程序 4.output2子程序 5.output3子程序 二、主程序 三、数据文件及源代码 源代码及数据文件.txt 注意:如果数据文件不使用DATA1.txt,则需要更改module中变量FILENAME的初值。
个人分类: 交流讨论|3412 次阅读|0 个评论
你会用Fortran“指针数组”吗?
热度 2 yangjj2005 2013-4-12 15:49
◆ 问题的产生 最近,筹划使用Fortran语言编程计算。也许是看惯了以往的编程风格(基于过程的,一步一步按部就班),不由得想起了另一种风格——Macro Based Programming(有点面向对象的风格,把一些“对象”的数据和方法写在一个module里面),但是里面涉及到很多指针的操作,好久没用到指针,于是操起一本教科书开始爬格子。先照猫画虎写了这样一段代码: 运行没有发现问题,心里美滋滋!回头看了一眼module文件,发现其中定义了一个PPERSON的数据类型: TYPE PPERSON TYPE(PERSON),POINTER :: P ENDTYPE 顿生疑惑,为什么要这样定义,直接使用指针数组多简单。着手将其改造成这样: TYPE(PERSON),POINTER :: P(:) 整个代码改成: 编译通过,运行出错,弹出: 问题就是这样。 ◆ 问题分析: 按图索骥,找到主程序调用子程序时出错,子程序在第24行出错: TEMP = P(I) 第一反应是两个数据类型不对造成的,顺手改成: TEMP = P(I) 再次运行,没有报错。仔细一瞧,结果和预期的结果对不上: 原来这样改也有问题。这是因为:TEMP是一个指针,它指向的变量如发生变动,那么它本身也会跟着变,而我们希望TEMP在交换时不随前一个变量变动,因而出来这样的结果。进一步分析,也没有找到其他合理的解释——为什么不能这样给TEMP赋值? ◆ 问题解决 继续,在网上查找类似的问题,果不其然,找到一篇( http://www.douban.com/note/184396109/ )。不过好心的网友回答地不甚清楚,好在他(她)提供了两个其他的帖子,我先打开( http://software.intel.com/en-us/forums/topic/280765 )。同样的问题,回复是:The concept of array of pointers does not exist in Fortran. 很惊讶,原来Fortran不存在指针数组的概念(我想这会大家应该明白为什么在标题里给指针数组打上双引号了吧?)。帖子中给出的解决方法同改造前的代码。 为了印证,继续打开第二个帖子( http://flyfeather.net/archives/213 ),一样的问题,一样的解答。 ◆ 总结 至此终于知道了问题的所在:Fortran不存在指针数组的概念,不能直接使用指针数组,必须通过一个包含指针的数据类型(如下)来定义数组,其效果等同于指针数组。 TYPE PPERSON TYPE(PERSON),POINTER :: P ENDTYPE 本文原代码: 改造前的Fortran源代码.txt
个人分类: 交流讨论|11388 次阅读|3 个评论
Fortran入门的总结
plgongcat 2013-4-3 16:06
http://asc.2dark.org/node/45
个人分类: f90|5712 次阅读|0 个评论
[转载]Eclipse安装与配置
jingguo 2013-3-12 16:39
Eclipse安装与配置 涉及软件: Eclipse IDE for C/C++ Developers 安装目录:$HOME/Software/eclipse 安装 注意:此前请确保已经安装了JRE 非常简单。将下载的包解压缩后放到要安装的位置即可,比如$HOME/Software/eclipse。 双击里面的”eclipse”即可启动。 配置 装好后还需要安装PTP和Photran。 第一步: 点击” Help “——》” Install New Software… “来进行软件的安装。 第二步: 在最上面” Work with “的输入框中输入一个国内镜像地址,如 http://mirror.neu.edu.cn/eclipse/releases/helios/ 1) 。然后直接回车即可(这个地址会被自动保存,以后可以再用)。稍等片刻会出来软件列表 第三步: 选择合适软件并安装。 在”General Purpose Tools”分组下勾选”Parallel Tools Platform (PTP) End-User Runtime” 在”Programming Languages”分组下勾选”Fortran Development Tools (Photran)” 2) 然后一路”Next”、”Finish”下去吧,中间会让你接收License。安装完成后提示重启eclipse 第四步: 安装Photran的Intel编译器支持 将地址改为PTP的更新地址 http://download.eclipse.org/tools/ptp/updates/helios 。在”Fortran Development Tools (Photran)“分组下勾选”Linux Intel(R) Fortran Compiler Support”并安装 第五步: 安装idb的Eclipse插件 手动安装。将 H O M E / S o f t w a r e / i n t e l / c o m p o s e r x e − 2011 / e c l i p s e s u p p o r t / c d t 7.0 / e c l i p s e 里 的 文 件 复 制 到 HOME/Software/eclipse 启动 如果直接双击eclipse是不能识别ifort等命令的,因为环境变量是在~/.bashrc中设置的。两种方法: 在命令行启动 建立启动器,修改启动器的启动命令 建立启动器方法 对于Gnome2.X而言(恕我不知道其它DE/WM/Gnome3怎么做),在菜单上 右键 -》 编辑菜单 ,打开主菜单编辑器。选择一个合适的分类,如 “编程” ,然后选择 “新建项目” 填入必要信息: 类型:应用程序 名称:eclipse(随意) 命令:bash -c ”. ~/.bashrc;$HOME/Software/eclipse/eclipse” 点击左侧的图标按钮可以更改图标。
2602 次阅读|0 个评论
[转载]VS2010/2008下采用fixedsys字体
luoxgg 2013-1-28 19:57
默认的vs2010/2008不支持fixedsys字体,因为wpf编辑器只支持true type的字体,而fixedsys不是true type字体。 一种解决方法是使用使用fixedsys Excelsior字体来代替, fixedsys Excelsior字体库的下载地址为 http://www.fixedsysexcelsior.com 以防万一,附件附了一份原字体。 FSEX300.ttf
个人分类: linux|4033 次阅读|0 个评论
FORTRAN与匈牙利命名法
热度 1 hellojwx 2013-1-27 02:10
FORTRAN是不区分大小写的,但是这并不妨碍我们采用对自己方便的变量命名法。 命名法notation是指程序员对自己编写的程序中变量的命名规则,原则上说只要符合某种语言的语法要求,变量可以起任意的名字,因为计算机只认识你输入的字符。但是对于编写程序的人来说,给变量起一个好懂易记的名字,其实非常重要。以FORTRAN为例,我们经常会看到一个形如i,ii,jjj之类的变量名,这些变量名重复度高容易混淆,同时因为没有实际意义,时间久了容易忘记,而且也不便于团队之间的交流。因此采用一套好的命名法十分有益。匈牙利命名法就是一套很好的规则。 匈牙利命名法是1972年匈牙利裔 程序员查尔斯西蒙尼 发明的,此人后来成了微软 的总设计师。 在匈牙利命名法中,一个变量名由一个或多个小写字母开始,这些字母有助于记忆变量的类型和用途,紧跟着的就是程序员选择的任何名称。这个后半部分的首字母可以大写,以区别前面的类型指示字母(称为驼峰大小写)。例如: iStep (i表明序号index,说明这个变量是整数型,用于循环的序号或者计数器) jPart (j跟i类似,比如在嵌套循环中外循环用了iPart,那么内循环可以用jPart、kPart等等) nAtoms (n表明数目number,说明这个变量是整数型,用于表示数组长度等等,有时也可以作计数器) arrXYZ (arr表明数组array,说明这个变量是一个数组的名字) dR (d表明差值difference,说明这个变量用于表明两个变量间的差值)。 这样当看到一个变量时,很快就是反应出它是什么类型,什么意义。其中,变量的意义比其类型更重要。以前在WINDOWS编程中曾经流行过叫作系统匈牙利命名法的注重类型的命名方法,机械地给每个变量套上一个公式:变量类型+变量名称,不过这种命名法争议很大。而且这种系统(机械)命名法并不是西蒙尼氏的本意。 在FORTRAN科学计算中,我们可以适当地采用匈牙利命名法,比如下面这个对势系统循环求总能的小程序: Module coor integer, parameter :: nAtomsTot = 100 real*8, dimension(nAtomsTot,3) :: arrXYZ End Module Program main implicit none real*8 :: uTot integer :: iAtom, jAtom real*8, external :: getPairEn uTot = 0.d0 do iAtom = 1, nAtomsTot - 1 do jAtom = iAtom + 1, nAtomsTot uTot = uTot + getPairEn(iAtom, jAtom) end do end do End Function getPairEn(iAtom, jAtom) use coor implicit none real*8 :: getPairEn integer :: iAtom, jAtom real*8 :: dR(3), r dR(:) = arrXYZ(iAtom,:) - arrXYZ(jAtom,:) r = sqrt(dot_product(dR,dR)) getPairEn = 1.d0/r End Function 这段程序包含三个部分:Module用于定义公有变量,自定义函数getPairEn用于计算一对原子间的相互作用,主程序中的循环用于计算总能。这个例子中的变量定义基本上符合了匈牙利命名法,比如 nAtomsTot 表示原子总数total number of atoms等等。函数名以get开头表明这个函数的作用是要返回一个值,而如果函数的作用是更新全局变量的话(例如移动原子)那么函数名可以以set开头(例如setAtomsMove(iAtom,dR))。
5551 次阅读|1 个评论
[转载]FORTRAN EVESF function
Irasater 2013-1-21 17:37
http://www.roguewave.com/Portals/0/products/imsl-numerical-libraries/fortran-library/docs/6.0/math/default.htm?turl=evesf.htm EVESF(输出的结果为本征值的绝对值按照从大到小的顺序) Computes the largest or smallest eigenvalues and the corresponding eigenvectors of a real symmetric matrix. Required Arguments NEVEC — Number of eigenvalues to be computed. (Input) A — Real symmetric matrix of order N . (Input) SMALL — Logical variable. (Input) If . TRUE ., the smallest NEVEC eigenvalues are computed. If . FALSE ., the largest NEVEC eigenvalues are computed. EVAL — Real vector of length NEVEC containing the eigenvalues of A in decreasing order of magnitude. (Output) EVEC — Real matrix of dimension N by NEVEC . (Output) The J -th eigenvector, corresponding to EVAL ( J ), is stored in the J -th column. Each vector is normalized to have Euclidean length equal to the value one. Optional Arguments N — Order of the matrix A . (Input) Default: N = SIZE ( A ,2). LDA — Leading dimension of A exactly as specified in the dimension statement in the calling program. (Input) Default: LDA = SIZE ( A ,1). LDEVEC — Leading dimension of EVEC exactly as specified in the dimension statement in the calling program. (Input) Default: LDEVEC = SIZE ( EVEC ,1). FORTRAN 90 Interface Generic: CALL EVESF (NEVEC , A , SMALL , EVAL , EVEC ) Specific: The specific interface names are S_ EVESF and D_ EVESF . FORTRAN 77 Interface Single: CALL EVESF (N , NEVEC , A , LDA , SMALL , EVAL , EVEC , LDEVEC) Double: The double precision name is DEVESF . Description Routine EVESF computes the largest or smallest eigenvalues and the corresponding eigenvectors of a real symmetric matrix. Orthogonal similarity transformations are used to reduce the matrix to an equivalent symmetric tridiagonal matrix. Then, an implicit rational QR algorithm is used to compute the eigenvalues of this tridiagonal matrix. Inverse iteration is used to compute the eigenvectors of the tridiagonal matrix. This is followed by orthogonalization of these vectors. The eigenvectors of the original matrix are computed by back transforming those of the tridiagonal matrix. The reduction routine is based on the EISPACK routine TRED2 . See Smith et al. (1976). The rational QR algorithm is called the PWK algorithm. It is given in Parlett (1980, page 169). The inverse iteration and orthogonalization computation is discussed in Hanson et al. (1990). The back transformation routine is based on the EISPACK routine TRBAK1 . Comments 1. Workspace may be explicitly provided, if desired, by use of E5ESF/DE5ESF . The reference is: CALL E5ESF (N, NEVEC, A, LDA, SMALL, EVAL, EVEC, LDEVEC, WK, IWK) The additional arguments are as follows: WK — Work array of length 9 N . IWK — Integer array of length N . 2. Informational errors Type Code 3 1 The iteration for an eigenvalue failed to converge. The best estimate will be returned. 3 2 Inverse iteration did not converge. Eigenvector is not correct for the specified eigenvalue. 3 3 The eigenvectors have lost orthogonality. Example In this example, a DATA statement is used to set A to a matrix given by Gregory and Karney (1969, page 55). The largest two eigenvalues and their eigenvectors are computed and printed. The performance index is also computed and printed. This serves as a check on the computations. For more details, see IMSL routine EPISF . USE EVESF_INT USE EPISF_INT USE UMACH_INT USE WRRRN_INT IMPLICIT NONE ! Declare variables INTEGER LDA, LDEVEC, N PARAMETER (N=4, LDA=N, LDEVEC=N) ! INTEGER NEVEC, NOUT REAL A(LDA,N), EVAL(N), EVEC(LDEVEC,N), PI LOGICAL SMALL ! ! Set values of A ! ! A = ( 5.0 4.0 1.0 1.0) ! ( 4.0 5.0 1.0 1.0) ! ( 1.0 1.0 4.0 2.0) ! ( 1.0 1.0 2.0 4.0) ! DATA A/5.0, 4.0, 1.0, 1.0, 4.0, 5.0, 1.0, 1.0, 1.0, 1.0, 4.0, 2.0, 1.0, 1.0, 2.0, 4.0/ ! ! Find eigenvalues and vectors of A NEVEC = 2 SMALL = .FALSE. CALL EVESF (NEVEC, A, SMALL, EVAL, EVEC) ! Compute performance index PI = EPISF(NEVEC,A,EVAL,EVEC) ! Print results CALL UMACH (2, NOUT) CALL WRRRN ('EVAL', EVAL, 1, NEVEC, 1) CALL WRRRN ('EVEC', EVEC, N, NEVEC, LDEVEC) WRITE (NOUT,'(/,A,F6.3)') ' Performance index = ', PI END Output EVAL 1 2 10.00 5.00 EVEC 1 2 1 0.6325 -0.3162 2 0.6325 -0.3162 3 0.3162 0.6325 4 0.3162 0.6325 Performance index = 0.031
个人分类: Software|2846 次阅读|0 个评论
[转载]Fortran 语法备忘录 (高级)
chenyuchen 2012-12-18 03:41
转自:https://sites.google.com/site/zhousicheng02/a_015 Fortran 语法备忘录 (高级) 混合语言编程 Mixed-Language-Programming 用Fortran调用C++语言的程序 例如一个简单的C++语言程序,求直角三角形斜边长度的达哥拉斯公式:保存为crout.cpp文件。 !----------------------------------------------------------------------- // C++ - Routine #include iostream.h #include math.h extern C void pythagoras (float a, float b, float *c) { *c = (float) sqrt(a*a+b*b); } !----------------------------------------------------------------------- 注:如果用C语言,则用stdio.h代替iostream.h,并且不需要extern C,其余过程类似。 Fortran程序:保存为fmain.f90 !----------------------------------------------------------------------- ! Demoprogramm: ! Aufrufen einer C-Routine von Fortran aus ! Die C-Routine wird innerhalb eines Modules deklariert module cproc interface ! definierte Schnittstelle subroutine pythagoras (a, b, res) !DEC$ ATTRIBUTES C :: pythagoras !DEC$ ATTRIBUTES REFERENCE :: res real :: a, b, res end subroutine end interface end module program fmain use cproc implicit none real :: x, y, z write(*,*) ' Berechnung der Hypotenusenlaenge eines rechtwickligen Dreiecks' write(*,*) ' Geben Sie die beiden Seitenlaengen ein, die den rechten Winkel' write(*,*) ' einschliessen:' read(*,*) x, y call pythagoras(x,y,z) write(*,*) ' Die Laenge der Hypotenuse betraegt: ', z end program fmain !----------------------------------------------------------------------- Fortran程序中添加的接口程序interface,对调用的C++与Fortran的数据传递进行说明。 接口程序写在Module当中,Fortran程序引用了该Module,就可以使用了。 参考链接: http://www.math.utah.edu/software/c-with-fortran.html http://www.chiralcomp.com/support/mixing_f77_c_cpp/mixing_f77_c_cpp.html http://www.acronymchile.com/fortran_and_cplusplus.html http://arnholm.org/software/cppf77/cppf77.htm 一个VC++调用Fortran动态链接库的例子: http://www.vckbase.com/code/viewcode.asp?id=2052 http://zhidao.baidu.com/question/652950.html?fr=qrl3 系统时间日期的子程序 date_and_time( ) date 至少8位字符,最左边的8位字符为JJJJMMTT (年月日) time 至少10位字符,最左边的10位字符为hhmmss.sss (时分秒.毫秒) zone 至少5位字符,最左边的5位字符为±hhmm (时分) 相对于 世界标准时间 values 一维整型数组,长度至少为8,分别为:年,月,日,时区,小时,分,秒,毫秒 例如: !----------------------------------------------------------------------- character(len=10) :: d,t integer,dimension(8) :: V d = call date_and_time(date=d,time=t) call date_and_time(values=V) PRINT *, Date=,d,Time=,t PRINT *, V end !----------------------------------------------------------------------- 系统时钟子程序 system_clock ( ) count 与计算机型号有关,基于计算机时钟, count_rate 一秒钟count的增加量 count_max count的最大值 三个数字均为数字,可以用来计算一个程序运行的时间:秒数=(t1-t0)/Δt !----------------------------------------------------------------------- integer :: n_p_sec, ia, ie; real :: t call system_clock(count_rate=n_p_sec)!once call system_clock(count=ia) DO i=1,10000 j=i END DO ! 被测试d程序块 call system_clock(count=ie) t = (ie-ia)/real(n_p_sec) write(unit=*,fmt=*) Zeit in Sekunden:,t end !----------------------------------------------------------------------- (以下程序转自 编程爱好者论坛 — Fortran讨论区 ) 1. 如何加大Stack size? 选Project = Settings = Link = Category: Output = Stack allocations Reserve: 这里填新值(默认为1M,若需要10M,则填10000000) 2. 如何用Fortran批量生成文件? 设要生成4000个文件,文件名为AA1-AA4000,如何写循环生成文件,而不用写4000次write命令呢? 用内部文件: character(len=80) :: filename,form integer :: i do i=1,4000 select case (i) case (1:9) write(form,'(i1)') i case (10:99) write(form,'(i2)') i case (100:999) write(form,'(i3)') i case (1000:9999) write(form,'(i4)') i end select write(filename,*) AA,trim(form),.TXT open(10,file=filename) write(10,*) i close(10) end do stop end 3. 如何用Fortran动态生成输出格式? 设有一个数组data(100),输出时,希望每行输出num个数,而num由用户输入,如何实现? 用内部文件: character(len=80) :: form real :: data(100) integer :: i,num data = (/ (i,i=1,100) /)/10.0 read(*,*) num write(form,*) (,num,f10.3) write(*,form) data stop end 将字符串改为大写的子程序 subroutine UpCase (str) !========================================= ! change to upper case !========================================= character(len=*),intent(in out) :: str integer(4) :: icha,LL,icval integer(4),parameter :: diff = ichar('a') - ichar('A') LL = len_trim(str) do icha=1,LL icval = ichar(str(icha:icha)) if (icval=ichar('a') .and. icval=ichar('z')) then str(icha:icha) = char(icval-diff) end if end do return end subroutine UpCase 5. 如何用F90/95生成随机数? 注意: 现在计算机产生的随机数都是伪随机数。 random_number(x) 产生一个0到1之间的随机数(x可以是向量),但是每次总是那几个数。 用了random_seed ()后,系统根据日期和时间随机地提供种子,使得随机数更随机了。 program random implicit none real :: x call random_seed () ! 系统根据日期和时间随机地提供种子 call random_number (x) ! 每次的随机数就都不一样了 write(*,*) x stop end program random 6. 函数/子程序超载的例子 设要编一个两个变量值互换的子程序swap(a,b),哑元a,b可能是实型数,整型数,数组,矩阵,字符串,派生类型等等。但是希望只用一个子程序接口swap(a,b)来实现。F90可以用类属接口来实现这种子程序超载: module Utilities implicit none private I_Swap,R_Swap,RVec_Swap,RMat_Swap,Type_Swap public :: Swap interface Swap module procedure I_Swap,R_Swap,RVec_Swap,RMat_Swap,Type_Swap end interface contains subroutine i_swap (a,b) ! 整型置换 integer (ikind),intent(in out) :: a,b integer (ikind) :: t 。。。 ! 略 end subroutine i_swap subroutine r_swap (a,b) ! 实型置换 real (rkind), intent(in out) :: a,b real (rkind) :: t t = a a = b b = t return end subroutine r_swap subroutine RVec_swap (a,b) ! 实型向量置换 real (rkind), intent(in out) :: a(:),b(:) integer (ikind) :: i do i=1, size(a) call R_Swap (a(i),b(i)) end do return end subroutine RVec_swap subroutine RMat_swap (a,b) ! 实型矩阵置换 。。。 ! 略 end subroutine RMat_swap subroutine Type_swap (a,b) ! 派生类型置换 。。。 ! 略 end subroutine Type_swap end module Utilities 8. 推荐好的代码风格 根据F90子集语言ELF90和F的要求整理(部分)。 “强迫用”的语言特性: + F90的自由格式的源代码。 + implicit none。 + 子过程的哑元都要有intent属性。 + 函数子程序的哑元必须指定为intent(in)。 + 所有子程序和函数都放在模块(module)中,然后引用(use)该模块;或者放在program中。 + 数组哑元要求是假定形状的,或者有固定的维数和大小。字符哑元要求是假定长度的。 + 对于recursive function(递归函数)语句,必须有result子句。 + 在所有派生类型(type)的定义语句中,必须用双冒号分隔符(::)。 + 主程序要求有program语句。 + 在程序单元的end语句中要求后跟程序单元的类型和名称。 + 在end type语句中要求后跟类型的名称。 + end program前必须有stop语句以表示停止执行。 + 子过程中必须有return语句,以表示返回。 + subroutine s( )并且call s( ),即必须有括号。 “不得用”的语言特性: - allocatable、intent、pointer、save、dimension、parameter和target语句形式。(用属性形式代替。) - external语句形式。(用显式的接口代替。) - assign、赋值go to、交错return、continue、entry、和计算go to 语句。 - include文件。(用模块代替。) - data和block data。(在类型声明语句中进行初始化或赋值。) - common块。(将全局数据放在模块中,用模块代替。) - equivalence。(被认为是许多难以查找的编程错误的来源。) - double precision语句。(用real语句声明双精度的实型数。) - 语句函数。(用内部函数代替。) - 专用固有函数。(用类属函数代替。) - 假定大小数组。(用假定形状数组代替。) - do n (其中n为语句标号)。(用do和end do代替。) - 非整数do变量和表达式。 - 同一行上多条语句。 - 逻辑型case表达式。 - 从if块外面分支到end if。 - where语句形式。(用where结构形式。) - 在open和inquire语句中的blank= 说明符。 - 双字关键词之间要求有空格:in out,go to。不能写为inout,goto。 Copyright © Zhousicheng https://sites.google.com/site/zhousicheng02/home
个人分类: 科研笔记|3447 次阅读|0 个评论
[转载]Fortran 语法备忘录 (中级)
chenyuchen 2012-12-18 03:40
Fortran 语法备忘录 (中级) 转自: https://sites.google.com/site/zhousicheng02/a_014 目录: 【主程序】【语句函数】【内在过程】【内部过程】【外部过程】 ◆外部函数◆外部子程序◆EXTERNAL属性和哑过程◆INTENT属性◆OPTIONAL属性◆哑元改名◆关键字变元INTRINSIC属性◆类属过程◆过程接口 INTERFACE◆超载操作符◆自定义操作符◆超载赋值号 【模块】【块数据】【指针】 【主程序】 !----------------------------------------------------------------------- ] END ] !----------------------------------------------------------------------- 【语句函数】 f(x)=x**2+1 【内在过程】 max,abs,sin,char。。。。 【内部过程】 Contains 与宿主程序共享变量名, 外部过程FUNCTION, SUBROUTINE都可以有自己的内部过程。 通常没有说明语句。 没有哑元,无哑实结合。 使用内部过程的规则:在宿主中不要定义子程序名和函数名的类型,也不能指定它们是有EXTERNAL属性。宿主中的变量名和数组名等在内部过程中有效,有相同的数值。但同一名若在内部过程中又进行了类型声明,则此名被视为其过程中的独立变量,无相同的数值。内部过程中也可引用另一内部过程。 例: !----------------------------------------------------------------------- program internal real a,b,c call find print *,c contains subroutine find read *, a,b c=sqrt(a**2+b**2) end subroutine find end !----------------------------------------------------------------------- 【外部过程】 过程=函数子程序 哑元调用方式:传址调用call by adress,即传递4字节的变量的内存地址。 ◆外部函数 调用:函数名(实元表) 函数名() 如果没有结果名,则函数名就是结果名。 !----------------------------------------------------------------------- FUNCTION 函数名( ) END !----------------------------------------------------------------------- ◆外部子程序: 调用: CALL 子程序名 哑元可以是变量名、数组名、过程名、指针名等均可作为哑元。它们之间用逗号隔开。 前缀是F90中新增的,它可以是: 或 。关键词:RECURSIVE(F90),PURE(F95),ELEMENTAL(F95)。RECURSIVE表示过程时可以直接或间接地调用自身,即递归调用,其过程是递归过程。 !----------------------------------------------------------------------- SUBROUTINE 子程序名 END ] !----------------------------------------------------------------------- 一个有用的例子:互换数字 !----------------------------------------------------------------------- SUBROUTINE swap(p,q) INTEGER :: p,q,r r=p;p=q;q=r RETURN END !----------------------------------------------------------------------- ◆EXTERNAL属性和哑过程 (哑元为外部过程,即哑过程) 指定EXTERNAL语句或属性说明实元实际上是外部过程 类型定义语句: 类型,EXTERNAL :: 外部函数名 … 或EXTERNAL语句:EXTERNAL … 哑元也可以是一个过程,这时作为哑元的过程称为哑过程。(至少两层调用) 例如: !----------------------------------------------------------------------- Programm main Real x,y External Plus !外部过程名作实元,必须用External说明,或者具有External属性 x=1.0 ; y=2.0 Print,* Calculate(x,y,Plus) !调用Calculate函数,实元为外部过程Plus End Program main Real Function Plus(a,b) !(第二层被调用的外部函数) Real, Intent(In) :: a,b Plus=a+b End Function Plus Real Function Calculate (x,y,func) Real, Intent(In) :: x,y Real, External func !类型定义语句, 说明哑元时一个外部过程, 也可以直接用External说明 Calculate=func(x,y) !调用自定义的外部函数 End Function Calculate !----------------------------------------------------------------------- 或者将 Real, External func 改为接口程序: Interface Real Function Plus(a,b) !Plus被接口块说明为一个哑元,即一个哑过程 Real, Intent(In) :: a,b End Function Plus End Interface ◆INTENT属性 (过程的哑元说明) 在类型定义语句中: 类型,INTENT(意图说明符) :: 哑元名表 或用INTENT语句 : INTENT(意图说明符) :: 哑元名表 意图说明符为以下字符串: IN 指明哑元仅用于向过程提供数据,过程的执行期间哑元不能被重定义或成为未定义的,相联合的实元可以是常数、变量、数组以及它们的算术表达式。 OUT 指明哑元用于把过程中的数据传回调用过程的程序,与之相结合的实元只允许是变量,不得为常数。 INOUT 指明哑元既可以用于向过程提供数据,也可用于返回数据,与之相结合的实元只允许是变量。 ◆OPTIONAL属性可选变元,部分哑元作哑实结合 内在函数PRESET用来反映它的自变量是否在程序执行部分中出现。PRESET(A)的值是一个逻辑值,以此来构造不同的算法。 例如,要求编一子程序,既能求四边形同长(A+B+C+D)的值,也能求三角形周长(A+B+C)的值。此时D就是可选择变元,并规定当D不出现时,置D值为零。子程序如下: !----------------------------------------------------------------------- SUBROUTINE SUM(S,A,B,C,D) IMPLICIT NONE REAL,INTENT(IN) :: A,B,C REAL,INTENT(IN),OPTIONAL :: D REAL,INTENT(OUT) :: S REAL :: TEMP IF(PRESET(D)) THEN TEMP=D ELSE TEMP=0. END IF S=A+B+C+TEMP END SUBROUTINE SUM !----------------------------------------------------------------------- ◆哑元改名 例如,对于上面求边长的子程序,如调用时欲把哑元名A,B,C,D改为物理意义明确的名称UPPER,DOWN,LEFT,RIGHT,只需在主调程序中写入接口块,在接口块的哑元表中用新的哑元名即可: !----------------------------------------------------------------------- PROGRAM SUMMATION INTERFACE SUBROUTINE SUM(S,UPPER,DOWN,LEFT,RIGHT) IMPLICIT NONE REAL,INTENT(IN) :: UPPER,DOWN,LEFT REAL,INTENT(IN),OPTIONAL :: RIGHT REAL,INTENT(OUT) :: S REAL :: TEMP END SUBROUTINE SUM END INTERFACE READ *, UPPER,DOWN,LEFT,RIGHT CALL SUBROUTINE SUM(S,UPPER,DOWN,LEFT,RIGHT) …… END PROGRAM SUMMATION !----------------------------------------------------------------------- ◆关键字变元 哑实结合:(哑元名=实元表达式) 例如: CALL TEST(1,10,D=1000,C=100) 同: CALL TEST(A=1,B=10,D=1000,C=100) F90也允许在调用语句中,前面部分实元不用关键字变元,只从某一个变元开始用关键字变元。 主调程序中如采用关键字变元调用过程,就必须写出被调子程序的接口块。 !----------------------------------------------------------------------- PROGRAM swap_pro !交换大小两个实数swap(a,b) INTERFACE SUBROUTINE swap(klein,gross) IMPLICIT NONE REAL,INTENT(out) :: klein,gross END SUBROUTINE swap END INTERFACE READ *, klein,gross CALL SUBROUTINE swap(klein,gross) …… END PROGRAM swap_pro !----------------------------------------------------------------------- ◆INTRINSIC属性 与EXTERNAL语句或属性说明的实元是外部过程相对应,INTRINSIC语句或属性用来说明实元实际上是内在过程。其一般形式为: 类型定义语句:类型,INTRINSIC :: 内在函数名 … 或INTRINSIC语句:INTRINSIC 内在过程名 … 内在过程名必须是内在过程的通用名或专用名。如果是专用名,则可以在其作用范围单元中作为一个过程的实元,但它必须出现在一个INTRINSIC语句中,或被该单元中的一个类型声明语句指明具有INTRINSIC属性。需要注意的是,一个内在过程名只能在INTRINSIC语句中出现一次,并且不能同时出现在INTRINSIC语句和EXTERNAL语句中。 例: !----------------------------------------------------------------------- PROGRAM MAIN REAL F REAL,INTRINSIC :: ALOG !说明Alog是内部函数,可以作实元 F=CALCULATE(0.0,1.0,ALOG) !使用内在函数ALOG作实元 … END PROGRAM !----------------------------------------------------------------------- 注意这里必须用专用名ALOG,而不能用通用名LOG。 ◆类属过程 允许用不同类型的实元与同一个哑元结合,如内在基本函数ABS(X),结合的实元可以是整型、实型与复型。 例如,要编写求两数之和的类属函数时,分别编写哑元是实型和整型的函数: !----------------------------------------------------------------------- FUNCTION SUM_REAL(A,B) RESULT(SUM_REAL_RESULT) REAL :: A,B,SUM_REAL_RESULT SUM_REAL_RESULT=A+B END FUNCTION SUM_REAL FUNCTION SUM_INTEGER(A,B) RESULT(SUM_INTEGER_RESULT) INTEGER :: A,B,SUM_INTEGER_RESULT SUM_INTEGER_RESULT=A+B END FUNCTION SUM_INTEGER !现在把这两个函数过程综合成一个类属函数,类属函数名取为MY_SUM,在主调程序应写明如下接口: PROGRAM SUMMATION INTERFACE MY_SUM FUNCTION SUM_REAL(A,B) RESULT(SUM_REAL_RESULT) REAL :: A,B,SUM_REAL_RESULT END FUNCTION SUM_REAL FUNCTION SUM_INTEGER(A,B) RESULT(SUM_INTEGER_RESULT) INTEGER :: A,B,SUM_INTEGER_RESULT END FUNCTION SUM_INTEGER END INTERFACE IMPLICIT NONE REAL :: X,Y INTEGER :: I,J READ *, X,Y,I,J PRINT *, MY_SUM(X,Y),MY_SUM(I,J) END PROGRAM SUMMATION !----------------------------------------------------------------------- ◆过程接口 INTERFACE 一个内部过程总是由程序单元中的语句来调用的。一般来讲,编译程序知道内部过程的一切情况,如知道该过程是一个函数或子程序、过程名、哑元的名字、变量类型和属性、函数结果的特性等等。这个信息的集合被称为过程的接口(interface)。 对于内部过程、内在过程和模块,过程接口对编译程序而言是己知的和显式给出的,故称显式接口。 如在调用一个外部过程或一个哑过程时,编译系统通常不知道该过程的各种情况,这种接口是隐式的。 用EXTERNAL语句来指明一个外部过程或哑过程,但此语句仅说明每一个外部名是一个外部过程名或哑过程名,并没有指明过程的接口,所以接口仍是隐式的。 为了全面准确地通知编译系统,在主调程序中有时需要加入接口块,以说明主调程序与被调程序的接口。接口块是F90中引进的新颖程序块,它显式指明了过程接口的机制。通过接口块可用为一个外部过程或哑过程指明一个显式的接口。这比EXTERNAL语句提供了更多的信息,也提高了程序的可读性。 过程接口确定过程被调用的形式,它由过程的特性、过程名、各哑元的名字和特性以及过程的类属标识符(可以省略)组成,一般它们都被写在一个过程的开头部分。此接口块被放在主调程序的说明部分中,通常还应写在类型说明语句之前,它的内容是被调用的过程中的说明部分,功能是通知编译系统,主调程序调用的过程中各种变元的类型、属性、性质等。 用法: !----------------------------------------------------------------------- INTERFACE … … END INTERFACE !----------------------------------------------------------------------- 其中类属说明的形式为: 类属名 - 类属过程 OPERATOR - 超载操作符、自定义操作符 ASSIGNMENT(=) - 超载赋值号 接口体的形式为: 函数语句 函数END语句 子程序语句 子程序END语句 模块过程语句的形式为:MODULE PROCEDURE 过程名表。 例: !----------------------------------------------------------------------- interface subroutine swap(x,y) real x,y end subroutine end interface real a,b read *,a,b call swap(a,b) end subroutine swap(x,y) real x,y z=x;x=y;y=z end subroutine !----------------------------------------------------------------------- 凡遇下列情况之一时,主调程序必须有接口块: 1、如果外部过程具有以下特征: 过程的哑元有可选择属性。 过程的哑元是假定形数组、指针变量、目标变量。 函数过程的结果是数组或指针。 对于字符型函数过程的结果、其长度不是常数,也非假定长度(*)。 2、如果调用过程时出现: 实元是关键字变元。 用一个类属名调用。 用超载赋值号(对于子程序)。 用超载操作符(对于函数)。 3、如果过程前缀关键词是ELEMENTAL ◆超载操作符 超载操作符的形式仍是系统内部操作符,如+、-、*、/等,但通过编出恰当的过程,可以扩充这些操作符的功能。例如;‘+’本来用于对数值作算术操作,但经恰当的编程后‘+’也可用于字符型操作,这就像车辆超载一样,故称为超载操作符。定义超载操作符,需先编写一个实现此超载(扩充)功能的函数过程,再在主调程序中编写过程的接口,在接口语句后加上超载说明,其一般形式为: INTERFACE OPERATOR(被超载使用的操作符号) 例如:要使‘+’超载能执行如下操作:把两个字符串的最后一个字母接起来。 !----------------------------------------------------------------------- PROGRAM ADD_CHARACTER IMPLICIT NONE CHARACTER(LEN=10) :: A,B INTEGER :: I,J INTERFACE OPERATOR(+) FUNCTION ADD(A,B) RESULT(ADD_RESULT) IMPLICIT NONE CHARACTER(LEN=*),INTENT(IN) :: A,B CHARACTER(LEN=2) :: ADD_RESULT END FUNCTION ADD END INTERFACE READ *, A,B PRINT *, A+B,2+3 END PROGRAM ADD_CHARACTER FUNCTION ADD(A,B) RESULT(ADD_RESULT) IMPLICIT NONE CHARACTER(LEN=*),INTENT(IN) :: A,B CHARACTER(LEN=2) :: ADD_RESULT ADD_RESULT=A(LEN_TRIM(A):LEN_TRIM(A))//B(LEN_TRIM(B):LEN_TRIM(B)) END FUNCTION ADD !----------------------------------------------------------------------- 接口的作用是向编译系统提示,遇到操作符‘+’时,如果操作数不是数值,就不是原来意义的加法,操作含义要到 FUNCTION ADD中找。当主调程序有了上述接口块后,在下面执行部分中执行字符串操作CH1+CH2时,+号作超载用。 ◆自定义操作符 .klein. .gross. .plus. INTERFACE OPERATOR(.plus.) ! .plus. = + MODULE PROCEDURE add END INTERFACE ◆超载赋值号 INTERFACE ASSIGNMENT(=) 例:编一程序把逻辑量超载赋值给整型变量。先编一个实现这一功能的子程序, !----------------------------------------------------------------------- SUBROUTINE LOG_INT(I,L) IMPLICIT NONE LOGICAL, INTENT(IN) :: L INTEGER, INTENT(OUT):: I IF(L) I=1 !I=.True. 得到1 IF(.NOT.L) I=0 !I=.Falsh. 得到0 END SUBROUTINE LOG_INT 再在主程序内编写接口, INTERFACE ASSIGNMENT(=) SUBROUTINE LOG_INT(I,L) IMPLICIT NONE LOGICAL, INTENT(IN) :: L INTEGER, INTENT(OUT):: I END SUBROUTINE LOG_INT END INTERFACE !----------------------------------------------------------------------- I=1 得到1 I=.True. 得到1 I=.Falsh. 得到0 【模块】 复制所有语句,共享所有变量 共享数据的2个方法:一个是哑实结合,一个就是数据共享 共享方式有:使用COMMON语句和EQUIVALENCE语句(F77),使用模块(F90)。另外,使用INCLUDE复制。 !----------------------------------------------------------------------- COMMON /]变量名表1 / /变量名表2]... EQUIVALENCE (变量名表1),(变量名表2),… !仅限于同一程序单元 INCLUDE '文件名 LIST]' !----------------------------------------------------------------------- 例如:下面的COMMON语句段 COMMON/happy/we,you,they COMMON/ /our,your,their COMMON/happy/i,he,she COMMON/angry/dog,cat,mouse COMMON my,his,her 等价于语句段, COMMON/happy/we,you,they,i,he,she COMMON/angry/dog,cat,mouse COMMON/ /our,your,their,my,his,her !----------------------------------------------------------------------- ◆模块用途: 包含通常使用的过程 声明全局变量和派生类型 声明外部过程的接口块 初始化全局数据和全局可分配数组 封装数据和处理数据的过程 存储在公共块COMMON中的变量可以被摘录和保存到模块之中。 如果一个程序单元中,只使用模块中的部分变量或需要重新命名部分变量,可以指定这些变量具有 ONLY 属性。 模块用法: !----------------------------------------------------------------------- MODULE 模块名 类型说明部分 ] END MODULE !----------------------------------------------------------------------- 例如: MODULE MY_MODULE REAL, PARAMETER :: Pi=3.141592654 CONTAINS SUBROUTINE SWAP(X,Y) REAL :: TEMP,X,Y TEMP=X X=Y Y=TEMP END SUBROUTINE SWAP END MODULE MY_MODULE 该模块内有一内部过程SWAP,引用这个模块的外部过程都将包含有此内部过程。 ◆使用模块: USE 模块名1, 模块名2, … 模块名n 它包括两方面:模块定义时规定只有哪些内容允许共享、引用模块时只要求共享哪些内容。 1) 模块的PRIVATE属性: 当定义派生类型的TYPE块写在模块中时,可以限制该派生类型定义的使用范围,以及类型定义内各成员的使用范围。譬如规定模块内的该派生类型或派生类型内的成员只供本模块内引用,不许模块外程序单元引用。其形式是: !----------------------------------------------------------------------- TYPE,PRIVATE :: 派生类型名 成员1类型说明 …… 成员n类型说明 END TYPE !----------------------------------------------------------------------- 使用PRIVATE专用特性后,可以禁止一切外部过程(包括主程序)访问派生类型的内部成员,而只是把派生定义类型作为一个整体黑箱使用。以后如果派生类型内部成员要改动,只需改写派生类型部分,引用模块的各程序单元可不必改动。 2) 模块内的变量改名: 如果需要对多个模块进行访问,而在不同的模块中可能用到了相同的名字,因此允许USE语句对被访问的实体重新命名,以解决局部实体和模块中访问实体之间的名字冲突问题。要重新命名时,USE语句具有下列形式: !----------------------------------------------------------------------- USE 模块名 !----------------------------------------------------------------------- 其中,改名列表的形式为:局部名1=块中名1, 局部名2=块中名2, …。其中,局部名是使用USE语句的程序单元中用的名字,块中名是待改的模块内部使用的变量名。 如模块中定义的变量名是A、B,程序单元中同名变量A、B与之共享,但若要在程序单元中把变量名改为C、D,则只需在单元内把引用语句写成: USE模块名, C=A, D=B 即可,而无需修改模块。 3) ONLY选项: 可以规定只取模块中一部分变量与本程序单元中实体共享,即只需要使用模块中的部分实体,其它实体没有共享关系。这时可在USE语句中使用ONLY选项。这时USE语句具有下列形式: !----------------------------------------------------------------------- USE模块名, ONLY : !----------------------------------------------------------------------- 其中,仅用名列表的形式为: 块中名1, 块中名2, …。 模块应用: 1) 全局数据 如果数据是在整个可执行程序中都要用到的全局数据,可以把它们放在一个模块中统一说明,在需要这些数据的程序单元内使用USE语句导出它们即可。例如: !----------------------------------------------------------------------- MODULE DATA_MODULE SAVE REAL :: A(10), B, C(50,10) INTEGER,PARAMETER :: I=10 COMPLEX D(I,I) END MODULE DATA_MODULE !----------------------------------------------------------------------- 如果要在程序单元中访问模块中定义的全部数据对象,可使用语句: USE DATA_MODULE 如果只需访问其中的A和D,则可使用语句: USE DATA_MODULE, ONLY : A,D 如果要避免名字冲突而改名的话,则可使用语句: USE DATA_MODULE, ONLY : A_MODULE=A, D_MODULE=D 2) 过程共享 利用模块,还可以把整个可执行程序中都要用到的全局过程封装在一起,即把这些过程放在一个模块中统一说明,而在需要这些过程的程序单元内使用USE语句导出它们即可,这就是模块过程。使用的方式是,首先在模块中把待用的全局过程定义为模块的内部过程,即写在CONTAINS语句之后,在调用过程单元中写上USE语句以调用此模块,再写上接口块,接口块的实体是模块过程语句: USE MODULE PROCEDURE 模块过程名表 例: !----------------------------------------------------------------------- PROGRAM CHANGE_KIND USE Module1 INTERFACE DEFAULT MODULE PROCEDURE Sub1, Sub2 END INTERFACE integer(2) in integer indef indef = DEFAULT(in) END PROGRAM MODULE Module1 CONTAINS FUNCTION Sub1(y) REAL(8) y sub1 = REAL(y) END FUNCTION FUNCTION Sub2(z) INTEGER(2) z sub2 = INT(z) END FUNCTION END MODULE !----------------------------------------------------------------------- 3) 公用派生类型 模块还可用来封装一些派生类型,供其它程序单元公用。例如: !----------------------------------------------------------------------- MODULE SPARSE_MATRIX TYPE NONEZERO REAL A INTEGER I,J END TYPE END MODULE SPARE_MATRIX !----------------------------------------------------------------------- 定义了一个由实数和两个整数构成的数据类型,用以表示稀疏矩阵的非零元素。其中的实数表示元素的值,两个整数表示该元素的下标。于是,派生类型NONZERO就可被其他单元通过USE语句来共用。 4) 全局可分配数组 在许多程序中需要一些全局公用的可分配数组,它们的大小在程序执行前是不知道的,这时也可用模块来实现。例如: !----------------------------------------------------------------------- PROGRAM MAIN … CAIL CONFIGURE_ARRAYS !分配数组 CALL COMPUTE !用分配好的数组进行计算 … END PROGRAM MAIN MODULE WORK_ARRAYS INTEGER N REAL,ALLOATABLE,SAVE :: A(:),B(:,:),C(:,:,:) END MODULE WORK_ARRAYS SUBROUTINE CONFIGURE_ARRAYS USE WORK_ARRAYS READ *,N ALLOCATE(A(N),B(N,N),C(N,N,2*N)) END SUBROUTINE CONFIGURE_ARRAYS SUBROUTINE COMPUTE USE WORK_ARRAYS … END SUBROUTINE COMPUTE !----------------------------------------------------------------------- 5) 抽象数据类型和超载运算 可以用模块来自定义抽象数据类型,为此只需在模块中先定义派生类型,再随后定义可在这种类型值上进行的运算即可。例如: !----------------------------------------------------------------------- MODULE INTERVAL_ARITHMETIC TYPE INTERVAL REAL LOWER,UPPER END TYPE INTERVAL INTERFACE OPERATOR(+) MODULE PROCEDURE COMB_INTERVALS END INTERFACE INTERFACE OPERATOR(*) MODULE PROCEDURE INTERSECTION_INTERVALS END INTERFACE CONTAINS FUNCTION COMB_INTERVALS(A,B) TYPE(INTERVAL) COMB_INTERVALS,A,B COMB_INTERVALS%LOWER=MIN(ALOWER,B%LOWER) COMB_INTERVALS%UPPER=MAX(AUPPER,B%UPPER) END FUNCTION FUNCTION INTERSECTION_INTERVALS(A,B) TYPE(INTERVAL) INTERSECTION_INTERVALS,A,B INTERSECTION_INTERVALS%LOWER=MAX(ALOWER,B%LOWER) INTERSECTION_INTERVALS%UPPER=MIN(AUPPER,B%UPPER) END FUNCTION END MODULE INTERVAL_ARITHMETIC !----------------------------------------------------------------------- 模块中定义了派生类型INTERVAL,它表示一个实数区间,通过接口块说明定义的运算符‘+’和‘*’号,它们分别是求两个区间的并计和交集。 【块数据】-- 模块 !----------------------------------------------------------------------- BLOCK DATA COMMON DATA END ] !----------------------------------------------------------------------- 【指针】 ◆用法1:指向变量 Integer, Target :: a Integer, Pointer :: p p=a p=3 !改变指针指向的变量 !----------------------------------------------------------------------- 用法2:指向一块内存 Allocate (p) !给指针p分配一块内存空间 Deallocate (p) !重新指定p之前最好将其释放 !----------------------------------------------------------------------- ◆查询: Associated (pointer, ) 检查针是否指向一个target,返回逻辑变量。 判断: 确保开始指针没有指向的函数null(),指向一块不能用的内存地址 Integer, Pointer :: p=null() !F95用法 或者用Nullify命令,设置指针为不能使用的内存地址 Nullify (pointer1, ) !F90用法 32位机,一个pointer记录一个地址,占4字节 ◆数组操作: 用法1: Integer, Target :: a(5)=(/1,2,3,4,5/) Integer, Pointer :: p(:) !声明p为一个一维指针数组,大小未定 p=a p=a(1:5:2) !p=部分数组 p=a(5:1:-1) !----------------------------------------------------------------------- 用法2 Integer, Pointer :: p(:) Allocate (p(3)) !分配指针p内存空间,3个整数 p=(/1,2,3,4,5/) !----------------------------------------------------------------------- ◆指针作为哑元需要添加接口程序: 求一维数组的最小值,函数名和变量均为指针 !----------------------------------------------------------------------- Implicit none Integer, target :: a(5) Integer, Pointer :: p(:) Interface Function getbig(p) Integer, Pointer :: p(:) Integer, pointer :: getbig End Function End Interface a=(/1,2,3,4,5/) p=a write (*,*) getbig(p) stop End Function getbig(p) Implicit none Integer, Pointer :: p(:) Integer, Pointer :: getbig Integer n,temp,i n=size(p,1) temp=0 do i=1,n if (tempp(i)) then temp=p(i) getbig=p(i) endif end do End Function !----------------------------------------------------------------------- 用Module来封装函数,相当于写好了Interface: !----------------------------------------------------------------------- Module func contains Function getbig(p) Implicit none Integer, Pointer :: p(:) Integer, Pointer :: getbig Integer n,temp,i n=size(p,1) temp=0 do i=1,n if (tempp(i)) then temp=p(i) getbig=p(i) endif end do End Function end module use func Implicit none Integer, target :: a(5) Integer, Pointer :: p(:) a=(/1,2,3,4,5/) p=a write (*,*) getbig(p) stop End !----------------------------------------------------------------------- 指针的应用: ◆指向多维数组: integer, target :: matrix(100:100) integer, pointer :: p(:,:) p=matrix(10:20,10:20) !----------------------------------------------------------------------- 指向Type类型: type person character*20 name integer number real old end type type(person) :: me,he,temp type(person),pointer :: pme,phe,ptemp pme=me phe=he ptemp=pme !----------------------------------------------------------------------- ◆指向Next module k_module type :: knoten integer :: i type (knoten), pointer :: next end type knoten type(knoten) punkt end k_module !----------------------------------------------------------------------- 第一行定义一个扩展类型knoten 第二行定义一个编号i,例如 punkt%i 第三行定义一个指针next,类型为knoten,例如:punkt%next指向一个地址。 指针punkt%next还没有指向的时候,是没有定义的,也就是说punkt%next%i不存在。 指针punkt%next只能指向一个knoten类型的地址,即变量punkt%next%i与punkt%next%next 例如: !----------------------------------------------------------------------- call k_module implict none type(knoten), target :: k(3) tyep(knoten), pointer :: p integer :: i p=k(1) !定义一个计数指针 k(1)%i=1 k(1)%next=k(2) !定义k(1)%next指向k(2) k(2)%i=2 k(1)%next=k(2) k(3)%i=3 nullify(k(3)%next) !定义k(3)的指针变量不再有指向! !连续输出的方法: i=1 do while (.true.) write (*,*) p%i if (.not. associated (p%next)) exit p=p%next end do stop end !----------------------------------------------------------------------- 循环运行过程: p=k(1) 输出p%i 即k(1)%i=1 p%next=k(2) p%next%next=k(3) p%next%next%i=k(3)%i=3 i=1, p=k(1)%next 即k(2) 输出p%i 即k(2)%i=1 i=2, p=k(2)%next 即k(3) 输出p%i 即k(3)%i=1 i=3, exit 数据结构为 k(1)=knoten(1,K(2)) k(2)=knoten(1,K(3)) k(3)=knoten(1,null()) Copyright © Zhousicheng
个人分类: 科研笔记|6097 次阅读|0 个评论
[转载]Fortran 语法备忘录 (初级)
热度 1 chenyuchen 2012-12-18 03:36
转自:https://sites.google.com/site/zhousicheng02/a_013 最简单的fortran程序: END 目录: 【注释】 【并行】 【续行】 【程序开始】 【常量】◆整型◆实型◆复型◆逻辑型◆字符◆例子◆其他进制 【变量】 【变量声明】◆类型说明种别说明◆属性说明◆例子◆赋初值 【数组】◆三元下标◆向量下标◆数组赋值◆数组运算◆WHERE构造◆FORALL构造◆函数名称◆动态数组 【派生类型】 【算术运算】 【关系运算】 【逻辑运算】 【判断IF构造】 【GOTO循环】 【DO循环】◆隐DO循环◆无循环变量◆跳出循环◆DO WHILE 【多重选择CASE构造】 【READ,WRITE 输入输出】◆文件◆WRITE语句◆READ◆I/O列表◆OPEN◆CLOSE◆定位语句◆内部文件 【FORMAT语句】 【一些说明】 【一些建议】 【注释】 整行注释,第一个字母为 C 或者 * 附加注释,与句后面用 ! !-------------------------------------------------------- ! 说明(F90) !-------------------------------------------------------- 【并行】 !-------------------------------------------------------- a=1.0 ; b=2.0 !-------------------------------------------------------- 【续行】 F90中一行为132列,允许有39个续行。在语句行最后加上续行符“”号。如果字符串被打断,跨2行以上,则在续行的开始位置也要加号。 第6列上写一个非空格和非零的字符(任何ASCII字符), 常用C 【程序开始】 !-------------------------------------------------------- PROGRAM main END PROGRAM main !-------------------------------------------------------- 【常量】 ◆整型 (默认4字节) 100_1 !单字节整型常量 2000 2000_4 !4字节整型常量 ◆实型 (默认4字节) print *, 3.14159265358979_8 ! 直接打印双精度数 print *, 3.14159265358979_4 ! 直接打印单精度数(7位有效数字) ◆复型 (默认2×4字节) (a,b)=(实数,实数) ◆逻辑型 (默认4字节) .TRUE. 和 .FALSE. a=.TRUE. ◆字符型 (F90用) name=char 例:I'm a boy. (长为10字节) 'I''m a boy.' (长为10字节) 字符子串: A(3:11) - 'CDE12345F', A(I+4:9) - 'E1234'(I=1),'1234'(I=2) A(:5) - 'ABCDE' A(11:) - 'FGH' A(:) - 'ABCDE12345FGH' A(3:3) - 'C' A=A(:5)//A(6:) !字符串的合并运算 求字符串长度的函数:LEN(字符串) 不计尾部空格的字符串长度函数:LEN_TRIM(字符串) 求子串在父串位置的函数:INDEX 验证字符串的函数:VERIFY 除去尾部空格函数:TRIM 比较字符大小的函数:LGE、LGT、LLE、LLT ◆例如: 21_2+7.6_4 表示整型种别为2的数21与实型种别为4的数7.6相加。 3.8E-5_4 0.87D-16 禁止说明种别值 (4.7_8,5) 复数表示用括号,逗号分开前面的实部(种别值为8的实数)和后面的虚部(缺省种别值的整数)。 .FALSE._4 表示逻辑型,其常数值是假,种别值是4。 1_'计算数学' 1_'αβγ' 字符串的字符不只限于Fortran字符集内,处理系统支持的图形符号也是允许的。 ◆整数的其他进制: 在F90中,还可以根据需要定义二进制、八进制和十六进制正整数常量。二进制常量的表示方法是以字母B开头,后跟定界符括起来的数字串,定界符可以是撇号或括号,数字是0或1,如:B'01011',B(01011)。八进制常量的表示方法是以字母O开头,后跟定界符括起来的数字串,数字范围是0至7,如:O'10472'。十六进制常量的表示方法是以字母Z开头,后跟定界符括起来的数字或字母串,数字范围是0至9,字母范围为A至F,如:Z'18E2D'。但要注意,整数的这些形式,仅限于出现在DATA赋值语句中。 【变量】 变量可用字符: 字母A-Y,数字0-9,下划线_ (最多31个字符) 数组 a(4) 【变量声明】 F90程序中的数据都有三个特征:类型、种别、属性 类型说明 :: 变量名表 例如:Real (KIND=4), DIMENSION(4) :: array !同:Dimension array(4) ◆类型说明 种别说明 整型说明语句 INTEGER( 1) 或 INTEGER*1 -128—127 INTEGER( 2) 或 INTEGER*2 -32768—32767 INTEGER( 4) 或 INTEGER*4 -2147483648—2147483647 缺省值 实型说明语句 REAL( 4) or REAL*4 通常实数的范围是10**-38—10**38之间的7位有效数字 缺省值 REAL( 8) or REAL*8 等价于双精度型 DOUBLE PRECISION 双精度说明语句 DOUBLE PRECISION 复型说明语句 COMPLEX( 4) or COMPLEX*8 缺省值 逻辑型说明语句 LOGICAL( 4) or LOGICAL*4 缺省值 字符型说明语句 CHARACTER( 1) ◆属性说明 属性关键字 描述 适用范围 ALLOCATABLE 说明动态数组 数组 AUTOMATIC 声明变量在堆栈中而不是在内存中 变量 DIMENSION 说明数组 数组变量 EXTERNAL 声明外部函数的名称 过程 INTENT 说明过程哑元的用意 过程哑元 INTRINSIC 声明一个内部函数 过程 OPTIONAL 允许过程被调用时省略哑元 过程哑元 PARAMETER 声明常量 常量 POINTER 声明数据对象为指针 变量 PRIVATE 限制模块中的实体访问于本块内 常量、变量或模块 PUBLIC 允许模块中的实体被外部使用 常量、变量或模块 SAVE 保存过程执行后其中的变量值 变量或公共块 STATIC 说明变量为静态存储 变量 TARGET 声明变量为目标 变量 VOLATILE 声明对象为完全不可预测并在编译时无优化 数据对象或公共块 ◆例如: REAL*8 a,b*4 REAL(8) a,b REAL(LEN=8) a,b !种类说明:( 种别值) INTEGER,PARAMETER :: Long=SELECTED_REAL_KIND(9,99) !属性说明 REAL :: A=2.8_LONG, B=1.23456789E60_LONG LOGICAL(KIND=2),DIMENSION(1:10) :: X !同DIMENSION*2 X(10) ?? CHARACTER(LEN=12,KIND=1) :: A,B CHARACTER(KIND=1,LEN=12) :: A,B CHARACTER(12,1) :: A,B CHARACTER*12 :: A,B CHARACTER(LEN=*),PARAMETER :: C_NAME='GIRL' !*号表示长度暂不确定 CHARACTER(LEN=*),PARAMETER :: C_NAME='BOY' CHARACTER(LEN=12) :: A,B*5,C,D*7,E !变量名后标出自己的特有长度 ◆赋初值: DATA 变量名表1/初值表1/ 变量名表2/初值表2/…] 例如: DIMENSION A(10,10) DATA A/100*1.0/ ! 按数组变量名统一初始化(重复次数*常数值) DATA A(1,1), A(10,1), A(3,3) /2*2.5, 2.0/ ! 按数组元素逐个初始化 DATA ((A(I,J),I=1,5,2),J=1,5) /15*1.0/ ! 按隐DO循环初始化 DATA cstuff/(-1.0,-1.0)/ !复数初始化 CHARACTER (LEN=10) name !字符串初始化 CHARACTER BELL, TAB, LF, FF, STARS*6 CHARACTER*8 help DATA name,STARS /'Zhang Fei','****'/ DATA BELL,TAB,LF,FF /7,9,10,12/ ! 用ACSII控制字符码赋于字符变量 DATA help(1:4),help(5:8) /2*'HELP'/ ! 用字符子串分段赋值 duiyul输入的数据可以是.TRUE.(真)或.FALSE.(假),也可以是以T或F字母开头的任何字符串 【数组】 !----------------------------------------------------------------------- real a(10) REAL A(10,2,3) ! 类型说明 DIMENSION A(10,2,3) ! DIMENSI0N语句 ALLOCATABLE B(:,:) ! ALLOCATABLE语句 POINTER C(:,:,:) ! POINTER语句 REAL,DIMENSION(2,5):: D ! 类型说明中的DIMENSION属性 REAL,ALLOCATABLE:: E(:,:,:) ! 类型说明中的ALLOCATABLE属性 REAL,POINTER:: F(:,:) ! 类型说明中的POINTER属性 !----------------------------------------------------------------------- 数组的秩至少是1。如果没有下标则指整个数组。下标必须用逗号隔开,可以用整型常量、变量或表达式,函数。 例如A指整个数组,A(1)指数组A的第1个元素,A(3:4)指数组A的第3和第4个元素,A(1:10:2)指的是数组A的第1,3,5,7,9个元素。数组片段本身也是数组。 ◆三元下标: : 。默认分别为最小、最大下标,步长默认为1。全部省略指数组全长。 例: !----------------------------------------------------------------------- REAL A(10) A(1:5:2)=3.0 !把元素A(1),A(3),A(5)置为3.0 A(:5:2)=3.O !把元素A(1),A(3),A(5)置为3.0,因为缺省下界为1 A(2::3)=3.O !把元素A(2),A(5),A(8)置为3.0,因为上界缺省值为10 A(7:9)=3.0 !把元素A(7),A(8),A(9)置为3.0,因为缺省步长为1 A(:)=3.0 !和A=3.0相同,将A的所有元素置为3.0 !----------------------------------------------------------------------- ◆向量下标 向量下标是一个一维整数数组(即向量),它可以从整个数组中选择片段。以任何顺序来指定数组元素。 例: !----------------------------------------------------------------------- REAL A(10),B(5,5) INTEGER I(4),J(3) I=(/5,3,8,2/) !定义向量I J=(/3,1,5/) !定义向量J A(I)=3.O !设置元素A(5),A(3),A(8),A(2)的值,同A(/5,3,8,2/) B(2,J)=3.O !设置元素B(2,3),B(2,1)和B(2,5)的值 !----------------------------------------------------------------------- 例: INTEGER :: a(4)=(/0,1,2,3/),b(3)=(/1,4,3/) 则a(b)与a同类型,与b同形状,取值为(/0,3,2/)。a(b)可以不是数组片段,而是更大的数组。如上面b为(/2,3,2,3,2,3/)时,a(b)为(/1,2,1,2,1,2/)。 (多对一数组片段,不可以在等号左边被赋值!) !----------------------------------------------------------------------- 例: CHARACTER(1) :: symbol(0:1)=(/'F','M'/) INTEGER :: bit(100) 若bit的元素列为0001101100111...,则symbol(bit)是用{F,M}字符构成的100字节的字符型数组FFFMMFMMFFMMM...。 !----------------------------------------------------------------------- ◆显式形状(Explicit-shape)数组 Dimension ( 上界 上界]…) 维界可以是常数或变量 ◆自动(Automatic)数组 用于子程序中的显型数组,维界可用变量表示,例: !----------------------------------------------------------------------- SUBROUTINE EXAMPLE(N,R1,R2) DIMENSION A(N,5),B(10*N) …… N=IFIX(R1)+IFIX(R2) !----------------------------------------------------------------------- 此例中的A和B都是自动数组。子程序被调用时,数组A和B的上界通过传入的变量N来确定,而以后N的值的变化对A和B的大小不会有影响。 ◆可调(Adjustable)数组 一个主程序的显型数组,作为子过程的一个哑元, 维界是哑元变量,或者常量。需要在子程序中用Dimension重新声明。例: !----------------------------------------------------------------------- DIMENSION A1(10,35) …… SUM1=THE_SUM(A1,10,35) END FUNCTION THE_SUM(A,M,N) DIMENSION A(M,N) SUMX=0.0 DO J=1,N DO I=1,M SUMX=SUMX+A(I,J) END DO END DO THE_SUM=SUMX END FUNCTION !----------------------------------------------------------------------- ◆假定形状(Assumed-shape)数组 作为子过程的一个哑元, 通过子程序来确定数组的维界(部分或者全部) 例: !----------------------------------------------------------------------- SUBROUTINE ASSUMED(A) REAL A(:,:,:) !数组A的秩为3,每一维的长度待定。当过程被调用时A将从传递的数组获得形状: REAL X(4,7,9) CALL ASSUMED(X) !----------------------------------------------------------------------- 于是A获得了数组维界(4,7,9),实际数组和假定形状数组的值必须相同。如果上面过程中数组声明了A(3:,0:,-2:),以哑元X(4,7,9)调用过程时,数组A的实际维界是A(3:6,0:6,-2:6)。 应用假定形状数组为哑元的过程时必须有显式的接口INTERFACE语句。 例: !----------------------------------------------------------------------- INTERFACE SUBROUTINE SUB(M) INTEGER M(:,1:,5:) END SUBROUTINE END INTERFACE INTEGER L(20,5:25,10) CALL SUB(L) END !----------------------------------------------------------------------- 在此例中数组M的维界是(1:20,1:21,5:14)。 ◆假定大小(Assumed-size)数组 这种数组是在过程中的哑元,它从实际传递过来的数组获得数组大小。除了最后一维的上界以外,其它所有特征(秩,长度和维界)都必须指定。声明一个假定大小数组时,最后一个的上界用星号*表示。它的一般形式是:( ... *)。 例: !----------------------------------------------------------------------- SUBROUTINE ASSUME(A) REAL A(2,2,*) REAL X(7) CALL ASSUME(X) !----------------------------------------------------------------------- 则数组X的元素与数组A的对应顺序是: X(1):A(1,1,1) X(2):A(2,1,1) X(3):A(1,2,1) X(4):A(2,2,1) X(5):A(1,1,2) X(6):A(2,1,2) X(7):A(1,2,2) A的第三维不是完整的,所以数组X不能作为哑元传递。 ◆延迟形状(Deferred-shape)数组 可分配数组必须以延迟形状的形式来声明。它每一维的长度只有在分配数组才被确定。声明迟形数组时,秩由冒号确定,但长度是未知的。 例: !----------------------------------------------------------------------- REAL,ALLOCATABLE:: A(:,:,:) INTEGER,ALLOCATABLE,TARGET:: K(:) !----------------------------------------------------------------------- 可分配数组可由下列方式声明:使用ALLOCATABLE语句、DIMENSION语句、TARGET语句或在类型声明中使用ALLOCATABLE属性。如果迟形数组以DIMENSION语句或TARGET语句声明,则在其它语句中必须给出ALLOCATABLE属性。 在迟形数组的大小、形状和维界没有确定之前,其任何部分都不能被引用,可分配数组的大小、形状和维界在ALLOCATE语句执行时才被确定。 例: !----------------------------------------------------------------------- INTEGER,ALLOCATABLE:: A(:,:) REAL,ALLOCATABLE,TARGET:: B(:,:),C(:) ALLOCATE(A(2,3),C(5),B(SIZE(C),12)) !----------------------------------------------------------------------- ◆数组赋值 (按列存储,从左到右) A(1,1) A(2,1) A(1,2) A(2,2) A=0 A(1:20)=A(20:1:-1) ! 将A数组元素值倒序重排 ◆数组构造器是由括号和斜线之间的一系列值或隐DO循环组成。其一般形式为:(/取值列表/)。取值列表可以是标量,隐DO循环或任意秩的数组。 例: INTEGER A(6) A=(/1,2,3,4,5,6/) ! 斜杠与括号间不能有空格 C1=(/4,8,7,6/) ! 标量表示 C2=(/B(I,1:5),B(I:J,7:9)/) ! 数组表示 C3=(/(I,I=1,4)/) ! 隐DO循环 C4=(/4,A(1:5),(I,I=1,4),7/) ! 混合表示 ◆用方括号代替括号和斜线,例如下面两个数组构造器是等价的: INTEGER C(4) C=(/4,8,7,6/) C= ◆冒号三元下标(代替隐DO循环)来指定值的范围和步长,例如下面两个数组构造器是等价的: 例:INTEGER D(3) D=(/1:5:2/) ! 三元下标格式 D=(/(I,I=1,5,2)/) ! 隐DO循环格式 ◆数组运算 !----------------------------------------------------------------------- A=B ! 并行处理 do i=1,n A(i)=B(i) end do !串行处理 !----------------------------------------------------------------------- ◆WHERE构造 WHERE语句的一般形式为:WHERE(屏蔽表达式) 赋值语句 WHERE构造的一般形式为: !----------------------------------------------------------------------- WHERE(数组逻辑表达式1) ] ] END WHERE !----------------------------------------------------------------------- ◆FORALL构造 FORALL是F95的新增功能。它是数组屏蔽赋值(WHERE语句和构造)功能的一对一元素的推广,其方式有FORALL语句和FORALL构造。 FORALL语句的一般形式为:FORALL(循环三元下标 … ) 赋值语句 FORALL构造的一般形式为: !----------------------------------------------------------------------- FORALL(循环三元下标 … ) END FORALL !----------------------------------------------------------------------- 屏蔽表达式是数组逻辑表达式,缺省值为.TRUE.。块是赋值语句,或为WHERE语句或构造,或为FORALL语句或构造。 循环三元下标的形式为:循环变量=下界:上界 。循环变量是整型的。步长不能为0,缺省值为1。 ◆函数名称 描述 ALL(mask ) 判断全部数组值在指定维上是否都满足mask的条件 ANY(mask ) 判断是否有数组值在指定维上满足mask的条件 COUNT(mask ) 统计在指定维上满足mask的条件的元素个数 CSHIFT(array,shift ) 进行指定维上的循环替换 DOT_PRODUCT(vector_a,vector_b) 进行两个向量的点乘 EOSHIFT(array,shift ) 在指定维上替换掉数组末端,复制边界值到数组末尾 LBOUND(array ) 返回指定维上的下界 MATMUL(matrix_a,matrix_b) 进行两个矩阵(二维数组)的乘积 MAXLOC(array ) 返回数组的全部元素或指定维元素当满足mask条件的最大值的位置 MAXVAL(array ) 返回在指定维上满足mask条件的最大值 MERGE(tsource,fsource,mask) 按mask条件组合两个数组 MINLOC(array ) 返回数组的全部元素或指定维元素当满足mask条件的最小值的位置 MINVAL(array ) 返回在指定维上满足mask条件的最小值 PACK(array,mask ) 使用mask条件把一个数组压缩至vector大小的向量 PRODUCT(array ) 返回在指定维上满足mask条件的元素的乘积 RESHAPE(source,shape ) 使用顺序order和补充pad数组元素来改变数组形状 SHAPE(source) 返回数组的形状 SIZE(array ) 返回数组在指定维上的长度 SPREAD(source,dim,ncopies) 通过增加一维来复制数组 SUM(array ) 返回在指定维上满足mask条件的元素的和 TRANSPOSE(matrix) 转置二维数组 UBOUND(array ) 返回指定维上的上界 UNPACK(vector,mask,field) 把向量在mask条件下填充field的元素解压至数组 ◆用ALLOCATE语句动态分配数组 ALLOCATE语句动态创建可分配数组,使内存和对象相联系。分配的对象可以被命名为任何有ALLOCATABLE属性的变量。它的一般形式为: ALLOCATE(数组名 )] ] ... )。 例: !----------------------------------------------------------------------- REAL A(:),B(:,:,:) ALLOCATABLE A,B ALLOCATE(A(- ◆数组查询: 数组形状 shape(a) m n 数组大小 size(a) mxn 数组下限 lbound(a) 1 1 数组上限 ubound(a) m n 【派生类型】 !-------------------------------------------------------- TYPE 派生类型名 成员1类型说明 成员n类型说明 END TYPE !-------------------------------------------------------- TYPE是关键字,表示派生类型定义开始。访问属性说明关键字是PUBLIC或PRIVATE,默认值是PUBLIC,即访问方式是共用的。PRIVATE表示该类型是专用的,这个关键字只有当TYPE块写在模块说明部分中时,才允许使用。如果不是在模块内定义的派生类型,不可使用PRTVATE。 例如 : !-------------------------------------------------------- TYPE CLASS_TYPE !年级_类 CHARACTER(LEN=50) :: DEPARTMENT, MAJOR !系,专业 END TYPE CLASS_TYPE TYPE SCORES_TYPE !成绩_类 INTEGER(1) :: MATH, PHYSICS, ENGLISH END TYPE SCORES_TYPE TYPE STUDENT_TYPE SEQUENCE !按定义的顺序储存各个成员 INTEGER(4) :: NUMBER !学号 TYPE(CLASS_TYPE) :: CLASS !班级_类(系,专业) CHARACTER(LEN=10) :: NAME, ADDRESS !姓名,地址 TYPE(SCORES_TYPE) :: SCORES !分数_类 END TYPE STUDENT_TYPE TYPE(STUDENT_TYPE),DIMENSION(40):: STUDENT !学生数组(40)有类别 TYPE(STUDENT_TYPE) Myself !-------------------------------------------------------- 其中:STUDENT(6)%SCORES%PHYSICS !第6个学生的物理成绩,该类型成员值 初始化一个成员:STUDENT(666666, 'BWL', '2004', 'John', 'Str. 1', 100, 100, 100) !该类型的值 用DATA初始化:DATA Myself/STUDENT(666666, '2004', 'BWL', 'John', 'Str. 1', 100, 100, 100)/ 个别元素初始化:DATA Myself%NUMBER, Myself%ENGLISH/88888, 99/ 显示初始化: TYPE(REPORT),PARAMETER :: SHE=STUDENT(0,'','','','',0,0,0) ◆结构构造函数(派生类型名) ME%CLASS=CLASS_TYPE(2004,BWL) !引用CLASS_TYPE的结构构造函数,进行赋值 SHE=STUDENT_TYPE(22222,ME%CLASS,'Sherry','Str. 2',SCORES_TYPE(60,60,60)) 【算术运算】 单项运算:(例 -a) 2项运算:加(+),减(-),乘(*),除(/),乘方(**) 乘方按“先右后左”,其它按“先左后右”原则 运算的优先顺序::① 括号 ② 函数 ③ ** ④ * / ⑤ + - 【关系运算】优先级低于【算术运算】 .GT. .GE. = .LT. .LE. = .EQ. == .NE. /= 【逻辑运算】优先级低于【关系运算】 .AND. .OR. .NOT. (单元素操作 .NOT.A) .EQV. .NEQV. 顺序是:.NOT. > .AND. > .OR. > .EQV. 和 .NEQV. 【判断IF构造】 IF构造中的每一个块,都可以嵌入一个完整的构造。但不可跨越2个块。嵌套IF需要加构造名。 !-------------------------------------------------------- IF (逻辑) THEN ... ELSE IF (逻辑) THEN ... ELSE ... END IF !-------------------------------------------------------- 块IF的执行步骤为:先执行块IF语句,求出逻辑表达式的值,如果此值为“真”,则将流程转到then块。执行then块中各个执行语句。执行完then块后跳过ELSE语句和else块,流程转到END IF语句处。如果逻辑表达式的值为“假”,则流程跳过then块,转到ELSE语句及else块。执行完else块以后流程转到END IF语句。 【GOTO循环】慎用! !-------------------------------------------------------- GOTO (语句标号) !-------------------------------------------------------- 【DO循环】 !-------------------------------------------------------- DO ] 循环变量=初值式,终值式 ... END DO !-------------------------------------------------------- 循环次数可以从循环初值、终值和步长计算出来:次数=INT((终值-初值+增量)/增量) 例:对于DO I=1.5,3.6,1.2 不要根据INT((3.6-1.5+1.2)/1.2)=2而认为循环次数为2,而应当先将实型量转化为整型量,即变成相当的循环语句 DO I=1,3,1 其循环次数为3次而不是2次。 例:对于DO X=1.5,3.6,1.2 由于循环变量不是整型的而是实型的,它的循环次数为2次。X取值分别是1.5,2.7。 ◆隐DO循环只能作为输入输出表的一部分出现 (I/O列表,循环变量名=初值,终值,增值) 例如: READ *,(VALUE(I),I=1,20) 表示读入VALUE(1),VALUE(2),…,VALUE(20)的值。 WRITE(*,*) (A,B,N=1,5) 表示在当前设备用默认格式重复输出A、B的值5次。 隐DO表可以嵌套, 例如:PRINT *, ((A(I,J), I=1,3), J=1,3) ◆无循环变量的DO构造 !-------------------------------------------------------- DO ... END DO !-------------------------------------------------------- ◆跳出循环方法: EXIT CYCLE 没有构造名,则默认跳出为最内层的循环 可以用IF语句进行判断: !-------------------------------------------------------- DO ... IF(逻辑表达式) Exit/CYCLE ... END DO !-------------------------------------------------------- ◆DO WHILE 当型循环 !-------------------------------------------------------- DO WHILE (逻辑表达式) ... END DO !-------------------------------------------------------- 【多重选择CASE构造】 !-------------------------------------------------------- SELECT CASE(case表达式) CASE(case选择符) ... ... END SELECT !-------------------------------------------------------- 程序执行时,CASE构造的控制机制是:(1)控制进入CASE构造后,先计算情况表达式的值;(2)如果第一个CASE语句选择符值与情况表达式值相等则执行语句块1,转出口;(3)如第一个选择符值不为情况表达式的值,再查下一个CASE语句的选择符值,满足执行语句块2,转出口,不满足再查下一个CASE语句的选择符值,直至全部情况选择符值都检查完;(4)如果全部情况选择符值都不符情况表达式的值,且又有CASE DEFAULT语句,则执行该语句后的DEFAULT块,否则直接转出口。 其中,case表达式是整型、字符型或逻辑型表达式,不能是实型和复型表达式。 (值表) 表示等于该值,各值之间用逗号分开 (下界:) 表示大于或等于该值 (:上界) 表示小于或等于该值 (下界: 上界) 表示在这两个值之间 (包括等于) 【READ,WRITE 输入输出】 READ *, a, b READ (*,*) a, b PRINT *, 'a=' , a , 'b=' , b WRITE *, WRITE (*,*) WRITE (*,/) Fortran支持两种文件的访问方式(顺序访问和直接访问)和三种文件的结构(有格式、无格式、二进制)。顺序访问或直接访问可以用于这三种结构的文件进行的每一种。因此,共有6种文件类型。 ◆格式化文件,记录数据内容的记录是以ASCII字符的方式存在的,每一条记录是以ASCII码中的回车符CR(0D)加换行符LF(0A)来结束的,可以用文本编辑软件打开格式文件并直接看懂其内容。OPEN语句默认的打开文件是格式文件 ◆无格式文件,由一系列物理块组成的记录组成,所存储的记录序列的存放方式与其在内存中的存放非常相似,所以在输入输出时几乎不需作转化。 ◆二进制文件,是处理最快、最简洁的一种文件,也是最紧凑的存储格式,适合于大批量数据的存储。在程序中可以用带有FORM=’BINARY’选项的OPEN语句来打开或建立二进制文件。 ◆顺序文件,数据必须一个记录接一个记录地按顺序被访问。也就是说,程序中要读写第N条记录时,必须至少已对前面的N-1记录进行过读操作。 ◆直接访问文件,记录可以以任意顺序进行读写操作。文件中的记录从1开始连续编号,记录的长度是通过OPEN语句中的RECL选项来描述的。直接文件中的记录是通过指定要访问的记录号来实现的。因此,如果想要实现数据的随机访问可以使用直接访问文件。直接文件应用的一个最常见的实例就是数据库。直接文件中的每个记录的长度必须相等。用直接方式建立的文件可以使用顺序方式打开进行读操作。用顺序方式建立的文件,只要记录长度相等,也可以用直接方式打开进行读操作。 !----------------------------------------------------------------------------------------------- ◆格式化顺序文件(默认) —个格式化文件是一个由按顺序写到文件中的有格式记录序列组成的,当要对文件进行读操作时,读取的顺序就是记录在文件中的存放顺序。文件中记录的长度不一定相同,记录也可以是空的。记录用回车符(0DH)和换行符(0AH)分开。 ◆格式化直接文件 在格式化直接文件中,所有记录的长度都相同并且可以以任意顺序读写。记录的长度由OPEN语句中的RECL=选项指定,该长度应该大于或等于最长的记录中的字节数。CR和LF是分隔符,不包括在RECL中。一旦某个直接访问记录被写入就不能再删除它,但可以覆盖这个记录。在输出到一个格式化直接文件时如果数据没有占满一个记录,则编译系统将在剩下的位置上补以空格,保证文件只包含长度相同的完整的记录。从文件中读数据时,当I/O列表或格式描述符中要读取的数据多于记录中的数据时,编译器也会以空格填充未读数据的变量。可以通过在打开文件的OPEN语句中设置PAD=NO来避免填补空格,此时输入记录必须有和输入列表和格式描述符所要求的一样多的数据,否则会产生错误。PAD=NO对输出没有影响。 ◆无格式顺序文件 无格式顺序文件中记录的长度可以不同,文件以130或少于130字节为一个物理块进行组织。每个物理块包含着输入到文件中的数据(最多128字节),编译系统在物理块之间加入两个1字节长的长度值以说明每个记录的起始和结束位置。一个逻辑记录包含一个或多个物理块,其大小可在程序中指定,编译系统会相应地使用需要数量的物理块。 ◆无格式直接文件 无格式直接文件是一系列非格式的记录,可以以任意顺序读写记录。记录的长度都相同,由OPEN语句中的RECL=选项指定。没有字节分隔符或其它表示记录结构的字节。 ◆ 二进制顺序文件 二进制顺序文件是一系列按同一顺序和同样二进制数个数来读写的值。其中没有记录边界,没有说明文件结构的特殊字节。数据读写时长度和形式都不改变。数据记录的长度可以不等。对于任何输入输出数据,内存中的字节序列就是文件中的字节序列。二进制顺序文件是处理最简洁、速度最快的文件。 ◆二进制直接文件 二进制直接文件存储一系列二进制数记录,它们可以按任何顺序访问。与二进制顺序文件不同的是,这些记录的长度是相等的,由OPEN语句中的RECL=选项指定。在二进制直接文件中可以写入部分记录,记录中末使用的部分将以未定义数据填充。 在二进制直接文件中可以只使用一条读或写语句来读写到多于一条的记录,而这样的操作在无格式直接文件中将引发错误。在无格式直接文件中所能进行的一切操作在二进制直接文件中都是合法的,另外,在二进制直接文件中提供了一种不依赖于填充二进制数据O的对记录的某部分进行读写操作的功能。二进制直接文件是所有6类文件中使用最灵活的一类。 ◆WRITE语句 !-------------------------------------------------------- WRITE ({ 单元|*} 格式说明符| 名称列表组名|*}] ) !-------------------------------------------------------- 如果省略UNIT=,则第一个参数必须是“单元”。如果省略了FMT=或NML=,则格式说明符或名称列表组名必须是第二个参数(格式化文件)。其后的几项参数次序可以任意。 单元:外部文件时是一个指定设备号的整型表达式,内部文件时是一个字符串、变量、数组、数组元素或非字符数组。 格式说明符:对于格式写操作是必需的,非格式写操作时不能有。 名称列表组名:如果它被说明,则I/O列表必须省略。 记录号:一个整数表达式指定要被写的记录序号,仅用于直接文件。文件中的第一条记录的记录号为1,缺省值为文件中的当前位置。 状态变量名:一个整型变量、数组元素。当无错误时,它返回值为0,有错误时则返回错误信息号。 错误标号:在同一个程序单位中的一个可执行语句的标号。如果指定了它,I/O错误将把控制传递给此标号处的语句,省略时取决于状态变量名的存在与否。 ◆READ语句 !-------------------------------------------------------- READ({ 单元|*} 格式说明符| 名称列表组名|*}] ) !-------------------------------------------------------- 文件结束标号:读到文件结束记录时把控制传递给标号处的语句。 记录结束标号:读完一个记录时把控制传递给标号处的语句。 ◆I/O列表 在内存中的变量和外部设备及外部文件之间传递数据 格式化I/O (见Format语句) 直接列表I/O 一系列由逗号或空格分开的值,分隔符(逗号,斜杠或空格),斜杠(/)将结束输入,逗号间为空值 名称列表I/O NAMELIST /名称列表组名/变量列表 /组名/变量列表]... 例如:名称列表输出:WRITE(*, namelist) ◆OPEN语句详细说明 OPEN( unit ) 其中的各项参数的意义及取值如下: 1) UNIT:设备号说明。unit是大于或等于0的正整数,设备号说明是OPEN语句的第—项时可以省略UNIT= 2) ACCESS:存取方式说明。access是字符串表达式: APPEND 追加方式 SEQUENTIAL 顺序访问方式 DIRECT 直接访问方式 当省略此说明项时为顺序访问方式。 3) ACTION:描述文件的读写属性。action是字符串表达式: READ 文件为只读方式打开 WRITE 文件为只写方式打开 READWRITE 文件为可读写方式打开 当省略此说明项时,文件打开顺序:READWRITE-READ-WRITE。 4) BLANK:说明数据格式输入字段中空格的含义。blank是字符串表达式: NULL 空格忽略不计,相当于在格式描述符中的BN编辑符 ZERO 空格处理成数字0,相当于BZ编辑符 当省略此说明项时为ZERO。此说明只能用于格式输入。 5) BLOCKSIZE:指定以字节为单位的设备缓存的大小,默认值为一4字节整数。 6) CARRIAGECONTROL:指明处理文件中的第一个字符的方式,其值为字符串表达式: Fortran 对第一个字符作一般的Fortran解释 LIST 指出在文件的每两个记录之间有—个空格 默认状态下,对于连接到打印机和显示器这样的设备,设置值为Fortran,对于连接到文件的设备, 设置值为LIST。当FORM被设成UNFORMATTED和BINARY时,其值被忽略。 7) DELIM:指明分隔直接列表或格式化名称列表记录的方式,其值为字符串表达式: APOSTROPHE 用单撇号(')分隔 QUOTE 用双撇号()分隔 NONE 不用分隔符 如果在OPEN语句中设置了分隔符,则在文件中的单撇号和双撇号都是成对出现的。 8) ERR:出错处理说明。其值是同一程序中的一条语句的标号,当OPEN语句执行出错时执行此语句。 如果省略该项,则出错时给出出错信息并终止运行。 9) FILE:文件名。是一字符串表达式,可以是空、合法的数据文件名字、设备名字或是作为内部文件的变量。 在WinNT/9x中允许使用长度大于8的文件名和长度大于3的文件扩展名。省略此项时,编译器将自动产生 一个文件名唯一的临时文件,这个临时文件将在结束运行或与文件连接的设备关闭后被删除掉。 10) FORM:记录格式说明。form是字符串表达式: FORMATTED 记录按有格式存放。 UNFORMATTED 记录按无格式存放。 当省略此说明项时为:对顺序文件是有格式的;对直接文件是无格式的。 11) IOFUS:指出一个新Quickwin子窗口是否为活动窗口,其值为逻辑值。缺省值为真。 12) IOSTAT:出错状态说明。iostat是—个缺省长度为4的整形变量。当执行此OPEN语句时系统给变量赋值: 零 没有发生错误 负数 文件结尾 正数 发生错误,其值视具体计算机系统而定 若省略该项则没有此功能。 13) PAD:从格式化文件中记录的数据少于要读取的数据时,是否用空格来填充没有从记录中读到数据的变量。 pad是字符串表达式: YES 填充(默认值) NO 不填充 14) POSITION:指定打开顺序文件的访问位置,position是字符串表达式: ASIA 已被连接的文件的访问位置是固定的,未被连接的文件的访问位置是文件的开始处。 REWIND 把文件的访问位置定在文件的开始处(文件己存在)。 APPEND 把文件的访问位置定在文件的末尾处(文件己存在)。 对于一个新文件,文件的访问位置总是被定在文件的开始处。 15) RECL:记录长度(单位为字节)说明。recl是指定的正整型量或算术表达式,用来指定直接文件中的 每条记录的字节数,或顺序文件中的记录的最大长度。 16) SHARE:指明当文件打开时是否实现文件的锁定。share是字符串表达式: DENYRW 动态读写模式。不允许其他的进程打开这个文件。 DENYWR 动态写模式。不允许其他的进程以写的方式打开这个文件。 DENYRD 动态读模式。不允许其他的进程以读的方式打开这个文件。 DENYNONE 默认的非动态模式。允许其他的进程打开这个文件。 17) STATUS:文件状态说明。status是字符串表达式: OLD 表示指定的文件是已经存在的老文件。 NEW 表示指定的文件尚不存在。 SCRATCH 表示与设备号相连接的文件在关闭时将被自动删除。该文件为程序运行过程的一个临时文件。 REPLACE 表示替换一个有相同名字的文件,如果没有同名的文件存在,将产生一个新文件。 UNKNOWN 表示文件可以是已存在的或不存在的。系统打开文件状态的次序为:OLO-NEW-创建新文件。若省略该项时默认的状态为UNKNOWN。 ◆CLOSE语句 CLOSE语句解除设备号与文件的连接,又称关闭文件。它的一般形式为: CLOSE( unit ) 其中除STATUS以外的各项参数的意义及取值与OPEN语句中的相同。STATUS是文件关闭后状态说明,其值是一字符串: DELETE 与设备连接的文件不保留,被删除 KEEP(或SAVE) 与设备号连接的文件保留下来不被删除 PRINT 将文件递交给打印机打印并被保留(仅对顺序文件) PRINT/DELETE 将文件递交给打印机后被删除 SUBMIT 插入一个进程以执行文件 SUBMIT/DELETE 插入一个进程以执行文件,当插入完成后被删除 默认设置将删除带有SCRATCH属性的临时文件,对其它文件为KEEP。 ◆文件指针定位语句 REWIND语句: 称为反绕语句,它使指定设备号的文件指针指向文件的开头,通常用于顺序文件的操作。它的一般形式为: REWIND {unit|( unit ) BACKSPACE语句: 称为回退语句,它使指定设备号的文件指针退回一个记录位置,一般用于顺序文件。它的一般形式为: BACKSPACE{unit|( unit ) 除了以下几种情况外,使用BACKSPACE语句正好使文件的指针向前移动一条记录:本条记录前再没有记录时文件指针的位置不变;文件指针的位置在一条记录的中间时,文件指针移到本条记录的开始处;本记录的前—记录是文件结束记录时,文件指针移到文件结束记录之前。 ◆内部文件 read(str,*) name !从str字符串中读取名字 read(str,*) i,j,k !从str字符串中读取数字i,j,k, zB.atr= 1 2 3,则i=1,j=2,k=3 write(str,'(Test, I4.4, dat)') 15 !str=Test0015.txt 【FORMAT语句】I/O操作方式之一 !-------------------------------------------------------- WRITE(*,100) Number 100 Format(I4) !FORMAT语句标号 !-------------------------------------------------------- ASSIGN 100 TO Mystyle WRITE(*, Mystyle) Number 100 Format(I4) !整型变量名 !-------------------------------------------------------- Mystyle='(I4)' !字符表达式或变量 WRITE(*,Mystyle) Number !-------------------------------------------------------- WRITE(*,'(I4)' ) Number !格式列表 !-------------------------------------------------------- CHARACTER(6) array(3) DATA array/'(I5’, ',I4', ',A16) '/ !也可以用数组 WRITE(*,array) I,I, Very good! !-------------------------------------------------------- 可重复的编辑符 ◆I编辑符 Iw w指定字段宽度,m表示需要输出的最少数字位数,不足添0 ◆F编辑符 Fw.d w指定字段宽度,d是输出数据的小数位数 ◆E编辑符 Ew.d w仍为字段宽度,d为以指数形式出现的数据的数字部分的小数位数 w≥d+7 ◆G编辑符 Gw.d 绝对值小于0.1或大于10**d的数用E格式,否则用F格式,有效位数为d位 ◆D编辑符 Dw.d 双精度 ◆L编辑符 Lw 值为“真”的,在输出一个字母T,“假”则以一个字母F表示,w1时左边补空格 ◆A编辑符 A 字符串的长度小于w,则左边补空格,不指定字段宽度w则按字符变量的的长度截取 ◆B、O、Z编辑符 二进制(B)、八进制(O)和十六进制(Z) Bw ,Ow ,Zw ◆EN、ES编辑符 工程计数法(EN)和科学计数法(ES) ENw.d ESw.d 下面的为不可重复的编辑符 ◆引号编辑符 ' '和 。例如WRITE(*, '('I''m a boy')') 或 WRITE(*, '(I 'm a boy)') ◆H编辑符 nH字符串, 例如WRITE(*,'(9HI 'm a boy)') ◆X编辑符 输出时产生空格 ◆斜杠/编辑符 从下一行开始输出下一个记录,用n个连续的斜杠,可以达到输出n-1个空行的效果 斜杠只表示结束本行输出,当扫描到右括号而列表中已无数据项时,输出即告结束。 ◆\和$编辑符 输出一个记录行后取消回车符,例如Write(*,'(Please Enter Your Age =,$)') ◆T编辑符 Tn,TLn,TRn 分别为第n个字符,左移和右移n个字符 ◆:编辑符 当I/O列表中没有更多的数据顶时,冒号(:)编辑符使格式控制结束 ◆P编辑符 改变小数点位置 ◆S编辑符 SP输出正号,SS不输出 ◆BN,BZ编辑符 BN忽略数字输入字段中内嵌和后续空格,BZ使开头、结尾空格和分散空格为零 空格式format(),用于WRITE中表示回车换行,READ中表示跳过相邻的下一个记录 “输入数据自带小数点优先”原则,F、E、EN、ES、G和D编辑下的输入中,输入区域的显式小数点将覆盖编辑描述符中对小数点位置的指定。例如,READ(*,’(F4.2)’) x当输入123.4时,x=123.4而非按格式指定的23.40。 【一些说明】 ◆Fortran允许使用的字符如下: 英文字母 :A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 阿拉伯数字 :0 1 2 3 4 5 6 7 8 9 特殊符号 :空格 = + - * / ( ) , . ': ! % ; $ ? _ (F90中新增的字符) ◆F90的语句顺序, 图片 ◆F90的运算优先级, 图片 ◆F90的常用函数, 图片 ◆声明时用::表示声明的变量的属性已经声明完全, 左边为变量的类型、种别、属性,右边为变量 ◆整数相除的商也是整数:5/2=2 , 1/2=0 ◆在判断两个实数是否相等时,最好判断|a-b| ε ◆取得随机数的方法: !-------------------------------------------------------- CALL RANDOM_SEED CALL RANDOM_NUMBER(a) ! a=0~1 !-------------------------------------------------------- 【一些建议】 ◆开始编制一个程序前,做好前期准备,画 流程图 ,或者用伪代码描述整个过程的算法。 ◆用Program语句,并且给其命名。 ◆用IMPLICT NONE语句,并自己声明所需要的每一个变量,避免出错。 ◆对于多重循环采用标签
个人分类: 科研笔记|6329 次阅读|1 个评论
python和fortran90的混编
zhoufcumt 2012-11-4 20:27
一个python和fortran90混编的例子 fortran90程序pow.f90: SUBROUTINE pow(x,n,p) IMPLICIT NONE !f2py intent(in) x !f2py intent(in) n !f2py intent(out) p REAL(KIND=8) :: x,n,p p = x**n END SUBROUTINE pow 这三个 !f2py intent(in) x !f2py intent(in) n !f2py intent(out) p 必须要有! 接着终端: f2py -m pow -c pow.f90 然后test.py内容: #!/usr/bin/env python import pow as pw x = 3. n = 3.5 p = pw.pow(x,n) print p 接着终端输入./test.py即可,下面继续深入python和fortran混编。
个人分类: Python|8891 次阅读|0 个评论
[转载]python和fortran之间的参数传递
zhoufcumt 2012-10-31 22:36
转载自:http://ifelseif.i.sohu.com/blog/view/151864079.htm 这个页面已经说的很详细了http://cens.ioc.ee/projects/f2py2e/usersguide/index.html#three-ways-to-wrap-getting-started 不过我还是从中提炼出几个特别需要注意的地方,以备自己日后查考 1. 数据类型,numpy默认的数据类型为float64也就是双精度浮点数,所以fortran里的浮点数也必须是双精度的,需要声明为real*8,默认的real是单精度的,不会报错但是会造成内存溢出! subroutine foo(nrow,ncol,array) integer nrow,ncol real*8 array(nrow,ncol) blahblahblah end subroutine 2. 有三种交互编译方法,最简单的是直接编译 pythonc:\python26\scripts\f2py.py-c--fcompiler=gnu95--compiler=mingw32-lmsvcr71-mfoofoo.f90 让f2py自己去认输入输出,但这样一来输入输出就不好控制,尤其是当传递数组的时候,要控制fortran程序是否把结果回写到原数组,需要更精确的定义。f2py提供了更精细的方法,先生成signature文件,手工改动后再编译 python c:\python26\scripts\f2py.py foo.f90 -m foo --overwrite-signature -h foo.pyf python c:\python26\scripts\f2py.py -c --fcompiler=gnu95 --compiler=mingw32 -lmsvcr71 foo.pyf foo.f90 最后还有一种方法,就是直接把signature信息当作注释写在f90文件中,只需要编译一下就可以了。 3.加intent(out)之后会把python对应函数中的相应变量隐藏,也就是不需要输入了!例如fortran中的函数为 subrouting foo(a,n) 给n维数组a加了intent(out)之后,表示它只是输出,不接受输入数据 于是在python中调用时,只需要指定维数n就可以了 foo(n)
个人分类: Python|5071 次阅读|0 个评论
[转载]Fortran实例——利用Eclipse建立工程与编译、运行、调试
surveying 2012-10-25 14:43
Fortran实例——建立工程与编译、运行、调试 涉及软件:Intel Fortran、Eclipse、gfortran/gdb 本文主要介绍应用Intel Fortran进行编译和调试。使用gfortran/gdb的方法是同样的,甚至更为简单——通常你不需要做任何额外设置。需要gfortran/gdb的请先检查是否安装 sudo apt-get install gfortran gdb 建立工程 第一步: 选择 “File” -》 “New” -》 “Project” 第二步: 在出现的向导窗口中选择”Fortran”-》”Fortran Project”,点击Next 第三步: 在最上面的”Project name”中输入工程名称,如”hello”。在”Project type”中选择Static Library-》Executable(Intel(R) Fortran)。点击Next,然后直接点击Finish。 会询问你是否切换到Fortran试图(Fortran perspective),确定。 至此,一个空的新工程已经建立完毕。现在写点代码以备后用。 第四步: 选择 “File” -》 “New” -》 “Source File” ,在”Source file”文本框中输入文件名,如hello.f90。点击Finish。 写个Hello world program hello implicit none write ( * , * ) "hello world" end program hello 编译与运行 由于在建立工程时已经选择了Intel的配置,这里直接用编译命令即会调用ifort(实际是在自动生成的makefile里设置了编译器为ifort) 编译: 选择 “Project” -》 “Build All” ,或者使用快捷键Ctrl+B。 运行: 选择 “Run” -》 “Run” ,或者使用快捷键Ctrl+F11。第一次运行会出来一个对话框,选”Local Fortran Application”,OK。接着会要求选择调试的配置,选”Intel(R) Debugger”,OK。 在底部的Console窗口里可以看到运行结果 调试 默认没有设置起始断点,所以选择先配置。 选择 “Run” -》 “Debug Configurations…“ 。点击”Fortran Local Application”-》”hello”(如果没有可以双击Fortran Local Application新建一个) 切换到Debugger选项卡,勾选”Stop on startup at”,并将后面的值改为”hello”(如果你的程序名是main就不用改)。点击Apply,然后点击Debug。 询问你是否切换到调试试图,Yes。 设置断点、单步等常见调试命令可以在Run菜单下找到。 视图 Eclipse中不同类型任务有不同视图。比如编译Fortran源码在Fortran视图,调试在调试视图。在上一步里,我们处在调试视图下,要返回Fortran视图 选择”Window”-》”Open Perspective”-》”Other”,选择Fortran,确定 或者。在右上角有目前打开的各个视图的列表,选择Fortran的视图,如图所示
11203 次阅读|0 个评论
基本搞定emacs下fortran90编程配置
热度 1 zhoufcumt 2012-10-20 23:24
感谢华师大夏师弟的指导,终于对emacs有了入门,并经过今天一天的奋斗,将fortran90的基本配置搞定了,输入i2pa,然后tab下直接出来integer(kind=2), parameter :: ,然后do、if等类似的配置也用快捷键搞定。实验了一个小的程序,效率相当之高,感觉vim和emacs相比还是有差距的,emacs的宏定制太随心所欲了,爽歪歪,等熟练起来,效率还得提高更多,期待!下一步配置下latex的,这样完全抛弃win的日子指日可待了!
个人分类: EMACS|4998 次阅读|1 个评论
[转载]fortran小结
surveying 2012-10-11 16:13
http://blog.sina.com.cn/s/blog_6706aca50100q17k.html 目录: 一、说明 二、概述 三、数据类型及基本输入输出 四、流程控制 五、循环 六、数组 七、函数 八、文件 一、说明 本文多数内容是我读彭国伦《Fortran 95 程序设计》的笔记。只读到第九章,主要是3~9 章,都是最基本的用法(原书共16章)。这里主要摘录了我看书过程中总结的一些Fortran和C不 同的地方,主要是语法方面。希望这份笔记能够给学过C但没有接触过Fortran的同学带去一些帮 助。要想得更清楚些,推荐看一下原书,觉得作者真的写得很好,很清楚;如果有C语言的基础, 看完前九应该很快的,花一两天就行了。觉得如果耐心看完本文,基本功能应该也可以顺利用起 来了。外,由于我之前没有用过Fortran,这次为了赶文档看书又看得很粗浅,大多数东西看过 之后都没得及仔细想,只是按着作者的意思去理解。所以这份笔记还处于纸上谈兵的层次。如果 有不妥的方,希望大家指正。谢谢! 文中 蓝色 的部分是程序代码, !后面的内容为注释 。 二、概述 1、名词解释 Fortran= For mula Tran slator/Translation 一看就知道有什么特色了:可以把接近数学语言的文本翻译成机械语言。的确,从一开始 ,IBM设计的时候就是为了方便数值计算和科学数据处理。设计强大的数组操作就是为了实现这一 目标。ortran奠定了高级语言发展的基础。现在Fortran在科研和机械方面应用很广。 2、Fortran的主要版本及差别 按其发展历史,Fortran编译器的版本其实很多。现在在广泛使用的是Fortran 77和Fortr an90。ortran 90在Fortran 77基础上添加了不少使用的功能,并且改良了77编程的版面格式, 所以编程时推荐使用90。鉴于很多现成的程序只有77版本,有必要知道77的一些基本常识,至少保 证能够看77程序。以下是77和90的一些格式上的区别。 Fortran 77: 固定格式(fixed format),程序代码扩展名:.f或.for (1)若某行以C,c或*开头,则该行被当成注释; (2)每行前六个字符不能写程序代码,可空着,或者1~5字符以数字表明行代码(用作格 式化输入出等);7~72为程序代码编写区;73往后被忽略; (3)太长的话可以续行,所续行的第六个字符必须是"0"以外的任何字符。 Fortran 90:自由格式(free format), 扩展名:.f90 (1)以"!"引导注释; (2)每行可132字符,行代码放在每行最前面; (3)以续行,放在该行末或下行初。 以下都是讨论Fortran 90。 3、Fortran的一些特点,和C的一些不同 其实很多,在下面涉及具体方面时可以看到。这里只是大致提一些。 (1)不分大小写 (2)每句末尾不必要写分号 (3)程序代码命令间的空格没有意义 (4)不像C,Fortran不使用{ } (5)数据类型多出了复数和逻辑判断类型。比如复数类型 complex :: a !声明复数的方法。复数显然方便了科学计算,满足了工程方面需求 a=(1.0,2.0) ! a=1+i (6)多出了乘幂运算(**)。乘幂除了整数还可以是实数形式。如开方,开立方 a=4.0**0.5,a=8.0**(1.0/3.0) 。 (7)数组有一些整体操作的功能;可以方便的对部分元素进行操作 (8)有些情况下可以声明大小待定的数组,很实用的功能 4、Fortran的基本程序结构 先看一看所谓的"Hello Fortran"程序。 program main !程序开始,main是program的名字,完全自定义 write(*,*) "Hello" !主程序 stop !终止程序 end ] !end用于封装代码,表示代码编写完毕。 中的内容可省略,下同。 再看一段实用一些的程序,好有点感性认识。程序用于计算圆柱的表面积,要求输入底面 半径和。其中展示了Fortran的一些特色用法。程序摘自维基。其实是一个叫 www.answers.com 的网上引的维基的网页。推荐去看看!能查到不少有意思的东西。 program cylinder !给主函数起个名字 ! Calculate the area of a cylinder. ! Declare variables and constants. ! constants=pi ! variables=radius squared and height implicit none ! Require all variables to be explicitly declared !这个一般都是要写上的。下面会进一步说明。 integer :: ierr character :: yn real :: radius, height, area real, parameter :: pi = 3.1415926536 !这是常量的声明方法 interactive_loop: do !do循环,Fortran中的循环可以加标签,如d前面的 !interactive_loop就是标签 ! Prompt the user for radius and height ! and read them. write (*,*) 'Enter radius and height.' !屏幕输出 read (*,*,iostat=ierr) radius,height !键盘输入。isotat的值用判断输入成功否。 ! If radius and height could not be read from input, ! then cycle through the loop. if (ierr /= 0) then write(*,*) 'Error, invalid input.' cycle interactive_loop !cycle 相当于C里的continue end if ! Compute area. The ** means "raise to a power." area = 2 * pi * (radius**2 + radius*height) ! 指数运算比C方便 ! Write the input variables (radius, height) ! and output (area) to the screen. write (*,'(1x,a7,f6.2,5x,a7,f6.2,5x,a5,f6.2)') !""表示续行。这里还显示了格式化输出 'radius=',radius,'height=',height,'area=',area yn = ' ' yn_loop: do !内嵌的另一个do循环 write(*,*) 'Perform another calculation? y ' read(*,'(a1)') yn if (yn=='y' .or. yn=='Y') exit yn_loop if (yn=='n' .or. yn=='N' .or. yn==' ') exit interactive_loop end do yn_loop !结束内嵌do循环 end do interactive_loop end program cylinder Fortran程序的主要结构就是这样了。一般还会有些module的部分在主函数前,函数在主函 数后。 三、数据类型及基本输入输出 1、数据类型,声明及赋初值 (1)integer: 短整型kind=2, 长整型kind=4 integer( 2) :: a=3 如果声明成integer:: a,则默认为长整型。 !"::" 在声明并同时赋初值时必须要写上;类型名后面有形容词时也必须保留::;其他情况可略去 !所谓形容词,可以看一下这个。比如声明常数 real,parameter :: pi=3.1415926 。parameter就是形容词。 (2)real:单精度kind=4(默认),双精度kind=8 real( 8) :: a=3.0 还有指数的形式,如1E10为单精度,1D10为双精度 (3)complex 单精度和双精度 complex( 4) b (4)character character( 10) c !len为最大长度 (5)logical logical*2 :: d=.ture. (等价于 logical(2)::d=.ture. ) (6)自定义类型type:类似于C中的struct Fortran 77中给变量赋初值常用DATA命令,可同时给多个变量赋初值 data a,b,string /1, 2.0, 'fortran'/ 与C不同的是,Fortran中变量不声明也能使用,即有默认类型(跟implicit命令有关)。按 照默认的定,以i,j,k,l,m,n开头的变量被定义为integer,其余为real。取消该设置需在程序声明 部分之前implicit none。彭国伦建议一般都使用该语句。 另一点关于声明的不同是Fortran有"等价声明": integer a,b equivalence(a,b) 使得a,b使用同一块内存。这样可以节省内存;有时可精简代码。如:equivalence(很长名 字的变量如三维数组的某个元素,a),之后使用a来编写程序就简洁多了。 2、基本输入输出 输入: read(*,*) a !从键盘读入 输出: write(*,*) "text" !在屏幕上输出。Fortran 77用' text'。Fortan 90中一般" "和' '都可 print *,"text" !只能用于屏幕输出 (*,*)完整写为(unit=*,fmt=*)。其中unit为输入/输出位置,如屏幕,文件等;fmt为 格式。如这两项都写成*,则按默认的方式进行,即上面描述的。print后面的*表示按默认格式输 出。 四、流程控制 1、运算符 (1)逻辑运算符 == /= = = !Fortran 90用法 .EQ. .NE. .GT. .GE. .LT. .LE. !Fortran 77用法 (2)涉及相互关系的集合运算符 .AND. .OR. .NOT. .EQV. .NEQV. ! 仅.NOT.连接一个表达式,其余左右两边都要有表达式(可以是logical类型的变量) !.EQV.:当两边逻辑运算值相同时为真, .NEQV.:当两边逻辑运算值不同时为真 2、IF (1) 基本 : if(逻辑判断式) then …… end if 如果then后面只有一句,可写为 if(逻辑判断式) …… !then和end if可省略 (2) 多重判断: if(条件1) then …… else if(条件2)then …… else if (条件3)then …… else …… end if (3) 嵌套: if(逻辑判断式) then if(逻辑判断式) then if(逻辑判断式) then else if(逻辑判断式) then …… else …… end if end if end if (4) 算术判断: program example implicit none real c write (*,*) "input a number" read (*,*) c if(c) 10,20,30 !10,20和30为行代码,根据c小于/等于/大于0,执行10/20/30行的程 10 write (*,*) "A" goto 40 !goto可实现跳到任意前面或后面的行代码处,但用多了破坏程序结 20 write (*,*) "B" goto 40 30 write (*,*) "C" goto 40 40 stop end 3、SELECT CASE 类似于C的switch语句 select case(变量) case(数值1) ! 比如case(1:5)代表1=变量=5会执行该模块 …… !case(1,3,5)代表变量等于1或3或5会执行该模块 case(数值2) !括号中数值只能是integer,character或logical型常量,不能real型 … case default …… end case 4、PAUSE, CONTINUE pause暂停程序执行,按enter可继续执行 continue貌似没什么用处,可用作封装程序的标志 五、循环 1、DO do counter=初值, 终值, 增/减量 !counter的值从初值到终值按增/减量变, …… !counter每取一个值对应着一次循环。增/减量不写则认为1 …… …… !循环主体也没有必要用{} …… end do Fortran 77中不是用end do来终止,而是下面这样子: do 循环最后一行的行代码 counter=初值, 终值, 增/减量 …… 行代码 …… !这是do的最后一行 2、DO WHILE do while(逻辑运算) …… …… end do 类似于C中的while(逻辑运算) {……}。 一开始那个计算圆柱表面积的程序中,应该也算是这一类。不过它是通过内部的if语句来 控制循。看来也是可以的,不过在这本书上没看到这样写。其实应该也可以归于下面这种。 3、没看到和C里面的do{……}while(逻辑运算); 相对应的循环语句,不过可以这样,保证 至少做一循环: do while(.ture.) …… …… if(逻辑运算) exit !exit就好比C里面的break。C里的continue在Fortran里是cycle end do 4、Fortran的一个特色:带署名的循环 可以这样,不易出错: outer: do i=1,3 inner: do j=1,3 …… end do inner end do outer 还可以这样,很方便: loop 1: do i=1,3 loop2: do j=1,3 if(i==3) exit loop1 !exit终止整个循环loop1 if(j==2) cycle loop2 !cycle跳出loop2的本次循环,进行loop2的下次循环 write(*,*) i,j end do loop2 end do loop1 还有一些循环主要用于Fortran中的数组运算,为Fortran特有,很实用。 六、数组 1、数组的声明 和C不同的是,Fortran中的数组元素的索引值写在()内,且高维的也只用一个(),如 integer a(5) !声明一个整型一维数组 real :: b(3,6) !声明一个实型二维数组 类型可以是integer, real, character, logical或type。最高可以到7维。 数组大小必须为常数。但是和C语言不同,Fortran也有办法使用大小可变的数组,方法如: integer, allocatable :: a(:) !声明小可变经过某个途径得知所需数组大小size之后,用下面的语句: allocate(a(size)) !配置内存空间 之后该数组和通过一般方法声明的数组完全相同。 与C不同,Fortran索引值默认为从1开始,而且可以在声明时改变该规则: integer a(-3:1) ! 索引值为-3,-2,-1,0,1 integer b(2:3,-1:3) !b(2~3,-1~3)为可使用的元素 2、数组在内存中的存放 和C不同,Fortran中的数组比如a(2,2)在内存中存放顺序为a(1,1),a(2,1),a(1,2),a(2,2 )。原则是放低维的元素,再放高维的元素。此规则称为column major。 3、赋初值 (1)最普通的做法: integer a(5) data a /1,2,3,4,5/ 或 integer :: a(5)=(/1,2,3,4,5/) 若 integer :: a(5)=5 ,则5个元素均为5 对于 integer :: a(2,2)=(/1,2,3,4/) 根据数组元素在内存中存放的方式,等价于赋值 a(1,1)=1,a(2,1)=2,a(1,2)=3,a(2,2)=4 (2)利用Fortran的特色:隐含式循环。看例子就明白了。 integer a(5) integer i data (a(i),i=2,4)/2,3,4/ !(a(i),i=2,4)表示i从2到4循环,增量为默认值1 还可以这样: integer i integer :: a(5)=(/1,(2,i=2,4),5/) !五个元素分别赋值为1,2,2,2,5 integer :: b(5)=(/i, i=1,5/) !五个元素分别赋值为1,2,3,4, 还可以嵌套 data ((a(i,j),i=1,2),j=1,2)=/1,2,3,4/ !a(1,1)=1,1(2,1)=2,a(1,2)=3,a(2,2)=4 4、操作整个数组 设a,b为相同类型、维数和大小的数组 a=5 !所有元素赋值为5 a=(/1,2,3/) !这里假设a为一维,a(1)=1,a(2)=2,a(3)=3 a=b !对应元素赋值,要求a,b,c维数和大小相同,下同 a=b+c a=b-c a=b*c a=b/c a=sin(b) !内部函数都可以这样用 5、操作部分数组元素 a为一维数组 a(3:5)=(/3,4,5/) !a(3)=3,a(4)=4,a(5)=5 a(1:5:2)=3 !a(1)=3,a(3)=3,a(5)=3 a(3:)=5 !a(3)以及之后的所有元素赋值为5 a(1:3)=b(4:6) !类似于这种的要求左右数组元素个数相同 a(:)=b(:,2) !a(1)=b(1,2),a(2)=b(2,2),以此类推 6、WHERE where形式上类似于if,但只用于设置数组。设有两个同样类型、维数和大小的数组a,b where(a3) b=a !a中小于3的元素赋值给b对应位置的元素 end where 再如: where(a(1:3)/=0) c=a !略去了end where,因为只跟了一行where可嵌,也 !可类似do循环有署名标签。 7、FORALL 有点像C中的for循环: forall(triplet1 ],mask) 其中triplet形如i=2:6:2,表示循环,最后一个数字省略则增量为1 例如: forall(i=1:5,j=1:5,a(i,j)10) a(i,j)=1 end forall 又如: forall(i=1:5,j=1:5,a(i,j)/=0) a(i,j)=1/a(i,j) forall也可以嵌套使用,好比C中for循环的嵌套。 七、函数 Fortran中函数分两类:子程序(subroutine)和自定义函数(function)。自定义函数本 质上就是学上的函数,一般要传递自变量给自定义函数,返回函数值。子程序不一定是这样,可 以没有返值。传递参数要注意类型的对应,这跟C是一样的。 1、子程序 目的:把某一段经常使用的有特定功能的程序独立出来,可以方便调用。 习惯上一般都把子程序放在主程序结束之后。 形式: subroutine name (parameter1, parameter2) !给子程序起一个有意义的名字。可以传递参数,这样可以有返回值。括号内也可以 空着,代不传递参数。 implicit none integer:: parameter1, parameter2 !需要定义一下接收参数的类型。 …… !接下来的程序编写跟主程序没有任何别。 …… mreturn !跟C不同,这里表示子程序执行后回到调用它的地方继续执行下面的程序。不一定放 !在最后。可以放在子程序的其他位置,作用相同;子程序中return之后的部分不执行。 end 调用:使用call命令直接使用,不需要声明。在调用处写: call subroutine name(parameter1,parameter2) 注意点: a.子程序之间也可相互调用。直接调用就是了,像在主程序中调用子程序一样。 b.传递参数的原理和C中不同。Fortran里是传址调用(call by address/reference),就是 传递时用参数和子程序中接收时用的参数使用同一个地址,尽管命名可以不同。这样如果子程序 的执行改子程序中接收参数的值,所传递的参数也相应发生变化。 c.子程序各自内部定义的变量具有独立性,类似于C。各自的行代码也具有独立性。因此各 个子程序主程序中有相同的变量名、行代码号,并不会相互影响。 2、自定义函数 和子程序的明显不同在于:需要在主程序中声明之后才能使用。调用方式也有差别。另外 按照惯例用函数不去改变自变量的值。如果要改变传递参数的值,习惯上用子程序来做。 声明方式: real, external :: function_name 一般自定义函数也是放在主程序之后。 形式: function function_name(parameter1, parameter2) implicit none real:: parameter1, parameter2 !声明函数参数类型,这是必需的 real::function_name !声明函数返回值类型,这是必需的 …… …… function_name=…. !返回值的表达式 return end 也可以这样直接声明返回值类型,简洁些: real function function_name(parameter1, parameter2) implicit none real:: parameter1, parameter2 !这个还是必需的 …… …… function_name=…. !返回值表达式 return end 调用: function_name(parameter1,parameter2) 不需要call命令。 自定义函数可以相互调用。调用时也需要事先声明。 总之,调用自定义函数前需要做声明,调用子程序则不需要。 3、关于函数中的变量 (1)注意类型的对应。Fortran中甚至可以传递数值常量,但只有跟函数定义的参数类型 对应才会到想要的结果。如call ShowReal(1.0)就必须用1.0而不是1。 (2)传递数组参数,也跟C一样是传地址,不过不一定是数组首地址,而可以是数组某个 指定元素地址。比如有数组a(5),调用call function(a)则传递a(1)的地址,调用call functio n(a(3))则递a(3)的地址。 (3)多维数组作为函数参数,跟C相反的是,最后一维的大小可以不写,其他维大小必须 写。这决于Fortran中数组元素column major的存放方式。 (4)在函数中,如果数组是接收用的参数,则在声明时可以用变量赋值它的大小,甚至可 以不指定小。例如: subroutine Array(num,size) implicit none integer:: size integer num(size) !可以定义一个数组,其大小是通过传递过来的参数决定的。这很实用 …… …… return end (5)save命令:将函数中的变量值在调用之后保留下来,下次调用此函数时该变量的值就 是上次保的值。只要在定义时加上save就行: integer, save :: a=1 (6)传递函数(包括自定义函数、库函数、子程序都是可以的)。类似于C中的函数指针需要在 主程序和调用函数的函数中都声明作为参数传递的函数。如 real, external :: function !自定义函数 real, intrinsic :: sin !库函数 external sub !子程序 (7)函数使用接口(interface):一段程序模块。以下情况必需: a.函数返回值为数组 b.指定参数位置来传递参数时 c.所调用的函数参数个数不固定 d.输入指标参数时 e.函数返回值为指针时。 具体用法结合例子容易看懂。例子都很长。看书吧。 4、全局变量 功能就不用说了。原理:根据声明时的相对位置关系而取用,不同与C中根据变量名使用。 如果在主程序中定义: integer :: a,b common a,b !就是这样定义全局变量的 在子程序或自定义函数中定义: integer :: c,d common c,d 则a和c共用相同内存,b和d共用相同内存。 全局变量太多时会很麻烦。可以把它们人为归类,只需在定义时在common后面加上区间名 。如 common /groupe1/ a, common /group2/ b 。这样使用时就不必把所有全局变量 都列出来,再声明 common /groupe1/ c 就可以用a、c全局变量了。 可以使用block data程序模块。在主程序和函数中不能直接使用前面提到的data命令给全 局变量赋初值。可以给它们各自赋初值;如果要使用data命令必须要这样: block data implicit none integer a,b,c real d,e common a b c common /group1/ d,e data a,b,c,d,e /1,2,3,4.0,5.0/ end ] 5、Module Module不是函数。它用于封装程序模块,一般是把具有相关功能的函数及变量封装在一起 。用法很单,但能提供很多方便,使程序变得简洁,比如使用全局变量不必每次都声明一长串, 写在odule里调用就行了。Module一般写在主程序开始之前。 形式: module module_name …… …… end ] 使用:在主程序或函数中使用时,需要在声明之前先写上一行:use module_name. Module中有函数时必须在contains命令之后(即在某一行写上contains然后下 面开始写数,多所有函数都写在这个contains之后)。并且module中定义过的变量在module里的 函数中可直接使用,函数之间也可以直接相互调用,连module中的自定义函数在被调用时也不用 先声明。 6、include放在需要的任何地方,插入另外的文件(必须在同一目录下)。如: include 'funcion.f90' 八、文件 1、文本文件 Fortran里有两种读取文件的方式,对应于两种文件 顺序读取:用于文本文件 直接读取:用于二进制文件 这里只摘录关于文本文件的读取。一般模式如下。 character(len=20)::filenamein="in.txt", filenameout="out.txt" !文件名 logical alive integer::fileidin=10,fileidout=20 !10,20是给文件编的号,除1,2,5,6的正整数都可,因为2、6是默认的输出位置(屏幕 ),1、5是默认的输入位置(键盘) integer::error real::in,out !下面这一段用于确认指定名字的文件是否存在 inquire(file=filenamein, exist=alive) !如果存在,alive赋值为0 if(.NOT. alive) then write(*,*) trim(filenamein), " doesn't exist." !trim用于删去filenamein中字串 !后面的stop多余空格,输出时好看些 end if open( fileidin, file=filenamein, status="old") open( fileidout,file=filenameout ) !unit指定输入/输出的位置。打开已有文件一定要用status="old";打开新文件用status="new"; !不指定status,则默认status="unknown",覆盖已有文件或打开新文件…… read( fileidin, 100,iostat=error )in !error=0表示正确读入数据。 100 format(1X,F6.3) !按一定格式输入输出,格式可以另外写并指定行代码,也可以直接写在read/write中 write(( fileidout, "(1X,F6.3)")out close(fileidin) close(fileidout) !1X代表一个空格。F6.3代表real型数据用占6个字符(含小数点),其中小数点后三位。 !常用的还有I3,用于整型数据,共占三个字符;A8,字符型,占8个字符。换行用 / 二进制文件的读取有所不同。不再列举。 2、内部文件 另一个很实用的读写功能是内部文件(internal file)。看看这个例子就明白了。 integer::a=1,b=2 character(len=20)::string write(unit=string,fmt="(I2,'+',I2,'=',I2)")a,b,a+b write(*,*)string 则结果输出1+2=3。反过来也是可以的: integer a character(len=20)::string="123" read(string,*)a write(*,*)a 则输出123
8689 次阅读|0 个评论
[转载]FORTRAN 函数列表
surveying 2012-10-10 17:21
符号约定: l I代表整型;R代表实型;C代表复型;CH代表字符型;S代表字符串;L代表逻辑型;A代表数组;P代表指针;T代表派生类型;AT为任意类型。 l s:P表示s类型为P类型(任意kind值)。s:P(k)表示s类型为P类型(kind值=k)。 l 表示可选参数。 l *表示常用函数。 表1 数值和类型转换函数 函数名 说明 ABS(x)* 求x的绝对值∣x∣。x:I、R, 结果类型同x; x:C, 结果:R AIMAG(x) 求x的虚部。x:C, 结果:R AINT(x )* 对x取整,并转换为实数(kind)。x:R, kind:I, 结果:R(kind) AMAX0(x 1 ,x 2 ,x 3 ,…)* 求x 1 ,x 2 ,x 3 ,…中最大值。x I :I, 结果:R AMIN0(x 1 ,x 2 ,x 3 ,…)* 求x 1 ,x 2 ,x 3 ,…中最小值。x I :I, 结果:R ANINT(x )* 对x四舍五入取整,并转换为实数(kind)。x:R, kind:I, 结果:R(kind) CEILING(x)* 求大于等于x的最小整数。x:R, 结果:I CMPLX(x )) 将参数转换为x、(x,0.0)或(x,y)。x:I、R、C, y:I、R,kind:I, 结果:C(kind) CONJG(x) 求x的共轭复数。x:C, 结果:C DBLE(x)* 将x转换为双精度实数。x:I、R、C, 结果:R(8) DCMPLX(x ) 将参数转换为x、(x,0.0)或(x,y)。x:I、R、C, y:I、R, 结果:C(8) DFLOAT(x) 将x转换为双精度实数。x:I, 结果:R(8) DIM(x,y)* 求x-y和0中最大值, 即MAX(x-y,0)。x:I、R, y的类型同x,结果类型同x DPROD(x,y) 求x和y的乘积,并转换为双精度实数。x:R, y:R, 结果:R(8) FLOAT(x)* 将x转换为单精度实数。x:I, 结果:R FLOOR(x)* 求小于等于x的最大整数。x:R, 结果:I IFIX(x)* 将x转换为整数(取整)。x:R, 结果:I IMAG(x) 同AIMAG(x) INT(x )* 将x转换为整数(取整)。x:I、R、C, kind:I, 结果:I(kind) LOGICAL(x )* 按kind值转换新逻辑值。x:L, 结果:L(kind) MAX(x 1 ,x 2 ,x 3 ,…)* 求x 1 ,x 2 ,x 3 ,…中最大值。x I 为任意类型, 结果类型同x I MAX1(x 1 ,x 2 ,x 3 ,…)* 求x 1 ,x 2 ,x 3 ,…中最大值(取整)。x I :R, 结果:I MIN(x 1 ,x 2 ,x 3 ,…)* 求x 1 ,x 2 ,x 3 ,…中最小值。x I 为任意类型, 结果类型同x I MIN1(x 1 ,x 2 ,x 3 ,…)* 求x 1 ,x 2 ,x 3 …中最小值(取整)。x I :R, 结果:I MOD(x,y)* 求x/y的余数,值为x-INT(x/y)*y。x:I、R, y的类型同x, 结果类型同x MODULO(x,y) 求x/y余数,值为x-FLOOR(x/y)*y。x:I、R, y的类型同x, 结果类型同x NINT(x )* 将x转换为整数(四舍五入)。x:R, kind:I, 结果:I(kind) REAL(x )* 将x转换为实数。x:I、R、C, kind:I, 结果:R(kind) SIGN(x,y)* 求x的绝对值乘以y的符号。x:I、R, y的类型同x, 结果类型同x SNGL(x) 将双精度实数转换为单精度实数。x:R(8), 结果:R ZEXT(x) 用0向左侧扩展x。x:I、L, 结果:I 表2 三角函数 函数名 说明 ACOS(x)* 求x的反余弦arccos(x)。x:R,结果类型同x,结果值域:0~π ACOSD(x)* 求x的反余弦arccos(x)。x:R,结果类型同x,结果值域:0~180° ASIN(x)* 求x的反正弦arcsin(x)。x:R,结果类型同x,结果为弧度,值域:0~π ASIND(x)* 求x的反正弦arcsin(x)。x:R,结果类型同x,结果为度,值域:0~180° ATAN(x)* 求x的反正切arctg(x)。x:R,结果类型同x,结果为弧度,值域:-π/2~π/2 ATAND(x)* 求x的反正切arctg(x)。x:R,结果类型同x,结果为度,值域:-90~90° ATAN2(y,x) 求x的反正切arctg(y/ x )。y:R,x和结果类型同x,结果值域:-π~π ATAN2D(y,x) 求x的反正切arctg(y/ x )。y:R,x和结果类型同x,结果值域:-180~180° COS(x)* 求x的余弦cos(x)。x:R、C,x取值弧度,结果类型同x COSD(x)* 求x的余弦cos(x)。x:R,x取值度,结果类型同x COSH(x) 求x的双曲余弦ch(x)。x:R,结果类型同x COTAN(x)* 求x的余切ctg(x)。x:R,x取值度,结果类型同x SIN(x)* 求x的正弦sin(x)。x:R、C,x取值弧度,结果类型同x SIND(x)* 求x的正弦sin(x)。x:R,x取值度,结果类型同x SINH(x) 求x的双曲正弦sh(x)。x:R,结果类型同x TAN(x)* 求x的正切tg(x)。x:R,x取值弧度,结果类型同x TAND(x)* 求x的正切tg(x)。x:R,x取值度,结果类型同x TANH(x) 求x的双曲正切th(x)。x:R,结果类型同x 注:三角函数名前有C、D的函数为复数、双精度型函数。 表3 指数、平方根和对数函数 函数名 说明 ALOG(x) 求x的自然对数ln(x)。x:R(4),结果:R(4) ALOG10(x) 求x以10为底一般对数log 10 (x)。x:R(4),结果:R(4) EXP(x)* 求指数,即e x 。x:R、C,结果类型同x LOG(x)* 求自然对数,即e x 。x:R、C,结果类型同x LOG10(x)* 求以10为底对数,即。x:R,结果类型同x SQRT(x)* 求x的平方根。x:R、C,结果类型同x 注:指数函数名、平方根函数名、对数函数名前有C、D的函数为复数、双精度型函数。 表4 参数查询函数 函数名 说明 ALLOCATED(a)* 判定动态数组a是否分配内存。a:A,结果:L,分配:.TRUE.,未分配:.FALSE. ASSOCIATED(p )* 判定指针p是否指向目标t。p:P,t:AT,结果:L,指向:.TRUE.,未指向:.FALSE. DIGITS(x) 查询x的机内编码数值部分二进制位数(除符号位和指数位)。x:I、R,结果:I EPSILON(x)* 查询x类型可表示的最小正实数。x:R,结果类型同x。最小正实数:1.1920929E-07 HUGE(x)* 查询x类型可表示的最大数。x:I、R,结果类型同x ILEN(x) 查询x的反码值。x:I,结果类型同x KIND(x)* 查询x的kind参数值。x:I、R、C、CH、L,结果:I MAXEXPONENT(x)* 查询x的最大正指数值。x:R,结果:I(4) MINEXPONENT(x)* 查询x的最大负指数值。x:R,结果:I(4) PRECISION(x)* 查询x类型有效数字位数。x:R、C,结果:I(4) PRESENT(x) 查询可选形参x是否有对应实参。x:AT,结果:L。有:.TRUE.,没有:.FALSE. RADIX(x) 查询x类型的基数。x:I、R,结果:L RANGE(x)* 查询x类型的指数范围。x:I、R、C,结果:I(4) SIZEOF(x)* 查询x的存储分配字节数。x:AT,结果:I(4) TINY(x)* 查询x的最小正值。x:R,结果类型同x 表5 实数检测和控制函数 函数名 说明 EXPONENT(x)* 求实数x机内编码表示的指数值。x:R,结果:I FRACTION(x)* 求实数x机内编码表示的小数值。x:R,结果类型同x NEAREST(x,s) 根据s的正负号求最接近x的值。x:R,结果:R,且不为0 RRSPACING(x) 求x与系统最大数之间的差值。x:R,结果类型同x SCALE(x,I)* 求x乘以2 i 。x:R,i:I,结果类型同x SET_EXPONENT(x,i) 求由x的机内编码小数值与指数i组成的实数。x:R,i:I,结果类型同x SPACING(x)* 求x与x最近值的差值绝对值。x:R,结果类型同x 表6 字符处理函数 函数名 说明 ACHAR(n) 将ASCII码n转换为对应字符。n:I,n值域:0~127,结果:CH(1) ADJUSTL(string)* 将字符串string左对齐,即去掉左端空格。string:CH(*),结果类型同string ADJUSTR(string)* 将字符串string右对齐,即去掉右端空格。string:CH(*),结果类型同string CHAR(n)* 将ASCII码n转换为对应字符。n:I,n值域:0~255,结果:CH(1) IACHAR(c)* 将字符c转换为对应的ASCII码。c:CH(1),结果:I ICHAR(c)* 将字符c转换为对应的ASCII码。c:CH(1),结果:I INDEX(s,ss )* 求子串ss在串s中起始位置。s:CH(*),ss:CH(*),b:L,结果:I。b为真从右起 LEN(s)* 求字符串s的长度。s:CH(*),结果:I LEN_TRIM(s)* 求字符串s去掉尾部空格后的字符数。s:CH(*),结果:I LGE (s1,s2)* 按ASCII码值判定字符串s1大于等于字符串s2。s1:CH(*),s1:CH(*),结果:L LGT(s1,s2)* 按ASCII码值判定字符串s1大于字符串s2。s1:CH(*),s1:CH(*),结果:L LLE(s1,s2)* 按ASCII码值判定字符串s1小于等于字符串s2。s1:CH(*),s1:CH(*),结果:L LLT(s1,s2)* 按ASCII码值判定字符串s1小于字符串s2。s1:CH(*),s1:CH(*),结果:L REPEAT(s,n)* 求字符串s重复n次的新字符串。s:CH(*),n:I,结果:CH(*) SCAN(s,st ) 求串st中任一字符在串s中的位置。s:CH(*),ss:CH(*),b:L,结果:I TRIM(s)* 求字符串s去掉首尾部空格后的字符数。s:CH(*),结果:CH(*) VERIFY(s,st ) 求不在串st中字符在s中位置。s:CH(*),ss:CH(*),b:L,结果:I。b为真右起 表7 二进制位操作函数 函数名 说明 BIT_SIZE(n)* 求n类型整数的最大二进制位数。n:I,结果类型同n BTEST(n,p) 判定整数n的二进制表示右起第p位是否为1。n:I,p:+I,p值域:0~64结果:L IAND(m,n)* 对m和n进行按位逻辑“与”运算。m:I,n:I,结果类型同m IBCHNG(n,p) 将整数n二进制表示右起第p位值取反。n:I,p:+I,p值域:0~64结果类型同n IBCLR(n,p) 将整数n二进制表示右起第p位置0。n:I,p:+I,p值域:0~64结果类型同n IBITS(i,p,l) 从整数n二进制表示右起第p位开始取l位。n:I,p:+I,l:+I,结果类型同n IBSET(n,p) 将整数n二进制表示右起第p位置1。n:I,p:+I,p值域:0~64结果类型同n IEOR(m,n)* 对m和n进行按位逻辑“异或”运算。m:I,n:I,结果类型同m IOR(m,n)* 对m和n进行按位逻辑“或”运算。m:I,n:I,结果类型同m ISHA(n,s)* 对n向左(s为正)或向右(s为负)移动s位(算术移位)。n:I,s:I,结果类型同n ISHC(n,s)* 对n向左(s为正)或向右(s为负)移动s位(循环移位)。n:I,s:I,结果类型同n ISHFT(n,s)* 对n向左(s为正)或向右(s为负)移动s位(逻辑移位)。n:I,s:I,结果类型同n ISHFTC(n,s ) 对n最右边size位向左(s为正)或向右(s为负)移动s位(循环移位) ISHL(n,s) 对n向左(s为正)或向右(s为负)移动s位(逻辑移位)。n:I,s:I,结果类型同n NOT(n)* 对n进行按位逻辑“非”运算。n:I,结果类型同n 表8 数组运算、查询和处理函数 函数名 说明 ALL(m )* 判定逻辑数组m各元素是否都为“真”。m;L-A,d:I,结果:L(缺省d)或L-A(d=维) ALLOCATED(a)* 判定动态数组a是否分配存储空间。a:A,结果:L。分配:.TRUE.,未分配.FALSE. ANY(m )* 判定逻辑数组m是否有一元素为“真”。m;L-A,d:I,结果:L(缺省d)或L-A(d=维) COUNT(m )* 计算逻辑数组m为“真”元素个数。m;L-A,d:I,结果:I(缺省d)或I-A(d=维) CSHIFT(a,s )* 将数组a元素按行(d=1或缺省)或按列(d=2)且向左(d0)或向右循环移动s次 EOSHIFT(a,s ) 将数组a元素按行(d=1或缺省)或按列(d=2)且向左(d0)或向右循环移动s次 LBOUND(a )* 求数组a某维d的下界。a;A,d:I,结果:I(d=1或缺省)或A(d=2) MATMUL(ma,mb)* 对二维数组(矩阵)ma和mb做乘积运算。ma:A,mb:A,结果:A MAXLOC(a )* 求数组a中对应掩码m为“真”最大元素下标值。a:A,m:L-A,结果:A,大小=维数 MAXVAL(a )* 求数组a中对应掩码m为“真”元素最大值。a:A,d:I,m:L-A,结果:A,大小=维数 MERGE(ts,fs,m) 将数组ts和fs按对应m掩码数组元素合并,掩码为“真”取ts值,否则取fs值 MINLOC(a )* 求数组a中对应掩码m为“真”最小元素下标值。a:A,m:L-A,结果:A,大小=维数 MINVAL(a )* 求数组a中对应掩码m为“真”元素最小值。a:A,d:I,m:L-A,结果:A,大小=维数 PACK(a,m ) 将数组a中对应m掩码数组元素为“真”元素组成一维数组并与一维数组v合并 PRODUCT(a ) 数组a中对应掩码m为“真”元素乘积。a:A,d:I,m:L-A,结果:A,大小=维数 RESHAPE(a,s)* 将数组a的形按数组s定义的形转换。数组形指数组维数、行数、列数、… SHAPE(a) 求数组a的形。a:A,结果:A(一维) SIZE(a )* 求数组a的元素个数。a:A,d:I,结果:I SPREAD(a,d,n) 以某维d扩展数组a的元素n次。a:A,d:I,n:I,结果:A SUM(a )* 数组a中对应掩码m为“真”元素之和。a:A,d:I,m:L-A,结果:A,大小=维数 TRANSPOSE(a).* 对数组a进行转置。a:A,结果:A LBOUND(a )* 求数组a某维d的上界。a;A,d:I,结果:I(d=1或缺省)或A(d=2) UNPACK(a,m,f) 将一维数组a、掩码数组m值和f值组合生成新数组。a;A,m:L-A,f:同a,结果:A 注: 参数m指逻辑型掩码数组,指明允许操作的数组元素。缺省掩码数组指对数组所有元 素进行操作
2654 次阅读|0 个评论
[转载]FORTRAN 中的goto/exit/cycle用法
surveying 2012-10-8 21:45
1,goto goto在Fortran77中就流传下来了,它提供一个任意跳跃到所赋值行代码的位置,如果是在一个do循环中如 do30i=1,N if(……)goto30 30continue 上语句的意思就是如果符合if里的条件,则会进行下一次循环。 2,pause pause的功能就能跟它的字面意思相同,程序执行到pause时,会暂停执行,直到用户按下Enter键才会继续执行。 3.continue continue这个命令没有实际的用途,它的功能就是继续向下执行程序 4,stop 它可用来结束程序执行。 5,cycle cycle命令可由略过循环的程序模块中,在cycle命令后面的所有程序代码,直接跳回循环的开头来进行下一次循环。 如 dofloor=1,dest if(floor==4)cycle write(*,*)floor enddo 执行结果如下 1 2 3 5 6 6 exit exit的功能是可以直接跳出一个正在进行的循环,不论是do循环还是dowhile循环。
23930 次阅读|0 个评论
netcdf用户手册—引言—文件格式
cwjwang 2012-9-29 16:11
(2)netcdf 文件格式 直到3.6.0版本,所有以前的版本唯一的格式二进制格式,现在这种格式被默认经典格式。在3.6.0版本一种新的二进制格式被引进,64—bit offsets,这种格式给用户创建更大的数据库。在4.0.0版本,第三种二进制格式被引进,HDF5格式。从这个版本开始,netcdf数据库可以用HDF5作为基本的格式之一。(只有用netcdf-4创建的HDF5格式的文件才能被netcdf-4识别) 默认条件下,netcdf使用经典模式,若要使用64-bit offsets 以及HDF5格式,需要在创建文件时候进行参数设置。为了网络的透明(独立于机器)netcdf经典和64-bit offsets 格式是像对XDR为代表的外部的补充。这种代表提供了独立于机器编码流的数据编译方式,极大的补充了计算机用8位连续的编译的方式。IEEE754 浮点指针标准作为浮点指针数据的标准。 如何选择格式 面对三种不同的文件格式,创建数据文件时候需仔细考虑选择合适的数据格式。文件一旦创建,其格式即固定。当打开存在的netcdf文件,netcdf库透明的检测格式,并适应之。然而, netcdf数据库在3.6.0版本以前不能势识别64-bit offsets,4.0版本之前不能读取netcdf4-hdf5文件。因此用户应在最大情况下使用经典格式,分发数据。当选择64-bit offset或者netcdf-4 文件格式,c程序应该用flag NC——64BIT—OFFSET 或者NC—NETCDF4在nc_creat函数创建的时候。在fortran同理。 netcdf经典格式 netcdf的初始格式的特在是用在文件头用4个位。所有的这类文件格式都有 “CDF\001”在文件头。这种格式的文件被默认经典格式。这种经典格式被广泛地被以前的版本使用,具有最大的可移植型,至今还是netcdf默认的文件格式。对于某些用户各种2GiB格式限制是一个问题;对于某些用户,64-bit offset 是合适的选择,这种格式极大的缓解了netcdf经典格式的大小的限制。64-bit offsets 以“CDF\002”为文件头。 netcdf-4 格式 在4.0版本,netcdf包含了另外一种新的格式HDF5.netcdf-4文件格式提供了例如组、复合类型,变量长度数组、新的未分配大小的整型、I/O并行接口等等新的特性。这些新的特性在以往的文件格式是不能用的。netcdf-4文件需要netcdf编译的时候“--enable-netcdf-4”,并且要求安装HDF51.8.0以上的版本。对于目前的netcdf-4.0发行的版本,netcdf-4特性只支持c和fortran 接口。
6860 次阅读|0 个评论
[转载]Reading FORTRAN unformatted binary files in C/C++
majian 2012-8-1 23:04
大一学的FORTRAN,都忘光了 关于无格式文件读写的一点资料,很有用 http://paulbourke.net/dataformats/fortran/ Reading FORTRAN unformatted binary files in C/C++ Problem Ever wanted to read binary files written by a FORTRAN program with a C/C++ program? Not such an unusual or unreasonable request but FORTRAN does some strange things ..... consider the following FORTRAN code, where "a" is a 3D array of 4 byte floating point values. open(60,file=filename,status='unknown',form='unformatted') write(60) nx,ny,nz do k = 1,nz do j = 1,ny write(60) (a(i,j,k),i=1,nx) enddo enddo close(60) What you will end up with is not a file that is (4 * nx) * ny * nz + 12 bytes long as it would be for the equivalent in most (if not all) other languages! Instead it will be nz * ny * (4 * nx + 8) + 20 bytes long. Why? Reason Each time the FORTRAN write is issued a "record" is written, the record consists of a 4 byte header, then the data, then a trailer that matches the header. The 4 byte header and trailer consist of the number of bytes that will be written in the data section. So the following write(60) nx,ny,nz gets written on the disk as follows where nx,ny,nz are each 4 bytes, the other numbers below are 2 byte integers written in decimal 0 12 nx ny nz 0 12 The total length written is 20 bytes. Similarly, the line write(60) (a(i,j,k),i=1,nx) gets written as follows assuming nx is 1024 and "a" is real*4 0 4096 a(1,j,k) a(2,j,k) .... a(1024,j,k) 0 4096 The total length is 4104 bytes. Fortunately, once this is understood, it is a trivial to read the correct things in C/C++. A consequence that is a bit shocking for many programmers is that the file created with the above code gives a file that is about 1/3 the size than one created with this code. open(60,file=filename,status='unknown',form='unformatted') write(60) nx,ny,nz do k = 1,nz do j = 1,ny do i = 1,nx write(60) a(i,j,k) enddo enddo enddo close(60) In this case each element of a is written in one record and consumes 12 bytes for a total file size of nx * ny * nz * 12 + 20. Note · This doesn't affect FORTRAN programs that might read these files, that is because the FORTRAN "read" commands know how to handle these unformatted files. · The discussion here does not address the transfer of binary files between machines with a different endian. In that case after a short, int, float, double is read the bytes must be rearranged. Fortunately this is relatively straightforward with these macros. · #define SWAP_2(x) ( (((x) 0xff) 8) | ((unsigned short)(x) 8) ) · #define SWAP_4(x) ( ((x) 24) | (((x) 8) 0x00ff0000) | \ · (((x) 8) 0x0000ff00) | ((x) 24) ) · #define FIX_SHORT(x) (*(unsigned short *)(x) = SWAP_2(*(unsigned short *)(x))) · #define FIX_LONG(x) (*(unsigned *)(x) = SWAP_4(*(unsigned *)(x))) · #define FIX_FLOAT(x) FIX_LONG(x) · It appears that the endianness of the 4 byte header and trailer reflect the endianness of the machine doing the writing. Of course if you know the format of the data being written then you can simply skip over the header/trailer bytes, but if you need to decode the file or do error checking then knowledge of the endian of the machine where the file was written and the endian of the machine where the file is being read is necessary. · And lastly, the above does not address the possibility (fairly rare these days) that the files may be transferred between two machines with different internal representations of floating point numbers. If that is the case then you're really in trouble and should probably revert to transferring the data in a readable ASCII format. · Update (Jan 2008): It would appear that on 64 bit machines the 2 header elements are each written as 4 bytes instead of 2 bytes each. · If the file is not already in existence then writing files in FORTRAN to avoid the above, one can use the access='stream' option. This option was introduced reasonably recently explicitly to overcome this issue. C++ 语言 : 用 C ++来读 Fortran 语言写的 bin ( unformatted )形式文件 /* How to read a bin file written by fortran in C program. Fortran write data in bin mode: open(unit=10,file='restart',form='unformatted',status='unknown') write(10) nx,ny,cfl,gamma,tcur,cpu write(10) (((uc(i,j,m,0),i=1,nx),j=1,ny),m=1,4) close(10) In Fortran, every write code, it will first write the bytes that will save, then is the data, and at the end also is the bytes saved. bytewrite, ,bytewrite so, if nx,ny is integer, cfl,gamma,tcur,cpu is double , then total bytes will be 2*4+4*8=40, then bytewrite will be 40. Also, in fortran, the file can not be read as: open(unit=10,file='restart',form='unformatted',status='unknown') read(10) nx,ny,cfl,gamma,tcur,cpu read(10) ((uc(i,j,1,0),i=1,nx),j=1,ny) read(10) ((uc(i,j,2,0),i=1,nx),j=1,ny) ! here will be an error will running close(10) the second write line, will first write nx*ny*4*(8) The code in C to read the file will be fp=fopen("restart","r"); fread(ntemp,sizeof(int),1,fp); fread(nx,sizeof(int),1,fp); fread(ny,sizeof(int),1,fp); fread(cfl,sizeof(double),1,fp); fread(gamma,sizeof(double),1,fp); fread(tcur,sizeof(double),1,fp); fread(cpu,sizeof(cpu),1,fp); fread(ntemp,sizeof(int),1,fp); // begin an other write line fread(ntemp,sizeof(int),1,fp); fread(data,sizeof(double),nx*ny*4),fp); fread(ntemp,sizeof(int),1,fp); */ #include stdlib.h #include stdio.h int main( int argc, char *argv = "restart" ; FILE *fp; int nx,ny; double cfl,gamma,t0,cpu0; double vmin,vmax; int nmin,nmax; int ntemp; if (argc== 1 ) fp=fopen( "restart" , "r" ); else fp=fopen(argv , "r" ); if (!fp) return 1 ; fread(ntemp, sizeof ( int ), 1 ,fp); printf( "write %d bytes\n" ,ntemp); fread(nx, sizeof ( int ), 1 ,fp); // fseek(fp,4,SEEK_SET); fread(ny, sizeof ( int ), 1 ,fp); // fseek(fp,16,SEEK_SET); fread(cfl, sizeof ( double ), 1 ,fp); fread(gamma, sizeof ( double ), 1 ,fp); fread(t0, sizeof ( double ), 1 ,fp); fread(cpu0, sizeof ( double ), 1 ,fp); printf( "Resolution:%d,%d\n" ,nx,ny); printf( "CFL:%f\n" ,cfl); printf( "Gamma:%f\n" ,gamma); printf( "CPU:%f\n" ,cpu0); printf( "Current Time:%f\n" ,t0); fread(ntemp, sizeof ( int ), 1 ,fp); printf( "read %d bytes\n" ,ntemp); fread(ntemp, sizeof ( int ), 1 ,fp); printf( "write %d=%dx%dx(4*8) bytes\n" ,ntemp,nx,ny); double *data= new double ; if (data) { for ( int m= 0 ;m 4 ;m++){ fread(data, sizeof ( double ),nx*ny,fp); double tmin,tmax; int nmax,nmin; tmin= 0.0 ,tmax= 0.0 ; nmin=- 1 ,nmax=- 1 ; for ( int j= 0 ;jnx*ny;j++) { if (data tmax) { tmax=data ; nmax=j; } if (data tmin) { tmin=data ; nmin=j; } } printf( "***Min:%f at %d \n" ,tmin,nmin); printf( "***Max:%f at %d \n" ,tmax,nmax); } } fclose(fp); fread(ntemp, sizeof ( int ), 1 ,fp); printf( "read %d=%dx%dx(4*8) bytes\n" ,ntemp,nx,ny); return 0 ; } http://www.ibm.com/developerworks/aix/library/au-endianc/index.html?ca=drs - Writing endian-independent code in C
个人分类: 技术|8185 次阅读|0 个评论
[转载]C++和fortran混编
热度 1 zhoufcumt 2012-5-18 13:53
粘帖过来的部分很乱,直接打开转载网址。 转载自:http://www.cae.tntech.edu/help/programming/mixed_languages Mixing Code in C, C++, and FORTRAN on Unix It is becoming increasingly common for engineers to write different parts of a final program in different languages. For example, you might use legacy FORTRAN code for calculations, C++ code for writing a graphical interface to the program, and C for other system functions. This page is intended to show simple examples of how to write programs and functions that can be accessed from other languages. Major points to keep in mind when mixing languages include: Call By Value / Call By Reference C and C++ default to passing arguments by value; FORTRAN defaults to passing arguments by reference. In other words, the normal way that FORTRAN subroutines or functions are called allows them to modify their argument variables inside the subroutine code, while C and C++ do not. C and C++ subroutines use a slightly different syntax to allow for modification of arguments, and this syntax is consistently used in the following examples. Splitting Code Into Multiple Files Normally, as your program code grows larger, you'll want to separate it into several files, one per function or subroutine. Then each source code file is compiled into an object file (a .o file in Unix, or a .obj file in Windows), and the various object files are linked together into a final single executable. The advantages of splitting your code up this way include: Being able to use different languages for different portions of the program Being able to delegate the writing of different functions to different people More efficient compilation, since a change to one source file only requires its object file to be recompiled and the object files to be relinked, rather than recompiling the entire body of code from scratch. Once you exceed the two or three source code files we use in the following examples, you'll almost definitely want to investigate using the make utility to automate the build process on your program. Internal Function Names When the compiler turns source code into object files, it might change the internal name of the function, for example, by appending or prepending underscores. In Unix, you can use the nm command to list those internal names: mwr@ch208m:~$ nm cppfunction.o cppfunction.o: Value Size Type Bind Other Shndx Name | 0| 0|SECT |LOCL |0 |4 | | 0| 0|SECT |LOCL |0 |2 | | 0| 0|SECT |LOCL |0 |3 | | 0| 0|NOTY |LOCL |0 |4 |__FRAME_BEGIN__ | 0| 76|FUNC |GLOB |0 |2 |cppfunction | 0| 0|FILE |LOCL |0 |ABS |cppfunction.C | 0| 0|NOTY |LOCL |0 |2 |gcc2_compiled. mwr@ch208m:~$ nm ffunction.o ffunction.o: Value Size Type Bind Other Shndx Name | 0| 0|SECT |LOCL |0 |2 | | 0| 0|SECT |LOCL |0 |3 | | 0| 0|FILE |LOCL |0 |ABS |ffunction.f | 0| 68|FUNC |GLOB |0 |2 |ffunction_ | 0| 0|NOTY |LOCL |0 |2 |gcc2_compiled. mwr@ch208m:~$ Note that the FORTRAN compiler appended a single underscore to the function name, while the C++ compiler left the name intact. Example 1: Main Program in C, with Subroutines in C, C++, and FORTRAN The C program is nothing out of the ordinary: it defines two variables, and calls various functions that change those variables' values. C requires that we use a "call by reference" syntax to make these changes persistent, rather than its default "call by value" method. Note that the name of the FORTRAN function called from the C program is ffunction_, a name we extracted via the nm command shown above. Note also that the C++ function has an extern "C" directive above the code of the function, indicating not that cppfunction() is written in C, but that it is called from a C-style interface instead of a C++ interface. File cprogram.c: #include stdio.h int main(void) { float a=1.0, b=2.0; printf("Before running Fortran function:\n"); printf("a=%f\n",a); printf("b=%f\n",b); ffunction_(a,b); printf("After running Fortran function:\n"); printf("a=%f\n",a); printf("b=%f\n",b); printf("Before running C++ function:\n"); printf("a=%f\n",a); printf("b=%f\n",b); cppfunction(a,b); printf("After running C++ function:\n"); printf("a=%f\n",a); printf("b=%f\n",b); printf("Before running C function:\n"); printf("a=%f\n",a); printf("b=%f\n",b); cfunction(a,b); printf("After running C function:\n"); printf("a=%f\n",a); printf("b=%f\n",b); return 0; } File ffunction.f: subroutine ffunction(a,b) a=3.0 b=4.0 end File cppfunction.C: extern "C" { void cppfunction(float *a, float *b); } void cppfunction(float *a, float *b) { *a=5.0; *b=6.0; } File cfunction1.c: void cfunction(float *a, float *b) { *a=7.0; *b=8.0; } Compilation Steps: each program is compiled into an object file using the appropriate compiler with the -c flag. After all the object files are created, the final gcc command links the object files together into a single executable: mwr@ch208m:~$ gcc -c cprogram.c mwr@ch208m:~$ g77 -c ffunction.f mwr@ch208m:~$ g++ -c cppfunction.C mwr@ch208m:~$ gcc -c cfunction1.c mwr@ch208m:~$ gcc -o cprogram cprogram.o ffunction.o cppfunction.o cfunction1.o mwr@ch208m:~$ Though this example problem does not require it, many of the math functions (for example, sin, cos, pow, etc.) require that you also link in the libm math library. Add a -lm flag to the final gcc command above to link in the math library. Results: mwr@ch208m:~$ ./cprogram Before running Fortran function: a=1.000000 b=2.000000 After running Fortran function: a=3.000000 b=4.000000 Before running C++ function: a=3.000000 b=4.000000 After running C++ function: a=5.000000 b=6.000000 Before running C function: a=5.000000 b=6.000000 After running C function: a=7.000000 b=8.000000 mwr@ch208m:~$ Example 2: Main Program in C++, with Subroutines in C, C++, and FORTRAN The only differences between the C++ code in a normal program is that we have to account for the C and FORTRAN subroutines expecting to be called with a C-style interface, and also that the FORTRAN compiler will append an underscore to the FORTRAN function name. Also, since the C++ function is held in a separate file from the C++ main program, we need to declare a function prototype before the main program code. File cppprogram.C: #include iostream.h extern "C" { void ffunction_(float *a, float *b); } extern "C" { void cfunction(float *a, float *b); } void cppfunction(float *a, float *b); int main() { float a=1.0, b=2.0; cout "Before running Fortran function:" endl; cout "a=" a endl; cout "b=" b endl; ffunction_(a,b); cout "After running Fortran function:" endl; cout "a=" a endl; cout "b=" b endl; cout "Before running C function:" endl; cout "a=" a endl; cout "b=" b endl; cfunction(a,b); cout "After running C function:" endl; cout "a=" a endl; cout "b=" b endl; cout "Before running C++ function:" endl; cout "a=" a endl; cout "b=" b endl; cppfunction(a,b); cout "After running C++ function:" endl; cout "a=" a endl; cout "b=" b endl; return 0; } File cfunction1.c: void cfunction(float *a, float *b) { *a=7.0; *b=8.0; } File ffunction.f: subroutine ffunction(a,b) a=3.0 b=4.0 end Compilation Steps: mwr@ch208m:~$ g++ -c cppprogram.C mwr@ch208m:~$ gcc -c cfunction1.c mwr@ch208m:~$ g++ -c cppfunction1.C mwr@ch208m:~$ g77 -c ffunction.f mwr@ch208m:~$ g++ -o cppprogram cppprogram.o cfunction1.o cppfunction1.o ffunction.o mwr@ch208m:~$ Results: mwr@ch208m:~$ ./cppprogram Before running Fortran function: a=1 b=2 After running Fortran function: a=3 b=4 Before running C function: a=3 b=4 After running C function: a=7 b=8 Before running C++ function: a=7 b=8 After running C++ function: a=5 b=6 mwr@ch208m:~$ Example 3: Main Program in FORTRAN, with Subroutines in C, C++, and Fortran Though the non-FORTRAN subroutines don't have any underscores after their names in the main FORTRAN program, running the nm command on fprogram.o shows that the FORTRAN program expects that they'll have underscores appended to the function name internally. Therefore, in both the C and C++ files, we need to write the function prototype statement accordingly, with an underscore after the function name. We also must use the extern "C" directive in the C++ file, indicating that the C++ function is called with a C-style interface, similar to the first example. File fprogram.f: program fprogram real a,b a=1.0 b=2.0 print*,'Before Fortran function is called:' print*,'a=',a print*,'b=',b call ffunction(a,b) print*,'After Fortran function is called:' print*,'a=',a print*,'b=',b print*,'Before C function is called:' print*,'a=',a print*,'b=',b call cfunction(a,b) print*,'After C function is called:' print*,'a=',a print*,'b=',b print*,'Before C++ function is called:' print*,'a=',a print*,'b=',b call cppfunction(a,b) print*,'After C++ function is called:' print*,'a=',a print*,'b=',b stop end File ffunction.f: subroutine ffunction(a,b) a=3.0 b=4.0 end File cppfunction2.C: extern "C" { void cppfunction_(float *a, float *b); } void cppfunction_(float *a, float *b) { *a=5.0; *b=6.0; } File cfunction2.c: void cfunction_(float *a, float *b) { *a=7.0; *b=8.0; } Compilation Steps: mwr@ch208m:~$ g77 -c fprogram.f mwr@ch208m:~$ g77 -c ffunction.f mwr@ch208m:~$ g++ -c cppfunction2.C mwr@ch208m:~$ gcc -c cfunction2.c mwr@ch208m:~$ g77 -lc -o fprogram fprogram.o ffunction.o cppfunction2.o cfunction2.o mwr@ch208m:~$ Results: mwr@ch208m:~$ ./fprogram Before Fortran function is called: a= 1. b= 2. After Fortran function is called: a= 3. b= 4. Before C function is called: a= 3. b= 4. After C function is called: a= 7. b= 8. Before C++ function is called: a= 7. b= 8. After C++ function is called: a= 5. b= 6. mwr@ch208m:~$
个人分类: Fortran|3074 次阅读|3 个评论
Fortran program writing *.eam.alloy potential for lammps
热度 4 starbinbin 2012-4-18 16:16
This blog is about a program written by my self for generating the *.eam.alloy file for lammps, which has been finished 2012.04.18. Potential bugs must exist, therefore, advice and suggestion about this program is highly appreciated. !############################################################################################################################# !EAM_Table Program Version 1.0 !Generating the setfl file for eam/alloy potential in lammps !Programmer: Bin Ouyang, McGill University !Supervisor: Jun Song, McGill uUniversity !Department of Mining and Materials Engineering, McGill University !Date: 2012.04.17 !############################################################################################################################# program eam_table implicit none character*50 :: para_file, out_file real :: re(2), fe(2), rou_e(2), arfa(2), beta(2), A(2), B(2), kai(2), lambda(2), Fn0(2), Fn1(2) real :: Fn2(2), Fn3(2), F0(2), F1(2), F2(2), F3(2), ita(2), F_e(2) real :: drho, dr, cutoff, latt_c(2), mass(2) integer :: Nrho, Nr, atom_num, ind(2) character* 50 :: comment(3) character* 7 :: elem(2), latt(2) real,allocatable :: rho(:, :), F(:, :), phi(:, :), radius(:, :) integer :: i, j real :: rou_n(2), rou_0(2) namelist /eam_para/ re, fe, rou_e, arfa, beta, A, B, kai, lambda, Fn0, Fn1, Fn2 , Fn3, F0, F1, F2, F3, ita, F_e namelist /eam_format/ drho, dr, cutoff, latt_c, Nrho, Nr, atom_num, ind, elem, comment, latt, mass do while(1) !display the welcome screen write(*,*)'EAM_Table Program Version 1.0' write(*,*)'=====================================================================' write(*,*)'Generating the setfl format file for eam/alloy potential in lammps' write(*,*)'Programmer: Bin Ouyang, McGill university' write(*,*)'Supervisor: Jun Song, McGill university' write(*,*)'Department of Mining and Materials Engineering, McGill University' write(*,*)'Date: 2012.04.17' write(*,*)'======================================================================' write(*,*)' ' write(*,*)'Please enter the name of eam parameter file' read(*,*) para_file !reading the parameter file of eam potential open(1,file = para_file,status = 'old') read(1,eam_para) read(1,eam_format) close(1) allocate(rho(Nrho, atom_num), F(Nrho, atom_num), radius(Nrho, atom_num)) allocate(phi(Nrho, atom_num + 1)) write(*,*)' ' write(*,*)'Reading parameter file successfully!' write(*,*)'Please enter the name of output file' write(*,*)'Format: ***.eam.alloy' read(*,*)out_file write(*,*)' ' !Calculating the required table of parameters for EAM do i = 1, Nrho radius(i, 1) = (i-1) * dr radius(i, 2) = radius(i, 1) enddo !discrete the radius do i = 1, 2 rho(:, i) = fe(i) * exp(-1 * beta(i) * (radius(:, i) / re(i) - 1)) / (1 + (radius(:, i) / re(i) - lambda(i)) ** 20) enddo !calculating the density of electonic rou_n = rou_e * 0.85 rou_0 = rou_e * 1.15 do i = 1, 2 do j = 1, Nr if(rho(j, i) rou_n(i))then F(j, i) = Fn0(i) + Fn1(i) * (rho(j, i) / 0.85 / rou_e(i)) + Fn2(i) * (rho(j, i) / 0.85 / rou_e(i)) ** 2 + Fn3(i) * (rho(j, i) / 0.85 / rou_e(i)) ** 3 elseif(rho(j, i) rou_0(i))then F(j, i) = F0(i) + F1(i) * (rho(j, i) / rou_e(i)) + F2(i) * (rho(j, i) / rou_e(i)) ** 2 + F3(i) * (rho(j, i) / rou_e(i)) ** 3 else F(j, i) = F_e(i) * (1 - ita(i) * log(rho(j, i) / rou_e(i))) * (rho(j, i) / rou_e(i)) ** ita(i) endif enddo enddo do i = 1, Nr phi(i, 1) = A(1) * exp(arfa(1) * (1 - radius(i,1) / re(1))) / (1 + (radius(i,1) / re(1) - kai(1)) ** 20) enddo do i = 1, Nr phi(i, 3) = A(2) * exp(arfa(2) * (1 - radius(2,2) / re(2))) / (1 + (radius(i,2) / re(2) - kai(2)) ** 20) enddo do i = 1, Nr phi(i, 2) = 0.5 * (rho(i, 1) / rho(i, 2) * phi(i, 3) + rho(i, 2) / rho(i, 1) * phi(i, 1)) enddo !End of calculation !Wrting the eam/alloy file open(2, file = out_file) write(2,*) comment(1) write(2,*) comment(2) write(2,*) comment(3) write(2,*) atom_num, ' ', elem(1), elem(2) write(2,*) Nrho, drho, Nr, dr, cutoff write(2,*) ind(1), mass(1), latt_c(1), latt(1) do i = 1, Nrho / 5 write(2,*) F(5 * i - 4, 1), F(5 * i - 3, 1), F(5 * i - 2, 1), F(5 * i - 1, 1), F(5 * i, 1) enddo do i = 1, Nr / 5 write(2,*) rho(5 * i - 4, 1), rho(5 * i - 3, 1), rho(5 * i - 2, 1), rho(5 * i - 1, 1), rho(5 * i, 1) enddo write(2,*) ind(2), mass(2), latt_c(2), latt(2) do i = 1, Nrho / 5 write(2,*) F(5 * i - 4, 2), F(5 * i - 3, 2), F(5 * i - 2, 2), F(5 * i - 1, 2), F(5 * i, 2) enddo do i = 1, Nr / 5 write(2,*) rho(5 * i - 4, 2), rho(5 * i - 3, 2), rho(5 * i - 2, 2), rho(5 * i - 1, 2), rho(5 * i, 2) enddo do i = 1, Nr / 5 write(2,*) phi(5 * i - 4, 1), phi(5 * i - 3, 1), phi(5 * i - 2, 1), phi(5 * i - 1, 1), phi(5 * i, 1) enddo do i = 1, Nr / 5 write(2,*) phi(5 * i - 4, 2), phi(5 * i - 3, 2), phi(5 * i - 2, 2), phi(5 * i - 1, 2), phi(5 * i, 2) enddo do i = 1, Nr / 5 write(2,*) phi(5 * i - 4, 3), phi(5 * i - 3, 3), phi(5 * i - 2, 3), phi(5 * i - 1, 3), phi(5 * i, 3) enddo close(2) write(*,*) 'The eam potential file has been output succesfully!' deallocate(rho, F, radius, phi) enddo end program
个人分类: Computational Materials Science|10010 次阅读|5 个评论
ifort 安装笔记
nadia1989 2012-3-20 20:41
安装 Intel Fortran Compiler Linux* 专业版 下载地址:http://www.intel.com/cd/software/products/apac/zho/343156.htm 下载后然后解压,里面有install.sh,直接运行就OK,默认的安装地址是/opt/intel。 在安装ifort编译器,里面就自带了Intel MKL数学函数库,因此不用再另行安装MKL库。 设置环境变量 在安装的最后,有提示如何设置环境变量:在终端编辑器里 $ vim ~/.bashrc #这个是设置个人配置的系统环境变量的地方 #可以模仿里面语法添加ifort的环境变量,我写的是 if ; then source /opt/intel/bin/compilervars.sh intel64 fi #添加MKL的环境变量,这个没有提示,但是MKL的说明文档里面有。 if ; then source /opt/intel/composer_xe_2011_sp1/mkl/bin/intel64/mklvars_intel64.sh fi 说明文档 安装的目录下会有一个文件夹提供说明文档,比如我的就是/opt/intel/composer_xe_2011_sp1/Documentation,发现不仅有ifort的说明文档,也有MKL的说明文档。 说明文档也可以从网上下载PDF版本的。我记得ifort的说明文档是3800多页的!!! 里面MKL的说明文档比较有用,其中提示了如何设置MKL环境变量。还有mkl_link_line_advisor.htm,对于如何选择库文件非常有用。但是听说大部分程序不用单独添加MKL的库文件,仅在ifort编译器的选项里面添加 -mkl这个优化参数就可以了。 ifort优化参数 ifort编译器提供了非常多的优化参数,平时用的时候,大家用 $ ifort --help | more 查看就可以 也可以定位到某个参数 $ifort --help | grep -5 '-mkl' -5表示显示查找到的行及下面5行的内容。
个人分类: compiler|18420 次阅读|0 个评论
Calling managed dll from Fortran
ceXuLi 2012-3-20 10:16
We love advanced computer language because it is easy to learn and easy to use. However, valuable heritage of basic language is still our treasures. It’s easy to quote “code” of basic language, such as FORTRAN, in advanced language. But the path of calling advanced “code” in basic language is tourist. In this article, the path of calling managed C# dll in FORTRAN is addressed. Routing C# cannot generate traditional dll, which is unmanageable and face to progress. So managed dll exported in C# cannot be used in FORTRAN directly. A wrapper is required. C++ can be used as this media. The managed dll from C# can be wrapped by C++. Further a unmanaged dll can be generated and called in Fortran. Basic knowledge Tip1: Export dll using c++ Step 1: Open VS2008: New, VC++, Win32 Project; then named the file as “Trans”. Then click next and choose application type as dll. Step 2: implement the following code in trans.cpp file: //____________________________________________________ #include "stdafx.h" extern "C" { __declspec(dllexport) void TRANS(int a, int b, int* c) ; } void TRANS(int a, int b, int* c) { *c = a+b; } //____________________________________________________ OK! You can find the trans.dll and trans.lib file in debug directory. Tip2: Calling dll in Fortran Step 1: Create a new “simple”, Fortran console application, Project. Compile it. Step 2: Copy trans.dll and trans.lib to the directory where the Fortran application lives. Step 3: Click Projects, Settings, Link. Then you can locate the box of “Object/Library modules”. Enter “trans.lib” in that box. “Space “ is using as the indicator between different libs. Step 4: The following code can be used to quote the function in dll files, as: !_______________________________________________________________ program Call implicit none ! Variables INTERFACE subroutine TRANS(a, b, c) !dec$ attributes c, dllimport, alias:'_TRANS' :: TRANS integer*4 a integer*4 b integer*4 c END subroutine END INTERFACE ! Body of Call integer Iget, Ia, Ib call trans(1, 2, Iget) write(*, *) Iget Read (*, *) Ia Read (*, *) Ib call trans(1, 2, Iget) write(*, *) Iget Read (*, *) Ia end program Call !_______________________________________________________________ OK! You can found the dll is work. Tip3: Debug c++ dll using Fortran exe Step 1: There are two ways to link the dll and exe. First, you can change the directory of dll generated in C++ from the debug directory of C++ project To the directory of Fortran project. Which follows: Projects, properties, output directory. Alternatively, You can copy the fortran exe to the debug directory to C++ project. Otherwise, the dll and the exe file are not linked together. Step2: click debugging, select the call.exe . Done! Tip 4: Generate dll utilizing C# Step 1: Open VS2008, Click “Other languages, Visual C#, Windows, Class library” To Create a new lib project. Named the project as Csharpdll Step 2: Typing the followed code in the calss1.cs //___________________________________________________________ namespace Csharpdll { public class Class1 { public static void Multiply(double a, double b, double c) { c = a*b; } } } //___________________________________________________________ Step 3: build the project. OK! You can find the dll in the project directory/Csharpdll/obj/. Copy csharpdll.dll to any place you want to use it. Tip 5: Calling C# dll in C++ Step 1: Open VS2008, Create a new, VC++, Win32, Console application. Name it as “trycall”. Step 2: Click Project, Properties, Configuration properties, General, Common language Runtime Support. Switch the value to Common language Runtime Support/Clr. Step 3: copy csharpdll.dll to the directories of this project, i.e. both the directory of debug and the directory saving cpp file. Step 4: Click Trycall.cpp in the source files and input the followed code: //_______________________________________________________ // Trycall.cpp : Defines the entry point for the console application. #include "stdafx.h" #include iostream using namespace std; #using "Csharpdll.dll" using namespace Csharpdll; int _tmain(int argc, _TCHAR* argv char* System::String string Integer*4 dimension D(*) int* int* //***********************Example**************************// 1st part in C#: //_______________________________________________________ // C#, Class1.cs namespace Csharpdll { public class Class1 { public static unsafe void Multiply(int* array, string msg) { array = array * array ; msg = msg.ToLower(); } } } //_______________________________________________________ 2nd part in the head file of C++: //_______________________________________________________ // C++, stdafx.h #include string using namespace std; using namespace System; std::string ConvertTostring(System::String^ str); //_______________________________________________________ 3rd part in the cpp file of C++: //_______________________________________________________ // C++, Trycall.cpp #include "stdafx.h" #include iostream using namespace std; #using "Csharpdll.dll" using namespace Csharpdll; #include string using namespace System; int _tmain(int argc, _TCHAR* argv ={9,9,9}; int *a; a = b; std::string str1 = "STRING IS TROUBLE!! HAHA"; cout str1 "\n"; //Convert the string String^ str2 = gcnew String(str1.c_str()); str1 = ConvertTostring(str2); cout str1 "\n"; Csharpdll ::Class1:: Multiply( a, str2); //attention: str2 is const, it’s value can be transfer into c# by dll. //but str2 will not be updated after the transfer. str1 = ConvertTostring(str2); cout str1 "\n"; cout b "\n"; cin b ; return 0; } std::string ConvertTostring(System::String^ str) { int q=(int)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str); char* p=(char*)q; return std::string(p); } //***********************Example**************************// Step by step: calling c# dll from FORTRAN Step 1: Create a class library project using C#. Set unsafe build mode! The main class contains: //___________________________________________________ namespace Remesh { public class Cremesh { public static unsafe void remesh(double* Coordinate, string key, double* variable) { } } } //___________________________________________________ Step 2: Create a win32 dll project using VC++. Copy Remesh.dll to the right directory. Set option: Common language Runtime Support/Clr. The WRAP.h head file contains: //___________________________________________________ #include string using namespace std; using namespace System; std::string ConvertTostring(System::String^ str); extern "C" { __declspec(dllexport) void REMESH(double* coor, char* key, double* variable, char* filename); } //___________________________________________________ The WRAP.cpp file contains: //___________________________________________________ #include "stdafx.h" #include "wrap.h" #include string using namespace std; using namespace System; #using "Remesh.dll" using namespace Remesh; std::string ConvertTostring(System::String^ str) { int q=(int)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str); char* p=(char*)q; return std::string(p); } void REMESH(double* coor, char* key, double* variable, char* filename) { std::string str; str = key; String^ Strkey = gcnew String(str.c_str()); str = filename; String^ Strfilename = gcnew String(str.c_str()); Remesh::Cremesh::remesh(coor, Strkey, variable, Strfilename); } //___________________________________________________ Step 3: Create a fortran project; copy wrap.dll and wrap.lib to the right diretory; add wrap.lib to the project link properties. The *.f90 contains !_____________________________________________________ program Call implicit none ! Variables INTERFACE subroutine REMESH(coor, key, variable, filename) !dec$ attributes c, dllimport, alias:'_REMESH' :: REMESH double precision coor character*(*) key double precision variable character*(*) filename END subroutine END INTERFACE double precision coor, variable character*100 filename character*10 key dimension coor(1000, 2), variable(10, 2), key(10) ! Body of Call call remesh(coor(1, 1), key(1), variable(1, 1), filename) end program Call !_____________________________________________________ Ok! Everything is done.
个人分类: Numerical Simulation|4467 次阅读|0 个评论
[转载]Compiling Fortran Under Linux
GoogleMIT 2012-2-23 16:46
本文引自UBUNTU LINUX Fortran 编程中相关文件后缀 .a 静态库 (archive) .f , .for , .FOR .ftn *, .f90 *, .f95 *, .f03 * Fortran源代码(不需编译预处理) .F , .fpp , .FPP .FTN *, .F90 *, .F95* , .F03 * Fortran源代码(需要编译预处理) .r Fortran源代码(需要RatFor编译预处理) .o 对象文件 .s 汇编语言代码 .so 动态库 其中,标 * 的后缀名是gfortran的文件后缀,g77不能识别。 单个源文件生成可执行程序 传统的 Fortran 程序(也就是以 Fortran 77 为代表的)只能用大写字符书写,而且每行前六个字符为特定用途所保留。第一列为字符 C 或 * 所保留,用来表征整行都是注释。第二列到第六列是为标号预留的。代码从第七列开始,到72列结束(73列及以后将被直接忽略,可作注释)。下面是示例程序 采用的是传统的 Fortran 格式: C helloworld.f C PROGRAM HELLOWORLD WRITE ( * , 10 ) 10 FORMAT ( 'hello, world' ) END PROGRAM HELLOWORLD 编译器 gfortran 并不要求所有代码都大写──程序中任何关键词都可以用小写字母。下面的命令将该程序编译成可执行文件: $ gfortran helloworld.f -o helloworld 注意到:gfortran 默认会将 .f, .for, .fpp, .ftn, .F, .FOR, .FPP 和 .FTN 结尾的文件作为固定格式处理,而将.f90, .f95, .f03, .F90, .F95 和 .F03 结尾的文件作为自由格式来处理。如果我们将上面程序文件重命名为 helloworld.f90,那么我们必须手动指定其为固定格式: $ mv helloworld.f helloworld.f90 $ gfortran helloworld.f90 -ffixed-form -o helloworld Fortran 90及以后的标准允许并鼓励用自由的格式书写 Fortran 代码。注释以感叹号(!)开始直到行尾。先前的程序采用自由格式重写如下,其中语句、标号都可从任一列开始: ! helloworldff.f90 ! Program Helloworld write ( * , 10 ) 10 format ( 'hello, world' ) end Program Helloworld 后缀名为 .f90,故 gfortran 将其作为自由格式处理 $ gfortran helloworldff.f90 -o helloworldff 同样,如果将程序重命名为传统后缀名,那么要通过在命令行中加入选项 -ffree-form 进行编译,如下: $ mv helloworldff.f90 helloworldff.for $ gfortran -ffree-form helloworldff.for -o helloworldff 由于两种格式的具有很大的区别,程序书写是只能选择其中的一种格式进行书写。注意:遵守后缀约定是很重要的。 多个源文件生成可执行程序 命令 gfortran 可将多个 fortran 源码文件编译链接成为一个单一的可执行程序。下面列出了一个保存在文件 caller.f 中的简单程序的主体部分,它调用一个函数并显示出结果: C caller.f C PROGRAM CALLER I = Iaverageof ( 10 , 20 , 83 ) WRITE ( * , 10 ) 'Average=' , I 10 FORMAT ( A,I5 ) END PROGRAM CALLER 名为 Iaverage 函数定义在另一个独立的源文件中,如下: C called.f C INTEGER FUNCTION Iaverageof ( i,j,k ) Iaverageof = ( i + j + k ) / 3 RETURN END FUNCTION Iaverageof 通过下面的语句这两个源码文件可被编译链接成一个名为 caller 的可执行程序: $ gfortran caller.f called.f -o caller 同样的结果可由下面的命令序列得到──先将每一个源码文件编译成对象文件,而后将对象文件链接为可执行程序: $ gfortran -c caller.f -o caller.o $ gfortran -c called.f -o called.o $ gfortran caller.o called.o -o caller 生成汇编代码 选项 -S 指示编译器 gfortran 生成汇编语言代码然後结束。要得到我们本文先前的 helloworld.f 例子的汇编代码,只需输入以下命令: $ gfortran -S helloworld.f 生成的汇编语言文件名为 helloworld.s。汇编语言的具体形式依赖于编译器的目标平台。 编译预处理 编译以 .F, .fpp, .FPP, .FTN, .F90, .F95 和 .F03 结尾的文件时,在它真正编译之前需要预处理。预处理器原本是为协助 C 语言工作所设计的。下面的例子是一个自由格式的 Fortran 程序,它通过预处理器将一个函数包含进主程序: ! evenup.F90 ! #define ROUNDUP #include "iruefunc.h" ! program evenup do 300 i = 11 , 22 j = irue ( i ) write ( * , 10 ) i,j 300 continue 10 format ( I5,I5 ) end program evenup 函数 irue() 的源代码保存在文件 iruefunc.h 中,根据宏 ROUNDUP 所定义的值的不同将产生不同的编译结果。该函数将任何一个奇数近似为一个偶数。默认情况下,它向下取近似,但是当 ROUNDUP 被定义时,该函数将向上取近似而得到一个偶数。ireu() 的函数体如下: integer function irue ( i ) k = i / 2 k = k * 2 if ( i . EQ . k ) then irue = i else #ifdef ROUNDUP irue = i + 1 #else irue = i - 1 #endif end if end function irue 下面的命令将该程序编译成可执行文件: $ gfortran evenup.F90 -o evenup 采用自由格式写程序以利用预处理器不是必须的。固定格式程序也可进行编译预处理,下面的程序也是有效的: C adder.F C #define SEVEN 7 #define NINE 9 C program adder isum = SEVEN + NINE write ( * , 10 ) isum 10 format ( I5 ) end program adder 下面的命令将该程序编译成可执行文件: $ gfortran adder.F -o adder 理解gfortran是gcc的前端 像 g++ 一样,gfortran 也只是设置过 Fortran 程序所需基本环境的 gcc 的一个前端。本文一开始的例子我们可以通过下面 gcc 的命令来编译: $ gcc helloworld.f -o helloworld -lgfortran -lgfortranbegin 库文件 libgfortranbegin.a (通过命令行选项 -lgfortranbegin 被调用) 包含运行和终止一个 Fortran 程序所必须的开始和退出代码。库文件 libgfortran.a 包含 Fortran 底层的输入输出等所需要的运行函数。当运行 gfortran 时,会自动链接这两个库。这和下面的命令是等价的: $ gfortran helloworld.f -o helloworld 当我们运行 gfortran 时,实际上运行并不是这个编译器,而是编译器驱动器。该驱动器解析命令行中所给出的选项,然后才调用真正的编译器,汇编器和链接器。默认情况下,编译器驱 动器根据命令行中给定的文件的后缀决定它自己下一步的动作:一个名为 foo.c 将传递给 C 编译器,而名为 foo.f95 的文件将传递给 Fortran 95 的编译器,等等。 理解了这一点,我们就可以知道 gcc helloworld.f 将自动调用 fortran 的编译器。只不过我们要为链接器指定必要的库。 理解了这一点,我们可以知道 gfortran helloworld.c 可以编译一个 c 程序,gfortran helloworld.cpp -lstdc++ 编译的是一个 C++ 程序。 特别:在Ubuntu下,NTFS,FAT格式分区的任何文件都没有可执行权限!也就是说,必须把代码放在自己的Home目录,然后编译运行才能成功。
个人分类: PROGRAM|611 次阅读|0 个评论
[转载]gfortran,g77,g95 Introductions
GoogleMIT 2012-2-23 16:35
Gcc Fortran Intro 出自Ubuntu中文 在 GCC 4.0 之前, g77 是 GCC 的一部分;此后, gfortran 是 GCC 的一部分。 g95 是一个基于 GCC 的 Fortran 编译器,它不是 GCC 的一部分。 g77介绍 g77 是 Fortran77 的编译器。它对 Fortran 77 标准提供完备的支持,并支持 Fortran 90 和 95 的部分特性。 由于 Fortran 77 标准在数值计算中的影响力,g77 可能是应用最广的Fortran编译器。 在 GCC 4.0 之前,g77 是 GCC 的一部分,但现在,g77 已经停止开发。 g77为何不再被支持 : gcc-4.0 改变了 gcc 中所有语言的前端界面。由于缺少志愿者和公司来更新 g77 到 gcc-4.0 的架构,因此它被废弃了。不同于 g77,gfortran 项目处于活跃开发期,因此它 取代了 g77 的位置。 这是一篇 g77使用入门 注意 :从8.10 开始,Ubuntu 软件仓库中不再包含 g77 软件包 gfortran介绍 GNU 的 Fortran 95 编译器,支持Fortran95和一部分Fortran2003的功能。 取代 g77 集成在 GCC 4.0 及以後版本中 这是一篇 gfortran使用入门 关于g95 gfortran 不是 g95 : gfortran 是一个 Fortran 95 的编译器,它是 GCC 的一部分。 g95 是另一个 Fortran 95 的编译器,它是一个基于 GCC 的编译器。 历史 : Andrew Vaught 在 2000 年上半年创建了 g95──一个使用 GCC 做后端的开放源代码的 Fortran 95 编译器。在随后的两年里,这是一个多人协作的项目,但是 2002 年下半年 Andrew Vaught 决定单独开发 g95。2003 年 1 月,gfortran 项目创建,它建立在当时 GPL 授权的 g95 源码的基础上,目的是允许协同开发并与 GCC 代码集成。 从那时起,Andrew 一个人在持续地开发 g95,g95 与 gfortran 的差别也越来越大。因此,gfortran 项目组也无法为 g95 提供支持或建议。
个人分类: PROGRAM|666 次阅读|0 个评论
修改6Sv4.1源码,生成查找表LUT文件
热度 1 wishmo 2011-12-16 10:41
逐像元大气校正,常预先计算查找表(LUT,LookUp Tabel),6S大气辐射传输模式也可以用来计算LUT。但6S源程序输出信息多,且浮点数输出精度低,不利于提取关键信息生成LUT,本文描述了怎样修改6S源码以生成LUT。 首先确定LUT内容要素。 阅读MODIS M?D04 气溶胶产品生成算法文档(NASA相关网页),归纳了以下查找表要素: 1) 太阳天顶角 观测天顶角 太阳方位角观测方位角之差(相对方位角) 散射角 2) 大气模式 3) 气溶胶模式 4) 550nm气溶胶光学厚度 5) 波段号 6) 大气透过率 7) atm. intrin. ref.(个人理解,这是大气程辐射换算后的反射率,用于校正计算) 8) total sca. (总散射,包括rayleigh散射和气溶胶散射,用于校正计算) 9) 表观反射率 10) 校正后的地表反射率 11) xa xb xc参数(物理意义不明,用于校正计算) 其次,阅读源码,明确LUT各要素在6S源码中的变量名。 6S大气校正计算源码(Excel验证中采用此公式) rog=rapp/tgasm rog=(rog-ainr(1,1)/tgasm)/sutott/sdtott rog=rog/(1.+rog*sast) xa=pi*sb/xmus/seb/tgasm/sutott/sdtott xb=srotot/sutott/sdtott/tgasm xc=sast 由计算源码确定需输出的变量。下面是输出LUT要素的Fortran77代码示例,建议放在源码main.f中stop一句之前。 write(*,*) asol,phi0,avis,phiv,adif,phi,idatm,iaer,v,taer55, 1 iwave,tgasm,ainr(1,1),sutott*sdtott,rapp,rog,xa,xb,xc 其中,asol是太阳天顶角,phi0是太阳方位角,avis是观测天顶角,phiv是观测方位角,adif是散射角,phi是相对方位角,idatm是大气模式号,iaer是气溶胶模式号,v是水平能见度,taer55是550nm气溶胶光学厚度,iwave表示波段号,tgasm表示大气透过率,ainr(1,1)是大气本身的反射率(姑且这么理解),sutott*sdtott表示总散射,rapp是表观反射率,rog是校正后的地表反射率,xa,xb,xc是校正计算参数。Fortran77每行长度不能超过80个字符,续前行需在特定位置指明(示例中的iwave前的1即表示续行)。 该示例源码没有指定任何输出样式。浮点数会按默认样式输出,小数点后的位数比较多,精度较好。挑选一个查找表文件用Excel验证后表明,excel公式计算值与6S输出值之间最大误差小于1E-7。说明方法是可行的。 再次,编译源码,编写Shell脚本: 编译环境:OpenSUSE操作系统 g95编译器,版本未知。 编译命令:g95 *.f -o sixs(在BRDF相关代码处可能有几个warning,本文不涉及BRDF,故暂不修改调试。在Windows下用f77编译,无warning,编译通过) 生成LUT的bash脚本getLUT.sh: function LUTCalc(){ #{42,44,48,25,27,30} IBand=$1 echo "Band ${IBand} is running" for Iref in {0.1,0.5} do fstat=${IBand}"_"${Iref}.res echo "asol,phi0,avis,phiv,adif,phi,idatm,iaer,v,taer55, iwave,tgasm,ainr,tott,rapp,rog,xa,xb,xc" ${fstat} for SolarZ in {0,15,30,45,75} do for SolarAz in {0,45,90,135} do for ViewZ in {0,15,30,45,75} do for Iaer in {1,2,3,5,6,7} do for Idatm in {1,2,3,4,5,6} do for IAod in {0.1,0.2,0.5,1.0}; do # Run sixs and output ./sixs EOF | tail -n 1 ${fstat} 0 $SolarZ $SolarAz $ViewZ 0 3 15 $Idatm $Iaer 0 $IAod -0 -1000 ${IBand} 0 0 0 0.0 -$Iref EOF done done done done done done done } function getLUT() { echo "LUT is Calcing" for iwave in {42,44,48,25,27,30} { LUTCalc ${iwave} } } 最好晚上计算早晨看结果,如果CPU给力的话,几个小时后就可以得到结果。 下面是生成的LUT示例: asol,phi0,avis,phiv,adif,phi,idatm,iaer,v,taer55, iwave,tgasm,ainr,tott,rapp,rog,xa,xb,xc 0.0000000 0.0000000 0.0000000 0.0000000 180.00000 0.0000000 1 1 63.664398 0.10000000 25 0.98984975 6.89081103E-02 0.80637234 0.10000000 3.87301818E-02 1.98730151E-03 8.71319696E-02 0.14777245 0.0000000 0.0000000 0.0000000 0.0000000 180.00000 0.0000000 1 1 26.739149 0.20000000 25 0.98984975 7.75793791E-02 0.76532620 0.10000000 2.94530466E-02 2.09388486E-03 0.10336593 0.16389242 0.0000000 0.0000000 0.0000000 0.0000000 180.00000 0.0000000 1 1 8.4940033 0.50000000 25 0.98984975 0.10173188 0.64870018 0.10000000 -2.69861287E-03 2.47033220E-03 0.15994170 0.20088956 0.0000000 0.0000000 0.0000000 0.0000000 180.00000 0.0000000 1 1 3.5674956 1.0000000 25 0.98984975 0.13688390 0.48083964 0.10000000 -7.89646432E-02 3.33272247E-03 0.29038188 0.24035060 ……
个人分类: 6S|3934 次阅读|1 个评论
intel visual fortran 备忘
Daniel1985 2011-11-19 22:11
1 在vs2008如何设置可以显示行号? 菜单-工具-选项在新窗口中左面树菜单中展开“文本编辑器”,找到子项“所有语言”在右面的面板中 显示的行号前面打勾 2 运行之后,dos窗口一闪而过的解决 stop的上一行加pause, 这样就能按回车以后再结束程序了 (注意不能在stop之后)。 比如程序: program main implicit none write(*,*) Hello! pausestop end 其实这是因为直接用F5 start debugging的原因 如果用ctrl+F5 start without debugging就不会出现上述的问题。 3 声明语句中的双冒号 加两个冒号是新的语法。 Integer :: iA , iB 这样子与 Integer iA , iB 没有任何区别 但是当变量声明包含修饰词,比如 Allocatable , Private , Public , Parameter , Intent , Optional 等的时候,两个冒号就非常有必要了,它表示修饰词结束了。 比如: Integer,Allocatable,Public :: iA( : ) , iB( : ) 就不能省略两个冒号。 推荐你定义变量时都采用 两个冒号的 新写法。 有了两个冒号你在声明是可以直接符值
个人分类: ABAQUS/PYTHON/FORTRAN|5561 次阅读|0 个评论
【Fortran】format语句中如何指定重复数目的格式描述符
热度 1 ddbb12 2011-10-12 09:37
好久没有写过与fortran程序相关的东西了。Fortran语言作为至今仍在研究界有很大的市场,因其语法简单,规则较少,使用灵活,的确是吾辈求解方程、数值模拟的一大神器。具体的关于Fortran语言的介绍和基本规则,可参考很多的参考书和搜索引擎。笔者本人学习的是 《 Fortran 95 程序设计》彭国伦 ,可参考本博书评。 昨天想到了这样一个问题: 例如,有一个可变数组(allocatable array)是N维的,dimesion :: a(N), 如果N=10,那么可以用 write(*,100)(a(i)i=1,10) 100 format(10(I3)) 之类的代码来进行输出操作。 但是,如果一旦N发生变化,那么只有修改format语句才能正确输出。而且,为了保持所有平台的兼容性,应该要求每次N发生变化的时候,都应该修改代码。这有失一般性。 那么 有没有可能在fortran的format语句中用变量来表示描述符的个数, 或者说, 有没有可能将format写成: format(N(I3))之类的格式呢? 实际上这个问题可以有多种解决途径: 1、 据说在CVF下可以使用一个尖括号来写成如nI3,从而来表示可变的重复次数。CVF的手册上也是如此说的,但是由于没有环境,因此没有做过测试。 2、可先设计一个不限长度的字符串,然后通过显示循环,将要输出的a(i)的每个元素都以内部文件的形式写入该字符串中。最后只需要输出该字符串即可。 3、实际上最后解决问题的方法是这样的,只需要将format中这个要重复描述符前面的这个数字设计成一个比待输出的元素个数的大的整数即可。该方法已经在gfortran下调试通过,没有任何错误。 当然,这种方法也有缺陷,如果这些重复输出元素不是输出列表中的最后一项就会出现格式错乱。、 话说,格式这种事情本来就很容易的错乱的。人们总是希望输出的东西好看,整齐,有规律,所以才有了格式这个玩意。Anyway,也算是个小问题。
个人分类: 计算机|12984 次阅读|1 个评论
[转载]Writing Matlab MEX files using Fortran 90 / 95
Amedee 2011-6-10 19:45
Writing Matlab MEX files using Fortran 90 / 95 (and the Intel Fortran Compiler) (Update: This text refers primarily to Matlab 6.5 (aka R13) on Linux. The new Matlab 7 introduces some serious problems with non-GNU compilers due to throwing C++ exceptions. Read on below for details.) The Mathworks do not officially support Fortran 90 / 95, but thanks to people like Benjamin Barrowes who wrote Matlab2FMex and the people at NAGWare it is easy to figure out how to write your MEX files in Fortran 95 without using the awkward mxCopy-functions. Lately, I also found this page which describes in detail all three possible ways of writing MEX files entirely in Fortran. However, of these three ways, two make a local copy of the data which is less than optimal when dealing with big data sets. The trick is to declare the pointer you get from mxGetPr as an integer pointer and pass it along with the array dimensions to a subroutine which expects a double precision array. Until now this works for me without any problems (Linux w/ Intel Compiler), but note that this procedure may not work with every compiler or on any platform. I may also add that this procedure does NOT work with the F programming language as it forbids passing "wrong" arguments to subroutines. First, we will have to explicitly define the used Matlab API functions, at least those who have a return value: module mexf90 interface function mxGetPr(pm) integer,pointer :: mxGetPr integer :: pm end function mxGetPr function mxGetM(pm) integer :: mxGetM,pm end function mxGetM function mxGetN(pm) integer :: mxGetN,pm end function mxGetN function mxCreateDoubleMatrix(m,n,type) integer :: m,n,type,mxCreateDoubleMatrix end function mxCreateDoubleMatrix function mxGetScalar(pm) integer :: pm double precision :: mxGetScalar end function mxGetScalar end interface end module mexf90 Just save this file under the name "mexf90.f90" and compile it. (For those unfamiliar with modules: you must prevent the compiler from also linking the file - this is usually done with the "-c" switch.) This module contains only the API functions we need, but adding further functions is straightforward. You may also click here to obtain a module file with more function definitions. Now the actual function, consisting of the subroutine "mexFunction" (the gateway to Matlab) and an example subroutine "scalarMult" which multiplies the matrix A with a scalar "alpha" and returns the result in matrix B. The comments should make clear how these functions work: subroutine mexFunction(nlhs, plhs, nrhs, prhs) use mexf90 ! API function definitions implicit none integer, intent(in) :: nlhs, nrhs integer, intent(in), dimension(*) :: prhs integer, intent(out), dimension(*) :: plhs integer :: m,n integer, pointer :: A,B ! These are the pointers to the matrix data double precision :: alpha ! Check input arguments if(nrhs /= 2) then call mexErrMsgTxt('Function requires two input arguments.'); end if ! Get data and size of the input matrix A = mxGetPr(prhs(1)) m = mxGetM(prhs(1)) n = mxGetN(prhs(1)) ! Get scalar value alpha = mxGetScalar(prhs(2)) ! Create output matrix plhs(1) = mxCreateDoubleMatrix(m,n,0) B = mxGetPr(plhs(1)) ! Call subroutine for multiplication call scalarMult(A,B,alpha,m,n) end subroutine mexFunction subroutine scalarMult(A,B,alpha,m,n) implicit none integer :: m,n double precision :: alpha ! Now define the matrices with the actual data type and dimension double precision, dimension(m,n) :: A,B ! Now do something useful... B = alpha * A end subroutine scalarMult For more complex functions it is a bit tedious to pass the dimensions along with the data pointers - if someone finds a better solution, please let me know! The Intel Fortran Compiler for Linux is free for non-commercial use, but you probably already know that... These are the relevant parts in the mexopts.sh file for MEXing the above file with the IFC FC='ifort' FFLAGS='-fPIC -u -w95 -warn all' FLIBS='$RPATH $MLIBS -L/opt/intel_fc_80/lib -lm' FOPTIMFLAGS='-O3' FDEBUGFLAGS='-g' LD='icc' LDFLAGS='-pthread -shared' LDOPTIMFLAGS='$FOPTIMFLAGS' LDDEBUGFLAGS='-g' As you can see, I do not use any map-files as that didn't work for me. It is important to note that linking with "ifort" will usually NOT work, the above file uses the Intel C++ Compiler "icc" instead. If you do not have icc installed, the GNU compiler "g++" should also work. If you also want to use functions like "print", e.g. for debugging purposes (mexprintf does not allow format strings in Fortran...), you will also have to link "ifcore", but you should remove that for the release version of your code. If you get error messages about unresolved symbols when executing the mex-file, make sure that the Intel libraries are scanned by ldconfig or put them into your LD_LIBRARY_PATH before starting Matlab. If you get an error message saying that __MAIN couldn't be found, check again that you are NOT linking with ifort. If you still have problems with unresolved symbols, try using the "-static-libcxa" switch. Now happy MEXing! Oh, and also check out LAPACK95, incredibly useful for porting M-files to MEX. And Matran looks very promising, although I didn't use it yet. Update: Problems with Matlab 7 (aka R14) The new Matlab 7 now uses C++ exceptions in library functions like mexErrMsgTxt. As Matlab was built on Linux with the GNU compiler, this breaks practically all other compilers which are unable to correctly propagate these exceptions - including the Intel Fortran Compiler (and also Ocaml, by the way...). (A sidemark: the Intel C Compiler (icc) will usually work as long as you always compile with "-Kc++"). This means, as soon as a Fortran MEX file compiled with the Intel Fortran Compiler calls mexErrMsgTxt, the complete Matlab session will abort and return to the shell, as there is no handler to catch the exception. I doubt that there exists an easy solution to this problem, as the implementation of exception handling in C++ differs between different compilers. Thus, if you want to write your files entirely in Fortran 95 and you want to use the IFC, don't call mexErrMsgTxt or your session will abort. If you have to use mexErrMsgTxt, you are for now forced to use a GNU Fortran compiler. Although Fortran and C don't "know" exception handling, the GNU C and Fortran compilers have a switch "-fexceptions" which includes code to correctly propagate these exceptions. For Fortran 95 you can use either g95 or GNU Fortran 95, both work for creating Matlab MEX files as long as you compile with "-fexceptions". However, both compilers are not yet stable, but they are making rapid progress recently.
个人分类: 程序设计|3501 次阅读|0 个评论
[转载]转自:编程爱好者论坛 Fortran区的一篇置顶文章
gibbscat 2011-6-3 11:15
最近,好几个人问到公共块出错的问题,都是出于一个原因,特在这里把老贴整理一下供大家参考。 问题: 20924057 Compiling Fortran... D:\FRAME2D.FOR D:\FRAME2D.FOR(314) : Warning: Because of COMMON, the alignment of object is inconsistent with its type COMMON /EM/LM(6),ND,ASA(6,6),RF(6),SA(6,6)--------------------------^ D:\FRAME2D.FOR(314) : Warning: Because of COMMON, the alignment of object is inconsistent with its type COMMON /EM/LM(6),ND,ASA(6,6),RF(6),SA(6,6) ---------------------------^ 解答: 这是老Fortran的规则。看你的公共语句: COMMON /EM/LM(6),ND,ASA(6,6),RF(6),SA(6,6),SF(6),T(3,3) 根据程序的隐含规则,LM(6),ND一共是7个整型数,每个整型数4个字节,共28个字节,不是8字节的整倍数。紧跟着是双精度的ASA(6,6),每个数是8个字节,可是ASA(1,1)不能从8字节整倍数位置开始存储,这就是the alignment of object is inconsistent with its type 的意思。 对于警告错误,可以忽略,一般不致于影响执行结果。如果要改的话,有几种办法: 1)在公共语句中,把双精度数放在前边,整形数跟在后边; 2)在ASA()前插一个整型变量(哪怕是没用的),用来占用4个字节,以使得后面的双精度数可以从8字节整数倍位置开始存储。 准则:作为好的编程习惯,建议在公共块中,把实型变量排在前边,把整型数据放在后边,就不会有对位不整的错误! 仅供参考。 补充:(05.06.01) 公共块不是用堆栈实现的,是内存中的一段连续存储的数据。 按照Fortran的规定,当读取双精度数据时,总是假定前面的数据长度是双精度数字节长度(8个字节)的整数倍。 对于本例,ASA(1,1)从第29个字节开始存放8个字节;可是读取的时候,要从第33个字节(28不是8的倍数,32是28之后最小的8的倍数)开始读入8个字节,这就是定位(alignment)错误。 所以F90之后不提倡用公共块共享数据,而可用更为灵活的module来代替公共块共享数据。 公共块是过时的语言功能
个人分类: 未分类|2505 次阅读|0 个评论
fortran中有关指针的用法总结
热度 2 HL2004 2011-6-2 16:58
原来在C和C++ 中指针这一章让我痛苦死,后来发现在实际中如此有用,现在又在学Fortran,发现这两门语言的指针还是有很多不同的地方,为了便于自己学习和理解,特将有关fortran指针的用法写下来,以便随时查阅。它最简单的应用可以用来保存变量,或者是动态使用内存,或者是用于数据结构. 1、标准fortran指针并不代表变了在内存中的地址,而是代表这个变量的别名,相当与C中的引用。通过指针,同一个变量存储单元可以通过多个变量名来访问。当指针被声明后,程序并不会立即给它分配存储空间,而是要等到通过allocate语句进行动态内存分配或者通过指针赋值语句于一个目标变量联合起来后才会分配。目标变量都必须在程序的声明阶段被赋予target属性。 2、C++里面指针是一个独立的变量,它的作用就是为了保持它所指向的变量在内存中的地址,而fortran的指针则是共享了与它联合的变量的存储空间。当指针执行重新设置时,应考虑对原来分配的内存进行必要的处理,处理的方法就是将其交给其他的指针的管理,或者通过deallocated释放掉,否则会在计算机中形成一块已经被配置却无法进行操作的被丢弃内存,即所谓的指针悬挂问题。指针的状态可以通过ASSOCIATED(pointer,target)来检查指针释放设置了指向,返回值为逻辑型变量。还可通过NULLIFY来解除指针当前的联合状态,并使其指向不能使用的内存地址。 3、指针数组,它用来指向数组、数组片段或者数组元素或者用来连续分配内存空间。声明指针数组时冰箱将数组声明称延迟数组形式,但指针P指向数组中的第一个元素时,必须写成如下形式,p=N(1:1),而不允许写成p=n(1)。指针数组与目标数组建立联合时,必须具有相同的秩。以为指针数组也可以指向二维或多位目标数组中的某一位或某一维的某一片段,但是多位指针数组不允许指向比自己维数低的目标数组。 4、在写module的时候,如果输出为指针,并且此指针指向为一个在子程序的一个值,需要把这个值的定义放在module下面,不能放在子程序下面,最好加上private属性即可,如果不这样,主程序就不能读取这个指针值。 5、指针也可以作为参数传递近过程中(包括函数子程序和子例行程序),并可以作为函数的结果返回主调程序,但是需要注意,当使用指针作为参数传递进过程时,在主调过程中必须对被调函数进行显式得得接口说明,指针参数在声明时不需要使用intent属性进行说明,如果函数的返回值是指针,也需对该函数进行显式的接口说明。 6、指针数组与可分配数组非常相似,但是可分配数组仅存在于声明它的过程中,过程执行完毕后由系统自动收回它所占的存储空间,而指针数组必须通过deallocate语句来释放存储空间。
30107 次阅读|2 个评论
[转载]Linux下编译Fortran
wlusheng 2011-5-26 09:08
注意:本文是关于 gfortran 的文章。如果你不清楚 gfortran , g77 , g95 等等的概念的话,不妨看看 GCC 的 Fortran 语言编译器介绍 ;如果你要用 g77 的话,这是一篇 g77 入门 。 href="http://wiki.ubuntu.org.cn/index.php?title=Compiling_Fortranaction=edit§ion=2" Fortran 编程中相关文件后缀 .a 静态库 (archive) .f,.for,.FOR .ftn*,.f90*,.f95*,.f03* Fortran 源代码(不需编译预处理) .F,.fpp,.FPP .FTN*,.F90*,.F95*,.F03* Fortran 源代码(需要编译预处理) .r Fortran 源代码(需要 RatFor 编译预处理) .o 对象文件 .s 汇编语言代码 .so 动态库 其中,标 * 的后缀名是 gfortran 的文件后缀, g77 不能识别。 href="http://wiki.ubuntu.org.cn/index.php?title=Compiling_Fortranaction=edit§ion=3" 单个源文件生成可执行程序 传统的 Fortran 程序(也就是以 Fortran77 为代表的)只能用大写字符书写,而且每行前六个字符为特定用途所保留。第一列为字符 C 或 * 所保留,用来表征整行都是注释。第二列到第六列是为标号预留的。代码从第七列开始,到 72 列结束( 73 列及以后将被直接忽略,可作注释)。下面是示例程序采用的是传统的 Fortran 格式: Chelloworld.f C PROGRAMHELLOWORLD WRITE(*,10) 10FORMAT('hello,world') ENDPROGRAMHELLOWORLD 编译器 gortran 并不要求所有代码都大写 ── 程序中任何关键词都可以用小写字母。下面的命令将该程序编译成可执行文件: $gfortranhelloworld.f-ohelloworld 注意到: gfortran 默认会将 .f,.for,.fpp,.ftn,.F,.FOR,.FPP 和 .FTN 结尾的文件作为固定格式处理,而将 .f90,.f95,.f03,.F90,.F95 和 .F03 结尾的文件作为自由格式来处理。如果我们将上面程序文件重命名为 helloworld.f90 ,那么我们必须手动指定其为固定格式: $mvhelloworld.fhelloworld.f90 $gfortranhelloworld.f90-ffixed-form-ohelloworld Fortran90 及以后的标准允许并鼓励用自由的格式书写 Fortran 代码。注释以感叹号(!)开始直到行尾。先前的程序采用自由格式重写如下,其中语句、标号都可从任一列开始: !helloworldff.f90 ! ProgramHelloworld write(*,10) 10format('hello,world') endProgramHelloworld 后缀名为 .f90 ,故 gfortran 将其作为自由格式处理 $gfortranhelloworldff.f90-ohelloworldff 同样,如果将程序重命名为传统后缀名,那么要通过在命令行中加入选项 -ffree-form 进行编译,如下: $mvhelloworldff.f90helloworldff.for $gfortran-ffree-formhelloworldff.for-ohelloworldff 由于两种格式的具有很大的区别,程序书写是只能选择其中的一种格式进行书写。注意:遵守后缀约定是很重要的。 多个源文件生成可执行程序 命令 gfortran 可将多个 fortran 源码文件编译链接成为一个单一的可执行程序。下面列出了一个保存在文件 caller.f 中的简单程序的主体部分,它调用一个函数并显示出结果: Ccaller.f C PROGRAMCALLER I=Iaverageof(10,20,83) WRITE(*,10)'Average=',I 10FORMAT(A,I5) ENDPROGRAMCALLER 名为 Iaverage 函数定义在另一个独立的源文件中,如下: Ccalled.f C INTEGERIaverageof(i,j,k) Iaverageof=(i+j+k)/3 RETURN ENDIaverageof 通过下面的语句这两个源码文件可被编译链接成一个名为 caller 的可执行程序: $gfortrancaller.fcalled.f-ocaller 同样的结果可由下面的命令序列得到 ── 先将每一个源码文件编译成对象文件,而后将对象文件链接为可执行程序: $gfortran-ccaller.f-ocaller.o $gfortran-ccalled.f-ocalled.o $gfortrancaller.ocalled.o-ocaller 生成汇编代码 选项 -S 指示编译器 gfortran 生成汇编语言代码然後结束。要得到我们本文先前的 helloworld.f 例子的汇编代码,只需输入以下命令: $gfortran-Shelloworld.f 生成的汇编语言文件名为 helloworld.s 。汇编语言的具体形式依赖于编译器的目标平台。 href="http://wiki.ubuntu.org.cn/index.php?title=Compiling_Fortranaction=edit§ion=6" 编译预处理 编译以 .F,.fpp,.FPP,.FTN,.F90,.F95 和 .F03 结尾的文件时,在它真正编译之前需要预处理。预处理器原本是为协助 C 语言工作所设计的。下面的例子是一个自由格式的 Fortran 程序,它通过预处理器将一个函数包含进主程序: !evenup.F90 ! #defineROUNDUP # include"iruefunc.h" ! programevenup do300i=11,22 j=irue(i) write(*,10)i,j 300continue 10format(I5,I5) endprogramevenup 函数 irue() 的源代码保存在文件 iruefunc.h 中,根据宏 ROUNDUP 所定义的值的不同将产生不同的编译结果。该函数将任何一个奇数近似为一个偶数。默认情况下,它向下取近似,但是当 ROUNDUP 被定义时,该函数将向上取近似而得到一个偶数。 ireu() 的函数体如下: integerirue(i) k=i/2 k=k*2 if(i.EQ.k)then irue=i else #ifdefROUNDUP irue=i+1 #else irue=i-1 #endif endif endirue 下面的命令将该程序编译成可执行文件: $gfortranevenup.F90-oevenup 采用自由格式写程序以利用预处理器不是必须的。固定格式程序也可进行编译预处理,下面的程序也是有效的: Cadder.F C #defineSEVEN7 #defineNINE9 C programadder isum=SEVEN+NINE write(*,10)isum 10format(I5) endprogramadder 下面的命令将该程序编译成可执行文件: $gfortranadder.F-oadder 理解 gfortran 是 gcc 的前端 像 g++ 一样, gfortran 也只是设置过 Fortran 程序所需基本环境的 gcc 的一个前端。本文一开始的例子我们可以通过下面 gcc 的命令来编译: $gcchelloworld.f-ohelloworld-lgfortran-lgfortranbegin 库文件 libgfortranbegin.a( 通过命令行选项 -lgfortranbegin 被调用 ) 包含运行和终止一个 Fortran 程序所必须的开始和退出代码。库文件 libgfortran.a 包含 Fortran 底层的输入输出等所需要的运行函数。当运行 gfortran 时,会自动链接这两个库。这和下面的命令是等价的: $gfortranhelloworld.f-ohelloworld 当我们运行 gfortran 时,实际上运行并不是这个编译器,而是编译器驱动器。该驱动器解析命令行中所给出的选项,然后才调用真正的编译器,汇编器和链接器。默认情况下,编译器驱动器根据命令行中给定的文件的后缀决定它自己下一步的动作:一个名为 foo.c 将传递给 C 编译器,而名为 foo.f95 的文件将传递给 Fortran95 的编译器,等等。 理解了这一点,我们就可以知道 gcchelloworld.f 将自动调用 fortran 的编译器。只不过我们要为链接器指定必要的库。 理解了这一点,我们可以知道 gfortranhelloworld.c 可以编译一个 c 程序, gfortranhelloworld.cpp-lstdc++ 编译的是一个 C++ 程序。 一.Fortran编译器的安装 Linux安装盘一般都自带有Fortran编译器,在SuSe9.1以前均带有g77,在Suse9.2以后为gfortran。但是,相对来说,由于g77和gfortran的编译的程序运行效率不是很高,所以都会选择再安装专业的Fortran编译器。 主流的Fortran 90/95编译器有PGI Fortran、HP Fortran Compiler(由Fortran PowerStation进化过来的)和Intel Fortran Compiler等。因为Intel Fortran Compiler9.1是Intel提供的免费的Non-Commercial版本,且在Intel平台上,具有较高的编译效率。它的发行版有 Windows和Linux两种。在此,主要介绍在SLES10.0 上安装 Intel Fortran Compiler 9.1 的过程。 1.下载安装包 本次安装包从ftp上下载,ifcliv91.bin,为光盘文件,可以直接拷贝到目录/home/hou/software/intel_fc_91下。 2.解压安装文件,命令如下: 编译器是安装在/opt/intel目录下,协议则在该目录下的licenses文件夹中。最后还要指定licenses的位置。具体命令如下: #mkdir -p /opt/intel/licenses #cp /home/hou/isoftware/intel_fc_91/Crack/i*.lic /opt/intel/licenses # export INTEL_LICENSE_FILE=opt/intel/licenses
个人分类: 计算机工具|0 个评论
[转载]科学计算的语言------Fortran95(著作)
swx0789 2011-4-6 14:03
第一篇 闲话 中国数学的 经典 著作大都以依据不同方法或不同类型分成 章节的问题集的形式出现。每一个别问题又都分成若干个 条目:条目一是“问”,提出有具体数值的问题;条目二 是“答”,给出这一问题的具体数值答案;条目三称为“术”, 一般说来乃是解答与条目一同种类型问题的普遍方法,实 际上就相当于现在计算机科学中的“ 算法 ”,但有时也相 当于一个公式或一个定理;条目四是“注”,说明“术” 的依据与理由,实质上相当于一种证明;宋元以来,可能 是由于印刷术的发达,往往加上条目五“草”,记述依据 “术”得出答案的详细计算过程。… 早在《九章算术》中,第八章方程章全章就是线性联立方 程组的解法。依刘微注:“并列为行,故谓之方程。”又 说:“令每行为率,二物者再程,三物者三程,皆如物数 程之。”当时以筹作算,依题意将数据按物数排成各行, 然后进行运算。…中学教科书解线性方程组的消元法,最 早即见之于《九章算术》。… 数学在中国古时历来称为算术。这正好反映了中国古算构 造性、计算性与机械化的特色。数学不仅为了 应用 ,即使 为了在纯数学范畴内获得具体结果,也一不能无算,二必 须有术。…中国古代算术的思想与方法,正好与近代计算 机的使用融合无间,也必将因此而重返青春,以另一种崭 新面貌在未来的数学发展中扮演重要角色。 ----吴文俊 《对中国传统数学的再认识》   -------------------------------------------------------------------------------- 吴文俊,中科院院士,我国最卓越的 现代 数学家之一,在拓扑学等领域取得世界性成就,近年积极推动数学机械化领域的发展。 第1章目的是计算 我们这本书描述了一门语言。不幸的是语言很难成为我们的爱好,更何况FORTRAN语言也无法仿效英语,能够被某些英语大师成功地转换为一种疯狂。因此几乎每一个学习过编程语言的人都会有一种痛苦地记忆,那就是一种极端缺乏趣味的痛苦,因为很容易我们就会忘记这种语言的用处。显然,如果一种工具连用处都不是那么明显的话,哪里还谈得上给人带来乐趣呢? 所以要想在FORTRAN语言的学习过程当中不至于总是遭遇无聊,唯一的解救之途就是一开始就要找到学习它的绝对充足的理由,并且在学习过程中还会不得不反复地依靠这个理由,来说服自己需要更加有耐心,更加能够忍受烦琐和约定,…逐渐地,也许我们能够从FORTRAN语言的惊人表达能力当中,体会到一种新的看待世界的角度,这大概会是我们享受那么多的枯燥之后,所能够得到的最佳回报。 所以我们不得不在进入正题之前,扯点闲话,看看学习FORTRAN的理由何在。 1.1始于计算,终于计算 在荒莽的历史尽头,我们常常听说人类之所以能够凭借虚弱的身躯,一次又一次地打退其他凶猛动物的猖狂进攻,而终于成就为今日当之无愧的“万兽之王”,这个原因有很多种解释,比如说是劳动,说是手,说是大脑,莫衷一是,各有各的 理论 和证据,确实是个很难取得定论的问题,因此我想提出一种新的解释,大概是不会遭到立刻的覆灭性打击。 人类的进化始于计算,我相信。 在有关非洲黑猩猩的纪录片里面,可以看到黑猩猩也会用手使用工具,一个黑猩猩家庭为了生活也勤于劳动,一个黑猩猩群落社会里面的形形色色的政治行为,显示它们的大脑也不是很闲,但有一次我看到黑猩猩们抢香蕉时,总是把自己已经抢到手的香蕉给弄丢了而无知觉,突然觉得它们最缺乏的也许是算数的能力。 所以我想,人类的起源一定跟计算有莫大的关系。 当然这些都是戏说而已,不过如果说人类的科学是从计算开始的,应该是不会有太大的争议的。 也许有人说人类的科学活动应该是起源于观察,但从动物行为学的角度来看,观察这种行为是很难界定的,而计算则是非常好界定的行为,因为最原始的计算行为显然应该就是数数。 可以想像,当人类开始意识到自我在这个世界的孤独存在时,对于日月星辰和风雨雷电的无常和力量该是多么地恐惧啊!而人类把自己从这种恐惧当中解救出来的第一个方法应该就是算数。 算数导致了人类最早的科学活动,即天文学和几何学。人们从对日月星辰的计数当中发现了天体的运行是有常的,而不是什么不可琢磨的妖魔鬼怪在主宰;从对土地与山河的度量中建立了明确的几何概念,从而对自己所生活的世界开始了真正的理解。而这种理解活动一旦开始,就无法停止了,演变至今,就构成了我们人类最值得自豪的成就-科学。 科学的历史表明了它的 基础 ,就是计算。 如果我们只是一味地观察,我们会纪录下来大量的经验,然而却无法理解这些经验的含义,更进一步,对于经验有了总结归纳之后,哪怕是进行计算的确切程度不同,也会导致不同的对于自然的理解。 一个很著名的例子,就是太阳系行星的运动的问题。 对于古人来说,所谓行星的意思就是那些星星是相对于恒星在明显地运动,在漫天的相互位置不变的恒星的对比之下,很少的这种匆匆行者是非常引人注目的。于是很早开始人们就纪录这些行星的运行轨道。在古希腊时代,最有名的关于这些行星运动的 研究 是托勒密完成的,他对有关行星的纪录数据进行了计算,得到了一个极其“圆满”的行星运动模型,即以地球为宇宙中心,行星围绕地球做严格圆周运动。现在看来,他的观测数据肯定不会是很精确,而他的计算也不会很准确,一直到开普勒,在获得了非常精确的行星轨道运行的数据的基础上,进行了大量的计算,最终得到了革命性的开普勒三定律。 接下来牛顿出场了,他更是一个计算天才,正是通过计算,从开普勒三定律获得了他的最伟大的发现,万有引力定律。 然后还是通过计算,我们现在可以运用牛顿万有引力公式的计算而把脚印留到月球的寂寞沙尘上。 还有一个非常独特的例子,就是中国的古代科学和技术,更是强烈地依赖计算。 相比于古希腊,中国的古代数学几乎完全是以计算为中心的,翻开中国的古代数学书,一个鲜明的特点就是几乎完全是针对问题的数学计算方面的内容,而很少有古希腊传统的公理,定理与证明。 实际上,古代中国在技术上一直领先于世界,很难说跟这种重视计算的风格没有关系。其中最有名的例子就是祖冲之关于圆周率的计算,而我们现在都很清楚,祖冲之的计算不仅是为圆周率提供了一个远比西方要精确的具体数值,更重要的是提供了一种非常通用的计算方法,即一种近似积分计算,而这种计算方法对于古代中国的技术进步的重要作用,正是一个表明计算的重要性的典范。 如果说计算在科学的早期发展中扮演了关键的作用,那么我们还是可以说计算仍然在现代科学技术扮演着关键的角色。 固然现代的科学技术领域广阔而渊深,然而哪里没有计算的身影呢?甚至在某些科学哲学家的看法里面,能否进行计算是衡量一个理论是否已经成为科学的一个部门的标准。也许这种说法不免偏激,但是计算对于科学与技术的意义,确实是一个深刻的问题。 我们不妨考察一下 物理学 里面计算所占据的地位,也许有助于更深地理解这个问题。 几乎是一直到十九世纪,物理学还是以实验为主要内容,进入二十世纪之后,一场物理学革命飞快地竖立了理论物理学的丰碑,足以与传统的实验物理学分庭抗礼。而这种局面在二战时期就开始改变,二战后在实验物理与理论物理之外,计算物理鼎足而立,导致这种变局的最关键的因素就是电子计算机的发明。 在计算机没有发明之前,人们不得不采取各种方式来回避过于庞大的计算问题,而大量的关键问题,又非得进行庞大的计算不可,以物理学为例,一旦我们透彻地解决了线性问题之后,接踵而来的就是远比线性现象更为广泛的非线性现象,对于非线性问题,我们已经掌握的解析方法极其贫乏,这就逼迫我们走上直接计算之路。 而除了非线性问题之外,物理学乃至整个自然科学看待世界的方式都是原子式的,即我们总是可以把一个现象理解为它的组成成分的相互作用的结果,于是我们就有了分子,原子等基本的科学观念,这种做法一直都是充满胜利的,然而这种胜利也是伴随着沉重代价的,即当我们重新面对复杂现象时,不得不从它的组成成分开始考虑,这就是统计物理学的基本思想,显然这种做法给我们带来了繁重的计算任务,因为迄今为止,我们人类对于一个包含超过了3个的,具有相互作用的成员的 系统 的精确解析求解,已经是有点一筹莫展了,更何况常见的那些动辄包含了成千上万成员的系统呢?因此在很大一部分情况下,我们唯一的选择,就是直接的计算。 因此我们可以说,计算确实是科学的基石之一。 至于我们所生活的这个几乎完全由科学技术所塑造的世界,哪里没有计算的身影呢?如果说计算帮助我们理解了自然,进一步我们还可以说计算创造了我们的生活。 任何一个科学上的对于自然的新的理解,要转变为为人所用的技术时,基本的途径就是计算。道理很显然,当我们要制造一种自然界从未出现过的物品时,我们需要的是具体的制造数据,而不能只是一堆公式,那么数据从哪里来呢?当然只能从对公式的计算得来。因此我们可以把计算理解为理论的实现途径。即理论无论是要反映真实世界,还是要体现到我们的实际物品上,它都需要通过一条叫计算的路,理论武装了我们的大脑,而计算则创造了我们的世界。 所以我们可以说,人类的历史,最早是始于计算,而这个历史的实现的最前沿,则仍然是终于计算。 1.2描述计算的语言 长久以来,人类的计算都是依靠自己来完成的,但如果仔细考虑一下,就会发现计算的实质是把一个经过清楚的分析,明确了求解的方法的问题, 分解 为明确,可行,而有限的步骤,显然这种工作本质上是机械性的,如果把已经得到求解方法的问题还交给人脑来计算,显然有浪费人力之嫌;从另外一个方面来讲,往往一个需要专门加以计算的问题,也是一个需要进行庞大的计算的问题,庞大到人力已经难以胜任的程度。 因此这两个方面都要求人类发明能够进行计算的机器,这就直接导致了计算机的发明。 今天当我们面临需要计算的问题时,对付问题的就不再只是一个大脑,而是一个大脑加上一台机器。所以接下来的问题就是如何让大脑与计算机协同工作,使得大脑能够专门用来寻求问题的算法,而计算机能够被用来计算解。 这种协同工作的第一个要求就是人需要能够把自己对于问题的计算过程的理解表示为机器的机械运动。首先人对于问题及其求解的理解是可以非常清晰地依靠数学语言来加以描述说明的。然后我们再看机器那一端。 所谓计算机就是能够自动执行一系列机器动作的机器,对于那些机器动作,我们可以把它们和基本的计算操作对应起来,这就使得有可能把计算问题的能行的计算步骤分解为有限的机器操作,这里剩下的问题就是一方面是我们熟知的描述问题求解的数学语言,另一方面就是描述机器操作的语言,这两个方面的语言如何对应的问题。 下面我们 讨论 一个非常简单的例子,用来说明这里面的语言翻译问题。 一个一元二次方程的求解是一个很简单的数学问题。设方程的形式为: ax^2+bx+c=0 那么方程的解的一般公式为: x1=(-b+(b^2-4ac)^(1/2))/2a x2=(-b-(b^2-4ac)^(1/2))/2a 注意我们现在的目的是计算,而不是求解析解,因此得到这个公式不是问题的结束,而是问题的开始,即运用这个公式,在a,b,c都有具体取值的情况下,求出具体的数值解。因为如果该方程描述的是一个抛体运动,那么我们需要知道的当然是解的数值,以便我们了解和控制该抛体运动。 所以我们还需要进一步把这个数学表述转述为一个具体的求值过程的描述如下: (1) 获得a,b,c的具体取值; (2) 根据a,b,c的具体取值计算 的值; (3) 如果 b^2-4ac大于0,那么分别计算上面的两个公式的数值,得到两个不同的实数根; (4) 如果b^2-4ac =0,那么计算公式 ,得到的x的值就是方程的两个相同的根; (5) 如果b^2-4ac 小于0,那么分别计算上面的两个公式的数值,得到两个不同的复数根。 这样得到的上面的5个计算步骤描述,才是希望计算机所执行的计算过程的一个大概描述。当然这个描述是基于我们已经获得的对于一元二次方程的求解问题的数学上的彻底了解。 那么描述计算机的操作的语言是什么样的形式呢? 我们知道所谓计算机的工作在本质上是运行一些基本的机械电子操作,固然我们可以把那些操作根据运算法则而定义为相应的运算,例如一个开关电路的 信号 叠加,可以适当地定义为二进制数值的加法,但显然不可能期望依靠这种物理现象能够直接地表达更加复杂的计算,例如上面公式里面的开平方。幸好我们依靠数学,可以做到把任何复杂的计算分解或近似分解为极少数简单计算的叠加与组合。因此最终我们已经至少在理论上可以期望使用计算机来完成那些具有明确求解过程的计算任务。 然而问题是当我们需要向计算机提交一个数值计算任务时,如果总是要求我们首先把计算任务的描述按照计算机所能够理解和执行的程度来进行的话,无疑会使得向计算机提交计算任务成为一件极其麻烦的事情,本来我们利用计算机的目的是为了减轻自己的机械劳动工作量,如果向计算机提交问题是如此麻烦的话,显然不符合我们发明计算机的初衷。 因此进入二十世纪五十年代后,提出了计算机高级语言的概念,所谓高级语言,就是非常接近我们平常对于数学问题的描述的语言,而所谓给出计算机高级语言,实质上并不是说计算机能够直接理解高级语言了,而是把高级语言到计算机内部的机器语言的翻译工作本身交给计算机来完成,因为这种翻译工作在规范了高级语言之后,是计算机自身就完全可以做到的,当然这个翻译任务本身又是一个需要我们首先明确给出一般解答的计算问题。 世界上第一个诞生的计算机高级语言就是我们在这本书里要给出的FORTRAN。 FORTRAN最早是IBM公司在1957给出的,专门用于向IBM自己生产的704计算机提交表述计算问题的。一个计算问题的求解过程如果使用了计算机语言来表述,就称为一个 程序 ,FORTRAN正是这样一种专门用来写程序的语言。 由于IBM的709计算机的成功,同时也使得FORTRAN获得了广泛的传播,这时FORTRAN的语言本性开始在另外一个方面显示了出来,即语言的生命就在于它的传播。于是大多数其它计算机制造商也纷纷使得他们的产品能够输入和理解FORTRAN语言。 FORTRAN语言从它的名称来源就可以知道是一种与某些特定机器进行通讯的语言工具。即FORTRAN语句可以通过特定的机器上配置的编译程序而翻译成机器语言,因此早期的FORTRAN语言具有强烈的与机器系统相关的一些特征,它们反映了那些特定机器的某些特点,显然如果考虑到高级语言的本质是要用来描述计算过程,即所谓算法语言,那么这些与机器相关的特征是没有意义的,而FORTRAN不断更新版本所带来的进步,也就包含了不断舍弃这种特征的目的在内。 FORTRAN的这种历史痕迹的一个例子就是把语言用那些特定机器所能接受的字符序列(亦就是IBM公司用来向计算机输入程序的卡片穿孔设备的48个字符)来定义.而它的那种卡片输入方式决定了很多的程序书写格式。例如通常把语言中的一个语句写在一个记录上,也就对应着穿孔的一张卡片;各个语句按顺序读入,对应着卡片的顺序读入;卡片的格式是固定的,构成后来所谓的固定源码形式。 这就是最初提出计算机高级语言时我们所拥有的第一种高级语言的状况。 不久降生的另外一种高级语言是ALGOL,这个名称就是算法语言的简称,因此可以预料到这种语言具有与FORTRAN非常不同的面貌,因为这种语言在 设计 初始,就不是计算机制造公司为某种特定机器设计的,而是纯粹面向描述计算过程的,也就是所谓面向算法描述的。 ALGOL最早是在1958年由德意志联邦共和国应用数学与力学协会提出的。ALGOL的设计目标显然比FORTRAN要来得高远,它希望不仅能够用于人对机器转述计算过程,也希望能够直接用于人与人之间的对于算法的描述。 为了实现这个通用的目的,ALGOL的字符不是针对任一具体机器定义的,因此它不反映任何一台特定机器的特性。实际上ALGOL所使用的字符与词汇完全是独立定义的。除此之外ALGOL还具有许多更加独特的性质。 不过语言终久摆脱不了它的市场属性,由于FORTRAN更加具有市场侵占能力,最终FORTRAN至今还是主流的科学计算编程语言,而ALGOL语言则不幸成为了任人凭吊的古董。 1.3为什么选择FORTRAN 到底选择什么样的语言,本身是一类非常具有争议性的问题。曾几何时,在科学计算领域,就沸沸腾腾地讨论过最好使用什么样的语言。也许我们可以说这是一个见仁见智的问题,因为我们作为语言的使用者,总是拣自己已经很熟悉的语言,当然总是自己能够很好驾驭的语言是最好的。但是具体地针对科学计算来说,由于科学计算问题具有自身的独特的价值标准,在这个价值标准之下,各种不同的语言还是可以进行客观比较的。 首先我们得把自己面临的任务界定清楚,也就是什么是科学计算问题? 所谓科学计算问题大体上包括如下三个涵义: ● 问题本身以及问题的解答都能够使用数学语言予以精确描述; ● 如果要使用通常的数学方法来给出我们所需要的数值答案,会很麻烦或者根本无法给出; ● 问题以一定的科学与技术知识作为背景。 我们会看到正是科学计算问题的这种内涵决定了它在选择计算语言时所具有的价值标准。 首先,一个科学计算问题总是要以一个数学计算问题的形式出现,因此描述科学计算问题的语言应该能够自然地描述数学问题,即要求编程语言和数学语言在表达方式上具有比较直接自然的对应关系。 然后一个科学计算问题之所以需要使用计算机,那肯定是因为这个问题具有一定的计算量,那么程序的运行效率往往是选择语言时最重要的考量因素。 正是在这两点上,FORTRAN是现在众多语言当中的绝对胜出者。 在描述数学语言的自然性方面, FORTRAN可以说比现在还“活”着的任何语言都强。当然在历史上曾经出现过象ALGOL那样的相当数学化的语言,可惜的是它缺乏市场生存能力,所以就只剩下FORTRAN独美于今了。FORTRAN擅长描述数学计算,这点应该是几乎没有什么争议的。也正是由于这个缘故,FORTRAN的易学是公认的。任何一个科技专业人员,只要对于一个具体问题的数学求解过程有明晰的概念,要把这个求解过程翻译为FORTRAN语言是非常轻松的。 至于执行速度方面,则常常有些似是而非的说法误导 初学者 。最典型的一个错误观念就是“C 代码 的执行速度最快”。这个说法来源于C语言的特殊性,因为C语言更多的是一种系统编程语言,对硬件的控制能力很强,在高级语言里面无出其右者,于是给人以C程序的速度必定最快的印象。但是忘记了这个速度快是来自C语言的系统编程特性,而在做科学计算时,并不需要过多地涉及到系统内核,因此C语言的长处在科学计算方面可以说并不能适当地发挥,相反,在数值计算方面,C绝对不是FORTRAN的对手,因为相对于C以系统编程为目的,FORTRAN是以科学计算为目的的,语言本身在设计之初,就考虑到了针对科学计算而进行优化,因此FORTRAN生成的可执行代码是高度优化的。 实际的运行效率方面的比较也表明了FORTRAN在科学计算方面的优越性。无论是国内还是国外,也无论是经典的串行机还是并行矢量机,大量的经验表明,在执行同一个科学计算任务时,C或C++代码的效率都低于FORTRAN代码,。 除了常见的对于C有着高效的迷信之外,还常常有着对于FORTRAN是如何如何落后的偏见。当然这种偏见是有来源的,那就是曾经功勋卓著的FORTRAN 77在很长一段 时间 里面,都缺乏进取心,使得迄今很多人提起FORTRAN,想到的就是在当今时代已经显得非常落后的FORTRAN 77。实质上,FORTRAN标准在进入FORTRAN 90时代之后,特别是现时的FORTRAN 95版本,可以说只要是对于科学计算有用的特性,C和C++有的,现在FORTRAN 95绝对不缺,而反过来FORTRAN 95所具有的很多针对科学计算的特性,却是C和C++所不具有的。哪怕是C++最引以为傲的面向对象性质,FORTRAN 2000也将全面引入。所以说,FORTRAN已经完全赶上了编程语言的潮流。 与程序运行的效能有关的另外一个重要方面,是程序语言能否支持程序的并行运行,在这点上,可以说FORTRAN表现了它的最大优势,因为FORTRAN 95正是着力于获得并行计算的能力的一个版本。 由于现代科学计算的规模越来越大,计算并行化是一条不得不走的路线,现代计算机硬件的发展,也使得并行化具有实际的普及前景,因为不仅专门的大型计算机是并行的,现在的一般PC都可以拥有多个处理器,因此现代的从事科学计算的用户不得不掌握并行化计算的编程能力。 但是进行并行化编程所遇到的一个主要问题,就是任何过程编程语言都内在地使用线性存储模式,也就是一个数组的元素总是被认为按照数组元素的先后顺序而连续地存储在内存单位里面,这样一种模式就决定了这样的过程编程语言无法真正地实现对并行计算的描述。而FORTRAN 95则完全改观了这种制约,因为在FORTRAN 95里面对于数组以及数组运算建立了全新的面向并行化计算的概念,诸如纯过程的概念,逐元过程的概念,FORALL结构等等,都有效地摆脱了线性存储模式的制约,使得FORTRAN 95成为描述并行计算的标准语言,特别是那些专用的数据并行化语言都纷纷采用FORTRAN作为基础语言,例如高性能FORTRAN(High Performance Fortran),Fortran D,Vienna Fortran,以及CRAFT等。这样就使得使用FORTRAN 95编写的程序可以直接在这些数据并行化语言的平台上运行,而反过来使用这些专用语言编写的程序也可以毫不困难地转移到FORTRAN 95平台上运行,这样一种局面使得FORTRAN在并行计算领域独领风骚。 综上所述,我们完全可以说FORTRAN 95是进行科学计算的最佳语言,作为需要进行科学计算的科学与技术领域的工作人员,掌握FORTRAN 95远比掌握C,C++等语言要重要得多,至于那些计算机符号代数与数值计算软件,例如MATHEMATICA,MAPLE, MATLAB ,Macsyma,MATHCAD等等,只能说是进行科学计算的教学模型与辅助工具,由于它们都提供了现成的算法,因此可以使得初学者能够应用于一些简单的场合,真正要用它们来对付稍微大一点的问题,有经验的用户都知道,那会是一件非常痛苦的强人所难的事情。因此最终要自由地进行科学计算,则非FORTRAN莫属。 1.4FORTRAN的进步 下面我们介绍一下FORTRAN在历史上所取得的重要进步,特别是FORTRAN 95有别于FORTRAN 90的特征,更能够引导我们走向高性能的FORTRAN并行编程的领域。 1.4.1FORTRAN 95的进步 FORTRAN的每一个进步,都意味着要把很多的语言特征归结为三类: ● 新的语言特征; 顾名思义,就是FORTRAN最新版本引入的全新语言。 ● 过时的语言特征; 就是暂时在最新的FORTRAN标准里面仍然可以使用,而不会出现不兼容的情形,但是被指明为过时的语言特征,是在未来的下一个FORTRAN版本里面将要被淘汰的部分。 ● 废弃的语言特征。 顾名思义,就是在过去的FORTRAN版本里面曾经出现过,但在现在的版本里面已经不许使用的语言特征。 下面分别介绍它们。 1.4.2新的语言特征 FORTRAN 95所增加的新的语言成分包括全新的功能和对旧有功能的改进两个部分。它们主要包括: ● FORALL语句与结构。 ● 纯(PURE)过程。 ● 逐元(ELEMENTAL)过程。 ● WHERE结构的扩展。 ● 默认初始化。 ● NULL固有 函数 。 ● CPU_TIME固有子例行程序。 ● 固有函数CEILING,FLOOR,MAXLOC,MINLOC的扩展。 ● 可分配数组的动态去分配。 ● 名称列表输入里面的注释。 ● 最小域宽格式说明。 ● 用于支持 IEEE 754/854浮点运算标准的某些修改。 下面分别予以说明。 1. FORALL语句与结构 FORALL语句与结构和纯过程这两个新的语言成分,主要是为了提高在多处理器系统上面的程序并行运行效率而引进的。 只要系统支持并行的赋值,FORALL语句以及结构,就能够自然地实现对一个庞大的数组的所有元素进行同时赋值,从而充分地利用了系统的并行效能。这个新特征的引入充分显示了FORTRAN在并行运算方面的努力。 2. 纯(PURE)过程 PURE是一个完全新引入的过程属性。具有PURE属性的函数或子例行程序,除了返回值之外,将不具有任何的后效。也就是说,对于它的任意变元或全局变量,它都不改变它们的值,指针关联状态,以及数据映射,也不执行输入输出。 在FORTRAN 95标准里面,一些场合要求其中的过程必须具有PURE属性。特别地PURE属性对于应用FORALL语句或结构进行并行赋值是必要条件。 3. 逐元(ELEMENTAL)过程 逐元过程同样是执行并行运算的强大工具。把一个逐元过程应用到一个数组,就把这个针对数组的运算转化为对数组的所有元素的单个的运算,所有单个的结果再组合起来,返回一个同样形状的数组。 4. WHERE结构的扩展 WHERE结构经过扩展,可以包含一个FORALL结构的嵌套,而FORALL结构又可以包含WHERE语句或结构的嵌套。两者的配合使用具有强大的功能。 5. 默认初始化 默认初始化可以应用于派生类型,包括哑元,这样就能保证具有指针成员的派生类型对象能够一直可以访问。从而避免了出现内存可分配,但是不能去分配的情形。 对于指针,可以使用新的固有函数NULL来给出初始的关联状态,也可以在使用数据之前,使用NULLIFY语句来获得初始化。 6. NULL固有函数 新引入的固有函数NULL的功能主要是给指针赋予一个初始的去关联的关联状态。 NULL函数可以在一个类型声明语句当中使用,给定一个指针的初始去关联状态,也可以在一个结构构造器里面使用,用来给定其中指针成员的初始去关联状态。 7. CPU_TIME固有子例行程序 固有函数CPU_TIME用来测量一个程序或一段代码所消耗的处理器时间。 这个函数的测量结果不一定是非常准确的,但在一些情形下是非常有用的。例如用来比较不同代码的运行时间,从而比较它们的效率;也可以用来 检测 一个并行程序是不是真正地把一个变元作为一个数组来并行处理,从而评价它的并行效率。 8. 固有函数CEILING,FLOOR,MAXLOC,MINLOC的扩展 在FORTRAN 90和高性能FORTRAN之间,某些固有函数以及相关函数存在某些不兼容,因为在高性能FORTRAN里面,在不同的变元位置增加了一个DIM函数。 在FORTRAN 95里面,就放松了对于变元顺序的要求,这样在变元序列当中MASK和DIM的出现就可以是任意的了,从而保证了FORTRAN 95与高性能FORTRAN的兼容性。 9. 可分配数组的动态去分配 在FORTRAN 95里面,当退出一个给定的作用域时,其中没有通过使用SAVE而得到保留的可分配数组,就自动地去分配,和使用DEALLOCATE语句的效果一样。这样就可以防止可能发生的内存遗漏,从而规范分配过程。 10. 名称列表输入里面的注释 在名称列表输入纪录当中可以使用注释,从而方便了用户。 11. 最小域宽格式说明 在使用数值的格式输出的时候,运用增强的输出格式编辑描述符,就可以对域宽进行极小化,从而避免输出时的白边。 12. 用于支持IEEE 754/854浮点运算标准的某些修改 在所有以前的FORTRAN版本里面,都不区分+0.和-0.,即正0和负0,在FORTRAN 95版本里面,就能够区分这两者了,即在使用SIGN函数时,可以通过让第一个变元为0,而第二个变元为负号,就得到负0。 1.4.3过时的语言特征 FORTRAN 95里面被划归为过时的语言特征,在FORTRAN 90里面还是允许正常使用的,但是在FORTRAN 95里面已经强烈地不提倡使用,因为它们在下一个FORTRAN版本里面会被彻底淘汰。这些过时的语言特征主要包括: ● 算术IF语句。 ● DO终止的某些形式。 ● 替代返回。 ● 计算GO TO语句。 ● 语句函数。 ● 可执行语句之间的DATA语句。 ● 哑长度字符函数。 ● 固定源码形式。 ● 字符型声明里面的CHARACTER*形式。 下面分别说明,并特别要注意它们为什么是多余的或容易产生错误的。 1. 算术IF语句 算术IF语句的功能完全可以通过使用IF语句或IF结构来替代。 2. DO终止的某些形式 共享 DO终止,或者不是使用END DO语句或CONTINUE语句的DO终止都是过时的,无论是从安全的角度,还是从清晰的角度,都最好使用具有不带标签的END DO语句的DO结构块形式。 3. 替代返回 替代返回强烈地依赖标签,是不值得提倡的,而且同样的功能完全可以通过CASE结构来实现。 4. 计算GO TO语句 计算GO TO语句完全可以使用CASE结构来代替,而且CASE结构还具有比计算GO TO语句更加广泛的功能。从结构化编程的角度来看,也应该使用CASE结构,而不是完全非结构化的,带有早期与系统相关特征的计算GO TO语句。 5. 语句函数 内部函数就具有语句函数的许多特征。而语句函数非常容易与赋值语句相混淆,所以语句函数也是不提倡使用的。 6. 可执行语句之间的DATA语句 如果在程序的可执行部分使用DATA语句的话,会容易让人产生一种错觉,即DATA语句能够在程序的执行过程当中进行赋值,而实际上,DATA语句只能用于数据的初始化,因此DATA语句最好还是只用于程序的说明部分。 7. 哑长度字符函数 哑长度字符函数要求在调用程序当中声明函数的名称,它完全可以通过运用具有显式界面或动态字符长度的子例行程序或函数来替代。 8. 固定源码形式 固定源码形式完全是由早期的卡片纸带式输入输出方式所决定,而现在卡片纸带式输入输出早已经不再使用,因此这种源码形式必定是要被淘汰的。 9. 字符型声明里面的CHARACTER*形式 在字符型声明语句当中用来说明字符长度的CHARACTER*完全是多余的,一般提倡采用更为合乎英语习惯的形式。 1.4.4废弃的语言特征 在FORTRAN 90里面还可以使用的语言特征有些已经被FORTRAN 95完全废弃了,因此要特别留意它们,以免出现 源代码 无法在FORTRAN 95系统里面调试通过的情况。 这些被FORTRAN 95废弃的语言特征主要包括: ● 实型和双精度实型的DO变量。 ● 从块的外部分支到块内部的END IF语句。 ● PAUSE语句。 ● ASSIGN语句,带标签的GO TO语句。 ● nH编辑描述符。 为了能够阅读使用FORTRAN 90以及更早版本的源代码,下面还是分别予以简要的说明。请注意它们遭到淘汰的原因所在。 1. 实型和双精度实型的DO变量 实型和双精度实型的DO变量或循环控制的DO参数都是难以移植的,而且也是很少用到的。因此现代的FORTRAN版本都要求DO变量为标量整型变量。 2. 从块的外部分支到块内部的END IF语句 从块的外部分支到块内部的END IF语句对于语句的执行序列的控制,是完全不必要的,也是不规范的,因此已经被完全淘汰了。 3. PAUSE语句 PAUSE语句用于把一个正在运行的程序挂起来,直到系统或操作重新开始运行。它的功能完全是多余的,因为WRITE语句可以发送消息到任何设备,例如操作控制台或终端,而READ语句则可以等待或接收来自同一个设备的消息。它们的配合使用就可以替代特别的PAUSE语句。 4. ASSIGN语句,带标签的GO TO语句 ASSIGN语句用来给一个整型变量赋予一个语句的标签。 它的一般用途就是,在程序的运行过程当中,通过ASSIGN语句就可以给该整型变量赋予分支目标语句的标签,从而给程序提供一种动态分支的能力。 然而整型变量除了可以具有标签值之外,还有可能具有一般的整数值,这就会容易导致程序错误,也使得程序难以阅读。 实际上ASSIGN语句,以及带标签的GO TO语句的功能,都可以由内部过程实现,因为ASSIGN语句无非就是纪录了一个可重用代码块完成运行后的返回点。 ASSIGN语句还可以把一个FORMAT语句的标签动态地赋予一个整型变量,然后该变量就可以用作WRITE,READ,PRINT语句的格式说明符。不过这个功能也可以通过把字符型变量,数组,以及常量用作格式说明符而得到实现。 总之,标签的使用总是不合时宜的。 5. nH编辑描述符 使用这个编辑描述符非常容易导致错误,因为跟在该描述符后面的字符数目很容易计算错误,而如果使用字符常量编辑描述符来行使相同的功能的话,则根本不需要计算字符数目。 2 # 发表于 2005-8-30 10:09 | 只看该作者 Re:科学计算的语言------Fortran95 第2篇. 计算的叙述 算法的每一个步骤,都必须给予确切的定义。对于算法当中所 考虑的每一种情况,每一个有待执行的动作,都必须严格地和 不含混地加以规定。…对于以描述算法作为目的而设计出来 的,采用了形式的定义的程序设计语言,或者说计算机语言, 它的每一个语句都必须有非常确切的意义。 ---- D.E.Knuth 《The Art of Computer Programming》 本质上FORTRAN就是一门语言,一门人与计算机赖以进行有效交流的语言,在这个意义上和我们使用的中文,英文等没有本质差别。现在假设要来描述一种大家都陌生的语言,那么总是要分成两个方面来描述,即一方面要描述这门语言的表象和形态,也就是它使用哪些符号,哪些词汇,一般的句式如何,怎样才能完整叙述一个任务之类;另一方面需要说明这门语言的语义,也就是说这门语言是如何用来表达我们需要它表达的意思的。 第4章基本上就是描述FORTRAN作为一种语言的基本形态,也就是书写这种语言的书写规则。 接下来几章则逐步说明如何用FORTRAN来表达我们的要求,或者反过来说,FORTRAN提供了些什么表达方式,以便我们用来向计算机提出合理的任务: ● 表达基本数据; ● 表达数据的结构; ● 完整地描述数据; ● 构造表达式; ● 驱动计算的赋值; ● 计算过程的结构控制; 在整个第二篇,我们将领略到FORTRAN 95是如何能够做到精致地描述计算的,而把一个问题阐述清楚了,就意味着问题已经解决了一大半。   -------------------------------------------------------------------------------- Donald E. Knuth (高纳德), Stanford University的The Art of Computer Programming荣休教授,而The Art of Computer Programming(计算机程序设计技巧)正是他的伟大著作的名称。洋洋七大卷的《The Art of Computer Programming》是当今全世界每一个计算机科学家所膜拜的圣经。1974年在该书刚完成前面很少一部分时,就给他带来了计算机科学家们梦寐以求的图灵奖。 第4章FORTRAN 95语言的形貌 要说明一门语言的形态,必须回答以下问题: ● 它使用哪些符号来表达信息? ● 它的词汇如何构成? ● 它的语句如何构成? ● 如何表达一个完整的任务? 具体的对于一门计算机语言,把这几个问题更加明确地转换过来,就是: ● 它使用键盘上的哪些符号,各个符号有哪些用途? ● 它的词汇如何由键盘字符构成?含有哪些固定的词汇?以及容许自由构成合法词汇的规则是什么? ● 它具有哪些固定的语句格式?以及容许自由构成合法语句的规则是什么? ● 我们交待给计算机的任何任务,都必须明确说明任务的开始,执行步骤和完成,因此一段完整的源代码应该具备什么样的形式?以及应该具备哪些要素? 本章就是要回答这些问题。 4.1FORTRAN语言所使用的字符 从最抽象的层面来看,人与计算机的交流只是信息的交流,而信息总是需要依靠某种信号来表示,对于人来说,最方便的就是字符。而对于计算机来说,自然就是键盘所能敲出的那些字符(信号),因此下面就是要说明: ● FORTRAN 95能识别键盘上敲出的哪些字符? ● 每个字符对于FORTRAN 95来说又意味着什么? 4.1.1FORTRAN 95所使用的基本字符 按照FORTRAN 95标准的规定,一切FORTRAN 95的实现平台都必须使用下面表4-1所列出来的这个基本的字符集,或者说,这个字符集是所有遵循FORTRAN 95标准的编译器所使用的字符集的公共子集。这样原则上,局限在这个字符集上的源码是能够被任何遵循FORTRAN 95标准的编译器所识别的。 表4-1基本的FORTRAN 95字符集: 文字字符 英文字母 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 数字 0 1 2 3 4 5 6 7 8 9 下划线 _ 特殊字符 图形名称 图形名称 空格 : 冒号 =等号 !叹号 十加号 ” 引号 一减号 % 百分号 * 星号 & 英语的and /斜线 ; 分号 ( 左括号 < 小于 ) 右活号 > 大于 ,逗号 ?问号 .小数点或句号 $ 货币符号 ’撇号 可以看到基本字符分为两大类:文字字符和特殊字符。除了货币符号可以本地化之外,其他任何字符都必须依照表里的形式。 对于基本字符有如下几个问题需要予以注意。 一.文字字符的用处: ● 主要是命名的作用,可以用来命名语言中的一切对象,这三种符号可以混合使用; ● 其中数字还具有它本来的含义,就是表示数目。 二.特殊字符的用处: 特殊字符主要具有功能的意义,如编辑功能,运算功能,语法功能等。 FORTRAN 95标准原则上接受小写字母。因此除了以下位置,大小写是等价的。 三.大小写必须区分的位置: ● 作为字符常量的字符串里面; ● 输入输出的纪录里面; ● 作为编辑描述符的引号或撇号里面。 因为在上述几种情形,大小写是字符型数据的不同数据取值。 如果不幸遇到一个FORTRAN 95标准的怪异的编译平台,偏偏不接受小写字母,这是FORTRAN 95标准所许可的,这时就得小心了。不过幸好我们常用的编译平台,例如CVF,都是接受小写字母的。 另外,在OPEN或者INQUIRE语句里面的FILE=或NAME=后面是否区分大小写,也是由编译平台指定的。 如果是需要调用其他语言写的子程序, 而恰好该种语言(例如C语言)是区分大小写的,这时就需要特别小心。 【例4-1】 如果用C写了两个子程序EIGEN和eigen,然后有如下的FROTRAN片断: EXTERNAL EIGEN ... CALL EIGEN ... END 这时它是该引用EIGEN还是eigen呢?如果所使用的FROTRAN系统正好是怪异的那种,没问题。如果是常见的如CVF,这时它就无法区分EIGEN和eigen,这样就必须给它们更换名称了。 四.数字的涵义: 除了以下情形,数字总是表示十进位数字 ● 属于二进制,八进制,十六进制的字面常量; ● 带有B,O,Z编辑描述符的输入输出纪录。 【例4-2】 以下语句当中的数字不是属于十进位数字: DATA I, J, K / O’1001’, 23.54, Z’5CA2’ / 其中第一个为八进制数,第二个为十进制数,第三个为十六进制数。 五.下划线的涵义: ● 下划线的主要作用就是置于单词之间代替空格,使得我们在命名时使用清楚的英语词汇。 ● 下划线不能置于任意名称的前面,但是可以置于名称的最后。 ● 下划线也用于在字面常量中区隔常量的值和种别参数。 无论给什么对象起名,都尽量使用完整的英语单词,同时使用下划线以区隔不同的单词。所谓好记性不如烂笔头,只有这样才能切实保证你在任何时候,在程序代码的任意位置都知道任意变量等的含义。 4.1.2与平台有关的FORTRAN辅助字符集 上节列出的基本字符集是在一切FROTRAN的编译平台都可以使用的,被FORTRAN 95标准规定为必须使用的默认字符集。另外还有些辅助的字符则是不同的平台有不同的用法约定。 辅助字符分两类:可打印字符和不可打印字符。 ● 可打印字符; 各种本地化语言的字符,象汉字,希腊字母等,都可以应用在字符串,注释,和输入输出纪录当中。 ● 不可打印字符。 主要就是控制字符,例如制表符Tab键。 制表符(Tab键)在FORTRAN77标准当中主要用来表示6个空格,这样在固定源程序形式的代码的每行的开头使用Tab,就自动地空出6个空格。 对于一个FORTRAN77标准的编译系统来说,在固定源程序形式里的Tab被看成是至少6个空格,而在自由源程序形式里的Tab被看成1个空格。这样如果Tab被放在文本当中用于输出格式控制,那么这种默认的转换方式,有时就会导致输出格式的混乱。 有关FORTRAN 95的辅助字符集的使用规则,请参考具体的编译系统的说明。 4.2词汇 所谓FORTRAN的词汇就是一个语句的最小的意义单位,它由一个或多个FORTRAN字符集里的字符组成。包括两类共6种,分类例举如下: ● 由文字字符组成的词汇,包括4种: · 语句关键词:IMPLICIT · 名称:EIGEN_FREQUENCY_3 · 由单个词汇组成的字面常量:1.234567_long · 标识符:213 ● 由特殊字符组成的— · 算符: +,.OR. · 定界符:逗号,=,=〉,:,::,;,%。 FORTRAN 95的一切合法的词汇都必须按照语法来构成。完备的构词语法规则在附录B给出。 下面分别予以详细说明。 1. 语句关键词 语句关键词的功用: ● 标志语句本身。 【例4-3】 下面的DO语句中的关键词DO本身标志了该语句 DO I=1,500 ● 标志选项。 【例4-4】 下面的INTENT语句当中的IN,OUT,或INOUT。 INTENT(IN),A。B INTENT(INOUT),X,Y,Z ● 用在语句当中,起分界的作用。 【例4-5】 如下面DO语句当中的WHILE DO WHILE( .NOT. VECTOR ) 并非所有的语句都必须包含关键词,在FORTRAN里面,赋值语句和函数都不需要关键词。 尽管FORTRAN 95不区分大小写,本书任何地方出现的语句关键词都使用大写字母。纯粹是为了醒目的原因。 2.名称 在一个程序当中,任何对象都需要有一个名称,给它们命名所得到的词汇,可以说就是一般语言里的名词,这样的对象包括:变量,命名常量,程序单元,过程,公用块,构造,派生类型,哑元等。 名称的拼写规则为: ● 名称必须由字母开头,可以由文字字符混合组成,而下划线不能作为名称的第一个字符。 ● 一个名称至多允许含有31个字符。 3. 常量 一个常量就是对一个值的合乎语法的字符标记。 常量分为字面常量和命名常量两种: ● 一个值如果没有在程序里面经过命名,则称为字面常量,这种常量不能取派生数据类型。 【例4-6】 66953 Z’5120A’ 2.3417 .TRUE. (33.2, 5.0) ● 一个值如果在程序里面经过命名,则称为命名常量,这种常量能取派生数据类型。 【例4-7】 在如下声明语句当中的常量UNSTABLE_POINT为命名常量: REAL, DIMENSION(3), PARAMETER ::UNSTABLE_POINT = (/5.332, 0.221, 190.632/) 对于常量的语义,将在说明数据时进一步讨论。 4. 语句标签 在一个程序单元内部,对任何一条语句,都可以在该语句的前面加上语句标签,以便在该程序单元内部的任何其他位置引用该语句。需要引用其他语句的语句包括CALL语句,DO结构,分支语句,输入输出语句等。 语句标签的书写规则为: ● 语句标签由1到5个十进制数字组成,其中必须至少有一个数字不能是0,例如000不能作为标识符; ● 标识符以0开头是没有任何意义的,例如0034与34没有区别。 ● 标识符不能放置于空语句之前。 【例4-8】 456 上面的语句只出现了一个语句标签,是不合法的。 ● 对于在一个程序单元内部,标识符不唯一出现的情形,具有特殊的含义,将在后面讨论。 5. 算符 算符用在表达式当中,通过运算而获得某种类型的值。 算符分为固有算符和自定义算符两类: ● 固有算符 在FORTRAN 95语法当中,R310规定了固有算符的构成法则。 【例4-9】 //表示字符串的连接 + 表示对数值的加法 .NOT. 表示逻辑否 .OR.表示逻辑或 上面都是固有算符。 ● 自定义算符 自定义算符的一般语法形式为: .XXX… . 即在两个句点之间有n个字符构成的字符串,n不大于31。 中间的字母串最好是一个表达该运算含义的英文单词。这个单词不能与固有算符或者逻辑常量里面已经使用了的单词重复。 6. 定界符 全部的定界符有如下12种形式: / ( ) (/ /) , = = : :: ; % 其中(和),(/和/)都必须成对出现 顾名思义,这些定界符的功能就是在一个连续的源码文本当中,用来把不同性质的源码成分区分开。它们的具体含义将在具体的语句当中说明。 4.3语句 一条语句由一些词汇组成,可以理解为表示要求计算机进行的一个动作,但一个说明,一个描述之类的,表面看好象不是计算机的一个动作,不过实质上同样要求机器内部的一个动作与之相对应,因此同样也构成FORTRAN的一条语句。 FORTRAN 95的语句分为两大类: ● 非执行语句 当需要引入或说明一个程序单元或子程序,或者是说明数据类型时,就需要使用非执行语句。 ● 可执行语句 当需要计算机进行一个指定动作时,就需要使用可执行语句。 FORTRAN 95全部的语句的具体分类,以及语法和例示参见附录A,语句的语法也参见附录B。在后面的有关章节则分别说明了所涉及到的主要的语句。 4.4源码形式 一个FORTRAN 95程序就是由以下三种形式的程序成分所构成的分行的文本: ● FORTRAN语句 ● 注释 ● INCLUDE行 在一个FORTRAN 95程序里面,一条语句占一行或多行,一行也可以有多条语句,程序文本当中可以包含空行,但不具有任何含义,被FORTRAN编译器忽略。 这种形态的文本就是FORTRAN的源代码(源程序)。 从FORTRAN90开始,对于源程序的书写格式要求已经完全现代化了,也就是出现了所谓自由源程序格式,而此前,FORTRAN的固定源程序格式一直是初学者视为畏途的主要因素,那种传统的固定源程序格式完全是FORTRAN作为始祖级的高级语言的遗留痕迹,因为早期的源程序输入不是通过键盘,而是运用穿孔纸带,正是穿孔纸带的格式规定了相应的源程序的书写格式。现在之所以我们还需要了解这点,是因为FORTRAN的悠久历史,决定了有大量的源代码正是使用了那种古老格式,那是一个今天我们不得不继承的宝库,要想使用它们,显然就得会读那种格式,因此我们只需要了解固定格式,却不需要遵循固定格式来写代码。 【例4-10】 在这个例子里面,表明了行与语句之间可以有多种排列形式: 这里的例子显示了所谓自由源程序格式的自由之所在。这里使用了作为一个语句在行与行之间连续的标志,而!后面的字符永远是注释。 23 FORMAT( 6Y, J9)!这是一条语句占有完整的一行的例子 37 FUNCTION string_concat(s1, !这里一条语句被分到两行s2) 空格是被忽略的。 64 FORMAT( 6Y, J0);37 FUNCTION !这里一行里有两条语句,其中一条 string_concat(s1, s2)!语句还只是它的一部分。 TYPE (string) :: s1, s2, string_concat string_concat%string_data = s1%string_data(1:s1%length) // s2%string_data(1:s2%length);string_concat%length !这行里包含两条 = s1%length + s2%length!部分语句。 END FUNCTION string_concat 源码文本的一般规则如下: ● 在一个程序单元内部,行与行之间的顺序是有意义的,只有两个例外: •注释行的顺序与位置可以非常自由; •在CONTAINS语句和END语句之间的子程序的顺序也可以是任意的。 ● 在一个程序单元内部,或者完全使用自由格式,或者完全使用固定格式。但是一个程序内部的不同程序单元则可以使用不同的格式。后面要说明为了便于协调起见,如何使用一种自由格式与固定格式兼容的特定格式。 ● 所谓字符文本是指如下两种情形下的字符串: • 作为一个字符字面常量的取值的字符或字符串; • 被字符串编辑描述符控制的字符或字符串。 那么描述符本身和续行符永远都不属于其邻近的字符文本。 ● 针对字符文本的规则与针对非字符文本的规则是不一样的。 【例4-11】 下面例子说明了空格在字符文本与非字符文本当中的不同使用规则: 22 CHAR = NAME01 // “KNOWLEDGEARCHIVE” 23 CHAR = NAME02 // “KNOWLEDGEARCHIVE” 在双引号里的字符串之间的空格是有意义的,因此上面的两个字符串是不同的。 DO43I=1,N DO 43 I = 1,N 而上面这两条语句是等价的。 4.4.1自由源程序格式 自由源程序格式的主要思想就是不限制语句在行内的位置。与固定格式相比,主要是空格的用法有差异。 自由源程序格式的一般规则如下: ● 对于FORTRAN的基本字符集而言,一行至多能容纳132个字符,如果出现非基本字符集当中的字符,则具体的平台会有相应的规定,这时,可能能够容纳的字符数目就会少于132。 【例4-12】 假如下面的语句刚好包含132个字符,但是其中含有中文字符: TEXT = CHINESE_SENTENCE’this line has exactly 132 characters and contains人’ 这时,一个具体的实现平台会有相应的规定,一般来说它会认为上面语句的字符太 多了,因此为保险起见,尽量使用续行符。 ● 只要字符!不是作为字符文本当中的一个字符,那么在该行内它后面的所有字符都是属于注释的内容。而FORTRAN对于注释内容没有任何限制,可以是任意形式,因为反正任何编译器对于注释部分都是忽略掉的。一行内可以在语句后面接注释内容,也可以整行就以!开头,这时该行就是完全的注释行。 总之,注释的位置可以是任意的,关键是一行的任意位置只要出现了注释符!,那么它后面直到行末,都会被编译器认为是注释内容而不加理会。因此不要把语句放置在一行内的注释后面。 ● 只要字符不是作为字符文本当中的一个字符,那么在该行内它后面只能接空格以及注释,在紧接着的行内只要存在非注释部分,那就是和该前面的部分是连续的,被称为连续行。 在FORTRAN里,一个语句所跟随的连续行不能超过39行。 一行的非注释部分不能只是一个续行符。 注释不能利用该字符来表示续行,因此如果注释部分的行末为字符,则它只是属 于注释内容的一个字符,不具有续行的意思。 ● 一行如果只包含空格字符,或者根本不包含任何字符(这两者表现一样),那么编译器总是把该行视为注释行,予以忽略。 ● 一行之内可以不止包含一条语句,语句之间必须用(;)加以分隔。 ● 任何辅助字符集当中的字符都可以在字符字面常量和字符串编辑符当中使用。 ● 标签被放置于语句之前,任何情形下都必须避免标签被认为是属于一条语句内部的字符。 按照FORTRAN语法,空语句是合法语句,只要空语句不是出现在一行的开头,因此连续的;;,甚至中间包含空格; ;,都会被认为是单个的;,因为字符;总是意味着它的前面是一条语句,即使为空语句,也不算语法错误。 【例4-13】 下面的语句都是合法的。 X=(3.0,4.6);Y=(44.5,566.0) 这里的;是作为语句分隔符 X=(3.0,4.6); 这里的;被忽略而不认为是错误 X=(3.0,4.6); ;;; ;Y=(44.5,566.0) 这里的; ;;; ;等价于一个;,因为分号之间的空格被认为是空语句,不算语法错误。 X=(3.0,4.6) ;Y=(44.5, 566.0) 这里分号放在一行的开头,因为该行是连续行。 Y=(44.5, 566.0);Z=”ZERO” 【例4-14】 下面的写法是错误的。 53 INTEGER X,Y !这里53是合法的标签 IF (X==0)76 Y=X!这里的标签76不能说明自己不属于IF语句 下面我们更加详细地说明在自由源码形式里面续行符和空格的用法。 1. 续行符 只是采用续行符的不同用法,就有可能产生完全不等价的语句,因为续行符能够导致名称的变化。所以如果一个名称,字符常量,或词汇被迫分行,则必须在前一行的末尾和后一行的开头紧接着字符使用。 【例4-15】 ENERGY = 0.5*MASS * VILOC!这里VILOCITY是一个变量名 ITY**2 ENERGY = 0.5*MASS * VILOC !这里变量名成了VILOC ITY! ITY**2 ENERGY = 0.5*MASS * VILOC !这里变量名成了VILOC ITY! ITY**2 上面的三个语句是完全不等价的! 2. 空格的使用规则 在具有固定名称以及固定格式的算符当中,不能随意使用空格。因为空格默认的功能就是分隔不同的词汇。 【例4-16】 CALL SUBROUTINE A CALL SUBROUTINE A!错误语句! IF X = .NOT . IF X = .NOT. !这两条语句不同 【例4-17】 下面语句当中的空格是不可少的: INTEGER X,Y IF A=0 DO Y=1,20 但不是所有情形下的词汇之间必须要有空格,在不会产生混乱的前提下,有些语句关键词之间的空格是可以省略的,对于语句关键词来说,所有这些不同的情形列举如下表4-1: 表4-1语句关键词中间空格含义的不同情形 非必须的空格 必要的空格 BLOCK DATA CASE DEFAULT DOUBLE COMPLEX DO WHILE DOUBLE COMPLEX DO WHILE DOUBLE PRECISION IMPLICIT type-specifier ELSE IF IMPLICIT NONE END BLOCK DATA INTERFACE ASSIGNMENT END DO INTERFACE OPERATOR END FILE MODULE PROCEDURE END FORALL RECURSIVE FUNCTION END FUNCTION RECURSIVE SUBROUTINE END IF RECURSIVE type-specifier FUNCTION END INTERFACE type-specifier FUNCTION END MODULE type-specifier RECURSIVE FUNCTION END PROGRAM END SELECT END SUBROUTINE END TYPE END WHERE GO TO IN OUT SELECT CASE 4.4.2固定源程序格式 今天已经没有必要按照固定格式书写源程序,只需要能够阅读古老的使用固定格式的代码,如果有必要把固定格式的源程序转换为自由格式,也可以运用某些小软件完成,因此下面只是简要地介绍固定格式的几个规则。 ● 语句只能书写在一行的第7到第72个格子上。 ● 空格除了在字符常量里以外,都是没有意义的。 ● 在某行第一格为字符C,或*,就表示该行整行都是注释。注释总是被忽略。 ● 字符!只要不是出现在第6格,也不是属于字符文本,则从它开始一直到该行的行末,都属于注释。 ● 一行如果只包含空格字符,或者根本不包含任何字符(这两者表现一样),那么编译器总是把该行视为注释行,予以忽略。 ● 一行中的多条语句用一个或多个分号分隔;分号可以出现在行末,但没有更多的意义;分号不能是一行的第7到第72个格子上第一个非空格字符。 ● 除了空格和0之外的任意字符,只要出现在某行第6格上,则表示该行为连续行。一行后面最多只能有19个连续行,第一行称为初始行。 ● 标识符只能出现在第1到第5格上,被连续的语句只有第一行可以使用标识符,这样下面所有的连续行的第1到第5格上只能是空格。 ● END语句不能被连续,它也不能被视为初始行。 4.4.3 兼容源程序格式 在某些情形下,需要书写能够同时被自由格式和固定格式兼容的源码,要做到这点,只需要遵循以下规则即可: ● 标识符只能出现在第1到第5格上,语句只能书写在一行的第7到第72个格子上。 ● 按照自由格式的规则使用空格。 ● 使用!作注释符,但不要放置在第6格,也不要是用使用字符C,或*作注释符。 ● 需要连续行时,在被连续行的第73格写,同时在连续行的第6格也写,第74格到80格保持空格或者只写注释。而连续行的第1到第5格上只能是空格。 下面就是一个同时满足两种源码形式要求的代码例程: 【例4-18】 Column: 12345678...73 _________________________________________________________________________ ! Define the function CUT_SIN DOUBLE PRECISION FUNCTION CUT_SIN(X) CUT_SIN = X - X**3/FACTOR(3) + X**5/FACTOR(5) - X**7/FACTOR(7) CONTAINS INTEGER FUNCTION FACTOR(N) FACTOR = 1 DO 10 I = N, 1, -1 10 FACTOR = FACTOR * I END FUNCTION FACTOR END FUNCTION CUT_SIN 4.4.4程序结构 在FORTRAN 95的语法规则里面规定了程序结构的完整定义,参见附录B。 不过那里的语法规则并没有完备地表述在一个程序单元里,各种语句应该遵循什么顺序。下面给出一般原则: ● 数据类型声明和指定的语句必须放置在可执行结构或语句之前; ● FORMAT,DATA,ENTRY语句也可以放置在可执行语句中间,不过把DATA语句放置在可执行语句中间是一种过时的做法; ● 如果出现USE语句,必须总是放在最前面; ● 如果出现内部子程序或模块子程序,则必须跟在CONTAINS语句后面。 【例4-19】 下面是一个典型的只包含一个程序单元,也就是主程序的FORTRAN程序: !本程序能够求出所有100-999之间每一位上的数字的立方和等于自身的三位数。 PROGRAM SUM_OF_CUBES INTEGER A,B,C DO A = 1,9 DO B = 0,9 DO C = 0,9 IF (100*A + 10*B + C == A**3 + B**3 + C**3) PRINT “(3I1)”, A,B,C END DO END DO END DO END PROGRAM SUM_OF_CUBES RUN SUM_OF_CUBES 153 370 371 407 大家不妨尝试一下,1000-9999之间还存在这样的数字吗?如果是平方呢? 下面的表4-2给出了程序单元的基本模式,其中处于同一水平位置的各语句之间没有严格的前后顺序,而不同的行则表示了严格的在程序当中出现的前后顺序: 表4-2程序单元的基本模式 程序,函数,子例行程序,模块,数据块语句 USE语句 FORMAT语句, ENTRY语句 IMPLICIT NONE PARAMETER语句 IMPLICIT语句 PARAMETER语句, DATA语句 派生数据类型定义, 接口块, 数据类型声明语句, 语句函数语句, 特定语句 DATA语句 可执行结构 CONTAINS语句 内部子程序或模块子程序 END语句 ●把 DATA语句放置在可执行结构中间已经过时。 ●语句函数语句已经过时。 表4-3给出一个特定的语句能够在什么结构中出现,不能在什么结构中出现的概貌: 表4-3语句的环境 作用单元的种类 语句 主程序 模块 数据块 外部子程序 模块子程序 内部子程序 接口块 USE语句 Y Y Y Y Y Y Y ENTRY语句 N N N Y Y N N FORMAT语句 Y N N Y Y Y N 其他声明 Y Y Y Y Y Y Y DATA语句 Y Y Y Y Y Y N 派生类型定义 Y Y Y Y Y Y Y 接口块 Y Y N Y Y Y Y 语句函数 Y N N Y Y Y N CONTAINS Y Y N Y Y N N 可执行语句 Y N N Y Y Y N 其中Y表示该语句可以在相应的结构当中出现,N表示不能出现。 4.5INCLUDE行 很多时候一个完整程序的源码还可以原封不动地移植到另一个程序源码的中间,这时并不需要把被移植源码完整地抄写过来,而只需要简单地运用INCLUDE行即可。 【例4-20】 PROGRAM GREEN_FUNCTION REAL X,Y,Z ….!语句省略 INCLUDE ‘GAUSE’ ….! 语句省略 END 这样源码文件GAUSE就直接进入程序GREEN_FUNCTION的源码里面,取代了INCLUDE行的位置。 INCLUDE行由关键词INCLUDE和其后的文件名称组成。其中的文件名称是一个字符文本常量。 ● INCLUDE行只是针对编译器的一个提示,而不是属于程序内的FORTRAN语句。 ● 其中的字符文本常量不能带作为命名常量的种别参数。 ● INCLUDE行必须放置在程序当中,其所引用文件应当出现的位置。 ● INCLUDE行所在行不能有任何其他文字,包括标识符,当然可以有注释。 ● INCLUDE行可以进行嵌套,嵌套的层数由具体的编译器规定。注意在嵌套的同时不能导致定义循环。 ● INCLUDE行之前的语句不能是被连续行,其后的语句也不能是连续行。 4.6与其他语言的语法要素方面的比较 下面简要地比较一下,作为一种语言,FORTRAN和其它语言在总的语法风格方面的异同。 一种计算机语言的语法远比任何自然语言的语法要来得简单明了,计算机语言的语法风格可以明确地归结为各种基本语法要素的选择。因此可以按照语法要素对不同的语言加以比较。 1.字符集 按照语言的所谓形式定义,语言就是取自一个有限字符集合的任意字符所构成的有限字符串的集合。 显然,一种语言选择哪些字符作为它的字符集,正是语言文法设计的第一步。 最常用的字符集就是ASCII字符集,而一套完整的字符集除基本字母和数字外,通常还包含一些特殊字符,以便为语言提供足够的表达手段。 表面看来,一种语言所使用的字符越多,它的表达能力应该是越大,然而在增大字符集的同时,也增加了编译时词法分析的分量,因此这里的折衷方式的不同,造就了不同语言的字符集的差异。 字符集的选择首先来自语言的输入输出设备。对于FORTRAN,C等大多数语言,是面向以工业标准键盘为主的输入输出设备的,因此这些语言的字符集可以说是大同小异的,不过也有例外,如APL语言就使用了ASCII字符集之外非常特殊的字符,因此这种语言的字符集就不能被大多数输入输出设备直接使用。 从计算机历史来讲,到了1960年代的早期,计算机行业对字符的表示大都由六位字节转变为八位字节,这样理论上就有了256个字符可用,分配给52个大小写字母、十个数字以及一些标点符号,应该是足够了。不过,由于计算机语言本地化的趋势越来越流行,语言的国际化导致各种民族语言的文字都要求进入字符集。除了法语,德语里的语音符号之外,希腊语,阿拉伯语之类的语言有着完全不同的字符集。而如果中文和日文要进入计算机语言,则需要一个有一万多符号的字符集。因此甚至出现了一种想法,就是考虑用十六位(65536)来表示字符集。 2. 语句标签 在FORTRAN77及其前面的标准里,语句标签的使用具有严重的时代烙印,而随着技术的进步,这种过时的玩意在大多数情况下不再具有任何价值,特别是严重地损害源码的可读性。因此语句标签在任何语言里都是属于需要淘汰的对象。 3.运算算符 字符+和-(加号和减号)在大多数语言里都是代表两种基本的数学运算,不过除了这两个符号之外,其他的运算算符就有相当多的花样了。 例如只是用特殊字符来表示基本运算算符,典型的就是APL语言,还有LISP语言、TIMES语言等的逻辑运算符;而大多数语言使用一些字符组合,同时也利用特殊字符来表示部分运算算符,有时还使用一些不属于上述两种之中任意一种的字符串来表示部分运算算符,例如FORTRAN里面的.EQ.表示相等,指数运算用**表示等。 4.关键词 计算机语言大都使用保留字与关键词,从而能够提高翻译器的检错能力。而且大多数以关键词开始的语句本身就表明了语句的类型。例如IF、READ等。 一种语言究竟使用多少保留字和关键词,往往是一个折衷的结果:保留字少,可以减少编程者的记忆负担,同时却会增加语法分析的困难;而如果使用大量的保留字,例如COBOL语言,使用了大量的保留字,使得编程者很难全部记下,因此就会常常出现不小心使用保留字作变量名的错误,当然使用很多的保留字,就会使得翻译过程中的语法分析变得更为简单。 特别需要注意的是,当一种语言出现新版本时,得小心是否引入了新的保留字,例如COBOL语言,就在新标准里引入了新的保留字,这就意味着那些在程序中使用新的保留字作为变量名(或其他名字)的旧程序,按照新标准在语法上就不再正确了,尽管这个程序一点也没有改过。对于FORTRAN来说,这类问题就没那么严重。 5.注释 尽管注释一定是被编译器忽略的,但却是文件中十分重要的一部分,因为注释是保证源码的可读性非常重要的手段。在不同的语言里,表示注释的方式非常不同,甚至在一种语言里,也可能有几种方法引入注释: 在C语言程序中,需要使用/*和*/这样的特殊标记来界定而不管行边界,这时常常出现的错误就是漏掉结束的界定符,使得后面的语句也变成了注解! FORTRAN 95语言中的! ,Ada语言中的-,或是C十十语言中的//,都是在行的任意位置开始而直到行的末尾结束。这种做法就更为合理方便。 6.空格 在不同的语言里空格的使用规则非常不同。这点需要引起注意。在FORTRAN语言中,空格在除了字符串数据以外的任何其他地方都没有意义。而在很多语言当中使用空格来作为分界符,这就使得空格在语法中有实际的含义。对于初学者来说,常常意识不到空格带来的问题。 7.定界符和括号 定界符的作用就是简单地标志类似语句或表达式之类的语法元素,目的就是增加源码的可读性,并且使得语法分析更加简单。特别需要注意的是,成对的定界符用来清楚地界定特定语法结构的边界,从而可以有效地消除二义性。 8.自由或固定格式 严格的固定字段格式一般是应用在汇编语言当中。而早期的FORTRAN则是使用了部分固定字段格式;其实很多语言早期同样是具有部分固定字段格式,不过现代语言的风格是完全排斥那种固定格式的,因此几乎所有语言,只要还没有被废弃,都已经采用了自由字段格式。 发表于 2005-8-30 10:10 | 只看该作者 Re:科学计算的语言------Fortran95 第5章 准备数据 从本章开始,我们将赋予语言实质性的语义,也就是规定上章所描述的语言的每一个细节所具有的涵义。这种涵义与其说是我们对于一种语言细节的定义,不如说是算法的要求,要求语言具有足够多的细节,用来表达在算法当中有可能出现的精细情节。 对于任何的问题,站在计算机的角度来看,总是可以把它抽象为如下图所示的结构: 输入数据 计算过程 输出数据 因此要准备通过计算来解决一个问题,首先要作到的是把该问题所涉及到的数据整理好,也就是列出所有的数据,然后根据数据的数学属性进行分类,这个分类的过程就是对数据施加足够的标记的过程,将来把这些数据输入到计算机,计算机将能够依据这些标记,辨识出数据所应该具有的数学属性,从而施加相应的合法数学运算。 所以作为向计算机描述计算问题的FORTRAN语言,它首先要作到的是约定如何给数据施加足够详细的标记。 对这个标记过程的第一个要求是保证准确性,也就是说这个语言的标记系统必须正确地反映真实世界的问题里面,数据所具有的数学属性,因此这个标记系统必然是与数据的数学分类结构保持一致的。 从数学的观点来看,世界上的所有数据,总是可以被表示为整数,实数,复数等等基本的数据种类,因此本章的内容就是讨论: ● FORTRAN语言如何把数据归结为一些基本数据类型; ● 然后为了足够详尽地描述每一个数据类型的属性,FORTRAN是如何施加相应的标记的; ● FORTRAN语言对于这些标记(语法形式)所约定的语义是什么。 然后我们就可以知道,要想用FORTRAN来描述一个问题的算法,并进而以问题算法的FORTRAN语言版本为媒介,通过计算机来得到计算结果,第一个步骤,就是准备好数据的FORTRAN描述。 5.1数据是什么 在上一章里,据称计算机能够使用语言,而且是非常类似于人类的语言,至少从形式上看很象,这难免会令某些人(特别是看过KUBRICK的影片《2001: A Space Odyssey》的观众们)感到恐惧:) 别怕!且先不讨论FORTRAN作为语言是否具有与人类语言等价的表达能力,至少从自然语言的语义学的角度来看,FORTRAN说出来的话其实是绝对空洞的,因为FORTRAN语言的全部语义基础就只是数据,而数据对机器而言,只是意味着经过编码的符号。 一台计算机其实是由以下6个部分组成: ● 数据------也就是基本数据元素以及数据结构; ● 基本操作------也就是一个对上述数据进行操作的基本操作集; ● 顺序控制------也就是一个控制针对数据的基本操作执行的时间顺序的机制; ● 数据存取------也就是一个如何给操作提供数据的机制; ● 存储管理------也就是一个数据存储分配机制; ● 操作环境------也就是一个支持程序和外部环境进行数据通讯的机制。 因此一台计算机 ● 在程序的使用者看来,就是给它输入数据,它再给你加工过的结果数据; ● 在程序的编制者看来,就是把对数据的处理过程表示为计算机有限的一系列基本操作(指令)的集合,使得计算机能够处理相应的数据; 因此,计算机的一切可以说都是围绕着数据----如何表达数据,如何处理数据。而计算机语言所要具备的两个部分的功能,首先就是完备的描述数据的性质,然后就是描述数据的处理过程。 那么,什么是数据呢? ● 数据就是符号化了的信息! 对于计算机来说,任何信息都只有表示为符号,才能被认可;反过来说,计算机只能输入符号,而不会也不能理解符号的含义,它的能力只是体现在按照既定规则来处理符号。 然后,就是给出数据的表示,即如何用符号来明确而无歧义地表达数据。 要使得符号具备数据的含义,需要经过这么几个步骤: ● 处理符号的第一步:给符号分类,并给出描述符号性质的方法。 这个分类是人作为设计者给符号规定语义的第一步,因为对于人来说,数据不能只是符号,而是具有来自真实世界的语义,设计者正是根据符号的这种语义,制定相应的处理符号的规则,而计算机要想能够正确地处理符号,基本的前提,就是每当引入一个数据,都得由人向机器声明这个数据是什么类型,这个数据具备什么性质,而且假设计算机已经被引入处理该种数据类型的规则。 ● 处理符号的第二步:区分常量与变量。 这一对范畴反映了最基本的人类抽象能力,也正是人类思维的基本模式。要想让机器模拟这种能力,最简单的做法就是:任何时候都必须首先声明,哪些符号表示常量,哪些符号表示变量,而变量相应的取值范围必须规定好,也就是说必须描述其取值为具有何种属性的常量值的集合;或至少已经被机器默认。 ● 处理符号的第三步:给每一类数据规定相应的合法运算。 对于一种数据可以执行什么样的运算,来自于语言设计者对数据语义的规定,只有当运算被表示为相应的机器指令或指令集合,这时在表面看来,机器才开始真正“理解”了数据的“涵义”。 因此可以说,数据的定义构成了计算机的“灵魂”。 按照上面讨论的步骤,说明一个数据类型包括四个方面: ● 命名的语法 ● 取值的范围与属性说明 ● 该数据类型的常量的表示方法 ● 定义合法的运算 因此相应的一个数据类型的四个要素就是: (1) 名字; (2) 值的集合; (3) 表示值(相应的常量)的方法; (4) 操作值的运算的集合。 对于这四个要素,FORTRAN一方面要约定它们的语法形式,从而可以构成符号描述的唯一性标记,保证相应的描述语句能够被FORTRAN编译系统无歧义的辨识,另一方面就是要给出这些语法形式所对应的含义。 5.2用FORTRAN来说明数据的性质 真实世界的数据显然是多种多样的,几匹马,轴承的内径,圆周长与直径的比值,电子的波函数,非各向同性电介质的电极化率,10个被试每日的最高血压等等,这些数据都具有非常不同的形式与性质,如果我们每针对一种数据形式,都把它定义为某一种新的数据类型,则肯定是烦不胜举,因此合理的途径是找到一种统一的数据描述方式,而对于科学计算问题来说,自然的数据分类方式是数学对数值数据的分类,再加上非数值型数据,会是非常适合于科学计算的数据表达方式。 当然,如果是以描述其他类型的问题为目的,如事务处理,符号演算等,则选用另外的数据分类方式会更有效。 至少从数学的观点来看,我们常常需要处理的数据,都可以表示为一些基本数据类型的组合,例如我们知道向量实际上就是一个数组,数组的每个元素为标量,因此应用数组这种结构,就可以自然地表示向量,另外复数尽管也可表示为一个二元数组,但是这种二元数组的乘法不同于二维向量,因此为了避免这种歧义, FORTRAN把复数当成一个基本数据类型。而几种基本标量里面,整数和实数都同样必须构成基本的数据类型。由此可以建立FORTRAN的对数据的类似描述。 由于真实世界问题的要求的不同,对数据的描述也有程度不等的情况,最基本的情况就是直接说明数据的类型以及其他属性,又由于数据表示的实现具有一个重要的参数,即存储空间,所以当问题要求的数据,不能满足于默认的存储空间的时候,这时,就需要进一步给出数据的种别参数,这是更加详细的数据描述。如果在真实世界问题当中出现的的数据对象,干脆不符合已有的固有数据类型以及数组的定义,这时就还需要根据用户的要求构造一个依赖于问题的特定的数据结构,这就是数据描述时会遇到的第三种情况。 对于这三种情况,FORTRAN的解决方式如下: 第一种情况: 确定数据的类型以及相应的可能具有的属性。 首先,FORTRAN所能辨识的数据类型首先分为两大类: ● 固有数据类型 ● 派生数据类型 根据语义上的基本差别,数据首先具有一些基本的类型,这些基本类型一般是和构成真实世界里的信息的那些基本元素相对应,比方说数字,字符等。然后其他情况下遇到的数据都可以由这些基本数据类型组合得到。不过一种语言具体的规定哪些基本的数据类型,往往受到该种语言主要应用的场合的影响,由于FORTRAN主要用来进行科学计算,因此它所定义的基本数据类型,正是与我们在科学计算问题当中遇到的数据类型相契合的。 ● 所谓固有类型,是FORTRAN语言所定义的最基本的数据类型,每一种固有类型是和该种数据类型相应的各种运算一起隐式定义的,也就是说一旦声明引入某种固有数据类型,则系统总是默认为对它进行相应的运算是合法的,并且总是可访问的。 这样就做到了每种数据类型都和它相应的运算捆绑在一起,使得问题的描述非常自然。 ● 固有类型包括五种: 整型(INTEGER)、实型(REAL)、复型(COMPLEX)、逻辑型(LOGICAL)和 字符型(CHARACTER)。 这个分类完全是遵循数据的数学分类,即整型指整数,实型指实数,复型指复数,逻辑型指逻辑值,字符型则是语言的基本元素。这样就可以把基本的数学语言一一对应的直接翻译为FORTRAN语言。 ● 所谓派生类型是由用户定义的,非隐式定义的类型,只要用一个类型定义来声明其成员是何种固有类型,或者是何种其它已经定义过的派生类型,就能够被FORTRAN认可为一种数据类型。 由于派生数据类型正是由固有数据类型充当成员而构成的,因此在结构关系上,可以把固有数据类型看成原子,而把派生数据类型看成分子。由于语言的根本目的就是为描述算法服务的,因此从这个角度出发,派生数据类型本质上体现了非常重要的数据抽象与数组合的思想,由于我们需要运用语言来描述的问题是开放性,我们很难划定需要FORTRAN来描述的问题的范围,因此通过构造派生数据类型,使得我们可以很自然而简洁地建立新的数据类型。这是FORTRAN在FORTRAN77标准之后的一个重大进步。派生类型数据最重要的用途就是扩充了数组这种重要的数据结构,由于数组在科学计算领域,是一种极端重要的数据结构类型,FORTRAN除了能够直接描述数组,同时还能描述更为广泛的派生数据类型,也就可以直接对一个数据集合的各个成员同时施加运算,拥有了这种自然的数据类型,就避免了象FORTRAN的早期版本那样,需要通过特别设计的算法来实现这种运算。 所谓固有数据类型的固有,对于FORTRAN来说,就是为每一种固有数据类型规定了它的存储模式。 在FORTRAN77及其之前的标准里,整型,实型,逻辑型数据都是使用了一个数值存储单元,而复型和双精度数据则使用了两个数值存储单元,字符型数据使用一个字符存储单元。由于存储模式是非常底层的语言实现结构,因此FORTRAN后续的标准要想保持兼容,只有继承这个约定。 因此在FORTRAN90与95当中,默认的整型,实型,逻辑型数据都是使用了一个数值存储单元,而默认的复型和双精度数据则使用了两个数值存储单元,默认的字符型数据使用一个字符存储单元。而作为语言的一个发展,在FORTRAN90之后的标准里,开始允许在一个程序单元内,由用户定义特定的不依赖于固有数据存储模式的数据类型,这就是第6章的派生数据类型。 建立一种数据类型,最大的好处就是可以把相关的运算和数据捆绑在一起,对于一个特定问题当中的数据对象,是否应该被明确地看成数据类型,属于语言设计的权宜,因为建立一种数据类型所能带来的好处可以用算法来补偿,而FORTRAN77之后的版本的选择是增加派生数据类型,这样使得我们可以在进行科学计算时,有更为自然的描述方式。 数据类型的全部分类总结如下: 固有数据类型 数值型数据 整型 实型 复型 非数值型数据 逻辑型 字符型 派生数据类型 就数据的属性而言,类型当然是最重要的属性了。在指定类型之后,紧接着的就是根据实际情况,看需要描述的数据是否还具有其他需要说明的属性。 对于数组来说,具有一个基本的属性,就是数组的大小,相应的就是如何指定数组的存储空间的大小的问题。 由于FORTRAN具备可分配数组与指针的功能,因此在程序开头并不一定需要指定数组的大小(维度),在程序执行过程当中,数组的大小会作为输入或计算结果被读入,这个指标可以针对具体的问题的要求,以及运行的状况而定。在FORTRAN还不具备这种动态功能的时候,就需要在数组声明里指定数组的维度,而在事先又很难准确预料程序运行过程当中对数组储存空间的要求,因此如果指定的数组过大,就会大量地浪费当时非常宝贵的内存空间,而如果指定的数据组过小,则肯定会在程序运行过程当中导致错误。 所以为了避免这些问题,现在一般不会在数据声明的时候精确指定数组大小,而是把数组处理成一个动态对象,从而有效地回避了这个问题。 数据的一个重要属性,就是它的可访问性。在FORTRAN语言里,模块提供了对数据的访问控制。任何数据对象只要想把自己局限在模块内使用,模块就能够提供足够的保护,使得外部程序无法访问该数据对象。模块的这个功能使得FORTRAN成为一种安全可靠的语言。 是否打开数据的其他许多具体的属性,取决于具体的数据应用环境,因此要讨论数据的每一种属性,在这里不太现实,我们只有等到下面具体地说明每一种声明语句时再具体说明,因为属性指定总是在出现在声明语句当中。 第二种情况: 指定固有数据类型的种别参数。 对于计算机来说,在确定数据的类型,从而可以引导到相应的运算之后,进一步就需要为数据在内存指定存储位置和存储空间,实际上对于冯纽曼型计算机来说,这个步骤是非常关键的,因为冯纽曼型计算机的要点,就是硬件之外的一切,都必须表示为数据,都必须存储在内存当中,然后在程序的运行当中,随时与CPU进行通讯,因此在程序的开头就明确数据在内存当中的位置与每个数据所占有空间的大小,是保证程序运行非常基本的要求。 ● 用来指定程序当中需要使用的每一种固有数据类型所要求占据的内存空间大小的属性由种别参数表示。给这个变量(参数)指定一个数值,就可以说明数据所需要的存储空间的大小,也就是程序允许的数值数据的位数和字符串的字符数目。 ● KIND(种类种别参数)分别说明整数类型的十进指数范围,实数类型和复数类型的十进制精度和指数范围,字符类型和逻辑类型的表示方法。 ● LEN(长度种别参数)对字符类型规定了字符的个数。 【例5-1】 REAL(KIND=3)::ABC,X,LONG CHARACTER(LEN=40,KIND=GREECE)::NAME 具体的种别参数的约定是与语言的具体实现相关的,因此具体的取值还是得参考编译器的文档。 ● 如果没有声明数据的种别,那么程序就会采用默认的参数,由于FORTRAN的早期版本没有引入种别参数,因此对于有不同精度要求的实型变量,直接采用了两种不同的数据类型,这就是REAL和DOUBLE PRECISION,从FORTRAN90以来的版本里,通过引入种别参数,对种别参数的不同取值,就足够表达不同的精度,而同时为了保证和早期版本的兼容,单独的数据类型声明DOUBLE PRECISION还是被保存下来了,这样一来,就产生了一个有一定任意性的后果,即新的语言标准对不同精度的实型数据,可以通过使用同一个数据类型的不同的种别参数值来表示,而同时用DOUBLE PRECISION作为数据声明也是有效的,这样就保证了源码向前的兼容性,却不具备向后的兼容性。 ● 对于在指定种别参数的数值时,一般是以字为单位,这样对于字长不同的机器而言,相互之间就会出现程序移植的困难,下面分情况予以说明: ● 实型----由于DOUBLE PRECISION是属于老式标准的遗留物,因此使用DOUBLE PRECISION作为数据声明的程序就不具备良好的可移植性,因为所谓双精度是针对具体的机器的字长而言的,对于32位的机器,双精度就是64位,而对于64位机器,双精度就意味着128位,这样在不同字长的机器环境里,双精度就具有不同的位数,使得程序无法在不同字长的平台之间进行直接的移植。因此在这种情况下,最好还是统一使用REAL的种别参数来表达算法所要求的实数精度。可以说种别参数一劳永逸地解决了实数精度的可移植性问题。 ● 复型----由于所谓复型本质上就是由两个实数表达的,因此按道理复型同样应该能够具有表达多种精度的能力,而实际上早期的版本在这方面是有欠缺的,不过随着FORTRAN90引入种别参数,就可以在COMPLEX的声明语句里通过运用种别参数来实现多种精度的表达,对于任何FORTRAN的实现,至少能表达两种精度,而一般来说是多于两种的。 ● 字符型----对于字符,一般的机器都是用单字节8bits来表示一个字符,这样就可以总共表示28=256个不同的字符,这对于任何以字母写出来的语言都是足够的了,不过对于汉语,日语这样一些语言就不够用了,一般得需要双字节,即16bits,这样就可以表达216个字符。因此字符型数据同样需要附加种别参数,以便除了使用默认的基本字符之外,还可以使用辅助字符集里的字符,从而实现程序的本地化。不过某个具体的编译器是否支持双字节字符,必须参考相应的手册。因为FORTRAN 95标准也没有强制要求FORTRAN的任何实现都必须支持双字节字符。 ● 逻辑型----由于一切逻辑型数据都只有两个值,因此如何确定逻辑型数据的存储空间应该是非常好办的,不过不幸的是,FORTRAN的早期版本规定逻辑型数据使用和实型数据一样大小的机器存储单位,这样当机器的字长很大时,就会非常的浪费机器的存储空间。因此到了FORTRAN90和FORTRAN 95,除了作为默认的情形,和旧的语言标准保持兼容之外,还可以通过指定种别参数,使得逻辑型数据的存储空间大小只有一个字,甚至一个bit。当然具体的使用方法需要参考相应编译器的说明。 ● 整型----显然在程序应用当中会出现几乎任何大小的整型数据,因此无法在语言标准里面统一的规定整型数据的存储空间大小,这就同样需要依靠种别参数来指定应该给具体问题当中的整型数据确定多大的存储空间。具体地指定方式属于编译器设计者的选择,需要参考相应编译器的语言说明。 第三种情况: 派生数据类型。 数据的本义就是对真实世界里的事物的描述。这种描述可以是简单的,如一个标量,也可以是复杂的,如一个张量,对于更复杂的对象,在自然语言里有一种自然的描述方法,就是使用一系列的词汇,每个词汇都是对象在某个方面的属性的度量;在计算机语言里,可以采用类似的解决方案,即把对象的每一个需要描述的性质用一个适当的基本数据类型来表示,这样用一组基本数据类型就可以描述该对象。而这一组数据可以看成是一个新的数据类型,表示了一个变量。 这样构造出来的数据类型称为派生数据类型,和固有数据类型一样,在声明派生数据类型时,需要给出名称,描述它的每一个元素的固有数据类型以及相应属性和种别参数(如果非默认的话),当然也需要适当地定义其运算。 既然这种派生数据类型是由一组数组成,就会出现两种情况: ● 这组数据都是属于一个数据类型 这样构成的派生数据类型就是数组,显然对于数组的元素的描述就可以统一进行。具体的用法会在后面专门说明。 ● 这组数据的各个元素属于不同的数据类型 这样构成的派生数据类型称为结构,这时就需要对每个数据元素进行分别的说明,即每一个元素的数据类型,可能有的属性,种别参数等等。 上面对派生数据类型的描述实际上是递归式的,即一个派生数据类型的元素同样可以是另一种派生数据类型,而没有限定必须是固有数据类型。 【例5-2】 下面是一个典型的派生数据类型。 TYPE SAMPLE REAL CURRENT COMPLEX (KIND = QUAD)PHASE CHARACTER (LEN = 50) SOURCE END TYPE SAMPLE TYPE (SAMPLE) SI401,SI402,SI403,SI404 在上面的例子里,首先定义了一个名称为SAMPLE的数据类型,每一个SAMPLE类型的数据由三个分量组成,它们的名称分别为CURRENT,PHASE,SOURCE,分别属于实型,复型和字符型,其中复型和字符型还分别说明了种别参数和字符长度属性,然后给出了程序当中需要使用的四个属于该种数据类型的变量:SI401,SI402,SI403,SI404。 上面例子当中派生数据类型的定义,以TYPE开始,以END TYPE结束。 5.3数据不同种类的存储模式 对于计算机来说,数据分类的第一个反应就是针对不同类型的数据约定不同的存储模式。 由于存储模式的规定涉及到编译环境的设置,因此存储模式的约定是与系统环境相关的,鉴于Compaq Visual Fortran的广泛应用,本节特别针对Compaq Visual Fortran系统而言的说明了数据的各种存储模式。 下表5-1列出了Compaq Visual Fortran所有的固有数据类型的存储空间要求,和相应的能够在这个空间里表达的数据规模。 表5-1 固有数据类型的存储模式: 数据类型 单位存储空间 能表示的数据规模 BYTE INTEGER(1) 1 byte (8 bits) BYTE表示等价于INTEGER(1)的带符号的整型数据类型。 INTEGER 参见INTEGER(2), INTEGER(4), 以及 INTEGER(8). 带符号的整型数据, 包括INTEGER(2), INTEGER(4), or INTEGER(8)。数据规模由编译器选项/integer_size:nn 控制。默认的规模控制选项为/integer_size:32 (等价于INTEGER(4))。 INTEGER(1) 1 byte (8 bits) 从-128到127带符号的整数。 INTEGER(2) 2 bytes (16 bits) 从-32,768到32,767带符号的整数。 INTEGER(4) 4 bytes (32 bits) 从-2,147,483,648到2,147,483,647带符号的整型数据。 INTEGER(8) 8 bytes (64 bits) 从-9,223,372,036,854,775,808到9,223,372,036,854,775,807带符号的整型数据。 REAL(4) REAL 4 bytes (32 bits) 从1.17549435E-38到 3.40282347E38的按照IEEE S_floating格式的单精度实型浮点值。在1.17549429E-38和1.40129846E-45之间的值是非常态的。 REAL(8) DOUBLE PRECISION 8 bytes (64 bits) 从2.2250738585072013D-308到1.7976931348623158D308的按照IEEE T_floating格式的双精度实型浮点值。在2.2250738585072008D-308和4.94065645841246544D-324之间的值是非常态的。 COMPLEX(4) COMPLEX 8 bytes (64 bits) 由一对从1.17549435E-38到 3.40282347E38的按照IEEE S_floating格式的单精度实型浮点值组成的单精度复型浮点值。在1.17549429E-38和1.40129846E-45之间的值是非常态的。 COMPLEX(8) DOUBLE COMPLEX 16 bytes (128 bits) 由一对从2.2250738585072013D-308到1.7976931348623158D308的按照IEEE T_floating格式的双精度实型浮点值组成的双精度复型浮点值。在2.2250738585072008D-308和4.94065645841246544D-324之间的值是非常态的。 LOGICAL 参见LOGICAL(2), LOGICAL(4), 以及LOGICAL(8). 逻辑型值, 包括LOGICAL(2), LOGICAL(4),以及 LOGICAL(8). 数据规模由编译器选项/integer_size:nn 控制。默认的规模控制选项为/integer_size:32 (等价于LOGICAL(4))。 LOGICAL(1) 1 byte (8 bits) 逻辑型值.TRUE. 或.FALSE. LOGICAL(2) 2 bytes (16 bits) 逻辑型值.TRUE. 或.FALSE. LOGICAL(4) 4 bytes (32 bits) 逻辑型值.TRUE. 或.FALSE. LOGICAL(8) 8 bytes (64 bits) 逻辑型值.TRUE. 或.FALSE. CHARACTER 每个字符1 byte (8 bits) 根据约定的字符编码表示的字符数据,通过字符数据的声明形式:CHARACTER(LEN=n)或 CHARACTER*n,其中n 表示byte数,来表示数据规模。 HOLLERITH 每个Hollerith 字符1 byte (8 bits) Hollerith 常量。 表中的INTEGER(4)等价于INTEGER(KIND=4)以及INTEGER*4. 5.4FORTRAN数据类型描述的四个基本属性 一个数据如何才是被完备描述了,以及FORTRAN所要求的描述一个数据的要素是哪些,是一个问题的两面。这个问题对于程序的作者是很重要的,因为FORTRAN现在允许用户自己定义合乎自己需要的派生数据类型,这就要求我们知道一个派生数据类型的定义是否完备。 FORTRAN的数据类型必须包含如下四个部分: ● 数据类型的名称 ● 数据取值的集合 ● 可以施加于数据的值的运算 ● 该数据类型的常量的表示形式 5.4.1数据类型的名称 要能够说明数据的类型所属,首先每种数据类型本身得有个名称,才能在描述数据对象的时候,说某个数据对象属于某个数据类型。 固有数据类型就只有5种,它们的名称:INTEGER,REAL,COMPLEX,LOGICAL,CHARACTER是语言标准的规定。 但派生数据类型则完全是程序作者自定义的,因此必须由作者使用TYPE来给出其构造的派生数据类型的名称,也就是说只要一个数据或一个变量的取值符合TYPE与END TYPE之间的定义,就被该程序单元识别为属于该数据类型,就可以应用相应的运算。 如果一个程序单元里出现的数据不能被识别为该程序单元的数据声明里的诸种类型,那么FORTRAN还会尝试运用一种方式来试图确定它的数据类型,就是根据数据名称的第一个字符来进行判别,这种方式属于FORTRAN的古老传统,因为早期FORTRAN所处理的数据类型比较单纯,顾可以如此简化处理,FORTRAN90与FORTRAN 95都继承了这点。 5.4.2数据取值的集合 对于每种数据类型,存在一个允许的具体取值的集合。而属于该数据类型的变量的取值范围必定是在这个集合内。 表面看起来数据类型的取值集合都是明确的数学意义,但是由于本质上计算机的任何具体取值,都必须是有限的,因此数据类型表面的所谓数学涵义并不是很符合实际的。固然整型必定是取整数值,但只能取有限的整数值,而且这个值还有上限,即一个整型数据能够取多大的整数不仅在机器的硬件方面有制约,在语言的具体实现上也进行了约束。 同样,对于实型来说,更不可能就是和实数集合等价,实型数据的具体取值同样只能取可有限表示的实数,即有限小数。至于某些软件(如MATHEMATICA)声称可以精确的引用无理常数,例如欧拉常数,实际上是使用了一个收敛级数来表达无理常数,只有当用户指定有理表示的精度后,计算机才对级数做相应的截断,给出相应精度的有理表示,而并不是说该常数的无限位表示完全存储在计算机里面。 ● 逻辑型数据能够取得的值的个数是完全确定的,即仅有真和假两个值(即两个元素)。由此可见所有逻辑型变量都是某种判断,而对该判断的取值只能或真或假,这里实际上就规定了FORTRAN语言只能用来表述满足排中律的数学。 ● 对于整型和实型来说,既然只可能取有限值,那么剩下的问题就是如何给某个具体取值分配存储空间了,由于程序单元是根据数据声明当中对数据取值的规划来确定如何为数值分配存储空间的, 因此对于具有极大处理能力的现代计算机而言,最好针对数值占用空间的大小进行分级,以做到在保证数值表达需求的前提下,尽量避免存储空间的浪费。FORTRAN为了给数值占用空间的大小分级,引入了种别参数(K1ND),使得在数据声明的时候,就可以一致地规定该类数据在表达时,允许占用空间的大小。 例如整型除了默认表示之外,还可以标志以种别参数“SHORT”,这个参数意味着在整型的默认取值范围了划出了一个子集,只要是属于这个子集的数据,允许系统给它分配较为小的,但更为合算的存储空间。 对于实型来所,则完全可以根据算法的需要,在开始的数据声明里,就给程序单元里可能出现的数据划出三流九等,使得程序对存储空间的占用更为合理。当然FORTRAN语言标准只是规定了实型必须至少在默认精度种别之外,还需要有一个双精度种别,而在FORTRAN的各种编译实现里,还可以规定更多的精度种别。 ● 对于字符型数据来说,它的存储空间完全和字符串长度成正比,因此只要直接规定字符串的字符个数,就可以一致地得到其存储空间分配标准。 ● 至于复型和派生类型,则完全以其他数据类型作为成员,自身没有什么特别的规定,因此也就没有独特的针对这两种数据类型的种别参数。 显然,FORTRAN通过运用种别参数来明确地规定数据的表示,使得Fortran的标准化程度得到了进一步提高,从而提高了程序的可移植性。 5.4.3数据类型的合法运算 允许施加于数据的运算同样可以分为两类,即与固有数据类型相应的固有运算,还有自定义运算。由于在FORTRAN里面,运算的主要语法功能是构成表达式,因此详细的关于运算的讨论,参见有关表达式的章节。 1. 固有运算 固有运算就是固有数据类型在FORTRAN里面指定了表示符从而可以直接引用的那些固有运算,根据运算所能施加的算元据的不同,一共分为四类: 1. 算术运算 2. 串联运算 3. 关系运算 4. 逻辑运算 简述如下: ● 算术运算 针对三种数值型数据,可以直接引用7种固有的算术运算: ● 2种一元运算: 求反运算,其运算符为-; 求同运算,其运算符为+。 这两种一元运算可以施加于任意数值型数据和种别参数的组合,其运算结果的数据类型与种别参数和算元的数据类型和种别参数保持一致。 ● 5种二元运算是: 加法运算,其运算符为+; 减法运算,其运算符为-; 乘法运算,其运算符为*; 除法运算,其运算符为/; 乘幂运算,其运算符为**。 这5种运算的两个算元可以是数值型数据的任意数据类型与任意种别参数的任意组合。 如果参与运算的两个算元不是同一个类型或种别参数不同,那么FORTRAN如何决定结果的数据类型或种别参数呢?基本的原则就是向需要存储空间大的操作数看齐,以免损失算元的信息。具体地说,就是: ● 若两个算元是相同类型和相同种别参数,则运算结果的类型与种别参数就是算元的类型与种别参数。 ● 若两个算元都是整型但种别参数不同,则运算结果的种别参数是取十进制幂范围大的那个算元的种别参数;若范围一样大,则由系统决定。 ● 当一个算元是整型、另一算元是实型或复型,则运算结果的种别参数就取那个实型或复型的算元的种别参数。 ● 若两个算元属于不同种别参数的实型或复型数据,则运算结果的种别参数取十进制精度高的那个算元的种别参数;若精度一样,则由系统决定取舍。 规定了运算结果的属性,具体的值就是通常的算术运算的结果,即 ● 加法为两个算元之和; ● 减法为两个算元之差; ● 乘法为两个算元之积; ● 除法为两个算元之商,如果两个算元都是整型数据,它们相除时称为整除,其结果商就是首先进行算术上的除法运算,得到的商去掉小数部分,取得的整数值即为整除的结果。这是为了满足上面关于保持类型一致性的规则。 例如:99/100的值为0;(-99)/100的值为0;58/3的值为19;(-58)/3的值为-19。 ● 乘幂为以第一个算元为底,第二个算元为指数的乘幂值。 ● 串联运算 针对相同种别参数的字符型数据定义了串联运算,其运算符是//。 串联运算的结果为保持种别参数不变的字符型数据。运算结果的值为第一算元的字符值,在右边紧接第二个算元的字符值。 例如:ABC//RTY的值为ABCRTY ● 关系运算 关系运算是分别针对整型、实型、复型和字符型数据来定义的二元运算。 关系运算的结果为逻辑型数据,即只能取.TRUE.和.FALSE.两个值之一。 FORTRAN 95定义了六种固有关系运算,这六种固有关系运算根据其可以施加的操作数的不同,又可以分为两类: 可以施加于除复型之外的数值类型,种别参数以及字符型的第一类: ● 大于,其运算符为.GT.,或>; ● 大于等于,其运算符为.GE.,或=; ● 小于,其运算符为.LT.,或<; ● 小于等于,其运算符为.LE.,或=; 可以施加于所有数值型与字符型的第二类: ● 等于,其运算符为.EQ.,或==; ● 不等于,其运算符为.NE.,或/=。 对于数值型数据来说,关系运算具有通常的涵义,并且两个算元可以是任意的数值型类型与任意种别参数的组合。当然只有复型不能比较大小,而只能比较是否相等。 对于字符数据来说,关系运算具有独特的涵义。 首先要求两个算元具有相同的种别类型参数,但是可以具有任意的长度。其关系运算的执行可以理解为执行下列几个步骤: (1)首先使两个作为算元的字符串的字符长度变为一致,如果相对来说有个字符的长度较短,就在右边以空格字符填充,直到两个算元长度相同为止。 (2)然后对两个算元按字符位置从左边第一个字符开始逐个进行比较判别,直到足够判别关系是否成立为止。 (3)而字符的比较是按字符在字符集中排列序列的位置的先后来进行的: ● 若字符1在字符2之前,则认为满足小于关系,小于等于关系和不等于关系; ● 若字符1在字符2之后,则认为满足大于关系,大于等于关系和不等于关系; ● 如果位置相同,即为同一个字符,则认为满足等于关系。 ● 所有空串都是相等的。 ● 等于关系和不等于关系的运算结果与字符集序列无关,而其它四种关系的运算结果是依赖于字符集排列序列的。由于ASCII的排列序列对于任何系统都是一致的,所以一般而言可移植性是能得到保证的。 ● 如果参与运算的默认字符数据值全是字母或全是数字,则按语言的规定,其顺序是严格确定的; ● 如果参与运算的默认字符数据值参杂了字母与数字,则把其中的数字看成字符,而排序则依赖于系统的具体规定。所以在使用时要注意这点。 ● 如果参与运算的字符数据值包含了非默认的字符型数据,则同样依赖于系统的规定。 ● 逻辑运算 针对任意种别参数的逻辑型数据定义五种逻辑运算。 我们知道数值型数据和字符型数据进行关系运算后的结果是逻辑型数据,此外还 可以根据算法的需要自定义逻辑型数据,逻辑运算就是施加于逻辑型数据,而得到逻辑型数据值的运算。 根据算元的数目,逻辑运算包含两种,其中一元运算为: ● 非运算,运算符为.NOT.; 非运算的运算结果定义如下表5-2: 表5-2非运算的运算结果 .NOT.的算元 .TRUE. .FALSE. .NOT.的运算值 .FALSE. .TRUE. 二元运算包括: · 与运算,运算符为.AND.; · 或运算,运算符为.OR.; · 逻辑等价运算,运算符为.EQV.; · 逻辑不等价运算,运算符为.NEQV.; 各运算的结果定义如下列各表: 表5-3与运算的运算 A.AND.B A B .TRUE. .FALSE. A .TRUE. .TRUE. .FALSE. B .FALSE. .FALSE. .FALSE. 表5-4或运算的运算 A.OR.B A B .TRUE. .FALSE. A .TRUE. .TRUE. .TRUE. B .FALSE. .TRUE. .FALSE. 表5-5逻辑等价运算的运算 A.EQV.B A B .TRUE. .FALSE. A .TRUE. .TRUE. .FALSE. B .FALSE. .FALSE. .TRUE. 表5-6逻辑不等价运算的运算 A.NEQV.B A B .TRUE. .FALSE. A .TRUE. .FALSE. .TRUE. B .FALSE. .TRUE. .FALSE. 逻辑运算的结果的种别参数的约定: ● 当两个算元的种别参数相同时,则结果的种别参数与算元的相同; ● 当两个算元的种别参数不同时,则结果的种别参数依赖于系统的约定。 2. 自定义运算 由于上面列出的固有运算,并不能满足我们在构造表达式时对运算的全部需求。显然,要使得语言具有开放性,就不可能期望通过指定有限的对象来概括任意需求,因此必然需要制定一个构造规则,以便允许程序作者自定义运算。所谓自定义运算就是需要程序作者根据算法的需要自己来定义的运算。 从语法的角度来讲,一个运算的定义包括三个部分: ● 符号的表示; 所谓符号的表示就是给出运算的名称,命名规则为一个字符串的左右分别加一个小数点(句点)。 【例5-3】 .REMAINDER. .REVERSE. .INTEGRAL. 固有运算的表示符号除了通常的数学表示符号之外,同时还有一套等价的字符串加左右句点的表示方法,这就和自定义运算的符号表示统一起来了。这样做的好处就是可以用符号串直接作为文字来表示运算的涵义,(例如上面的三个名称就可以用来表示求余,反号,求积分这三种运算),从而便于程序的写作和阅读。这是一个值得遵循的良好的写作风格。 ● 运用固有运算的组合给出的自定义运算的定义; 自定义运算的定义是通过函数用OPERATOR来完成的,具体的说明见有关过程的章节。 ● 自定义运算的算元集合的描述。数学上定义一个函数,必定要指出函数的定义域,同样一种自定义的运算也需要指定能够施加于其上的算元的范围,这里包括如下几种情况: · 定义在某个固有数据类型的真子集上; 如果对一个固有运算也做这样的限制,那么就把这个固有运算看成自定义运算了。 · 定义在不止一个固有数据类型上,例如数值型数据和字符型数据的某种组合上; 可以针对某个固有运算做这样的扩展,同样视之为自定义运算。 · 定义在派生数据类型上; 这样得首先定义该派生数据类型。 · 定义在上述任意情形的组合而成的集合上。 5.4.4数据类型的常量的表示形式 数据在程序当中的行为,除了以指定数据类型的变量形式出现之外,还有就是以常量形式出现,也就是给出某个数据类型的具体取值的形式。 因此对于数据类型的说明,还包括给出该数据类型的常量的书写语法。 【例5-4】 下面给出每一种数据类型的说明常量的例子: 例子 数据类型 例子的取值 345 INTEGER 345 713.2或7.132E2 REAL 713.2 (2.77,5.38) COMPLEX 2.77+5.38i .TRUE. LOGICAL TRUE “SPACE_A” CHARACTERSPACE_A SAMPLE(1.582,(3.2,5.5),”CHENG”) 派生类型 SAMPLE(1.582,3.2+5.5i,”CHENG”) 可以看出,对于数值型数据,直接给出常量数据,就可以了,而对于字符型数据则需要写在定界符里面,对于派生类型则需要遵循派生数据类型的说明语法。 当然一个数据类型里的常量同样具有种别参数的属性,只要给出的常量不是属于默认的种别,就需要给常量加上种别参数,而种别参数有两种情况: ● 一种是采用整数,由于不同的编译器对于这些整数的具体解释有可能是不同的,因此会妨碍程序的可移植性; ● 一种是采用命名常量来作为种别参数,那么只要这些命名常量一直被使用,就能保证对它的解释的一致性。 【例5-5】 类型 例子 INTEGER 2_SHORT REAL 3.14159267895632_QUAD COMPLEX (3.14159_HIGH, 56.2) LOGICAL .TRUE._BYTE CHARACTER CHINESE_”例子” 例子里SHORT,QUAD,HIGH,BYTE,CHINESE都是命名常量。 对于整型,实型,复型和逻辑型来说,种别参数写在数据的右边,以下划线隔开,而字符型则是写在数据的左边,同样以下划线隔开。 5.5数据的基本类型:固有数据类型 对于计算机来说,数据的意义无非就是要知道在存储空间为一个特定的数据划出多大的空间来装载它,然后才谈得上给每一个数据编制地址,从而随时可以对数据进行读入读出操作。 确定数据占用空间大小的自然方式就是统一地给一类数据指定固定的存储模式,这就是FORTRAN早期的做法,即整型,实型,逻辑型统一地用一个数值存储单位来存储,而双精度实型与复型则统一采用两个数值存储单位来存储,字符型数据则统一采用一个字符存储单位来存储。由于数值存储与字符存储具有不一样的情况,因此这两种存储单元的字节数大小不一样。 不过语言的进步,毋宁说是算法的进步,要求语言能够提供更加灵活的存储模式的可选择性,这就是FORTRAN90引进的种别参数,这样就扩充了固有数据类型的存储模式。同时为了使得程序能够与旧的标准兼容,一般采取在默认的情况下采取旧的存储模式,而需要扩充时,则额外加上种别参数。 数据的存储模式是通过对数据进行声明来指定的。详尽的声明语句的使用参见数据的声明,不过下面我们给出描述各个数据类型的四个基本属性的词法与句法,以备寻检。 5.5.1整型 何谓FORTRAN里的整数? 数学上的整数用整型数据来表示。而所谓整数,具体表示出来,在数学上一般的表示形式如下: 。 其中: i 是一个任意的整数。 s 表示正负符号(可以取+1或-1)。 l 是一个正整数,表示i的位数,即表示i需要多少个数字。 r 是一个大于1的正整数,表示i的进制的基数,即逢r进一位的意思。 wk 是一个小于r的非负整数,表示了i的每一位的值。 例如一个形如-41的整数,也可以表示为二进制形式-0101001,因为我们有: 要完全的描述整数i,显然s,l,r,wk(k=1,2,…l)这些数值都是必须提供的。 例如给出十进制整数-41,实际上也就是提供了 s=-1;l=2;r=10;w1=1;w2=4 这一套完整的信息。 不过如果我们的目的是描述一个取整数值的变量n的数据类型,显然s和wk(k=1,2,…l)都无须给出,而整型数据的分类都是围绕l和r来进行的。 【例5-6】 下面的带种别参数的整型数据的声明语句: INTEGER(4) i 实际上表示的是如下形式的整数: 也就是取l=31和r=2。 下面我们就给出说明整型数据的四个基本属性的方式与相关功能函数 1. 整型的名称 整型的名称就是INTEGER。也可以说就是声明整型数据类型的语句的关键词。 声明一个数据对象属于整型数据的基本语句句法为: INTEGER kind-parameter) ] :: ]entity-list 【例5-7】 以下这些声明语句主要是要说明数据项: INTEGER X INTEGER DIMENSION( , POINTER :: days, hours INTEGER(SHORT)RED_BALL INTEGER(2) POINTER :: k, limit INTEGER(1) DIMENSION(10) :: min 【例5-8】 以下这些声明语句主要是要说明数据的属性: INTEGER days, hours INTEGER(2) k, limit INTEGER(1) min DIMENSION days( , hours( , min (10) POINTER days, hours, k, limit 整型数据也可用于指出某个变量为整型也可以构成一个条件语句。 【例5-9】 INTEGER I, X READ (*,*) I IF (I) THEN X = 1 END IF 2. 整型数据的取值 取值为整数值的数据对象被定义为整型数据对象。 值得注意的是整型数据的取值范围,无论如何都只能是整数集合的一个真子集,因为计算机所能表示出来的整数的大小是受到一个有限数值的限制的。至于某一个具体的编译器能够表示的最大的数值是多少,并不是统一的,需要具体的依据编译器的约定,当然只有在我们需要考虑有可能取非常大的数值时,才会注意到系统的这方面的限制。 更具体地考虑一下,当我们要定义一个整型变量的时候,我们不止是需要考虑它是否可能取非常大的数值,更重要的是要在满足算法的数据精度要求与节约机器的内存空间之间取得某种折中。因为尽管语言标准只要求编译器提供整数的一种存储标准,但是现在一般的编译器都能提供多种存储模式,针对算法的具体情况,程序作者就可以选择不同的数据存储模式,来获得高效的程序。 FORTRAN语言可以很方便地描述数据对象的具体的存储模式,那就是种别参数,实际上对于FORTRAN 来说,每一个数据总是认为它不仅属于一个数据类型,进一步还属于该数据类型的某个种别,在种别参数没有表达出来的时候,就会赋予默认的种别参数值。 由于FORTRAN标准只是规范了固有数据类型的定义,对于每一个固有数据类型所定义的各个种别的具体存储模式,则一般由具体的编译系统来约定,因为给具有不同字长的数据规定适当的存储空间,得依据系统不同具体情况来确定。 FORTRAN提供了三个固有函数,可以用于查询具体的某个系统对整型数据的存储模式的设置: · KIND · RANGE · SELECTED_INT_KIND ● 固有函数KIND 这个函数能够给出任意属于固有数据类型的数据对象的种别参数 句法: result = KIND (x) 输入x,可以是任意属于固有数据类型的数据对象,如变量或常量。 输出为一个属于默认整型数据的标量,就是x的种别参数值。 【例5-10】 KIND (0)为默认整型的种别参数值; KIND (12)同样为默认整型的种别参数值; ● 固有查询函数RANGE 这个函数能够给出数据用十进制表示时的幂次范围。 一般句法为: result = RANGE (x) 输入x是数值型数据。 输出为一个属于默认整型数据的标量,表示数据用十进制表示时的幂次范围。 由于机器的存储模式是以二进制的位数为单位的,因此这个函数的作用就在于把二进 制的幂次范围转换为十进制的幂次范围。 对于一个整型变量,输出值为INT(LOG10( HUGE(x) ))。 对于一个实型或复型变量,输出值为INT(MIN (LOG10( HUGE(x) ), -LOG10( TINY(x) ))). 【例5-11】 如果变量X属于REAL(4),那么RANGE (X)的值为37. (因为HUGE(X) = (1 - 2-24) x 2128 TINY(X) = 2-126,而 ; )。 ● 固有转换函数SELECTED_INT_KIND 这个函数能够根据数据取值的十进制幂次范围,给出其种别参数值。 一般句法为: result = SELECTED_INT_KIND (l) 输入十进制最大幂次l。 输出满足-10l n 10l的变量n的种别参数值。 如果系统不支持相应的整型种别,就会返回数值-1。 如果该数据同时满足不止一个种别参数值,则取具有最小十进制幂次范围的种别参数 值。 由于很多情况下,我们只知道数据取值的十进制幂次范围,因此这个函数就提供了通 过十进制幂次范围来指定种别参数的方法。 【例5-12】 INTEGER (SELECTED_INT_KIND (6))X!等价于INTEGER(4)X,而X !的取值最大可以达到106,而最小 !可以达到10-6。 INTEGER (SELECTED_INT_KIND (5)) N,M !声明了变量N和M的取 !值最大可以达到105,而最小可以达 !到10-5。 i = SELECTED_INT_KIND(8) ! 返回4 i = SELECTED_INT_KIND(3) ! 返回 2 i = SELECTED_INT_KIND(10) ! 返回-1, 因为系统不提供如此高 !的精度。 3. 运算 施加于整型数据的运算包括: ● 一元固有算术运算:求反运算-;求同运算+。 ● 二元固有算术运算:加法运算+;减法运算-;乘法运算*;除法运算/;乘幂运算**。 ● 二元固有关系运算:大于.GT.或>;大于等于.GE.或=;小于.LT.或<;小于等于.LE. 或=;等于.EQ.或==;不等于.NE.或/=。 固有算术运算的结果仍然为整型数据,而固有关系运算的结果为默认种别类型的逻辑型数据。 4. 常量的表示形式 所谓常量,可以理解为取了具体的定值的数据对象,因此整型常量从形式上讲,就是一串数字,可能在前面(左端)加上正负号,也可能在后面(右端)加上下划线,然后跟一个种别参数。 整型字面常量的一般形式为(R403): n 其中: s表示正负号;如果取负号(-),则这个负号是不可缺的,如果取正号(+),则是可选 的。因此不带任何符号的数字串被默认为正数。 n表示数字(0到9),从左端开始第一个非0数字开始,它左边的任何0都会被忽略。 在默认情形下,这些数字都被认为是十进制表示形式的数值。 k是一个可选的种别参数,必须用下划线( _ )和表示数据的数字串区分开。 种别参数的一般语法形式为: digit-string scalar-integer-constant-name 即数字串,或者是取整型标量值的命名常量。 【例5-13】 1表示INTEGER(1); 2表示INTEGER(2); 4表示INTEGER(4); 8表示INTEGER(8); LONG SHORT 其中1,2,4,8同样属于默认种别的整型数字,而LONG和SHORT都是命名常量,它们的具体取值完全依赖于编译系统。 在默认情形下,整型常量总是被解释为十进制表示的数值,除了这种默认的十进制表达形式之外,还可以在DATA语句当中初始化表示为其他系统许可的进制形式。 在FORTRAN语言标准里面,规定了十进制之外的三种进制形式: ● 二进制常量形式(R408): B ’ digit …’ B “ digit …” 其中的数字只能是0或1. 即二进制常量表示为以字母B开头,后跟用一对撇号或引号括起来的数字串,而且每 个数字不是0就是l。 ● 八进制常量形式(R409): O ’ digit …’ O “ digit …” 其中的数字只能是0到7. 八进制常量表示为以字母O开头,后跟用一对撇号或引号括起来的数字串,而且每个 数字是0到7之间的一个数字。 ● 十六进制常量形式(R410): Z ’ digit …’ Z “ digit …” 其中的数字只能是0到9,和A到F这五个字母,用来表示10到15. 十六进制常量表示为以字母z开头,后服用一对撇号或引号括起来的数字或字母的串, 而且每个数字是数字0到9或字母A到F之一。 这里定义的二进制、八进制和十六进制的字面常量形式只能用于DATA语句中。 如果要在CVF系统里面使用非十进制来表示数值,除了属于FORTRAN标准的二进制,八进制,十六进制三种额外的进制形式之外,还可以使用如下的语法形式表示更多的进制形式: #] nnn... 其中: base是从2到36的任意整数。这表明CVF可以使用从2进制一直到36进制来表 示整数。 而如果base省略了,但是给出了符号#,那么表示后面的整型数据被看成16进制, 如果base 和#都被省略了,那么后面的整型数据就被看成默认的10进制。 为什么取能够表示的最大进制是36呢,因为一般从英文26字母里面来取得 符号来表示0到9之外的数字表示符号,一般的约定就是按照字典顺序,例 如如果使用11进制,那么A表示10,如果使用36进制,那么A表示10,B 表示11,C表示12等等一直到Z表示35。 这种表示方法里面,字母的大小写是不加区别的。 【例5-14】 下面的7个变量所赋予的整型数值都是表示十进制的3,994,575: I = 2#1111001111001111001111 m = 7#45644664 J = +8#17171717 K = #3CF3CF n = +17#2DE110 L = 3994575 index = 36#2DM8F 【例5-15】 这些都是正确的整型常量; 0 -112 +43212 62_2 1992110235764803_8 31_SHORT 9999999999999999999_LONG 【例5-16】 这些都是错误的整型常量: 9999999999999999999!对于默认的种别参数来说,这个数太大了。 3.14 !不允许出现小数点。 32,767 !不允许出现逗号。 33_3 !3不是一个有定义的种别参数。 【例5-17】 下面都是非十进制形式的整型常量: FORTRAN赋值 十进制值 十六进制值 LOGICAL(1)X INTEGER(1)X X = -128 -128 Z'80' X = 127 127 Z'7F' X = 255 -1 Z'FF' X = 255 255 Z'FF' X = -32768 -32768 Z'8000' X = 32767 32767 Z'7FFF' X = 65535 -1 Z'FFFF' 数字串后的下划线和种别参数是可选项。如果省略,就被认为是默认整型,这时系统默认的种别参数值就是固有查询函数KIND(0)的结果。 SHORT是命名常量,且具有常量值,这个值必须是非负的,而且与一种表示方法相对应。 非字面常量的数值数据都有一个名字,对于名字要说明其类型,具体说明的方法参见第6章。 5.5.2实型 何谓FORTRAN里的实数? 计算机显然也不能表示所有的实数,因为计算机只能表示有限形式的数值,并且表达一个数值所使用的数字数目也是受到限制的。 对于计算机来说,只能用下面的形式来表示实数: 。 其中: ● x 是一个实数。 ● s 表示正负符号(可以取+1或-1)。 ● r 是一个大于1的正整数,表示x的进制的基数,即逢r进一位的意思。 ● l 是一个大于1的正整数,表示x的位数,即表示x需要多少个浮点数字来表示。 显然这依赖于系统对r的选择。 ● wk是一个小于r的非负整数,表示了x的每一位的值,而且w1不能是0。 ● e 为幂次范围emin 到emax 的一个整数。 ● 如果x = 0,那么wk 和e定义为0。 从实型数据的数学表达式可以看出,如果要对实型数据的存储模式进行分类的话,在默认取十进制的情况下,就一定是围绕l和e来进行的,例如单精度实型(REAL(4))的定义如下: x=0,或者 ● 一个实型数据值最多可以用多少个浮点数字来表示,依赖于系统的约定,下表5-7 是CVF在不同环境里的相应约定。 表5-7实型数据值的浮点数字 IEEE S_floating 24 Compaq (以前是DIGITAL) VAX F_floating 1 24 IEEE T_floating 53 Compaq VAX D_floating 1 53 2 Compaq VAX G_floating 1 53 1仅限于VMS 2VAX D_floating的内存格式是56位浮点数,不过实际只有53位用于计算,因此Compaq Fortran认为它是53位。 ● 幂次范围emin 到emax同样依赖于系统的约定,下表5-8是CVF在不同环境里的相 应约定: 表5-8幂次范围的浮点数字 emin emax IEEE S_floating -125 128 Compaq VAX F_floating 1 -127 127 IEEE T_floating -1021 1024 Compaq VAX D_floating 1 -127 127 Compaq VAX G_floating 1 -1023 1023 1VMS only 1. 名称 实型数据的名称是REAL,不过实型数据还有一个种别,拥有单独的名称,即DOUBLE PRECISION,这属于历史的遗留,是在FORTRAN没有出现种别参数的时候所增加的双精度实型数据的名称,当然使用种别参数是完全可以代替使用这个名称。 实型数据的声明的语法形式如下: REAL n) ] entity-list DOUBLE PRECISION entity-list 其中: n取值为种别参数4, 8, 或16。 种别参数16只出现在OpenVMS, Tru64 UNIX, 和Linux环境当中。 ● 种别参数是可选的,如果使用了种别参数,那么给出的实型数据对象就属于相应 的种别;如果没有使用种别参数,那么给出的实型数据对象就属于默认实型。 ● DOUBLE PRECISION实际上就是REAL(8)。如果使用DOUBLE PRECISION就不能再使用种别参数。 ● 如果要改变实型的默认指定。可以使用编译选项/real_size:size。 给出实型数据对象的名称的例子如下。 【例5-18】 这些声明语句主要说明数据项。 REAL (KIND = high), OPTIONAL :: testval REAL, SAVE :: a(10), b(20,30) 这些声明语句主要说明数据属性。 REAL (KIND = high) testval REAL a(10), b(20,30) OPTIONAL testval SAVE a, b 2. 实型的取值 鉴于FORTRAN的所谓实型只是有限位有理数的集合,因此同样有必要依据精度对实型数据进行分类,以避免给任意的实型数据安排同一个存储模式,从而有可能导致非常大的内存空间的浪费。 FORTRAN标准规定了实型至少要有两种精度形式,一种是默认实型,一种是在默认实型的基础上精度加倍,得到双精度实型。 在FORTRAN90以后,为了与早期的语言版本相容,保留了说明默认双精度实型的关键词DOUBLE PRECISION,不过可以使用REAL加双精度种别参数(REAL(8))的形式,或直接使用指数符D加指数的形式代替。 和整型类似,FORTRAN设置了四个固有函数,可以用来查询具体的某个系统对实型数据在精度方面的约定。这四个固有函数为: · KIND · PRECISION · RANGE · SELECTED_REAL_KIND ● 固有函数KIND 这个函数用于查询变量的种别参数。由于实型数据的双精度型被认为是一种单独的实 型数据种别,因此针对它的种别参数查询函数使用KIND(0.0D0),而对于默认实型则 使用KIND(0.0)。 句法: result = KIND (x) 输入x可以是任何属于固有数据类型的数据对象。 函数结果为一个属于默认整型的标量,表示x的种别参数。 【例5-19】 KIND (0.0)为默认实型的种别参数。 ● 固有函数PRECISION 这个函数给出实数的有限十进制小数表示或近似表示的十进制精度。 函数输出为一个正整数p,表示数据取值至少含有p个有效数。 句法: result = PRECISION (x) 输入x必须是实型或复型,可以取值为标量或数组。 函数结果为一个属于默认整型的数值,等于INT((DIGITS(x) - 1) * LOG10(RADIX(x)))。 如果 RADIX(x)等于10的某个整数次幂,则结果加1。 如果X是一个REAL(4)值,那么PRECISION(X)等于6。因为: INT ((24-1) * LOG10 (2.)) = INT (6.92...)。 ● 固有函数RANGE 参见应用于整型数据的该函数的说明。 ● 固有函数SELECTED_REAL_KIND 这个函数能够根据数据的十进制精度和十进制幂次范围,给出其种别参数值。 使用这个函数,就可以在只了解数据的十进制表示的精度与幂次范围要求的情况下, 在数据说明当中指定其种别参数。 一般形式: result = SELECTED_REAL_KIND ( ) 其中: p作为可选输入,属于默认整型的标量; r作为可选输入,属于默认整型的标量。 这两个可选量必须至少出现一个。 输入表示十进制精度的p或(和)十进制最大幂次r。 输出具有十进制精度的p或(和)满足-10r n 10r的变量n的种别参数值。 显然其中的p就是函数PRECISION返回的结果,r就是函数RANGE返回的结果。 如果系统不支持相应的实型种别,就会出现如下结果: ● 如果系统不支持相应精度,则得到-1; ● 如果系统不支持相应幂次范围,则得到-2; ● 如果系统两者都不支持,则得到-3。 如果该数据同时满足不止一个种别参数值,则取具有最小精度的种别参数值。 【例5-20】 REAL (SELECTED_REAL_KIND (5))X!表示X具有至少5位有效数,并且 !没有指定幂次范围。 REAL (SELECTED_REAL_KIND (6, 50))X!表示X具有至少6位有效数, !并且指定幂次范围为1050到10-50。 REAL (SELECTED_REAL_KIND (6, 70))!等价于REAL (8) i = SELECTED_REAL_KIND(r=200)! returns 8 i = SELECTED_REAL_KIND(13) ! returns 8 5.5.3实型数据的运算 施加于实型数据的运算包括: ● 一元固有算术运算:求反运算-;求同运算+。 ● 二元固有算术运算:加法运算+;减法运算-;乘法运算*;除法运算/;乘幂运算**。 ● 二元固有关系运算:大于.GT.或>;大于等于.GE.或=;小于.LT.或<;小于等于.LE. 或=;等于.EQ.或==;不等于.NE.或/=。 对实型数据施加固有算术运算的结果总是实型数据,即使二元运算的某个运算元为整型,结果仍然被视为实型。 固有关系运算的结果为属于默认种别类型的逻辑型数据。 5.5.4实型常量的形式 FORTRAN可以写出有限位的整数,也可以写出有限位的小数,然后可以再加上正负号,到此为止,实数里面的非有限小数就只能用有限小数来近似了。 带符号的实型字面常量的写法(R412)有以下几种形式: 不含指数部分的实常量: n 含指数部分的实常量: n E nn... n D nn... n Q nn... 413 其中: s表示正负号,如果是负数必须有(-),如果是正数,(+)可选。 n表示从0到9的数字(默认实型的情况下)。如果不含指数部分,则其中必须包含 小数点。在整数与小数部分的数字串遵循R402,指数部分的数字串遵循R416和 R401。 k是种别参数:REAL(4)是4, REAL(8)是8,种别参数前面必须有下划线( _ )。 E,D表示指数符(R415)。后接指数表示10的幂次,例如1.0E6 表示1.0 * 10**6。 ● FORTRAN只能表示实数的有限小数部分,并且按十进制科学记数法解释,即有 效数乘以10的指数幂。书写的有效数字个数可以多于系统约定的用来近似表示该 常量值的位数。 ● 在一个实型常量里同时出现种别参数与指数时,使用E。 ● 指数符使用D,表示常量为双精度实型数据(REAL(8)),由于这种精度的实型是单 独定义的,因此不能再带有种别参数。它的种别参数值是固有查询函数 KIND(0.0D0)的结果,而且约定其表示方法的十进制精度要大于默认实型的精度。 ● 带种别参数的实型常量受到其种别参数的约定。 ● 既不带指数,也没有种别参数的实型常量为默认的单精度常量,即REAL(4),此 时种别参数值是固有查询函数KIND(0.0)的结果。 ● 应用种别参数来规定所谓的有效数的个数时,数字串里第一个非0数字左边所有 的0全被忽略。例如00.0000562389里面,5之前所有的0都不计入有效数。 ● 如果实型常量不含指数部分,则在数字串部分必须包含小数点,如果有效数为不 带小数点的数字串,即整型字面常量时,必须出现指数符及指数,否则就变为整 型数据了;如果含指数部分,则小数点不是必须的,而指数部分的数字串里不能 出现小数点。 ● 在默认情况下,指数符E指单精度实型(REAL(4)),而如果带有种别参数,则依 种别参数,例如-7.E2_8为双精度实型常量,也可以写为-7.D2。 ● 如果实型常量的数字串当中出现指数符,那么指数数字串不能省略,但可以是0。 ● 种别参数可以使用HIGH,LOW,QUAD等命名常量,其具体取值由系统约定。 单精度实型(REAL(4))的定义如下: x=0,或者 【例5-21】 下面都是合法的实型常量: -12.78 +3.73E2 -13.6E_4 10.93E7_QUAD .96D2 3.14159_4 -.00127 +5.0E3 2E-3_4 123456789D+5 123456789E+5_8 +2.7843D00 2E200_8 123456789Q4000 1.23Q-400 【例5-22】 下面都是不合法的实型常量: 1,234,567.! 数字串当中不能含有逗号 325E-47 !对于REAL来说,这个数太小;但是属于合法的DOUBLE PRECISION !常量。 -47.E47 !对于REAL来说,这个数太大;但是属于合法的DOUBLE PRECISION常 !量。 625._6 !6不是合法的实型种别参数。 100 !在不含指数部分的情况下,没有小数点,故只能是合法的整型常量。 $25.00!不允许含特许字符。 -.25D0_2 !2不是合法的实型种别参数。 +2.7182812846182 !没写指数符D,不过在忽略7个有效数之外的数字之后,可 !以视为合法的单精度实型常量。 1234567890D45 !这个数对于D_floating格式来说太大;但属于合法的G_floating和 !T_floating格式。 123456789.D400 !这个数对于双精度格式来说太大。 123456789.D-400 !这个数对于双精度格式来说太小。 1.Q5000 !这个数对于三倍精度格式来说太大。 1.Q-5000 !这个数对于三倍精度格式来说太小。 5.6复型 两个实型数据就构成一个复型数据,因为在数学上,一个复数本质上就是一个实数二元组。 和实型值集合只是实数的一个子集一样,复型值集合只是数学上复数的近似表示的一个子集。复型的值运用实值的有序对来表示,第一个实值称为实部,第二个实值称为虚部。 复型数据的取值的写法如下: (c,c) 其中逗号与括号不可或缺,而c可以取: ● 整型常量或REAL(4)常量,得到COMPLEX(4)常量; ● 整型常量,REAL(4)常量,或DOUBLE PRECISION (REAL(8))常量,得到COMPLEX(8)常量; 正是由于复型本质上由实型组合成,因此复型数据的存储模式依据实型而定。 5.6.1 名称 复型的名称为COMPLEX,说明复型数据类型的句法为: COMPLEX kind-parameter)] entity-list 其中: n为种别参数4 or 8,同于实型。 ● 由于复型数据种别参数是应用于实部和虚部两个实型数据的,如果出现种别参 数,该种别参数指定实部和虚部两个实型数据的种别;如果不带种别参数,则称 此复型数据为默认复型,即实部和虚部都属于默认实型。 【例5-23】 COMPLEX ch COMPLEX (KIND=4),PRIVATE :: zz, yy !等价于COMPLEX*8 zz, yy COMPLEX(8) ax, by! 等价于COMPLEX*16 ax, by COMPLEX (kind(4)) y(10) complex (kind=8)x, z(10) 5.6.2 复型数据的取值 复型数据对象的取值的种别完全由实型而来,根据两个实型分量的组合,可以分为如下情形: ● 两个实型分量都是默认实型,则相应复型也称为默认复型; ● 两个实型分量都是双精度实型,则相应复型也称为双精度复型; ● 给复型对象指定一个种别参数,意味着同时给它的两个实型分量指定了同样的种别参数; ● 两个分量都是整型对象,则都转换成默认实型,因而认为是默认复型的; ● 其中一个分量是整型对象,另一个分量是实型对象,则将整型的分量转换成与另一实 型分量相同种别的实型对象,其种别参数就是实型分量的种别参数。 ● 如果两个实型分量的种别参数不同,则具有精度高的分量的种别参数就是复型的种别 参数,并且把另一分量转换为这个精度高的种别形式。 这里显然符合FORTRAN对存储模式一致性处理的一般原则,即当需要统一存储具有不同存储模式的几种数据时,就一律采用需要存储空间最大的那种模式。 应用于复型的固有查询函数与实型相同,其中有关KIND,RANGE,PRECISION的说明参见实型的相应说明。 SELECTED_REAL_KIND也可以直接用于复型对象,用法与功能都与用于实型对象一致。 【例5-24】 COMPLEX (SELECTED_REAL_KIND(5,50))CY !表示复型变量CY至少具有 !5位有效数,同时取值范围 !可达10-50到1050。 5.6.3复型的运算 施加于复型数据的运算包括: ● 一元固有算术运算:求反运算-;求同运算+。 ● 二元固有算术运算:加法运算+;减法运算-;乘法运算*;除法运算/;乘幂运算**。 ● 二元固有关系运算:大于.GT.或>;大于等于.GE.或=;小于.LT.或<;小于等于.LE. 或=;等于.EQ.或==;不等于.NE.或/=。 这里的算术运算就是定义在复数集合上算术运算。 对复型数据施加固有算术运算的结果总是复型数据,即使二元运算的某个运算元为整型,或实型,结果仍然被视为复型。 固有关系运算的结果为属于默认种别类型的逻辑型数据。 5.6.4 复型常量的形式 复型字面常量的句法(R417)为: (real-part,imaginary-part) 其中实部和虚部可以是带号整型字面常量(R403)与带号实型字面常量(R412)的任意组合。 复型数据对象的种别如何确定,参见上面的6.4.5.2节 【例5-25】 下面是合法的复型常量: (1,3) !默认复型 (7.0,-7.0) !默认复型 (0,3.6E5) !默认复型 (0.5_4,+8.6E29)!如果假定种别参数值4比8所表示的实数精度低,则该复型数据具有 !种别参数值8 【例5-26】 下面是不合法的复型常量: (1.23,) !漏写了作为虚部的整型或单精度实型常量值。 (1.0, 2H12) !不能用Hollerith常量 (,1.23Q0) !漏写了实部 (1.7039E0,-1.7039D0)! 2个常量都不属于REAL(16),但属于合法的双精度常量。 5.7 逻辑型 用来作为任何判断的结果的变量或常量,就是逻辑型数据对象。之所以把它单独归结为一种数据类型,是因为它的取值只有是和否两个,在存储方面可以非常简单,为了充分显示这个特点,有必要单独作为一种数据的类型。 5.7.1名称 逻辑型数据对象的名称为LOGICAL,它的声明语句的句法为: LOGICAL kind-parameter n)] entity-list 其中n可取种别参数1, 2, 4, 或8。 种别参数是可选项,如果没有使用种别参数,则为默认逻辑型。 【例5-27】 下面的声明语句主要说明逻辑型对象。 LOGICAL, ALLOCATABLE :: flag1, flag2 LOGICAL (KIND = byte), SAVE :: doit, don’t 【例5-28】 下面的声明语句主要说明对象的属性。 LOGICAL flag1, flag2 LOGICAL (KIND = byte) doit, dont ALLOCATABLE flag1, flag2 SAVE doit, dont 5.7.2逻辑型数据取值 逻辑型数据取值只有两个可能,即表示真的(.TRUE.)和表示假的(.FALSE.)的两个值,它们后面可以跟可选的下划线与种别参数。 一个系统至少要提供一种表示方法作为默认逻辑型,在默认的情形下,一个逻辑型值需要和默认实型值一样大的存储空间,它的表示方法就是省略可选的下划线和种别参数,此时种别参数值可以通过固有查询函数KIND(.FALSE.)获得。 大多数系统还提供其他存储模式,例如一个逻辑型值采用一个bit或一个byte来存储,具体的种别参数的约定依赖于具体的系统。 至于查询数值型数据的种别参数的固有函数SELECTED_INT_KIND和SELECTED_REAL_KIND对于逻辑型没有相应的函数。 5.7.3逻辑型数据的运算 施加于逻辑型对象的运算包括: ● 一元固有运算:非(.NOT.) ● 二元固有运算:与运算(.AND.);或运算(.OR.);逻辑等值运算(.EQV.);逻辑不 等值运算(.NEQV.)。 对逻辑型值施加上面的运算,仍然得到逻辑型值。 5.7.4逻辑型常量 逻辑型常量只有两个,它们也可以附加系统约定的种别参数,句法为: .TRUE. .FALSE. 其中k为可选的种别参数: LOGICAL(1)是1;LOGICAL(2)是2;LOGICAL(4)是4;LOGICAL(8)是8。注意种别参数前面一定要有下划线( _ )。这里的种别参数是与实型数据的种别参数保持一致的。 【例5-29】 .TRUE._BIT .FALSE._LONG !这里的LONG必须是已说明的有名常量,并且具有非负的整常量值。 !其存储模式必须是已经说明的. 5.8字符型 字符型数据对象在系统认可的字符集当中取值,其唯一形式就是由有限个属于系统字符集当中的字符构成字符串。 5.8.1名称 字符型数据的名词为CHARACTER,字符型对象的基本声明句法为: CHARACTER length-parameter kind-parameter])] entity-list 可以看到其中各种可选项的组合情况比较多,为清晰起见,分别写出如下: CHARACTER CHARACTER( n) CHARACTER( len) CHARACTER( len n]) CHARACTER(KIND=n ) CHARACTER*len 其中n取种别参数值1; len是字符长度值,不是种别参数值,一般取正整数值,也可能取星号或特定表达式。 参见表达式 有关CHARACTER声明语句,还参见数据声明 【例5-30】 下面是不同形式的字符型数据的声明语句。 CHARACTER (70)PROJECT CHARACTER (LEN=30, KIND=GERMAN)TRANSFORMATION CHARACTER (LEN=25,KIND=GREEK),DIMENSION(11)::Z1 5.8.2 字符型的取值 字符型能取的值的集合就是所有有限字符串的集合。 所谓一个有限字符串就是一个字符按照文字方式排列的有限序列,如果从左到右对每一个字符进行编号,得到1,2,3.……,n,那么n就是该字符串的长度,即该字符串所有字符(包括重复字符)的数目。 决定一个字符串变量的值的存储空间的因素有两个: ● 字符串的长度; ● 字符的存储单位的大小。 因此这两个因素都是在描述字符型对象时,系统需要知道的参数。 字符长度作为一种种别参数,它的取值分为如下情形: ● 可以等于0,这时称长度等于零的字符串为空串; ● 在没有指定LEN值的时候,默认长度值为1; ● 根据字符数目,取大于0的整数。当然这个整数存在上界,由系统给出约定。 系统至少需提供一种定义字符型数据值的集合的表示方法,一般是会有几种方法,每种方法用不同的种别参数来区分。 种别参数的具体取值依赖于具体的系统的约定。 字符型数据还有一个需要仔细加以约定的属性是字符集的排序问题。 由于字符型数据的运算除了串联之外,还有一类关系运算,需要依据对字符集的字符顺序的规定。一般原则如下: ● 首先出于可移植性的要求,最好把字符集限制在FORTRAN的基本字符集。 ● 空格优先于所有字母与数字; ● 字母按照英文字母表排序; ● 数字按照0,1,2,…,9的顺序排序; ● 字母与数字不能间杂; ● 除了空格,对其他特殊字符与下划线的位置都没有约束; ● 语言标准不要求系统支持ASCII编码,但要求提供固有函数,ACHAR和IACHAR, 用来在系统编码和ASCII编码之间进行转换。 ● 固有函数LGT,LGE,LLE,LLT可用来基于ASCII顺序进行排序比较运算。 5.8.3运算 可以施加于字符型数据的运算包括: ● 串联运算:运算符是//,针对具有相同种别参数的字符型数据而定义。其运算结果仍 然为一个保持种别参数不变的字符型数据。 ● 关系运算: ● 大于,其运算符为.GT.,或>; ● 大于等于,其运算符为.GE.,或=; ● 小于,其运算符为.LT.,或<; ● 小于等于,其运算符为.LE.,或=; ● 等于,其运算符为.EQ.,或==; ● 不等于,其运算符为.NE.,或/=。 关系运算的结果为逻辑型数据,即只能取.TRUE.和.FALSE.两个值之一。 5.8.4字符型数据常量 字符字面常量用可选的种别参数,然后是下划线后跟用撇号或引号为界的,由可表示字符组成的字符串来表示。字符型字面常量的构成句法(R420)如下: ' ' " " 其中: k为可选种别参数,默认情形为1,后接下划线( _ )。 注意字符型数据的种别参数置于常量前面(左边)。 ch 为ASCII字符,或者说系统可表示字符。 前一个撇号或引号称为前定界符,后一个称为尾定界符,它们统称为定界符。 ● 字符型常量的值限制在定界符之间,而定界符本身不属于字符串。 ● 定界符之间的任意字符,包括空格和Tab键都属于字符串,不过对于控制符得参 考系统的约定。 ● 字符串当中可以包含定界符本身,不过就只能看成字符,不能看成是字符型常量 的定界符。当用引号作定界符时,则用一个撇号来表示该字符,当用撇号作定界 符时,则用两个连续的撇号来表示,且其间不夹有空格字符,此时两个连续的撇 号计作一个字符。对字符字面值中的引号也类似地处理 ● 长度为0的字符常量,也就是空串,由两个连续的同样的定界符表示,中间不夹 空格。 ● 如果前定界符前不出现种别参数和下划线,则该字符常量为默认字符型。 ● 对于可表示字符的约定与源程序形式有关。在固定源码形式中,它依赖于系统字 符集,它是除一部分(甚至全部)控制字符外的所有字符;在自由源码形式中,它可以 包含系统的附加图形字符。 【例5-31】 下面是合法的字符常量。 "WHAT KIND TYPE? " 'TODAY''S DATE IS: ' "The average is: " '' “” ‘I’’m a student’ “I’m a student” CHINESE_”汉字的种别参数CHINESE作为命名常量已经被定义” 【例5-32】 下面是不合法的字符常量: 'HEADINGS !缺少尾定界符 'Map Number:" !定界符的首尾不配套 Re:科学计算的语言------Fortran95 第6章 构造数据 固有数据类型只是描述了问题当中出现的基本数据形式,但在实际的计算当中,计算对象往往并不只是限于那些固有数据类型,而是一些数据结构。 例如: 线性数学问题里面的计算对象很少是单纯的标量,而是诸如行列式,向量这样的由 一系列标量组成的数据结构。 甚至在计算过程中,还需要以这种数据结构的某个特定部分作为计算对象,例如行 列式或向量的单独的行与列,或对角线,部分块等 又例如: 样品的属性值可以是多方面的,例如在测量一个导体样品的交流输运性质时,需要考虑的物理量包括电流,电压,相位等,如果以每个样品的属性值作为数据对象,那么这种数据对象就至少包括3个成员:电流,电压,相位。它们的取值的类型不同,精度要求也可能不同,这3个数值同样构成一个数据结构。 因此,进一步FORTRAN 95需要以固有数据对象为基础,能够构造一定的数据结构,从而能够作到基于数据结构进行运算。 本章讨论的就是FORTRAN 95构造数据结构的几种方式: ● 派生数据类型; ● 数据结构的子对象; ● 数据结构的成员; ● 数组; ● 指针。 特别的,我们首先讨论FORTRAN 95关于数据对象的一个基本分类:变量与常量。它们是数据在程序当中的2种基本行为方式,是我们理解FORTRAN 95语言范畴的关键。 6.1数据的2种基本行为—变量与常量 首先我们给出几个抽象的基本概念,在这里只是给出大意而不作更加形式化的讨论,完全只是为了后面行文的方便。读者不用深究它们的精确定义,只需要在后面行文当中遇到这些概念时,知道回到这里稍加体会即可。 ● 数据对象: 计算过程当中,任何充当计算对象的数据内容,都称为数据对象,是最一般意义上 的数据个体,今后提到数据对象,我们可以理解为一个固有数据类型,一个属于某 固有数据类型的变量,一个常量,一个标量,一个数据结构,一个数据结构的成员 或子对象,表达式求值的结果,或函数引用的执行结果(即函数值)。数据对象有数 据类型(固有的或派生的),可以有数据值。可以是一个数据的集合,集合的元素 数目称为秩,每个数据对象有一个秩。它可以是一个标量或是一个数组。有名数 据对象的类型可以显式或隐式地规定。 ● 子对象: 子对象是某些有名对象的一部分,可由程序的其他部分引用和独立地定义。包括数据组的部分,字符串的部分(子串)和结构的部分(成员)。作为子对象的来源的数据对象称为该子对象的父对象。子对象只能用子对象描述符来引用。变量的子对象是变量。 ● 标量: 任何单一的数据对象,无论它是属于固有数据类型还是属于派生数据类型,就称为标量。 ● 数组: 一个标量的集合,如果其中每一个元素都具有相同的数据类型,相同的种别参数,那么无论它们组织成什么形式,例如行,列,块,甚至更高维度,都把这种集合称为数组。 ● 派生数据类型: 一个标量的集合,如果其中元素的种别参数不同,或者数据类型不同,那么把这个 集合称为结构,或派生数据类型。 从程序运行这么一个动态过程的角度来看,特别是基于下面的基本计算模式: 输入数据 程序运行 输出数据 数据可以有2种基本行为方式: ● 数据对象在整个程序运行过程当中保持不变; ● 数据对象在整个程序运行过程当中发生变化。 保持不变的称为常量,发生变化的称为变量。 FORTRAN 95中常量与变量,标量与数组这些概念与早期的FORTRAN版本有微妙的差异。请熟悉FORTRAN77的读者注意。 ● 常量有两种,一种是字面常量,早期的FORTRAN版本称为常量;另一种是命名常量,早期的FORTRAN版本称为常数符号名,显然新的名称突出表达了该数据作为常量的特点,它与字面常量的最大差别在于有一个名字。这个名字可以在它的定义作用域当中任意引用。它们的共同点在于,它们的值在可执行程序执行期间是不变的。 ● 字面常量是标量,根据它的字面的句法构成来表明其数据类型,种别参数以及值。在一个可执行程序中,所有具有相同形式的字面常量具有相同的值。命名常量则是一个与具有PARAMETBR属性的名字相结合的常量。它的数据类型,种别参数和取值,都需要预先定义,而一旦定义之后,在其定义的作用域内都不会改变,而且这个名称也必须接受唯一解释的。 ● 变量的值即使用DATA语句给以初始化也能在程序运行过程当中发生变化。而FORTRAN 95的变量包括了早期FORTRAN标准里面所谓的变量和数组。具有DIMENSION属性的名字在FORTRAN 95标准里面也称为变量,属于数组。 ● 数组是标量的有序集合,它在FORTRAN 95标准当中有许多新的扩充,将在后面作详细讨论。 在程序运行过程当中,对数据的使用首先必须是定义,然后才能是引用,所谓定义和引用的差别在于: ● 对于一个变量,当程序中确定其值时称为定义,而使用其值时称为引用,已经定义了的变量可以重定义或者回复为无定义。 例如,在程序单元中使用DATA语句使得一个变量具有初值,在程序执行过程中又可以对它赋值而得到重定义;如果该变量不具有SAVE属性的话,当该程序单元执行RETURN语句或END语句后,它就回复为无定义状态。 ● 对于一个常量,它的值可以被引用但不能被重定义。命名常量虽然出现的形式是一个名字,而不是一个具体的值,但它只是常量的一个便于引用的标记。 【例6-1】 X = 0 N = X+1 在第一个语句当中,变量X被赋值为常量值0,这个语句的执行与X是否在此之前被定义为其他值无关,总之此时X得到一个定义,此后只要X没有被重定义,它就能用值0被程序引用。 在第二个语句当中,X被引用,并且在执行加1的运算之后,得到的值被赋值给N。 在第一个语句当中的X和在第二个语句当中的N都不是被引用,而是被定义,而在第二个语句当中的X属于被引用。 变量(variable)的句法形式(R601)为; scalar-variable-name array- variable-name subobject 其中子对象(subobject)的句法形式(R602)为: array-element array-section structure-component substring 变量可以是任意的数据类型,在一定的上下文当中,变量也必须取一定的数据类型。 常量父对象的子对象不可能是变量。 6.2数据的结构----派生数据类型 五个固有数据类型已经能够作到描述,或者近似描述整数,实数,复数,逻辑值,字符串这五类常用数据对象。 考虑一下FORTRAN建立这五种相对独立的数据类型的出发点,是有助于我们进一步理解语言的数据描述这个重要任务的。 首先考虑整数。 似乎整数是一切计算的基本出发点,这里是没有省略的余地的,想像一下,没有了整数,我们还能做什么计算呢?别忘了Kronecker的名言:“上帝创造了自然数,其余都是人造的”。 然后就是实数。 由于FORTRAN本质上只能表示有限位有理数,因此实际上也可以通过整数的除法来得到实型数据的近似表示,比方说用一个整数二元组(37,71)来表示37/71这个有理数,相应的也可以用来近似地表示实数,我们可以来衡量一下这样做的利弊: ● 好处就是可以不用建立实型数据类型,也就是不用引入小数点,减少了一个数据类型,也就简化了FORTRAN语言的实现; ● 但同时坏处就是把麻烦留给了程序作者,在处理涉及实数的问题时,肯定会因为不能直接表达实数而增加算法的复杂性,甚至在语言本身,也有可能不得不增加一些函数之类的语言成分,用来解决由此产生的一些问题。 所以FORTRAN的解决方案就是直接引入实数的有限有理小数表示(实际上一般的程序语言都是这样做的),对于语言本身来说,无非就是增加了对小数点的语义解释,增加了对浮点数值的多种存储模式(FORTRAN使用种别参数来进行分类)的建立等,由此而可以在语言当中自然的使用有限有理小数所表示的实数。 再来分析一下复数。 本质上复数就是一个实数的二元组,而且这个二元组与复数是一一对应的,因此可以很完美的通过运用二元实数组来代替复数,这也是大多数程序语言的处理方式。但是FORTRAN的选择是建立一个独立的复型数据类型,尽管得为这个独立的数据类型定义相应的运算,但是由于FORTRAN主要就是面向科学计算的语言,因此FORTRAN能够直接写出复数,成为FORTRAN的一个优点,极大地方便了程序作者。 由此可见,建立一个数据类型的主要功能就是面向问题,以能够直接描述算法为目的。 那么我们是否有可能从实际问题当中归纳出一些数据类型,然后可以期望这些数据类型就可以满足任何问题的需要呢? 显然是不可能的。 一方面,程序语言必须能够精确描述任何需要用它来解决的问题当中出现的数据;另一方面我们不可能给每一个新问题当中出现的独特的数据结构形式规定一种相应的数据类型,唯一的解决方法,就是在FORTRAN里面约定一种语法机制,可以依据这种语法机制来描述与问题要求相适应的数据类型。反过来只要是依据这个语法机制构造出来的语言结构都能被FORTRAN编译器辨认为一种数据类型,从而知道按照什么存储模式来存储该数据,也知道应该对该种数据施加什么运算。 这就是FORTRAN从90版标准以来设置派生数据类型的出发点。 鉴于固有数据类型被认为是真实世界最基本的数据类型,任何的数据对象,或者就是属于某种固有数据类型,或者就是可以由一系列固有数据描述,因此构造派生数据类型的基本思想就是认为一个派生数据等于一系列成员的集合,这个集合构成一种数据结构,而每一个成员或者仍然属于一个派生数据类型,或者就是属于某种固有数据类型。这样对一个派生数据类型的描述,就归结到对它的成员的描述,另外再加上给出该数据类型的名称,并可选择性地描述其他属性。 这样得到的所谓派生数据类型,和五种固有数据类型不同的地方主要在于: ● 每一种固有数据类型都拥有一个特定的名称,用来作为数据声明的关键词;而派生数据类型则需要程序作者自己首先定义一个名称,然后才能在后面作为关键词进行声明。 ● 每一种固有数据类型都已经定义好了自己的常量书写方式;而派生数据类型则依据其成员的常量形式来确定自己的常量的形式。 ● 每一种固有数据类型都已经定义好了自己的特定的运算;而派生数据类型则需要程序作者自己预先依据实际问题的需要来定义好相应的运算。 ● 每一种固有数据类型都已经定义好了自己的取值范围以及相应的多种存储模式;而派生数据类型,则完全依据其成员的取值范围与存储模式,来确定自己的取值范围和存储方式。 ● 派生数据类型可以在数据类型的定义里,就给出该数据类型的默认初始值,使得今后属于该类型的数据对象都一定取该初始值;而固有数据类型的定义里面不含有定义初始值的部分,要想显式地给出变量初始值,得需要使用数据类型声明语句或者DATA语句。 ● 固有类型的名称是全局性的,在任何环境总是可访问的,而派生类型只可在其定义的作用域内访问它们。 FORTRAN定义的数组同样是数据对象的集合,不过派生数据类型与数组的差别在于组成数组的各个成员必须是相同的数据类型,而派生数据类型的特点就在于它的成员可以是属于不同的数据类型。派生数据类型的成员可以仍然是某个派生数据类型,但数组的成员就不能是数组。 要想让数组的成员为数组只能通过间接的方式,即取数组的唯一成员为一个派生数据类型,然后该派生数据类型的成员取为数组。 对于这样构造出来的与具体问题密切相关的派生数据类型,由于其所具有的内部结构个性太强,显然我们需要定义一些与这种结构相关的特征属性,可以用来描述这种个性: ● 正是由于一个已经定义好的派生数据类型具有完全个性化的内部结构,因此如果一个派生数据类型是定义在一个模块内部的话,自然的问题就是这个派生数据类型的面向模块外部的可访问性如何控制。 在默认情形下,一个在模块内部定义的派生数据类型可以被模块外部的任意程序单元访问,这个默认状态等价于使用PUBLIC语句。 反之,也可以通过使用PRIVATE语句来阻止模块外程序单元的访问。 不过,由于一个模块包含许多不同层次的数据对象,因此PRIVATE的作用对象必须是精确指定的,这里分为以下几种情况: · 每一个对象都可以单独地通过使用属性PUBLIC或PRIVATE来指定其可访问性。 · 对于一个派生数据类型,可以在定义类型的语句当中设置可选的PUBLIC或PRIVATE描述符。 · 使用单独的针对类型名称的PRIVATE语句或在类型定义当中使用PRIVATE描述符,可以把对该类型访问控制在本模块之内。 · 在类型定义的内部使用单独的PRIVATE语句,可以只控制该派生类型的特定的组元的模块可访问性,而不影响整个类型的默认下的可访问性。 ● 组成一个派生类型的各个成员可以按照一定的顺序进行存储,这是通过在派生类型的定义当中运用SEQUENCE语句得到的。 一个派生类型的定义当中,成员的叙述上的顺序并不表示它们具有相应的存储上的顺序,只有在定义当中设置一个SEQUENCE语句,才能对这些成员的存储进行排序。从而可以进一步对这些成员应用COMMON语句或EQUIVALENCE语句。 ● 一个派生数据类型的取值范围显然就是它的所有成员的取值范围的组合,使用集合论语言就是各成员值集的乘积。FORTRAN规定了一个语法机制来给派生数据类型构造值的形式,例如用来定义命名常量,这样就可以在类型声明语句或PARAMETER语句当中使用其命名常量。这种语法机制称为派生数据类型的结构构造器。 至此我们可以看到FORTRAN语言对派生数据类型的定义,不是以某种真实世界的自然的数据集合为依据,而是提供一种描述方式,并且在语言当中约定,只有运用这种方式进行描述,就可以成立为一种数据类型。而对于通过这种方式成立的数据类型,同样包含四个方面的基本属性: ●拥有名称,以备引用; ●取值在一定的集合上; ●可以施加的运算; ●常量的写法。 下面就从这四个方面来说明派生数据类型。 6.2.1派生数据类型的构造 给出一个派生数据类型的名称,同时意味着要给出该派生数据类型的完整定义或着说描述。一个完整的派生数据类型的句法形式(R422)为: TYPE :: ] type-name … component-definition-statement … END TYPE 其中: · 可选的访问说明(access-specification)为关键词PUBLIC或PRIVATE。 · 类型名称(type-name)需要由程序作者自己给出,最好是有意义的英文词汇或缩写。 · 私用序列语句(private-sequence-statement)为PRIVATE语句和SEQUENCE语句。 每一条成员定义语句(component-definition-statement)(R425)包含一个数据对象的类型说明(R502),其句法形式为: type-specification :: ] component-declaration-list 其中: · 类型说明(type-specification)表示一个固有数据类型,或者是一个已经定义过的派生数据类型,不过如果这个描述符后面带有POINTER属性,由于指针的赋值特性,那么该成员就可以表示任意可访问的派生类型,包括正在被定义的派生类型自身。 · 成员属性(component-attribute)(R426)只能是POINTER或DIMENSION,即对于指针性质的成员,使用可选的属性POINTER;而对于数组成员,使用可选的属性DIMENSION,后面还可以再加上数组描述。POINTER和DIMENSION这两个属性可以同时出现。 · 成员声明(component-declaration)给出成员的名称。 成员声明(R428)的句法形式为: component-name 其中: · 成员数组说明(component-array-specification)是可选项,放置在括号当中。如果该数组具有指针属性,那么它的形状未定,否则就是显形状。在显形状的描述当中,数组的界都必须是标量整型常量表达式。如果在这里没有指出数组的界,那么一定要在后面的DIMENSION属性里说明。 · 字符长度参数(character-length)是一个可选的标量整型字面常量,它必须以星号开头。当然这个参数只能用于修饰字符型成员。 成员初始化(component-initialization)(R429)取下面2种形式之一: =initialization-expression =NULL() 其中: · 初始化表达式(initialization-expression)用来说明非指针成员的默认初始值。 · =NULL()即把成员作为指针赋值为不带变元的固有函数NULL的结果,表示指针成员的未结合或空置这2种初始结合状态。初始化指针状态的用处在于,很多情形下,要设置一个指针,都必须首先要求该指针具有确定的结合状态。 构造了一个派生数据类型之后,要使用这个派生类型,自然需要声明属于该类型的变量,对于派生类型来说,变量的声明采用如下句法形式: TYPE(type-name) entity-list 注意这个TYPE语句和定义派生类型的TYPE语句在句法上有差别:声明变量时,TYPE后面必须接括号里面的变量所属派生类型名称;而定义派生类型时,后接不带括号的派生类型名称。 构造了一个派生数据类型之后,经常需要引用其中的成员,引用的句法形式为: parent-structure % component-name 其中父结构(parent-structure)指需要引用的成员所属的派生数据类型(或数据结构)。 派生数据类型构造的一般规则如下: ● 派生数据类型的定义当中给出的类型名称不能与任何固有数据类型的名称重合,也不能与任何本模块能够访问的其他派生数据类型的名称重合。必须保证该名称在它的有效作用域内是指称同一个类型对象。而派生数据类型内部成员的名称的作用域只局限于该派生类型内部,因此不同派生类型的成员可以使用相同的名称,即使这些不同的派生类型属于同一个作用域。同样的,不同作用域当中如果出现了相同的派生类型名称,那么它们实际上是不相干的数据对象。 ● END TYPE语句后面或者省略类型名称,或者后接该类型的名称,不能出现不一致的情况。 ● 一个作用域内,一个派生类型的名称只能定义一次。 ● 在一个派生类型的定义当中,PRIVATE语句只能出现一次。 ● 在一个派生类型的定义当中,SEQUENCE语句也只能出现一次。 ● 只有当一个类型定义是放置于一个模块的规则说明部分时,才能使用访问控制符PUBLIC或PRIVATE。 ● 只要派生类型的定义当中出现SEQUENCE语句,就说明该派生类型的所有成员都必须具有顺序属性 (即其中也必须出现SEQUENCE),并且在定义中成员定义的次序就说明了该类型的对象的存储序列。 ● 在一个派生类型的定义当中,必须至少包含一个成员的定义。 ● 在成员定义当中,任何特定的成员属性都只能出现一次。 ● 一个成员只有在具有指针属性时,才可声明为属于被定义的派生类型本身。 ● 任何非指针的数组成员,都必须表示为显形状形式,其上下界都是整型常量 表达式。 ● 如果成员是指定了长度的字符型数据,那么它的长度必须是整型常量描述表达 式。如果没有指定长度值,那么默认值为1。 ● 如果进行成员初始化,那么必须使用双分号间隔符。 ● 在2种初始化方式中,初始化表达式只能用于非指针变量,也可以说等价赋值符号(=)只能用于非指针变量。而固有函数NULL()则是用于指针变量的。 ● 初始化表达式只能在派生类型的作用域当中取值。 ● 一个派生类型内部的成员当中,可以只有部分成员是默认初始化的,即取得默认初始值,或取得默认初始结合状态,而不是一定要求所有成员都必须初始化。 ● 一个成员如果是数组,那么它的默认初始值必须满足数组的定义,可以是一个数组构造器,也可以是一个标量,这个标量表示了该数组成员的每个元素的初始值。 下面给出不同情形下构造派生数据类型的例子: 【例6-2】 TYPE SET INTEGER N, M END TYPE TYPE (SET), DIMENSION (2, 2) :: a, b(3) 在这个派生类型的定义当中,a和b都是属于派生数据类型SET的数组变量。 【例6-3】 TYPE CURRENT REAL::HIGH=5.5, LOW=1.2 END TYPE CURRENT 在这个派生类型的定义当中给出了其唯一实型成员的初始值. 【例6-4】 TYPE employee_name CHARACTER(25) last_name CHARACTER(15) first_name END TYPE TYPE employee_addr CHARACTER(20) street_name INTEGER(2) street_number INTEGER(2) apt_number CHARACTER(20) city CHARACTER(2) state INTEGER(4) zip END TYPE 在上面2个派生类型的定义当中,一个派生数据对象是另一个派生数据对象的成员。 【例6-5】 例6-4中定义的数据对象还可以被第3个数据类型引用,如: TYPE employee_data TYPE (employee_name) :: name TYPE (employee_addr) :: addr INTEGER(4) telephone INTEGER(2) date_of_birth INTEGER(2) date_of_hire INTEGER(2) social_security(3) LOGICAL(2) married INTEGER(2) dependents END TYPE 【例6-6】 TYPE ARTICLE CHARACTER(LEN=100)ABSTRACT INTEGER LINES CHARACTER,POINTER::TEXT(:) END TYPE ARTICLE 在这个派生数据类型里面包含一个指针成员。 【例6-7】 TYPE LINK INTEGER VALUE TYPE(LINK),POINTER::PREVIOUS=NULL() TYPE(LINK),POINTER::NEXT=NULL() END TYPE LINK TYPE (LINK),POINTRE::A_LINK ALLOCATE(LINK) 在这个派生数据类型当中,指针成员的类型已经被定义,这种派生数据类型被广泛应用于链表与树结构。 【例6-8】 TYPE,PRIVATE::FILE INTEGER FILE_NO CHARACTER(LEN=30) FOLDER_NAME CHARACTER(LEN=10) ACCESS_LEVEL END TYPE 这个派生数据类型定义在一个模块当中,但是对这个模块保持私用状态。 6.2.2派生数据类型的取值,运算,以及常量表达式 一个派生数据类型的值集就是它的成员的值集的乘积,也就是它的成员的所有可能取值的所有可能的组合. 我们知道每个固有类型都有一个固定的值集,在任何环境下引用固有类型,这个值集都是一致的,而派生类型的数据值集则完全是根据它的成分的数据值集确定的。 由于派生数据类型本质上是一种数据结构,因此对派生类型对象的运算的定义,需要使用具有OPERATOR界面的函数,而赋值则使用具有ASSIGNMENT界面的子例行程序。详细的有关子例行程序及其界面的讨论参见第13章。 上面给出的派生数据类型的定义过程,实质上就是给出了一个语法机制,根据这个语法机制构造出来的语言结构肯定能够被FORTRAN编译器识别为派生数据类型对象,即或者是属于该派生数据类型的变量,或者是属于它的变量的具体取值。 因此派生数据类型的定义自动给出它的取值的结构构造器,该结构构造器生成一个数据序列,其中每一个数据对应着该类型数据对象的一个成员的取值。而该类型数据对象的所有成员的所有取值的任意组合,正是该结构构造器所能生成的所有常量。 通过这样的结构构造器生成的常量,也可以用来给定义为该派生类型的命名常量赋值。 由于派生数据类型的成员除了可以是固有数据类型之外,还可以是任意的数据结构,特别是设置为 FORTRAN的一种重要的数据结构-数组。在这种情况下,特别定义了一种用来描述派生类型的数组成员的语法机制,称为数组构造器。 当数组构造器的取值全是常量表达式时,得到数组值常量表达式,这样一个表达式可以作为一个命名常量的数组成员。 数组构造器不仅是可以用来给出一个数据结构的成员的值,也可以用来给出任意数据类型的数组值。 6.2.3数据结构构造器 一个属于派生数据类型的常量的句法形式(R431)为: type-name (expression-list) 其中 类型名称(type-name)为某个派生数据类型的名称。 表达式列表(expression-list)为给出派生数据类型的各个成员值的表达式序列。 结构构造器的一般规则如下: ● 结构构造器不能在它所代表的派生数据类型被定义之前出现,也就是说它给出的派生类型名称不能置空。 ● 结构构造器给出的值必须与type-name所指的派生类型的成员一一对应,书写顺序也必须和派生类型定义里各成员的排列顺序保持一致。为了保证与各个成员在类型,种别参数,长度以及秩的一致,有时可能需要对值进行适当的转换。 ● 如果派生类型包含数组成员,那么在各个成员值的表达式序列当中数组的形状,必须与类型定义当中的该数组成员的形状保持一致。 ● 如果派生类型包含指针成员,那么在各个成员值的表达式序列当中,该指针所对应的值,必须是相应指针赋值语句当中的有效目标。 ● 如果结构构造器的所有成员值都是常量表达式,那么该构造器实质上就构成一个派生类型的常量表达式。 【例6-9】 考虑下面的派生数据类型: TYPE EMPLOYEE INTEGER ID CHARACTER(LEN=40) NAME END TYPE EMPLOYEE 可以得到下面的结构构造器: EMPLOYEE(5645, "陈辉") 【例子6-10】 下面的派生数据类型包含另外的派生类型作为成员: TYPE ITEM REAL COST CHARACTER(LEN=30) SUPPLIER CHARACTER(LEN=20) ITEM_NAME END TYPE ITEM TYPE PRODUCE REAL MARKUP TYPE(ITEM) BOOK END TYPE PRODUCE 这时,就需要使用内嵌的结构构造器来得到成员取值: PRODUCE(.70, ITEM (.25, "XIN HUA", "A BOY")) 6.3子串 从符号的意义上来看,下面两个数据对象都是属于字符串: ASDFJASDF 12349123123 如果我们把12349123123看成是标量,那么没有理由不把ASDFJASDF也看成是标量。然后进一步由标量构造成数组,同样可以用字符串作为数组的分量,由于字符串的参数就是它的长度,因此在用字符串构成数组时,有必要限制它们的长度保持一样。 要以长度为参数构造字符串,一个自然的途径就是从一个字符串当中,按照长度截取其连续的某个部分,这就提出了子串的概念。 子串的句法形式(R609)为: parent-string(substring-range) 其中父串(parent-string)的句法形式(R610)为以下诸种形式之一: scalar-variable-name array-element scalar-structure-component scalar-constant 子串范围(substring-range)的句法形式(R611)为: : 其中子串的始点和终点为标记父串里字符位置的下标的整型表达式,始点为子串最左边的字符在父串里所处的下标表达式,终点为子串最右边的字符在父串里所处的下标表达式,显然,始点和终点都处于1到LEN之间,LEN为父串的长度。如果始点值大于终点值,那么系统认为该子串的长度为0。给出始点和终点,就能从父串截取到相应的子串。 子串的一般规则如下: ● 字符串属于字符型数据类型,父串与子串都是字符串。 ● 字符串的下标是按照从左到右的顺序,从1开始,一直到LEN,LEN为字符串的 长度。 ● 子串是父串里从第i个字符开始,到第j个字符结束的连续的一段字符串,I和j 都大于等于1,小于等于LEN,下标为i的字符称为子串的始点,下标为j的字符 称为子串的终点。 ● 如果始点与终点的下标表达式的值不是整型,则必须转换为整型。 ● 如果i没有给出,默认i为1;如果j没有给出,默认j为LEN。 ● 子串的长度可以是0,但不能是负值,如果给出的i大于j,则系统认为子串的长 度为0。 计算子串的长度使用函数MAX,公式为: MAX(ending-position - starting-position + 1,0) ● 如果父串的长度为0,那么它的任意子串的长度都是0。 【例6-11】 CHARACTER(10)signal_peptide signal_peptide = “HLA-A*0301” signal_peptide(1:3) = “HAI” PRINT *, signal_peptide(7:10) 在这个例子里,signal_peptide被定义为长度为10个字符的字符型变量,在第一个赋值语句里,该变量被赋值为字符串“HLA-A*0301”,在第二个赋值语句里,该变量的下标从1到3的子串被引用,并且被赋值为字符串“HAI”,这样signal_peptide的值就变成了“HAI-A*0301”,接下来的打印语句的执行结果是打印下标从7到10的子串,为“0301”。 【例6-12】 CHARACTER*8 A, Asteroid Asteroid = '2000 DG8' A = Asteroid (1:4) 在这个例子里,首先定义了长度为8的2个字符型变量A和Asteroid,然后给出2个赋值语句,对字符型变量Asteroid赋值为'2000 DG8',而对A赋值为Asteroid的一个子串'2000'。 【例6-13】 TYPE nearly_isotropic_comets INTEGER inclination REAL semi-major_axis, eccentricity, perihelion_distance, Absolute_Magnitude CHARACTER*12 designation END TYPE nearly_isotropic_comets TYPE(nearly_isotropic_comets) 2000YEAR CHARACTER*12 ASTEROID, BIG_INCLINATION(5) 由上面定义的字符串,可以得到如下合法子串: BIG_INCLINATION(2) (1:4)!父串为数组元素 2000YEAR % designation(6:12)!父串为结构成员 ASTEROID (1:4)!父串为标量变量 “ABCDEFGH”(N:N+1) !父串为字符常量 如果一个数组的分量为字符串,那么由这个数组构造子串,可以有很多种合法的可能性,例如,BIG_INCLINATION( (1:4),表示对一个由子串组成的数组片断的引用;BIG_INCLINATION(1:4) (1:4),同样表示一个由子串组成的数组片断的引用。因此如果要从一系列长度一致的字符串构造相同下标范围的子串,可以通过对数据组的片断引用而实现。不过必须注意数组片断下标放置于子串下标之前。由于表示数组片断的句法形式和表示子串下标范围的句法形式一样,因此必须使用这个顺序安排,才能得到无歧义的表示。 【例6-14】 下面2个引用含义完全不同: BIG_INCLINATION( (1:4) BIG_INCLINATION (1:4) 前者表示由5个子串构成的数组,后者表示由4个父串构成的数组。 ● 由“ABCDEFGH”(N:N+1)得到的子串既非常量,也非变量,因为它的下标是一个变量,得到的子串称为常量子对象。参见表达式的形式。 6.4结构成员 所谓结构就是一个集合,其元素可以是任意数据类型,也可以是标量或数组,一个结构至少包含一个元素,这样的结构本身就是一种派生数据类型,由同一类型的多个结构又可以组成一个数组。因此我们可以看到,结构的定义包含递归的成分,这样能够保证提供最大的灵活性,以便足够充分地描述实际问题当中出现的数据结构,减少因为数据结构复杂而导致算法复杂的压力。 定义一个结构之后,如果要引用其成员,可以直接使用如下形式: parent ] ... %component 其中: parent为父结构名称。 百分符号(%)称为成员选择符。 component为其左边邻接父结构或成员的成员 section-subscript-list表示片断下标列,如果该下标列包含下标三元组或者向量下标,那么就表示引用一个数组片断。 由于结构定义具有递归成分,因此要引用结构的成员,就会出现很多的情形,更加抽象的理解是把结构成员的引用(R614)看是一种数据引用(R612)的形式。 所谓数据引用,是把任何被引用的数据看成某个数据集合的部件,这种包含关系可以任意多层地嵌套,因此结构成员的引用也可以运用句法形式(参见附录B)递归定义如下: part-reference … 其中部件引用(part-reference)(R613)的形式定义为: part-name 其中片断下标(section-subscript)(R618)的形式定义为以下几种情况之一: subscript subscript-triplet vector-subscript 有关数组片断的下标参见6.5.5节。 结构成员引用的一般规则如下: ● 结构成员的引用作为数据部件的引用,要求被引用的部件是属于多个部件之中的 一个。 ● 作为结构成员引用的数据引用的最右边的被引用的部件,必须是一个部件名称, 如果该部件引用具有形式: part-name (section-subscript-list) 那么被引用的就是一个数组片断,在最简单的情形下,是一个数组元素。 ● 在上述数据引用定义里,除了最右边的部件名之外,每一个部件名称都必须是指 称一个派生类型数据对象。 ● 数据引用时,除了最左边的部件名之外,每一个部件名称都必须是其左边邻接部 件名称所表示的派生类型的成员。 ● 在上述数据引用定义里,任意位置的部件都可以是数组,一个数组右边邻接 的成员不能具有指针属性。 ● 如果父结构具有INTENT, TARGET, 或PARAMETER属性,则其结构成员也具有 同样的属性。 ● 在父结构还没有被声明之前,其结构成员不能被引用。 ● 结构成员如果含有非0秩的部件,那么它的秩就是非0秩的部件的秩,否则秩为0。 结构成员的类型和类型参量(如果存在的话),与最右边的部件保持一致。 【例6-15】 下面定义一个包含2个成员的派生数据类型: TYPE EMPLOYEE INTEGER ID CHARACTER(LEN=40) NAME END TYPE EMPLOYEE 然后声明变量CONTRACT 属于派生数据类型EMPLOYEE,并且引用该变量的一 个成员。 TYPE(EMPLOYEE) :: CONTRACT CONTRACT%ID 【例6-16】 下面定义一个派生类型作为另一个派生类型的成员: TYPE DOT REAL X, Y END TYPE DOT .... TYPE SCREEN TYPE(DOT) C, D END TYPE SCREEN 然后声明变量M属于派生类型SCREEN,并且引用该变量的几个成员: TYPE(SCREEN) M M%C M%D (both of type DOT); M%C has components M%C%X M%C%Y of type REAL. 【例6-17】 下面定义一个包含数组成员的派生类型: TYPE CAR_INFO INTEGER YEAR CHARACTER(LEN=15), DIMENSION(10) :: MAKER CHARACTER(LEN=10) MODEL, BODY_TYPE*8 REAL PRICE END TYPE ... TYPE(CAR_INFO) MY_CAR 【例6-18】下面定义派生类型以及属于该类型的2个变量: TYPE CHARGE INTEGER PARTS(40) REAL LABOR REAL MILEAGE END TYPE CHARGE TYPE(CHARGE) MONTH TYPE(CHARGE) YEAR(12) 如下引用的数组都是合法的: MONTH%PARTS(I) ! An array element MONTH%PARTS(I:K) ! An array section YEAR(I)%PARTS! An array structure component (a whole array) YEAR(J)%PARTS(I) ! An array element YEAR(J)%PARTS(I:K) ! An array section YEAR(J:K)%PARTS(I) ! An array section YEAR%PARTS(I)! An array section 【例6-19】 下面定义的派生类型包含一个已有定义的指针成员: TYPE NUMBER INTEGER NUM TYPE(NUMBER), POINTER :: START_NUM = NULL( ) TYPE(NUMBER), POINTER :: NEXT_NUM = NULL( ) END TYPE 这样的结构可以用来构造链表,注意其中的指针为默认的非结合初始状态。 【例6-20】 下面定义一个私用派生类型: TYPE, PRIVATE :: SYMBOL LOGICAL TEST CHARACTER(LEN=50) EXPLANATION END TYPE SYMBOL 这个派生类型属于模块私用类型,该模块可以被其他作用域访问,但该类型只能被本模块引用。 6.5数组 数组同样是一个集合,它的元素必须是标量,其标量元素可以属于任何的固有数据类型,派生数据类型,甚至是结构,但数组最关键的一个特征是: 数组的所有标量元素必须属于同一个数据类型,并且具有同样的种别参数。 这个特征决定了数组是一种功能强大的数据结构,因为存在大量的实际问题,需要用同一个计算过程来处理大规模的同种类型的数据,因此FORTRAN 95提供了强大的内部运算和固有函数,专门用来处理整个数组,或者数组元素,或者数组片断,同时硬件的并行化发展,也为这一类通常是极大规模的运算,提供了优化的解决方案。 在FORTRAN的早期版本里面,数组是作为数据的一种属性加以描述的,所使用的关键词为DIMENSION。这个规则一直被保留下来,因此任何数据对象只要是具有DIMENSION属性,就一定为数组。 6.5.1数组的结构 数组的结构可以抽象地理解为多维离散空间的点的集合,即这些点都有相同的维度,在每一维上的坐标取值都是离散的标量。 一个具体的例子就是表格。实际上表格总是可以用二维数组加以描述,例如取上章的如下表格: 表6-1表格可以看成数组 作用单元的种类 语句 主程序 模块 数据块 外部子程序 模块子程序 内部子程序 接口块 USE语句 Y Y Y Y Y Y Y ENTRY语句 N N N Y Y N N FORMAT语句 Y N N Y Y Y N 其他声明 Y Y Y Y Y Y Y DATA语句 Y Y Y Y Y Y N 导出类型定义 Y Y Y Y Y Y Y 接口块 Y Y N Y Y Y Y 语句函数 Y N N Y Y Y N CONTAINS Y Y N Y Y N N 可执行语句 Y N N Y Y Y N 如果该表格的行,即作用单元的种类看成是一个维度a;把该表格的列,即语句的种类,看成是另一个维度b,那么该表格的每一个元素都可以用符号E作用单元的种类,语句的种类,或者Ea,b,来表示,其中a可以取7个值: 主程序,模块,数据块,外部子程序,模块子程序,内部子程序,接口块。 b可以取10个值: USE语句,ENTRY语句,FORMAT语句,其他声明,DATA语句,导出类型定义,接口块,语句函数,CONTAINS,可执行语句。 而每个元素Ea,b取值为符号Y或N。 这样我们就把一个表格完全的用一个数组加以描述了。 从上面的例子可以看到,数组的结构是由维度,和每个维度的取值数目决定的。其中: 一个数组的维度的数目称为该数组的秩; 每个维度的坐标数目称为数组在该维度的宽度; 那么一个数组的秩和每个维度的宽度就决定了该数组的形状; 一个数组的所有维度的宽度的乘积,称为该数组的尺度,也就是该数组的元素的数目。 数组的秩至少可以达到7,这是FORTRAN标准所要求的任何编译器都必需达到的数组处理规模,当然实际的编译器所能处理的数组的秩都要大于7。同时FORTRAN标准对于宽度的大小没有限制。 数组的形状可以用类似于向量的形式表示,即括号当中的一串标量数值,用逗号相隔,每个数值表示相应的维度的宽度。例如: REAL A(2,25,10) 表示以实型数值为元素的数组A,含有3维,每个维度的宽度分别是2,25,10。 在描述每个维度的宽度时,可以使用下标范围的形式,也就同时说明了该维度的元素的下标表示方法,例如: REAL A(2,0:24,10) 这个数组可以用上面的语句来描述,但是这里的描述方式更加具体地说明了数组的第2个维度的下标标示方式,即用从0到24的下标了标示25个该维度的坐标。 显然数组A的尺度为 ,即数组A包含500个元素。同样形状的数组A还可以用别的形式加以说明。 【例6-21】 DIMENSION A(2,0:24,10) REAL,DIMENSION(2,0:24,10):: A COMMON A(2,0:24,10) TARGET A(2,0:24,10) 由于机器表达数据的有限性,数组在每个维度上的坐标都必定有上下界,当在数组描述时使用了下标,那么最小下标值就是数组在该维度的下界,最大下标值就是数组在该维度的上界。 必需注意的是这时数组的下标范围可以取任意的整数值范围,而如果没有在数组说明当中说明下标范围,只是给出其宽度n,那么数组在该维度的默认下标范围是1:n,即从1到n的自然数。 因此与上面同样形状的数组A还可以表示为: REAL A(2,-5:19,10) REAL A(2,5:29,10) 关于形状的描述里面的上下界的表示,参见第8章的有关小节。 6.5.2全数组 由于数组是一个数据结构,因此对于数组的引用,可以是引用整个数组,也可以是引用数组的某个元素,某些元素,或数组的片断,具体的分别在于,如果是引用了数组的名称,无论是一个常量数组的名称,还是一个变量数组的名称,只要在引用该数组名称时,不包含该数组的下标列,或数组片断的下标列,那么就是把该数组作为一个整体加以引用,即作为一个全数组。 一个全数组如果具有INTENT,TARGET,PARAMETER这些属性,那么它的任意元素和片断都具有相应的属性; 一个全数组如果具有POINTER属性,注意,它的任意元素和片断都不具有相应的POINTER属性。 6.5.3数组元素 数组元素的引用很简单,直接用下标给出该元素在每一个维度上的坐标即可。 【例6-22】 声明1维数组X如下: INTEGER,DIMENSION(25)::X 那么对X的元素的引用例子为: X(3),X(12),X(24)。 声明3维数组A如下: REAL,DIMENSION(2,25,10):: A 那么对A的元素的引用例子为: A(1,12,5),A(2,22,8),A(1,17,10)。 这里的(1,12,5)等称为下标列。 6.5.4数组片断 数组最灵活的功能体现在能够以数组的任意部分为对象作运算,把一个数组的任意指定部分作为一个新的数组,称为数组片断,而其所属的数组称为该数组片断的父数组。 数组片断的引用,首先是一个父数组变量名称,然后是下标列,不过这里的下标列里面,必需至少包含一个下标是用下标三元组,或者是下标向量来表示的,否则这个引用就成了对数组元素的引用。 下标三元组和下标向量的定义见下节。 【例6-23】 下面是引用了一个数组片断: INTEGER,DIMENSION(25)::X … X(10:16)=78 上面定义了1维整型数组X,其宽度为25,后面引用了X的一个片断,由X的第10个坐标到第16个坐标这7个元素组成,在这个引用当中,X所指定的这7个元素都被赋值为78。 注意这里的坐标引用(10:16)为一个省略了第3项的下标三元组。 6.5.5数组元素和数组片断的句法形式 数组元素的引用实质上就是数据引用,因此数组元素的一般句法形式就是R612。 数组片断的一般句法形式(R616)为一个数据引用,后接一个可选的包含在括号当中的子串范围。子串范围的句法形式见6.3节的R611。 数据引用当中的组分名称可以后接一个可选的片断下标列,片断下标的句法形式(R618)可以是如下3种之一: subscript subscript-triplet vector-subscript 其中下标三元组(subscript-triplet)的句法形式(R619)是: : : 下标向量(vector-subscript)(R621)则就是一般的秩为1的整型数组表达式。 这里的下标和步长(stride)都必须是整型标量表达式。 一般规则如下: ● 如果被引用数据是一个数组元素,那么这个数据引用的每个部件引用都必需是秩为 0,而最后的部件引用必需包含一个下标列。 ● 如果被引用数据是一个数组片断,那么必须有一个部件引用为非0秩,并且或者是 最后的部件引用非0秩的片断下标列,或者是另外一个部件引用具有非0秩。 ● 如果一个数组片断以后接子串范围的数据引用的形式出现,那么最右边的部件名称 必须是字符型。 ● 在数组的每一个维度上,片断的下标都必须给出。 【例6-24】 下面都是合法的数组元素与数组片断的引用: X(2,3) X(1:N:2,M) Y(: , : , (1:3) SCALAR_A % ARRAY_D(L) SCALAR_A % ARRAY_D(1 ) SCALAR_A % ARRAY_E(1:N)%SCALAR_D ARRAY_F(1:N:2) % ARRAY_G(I,J) % STRING(K)( TYPE REPAIR_BILL REAL PARTS (30) REAL LABOR END TYPE REPAIR_BILL TYPE VEHICLE CHARACTER(LEN=35)OWNER INTEGER MILEAGE TYPE(REPAIR_BILL)COST END TYPE VEHICLE TYPE(VEHICLE)BLACK_BURKYELLOW_XIALI PRINT*,BLACK_BURK % COST % PARTS PRINT*,YELLOW_XIALI % COST % LABOR PRINT*,BLACK_BURK % OWNER TYPE(REPAIR_BILL)FIRST TYPE(REPAIR_BILL)FOR2000(10) 【例6-25】 下面都是合法的标量父结构: FIRST % LABOR FIRST % PARTS(I) FIRST % PARTS FIRST % PARTS(I:J) FOR2000(K)% LABOR FOR2000(K)% PARTS(I) FOR2000(K)% PARTS FOR2000(K)% PARTS(I:J) 【例6-26】 下面都是合法的向量父结构: FOR2000 % LABOR FOR2000 % PARTS(I) FOR2000(K:L)% LABOR FOR2000(K:L) % PARTS(I) 【例6-27】 下面都是非法的向量父结构: FOR2000(K:L) % PARTS!非法 FOR2000(K:L) % PARTS(I:J) !非法 FOR2000 % PARTS !非法 FOR2000 % PARTS(I:J)!非法 1. 下标 在对数组元素作引用时,每一个下标都必须处于该元素所属维度的下标上下界之中。 如果下标出现在数组片断的引用当中,那就意味着该被引用的片断相对于其父数组缺少了相应的维度,因此片断的秩也就相应地要减少1,因此在引用数组片断时,如果父数组的某个维度只是以其上下界的某个坐标出现在其片断引用当中,显然该维度实际上在其片断里就被压缩为1点了,也就是减少了一个维度。 2. 下标三元组 所谓下标三元组是一种表示从在某个维度上连续取值的数组里,有规律地按照固定间隔取数组片断的表达形式。 下标三元组的一般形式(R619)见上。 其中第一个数值表示取片断的相应维度上的下界;第二个数值表示取片断的相应维度上的上界;第三个数值表示在从上界到下界,按照固定间隔取坐标时的步长。 下标三元组的一般规则如下: ● 如果下界被省略了,那么所取默认值为父数组所声明的下界; ● 如果上界被省略了,那么所取默认值为父数组所声明的上界; ● 如果步长被省略了,那么所取默认值为1; ● 如果上下界和步长都被省略了,而只剩下冒号(:),那么就是取整个维度范围; ● 如果步长为正整数,那么该片断的下标为一个数列,取值为从下界开始,以步长为 间隔,递增直至不大于上界的最大下标值;这时如果下界大于上界,那么该片断的下 标数列为空集; ● 如果一个片断的下标数列为空集,那么该数组片断的尺度为0; ● 步长不能取0; ● 如果步长为负整数,那么该片断的下标为一个数列,取值为从下界开始,以步长为 间隔,递减直至不小于上界的最小下标值;这时如果下界小于上界,那么该片断的下 标数列为空集; ● 只要按照三元组所取的数组元素都包含在父数组的某个维度范围里面,那么该三元 组所给出的范围可以超出父数组所声明的相应维度范围。 【例6-28】 如果数组A声明如下: A(7,8,9) 而A的一个片断为: A(4:7,5,3:4) 那么该片断的秩为2,比其父数组A的秩小1,因为该片断引用当中的第2个维度出现了一个单独的下标,故维度减小了1;而片断的形状为(4,2),尺度为8,它的8个元素为: A(4,5,3) A(4,5,4) A(5,5,3) A(5,5,4) A(6,5,3) A(6,5,4) A(7,5,3) A(7,5,4) 【例6-29】 给出数组A(100),而它的一个片断为A(45:28:-5),那么该片断的秩为1,形状为(4),尺度为4,它的所有元素为: A(45) A(40) A(35) A(30) 而如果取其片断为A(45:28),这时默认步长为1,显然其尺度为0; 如果取其片断为A(25:110:35),尽管第2个下标超出了父数组A的下标范围,但是该片断的元素却没有超出父数组的元素范围,因此这种片断取法是合法的,它的所有元素为: A(25) A(60) A(95) 3. 下标向量 鉴于依靠下标三元组所截取的数组片断是具有规则形式的数组片断,当我们需要其他形式的数组片断时,三元组就无法胜任相应的描述数组的任务了,因此FORTRAN给出了一种具有最大的灵活性的截取数组片断的方式,即下标向量。 所谓下标向量,就是由一些下标值构成的一个向量,这些下标值取自父数组在某个维度上的下标范围,由与这些下标值相应的坐标就构成了父数组的一个数组片断。 【例6-30】 INTEGER I(10) REAL A(100) … I=(/ 23,35,11,46,57,65,78,89,90,1/) A(I)=3.34 这段例程里首先定义了2个秩为1的数组变量I(10)和A(100),也即向量,然后在后面用赋值语句给出I的一个具体赋值,接着以I为数组下标变量,给出了数组A的部分元素的一个具体赋值,实际上,这里I就是下标向量,而A(I)则是由下标向量I所给出的父数组A(100)的一个数组片断。 注意其中根据向量I的赋值,A的片断截取是任意顺序的,这就体现了利用下标向量来截取数组片断的特点。 不过虽然由下标向量来截取数组片断具有极大的灵活性,但是根据下标向量而得到的数组片断具有如下限制: 1.不能作为外部文件; 2.不能作为指针目标; 3.不能作为哑元INTENT(OUT)或INTENT(INPUT)的实元。 下标向量的构成允许出现重复的元素,例如在上面的例子里面,下标向量I可以赋值为: I=(/ 23,35,23,46,57,65,78,89,90,1/) 这样A(23)就会被访问2次,即A(I(1))和A(I(3)),这样得到的数组片断仍然是合法的,被称为多对一数组片断,这样的数组片断不能出现在如下场合: ● 赋值语句当中等号的左边; ● READ语句的输入项。 6.5.6下标,下标三元组和下标向量的混合使用 上面说明了如何分别使用下标,下标三元组和下标向量这三种方式来截取数组片断,实际上,这三种方式可以根据具体问题的要求来混合使用,下面是1个例子。 【例6-31】 给出父数组A如下: A(20,10,20) 可以构造A的一个数组片断如下: A(4:22:7, 9, 10:15:3) 该片断的所有元素如下: A(4,9,10) A(11,9,10) A(18,9,10) A(4,9,13) A(11,9,13) A(18,9,13) 然后我们还可以给出下标数组(向量): INTEGER DIMENSION(4)::I=(/3,7,3,9/) 运用这个下标向量可以构造数组片断: A(10:20:8, I, 14) 该数组片断的所有元素为: A(10,3,14) A(18,3,14) A(10,7,14) A(18,7,14) A(10,3,14) A(18,3,14) A(10,9,14) A(18,9,14) 可以看到其中包含了重复的元素,即多对一数组片断。 6.5.7数组元素序 由于计算机在实质上只能按照一维的串行顺序来处理数据,因此对于高于1维的数组,就产生一个如何表达其中数据的问题,这就需要约定一个统一的把多维数组转化为一个便于计算机处理的数据序列的方法,按照这个统一约定得到的数列就表示了数组元素的顺序,即数组元素序。 由于一个多维数组的各个维度是可以用序列的方式表示出来的,因此一个自然的表达数组元素序的方法就是,规定数组维度的顺序即各个维度被依次遍历的顺序, 【例6-32】 取如下2维数组: A(2,3) 按照它的形状的表示方法,它的第1维有2个下标,第2维有3个下标,那么这个数组的元素的排列顺序就是: A(1,1),A(2,1),A(1,2),A(2,2),A(1,3),A(2,3)。 即先遍历第1维的2个下标而保持第2维的下标不变,然后再遍历第2维的下标,直观地看,就是依照数组元素顺序排列下来后,第1维的下标的变化最快,而最后1维的下标变化最慢,其他维度的下标的变化依次类推。 对于按照数组元素序排列的数组元素数列,可以给以这个数列本身的下标标记,这个下标称为下标顺序值。 按照上面约定的从多维数组到串行序列的转换规则,可以给出一般的多维数组的元素的下标顺序值的计算公式。 当然,对于一个数组片断,它的元素的下标顺序值不能指定为其所在父数组里的下标顺序值,而只能是把该片断本身看成一个数组之后,重新给出的下标顺序值。 例如对于数组A(10),取其片断A(2:9:3),该片断元素包括A(2),A(5),A(8),对于1维数组A(10)来说,这些元素的下标顺序值就是它们的下标,即2,5,8。而对于数组A(2:9:3)来说,它们的下标顺序值却分别是1,2,3。 6.5.6数组构造器 所谓数组构造器就是给数组元素赋值时的句法形式,即数组元素的写法。由于语言的表述在本质上只能是一个字符串,因此数组元素的取值的写法只能是针对1维数组而言,因此数组构造器在形式上得到的是一个秩为1的数组的元素具体取值。 作为一个秩为1的数组的元素,自然的形式就是一个元素组成的序列,但是为了在句法上标志其构成一个数组,约定使用一对括号与斜线把所有元素值和其上下文区分开,因此一个数组构造器的句法形式(R432)为: (/ ac-value-list /) 数组构造器元素取值列(ac-value-list)中的元素取值作为数据对象,可以是表达式(R723),或者是隐性do变量取值(R434),所谓隐性do变量取值是直接以变量的形式来表示do结构含义的变量的具体取值。 【例6-33】 (SQRT(REAL(K)),K=10,13) 这里REAL(K)为变量形式,(SQRT(REAL(K)),K=10,20)则具有do结构的含义,经过K的具体取值,得到一个数组: (/ 3.162,3.317,3.464,3.606 /) 它的一般句法为: (ac-value-list,ac-do-variable=scalar-integer-expression, scalar-integer-expression ) 其中数组构造器值可能有三种形式: ● 标量表达式。 【例6-34】 /2.4,6.4,2.4,2.5,9.8/) ● 数组表达式。 【例6-35】 (/X(1:10,I),X(30:30,I-2)/) ● 隐式do变量。 【例6-36】 (/(SQRT(REAL(I)),I=0,10)/) 数组构造器的一般规则为: ●数组构造器里的每一个元素取值都必须是相同的数据类型,相同的种别参数, 相同的长度参数。 ● 数组构造器的数据类型与种别参数就是它的表达式取值的数据类型与种别参数。 ● 如果数组构造器不含有表达式或者其隐性do变量无法实际取值,那么得到一个 秩为0,尺度为0的数组。 ● 数组构造器do变量必须是标量整型命名变量,该变量具有数组构造器do变量的 作用域。 ● 如果一个数组构造器do变量包含在另一个数组构造器do变量里面,它们不能是 同一个变量
个人分类: 计算机|0 个评论
[转载]Fortran中公共块的运行和修改
liuph 2010-11-26 19:53
转自:编程爱好者论坛 Fortran区的一篇置顶文章 最近,好几个人问到公共块出错的问题,都是出于一个原因,特在这里把老贴整理一下供大家参考。 问题: 20924057 Compiling Fortran... D:\FRAME2D.FOR D:\FRAME2D.FOR(314) : Warning: Because of COMMON, the alignment of object is inconsistent with its type COMMON /EM/LM(6),ND,ASA(6,6),RF(6),SA(6,6) --------------------------^ D:\FRAME2D.FOR(314) : Warning: Because of COMMON, the alignment of object is inconsistent with its type COMMON /EM/LM(6),ND,ASA(6,6),RF(6),SA(6,6) ---------------------------^ 解答: 这是老Fortran的规则。看你的公共语句: COMMON /EM/LM(6),ND,ASA(6,6),RF(6),SA(6,6),SF(6),T(3,3) 根据程序的隐含规则,LM(6),ND一共是7个整型数,每个整型数4个字节,共28个字节,不是8字节的整倍数。紧跟着是双精度的ASA(6,6),每个数是8个字节,可是ASA(1,1)不能从8字节整倍数位置开始存储,这就是the alignment of object is inconsistent with its type 的意思。 对于警告错误,可以忽略,一般不致于影响执行结果。如果要改的话,有几种办法: 1)在公共语句中,把双精度数放在前边,整形数跟在后边; 2)在ASA()前插一个整型变量(哪怕是没用的),用来占用4个字节,以使得后面的双精度数可以从8字节整数倍位置开始存储。 准则:作为好的编程习惯,建议在公共块中,把实型变量排在前边,把整型数据放在后边,就不会有对位不整的错误! 仅供参考。 补充:(05.06.01) 公共块不是用堆栈实现的,是内存中的一段连续存储的数据。 按照Fortran的规定,当读取双精度数据时,总是假定前面的数据长度是双精度数字节长度(8个字节)的整数倍。 对于本例,ASA(1,1)从第29个字节开始存放8个字节;可是读取的时候,要从第33个字节(28不是8的倍数,32是28之后最小的8的倍数)开始读入8个字节,这就是定位(alignment)错误。 所以F90之后不提倡用公共块共享数据,而可用更为灵活的module来代替公共块共享数据。 公共块是过时的语言功能
个人分类: 生活点滴|5957 次阅读|0 个评论
[转载]如何在fortran中读写文件时不换行
热度 1 liuph 2010-10-12 22:21
如何在fortran中读写文件时不换行(上传供大家学习) 转自ifelseif的博客 如何在fortran中读写文件时不换行?这是个极简单又极复杂的问题,简单到只要一个字符,复杂到翻破了好几本语法书也没找见。fortran中默认一条read或者write结束之后就换一行,但是读和写还有些不太一样。 读文件时,read之后如果写了一个数组,就像这样: read(10,*)Y(1:n) 整整一行数就全都读到数组里了。但是如果用write,写到文件中却不是这个样子,会给你一个超级长的文件然后每行只有一个数。有一个选项叫ADVANCE='YES'/'NO',可以控制输入输出语句完了之后要不要换行,默认是'YES',很不幸,在intel的fortran中这个选项只对read起作用,write依旧不行。 在fortran的输入输出中,/表示换行,那么\表示什么意思呢,就是不换行。这是我在网上逛了老半天才看到的,为了防止忘记,写到博客里面,立此存照。 下面是一段fortran代码样例,要处理的数据20个数就会换一行,一般来讲最后一行是不满20个数的,需要用个同余判断一下 PROGRAM MAIN IMPLICIT NONE INTEGER I,J,NY,A,B REAL X,Y(60),Z(60) OPEN(UNIT=10,FILE='SX-RIVER.TXT') OPEN(UNIT=11,FILE='SX-OUTPUT.TXT') DO I=1,9 READ(10,*) END DO DO I=1,372 !!!!!!!!! DATA INPUT !!!!!!!!!!!!!!!! READ(10,(18X,I2,1X,F7.3))NY,X CALL MOD(20,NY,A,B) DO J=1,A READ(10,(20(1X,F7.2)))Y(20*(J-1)+1:20*J) END DO DO J=1,B READ(10,(1X,F7.2),ADVANCE='NO')Y(20*A+J) END DO READ(10,*) DO J=1,A READ(10,(20(1X,F7.2)))Z(20*(J-1)+1:20*J) END DO DO J=1,B READ(10,(1X,F7.2),ADVANCE='NO')Z(20*A+J) END DO READ(10,*) !!!!!!!!! DATA OUTPUT !!!!!!!!!!!!!!! !WRITE(11,(I4,1X,F7.3))NY,X WRITE(11,(1X,F7.2,\))Y(1:NY) WRITE(11,*) WRITE(11,(1X,F7.2,\))Z(1:NY) WRITE(11,*) END DO CLOSE(10) CLOSE(11) STOP END PROGRAM MAIN !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! SUBROUTINE FOR CONGRUENCE(TONGYU) ! ! Y=A*X+B ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! SUBROUTINE MOD(X,Y,A,B) IMPLICIT NONE INTEGER X,Y,A,B,TMP A=1 TMP=X DO WHILE(Y.GT.TMP) A=A+1 TMP=TMP+X END DO A=A-1 B=X-(TMP-Y) RETURN END SUBROUTINE MOD
个人分类: 生活点滴|11257 次阅读|0 个评论
[Fortran]用Unformatted格式生成的超大文件的另类通用读法
lefang 2010-10-1 16:40
有一个问题困扰我许多年了:同样是Fortran语言用Unformatted格式生成的数据文件,在32位和64位机器上大小不同。关于这一问题,在 这里 描述道: Update (Jan 2008): It would appear that on 64 bit machines the 2 header elements are each written as 4 bytes instead of 2 bytes each. 但也没有给出解决方案。 终于我想到一个另类做法,可以通用地读取这样的文件。 假设原来的文件是这样生成的: real*8 u_tmp(k1:kmax,k1:kmax,0:n/16-1) open(10,file=filename,form='unformatted') write(10)(((u_tmp(i,j,k),i=k1,k2),j=k1,k2),k=0,n/16-1) close(10) 如果原来文件是在64位机上生成的,那么不需要做特殊处理。否则,假设生成的文件是u.dat,则另外新建一个4字节的文件,比如叫tmp。执行 cat z u.dat u_new.dat 将4个字节插到u.dat的文件头,使得新文件的偏移量和原文件相同。 接着,用下面的程序读取 open(10,file=dat(ifn),access='direct', recl=8) do k = 0, n/16-1 do j = k1, k2 do i = k1, k2 read(10, rec = k*n*n+(j-k1)*n+i-k1+2) u_tmp(i,j,k) enddo enddo enddo 这仅仅是针对real*8的读法,但其他格式的做法也类似
个人分类: GNU Linux|8421 次阅读|0 个评论
[FP-LMTO方法] LmtART 7.04 的安装
热度 5 wonvein 2010-9-25 21:24
LmtART使用Fortran 90编写,用完全势线性muffin-tin(FP-LMTO)轨道方法计算电子结构。它在密度泛函理论的框架内进行键结构,总能量,和力的计算。 详细介绍见: 官方主页: http://www.fkf.mpg.de/andersen/docs/lmtoart_programs.html 官方主页: http://www.physics.ucdavis.edu/~mindlab/MaterialResearch/Scientific/index_lmtart.htm 最近用到lmtART这个软件,安装时遇到了问题,发现这个软件并不提供Makefile文件,而且是f和f90混编。为了编译LmtART,我只好自己写了Makefile文件,可以顺利编译。 其安装步骤为: 1 Edit the file man_lmtsetup.f (lines 119-122) and specify the path to the scratch and atomdat directories. Also check that other items match your computer settings. NAMEDENS='/public/home/wangwei/Apps/LmtART/den.' ! atomic density data NAMEATOM='/public/home/wangwei/Apps/LmtART/rat.' ! input atomic files NAMESTRC='/public/home/wangwei/Apps/LmtART/str.' ! structure files NAMELMTO='/public/home/wangwei/Apps/LmtART/lmt.' ! LMTO atom files 2. make Makefile for LmtART: # Makefile for LmtART v7.04 # Written by Wei Wang # 2010/07/26 #------------------------------------------------------------------------------- # Fortran compiler #------------------------------------------------------------------------------- FC=ifort FFLAGS = -O3 AR = ar #------------------------------------------------------------------------------- # Suffix rules #------------------------------------------------------------------------------- .SUFFIXES: .o .f .f.o: $(FC) $(FFLAGS) -c $ .SUFFIXES: .o .f90 .f90.o: $(FC) $(FFLAGS) -c $ #------------------------------------------------------------------------------- # Source files #------------------------------------------------------------------------------- SRC_mod_F77= mod_atoms.f mod_common.fmod_hubbard.fmod_models.f mod_qmc.f mod_supra.f\ mod_dimart.fmod_init.f mod_phonons.fmod_response.fmod_work.f SRC_mod_F90=mod_cls_l_lib.f90mod_cls_l_time.f90 mod_cls_l.f90 SRC_main=man_main.f SRC_F77=bnd_allocate.fchi_hubpar.f del_dtosint.f dmf_lmtqup.f frc_energy.f imp_hybint.f ini_makettr.f lib_ranw.f mdl_init.f opt_optpar.f pot_multasa.f rat_shletot.f \ bnd_bndpar.f chi_intchi.f del_dtospar.f dmf_lmtrat.f frc_forces.f imp_hyblev.f ini_readini.f lib_ratmom.f mdl_mixdmf.f opt_optpsi.f pot_multftr.f rat_shlfiles.f \ bnd_conv.f chi_inthiq.f del_dynbnd.f dmf_lmtrea.f frc_intvar.f imp_impfock.fini_readpnt.f lib_relharm.f mdl_modelhyb.fopt_optptb.f pot_multrho.f rat_shlmat.f \ bnd_coreny.f chi_lmtchi.f del_dynsym.f dmf_mixdmf.f frc_lmtvar.f imp_impfsig.fini_readscf.f lib_simq.f mdl_readmdl.f opt_opttet.f pot_potfiles.frat_siggrf.f \ bnd_fndfrm.f chi_lmtvec.f del_epibnd.f dmf_ordbnd.f frc_pulend.f imp_imphub1.fini_readstr.f lib_sphfun.f mdl_scfhub1.f opt_setopt.f pot_print2.f rat_slater.f \ bnd_ftrmat.f chi_magmat.f del_episym.f dmf_ordvec.f frc_pulint.f imp_impirat.fini_rpr48.f lib_sphharm.f mdl_scfirat.f opt_symopt.f pot_vcftr.f rat_storage.f \ bnd_getenr.f chi_magpar.f del_lmtvec.f dmf_setdos.f ftb_allocate.fimp_impqmcf.fini_storage.f lib_splin3.f mdl_scfqmcf.f phn_ctrlinf.f pot_vcoul.f rat_vcoul.f \ bnd_intmat.f chi_mttrd.f del_nmtfiles.fdmf_setfat.f ftb_denmat.f imp_impratf.fini_test.f lib_splkff.f mdl_scfratf.f phn_deltet.f pot_vexch.f rat_vexch.f \ bnd_intpar.f chi_mttrs.f del_psivec.f dmf_setfrs.f ftb_energy.f imp_impsbmf.flib_broy6.f lib_stnmat.f mdl_scfsbmf.f phn_dynmat.f pot_vxftr.f rho_emader.f \ bnd_lmtbnd.f chi_orbpar.f del_rintpar.f dmf_setgrf.f ftb_ftbbnd.f imp_setdos.f lib_calc.f lib_timel.f mdl_setgrf.f phn_epitet.f pot_vzero.f rho_mixro1.f \ bnd_lmtdft.f chi_printq.f del_rtosint.f dmf_sethyb.f ftb_ftbfat.f imp_sigrat.f lib_cinv.f lib_transcub.f mdl_storage.f phn_init.f qmc_allocate.frho_mixrob.f \ bnd_lmtfat.f chi_setchi.f del_sgmpar.f dmf_sgmdat.f ftb_ftbhub.f imp_sunhub1.flib_cmpdiag.f lib_wigmat.f phn_readlrt.f qmc_ctrlinf.f rho_orbmag.f \ bnd_lmtmat.f chi_sethiq.f del_strvec.f dmf_sgmmas.f ftb_ftbmft.f imp_sunirat.flib_conjgrad.f man_artinp.f phn_runtask.f qmc_getgrf.f rho_rencor.f \ bnd_lmtpar.f chi_setjey.f del_symdps.f dmf_sigmaw.f ftb_ftbpar.f imp_sunsbmf.flib_corsch.f man_artout.f phn_setepi.f qmc_init.f rho_renrov.f \ bnd_nmtpar.f chi_symchi.f del_symdrho.f dmf_tosint.f ftb_ftbstr.f imp_vertex.f lib_csplines.f man_atoms.f phn_setphn.f qmc_qmcpra.f rho_rhofiles.f \ bnd_orbpar.f cls_chem1.f dmf_allocate.fdmf_tospar.f ftb_hubint.f ini_blowmts.flib_cubharm.f man_electrons.fphn_spectra.f qmc_qmcyif.f rho_rhoful.f \ bnd_ovrpar.f cls_chem2.f dmf_denmat.f dmf_ttrint.f ftb_mixden.f ini_ctrlinf.flib_delrad.f man_ftbscf.f phn_storage.f qmc_readqmc.f str_dstr.f \ bnd_placeny.f cls_shellxnot.fdmf_energy.f dpt_delpot.f ftb_symden.f ini_getchi.f lib_deriv1.f man_impscf.f phn_symnuc.f qmc_setgrf.f str_hstr.f \ bnd_potpar.f del_allocate.f dmf_fndfrm.f dpt_delvhub.f hop_alpcon.f ini_getdos.f lib_det.f man_lmtchn.f plz_allocate.fqmc_storage.f str_sitegen.f \ bnd_seny.f del_d2intpar.f dmf_ftbfat.f dpt_dmultftr.fhop_findirr.f ini_getfat.f lib_dgemm.f man_lmtscf.f plz_plipar.f rat_agfmat.f str_strmsh.f \ bnd_setdos.f del_d2lmat.f dmf_ftbima.f dpt_dmultrho.fhop_gethtb.f ini_getfrs.f lib_dilog.f man_lmtsetup.f plz_plsbnd.f rat_allocate.fstr_vecgen.f \ bnd_setenr.f del_d2lmto.f dmf_ftbmas.f dpt_dvcftr.f hop_readhop.f ini_getgrf.f lib_drsub.f plz_plssym.f rat_cgfmat.f sup_ctrlinf.f \ bnd_seteny.f del_d2tosend.f dmf_ftbqup.f dpt_dvcoul.f hop_scrcon.f ini_gethiq.f lib_eigen1c.f man_mdlchn.f odf_allocate.fplz_plwcrd.f rat_clsgor.f sup_elifun.f \ bnd_setfat.f del_d2tosint.f dmf_ftbrat.f dpt_dvexch.f hop_scrstr.f ini_gethyb.f lib_erf.f man_mdlsetup.f odf_ftbodf.f plz_plzbnd.f rat_clsmat.f sup_init.f \ bnd_setfrs.f del_d2vint.f dmf_ftbrea.f dpt_dvxftr.f hop_stralp.f ini_getodf.f lib_forcesym.f man_models.f odf_lmtodf.f plz_plzend.f rat_clssav.f sup_omegaq.f \ bnd_strgnt.f del_delbnd.f dmf_grfbnd.f dpt_gradpot.f hub_hubpar.f ini_getopt.f lib_formt.f man_phead.f odf_odfbnd.f plz_plzfiles.frat_clsyin.f sup_phndos.f \ bnd_sum.f del_deleny.f dmf_grfexp.f dpt_potfiles.fhub_hubpot.f ini_groups.f lib_gradfun.f man_phnchn.f odf_odfint.f plz_plzinl.f rat_ctrlinf.f sup_readdyn.f \ bnd_symtos.f del_delexp.f dmf_grfgrp.f dpt_print3.f hub_mixhub.f ini_init.f lib_hubmat.f man_phnsetup.f odf_odfmat.f plz_plzint.f rat_energy.f sup_readepi.f \ bnd_tosend.f del_delgnt.f dmf_grflev.f dpt_scrpot.f hub_msbmesh.f ini_makeatm.flib_inverse.f man_phonons.f odf_odfpar.f plz_plzmat.f rat_getads.f sup_readsup.f \ bnd_tosint.f del_delmat.fdmf_grfloc.f dro_dbroy4n.f hub_readhub.f ini_makeenv.flib_lsqmom.f man_qmcchn.f odf_odfpsi.f plz_plzmto.f rat_getgrf.f sup_storage.f \ bnd_tospar.f del_delmto.f dmf_grfmat.f dro_delrho.f hub_readrep.f ini_makefft.flib_mklegw.f man_qmc.f odf_odfttr.f plz_plzmts.f rat_init.f sup_ttrint.f \ bnd_ttrint.f del_dhubpar.f dmf_grfpar.f dro_drofiles.fhub_rhohub.f ini_makegnt.flib_morefun.f man_qmcsetup.f odf_setodf.f plz_plzpar.f rat_matatm.f sup_widths.f \ chi_allocate.fdel_dinlmat.f dmf_grfwgt.f dro_gradrho.f imp_agfirat.f ini_makegrp.flib_order.f man_ratchn.f odf_symodf.f plz_plzpin.f rat_mixrat.f ttr_fermicof.f \ chi_chifiles.fdel_dintmat.f dmf_hubmat.f dro_gradrps.f imp_combrep.f ini_makehan.flib_pade.f man_ratsetup.f opt_allocate.fplz_plzsym.f rat_ratden.f ttr_fermiint.f \ chi_chimat.f del_dintpar.f dmf_imphyb.f dro_magdro.f imp_crfhub1.f ini_makeplw.flib_paulim.f man_supchn.f opt_ftbopt.f plz_polarz.f rat_ratmesh.f ttr_mcttrint.f \ chi_chipar.f del_dlmtpar.f dmf_implev.f dro_mixbrd.f imp_crfirat.f ini_makerad.flib_pcoefs.f man_supra.f opt_lmtopt.f plz_screen.f rat_ratscf.f ttr_ttrvel.f \ chi_chitet.f del_dnmtpar.f dmf_impmod.f dro_mixdps.f imp_crfqmcm.f ini_makescf.flib_prattpols.fman_supsetup.f opt_optdhk.f pot_exchcorr.frat_readrat.f \ chi_delmsh.f del_dovrpar.f dmf_lmtfat.f dro_mixdro.f imp_crfsbmf.f ini_makesmt.flib_pzeros.f mdl_allocate.f opt_opthan.f pot_gga91.f rat_setads.f \ chi_ftbchi.f del_dpotpar.f dmf_lmtima.f dro_spldps.f imp_fftsub.f ini_makesym.flib_qd.f mdl_ctrlinf.f opt_optint.f pot_gga96.f rat_setgrf.f \ chi_ftbvec.f del_dtosend.f dmf_lmtmas.f dro_spldro.f imp_hybfun.f ini_maketei.flib_radsch.f mdl_getgrf.f opt_optmat.f pot_lsda.f rat_shells.f SRC_F90=cls_angular1.f90 cls_exactdiag.f90 cls_fockvec1.f90cls_greenfun1.f90cls_hamilton2.f90cls_main1.f90 cls_subrtn.f90qmc_run.f90 qmc_sampling_PC_diag.f90 \ cls_angular2.f90cls_fill1.f90 cls_fockvec2.f90cls_greenfun2.f90cls_impurity.f90 cls_main2.f90lib_rapx.f90 qmc_run_switch.f90qmc_sampling_PC_random.f90 \ cls_bath.f90 cls_fill2.f90 cls_fun.f90cls_hamilton1.f90cls_l_diag.f90 cls_main.f90 qmc_fourier.f90 qmc_sampling.f90 OBJ_mod_F77 = $(SRC_mod_F77:.f=.o) OBJ_mod_F90 = $(SRC_mod_F90:.f90=.o) OBJ_F77 = $(SRC_F77:.f=.o) OBJ_F90 = $(SRC_F90:.f90=.o) OBJ_main = $(SRC_main:.f=.o) OBJ=$(OBJ_mod_F77) $(OBJ_mod_F90) $(OBJ_main) $(OBJ_F77) $(OBJ_F90) EXE = lmtart lmtart: $(OBJ) $(FC) $(FFLAGS) -o $(EXE) $(OBJ) $(LIB) clean: rm -f *.o *.mod *~ fort.* ifc* *.log $(EXE)
个人分类: 量化软件|9659 次阅读|4 个评论
这里有一本fortran编程的书
热度 3 huang840828 2010-9-25 20:24
我今天弄到了一本书,名字是:Sample page from NUMERICAL RECIPES IN FORTRAN 77 THE ART OF SCIENTIFIC COMPUTING 1100多页啊,刚才看了一下。这本书太牛了,几乎面面俱到。谁要是把这本书一个一个地练习一遍,我觉得吧:不但是fortran编程对他小菜一碟,就是有限元以后也不是什么难的东西了。有点夸张啊,太高兴了,原谅原谅! 呵呵,找了一个晚上,最后是有点巧取豪夺弄过来的。谁要的话,可以说一声。大约7M,最好是留QQ邮箱吧! 下面给出本书的目录吧: 目录 分卷压缩: part1 分卷压缩: part2 我压缩的时候没注意,这里下载之后注意要把扩展名.zip改为.rar
个人分类: Fortran|5381 次阅读|16 个评论
最简单的语言和最复杂的语言
热度 1 马红孺 2010-5-5 20:57
最近在学习 Matlab, 据说这是一个功能强大,易学易用的软件。学得很吃力,有那么多函数,怎么都记不住。 记得大学时开了一门课,叫FORTRAN程序设计,一学期,可能是36学时(或54?),没有见过计算机, 什么也没学会,只记得那位老师把FORTRAN读成 伐儿犬。大四做毕设,需要算一个定积分,开始在程序 纸上写程序,送给穿孔员做卡片,再把一叠卡片交给操作员,在机房外面等结果,才开始对Fortran有点认识。 研一时,导师买了一台IBM PC,8086 cpu,128k 内存(后扩充到384k),两个320k的5吋软驱, DOS1.0操作系统,带一个BASIC程序。开始用Basic算题。过了一阵,升级到dos2.0,又买了 Fortran编译器,突然发现用Fortran比用Basic快了很多,于是知道了编译语言和解释语言的差别。 在导师的带领下,我们一帮师兄弟在这台机器上制造了50余篇文章,还拿了一个自然科学四等奖。 过了一阵,感觉Fortran还是不够快,于是便拿着Dos2.0的说明书,用Debug写汇编程序,果然速度 又有提高。 再后来,计算机不断升级,但一直用Fortran写程序,从Fortran 4, 再到Fortran 77。 在用了10多年Fortran后,开始带学生了,突然发现学生竟然不懂Fortran,而是用C写程序。 为了与时俱进,借着给学生看程序的机会学会了C编程,后来为了赶时髦,又写过一点Java 程序。 比较起来,汇编语言是最简单的,学起来最容易,上手也最快,但要写一个大一点的程序, 需要付出的劳动最大。 Fortran和C差不多, Java稍为复杂一些,主要是比Fortran和C多了一些 概念。感觉最复杂的是 Matlab,折腾了两个星期了,还是记不住那些函数和命令。
个人分类: 胡言乱语|13913 次阅读|12 个评论
详论fortran格式化输出
dabing 2009-11-9 22:27
格式化输出的控制字符非常的丰富,但常用的并不多,一般说来: I 、F、E、A、X 是最常使用的几个格式,最好把它们都记下来。 Iw 以w个字符的宽度来输出整数,至少输出m个数字。 如:write(*,(I5)) 100 输出:_ _100 ; 前面两空格 Fw.d 以w个字符文本框来输出浮点数,小数部分占d个字符宽,输出文本框的设置不中会出现*号。如:write(*,(F9.3)) 123.45 输出:_ _123.450 ; 前面两空格,后补0 Ew.d 用科学计数法,以w个字符宽来输出浮点数,小数部分占d个字符宽,指数部分最少输出e个数字。如:write(*,(E15.7) 123.45 输出:_ _0.1234500E+03 ; 输出不中15个字符的部分补上空白,小数部分不足7位的会补0 Dw.d 使用方法同Ew.d,差别在于输出时用来代表指数的字母由E换成D。 Aw 以w个字符宽来输出字符串。 write(*,(A10)) Hello 固定用是为10我个字符段来输出字符串,不足的前面补空格 nX 输出位置向右移动n位。write(*,(5X,I3)) 100 ; 将先填5个空格,再输出整数。 Lw 以w个字符宽来输出T或F的真假值。write(*,(L4)) .true. ;程序会输出3个空格和一个T / 换行输出。write(*,(I3//3)) 10,10 程序会得出4行,中间两行空格是从除号/得到的。 Tc 把输出的位置移动到本行的第c个字节。 TLn 输出位置向左相对移动n个字节。 TRn 输出位置向左相对移动n个字节。 SP、SS 加了SP后,输出数字时如数值为正则加上+,SS则是用来取消SP的功能。 如 write(*,(SP , I5 , I5 , SS , I5)) 5 , 5 , 5 输出:+5 +55 BN、BZ BN定义在输入时没有数据的字节代表没有东西。BZ定义在没有数据的字节代表0 另有 kP 、Bw 、Ow 、Zw 用的较少,在需要时可以查询。 2009-11-09于承德
个人分类: Fortran文档|39064 次阅读|1 个评论

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-6-16 10:57

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部