【问题标题】:Fortran Arrays change values automaticallyFortran 数组自动更改值
【发布时间】:2016-06-26 02:37:31
【问题描述】:

我正在做关于在 ANSYS CFX 上进行风力涡轮机模拟的硕士论文。要计算参数,我必须使用 FORTRAN 编写的代码。我遇到了一个非常奇怪的问题,需要你的帮助。完整的 fortran 代码很长,但我只会在导致问题的代码中发布子例程。

我正在使用子程序将叶片划分为径向元素。使用两种类型的划分,然后将元素的径向位置存储在两个一维数组 RI 和 RJ 中。

稍后在代码中,我遇到了一些错误,我将其追溯到这个子程序,它没有给我正确的 RJ 值。子程序给出了正确的 RI 值。然后我使用计算correct values of RJ 的相同循环显示了 RJ 的结果。幸运的是,RJ 的值是正确的。然后在同一个子程序中,在那个循环之后,我立即开始另一个循环以再次显示 RJ 的值,不幸的是这次它们不正确incorrect values of RJ,即使两个循环之间没有任何东西可以改变 RJ 的值。似乎 RJ 的每个元素都被 RI 的下一个元素替换。因此,我开始知道这是错误的根源。我重新检查了程序,似乎没有错误,错误源于子程序内部的某个地方。我在没有 ANSYS CFX 的 fortran 上使用另一个程序使用相同的子程序来计算风力涡轮机的性能,尽管这两个程序中的两个子程序完全相同,但这个问题并没有出现在该程序中。我在这件事上需要帮助,因为过去几天我都陷入了困境。我正在使用 INTEL COMPOSER 附带的 Intel fortran 编译器。

子程序如下

CALL INIT(M,PI,PREC,R,HUBRAD,RI,RJ)

SUBROUTINE INIT(M,PI,PREC,R,HUBRAD,RI,RJ)


  INTEGER:: M
  REAL:: PI,PREC,R,HUBRAD
  REAL:: RI(41),RJ(41)
  !,RRI,RRJ)

    CALL LLPOINTS (PI,PREC,R,HUBRAD,M,RI,RJ)


    return
    End Subroutine

    SUBROUTINE LLPOINTS (PI,PREC,R,HUBRAD,M,RI,RJ)

  !Input arguments: M,Pi, PREC, R, HUBRAD 
  !Output arguments : RI, RJ

  INTEGER:: M
  REAL::PREC,R,HUBRAD
  REAL:: RI(41),RJ(41)
  INTEGER :: J
  character*100 ::string1, string2, string3

  CALL MESAGE( 'WRITE', 'subroutine INIT START' )  

  CALL MESAGE( 'WRITE', "RI   RJ    DJ")

  DO J=1,M+1   


        IF (J.LT.M+1) THEN   
        RI(J)=0.5E+00*(1.E+00+HUBRAD/R)-0.5E+00*(1.E+00-HUBRAD/R)
       &*COS((J-0.5E+00)*PI/M)

        RJ(J)=0.5E+00*(1.E+00+HUBRAD/R)-0.5E+00*(1.E+00-HUBRAD/R)
       &*COS((J-1.E+00)*PI/M)


        IF (ABS(RI(J)).LT.(1/PREC)) THEN

              STOP 
        ENDIF

        ELSE
           RJ(J)=1.E+00

          END IF


          write (string1,*) RI(J)
          write (string2,*) RJ(J)


          CALL MESAGE( 'WRITE', string1//' '//string2) 

      END DO

      CALL MESAGE( 'WRITE','ri     rj')
      do j =1,m

          write (string1,*) RI(J)
          write (string2,*) RJ(J)
          CALL MESAGE( 'WRITE', string1//' '//string2 ) 

      end do
      CALL MESAGE( 'WRITE', 'subroutine LLINE OK' )  

  END SUBROUTINE

您会想知道为什么 LLPOINTS 无缘无故地存在于 INIT 子例程中。实际上在原程序中子程序INIT还有很多其他的子程序。我只使用了 LLPOINTS 并丢弃了其余的因为我不需要它们

我已经检查过我的程序在任何子程序中都没有隐含的 none,根据我的有限知识,这些子程序应该存在。当我在所有子例程中添加隐式 none 时,出现了一些编译错误,并且似乎在这些错误中,我发现存在与许多变量的声明有关的问题。我已经解决了这些问题,现在当所有问题都得到解决时,我在编译中遇到了以下错误,这在implicit none之前没有出现。

错误:函数 ACD_Dp 中引用的外部符号 LLINE 无法解析。

我不知道如何处理这个错误

【问题讨论】:

  • 您能否说明如何调用此子例程 (LLPOINTS),以及如何定义 RIRJ 的实际参数?实际参数可能被错误地传递,以至于它们指向的内存区域仅相差 1。
  • 基本上参数“m”是涡轮叶片被划分成的数字。m的值从另一个输入文件中读取并传递给这个子程序。我已经测试过了,m 的值被正确地传递给了这个子程序。 RI 和 RJ 的大小故意限制为 41。这样做是为了避免可分配的数组。默认情况下,该程序只能工作到 m=40 个元素(划分为两个数组 RI n RJ 使得 RI 元素的数量等于 m 并且 RJ 元素的数量等于 m+1)。任何小于 40 的 m 值都应该有效。
  • 检查我编辑的问题以调用(LLifine)子程序
  • 你在某处有任何名为LLINE 的函数(或子程序)吗? LLINE 在你的日常生活中是如何使用的?编译器认为LLINE 是一个函数,而它在任何源代码中都找不到它的定义(在链接时)。

标签: arrays fortran intel-fortran


【解决方案1】:

奇怪的行为可能源于一些(不正确的)实际参数,而不是子程序本身。为了解释这一点,我们首先考虑INIT()LLPOINTS()的简化版本如下:

subroutine INIT ( a, b )
    implicit none
    real :: a( 5 ), b( 5 )

    call LLPOINTS ( a, b )
end subroutine

subroutine LLPOINTS ( a, b )
    implicit none
    real :: a( 5 ), b( 5 )
    integer :: i

    print *, "output (1):"
    do i = 1, 5
        a( i ) = i                !! set some values to a(:) and b(:)
        b( i ) = i * 100
        print *, a( i ), b( i )   !! check the values
    enddo

    print *, "output (2):"
    do i = 1, 4
        print *, a( i ), b( i )   !! check the values again
    enddo
end subroutine

该程序将一些值设置为a(:)b(:) 并打印两次它们的值以进行双重检查(如在 OP 的程序中)。现在我们考虑主程序:

program main
    real :: a( 5 ), b( 5 )
    call INIT ( a, b )
end

给出了预期的结果(ifort test.f90 和 v14.0):

 output (1):
   1.000000       100.0000
   2.000000       200.0000
   3.000000       300.0000
   4.000000       400.0000
   5.000000       500.0000
 output (2):
   1.000000       100.0000
   2.000000       200.0000
   3.000000       300.0000
   4.000000       400.0000

接下来,让我们假设 ab 被错误地声明为标量变量

program main
    real :: a, b
    call INIT ( a, b )
end

甚至没有声明(即使用默认的implicit real(a-h,o-z) 规则)

program main
    call INIT ( a, b )
end

然后我们得到

 output (1):
   1.000000       100.0000
   2.000000       200.0000
   3.000000       300.0000
   4.000000       400.0000
   5.000000       500.0000
 output (2):
   1.000000       2.000000
   2.000000       3.000000
   3.000000       4.000000
   4.000000       5.000000

其模式似乎与 OP 的输出非常相似(即,输出 (2) 中的所有元素都移动了 1)。也就是说,这种奇怪行为的原因可能是我们传递了标量并访问了无效的内存区域(假设ab 在内存中连续对齐,并带有一些尾随内存区域)。如果是这样,main()LLPOINTS() 之间的内存映射可能如下所示:

a       b      NG     NG     NG     NG
---------------------------------------
a(1)    a(2)   a(3)   a(4)   a(5)
        b(1)   b(2)   b(3)   b(4)   b(5)

如果是这种情况,由于a(i+1) = b(i),我们在上面的输出(2)中得到了奇怪的结果。我们可以通过插入类似的行来确认这一点

if ( loc( a(2) ) == loc( b(1) ) ) stop "trapped (ifort)"

LLPOINTS()。更重要的是,如果我们附上

ifort -check test.f90

选项,我们可以自动检测到这一点(有分段错误)。所以你能试试这个选项,看看是否是这种情况......?

【讨论】:

    【解决方案2】:

    是的,你是对的。我已经放了以下几行

    If ( loc( RJ(1) ) == loc( RI(2) ) ) then
    call mesage ('write','trapped (ifort)')
    
    stop
    
    End If
    

    我得到了错误,打印被困 (ifort)。 但问题是,我没有在任何地方声明标量参数 RI、RJ。我一直将它们声明为向量 RI(41) 和 RJ(41)

    【讨论】:

    • 很好,我想我们可能走在正确的轨道上(即实际参数的一些问题)。还有其他几种可能导致此错误,但我相信将implicit none 附加到相关例程以及附加-check all(或/check:all)可能更有用(比猜测)给出一些警告或错误消息(希望!)。在模块中包含相关例程对于检查子例程接口也很有用(例如,参数的数量和类型是否正确)。我还在回答中添加了另一种方法。
    • 那你有什么建议?我应该如何进行?我不是专业的程序员,除了知道基本的工作原理外,几乎没有编程背景。非常感谢您对如何解决问题的更多解释。
    • 嗯,你试过ifort -check all(或ifort /check:all)吗?从这一点开始,除非您从带有此类编译器选项的警告或错误消息中获得更多信息,或者在问题中包含更多相关代码,否则我们无法更进一步。在您的编译器环境中添加此选项是否很棘手? (我从未将 Windows 用于 Intel fortran,在这种情况下非常抱歉。)
    • 再试一次:你能把主程序(带有program语句),包括CALL INIT(...)语句的例程,以及将这两个程序单元连接到问题中的所有子例程(通过编辑它)?我的猜测是,在这些子例程中传递参数时会有一些问题(最可能的情况是某些例程没有声明,由于隐式规则而导致标量)。另外,请尝试在您的问题中包含您的“答案”作为“更新”(将所有信息收集在一个地方)。
    • 因为这也可能是一些与 Intel + Windows 相关的问题,我认为询问 Intel 论坛也很有用(请参阅问题中的第一条评论)。
    猜你喜欢
    • 1970-01-01
    • 2016-12-10
    • 2011-07-21
    • 1970-01-01
    • 2021-08-06
    • 2012-06-28
    • 1970-01-01
    • 2014-02-28
    • 1970-01-01
    相关资源
    最近更新 更多