Skip to content

Commit

Permalink
Add a gefs test (#1774)
Browse files Browse the repository at this point in the history
This PR:
- temporarily adds a hack to allow testing of the GEFS forecast until the staging
is automated.
- adds a test for GEFS
- updates `setup_expt.py` to compartmentalize gfs and gefs setup options. GEFS
has a single mode `forecast-only` and is necessary for `create_experiment.py` to
work as designed. To eliminate it would need major work.
  • Loading branch information
aerorahul authored Aug 7, 2023
1 parent 570206a commit fe97b19
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 57 deletions.
17 changes: 17 additions & 0 deletions ci/cases/C48_S2SA_gefs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
experiment:
type: gefs
mode: forecast-only

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/gefs_ci_defaults.yaml
4 changes: 4 additions & 0 deletions ci/platforms/gefs_ci_defaults.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defaults:
!INC {{ HOMEgfs }}/parm/config/gefs/yaml/defaults.yaml
base:
ACCOUNT: ${SLURM_ACCOUNT}
7 changes: 6 additions & 1 deletion parm/config/gefs/yaml/defaults.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
# This file intentionally left blank
base:
DO_JEDIATMVAR: "NO"
DO_JEDIATMENS: "NO"
DO_JEDIOCNVAR: "NO"
DO_JEDILANDDA: "NO"
DO_MERGENSST: "NO"
147 changes: 91 additions & 56 deletions workflow/setup_expt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}/{current_cycle_dir} {comrot}/{current_cycle_dir}"
os.system(cmd)
return


Expand Down Expand Up @@ -379,6 +387,65 @@ 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
Expand All @@ -393,70 +460,38 @@ 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'))

return parser.parse_args(argv[0][0] if len(argv[0]) else 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(list(*argv) if len(argv) else None)


def query_and_clean(dirname):
Expand Down Expand Up @@ -492,7 +527,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)
Expand Down

0 comments on commit fe97b19

Please sign in to comment.