fortran调用abaqus如何调用C++写成的库函数

C/C++调用Fortran的使用说明
来源:C/C++ 作者:C/C++
[字体:大 中 小]
这里将详细介绍一下在C++中如何调用用Fortran语言编写函数的问题,即Fortran与C++的混合编程问题。
通常情况下,C++与Fortran的混合编程问题是利用动态链接库的方式进行的,换句话说,如果在C/C++中调用Fortran函数,应将Fortran函数按照一定的协议编译为动态链接库,然后即可实现二者的混合编程问题。实现这一目的有两种方式:显示链接与隐式链接,下面通过两个非常简单的实例分别介绍之。
编译环境:Fortran:推荐使用Compaq Visual Fortran 6.0以上的版本,C++:Microsoft Visual C++6.0。
显式链接,
(a) 找开CVF编译器,然后新建一个Fortran DLL工程(选择Fortran Dynamic Link Library),并指定工程名,如下图所示:
按确定按钮,然后新建一个Fortran 源文件,并输入以下的Fortran代码:
(这里仅为说明问题,实际的问题比下面的代码要复杂得多。)
SUBROUTINE OUTPUT(N)
!必须声明本函数为输出函数:DLLEXPORT
!DEC$ ATTRIBUTES DLLEXPORT::OUTPUT  
IMPLICIT NONE
WRITE(*,*) "N=",n
END SUBROUTINE OUTPUT
经编译,链接后,将在Debug目录下生成两个文件,即dll文件与lib文件。
(b) 启动Visual C++6.0,然后新建一个console工程(即Win32 Console Application,当然在MFC中也是完成可行的),如下图所示:
按确定按钮,新建一个空的工程后,再新建一个C++源文件,并输入以下的代码:
#include &iostream.h&
#include &windows.h&
int main()
typedef void (_stdcall * wndProc)(int& );
HINSTANCE hLibrary=LoadLibrary("pp.dll"); //加载动态库文件
if(hLibrary==NULL)
cout&&"can't find the dll file"&&
return -1;
wndProc test=(wndProc)GetProcAddress(hLibrary,"OUTPUT"); //获得Fortran导出函数的地址
if(test==NULL)
cout&&"can't find the function file."&&
return -2;
test(n); //调用fortran函数
FreeLibrary(hLibrary); //卸载动态库文件
当编译通过后,将由fortran编译器生成的动态库文件(本算例为pp.dll)复制到C++的Debug文件夹中(即mm\debug),然后点击执行即可,程序的动行结果为:
与上文所介绍的显示链接相比,隐式链接要相对容易一点。下面也通过一个算例进行说明。
(1) 建立一个Fortran动态库文件,其方法与上述完全相同,然后在CVF编译器中输入以下的代码:
SUBROUTINE OUTPUT(N)
!下面为对Fortran函数的声明
!ms$if .not. defined(LINKDIRECT)
!ms$attributes dllexport :: OUTPUT
IMPLICIT NONE
WRITE(*,*) "N=",n
END SUBROUTINE OUTPUT
编译通过后,将在Fortran工程文件夹的Debug文件夹中生成两个文件,即*.dll文件与*.lib文件。
(2) 新建一个C++ Console项目,并新建一个C++文件,然后输入以下的代码:
#include &iostream.h&
#include &windows.h&
//声明函数OUTPUT为extern型的,即是从外部调用的。
extern "C" void _stdcall OUTPUT(int& n);
int main()
OUTPUT(n);
然后将Fortran编译器生成的两个文件(dll文件与lib文件)复制到C++的当前目录下,并将lib文件加入到当前的C++工程项目中,如下图所示:
点击菜单“工程-添加工程-Files”,然后选中pp.lib文件即可,如下图所示:
此时即可通过编译,执行此程序,其输出结果如下所示:
本文通过两个简单的实例详细介绍了如何在C++中调用Fortran函数的两种方法,即显式链接与隐式链接。当然实际中我们所遇到的问题将会比本实例要复杂得多,本文仅作为抛砖引玉之用。显式链接与隐式链接两种方法均各自有其优缺点,但由于隐式链接要比显示链接容易得多,也易于理解,实际的大部分算例均采用这种方式。
在linux中操作
对于通常的用户,接促C语言的较多,但是,C语言的开发者,有时还想利用高效的Fortran 数据包 或者是Fortran语言的开发者想借用C提供的强大辅助功能,为此,为了方便大家对二语言相互调用的学习。这里给出了一些简单的入门性技术介绍。
1. C 调用Fortran
编辑Fortran 源文件 add.f95
subroutine add(a, b, c)
implicit none
real:: a, b, c
! the add routine
write(*,*) a, ’+’, b, ’=’, c
end subroutine add
这里部介绍Fortran的语法格式,add.f95的功能是建立一个add routine 也就是函数。
编译 add.f95
f95 -o add.o -c add.f95
编辑C 主程序main.c
1 #include &stdio.h&
3 extern void add_(float *a, float *b, float *c);
5 int main(int argc, char *argvs[])
float a, b,
add_(&a, &b, &c);
3: 声明你要调用的函数,这里注意,是add_,调用的时候用指针,即变量地址,你应该明白了Fortran的调用是改变参数值的。
10:调用fortran函数
编译与执行
#gfortran -o main main.c add.o
NOTES; if you use the gcc tool, it will generate error
#gcc -o main main.c add.o
add.o: In function `add_’:
add.f95:(.text+0x4c): undefined reference to `_gfortran_st_write’
add.f95:(.text+0x69): undefined reference to `_gfortran_transfer_real’
add.f95:(.text+0x87): undefined reference to `_gfortran_transfer_character’
add.f95:(.text+0xa4): undefined reference to `_gfortran_transfer_real’
add.f95:(.text+0xc2): undefined reference to `_gfortran_transfer_character’
add.f95:(.text+0xdf): undefined reference to `_gfortran_transfer_real’
add.f95:(.text+0xed): undefined reference to `_gfortran_st_write_done’
collect2: ld returned 1 exit status
Some Wrong with your PATH for you gcc library
OK! you are clever and has known how call fortran routines in the C progaram. Next part I show you how to call C routines in the Fortran program!
2, Fortran 调用 C 函数
编辑C源码add.c
1 #include &stdio.h&
3 void add_(float *a, float *b, float *c)
*c = *a + *b;
printf(\"%f + %f = %f\\n\", *a, *b, *c);
Notes The funtion name \"add_\"
编译源代码
# gcc -o add.o -c add.c
编辑fortran 主程序main.f95,编译和执行
1 PROGRAM MAIN
implicit none [Page]
real:: i, j, k
! The first fortran program
call add(i, j, k)
11 end program MAIN
NOTE 9 line
# gfortran -o main main.f95 add.o (or f95 -o main main.f95 add.o)
5.000000 + 8.000000 = 13.000000
OK. If you reach here, congratulations, you have learned how to call routines between C and fortran!
Calling Fortran routines from C++
Example-1: Calling routines and functions
The following sample shows how Fortran routines and functions can be called from a C++ program.
(1) The C++ file:
This illustrates how a Fortran routine and function may be
called from a main program in C++
#include &iostream.h&
extern "C"
void __stdcall FR1(int*,int *);
int __stdcall FF1(int *);
int main()
int n=10,nSq,nC
FR1(&n,&nSq);
cout && "The square is:" && nSq &&
nCube=FF1(&n);
cout && "The Cube is:" && nCube &&
(2) The Fortran file:
SUBROUTINE FR1(N,M)
COMPUTES THE SQUARE OF N, RETURNS IN M
INTEGER FUNCTION FF1(N)
COMPUTES THE CUBE OF N
Back to Top
Example-2: Passing C char string to a Fortran routine
The following sample shows how a C char string may be passed from a C++ program to a Fortran routine.
(1) The C++ file:
This illustrates how a Fortran routine may be
called from a main program in C++, and a char[] string passed to it
#include &iostream.h&
#include &string.h&
extern "C"
void __stdcall FR1(int *,int *,char *);
int main()
int n=10,nSq;
char szCtest[20];
strcpy(szCtest,"teststring");
FR1(&n,&nSq,szCtest);
cout && "The square is:" && nSq &&
(2) The Fortran file:
SUBROUTINE FR1(N,M,CSTR)
INTEGER*4 CSTR(1)
HERE WE RECEIVE THE C CHAR STRING IN AN INTEGER ARRAY
COULD ALSO HAVE USED A BYTE ARRAY
WRITE(6,20) (CSTR(L),L=1,3)
formAT(' CSTR=',3A4)
WRITE(6,*) 'DONE'
Back to Top
Example-3: Passing arrays to a Fortran routine
The following sample shows how arrays may be passed from a C++ program to a Fortran routine.
(1) The C++ file:
// Illustrate passing integer and floating point arrays
// from C++ to Fortran
#include &iostream.h&
extern "C"
int __stdcall SUMIT(int *,int*);
float __stdcall MEAN(float*,int*);
int main()
int iA[]={3,5,6,7,2,3,4,5,11,7},iN=10,iS
float fpA[]={1.2f,3.f,44.f,2.5f,-1.3f,33.44f,5.f,0.3f,-3.6f,24.1f},fpM
iSum=SUMIT(iA,&iN);
cout && "The Sum of iA is:" && iSum &&
fpMean=MEAN(fpA,&iN);
cout && "The Mean of fpA is:" && fpMean &&
(2) The Fortran file:
INTEGER FUNCTION SUMIT(IA,N)
INTEGER IA(1)
DO 50 J=1,N
ISUM=ISUM+IA(J)
SUMIT=ISUM
REAL FUNCTION MEAN(RA,N)
REAL RA(1)
DO 50 J=1,N
SUM=SUM+RA(J)
IF(N.GT.0) MEAN=SUM/FLOAT(N)
Back to Top
Calling C++ routines from Fortran
The following examples work with Microsoft Visual C++ and Compaq Visual Fortran. Your mileage may vary on other systems.
Example-1: Calling routines and functions
The following sample shows how C++ routines and functions can be called from a Fortran program.
(1) The Fortran file:
INTEGER CR2
CALL CR1(N,M)
WRITE(6,20) N,M
formAT(' The square of',I3,' is',I4)
WRITE(6,30) N,K
formAT(' The cube of',I3,' is',I15)
(2) The C++ files:
extern "C"
void __stdcall CR1(int *,int *);
int __stdcall CR2(int *);
void __stdcall CR1(int *n, int *m)
// Compute the square of n, return in m
int __stdcall CR2(int *n)
Compute the cube of n
Back to Top
Further Reading
These are some other sources of information.
Digital Visual Fortran Programmer's Guide, esp. the chapter titled "Programming with Mixed Languages". This online book is included with all recent versions of the compiler. The book is also available for download in PDF format by clicking here.
Mixed-Language Issues (from Microsoft)
Also see Microsoft's C Calls to Fortran page.
Mixed Language Programming using C++ and Fortran 77 by Carsten A. Arnholm has many examples.
Mixed Language Programming - Fortran and C by Allan, Chipperfield and Warren-Smith is another good source.
FTN95 Mixed Language Programming from the University of Salford.
A Comparison of C++, Fortran 90 and Oberon-2 for Scientific Programming by Bernd M?sli.
Interfacing Fortran and C by Janne Saarela.
Mixed Language Programming from Pittsburgh Supercomputing Center.
Some examples from DEC:
Visual C/C++ Calling Visual Fortran DLL Example
Visual Fortran Calling Visual C Example
Visual Basic - Visual Fortran DLL Example
Will C++ be faster than Fortran? by T.L.Veldhuizen and M.E.Jernigan.
Interfacing Absoft Fortran with C.
Mixing ANSI-C with Fortran 77 or Fortran 90 by B. Einarsson.
Comparison of C++ and Fortran 90 for Object Oriented Scientific Programming by J.Cary, S.Shasharina, J.Cummings, J.Reynders and P.Hinker.
Fortran and C Programming from Iowa State University.
Win32 Fortran Compiler Comparisons by John Appleyard.
Calling Fortran Routines from C/C++ by J. Thornburg.
f2c - Fortran to C Converter
------------------------------file:a.cpp-------------
#include &iostream.h&
#include &math.h&
extern "C"
void _stdcall TESTA(int *);
void main()
a = sin(1.00);
cout&&"a="&&a;
TESTA(&ii);
------------------------------file:b.f-------------
SUBROUTINE TESTA(IA)
WRITE(*,*) "IA=", IA
编译提示错误:
Linking...
dfor.lib(matherr.obj) : error LNK2005: __matherr already defined in LIBCD.lib(matherr.obj)
LINK : warning LNK4098: defaultlib "libc.lib" conflicts wi use /NODEFAULTLIB:library
Debug/StaggeredGrid.exe : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.
经过分析发现是因为在main函数里面调用了sin数学函数,解决的方法是添加文件头:
#pragma comment (lib, "dfor")
FORTRAN/C/C++混合编程
RTRAN 混合编程
混合编程在软件编程中是经常遇到的问题,尤其是C/C++/FORTRAN的混合编程,本文主要说明以上三种语言混合编程中经常遇到的问题,同时,也说明了不同平台下混合编程应注意的...
通过DLL实现Fortran和C++接口
Fortran和C++各有优势,C++无论是在编写Windows窗口还是在编程效率上都远远优于Fortran,但Fortran也有C++做不到的地方,在数值计算领域,Fortran的计算速度是众所周知...
(一)c++调用自己编写的静态库
c++调用自己编写的静态库
1.静态库的介绍:
一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,静态库与汇编生成的目标文件一起链接为可执行文件。
2. 静态库的优缺点:
编译C++与Fortran
编译C++与FortranGCC 是 GNU 编译器集合(GNU Compiler Collection)的首字母缩写词。GNU 编译器集合包含 C,C++,Objective-C,Fortran,J...
c++调用fortran程序库主要有两种方法,下面以在windows+mingw(g77,g95)下的例子进行说明:1. 静态连接首先要注意fortran和C++程序中函数名称的不同,下面是fortr...
编写DLL和调用DLL的程序后,首先将该程序复制到DLL项目文件的DEBUG目录下,接着在VS中打开FORTRAN DLL项目的&项目属性& 对象框,切换到&debugging&标签.设置&Comma...
怎样从C 中调用FORTRAN
Ada,LISP) 的函数?反之亦然? 这完全依赖于机器以及使用的各个编译器的特别调用顺序, 有可能完全做不到。仔细阅...
一个c语言的应用程序,实现在c中显示&em&调用fortran&/em&函数...&em&fortran调用&/em&C++ &em&dll&/em& 立即下载
上传者: ysp8293 时间...欧洲能源一哥照样用区块链玩转&em&2B&/em&的生意
人物志 ...
文件夹&em&c&/em& and fortran内是制作的winform程序&em&调用dll&/em&,按钮start开启线程,&em&调用fortran&/em&第一个方法,开启无限计算方法,使用timer计时器实时调用第二个方法,得到当前计算值。...
没有更多推荐了,I m including a fortran90 program that is not mine in my C++ project .
In the first stept I try to call the function by their name_() and i get the error
"undefined reference to mp_mpi_cartesian_init_ "by dispalying the symbol of the obj file (using nm) i found that the function are called by their module as module_function_ so i add the module name and i Get the same problem but between fortran obj
such as "Constants.f90:(.text+0x36): undefined reference to __powi4i4"
here is the c++ code :
#include &iostream&
#include &complex&
extern"C" {
mod_save_wave_mp_read_it_psi_(int * it,complex&double&*
psi_E1E2 );
mod_mpi_cartesian_mp_mpi_cartesian_init_( );
extern int mod_mpl_h_mp_iproc_ ;
complex&double&
psi_local[512*24*512*24];
mod_mpi_cartesian_mp_mpi_cartesian_init_();
cout && "proc
:" && mod_mpl_h_mp_iproc_ && "avant lecture\n";
mod_save_wave_mp_read_it_psi_(&it,psi_local);
cout && "psi ="&& psi_local[0] && "poiur le proc "&&mod_mpl_h_mp_iproc_ &&"\n";
and this is an exemple of a module :
MODULE mod_save_wave
USE mod_constants
USE mod_MPI_CARTESIAN
USE mod_time_mesure,
ONLY : tempsEcoule
USE mod_input_data, ONLY : Nt_laserPsansLaser
USE mod_input_data, ONLY : n_phi, n_rho1_seg, n_rho2_seg
USE mod_input_data, ONLY : Nt_periode, save_periodique
!////////////////////////////////////////////////////////////////
IMPLICIT NONE
REAL(kind=d_t)
:: prog_start_time, time_max_second !
character(len=80)
:: IntermedWaveDir
!================================================================
SUBROUTINE begin_count_time()
IMPLICIT NONE
prog_start_time = tempsEcoule()
END SUBROUTINE begin_count_time
SUBROUTINE READ_IT_PSI( it,
psi_E1E2 )
IMPLICIT NONE
!////////////////////////////////////////////////////////////////////////////////
COMPLEX(kind=d_t), DIMENSION(n_phi,n_rho1_seg,n_phi,n_rho2_seg) :: psi_E1E2 !
!================================================================================
integer :: c
do c = 0, c_max-1
if( mod(iproc,c_max)==c ) then
!////////////////////////////////////////////////////////////////////////////////
OPEN( unit=11,file=concat(trim(IntermedWaveDir),concat(concat('BACK/wave_',str_iproc),'_2p2p2')),&
status='old', form='unformatted', MODE='READ'
READ(11) it
READ(11) psi_E1E2
print*,'iproc,readed it=',iproc, it
CALL MPI_BARRIER(MPI_COMM_WORLD,infompi)
!================================================================================
!================================================================================
END SUBROUTINE READ_IT_PSI
SUBROUTINE WRITE_IT_PSI( it, psi_E1E2 )
IMPLICIT NONE
!////////////////////////////////////////////////////////////////////////////////
COMPLEX(kind=d_t), DIMENSION(n_phi,n_rho1_seg,n_phi,n_rho2_seg) :: psi_E1E2 !
!================================================================================
integer :: c
do c = 0, c_max-1
if( mod(iproc,c_max)==c ) then
!////////////////////////////////////////////////////////////////////////////////
OPEN( unit=11,file=concat(trim(IntermedWaveDir),concat(concat('wave_',str_iproc),'_2p2p2')),&
form='unformatted') !
WRITE(11) it+1
!---- recommence a partir de la prochaine iterat!
write(11) psi_E1E2
CALL MPI_BARRIER(MPI_COMM_WORLD,infompi)
!================================================================================
END SUBROUTINE WRITE_IT_PSI
END MODULE mod_save_wave
解决方案 I am assuming you are using the g++, gfortran, mpif90 toolchain.
If you have a module
module rocker
subroutine bye_baby
end subroutine
The external C declaration for it in C++ is
extern "C"
,-- 2 Leading underscores to start
| ,-- then the module name
,-- then _MOD_
,-- then the subroutine name
extern void __rocker_MOD_bye_baby();
You may also need to add attribute((stdcall)) after the extern.
By default, C assumes cdecl which stacks the parameters differently.
本文地址: &
我在我的C ++项目中包括一个不是我的fortran90程序。
在第一个步骤中,我尝试通过它们的name_()调用函数,并通过显示obj文件的符号来获得错误“未定义的引用mp_mpi_cartesian_init_”使用nm)我发现,函数被调用他们的模块作为module_function_所以我添加模块名称和我获得相同的问题,但在fortran obj之间,如“Constants.f90 :(。文本+ 0x36):未定义引用__powi4i4”
这里是c ++代码:
#include& iostream&
#include& complex&
extern“C”{
void mod_save_wave_mp_read_it_psi_(int * it,complex& double& * psi_E1E2);
void mod_mpi_cartesian_mp_mpi_cartesian_init_();
extern int mod_mpl_h_mp_iproc_; }
int main(){ complex& double& psi_local [512 * 24 * 512 * 24];
mod_mpi_cartesian_mp_mpi_cartesian_init_();
cout&& “proc:”& mod_mpl_h_mp_iproc_& “avant lecture\”;
mod_save_wave_mp_read_it_psi _(& it,psi_local);
cout&& “psi =”&& psi_local [0] <& “poiur le proc”&& mod_mpl_h_mp_iproc_&&“\\\”; }
这是模块的示例:
MODULE mod_save_wave
USE mod_constants
USE mod_MPI_CARTESIAN
USE mod_time_mesure,ONLY:tempsEcoule
USE mod_input_data,ONLY:Nt_laserPsansLaser
USE mod_input_data,ONLY:n_phi,n_rho1_seg,n_rho2_seg
USE mod_input_data,ONLY:Nt_periode,save_periodique
! ////////////////////////////////////////////////// ////////////
IMPLICIT NONE!
REAL(kind = d_t):: prog_start_time,time_max_second!
character(len = 80):: IntermedWaveDir !============================== ==============================
SUBROUTINE begin_count_time() IMPLICIT NONE
prog_start_time = tempsEcoule()!
END SUBROUTINE begin_count_time
SUBROUTINE READ_IT_PSI(it,psi_E1E2) IMPLICIT无!///////// ////////////////////////////////////////////////// /////////////////////
INTEGER :: it!
COMPLEX(kind = d_t),DIMENSION(n_phi,n_rho1_seg,n_phi,n_rho2_seg):: psi_E1E2! !========================================= =================================
integer :: c
do c = 0,c_max-1
if(mod(iproc,c_max)== c)then
!/////////// ////////////////////////////////////////////////// ///////////////////
OPEN(unit = 11,file = concat(trim(IntermedWaveDir),concat(concat('BACK / wave _',str_iproc) ,'_ 2p2p2')),&
status ='old',form ='unformatted',MODE ='READ') READ(11)
READ(11)psi_E1E2!
CLOSE(11)!
print *,'iproc,readed it =',iproc,it
CALL MPI_BARRIER(MPI_COMM_WORLD,infompi)! !========================================= =================================
enddo !===== ==================================================== =========================
END SUBROUTINE READ_IT_PSI
SUBROUTINE WRITE_IT_PSI (it,psi_E1E2) IMPLICIT无!//////////////////////////////////// //////////////////////////////////////////////
INTEGER :: it!
COMPLEX(kind = d_t),DIMENSION(n_phi,n_rho1_seg,n_phi,n_rho2_seg):: psi_E1E2! !=========================================== =================================
integer :: c
do c = 0,c_max-1
if(mod(iproc,c_max)== c)then !/////////////// ////////////////////////////////////////////////// ///////////////
OPEN(unit = 11,file = concat(trim(IntermedWaveDir),concat(concat('wave _',str_iproc),'_ 2p2p2')) ,&
form ='unformatted')!
WRITE(11)it + 1!----重新启动partar de la prochaine iterat!
write(11)psi_E1E2!
CLOSE(11)!
CALL MPI_BARRIER(MPI_COMM_WORLD,infompi)! !========================================= =================================
END SUBROUTINE WRITE_IT_PSI
END MODULE mod_save_wave
解决方案 我假设你使用的是g ++,gfortran,mpif90工具链。如果您有一个模块
模块摇杆包含子程序bye_baby
end subroutine
C ++中的外部C语言声明是
extern“C” { //, -
2开头的下划线 // | , - 然后模块名称 // | | , -
// | | | , - 然后子程序名 // V V V V
extern void __rocker_MOD_bye_baby(); }
您可能还需要添加属性<stdcall))之后。默认情况下,C假定为cdecl,它以不同的方式堆叠参数。
本文地址: &
扫一扫关注IT屋
微信公众号搜索 “ IT屋 ” ,选择关注
与百万开发者在一起
(window.slotbydup = window.slotbydup || []).push({
id: '5828425',
container: s,
size: '300,250',
display: 'inlay-fix'和 C++ 相比,用 Fortran 编程是怎样的体验?-土地公问答
和 C++ 相比,用 Fortran 编程是怎样的体验?
和 C++ 相比,用 Fortran 编程是怎样的体验?
流体力学学生,这学期编程都要求用 Fortran,可我还不会……
你们来感受一下 VASP 里面一个普通的函数调用(VASP 的文风算好的了)
体验。呵呵。体验。我因此砸烂过一支键盘。具体情形,请参照“德国 boy”相关视频,自行脑补。哪怕知道可能要因此遭受一些人参公鸡,我也还是来学王垠,开一发地图炮吧。特别声明,以下内容基本都是调侃和玩笑,请看官不要太认真;特别地,这些问题,很大程度上是学界的代码开发模式的问题,Fortran 不过是这些问题的一个替罪羊罢了。开喷。Fortran 是一个应该得到恰当的荣誉并下葬安息的亡灵。只是,今日之学界中,这个亡灵喜欢跳出来诈诈尸,口中念叨着些咒语,大致是些“祖宗之法不可破”、“前人智慧的结晶”、“语法严谨、简单易学、运行如风”之类,听着让人心里发毛。就不多说这僵尸还诞下了一个名叫 IDL 的怪胎的事儿了,省得过分地得罪同行。这里并不全是说 Fortran 本身的不好:(被这帮人视若无物的)C/C++ 中的未定义行为、泛滥的宏定义,也会坑得人生活不能自理。更多的,是 Fortran 所代表的编程范式的问题,因为一门语言包含了它的编程范式。 Fortran 有编程范式么?若说有,那大概可以用这几个词来形容:粗暴、混乱、不计后果。这时,Fortran 的“语法严谨”便显得有些可笑:这反倒使得程序员不再小心谨慎,结果把自己陷入更多的坑中。一旦一份代码用上了 Fortran,则往往意味着,这份代码背上了沉重的历史负担——你可以说它融汇了前人的智慧,但你更应该看到前人的局限,以及,由涓滴之流般的偷懒汇成的江河般泛滥的愚蠢和短视。所以,以下几个问题中,至少出现一个的几率,将变得非常大:1. 更换平台编译时、编译旧代码时,动辄出错,且难以排查——很多时候是因为 gfortran 的 F77 兼容模式不能完全兼容某些写法,剩下则多是 ifort 和 gfortran (以及它们各自的用户)间的恩♂恩♂怨♂怨,少数时候是代码中调用的某些库的 API 做了个不痛不痒的改名(没错 fftw 我说的就是你);2. 代码中有 goto;3. 你会看到一个上千行甚至几千行的 subroutine/function;4. 你会看到一个有一百多个参数的 subroutine/function;5. 各种不知所云的变量名,以及完全无用的注释;6. “if a & 0”?恭喜,编译错误,因为,“你那个 0 是个短整形,而 a 是个长整/浮点”;7, 你得经常跑到一个函数的开头去看一个变量的定义;8. write 的某个数字超宽了?恭喜你,你得到了一堆星号,谁关心你辛辛苦苦跑出来的数字去哪儿了啊——我当然知道这么做有它固有的原因,不过,这真是“时代的局限”五个字的最好样本之一;9. 各种没有料到的惊喜,酸爽无比,把 least surprise 原则破坏殆尽。最重要的,一份用上 Fortran 的代码,很可能有着根本没写过代码、速成培训后大干快上大炼钢铁的半吊子程序猿/程序媛——哪怕,他们可能是非常优秀的科研人员,他们的智商可能是我的两倍左右甚至三倍多,但是,不会写程序就是不会写程序。请看官不要说数组下标越界检查之类(在足够复杂的代码中几乎从未正确发挥过作用的)算不上优势的优势了,也不要抱着“Fortran 就是比 C/C++ 快、容易优化”、“Fortran 的数值计算库丰富、容易使用、高度优化”(我真的不信有啥东西的优化的高度能比过 -O3 下的 STL)的迷思不放了。上已有够多的人详细论证这个问题了,英文网站上相关的论述更是一挖一麻袋。以及,最重要的,我一直认为,作为一份科研用途的代码,可维护性的提升比运行速度的提升,重要得太多太多太多太多了。平庸的运行速度,至多消耗了更多的机时——这在至今仍然日新月异地发展着的计算机那儿常常不是个事儿;低劣的可维护性,吞噬的则是科研中两样最重要的东西:一曰可重现性,二曰(有经验的)科研人员的生命。我承认,用 C/C++/Julia 仍然有一定概率写出难以维护的代码,但至少,客观上看,Fortran 带来的沉重历史负担,基本摧毁了”存在可维护性“的可能性;同时,Fortran 的低门槛,客观上使得太多人养成了大炼钢铁式的编程习惯,让很多原本可圈可点的代码落到最后臭不可闻。我心目中的”优秀代码“,也算是有个参照标准的——比如,Linux 内核。它固然有过并有着许许多多的 bug,可是它是一个庞大却可以维护的系统。如果 Linux Kernel 是荷马史诗,那么,那些(以 Fortran 为首的)科研所用代码,至多只能算是《尤利西斯》了。然后,是的,我知道孤证不立,我知道有人喜欢喷“这是逻辑错误”,也有人会讲另一面的故事,但我还是要讲故事。我刚到某屯搬砖的时候,与一个师兄合作,用上一份叫做 Pinocchio 的代码(PINOCCHIO Home Page)。这份代码是用来模拟星系团尺度上的暗物质演化的,使用微扰论替代直接求解 N-S 方程,使其可以在小计算机上跑出效果还不错的大尺度结构模拟。首先,花了一个周末,这货才能被成功编译,原因是 gfortran 不接受某些 ifort 的写法——这还不是整形长度之类的细节,而就是关于某些日常用法的语法不一致——以及 fftw 更改 API 的坑。编译成功,随即跑了个
的,looking good。接着,就遇上大坑了。我们需要
的分辨率,可一把边长设置为 1500,程序马上就崩溃了。整形溢出。那好说嘛,把数组指标改为长整型不就得了——当然不,这样更改后的代码,同样跑 ,得出的结果与数组下标为普通整型时不!一!致!(固定随机数种子之后,这个代码每次得到的结果是完全一样的)调了整整一周,毫无进展。决定暂时不管那么多,先跑 ,跑完后,看到输出数据里头一堆 *****(输出超宽),心都碎了…… 以及,我要喷我最想喷的货色:Enzo(The Enzo Project)。正是这个奇葩,让我失去了对天体物理 simulation 的几乎所有兴趣和信心。虽然这个 code 的 C++ 部分也很烂,但 Fortran 部分更是坑得人生活不能自理,最后便合成了一篇集 C++ 和 Fortran 烂法之大成的东西。就在前不久,我这个 rotation 项目都快结束了,还被它给坑过。我那位 rotation 老板在二十年前为此写过一个模块,用 F77 写的。时至今日,他一直以为该模块在此程序中发挥着巨大的作用,直到我发现,调用这个模块的部分,被人注释掉了。他顿时花容失色,令我重启该调用,却发现巨量无法修正的编译错误——是的,gfortran 的“兼容模式”下支持的 F77 已经不是他当年那个 F77 了。老板长叹一声道,幸好我后来用了 C++……然后,再来看点儿来自 enzo 的酸掉你大牙的东西,F77 和 F90 都有。比如,这个宏伟的参数列表:以及前一个函数在 C++ 中的调用,是不是感受到了一种灵压:这个赋值方式真是不错:此朵数组的眼神,忧郁得让人蛋疼:Fortran,Let's GOTO places(广告语原句:Toyota, let's go places):当然还有很多 crap,比如:现在的计算机,缓存都已经够大了;函数调用带来的开销,早都是零头了;还在以这种手段拼命避免函数调用,简直就是防止他人维护,良心大大的坏了。若是用 cpp 写个 class,也能省得一不小心写错一个参数的位置啊——面向对象再是大坑,至少能让人们有一个靠谱的 namespace 可用(F90/95 的命名空间?呵呵)。就那种可维护性,算得再快,又能怎样?最后,私货。高德纳(曾经记成 RMS 了,感谢纠正):“过早的优化是万恶之源”。我得加一句,“自以为是的优化是万恶之源”,包括在今日仍然坚持使用 Fortran 开发新项目。人在做,天在看,implicit 留隐患;GOTO 一出亲友散,非结构化天下乱。诚心诚念 CPlus 好,抓紧重构灭退保。众生皆为数值来,Fortran 险恶杀时间。CPlus 弟子说真相,教你脱险莫拒绝。天灭 Fortran,快看 Julia!Numpy/Scipy/Cython 坠吼!Haskell 赛高!全世界被 Fortran 坑过的人,联合起来!Fortran-sufferers of the world, unite!
有人说写代码的人习惯不好,不能让语言背黑锅......我想说这锅Fortran还真是背定了!因为Fortran本身就鼓励你养成写代码的坏习惯!比如最基本的implicit变量声明......时至今日还有很多Fortran用户拒绝使用implicit none,这锅不该Fortran背么?(顺手举个例子,我师弟刚进组的时候坚持不用implicit none,然后发现他自己的toy code出现了愚蠢的错误后才认识到这个问题)再比如蛋疼的函数interface......丑陋得不能直视......没有头文件这回事就不要定义interface了好么......这玩意儿直接让代码的可维护性下降一个量级......再比如指针......没有指针就没有指针呗,非要搞假指针......直接导致很多Fortran用户根本没理解什么是指针......PS:还有数组存储顺序的事......至少跟99%的地球人思维习惯相反吧......
简单点评一下Fortran历史上几个大的变化:FORTRAN 77的可读性很差,很多作者都提到了。我读过不少FORTRAN 77格式下的源代码,例如著名的FEAP 真是要了命了。Fortran 90之后略好,但不支持OOP。出现了module,类似于C++的namespace,如果好好安排还是可以让一组程序显得很有序。Fortran 2003之后支持了OOP,但各种IDE对Fortran 2003之后的支持都很糟糕。我自己的感觉是这样:C++肯定是很好的,但需要非常用心的学习,C++就好象是一把瑞士军刀,用C++干Fortran擅长的科学计算,需要好好学一些更深层次的东西,否则计算效率一个疏忽就下来了。这就引出一个挺尴尬的事情,一些工科专业由于学时和师资力量,暂时还提供不了很专业的指导老师来指导科学计算的程序设计;而计算机系一般也不会有针对科学计算的课程。我自己的经历是,本科时候所谓的“学过”C++。读博的时候学的Fortran,感觉上手很快,且一些自带的数学函数(尤其是关于向量矩阵的)很好用。由于导师的安排,我自己开发了一个通用有限元程序,代码量10万+,包含代码4万+,注释4万+,其余1万+空行。又用C++基于Gmsh(基于FLTK库)开发了一个简单的界面。对自己的Fortran的评价是:能规避包括很多答主提到的Fortran的各种奇葩的限制,能尽可能提高程序的可读性,能运用简单的OOP的思想尽可能提高程序的可维护性、可延展性。我组里的同学拿了代码之后很快就能上手,也算是一个小的佐证。---晒一下代码风格吧,给从事科学计算的朋友一些Fortran 的感觉。这个类的定义很简单,存储的是系统的wall time,用来计算一个FEM model的运行时间等。这个是文件头注释:这个是Fortran里面一个类(其实还是用的自定义数据结构type来实现,只是增加了type-bounded subroutines)这个是type-bounded subroutine的具体函数体(部分):
我的手机解锁画面上写着 implicit none别问为什么,这是一个伤心的故事说到毕设,我的也是改导师的程序的一个模块的代码,他所有字母全部是大写。。。那酸爽。。。。。
补充两个编译原理老师给的例子:1) 关键字不保留的弊端if then then then= else else= end if写编译器的人估计该疯了。。。2) 忽略空格:do 8 I = do 8 I = 3,75do8I = 请说出上面的代码等价吗?写编译器的人估计又要疯了。。
仿佛没有了软肋,又仿佛失去了盔甲
曾经做过一段时间,当时已经习惯C++,觉得Fortran可读性不强,维护得很累。
只要是用Fortran的项目,基本上都是延续多年的老项目,这就意味着巨大的历史包袱;接手一份历时几十年的老代码,这感觉。。。或者就是未经过系统的软件训练的非IT行业工程师的作品,这就意味着他们当初写代码的时候基本上是怀着一种写草稿的心态,代码能run,结果正确就好了么。。。。。。
FORTRAN 在今天最大的作用是让一些至今没学会基本编程原则的二杆子“科学家” 能有“尚未被时代抛弃” 的错觉。
读博以来做的最正确的事情是花了两个月用c++重写了一遍组里的遗产fortran代码。Fortran遗产代码多数是F77.毕竟在F90逐渐流行的年代C++已经很流行了,没多少人在那个年代开新项目会坚持选择Fortran。F77的恶心写法在高票答案基础上再提几个,比如implicit type,某些字母开头的变量默认某类型common block ,global constant 直接导致程序结构失去控制,没有清晰的进入和退出点,数据交换也是一团乱。F77的另一个恶心之处是由于以上缺点导致的程序结构一团乱,就算想把功能分离出来写个wrapper然后在c++里调用都很难。新标准F号称也可以做OO但是写起来实在是非常恶心,跟c++11比起来差太远了。编译器、开发工具也没有很快跟上。最后说一点,现在已经很难找到靠谱的免费Fortran编译器了。gfortran对f77、f90/95我都遇到过加某些选项出现很奇怪的行为。要是买ifort、pgi fortran,也很难保证在十年这个尺度上的靠谱。
如果要程序可维护,需要手动拿数组实现结构体之类,或者至少搞类似的变量命名体系和头文件。再加上CFD程序有大量状态变量,感觉类似拿C写Verilog HDL(硬件描述语言,这货不是程序)的模拟器。以前写多了CFD代码又没掌握Matlab模电和数电混合仿真(在当时还很不常用)的时候为了仿真混合信号电路干过拿C做壳仿真Verilog这种奇怪的事情,而且脱壳之后的Verilog代码编译成硬件电路全都没有问题。如果要搞控制逻辑较复杂的程序,FORTRAN程序惯用的大量全局状态和解耦不彻底的风格作大死,导致头文件膨胀和状态过多难以维护。哪怕写成C结构体或者C++类都作死。如B9Creator 3D打印机的单片机控制部分,一个美国空军退役军人业余写出来开源的Arduino控制程序,感觉居然像FORTRAN计算程序。只要改一点就有无穷无尽的bug,还是实时控制系统,错一次就打印报销。如果美军用这种软件风格,软件超期就不奇怪了。
感觉是物理系学生的装逼利器,他们心中最完美的语言,没有之一。就像php一般完美。导致每次聊相关话题时内心都无比呵呵。感觉他们心中的编程语言鄙视链可能是Fortran & Mathematica &&... && Matlab
1. 高性能计算中上百个参数、上千行的函数很正常,用C/C++也一样。模块化策略确实不如C++完善。2. 请使用Fortran90而不是Fortran77,ifort而不是gfortran。3. 单从数组语义、指针语义来看,Fortran确实比C/C++更适合优化,尤其是向量化。4. 对性能没有极致要求的话,一般的科学计算用Cython/Julia/Matlab..就行。请区分高性能计算和科学计算
Fortran最大的好就是“简单“,很多工科狗才不管什么beautiful code呢。我组里一哥们因为要用一个开源的AMR library,然后去学C++,被虐的跟狗一样。
二十年前做为工科生学过依稀记得一堆星号in命名规则80列约束后来因为c语言可以settextcolor和initgraph就果断抛弃fortran了我的结论是:不要拿它做入门语言
不要随便给FORTRAN甩锅,绝大部分锅都应该是蹩脚程序员的。你们说变量名,Fortran没有强制使用大写,也没有不让你们用下划线,也没有不让你们写注释来解释变量的含义。你们说implicit none, 这不过是一种name convention而已。你们说不用implicit none会手滑打错名字然后引入了一个新的未定义变量,但ifort和gfortran都能检查未定义变量啊,你们又不用。ifort还能自动检查数组越界呢,你们搞出来SEG FAULT的时候知道开这个选项么?说字符输出宽度的,'A'是可以不制定宽度自由输出的呀,非要用'X'怪谁。Fortran 也能用DOENDDO, IFENDIF块,不用 DO label的呀。说FORTRAN没有指针的,函数传的都是地址啊。数组名也是地址啊,你不能自加指针可以自加偏移量啊。COMMON BLOCK是可以命名的的呀,这不就是类的Static成员么?说的好像给你们用C++你们就能把程序写得漂亮似的。虽然我现在也不用FORTRAN而用C++,不是因为fortran不好,而是因为fortran的编译器不好。Intel fortran只有Linux版的有非商业授权,gfortran又是要死不死的。
读书的时候搞过一段时间的Fortran解数值方程,用的是Fortran95,并没有感觉和C++有太大的差别。当然代码混乱那是必然的,因为物理学搞这个的基本没有受过正规的编程训练,只要结果正确,可维护性,拓展性不在他们的考虑范围内。并且他们一般不会加注释,so,如果你要读清楚的话,只能去找本人,这个效率会很低。如果人已经离组了。。会灰常悲剧。我当时要解一个氢原子在激光场的薛定谔方程,一个师兄写了大概400行代码解出来了,但是因为他联系不上,我一个人默默的看了两个多月才完全搞清楚。当然,与我不太理解他的算法也有关系。但是,fortran的计算速度确实很快的。有人说在计算能力发达的今天不需要,但是科学计算有时候要算很长时间。。。当时我们的机器有32个核,每个核8个CPU,看起来是够了。但是差不多有十来个人要用,每个人分的节点很有限,随便模拟一次就是一天。如果换C++,计算会更慢。你愿意等么?所以我认为,应该改变的是理工科学生的编程习惯,而不应该是这一门语言。
关于一些人提到的函数的输入变量太多的问题,目前想不到有什么好的办法可以解决。输入变量多,是因为——很多时候真的需要输入这么多变量。当然,或许可以把一些变量“打包”,定义成一些专门的数据类型,或者采用一个数组,让数组每个元素代表一个变量。但这只是形式上让函数调用看起来简单一些而已,实质上没变。并且,“打包”之后对函数各个输入变量的赋值会更隐蔽,更难发现隐藏的错误(比如忘记赋值而采用了默认值)。不是说"explicit is better than implicit"嘛。另一种办法是采用全局变量来减少函数的输入参数个数,但这么做显然更坏吧?事实上之前看一些著名开源项目的C代码,一样有类似的现象。所以,有些问题本来就很复杂(就好像有人评论王垠对LaTeX的批评一样:LaTeX有各种难用的地方,是因为排版天生是个复杂的问题),难以通过纯粹技术性的办法约简。并且复杂就容易出错,这个似乎也跟语言关系不大。。当然各位如果有好办法欢迎分享。GOTO的问题,一部分是因为在Fortran90之前没有C那样的条件判断语句。这种情况在多数时候只是个很小的语法糖问题,对程序流程控制影响不大(也就是说,换成if esle ... 代码不需要改很多)。当然还有很多不是这样的情况,比如一下跳很远的。偶尔用一下还可以。觉得比较方便的是 write(*, "(这样的输出格式控制。貌似C没有?有的话请指教。对于C程序员,或者把C++当C用的程序员,我觉得最新的Fortran ()用起来其实跟C差别不大,除了有些地方形式上繁琐点。。很多东西只是个习惯问题。再牛的语言,都可以写得很混乱,难以维护。个人最讨厌的是某些老代码里的全盘大写。。。
一样的,只要掌握最基本的循环,条件,以及如何定义变量,如何写子程序,如何读写文件就够了,都是些基本规则,这些掌握后就可以真正用来做事情了。
其它类似问题
其它人正在问的问题}

我要回帖

更多关于 fortran调用abaqus 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信