2015年3月15日星期日

在Abaqus中使用c++写用户子程序

Abaqus 6.13之后的版本多了一个功能,可以用c++或者c来写用户子程序了;之前的版本中只支持使用fortran语言。
网上在这方面的资料太少,因此把我的折腾过程记录一下。

  • 在abaqus中加载编译环境用和fortran相同的方法(不过我试的时候没需要,应该是ifortvar带进去了)
  • abaqus_v6.env里没什么要改的
  • 在cpp文件中如下修改
    • 要先#include <aba_for_c.h>
      • 这会带进来个FOR_NAME宏,所有从abaqus调用的函数,以及调用abaqus的函数,都用类似下面的方法声明
      • extern "C" 是让cpp用c的方法声明符号——即符号名称中不包含变量类型,不知重载是怎么解决的
        • 就算不能重载也没关系,只有和abaqus通信的函数要这么处理
      • 例子
        • extern "C" void FOR_NAME(xit)();
          • 这个是abaqus提供的函数
          • 对于需要传入的参数中包含字符串的,见文末说明。
        • extern "C" void FOR_NAME(umat)(double* STRESS,void* STATEV,double*DDSDDEori,void* SSE,void* SPD,void* SCD,void* RPL,void* DDSDDT,void* DRPLDE,void* DRPLDT,void* STRAN,double* DSTRAN,void* TIME,void* DTIME,void* TEMP,void* DTEMP,void* PREDEF,void* DPRED,void* CMNAME,int& NDI,int& NSHR,int& NTENS,void* NSTATV,double* PROPS,void* NPROPS,void* COORDS,void* DROT,void* PNEWDT,void* CELENT,void* DFGRD0,void* DFGRD1,void* NOEL,void* NPT,void* LAYER,void* KSPT,void* KSTEP,void* KINC){ }
          • 这里的void*都是偷懒了,只声明对自己要用的变量就行
    • 对于二维数组,fortran是列优先的,cpp是行优先的
    • fortran是1开始数的,cpp是0开始数的
    • DDSDDE这个二维数组,有两种办法
      • 如果你确信你只对3维问题跑,则它一定是6*6的,可以把DDSDDE在函数头里声明为 double DDSDDE[][6],或者double (*DDSDDE)[6]
      • 否则的话,开头声明为double* DDSDDEori
        • double** DDSDDE=new double*[NTENS];
        • for(int i=0;i<NTENS;i++) DDSDDE[i]=DDSDDEori+NTENS*i;
        • 这个方法略占行数,不过并没有效率下降。
完成了上面的配置之后,你应当能跑一个典型的c++写的用户子程序(如umat了)。但为了使用方便起见,你应该还想要调用外部库。
  • 调用外部库
    • 如armadillo
    • 正常安装armadillo库之后即可使用
    • 如果要矩阵乘法,就要BLAS;要解方程,就要LAPACK
      • 他俩的lib应当从链接器中提供,这意味着——
      • 修改abaqus_v6.env中的link_sl,在末尾添加'blas_win64_MT.lib' , 'lapack_win64_MT.lib' ,并将两个文件拷贝到vs的cpp库中(选择amd64)目录
        • 在我的机子上是 C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\lib\amd64
      • 将对应的两个dll拷到system32里
        • 否则程序运行中会报错弹框退出
    • 可以用mat ddsddeMat=mat(DDSDDEori,NTENS,NTENS,false,true);直接构建对应 的矩阵或列向量
      • 开头把DDSDDE声明为double*或者void*
      • 第一个false为不拷贝数据,直接用原始位置
      • 第二个true为strict,即不添加空间,保证总是用这一段
      • 要注意这里不用转置
        • 二维数组中,fortran是列优先的,c++是行优先的,但arma使用一维数组初始化矩阵时,是列优先的(至少在armadillo-4.650.3中是)。
最后说点兼容性问题
  • abaqus 6.13或更高版本支持在standard或者explicit中用c或cpp
  • abaqus 6.14-2中仍然不支持直接用cae来指定cpp后缀的用户子程序,只能用命令行的user=xxxxx.cpp来指定。因此要用cae生成input文件,用命令行计算,最后可以用cae查看计算结果
---
从c++调用fortran的函数,其参数列表中含有字符串
——关于fortran 中的character*
fortran中的字符串是自知长度的,可以通过len()访问
在c++中无法产生这一类

在向subroutine传递字符串时,具体行为是编译器相关的
  • 有的编译器会传递一个结构体,其中包含1个char*指针,和一个size_t的值
  • 有的编译器在整个函数的参数列表末尾按顺序依次再传递各个字符串的长度
intel fortran 是第二种

对于第一种情况,可以构造一个类CHARACTER来模拟行为
这里有个写好的

对于第二种情况,在c++中声明函数就要考虑到末尾的整形变量,调用时也要如此考虑
  • 声明 extern "C" void FOR_NAME(getoutdir) (char* outdir, int* lenoutdir, int len);
  • 调用 getoutdir(temp, &len, 200);
    • 其中200为char temp[200];
参阅
http://www.yolinux.com/TUTORIALS/LinuxTutorialMixingFortranAndC.html
https://computing.llnl.gov/tutorials/bgq/ 中的 Miscellaneous 一节
http://arnholm.org/software/cppf77/cppf77.htm

1 条评论: