我一直在用 Fortran 编写一个软件包,用于解决 2D2V 中的 Vlasov-Poisson 系统。我希望该软件在其当前应用之外有用(例如,具有不同数量的位置或速度维度的系统、使用的数值方法的变化、完整 Maxwell 系统的解决方案等),这导致我使用抽象数据类型 (ADT)对于一些底层结构,以提高通用性和可重用性。
在我目前的离散化中,我在空间中使用拉伸笛卡尔网格,在速度中使用 Hermite 基函数。这是我当前实现的草图:
type cartesian_grid
! dimension of grid
int :: N
! physical coordinates
double precision, pointer :: x(:)
! metric
double precision, pointer :: xi_x(:)
! step size of computational coordinates
double precision :: delta_xi
end type
type hermite_grid
! length of coordinate array
int :: N
! coordinate array
double precision, pointer :: v(:)
! Moments of basis functions - used to compute moments of f.
double precision, pointer :: I0(:), I1(:)
! Scaling factor of Hermite basis functions
double precision :: ux
end type
对于我的时间积分,我需要计算 Vlasov 方程的 RHS:
call vlapo_dfdt( f, cartesian_grid1, cartesian_grid2, hermite_grid1, hermit_grid2, dfdt)
最后一个组件是显式时间积分器,它集成从到. 我写过很多时间积分器(RK4、RK Feldberg、AB2 等)。这些当前使用以下接口调用:
call time_integrator( t, h, f, cartesian_grid1, cartesian_grid2, hermite_grid1, hermite_grid2, vlapo_dfdt)
这是我遇到麻烦的地方。我想概括一下,以便网格表示不固定。这样,我可以将速度网格更改为(比如说)笛卡尔表示,而无需编写另一个时间积分例程。问题是我当前的积分器需要一个类型(hermite_grid)并且不接受速度网格的类型(cartesian_grid)。
我想概括这一点,以便我整合从到如下:
call time_integrator( t, h, f, space_grid1, space_grid2, vel_grid1, vel_grid2, deriv_function)
其中 space_grid1、space_grid2、vel_grid1、vel_grid2 可以是 Hermite 或笛卡尔网格,而 deriv_function 是与 vlapo_dfdt() 具有相同接口的过程变量。这样我就可以更改导数的实现,而无需重新编写时间步进器。
问题似乎是我的 ADT 不够抽象,但是如何在将计算导数所需的数据传递到我的 deriv_function() 的同时进一步概括它们?对于笛卡尔坐标,我需要度量和计算坐标步长。对于 Hermite 坐标,我需要基函数和比例因子的矩。对于其他表示,我完全需要其他数据。
在现代 Fortran 中实现这一目标的最佳方法是什么?这似乎是计算科学中一个普遍存在的问题,我认为有一种标准(或最佳实践)方法可以做到这一点。