在 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,ParticleA
和 ParticleB
都可以从中继承,例如
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
然后您可以使用这些 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_particles
与 size(particle_group%particles)
分开是多余的,但我想如果您开始执行更高级的 memory-storage 策略(例如 C++ std::vector-样式存储)。
我是 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,ParticleA
和 ParticleB
都可以从中继承,例如
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
然后您可以使用这些 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_particles
与 size(particle_group%particles)
分开是多余的,但我想如果您开始执行更高级的 memory-storage 策略(例如 C++ std::vector-样式存储)。