PALM 代码函数调用链解析

notes

This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.

搞清楚 PALM 函数调用链解析是为了更好的进行 debug。

palm.f90

程序的主入口在 palm.f90 中。开头的一大段暂时不看,都是 use 声明,变量声明和 mpi 并行设置。真正有意义的代码从大约 220 行开始。

首先是读取 namelist 设置。

1
2
! Read control parameters from NAMELIST files and read environment-variables.
call parin

parin.f90

这个文件中只有一个 parin 函数,用来从 namelistENVPAR (所谓的环境变量) 中读取设置。实际上这也相当于一个总入口,除了读取部分设置外,其中有调用各个子函数读取剩余的设置。

初始化参数在 parin 中读取。

1
read (11, initialization_parameters, IOSTAT=io_status)

表层数据输出参数在 surface_data_output_parin 中读取。

1
2
3
4
5
! parin.f90
call surface_data_output_parin

! surface_data_output_mod.f90
read (11, surface_data_output_parameters, IOSTAT=io_status)

PALM 各个模块的参数在 module_interface_parin 中读取,这个函数又调用了很多其他子函数来读取,暂时不详细深究。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
! parin.f90
call module_interface_parin

! model_interface.f90
call dynamics_parin
call bio_parin
call bcm_parin
call chem_parin
call dcep_parin
call det_parin
call dmp_parin
call f8c_parin
call fct_parin
call flight_parin
call gust_parin
call im_parin
call lpm_parin
call lsm_parin
call mas_parin
call nesting_offl_parin
call ocean_parin
call pcm_parin
call radiation_parin
call salsa_parin
call slurb_parin
call spectra_parin
call stg_parin
call trm_parin
call turbulent_inflow_parin
call usm_parin
call uv_radiation_parin
call vm_parin
call wtm_parin
call user_parin

除了读取参数设置,parin 还计算了分块的数量,以并行读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
io_blocks = global_procs / maximum_parallel_io_streams
io_group = mod(global_id + 1, io_blocks)
...
! Blockwise I/O does not work together with MPI-I/O
if (restart_data_format_input(1:3) == 'mpi' .and. io_blocks /= 1) then
call rrd_global
else
! Data is read in parallel by groups of PEs
do i = 0, io_blocks - 1
if (i == io_group) then
call rrd_global
end if
#if defined( __parallel )
call MPI_BARRIER(comm2d, ierr)
#endif
end do
end if

运行时参数也在 parin 中读取。如果是 restart 模拟,这个函数会再读取 restart 参数后再调用一次,以让新的设置覆盖旧参数。

1
read (11, runtime_parameters, IOSTAT=io_status)

如果是嵌套模拟,还会设置子区域的边界条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (nested_run .and. child_domain) then
! Set conditions along x.
if (.not. (nesting_bounds == 'vertical_only' .or. nesting_bounds == 'cyclic_along_x')) &
then
bc_lr = 'nested'
bc_lr_set_internally = .true.
end if
! Set conditions along y.
if (.not. (nesting_bounds == 'vertical_only' .or. nesting_bounds == 'cyclic_along_y')) &
then
bc_ns = 'nested'
bc_ns_set_internally = .true.
end if
end if

以及一些正常的边界条件的设置。


回到主函数

参数读取完毕之后,是关于处理器拓扑的计算。因为 PALM 模式中的某些算法对 CPU 核数有要求。详细内容暂时先跳过。

1
2
! Determine processor topology and local array indices.
call init_pegrid

然后是输入数据的检查,主要是在启用了 __netcdf 的情况下检查各个 driver 是否存在。

1
2
! Check if input file according to input-data standard exists.
call netcdf_data_input_inquire_file

然后是网格的初始化。

1
2
! Generate grid parameters.
call init_grid

然后是输入数据一些全局属性的读取。

1
2
! Read global attributes if available.
call netcdf_data_input_init

然后是地形数据的读取。

1
2
! Initialize topography.
call init_topography

topography_mod.f90

在函数 init_topography 中,首先调用了子函数 topography_input

以下只考虑从 static driver 输入地形的情况。


该函数首先检查地形数据的属性,判断其网格分布和模式的网格是否匹配。然后试图读取地形高度数据,存储在 terrain_height_f % var 中。

1
2
3
4
5
6
7
8
9
10
! Terrain height. First, get variable-related _fillvalue attribute.
if (check_existence(var_names, 'zt')) then
terrain_height_f % from_file = .true.
call get_attribute(id_topo, char_fill, terrain_height_f % fill, .false., 'zt')
! Input 2D terrain height.
allocate (terrain_height_f % var(nys:nyn, nxl:nxr))
call get_variable(id_topo, 'zt', terrain_height_f % var, nxl, nxr, nys, nyn, nbgp=0)
else
terrain_height_f % from_file = .false.
end if

然后通过判断 buildings_2dbuildings_3d 是否存在来读取建筑数据,分别存储在 buildings_f % var_2dbuildings_f % var_3d 中。如果两者都存在,优先使用 3d 的数据。建筑物的 id (building_id) 和类型 (building_type) 也会被读取,存储在 building_id_f % varbuilding_type_f % var 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
! Read building IDs and its FillValue attribute. Further required for mapping buildings on top
! of orography.
if (check_existence(var_names, 'building_id')) then
building_id_f % from_file = .true.
call get_attribute(id_topo, char_fill, building_id_f % fill, .false., 'building_id')
allocate (building_id_f % var(nysg:nyng, nxlg:nxrg))
call get_variable(id_topo, 'building_id', building_id_f % var, nxl, nxr, nys, nyn, &
nbgp=nbgp)
else
building_id_f % from_file = .false.
end if
! Read building_type and required attributes.
if (check_existence(var_names, 'building_type')) then
building_type_f % from_file = .true.
call get_attribute(id_topo, char_fill, building_type_f % fill, .false., 'building_type')
allocate (building_type_f % var(nysg:nyng, nxlg:nxrg))
call get_variable(id_topo, 'building_type', building_type_f % var, nxl, nxr, nys, nyn, &
nbgp=nbgp)
else
building_type_f % from_file = .false.
end if

然后函数 topography_prepare_and_check 被调用,来进行相关的初始化和一致性检查。

1
2
! Perform general initialization tasks and consistency checks.
call topography_prepare_and_check

Author: Syize

Permalink: https://blog.syize.cn/2026/02/28/note-palm-code-call-chain/

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Syizeのblog

Comments