From c768cd469e758f17685f5b1251c9c2311f8df9db Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Fri, 4 Aug 2023 10:55:55 -0400 Subject: [PATCH 1/6] add a gefs test with a hack --- ci/cases/C48_S2S_gefs.yaml | 16 ++++++++++++++++ workflow/setup_expt.py | 13 ++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 ci/cases/C48_S2S_gefs.yaml diff --git a/ci/cases/C48_S2S_gefs.yaml b/ci/cases/C48_S2S_gefs.yaml new file mode 100644 index 0000000000..44869fb407 --- /dev/null +++ b/ci/cases/C48_S2S_gefs.yaml @@ -0,0 +1,16 @@ +experiment: + type: gefs + +arguments: + pslot: ${pslot} + app: S2SA + resdet: 48 + resens: 48 + nens: 2 + gfs_cyc: 1 + comrot: ${RUNTESTS}/COMROT + expdir: ${RUNTESTS}/EXPDIR + icsdir: ${ICSDIR_ROOT}/C48C48mx500 + idate: 2021032312 + edate: 2021032312 + yaml: ${HOMEgfs_PR}/ci/platforms/gfs_defaults_ci-updates.yaml diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index 803a704b2a..61ffd837b0 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -224,7 +224,15 @@ def fill_COMROT_forecasts(host, inputs): """ Implementation of 'fill_COMROT' for forecast-only mode """ - print('forecast-only mode treats ICs differently and cannot be staged here') + if inputs.system in ['gfs']: + print('forecast-only mode treats ICs differently and cannot be staged here') + elif inputs.system in ['gefs']: # Temporarily copy ICs from icsdir into COM for testing + print('temporary hack to stage gefs ICs for testing') + comrot = os.path.join(inputs.comrot, inputs.pslot) + idatestr = datetime_to_YMDH(inputs.idate) + current_cycle_dir = f'gefs.{idatestr[:8]}}' + cmd = f"cp -as {inputs.icsdir}/gefs.{idatestr} {comrot}/{current_cycle_dir}" + os.system(cmd) return @@ -455,6 +463,9 @@ def input_args(*argv): default=os.path.join(_top, 'parm/config/gefs')) gefs.add_argument('--yaml', help='Defaults to substitute from', type=str, required=False, default=os.path.join(_top, 'parm/config/gefs/yaml/defaults.yaml')) + gefs.add_argument('--icsdir', help='full path to initial condition directory + [temporary hack in place for testing]', + type=str, required=False, default=None) return parser.parse_args(argv[0][0] if len(argv[0]) else None) From 61f051106b80897528cc58c5871c986de5c95ef4 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Fri, 4 Aug 2023 11:03:50 -0400 Subject: [PATCH 2/6] fix the name of the yaml to match the test --- ci/cases/{C48_S2S_gefs.yaml => C48_S2SA_gefs.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ci/cases/{C48_S2S_gefs.yaml => C48_S2SA_gefs.yaml} (100%) diff --git a/ci/cases/C48_S2S_gefs.yaml b/ci/cases/C48_S2SA_gefs.yaml similarity index 100% rename from ci/cases/C48_S2S_gefs.yaml rename to ci/cases/C48_S2SA_gefs.yaml From 6d341c0a2fc39b13a009afd36939d938c0d47adc Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Fri, 4 Aug 2023 12:28:52 -0400 Subject: [PATCH 3/6] fix bugs found in review --- workflow/setup_expt.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index 61ffd837b0..342b7c77c5 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -230,8 +230,8 @@ def fill_COMROT_forecasts(host, inputs): print('temporary hack to stage gefs ICs for testing') comrot = os.path.join(inputs.comrot, inputs.pslot) idatestr = datetime_to_YMDH(inputs.idate) - current_cycle_dir = f'gefs.{idatestr[:8]}}' - cmd = f"cp -as {inputs.icsdir}/gefs.{idatestr} {comrot}/{current_cycle_dir}" + current_cycle_dir = f"gefs.{idatestr[:8]}" + cmd = f"cp -as {inputs.icsdir}/{current_cycle_dir} {comrot}/{current_cycle_dir}" os.system(cmd) return @@ -463,8 +463,7 @@ def input_args(*argv): default=os.path.join(_top, 'parm/config/gefs')) gefs.add_argument('--yaml', help='Defaults to substitute from', type=str, required=False, default=os.path.join(_top, 'parm/config/gefs/yaml/defaults.yaml')) - gefs.add_argument('--icsdir', help='full path to initial condition directory - [temporary hack in place for testing]', + gefs.add_argument('--icsdir', help='full path to initial condition directory [temporary hack in place for testing]', type=str, required=False, default=None) return parser.parse_args(argv[0][0] if len(argv[0]) else None) From 84c9222e4b5f4e71844f677ff458f4776f9b1721 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Fri, 4 Aug 2023 22:42:36 -0400 Subject: [PATCH 4/6] fix the staging of gefs and update setup_expt.py to handle gefs forecast-only --- ci/cases/C48_S2SA_gefs.yaml | 3 +- ci/platforms/gefs_ci_defaults.yaml | 4 + parm/config/gefs/yaml/defaults.yaml | 7 +- workflow/setup_expt.py | 134 +++++++++++++++++----------- 4 files changed, 92 insertions(+), 56 deletions(-) create mode 100644 ci/platforms/gefs_ci_defaults.yaml diff --git a/ci/cases/C48_S2SA_gefs.yaml b/ci/cases/C48_S2SA_gefs.yaml index 44869fb407..4d5f066858 100644 --- a/ci/cases/C48_S2SA_gefs.yaml +++ b/ci/cases/C48_S2SA_gefs.yaml @@ -1,5 +1,6 @@ experiment: type: gefs + mode: forecast-only arguments: pslot: ${pslot} @@ -13,4 +14,4 @@ arguments: icsdir: ${ICSDIR_ROOT}/C48C48mx500 idate: 2021032312 edate: 2021032312 - yaml: ${HOMEgfs_PR}/ci/platforms/gfs_defaults_ci-updates.yaml + yaml: ${HOMEgfs_PR}/ci/platforms/gefs_ci_defaults.yaml diff --git a/ci/platforms/gefs_ci_defaults.yaml b/ci/platforms/gefs_ci_defaults.yaml new file mode 100644 index 0000000000..2aa30d6be4 --- /dev/null +++ b/ci/platforms/gefs_ci_defaults.yaml @@ -0,0 +1,4 @@ +defaults: + !INC {{ HOMEgfs }}/parm/config/gefs/yaml/defaults.yaml +base: + ACCOUNT: ${SLURM_ACCOUNT} diff --git a/parm/config/gefs/yaml/defaults.yaml b/parm/config/gefs/yaml/defaults.yaml index 6e7633bfe0..ce5d8aeb3d 100644 --- a/parm/config/gefs/yaml/defaults.yaml +++ b/parm/config/gefs/yaml/defaults.yaml @@ -1 +1,6 @@ -# This file intentionally left blank +base: + DO_JEDIATMVAR: "NO" + DO_JEDIATMENS: "NO" + DO_JEDIOCNVAR: "NO" + DO_JEDILANDDA: "NO" + DO_MERGENSST: "NO" diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index 342b7c77c5..e84939d1b6 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -387,6 +387,66 @@ def input_args(*argv): Method to collect user arguments for `setup_expt.py` """ + ufs_apps = ['ATM', 'ATMA', 'ATMW', 'S2S', 'S2SA', 'S2SW'] + + def _common_args(parser): + parser.add_argument('--pslot', help='parallel experiment name', + type=str, required=False, default='test') + parser.add_argument('--resdet', help='resolution of the deterministic model forecast', + type=int, required=False, default=384) + parser.add_argument('--comrot', help='full path to COMROT', + type=str, required=False, default=os.getenv('HOME')) + parser.add_argument('--expdir', help='full path to EXPDIR', + type=str, required=False, default=os.getenv('HOME')) + parser.add_argument('--idate', help='starting date of experiment, initial conditions must exist!', + required=True, type=lambda dd: to_datetime(dd)) + parser.add_argument('--edate', help='end date experiment', required=True, type=lambda dd: to_datetime(dd)) + return parser + + def _gfs_args(parser): + parser.add_argument('--start', help='restart mode: warm or cold', type=str, + choices=['warm', 'cold'], required=False, default='cold') + parser.add_argument('--cdump', help='CDUMP to start the experiment', + type=str, required=False, default='gdas') + # --configdir is hidden from help + parser.add_argument('--configdir', help=SUPPRESS, type=str, required=False, default=os.path.join(_top, 'parm/config/gfs')) + parser.add_argument('--yaml', help='Defaults to substitute from', type=str, + required=False, default=os.path.join(_top, 'parm/config/gfs/yaml/defaults.yaml')) + return parser + + def _gfs_cycled_args(parser): + parser.add_argument('--icsdir', help='full path to initial condition directory', type=str, required=False, default=None) + parser.add_argument('--app', help='UFS application', type=str, + choices=ufs_apps, required=False, default='ATM') + parser.add_argument('--gfs_cyc', help='cycles to run forecast', type=int, + choices=[0, 1, 2, 4], default=1, required=False) + return parser + + def _gfs_or_gefs_ensemble_args(parser): + parser.add_argument('--resens', help='resolution of the ensemble model forecast', + type=int, required=False, default=192) + parser.add_argument('--nens', help='number of ensemble members', + type=int, required=False, default=20) + return parser + + def _gfs_or_gefs_forecast_args(parser): + parser.add_argument('--app', help='UFS application', type=str, + choices=ufs_apps + ['S2SWA'], required=False, default='ATM') + parser.add_argument('--gfs_cyc', help='Number of forecasts per day', type=int, + choices=[1, 2, 4], default=1, required=False) + return parser + + def _gefs_args(parser): + parser.add_argument('--start', help=SUPPRESS, type=str, required=False, default='cold') + parser.add_argument('--configdir', help=SUPPRESS, type=str, required=False, + default=os.path.join(_top, 'parm/config/gefs')) + parser.add_argument('--yaml', help='Defaults to substitute from', type=str, required=False, + default=os.path.join(_top, 'parm/config/gefs/yaml/defaults.yaml')) + parser.add_argument('--icsdir', help='full path to initial condition directory [temporary hack in place for testing]', + type=str, required=False, default=None) + return parser + + description = """ Setup files and directories to start a GFS parallel.\n Create EXPDIR, copy config files.\n @@ -401,70 +461,36 @@ def input_args(*argv): gfs = sysparser.add_parser('gfs', help='arguments for GFS') gefs = sysparser.add_parser('gefs', help='arguments for GEFS') - modeparser = gfs.add_subparsers(dest='mode') - cycled = modeparser.add_parser('cycled', help='arguments for cycled mode') - forecasts = modeparser.add_parser('forecast-only', help='arguments for forecast-only mode') + gfsmodeparser = gfs.add_subparsers(dest='mode') + gfscycled = gfsmodeparser.add_parser('cycled', help='arguments for cycled mode') + gfsforecasts = gfsmodeparser.add_parser('forecast-only', help='arguments for forecast-only mode') - # Common arguments across all modes - for subp in [cycled, forecasts, gefs]: - subp.add_argument('--pslot', help='parallel experiment name', - type=str, required=False, default='test') - subp.add_argument('--resdet', help='resolution of the deterministic model forecast', - type=int, required=False, default=384) - subp.add_argument('--comrot', help='full path to COMROT', - type=str, required=False, default=os.getenv('HOME')) - subp.add_argument('--expdir', help='full path to EXPDIR', - type=str, required=False, default=os.getenv('HOME')) - subp.add_argument('--idate', help='starting date of experiment, initial conditions must exist!', - required=True, type=lambda dd: to_datetime(dd)) - subp.add_argument('--edate', help='end date experiment', required=True, type=lambda dd: to_datetime(dd)) + gefsmodeparser = gefs.add_subparsers(dest='mode') + gefsforecasts = gefsmodeparser.add_parser('forecast-only', help='arguments for forecast-only mode') - ufs_apps = ['ATM', 'ATMA', 'ATMW', 'S2S', 'S2SA', 'S2SW'] + # Common arguments across all modes + for subp in [gfscycled, gfsforecasts, gefsforecasts]: + subp = _common_args(subp) # GFS-only arguments - for subp in [cycled, forecasts]: - subp.add_argument('--start', help='restart mode: warm or cold', type=str, - choices=['warm', 'cold'], required=False, default='cold') - subp.add_argument('--cdump', help='CDUMP to start the experiment', - type=str, required=False, default='gdas') - # --configdir is hidden from help - subp.add_argument('--configdir', help=SUPPRESS, type=str, required=False, default=os.path.join(_top, 'parm/config/gfs')) - subp.add_argument('--yaml', help='Defaults to substitute from', type=str, - required=False, default=os.path.join(_top, 'parm/config/gfs/yaml/defaults.yaml')) + for subp in [gfscycled, gfsforecasts]: + subp = _gfs_args(subp) # ensemble-only arguments - for subp in [cycled, gefs]: - subp.add_argument('--resens', help='resolution of the ensemble model forecast', - type=int, required=False, default=192) - subp.add_argument('--nens', help='number of ensemble members', - type=int, required=False, default=20) + for subp in [gfscycled, gefsforecasts]: + subp = _gfs_or_gefs_ensemble_args(subp) # GFS/GEFS forecast-only additional arguments - for subp in [forecasts, gefs]: - subp.add_argument('--app', help='UFS application', type=str, - choices=ufs_apps + ['S2SWA'], required=False, default='ATM') - subp.add_argument('--gfs_cyc', help='Number of forecasts per day', type=int, - choices=[1, 2, 4], default=1, required=False) + for subp in [gfsforecasts, gefsforecasts]: + subp = _gfs_or_gefs_forecast_args(subp) # cycled mode additional arguments - cycled.add_argument('--icsdir', help='full path to initial condition directory', type=str, required=False, default=None) - cycled.add_argument('--app', help='UFS application', type=str, - choices=ufs_apps, required=False, default='ATM') - cycled.add_argument('--gfs_cyc', help='cycles to run forecast', type=int, - choices=[0, 1, 2, 4], default=1, required=False) - - # GEFS-only arguments - # Create hidden mode argument since there is real option for GEFS - gefs.add_argument('--mode', help=SUPPRESS, type=str, required=False, default='forecast-only') - # Create hidden start argument since GEFS is always cold start - gefs.add_argument('--start', help=SUPPRESS, type=str, required=False, default='cold') - # Create hidden arguments for configdir and yaml - gefs.add_argument('--configdir', help=SUPPRESS, type=str, required=False, - default=os.path.join(_top, 'parm/config/gefs')) - gefs.add_argument('--yaml', help='Defaults to substitute from', type=str, required=False, - default=os.path.join(_top, 'parm/config/gefs/yaml/defaults.yaml')) - gefs.add_argument('--icsdir', help='full path to initial condition directory [temporary hack in place for testing]', - type=str, required=False, default=None) + for subp in [gfscycled]: + subp = _gfs_cycled_args(subp) + + # GEFS forecast-only arguments + for subp in [gefsforecasts]: + subp = _gefs_args(subp) return parser.parse_args(argv[0][0] if len(argv[0]) else None) From cdb3a3fa0b75999ed9f5b9d0982c8b4f882ed6f3 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Fri, 4 Aug 2023 23:02:03 -0400 Subject: [PATCH 5/6] cleaner way to parse arguments that may or maynot be passed --- workflow/setup_expt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index e84939d1b6..7fa48362b3 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -492,7 +492,7 @@ def _gefs_args(parser): for subp in [gefsforecasts]: subp = _gefs_args(subp) - return parser.parse_args(argv[0][0] if len(argv[0]) else None) + return parser.parse_args(list(*argv) if len(argv) else None) def query_and_clean(dirname): @@ -528,7 +528,7 @@ def validate_user_request(host, inputs): def main(*argv): - user_inputs = input_args(argv) + user_inputs = input_args(*argv) host = Host() validate_user_request(host, user_inputs) From 379fcd53b75a7b836817ed1ad7965e1f4d6bea52 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Fri, 4 Aug 2023 23:05:08 -0400 Subject: [PATCH 6/6] fix pynorms --- workflow/setup_expt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index 7fa48362b3..7f9651f79a 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -446,7 +446,6 @@ def _gefs_args(parser): type=str, required=False, default=None) return parser - description = """ Setup files and directories to start a GFS parallel.\n Create EXPDIR, copy config files.\n