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

Assigning pointers to allocatable derived type components in Fortran

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

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 

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

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 中有没有一种方法可以在初始化期间存储要循环的数组(作为指针?),这样我就不必在每次循环迭代时都检查 particle_kind ?或者有更好的方法来处理这个问题吗?

如果你只对在运行时使用 either ParticleA or ParticleB 那么你可以实现你想要什么使用多态数组。

您首先必须定义一个父项 class,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 编写为具有单个多态数组,在运行时可以是 ParticleAParticleB 类型,类似于

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

然后您可以使用这些 class 等

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

在简单的示例中,将 particle_group%no_particlessize(particle_group%particles) 分开是多余的,但我想如果您开始执行更高级的 memory-storage 策略(例如 C++ std::vector-样式存储)。