diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index c2c2004..97b5ee6 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -5,9 +5,10 @@ - MOM6 OCEAN + MOM6 OCEAN MARBL Biogeochemistry Tracer Library base biotic tracers MARBL Biogeochemistry Tracer Library abiotic DIC tracers + phys cycle option @@ -206,6 +207,44 @@ + + integer + 0 + run_component_mom + env_run.xml + number of years to run before cycling MOM6 physics + MOM6 physics cycling only occurs if MOM6_PHYS_CYCLE_YEARS_IN_CYCLE>0 + and pre- and post-run scripts are specified in env vars + PRERUN_SCRIPT and POSTRUN_SCRIPT + + + + integer + 0 + run_component_mom + env_run.xml + number of months run since most recent cycling of MOM6 physics + + + + char + + run_component_mom + env_run.xml + File containing tracer IC filenames + + + + logical + TRUE,FALSE + FALSE + run_component_mom + env_run.xml + Rescale MARBL tracers so that their vertical integrals match + their vertical integrals computed from the IC file(s). + + + ========================================= MOM6 naming conventions diff --git a/cime_config/config_compsets.xml b/cime_config/config_compsets.xml index 6e5cf63..e57bcbb 100644 --- a/cime_config/config_compsets.xml +++ b/cime_config/config_compsets.xml @@ -115,9 +115,50 @@ 1850_DATM%JRA-1p5-2023_SLND_CICE_MOM6%MARBL-BIO_DROF%JRA-1p5-2023_SGLC_WW3 + + + + GW1850MARBL_CPLHIST + 1850_DATM%CPLHIST_SLND_CICE_MOM6%MARBL-BIO_DROF%CPLHIST_SGLC_WW3 + + + + GW1850MARBL_CPLHIST_PHYS_CYCLE + 1850_DATM%CPLHIST_SLND_CICE_MOM6%MARBL-BIO%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3 + + GW1850ABIOMARBL_JRA 1850_DATM%JRA-1p5-2023_SLND_CICE_MOM6%MARBL-ABIO_DROF%JRA-1p5-2023_SGLC_WW3 + + + + + + + $SRCROOT/components/mom/cime_config/phys_cycle_prerun + + + + + + $SRCROOT/components/mom/cime_config/phys_cycle_postrun + + + + + + FALSE + + + + + diff --git a/cime_config/config_pes.xml b/cime_config/config_pes.xml index 6ae579d..36561ac 100644 --- a/cime_config/config_pes.xml +++ b/cime_config/config_pes.xml @@ -210,39 +210,6 @@ 0 - - none - - 384 - 384 - 384 - 384 - 2560 - 1 - 128 - 1 - - - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - - - 0 - 0 - 0 - 0 - 512 - 0 - 384 - 0 - - none @@ -413,6 +380,44 @@ + + + + MARBL 2/3, any DATM forcing, including CPLHIST + + 384 + 384 + 384 + 384 + 2560 + 1 + 128 + 1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + 0 + 0 + 0 + 512 + 0 + 384 + 0 + + + + + diff --git a/cime_config/phys_cycle_postrun b/cime_config/phys_cycle_postrun new file mode 100755 index 0000000..730ca14 --- /dev/null +++ b/cime_config/phys_cycle_postrun @@ -0,0 +1,121 @@ +#!/usr/bin/env python + +"""mom6 physics cycle postrun script +""" + +import logging +import os +import sys + +_CIMEROOT = os.environ.get("CIMEROOT") +if _CIMEROOT is None: + raise SystemExit("ERROR: must set CIMEROOT environment variable") +sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) + +from standard_script_setup import * +from CIME.case import Case +from CIME.utils import expect + +logger = logging.getLogger(__name__) + +############################################################################### +def phys_cycle_postrun(caseroot): +############################################################################### + + ch = logging.StreamHandler(stream=sys.stdout) + ch.setLevel(logging.INFO) + logger.addHandler(ch) + + with Case(caseroot, read_only=False) as case: + mom6_phys_cycle_years_in_cycle = case.get_value("MOM6_PHYS_CYCLE_YEARS_IN_CYCLE") + expect(mom6_phys_cycle_years_in_cycle is not None, + f"MOM6_PHYS_CYCLE_YEARS_IN_CYCLE should be defined when {sys.argv[0]} is called") + if mom6_phys_cycle_years_in_cycle <= 0: + return + + # ------------------------------------------------------------------------- + # Determine if model has run the length of MOM6 physics run cycle length + # this assumes that STOP_ options have not been changed during the submission + # ------------------------------------------------------------------------- + + stop_option = case.get_value("STOP_OPTION") + stop_n = case.get_value("STOP_N") + + mom6_phys_cycle_months_run_since_cycle = case.get_value( + "MOM6_PHYS_CYCLE_MONTHS_RUN_SINCE_CYCLE" + ) + + # we know from phys_cycle_prerun that stop_option is either "nmonth*" or "nyear*" + + if stop_option.find("nmonth") == 0: + mom6_phys_cycle_months_run_since_cycle_new = ( + mom6_phys_cycle_months_run_since_cycle + stop_n + ) + if stop_option.find("nyear") == 0: + mom6_phys_cycle_months_run_since_cycle_new = ( + mom6_phys_cycle_months_run_since_cycle + stop_n * 12 + ) + + case.set_value("MOM6_PHYS_CYCLE_MONTHS_RUN_SINCE_CYCLE", + mom6_phys_cycle_months_run_since_cycle_new) + logger.info("MOM6_PHYS_CYCLE_MONTHS_RUN_SINCE_CYCLE incremented to %d", + mom6_phys_cycle_months_run_since_cycle_new) + + if mom6_phys_cycle_months_run_since_cycle_new < mom6_phys_cycle_years_in_cycle * 12: + case.set_value("CONTINUE_RUN", "TRUE") + case.set_value("MOM6_TRACER_IC_FILE_OVERRIDE_POINTER", "") + case.set_value("MARBL_ENFORCE_TRACER_ZINT", "FALSE") + case.flush() + return + + # ------------------------------------------------------------------------- + # Model has run the length of MOM6 physics run cycle length, reset model physics + # ------------------------------------------------------------------------- + + logger.info("resetting model physics") + case.set_value("CONTINUE_RUN", "FALSE") + case.set_value("MOM6_PHYS_CYCLE_MONTHS_RUN_SINCE_CYCLE", 0) + + # ------------------------------------------------------------------------- + # Compute new RUN_STARTDATE + # ------------------------------------------------------------------------- + + run_startdate = case.get_value("RUN_STARTDATE") + yyyy = int(run_startdate[:4]) + yyyy = yyyy + mom6_phys_cycle_years_in_cycle + run_startdate = f"{yyyy:04d}{run_startdate[4:]}" + case.set_value("RUN_STARTDATE", run_startdate) + logger.info("RUN_STARTDATE incremented to %s", run_startdate) + + # ------------------------------------------------------------------------- + # Configure tracer module initialization for new PHYS_CYCLE cycle + # ------------------------------------------------------------------------- + + case.set_value("MOM6_TRACER_IC_FILE_OVERRIDE_POINTER", + f"./rpointer.ocn.{run_startdate}-00000") + case.set_value("MARBL_ENFORCE_TRACER_ZINT", "TRUE") + + # ------------------------------------------------------------------------- + # Update CPLHIST ALIGN variables + # ------------------------------------------------------------------------- + + varnames_dict = { + 'DATM_MODE': 'DATM_YR_ALIGN', + 'DROF_MODE': 'DROF_CPLHIST_YR_ALIGN' + } + for mode_varname, align_varname in varnames_dict.items(): + if case.get_value(mode_varname) == "CPLHIST": + align_val = case.get_value(align_varname) + if align_val is not None: + align_val = align_val + mom6_phys_cycle_years_in_cycle + case.set_value(align_varname, align_val) + logger.info("%s incremented to %d", align_varname, align_val) + + case.flush() + +############################################################################### + +if __name__ == "__main__": + expect(len(sys.argv) == 2, "caseroot is a required input argument") + caseroot = sys.argv[1] + phys_cycle_postrun(caseroot) diff --git a/cime_config/phys_cycle_prerun b/cime_config/phys_cycle_prerun new file mode 100755 index 0000000..7ce7a6e --- /dev/null +++ b/cime_config/phys_cycle_prerun @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +"""mom6 physics cycle prerun script +""" + +import logging +import os +import sys + +_CIMEROOT = os.environ.get("CIMEROOT") +if _CIMEROOT is None: + raise SystemExit("ERROR: must set CIMEROOT environment variable") +sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) + +from standard_script_setup import * +from CIME.case import Case +from CIME.utils import expect + +logger = logging.getLogger(__name__) + +############################################################################### +def phys_cycle_prerun(caseroot): +############################################################################### + + ch = logging.StreamHandler(stream=sys.stdout) + ch.setLevel(logging.INFO) + logger.addHandler(ch) + + with Case(caseroot, read_only=False) as case: + mom6_phys_cycle_years_in_cycle = case.get_value("MOM6_PHYS_CYCLE_YEARS_IN_CYCLE") + expect(mom6_phys_cycle_years_in_cycle is not None, + f"MOM6_PHYS_CYCLE_YEARS_IN_CYCLE should be defined when {sys.argv[0]} is called") + logger.info("mom6_phys_cycle_years_in_cycle = %d", mom6_phys_cycle_years_in_cycle) + if mom6_phys_cycle_years_in_cycle <= 0: + return + + # RUN_TYPE cannot be branch + expect(case.get_value("RUN_TYPE") != "branch", + "RUN_TYPE set to branch not implemented for MOM6 physics cycling" + " because RUN_STARTDATE cannot be incremented") + + # stop_option must be nmonth, nmonths, nyear or nyears + stop_option = case.get_value("STOP_OPTION") + expect((stop_option.find("nmonth") == 0) or (stop_option.find("nyear") == 0), + f"STOP_OPTION={stop_option} not implemented for MOM6 physics cycling") + + # ------------------------------------------------------------------------- + # Ensure that phys_cycle scripts are in charge of setting CONTINUE_RUN + # ------------------------------------------------------------------------- + case.set_value("RESUBMIT_SETS_CONTINUE_RUN", "FALSE") + case.flush() + + # ------------------------------------------------------------------------- + # Ensure that the current run length will not overshoot the phys_cycle run length + # ------------------------------------------------------------------------- + + mom6_phys_cycle_months_run_since_cycle = case.get_value( + "MOM6_PHYS_CYCLE_MONTHS_RUN_SINCE_CYCLE" + ) + mom6_phys_cycle_years_in_cycle = case.get_value("MOM6_PHYS_CYCLE_YEARS_IN_CYCLE") + + stop_n = case.get_value("STOP_N") + if stop_option.find("nmonth") == 0: + mom6_phys_cycle_months_run_since_cycle_new = ( + mom6_phys_cycle_months_run_since_cycle + stop_n + ) + elif stop_option.find("nyear") == 0: + mom6_phys_cycle_months_run_since_cycle_new = ( + mom6_phys_cycle_months_run_since_cycle + stop_n * 12 + ) + + expect(mom6_phys_cycle_months_run_since_cycle_new <= mom6_phys_cycle_years_in_cycle * 12, + "run will overshoot phys_cycle run length") + +############################################################################### + +if __name__ == "__main__": + expect(len(sys.argv) == 2, "caseroot is a required input argument") + caseroot = sys.argv[1] + phys_cycle_prerun(caseroot) diff --git a/param_templates/MOM_input.yaml b/param_templates/MOM_input.yaml index 83335d6..9f0862d 100644 --- a/param_templates/MOM_input.yaml +++ b/param_templates/MOM_input.yaml @@ -355,11 +355,17 @@ Global: for the MARBL tracer package." datatype: string value: - $RUN_TYPE == "hybrid": - = f'./{$RUN_REFCASE}.mom6{$INST_SUFFIX}.r.{$RUN_REFDATE}-{$RUN_REFTOD}.nc' + $MARBL_CONFIG == "latest": ${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc + $MARBL_CONFIG == "latest+4p2z": ${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc + MARBL_TRACERS_IC_FILE_OVERRIDE_POINTER: + description: | + "File containing tracer IC filenames overriding MARBL_TRACERS_IC_FILE." + datatype: string + value: + $MOM6_TRACER_IC_FILE_OVERRIDE_POINTER != "": $MOM6_TRACER_IC_FILE_OVERRIDE_POINTER else: - $MARBL_CONFIG == "latest": ${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc - $MARBL_CONFIG == "latest+4p2z": ${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc + $RUN_TYPE == "hybrid": + = f'./rpointer.ocn.{$RUN_REFDATE}-{$RUN_REFTOD}' MARBL_TRACERS_IC_FILE_IS_Z: description: | "[Boolean] default = True @@ -368,6 +374,31 @@ Global: units: Boolean value: $RUN_TYPE == "hybrid": False + MARBL_REMAP_NON_Z_IC: + description: | + "[Boolean] default = False + If true, and MARBL_IC_FILE_IS_Z is false, + then remap IC vals to current model thicknesses." + datatype: logical + units: Boolean + value: + $MOM6_PHYS_CYCLE_YEARS_IN_CYCLE > 0: True + MARBL_ENFORCE_TRACER_ZINT: + description: | + "Rescale MARBL tracers so that their vertical integrals match + their vertical integrals computed from the IC file(s). + Value is determined by MARBL_ENFORCE_TRACER_ZINT in env_run.xml" + datatype: logical + units: Boolean + value: $MARBL_ENFORCE_TRACER_ZINT + MARBL_TRACERS_INIT_VERTICAL_REMAP_ONLY: + description: | + "[Boolean] default = False + If true, initial conditions are on the model horizontal grid." + datatype: logical + units: Boolean + value: + $MOM6_PHYS_CYCLE_YEARS_IN_CYCLE > 0: True MARBL_IC_MIN_VAL: description: | "Minimum value of tracer initial conditions @@ -1432,6 +1463,8 @@ Global: by a comma or space, e.g. FILE:lev.nc,dz or FILE:lev.nc,interfaces=zw WOA09[:N] - the WOA09 vertical grid (approximately) + WOA09INT[:N] - layers spanned by the WOA09 depths + WOA23INT[:N] - layers spanned by the WOA23 depths FNC1:string - FNC1:dz_min,H_total,power,precision HYBRID:string - read from a file. The string specifies the filename and two variable names, separated @@ -3396,14 +3429,24 @@ Global: value: $RUN_TYPE == "hybrid": = f'./{$RUN_REFCASE}.mom6{$INST_SUFFIX}.r.{$RUN_REFDATE}-{$RUN_REFTOD}.nc' - AGE_IC_FILE: + AGE_IC_FILE_OVERRIDE_POINTER: description: | - "The file in which the age-tracer initial values can be found, or an empty - string for internal initialization." + "File containing tracer IC filenames overriding AGE_IC_FILE." datatype: string value: - $RUN_TYPE == "hybrid" and $CONTINUE_RUN == False: - = f'./{$RUN_REFCASE}.mom6{$INST_SUFFIX}.r.{$RUN_REFDATE}-{$RUN_REFTOD}.nc' + $MOM6_TRACER_IC_FILE_OVERRIDE_POINTER != "": $MOM6_TRACER_IC_FILE_OVERRIDE_POINTER + else: + $RUN_TYPE == "hybrid": + = f'./rpointer.ocn.{$RUN_REFDATE}-{$RUN_REFTOD}' + AGE_REMAP_NON_Z_IC: + description: | + "[Boolean] default = False + If true, and AGE_IC_FILE_IS_Z is false, + then remap IC vals to current model thicknesses." + datatype: logical + units: Boolean + value: + $MOM6_PHYS_CYCLE_YEARS_IN_CYCLE > 0: True T_REF: description: | "[degC] diff --git a/param_templates/json/MOM_input.json b/param_templates/json/MOM_input.json index dba1d1b..2e72117 100644 --- a/param_templates/json/MOM_input.json +++ b/param_templates/json/MOM_input.json @@ -258,10 +258,17 @@ "description": "\"Name of file containing initial conditions\nfor the MARBL tracer package.\"\n", "datatype": "string", "value": { - "$RUN_TYPE == \"hybrid\"": "= f'./{$RUN_REFCASE}.mom6{$INST_SUFFIX}.r.{$RUN_REFDATE}-{$RUN_REFTOD}.nc'", + "$MARBL_CONFIG == \"latest\"": "${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc", + "$MARBL_CONFIG == \"latest+4p2z\"": "${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc" + } + }, + "MARBL_TRACERS_IC_FILE_OVERRIDE_POINTER": { + "description": "\"File containing tracer IC filenames overriding MARBL_TRACERS_IC_FILE.\"\n", + "datatype": "string", + "value": { + "$MOM6_TRACER_IC_FILE_OVERRIDE_POINTER != \"\"": "$MOM6_TRACER_IC_FILE_OVERRIDE_POINTER", "else": { - "$MARBL_CONFIG == \"latest\"": "${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc", - "$MARBL_CONFIG == \"latest+4p2z\"": "${DIN_LOC_ROOT}/ocn/mom/grid_indpt/ecosys_jan_IC_omip_latlon_1x1_180W_c250613.nc" + "$RUN_TYPE == \"hybrid\"": "= f'./rpointer.ocn.{$RUN_REFDATE}-{$RUN_REFTOD}'" } } }, @@ -273,6 +280,28 @@ "$RUN_TYPE == \"hybrid\"": false } }, + "MARBL_REMAP_NON_Z_IC": { + "description": "\"[Boolean] default = False\nIf true, and MARBL_IC_FILE_IS_Z is false,\nthen remap IC vals to current model thicknesses.\"\n", + "datatype": "logical", + "units": "Boolean", + "value": { + "$MOM6_PHYS_CYCLE_YEARS_IN_CYCLE > 0": true + } + }, + "MARBL_ENFORCE_TRACER_ZINT": { + "description": "\"Rescale MARBL tracers so that their vertical integrals match\n their vertical integrals computed from the IC file(s).\n Value is determined by MARBL_ENFORCE_TRACER_ZINT in env_run.xml\"\n", + "datatype": "logical", + "units": "Boolean", + "value": "$MARBL_ENFORCE_TRACER_ZINT" + }, + "MARBL_TRACERS_INIT_VERTICAL_REMAP_ONLY": { + "description": "\"[Boolean] default = False\nIf true, initial conditions are on the model horizontal grid.\"\n", + "datatype": "logical", + "units": "Boolean", + "value": { + "$MOM6_PHYS_CYCLE_YEARS_IN_CYCLE > 0": true + } + }, "MARBL_IC_MIN_VAL": { "description": "\"Minimum value of tracer initial conditions\n (when initializing from Z)\"\n", "datatype": "real", @@ -1126,7 +1155,7 @@ } }, "DIAG_COORD_DEF_RHO2": { - "description": "\" default = 'WOA09'\nDetermines how to specify the coordinate resolution. Valid options are:\nPARAM - use the vector-parameter DIAG_COORD_RES_RHO2\nUNIFORM[:N] - uniformly distributed\nFILE:string - read from a file. The string specifies\nthe filename and variable name, separated\nby a comma or space, e.g. FILE:lev.nc,dz\nor FILE:lev.nc,interfaces=zw\nWOA09[:N] - the WOA09 vertical grid (approximately)\nFNC1:string - FNC1:dz_min,H_total,power,precision\nHYBRID:string - read from a file. The string specifies\nthe filename and two variable names, separated\nby a comma or space, for sigma-2 and dz. e.g.\nHYBRID:vgrid.nc,sigma2,dz\"\n", + "description": "\" default = 'WOA09'\nDetermines how to specify the coordinate resolution. Valid options are:\nPARAM - use the vector-parameter DIAG_COORD_RES_RHO2\nUNIFORM[:N] - uniformly distributed\nFILE:string - read from a file. The string specifies\nthe filename and variable name, separated\nby a comma or space, e.g. FILE:lev.nc,dz\nor FILE:lev.nc,interfaces=zw\nWOA09[:N] - the WOA09 vertical grid (approximately)\nWOA09INT[:N] - layers spanned by the WOA09 depths\nWOA23INT[:N] - layers spanned by the WOA23 depths\nFNC1:string - FNC1:dz_min,H_total,power,precision\nHYBRID:string - read from a file. The string specifies\nthe filename and two variable names, separated\nby a comma or space, for sigma-2 and dz. e.g.\nHYBRID:vgrid.nc,sigma2,dz\"\n", "datatype": "string", "value": { "$OCN_GRID in [\"tx2_3v2\", \"tx2_3v3\"]": "\"FILE:ocean_rho2_190917.nc,interfaces=rho2\"" @@ -2769,11 +2798,22 @@ "$RUN_TYPE == \"hybrid\"": "= f'./{$RUN_REFCASE}.mom6{$INST_SUFFIX}.r.{$RUN_REFDATE}-{$RUN_REFTOD}.nc'" } }, - "AGE_IC_FILE": { - "description": "\"The file in which the age-tracer initial values can be found, or an empty\nstring for internal initialization.\"\n", + "AGE_IC_FILE_OVERRIDE_POINTER": { + "description": "\"File containing tracer IC filenames overriding AGE_IC_FILE.\"\n", "datatype": "string", "value": { - "$RUN_TYPE == \"hybrid\" and $CONTINUE_RUN == False": "= f'./{$RUN_REFCASE}.mom6{$INST_SUFFIX}.r.{$RUN_REFDATE}-{$RUN_REFTOD}.nc'" + "$MOM6_TRACER_IC_FILE_OVERRIDE_POINTER != \"\"": "$MOM6_TRACER_IC_FILE_OVERRIDE_POINTER", + "else": { + "$RUN_TYPE == \"hybrid\"": "= f'./rpointer.ocn.{$RUN_REFDATE}-{$RUN_REFTOD}'" + } + } + }, + "AGE_REMAP_NON_Z_IC": { + "description": "\"[Boolean] default = False\nIf true, and AGE_IC_FILE_IS_Z is false,\nthen remap IC vals to current model thicknesses.\"\n", + "datatype": "logical", + "units": "Boolean", + "value": { + "$MOM6_PHYS_CYCLE_YEARS_IN_CYCLE > 0": true } }, "T_REF": {