Skip to content

Commit 52509fa

Browse files
committed
Add aerosol pfUnit tests (coauthored with claude-opus-4.6)
1 parent 522c3d4 commit 52509fa

17 files changed

Lines changed: 1602 additions & 0 deletions

‎test/unit/fortran/CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ if(CAM_SIMA_ENABLE_TESTS OR CAM_SIMA_ENABLE_CODE_COVERAGE OR CAM_SIMA_ENABLE_IO_
3232

3333
if(CAM_SIMA_ENABLE_TESTS)
3434
add_subdirectory(src/core_utils)
35+
add_subdirectory(src/aerosol)
3536
endif()
3637
if(CAM_SIMA_ENABLE_IO_TESTS)
3738
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Unit tests for aerosol modules
2+
#
3+
# Organized by dependency complexity:
4+
# Tier 1: Pure functions (table_interp) — no mocking needed
5+
# Tier 2: Parsing/definitions (parse_rad_specifier, list_populate) — minimal mocking
6+
# Tier 3: Concrete BAM implementations — mock at radiative_aerosol facade level
7+
# Tier 4: Concrete optics types — uses Tier 3 infrastructure end-to-end
8+
#
9+
# Libraries are structured to avoid duplicate compilation:
10+
# aerosol_test_mocks — CAM infrastructure stubs (cam_abortutils, etc.)
11+
# table_interp_testlib — Tier 1 (pure functions, no mock deps)
12+
# rad_aer_defs_testlib — Tier 2 (radiative_aerosol_definitions parsing)
13+
# bulk_aero_testlib — Tier 3 (BAM properties + state + all their deps)
14+
# insoluble_optics_testlib — Tier 4 (adds optics module on top of Tier 3)
15+
16+
set(AEROSOL_SRC ${CMAKE_SOURCE_DIR}/../../../src/aerosol)
17+
set(UTILS_SRC ${CMAKE_SOURCE_DIR}/../../../src/utils)
18+
set(CORE_UTILS ${CMAKE_SOURCE_DIR}/../../../src/core_utils)
19+
# ccpp_kinds.F90 stub. When CAM_SIMA_ENABLE_IO_TESTS is also ON, the
20+
# ccpp_framework target is built separately. We compile our own copy to
21+
# avoid depending on that target being present.
22+
set(CCPP_STUB ${CMAKE_SOURCE_DIR}/ccpp_framework_stub/ccpp_kinds.F90)
23+
24+
# ========================================================================
25+
# Mock library: lightweight stubs for CAM infrastructure modules
26+
# ========================================================================
27+
add_library(aerosol_test_mocks
28+
shr_kind_mod.F90
29+
mock_cam_abortutils.F90
30+
mock_cam_logfile.F90
31+
mock_spmd_utils.F90
32+
mock_physconst.F90
33+
mock_shr_infnan_mod.F90
34+
mock_shr_string_mod.F90
35+
)
36+
target_compile_options(aerosol_test_mocks PRIVATE -ffree-line-length-none)
37+
target_include_directories(aerosol_test_mocks PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
38+
39+
# ========================================================================
40+
# Tier 1: table_interp — pure interpolation functions
41+
# ========================================================================
42+
add_library(table_interp_testlib
43+
${UTILS_SRC}/table_interp_mod.F90
44+
)
45+
target_link_libraries(table_interp_testlib PUBLIC aerosol_test_mocks)
46+
target_compile_options(table_interp_testlib PRIVATE -ffree-line-length-none)
47+
48+
add_pfunit_ctest(test_table_interp
49+
TEST_SOURCES test_table_interp.pf
50+
LINK_LIBRARIES table_interp_testlib)
51+
52+
# ========================================================================
53+
# Tier 2: radiative_aerosol_definitions parsing and list population
54+
# ========================================================================
55+
add_library(rad_aer_defs_testlib
56+
${AEROSOL_SRC}/radiative_aerosol_definitions.F90
57+
)
58+
target_link_libraries(rad_aer_defs_testlib PUBLIC aerosol_test_mocks)
59+
target_compile_options(rad_aer_defs_testlib PRIVATE -ffree-line-length-none)
60+
61+
add_pfunit_ctest(test_parse_rad_specifier
62+
TEST_SOURCES test_parse_rad_specifier.pf
63+
LINK_LIBRARIES rad_aer_defs_testlib)
64+
65+
add_pfunit_ctest(test_list_populate
66+
TEST_SOURCES test_list_populate.pf
67+
LINK_LIBRARIES rad_aer_defs_testlib)
68+
69+
# ========================================================================
70+
# Tier 3: Shared library for BAM properties + state + mocks
71+
# ========================================================================
72+
add_library(bulk_aero_testlib
73+
${AEROSOL_SRC}/aerosol_properties_mod.F90
74+
${AEROSOL_SRC}/aerosol_state_mod.F90
75+
${AEROSOL_SRC}/bulk_aerosol_properties_mod.F90
76+
${AEROSOL_SRC}/bulk_aerosol_state_mod.F90
77+
${UTILS_SRC}/string_utils.F90
78+
${CORE_UTILS}/string_core_utils.F90
79+
mock_radiative_aerosol.F90
80+
mock_aerosol_mmr_ccpp.F90
81+
${CCPP_STUB}
82+
)
83+
target_link_libraries(bulk_aero_testlib PUBLIC aerosol_test_mocks)
84+
target_compile_options(bulk_aero_testlib PRIVATE -ffree-line-length-none)
85+
86+
add_pfunit_ctest(test_bulk_aerosol_properties
87+
TEST_SOURCES test_bulk_aerosol_properties.pf
88+
LINK_LIBRARIES bulk_aero_testlib)
89+
90+
add_pfunit_ctest(test_bulk_aerosol_state
91+
TEST_SOURCES test_bulk_aerosol_state.pf
92+
LINK_LIBRARIES bulk_aero_testlib)
93+
94+
# ========================================================================
95+
# Tier 4: insoluble_aerosol_optics — end-to-end with real BAM objects
96+
# ========================================================================
97+
add_library(insoluble_optics_testlib
98+
${AEROSOL_SRC}/aerosol_optics_mod.F90
99+
${AEROSOL_SRC}/insoluble_aerosol_optics_mod.F90
100+
)
101+
target_link_libraries(insoluble_optics_testlib PUBLIC bulk_aero_testlib)
102+
target_compile_options(insoluble_optics_testlib PRIVATE -ffree-line-length-none)
103+
104+
add_pfunit_ctest(test_insoluble_aerosol_optics
105+
TEST_SOURCES test_insoluble_aerosol_optics.pf
106+
LINK_LIBRARIES insoluble_optics_testlib)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
!-----------------------------------------------------------------------
2+
! Mock aerosol_mmr_ccpp module for unit testing bulk_aerosol_state.
3+
!
4+
! The real module retrieves aerosol mixing ratios from the CCPP
5+
! constituents array by looking up indices that were resolved during
6+
! initialization. This mock bypasses the index resolution and directly
7+
! returns constituents(:,:,aer_idx) for a given aerosol index.
8+
!
9+
! This means the test constituents array should be set up so that
10+
! constituents(:,:,i) contains the MMR for aerosol i.
11+
!-----------------------------------------------------------------------
12+
module aerosol_mmr_ccpp
13+
14+
use shr_kind_mod, only: r8 => shr_kind_r8
15+
use ccpp_kinds, only: kind_phys
16+
17+
implicit none
18+
private
19+
20+
! Generic interface matching the real module
21+
interface rad_cnst_get_aer_mmr
22+
module procedure rad_cnst_get_aer_mmr_by_idx
23+
end interface
24+
25+
public :: rad_cnst_get_aer_mmr
26+
27+
contains
28+
29+
!-----------------------------------------------------------------------
30+
! Mock rad_cnst_get_aer_mmr: directly index into constituents(:,:,aer_idx).
31+
! In the real module, aer_idx is used to look up the resolved CCPP index.
32+
! Here we short-circuit that and use aer_idx directly as the 3rd dimension.
33+
!-----------------------------------------------------------------------
34+
subroutine rad_cnst_get_aer_mmr_by_idx(list_idx, aer_idx, constituents, mmr)
35+
integer, intent(in) :: list_idx
36+
integer, intent(in) :: aer_idx
37+
real(kind_phys), target, intent(in) :: constituents(:,:,:)
38+
real(r8), pointer :: mmr(:,:)
39+
40+
mmr => constituents(:, :, aer_idx)
41+
end subroutine rad_cnst_get_aer_mmr_by_idx
42+
43+
end module aerosol_mmr_ccpp
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
!-----------------------------------------------------------------------
2+
! Mock cam_abortutils for unit testing.
3+
! Provides endrun that prints a message and calls error stop.
4+
!-----------------------------------------------------------------------
5+
module cam_abortutils
6+
7+
implicit none
8+
private
9+
10+
public :: endrun
11+
12+
contains
13+
14+
subroutine endrun(msg, file, line)
15+
character(len=*), intent(in) :: msg
16+
character(len=*), intent(in), optional :: file
17+
integer, intent(in), optional :: line
18+
if (present(file) .and. present(line)) then
19+
write(*,*) 'MOCK endrun: ', trim(msg), ' at ', trim(file), ':', line
20+
else
21+
write(*,*) 'MOCK endrun: ', trim(msg)
22+
end if
23+
error stop 1
24+
end subroutine endrun
25+
26+
end module cam_abortutils
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
!-----------------------------------------------------------------------
2+
! Mock cam_logfile for unit testing.
3+
! Provides iulog pointing to stdout (unit 6).
4+
!-----------------------------------------------------------------------
5+
module cam_logfile
6+
7+
implicit none
8+
9+
integer, public :: iulog = 6
10+
11+
end module cam_logfile
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
!-----------------------------------------------------------------------
2+
! Mock physconst for unit testing.
3+
! Provides physical constants used by aerosol modules.
4+
!-----------------------------------------------------------------------
5+
module physconst
6+
7+
use shr_kind_mod, only: r8 => shr_kind_r8
8+
9+
implicit none
10+
11+
real(r8), public, parameter :: pi = 3.14159265358979323846_r8
12+
real(r8), public, parameter :: rga = 1._r8 / 9.80616_r8
13+
14+
end module physconst

0 commit comments

Comments
 (0)