Могу ли я передать переменную производному типу, чтобы каждый экземпляр моего производного типа мог иметь массивы разной длины?

Как лучше всего организовать в программе 11 одинаковых, но разного размера массивов без свойства allocatable?

Я представляю что-то вроде этого:

TYPE MyType(i)
   integer, intent(in) :: i

   integer, dimension(i,i) :: A
   integer, dimension(2*i,i) :: B
   integer, dimension(i,2*i) :: C

end type MyType

Затем в основной программе я могу объявить что-то вроде этого:

type(mytype), dimension(N) :: Array

При этом i-й элемент массива имеет доступ к трем массивам A, B и C, и каждый из этих трех массивов имеет разные размеры.

Проблема, с которой я сталкиваюсь в настоящее время, заключается в том, что я решаю проблему QM, и у меня есть 11 различных массивов, которые различаются по размеру, но все они зависят от одного и того же параметра (поскольку размер A, B и C зависит от i). Фактические значения этих массивов также не меняются.

Моя программа рассматривает различные виды систем, каждая из которых имеет свои A, B и C (просто для продолжения аналогии), и в каждой системе A, B и C имеют уникальный размер.

Если бы я знал, что ищу 6 различных типов систем, мне понадобилось бы 6 разных копий A, B и C.

В настоящее время A, B и C не являются частью производного типа, а вместо этого выделяются и пересчитываются на каждой итерации. Этот расчет занимает более одной десятой секунды для больших систем. Но я усредняю ​​свои результаты ~ 100 000 раз, что означает, что это может дать серьезную экономию времени. Кроме того, мне не хватает памяти.

Я пробовал вычислять эти массивы в другой программе, записывать их в файл и читать при необходимости, но, к сожалению, это было не быстрее, чем одновременный пересчет.

Примечание. Вот как выглядят мои фактические массивы:

  integer, dimension(:,:), allocatable :: fock_states      
  integer, dimension(:,:), allocatable :: PES_down, PES_up  
  integer, dimension(:,:), allocatable :: IPES_down, IPES_up 
  integer, dimension(:,:), allocatable :: phase_PES_down, phase_PES_up    
  integer, dimension(:,:), allocatable :: phase_IPES_down, phase_IPES_up 
  integer, dimension(:,:), allocatable :: msize       
  integer, dimension(:,:), allocatable :: mblock      

Каждый массив имеет разный размер для каждой системы.

Изменить:

Итак, что мне действительно нужно, так это N копий массивов в списке чуть выше этого редактирования. Массивы, принадлежащие i-й копии, имеют размер, который масштабируется с i (например, PES_down имеет размерность (i,4**i)). Насколько я понимаю, это означает, что мне нужно N разных объявлений переменных с типом MyType. Обычно это нормально, но проблема в том, что N определяется во время компиляции, но может меняться между запусками.

N имеет определенный максимум, но кажется, что много памяти потрачено впустую, когда я знаю, что не буду использовать массивы.


person Canadianphysics216    schedule 12.10.2018    source источник
comment
Итак, вы хотите, чтобы ваша переменная массива с именем Array имела размерность N, где N берется из пользовательского ввода? Кроме того, каждый элемент этого массива имеет производный тип с компонентами массива, где размерность этих массивов пропорциональна положению элемента в массиве?   -  person Rodrigo Rodrigues    schedule 15.10.2018
comment
Да, я думаю, что это точно. Я сейчас делаю это, как вы предложили в своем комментарии. Я не проводил много тестов, но мой опыт показывает, что выделяемые массивы работают медленнее, чем те, которые не являются. Поэтому я надеюсь, что определение его во время компиляции будет намного быстрее.   -  person Canadianphysics216    schedule 17.10.2018


Ответы (2)


(См. этот ответ для более подробного объяснения).

Как сказал @roygvib в своем комментарии, да, использование параметризованных производных типов в этом случае не только возможно, но и идеально подходит. Это одна из основных задач, которую PDT призван решить.

type :: MyType(i)
  integer, len :: i
  integer, dimension(i,i) :: A
  integer, dimension(2*i,i) :: B
  integer, dimension(i,2*i) :: C
  ! (...)
end type

Затем в основной программе вы должны объявить свой объект следующим образом (где i — известный параметр длины для текущего типа системы):

type(mytype(i)), dimension(N) :: Array

Но сначала проверьте наличие этой возможности в вашем компиляторе.

person Rodrigo Rodrigues    schedule 13.10.2018
comment
Вы имеете в виду, что OP хочет хранить объекты с разными параметрами типа в одном массиве? Если да, то это невозможно, потому что элементы массива должны иметь одинаковый размер хранилища (формально, одного и того же типа и вида). - person Rodrigo Rodrigues; 13.10.2018
comment
Я не уверен, но я думаю, что компоненты указателя - единственный способ эмулировать зубчатый массив. - person Rodrigo Rodrigues; 13.10.2018
comment
Кажется, это полностью соответствует тому, о чем я прошу. Теперь я понимаю, что мне нужна функциональность в дополнение к тому, что я описал в своем первоначальном посте. Я добавлю редактирование, чтобы уточнить дальше. - person Canadianphysics216; 15.10.2018

Я думаю, было бы проще всего использовать производный тип, содержащий A, B и C с переменной размера i, и выделять их для каждого i с помощью некоторой процедуры инициализации (здесь MyType_init()).

module mytype_mod
    implicit none

    type MyType
        integer :: i
        integer, dimension(:,:), allocatable :: A, B, C
    end type
contains

    subroutine MyType_init( this, i )
        type(MyType), intent(inout) :: this
        integer, intent(in) :: i

        allocate( this % A( i,   i   ), &
                  this % B( 2*i, i   ), &
                  this % C( i,   2*i ) )

        this % A = 0  !! initial values
        this % B = 0
        this % C = 0
    end subroutine

end module

program main
    use mytype_mod
    implicit none
    integer, parameter :: N = 2
    type(MyType) :: array( N )
    integer i

    do i = 1, N
        call MyType_init( array( i ), i )

        array( i ) % A(:,:) = i * 10   !! dummy data for check
        array( i ) % B(:,:) = i * 20
        array( i ) % C(:,:) = i * 30
    enddo

    do i = 1, N
        print *
        print *, "i = ", i
        print *, "    A = ", array( i ) % A(:,:)
        print *, "    B = ", array( i ) % B(:,:)
        print *, "    C = ", array( i ) % C(:,:)
    enddo

end program

Результат (с gfortran 8.1):

 i =            1
     A =           10
     B =           20          20
     C =           30          30

 i =            2
     A =           20          20          20          20
     B =           40          40          40          40          40          40          40          40
     C =           60          60          60          60          60          60          60          60

array(:) в основной программе можно выделить, так что

type(MyType), allocatable :: array(:)

N = 6
allocate( array( N ) )

что может быть полезно, когда N считывается из входного файла. Кроме того, мы можем создать процедуру с привязкой к типу из MyType_init(), изменив строки с (*) ниже (для использования стиля OO).

module mytype_m
    implicit none

    type MyType
        integer :: i
        integer, dimension(:,:), allocatable :: A, B, C
    contains
        procedure :: init => MyType_init   ! (*) a type-bound procedure
    end type

contains

    subroutine MyType_init( this, i )
        class(MyType), intent(inout) :: this   ! (*) use "class" instead of "type"
        ...
    end subroutine
end module

program main
    ...
    do i = 1, N
        call array( i ) % init( i )  ! (*) use a type-bound procedure
        ...
    enddo
    ...
end program
person roygvib    schedule 13.10.2018
comment
Но мне интересно, можно ли для этой цели использовать параметризованные типы...? - person roygvib; 13.10.2018