Skip to content

Commit 77fa975

Browse files
authored
Check intent(out) and intent(inout) variables during physics_check_data (#413)
Tag name (required for release branches): Originator(s): peverwhee Description (include the issue title, and the keyword ['closes', 'fixes', 'resolves'] followed by the issue number): Updates so that: 1. Variable set in `physics_read_data` is only intent(in) and intent(inout) variables (no changes, this is the prior behavior) 2. Variable set in `physics_check_data` is only intent(inout) and intent(out) variables (new behavior) - Also changed the logic in `physics_check_data` to not rely on is_read_from_file (because that's set during read_data which is not necessarily done for these variables!) and to not error when a variable cannot be read from file. closes #411 Describe any changes made to build system: n/a Describe any changes made to the namelist: n/a List any changes to the defaults for the input datasets (e.g. boundary datasets): n/a List all files eliminated and why: n/a List all files added and what they do: n/a List all existing files that have been modified, and describe the changes: (Helpful git command: `git diff --name-status development...<your_branch_name>`) M src/data/write_init_files.py - mods to grab two lists of variables - intent(in)/intent(inout) and intent(inout)/intent(out) - don't error if variable has weird dimensions/cannot be read from file - still check all constituents M test/unit/python/sample_files/write_init_files/simple_reg.xml M test/unit/python/sample_files/write_init_files/temp_adjust.F90 - add new output-only variable to make sure the code is generated to check it M test/unit/python/sample_files/write_init_files/phys_vars_init_check_*.F90 M test/unit/python/sample_files/write_init_files/physics_inputs_*.F90 - update expected files to account for new check_data logic in generated code If there are new failures (compared to the `test/existing-test-failures.txt` file), have them OK'd by the gatekeeper, note them here, and add them to the file. If there are baseline differences, include the test and the reason for the diff. What is the nature of the change? Roundoff? derecho/intel/aux_sima: all PASS derecho/gnu/aux_sima: all PASS If this changes climate describe any run(s) done to evaluate the new climate in enough detail that it(they) could be reproduced: CAM-SIMA date used for the baseline comparison tests if different than latest:
1 parent 8a0689e commit 77fa975

22 files changed

+555
-454
lines changed

src/data/write_init_files.py

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
'number_of_mpi_tasks'}
3434
# Variable input types
3535
_INPUT_TYPES = set(['in', 'inout'])
36+
_OUTPUT_TYPES = set(['out', 'inout'])
3637

3738
# Include files to insert in the module preamble
3839
_PHYS_VARS_PREAMBLE_INCS = ["cam_var_init_marks_decl.inc"]
@@ -131,7 +132,7 @@ def write_init_files(cap_database, ic_names, registry_constituents, vars_init_va
131132

132133
# Gather all the host model variables that are required by
133134
# any of the compiled CCPP physics suites.
134-
host_vars, constituent_set, retmsg = gather_ccpp_req_vars(cap_database, registry_constituents)
135+
in_vars, out_vars, constituent_set, retmsg = gather_ccpp_req_vars(cap_database, registry_constituents)
135136

136137
# Quit now if there are missing variables
137138
if retmsg:
@@ -173,13 +174,17 @@ def write_init_files(cap_database, ic_names, registry_constituents, vars_init_va
173174
outfile.include(filepath)
174175
# end for
175176

177+
all_req_vars = in_vars + out_vars
178+
# Remove duplicates but preserve order for testing
179+
unique_req_vars = list(OrderedDict.fromkeys(all_req_vars))
180+
176181
# Write public parameters:
177-
retvals = write_ic_params(outfile, host_vars, ic_names, registry_constituents)
182+
retvals = write_ic_params(outfile, unique_req_vars, ic_names, registry_constituents)
178183
ic_names, ic_max_len, stdname_max_len = retvals
179184

180185
# Write initial condition arrays:
181186
write_ic_arrays(outfile, ic_names, ic_max_len,
182-
stdname_max_len, host_vars, registry_constituents)
187+
stdname_max_len, unique_req_vars, registry_constituents)
183188

184189
# Add "contains" statement:
185190
outfile.end_module_header()
@@ -234,18 +239,19 @@ def write_init_files(cap_database, ic_names, registry_constituents, vars_init_va
234239
# Grab the host dictionary from the database
235240
host_dict = cap_database.host_model_dict()
236241

237-
# Collect imported host variables
238-
host_imports = collect_host_var_imports(host_vars, host_dict, constituent_set)
239-
242+
# Collect imported host variables for physics read
243+
host_imports = collect_host_var_imports(in_vars, host_dict, constituent_set)
240244
# Write physics_read_data subroutine:
241-
write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports,
245+
write_phys_read_subroutine(outfile, host_dict, in_vars, host_imports,
242246
phys_check_fname_str, constituent_set,
243247
vars_init_value)
244248

245249
outfile.blank_line()
246250

251+
# Collect imported host variables for physics check
252+
host_imports = collect_host_var_imports(out_vars, host_dict, constituent_set)
247253
# Write physics_check_data subroutine:
248-
write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports,
254+
write_phys_check_subroutine(outfile, host_dict, out_vars, host_imports,
249255
phys_check_fname_str, constituent_set)
250256

251257
# --------------------------------------
@@ -315,7 +321,8 @@ def gather_ccpp_req_vars(cap_database, registry_constituents):
315321

316322
# Dictionary of all 'in' and 'inout' suite variables.
317323
# Key is standard name, value is host-model or constituent variable
318-
req_vars = {}
324+
in_vars = {}
325+
out_vars = {}
319326
missing_vars = set()
320327
constituent_vars = set()
321328
retmsg = ""
@@ -330,22 +337,31 @@ def gather_ccpp_req_vars(cap_database, registry_constituents):
330337
intent = cvar.get_prop_value('intent')
331338
is_const = cvar.get_prop_value('advected') or cvar.get_prop_value('constituent')
332339
if ((intent in _INPUT_TYPES) and
333-
(stdname not in req_vars) and
340+
(stdname not in in_vars) and
334341
(stdname not in _EXCLUDED_STDNAMES)):
335342
if is_const:
336343
#Add variable to constituent set:
337344
constituent_vars.add(stdname)
338345
#Add variable to required variable list if it's not a registry constituent
339346
if stdname not in registry_constituents:
340-
req_vars[stdname] = cvar
347+
in_vars[stdname] = cvar
341348
# end if
342349
else:
343350
# We need to work with the host model version of this variable
344351
missing = _find_and_add_host_variable(stdname, host_dict,
345-
req_vars)
352+
in_vars)
346353
missing_vars.update(missing)
347354
# end if
348-
# end if (do not include output variables)
355+
# end if (only input variables)
356+
if ((intent in _OUTPUT_TYPES) and
357+
(stdname not in out_vars) and
358+
(stdname not in _EXCLUDED_STDNAMES)):
359+
if not is_const:
360+
missing = _find_and_add_host_variable(stdname, host_dict,
361+
out_vars)
362+
# do nothing with missing variables
363+
# end if
364+
# end if (only output variables)
349365
# end for (loop over call list)
350366
# end for (loop over phases)
351367

@@ -354,7 +370,7 @@ def gather_ccpp_req_vars(cap_database, registry_constituents):
354370
retmsg = f"Error: Missing required host variables: {mvlist}"
355371
# end if
356372
# Return the required variables as a list
357-
return list(req_vars.values()), constituent_vars, retmsg
373+
return list(in_vars.values()), list(out_vars.values()), constituent_vars, retmsg
358374

359375
##########################
360376
#FORTRAN WRITING FUNCTIONS
@@ -1178,8 +1194,6 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports,
11781194
outfile.write("end do", 2)
11791195
outfile.blank_line()
11801196

1181-
# start default case steps:
1182-
11831197
# End subroutine:
11841198
outfile.write("end subroutine physics_read_data", 1)
11851199

@@ -1240,8 +1254,8 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports,
12401254
call_str += f"timestep, {var_locname}, '{var_stdname}', "
12411255
call_str += "min_difference, min_relative_value, is_first, diff_found)"
12421256
else:
1243-
call_str = f"call endrun('Cannot check status of {var_locname}'" + \
1244-
f"//', {reason}')"
1257+
# For check field, don't endrun
1258+
call_str = f"! do nothing - '{var_locname}' can't be checked against a file because {reason}"
12451259
# end if
12461260
# Add string to dictionary:
12471261
call_string_dict[call_string_key] = call_str
@@ -1376,7 +1390,7 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports,
13761390
"that they can be read from input file if need be:", 3)
13771391
outfile.write("call ccpp_physics_suite_variables(suite_names" + \
13781392
"(suite_idx), ccpp_required_data, errmsg, errflg, " + \
1379-
"input_vars=.true., output_vars=.false.)", 4)
1393+
"input_vars=.false., output_vars=.true.)", 4)
13801394
outfile.blank_line()
13811395

13821396
# Loop over required variables:
@@ -1385,37 +1399,48 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports,
13851399
outfile.write("do req_idx = 1, size(ccpp_required_data, 1)", 3)
13861400
outfile.blank_line()
13871401

1388-
# First check if the required variable is a constituent
1389-
outfile.comment("First check if the required variable is a constituent:", 4)
1390-
outfile.write("call const_get_index(ccpp_required_data(req_idx), constituent_idx, abort=.false., warning=.false.)", 4)
1391-
outfile.write("if (constituent_idx > -1) then", 4)
1392-
outfile.write("cycle", 5)
1393-
outfile.write("else", 4)
1394-
outfile.comment("The required variable is not a constituent. Check if the variable was read from a file", 5)
1395-
13961402
# Call input name search function:
1397-
outfile.comment("Find IC file input name array index for required variable:", 5)
1398-
outfile.write("call is_read_from_file(ccpp_required_data(req_idx), " + \
1399-
"is_read, stdnam_idx_out=name_idx)", 5)
1400-
outfile.write("if (.not. is_read) then", 5)
1401-
outfile.write("cycle", 6)
1402-
outfile.write("end if", 5)
1403+
outfile.comment("Find IC file input name array index for required variable:", 4)
1404+
outfile.write("name_idx = find_input_name_idx(ccpp_required_data(req_idx), .true., constituent_idx)", 4)
1405+
1406+
# Start select-case statement:
1407+
outfile.blank_line()
1408+
outfile.comment("Check for special index values:", 4)
1409+
outfile.write("select case (name_idx)", 4)
1410+
outfile.blank_line()
1411+
1412+
# Skip constituent variables:
1413+
outfile.write("case (const_idx)", 5)
1414+
outfile.blank_line()
1415+
outfile.comment("If variable is a constituent, then do nothing. We'll handle these later", 6)
1416+
outfile.blank_line()
1417+
1418+
# Generate error message if required variable isn't found:
1419+
outfile.write("case (no_exist_idx)", 5)
1420+
outfile.blank_line()
1421+
outfile.comment("If the index for an output variable was not found, then do nothing. We won't try to check these.", 6)
1422+
outfile.blank_line()
1423+
1424+
# start default case steps:
1425+
outfile.write("case default", 5)
1426+
outfile.blank_line()
14031427

14041428
# Generate "check_field" calls:
1405-
outfile.comment("Check variable vs input check file:", 5)
1429+
outfile.comment("Check variable vs input check file:", 6)
14061430
outfile.blank_line()
1407-
outfile.write("select case (trim(phys_var_stdnames(name_idx)))", 5)
1431+
outfile.write("select case (trim(phys_var_stdnames(name_idx)))", 6)
14081432
for case_call, read_call in call_string_dict.items():
1409-
outfile.write(case_call, 5)
1410-
outfile.write(read_call, 6)
1433+
outfile.write(case_call, 6)
1434+
outfile.write(read_call, 7)
14111435
outfile.blank_line()
1412-
outfile.write("end select !check variables", 5)
1413-
outfile.write("if (diff_found) then", 5)
1414-
outfile.write("overall_diff_found = .true.", 6)
1415-
outfile.write("end if", 5)
1416-
outfile.write("end if !check if constituent", 4)
1436+
outfile.write("end select !check variables", 6)
1437+
outfile.write("if (diff_found) then", 6)
1438+
outfile.write("overall_diff_found = .true.", 7)
1439+
outfile.write("end if", 6)
14171440

14181441
# End select case and required variables loop:
1442+
outfile.write("end select !special indices", 4)
1443+
outfile.blank_line()
14191444
outfile.write("end do !Suite-required variables", 3)
14201445
outfile.blank_line()
14211446

test/unit/python/sample_files/write_init_files/phys_vars_init_check_simple.F90

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,21 @@ module phys_vars_init_check_simple
3333
integer, public, parameter :: PARAM = 2
3434
integer, public, parameter :: READ_FROM_FILE = 3
3535
! Total number of physics-related variables:
36-
integer, public, parameter :: phys_var_num = 2
36+
integer, public, parameter :: phys_var_num = 4
3737
integer, public, parameter :: phys_const_num = 16
3838

3939
!Max length of physics-related variable standard names:
4040
integer, public, parameter :: std_name_len = 25
4141

4242
! Max length of input (IC) file variable names:
43-
integer, public, parameter :: ic_name_len = 5
43+
integer, public, parameter :: ic_name_len = 9
4444

4545
! Physics-related input variable standard names:
4646
character(len=25), public, protected :: phys_var_stdnames(phys_var_num) = (/ &
4747
'potential_temperature ', &
48-
'air_pressure_at_sea_level' /)
48+
'air_pressure_at_sea_level', &
49+
'tendency_of_peverwhee ', &
50+
'scalar_variable_llama ' /)
4951

5052
character(len=36), public, protected :: phys_const_stdnames(phys_const_num) = (/ &
5153
"ccpp_constituent_minimum_values ", &
@@ -65,17 +67,23 @@ module phys_vars_init_check_simple
6567
"suite_name ", &
6668
"suite_part " /)
6769
!Array storing all registered IC file input names for each variable:
68-
character(len=5), public, protected :: input_var_names(1, phys_var_num) = reshape((/ &
69-
'theta', &
70-
'slp ' /), (/1, phys_var_num/))
70+
character(len=9), public, protected :: input_var_names(1, phys_var_num) = reshape((/ &
71+
'theta ', &
72+
'slp ', &
73+
'ptend ', &
74+
'var_nodim' /), (/1, phys_var_num/))
7175

7276
! Array indicating whether or not variable is protected:
7377
logical, public, protected :: protected_vars(phys_var_num)= (/ &
78+
.false., &
79+
.false., &
7480
.false., &
7581
.false. /)
7682

7783
! Variable state (UNINITIALIZED, INTIIALIZED, PARAM or READ_FROM_FILE):
7884
integer, public, protected :: initialized_vars(phys_var_num)= (/ &
85+
UNINITIALIZED, &
86+
UNINITIALIZED, &
7987
UNINITIALIZED, &
8088
UNINITIALIZED /)
8189

test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference,
235235
use cam_pio_utils, only: cam_pio_openfile, cam_pio_closefile
236236
use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t
237237
use phys_vars_init_check_4D, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len
238-
use physics_types_4D, only: slp, theta
238+
use physics_types_4D, only: theta
239239

240240
! Dummy arguments
241241
character(len=SHR_KIND_CL), intent(in) :: file_name
@@ -304,37 +304,40 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference,
304304
do suite_idx = 1, size(suite_names, 1)
305305

306306
! Search for all needed CCPP input variables, so that they can be read from input file if need be:
307-
call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.)
307+
call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.false., output_vars=.true.)
308308

309309
! Loop over all required variables as specified by CCPP suite:
310310
do req_idx = 1, size(ccpp_required_data, 1)
311311

312-
! First check if the required variable is a constituent:
313-
call const_get_index(ccpp_required_data(req_idx), constituent_idx, abort=.false., warning=.false.)
314-
if (constituent_idx > -1) then
315-
cycle
316-
else
317-
! The required variable is not a constituent. Check if the variable was read from a file
318-
! Find IC file input name array index for required variable:
319-
call is_read_from_file(ccpp_required_data(req_idx), is_read, stdnam_idx_out=name_idx)
320-
if (.not. is_read) then
321-
cycle
322-
end if
323-
! Check variable vs input check file:
312+
! Find IC file input name array index for required variable:
313+
name_idx = find_input_name_idx(ccpp_required_data(req_idx), .true., constituent_idx)
324314

325-
select case (trim(phys_var_stdnames(name_idx)))
326-
case ('potential_temperature')
327-
call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, &
328-
min_relative_value, is_first, diff_found)
315+
! Check for special index values:
316+
select case (name_idx)
329317

330-
case ('air_pressure_at_sea_level')
331-
call endrun('Cannot check status of slp'//', slp has unsupported dimension, timestep_for_physics.')
318+
case (const_idx)
319+
320+
! If variable is a constituent, then do nothing. We'll handle these later
321+
322+
case (no_exist_idx)
323+
324+
! If the index for an output variable was not found, then do nothing. We won't try to check these.
325+
326+
case default
327+
328+
! Check variable vs input check file:
329+
330+
select case (trim(phys_var_stdnames(name_idx)))
331+
case ('potential_temperature')
332+
call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, &
333+
min_relative_value, is_first, diff_found)
334+
335+
end select !check variables
336+
if (diff_found) then
337+
overall_diff_found = .true.
338+
end if
339+
end select !special indices
332340

333-
end select !check variables
334-
if (diff_found) then
335-
overall_diff_found = .true.
336-
end if
337-
end if !check if constituent
338341
end do !Suite-required variables
339342

340343
! Deallocate required variables array for use in next suite:

0 commit comments

Comments
 (0)