Обобщение вашей операции для определенного объявленного типа в Fortran

У меня есть структура массивов, использующая объявленный тип в Fortran

e.g.

 type t_data 

    integer :: N
    real, allocatable :: x(:)
    real, allocatable :: z(:)      
    real, allocatable :: y(:)

    contains 

    procedure :: copy
    procedure :: SWAP 
    procedure :: copy_element

 end type 

 ! constructor 
 interface t_data
    module procedure constructor 
 end interface  

 contains 

 subroutine copy(this, old) 
    class(t_data), intent(inout)    :: this
    type(t_data), intent(in)        :: old
    do i = 1, old% N
       this% x(i) = old% x(i) 
       etc .. 
    end do
 end subroutine 

 subroutine copy(this, old) 
    class(t_data), intent(inout)    :: this
    type(t_data), intent(in)        :: old
    do i = 1, old% N
       this% x(i) = old% x(i) 
       etc .. 
    end do
 end subroutine 

 function constructor(size_) 
    integer, intent(in) :: size_ 
    type(t_data), :: constructor
    allocate(constructor% x(size_)) 
    allocate(constructor% y(size_) ) 
    ! etc 
 end function 

 subroutine swap(this, from, i1,i2) 
    class(t_particle_data), intent(inout) :: this
    type(t_particle_data), intent(in)    :: from
    integer, intent(in)                  :: i1, i2

    this% x(i1) = from% x(i2) 
    ! etc 
 end subroutine

Это набор примеров процедур, которые должны выполнять одинаковые операции со всеми массивами объявленного типа t_data. Мой вопрос заключается в том, как сделать его более удобным для решения ситуации, когда мы, например, позже хотим добавить новый компонент в объявленный тип. В настоящее время, когда я добавляю новый массив в свой t_data, мне нужно пройти через все эти процедуры, конструкторы, деконструкторы и добавить компонент.

Я спрашиваю, есть ли способ сделать это более легким.

МОЯ ЗАЯВКА

Обратите внимание, что этот тип данных используется для моделирования частиц. Изначально я выделяю t_data большим числом. Однако позже во время моделирования мне может понадобиться больше частиц. Следовательно, я выделяю новый t_data с большим объемом памяти и копирую старый t_data до его старого размера.

   subroutine NEW_ALLOC(new, old)
   type(t_data), intent(out) :: new  
   type(t_data), intent(inout) :: old  

   nsize = old% N * 2 ! allocate twice the old size 

   new = T_DATA(nsize) 
   call new% copy(old) 
   !DEALLCOte OLD
   end subroutine 

У кого-нибудь есть/можно ли сделать это более умным способом. Я не против смешать это с C/C++?


person A2LBK    schedule 05.09.2019    source источник
comment
Что делает подпрограмма copy, что отличается от внутреннего присваивания? Почему это связано с типом? Зачем использовать цикл для назначения массива? Как переход к массиву структур упрощает задачу? Вы спрашиваете, как последовательно применить некоторую операцию ко всем компонентам производного типа?   -  person IanH    schedule 05.09.2019
comment
@IanH, первый вопрос: без разницы, однако, в моем случае я выделяю больше элементов, чем необходимо в начале моей симуляции. Во время сима мне иногда требуется больше элементов, поэтому я выделяю тип данных с большим количеством элементов, а затем копирую только до размера «старых» данных. Второй квест: не должен. Третий вопрос: я думаю, из моего описания вы бы поняли, почему теперь это облегчает. Вам последний вопрос. Тогда да, пожалуйста, это ИМЕННО то, что я прошу. Пожалуйста, помогите мне, если у вас есть идея   -  person A2LBK    schedule 05.09.2019
comment
@highperformancemark, пожалуйста, объясните подробнее   -  person A2LBK    schedule 05.09.2019
comment
Обратите внимание, у меня была ошибка в моем примере, цикл должен идти от 1 до старого %N, так как мы только копируем старые элементы в новые (этот). Теперь новый тип (это) будет иметь N больше, чем старый% N, поэтому мой пример был бы ошибкой.   -  person A2LBK    schedule 05.09.2019
comment
@HighPerformanceMark Не могли бы вы взглянуть на мою исправленную тему?   -  person A2LBK    schedule 06.09.2019
comment
@IanH Не могли бы вы взглянуть на мою исправленную ветку?   -  person A2LBK    schedule 06.09.2019
comment
Мой вопрос заключается в том, как сделать его более удобным для обслуживания, чтобы справиться с ситуацией, когда мы, например, позже хотим добавить новый компонент в производный тип. В настоящее время, когда я добавляю новый массив в свои t_data, мне нужно пройти через все эти процедуры, конструкторы, деконструкторы и добавить новый компонент вручную.   -  person A2LBK    schedule 06.09.2019
comment
Не уверен, что это имеет прямое отношение, но вот вопрос, который я задал о создании функций, которые могут принимать произвольные внутренние типы и, вероятно, несколько расширяемы для производных типов: stackoverflow.com/questions/ 57792982/   -  person JohnE    schedule 06.09.2019
comment
Возможно, этот смысл будет полезен (обратите внимание, что это первая из двух частей): /522f2669979ed6d0582b8e80cf6c95fd   -  person JohnE    schedule 06.09.2019
comment
Если массив структур делает кодирование намного проще (чем структура массивов), это все еще может быть вариантом? (учитывая, что производительность не слишком отличается.) Fortran также может обращаться к скалярным компонентам как mystructs(:) % x и т. д., которые могут быть переданы другим процедурам как обычный массив предполагаемой формы.   -  person roygvib    schedule 07.09.2019
comment
У меня действительно есть пример того, что вы предлагаете @roygvib. Могу только сказать, что разница в производительности слишком разная. Особенно, когда я использую векторизацию.   -  person A2LBK    schedule 09.09.2019
comment
К вашему сведению, эта страница форума Intel, похоже, обсуждает накладные расходы на производительность при использовании SOA и AOS (с векторизацией). Я предполагаю, что также будет гибрид (или промежуточный) несколько AOS в структурном подходе.   -  person roygvib    schedule 09.09.2019
comment
Другими случайными идеями могут быть...: (1) тщательно писать процедуры инициализации, чтобы все типы компонентов обновлялись автоматически (например, путем получения старого объекта); (2) подготовьте отдельную переменную менеджера (например, массив указателей массива, X), которые указывают на поля SOA, затем передайте этот массив желаемой процедуре для выполнения унифицированной работы над X(i) % arr_ptr(:); (3) увеличить или уменьшить размер данных на месте (вместо копирования всех объектов), например. путем выделения несколько больших массивов компонентов с самого начала и т. д.   -  person roygvib    schedule 09.09.2019


Ответы (1)


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

Вот как бы я поступил в этой ситуации и сколько программистов на Фортране справились с этой ситуацией. Я не вижу настоятельной необходимости иметь производный тип, содержащий 3 массива координат, и такой подход к проблеме, как опасается OP, требует, чтобы добавление еще одного измерения к проблеме требовало пересмотра кода, например, добавления массива элементов real, allocatable :: w(:) на t_data и перекодировать все связанные с типом процедуры, работающие с этим типом.

Так что откажитесь от этого подхода в пользу

TYPE t_data
   REAL, DIMENSION(:,:), ALLOCATABLE :: elements
END TYPE t_data

давайте пару экземпляров для экспозиции

TYPE(t_data) :: t1 ,t2, t3

и мы можем выделить elements членов любого из них таким образом

ALLOCATE(t1%elements(3,10))

что так же легко может быть

ALLOCATE(t1%elements(6,100))

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

Теперь скопировать t1 так же просто, как

t2 = t1

Современный Фортран даже заботится об автоматическом выделении elements из t2. Поэтому я не вижу необходимости определять процедуры для копирования целых экземпляров t_data. Что касается обмена данными, нарезки и нарезки, это так же просто, как

t2%elements(1,:) = t1%elements(2,:)

даже

t2%elements(1,:) = t1%elements(1,6:1:-1)

or

t2%elements(1,:) = t1%elements(1,[1,3,5,2,4,6])

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

Далее к вопросу о необходимости выделять больше места для элементов во время выполнения. Сначала временный массив

REAL, DIMENSION(:,:), ALLOCATABLE :: temp

затем небольшой код, подобный этому, чтобы удвоить размер elements.

ALLOCATE(temp(3,2*n))
temp(:,1:n) = t2%elements(:,1:n)
CALL MOVE_ALLOC(to=t2%elements,from=temp)

Опять же, вы можете заключить это в процедуру, и если вам нужна помощь в этом, попросите об этом.

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

person High Performance Mark    schedule 07.09.2019
comment
Спасибо за ответ. Теперь мой пример явно упрощен. Но вообще говоря, мой код состоит из тысяч строк со множеством объектов для разных частей. Этот объект, в частности, буквально содержит более 20 переменных, и хотя это может быть хорошим способом обойти его в некоторых ограниченных случаях, я, честно говоря, не думаю, что это работает с моим приложением. Представьте, что у меня есть 20 компонентов производного типа, тогда мне понадобится от t1 до t20. - person A2LBK; 09.09.2019
comment
К сожалению, я могу ответить только на вопрос, который вы задаете, поэтому я не удивлен, что мой ответ слишком прост для вашего реального случая. - person High Performance Mark; 09.09.2019
comment
При всем уважении, Ваше решение перемещает эту проблему от необходимости обработки многих компонентов в процедуре к обработке многих объявленных типов. Весь смысл этого заключался в том, как решить проблему, когда мы могли бы захотеть создать дополнительные компоненты для нашего типа. Это решение здесь потребует дополнительных объявленных типов t4,t5, которые необходимо включить в различные подпрограммы. Я понимаю вашу точку зрения, которую я также признал, однако я думаю, что будет вполне справедливо сказать, что это не решает проблему, которая представлена. - person A2LBK; 09.09.2019
comment
Что ж, как я уже писал ранее, я действительно не понимаю вашего вопроса, поэтому неудивительно, что мой «ответ» бесполезен для вас! - person High Performance Mark; 10.09.2019