Skip to content

Commit f210ab4

Browse files
authored
Merge pull request #82 from billsacks/water_tracers_part2
Add more infrastructure for water tracers, needed by CMEPS. The biggest addition here is a routine to check tracer ratios against expectations, with associated unit tests. There are also a few other small additions to shr_wtracers_mod as well as some other additions needed for the unit test build. Testing done: In the context of cesm3_0_alpha08o, with this branch along with ESCOMP/CMEPS#638, ran CESM prealpha tests on derecho and izumi, and aux_glc tests on derecho, all with baseline comparisons. All failures also failed in the baseline except for `SMS_D_Ln9_P1536x1.ne0CONUSne30x8_ne0CONUSne30x8_mt12.FHIST.derecho_intel.cam-outfrq9s` (which seemed to fail due to ESCOMP/CTSM#3772, which is a sporadic issue), and all baseline comparisons passed. (Note: full testing was done before final commits addressing review points; only minimal additional testing was done after those final commits.)
2 parents 9aa0b44 + c322156 commit f210ab4

10 files changed

Lines changed: 644 additions & 11 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ endif()
9494

9595
# Among other things, this handles the genf90 generation
9696
add_subdirectory(src)
97+
add_subdirectory(src/water_isotopes)
9798

9899
file(GLOB FSOURCES "src/*.F90" "src/water_isotopes/*.F90" "RandNum/src/*.F90" "RandNum/src/*/*.F90")
99100
file(GLOB CSOURCES "src/*.c" "RandNum/src/*/*.c")
@@ -110,6 +111,7 @@ target_include_directories(csm_share PRIVATE ${CMAKE_BINARY_DIR})
110111
if(UNITTESTS)
111112
# need to turn the warning check off for pfunit
112113
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wno-error ${CMAKE_Fortran_COMPILER_DIRECTIVE} -I${CMAKE_BINARY_DIR}/unittests/shr_assert_test/mod/assert/ ")
113-
add_subdirectory(${CMAKE_SOURCE_DIR}/unit_test_stubs/util csm_share_stubs)
114+
add_subdirectory(${CMAKE_SOURCE_DIR}/unit_test_stubs/util csm_share_stubs_util)
115+
add_subdirectory(${CMAKE_SOURCE_DIR}/unit_test_stubs/gptl csm_share_stubs_gptl)
114116
add_subdirectory(${CMAKE_SOURCE_DIR}/test/unit ${CMAKE_BINARY_DIR}/unittests)
115117
endif()

src/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ list(APPEND share_sources
2626
shr_mpi_mod.F90
2727
shr_pio_mod.F90
2828
shr_wv_sat_mod.F90
29-
m_MergeSorts.F90)
29+
m_MergeSorts.F90
30+
nuopc_shr_methods.F90)
3031

3132
sourcelist_to_parent(share_sources)
3233

src/water_isotopes/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
list(APPEND share_sources
2+
shr_wtracers_mod.F90)
3+
4+
sourcelist_to_parent(share_sources)

src/water_isotopes/shr_wtracers_mod.F90

Lines changed: 290 additions & 8 deletions
Large diffs are not rendered by default.

test/unit/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ add_subdirectory(dynamic_vector)
1818

1919
add_subdirectory(shr_vmath_test)
2020

21+
add_subdirectory(shr_wtracers_test)
22+
2123
add_subdirectory(shr_wv_sat_test)
2224

2325
add_subdirectory(shr_precip_test)
2426

25-
add_subdirectory(shr_cal_test)
27+
add_subdirectory(shr_cal_test)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
set (pf_sources
2+
test_shr_wtracers.pf
3+
)
4+
5+
set(sources_needed
6+
shr_wtracers_mod.F90
7+
shr_assert_mod.F90
8+
shr_infnan_mod.F90
9+
shr_kind_mod.F90
10+
shr_log_mod.F90
11+
shr_strconvert_mod.F90
12+
shr_string_mod.F90
13+
shr_sys_mod.nompi_abortthrows.F90
14+
shr_abort_mod.abortthrows.F90
15+
shr_timer_mod.F90
16+
nuopc_shr_methods.F90
17+
gptl.F90)
18+
19+
extract_sources("${sources_needed}" "${share_sources}" test_sources)
20+
21+
add_pfunit_ctest(shr_wtracers
22+
TEST_SOURCES "${pf_sources}"
23+
OTHER_SOURCES "${test_sources}")
24+
25+
declare_generated_dependencies(shr_wtracers "${share_genf90_sources}")
26+
27+
target_link_libraries(shr_wtracers esmf)
28+
# The following adds all dependencies of ESMF, including PIO, NetCDF, etc.:
29+
target_link_libraries(shr_wtracers ESMF::ESMF)
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
module test_shr_wtracers
2+
3+
! Tests of shr_wtracers_mod
4+
5+
use funit
6+
7+
use shr_wtracers_mod, only : shr_wtracers_initialized, shr_wtracers_finalize
8+
use shr_wtracers_mod, only : shr_wtracers_init_directly_for_testing
9+
use shr_wtracers_mod, only : shr_wtracers_check_tracer_ratios
10+
use shr_wtracers_mod, only : WATER_SPECIES_NAME_BULK
11+
12+
use shr_kind_mod, only : r8=>SHR_KIND_R8
13+
use shr_infnan_mod, only : assignment(=), nan => shr_infnan_nan
14+
use ESMF, only : ESMF_SUCCESS
15+
16+
implicit none
17+
18+
@TestCase
19+
type, extends(TestCase) :: TestShrWtracers
20+
contains
21+
procedure :: setUp
22+
procedure :: tearDown
23+
end type TestShrWtracers
24+
25+
contains
26+
27+
subroutine setUp(this)
28+
class(TestShrWtracers), intent(inout) :: this
29+
end subroutine setUp
30+
31+
subroutine tearDown(this)
32+
class(TestShrWtracers), intent(inout) :: this
33+
34+
integer :: rc
35+
36+
if (shr_wtracers_initialized()) then
37+
call shr_wtracers_finalize(rc)
38+
end if
39+
end subroutine tearDown
40+
41+
! ------------------------------------------------------------------------
42+
! Tests of shr_wtracers_check_tracer_ratios
43+
! ------------------------------------------------------------------------
44+
45+
@Test
46+
subroutine test_shr_wtracers_check_tracer_ratios_both0(this)
47+
! The tracer ratio test should pass when both tracer and bulk are 0
48+
class(TestShrWtracers), intent(inout) :: this
49+
integer :: rc
50+
real(r8) :: bulk(4)
51+
real(r8) :: tracers(3, 4)
52+
53+
call shr_wtracers_init_directly_for_testing( &
54+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
55+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
56+
water_tracer_initial_ratios = [2._r8, 2._r8, 2._r8], &
57+
rc = rc)
58+
@assertEqual(ESMF_SUCCESS, rc)
59+
60+
bulk(:) = 0._r8
61+
tracers(:,:) = 0._r8
62+
63+
! The test passes if the following call runs successfully without aborting
64+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
65+
end subroutine test_shr_wtracers_check_tracer_ratios_both0
66+
67+
@Test
68+
subroutine test_shr_wtracers_check_tracer_ratios_correct(this)
69+
! The tracer ratio test should pass when the tracer ratios are correct
70+
class(TestShrWtracers), intent(inout) :: this
71+
integer :: rc
72+
real(r8) :: bulk(4)
73+
real(r8) :: tracers(3, 4)
74+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
75+
integer :: i
76+
77+
call shr_wtracers_init_directly_for_testing( &
78+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
79+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
80+
water_tracer_initial_ratios = ratios, &
81+
rc = rc)
82+
@assertEqual(ESMF_SUCCESS, rc)
83+
84+
bulk(:) = [1._r8, 2._r8, 3._r8, 4._r8]
85+
do i = 1, 3
86+
tracers(i,:) = ratios(i) * bulk(:)
87+
end do
88+
89+
! The test passes if the following call runs successfully without aborting
90+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
91+
end subroutine test_shr_wtracers_check_tracer_ratios_correct
92+
93+
@Test
94+
subroutine test_shr_wtracers_check_tracer_ratios_differ(this)
95+
! The tracer ratio test should fail when some tracer ratio differs from expected
96+
class(TestShrWtracers), intent(inout) :: this
97+
integer :: rc
98+
real(r8) :: bulk(4)
99+
real(r8) :: tracers(3, 4)
100+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
101+
integer :: i
102+
103+
call shr_wtracers_init_directly_for_testing( &
104+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
105+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
106+
water_tracer_initial_ratios = ratios, &
107+
rc = rc)
108+
@assertEqual(ESMF_SUCCESS, rc)
109+
110+
bulk(:) = [1._r8, 2._r8, 3._r8, 4._r8]
111+
do i = 1, 3
112+
tracers(i,:) = ratios(i) * bulk(:)
113+
end do
114+
tracers(2,3) = tracers(2,3) * 1.1_r8
115+
116+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
117+
@assertExceptionRaised("ABORTED: shr_wtracers_check_tracer_ratios ERROR: tracer does not agree with bulk water for variable 'test', tracer 'tracer2', at index 3")
118+
end subroutine test_shr_wtracers_check_tracer_ratios_differ
119+
120+
@Test
121+
subroutine test_shr_wtracers_check_tracer_ratios_tracer0(this)
122+
! The tracer ratio test should fail when some tracer value is zero despite non-zero bulk
123+
class(TestShrWtracers), intent(inout) :: this
124+
integer :: rc
125+
real(r8) :: bulk(4)
126+
real(r8) :: tracers(3, 4)
127+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
128+
integer :: i
129+
130+
call shr_wtracers_init_directly_for_testing( &
131+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
132+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
133+
water_tracer_initial_ratios = ratios, &
134+
rc = rc)
135+
@assertEqual(ESMF_SUCCESS, rc)
136+
137+
bulk(:) = [1._r8, 2._r8, 3._r8, 4._r8]
138+
do i = 1, 3
139+
tracers(i,:) = ratios(i) * bulk(:)
140+
end do
141+
tracers(2,3) = 0._r8
142+
143+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
144+
@assertExceptionRaised("ABORTED: shr_wtracers_check_tracer_ratios ERROR: tracer does not agree with bulk water for variable 'test', tracer 'tracer2', at index 3")
145+
end subroutine test_shr_wtracers_check_tracer_ratios_tracer0
146+
147+
@Test
148+
subroutine test_shr_wtracers_check_tracer_ratios_bulk0(this)
149+
! The tracer ratio test should fail when some bulk value is zero despite non-zero tracer
150+
class(TestShrWtracers), intent(inout) :: this
151+
integer :: rc
152+
real(r8) :: bulk(4)
153+
real(r8) :: tracers(3, 4)
154+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
155+
integer :: i
156+
157+
call shr_wtracers_init_directly_for_testing( &
158+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
159+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
160+
water_tracer_initial_ratios = ratios, &
161+
rc = rc)
162+
@assertEqual(ESMF_SUCCESS, rc)
163+
164+
bulk(:) = [1._r8, 2._r8, 3._r8, 4._r8]
165+
do i = 1, 3
166+
tracers(i,:) = ratios(i) * bulk(:)
167+
end do
168+
bulk(3) = 0._r8
169+
170+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
171+
@assertExceptionRaised("ABORTED: shr_wtracers_check_tracer_ratios ERROR: tracer does not agree with bulk water for variable 'test', tracer 'tracer1', at index 3")
172+
end subroutine test_shr_wtracers_check_tracer_ratios_bulk0
173+
174+
@Test
175+
subroutine test_shr_wtracers_check_tracer_ratios_bothnan(this)
176+
! The tracer ratio test should pass when both tracer and bulk are NaN
177+
class(TestShrWtracers), intent(inout) :: this
178+
integer :: rc
179+
real(r8) :: bulk(4)
180+
real(r8) :: tracers(3, 4)
181+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
182+
integer :: i
183+
184+
call shr_wtracers_init_directly_for_testing( &
185+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
186+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
187+
water_tracer_initial_ratios = ratios, &
188+
rc = rc)
189+
@assertEqual(ESMF_SUCCESS, rc)
190+
191+
bulk(:) = [1._r8, 2._r8, 3._r8, 4._r8]
192+
do i = 1, 3
193+
tracers(i,:) = ratios(i) * bulk(:)
194+
end do
195+
bulk(3) = nan
196+
tracers(:,3) = nan
197+
198+
! The test passes if the following call runs successfully without aborting
199+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
200+
end subroutine test_shr_wtracers_check_tracer_ratios_bothnan
201+
202+
@Test
203+
subroutine test_shr_wtracers_check_tracer_ratios_tracernan(this)
204+
! The tracer ratio test should fail when some tracer value is NaN despite non-NaN bulk
205+
class(TestShrWtracers), intent(inout) :: this
206+
integer :: rc
207+
real(r8) :: bulk(4)
208+
real(r8) :: tracers(3, 4)
209+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
210+
integer :: i
211+
212+
call shr_wtracers_init_directly_for_testing( &
213+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
214+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
215+
water_tracer_initial_ratios = ratios, &
216+
rc = rc)
217+
@assertEqual(ESMF_SUCCESS, rc)
218+
219+
bulk(:) = [1._r8, 2._r8, 3._r8, 4._r8]
220+
do i = 1, 3
221+
tracers(i,:) = ratios(i) * bulk(:)
222+
end do
223+
tracers(2,3) = nan
224+
225+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
226+
@assertExceptionRaised("ABORTED: shr_wtracers_check_tracer_ratios ERROR: tracer does not agree with bulk water for variable 'test', tracer 'tracer2', at index 3")
227+
end subroutine test_shr_wtracers_check_tracer_ratios_tracernan
228+
229+
! ------------------------------------------------------------------------
230+
! Tests of shr_wtracers_check_tracer_ratios with 2-d bulk arrays
231+
! ------------------------------------------------------------------------
232+
233+
@Test
234+
subroutine test_shr_wtracers_check_tracer_ratios_2d_correct(this)
235+
! The tracer ratio test should pass when the tracer ratios are correct (2-d bulk)
236+
class(TestShrWtracers), intent(inout) :: this
237+
integer :: rc
238+
real(r8) :: bulk(2, 4)
239+
real(r8) :: tracers(2, 3, 4)
240+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
241+
integer :: i, j
242+
243+
call shr_wtracers_init_directly_for_testing( &
244+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
245+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
246+
water_tracer_initial_ratios = ratios, &
247+
rc = rc)
248+
@assertEqual(ESMF_SUCCESS, rc)
249+
250+
bulk(1,:) = [1._r8, 2._r8, 3._r8, 4._r8]
251+
bulk(2,:) = [5._r8, 6._r8, 7._r8, 8._r8]
252+
do i = 1, 3
253+
do j = 1, 2
254+
tracers(j,i,:) = ratios(i) * bulk(j,:)
255+
end do
256+
end do
257+
258+
! The test passes if the following call runs successfully without aborting
259+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
260+
end subroutine test_shr_wtracers_check_tracer_ratios_2d_correct
261+
262+
@Test
263+
subroutine test_shr_wtracers_check_tracer_ratios_2d_differ(this)
264+
! The tracer ratio test should fail when some tracer ratio differs from expected (2-d bulk)
265+
class(TestShrWtracers), intent(inout) :: this
266+
integer :: rc
267+
real(r8) :: bulk(2, 4)
268+
real(r8) :: tracers(2, 3, 4)
269+
real(r8), parameter :: ratios(3) = [2._r8, 3._r8, 4._r8]
270+
integer :: i, j
271+
272+
call shr_wtracers_init_directly_for_testing( &
273+
water_tracer_names = ["tracer1", "tracer2", "tracer3"], &
274+
water_tracer_species = [WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK, WATER_SPECIES_NAME_BULK], &
275+
water_tracer_initial_ratios = ratios, &
276+
rc = rc)
277+
@assertEqual(ESMF_SUCCESS, rc)
278+
279+
bulk(1,:) = [1._r8, 2._r8, 3._r8, 4._r8]
280+
bulk(2,:) = [5._r8, 6._r8, 7._r8, 8._r8]
281+
do i = 1, 3
282+
do j = 1, 2
283+
tracers(j,i,:) = ratios(i) * bulk(j,:)
284+
end do
285+
end do
286+
tracers(2,2,3) = tracers(2,2,3) * 1.1_r8
287+
288+
call shr_wtracers_check_tracer_ratios(tracers, bulk, "test")
289+
@assertExceptionRaised("ABORTED: shr_wtracers_check_tracer_ratios ERROR: tracer does not agree with bulk water for variable 'test', tracer 'tracer2', at index 3")
290+
end subroutine test_shr_wtracers_check_tracer_ratios_2d_differ
291+
292+
end module test_shr_wtracers
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# In the real build, gptl would be a separate library. Here, for simplicity, we add the
2+
# stub version of gptl to the share library.
3+
list(APPEND share_sources
4+
gptl.F90)
5+
6+
sourcelist_to_parent(share_sources)

unit_test_stubs/gptl/README

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This directory contains stubs of the gptl timing library. In real builds, we typically
2+
link against a pre-built gptl. Here, for simplicity, we facilitate including the necessary
3+
stubs in the share library so we can avoid linking against an actual gptl.

unit_test_stubs/gptl/gptl.F90

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
! Stubs of the gptl timing library
2+
3+
! Note that in the real gptl, these are defined in C. Here we define them in Fortran for
4+
! simplicity, but keep them outside of a module for consistency with their usage (via an
5+
! "external" statement rather than a "use" statement).
6+
7+
function GPTLprint_memusage(msg) result(ierr)
8+
character(len=*), intent(in) :: msg
9+
integer :: ierr
10+
11+
ierr = 0
12+
end function GPTLprint_memusage

0 commit comments

Comments
 (0)