Производные типы с размещаемыми массивами и операторами перегрузки в Fortran - PullRequest
0 голосов
/ 29 октября 2018

В моем коде я использую выделяемый производный тип данных (скажем, тип данных), в котором я храню многомерные размещаемые массивы (x и y). В том же модуле я также определяю подпрограммы для выделения / освобождения всего объекта, оператор присваивания (=) и дополнительные операторы перегрузки (*) и (+). Теперь я выделяю data1 (типа data), а также data1% x и data1% y в моей основной программе, инициализирую их и выполняю простую операцию с использованием операторов перегрузки (скажем, простого умножения всех элементов данных1). % 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, кажется, работает.

Я также немного поиграл с этим минимальным кодом и обнаружил, что, например, он работает с оптимизированным ifort 17, если структура данных «data» содержит один элемент x, или если он сам не является выделяемым массивом .

Вопрос очень прост: была ли проблема в более ранних версиях компилятора ifort или я что-то не так делаю? На данный момент я нашел очень простой обходной путь (который состоит в переопределении оператора (*) для работы с отдельными элементами данных, т.е. без какого-либо цикла в function data_times_data), но мне было бы интересно узнать чистый способ переписать код выше, чтобы избежать текущей проблемы при полном использовании функциональных возможностей оператора перегрузки.

Большое спасибо.

1 Ответ

0 голосов
/ 30 октября 2018

Я могу подтвердить segfault с помощью 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 - лучший стиль, в отличие от жесткого кодирования измерений в функциях, хотя, глядя на стандарт Фортрана, я не могу сразу найти ничего, что прямо запрещает то, что вы пытались сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...