在 Fortran 中将指针分配给可分配的派生类型组件

发布于 2025-01-20 11:40:00 字数 1115 浏览 0 评论 0原文

我是 Fortran 新手,正在开发一个代码,我经常在其中对两个相似的派生类型(在本例中表示科学计算代码中的粒子模型)进行操作。这两种类型是相同的,只是一种仅具有平移位置和速度分量,而另一种还包括角位置和速度。

type(particle_A)
  real :: position(3)
  real :: velocity(3)
end type

type(particle_B)
  real :: position(6)
  real :: velocity(6)
end type

我有另一种派生类型,它将两种粒子的数组(以及实际应用程序中的其他一些数据)分组:

type(particleGroup)
  type(particle_A), allocatable :: particleA_array(:)
  integer :: NparticlesA
  type(particle_B), allocatable :: particleB_array(:)
  integer :: NparticlesB
  character(len=30) :: particle_kind
end type 

如果有更多粒子进入此特定过程的模拟,则在运行时期间分配和重新分配两个数组。根据粒子类型(在初始化期间)的设置,我想循环这两个数组之一并调用一些子例程(例如,用于更新位置和速度)。像这样(在循环第一个数组的情况下)

do iParticle = 1, nParticlesA
  call updateVelocity( particleGroup%particleA_array(iParticle) )
end do

循环第二个数组

do iParticle = 1, nParticlesB
  call updateVelocity( particleGroup%particleB_array(iParticle) )
end do

和使用使用接口块实现的通用过程 updateVelocity 。 Fortran 中有没有一种方法可以存储初始化期间要循环的数组(作为指针?),这样我就不必在每次循环迭代时检查 article_kind ?或者有更好的方法来处理这个问题吗?

I am new to Fortran and am developing a code in which I frequently do operations on two similar derived types (in this case representing models of particles in a scientific computing code). The two types are identical except one has only translational position and velocity components whereas the other also includes angular positions and velocities.

type(particle_A)
  real :: position(3)
  real :: velocity(3)
end type

type(particle_B)
  real :: position(6)
  real :: velocity(6)
end type

I have another derived type which groups arrays of both kinds of particles (together with some other data in the actual application):

type(particleGroup)
  type(particle_A), allocatable :: particleA_array(:)
  integer :: NparticlesA
  type(particle_B), allocatable :: particleB_array(:)
  integer :: NparticlesB
  character(len=30) :: particle_kind
end type 

Where both arrays are allocated and re-allocated during runtime if more particles enter the simulation on this particular process. Depending on what particle_kind is set to (during initialization) I want to loop over one of these two arrays and call some subroutine (e.g. for updating the position and velocity). Something like this (in case of looping over the first array)

do iParticle = 1, nParticlesA
  call updateVelocity( particleGroup%particleA_array(iParticle) )
end do

and this for looping over the second array

do iParticle = 1, nParticlesB
  call updateVelocity( particleGroup%particleB_array(iParticle) )
end do

with the generic procedure updateVelocity implemented using an interface block.
Is there a way in Fortran to store which array to loop over during initialization (as a pointer?), so that I do not have to check particle_kind at every loop iteration? Or is there some better way of handling this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

流年里的时光 2025-01-27 11:40:01

如果您只对在运行时使用 ParticleA ParticleB 感兴趣,那么您可以使用多态数组。

您首先必须定义一个父类,ParticleAParticleB 都可以从该父类继承,类似于

module Particle_module
  implicit none

  type, abstract :: Particle
  contains
    procedure(Particle_update_velocity), deferred :: update_velocity
  end type

  interface
    subroutine Particle_update_velocity(this)
      import Particle
      class(Particle), intent(inout) :: this
    end subroutine
  end interface
end module

然后您可以将 ParticleA 写为

module ParticleA_module
  use Particle_module
  implicit none

  type, extends(Particle) :: ParticleA
    real :: position(3)
    real :: velocity(3)
  contains
    procedure :: update_velocity => ParticleA_update_velocity
  end type
contains
  subroutine ParticleA_update_velocity(this)
    class(ParticleA), intent(inout) :: this

    ! ParticleA's update_velocity code goes here.
  end subroutine
end module

和 类似对于ParticleB

这将允许您将 ParticleGroup 编写为具有单个多态数组,该数组在运行时可以是 ParticleA 类型或 ParticleB 类型,例如

module ParticleGroup_module
  use Particle_module
  implicit none

  type :: ParticleGroup
    class(Particle), allocatable :: particles(:)
    integer :: no_particles
  contains
   procedure :: update_velocities => ParticleGroup_update_velocities
  end type
contains
  subroutine ParticleGroup_update_velocities(this)
    class(ParticleGroup), intent(inout) :: this

    integer :: i

    do i=1,this%no_particles
      call this%particles(i)%update_velocity()
    enddo
  end subroutine
end module

You然后可以使用这些类,例如

program p
  use Particle_module
  use ParticleA_module
  use ParticleB_module
  use ParticleGroup_module
  implicit none

  type(ParticleGroup) :: particle_group

  ! Fill out the variables of `particle_group`.
  ! Note that the `%particles` array now has the concrete type of `ParticleA`.
  particle_group%particles = [ &
     & ParticleA([0., 0., 0.], [1., 2., 3.]), &
     & ParticleA([1., 1., 1.], [2., 3., 1.]) &
     & ]
  particle_group%no_particles = 2

  ! Call all the `update_velocity` subroutines.
  call particle_group%update_velocities()
end program

在简单的示例中,将 article_group%no_articles 单独保留到 size(article_group%articles) 是多余的,但我想如果您开始这样做,您可能需要它更先进内存存储策略(例如C++ std::向量式存储)。

If you are only interested in using either ParticleA or ParticleB at runtime then you can achieve what you want using a polymorphic array.

You would first have to define a parent class, from which both ParticleA and ParticleB can inherit, something like

module Particle_module
  implicit none

  type, abstract :: Particle
  contains
    procedure(Particle_update_velocity), deferred :: update_velocity
  end type

  interface
    subroutine Particle_update_velocity(this)
      import Particle
      class(Particle), intent(inout) :: this
    end subroutine
  end interface
end module

Then you could write ParticleA as

module ParticleA_module
  use Particle_module
  implicit none

  type, extends(Particle) :: ParticleA
    real :: position(3)
    real :: velocity(3)
  contains
    procedure :: update_velocity => ParticleA_update_velocity
  end type
contains
  subroutine ParticleA_update_velocity(this)
    class(ParticleA), intent(inout) :: this

    ! ParticleA's update_velocity code goes here.
  end subroutine
end module

and analogously for ParticleB.

This would allow you to write ParticleGroup as having a single polymorphic array, which could be either of type ParticleA or ParticleB at runtime, something like

module ParticleGroup_module
  use Particle_module
  implicit none

  type :: ParticleGroup
    class(Particle), allocatable :: particles(:)
    integer :: no_particles
  contains
   procedure :: update_velocities => ParticleGroup_update_velocities
  end type
contains
  subroutine ParticleGroup_update_velocities(this)
    class(ParticleGroup), intent(inout) :: this

    integer :: i

    do i=1,this%no_particles
      call this%particles(i)%update_velocity()
    enddo
  end subroutine
end module

You could then use these classes like

program p
  use Particle_module
  use ParticleA_module
  use ParticleB_module
  use ParticleGroup_module
  implicit none

  type(ParticleGroup) :: particle_group

  ! Fill out the variables of `particle_group`.
  ! Note that the `%particles` array now has the concrete type of `ParticleA`.
  particle_group%particles = [ &
     & ParticleA([0., 0., 0.], [1., 2., 3.]), &
     & ParticleA([1., 1., 1.], [2., 3., 1.]) &
     & ]
  particle_group%no_particles = 2

  ! Call all the `update_velocity` subroutines.
  call particle_group%update_velocities()
end program

In simple examples, keeping particle_group%no_particles separately to size(particle_group%particles) is redundant, but I guess you might need it if you start doing more advanced memory-storage strategies (e.g. C++ std::vector-style storage).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文