从c ++调用FORTRAN子例程会产生非法的参数值

时间:2017-02-23 10:03:56

标签: c++ interface fortran eigen eigenvalue

我正在尝试解决特定的特征值问题(所谓的陀螺特征值问题) 使用大的稀疏矩阵(来自FEM dsicretization)。编程语言是C ++。

EVP的标准参考是ARPACK。唉,它只实现了“经典”的Arnoldi过程, 这不适合这些问题(c.f。Structure Preserving Methods)。

最近我发现了这个Algorithm 961引用,它也提供了一些代码 - 在FORTRAN中! 所以我试图在C ++中包含DGHUTR例程,但无济于事。 下面是MWE,它是C ++中DGHUTR(TDGHUTR.f)测试的改编:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <solution>
    <add key="disableSourceControlIntegration" value="true" />
  </solution>
  <config>
    <add key="repositoryPath" value="..\..\..\..\NugetPackages" />
  </config>
</configuration>

编译完成(它使用了很多其他东西):

#include <Eigen/Dense>
#include <Eigen/Sparse>
//definition stolen from ARPACK++
#define F77NAME(x) x ## _

//Interface to the SHEIG library function DGHUTR
#ifdef __cplusplus
extern "C"
{
#endif
  void F77NAME(dghutr)( char* JOB, char* COMPQ1, char* COMPQ2, int* N, double* A, int* LDA,
                        double* DE, int* LDDE, double* C1, int* LDC1, double* VW, int* LDVW,
                        double* Q1, int* LDQ1, double* Q2, int* LDQ2, double* B, int* LDB,
                        double* F, int* LDF, double* C2, int* LDC2, double* ALPHAR, double* ALPHAI,
                        double* BETA, int* IWORK, int* LIWORK, double* DWORK,int* LDWORK, int* INFO );
#ifdef __cplusplus
}
#endif


int main(void){
  // define system sizes
  int N(8),  M(N/2);
  std::cout << "Sizes: " << N << '\t' << M << std::endl;


  char job('E'),  compq1('I'),  compq2('I');
  int lda(M),  ldde(M),  ldq1(N),  ldq2(N),  ldb(M),  ldc1(M),  ldc2(M),  ldf(M),  ldvw(M);

  int ldwork = 2*N*N+std::max(4*N+4, 32);
  int liwork = N+12;


  // workspace arrays
  int* iwork = new int[liwork];
  double* dwork = new double[ldwork];
  int info(0);
  // auxiliary matrices and  vectors
  Eigen::MatrixXd F(ldf, M),  C2(ldc2, M),  Q1(ldq1, N),  Q2(ldq2, N),  B(ldb, M);
  Eigen::VectorXd alphaR(M),  alphaI(M),  beta(M);

  //matrices with data
  Eigen::MatrixXd A(lda,M), DE(ldde,M+1), C1(ldc1,M), VW(ldvw,M+1);

     A << 3.1472,   1.3236,   4.5751,   4.5717,
   4.0579,  -4.0246,   4.6489,  -0.1462,
  -3.7301,  -2.2150,  -3.4239,   3.0028,
   4.1338,   0.4688,   4.7059,  -3.5811;

   DE << 0.0000,   0.0000,  -1.5510,  -4.5974,  -2.5127,
   3.5071,   0.0000,   0.0000,   1.5961,   2.4490,  
  -3.1428,   2.5648,   0.0000,   0.0000,  -0.0596, 
   3.0340,   2.4892,  -1.1604,   0.0000,   0.0000;

   C1 <<  0.6882,  -3.3782,  -3.3435,   1.8921,
  -0.3061,   2.9428,   1.0198,   2.4815,
  -4.8810,  -1.8878,  -2.3703,  -0.4946,
  -1.6288,   0.2853,   1.5408,  -4.1618;

   VW <<  -2.4013,  -2.7102,   0.3834,  -3.9335,   3.1730,
  -3.1815,  -2.3620,   4.9613,   4.6190,   3.6869,
   3.6929,   0.7970,  0.4986,  -4.9537,  -4.1556,
   3.5303,   1.2206,  -1.4905,   0.1325,  -1.0022;

  /* outputs of each parameter save for dwork,iwork to check correctness. */

  F77NAME(dghutr)( &job, &compq1, &compq2, &N, A.data(), &lda, DE.data(), &ldde,  C1.data(), &ldc1, VW.data(), &ldvw,
                         Q1.data(), &ldq1,  Q2.data(), &ldq2,  B.data(), &ldb,
                         F.data(), &ldf,  C2.data(), &ldc2, alphaR.data(),  alphaI.data(),
                         beta.data(), iwork, &liwork, dwork, &ldwork, &info );
  std::cout << "result: " << info << std::endl;
  delete[] iwork;
  delete[] dwork;
}

唉,每当我运行生成的可执行文件时,它都会给我:

g++ -o eigensolver EigenSHEIGSolver.cpp -I/home/shared/eigen-eigen-1306d75b4a21  /home/shared/SHIRA/SHEVP/src/shheig64.a /home/shared/SHIRA/SLICOT_Lib/slicot64.a /home/shared/SHIRA/SLICOT_Lib/lpkaux64.a /home/shared/ATLAS/builddir/lib/libptlapack.a /home/shared/ATLAS/builddir/lib/libptcblas.a /home/shared/ATLAS/builddir/lib/libptf77blas.a /home/shared/ATLAS/builddir/lib/libatlas.a /home/shared/ATLAS/builddir/lib/libptcblas.a -lgfortran -lpthread

我的FORTRAN知识非常有限,上面的代码主要是使用 YoLinux Tutorial mixing FORTRAN and CCRAY Docs 作为参考。 据我了解,例程报告了 ** On entry to DGHUTR parameter number 8 had an illegal value 变量的错误。不过,我不知道为什么。

有人可以为我解释一下吗?

N.B。根据{{​​3}} Eigen默认以col-major顺序存储矩阵,因此它应该可以与FORTRAN连接。 FORTRAN子程序DGHUTR是

ldde

更新:这是修改后的DGHUTR子程序的输出(基本上是添加打印):

SUBROUTINE DGHUTR( JOB, COMPQ1, COMPQ2, N, A, LDA, DE, LDDE, C1,
 $                   LDC1, VW, LDVW, Q1, LDQ1, Q2, LDQ2, B, LDB, F,
 $                   LDF, C2, LDC2, ALPHAR, ALPHAI, BETA, IWORK,
 $                   LIWORK, DWORK, LDWORK, INFO )

正如我可以看到字符被正确接收, JOB T COMPQ1 I COMPQ2 I LDA 17179869188 LDDE 34359738372 LDC1 17179869188 LDVW 704374636548 LDQ1 34359738376 LDB 17179869188 LDF 17179869188 LDC2 17179869188 LIWORK 20 LDWORK 85899346084 N 17179869192 LDDE 34359738372 INFO 6227620798727716864 也是如此,前提是我使用LIWORK集进行编译。我猜测-O2确实会破坏参数。尝试从g++恢复为gcc-5并未解决问题。如果没有优化,gcc-4.8值似乎在程序的每次运行中都会发生变化,而在使用LDA进行编译时它仍然是固定的。

1 个答案:

答案 0 :(得分:0)

我想我找到了一直困扰着我的问题的根源。 fortran例程接收的值对优化标志的依赖性是一种 暗示C ++和C ++如何解释存储的变量可能有问题 FORTRAN。 在找到17179869188的具体值并找到它之后 SO post 我尝试使用库的编译器标志

当我获取SLICOT时,我获取了源代码和一个用gfortran预编译的库(slicot_linux_gfortran.tar.gz)。 后一个带有{.1}的make.inc SHHEVP例程在make.inc

中包含以下注释
OPTS = -O2 -fpic -fdefault-integer-8

所以我按照建议做了 - 那就是问题!

删除IMPORTANT: Use the options -fPIC -fdefault-integer-8 for 64bit architectures. 并重新编译SLICOT和DGHUTR解决了我的问题。现在上面给出的代码 编译和FORTRAN子例程接收正确的值。计算结果如下 与DGHUTR来源提供的参考结果一致。

顺便提一下,大多数SLICOT测试现在正在运行。用旧标志汇编的例子 停在TAB01ND,这将永远挂起。现在我开始使用TMB03LD,其编译失败并带有

-fdefault-integer-8

但就目前而言,我并不担心。