【问题标题】:Derived types with allocatable arrays and overload operators in FortranFortran 中具有可分配数组和重载运算符的派生类型
【发布时间】:2018-10-29 14:30:01
【问题描述】:

在我的代码中,我使用了一种可分配的派生数据类型(例如,类型数据),其中存储了多维可分配数组(x 和 y)。在同一个模块中,我还定义了分配/解除分配整个事物的例程、赋值运算符 (=) 以及其他重载运算符 (*) 和 (+)。现在,我在主程序中分配 data1(数据类型)以及 data1%x 和 data1%y,初始化它们,并使用重载运算符执行一个简单的操作(假设是 data1 的所有元素的简单乘法%x 和 data1%y 为常数)。这是编译和重现我刚刚描述的内容的最小代码:

program minimal

  USE dimensions
  USE typedef

  IMPLICIT NONE

  integer :: i, k
  type(data), dimension(:), allocatable :: data1, data2

  call alloc ( data1 )
  call alloc ( data2 )

  do k = 1 , ndat
    data1(k)%x = real(k)
    data1(k)%y = -real(k)
    data2(k)%x = 0.
    data2(k)%y = 0.
  enddo

  do i = 1, 10
    data2 = data2 + 2.*data1
  enddo

  do k = 1, ndat
    print*, k, maxval(data2(k)%x), maxval(data2(k)%y)
  enddo

  call dealloc ( data1 )
  call dealloc ( data2 )

end program

和模块:

module dimensions
  integer :: ndat=2
  integer :: m1=10, m2=50
  integer :: n1=10, n2=50
end module dimensions


module typedef

  USE dimensions

  type :: data
    real, dimension(:,:), allocatable :: x
    real, dimension(:,:), allocatable :: y
  end type data

  interface alloc
    module procedure alloc_data
  end interface alloc

  interface dealloc
    module procedure dealloc_data
  end interface dealloc

  interface assignment (=)
    module procedure data_to_data
  end interface

  interface operator (*)
    module procedure const_times_data
  end interface

  interface operator (+)
    module procedure data_plus_data
  end interface

  CONTAINS

  subroutine alloc_data (data1)
    type(data), dimension(:), allocatable, intent(inout) :: data1
    integer :: i

    allocate ( data1(1:ndat) )
    do i = 1, ndat
      allocate ( data1(i)%x(m1:m2,n1:n2) )
      allocate ( data1(i)%y(m1:m2,n1:n2) )
    enddo

  end subroutine alloc_data

  subroutine dealloc_data (data1)
    type(data), dimension(:), allocatable, intent(inout) :: data1
    integer :: i

    do i = 1, ndat
      deallocate ( data1(i)%x )
      deallocate ( data1(i)%y )
    enddo
    deallocate ( data1 )

  end subroutine dealloc_data

  subroutine data_to_data (data2,data1)
    type(data), dimension(:), intent(in) :: data1
    type(data), dimension(1:ndat), intent(out) :: data2
    integer :: i

    do i = 1, ndat
      data2(i)%x = data1(i)%x
      data2(i)%y = data1(i)%y
    enddo

  end subroutine data_to_data

  function const_times_data (c,data1) result(data2)
    type(data), dimension(:), intent(in) :: data1
    real, intent(in) :: c
    type(data), dimension(1:ndat) :: data2
    integer :: i

    do i = 1, ndat
      data2(i)%x = c*data1(i)%x
      data2(i)%y = c*data1(i)%y
    enddo

  end function const_times_data

  function data_plus_data (data1,data2) result(data3)
    type(data), dimension(:), intent(in) :: data1, data2
    type(data), dimension(1:ndat) :: data3
    integer :: i

    do i = 1, ndat
      data3(i)%x = data1(i)%x + data2(i)%x
      data3(i)%y = data1(i)%y + data2(i)%y
    enddo

  end function data_plus_data

end module typedef

使用 ifort 17.0(我们机器上的推荐版本)和 -O0 选项进行调试编译代码不会返回任何问题。但是,使用优化级别 -O2 或 -O3 会产生分段错误。我用 ifort 18.0 尝试了相同的过程,结果相同,而 ifort 19.0 似乎可以工作。

我也对这个最小的代码进行了一些尝试,发现例如,如果数据结构“data”包含单个元素 x,或者它本身不是可分配的数组,它可以与优化的 ifort 17 一起使用.

问题很简单:早期版本的 ifort 编译器有问题,还是我做错了什么?现在,我找到了一个非常简单的解决方法(包括重新定义运算符 (*) 以处理单个数据元素,即 function data_times_data 中没有任何循环),但我有兴趣知道一种干净的重写方法上面的代码可以在充分利用重载运算符功能的同时避免当前问题。

非常感谢。

【问题讨论】:

  • data2 已分配但未在循环内的第一个表达式 data2 = data2 + 2.*data1 之前初始化,并且您在赋值的 rhs 上使用它。也许这就是导致段错误的原因。
  • 是的,感谢您注意到这一点。在原始代码中,data2在进入循环之前被初始化为0,它仍然会产生错误。编辑上面显示的代码以初始化 data2 也无济于事。这是我忘记初始化的错误,但我可以确认问题不是来自那里。
  • 请更新问题,以便初始化,其他人不会因初始化问题而绊倒。
  • 我现在已经编辑了代码以在初始化循环中初始化 data2。
  • 出于好奇,是否将 data_to_data() 中 data2 的 intent(out) 更改为 intent(inout) 可能会删除段错误(w/ 或 w/o -standard-semantics)?

标签: fortran intel-fortran


【解决方案1】:

我可以用 ifort 18.0 确认段错误。由于某种原因,在重载 +* 运算符时,编译器不喜欢将虚拟参数作为数组。我建议保留参数标量并改为使用elemental 函数:

module dimensions
  integer :: ndat=2
  integer :: m1=10, m2=50
  integer :: n1=10, n2=50
end module dimensions


module typedef

  USE dimensions

  type :: data
    real, dimension(:,:), allocatable :: x
    real, dimension(:,:), allocatable :: y
  end type data

  interface alloc
    module procedure alloc_data
  end interface alloc

  interface dealloc
    module procedure dealloc_data
  end interface dealloc

  interface assignment (=)
    module procedure data_to_data
  end interface

  interface operator (*)
    module procedure const_times_data
  end interface

  interface operator (+)
    module procedure data_plus_data
  end interface

  CONTAINS

  subroutine alloc_data (data1)
    type(data), dimension(:), allocatable, intent(inout) :: data1
    integer :: i

    allocate ( data1(1:ndat) )
    do i = 1, ndat
      allocate ( data1(i)%x(m1:m2,n1:n2) )
      allocate ( data1(i)%y(m1:m2,n1:n2) )
    enddo

  end subroutine alloc_data

  subroutine dealloc_data (data1)
    type(data), dimension(:), allocatable, intent(inout) :: data1
    integer :: i

    do i = 1, ndat
      deallocate ( data1(i)%x )
      deallocate ( data1(i)%y )
    enddo
    deallocate ( data1 )

  end subroutine dealloc_data

  elemental subroutine data_to_data (data2,data1)
    type(data), intent(in) :: data1
    type(data), intent(out) :: data2
    integer :: i

    data2%x = data1%x
    data2%y = data1%y

  end subroutine data_to_data

  elemental function const_times_data (c,data1) result(data2)
    type(data), intent(in) :: data1
    real, intent(in) :: c
    type(data) :: data2
    integer :: i

    data2%x = c*data1%x
    data2%y = c*data1%y

  end function const_times_data

  elemental function data_plus_data (data1,data2) result(data3)
    type(data), intent(in) :: data1, data2
    type(data) :: data3
    integer :: i

    data3%x = data1%x + data2%x
    data3%y = data1%y + data2%y

  end function data_plus_data

end module typedef

我认为无论如何使用elemental 是更好的风格,而不是将尺寸硬编码到函数中,尽管查看 Fortran 标准我无法立即找到任何直接禁止您尝试做的事情的内容。

【讨论】:

  • 非常感谢您的回复。我确实考虑过将所有运算符转换为基本函数,但这比我实现的快速修复需要更多的工作(我只是懒惰在那里)。不过,我相信这是我最终会采用的解决方案。
  • @JuS 我假设您还在英特尔网站的论坛中创建了一个错误报告。
  • 没有。我想首先通过询问 fortran 专家来确保我没有做错任何事情。我不想提交一个错误报告,结果证明它只是糟糕的编码。
  • 由于 ifort 19 有效,错误报告似乎不合适。
猜你喜欢
  • 1970-01-01
  • 2020-03-17
  • 2016-10-04
  • 2014-10-18
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 2016-11-08
  • 2014-10-19
相关资源
最近更新 更多