fortran - 悲催的科学匠人 - 冷水's blog
我被硬特fortran编译器坑的事迹
最早先,gfortran下没有问题的代码在硬特编译后出现segment fault。结果发现数组整体赋值出了问题,比如
w=w1
只要数组w和w1特别大,就fault了。只好遇到比较大的数组,就把用循环。
DO k=0,kb w(:,:,:,k)=w1(:,:,:,k) ENDDO
最近又被坑了一次,而且完全与上面这个经验相反。
id=1
DO n=1,num
! get i,j,k
...
DO l=ivs,ivt
buff(id)=w(l,i,j,k)
id = id +1
ENDDO
ENDDO
这个片段是将w中的元素按照给定的顺序填充到一维数组buff中。
按照debug模式编译没有问题。但是优化编译后出错。最后改为
id=1 ncmp = ivt-ivs+1 DO n=1,num ! get i,j,k ... buff(id:id+ncmp-1)=w1(ivs:ivt,i,j,k) id = id + ncmp ENDDO
gfortran的类型绑定方法还是没法用
至少在 gfortran 4.5上还不行
本来希望能够采用类似C++那样的成员函数调用方式
CALL obja%fun1(arg1, arg2, arg3)
结果不太行
只好改回老式的
CALL fun1(obja, arg1, arg2, arg3 )
可能在新版上可以,或者intel fortran
不过不想换编译器了
合并plot3d function文件
tecplot不支持导入多个function文件,这导致我计算出的速度数据文件和压强数据文件以及涡量数据文件没法在一个图中操作显示。
没法,写个小玩意克服一下
使用方法
$ mergep3df newp3df newname p3df1 name1 p3df2 name2 p3df3 name3 ...
MODULE mergep3d
CONTAINS
SUBROUTINE mergep3dfunc(ndatafiles, datafiles, &
nb,ni,nj,nk, nvar, newp3df)
IMPLICIT NONE
INTEGER,INTENT(IN) :: ndatafiles, nvar(:)
CHARACTER(LEN=*),DIMENSION(:),INTENT(IN) :: datafiles
CHARACTER(LEN=*),INTENT(IN) :: newp3df
INTEGER,POINTER,DIMENSION(:) :: ni,nj,nk
INTEGER,INTENT(in) :: nb
! local
INTEGER nvars, tmpint, b, n, i,j,k,l
INTEGER,POINTER,DIMENSION(:) :: offset, funit,shift
REAL(4),POINTER,DIMENSION(:,:,:,:) :: gdata
nvars = SUM( nvar(1:ndatafiles) )
OPEN(33,FILE=TRIM(newp3df),action='write', form='unformatted')
WRITE(33) nb
WRITE(33) (ni(b),nj(b),nk(b),nvars, b=1,nb)
ALLOCATE( offset(ndatafiles), funit(ndatafiles), shift(ndatafiles) )
offset=0
shift = 0
DO n=1, ndatafiles
funit(n) = 1000 + n
OPEN(UNIT=funit(n),FILE=TRIM(datafiles(n)),action='read', form='unformatted')
READ(funit(n)) tmpint
READ(funit(n)) (tmpint,tmpint,tmpint,tmpint, b=1,nb)
offset(n) = FTELL(funit(n))
shift(n+1) = shift(n) + nvar(n)
ENDDO
DO b=1, nb
ALLOCATE( gdata(ni(b),nj(b),nk(b), nvars) )
DO n=1, ndatafiles
READ(funit(n)) (( ((gdata(i,j,k,l),i=1,ni(b)),j=1,nj(b)),k=1,nk(b) ), &
l=shift(n)+1,shift(n+1) )
ENDDO
WRITE(33) (( ((gdata(i,j,k,l),i=1,ni(b)),j=1,nj(b)),k=1,nk(b) ), l=1,nvars )
ENDDO
END SUBROUTINE
SUBROUTINE checkp3dfunc(ndatafiles, datafiles, &
nb, ni, nj, nk, ierr)
IMPLICIT NONE
INTEGER,INTENT(IN) :: ndatafiles
CHARACTER(LEN=*),DIMENSION(:),INTENT(IN) :: datafiles
INTEGER,POINTER,DIMENSION(:) :: ni,nj,nk
INTEGER,INTENT(OUT) :: nb, ierr
! local
INTEGER :: n,b,db,nfunc,dfunc
INTEGER,DIMENSION(:),POINTER :: di,dj,dk
OPEN(33,FILE=TRIM(datafiles(1)),action='read', form='unformatted')
READ(33) nb; WRITE(*,*) 'nblock=',nb
ALLOCATE(ni(nb),nj(nb),nk(nb), &
di(nb),dj(nb),dk(nb), )
READ(33) (ni(b),nj(b),nk(b),nfunc, b=1,nb)
WRITE(*,*) 'done read'
!DO n=1,nb
! WRITE(*,*) ni(n),nj(n),nk(n),nvar(n)
!ENDDO
CLOSE(33)
ierr = 0
DO n=1,ndatafiles
OPEN(33,FILE=TRIM(datafiles(n)),action='read', form='unformatted')
READ(33) db
IF(db /= nb) THEN
ierr = 1
CLOSE(33)
RETURN
ENDIF
READ(33) (di(b),dj(b),dk(b),dfunc, b=1,nb)
DO b=1,nb
IF(di(b) /= ni(b) .OR. dj(b) /= nj(b) .OR. dk(b) /= nk(b) ) THEN
WRITE(*,*) 'block ',b, di(b),dj(b),dk(b), ni(b),nj(b),nk(b)
ierr = 2
CLOSE(33)
RETURN
ENDIF
ENDDO
CLOSE(33)
ENDDO
END SUBROUTINE
SUBROUTINE getvarnames(ndatafiles, datafiles,varnames, nvar)
IMPLICIT NONE
INTEGER,INTENT(OUT) :: ndatafiles
INTEGER,DIMENSION(:), INTENT(OUT) :: nvar
CHARACTER(LEN=*),DIMENSION(:),INTENT(OUT) :: varnames,datafiles
! local
CHARACTER(LEN=128) :: tmpstr
INTEGER :: IO, argn,n,i
LOGICAL :: notend
argn = command_argument_count()
ndatafiles = (argn - 2)/2
WRITE(*,*) 'argn=',argn,'ndatafiles =',ndatafiles
i=0
DO n=1,ndatafiles
nvar(n)=0
CALL get_command_argument(n+n+1, datafiles(n))
CALL get_command_argument(n+n+2, tmpstr)
!WRITE(*,*) tmpstr
OPEN(33,FILE=TRIM(tmpstr))
notend = .TRUE.
DO WHILE (notend)
READ(33, *, IOSTAT = IO ) tmpstr
IF(IO>=0) THEN
nvar(n) = nvar(n) + 1
i = i + 1
varnames(i) = tmpstr
WRITE(*,*) i, TRIM( varnames(i) )
ELSE
notend = .FALSE.
ENDIF
ENDDO
CLOSE(33)
ENDDO
!DO n=1,i
! WRITE(*,*) TRIM( varnames(n) )
!ENDDO
!WRITE(*,*) nvar(1:ndatafiles)
END SUBROUTINE
SUBROUTINE mergename(ndata, nvar, varnames, namefile)
INTEGER,INTENT(IN) :: ndata
INTEGER,DIMENSION(:),INTENT(IN) :: nvar
CHARACTER(LEN=*),DIMENSION(:),INTENT(IN) :: varnames
CHARACTER(LEN=*),INTENT(IN) :: namefile
! local
INTEGER :: i,j,n
OPEN(33,FILE=TRIM(namefile))
j = 0
DO n=1, ndata
DO i=1, nvar(n)
j=j+1
WRITE(33, *) TRIM(varnames(j))
ENDDO
ENDDO
END SUBROUTINE
END MODULE
PROGRAM main
USE mergep3d
IMPLICIT NONE
INTEGER :: ndata,nvar(1024),nb,ierr
INTEGER,POINTER,DIMENSION(:) :: ni,nj,nk
CHARACTER(LEN=64) :: fnames(1024),varnames(1024),newp3df,newname
CALL getvarnames(ndata, fnames, varnames, nvar)
CALL checkp3dfunc(ndata, fnames, nb,ni,nj,nk, ierr)
WRITE(*,*) 'ierr=',ierr
CALL get_command_argument(2,newp3df)
CALL get_command_argument(3,newname)
CALL mergep3dfunc(ndata, fnames, nb,ni,nj,nk, nvar, TRIM(newp3df) )
CALL mergename(ndata, nvar, varnames, TRIM(newname) )
END
【轮子DIY】一个比较通用的fortran90程序make系统
一个项目中的副产品,目的是为fotran90项目构造简单的make系统
目录结构是主目录下必须包含bin,src和make目录。make目录下是一些makefile,而src就是源代码了。由于fortran的module特殊性,如果有module,src必须包含一个mod目录。如果包含fortran77代码,我也专门开辟一个f77目录包含这些文件,当然叶可以采用其它办法。此外,如果包含混编的c代码,我放入anisc目录。其它代码那找类别分别创建src的子目录即可。一个例子如下
poject
+---bin
+---make
+---src
+---mod
+---f77
+---anisc
+---main
make目录中关键的文件有
- make.inc,定义所有编译参数
- make_mod,专用于编译module
- make_f77,专用于编译fortran77代码
- make_clib,专用于编译c文件
- make_sub,用于编译其它src下子目录中fortran90代码
- Makefile,主make文件
make.inc
# CMODE
# options:
# gnu,
# pgi,
# intel
COMPILER=gnu
# path
PROJECT_HOME=..
SRCDIR=$(PROJECT_HOME)/src
OBJDIR=$(PROJECT_HOME)/obj_$(COMPILER)/$(CMODE)
MODDIR=$(PROJECT_HOME)/mod
BINDIR=$(PROJECT_HOME)/bin
# PGI fortran and C
ifeq ($(COMPILER),pgi)
FC=mpif90
LINK=mpif90
CC=pgcc
ifeq ($(CMODE),debug)
FFLAGS0 = -module $(OBJDIR) -g -r8 -Kieee -C
else
CMODE = release
FFLAGS0 = -module $(OBJDIR) -O2 -r8 -Kieee
endif
ARCH = ar
ARCHFLAG = cr
endif
# Intel fortran and C
ifeq ($(COMPILER),intel)
FC=mpif90
LINK=mpif90
CC=icc
ifeq ($(CMODE),debug)
FFLAGS0 = -module $(OBJDIR) -g -r8 -check all
else
CMODE = release
FFLAGS0 = -module $(OBJDIR) -O2 -r8
endif
ARCH = ar
ARCHFLAG = cr
endif
# GNU fortran and C
ifeq ($(COMPILER),gnu)
FC=mpif90
LINK=mpif90
CC=gcc
ifeq ($(CMODE),debug)
FFLAGS0 = -J$(OBJDIR) -fdefault-real-8 -frecord-marker=4 -g -fcheck=all -fbacktrace
else
CMODE = release
FFLAGS0 = -J$(OBJDIR) -O2 -fdefault-real-8 -frecord-marker=4
endif
ARCH = ar
ARCHFLAG = cr
endif
FFLAGS = $(FFLAGS0)
# LIB dir 这里加入必要的库和路径
LIBDIR= #-L$(BLASDIR) -L$(LAPACKDIR)
LIBS = #
# output exe file, 你可以修改run为其它名称
EXE=$(BINDIR)/run_$(COMPILER)_$(CMODE)
make_f77
include make.inc MYSRC= $(wildcard $(SRCDIR)/$(SUB)/*.f) MYTMP= $(patsubst %.f,%.o, $(MYSRC)) MYOBJ= $(patsubst $(SRCDIR)/$(SUB)%,$(OBJDIR)%, $(MYTMP)) all: $(MYOBJ) $(MYOBJ): $(OBJDIR)/%.o: $(SRCDIR)/$(SUB)/%.f $(FC) -c $(FFLAGS) $< -o $@ clean: rm -f $(MYOBJ) submit:
make_clib
include make.inc MYSRC= $(wildcard $(SRCDIR)/$(SUB)/*.c) MYTMP= $(patsubst %.c,%.o, $(MYSRC)) MYOBJ= $(patsubst $(SRCDIR)/$(SUB)%,$(OBJDIR)%, $(MYTMP)) all: $(MYOBJ) $(MYOBJ): $(OBJDIR)/%.o: $(SRCDIR)/$(SUB)/%.c $(CC) -c $< -o $@ clean: rm -f $(MYOBJ) submit:
make_sub
include make.inc MYSRC= $(wildcard $(SRCDIR)/$(SUB)/*.f90) MYTMP= $(patsubst %.f90,%.o, $(MYSRC)) MYOBJ= $(patsubst $(SRCDIR)/$(SUB)/%,$(OBJDIR)/$(DEST)/%, $(MYTMP)) all: $(MYOBJ) $(MYOBJ): $(OBJDIR)/$(DEST)/%.o: $(SRCDIR)/$(SUB)/%.f90 $(FC) -c $(FFLAGS) $< -o $@ clean: rm -f $(MYOBJ) submit:
make_mod
include make.inc
SUB=mod
# 这里必须按照一定顺序填写module源代码文件
MYSRC = $(SRCDIR)/$(SUB)/mod1.f90 \
$(SRCDIR)/$(SUB)/mod2.f90 \
$(SRCDIR)/$(SUB)/mod3.f90
MYTMP= $(patsubst %.f90,%.o, $(MYSRC))
MYOBJ= $(patsubst $(SRCDIR)/$(SUB)%,$(OBJDIR)%, $(MYTMP))
all: $(MYOBJ)
$(MYOBJ): $(OBJDIR)/%.o: $(SRCDIR)/$(SUB)/%.f90
$(FC) -c $(FFLAGS) $< -o $@
clean:
rm -f $(MYOBJ)
Makefile
include make.inc TMPLIB = $(OBJDIR)/../libmytmp_$(CMODE).a all: objs main main: $(TMPLIB) $(LINK) $(TMPLIB) $(LIBDIR) $(LIBS) $(FFLAGS) -o $(EXE) #编译所有obj文件并构造库,在make_mod后列出所有src子目录的编译命令 make -f make_sub "SUB=子目录名称" objs: make -f make_mod make -f make_sub "SUB=main" make -f make_f77 "SUB=f77" make -f make_clib "SUB=anis_c" $(ARCH) $(ARCHFLAG) $(OBJDIR)/../libmytmp_$(CMODE).a $(OBJDIR)/*.o #删除所有obj文件、mod文件和库,在make_mod后列出所有src子目录的编译命令 make -f make_sub "SUB=子目录名称" clean 来清理给定类别的obj文件 #这里只是举例说明可以指定clean一部分文件,在某些场合下可以采用这个方法进行较为精确的clean控制 cleanall: make -f make_mod clean make -f make_sub "SUB=main" clean make -f make_f77 "SUB=f77" clean make -f make_clib "SUB=anis_c" clean rm -f $(OBJDIR)/../libmytmp_$(CMODE).a rm -f $(OBJDIR)/*.mod #粗暴的删除所有编译生成的文件, clean: rm -f $(OBJDIR)/../libmytmp_$(CMODE).a rm -f $(OBJDIR)/*.mod rm -f $(OBJDIR)/*.o #创建obj文件目录 dir: mkdir ../obj_$(COMPILER) mkdir ../obj_$(COMPILER)/debug mkdir ../obj_$(COMPILER)/release
具体使用时,只需要为module手工填写文件名,其它非module的代码一律自动寻找。第一次编译,需要先 make dir创建目录,而后再make。默认编译优化版本,使用make CMODE=debug会编译调试版本。