diff --git a/datm/cime_config/buildnml b/datm/cime_config/buildnml index e81cdb55e..51716e8db 100755 --- a/datm/cime_config/buildnml +++ b/datm/cime_config/buildnml @@ -89,6 +89,164 @@ def _get_neon_data_availability(case, neonsite): expect(newestdate, "No tower data found on server for NEON site {}".format(neonsite)) return None +#################################################################################### +def _handle_pts_mode( case, config ): +#################################################################################### + """ Handle PTS mode settings for single column runs. If PTS_LON and PTS_LAT are set, then set config[single_column] to true. + """ + missing = -999.99 + if case.get_value('PTS_LON'): + scol_lon = float(case.get_value('PTS_LON')) + else: + scol_lon = missing + if case.get_value('PTS_LAT'): + scol_lat = float(case.get_value('PTS_LAT')) + else: + scol_lat = missing + if case.get_value('PTS_DOMAINFILE'): + if scol_lon > missing and scol_lat > missing and case.get_value("PTS_DOMAINFILE") != "UNSET": + config['single_column'] = 'true' + else: + config['single_column'] = 'false' + + # Check that if either PTS_LON/PTS_LAT is set the other is as well + # TODO: NOTE: This should really be in CMEPS + if (scol_lon != missing and scol_lat == missing) or (scol_lon == missing and scol_lat != missing): + expect(False, "If either PTS_LON or PTS_LAT is set, the other must be set as well") + + # Verify that the expected output setting was actually done + expect(config['single_column'] is not None, "single_column should have been set in this subroutine") + +#################################################################################### +def _handle_model_grid(case, config): +#################################################################################### + """ Set config['model grid'] always + Also do some checking and set needed variables for a CLM user defined grid. + For a CLM single-point/regional case (CLM user defined grid) + Set config['model_grid'] to CLM_USRDAT to signify this + For a NEON case + set config['neonsite'] + config['neon'] is True + For a PLUMBER2 case + set config['plumber2site'] + config['plumber2'] is True + + Will fail if settings aren't setup as expected + + Returns an empty list normally; for a NEON case it returns the list of available NEON data + """ + available_neon_data = None + model_grid = case.get_value("GRID") + comp_lnd = case.get_value("COMP_LND") + clm_usrdat_name = case.get_value("CLM_USRDAT_NAME") # NOTE: Will be None if not coupled to CLM + if 'CLM_USRDAT' in model_grid: + config['model_grid'] = 'CLM_USRDAT' + # If CLM_USRDAT is being used, CLM_USRDAT_NAME must be set + expect(clm_usrdat_name is not None, "CLM_USRDAT_NAME must be set when using CLM_USRDAT grid") + if 'NEON' in clm_usrdat_name: + neonsite = case.get_value("NEONSITE") + config['neonsite'] = neonsite + if neonsite: + config['neon'] = "True" + # download and use the listing.csv file to determine data availablity + available_neon_data = _get_neon_data_availability(case, neonsite) + if 'PLUMBER2' in clm_usrdat_name: + plumber2site = case.get_value('PLUMBER2SITE') + config['plumber2site'] = plumber2site + if plumber2site: + config['plumber'] = "True" + else: + config['model_grid'] = model_grid + if comp_lnd != "clm": + expect(clm_usrdat_name is None, "CLM_USRDAT_NAME is not expected to exist when the land model is NOT CLM") + expect(available_neon_data is None, "available_neon_data should NOT be set when the land model is NOT CLM") + expect(not 'neonsite' in config, "neonsite should have NOT been set when the land model is NOT CLM") + expect(not 'neon' in config, "neon should have NOT been set when the land model is NOT CLM") + expect(not 'plumber2site' in config, "plumber2site should have NOT been set when the land model is NOT CLM") + expect(not 'plumber' in config, "plumber should have NOT been set when the land model is NOT CLM") + else: + expect(clm_usrdat_name == "UNSET", "CLM_USRDAT_NAME must NOT be set when NOT using a CLM_USRDAT grid") + + # + # Ensure output settings are done as expected + # + expect(config['model_grid'] is not None, "model_grid should have been set in this subroutine") + + # + # Check for expected settings when coupled to CLM for NEON and PLUMBER2 cases + # + if comp_lnd == "clm": + if "NEON" in clm_usrdat_name: + expect(config['neon'] == "True", "neon should have been set in this subroutine, since NEON is on") + expect(config['neonsite'] is not None, "neonsite should have been set in this subroutine, since NEON is on") + expect(config['model_grid'] == "CLM_USRDAT", "model_grid should have been set to CLM_USRDAT because NEON is on") + else: + expect(available_neon_data is None, "available_neon_data should NOT be set if NEON is off") + expect(case.get_value('NEONSITE') == "", "NEONSITE should NOT have been set in this subroutine, since NEON is off") + expect(not 'neonsite' in config, "neonsite should have NOT been set in this subroutine, since NEON is off") + expect(not 'neon' in config, "neon should have NOT been set in this subroutine, since NEON is off") + + missing_yr_filename = 9999 + if "PLUMBER2" in clm_usrdat_name: + expect(config['plumber2site'] is not None, "plumber2site should have been set in this subroutine") + expect(config['model_grid'] == "CLM_USRDAT", "model_grid should have been set to CLM_USRDAT because PLUMBER2 is on") + expect(config['plumber'] == "True", "plumber should have been set in this subroutine, since PLUMBER2 is on") + datm_mode = case.get_value("DATM_MODE") + if ( datm_mode == "1PT" ): + expect(case.get_value('DATM_YR_START_FILENAME') != missing_yr_filename, "DATM_YR_START_FILENAME should have been set, since PLUMBER2 on") + expect(case.get_value('DATM_YR_END_FILENAME') != missing_yr_filename, "DATM_YR_END_FILENAME should have been set, since PLUMBER2 on") + else: + expect(case.get_value('DATM_YR_START_FILENAME') == missing_yr_filename, "DATM_YR_START_FILENAME should NOT have been set, since PLUMBER2 is off") + expect(case.get_value('DATM_YR_END_FILENAME') == missing_yr_filename, "DATM_YR_END_FILENAME should NOT have been set, since PLUMBER2 is off") + + else: + expect(case.get_value('PLUMBER2SITE') == "", "PLUMBER2SITE should NOT have been set in this subroutine, since PLUMBER2 is off") + expect(not 'plumber2site' in config, "plumber2site should have NOT been set in this subroutine, since PLUMBER2 is off") + expect(not 'plumber' in config, "plumber should have NOT been set in this subroutine, since PLUMBER2 is off") + + # Return the data available for NEON + return available_neon_data + +#################################################################################### +def _error_check_cplhist_mode(case, config): +#################################################################################### + """ Error checks for cplhist mode. + Make sure needed settings are set when used. + Make sure directories expected exist + Make sure it works for all the different cplhist options + """ + + check_cplhist = False + if "cplhist" in config['datm_mode']: + check_cplhist = True + if "cplhist" in case.get_value("DATM_CO2_TSERIES"): + check_cplhist = True + if "cplhist" in case.get_value("DATM_PRESAERO"): + check_cplhist = True + if "cplhist" in case.get_value("DATM_PRESNDEP"): + check_cplhist = True + if "cplhist" in case.get_value("DATM_PRESO3"): + check_cplhist = True + if "cplhist" in case.get_value("DATM_TOPO"): + check_cplhist = True + + if check_cplhist: + cplhist_dir = case.get_value("DATM_CPLHIST_DIR") + cplhist_case = case.get_value("DATM_CPLHIST_CASE") + cplhist_domain = case.get_value("DATM_CPLHIST_DOMAIN_FILE") + expect(cplhist_case is not None or cplhist_case != UNSET, "DATM_CPLHIST_CASE must be set when using cplhist mode") + expect(cplhist_dir is not None, "DATM_CPLHIST_DIR must be set when using cplhist mode") + # Check that the CPLHIST directory exists + # This is a little unusual in buildnml, as the check_input_data will make sure the files exist + # But, most often the problem is that I'm pointing to the wrong directory, so seeing that the directory + # doesn't exist is easier than showing all of the myriad of files that are missing later + unres_cplhist_dir = case.get_value("DATM_CPLHIST_DIR", resolved=False) + expect(os.path.isdir(cplhist_dir), "DATM_CPLHIST_DIR {} does not exist (directory with input variables is {})".format(cplhist_dir, unres_cplhist_dir)) + else: + cplhist_domain = case.get_value("DATM_CPLHIST_DOMAIN_FILE") + expect(cplhist_domain == "null", "DATM_CPLHIST_DOMAIN_FILE can only be set when using cplhist mode") + cplhist_case = case.get_value("DATM_CPLHIST_CASE") + expect(cplhist_case == "UNSET", "DATM_CPLHIST_CASE should only be set when using cplhist mode") # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements #################################################################################### @@ -115,7 +273,6 @@ def _create_namelists(case, confdir, inst_string, infile, nmlgen, data_list_path clm_usrdat_name = case.get_value("CLM_USRDAT_NAME") datm_co2_tseries = case.get_value("DATM_CO2_TSERIES") atm_grid = case.get_value("ATM_GRID") - model_grid = case.get_value("GRID") comp_lnd = case.get_value("COMP_LND") compset = case.get_value("COMPSET") @@ -142,21 +299,8 @@ def _create_namelists(case, confdir, inst_string, infile, nmlgen, data_list_path # Initialize namelist defaults config = {} - available_neon_data = None - if 'CLM_USRDAT' in model_grid: - config['model_grid'] = 'CLM_USRDAT' - if 'NEON' in clm_usrdat_name: - neonsite = case.get_value("NEONSITE") - if neonsite: - config['neon'] = "True" - # download and use the listing.csv file to determine data availablity - available_neon_data = _get_neon_data_availability(case, neonsite) - if 'PLUMBER2' in clm_usrdat_name: - plumber2site = case.get_value('PLUMBER2SITE') - if plumber2site: - config['plumber'] = "True" - else: - config['model_grid'] = model_grid + + available_neon_data = _handle_model_grid(case, config) config['datm_mode'] = datm_mode config['datm_co2_tseries'] = datm_co2_tseries @@ -165,19 +309,9 @@ def _create_namelists(case, confdir, inst_string, infile, nmlgen, data_list_path config['datm_preso3'] = datm_preso3 config['clm_usrdat_name'] = clm_usrdat_name - if case.get_value('PTS_LON'): - scol_lon = float(case.get_value('PTS_LON')) - else: - scol_lon = -999. - if case.get_value('PTS_LAT'): - scol_lat = float(case.get_value('PTS_LAT')) - else: - scol_lat = -999. - if case.get_value('PTS_DOMAINFILE'): - if scol_lon > -999. and scol_lat > -999. and case.get_value("PTS_DOMAINFILE") != "UNSET": - config['single_column'] = 'true' - else: - config['single_column'] = 'false' + _handle_pts_mode( case, config ) + + _error_check_cplhist_mode(case, config) nmlgen.init_defaults(infile, config) @@ -211,11 +345,11 @@ def _create_namelists(case, confdir, inst_string, infile, nmlgen, data_list_path if datm_co2_tseries != "none": streamlist.append("co2tseries.{}".format(datm_co2_tseries)) if clm_usrdat_name == 'NEON.PRISM': - streamlist.append(clm_usrdat_name+"_PRECIP."+neonsite) + streamlist.append(clm_usrdat_name+"_PRECIP."+config['neonsite']) if clm_usrdat_name == 'NEON': - streamlist.append(clm_usrdat_name+".NEON_PRECIP."+neonsite) + streamlist.append(clm_usrdat_name+".NEON_PRECIP."+config['neonsite']) if clm_usrdat_name == 'PLUMBER2': - streamlist.append(clm_usrdat_name+"."+plumber2site) + streamlist.append(clm_usrdat_name+"."+config['plumber2site']) bias_correct = nmlgen.get_value("bias_correct") if bias_correct is not None: diff --git a/datm/cime_config/config_component.xml b/datm/cime_config/config_component.xml index 2600d3163..41305fe2a 100644 --- a/datm/cime_config/config_component.xml +++ b/datm/cime_config/config_component.xml @@ -104,10 +104,13 @@ char - none,clim_1850_cmip7,clim_2000_cmip7,clim_2010_cmip7,hist_cmip7,clim_1850_cmip6,clim_2000_cmip6,clim_2010_cmip6,hist_cmip6,SSP1-2.6,SSP2-4.5,SSP3-7.0,SSP5-3.4,SSP5-8.5,cplhist + + + none,clim_1850_cesm3_for_cmip7,clim_1850_cmip7,clim_2000_cmip7,clim_2010_cmip7,hist_cmip7,clim_1850_cmip6,clim_2000_cmip6,clim_2010_cmip6,hist_cmip6,SSP1-2.6,SSP2-4.5,SSP3-7.0,SSP5-3.4,SSP5-8.5,cplhist clim_2000 clim_1850_cmip6 + clim_1850_cesm3_for_cmip7 clim_2000_cmip6 clim_2010_cmip6 hist_cmip6 @@ -178,11 +181,15 @@ cmip6_20tr omip.iaf omip.jra - cplhist + cplhist run_component_datm env_run.xml - DATM CO2 time series + DATM CO2 time series + NOTE: This needs to be coordinated with CCSM_BGC in CMEPS, and with either the ocean or land model + For CLM coordinate it with the CLM_CO2_TYPE variable + For MOM coordinate it with the OCN_CO2_TYPE variable + @@ -205,15 +212,30 @@ char UNSET + + $DIN_LOC_ROOT/cplhist/cesm3_0/$DATM_CPLHIST_CASE/atm + run_component_datm env_run.xml - directory for coupler history data mode (only used for when DATM_MODE is CPLHIST) + + Directory for coupler history data mode (used when DATM_MODE is CPLHIST) + May also be used if and of these are set to cplhist: DATM_PRESAERO, DATM_NDEP, DATM_CO2_TSERIES, DATM_PRESO3, or DATM_TOPO + NOTE: This needs to be the full directory path where the CPLHIST files are stored, including the subdirectory for the CASENAME (that can be set using $DATM_CPLHIST_CASE) + We thus recommend that $DATM_CPLHIST_CASE be used in here to specify that case subdirectory. + This allows the DATM_CPLHIST_DIR to be set for all your CPLHIST datafiles, and you only need to modify $DATM_CPLHIST_CASE to point to a different CASE. + For example, to use the CPLHIST data generated from a case that you ran do this: + ./xmlchange DATM_CPLHIST_DIR='$CIME_OUTPUT_ROOT/archive/$DATM_CPLHIST_CASE/cpl/hist' + Which will then use the CPL history files in your archive directory where the case that created the CPLHIST data you want to use is" + char UNSET + + b.e30_alpha08o.B1850C_MTso.ne30_t232_wgx3.330 + run_component_datm env_run.xml case name used to determine stream filenames when DATM_MODE is CPLHIST @@ -278,6 +300,7 @@ 1901 1901 0 + 101 2000 1972 1948 @@ -349,6 +372,7 @@ 1920 1920 -1 + 101 2004 2004 1972 diff --git a/datm/cime_config/stream_definition_datm.xml b/datm/cime_config/stream_definition_datm.xml index ced657e8e..9c11ef540 100644 --- a/datm/cime_config/stream_definition_datm.xml +++ b/datm/cime_config/stream_definition_datm.xml @@ -3788,7 +3788,7 @@ none - $DIN_LOC_ROOT/atm/datm7/CO2/co2_input4MIPs_GHGConcentrations_CMIP_CR-CMIP-1-0-0_gm_1750-2022_CMIP7_c20250228.nc + $DIN_LOC_ROOT/atm/datm7/CO2/co2_input4MIPs_GHGConcentrations_CMIP_CR-CMIP-1-0-0_gm_1750-2022_CMIP7_c20260601.nc co2 Sa_co2diag @@ -4924,6 +4924,39 @@ single + + + $DIN_LOC_ROOT/share/meshes/fv0.9x1.25_141008_polemod_ESMFmesh.nc + + + $DIN_LOC_ROOT/atm/cam/chem/trop_strat_mam5_ts4_aero_spinup/ndep/fndep_clm_B1850C_MT4s.1850monthly.ne30_c251220.nc + + + + NDEP_NHx_month Faxa_ndep_nhx + NDEP_NOy_month Faxa_ndep_noy + + null + + bilinear + + null + 1 + 1850 + 1850 + 0 + + linear + + + cycle + + + 1.5 + + single + + $DIN_LOC_ROOT/share/meshes/fv0.9x1.25_141008_polemod_ESMFmesh.nc @@ -5271,10 +5304,13 @@ $ATM_DOMAIN_MESH - $DATM_CPLHIST_DIR/$DATM_CPLHIST_CASE.cpl.ha2x1d.%ym.nc + $DATM_CPLHIST_DIR/$DATM_CPLHIST_CASE.cpl.hx.atm.24h.avrg.%ymd-00000.nc - a2x1d_Faxa_ndep Faxa_ndep + + + atmImp_Faxa_ndep1 Faxa_ndep_nhx + atmImp_Faxa_ndep2 Faxa_ndep_noy null diff --git a/datm/cime_config/testdefs/testlist_datm.xml b/datm/cime_config/testdefs/testlist_datm.xml index 361c38b7d..580d8b49e 100644 --- a/datm/cime_config/testdefs/testlist_datm.xml +++ b/datm/cime_config/testdefs/testlist_datm.xml @@ -43,7 +43,7 @@ - + diff --git a/datm/cime_config/testdefs/testmods_dirs/datm/cplhist/shell_commands b/datm/cime_config/testdefs/testmods_dirs/datm/cplhist/shell_commands deleted file mode 100644 index 847d5514e..000000000 --- a/datm/cime_config/testdefs/testmods_dirs/datm/cplhist/shell_commands +++ /dev/null @@ -1,6 +0,0 @@ -./xmlchange DATM_CPLHIST_DIR='$DIN_LOC_ROOT/cplhist/cesm3_0/$DATM_CPLHIST_CASE/atm' -./xmlchange DATM_CPLHIST_CASE="b.e30_alpha08o.B1850C_MTso.ne30_t232_wgx3.330" -./xmlchange DATM_YR_START="101" -./xmlchange DATM_YR_END="101" -./xmlchange RUN_STARTDATE="0101-01-01" -./xmlchange DATM_PRESNDEP="clim_1850_cmip6"