From 1eb62f3f5a46950438ea1781605c585b13d28ca9 Mon Sep 17 00:00:00 2001 From: Jiarui Dong Date: Fri, 8 Nov 2024 21:36:26 -0500 Subject: [PATCH 01/10] Update snow obs processing job (#3055) This PR removes the `prepsnowobs` job, and add the IMS snow obs processing to the snow analysis job and it will only process the snow cover obs if the cycle is 00z. Resolves #2902 --------- Co-authored-by: Cory Martin Co-authored-by: David Huber <69919478+DavidHuber-NOAA@users.noreply.github.com> --- env/HERA.env | 6 ++-- env/HERCULES.env | 6 ++-- env/JET.env | 6 ++-- env/ORION.env | 6 ++-- env/S4.env | 6 ++-- env/WCOSS2.env | 6 ++-- jobs/JGLOBAL_PREP_SNOW_OBS | 50 ----------------------------- jobs/rocoto/prepsnowobs.sh | 26 --------------- jobs/rocoto/snowanl.sh | 8 +++++ parm/config/gfs/config.prepsnowobs | 18 ----------- parm/config/gfs/config.resources | 9 +----- parm/config/gfs/config.snowanl | 6 ++++ scripts/exglobal_prep_snow_obs.py | 24 -------------- scripts/exglobal_snow_analysis.py | 2 ++ workflow/applications/gfs_cycled.py | 4 +-- workflow/rocoto/gfs_tasks.py | 28 +--------------- workflow/rocoto/tasks.py | 2 +- 17 files changed, 33 insertions(+), 180 deletions(-) delete mode 100755 jobs/JGLOBAL_PREP_SNOW_OBS delete mode 100755 jobs/rocoto/prepsnowobs.sh delete mode 100644 parm/config/gfs/config.prepsnowobs delete mode 100755 scripts/exglobal_prep_snow_obs.py diff --git a/env/HERA.env b/env/HERA.env index 259461b1ac..f10bfcc537 100755 --- a/env/HERA.env +++ b/env/HERA.env @@ -52,10 +52,6 @@ if [[ "${step}" = "prep" ]] || [[ "${step}" = "prepbufr" ]]; then export sys_tp="HERA" export launcher_PREP="srun --hint=nomultithread" -elif [[ "${step}" = "prepsnowobs" ]]; then - - export APRUN_CALCFIMS="${APRUN_default}" - elif [[ "${step}" = "prep_emissions" ]]; then export APRUN="${APRUN_default}" @@ -116,6 +112,8 @@ elif [[ "${step}" = "prepobsaero" ]]; then elif [[ "${step}" = "snowanl" ]]; then + export APRUN_CALCFIMS="${launcher} -n 1" + export NTHREADS_SNOWANL=${NTHREADSmax} export APRUN_SNOWANL="${APRUN_default} --cpus-per-task=${NTHREADS_SNOWANL}" diff --git a/env/HERCULES.env b/env/HERCULES.env index bed1d11281..15e3928f08 100755 --- a/env/HERCULES.env +++ b/env/HERCULES.env @@ -50,10 +50,6 @@ case ${step} in export sys_tp="HERCULES" export launcher_PREP="srun --hint=nomultithread" ;; - "prepsnowobs") - - export APRUN_CALCFIMS="${APRUN_default}" - ;; "prep_emissions") export APRUN="${APRUN_default}" @@ -115,6 +111,8 @@ case ${step} in ;; "snowanl") + export APRUN_CALCFIMS="${launcher} -n 1" + export NTHREADS_SNOWANL=${NTHREADSmax} export APRUN_SNOWANL="${APRUN_default} --cpus-per-task=${NTHREADS_SNOWANL}" diff --git a/env/JET.env b/env/JET.env index dbc249d4d6..6465b69acd 100755 --- a/env/JET.env +++ b/env/JET.env @@ -40,10 +40,6 @@ if [[ "${step}" = "prep" ]] || [[ "${step}" = "prepbufr" ]]; then export sys_tp="JET" export launcher_PREP="srun" -elif [[ "${step}" = "prepsnowobs" ]]; then - - export APRUN_CALCFIMS="${launcher} -n 1" - elif [[ "${step}" = "prep_emissions" ]]; then export APRUN="${launcher} -n 1" @@ -99,6 +95,8 @@ elif [[ "${step}" = "prepobsaero" ]]; then elif [[ "${step}" = "snowanl" ]]; then + export APRUN_CALCFIMS="${launcher} -n 1" + export NTHREADS_SNOWANL=${NTHREADSmax} export APRUN_SNOWANL="${APRUN_default}" diff --git a/env/ORION.env b/env/ORION.env index 06ae2c1a63..1dc49e9362 100755 --- a/env/ORION.env +++ b/env/ORION.env @@ -47,10 +47,6 @@ if [[ "${step}" = "prep" ]] || [[ "${step}" = "prepbufr" ]]; then export sys_tp="ORION" export launcher_PREP="srun --hint=nomultithread" -elif [[ "${step}" = "prepsnowobs" ]]; then - - export APRUN_CALCFIMS="${launcher} -n 1" - elif [[ "${step}" = "prep_emissions" ]]; then export APRUN="${launcher} -n 1" @@ -106,6 +102,8 @@ elif [[ "${step}" = "prepobsaero" ]]; then elif [[ "${step}" = "snowanl" ]]; then + export APRUN_CALCFIMS="${launcher} -n 1" + export NTHREADS_SNOWANL=${NTHREADSmax} export APRUN_SNOWANL="${APRUN_default} --cpus-per-task=${NTHREADS_SNOWANL}" diff --git a/env/S4.env b/env/S4.env index 5d5ffd23b1..9a5baf29ed 100755 --- a/env/S4.env +++ b/env/S4.env @@ -40,10 +40,6 @@ if [[ "${step}" = "prep" ]] || [[ "${step}" = "prepbufr" ]]; then export sys_tp="S4" export launcher_PREP="srun" -elif [[ "${step}" = "prepsnowobs" ]]; then - - export APRUN_CALCFIMS="${APRUN_default}" - elif [[ "${step}" = "prep_emissions" ]]; then export APRUN="${APRUN_default}" @@ -99,6 +95,8 @@ elif [[ "${step}" = "prepobsaero" ]]; then elif [[ "${step}" = "snowanl" ]]; then + export APRUN_CALCFIMS="${launcher} -n 1" + export NTHREADS_SNOWANL=${NTHREADSmax} export APRUN_SNOWANL="${APRUN_default}" diff --git a/env/WCOSS2.env b/env/WCOSS2.env index c67c16f929..fff8f7b096 100755 --- a/env/WCOSS2.env +++ b/env/WCOSS2.env @@ -34,10 +34,6 @@ if [[ "${step}" = "prep" ]] || [[ "${step}" = "prepbufr" ]]; then export sys_tp="wcoss2" export launcher_PREP="mpiexec" -elif [[ "${step}" = "prepsnowobs" ]]; then - - export APRUN_CALCFIMS="${APRUN_default}" - elif [[ "${step}" = "prep_emissions" ]]; then export APRUN="${APRUN_default}" @@ -92,6 +88,8 @@ elif [[ "${step}" = "prepobsaero" ]]; then elif [[ "${step}" = "snowanl" ]]; then + export APRUN_CALCFIMS="${launcher} -n 1" + export NTHREADS_SNOWANL=${NTHREADSmax} export APRUN_SNOWANL="${APRUN_default}" diff --git a/jobs/JGLOBAL_PREP_SNOW_OBS b/jobs/JGLOBAL_PREP_SNOW_OBS deleted file mode 100755 index 0e3557697d..0000000000 --- a/jobs/JGLOBAL_PREP_SNOW_OBS +++ /dev/null @@ -1,50 +0,0 @@ -#! /usr/bin/env bash - -source "${HOMEgfs}/ush/preamble.sh" -export DATA=${DATA:-${DATAROOT}/${RUN}snowanl_${cyc}} -source "${HOMEgfs}/ush/jjob_header.sh" -e "prepsnowobs" -c "base prepsnowobs" - -############################################## -# Set variables used in the script -############################################## -# Ignore possible spelling error (nothing is misspelled) -# shellcheck disable=SC2153 -GDATE=$(date --utc +%Y%m%d%H -d "${PDY} ${cyc} - ${assim_freq} hours") -gPDY=${GDATE:0:8} -gcyc=${GDATE:8:2} -GDUMP="gdas" - -############################################## -# Begin JOB SPECIFIC work -############################################## -# Generate COM variables from templates -YMD=${PDY} HH=${cyc} declare_from_tmpl -rx COM_OBS - -RUN=${GDUMP} YMD=${gPDY} HH=${gcyc} declare_from_tmpl -rx \ - COM_ATMOS_RESTART_PREV:COM_ATMOS_RESTART_TMPL - -############################################################### -# Run relevant script -EXSCRIPT=${GDASSNOWPREPPY:-${SCRgfs}/exglobal_prep_snow_obs.py} -${EXSCRIPT} -status=$? -[[ ${status} -ne 0 ]] && (echo "FATAL ERROR: Error executing ${EXSCRIPT}, ABORT!"; exit "${status}") - -############################################## -# End JOB SPECIFIC work -############################################## - -############################################## -# Final processing -############################################## -if [[ -e "${pgmout}" ]] ; then - cat "${pgmout}" -fi - -########################################## -# Remove the Temporary working directory -########################################## -cd "${DATAROOT}" || exit 1 -[[ ${KEEPDATA} = "NO" ]] && rm -rf "${DATA}" - -exit 0 diff --git a/jobs/rocoto/prepsnowobs.sh b/jobs/rocoto/prepsnowobs.sh deleted file mode 100755 index 3f23bc16a5..0000000000 --- a/jobs/rocoto/prepsnowobs.sh +++ /dev/null @@ -1,26 +0,0 @@ -#! /usr/bin/env bash - -source "${HOMEgfs}/ush/preamble.sh" - -############################################################### -# Source UFSDA workflow modules -. "${HOMEgfs}/ush/load_ufsda_modules.sh" -status=$? -[[ ${status} -ne 0 ]] && exit "${status}" - -export job="prepsnowobs" -export jobid="${job}.$$" - -############################################################### -# setup python path for ioda utilities -# shellcheck disable=SC2311 -pyiodaPATH="${HOMEgfs}/sorc/gdas.cd/build/lib/python$(detect_py_ver)/" -gdasappPATH="${HOMEgfs}/sorc/gdas.cd/sorc/iodaconv/src:${pyiodaPATH}" -PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}:${gdasappPATH}" -export PYTHONPATH - -############################################################### -# Execute the JJOB -"${HOMEgfs}/jobs/JGLOBAL_PREP_SNOW_OBS" -status=$? -exit "${status}" diff --git a/jobs/rocoto/snowanl.sh b/jobs/rocoto/snowanl.sh index 97df7a46c7..cf1ddd688b 100755 --- a/jobs/rocoto/snowanl.sh +++ b/jobs/rocoto/snowanl.sh @@ -11,6 +11,14 @@ status=$? export job="snowanl" export jobid="${job}.$$" +############################################################### +# setup python path for ioda utilities +# shellcheck disable=SC2311 +pyiodaPATH="${HOMEgfs}/sorc/gdas.cd/build/lib/python$(detect_py_ver)/" +gdasappPATH="${HOMEgfs}/sorc/gdas.cd/sorc/iodaconv/src:${pyiodaPATH}" +PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}:${gdasappPATH}" +export PYTHONPATH + ############################################################### # Execute the JJOB "${HOMEgfs}/jobs/JGLOBAL_SNOW_ANALYSIS" diff --git a/parm/config/gfs/config.prepsnowobs b/parm/config/gfs/config.prepsnowobs deleted file mode 100644 index 20bdd89ddf..0000000000 --- a/parm/config/gfs/config.prepsnowobs +++ /dev/null @@ -1,18 +0,0 @@ -#! /usr/bin/env bash - -########## config.prepsnowobs ########## -# Snow Obs Prep specific - -echo "BEGIN: config.prepsnowobs" - -# Get task specific resources -. "${EXPDIR}/config.resources" prepsnowobs - -export IMS_OBS_LIST="${PARMgfs}/gdas/snow/prep/prep_ims.yaml.j2" - -export CALCFIMSEXE="${EXECgfs}/calcfIMS.exe" -export FIMS_NML_TMPL="${PARMgfs}/gdas/snow/prep/fims.nml.j2" - -export IMS2IODACONV="${USHgfs}/imsfv3_scf2ioda.py" - -echo "END: config.prepsnowobs" diff --git a/parm/config/gfs/config.resources b/parm/config/gfs/config.resources index 14e6f0d7fb..cddd1643fd 100644 --- a/parm/config/gfs/config.resources +++ b/parm/config/gfs/config.resources @@ -12,7 +12,7 @@ if (( $# != 1 )); then echo "Must specify an input task argument to set resource variables!" echo "argument can be any one of the following:" echo "stage_ic aerosol_init" - echo "prep prepsnowobs prepatmiodaobs" + echo "prep prepatmiodaobs" echo "atmanlinit atmanlvar atmanlfv3inc atmanlfinal" echo "atmensanlinit atmensanlobs atmensanlsol atmensanlletkf atmensanlfv3inc atmensanlfinal" echo "snowanl esnowrecen" @@ -152,13 +152,6 @@ case ${step} in memory="${mem_node_max}" ;; - "prepsnowobs") - walltime="00:05:00" - ntasks=1 - threads_per_task=1 - tasks_per_node=1 - ;; - "prepatmiodaobs") walltime="00:30:00" ntasks=1 diff --git a/parm/config/gfs/config.snowanl b/parm/config/gfs/config.snowanl index b1460dfa67..1aeaf58e46 100644 --- a/parm/config/gfs/config.snowanl +++ b/parm/config/gfs/config.snowanl @@ -19,6 +19,12 @@ export JEDIYAML="${PARMgfs}/gdas/snow/letkfoi/letkfoi.yaml.j2" export SNOWDEPTHVAR="snodl" export BESTDDEV="30." # Background Error Std. Dev. for LETKFOI +# Process IMS snowcover into snow depth +export IMS_OBS_LIST="${PARMgfs}/gdas/snow/prep/prep_ims.yaml.j2" +export CALCFIMSEXE="${EXECgfs}/calcfIMS.exe" +export FIMS_NML_TMPL="${PARMgfs}/gdas/snow/prep/fims.nml.j2" +export IMS2IODACONV="${USHgfs}/imsfv3_scf2ioda.py" + # Name of the executable that applies increment to bkg and its namelist template export APPLY_INCR_EXE="${EXECgfs}/apply_incr.exe" export APPLY_INCR_NML_TMPL="${PARMgfs}/gdas/snow/letkfoi/apply_incr_nml.j2" diff --git a/scripts/exglobal_prep_snow_obs.py b/scripts/exglobal_prep_snow_obs.py deleted file mode 100755 index aa1eb1bb7d..0000000000 --- a/scripts/exglobal_prep_snow_obs.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 -# exglobal_prep_snow_obs.py -# This script creates a SnowAnalysis object -# and runs the prepare_GTS and prepare_IMS method -# which perform the pre-processing for GTS and IMS data -import os - -from wxflow import Logger, cast_strdict_as_dtypedict -from pygfs.task.snow_analysis import SnowAnalysis - - -# Initialize root logger -logger = Logger(level=os.environ.get("LOGGING_LEVEL", "DEBUG"), colored_log=True) - - -if __name__ == '__main__': - - # Take configuration from environment and cast it as python dictionary - config = cast_strdict_as_dtypedict(os.environ) - - # Instantiate the snow prepare task - SnowAnl = SnowAnalysis(config) - if SnowAnl.task_config.cyc == 0: - SnowAnl.prepare_IMS() diff --git a/scripts/exglobal_snow_analysis.py b/scripts/exglobal_snow_analysis.py index fe050f5af5..dd52b699dc 100755 --- a/scripts/exglobal_snow_analysis.py +++ b/scripts/exglobal_snow_analysis.py @@ -19,6 +19,8 @@ # Instantiate the snow analysis task anl = SnowAnalysis(config) + if anl.task_config.cyc == 0: + anl.prepare_IMS() anl.initialize() anl.execute() anl.finalize() diff --git a/workflow/applications/gfs_cycled.py b/workflow/applications/gfs_cycled.py index f92bf95fba..e85e8b159f 100644 --- a/workflow/applications/gfs_cycled.py +++ b/workflow/applications/gfs_cycled.py @@ -113,7 +113,7 @@ def _get_app_configs(self): configs += ['prepobsaero'] if self.do_jedisnowda: - configs += ['prepsnowobs', 'snowanl'] + configs += ['snowanl'] if self.do_hybvar: configs += ['esnowrecen'] @@ -156,7 +156,7 @@ def get_task_names(self): gdas_gfs_common_tasks_before_fcst += ['sfcanl', 'analcalc'] if self.do_jedisnowda: - gdas_gfs_common_tasks_before_fcst += ['prepsnowobs', 'snowanl'] + gdas_gfs_common_tasks_before_fcst += ['snowanl'] wave_prep_tasks = ['waveinit', 'waveprep'] wave_bndpnt_tasks = ['wavepostbndpnt', 'wavepostbndpntbll'] diff --git a/workflow/rocoto/gfs_tasks.py b/workflow/rocoto/gfs_tasks.py index d3bb68a6b8..461241450e 100644 --- a/workflow/rocoto/gfs_tasks.py +++ b/workflow/rocoto/gfs_tasks.py @@ -563,34 +563,10 @@ def aeroanlfinal(self): return task - def prepsnowobs(self): - - deps = [] - dep_dict = {'type': 'task', 'name': f'{self.run}_prep'} - deps.append(rocoto.add_dependency(dep_dict)) - dependencies = rocoto.create_dependency(dep=deps) - - resources = self.get_resource('prepsnowobs') - task_name = f'{self.run}_prepsnowobs' - task_dict = {'task_name': task_name, - 'resources': resources, - 'dependency': dependencies, - 'envars': self.envars, - 'cycledef': self.run.replace('enkf', ''), - 'command': f'{self.HOMEgfs}/jobs/rocoto/prepsnowobs.sh', - 'job_name': f'{self.pslot}_{task_name}_@H', - 'log': f'{self.rotdir}/logs/@Y@m@d@H/{task_name}.log', - 'maxtries': '&MAXTRIES;' - } - - task = rocoto.create_task(task_dict) - - return task - def snowanl(self): deps = [] - dep_dict = {'type': 'task', 'name': f'{self.run}_prepsnowobs'} + dep_dict = {'type': 'task', 'name': f'{self.run}_prep'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep=deps) @@ -613,8 +589,6 @@ def snowanl(self): def esnowrecen(self): deps = [] - dep_dict = {'type': 'task', 'name': f'{self.run.replace("enkf","")}_prepsnowobs'} - deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'task', 'name': f'{self.run.replace("enkf","")}_snowanl'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': f'{self.run}_epmn', 'offset': f"-{timedelta_to_HMS(self._base['interval_gdas'])}"} diff --git a/workflow/rocoto/tasks.py b/workflow/rocoto/tasks.py index b989def13f..df56f90718 100644 --- a/workflow/rocoto/tasks.py +++ b/workflow/rocoto/tasks.py @@ -20,7 +20,7 @@ class Tasks: 'eobs', 'eomg', 'epos', 'esfc', 'eupd', 'atmensanlinit', 'atmensanlobs', 'atmensanlsol', 'atmensanlletkf', 'atmensanlfv3inc', 'atmensanlfinal', 'aeroanlinit', 'aeroanlvar', 'aeroanlfinal', 'aeroanlgenb', - 'prepsnowobs', 'snowanl', 'esnowrecen', + 'snowanl', 'esnowrecen', 'fcst', 'atmanlupp', 'atmanlprod', 'atmupp', 'goesupp', 'atmos_prod', 'ocean_prod', 'ice_prod', From 3f683979770bd89b3325e3a7ca77104e6e0477bb Mon Sep 17 00:00:00 2001 From: mingshichen-noaa <48537176+mingshichen-noaa@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:36:59 -0500 Subject: [PATCH 02/10] Update JGDAS ENKF ECEN job (#3050) NCO has requested that each COM variable specify whether it is an input or an output. This completes that process for the global jgdas enkf ecen job. Refs #2451 --- jobs/JGDAS_ENKF_ECEN | 6 ++--- scripts/exgdas_enkf_ecen.sh | 44 ++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/jobs/JGDAS_ENKF_ECEN b/jobs/JGDAS_ENKF_ECEN index 38bf847b38..71efd6ede9 100755 --- a/jobs/JGDAS_ENKF_ECEN +++ b/jobs/JGDAS_ENKF_ECEN @@ -29,13 +29,13 @@ export GPREFIX="${GDUMP}.t${gcyc}z." export GPREFIX_ENS="${GDUMP_ENS}.t${gcyc}z." RUN=${RUN/enkf} YMD=${PDY} HH=${cyc} declare_from_tmpl -rx \ - COM_ATMOS_ANALYSIS_DET:COM_ATMOS_ANALYSIS_TMPL + COMIN_ATMOS_ANALYSIS_DET:COM_ATMOS_ANALYSIS_TMPL MEMDIR="ensstat" YMD=${PDY} HH=${cyc} declare_from_tmpl -rx \ - COM_ATMOS_ANALYSIS_STAT:COM_ATMOS_ANALYSIS_TMPL + COMOUT_ATMOS_ANALYSIS_STAT:COM_ATMOS_ANALYSIS_TMPL MEMDIR="ensstat" RUN=${GDUMP} YMD=${gPDY} HH=${gcyc} declare_from_tmpl -rx \ - COM_ATMOS_HISTORY_STAT_PREV:COM_ATMOS_HISTORY_TMPL + COMIN_ATMOS_HISTORY_STAT_PREV:COM_ATMOS_HISTORY_TMPL ############################################################### diff --git a/scripts/exgdas_enkf_ecen.sh b/scripts/exgdas_enkf_ecen.sh index 442b0b04a1..f84df82875 100755 --- a/scripts/exgdas_enkf_ecen.sh +++ b/scripts/exgdas_enkf_ecen.sh @@ -117,37 +117,37 @@ for imem in $(seq 1 $NMEM_ENS); do memchar="mem"$(printf %03i $imem) MEMDIR=${memchar} YMD=${PDY} HH=${cyc} declare_from_tmpl -x \ - COM_ATMOS_ANALYSIS_MEM:COM_ATMOS_ANALYSIS_TMPL + COMOUT_ATMOS_ANALYSIS_MEM:COM_ATMOS_ANALYSIS_TMPL MEMDIR=${gmemchar} RUN=${GDUMP_ENS} YMD=${gPDY} HH=${gcyc} declare_from_tmpl -x \ - COM_ATMOS_HISTORY_MEM_PREV:COM_ATMOS_HISTORY_TMPL + COMIN_ATMOS_HISTORY_MEM_PREV:COM_ATMOS_HISTORY_TMPL - ${NLN} "${COM_ATMOS_HISTORY_MEM_PREV}/${GPREFIX_ENS}atmf00${FHR}${ENKF_SUFFIX}.nc" "./atmges_${memchar}" + ${NLN} "${COMIN_ATMOS_HISTORY_MEM_PREV}/${GPREFIX_ENS}atmf00${FHR}${ENKF_SUFFIX}.nc" "./atmges_${memchar}" if [ $DO_CALC_INCREMENT = "YES" ]; then if [ $FHR -eq 6 ]; then - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atmanl.nc" "./atmanl_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atmanl.nc" "./atmanl_${memchar}" else - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atma00${FHR}.nc" "./atmanl_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atma00${FHR}.nc" "./atmanl_${memchar}" fi fi - mkdir -p "${COM_ATMOS_ANALYSIS_MEM}" + mkdir -p "${COMOUT_ATMOS_ANALYSIS_MEM}" if [ $FHR -eq 6 ]; then - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atminc.nc" "./atminc_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atminc.nc" "./atminc_${memchar}" else - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atmi00${FHR}.nc" "./atminc_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}atmi00${FHR}.nc" "./atminc_${memchar}" fi if [[ $RECENTER_ENKF = "YES" ]]; then if [ $DO_CALC_INCREMENT = "YES" ]; then if [ $FHR -eq 6 ]; then - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratmanl.nc" "./ratmanl_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratmanl.nc" "./ratmanl_${memchar}" else - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratma00${FHR}.nc" "./ratmanl_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratma00${FHR}.nc" "./ratmanl_${memchar}" fi else if [ $FHR -eq 6 ]; then - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratminc.nc" "./ratminc_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratminc.nc" "./ratminc_${memchar}" else - ${NLN} "${COM_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratmi00${FHR}.nc" "./ratminc_${memchar}" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_MEM}/${APREFIX_ENS}ratmi00${FHR}.nc" "./ratminc_${memchar}" fi fi fi @@ -156,9 +156,9 @@ done if [ $DO_CALC_INCREMENT = "YES" ]; then # Link ensemble mean analysis if [ $FHR -eq 6 ]; then - ${NLN} "${COM_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atmanl.ensmean.nc" "./atmanl_ensmean" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atmanl.ensmean.nc" "./atmanl_ensmean" else - ${NLN} "${COM_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atma00${FHR}.ensmean.nc" "./atmanl_ensmean" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atma00${FHR}.ensmean.nc" "./atmanl_ensmean" fi # Compute ensemble mean analysis @@ -176,9 +176,9 @@ if [ $DO_CALC_INCREMENT = "YES" ]; then else # Link ensemble mean increment if [ $FHR -eq 6 ]; then - ${NLN} "${COM_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atminc.ensmean.nc" "./atminc_ensmean" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atminc.ensmean.nc" "./atminc_ensmean" else - ${NLN} "${COM_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atmi00${FHR}.ensmean.nc" "./atminc_ensmean" + ${NLN} "${COMOUT_ATMOS_ANALYSIS_STAT}/${APREFIX_ENS}atmi00${FHR}.ensmean.nc" "./atminc_ensmean" fi # Compute ensemble mean increment @@ -195,8 +195,8 @@ else export err=$?; err_chk # If available, link to ensemble mean guess. Otherwise, compute ensemble mean guess - if [[ -s "${COM_ATMOS_HISTORY_STAT_PREV}/${GPREFIX_ENS}atmf00${FHR}.ensmean.nc" ]]; then - ${NLN} "${COM_ATMOS_HISTORY_STAT_PREV}/${GPREFIX_ENS}atmf00${FHR}.ensmean.nc" "./atmges_ensmean" + if [[ -s "${COMIN_ATMOS_HISTORY_STAT_PREV}/${GPREFIX_ENS}atmf00${FHR}.ensmean.nc" ]]; then + ${NLN} "${COMIN_ATMOS_HISTORY_STAT_PREV}/${GPREFIX_ENS}atmf00${FHR}.ensmean.nc" "./atmges_ensmean" else DATAPATH="./" ATMGESNAME="atmges" @@ -231,11 +231,11 @@ if [ $RECENTER_ENKF = "YES" ]; then # GSI EnVar analysis if [ $FHR -eq 6 ]; then - ATMANL_GSI="${COM_ATMOS_ANALYSIS_DET}/${APREFIX}atmanl.nc" - ATMANL_GSI_ENSRES="${COM_ATMOS_ANALYSIS_DET}/${APREFIX}atmanl.ensres.nc" + ATMANL_GSI="${COMIN_ATMOS_ANALYSIS_DET}/${APREFIX}atmanl.nc" + ATMANL_GSI_ENSRES="${COMIN_ATMOS_ANALYSIS_DET}/${APREFIX}atmanl.ensres.nc" else - ATMANL_GSI="${COM_ATMOS_ANALYSIS_DET}/${APREFIX}atma00${FHR}.nc" - ATMANL_GSI_ENSRES="${COM_ATMOS_ANALYSIS_DET}/${APREFIX}atma00${FHR}.ensres.nc" + ATMANL_GSI="${COMIN_ATMOS_ANALYSIS_DET}/${APREFIX}atma00${FHR}.nc" + ATMANL_GSI_ENSRES="${COMIN_ATMOS_ANALYSIS_DET}/${APREFIX}atma00${FHR}.ensres.nc" fi # if we already have a ensemble resolution GSI analysis then just link to it From e6df3b361c5efdde0e16dbaf8751be77543a23e6 Mon Sep 17 00:00:00 2001 From: TerrenceMcGuinness-NOAA Date: Tue, 12 Nov 2024 14:18:09 +0000 Subject: [PATCH 03/10] CI GitHub pipeline (hotfix) update for fetching repo name (#3084) # Description Simple bug fix to GitHub CI pipeline script for Parallel Works. Introduced a bug on its last update for setting the repo name in advance of running the GitHub CLI command for getting the repo owner and branch name of PRs. # Type of change - [x] Bug fix (fixes something broken) - [ ] New feature (adds functionality) - [ ] Maintenance (code refactor, clean-up, new CI test, etc.) # Change characteristics - Is this a breaking change (a change in existing functionality)? NO - Does this change require a documentation update? NO - Does this change require an update to any of the following submodules? # How has this been tested? Gets tested when update to default repo gets checked in. --------- Co-authored-by: Terry McGuinness --- .github/workflows/pw_aws_ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pw_aws_ci.yaml b/.github/workflows/pw_aws_ci.yaml index f398ca4baf..6225b0503a 100644 --- a/.github/workflows/pw_aws_ci.yaml +++ b/.github/workflows/pw_aws_ci.yaml @@ -72,9 +72,9 @@ jobs: id: get-branch run: | pr_number=${{ github.event.inputs.pr_number }} + repo=${{ github.repository }} if [ "$pr_number" -eq "0" ]; then branch=${{ github.event.inputs.ref }} - repo=${{ github.repository }} else branch=$(gh pr view $pr_number --repo $repo --json headRefName --jq '.headRefName') repo_owner=$(gh pr view $pr_number --repo $repo --json headRepositoryOwner --jq '.headRepositoryOwner.login') From 18724999f9903b58bd1ba399e95286e521f643eb Mon Sep 17 00:00:00 2001 From: TerrenceMcGuinness-NOAA Date: Wed, 13 Nov 2024 03:21:51 -0500 Subject: [PATCH 04/10] Remove RUNDIRS before running CI cases to cover re-run events (#3076) This Pr removes **pslot** dir (with hash) from local archive folder on CI case completion and also adds `cleanup_experiment` function in the BASH utilities to clean up after a CI case runs. This change consolidates cleanup actions and adds functionality to clean the local archive folders. Key changes: - Fixes bug for cleaning up the `RUNDIRS` directory before new experiments are run in the case the pipeline is being ran for a second time - Updated `$HOMEgfs/ci/scripts/check_ci.sh` to use the new `cleanup_experiment` function under the BASH CI system. - Added `cleanup_experiment `function in `$HOMEgfs/ci/scripts/utils/ci_utils.sh` to handle various cleanups including: - COMROOT/EXPDIR per case - ARCDIR and ATADIR directories for archive - STMP/RUNDIRS/${PSLOT} - Added get config var from EXPDIR utility [get_config_var.py](https://github.com/NOAA-EMC/global-workflow/pull/2961/files#diff-b780733ae1d45917730364e09a6c510e79d4cc8cffad6d9020c7961c53b987bc) for getting config values in BASH Resolves #2954 Resolves #3066 --- ci/Jenkinsfile | 1 + ci/scripts/check_ci.sh | 3 +-- ci/scripts/utils/ci_utils.sh | 26 ++++++++++++++++-- ci/scripts/utils/get_config_var.py | 43 ++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100755 ci/scripts/utils/get_config_var.py diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile index 6a2e064be0..b3bd6a917a 100644 --- a/ci/Jenkinsfile +++ b/ci/Jenkinsfile @@ -222,6 +222,7 @@ pipeline { def build_system = yaml_case.experiment.system try { sh(script: "${HOMEgfs}/ci/scripts/run-check_ci.sh ${CUSTOM_WORKSPACE} ${pslot} ${build_system}") + sh(script: "${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh cleanup_experiment ${CUSTOM_WORKSPACE}/RUNTESTS/EXPDIR/${pslot}") } catch (Exception error_experment) { sh(script: "${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh cancel_batch_jobs ${pslot}") ws(CUSTOM_WORKSPACE) { diff --git a/ci/scripts/check_ci.sh b/ci/scripts/check_ci.sh index 825d8f5e8b..a89a661042 100755 --- a/ci/scripts/check_ci.sh +++ b/ci/scripts/check_ci.sh @@ -168,8 +168,7 @@ for pr in ${pr_list}; do fi if [[ "${rocoto_state}" == "DONE" ]]; then #Remove Experment cases that completed successfully - rm -Rf "${pslot_dir}" - rm -Rf "${pr_dir}/RUNTESTS/COMROOT/${pslot}" + "${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh" cleanup_experiment "${pslot_dir}" rm -f "${output_ci_single}" # echo "\`\`\`" > "${output_ci_single}" DATE=$(date +'%D %r') diff --git a/ci/scripts/utils/ci_utils.sh b/ci/scripts/utils/ci_utils.sh index 2a51467d38..56b0571adc 100755 --- a/ci/scripts/utils/ci_utils.sh +++ b/ci/scripts/utils/ci_utils.sh @@ -122,7 +122,9 @@ function create_experiment () { source "${HOMEgfs}/ci/platforms/config.${MACHINE_ID}" source "${HOMEgfs}/workflow/gw_setup.sh" - # Remove RUNDIRS dir incase this is a retry + # Remove RUNDIRS dir incase this is a retry (STMP now in host file) + STMP=$("${HOMEgfs}/ci/scripts/utils/parse_yaml.py" -y "${HOMEgfs}/workflow/hosts/${MACHINE_ID}.yaml" -k STMP -s) + echo "Removing ${STMP}/RUNDIRS/${pslot} directory incase this is a retry" rm -Rf "${STMP}/RUNDIRS/${pslot}" "${HOMEgfs}/${system}/workflow/create_experiment.py" --overwrite --yaml "${yaml_config}" @@ -137,7 +139,6 @@ function publish_logs() { local PR_header="$1" local dir_path="$2" local file="$3" - local full_paths="" while IFS= read -r line; do full_path="${dir_path}/${line}" @@ -155,3 +156,24 @@ function publish_logs() { fi echo "${URL}" } + +function cleanup_experiment() { + + local EXPDIR="$1" + local pslot + local ARCDIR + local ATARDIR + local COMROOT + + EXPDIR="$1" + pslot=$(basename "${EXPDIR}") + + # Use the Python utility to get the required variables + read -r ARCDIR ATARDIR STMP COMROOT < <("${HOMEgfs}/ci/scripts/utils/get_config_var.py" ARCDIR ATARDIR STMP COMROOT "${EXPDIR}") || true + + rm -Rf "${ARCDIR:?}" + rm -Rf "${ATARDIR:?}" + rm -Rf "${COMROOT}/${pslot:?}" + rm -Rf "${EXPDIR}/${pslot:?}" + rm -Rf "${STMP}/RUNDIRS/${pslot:?}" +} diff --git a/ci/scripts/utils/get_config_var.py b/ci/scripts/utils/get_config_var.py new file mode 100755 index 0000000000..84559b8c14 --- /dev/null +++ b/ci/scripts/utils/get_config_var.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import os +import argparse +from wxflow import Configuration + + +def get_config_vars(var_names, config_path): + """ + GET_CONFIG_VARS Get configuration variables from a config file or directory. + Parameters: + var_names (list of str): The names of the configuration variables to retrieve. + config_path (str): The path to the configuration file or directory. + Returns: + list of str: The values of the specified configuration variables. + """ + if os.path.isfile(config_path): + config_dir = os.path.dirname(config_path) + config_file = os.path.basename(config_path) + elif os.path.isdir(config_path): + config_dir = config_path + config_file = 'config.base' + config = Configuration(config_dir) + config_data = config.parse_config(config_file) + return [config_data[var_name] for var_name in var_names] + + +if __name__ == "__main__": + """ + Main entry point for the script. + Parses command-line arguments and retrieves the specified configuration variables. + """ + parser = argparse.ArgumentParser(description="Get configuration variables from a config file or directory.") + parser.add_argument("var_names", nargs='+', help="The names of the configuration variables to retrieve.") + parser.add_argument("config_path", help="The path to the configuration file or directory.") + + args = parser.parse_args() + + var_names = args.var_names + config_path = args.config_path + + values = get_config_vars(var_names, config_path) + print(" ".join(values)) From 3e78e586da58b9be7fc0caa2cb4437bffa337522 Mon Sep 17 00:00:00 2001 From: David Huber <69919478+DavidHuber-NOAA@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:46:02 -0500 Subject: [PATCH 05/10] Move machine-based options from config.base to host files (#3053) This moves all machine-specific options to the workflow/hosts files from the config.* files. This also turns HPSS archiving on for WCOSS2 by default. This turns OFF UFS DA tests on WCOSS2. Resolves #2942 Resolves #3087 --------- Co-authored-by: Walter Kolczynski - NOAA Co-authored-by: Rahul Mahajan --- ci/cases/pr/C96C48_ufs_hybatmDA.yaml | 2 +- ci/cases/yamls/ufs_hybatmDA_defaults.ci.yaml | 1 - modulefiles/module_gwsetup.wcoss2.lua | 3 + parm/archive/gfs_downstream.yaml.j2 | 4 +- parm/config/gefs/config.base | 13 +-- parm/config/gfs/config.aero | 30 +------ parm/config/gfs/config.base | 20 +---- parm/config/gfs/config.prepoceanobs | 7 +- parm/config/gfs/yaml/defaults.yaml | 7 +- workflow/generate_workflows.sh | 2 + workflow/hosts/awspw.yaml | 4 + workflow/hosts/azurepw.yaml | 1 + workflow/hosts/gaea.yaml | 1 + workflow/hosts/googlepw.yaml | 1 + workflow/hosts/hera.yaml | 1 + workflow/hosts/hercules.yaml | 3 + workflow/hosts/jet.yaml | 1 + workflow/hosts/orion.yaml | 3 + workflow/hosts/s4.yaml | 1 + workflow/hosts/wcoss2.yaml | 1 + workflow/setup_expt.py | 89 +++++++++++++------- 21 files changed, 100 insertions(+), 95 deletions(-) diff --git a/ci/cases/pr/C96C48_ufs_hybatmDA.yaml b/ci/cases/pr/C96C48_ufs_hybatmDA.yaml index 41a8baa725..031054079a 100644 --- a/ci/cases/pr/C96C48_ufs_hybatmDA.yaml +++ b/ci/cases/pr/C96C48_ufs_hybatmDA.yaml @@ -21,4 +21,4 @@ skip_ci_on_hosts: - gaea - orion - hercules - + - wcoss2 diff --git a/ci/cases/yamls/ufs_hybatmDA_defaults.ci.yaml b/ci/cases/yamls/ufs_hybatmDA_defaults.ci.yaml index c4fa54dcc8..b956066f6b 100644 --- a/ci/cases/yamls/ufs_hybatmDA_defaults.ci.yaml +++ b/ci/cases/yamls/ufs_hybatmDA_defaults.ci.yaml @@ -17,4 +17,3 @@ nsst: NST_MODEL: "1" sfcanl: DONST: "NO" - diff --git a/modulefiles/module_gwsetup.wcoss2.lua b/modulefiles/module_gwsetup.wcoss2.lua index a2440569db..86bdad3c56 100644 --- a/modulefiles/module_gwsetup.wcoss2.lua +++ b/modulefiles/module_gwsetup.wcoss2.lua @@ -4,5 +4,8 @@ Load environment to run GFS workflow ci scripts on WCOSS2 prepend_path("MODULEPATH", "/apps/ops/test/nco/modulefiles/core") load(pathJoin("rocoto","1.3.5")) +load(pathJoin("PrgEnv-intel")) +load(pathJoin("intel","19.1.3.304")) +load(pathJoin("python", "3.8.6")) whatis("Description: GFS run setup environment") diff --git a/parm/archive/gfs_downstream.yaml.j2 b/parm/archive/gfs_downstream.yaml.j2 index ed5317b42c..94bdd1df56 100644 --- a/parm/archive/gfs_downstream.yaml.j2 +++ b/parm/archive/gfs_downstream.yaml.j2 @@ -6,7 +6,7 @@ gfs_downstream: - "{{ COMIN_ATMOS_GEMPAK | relpath(ROTDIR) }}/gfs_{{ cycle_YMDH }}.sfc" - "{{ COMIN_ATMOS_GEMPAK | relpath(ROTDIR) }}/gfs_{{ cycle_YMDH }}.snd" {% for i in range(1, NUM_SND_COLLECTIVES) %} - - "{{ COMIN_ATMOS_WMO | relpath(ROTDIR) }}/gfs_collective{{ i }}.postsnd_{{ cycle_HH }}" + - "{{ COMIN_ATMOS_BUFR | relpath(ROTDIR) }}/gfs_collective{{ i }}.fil" {% endfor %} - - "{{ COMIN_ATMOS_BUFR | relpath(ROTDIR) }}/bufr.t{{ cycle_HH }}z" + - "{{ COMIN_ATMOS_BUFR | relpath(ROTDIR) }}/bufr.??????.{{ cycle_YMDH }}" - "{{ COMIN_ATMOS_BUFR | relpath(ROTDIR) }}/gfs.t{{ cycle_HH }}z.bufrsnd.tar.gz" diff --git a/parm/config/gefs/config.base b/parm/config/gefs/config.base index 05aabaa323..e769015937 100644 --- a/parm/config/gefs/config.base +++ b/parm/config/gefs/config.base @@ -210,12 +210,11 @@ case "${APP}" in if [[ "${APP}" =~ ^S2SW ]]; then export DO_WAVE="YES" export WAVE_RUN="both" - export cplwav2atm=".true." fi ;; *) - echo "Unrecognized APP: ${1}" - exit 1 + echo "FATAL ERROR: Unrecognized APP: '${APP}'" + exit 2 ;; esac @@ -327,7 +326,7 @@ export HPSSARCH="@HPSSARCH@" # save data to HPSS archive export LOCALARCH="@LOCALARCH@" # save data to local archive if [[ ${HPSSARCH} = "YES" ]] && [[ ${LOCALARCH} = "YES" ]]; then echo "Both HPSS and local archiving selected. Please choose one or the other." - exit 2 + exit 3 fi export ARCH_CYC=00 # Archive data at this cycle for warm_start capability export ARCH_WARMICFREQ=4 # Archive frequency in days for warm_start capability @@ -338,10 +337,4 @@ export DELETE_COM_IN_ARCHIVE_JOB="YES" # NO=retain ROTDIR. YES default in arc # Number of regional collectives to create soundings for export NUM_SND_COLLECTIVES=${NUM_SND_COLLECTIVES:-9} -# The tracker, genesis, and METplus jobs are not supported on CSPs yet -# TODO: we should place these in workflow/hosts/[aws|azure|google]pw.yaml as part of CSP's setup, not for general. -if [[ "${machine}" =~ "PW" ]]; then - export DO_WAVE="NO" -fi - echo "END: config.base" diff --git a/parm/config/gfs/config.aero b/parm/config/gfs/config.aero index f49593a439..bbfb782636 100644 --- a/parm/config/gfs/config.aero +++ b/parm/config/gfs/config.aero @@ -2,36 +2,8 @@ # UFS-Aerosols settings -# Turn off warnings about unused variables -# shellcheck disable=SC2034 - - # Path to the input data tree -case ${machine} in - "HERA") - AERO_INPUTS_DIR="/scratch1/NCEPDEV/global/glopara/data/gocart_emissions" - ;; - "ORION" | "HERCULES") - AERO_INPUTS_DIR="/work2/noaa/global/wkolczyn/noscrub/global-workflow/gocart_emissions" - ;; - "S4") - AERO_INPUTS_DIR="/data/prod/glopara/gocart_emissions" - ;; - "WCOSS2") - AERO_INPUTS_DIR="/lfs/h2/emc/global/noscrub/emc.global/data/gocart_emissions" - ;; - "GAEA") - AERO_INPUTS_DIR="/gpfs/f5/epic/proj-shared/global/glopara/data/gocart_emissions" - ;; - "JET") - AERO_INPUTS_DIR="/lfs5/HFIP/hfv3gfs/glopara/data/gocart_emissions" - ;; - *) - echo "FATAL ERROR: Machine ${machine} unsupported for aerosols" - exit 2 - ;; -esac -export AERO_INPUTS_DIR +export AERO_INPUTS_DIR=@AERO_INPUTS_DIR@ export AERO_DIAG_TABLE="${PARMgfs}/ufs/fv3/diag_table.aero" export AERO_FIELD_TABLE="${PARMgfs}/ufs/fv3/field_table.aero" diff --git a/parm/config/gfs/config.base b/parm/config/gfs/config.base index ccb05abe88..4f702f9668 100644 --- a/parm/config/gfs/config.base +++ b/parm/config/gfs/config.base @@ -260,7 +260,7 @@ case "${APP}" in ;; *) echo "Unrecognized APP: '${APP}'" - exit 1 + exit 2 ;; esac @@ -462,7 +462,7 @@ export HPSSARCH="@HPSSARCH@" # save data to HPSS archive export LOCALARCH="@LOCALARCH@" # save data to local archive if [[ ${HPSSARCH} = "YES" ]] && [[ ${LOCALARCH} = "YES" ]]; then echo "Both HPSS and local archiving selected. Please choose one or the other." - exit 2 + exit 3 fi export ARCH_CYC=00 # Archive data at this cycle for warm_start capability export ARCH_WARMICFREQ=4 # Archive frequency in days for warm_start capability @@ -483,20 +483,4 @@ export OFFSET_START_HOUR=0 # Number of regional collectives to create soundings for export NUM_SND_COLLECTIVES=${NUM_SND_COLLECTIVES:-9} -# The tracker, genesis, and METplus jobs are not supported on CSPs yet -# TODO: we should place these in workflow/hosts/awspw.yaml as part of AWS/AZURE setup, not for general. -if [[ "${machine}" =~ "PW" ]]; then - export DO_TRACKER="NO" - export DO_GENESIS="NO" - export DO_METP="NO" - export DO_WAVE="NO" -fi - -# The tracker and genesis are not installed on Orion/Hercules yet; this requires spack-stack builds of the package. -# TODO: we should place these in workflow/hosts/[orion|hercules].yaml. -if [[ "${machine}" == "ORION" || "${machine}" == "HERCULES" ]]; then - export DO_TRACKER="NO" - export DO_GENESIS="NO" -fi - echo "END: config.base" diff --git a/parm/config/gfs/config.prepoceanobs b/parm/config/gfs/config.prepoceanobs index 0963a5c42d..3aeff8a3de 100644 --- a/parm/config/gfs/config.prepoceanobs +++ b/parm/config/gfs/config.prepoceanobs @@ -14,7 +14,12 @@ export OBS_LIST=@SOCA_OBS_LIST@ export OBS_YAML=${OBS_LIST} # ocean analysis needs own dmpdir until standard dmpdir has full ocean obs -export DMPDIR=@DMPDIR@ +use_exp_obs="@use_exp_obs@" +if [[ "${use_exp_obs}" == "YES" ]]; then + dmpdir_exp="@dmpdir_exp@" +fi + +export DMPDIR="${dmpdir_exp:-${DMPDIR}}" # For BUFR2IODA json and python scripts export JSON_TMPL_DIR="${PARMgfs}/gdas/ioda/bufr2ioda" diff --git a/parm/config/gfs/yaml/defaults.yaml b/parm/config/gfs/yaml/defaults.yaml index dfc67d1237..78caf46f5d 100644 --- a/parm/config/gfs/yaml/defaults.yaml +++ b/parm/config/gfs/yaml/defaults.yaml @@ -61,4 +61,9 @@ prepoceanobs: SOCA_INPUT_FIX_DIR: "${FIXgfs}/gdas/soca/72x35x25/soca" SOCA_OBS_LIST: "${PARMgfs}/gdas/soca/obs/obs_list.yaml" # TODO: This is also repeated in ocnanal OBSPREP_YAML: "${PARMgfs}/gdas/soca/obsprep/obsprep_config.yaml" - DMPDIR: "${BASE_DATA}/experimental_obs" + use_exp_obs: "YES" + dmpdir_exp: "${BASE_DATA}/experimental_obs" + +# config.aero has just a system-specific path to add. +# This is handled by the setup_expt.py, but it has to be told to write to it. +aero: {} diff --git a/workflow/generate_workflows.sh b/workflow/generate_workflows.sh index c2565140d3..6a4cb9910a 100755 --- a/workflow/generate_workflows.sh +++ b/workflow/generate_workflows.sh @@ -150,10 +150,12 @@ while [[ $# -gt 0 && "$1" != "--" ]]; do :) echo "[${BASH_SOURCE[0]}]: ${option} requires an argument" _usage + exit 1 ;; *) echo "[${BASH_SOURCE[0]}]: Unrecognized option: ${option}" _usage + exit 1 ;; esac done diff --git a/workflow/hosts/awspw.yaml b/workflow/hosts/awspw.yaml index ef17d8f2f4..b98c838faa 100644 --- a/workflow/hosts/awspw.yaml +++ b/workflow/hosts/awspw.yaml @@ -24,4 +24,8 @@ LOCALARCH: 'NO' ATARDIR: '' # TODO: This will not yet work from AWS. MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' +DO_TRACKER: 'NO' +DO_GENESIS: 'NO' +DO_METP: 'NO' +SUPPORT_WAVES: 'NO' SUPPORTED_RESOLUTIONS: ['C48', 'C96'] # TODO: Test and support all cubed-sphere resolutions. diff --git a/workflow/hosts/azurepw.yaml b/workflow/hosts/azurepw.yaml index 1769f9ee19..4725e28962 100644 --- a/workflow/hosts/azurepw.yaml +++ b/workflow/hosts/azurepw.yaml @@ -24,4 +24,5 @@ LOCALARCH: 'NO' ATARDIR: '' # TODO: This will not yet work from AZURE. MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' +SUPPORT_WAVES: 'NO' SUPPORTED_RESOLUTIONS: ['C48', 'C96'] # TODO: Test and support all cubed-sphere resolutions. diff --git a/workflow/hosts/gaea.yaml b/workflow/hosts/gaea.yaml index 5a37b5dabf..23ac75ca03 100644 --- a/workflow/hosts/gaea.yaml +++ b/workflow/hosts/gaea.yaml @@ -26,3 +26,4 @@ ATARDIR: '${NOSCRUB}/archive_rotdir/${PSLOT}' MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' SUPPORTED_RESOLUTIONS: ['C1152', 'C768', 'C384', 'C192', 'C96', 'C48'] +AERO_INPUTS_DIR: /gpfs/f5/epic/proj-shared/global/glopara/data/gocart_emissions diff --git a/workflow/hosts/googlepw.yaml b/workflow/hosts/googlepw.yaml index daf6cd1eb2..1b979b6bc9 100644 --- a/workflow/hosts/googlepw.yaml +++ b/workflow/hosts/googlepw.yaml @@ -24,4 +24,5 @@ LOCALARCH: 'NO' ATARDIR: '' # TODO: This will not yet work from GOOGLE. MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' +SUPPORT_WAVES: 'NO' SUPPORTED_RESOLUTIONS: ['C48', 'C96'] # TODO: Test and support all cubed-sphere resolutions. diff --git a/workflow/hosts/hera.yaml b/workflow/hosts/hera.yaml index fa2c351aa1..e9e749ad3c 100644 --- a/workflow/hosts/hera.yaml +++ b/workflow/hosts/hera.yaml @@ -28,3 +28,4 @@ SUPPORTED_RESOLUTIONS: ['C1152', 'C768', 'C384', 'C192', 'C96', 'C48'] COMINecmwf: /scratch1/NCEPDEV/global/glopara/data/external_gempak/ecmwf COMINnam: /scratch1/NCEPDEV/global/glopara/data/external_gempak/nam COMINukmet: /scratch1/NCEPDEV/global/glopara/data/external_gempak/ukmet +AERO_INPUTS_DIR: /scratch1/NCEPDEV/global/glopara/data/gocart_emissions diff --git a/workflow/hosts/hercules.yaml b/workflow/hosts/hercules.yaml index 73fde6cde6..f528761cf1 100644 --- a/workflow/hosts/hercules.yaml +++ b/workflow/hosts/hercules.yaml @@ -24,7 +24,10 @@ LOCALARCH: 'NO' ATARDIR: '${NOSCRUB}/archive_rotdir/${PSLOT}' MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' +DO_TRACKER: 'NO' +DO_GENESIS: 'NO' SUPPORTED_RESOLUTIONS: ['C1152', 'C768', 'C384', 'C192', 'C96', 'C48'] COMINecmwf: /work/noaa/global/glopara/data/external_gempak/ecmwf COMINnam: /work/noaa/global/glopara/data/external_gempak/nam COMINukmet: /work/noaa/global/glopara/data/external_gempak/ukmet +AERO_INPUTS_DIR: /work2/noaa/global/wkolczyn/noscrub/global-workflow/gocart_emissions diff --git a/workflow/hosts/jet.yaml b/workflow/hosts/jet.yaml index a53224fe52..737e2e7f94 100644 --- a/workflow/hosts/jet.yaml +++ b/workflow/hosts/jet.yaml @@ -28,3 +28,4 @@ SUPPORTED_RESOLUTIONS: ['C384', 'C192', 'C96', 'C48'] COMINecmwf: /mnt/lfs5/HFIP/hfv3gfs/glopara/data/external_gempak/ecmwf COMINnam: /mnt/lfs5/HFIP/hfv3gfs/glopara/data/external_gempak/nam COMINukmet: /mnt/lfs5/HFIP/hfv3gfs/glopara/data/external_gempak/ukmet +AERO_INPUTS_DIR: /lfs5/HFIP/hfv3gfs/glopara/data/gocart_emissions diff --git a/workflow/hosts/orion.yaml b/workflow/hosts/orion.yaml index d47b2b2bab..985c24c6fb 100644 --- a/workflow/hosts/orion.yaml +++ b/workflow/hosts/orion.yaml @@ -24,7 +24,10 @@ LOCALARCH: 'NO' ATARDIR: '${NOSCRUB}/archive_rotdir/${PSLOT}' MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' +DO_TRACKER: 'NO' +DO_GENESIS: 'NO' SUPPORTED_RESOLUTIONS: ['C1152', 'C768', 'C384', 'C192', 'C96', 'C48'] COMINecmwf: /work/noaa/global/glopara/data/external_gempak/ecmwf COMINnam: /work/noaa/global/glopara/data/external_gempak/nam COMINukmet: /work/noaa/global/glopara/data/external_gempak/ukmet +AERO_INPUTS_DIR: /work2/noaa/global/wkolczyn/noscrub/global-workflow/gocart_emissions diff --git a/workflow/hosts/s4.yaml b/workflow/hosts/s4.yaml index b93fefec39..2e77c112b1 100644 --- a/workflow/hosts/s4.yaml +++ b/workflow/hosts/s4.yaml @@ -25,3 +25,4 @@ ATARDIR: '${NOSCRUB}/archive_rotdir/${PSLOT}' MAKE_NSSTBUFR: 'YES' MAKE_ACFTBUFR: 'YES' SUPPORTED_RESOLUTIONS: ['C384', 'C192', 'C96', 'C48'] +AERO_INPUTS_DIR: /data/prod/glopara/gocart_emissions diff --git a/workflow/hosts/wcoss2.yaml b/workflow/hosts/wcoss2.yaml index 15f0705f91..4fb4b1d64a 100644 --- a/workflow/hosts/wcoss2.yaml +++ b/workflow/hosts/wcoss2.yaml @@ -28,3 +28,4 @@ SUPPORTED_RESOLUTIONS: ['C1152', 'C768', 'C384', 'C192', 'C96', 'C48'] COMINecmwf: /lfs/h2/emc/global/noscrub/emc.global/data/external_gempak/ecmwf COMINnam: /lfs/h2/emc/global/noscrub/emc.global/data/external_gempak/nam COMINukmet: /lfs/h2/emc/global/noscrub/emc.global/data/external_gempak/ukmet +AERO_INPUTS_DIR: /lfs/h2/emc/global/noscrub/emc.global/data/gocart_emissions diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index f32203e600..27da4943d3 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -7,7 +7,6 @@ import os import glob import shutil -import warnings from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter, SUPPRESS, ArgumentTypeError from hosts import Host @@ -29,7 +28,7 @@ def makedirs_if_missing(dirname): os.makedirs(dirname) -def fill_EXPDIR(inputs): +def fill_expdir(inputs): """ Method to copy config files from workflow to experiment directory INPUTS: @@ -50,18 +49,31 @@ def fill_EXPDIR(inputs): def update_configs(host, inputs): def _update_defaults(dict_in: dict) -> dict: + # Given an input dict_in of the form + # {defaults: {config_name: {var1: value1, ...}, }, config_name: {var1: value1, ...}} + # Replace values in ['defaults']['config_name']['var1'] with ['config_name']['var1'] + # and return the ['defaults'] subdictionary as its own new dictionary. defaults = dict_in.pop('defaults', AttrDict()) + if 'defaults' in defaults: + _update_defaults(defaults) defaults.update(dict_in) return defaults - # Read in the YAML file to fill out templates and override host defaults + # Convert the inputs to an AttrDict data = AttrDict(host.info, **inputs.__dict__) + + # Read in the YAML file to fill out templates data.HOMEgfs = _top yaml_path = inputs.yaml if not os.path.exists(yaml_path): - raise IOError(f'YAML file does not exist, check path:' + yaml_path) - yaml_dict = _update_defaults(AttrDict(parse_j2yaml(yaml_path, data))) + raise FileNotFoundError(f'YAML file does not exist, check path: {yaml_path}') + yaml_dict = parse_j2yaml(yaml_path, data) + + # yaml_dict is in the form {defaults: {key1: val1, ...}, base: {key1: val1, ...}, ...} + # _update_defaults replaces any keys/values in defaults with matching keys in base + yaml_dict = _update_defaults(yaml_dict) + # Override the YAML defaults with the host-specific capabilities # First update config.base edit_baseconfig(host, inputs, yaml_dict) @@ -73,7 +85,7 @@ def _update_defaults(dict_in: dict) -> dict: stage_dict = dict(stage_dict, **host_dict) stage_input = f'{inputs.configdir}/config.stage_ic' stage_output = f'{inputs.expdir}/{inputs.pslot}/config.stage_ic' - edit_config(stage_input, stage_output, stage_dict) + edit_config(stage_input, stage_output, host_dict, stage_dict) # Loop over other configs and update them with defaults for cfg in yaml_dict.keys(): @@ -81,7 +93,7 @@ def _update_defaults(dict_in: dict) -> dict: continue cfg_file = f'{inputs.expdir}/{inputs.pslot}/config.{cfg}' cfg_dict = get_template_dict(yaml_dict[cfg]) - edit_config(cfg_file, cfg_file, cfg_dict) + edit_config(cfg_file, cfg_file, host_dict, cfg_dict) return @@ -92,20 +104,19 @@ def edit_baseconfig(host, inputs, yaml_dict): to `EXPDIR/pslot/config.base` """ - tmpl_dict = { + # Create base_dict which holds templated variables to be written to config.base + base_dict = { "@HOMEgfs@": _top, "@MACHINE@": host.machine.upper()} - # Replace host related items - extend_dict = get_template_dict(host.info) - tmpl_dict = dict(tmpl_dict, **extend_dict) - if inputs.start in ["warm"]: is_warm_start = ".true." elif inputs.start in ["cold"]: is_warm_start = ".false." + else: + raise ValueError(f"Invalid start type: {inputs.start}") - extend_dict = dict() + # Construct a dictionary from user inputs extend_dict = { "@PSLOT@": inputs.pslot, "@SDATE@": datetime_to_YMDH(inputs.idate), @@ -121,35 +132,37 @@ def edit_baseconfig(host, inputs, yaml_dict): "@APP@": inputs.app, "@NMEM_ENS@": getattr(inputs, 'nens', 0) } - tmpl_dict = dict(tmpl_dict, **extend_dict) - extend_dict = dict() if getattr(inputs, 'nens', 0) > 0: - extend_dict = { - "@CASEENS@": f'C{inputs.resensatmos}', - } - tmpl_dict = dict(tmpl_dict, **extend_dict) + extend_dict['@CASEENS@'] = f'C{inputs.resensatmos}' - extend_dict = dict() if inputs.mode in ['cycled']: - extend_dict = { - "@DOHYBVAR@": "YES" if inputs.nens > 0 else "NO", - } - tmpl_dict = dict(tmpl_dict, **extend_dict) + extend_dict["@DOHYBVAR@"] = "YES" if inputs.nens > 0 else "NO" - try: - tmpl_dict = dict(tmpl_dict, **get_template_dict(yaml_dict['base'])) - except KeyError: - pass + # Further extend/redefine base_dict with extend_dict + base_dict = dict(base_dict, **extend_dict) + + # Add/override 'base'-specific declarations in base_dict + if 'base' in yaml_dict: + base_dict = dict(base_dict, **get_template_dict(yaml_dict['base'])) base_input = f'{inputs.configdir}/config.base' base_output = f'{inputs.expdir}/{inputs.pslot}/config.base' - edit_config(base_input, base_output, tmpl_dict) + edit_config(base_input, base_output, host.info, base_dict) return -def edit_config(input_config, output_config, config_dict): +def edit_config(input_config, output_config, host_info, config_dict): + """ + Given a templated input_config filename, parse it based on config_dict and + host_info and write it out to the output_config filename. + """ + + # Override defaults with machine-specific capabilties + # e.g. some machines are not able to run metp jobs + host_dict = get_template_dict(host_info) + config_dict = dict(config_dict, **host_dict) # Read input config with open(input_config, 'rt') as fi: @@ -173,9 +186,17 @@ def edit_config(input_config, output_config, config_dict): def get_template_dict(input_dict): + # Reads a templated input dictionary and updates the output + output_dict = dict() + for key, value in input_dict.items(): - output_dict[f'@{key}@'] = value + # In some cases, the same config may be templated twice + # Prevent adding additional "@"s + if "@" in key: + output_dict[f'{key}'] = value + else: + output_dict[f'@{key}@'] = value return output_dict @@ -350,6 +371,7 @@ def query_and_clean(dirname, force_clean=False): def validate_user_request(host, inputs): supp_res = host.info['SUPPORTED_RESOLUTIONS'] + supp_waves = host.info.get('SUPPORT_WAVES', 'YES') machine = host.machine for attr in ['resdetatmos', 'resensatmos']: try: @@ -359,6 +381,9 @@ def validate_user_request(host, inputs): if expt_res not in supp_res: raise NotImplementedError(f"Supported resolutions on {machine} are:\n{', '.join(supp_res)}") + if "W" in inputs.app and supp_waves == "NO": + raise NotImplementedError(f"Waves are not supported on {machine}") + def get_ocean_resolution(resdetatmos): """ @@ -397,7 +422,7 @@ def main(*argv): if create_expdir: makedirs_if_missing(expdir) - fill_EXPDIR(user_inputs) + fill_expdir(user_inputs) update_configs(host, user_inputs) print(f"*" * 100) From 57c8aa36ed6ae9a32797b0af0a8029fd843b99e3 Mon Sep 17 00:00:00 2001 From: Neil Barton <103681022+NeilBarton-NOAA@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:57:17 -0500 Subject: [PATCH 06/10] SFS Runs at C96mx100 (#2960) Adds the ability to run C96mx100 with SFS options. Included in this PR is the ability to use the interpolated/coarse grained 1 degree MOM6 ICs. Staging the ATM and OCN perturbation files without using the +03 replay ICs. --------- Co-authored-by: Eric.Sinsky Co-authored-by: Walter Kolczynski - NOAA Co-authored-by: Rahul Mahajan --- .gitignore | 1 + ci/cases/sfs/C96mx100_S2S.yaml | 19 + ci/cases/yamls/gefs_defaults_ci.yaml | 8 + ci/cases/yamls/gefs_replay_ci.yaml | 4 + ci/cases/yamls/sfs_defaults.yaml | 33 ++ parm/config/gefs/config.atmos_products | 13 +- parm/config/gefs/config.base | 5 +- parm/config/gefs/config.efcs | 6 +- parm/config/gefs/config.fcst | 20 +- parm/config/gefs/config.stage_ic | 4 + parm/config/gefs/config.ufs | 11 +- parm/config/gefs/yaml/defaults.yaml | 13 + parm/config/gfs/config.ocn | 9 +- parm/config/gfs/config.stage_ic | 4 + parm/product/sfs.0p25.f000.paramlist.a.txt | 39 ++ parm/product/sfs.0p25.fFFF.paramlist.a.txt | 52 ++ parm/product/sfs.0p25.fFFF.paramlist.b.txt | 549 +++++++++++++++++++++ parm/stage/master_gefs.yaml.j2 | 4 +- scripts/exglobal_stage_ic.py | 3 +- sorc/build_all.sh | 10 +- sorc/build_ufs.sh | 5 +- sorc/link_workflow.sh | 2 +- ush/forecast_det.sh | 4 + ush/forecast_predet.sh | 13 +- ush/parsing_namelists_FV3.sh | 2 +- 25 files changed, 803 insertions(+), 30 deletions(-) create mode 100644 ci/cases/sfs/C96mx100_S2S.yaml create mode 100644 ci/cases/yamls/sfs_defaults.yaml create mode 100644 parm/product/sfs.0p25.f000.paramlist.a.txt create mode 100644 parm/product/sfs.0p25.fFFF.paramlist.a.txt create mode 100644 parm/product/sfs.0p25.fFFF.paramlist.b.txt diff --git a/.gitignore b/.gitignore index 4ec62993d3..49fb3f438a 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,7 @@ parm/post/params_grib2_tbl_new parm/post/post_tag_gfs128 parm/post/gfs parm/post/gefs +parm/post/sfs parm/post/ocean.csv parm/post/ice.csv parm/post/ocnicepost.nml.jinja2 diff --git a/ci/cases/sfs/C96mx100_S2S.yaml b/ci/cases/sfs/C96mx100_S2S.yaml new file mode 100644 index 0000000000..6bdb9a4887 --- /dev/null +++ b/ci/cases/sfs/C96mx100_S2S.yaml @@ -0,0 +1,19 @@ +experiment: + system: gefs + mode: forecast-only + +arguments: + idate: 1994050100 + edate: 1994050100 + pslot: {{ 'pslot' | getenv }} + app: S2S + resdetatmos: 96 + resensatmos: 96 + resdetocean: 1 + start: 'cold' + nens: 10 + comroot: {{ 'RUNTESTS' | getenv }}/COMROOT + expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR + icsdir: {{ 'TOPICDIR' | getenv }}/HR4/C96mx100 + yaml: {{ HOMEgfs }}/ci/cases/yamls/sfs_defaults.yaml + diff --git a/ci/cases/yamls/gefs_defaults_ci.yaml b/ci/cases/yamls/gefs_defaults_ci.yaml index 05a97edd90..a06aed638a 100644 --- a/ci/cases/yamls/gefs_defaults_ci.yaml +++ b/ci/cases/yamls/gefs_defaults_ci.yaml @@ -2,3 +2,11 @@ defaults: !INC {{ HOMEgfs }}/parm/config/gefs/yaml/defaults.yaml base: ACCOUNT: {{ 'HPC_ACCOUNT' | getenv }} + SFS_POST: "NO" + FHOUT_GFS: 6 +stage_ic: + USE_OCN_ENS_PERTURB_FILES: "NO" + USE_ATM_ENS_PERTURB_FILES: "NO" +ocn: + MOM6_INTERP_ICS: "NO" + diff --git a/ci/cases/yamls/gefs_replay_ci.yaml b/ci/cases/yamls/gefs_replay_ci.yaml index dfbd9ae065..b1155aade1 100644 --- a/ci/cases/yamls/gefs_replay_ci.yaml +++ b/ci/cases/yamls/gefs_replay_ci.yaml @@ -11,4 +11,8 @@ base: FHOUT_OCN_GFS: 24 FHOUT_ICE_GFS: 24 HOMEDIR: {{ 'RUNTESTS' | getenv }}/GLOBAL + SFS_POST: "NO" +stage_ic: + USE_OCN_ENS_PERTURB_FILES: "YES" + USE_ATM_ENS_PERTURB_FILES: "YES" diff --git a/ci/cases/yamls/sfs_defaults.yaml b/ci/cases/yamls/sfs_defaults.yaml new file mode 100644 index 0000000000..b1de60ce71 --- /dev/null +++ b/ci/cases/yamls/sfs_defaults.yaml @@ -0,0 +1,33 @@ +base: + DO_JEDIATMVAR: "NO" + DO_JEDIATMENS: "NO" + DO_JEDIOCNVAR: "NO" + DO_JEDISNOWDA: "NO" + DO_MERGENSST: "NO" + DO_BUFRSND: "NO" + DO_GEMPAK: "NO" + DO_AWIPS: "NO" + KEEPDATA: "YES" + DO_EXTRACTVARS: "NO" + FHMAX_GFS: 2976 + FHMAX_HF_GFS: 0 + FHOUT_HF_GFS: 1 + FHOUT_GFS: 24 + FHOUT_OCN_GFS: 24 + FHOUT_ICE_GFS: 24 + FCST_BREAKPOINTS: "" + REPLAY_ICS: "NO" + HPSSARCH: "NO" + LOCALARCH: "NO" + SFS_POST: "YES" + ACCOUNT: {{ 'HPC_ACCOUNT' | getenv }} +fcst: + TYPE: "hydro" + MONO: "mono" + reforecast: "YES" + FHZER: 24 +stage_ic: + USE_OCN_ENS_PERTURB_FILES: "YES" + USE_ATM_ENS_PERTURB_FILES: "YES" +ocn: + MOM6_INTERP_ICS: "YES" diff --git a/parm/config/gefs/config.atmos_products b/parm/config/gefs/config.atmos_products index 73614ba08e..e8aae324e1 100644 --- a/parm/config/gefs/config.atmos_products +++ b/parm/config/gefs/config.atmos_products @@ -25,9 +25,14 @@ fi export FLXGF="NO" # Create interpolated sflux.1p00 file # paramlist files for the different forecast hours and downsets -export paramlista="${PARMgfs}/product/gefs.0p25.fFFF.paramlist.a.txt" -export paramlista_anl="${PARMgfs}/product/gefs.0p25.anl.paramlist.a.txt" -export paramlista_f000="${PARMgfs}/product/gefs.0p25.f000.paramlist.a.txt" -export paramlistb="${PARMgfs}/product/gefs.0p25.fFFF.paramlist.b.txt" +if [[ ${SFS_POST} == "YES" ]]; then + export post_prefix='sfs' +else + export post_prefix='gefs' +fi +export paramlista="${PARMgfs}/product/${post_prefix}.0p25.fFFF.paramlist.a.txt" +export paramlista_anl="${PARMgfs}/product/${post_prefix}.0p25.anl.paramlist.a.txt" +export paramlista_f000="${PARMgfs}/product/${post_prefix}.0p25.f000.paramlist.a.txt" +export paramlistb="${PARMgfs}/product/${post_prefix}.0p25.fFFF.paramlist.b.txt" echo "END: config.atmos_products" diff --git a/parm/config/gefs/config.base b/parm/config/gefs/config.base index e769015937..13f286c494 100644 --- a/parm/config/gefs/config.base +++ b/parm/config/gefs/config.base @@ -64,6 +64,7 @@ export REALTIME="YES" # Experiment mode (cycled or forecast-only) export MODE="@MODE@" # cycled/forecast-only +export SFS_POST="@SFS_POST@" # TODO, place holder until RUN=SFS is developed #################################################### # DO NOT ADD MACHINE DEPENDENT STUFF BELOW THIS LINE @@ -237,7 +238,6 @@ else export OFFSET_START_HOUR=0 fi - # GFS output and frequency export FHMIN_GFS=0 export FHMAX_GFS="@FHMAX_GFS@" @@ -245,7 +245,7 @@ export FHMAX_GFS="@FHMAX_GFS@" breakpnts="@FCST_BREAKPOINTS@" export FCST_SEGMENTS="${FHMIN_GFS},${breakpnts:+${breakpnts},}${FHMAX_GFS}" -export FHOUT_GFS=6 +export FHOUT_GFS=@FHOUT_GFS@ export FHMAX_HF_GFS=@FHMAX_HF_GFS@ export FHOUT_HF_GFS=@FHOUT_HF_GFS@ export FHOUT_OCN_GFS=@FHOUT_OCN_GFS@ @@ -295,6 +295,7 @@ export ENSMEM=${ENSMEM:-"000"} export MEMDIR="mem${ENSMEM}" export DOIAU="NO" # While we are not doing IAU, we may want to warm start w/ IAU in the future + # Check if cycle is warm starting with IAU if [[ "${EXP_WARM_START}" = ".true." ]]; then if [[ "${DOIAU}" = "YES" ]]; then diff --git a/parm/config/gefs/config.efcs b/parm/config/gefs/config.efcs index 0086121450..27d7be235d 100644 --- a/parm/config/gefs/config.efcs +++ b/parm/config/gefs/config.efcs @@ -27,7 +27,11 @@ source "${EXPDIR}/config.ufs" ${string} source "${EXPDIR}/config.resources" efcs # Stochastic physics parameters (only for ensemble forecasts) -export DO_SKEB="YES" +if [[ "${CASE}" == "C96" ]] ; then + export DO_SKEB="NO" # SKEB turned off for C96 +else + export DO_SKEB="YES" # SKEB turned on for all other resolutions +fi export SKEB="0.8,-999,-999,-999,-999" export SKEB_TAU="2.16E4,2.592E5,2.592E6,7.776E6,3.1536E7" export SKEB_LSCALE="500.E3,1000.E3,2000.E3,2000.E3,2000.E3" diff --git a/parm/config/gefs/config.fcst b/parm/config/gefs/config.fcst index b2a9c10afe..c600c8edbf 100644 --- a/parm/config/gefs/config.fcst +++ b/parm/config/gefs/config.fcst @@ -44,6 +44,7 @@ export FHOUT=${FHOUT_GFS} export FHOUT_HF=${FHOUT_HF_GFS} export FHOUT_OCN=${FHOUT_OCN_GFS} export FHOUT_ICE=${FHOUT_ICE_GFS} +export FHZER=@FHZER@ # Get task specific resources source "${EXPDIR}/config.resources" fcst @@ -66,8 +67,8 @@ export FCSTEXEC="ufs_model.x" ####################################################################### # Model configuration -export TYPE="nh" -export MONO="non-mono" +export TYPE=@TYPE@ +export MONO=@MONO@ # Use stratosphere h2o physics export h2o_phys=".true." @@ -201,6 +202,11 @@ case ${imp_physics} in export hord_xx_nh_nonmono=5 export vtdm4_nh_nonmono=0.02 export nord=2 + if [[ "${TYPE}" == "nh"* ]]; then + export dddmp=0.1 + else + export dddmp=0. + fi export dddmp=0.1 export d4_bg=0.12 ;; @@ -221,7 +227,11 @@ case ${imp_physics} in export vtdm4_nh_nonmono=0.02 export nord=2 export d4_bg=0.12 - export dddmp=0.1 + if [[ "${TYPE}" == "nh"* ]]; then + export dddmp=0.1 + else + export dddmp=0. + fi ;; *) echo "Unknown microphysics option, ABORT!" ;; esac @@ -268,9 +278,7 @@ else export io_layout="1,1" fi -if (( OFFSET_START_HOUR != 0 )); then - export reforecast="YES" -fi +export reforecast=@reforecast@ # Remember config.efcs will over-ride these values for ensemble forecasts # if these variables are re-defined there. # Otherwise, the ensemble forecast will inherit from config.fcst diff --git a/parm/config/gefs/config.stage_ic b/parm/config/gefs/config.stage_ic index cac65c74b9..92c6bb2a9b 100644 --- a/parm/config/gefs/config.stage_ic +++ b/parm/config/gefs/config.stage_ic @@ -30,4 +30,8 @@ if [[ -z "${ICSDIR}" ]] ; then fi +#use of perturbations files for ensembles +export USE_OCN_ENS_PERTURB_FILES=@USE_OCN_ENS_PERTURB_FILES@ +export USE_ATM_ENS_PERTURB_FILES=@USE_ATM_ENS_PERTURB_FILES@ + echo "END: config.stage_ic" diff --git a/parm/config/gefs/config.ufs b/parm/config/gefs/config.ufs index bc3950490e..5b7ba4c0af 100644 --- a/parm/config/gefs/config.ufs +++ b/parm/config/gefs/config.ufs @@ -80,7 +80,7 @@ case "${fv3_res}" in export nthreads_fv3_gfs=1 export nthreads_ufs=1 export nthreads_ufs_gfs=1 - export xr_cnvcld=.false. # Do not pass conv. clouds to Xu-Randall cloud fraction + export xr_cnvcld=".true." # Pass conv. clouds to Xu-Randall cloud fraction export cdmbgwd="0.071,2.1,1.0,1.0" # mountain blocking, ogwd, cgwd, cgwd src scaling export cdmbgwd_gsl="40.0,1.77,1.0,1.0" # settings for GSL drag suite export k_split=1 @@ -98,13 +98,13 @@ case "${fv3_res}" in export DELTIM=600 export layout_x=2 export layout_y=2 - export layout_x_gfs=2 - export layout_y_gfs=2 + export layout_x_gfs=4 + export layout_y_gfs=4 export nthreads_fv3=1 export nthreads_fv3_gfs=1 export nthreads_ufs=1 export nthreads_ufs_gfs=1 - export xr_cnvcld=".false." # Do not pass conv. clouds to Xu-Randall cloud fraction + export xr_cnvcld=".true." # Pass conv. clouds to Xu-Randall cloud fraction export cdmbgwd="0.14,1.8,1.0,1.0" # mountain blocking, ogwd, cgwd, cgwd src scaling export cdmbgwd_gsl="20.0,2.5,1.0,1.0" # settings for GSL drag suite export knob_ugwp_tauamp=3.0e-3 # setting for UGWPv1 non-stationary GWD @@ -131,6 +131,7 @@ case "${fv3_res}" in export cdmbgwd="0.23,1.5,1.0,1.0" # mountain blocking, ogwd, cgwd, cgwd src scaling export cdmbgwd_gsl="10.0,3.5,1.0,1.0" # settings for GSL drag suite export knob_ugwp_tauamp=1.5e-3 # setting for UGWPv1 non-stationary GWD + export xr_cnvcld=".true." # Pass conv. clouds to Xu-Randall cloud fraction export k_split=2 export n_split=4 export tau=6.0 @@ -337,7 +338,7 @@ if [[ "${skip_mom6}" == "false" ]]; then DT_THERM_MOM6='3600' FRUNOFF="runoff.daitren.clim.1deg.nc" CHLCLIM="seawifs_1998-2006_smoothed_2X.nc" - MOM6_RESTART_SETTING='r' + MOM6_RESTART_SETTING=${MOM6_RESTART_SETTING:-'r'} MOM6_RIVER_RUNOFF='False' eps_imesh="2.5e-1" TOPOEDITS="ufs.topo_edits_011818.nc" diff --git a/parm/config/gefs/yaml/defaults.yaml b/parm/config/gefs/yaml/defaults.yaml index b5870b3e7e..f0e8772b67 100644 --- a/parm/config/gefs/yaml/defaults.yaml +++ b/parm/config/gefs/yaml/defaults.yaml @@ -15,7 +15,20 @@ base: FCST_BREAKPOINTS: "48" REPLAY_ICS: "NO" USE_OCN_PERTURB_FILES: "false" + FHOUT_GFS: 6 FHOUT_OCN_GFS: 6 FHOUT_ICE_GFS: 6 HPSSARCH: "NO" LOCALARCH: "NO" + SFS_POST: "NO" +fcst: + reforecast: "NO" + FHZER: 6 + TYPE: "nh" + MONO: "non-mono" +stage_ic: + USE_OCN_ENS_PERTURB_FILES: "NO" + USE_ATM_ENS_PERTURB_FILES: "NO" +ocn: + MOM6_INTERP_ICS: "NO" + diff --git a/parm/config/gfs/config.ocn b/parm/config/gfs/config.ocn index 317a76e58a..f7c7559f9a 100644 --- a/parm/config/gfs/config.ocn +++ b/parm/config/gfs/config.ocn @@ -24,6 +24,11 @@ else export ODA_INCUPD_NHOURS="3.0" fi - - +MOM6_INTERP_ICS=@MOM6_INTERP_ICS@ +if [[ "${MOM6_INTERP_ICS}" == "YES" ]]; then + export MOM6_RESTART_SETTING='n' + export MOM6_WARMSTART_FILE="MOM.res.nc" + export MOM6_INIT_FROM_Z='False' + export MOM6_INIT_UV='file' +fi echo "END: config.ocn" diff --git a/parm/config/gfs/config.stage_ic b/parm/config/gfs/config.stage_ic index 7aa0c25f32..d0113fac63 100644 --- a/parm/config/gfs/config.stage_ic +++ b/parm/config/gfs/config.stage_ic @@ -29,4 +29,8 @@ if [[ -z "${ICSDIR}" ]] ; then fi +#use of perturbations files for ensembles +export USE_OCN_ENS_PERTURB_FILES="NO" +export USE_ATM_ENS_PERTURB_FILES="NO" + echo "END: config.stage_ic" diff --git a/parm/product/sfs.0p25.f000.paramlist.a.txt b/parm/product/sfs.0p25.f000.paramlist.a.txt new file mode 100644 index 0000000000..4fdb8f9713 --- /dev/null +++ b/parm/product/sfs.0p25.f000.paramlist.a.txt @@ -0,0 +1,39 @@ +:HGT:surface: +:PRMSL:mean sea level: +:PRES:surface: +:TMP:2 m above ground: +:TMAX:2 m above ground: +:TMIN:2 m above ground: +:RH:2 m above ground: +:DPT:2 m above ground: +:UGRD:10 m above ground: +:VGRD:10 m above ground: +:APCP:surface: +:CRAIN:surface: +:CSNOW:surface: +:CFRZR:surface: +:CICEP:surface: +:PWAT:entire atmosphere (considered as a single layer): +:CAPE:180-0 mb above ground: +:CAPE:surface: +:CIN:180-0 mb above ground: +:CIN:surface: +:CPOFP:surface: +:HLCY:3000-0 m above ground: +:TCDC:entire atmosphere: +:WEASD:surface: +:SNOD:surface: +:ULWRF:top of atmosphere: +:DSWRF:surface: +:DLWRF:surface: +:USWRF:surface: +:ULWRF:surface: +:GUST:surface: +:SHTFL:surface: +:LHTFL:surface: +:ICETK:surface: +:TSOIL:0-0.1 +:SOILW:0-0.1 +:MSLET:mean sea level: +:VIS:surface: +:HGT:cloud ceiling: diff --git a/parm/product/sfs.0p25.fFFF.paramlist.a.txt b/parm/product/sfs.0p25.fFFF.paramlist.a.txt new file mode 100644 index 0000000000..7e5497f1d2 --- /dev/null +++ b/parm/product/sfs.0p25.fFFF.paramlist.a.txt @@ -0,0 +1,52 @@ +:PRMSL:mean sea level: +:PRES:surface: +:TMP:surface: +:TMP:2 m above ground: +:TMP:850 mb: +:TMP:500 mb: +:TMP:200 mb: +:TMAX:2 m above ground: +:TMIN:2 m above ground: +:RH:2 m above ground: +:DPT:2 m above ground: +:UGRD:10 m above ground: +:UGRD:850 mb: +:UGRD:500 mb: +:UGRD:200 mb: +:VGRD:10 m above ground: +:VGRD:850 mb: +:VGRD:500 mb: +:VGRD:200 mb: +:HGT:850 mb: +:HGT:500 mb: +:HGT:200 mb: +:APCP:surface: +:CRAIN:surface: +:CSNOW:surface: +:CFRZR:surface: +:CICEP:surface: +:FDNSSTMP:surface: +:PWAT:entire atmosphere (considered as a single layer): +:CAPE:180-0 mb above ground: +:CAPE:surface: +:CIN:180-0 mb above ground: +:CIN:surface: +:HLCY:3000-0 m above ground: +:TCDC:entire atmosphere (considered as a single layer): +:WEASD:surface: +:SNOD:surface: +:ULWRF:top of atmosphere: +:DSWRF:surface: +:CPOFP:surface: +:DLWRF:surface: +:USWRF:surface: +:ULWRF:surface: +:GUST:surface: +:SHTFL:surface: +:LHTFL:surface: +:ICETK:surface: +:TSOIL:0-0.1 +:SOILW:0-0.1 +:MSLET:mean sea level: +:VIS:surface: +:HGT:cloud ceiling: diff --git a/parm/product/sfs.0p25.fFFF.paramlist.b.txt b/parm/product/sfs.0p25.fFFF.paramlist.b.txt new file mode 100644 index 0000000000..3a44c0e042 --- /dev/null +++ b/parm/product/sfs.0p25.fFFF.paramlist.b.txt @@ -0,0 +1,549 @@ +############################# sorted pgrb2a + pgrb2b 201502 +:4LFTX:surface: +:5WAVH:500 mb: +:ABSV:1000 mb: +:ABSV:100 mb: +:ABSV:10 mb: +:ABSV:150 mb: +:ABSV:200 mb: +:ABSV:20 mb: +:ABSV:250 mb: +:ABSV:300 mb: +:ABSV:30 mb: +:ABSV:350 mb: +:ABSV:400 mb: +:ABSV:450 mb: +:ABSV:500 mb: +:ABSV:50 mb: +:ABSV:550 mb: +:ABSV:600 mb: +:ABSV:650 mb: +:ABSV:700 mb: +:ABSV:70 mb: +:ABSV:750 mb: +:ABSV:800 mb: +:ABSV:850 mb: +:ABSV:900 mb: +:ABSV:925 mb: +:ABSV:950 mb: +:ABSV:975 mb: +:ACPCP:surface: +:ALBDO:surface: +:BRTMP:top of atmosphere: +:CAPE:255-0 mb above ground: +:CDUVB:surface: +:CIN:255-0 mb above ground: +:CLWMR:1000 mb: +:CLWMR:100 mb: +:CLWMR:10 mb: +:CLWMR:150 mb: +:CLWMR:200 mb: +:CLWMR:20 mb: +:CLWMR:250 mb: +:CLWMR:300 mb: +:CLWMR:30 mb: +:CLWMR:350 mb: +:CLWMR:400 mb: +:CLWMR:450 mb: +:CLWMR:500 mb: +:CLWMR:50 mb: +:CLWMR:550 mb: +:CLWMR:600 mb: +:CLWMR:650 mb: +:CLWMR:700 mb: +:CLWMR:70 mb: +:CLWMR:750 mb: +:CLWMR:800 mb: +:CLWMR:850 mb: +:CLWMR:900 mb: +:CLWMR:925 mb: +:CLWMR:950 mb: +:CLWMR:975 mb: +:CNWAT:surface: +:CPRAT:surface: +:CWAT:entire atmosphere (considered as a single layer): +:CWORK:entire atmosphere (considered as a single layer): +:DPT:30-0 mb above ground: +:DUVB:surface: +:FLDCP:surface: +:FRICV:surface: +:GFLUX:surface: +:HGT:0C isotherm: +:HGT:1000 mb: +:HGT:100 mb: +:HGT:10 mb: +:HGT:1 mb: +:HGT:150 mb: +:HGT:200 mb: +:HGT:20 mb: +:HGT:2 mb: +:HGT:250 mb: +:HGT:300 mb: +:HGT:30 mb: +:HGT:3 mb: +:HGT:350 mb: +:HGT:400 mb: +:HGT:450 mb: +:HGT:500 mb: +:HGT:50 mb: +:HGT:5 mb: +:HGT:550 mb: +:HGT:600 mb: +:HGT:650 mb: +:HGT:700 mb: +:HGT:70 mb: +:HGT:7 mb: +:HGT:750 mb: +:HGT:800 mb: +:HGT:850 mb: +:HGT:900 mb: +:HGT:925 mb: +:HGT:950 mb: +:HGT:975 mb: +:HGT:highest tropospheric freezing level: +:HGT:max wind: +:HGT:PV=-1.5e-06 (Km^2/kg/s) surface: +:HGT:PV=1.5e-06 (Km^2/kg/s) surface: +:HGT:PV=-1e-06 (Km^2/kg/s) surface: +:HGT:PV=1e-06 (Km^2/kg/s) surface: +:HGT:PV=-2e-06 (Km^2/kg/s) surface: +:HGT:PV=2e-06 (Km^2/kg/s) surface: +:HGT:PV=-5e-07 (Km^2/kg/s) surface: +:HGT:PV=5e-07 (Km^2/kg/s) surface: +:HGT:surface: +:HGT:tropopause: +:HINDEX:surface: +:HPBL:surface: +:ICAHT:max wind: +:ICAHT:tropopause: +:ICEC:surface: +:ICIP:300 mb: +:ICIP:400 mb: +:ICIP:500 mb: +:ICIP:600 mb: +:ICIP:700 mb: +:ICIP:800 mb: +:ICSEV:300 mb: +:ICSEV:400 mb: +:ICSEV:500 mb: +:ICSEV:600 mb: +:ICSEV:700 mb: +:ICSEV:800 mb: +:LAND:surface: +:LFTX:surface: +:MNTSF:320 K isentropic level: +:NCPCP:surface: +:O3MR:100 mb: +:O3MR:10 mb: +:O3MR:125 mb: +:O3MR:150 mb: +:O3MR:1 mb: +:O3MR:200 mb: +:O3MR:20 mb: +:O3MR:250 mb: +:O3MR:2 mb: +:O3MR:300 mb: +:O3MR:30 mb: +:O3MR:350 mb: +:O3MR:3 mb: +:O3MR:400 mb: +:O3MR:50 mb: +:O3MR:5 mb: +:O3MR:70 mb: +:O3MR:7 mb: +:PEVPR:surface: +:PLI:30-0 mb above ground: +:PLPL:255-0 mb above ground: +:POT:0.995 sigma level: +:PRATE:surface: +:PRES:80 m above ground: +:PRES:convective cloud bottom level: +:PRES:convective cloud top level: +:PRES:high cloud bottom level: +:PRES:high cloud top level: +:PRES:low cloud bottom level: +:PRES:low cloud top level: +:PRES:max wind: +:PRES:mean sea level: +:PRES:middle cloud bottom level: +:PRES:middle cloud top level: +:PRES:PV=-1.5e-06 (Km^2/kg/s) surface: +:PRES:PV=1.5e-06 (Km^2/kg/s) surface: +:PRES:PV=-1e-06 (Km^2/kg/s) surface: +:PRES:PV=1e-06 (Km^2/kg/s) surface: +:PRES:PV=-2e-06 (Km^2/kg/s) surface: +:PRES:PV=2e-06 (Km^2/kg/s) surface: +:PRES:PV=-5e-07 (Km^2/kg/s) surface: +:PRES:PV=5e-07 (Km^2/kg/s) surface: +:PRES:tropopause: +:PVORT:310 K isentropic level: +:PVORT:320 K isentropic level: +:PVORT:350 K isentropic level: +:PVORT:450 K isentropic level: +:PVORT:550 K isentropic level: +:PVORT:650 K isentropic level: +:PWAT:30-0 mb above ground: +:RH:0.33-1 sigma layer: +:RH:0.44-0.72 sigma layer: +:RH:0.44-1 sigma layer: +:RH:0.72-0.94 sigma layer: +:RH:0.995 sigma level: +:RH:0C isotherm: +:RH:1000 mb: +:RH:100 mb: +:RH:10 mb: +:RH:120-90 mb above ground: +:RH:150-120 mb above ground: +:RH:150 mb: +:RH:180-150 mb above ground: +:RH:200 mb: +:RH:20 mb: +:RH:250 mb: +:RH:300 mb: +:RH:30-0 mb above ground: +:RH:30 mb: +:RH:350 mb: +:RH:400 mb: +:RH:450 mb: +:RH:500 mb: +:RH:50 mb: +:RH:550 mb: +:RH:600 mb: +:RH:60-30 mb above ground: +:RH:650 mb: +:RH:700 mb: +:RH:70 mb: +:RH:750 mb: +:RH:800 mb: +:RH:850 mb: +:RH:900 mb: +:RH:90-60 mb above ground: +:RH:925 mb: +:RH:950 mb: +:RH:975 mb: +:RH:entire atmosphere (considered as a single layer): +:RH:highest tropospheric freezing level: +:SFCR:surface: +:SNOWC:surface: +:SNOHF:surface: +:SOILL:0-0.1 m below ground: +:SOILL:0.1-0.4 m below ground: +:SOILL:0.4-1 m below ground: +:SOILL:1-2 m below ground: +:SOILW:0.1-0.4 m below ground: +:SOILW:0.4-1 m below ground: +:SOILW:1-2 m below ground: +:SPFH:1000 mb: +:SPFH:100 mb: +:SPFH:10 mb: +:SPFH:1 mb: +:SPFH:120-90 mb above ground: +:SPFH:150-120 mb above ground: +:SPFH:150 mb: +:SPFH:180-150 mb above ground: +:SPFH:200 mb: +:SPFH:20 mb: +:SPFH:2 mb: +:SPFH:250 mb: +:SPFH:2 m above ground: +:SPFH:300 mb: +:SPFH:30-0 mb above ground: +:SPFH:30 mb: +:SPFH:3 mb: +:SPFH:350 mb: +:SPFH:400 mb: +:SPFH:450 mb: +:SPFH:500 mb: +:SPFH:50 mb: +:SPFH:5 mb: +:SPFH:550 mb: +:SPFH:600 mb: +:SPFH:60-30 mb above ground: +:SPFH:650 mb: +:SPFH:700 mb: +:SPFH:70 mb: +:SPFH:7 mb: +:SPFH:750 mb: +:SPFH:800 mb: +:SPFH:80 m above ground: +:SPFH:850 mb: +:SPFH:900 mb: +:SPFH:90-60 mb above ground: +:SPFH:925 mb: +:SPFH:950 mb: +:SPFH:975 mb: +:SUNSD:surface: +:TCDC:475 mb: +:TCDC:boundary layer cloud layer: +:TCDC:convective cloud layer: +:TCDC:high cloud layer: +:TCDC:low cloud layer: +:TCDC:middle cloud layer: +:TMP:0.995 sigma level: +:TMP:1000 mb: +:TMP:100 m above ground: +:TMP:100 mb: +:TMP:10 mb: +:TMP:1 mb: +:TMP:120-90 mb above ground: +:TMP:150-120 mb above ground: +:TMP:150 mb: +:TMP:180-150 mb above ground: +:TMP:1829 m above mean sea level: +:TMP:200 mb: +:TMP:20 mb: +:TMP:2 mb: +:TMP:250 mb: +:TMP:2743 m above mean sea level: +:TMP:300 mb: +:TMP:30-0 mb above ground: +:TMP:305 m above mean sea level: +:TMP:30 mb: +:TMP:3 mb: +:TMP:320 K isentropic level: +:TMP:350 mb: +:TMP:3658 m above mean sea level: +:TMP:400 mb: +:TMP:450 K isentropic level: +:TMP:450 mb: +:TMP:4572 m above mean sea level: +:TMP:457 m above mean sea level: +:TMP:500 mb: +:TMP:50 mb: +:TMP:5 mb: +:TMP:550 K isentropic level: +:TMP:550 mb: +:TMP:600 mb: +:TMP:60-30 mb above ground: +:TMP:610 m above mean sea level: +:TMP:650 K isentropic level: +:TMP:650 mb: +:TMP:700 mb: +:TMP:70 mb: +:TMP:7 mb: +:TMP:750 mb: +:TMP:800 mb: +:TMP:80 m above ground: +:TMP:850 mb: +:TMP:900 mb: +:TMP:90-60 mb above ground: +:TMP:914 m above mean sea level: +:TMP:925 mb: +:TMP:950 mb: +:TMP:975 mb: +:TMP:high cloud top level: +:TMP:low cloud top level: +:TMP:max wind: +:TMP:middle cloud top level: +:TMP:PV=-1.5e-06 (Km^2/kg/s) surface: +:TMP:PV=1.5e-06 (Km^2/kg/s) surface: +:TMP:PV=-1e-06 (Km^2/kg/s) surface: +:TMP:PV=1e-06 (Km^2/kg/s) surface: +:TMP:PV=-2e-06 (Km^2/kg/s) surface: +:TMP:PV=2e-06 (Km^2/kg/s) surface: +:TMP:PV=-5e-07 (Km^2/kg/s) surface: +:TMP:PV=5e-07 (Km^2/kg/s) surface: +:TMP:tropopause: +:TOZNE:entire atmosphere (considered as a single layer): +:TSNOWP:surface: +:TSOIL:0.1-0.4 m below ground: +:TSOIL:0.4-1 m below ground: +:TSOIL:1-2 m below ground: +:UFLX:surface: +:UGRD:0.995 sigma level: +:UGRD:1000 mb: +:UGRD:100 m above ground: +:UGRD:100 mb: +:UGRD:10 mb: +:UGRD:1 mb: +:UGRD:120-90 mb above ground: +:UGRD:150-120 mb above ground: +:UGRD:150 mb: +:UGRD:180-150 mb above ground: +:UGRD:1829 m above mean sea level: +:UGRD:20 mb: +:UGRD:2 mb: +:UGRD:250 mb: +:UGRD:2743 m above mean sea level: +:UGRD:300 mb: +:UGRD:30-0 mb above ground: +:UGRD:305 m above mean sea level: +:UGRD:30 mb: +:UGRD:3 mb: +:UGRD:320 K isentropic level: +:UGRD:350 mb: +:UGRD:3658 m above mean sea level: +:UGRD:400 mb: +:UGRD:450 K isentropic level: +:UGRD:450 mb: +:UGRD:4572 m above mean sea level: +:UGRD:457 m above mean sea level: +:UGRD:50 mb: +:UGRD:5 mb: +:UGRD:550 K isentropic level: +:UGRD:550 mb: +:UGRD:600 mb: +:UGRD:60-30 mb above ground: +:UGRD:610 m above mean sea level: +:UGRD:650 K isentropic level: +:UGRD:650 mb: +:UGRD:700 mb: +:UGRD:70 mb: +:UGRD:7 mb: +:UGRD:750 mb: +:UGRD:800 mb: +:UGRD:80 m above ground: +:UGRD:900 mb: +:UGRD:90-60 mb above ground: +:UGRD:914 m above mean sea level: +:UGRD:925 mb: +:UGRD:950 mb: +:UGRD:975 mb: +:UGRD:max wind: +:UGRD:planetary boundary layer: +:UGRD:PV=-1.5e-06 (Km^2/kg/s) surface: +:UGRD:PV=1.5e-06 (Km^2/kg/s) surface: +:UGRD:PV=-1e-06 (Km^2/kg/s) surface: +:UGRD:PV=1e-06 (Km^2/kg/s) surface: +:UGRD:PV=-2e-06 (Km^2/kg/s) surface: +:UGRD:PV=2e-06 (Km^2/kg/s) surface: +:UGRD:PV=-5e-07 (Km^2/kg/s) surface: +:UGRD:PV=5e-07 (Km^2/kg/s) surface: +:UGRD:tropopause: +:U-GWD:surface: +:USTM:6000-0 m above ground: +:USWRF:top of atmosphere: +:APTMP:2 m above ground +:VFLX:surface: +:VGRD:0.995 sigma level: +:VGRD:1000 mb: +:VGRD:100 m above ground: +:VGRD:100 mb: +:VGRD:10 mb: +:VGRD:1 mb: +:VGRD:120-90 mb above ground: +:VGRD:150-120 mb above ground: +:VGRD:150 mb: +:VGRD:180-150 mb above ground: +:VGRD:1829 m above mean sea level: +:VGRD:200 mb: +:VGRD:20 mb: +:VGRD:2 mb: +:VGRD:250 mb: +:VGRD:2743 m above mean sea level: +:VGRD:300 mb: +:VGRD:30-0 mb above ground: +:VGRD:305 m above mean sea level: +:VGRD:30 mb: +:VGRD:3 mb: +:VGRD:320 K isentropic level: +:VGRD:350 mb: +:VGRD:3658 m above mean sea level: +:VGRD:400 mb: +:VGRD:450 K isentropic level: +:VGRD:450 mb: +:VGRD:4572 m above mean sea level: +:VGRD:457 m above mean sea level: +:VGRD:50 mb: +:VGRD:5 mb: +:VGRD:550 K isentropic level: +:VGRD:550 mb: +:VGRD:600 mb: +:VGRD:60-30 mb above ground: +:VGRD:610 m above mean sea level: +:VGRD:650 K isentropic level: +:VGRD:650 mb: +:VGRD:700 mb: +:VGRD:70 mb: +:VGRD:7 mb: +:VGRD:750 mb: +:VGRD:800 mb: +:VGRD:80 m above ground: +:VGRD:900 mb: +:VGRD:90-60 mb above ground: +:VGRD:914 m above mean sea level: +:VGRD:925 mb: +:VGRD:950 mb: +:VGRD:975 mb: +:VGRD:max wind: +:VGRD:planetary boundary layer: +:VGRD:PV=-1.5e-06 (Km^2/kg/s) surface: +:VGRD:PV=1.5e-06 (Km^2/kg/s) surface: +:VGRD:PV=-1e-06 (Km^2/kg/s) surface: +:VGRD:PV=1e-06 (Km^2/kg/s) surface: +:VGRD:PV=-2e-06 (Km^2/kg/s) surface: +:VGRD:PV=2e-06 (Km^2/kg/s) surface: +:VGRD:PV=-5e-07 (Km^2/kg/s) surface: +:VGRD:PV=5e-07 (Km^2/kg/s) surface: +:VGRD:tropopause: +:V-GWD:surface: +:VRATE:planetary boundary layer: +:VSTM:6000-0 m above ground: +:VVEL:0.995 sigma level: +:VVEL:1 mb: +:VVEL:2 mb: +:VVEL:3 mb: +:VVEL:5 mb: +:VVEL:7 mb: +:VVEL:10 mb: +:VVEL:20 mb: +:VVEL:30 mb: +:VVEL:50 mb: +:VVEL:70 mb: +:VVEL:1000 mb: +:VVEL:100 mb: +:VVEL:150 mb: +:VVEL:200 mb: +:VVEL:250 mb: +:VVEL:300 mb: +:VVEL:350 mb: +:VVEL:400 mb: +:VVEL:450 mb: +:VVEL:500 mb: +:VVEL:550 mb: +:VVEL:600 mb: +:VVEL:650 mb: +:VVEL:700 mb: +:VVEL:750 mb: +:VVEL:800 mb: +:VVEL:850 mb: +:VVEL:900 mb: +:VVEL:925 mb: +:VVEL:950 mb: +:VVEL:975 mb: +:VWSH:PV=-1.5e-06 (Km^2/kg/s) surface: +:VWSH:PV=1.5e-06 (Km^2/kg/s) surface: +:VWSH:PV=-1e-06 (Km^2/kg/s) surface: +:VWSH:PV=1e-06 (Km^2/kg/s) surface: +:VWSH:PV=-2e-06 (Km^2/kg/s) surface: +:VWSH:PV=2e-06 (Km^2/kg/s) surface: +:VWSH:PV=-5e-07 (Km^2/kg/s) surface: +:VWSH:PV=5e-07 (Km^2/kg/s) surface: +:VWSH:tropopause: +:WATR:surface: +:WILT:surface: +:PRES:1 hybrid level: +:HGT:1 hybrid level: +:TMP:1 hybrid level: +:RH:1 hybrid level: +:UGRD:1 hybrid level: +:VGRD:1 hybrid level: +:PRES:2 hybrid level: +:HGT:2 hybrid level: +:TMP:2 hybrid level: +:RH:2 hybrid level: +:UGRD:2 hybrid level: +:VGRD:2 hybrid level: +:PRES:3 hybrid level: +:HGT:3 hybrid level: +:TMP:3 hybrid level: +:RH:3 hybrid level: +:UGRD:3 hybrid level: +:VGRD:3 hybrid level: +:PRES:4 hybrid level: +:HGT:4 hybrid level: +:TMP:4 hybrid level: +:RH:4 hybrid level: +:UGRD:4 hybrid level: +:VGRD:4 hybrid level: +;############################ do not leave a blank line at the end diff --git a/parm/stage/master_gefs.yaml.j2 b/parm/stage/master_gefs.yaml.j2 index 2bfe3a9d58..4f8f645b02 100644 --- a/parm/stage/master_gefs.yaml.j2 +++ b/parm/stage/master_gefs.yaml.j2 @@ -114,7 +114,7 @@ {% endfilter %} {% endif %} -{% if REPLAY_ICS %} +{% if USE_ATM_ENS_PERTURB_FILES %} {% filter indent(width=4) %} {% include "atmosphere_ens_perturbations.yaml.j2" %} {% endfilter %} @@ -135,7 +135,7 @@ {% include "ocean_rerun.yaml.j2" %} {% endfilter %} {% endif %} -{% if REPLAY_ICS %} +{% if USE_OCN_ENS_PERTURB_FILES %} {% filter indent(width=4) %} {% include "ocean_ens_perturbations.yaml.j2" %} {% endfilter %} diff --git a/scripts/exglobal_stage_ic.py b/scripts/exglobal_stage_ic.py index d737d83b47..1a49c4f64d 100755 --- a/scripts/exglobal_stage_ic.py +++ b/scripts/exglobal_stage_ic.py @@ -22,7 +22,8 @@ def main(): 'assim_freq', 'current_cycle', 'previous_cycle', 'ROTDIR', 'ICSDIR', 'STAGE_IC_YAML_TMPL', 'DO_JEDIATMVAR', 'OCNRES', 'waveGRD', 'ntiles', 'DOIAU', 'DO_JEDIOCNVAR', - 'REPLAY_ICS', 'DO_WAVE', 'DO_OCN', 'DO_ICE', 'DO_NEST'] + 'REPLAY_ICS', 'DO_WAVE', 'DO_OCN', 'DO_ICE', 'DO_NEST', + 'USE_ATM_ENS_PERTURB_FILES', 'USE_OCN_ENS_PERTURB_FILES'] stage_dict = AttrDict() for key in keys: diff --git a/sorc/build_all.sh b/sorc/build_all.sh index b2f4e6ce0e..e75c853c39 100755 --- a/sorc/build_all.sh +++ b/sorc/build_all.sh @@ -16,7 +16,7 @@ function _usage() { Builds all of the global-workflow components by calling the individual build scripts in sequence. -Usage: ${BASH_SOURCE[0]} [-a UFS_app][-c build_config][-d][-f][-h][-j n][-v][-w] +Usage: ${BASH_SOURCE[0]} [-a UFS_app][-c build_config][-d][-f][-h][-j n][-v][-w][-y] -a UFS_app: Build a specific UFS app instead of the default -d: @@ -37,6 +37,8 @@ Usage: ${BASH_SOURCE[0]} [-a UFS_app][-c build_config][-d][-f][-h][-j n][-v][-w] Execute all build scripts with -v option to turn on verbose where supported -w: Use structured wave grid + -y: + Use hydrostatic version of FV3 EOF exit 1 } @@ -51,11 +53,12 @@ _build_gsi="NO" _build_debug="" _verbose_opt="" _wave_opt="" +_hydro_opt="" _build_job_max=20 _quick_kill="NO" # Reset option counter in case this script is sourced OPTIND=1 -while getopts ":a:dfghj:kuvw" option; do +while getopts ":a:dfghj:kuvwy" option; do case "${option}" in a) _build_ufs_opt+="-a ${OPTARG} ";; f) _build_ufs_opt+="-f ";; @@ -67,6 +70,7 @@ while getopts ":a:dfghj:kuvw" option; do u) _build_ufsda="YES" ;; v) _verbose_opt="-v";; w) _wave_opt="-w";; + y) _hydro_opt="-y";; :) echo "[${BASH_SOURCE[0]}]: ${option} requires an argument" _usage @@ -129,7 +133,7 @@ declare -A build_opts big_jobs=0 build_jobs["ufs"]=8 big_jobs=$((big_jobs+1)) -build_opts["ufs"]="${_wave_opt} ${_verbose_opt} ${_build_ufs_opt} ${_build_debug}" +build_opts["ufs"]="${_wave_opt} ${_hydro_opt} ${_verbose_opt} ${_build_ufs_opt} ${_build_debug}" build_jobs["upp"]=1 build_opts["upp"]="${_build_debug}" diff --git a/sorc/build_ufs.sh b/sorc/build_ufs.sh index 44c8c7a2ad..6432962a5a 100755 --- a/sorc/build_ufs.sh +++ b/sorc/build_ufs.sh @@ -7,8 +7,9 @@ cwd=$(pwd) APP="S2SWA" CCPP_SUITES="FV3_GFS_v17_p8_ugwpv1,FV3_GFS_v17_coupled_p8_ugwpv1,FV3_global_nest_v1" # TODO: does the g-w need to build with all these CCPP_SUITES? PDLIB="ON" +HYDRO="OFF" -while getopts ":da:fj:vw" option; do +while getopts ":da:fj:vwy" option; do case "${option}" in d) BUILD_TYPE="Debug";; a) APP="${OPTARG}";; @@ -16,6 +17,7 @@ while getopts ":da:fj:vw" option; do j) BUILD_JOBS="${OPTARG}";; v) export BUILD_VERBOSE="YES";; w) PDLIB="OFF";; + y) HYDRO="ON";; :) echo "[${BASH_SOURCE[0]}]: ${option} requires an argument" ;; @@ -32,6 +34,7 @@ source "./tests/module-setup.sh" MAKE_OPT="-DAPP=${APP} -D32BIT=ON -DCCPP_SUITES=${CCPP_SUITES}" [[ ${PDLIB:-"OFF"} = "ON" ]] && MAKE_OPT+=" -DPDLIB=ON" +[[ ${HYDRO:-"OFF"} = "ON" ]] && MAKE_OPT+=" -DHYDRO=ON" if [[ ${BUILD_TYPE:-"Release"} = "DEBUG" ]] ; then MAKE_OPT+=" -DDEBUG=ON" elif [[ "${FASTER:-OFF}" == ON ]] ; then diff --git a/sorc/link_workflow.sh b/sorc/link_workflow.sh index 3d81f7b7d4..b35b7ff35a 100755 --- a/sorc/link_workflow.sh +++ b/sorc/link_workflow.sh @@ -148,7 +148,7 @@ for file in params_grib2_tbl_new nam_micro_lookup.dat do ${LINK_OR_COPY} "${HOMEgfs}/sorc/upp.fd/parm/${file}" . done -for dir in gfs gefs +for dir in gfs gefs sfs do ${LINK_OR_COPY} "${HOMEgfs}/sorc/upp.fd/parm/${dir}" . done diff --git a/ush/forecast_det.sh b/ush/forecast_det.sh index 72064ac7f5..6d321aa620 100755 --- a/ush/forecast_det.sh +++ b/ush/forecast_det.sh @@ -82,6 +82,10 @@ UFS_det(){ # TODO: add checks for other MOM6 restarts as well mom6_rst_ok="NO" fi + MOM6_RESTART_SETTING='r' + MOM6_INIT_FROM_Z=True + MOM6_WARMSTART_FILE="none" + MOM6_INIT_UV="zero" fi # Check for CICE6 restart availability diff --git a/ush/forecast_predet.sh b/ush/forecast_predet.sh index d359a86622..43c9eb968f 100755 --- a/ush/forecast_predet.sh +++ b/ush/forecast_predet.sh @@ -332,9 +332,11 @@ FV3_predet(){ if [[ "${TYPE}" == "nh" ]]; then # monotonic and non-hydrostatic hord_mt=${hord_mt_nh_mono:-"10"} hord_xx=${hord_xx_nh_mono:-"10"} + hord_dp=-${hord_xx_nh_nonmono:-"-10"} else # monotonic and hydrostatic hord_mt=${hord_mt_hydro_mono:-"10"} hord_xx=${hord_xx_hydro_mono:-"10"} + hord_dp=-${hord_xx_nh_nonmono:-"-10"} fi else # non-monotonic options d_con=${d_con_nonmono:-"1."} @@ -342,9 +344,15 @@ FV3_predet(){ if [[ "${TYPE}" == "nh" ]]; then # non-monotonic and non-hydrostatic hord_mt=${hord_mt_nh_nonmono:-"5"} hord_xx=${hord_xx_nh_nonmono:-"5"} + hord_dp=${hord_xx_hydro_mono:-"-5"} else # non-monotonic and hydrostatic hord_mt=${hord_mt_hydro_nonmono:-"10"} hord_xx=${hord_xx_hydro_nonmono:-"10"} + hord_dp=${hord_xx_hydro_mono:-"10"} + kord_tm=${kord_tm_hydro_mono:-"-12"} + kord_mt=${kord_mt_hydro_mono:-"12"} + kord_wz=${kord_wz_hydro_mono:-"12"} + kord_tr=${kord_tr_hydro_mono:-"12"} fi fi @@ -543,9 +551,12 @@ FV3_predet(){ if [[ "${RUN}" =~ "gdas" || "${RUN}" =~ "gfs" ]]; then # RUN = gdas | enkfgdas | gfs | enkfgfs ${NCP} "${PARMgfs}/post/gfs/postxconfig-NT-gfs-two.txt" "${DATA}/postxconfig-NT.txt" ${NCP} "${PARMgfs}/post/gfs/postxconfig-NT-gfs-f00-two.txt" "${DATA}/postxconfig-NT_FH00.txt" - elif [[ "${RUN}" == "gefs" ]]; then # RUN = gefs + elif [[ "${RUN}" == "gefs" && "${SFS_POST:-NO}" == "NO" ]]; then # RUN = gefs ${NCP} "${PARMgfs}/post/gefs/postxconfig-NT-gefs.txt" "${DATA}/postxconfig-NT.txt" ${NCP} "${PARMgfs}/post/gefs/postxconfig-NT-gefs-f00.txt" "${DATA}/postxconfig-NT_FH00.txt" + elif [[ "${RUN}" == "gefs" && "${SFS_POST:-NO}" == "YES" ]]; then # RUN = sfs output + ${NCP} "${PARMgfs}/post/sfs/postxconfig-NT-sfs.txt" "${DATA}/postxconfig-NT.txt" + ${NCP} "${PARMgfs}/post/sfs/postxconfig-NT-sfs.txt" "${DATA}/postxconfig-NT_FH00.txt" fi fi } diff --git a/ush/parsing_namelists_FV3.sh b/ush/parsing_namelists_FV3.sh index 617ecff719..9279b284f8 100755 --- a/ush/parsing_namelists_FV3.sh +++ b/ush/parsing_namelists_FV3.sh @@ -163,7 +163,7 @@ cat > input.nml < Date: Fri, 15 Nov 2024 03:28:26 -0500 Subject: [PATCH 07/10] Update JGDAS ENKF POST job (#3090) NCO has requested that each COM variable specify whether it is an input or an output. This completes that process for the global jgdas enkf post job. Refs #2451 --- scripts/exgdas_enkf_post.sh | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/scripts/exgdas_enkf_post.sh b/scripts/exgdas_enkf_post.sh index 6e93284695..06ce2e16fb 100755 --- a/scripts/exgdas_enkf_post.sh +++ b/scripts/exgdas_enkf_post.sh @@ -66,31 +66,34 @@ export OMP_NUM_THREADS=$NTHREADS_EPOS # Forecast ensemble member files for imem in $(seq 1 $NMEM_ENS); do memchar="mem"$(printf %03i "${imem}") - MEMDIR=${memchar} YMD=${PDY} HH=${cyc} declare_from_tmpl -x COM_ATMOS_HISTORY:COM_ATMOS_HISTORY_TMPL + MEMDIR=${memchar} YMD=${PDY} HH=${cyc} declare_from_tmpl -x \ + COMIN_ATMOS_HISTORY:COM_ATMOS_HISTORY_TMPL for fhr in $(seq $FHMIN $FHOUT $FHMAX); do fhrchar=$(printf %03i $fhr) - ${NLN} "${COM_ATMOS_HISTORY}/${PREFIX}sfcf${fhrchar}.nc" "sfcf${fhrchar}_${memchar}" - ${NLN} "${COM_ATMOS_HISTORY}/${PREFIX}atmf${fhrchar}.nc" "atmf${fhrchar}_${memchar}" + ${NLN} "${COMIN_ATMOS_HISTORY}/${PREFIX}sfcf${fhrchar}.nc" "sfcf${fhrchar}_${memchar}" + ${NLN} "${COMIN_ATMOS_HISTORY}/${PREFIX}atmf${fhrchar}.nc" "atmf${fhrchar}_${memchar}" done done # Forecast ensemble mean and smoothed files -MEMDIR="ensstat" YMD=${PDY} HH=${cyc} declare_from_tmpl -rx COM_ATMOS_HISTORY_STAT:COM_ATMOS_HISTORY_TMPL -if [[ ! -d "${COM_ATMOS_HISTORY_STAT}" ]]; then mkdir -p "${COM_ATMOS_HISTORY_STAT}"; fi +MEMDIR="ensstat" YMD=${PDY} HH=${cyc} declare_from_tmpl -rx \ + COMOUT_ATMOS_HISTORY_STAT:COM_ATMOS_HISTORY_TMPL +if [[ ! -d "${COMOUT_ATMOS_HISTORY_STAT}" ]]; then mkdir -p "${COMOUT_ATMOS_HISTORY_STAT}"; fi for fhr in $(seq $FHMIN $FHOUT $FHMAX); do fhrchar=$(printf %03i $fhr) - ${NLN} "${COM_ATMOS_HISTORY_STAT}/${PREFIX}sfcf${fhrchar}.ensmean.nc" "sfcf${fhrchar}.ensmean" - ${NLN} "${COM_ATMOS_HISTORY_STAT}/${PREFIX}atmf${fhrchar}.ensmean.nc" "atmf${fhrchar}.ensmean" + ${NLN} "${COMOUT_ATMOS_HISTORY_STAT}/${PREFIX}sfcf${fhrchar}.ensmean.nc" "sfcf${fhrchar}.ensmean" + ${NLN} "${COMOUT_ATMOS_HISTORY_STAT}/${PREFIX}atmf${fhrchar}.ensmean.nc" "atmf${fhrchar}.ensmean" if [ $SMOOTH_ENKF = "YES" ]; then for imem in $(seq 1 $NMEM_ENS); do memchar="mem"$(printf %03i "${imem}") - MEMDIR="${memchar}" YMD=${PDY} HH=${cyc} declare_from_tmpl -x COM_ATMOS_HISTORY - ${NLN} "${COM_ATMOS_HISTORY}/${PREFIX}atmf${fhrchar}${ENKF_SUFFIX}.nc" "atmf${fhrchar}${ENKF_SUFFIX}_${memchar}" + MEMDIR="${memchar}" YMD=${PDY} HH=${cyc} declare_from_tmpl -x \ + COMIN_ATMOS_HISTORY:COM_ATMOS_HISTORY_TMPL + ${NLN} "${COMIN_ATMOS_HISTORY}/${PREFIX}atmf${fhrchar}${ENKF_SUFFIX}.nc" "atmf${fhrchar}${ENKF_SUFFIX}_${memchar}" done fi - [[ $ENKF_SPREAD = "YES" ]] && ${NLN} "${COM_ATMOS_HISTORY_STAT}/${PREFIX}atmf${fhrchar}.ensspread.nc" "atmf${fhrchar}.ensspread" + [[ $ENKF_SPREAD = "YES" ]] && ${NLN} "${COMOUT_ATMOS_HISTORY_STAT}/${PREFIX}atmf${fhrchar}.ensspread.nc" "atmf${fhrchar}.ensspread" done ################################################################################ @@ -145,7 +148,7 @@ if [ $SENDDBN = "YES" ]; then fhrchar=$(printf %03i $fhr) if [ $(expr $fhr % 3) -eq 0 ]; then if [ -s ./sfcf${fhrchar}.ensmean ]; then - ${DBNROOT}/bin/dbn_alert "MODEL" "GFS_ENKF" "${job}" "${COM_ATMOS_HISTORY_STAT}/${PREFIX}sfcf${fhrchar}.ensmean.nc" + ${DBNROOT}/bin/dbn_alert "MODEL" "GFS_ENKF" "${job}" "${COMOUT_ATMOS_HISTORY_STAT}/${PREFIX}sfcf${fhrchar}.ensmean.nc" fi fi done From 3209bea5f089986a4c1656c20cec36c61cc7619e Mon Sep 17 00:00:00 2001 From: CatherineThomas-NOAA <59020064+CatherineThomas-NOAA@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:37:39 -0500 Subject: [PATCH 08/10] Enable tapering of atm ens perts at the model top (#3097) This commit turns on the tapering of atmospheric ensemble perturbations at the model top in the EnKF to reduce the upper layer ensemble spread. Resolves #3096 --------- Co-authored-by: Rahul Mahajan --- parm/config/gfs/config.eupd | 2 +- scripts/exgdas_enkf_update.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/parm/config/gfs/config.eupd b/parm/config/gfs/config.eupd index 2ff48240ae..7b0c8994c2 100644 --- a/parm/config/gfs/config.eupd +++ b/parm/config/gfs/config.eupd @@ -28,7 +28,7 @@ export lnsigcutoff=2.75 # ignored if modelspace_vloc=.true. export lobsdiag_forenkf=".true." # use jacobian. must be .true. if modelspace_vloc=".true." # need to specify .true. setting since config.anal sets to .false. - +export taperanalperts=".true." export NAM_ENKF="smoothparm=35," echo "END: config.eupd" diff --git a/scripts/exgdas_enkf_update.sh b/scripts/exgdas_enkf_update.sh index e924274d39..752cb07a6b 100755 --- a/scripts/exgdas_enkf_update.sh +++ b/scripts/exgdas_enkf_update.sh @@ -77,6 +77,7 @@ write_spread_diag=${write_spread_diag:-".false."} cnvw_option=${cnvw_option:-".false."} netcdf_diag=${netcdf_diag:-".true."} modelspace_vloc=${modelspace_vloc:-".false."} # if true, 'vlocal_eig.dat' is needed +taperanalperts=${taperanalperts:-".false."} IAUFHRS_ENKF=${IAUFHRS_ENKF:-6} NMEM_ENS_MAX=${NMEM_ENS:-80} if [ "${RUN}" = "enkfgfs" ]; then @@ -287,7 +288,7 @@ cat > enkf.nml << EOFnml univaroz=.false.,adp_anglebc=.true.,angord=4,use_edges=.false.,emiss_bc=.true., letkf_flag=${letkf_flag},nobsl_max=${nobsl_max},denkf=${denkf},getkf=${getkf}., nhr_anal=${IAUFHRS_ENKF},nhr_state=${IAUFHRS_ENKF}, - lobsdiag_forenkf=$lobsdiag_forenkf, + lobsdiag_forenkf=${lobsdiag_forenkf},taperanalperts=${taperanalperts}, write_spread_diag=$write_spread_diag, modelspace_vloc=$modelspace_vloc, use_correlated_oberrs=${use_correlated_oberrs}, From 7ff942eb3e6749b95ff40773b20109258a33f17f Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA <134300700+DavidNew-NOAA@users.noreply.github.com> Date: Tue, 19 Nov 2024 06:12:19 -0500 Subject: [PATCH 09/10] JCB-based obs+bias staging, Jedi class updates, and marine B-matrix refactoring (#2992) This PR is a companion to GDASApp PR NOAA-EMC/GDASApp#1312 (merged) and NOAA-EMC/jcb-gdas#31 (merged). This PR does three things: 1. It changes the observation and bias staging for the atmospheric analysis tasks to use JCB templates instead of reading the full JEDI input configuration dictionary in order to construct a list of files to stage. This is cleaner and places fewer constraints on how to initialize the analysis. 2. The ```Jedi``` constructor now takes as input a dictionary that is essentially subset of the ```task_config``` dictionary. This makes the code clearer and less opaque and makes debugging easier. Each dictionary is constructed from a YAML file with configuration parameters for each JEDI application that is run. 3. All JEDI applications and their input YAMLs are now initialized in the initialize job of the ```AtmAnalysis``` and ```AtmEnsAnalysis```. Before, in the ```atmensanl*``` jobs for example, the LETKF solver was initialized in the ```atmensanlinit```cjob, but the LETKF solver and FV3 increment converter were both initialized and executed in the ```atmensanlobs``` and ```atmensanlfv3inc``` jobs respectively. This makes more sense in terms of resource allocation. Addendum: I'm now rolling in the refactoring of the marine B-matrix task into this PR. That makes it also a companion of NOAA-EMC/GDASApp#1346 and NOAA-EMC/jcb-gdas#36. These new changes introduce the ```Jedi``` class and JCB into the marine B-matrix job. Partially resolvesNOAA-EMC/GDASApp#1296 --------- Co-authored-by: RussTreadon-NOAA Co-authored-by: Rahul Mahajan --- env/HERCULES.env | 7 +- env/WCOSS2.env | 10 +- parm/config/gfs/config.atmanl | 7 +- parm/config/gfs/config.atmanlfv3inc | 3 - parm/config/gfs/config.atmensanl | 13 +- parm/config/gfs/config.atmensanlfv3inc | 3 - parm/config/gfs/config.atmensanlobs | 2 - parm/config/gfs/config.atmensanlsol | 2 - parm/config/gfs/config.marineanl | 2 + parm/config/gfs/config.marineanlletkf | 2 +- parm/config/gfs/config.marinebmat | 8 - parm/config/gfs/config.resources.ORION | 3 +- parm/config/gfs/yaml/defaults.yaml | 9 +- parm/gdas/atmanl_jedi_config.yaml.j2 | 13 + parm/gdas/atmensanl_jedi_config.yaml.j2 | 27 ++ parm/gdas/soca_bmat_jedi_config.yaml.j2 | 42 ++ .../exglobal_atm_analysis_fv3_increment.py | 9 +- scripts/exglobal_atm_analysis_initialize.py | 9 +- scripts/exglobal_atm_analysis_variational.py | 8 +- .../exglobal_atmens_analysis_fv3_increment.py | 9 +- .../exglobal_atmens_analysis_initialize.py | 12 +- scripts/exglobal_atmens_analysis_letkf.py | 11 +- scripts/exglobal_atmens_analysis_obs.py | 10 +- scripts/exglobal_atmens_analysis_sol.py | 11 +- sorc/gdas.cd | 2 +- ush/python/pygfs/jedi/jedi.py | 440 +++++++++--------- ush/python/pygfs/task/atm_analysis.py | 102 ++-- ush/python/pygfs/task/atmens_analysis.py | 107 ++--- ush/python/pygfs/task/marine_analysis.py | 7 +- ush/python/pygfs/task/marine_bmat.py | 240 ++++------ versions/fix.ver | 2 +- 31 files changed, 547 insertions(+), 585 deletions(-) create mode 100644 parm/gdas/atmanl_jedi_config.yaml.j2 create mode 100644 parm/gdas/atmensanl_jedi_config.yaml.j2 create mode 100644 parm/gdas/soca_bmat_jedi_config.yaml.j2 diff --git a/env/HERCULES.env b/env/HERCULES.env index 15e3928f08..fccc2f87a5 100755 --- a/env/HERCULES.env +++ b/env/HERCULES.env @@ -145,7 +145,7 @@ case ${step} in export NTHREADS_OCNANALECEN=${threads_per_task_ocnanalecen:-${max_threads_per_task}} [[ ${NTHREADS_OCNANALECEN} -gt ${max_threads_per_task} ]] && export NTHREADS_OCNANALECEN=${max_threads_per_task} export APRUN_OCNANALECEN="${launcher} -n ${ntasks_ocnanalecen} --cpus-per-task=${NTHREADS_OCNANALECEN}" -;; + ;; "marineanlchkpt") export APRUNCFP="${launcher} -n \$ncmd ${mpmd_opt}" @@ -153,6 +153,11 @@ case ${step} in export NTHREADS_OCNANAL=${NTHREADSmax} export APRUN_MARINEANLCHKPT="${APRUN_default} --cpus-per-task=${NTHREADS_OCNANAL}" ;; + "marineanlletkf") + + export NTHREADS_MARINEANLLETKF=${NTHREADSmax} + export APRUN_MARINEANLLETKF="${APRUN_default}" + ;; "anal" | "analcalc") export MKL_NUM_THREADS=4 diff --git a/env/WCOSS2.env b/env/WCOSS2.env index fff8f7b096..27001bebd7 100755 --- a/env/WCOSS2.env +++ b/env/WCOSS2.env @@ -107,17 +107,15 @@ elif [[ "${step}" = "marinebmat" ]]; then export APRUNCFP="${launcher} -n \$ncmd --multi-prog" export APRUN_MARINEBMAT="${APRUN_default}" -elif [[ "${step}" = "ocnanalrun" ]]; then +elif [[ "${step}" = "marineanlvar" ]]; then export APRUNCFP="${launcher} -n \$ncmd --multi-prog" + export APRUN_MARINEANLVAR="${APRUN_default}" - export APRUN_OCNANAL="${APRUN_default}" - -elif [[ "${step}" = "ocnanalchkpt" ]]; then +elif [[ "${step}" = "marineanlchkpt" ]]; then export APRUNCFP="${launcher} -n \$ncmd --multi-prog" - - export APRUN_OCNANAL="${APRUN_default}" + export APRUN_MARINEANLCHKPT="${APRUN_default}" elif [[ "${step}" = "ocnanalecen" ]]; then diff --git a/parm/config/gfs/config.atmanl b/parm/config/gfs/config.atmanl index 9a06088ecc..1d700a479c 100644 --- a/parm/config/gfs/config.atmanl +++ b/parm/config/gfs/config.atmanl @@ -5,8 +5,7 @@ echo "BEGIN: config.atmanl" -export JCB_BASE_YAML="${PARMgfs}/gdas/atm/jcb-base.yaml.j2" -export JCB_ALGO_YAML=@JCB_ALGO_YAML@ +export JCB_ALGO_YAML_VAR=@JCB_ALGO_YAML_VAR@ export STATICB_TYPE=@STATICB_TYPE@ export LOCALIZATION_TYPE="bump" @@ -23,6 +22,8 @@ fi export CRTM_FIX_YAML="${PARMgfs}/gdas/atm_crtm_coeff.yaml.j2" export JEDI_FIX_YAML="${PARMgfs}/gdas/atm_jedi_fix.yaml.j2" + +export JEDI_CONFIG_YAML="${PARMgfs}/gdas/atmanl_jedi_config.yaml.j2" export VAR_BKG_STAGING_YAML="${PARMgfs}/gdas/staging/atm_var_bkg.yaml.j2" export BERROR_STAGING_YAML="${PARMgfs}/gdas/staging/atm_berror_${STATICB_TYPE}.yaml.j2" export FV3ENS_STAGING_YAML="${PARMgfs}/gdas/staging/atm_var_fv3ens.yaml.j2" @@ -33,6 +34,4 @@ export layout_y_atmanl=@LAYOUT_Y_ATMANL@ export io_layout_x=@IO_LAYOUT_X@ export io_layout_y=@IO_LAYOUT_Y@ -export JEDIEXE=${EXECgfs}/gdas.x - echo "END: config.atmanl" diff --git a/parm/config/gfs/config.atmanlfv3inc b/parm/config/gfs/config.atmanlfv3inc index ab7efa3a60..4e7714628e 100644 --- a/parm/config/gfs/config.atmanlfv3inc +++ b/parm/config/gfs/config.atmanlfv3inc @@ -8,7 +8,4 @@ echo "BEGIN: config.atmanlfv3inc" # Get task specific resources . "${EXPDIR}/config.resources" atmanlfv3inc -export JCB_ALGO=fv3jedi_fv3inc_variational -export JEDIEXE=${EXECgfs}/fv3jedi_fv3inc.x - echo "END: config.atmanlfv3inc" diff --git a/parm/config/gfs/config.atmensanl b/parm/config/gfs/config.atmensanl index f5a1278248..2726f655bd 100644 --- a/parm/config/gfs/config.atmensanl +++ b/parm/config/gfs/config.atmensanl @@ -5,17 +5,16 @@ echo "BEGIN: config.atmensanl" -export JCB_BASE_YAML="${PARMgfs}/gdas/atm/jcb-base.yaml.j2" -if [[ ${lobsdiag_forenkf} = ".false." ]] ; then - export JCB_ALGO_YAML=@JCB_ALGO_YAML_LETKF@ -else - export JCB_ALGO_YAML=@JCB_ALGO_YAML_OBS@ -fi +export JCB_ALGO_YAML_LETKF=@JCB_ALGO_YAML_LETKF@ +export JCB_ALGO_YAML_OBS=@JCB_ALGO_YAML_OBS@ +export JCB_ALGO_YAML_SOL=@JCB_ALGO_YAML_SOL@ export INTERP_METHOD='barycentric' export CRTM_FIX_YAML="${PARMgfs}/gdas/atm_crtm_coeff.yaml.j2" export JEDI_FIX_YAML="${PARMgfs}/gdas/atm_jedi_fix.yaml.j2" + +export JEDI_CONFIG_YAML="${PARMgfs}/gdas/atmensanl_jedi_config.yaml.j2" export LGETKF_BKG_STAGING_YAML="${PARMgfs}/gdas/staging/atm_lgetkf_bkg.yaml.j2" export layout_x_atmensanl=@LAYOUT_X_ATMENSANL@ @@ -24,6 +23,4 @@ export layout_y_atmensanl=@LAYOUT_Y_ATMENSANL@ export io_layout_x=@IO_LAYOUT_X@ export io_layout_y=@IO_LAYOUT_Y@ -export JEDIEXE=${EXECgfs}/gdas.x - echo "END: config.atmensanl" diff --git a/parm/config/gfs/config.atmensanlfv3inc b/parm/config/gfs/config.atmensanlfv3inc index 2dc73f3f6e..fe3337e5a2 100644 --- a/parm/config/gfs/config.atmensanlfv3inc +++ b/parm/config/gfs/config.atmensanlfv3inc @@ -8,7 +8,4 @@ echo "BEGIN: config.atmensanlfv3inc" # Get task specific resources . "${EXPDIR}/config.resources" atmensanlfv3inc -export JCB_ALGO=fv3jedi_fv3inc_lgetkf -export JEDIEXE=${EXECgfs}/fv3jedi_fv3inc.x - echo "END: config.atmensanlfv3inc" diff --git a/parm/config/gfs/config.atmensanlobs b/parm/config/gfs/config.atmensanlobs index dff3fa3095..c7e050b009 100644 --- a/parm/config/gfs/config.atmensanlobs +++ b/parm/config/gfs/config.atmensanlobs @@ -8,6 +8,4 @@ echo "BEGIN: config.atmensanlobs" # Get task specific resources . "${EXPDIR}/config.resources" atmensanlobs -export JCB_ALGO_YAML=@JCB_ALGO_YAML@ - echo "END: config.atmensanlobs" diff --git a/parm/config/gfs/config.atmensanlsol b/parm/config/gfs/config.atmensanlsol index dac161373b..8ef905d1bd 100644 --- a/parm/config/gfs/config.atmensanlsol +++ b/parm/config/gfs/config.atmensanlsol @@ -8,6 +8,4 @@ echo "BEGIN: config.atmensanlsol" # Get task specific resources . "${EXPDIR}/config.resources" atmensanlsol -export JCB_ALGO_YAML=@JCB_ALGO_YAML@ - echo "END: config.atmensanlsol" diff --git a/parm/config/gfs/config.marineanl b/parm/config/gfs/config.marineanl index a19fc015e2..0b55fa447d 100644 --- a/parm/config/gfs/config.marineanl +++ b/parm/config/gfs/config.marineanl @@ -5,6 +5,8 @@ echo "BEGIN: config.marineanl" +export JEDI_CONFIG_YAML="${PARMgfs}/gdas/soca_bmat_jedi_config.yaml.j2" + export MARINE_OBS_YAML_DIR="${PARMgfs}/gdas/soca/obs/config" export MARINE_OBS_LIST_YAML=@SOCA_OBS_LIST@ export SOCA_INPUT_FIX_DIR=@SOCA_INPUT_FIX_DIR@ diff --git a/parm/config/gfs/config.marineanlletkf b/parm/config/gfs/config.marineanlletkf index 8b84af4eaa..93f03f80ff 100644 --- a/parm/config/gfs/config.marineanlletkf +++ b/parm/config/gfs/config.marineanlletkf @@ -14,7 +14,7 @@ export MARINE_LETKF_STAGE_YAML_TMPL="${PARMgfs}/gdas/soca/letkf/letkf_stage.yaml export MARINE_LETKF_SAVE_YAML_TMPL="${PARMgfs}/gdas/soca/letkf/letkf_save.yaml.j2" export GRIDGEN_EXEC="${EXECgfs}/gdas_soca_gridgen.x" -export GRIDGEN_YAML="${PARMgfs}/gdas/soca/gridgen/gridgen.yaml" +export GRIDGEN_YAML="${HOMEgfs}/sorc/gdas.cd/parm/jcb-gdas/algorithm/marine/soca_gridgen.yaml.j2" export DIST_HALO_SIZE=500000 echo "END: config.marineanlletkf" diff --git a/parm/config/gfs/config.marinebmat b/parm/config/gfs/config.marinebmat index 00352737d0..d88739dced 100644 --- a/parm/config/gfs/config.marinebmat +++ b/parm/config/gfs/config.marinebmat @@ -8,12 +8,4 @@ echo "BEGIN: config.marinebmat" # Get task specific resources . "${EXPDIR}/config.resources" marinebmat -export BERROR_DIAGB_YAML="${PARMgfs}/gdas/soca/berror/soca_diagb.yaml.j2" -export BERROR_VTSCALES_YAML="${PARMgfs}/gdas/soca/berror/soca_vtscales.yaml.j2" -export BERROR_DIFFV_YAML="${PARMgfs}/gdas/soca/berror/soca_parameters_diffusion_vt.yaml.j2" -export BERROR_HZSCALES_YAML="${PARMgfs}/gdas/soca/berror/soca_setcorscales.yaml" -export BERROR_DIFFH_YAML="${PARMgfs}/gdas/soca/berror/soca_parameters_diffusion_hz.yaml.j2" -export BERROR_ENS_RECENTER_YAML="${PARMgfs}/gdas/soca/berror/soca_ensb.yaml.j2" -export BERROR_HYB_WEIGHTS_YAML="${PARMgfs}/gdas/soca/berror/soca_ensweights.yaml.j2" - echo "END: config.marinebmat" diff --git a/parm/config/gfs/config.resources.ORION b/parm/config/gfs/config.resources.ORION index 461b6f14f7..fceb8feeb1 100644 --- a/parm/config/gfs/config.resources.ORION +++ b/parm/config/gfs/config.resources.ORION @@ -25,7 +25,8 @@ case ${step} in ;; "atmanlvar") # Run on 8 nodes for memory requirement - export tasks_per_node=8 + export tasks_per_node_gdas=8 + export tasks_per_node_gfs=8 export walltime="00:45:00" ;; "atmensanlobs") diff --git a/parm/config/gfs/yaml/defaults.yaml b/parm/config/gfs/yaml/defaults.yaml index 78caf46f5d..d8cf76a47b 100644 --- a/parm/config/gfs/yaml/defaults.yaml +++ b/parm/config/gfs/yaml/defaults.yaml @@ -23,7 +23,7 @@ base: FHMAX_ENKF_GFS: 12 atmanl: - JCB_ALGO_YAML: "${PARMgfs}/gdas/atm/jcb-prototype_3dvar.yaml.j2" + JCB_ALGO_YAML_VAR: "${PARMgfs}/gdas/atm/jcb-prototype_3dvar.yaml.j2" STATICB_TYPE: "gsibec" LAYOUT_X_ATMANL: 8 LAYOUT_Y_ATMANL: 8 @@ -33,16 +33,11 @@ atmanl: atmensanl: JCB_ALGO_YAML_LETKF: "${PARMgfs}/gdas/atm/jcb-prototype_lgetkf.yaml.j2" JCB_ALGO_YAML_OBS: "${PARMgfs}/gdas/atm/jcb-prototype_lgetkf_observer.yaml.j2" + JCB_ALGO_YAML_SOL: "${PARMgfs}/gdas/atm/jcb-prototype_lgetkf_solver.yaml.j2" LAYOUT_X_ATMENSANL: 8 LAYOUT_Y_ATMENSANL: 8 IO_LAYOUT_X: 1 IO_LAYOUT_Y: 1 - -atmensanlobs: - JCB_ALGO_YAML: "${PARMgfs}/gdas/atm/jcb-prototype_lgetkf_observer.yaml.j2" - -atmensanlsol: - JCB_ALGO_YAML: "${PARMgfs}/gdas/atm/jcb-prototype_lgetkf_solver.yaml.j2" aeroanl: IO_LAYOUT_X: 1 diff --git a/parm/gdas/atmanl_jedi_config.yaml.j2 b/parm/gdas/atmanl_jedi_config.yaml.j2 new file mode 100644 index 0000000000..4046ba0931 --- /dev/null +++ b/parm/gdas/atmanl_jedi_config.yaml.j2 @@ -0,0 +1,13 @@ +atmanlvar: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas.x' + mpi_cmd: '{{ APRUN_ATMANLVAR }}' + jedi_args: ['fv3jedi', 'variational'] + jcb_base_yaml: '{{ PARMgfs }}/gdas/atm/jcb-base.yaml.j2' + jcb_algo_yaml: '{{ JCB_ALGO_YAML_VAR }}' +atmanlfv3inc: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/fv3jedi_fv3inc.x' + mpi_cmd: '{{ APRUN_ATMANLFV3INC }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/atm/jcb-base.yaml.j2' + jcb_algo: fv3jedi_fv3inc_variational diff --git a/parm/gdas/atmensanl_jedi_config.yaml.j2 b/parm/gdas/atmensanl_jedi_config.yaml.j2 new file mode 100644 index 0000000000..9ab2ec6ace --- /dev/null +++ b/parm/gdas/atmensanl_jedi_config.yaml.j2 @@ -0,0 +1,27 @@ +atmensanlobs: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas.x' + mpi_cmd: '{{ APRUN_ATMENSANLOBS }}' + jedi_args: ['fv3jedi', 'localensembleda'] + jcb_base_yaml: '{{ PARMgfs }}/gdas/atm/jcb-base.yaml.j2' + jcb_algo_yaml: '{{ JCB_ALGO_YAML_OBS }}' +atmensanlsol: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas.x' + mpi_cmd: '{{ APRUN_ATMENSANLSOL }}' + jedi_args: ['fv3jedi', 'localensembleda'] + jcb_base_yaml: '{{ PARMgfs }}/gdas/atm/jcb-base.yaml.j2' + jcb_algo_yaml: '{{ JCB_ALGO_YAML_SOL }}' +atmensanlfv3inc: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/fv3jedi_fv3inc.x' + mpi_cmd: '{{ APRUN_ATMENSANLFV3INC }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/atm/jcb-base.yaml.j2' + jcb_algo: fv3jedi_fv3inc_lgetkf +atmensanlletkf: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas.x' + mpi_cmd: '{{ APRUN_ATMENSANLLETKF }}' + jedi_args: ['fv3jedi', 'localensembleda'] + jcb_base_yaml: '{{ PARMgfs }}/gdas/atm/jcb-base.yaml.j2' + jcb_algo_yaml: '{{ JCB_ALGO_YAML_LETKF }}' diff --git a/parm/gdas/soca_bmat_jedi_config.yaml.j2 b/parm/gdas/soca_bmat_jedi_config.yaml.j2 new file mode 100644 index 0000000000..4e476d3117 --- /dev/null +++ b/parm/gdas/soca_bmat_jedi_config.yaml.j2 @@ -0,0 +1,42 @@ +gridgen: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_soca_gridgen.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_gridgen +soca_diagb: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_soca_diagb.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_diagb +soca_parameters_diffusion_vt: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_soca_error_covariance_toolbox.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_parameters_diffusion_vt +soca_setcorscales: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_soca_setcorscales.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_setcorscales +soca_parameters_diffusion_hz: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_soca_error_covariance_toolbox.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_parameters_diffusion_hz +soca_ensb: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_ens_handler.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_ensb +soca_ensweights: + rundir: '{{ DATA }}' + exe_src: '{{ EXECgfs }}/gdas_socahybridweights.x' + mpi_cmd: '{{ APRUN_MARINEBMAT }}' + jcb_base_yaml: '{{ PARMgfs }}/gdas/soca/marine-jcb-base.yaml' + jcb_algo: soca_ensweights diff --git a/scripts/exglobal_atm_analysis_fv3_increment.py b/scripts/exglobal_atm_analysis_fv3_increment.py index 72413ddbd4..c5a3e70943 100755 --- a/scripts/exglobal_atm_analysis_fv3_increment.py +++ b/scripts/exglobal_atm_analysis_fv3_increment.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # exglobal_atm_analysis_fv3_increment.py # This script creates an AtmAnalysis object -# and runs the initialize_fv3inc and execute methods -# which convert the JEDI increment into an FV3 increment +# and runs the execute method which runs the JEDI +# FV3 increment converter import os from wxflow import Logger, cast_strdict_as_dtypedict @@ -18,8 +18,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atm analysis object - AtmAnl = AtmAnalysis(config, 'atmanlfv3inc') + AtmAnl = AtmAnalysis(config) # Initialize and execute FV3 increment converter - AtmAnl.initialize_jedi() - AtmAnl.execute(config.APRUN_ATMANLFV3INC) + AtmAnl.execute('atmanlfv3inc') diff --git a/scripts/exglobal_atm_analysis_initialize.py b/scripts/exglobal_atm_analysis_initialize.py index 9deae07bb3..749d320111 100755 --- a/scripts/exglobal_atm_analysis_initialize.py +++ b/scripts/exglobal_atm_analysis_initialize.py @@ -2,8 +2,8 @@ # exglobal_atm_analysis_initialize.py # This script creates an AtmAnalysis class # and runs the initialize method -# which create and stage the runtime directory -# and create the YAML configuration +# which creates and stages the runtime directory +# and creates the YAML configuration # for a global atm variational analysis import os @@ -20,8 +20,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atm analysis task - AtmAnl = AtmAnalysis(config, 'atmanlvar') + AtmAnl = AtmAnalysis(config) # Initialize JEDI variational analysis - AtmAnl.initialize_jedi() - AtmAnl.initialize_analysis() + AtmAnl.initialize() diff --git a/scripts/exglobal_atm_analysis_variational.py b/scripts/exglobal_atm_analysis_variational.py index 8359532069..9ad121f76c 100755 --- a/scripts/exglobal_atm_analysis_variational.py +++ b/scripts/exglobal_atm_analysis_variational.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # exglobal_atm_analysis_variational.py # This script creates an AtmAnalysis object -# and runs the execute method -# which executes the global atm variational analysis +# and runs the execute method which runs the JEDI +# variational analysis application import os from wxflow import Logger, cast_strdict_as_dtypedict @@ -18,7 +18,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atm analysis task - AtmAnl = AtmAnalysis(config, 'atmanlvar') + AtmAnl = AtmAnalysis(config) # Execute JEDI variational analysis - AtmAnl.execute(config.APRUN_ATMANLVAR, ['fv3jedi', 'variational']) + AtmAnl.execute('atmanlvar') diff --git a/scripts/exglobal_atmens_analysis_fv3_increment.py b/scripts/exglobal_atmens_analysis_fv3_increment.py index 48eb6a6a1e..4506b28033 100755 --- a/scripts/exglobal_atmens_analysis_fv3_increment.py +++ b/scripts/exglobal_atmens_analysis_fv3_increment.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # exglobal_atmens_analysis_fv3_increment.py # This script creates an AtmEnsAnalysis object -# and runs the initialize_fv3inc and execute methods -# which convert the JEDI increment into an FV3 increment +# and runs the execute method which runs the JEDI +# FV3 increment converter application import os from wxflow import Logger, cast_strdict_as_dtypedict @@ -18,8 +18,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atmens analysis object - AtmEnsAnl = AtmEnsAnalysis(config, 'atmensanlfv3inc') + AtmEnsAnl = AtmEnsAnalysis(config) # Initialize and execute JEDI FV3 increment converter - AtmEnsAnl.initialize_jedi() - AtmEnsAnl.execute(config.APRUN_ATMENSANLFV3INC) + AtmEnsAnl.execute('atmensanlfv3inc') diff --git a/scripts/exglobal_atmens_analysis_initialize.py b/scripts/exglobal_atmens_analysis_initialize.py index 326fe80628..124e755594 100755 --- a/scripts/exglobal_atmens_analysis_initialize.py +++ b/scripts/exglobal_atmens_analysis_initialize.py @@ -2,8 +2,8 @@ # exglobal_atmens_analysis_initialize.py # This script creates an AtmEnsAnalysis class # and runs the initialize method -# which create and stage the runtime directory -# and create the YAML configuration +# which creates and stages the runtime directory +# and creates the YAML configuration # for a global atm local ensemble analysis import os @@ -20,11 +20,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atmens analysis task - if not config.lobsdiag_forenkf: - AtmEnsAnl = AtmEnsAnalysis(config, 'atmensanlletkf') - else: - AtmEnsAnl = AtmEnsAnalysis(config, 'atmensanlobs') + AtmEnsAnl = AtmEnsAnalysis(config) # Initialize JEDI ensemble DA analysis - AtmEnsAnl.initialize_jedi() - AtmEnsAnl.initialize_analysis() + AtmEnsAnl.initialize() diff --git a/scripts/exglobal_atmens_analysis_letkf.py b/scripts/exglobal_atmens_analysis_letkf.py index 45b06524fe..dea9ace5b8 100755 --- a/scripts/exglobal_atmens_analysis_letkf.py +++ b/scripts/exglobal_atmens_analysis_letkf.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # exglobal_atmens_analysis_letkf.py # This script creates an AtmEnsAnalysis object -# and runs the execute method which executes -# the global atm local ensemble analysis +# and initializes and runs the full JEDI LETKF +# application import os from wxflow import Logger, cast_strdict_as_dtypedict @@ -18,7 +18,10 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atmens analysis task - AtmEnsAnl = AtmEnsAnalysis(config, 'atmensanlletkf') + AtmEnsAnl = AtmEnsAnalysis(config) + + # Initalize JEDI full ensemble DA application + AtmEnsAnl.initialize_letkf() # Execute the JEDI ensemble DA analysis - AtmEnsAnl.execute(config.APRUN_ATMENSANLLETKF, ['fv3jedi', 'localensembleda']) + AtmEnsAnl.execute('atmensanlletkf') diff --git a/scripts/exglobal_atmens_analysis_obs.py b/scripts/exglobal_atmens_analysis_obs.py index c701f8cb4e..b09c67703f 100755 --- a/scripts/exglobal_atmens_analysis_obs.py +++ b/scripts/exglobal_atmens_analysis_obs.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # exglobal_atmens_analysis_obs.py # This script creates an AtmEnsAnalysis object -# and runs the execute method -# which executes the global atm local ensemble analysis in observer mode +# and runs the execute method which runs the JEDI LETKF +# application in observer mode import os from wxflow import Logger, cast_strdict_as_dtypedict @@ -18,7 +18,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atmens analysis task - AtmEnsAnl = AtmEnsAnalysis(config, 'atmensanlobs') + AtmEnsAnl = AtmEnsAnalysis(config) - # Initialize and execute JEDI ensembler DA analysis in observer mode - AtmEnsAnl.execute(config.APRUN_ATMENSANLOBS, ['fv3jedi', 'localensembleda']) + # Execute JEDI ensembler DA analysis in observer mode + AtmEnsAnl.execute('atmensanlobs') diff --git a/scripts/exglobal_atmens_analysis_sol.py b/scripts/exglobal_atmens_analysis_sol.py index be78e694b1..85dc228a5a 100755 --- a/scripts/exglobal_atmens_analysis_sol.py +++ b/scripts/exglobal_atmens_analysis_sol.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # exglobal_atmens_analysis_sol.py # This script creates an AtmEnsAnalysis object -# and runs the execute method -# which executes the global atm local ensemble analysis in solver mode +# and runs the execute method which runs the JEDI LETKF +# application in solver mode import os from wxflow import Logger, cast_strdict_as_dtypedict @@ -18,8 +18,7 @@ config = cast_strdict_as_dtypedict(os.environ) # Instantiate the atmens analysis task - AtmEnsAnl = AtmEnsAnalysis(config, 'atmensanlsol') + AtmEnsAnl = AtmEnsAnalysis(config) - # Initialize and execute JEDI ensemble DA analysis in solver mode - AtmEnsAnl.initialize_jedi() - AtmEnsAnl.execute(config.APRUN_ATMENSANLSOL, ['fv3jedi', 'localensembleda']) + # Execute JEDI ensemble DA analysis in solver mode + AtmEnsAnl.execute('atmensanlsol') diff --git a/sorc/gdas.cd b/sorc/gdas.cd index 764f58cebd..e514b92656 160000 --- a/sorc/gdas.cd +++ b/sorc/gdas.cd @@ -1 +1 @@ -Subproject commit 764f58cebdf64f3695d89538994a50183e5884d9 +Subproject commit e514b926561bfc8fa3de741876505aff74255c95 diff --git a/ush/python/pygfs/jedi/jedi.py b/ush/python/pygfs/jedi/jedi.py index 415a0a3c08..2806ba4bce 100644 --- a/ush/python/pygfs/jedi/jedi.py +++ b/ush/python/pygfs/jedi/jedi.py @@ -4,145 +4,190 @@ import tarfile from logging import getLogger from typing import List, Dict, Any, Optional +from pprint import pformat from jcb import render -from wxflow import (AttrDict, - FileHandler, +from wxflow import (AttrDict, FileHandler, Task, Executable, chdir, rm_p, - parse_j2yaml, + parse_j2yaml, save_as_yaml, logit, - Task, - Executable, WorkflowException) logger = getLogger(__name__.split('.')[-1]) +required_jedi_keys = ['rundir', 'exe_src', 'mpi_cmd'] +optional_jedi_keys = ['jedi_args', 'jcb_base_yaml', 'jcb_algo', 'jcb_algo_yaml'] + class Jedi: """ Class for initializing and executing JEDI applications """ + @logit(logger, name="Jedi") - def __init__(self, task_config: AttrDict, yaml_name: Optional[str] = None) -> None: + def __init__(self, config: Dict[str, Any]) -> None: """Constructor for JEDI objects This method will construct a Jedi object. This includes: - - save a copy of task_config for provenance - - set the default JEDI YAML and executable names - - set an empty AttrDict for the JEDI config - - set the default directory for J2-YAML templates + - create the jedi_config AttrDict and extend it with additional required entries + - save a copy of jedi_config Parameters ---------- - task_config: AttrDict - Attribute-dictionary of all configuration variables associated with a GDAS task. - yaml_name: str, optional - Name of YAML file for JEDI configuration + config: AttrDict + Attribute-dictionary of all configuration variables required for the Jedi class Returns ---------- None """ - # For provenance, save incoming task_config as a private attribute of JEDI object - self._task_config = task_config - - _exe_name = os.path.basename(task_config.JEDIEXE) - - self.exe = os.path.join(task_config.DATA, _exe_name) - if yaml_name: - self.yaml = os.path.join(task_config.DATA, yaml_name + '.yaml') - else: - self.yaml = os.path.join(task_config.DATA, os.path.splitext(_exe_name)[0] + '.yaml') - self.config = AttrDict() - self.j2tmpl_dir = os.path.join(task_config.PARMgfs, 'gdas') + # Make sure input dictionary for Jedi class constructor has the required keys + if 'yaml_name' not in config: + logger.error(f"FATAL ERROR: Key 'yaml_name' not found in config") + raise KeyError(f"FATAL ERROR: Key 'yaml_name' not found in config") + for key in required_jedi_keys: + if key not in config: + logger.error(f"FATAL ERROR: Required key '{key}' not found in config") + raise KeyError(f"FATAL ERROR: Required key '{key}' not found in config") + + # Create the configuration dictionary for JEDI object + local_dict = AttrDict( + { + 'exe': os.path.join(config.rundir, os.path.basename(config.exe_src)), + 'yaml': os.path.join(config.rundir, config.yaml_name + '.yaml'), + 'input_config': None + } + ) + self.jedi_config = AttrDict(**config, **local_dict) + + # Set optional keys in jedi_config to None if not already present + for key in optional_jedi_keys: + if key not in self.jedi_config: + self.jedi_config[key] = None + + # Save a copy of jedi_config + self._jedi_config = self.jedi_config.deepcopy() @logit(logger) - def set_config(self, task_config: AttrDict, algorithm: Optional[str] = None) -> AttrDict: - """Compile a JEDI configuration dictionary from a template file and save to a YAML file + def initialize(self, task_config: AttrDict) -> None: + """Initialize JEDI application + + This method will initialize a JEDI application. + This includes: + - generating JEDI input YAML config + - saving JEDI input YAML config to run directory + - linking the JEDI executable to run directory Parameters ---------- - task_config : AttrDict - Dictionary of all configuration variables associated with a GDAS task. - algorithm (optional) : str - Name of the algorithm used to generate the JEDI configuration dictionary. - It will override the algorithm set in the task_config.JCB_<>_YAML file. + task_config: AttrDict + Attribute-dictionary of all configuration variables associated with a GDAS task. Returns ---------- None """ - if 'JCB_BASE_YAML' in task_config.keys(): - # Step 1: Fill templates of the JCB base YAML file - jcb_config = parse_j2yaml(task_config.JCB_BASE_YAML, task_config) - - # Step 2: If algorithm is present then override the algorithm in the JEDI - # config. Otherwise, if the algorithm J2-YAML is present, fill - # its templates and merge. - if algorithm: - jcb_config['algorithm'] = algorithm - elif 'JCB_ALGO' in task_config.keys(): - jcb_config['algorithm'] = task_config.JCB_ALGO - elif 'JCB_ALGO_YAML' in task_config.keys(): - jcb_algo_config = parse_j2yaml(task_config.JCB_ALGO_YAML, task_config) - jcb_config.update(jcb_algo_config) - - # Step 3: Generate the JEDI YAML using JCB - self.config = render(jcb_config) - elif 'JEDIYAML' in task_config.keys(): - # Generate JEDI YAML without using JCB - self.config = parse_j2yaml(task_config.JEDIYAML, task_config, - searchpath=self.j2tmpl_dir) - else: - logger.exception(f"FATAL ERROR: Unable to compile JEDI configuration dictionary, ABORT!") - raise KeyError(f"FATAL ERROR: Task config must contain JCB_BASE_YAML or JEDIYAML") + # Render JEDI config dictionary + logger.info(f"Generating JEDI YAML config: {self.jedi_config.yaml}") + self.jedi_config.input_config = self.render_jcb(task_config) + logger.debug(f"JEDI config:\n{pformat(self.jedi_config.input_config)}") + + # Save JEDI config dictionary to YAML in run directory + logger.debug(f"Writing JEDI YAML config to: {self.jedi_config.yaml}") + save_as_yaml(self.jedi_config.input_config, self.jedi_config.yaml) + + # Link JEDI executable to run directory + logger.info(f"Linking JEDI executable {self.jedi_config.exe_src} to {self.jedi_config.exe}") + self.link_exe() @logit(logger) - def execute(self, task_config: AttrDict, aprun_cmd: str, jedi_args: Optional[List] = None) -> None: + def execute(self) -> None: """Execute JEDI application Parameters ---------- - task_config: AttrDict - Attribute-dictionary of all configuration variables associated with a GDAS task. - aprun_cmd: str - String comprising the run command for the JEDI executable. - jedi_args (optional): List - List of strings comprising optional input arguments for the JEDI executable. + None Returns ---------- - jedi_config: AttrDict - Attribute-dictionary of JEDI configuration rendered from a template. + None """ - chdir(task_config.DATA) + chdir(self.jedi_config.rundir) - exec_cmd = Executable(aprun_cmd) - exec_cmd.add_default_arg(self.exe) - if jedi_args: - for arg in jedi_args: + exec_cmd = Executable(self.jedi_config.mpi_cmd) + exec_cmd.add_default_arg(self.jedi_config.exe) + if self.jedi_config.jedi_args is not None: + for arg in self.jedi_config.jedi_args: exec_cmd.add_default_arg(arg) - exec_cmd.add_default_arg(self.yaml) + exec_cmd.add_default_arg(self.jedi_config.yaml) + logger.info(f"Executing {exec_cmd}") try: exec_cmd() except OSError: + logger.error(f"FATAL ERROR: Failed to execute {exec_cmd}") raise OSError(f"FATAL ERROR: Failed to execute {exec_cmd}") except Exception: + logger.error(f"FATAL ERROR: An error occurred during execution of {exec_cmd}") raise WorkflowException(f"FATAL ERROR: An error occurred during execution of {exec_cmd}") - @staticmethod @logit(logger) - def link_exe(task_config: AttrDict) -> None: + def render_jcb(self, task_config: AttrDict, algorithm: Optional[str] = None) -> AttrDict: + """Compile a JEDI configuration dictionary from a template file and save to a YAML file + + Parameters + ---------- + task_config : AttrDict + Dictionary of all configuration variables associated with a GDAS task. + algorithm (optional) : str + Name of the algorithm used to generate the JEDI configuration dictionary. + It will override the algorithm set in the jedi_config.jcb_algo_yaml file. + + Returns + ---------- + jedi_input_config: AttrDict + Attribute-dictionary of JEDI configuration rendered from a template. + """ + + # Fill JCB base YAML template and build JCB config dictionary + if self.jedi_config.jcb_base_yaml is not None: + jcb_config = parse_j2yaml(self.jedi_config.jcb_base_yaml, task_config) + else: + logger.error(f"FATAL ERROR: JCB base YAML must be specified in order to render YAML using JCB") + raise KeyError(f"FATAL ERROR: JCB base YAML must be specified in order to render YAML using JCB") + + # Add JCB algorithm YAML, if it exists, to JCB config dictionary + if self.jedi_config.jcb_algo_yaml is not None: + jcb_config.update(parse_j2yaml(self.jedi_config.jcb_algo_yaml, task_config)) + + # Set algorithm in JCB config dictionary + if algorithm is not None: + jcb_config['algorithm'] = algorithm + elif self.jedi_config.jcb_algo is not None: + jcb_config['algorithm'] = self.jedi_config.jcb_algo + elif 'algorithm' in jcb_config: + pass + else: + logger.error(f"FATAL ERROR: JCB algorithm must be specified as input to jedi.render_jcb(), " + + "in JEDI configuration dictionary as jcb_algo, or in JCB algorithm YAML") + raise Exception(f"FATAL ERROR: JCB algorithm must be specified as input to jedi.render_jcb(), " + + "in JEDI configuration dictionary as jcb_algo, or in JCB algorithm YAML") + + # Generate JEDI YAML config by rendering JCB config dictionary + jedi_input_config = render(jcb_config) + + return jedi_input_config + + @logit(logger) + def link_exe(self) -> None: """Link JEDI executable to run directory Parameters ---------- - task_config: AttrDict - Attribute-dictionary of all configuration variables associated with a GDAS task. + None Returns ---------- @@ -152,185 +197,156 @@ def link_exe(task_config: AttrDict) -> None: # TODO: linking is not permitted per EE2. # Needs work in JEDI to be able to copy the exec. [NOAA-EMC/GDASApp#1254] logger.warn("Linking is not permitted per EE2.") - exe_dest = os.path.join(task_config.DATA, os.path.basename(task_config.JEDIEXE)) - if os.path.exists(exe_dest): - rm_p(exe_dest) - os.symlink(task_config.JEDIEXE, exe_dest) + if not os.path.exists(self.jedi_config.exe): + os.symlink(self.jedi_config.exe_src, self.jedi_config.exe) + @staticmethod @logit(logger) - def get_obs_dict(self, task_config: AttrDict) -> Dict[str, Any]: - """Compile a dictionary of observation files to copy - - This method extracts 'observers' from the JEDI yaml and from that list, extracts a list of - observation files that are to be copied to the run directory - from the observation input directory + def get_jedi_dict(jedi_config_yaml: str, task_config: AttrDict, expected_block_names: Optional[list] = None): + """Get dictionary of Jedi objects from YAML specifying their configuration dictionaries Parameters ---------- - task_config: AttrDict - Attribute-dictionary of all configuration variables associated with a GDAS task. + jedi_config_yaml : str + path to YAML specifying configuration dictionaries for Jedi objects + task_config : str + attribute-dictionary of all configuration variables associated with a GDAS task + expected_block_names (optional) : str + list of names of blocks expected to be in jedi_config_yaml YAML file Returns ---------- - obs_dict: Dict - a dictionary containing the list of observation files to copy for FileHandler + None """ - observations = find_value_in_nested_dict(self.config, 'observations') + # Initialize dictionary of Jedi objects + jedi_dict = AttrDict() - copylist = [] - for ob in observations['observers']: - obfile = ob['obs space']['obsdatain']['engine']['obsfile'] - basename = os.path.basename(obfile) - copylist.append([os.path.join(task_config.COM_OBS, basename), obfile]) - obs_dict = { - 'mkdir': [os.path.join(task_config.DATA, 'obs')], - 'copy': copylist - } - return obs_dict + # Parse J2-YAML file for dictionary of JEDI configuration dictionaries + jedi_config_dict = parse_j2yaml(jedi_config_yaml, task_config) - @logit(logger) - def get_bias_dict(self, task_config: AttrDict, bias_file) -> Dict[str, Any]: - """Compile a dictionary of observation files to copy + # Loop through dictionary of Jedi configuration dictionaries + for block_name in jedi_config_dict: + # yaml_name key is set to name for this block + jedi_config_dict[block_name]['yaml_name'] = block_name + + # Make sure all required keys present + for key in required_jedi_keys: + if key not in jedi_config_dict[block_name]: + logger.error(f"FATAL ERROR: Required key {key} not found in {jedi_config_yaml} for block {block_name}.") + raise KeyError(f"FATAL ERROR: Required key {key} not found in {jedi_config_yaml} for block {block_name}.") + + # Set optional keys to None + for key in optional_jedi_keys: + if key not in jedi_config_dict[block_name]: + jedi_config_dict[block_name][key] = None + + # Construct JEDI object + jedi_dict[block_name] = Jedi(jedi_config_dict[block_name]) - This method extracts 'observers' from the JEDI yaml and determines from that list - if bias correction tar files are to be copied to the run directory - from the component directory. + # Make sure jedi_dict has the blocks we expect + if expected_block_names: + for block_name in expected_block_names: + if block_name not in jedi_dict: + logger.error(f"FATAL ERROR: Expected block {block_name} not present {jedi_config_yaml}") + raise Exception(f"FATAL ERROR: Expected block {block_name} not present {jedi_config_yaml}") + if len(jedi_dict) > len(expected_block_names): + logger.error(f"FATAL ERROR: {jedi_config_yaml} specifies more Jedi objects than expected.") + raise Exception(f"FATAL ERROR: {jedi_config_yaml} specifies more Jedi objects than expected.") + + # Return dictionary of JEDI objects + return jedi_dict + + @staticmethod + @logit(logger) + def remove_redundant(input_list: List) -> List: + """Remove reduncancies from list with possible redundant, non-mutable elements Parameters ---------- - task_config: AttrDict - Attribute-dictionary of all configuration variables associated with a GDAS task. - bias_file - name of bias correction tar file + input_list : List + List with possible redundant, non-mutable elements Returns ---------- - bias_dict: Dict - a dictionary containing the list of observation bias files to copy for FileHandler + output_list : List + Input list but with redundancies removed """ - observations = find_value_in_nested_dict(self.config, 'observations') - - copylist = [] - for ob in observations['observers']: - if 'obs bias' in ob.keys(): - obfile = ob['obs bias']['input file'] - obdir = os.path.dirname(obfile) - basename = os.path.basename(obfile) - prefix = '.'.join(basename.split('.')[:-3]) - bfile = f"{prefix}.{bias_file}" - tar_file = os.path.join(obdir, bfile) - copylist.append([os.path.join(task_config.VarBcDir, bfile), tar_file]) - break + output_list = [] + for item in input_list: + if item not in output_list: + output_list.append(item) - bias_dict = { - 'mkdir': [os.path.join(task_config.DATA, 'bc')], - 'copy': copylist - } - - return bias_dict + return output_list @staticmethod @logit(logger) - def extract_tar(tar_file: str) -> None: - """Extract files from a tarball + def extract_tar_from_filehandler_dict(filehandler_dict) -> None: + """Extract tarballs from FileHandler input dictionary - This method extract files from a tarball + This method extracts files from tarballs specified in a FileHander + input dictionary for the 'copy' action. Parameters ---------- - tar_file - path/name of tarball + filehandler_dict + Input dictionary for FileHandler Returns ---------- None """ - # extract files from tar file - tar_path = os.path.dirname(tar_file) - try: - with tarfile.open(tar_file, "r") as tarball: - tarball.extractall(path=tar_path) - logger.info(f"Extract {tarball.getnames()}") - except tarfile.ReadError as err: - if tarfile.is_tarfile(tar_file): - logger.error(f"FATAL ERROR: {tar_file} could not be read") - raise tarfile.ReadError(f"FATAL ERROR: unable to read {tar_file}") + for item in filehandler_dict['copy']: + # Use the filename from the destination entry if it's a file path + # Otherwise, it's a directory, so use the source entry filename + if os.path.isfile(item[1]): + filename = os.path.basename(item[1]) else: - logger.info() - except tarfile.ExtractError as err: - logger.exception(f"FATAL ERROR: unable to extract from {tar_file}") - raise tarfile.ExtractError("FATAL ERROR: unable to extract from {tar_file}") + filename = os.path.basename(item[0]) + + # Check if file is a tar ball + if os.path.splitext(filename)[1] == '.tar': + tar_file = f"{os.path.dirname(item[1])}/{filename}" + + # Extract tarball + logger.info(f"Extract files from {tar_file}") + extract_tar(tar_file) @logit(logger) -def find_value_in_nested_dict(nested_dict: Dict, target_key: str) -> Any: - """ - Recursively search through a nested dictionary and return the value for the target key. - This returns the first target key it finds. So if a key exists in a subsequent - nested dictionary, it will not be found. +def extract_tar(tar_file: str) -> None: + """Extract files from a tarball + + This method extract files from a tarball Parameters ---------- - nested_dict : Dict - Dictionary to search - target_key : str - Key to search for + tar_file + path/name of tarball Returns - ------- - Any - Value of the target key - - Raises - ------ - KeyError - If key is not found in dictionary - - TODO: if this gives issues due to landing on an incorrect key in the nested - dictionary, we will have to implement a more concrete method to search for a key - given a more complete address. See resolved conversations in PR 2387 - - # Example usage: - nested_dict = { - 'a': { - 'b': { - 'c': 1, - 'd': { - 'e': 2, - 'f': 3 - } - }, - 'g': 4 - }, - 'h': { - 'i': 5 - }, - 'j': { - 'k': 6 - } - } - - user_key = input("Enter the key to search for: ") - result = find_value_in_nested_dict(nested_dict, user_key) + ---------- + None """ - if not isinstance(nested_dict, dict): - raise TypeError(f"Input is not of type(dict)") - - result = nested_dict.get(target_key) - if result is not None: - return result - - for value in nested_dict.values(): - if isinstance(value, dict): - try: - result = find_value_in_nested_dict(value, target_key) - if result is not None: - return result - except KeyError: - pass - - raise KeyError(f"Key '{target_key}' not found in the nested dictionary") + # extract files from tar file + tar_path = os.path.dirname(tar_file) + try: + with tarfile.open(tar_file, "r") as tarball: + tarball.extractall(path=tar_path) + logger.info(f"Extract {tarball.getnames()}") + except tarfile.FileExistsError as err: + logger.exception(f"FATAL ERROR: {tar_file} does not exist") + raise tarfile.FileExistsError(f"FATAL ERROR: {tar_file} does not exist") + except tarfile.ReadError as err: + if tarfile.is_tarfile(tar_file): + logger.error(f"FATAL ERROR: tar archive {tar_file} could not be read") + raise tarfile.ReadError(f"FATAL ERROR: tar archive {tar_file} could not be read") + else: + logger.error(f"FATAL ERROR: {tar_file} is not a tar archive") + raise tarfile.ReadError(f"FATAL ERROR: {tar_file} is not a tar archive") + except tarfile.ExtractError as err: + logger.exception(f"FATAL ERROR: unable to extract from {tar_file}") + raise tarfile.ExtractError("FATAL ERROR: unable to extract from {tar_file}") diff --git a/ush/python/pygfs/task/atm_analysis.py b/ush/python/pygfs/task/atm_analysis.py index 5f67ea9d72..5c3aa0f764 100644 --- a/ush/python/pygfs/task/atm_analysis.py +++ b/ush/python/pygfs/task/atm_analysis.py @@ -6,8 +6,7 @@ import tarfile from logging import getLogger from pprint import pformat -from typing import Optional, Dict, Any - +from typing import Any, Dict, List, Optional from wxflow import (AttrDict, FileHandler, add_to_datetime, to_fv3time, to_timedelta, to_YMDH, @@ -24,20 +23,18 @@ class AtmAnalysis(Task): Class for JEDI-based global atm analysis tasks """ @logit(logger, name="AtmAnalysis") - def __init__(self, config: Dict[str, Any], yaml_name: Optional[str] = None): + def __init__(self, config: Dict[str, Any]): """Constructor global atm analysis task This method will construct a global atm analysis task. This includes: - extending the task_config attribute AttrDict to include parameters required for this task - - instantiate the Jedi attribute object + - instantiate the Jedi attribute objects Parameters ---------- config: Dict dictionary object containing task configuration - yaml_name: str, optional - name of YAML file for JEDI configuration Returns ---------- @@ -73,46 +70,17 @@ def __init__(self, config: Dict[str, Any], yaml_name: Optional[str] = None): # Extend task_config with local_dict self.task_config = AttrDict(**self.task_config, **local_dict) - # Create JEDI object - self.jedi = Jedi(self.task_config, yaml_name) - - @logit(logger) - def initialize_jedi(self): - """Initialize JEDI application - - This method will initialize a JEDI application used in the global atm analysis. - This includes: - - generating and saving JEDI YAML config - - linking the JEDI executable - - Parameters - ---------- - None - - Returns - ---------- - None - """ - - # get JEDI-to-FV3 increment converter config and save to YAML file - logger.info(f"Generating JEDI YAML config: {self.jedi.yaml}") - self.jedi.set_config(self.task_config) - logger.debug(f"JEDI config:\n{pformat(self.jedi.config)}") - - # save JEDI config to YAML file - logger.debug(f"Writing JEDI YAML config to: {self.jedi.yaml}") - save_as_yaml(self.jedi.config, self.jedi.yaml) - - # link JEDI executable - logger.info(f"Linking JEDI executable {self.task_config.JEDIEXE} to {self.jedi.exe}") - self.jedi.link_exe(self.task_config) + # Create dictionary of Jedi objects + expected_keys = ['atmanlvar', 'atmanlfv3inc'] + self.jedi_dict = Jedi.get_jedi_dict(self.task_config.JEDI_CONFIG_YAML, self.task_config, expected_keys) @logit(logger) - def initialize_analysis(self) -> None: + def initialize(self) -> None: """Initialize a global atm analysis This method will initialize a global atm analysis. This includes: + - initialize JEDI applications - staging observation files - staging bias correction files - staging CRTM fix files @@ -129,26 +97,33 @@ def initialize_analysis(self) -> None: ---------- None """ - super().initialize() + + # initialize JEDI variational application + logger.info(f"Initializing JEDI variational DA application") + self.jedi_dict['atmanlvar'].initialize(self.task_config) + + # initialize JEDI FV3 increment conversion application + logger.info(f"Initializing JEDI FV3 increment conversion application") + self.jedi_dict['atmanlfv3inc'].initialize(self.task_config) # stage observations - logger.info(f"Staging list of observation files generated from JEDI config") - obs_dict = self.jedi.get_obs_dict(self.task_config) + logger.info(f"Staging list of observation files") + obs_dict = self.jedi_dict['atmanlvar'].render_jcb(self.task_config, 'atm_obs_staging') FileHandler(obs_dict).sync() logger.debug(f"Observation files:\n{pformat(obs_dict)}") # stage bias corrections - logger.info(f"Staging list of bias correction files generated from JEDI config") - self.task_config.VarBcDir = f"{self.task_config.COM_ATMOS_ANALYSIS_PREV}" - bias_file = f"rad_varbc_params.tar" - bias_dict = self.jedi.get_bias_dict(self.task_config, bias_file) - FileHandler(bias_dict).sync() - logger.debug(f"Bias correction files:\n{pformat(bias_dict)}") - - # extract bias corrections - tar_file = os.path.join(self.task_config.DATA, 'obs', f"{self.task_config.GPREFIX}{bias_file}") - logger.info(f"Extract bias correction files from {tar_file}") - self.jedi.extract_tar(tar_file) + logger.info(f"Staging list of bias correction files") + bias_dict = self.jedi_dict['atmanlvar'].render_jcb(self.task_config, 'atm_bias_staging') + if bias_dict['copy'] is None: + logger.info(f"No bias correction files to stage") + else: + bias_dict['copy'] = Jedi.remove_redundant(bias_dict['copy']) + FileHandler(bias_dict).sync() + logger.debug(f"Bias correction files:\n{pformat(bias_dict)}") + + # extract bias corrections + Jedi.extract_tar_from_filehandler_dict(bias_dict) # stage CRTM fix files logger.info(f"Staging CRTM fix files from {self.task_config.CRTM_FIX_YAML}") @@ -193,29 +168,20 @@ def initialize_analysis(self) -> None: FileHandler({'mkdir': newdirs}).sync() @logit(logger) - def execute(self, aprun_cmd: str, jedi_args: Optional[str] = None) -> None: - """Run JEDI executable - - This method will run JEDI executables for the global atm analysis + def execute(self, jedi_dict_key: str) -> None: + """Execute JEDI application of atm analysis Parameters ---------- - aprun_cmd : str - Run command for JEDI application on HPC system - jedi_args : List - List of additional optional arguments for JEDI application + jedi_dict_key + key specifying particular Jedi object in self.jedi_dict Returns ---------- None """ - if jedi_args: - logger.info(f"Executing {self.jedi.exe} {' '.join(jedi_args)} {self.jedi.yaml}") - else: - logger.info(f"Executing {self.jedi.exe} {self.jedi.yaml}") - - self.jedi.execute(self.task_config, aprun_cmd, jedi_args) + self.jedi_dict[jedi_dict_key].execute() @logit(logger) def finalize(self) -> None: diff --git a/ush/python/pygfs/task/atmens_analysis.py b/ush/python/pygfs/task/atmens_analysis.py index 4b2f8ebbf4..81cae238bb 100644 --- a/ush/python/pygfs/task/atmens_analysis.py +++ b/ush/python/pygfs/task/atmens_analysis.py @@ -28,20 +28,18 @@ class AtmEnsAnalysis(Task): Class for JEDI-based global atmens analysis tasks """ @logit(logger, name="AtmEnsAnalysis") - def __init__(self, config: Dict[str, Any], yaml_name: Optional[str] = None): + def __init__(self, config: Dict[str, Any]): """Constructor global atmens analysis task This method will construct a global atmens analysis task. This includes: - extending the task_config attribute AttrDict to include parameters required for this task - - instantiate the Jedi attribute object + - instantiate the Jedi attribute objects Parameters ---------- config: Dict dictionary object containing task configuration - yaml_name: str, optional - name of YAML file for JEDI configuration Returns ---------- @@ -73,46 +71,17 @@ def __init__(self, config: Dict[str, Any], yaml_name: Optional[str] = None): # Extend task_config with local_dict self.task_config = AttrDict(**self.task_config, **local_dict) - # Create JEDI object - self.jedi = Jedi(self.task_config, yaml_name) + # Create dictionary of JEDI objects + expected_keys = ['atmensanlobs', 'atmensanlsol', 'atmensanlfv3inc', 'atmensanlletkf'] + self.jedi_dict = Jedi.get_jedi_dict(self.task_config.JEDI_CONFIG_YAML, self.task_config, expected_keys) @logit(logger) - def initialize_jedi(self): - """Initialize JEDI application - - This method will initialize a JEDI application used in the global atmens analysis. - This includes: - - generating and saving JEDI YAML config - - linking the JEDI executable - - Parameters - ---------- - None - - Returns - ---------- - None - """ - - # get JEDI config and save to YAML file - logger.info(f"Generating JEDI config: {self.jedi.yaml}") - self.jedi.set_config(self.task_config) - logger.debug(f"JEDI config:\n{pformat(self.jedi.config)}") - - # save JEDI config to YAML file - logger.info(f"Writing JEDI config to YAML file: {self.jedi.yaml}") - save_as_yaml(self.jedi.config, self.jedi.yaml) - - # link JEDI-to-FV3 increment converter executable - logger.info(f"Linking JEDI executable {self.task_config.JEDIEXE} to {self.jedi.exe}") - self.jedi.link_exe(self.task_config) - - @logit(logger) - def initialize_analysis(self) -> None: + def initialize(self) -> None: """Initialize a global atmens analysis This method will initialize a global atmens analysis. This includes: + - initialize JEDI applications - staging observation files - staging bias correction files - staging CRTM fix files @@ -128,26 +97,34 @@ def initialize_analysis(self) -> None: ---------- None """ - super().initialize() + + # initialize JEDI LETKF observer application + logger.info(f"Initializing JEDI LETKF observer application") + self.jedi_dict['atmensanlobs'].initialize(self.task_config) + + # initialize JEDI LETKF solver application + logger.info(f"Initializing JEDI LETKF solver application") + self.jedi_dict['atmensanlsol'].initialize(self.task_config) + + # initialize JEDI FV3 increment conversion application + logger.info(f"Initializing JEDI FV3 increment conversion application") + self.jedi_dict['atmensanlfv3inc'].initialize(self.task_config) # stage observations - logger.info(f"Staging list of observation files generated from JEDI config") - obs_dict = self.jedi.get_obs_dict(self.task_config) + logger.info(f"Staging list of observation files") + obs_dict = self.jedi_dict['atmensanlobs'].render_jcb(self.task_config, 'atm_obs_staging') FileHandler(obs_dict).sync() logger.debug(f"Observation files:\n{pformat(obs_dict)}") # stage bias corrections - logger.info(f"Staging list of bias correction files generated from JEDI config") - self.task_config.VarBcDir = f"{self.task_config.COM_ATMOS_ANALYSIS_PREV}" - bias_file = f"rad_varbc_params.tar" - bias_dict = self.jedi.get_bias_dict(self.task_config, bias_file) + logger.info(f"Staging list of bias correction files") + bias_dict = self.jedi_dict['atmensanlobs'].render_jcb(self.task_config, 'atm_bias_staging') + bias_dict['copy'] = Jedi.remove_redundant(bias_dict['copy']) FileHandler(bias_dict).sync() logger.debug(f"Bias correction files:\n{pformat(bias_dict)}") # extract bias corrections - tar_file = os.path.join(self.task_config.DATA, 'obs', f"{self.task_config.GPREFIX}{bias_file}") - logger.info(f"Extract bias correction files from {tar_file}") - self.jedi.extract_tar(tar_file) + Jedi.extract_tar_from_filehandler_dict(bias_dict) # stage CRTM fix files logger.info(f"Staging CRTM fix files from {self.task_config.CRTM_FIX_YAML}") @@ -176,28 +153,38 @@ def initialize_analysis(self) -> None: FileHandler({'mkdir': newdirs}).sync() @logit(logger) - def execute(self, aprun_cmd: str, jedi_args: Optional[str] = None) -> None: - """Run JEDI executable + def initialize_letkf(self) -> None: + """Initialize a global atmens analysis - This method will run JEDI executables for the global atmens analysis + Note: This would normally be done in AtmEnsAnalysis.initialize(), but that method + now initializes the split observer-solver. This method is just for testing. Parameters ---------- - aprun_cmd : str - Run command for JEDI application on HPC system - jedi_args : List - List of additional optional arguments for JEDI application + None + Returns ---------- None """ - if jedi_args: - logger.info(f"Executing {self.jedi.exe} {' '.join(jedi_args)} {self.jedi.yaml}") - else: - logger.info(f"Executing {self.jedi.exe} {self.jedi.yaml}") + self.jedi_dict['atmensanlletkf'].initialize(self.task_config) + + @logit(logger) + def execute(self, jedi_dict_key: str) -> None: + """Execute JEDI application of atmens analysis + + Parameters + ---------- + jedi_dict_key + key specifying a particular Jedi object in self.jedi_dict + + Returns + ---------- + None + """ - self.jedi.execute(self.task_config, aprun_cmd, jedi_args) + self.jedi_dict[jedi_dict_key].execute() @logit(logger) def finalize(self) -> None: diff --git a/ush/python/pygfs/task/marine_analysis.py b/ush/python/pygfs/task/marine_analysis.py index e7b7b5e948..75cc28c7b3 100644 --- a/ush/python/pygfs/task/marine_analysis.py +++ b/ush/python/pygfs/task/marine_analysis.py @@ -15,7 +15,7 @@ from wxflow import (AttrDict, FileHandler, add_to_datetime, to_timedelta, to_YMD, - parse_j2yaml, + parse_j2yaml, parse_yaml, logit, Executable, Task, @@ -200,7 +200,7 @@ def _prep_variational_yaml(self: Task) -> None: # Add the things to the envconfig in order to template JCB files envconfig_jcb['PARMgfs'] = self.task_config.PARMgfs - envconfig_jcb['nmem_ens'] = self.task_config.NMEM_ENS + envconfig_jcb['NMEM_ENS'] = self.task_config.NMEM_ENS envconfig_jcb['berror_model'] = 'marine_background_error_static_diffusion' if self.task_config.NMEM_ENS > 3: envconfig_jcb['berror_model'] = 'marine_background_error_hybrid_diffusion_diffusion' @@ -234,6 +234,9 @@ def _prep_variational_yaml(self: Task) -> None: jcb_config['window_begin'] = self.task_config.MARINE_WINDOW_BEGIN.strftime('%Y-%m-%dT%H:%M:%SZ') jcb_config['window_middle'] = self.task_config.MARINE_WINDOW_MIDDLE.strftime('%Y-%m-%dT%H:%M:%SZ') + # Current hack so that this is not done directly in the JCB base yaml + jcb_config['marine_pseudo_model_states'] = parse_yaml('bkg_list.yaml') + # Render the full JEDI configuration file using JCB jedi_config = render(jcb_config) diff --git a/ush/python/pygfs/task/marine_bmat.py b/ush/python/pygfs/task/marine_bmat.py index a4a5b4f144..a21699227b 100644 --- a/ush/python/pygfs/task/marine_bmat.py +++ b/ush/python/pygfs/task/marine_bmat.py @@ -9,21 +9,40 @@ FileHandler, add_to_datetime, to_timedelta, chdir, - parse_j2yaml, + parse_j2yaml, save_as_yaml, logit, Executable, Task) +from pygfs.jedi import Jedi + logger = getLogger(__name__.split('.')[-1]) class MarineBMat(Task): """ - Class for global marine B-matrix tasks + Class for global marine B-matrix tasks. """ @logit(logger, name="MarineBMat") def __init__(self, config): + """Constructor for marine B-matrix task + + This method will construct the marine B-matrix task object + This includes: + - extending the task_config AttrDict to include parameters required for this task + - instantiate the Jedi attribute objects + + Parameters + ---------- + config: Dict + dictionary object containing task configuration + + Returns + ---------- + None + """ super().__init__(config) + _home_gdas = os.path.join(self.task_config.HOMEgfs, 'sorc', 'gdas.cd') _calc_scale_exec = os.path.join(self.task_config.HOMEgfs, 'ush', 'soca', 'calc_scales.py') _window_begin = add_to_datetime(self.task_config.current_cycle, @@ -38,18 +57,26 @@ def __init__(self, config): local_dict = AttrDict( { 'PARMsoca': os.path.join(self.task_config.PARMgfs, 'gdas', 'soca'), + 'CALC_SCALE_EXEC': _calc_scale_exec, 'MARINE_WINDOW_BEGIN': _window_begin, - 'MARINE_WINDOW_END': _window_end, 'MARINE_WINDOW_MIDDLE': self.task_config.current_cycle, + 'MARINE_WINDOW_END': _window_end, + 'MARINE_WINDOW_LENGTH': f"PT{self.task_config['assim_freq']}H", 'ENSPERT_RELPATH': _enspert_relpath, - 'CALC_SCALE_EXEC': _calc_scale_exec, - 'APREFIX': f"{self.task_config.RUN}.t{self.task_config.cyc:02d}z." + 'MOM6_LEVS': mdau.get_mom6_levels(str(self.task_config.OCNRES)), + 'APREFIX': f"{self.task_config.RUN}.t{self.task_config.cyc:02d}z.", + 'OPREFIX': f"{self.task_config.RUN}.t{self.task_config.cyc:02d}z." } ) # Extend task_config with local_dict self.task_config = AttrDict(**self.task_config, **local_dict) + # Create dictionary of Jedi objects + expected_keys = ['gridgen', 'soca_diagb', 'soca_parameters_diffusion_vt', 'soca_setcorscales', + 'soca_parameters_diffusion_hz', 'soca_ensb', 'soca_ensweights'] + self.jedi_dict = Jedi.get_jedi_dict(self.task_config.JEDI_CONFIG_YAML, self.task_config, expected_keys) + @logit(logger) def initialize(self: Task) -> None: """Initialize a global B-matrix @@ -60,10 +87,18 @@ def initialize(self: Task) -> None: - staging SOCA fix files - staging static ensemble members (optional) - staging ensemble members (optional) - - generating the YAML files for the JEDI and GDASApp executables + - initializing the soca_vtscales Python script + - initializing the JEDI applications - creating output directories + + Parameters + ---------- + None + + Returns + ---------- + None """ - super().initialize() # stage fix files logger.info(f"Staging SOCA fix files from {self.task_config.SOCA_INPUT_FIX_DIR}") @@ -78,54 +113,32 @@ def initialize(self: Task) -> None: bkg_list = parse_j2yaml(self.task_config.MARINE_DET_STAGE_BKG_YAML_TMPL, self.task_config) FileHandler(bkg_list).sync() - # stage the soca utility yamls (gridgen, fields and ufo mapping yamls) + # stage the soca utility yamls (fields and ufo mapping yamls) logger.info(f"Staging SOCA utility yaml files") soca_utility_list = parse_j2yaml(self.task_config.MARINE_UTILITY_YAML_TMPL, self.task_config) FileHandler(soca_utility_list).sync() - # generate the variance partitioning YAML file - logger.info(f"Generate variance partitioning YAML file from {self.task_config.BERROR_DIAGB_YAML}") - diagb_config = parse_j2yaml(path=self.task_config.BERROR_DIAGB_YAML, data=self.task_config) - diagb_config.save(os.path.join(self.task_config.DATA, 'soca_diagb.yaml')) - - # generate the vertical decorrelation scale YAML file - logger.info(f"Generate the vertical correlation scale YAML file from {self.task_config.BERROR_VTSCALES_YAML}") - vtscales_config = parse_j2yaml(path=self.task_config.BERROR_VTSCALES_YAML, data=self.task_config) - vtscales_config.save(os.path.join(self.task_config.DATA, 'soca_vtscales.yaml')) - - # generate vertical diffusion scale YAML file - logger.info(f"Generate vertical diffusion YAML file from {self.task_config.BERROR_DIFFV_YAML}") - diffvz_config = parse_j2yaml(path=self.task_config.BERROR_DIFFV_YAML, data=self.task_config) - diffvz_config.save(os.path.join(self.task_config.DATA, 'soca_parameters_diffusion_vt.yaml')) - - # generate the horizontal diffusion YAML files - if True: # TODO(G): skip this section once we have optimized the scales - # stage the correlation scale configuration - logger.info(f"Generate correlation scale YAML file from {self.task_config.BERROR_HZSCALES_YAML}") - FileHandler({'copy': [[self.task_config.BERROR_HZSCALES_YAML, - os.path.join(self.task_config.DATA, 'soca_setcorscales.yaml')]]}).sync() - - # generate horizontal diffusion scale YAML file - logger.info(f"Generate horizontal diffusion scale YAML file from {self.task_config.BERROR_DIFFH_YAML}") - diffhz_config = parse_j2yaml(path=self.task_config.BERROR_DIFFH_YAML, data=self.task_config) - diffhz_config.save(os.path.join(self.task_config.DATA, 'soca_parameters_diffusion_hz.yaml')) + # initialize vtscales python script + vtscales_config = self.jedi_dict['soca_parameters_diffusion_vt'].render_jcb(self.task_config, 'soca_vtscales') + save_as_yaml(vtscales_config, os.path.join(self.task_config.DATA, 'soca_vtscales.yaml')) + FileHandler({'copy': [[os.path.join(self.task_config.CALC_SCALE_EXEC), + os.path.join(self.task_config.DATA, 'calc_scales.x')]]}).sync() + + # initialize JEDI applications + self.jedi_dict['gridgen'].initialize(self.task_config) + self.jedi_dict['soca_diagb'].initialize(self.task_config) + self.jedi_dict['soca_parameters_diffusion_vt'].initialize(self.task_config) + self.jedi_dict['soca_setcorscales'].initialize(self.task_config) + self.jedi_dict['soca_parameters_diffusion_hz'].initialize(self.task_config) + if self.task_config.DOHYBVAR == "YES" or self.task_config.NMEM_ENS > 2: + self.jedi_dict['soca_ensb'].initialize(self.task_config) + self.jedi_dict['soca_ensweights'].initialize(self.task_config) - # hybrid EnVAR case + # stage ensemble members for the hybrid background error if self.task_config.DOHYBVAR == "YES" or self.task_config.NMEM_ENS > 2: - # stage ensemble membersfiles for use in hybrid background error logger.debug(f"Stage ensemble members for the hybrid background error") mdau.stage_ens_mem(self.task_config) - # generate ensemble recentering/rebalancing YAML file - logger.debug("Generate ensemble recentering YAML file") - ensrecenter_config = parse_j2yaml(path=self.task_config.BERROR_ENS_RECENTER_YAML, data=self.task_config) - ensrecenter_config.save(os.path.join(self.task_config.DATA, 'soca_ensb.yaml')) - - # generate ensemble weights YAML file - logger.debug("Generate hybrid-weigths YAML file") - hybridweights_config = parse_j2yaml(path=self.task_config.BERROR_HYB_WEIGHTS_YAML, data=self.task_config) - hybridweights_config.save(os.path.join(self.task_config.DATA, 'soca_ensweights.yaml')) - # create the symbolic link to the static B-matrix directory link_target = os.path.join(self.task_config.DATAstaticb) link_name = os.path.join(self.task_config.DATA, 'staticb') @@ -134,130 +147,44 @@ def initialize(self: Task) -> None: os.symlink(link_target, link_name) @logit(logger) - def gridgen(self: Task) -> None: - # link gdas_soca_gridgen.x - mdau.link_executable(self.task_config, 'gdas_soca_gridgen.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_soca_gridgen.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('gridgen.yaml') - - mdau.run(exec_cmd) + def execute(self) -> None: + """Generate the full B-matrix - @logit(logger) - def variance_partitioning(self: Task) -> None: - # link the variance partitioning executable, gdas_soca_diagb.x - mdau.link_executable(self.task_config, 'gdas_soca_diagb.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_soca_diagb.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('soca_diagb.yaml') + This method will generate the full B-matrix according to the configuration. + This includes: + - running all JEDI application and Python scripts required to generate the B-matrix - mdau.run(exec_cmd) + Parameters + ---------- + None - @logit(logger) - def horizontal_diffusion(self: Task) -> None: - """Generate the horizontal diffusion coefficients + Returns + ---------- + None """ - # link the executable that computes the correlation scales, gdas_soca_setcorscales.x, - # and prepare the command to run it - mdau.link_executable(self.task_config, 'gdas_soca_setcorscales.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_soca_setcorscales.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('soca_setcorscales.yaml') - # create a files containing the correlation scales - mdau.run(exec_cmd) + self.jedi_dict['gridgen'].execute() - # link the executable that computes the correlation scales, gdas_soca_error_covariance_toolbox.x, - # and prepare the command to run it - mdau.link_executable(self.task_config, 'gdas_soca_error_covariance_toolbox.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_soca_error_covariance_toolbox.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('soca_parameters_diffusion_hz.yaml') + # variance partitioning + self.jedi_dict['soca_diagb'].execute() - # compute the coefficients of the diffusion operator - mdau.run(exec_cmd) + # horizontal diffusion + self.jedi_dict['soca_setcorscales'].execute() + self.jedi_dict['soca_parameters_diffusion_hz'].execute() - @logit(logger) - def vertical_diffusion(self: Task) -> None: - """Generate the vertical diffusion coefficients - """ - # compute the vertical correlation scales based on the MLD - FileHandler({'copy': [[os.path.join(self.task_config.CALC_SCALE_EXEC), - os.path.join(self.task_config.DATA, 'calc_scales.x')]]}).sync() + # vertical diffusion exec_cmd = Executable("python") exec_name = os.path.join(self.task_config.DATA, 'calc_scales.x') exec_cmd.add_default_arg(exec_name) exec_cmd.add_default_arg('soca_vtscales.yaml') mdau.run(exec_cmd) - # link the executable that computes the correlation scales, gdas_soca_error_covariance_toolbox.x, - # and prepare the command to run it - mdau.link_executable(self.task_config, 'gdas_soca_error_covariance_toolbox.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_soca_error_covariance_toolbox.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('soca_parameters_diffusion_vt.yaml') - - # compute the coefficients of the diffusion operator - mdau.run(exec_cmd) - - @logit(logger) - def ensemble_perturbations(self: Task) -> None: - """Generate the 3D ensemble of perturbation for the 3DEnVAR - - This method will generate ensemble perturbations re-balanced w.r.t the - deterministic background. - This includes: - - computing a storing the unbalanced ensemble perturbations' statistics - - recentering the ensemble members around the deterministic background and - accounting for the nonlinear steric recentering - - saving the recentered ensemble statistics - """ - mdau.link_executable(self.task_config, 'gdas_ens_handler.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_ens_handler.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('soca_ensb.yaml') + self.jedi_dict['soca_parameters_diffusion_vt'].execute() - # generate the ensemble perturbations - mdau.run(exec_cmd) - - @logit(logger) - def hybrid_weight(self: Task) -> None: - """Generate the hybrid weights for the 3DEnVAR - - This method will generate the 3D fields hybrid weights for the 3DEnVAR for each - variables. - TODO(G): Currently implemented for the specific case of the static ensemble members only - """ - mdau.link_executable(self.task_config, 'gdas_socahybridweights.x') - exec_cmd = Executable(self.task_config.APRUN_MARINEBMAT) - exec_name = os.path.join(self.task_config.DATA, 'gdas_socahybridweights.x') - exec_cmd.add_default_arg(exec_name) - exec_cmd.add_default_arg('soca_ensweights.yaml') - - # compute the ensemble weights - mdau.run(exec_cmd) - - @logit(logger) - def execute(self: Task) -> None: - """Generate the full B-matrix - - This method will generate the full B-matrix according to the configuration. - """ - chdir(self.task_config.DATA) - self.gridgen() # TODO: This should be optional in case the geometry file was staged - self.variance_partitioning() - self.horizontal_diffusion() # TODO: Make this optional once we've converged on an acceptable set of scales - self.vertical_diffusion() # hybrid EnVAR case if self.task_config.DOHYBVAR == "YES" or self.task_config.NMEM_ENS > 2: - self.ensemble_perturbations() # TODO: refactor this from the old scripts - self.hybrid_weight() # TODO: refactor this from the old scripts + self.jedi_dict['soca_ensb'].execute() + self.jedi_dict['soca_ensweights'].execute() @logit(logger) def finalize(self: Task) -> None: @@ -270,6 +197,13 @@ def finalize(self: Task) -> None: - keep the re-balanced ensemble perturbation files in DATAenspert - ... + Parameters + ---------- + None + + Returns + ---------- + None """ # Copy the soca grid if it was created grid_file = os.path.join(self.task_config.DATA, 'soca_gridspec.nc') diff --git a/versions/fix.ver b/versions/fix.ver index b175791196..4739ce778a 100644 --- a/versions/fix.ver +++ b/versions/fix.ver @@ -8,7 +8,7 @@ export cice_ver=20240416 export cpl_ver=20230526 export datm_ver=20220805 export gdas_crtm_ver=20220805 -export gdas_fv3jedi_ver=20241022 +export gdas_fv3jedi_ver=20241115 export gdas_soca_ver=20240802 export gdas_gsibec_ver=20240416 export gdas_obs_ver=20240213 From 1563594f6b723b40cd84badf73f24f3630fc45c6 Mon Sep 17 00:00:00 2001 From: BoCui-NOAA <53531984+BoCui-NOAA@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:05:25 -0500 Subject: [PATCH 10/10] Add new stations to GFS BUFR sounding products (#3107) Andrew Benjamin requested the addition of new buff sounding stations to the GFS system. He provided an updated global station list that includes 121 new stations and 3 updated ones. Based on this list, several parameter files for GFSv17 have been updated, including grid point location files for C76, C768, and C1152 resolutions. --- parm/product/bufr_ij_gfs_C1152.txt | 127 +++++++++++++++++++++++++++- parm/product/bufr_ij_gfs_C768.txt | 127 +++++++++++++++++++++++++++- parm/product/bufr_ij_gfs_C96.txt | 127 +++++++++++++++++++++++++++- parm/product/bufr_stalist.meteo.gfs | 127 +++++++++++++++++++++++++++- 4 files changed, 496 insertions(+), 12 deletions(-) diff --git a/parm/product/bufr_ij_gfs_C1152.txt b/parm/product/bufr_ij_gfs_C1152.txt index 321026f3d1..e8c6e3fc34 100644 --- a/parm/product/bufr_ij_gfs_C1152.txt +++ b/parm/product/bufr_ij_gfs_C1152.txt @@ -111,9 +111,9 @@ 111 3499 558 46.42 -86.65 112 3461 651 39.16 -89.67 113 3308 588 44.05 -101.60 - 114 3505 594 43.58 -86.24 - 115 3517 596 43.43 -85.30 - 116 3528 597 43.37 -84.44 + 114 3502 589 43.96 -86.41 + 115 3514 592 43.73 -85.51 + 116 3524 594 43.62 -84.74 117 3274 712 34.38 -104.23 118 3994 554 46.70 -48.00 119 3903 548 47.20 -55.10 @@ -2113,3 +2113,124 @@ 2113 1641 1351 -15.51 128.15 2114 4514 416 57.48 -7.36 2115 130 456 54.38 10.13 + 2116 3685 625 41.24 -72.12 + 2117 3584 733 32.70 -80.00 + 2118 3468 539 47.91 -89.14 + 2119 3478 554 46.67 -88.35 + 2120 3037 573 45.24 -122.77 + 2121 2612 886 20.79 -156.01 + 2122 2608 887 20.71 -156.25 + 2123 2618 898 19.82 -155.47 + 2124 2563 870 22.04 -159.78 + 2125 2613 867 22.27 -155.86 + 2126 2617 903 19.47 -155.59 + 2127 2338 791 28.20 -177.38 + 2128 2568 868 22.21 -159.44 + 2129 2589 877 21.45 -157.77 + 2130 2617 907 19.10 -155.57 + 2131 3587 660 38.43 -79.84 + 2132 3657 578 44.85 -74.33 + 2133 3671 581 44.56 -73.33 + 2134 3670 576 45.00 -73.33 + 2135 3676 594 43.60 -72.82 + 2136 3675 581 44.59 -72.92 + 2137 3674 589 43.98 -73.03 + 2138 3491 718 33.90 -87.31 + 2139 3508 731 32.91 -85.96 + 2140 3584 733 32.70 -80.00 + 2141 3614 607 42.57 -77.71 + 2142 3582 783 28.85 -80.23 + 2143 3581 768 30.02 -80.25 + 2144 3488 770 29.80 -87.50 + 2145 3536 787 28.50 -83.75 + 2146 3531 778 29.25 -84.20 + 2147 3577 770 29.80 -80.60 + 2148 3507 771 29.77 -86.03 + 2149 3033 580 44.64 -123.06 + 2150 3052 569 45.52 -121.59 + 2151 3015 562 46.10 -124.50 + 2152 3015 581 44.60 -124.50 + 2153 2998 564 45.90 -125.80 + 2154 3529 670 37.63 -84.33 + 2155 3250 655 38.81 -106.12 + 2156 3258 635 40.38 -105.52 + 2157 3263 660 38.43 -105.10 + 2158 3510 620 41.53 -85.79 + 2159 3519 619 41.64 -85.08 + 2160 3306 665 38.00 -101.75 + 2161 3698 612 42.18 -71.17 + 2162 3690 607 42.55 -71.76 + 2163 3679 607 42.59 -72.60 + 2164 3704 606 42.62 -70.67 + 2165 3699 616 41.87 -71.02 + 2166 3679 610 42.33 -72.63 + 2167 3618 647 39.41 -77.37 + 2168 3621 651 39.17 -77.17 + 2169 3700 589 43.99 -70.95 + 2170 3542 594 43.56 -83.34 + 2171 3536 611 42.22 -83.75 + 2172 3411 547 47.22 -93.52 + 2173 3419 567 45.70 -92.95 + 2174 3407 574 45.16 -93.84 + 2175 3415 612 42.15 -93.22 + 2176 3452 540 47.84 -90.38 + 2177 3219 557 46.48 -108.54 + 2178 3693 588 44.03 -71.49 + 2179 3256 685 36.45 -105.67 + 2180 3248 693 35.88 -106.27 + 2181 3568 624 41.21 -81.25 + 2182 3527 637 40.20 -84.53 + 2183 3624 641 39.93 -76.88 + 2184 3641 637 40.23 -75.55 + 2185 3674 603 42.89 -73.04 + 2186 3445 556 46.55 -90.92 + 2187 3599 662 38.27 -78.90 + 2188 3040 672 37.51 -122.50 + 2189 3175 632 40.62 -111.99 + 2190 3178 706 34.85 -111.79 + 2191 3183 713 34.26 -111.34 + 2192 3209 715 34.13 -109.31 + 2193 3172 700 35.31 -112.19 + 2194 3212 696 35.65 -109.07 + 2195 3185 689 36.14 -111.24 + 2196 3206 689 36.15 -109.55 + 2197 3172 682 36.71 -112.22 + 2198 3197 682 36.73 -110.25 + 2199 3166 688 36.24 -112.69 + 2200 3157 697 35.53 -113.43 + 2201 3160 709 34.58 -113.18 + 2202 3194 694 35.79 -110.51 + 2203 3212 679 36.92 -109.09 + 2204 3211 685 36.47 -109.15 + 2205 307 666 37.94 23.94 + 2206 3683 873 21.77 -72.27 + 2207 1930 385 59.91 150.72 + 2208 2029 471 53.17 158.45 + 2209 979 372 60.95 76.48 + 2210 4528 468 53.43 -6.25 + 2211 4499 544 47.48 -8.54 + 2212 362 1487 -26.14 28.23 + 2213 33 524 49.02 2.53 + 2214 93 593 43.65 7.21 + 2215 4563 634 40.47 -3.58 + 2216 4286 679 36.97 -25.17 + 2217 4492 656 38.77 -9.13 + 2218 112 568 45.63 8.72 + 2219 854 300 66.53 66.67 + 2220 1303 431 56.28 101.75 + 2221 1336 483 52.27 104.32 + 2222 1731 531 48.53 135.19 + 2223 447 742 32.00 34.90 + 2224 1619 672 37.47 126.43 + 2225 1632 682 36.72 127.50 + 2226 1619 723 33.50 126.48 + 2227 1670 722 33.58 130.45 + 2228 1343 1003 11.60 104.86 + 2229 4434 1029 9.57 -13.62 + 2230 711 1419 -20.89 55.53 + 2231 3508 744 31.86 -86.01 + 2232 3429 658 38.59 -92.16 + 2233 3024 570 45.42 -123.82 + 2234 3828 976 13.75 -60.95 + 2235 3818 998 12.00 -61.78 + 2236 3654 1259 -8.37 -74.57 diff --git a/parm/product/bufr_ij_gfs_C768.txt b/parm/product/bufr_ij_gfs_C768.txt index 568717de72..41906ee438 100644 --- a/parm/product/bufr_ij_gfs_C768.txt +++ b/parm/product/bufr_ij_gfs_C768.txt @@ -111,9 +111,9 @@ 111 2333 372 46.42 -86.65 112 2307 434 39.16 -89.67 113 2206 392 44.05 -101.60 - 114 2337 396 43.58 -86.24 - 115 2345 397 43.43 -85.30 - 116 2352 398 43.37 -84.44 + 114 2335 393 43.96 -86.41 + 115 2343 395 43.73 -85.51 + 116 2349 396 43.62 -84.74 117 2183 475 34.38 -104.23 118 2663 369 46.70 -48.00 119 2602 365 47.20 -55.10 @@ -2113,3 +2113,124 @@ 2113 1094 900 -15.51 128.15 2114 3010 277 57.48 -7.36 2115 87 304 54.38 10.13 + 2116 2457 416 41.24 -72.12 + 2117 2390 489 32.70 -80.00 + 2118 2312 359 47.91 -89.14 + 2119 2319 370 46.67 -88.35 + 2120 2025 382 45.24 -122.77 + 2121 1741 591 20.79 -156.01 + 2122 1739 591 20.71 -156.25 + 2123 1746 599 19.82 -155.47 + 2124 1709 580 22.04 -159.78 + 2125 1742 578 22.27 -155.86 + 2126 1745 602 19.47 -155.59 + 2127 1559 527 28.20 -177.38 + 2128 1712 578 22.21 -159.44 + 2129 1726 585 21.45 -157.77 + 2130 1745 605 19.10 -155.57 + 2131 2391 440 38.43 -79.84 + 2132 2438 385 44.85 -74.33 + 2133 2447 388 44.56 -73.33 + 2134 2447 385 45.00 -73.33 + 2135 2451 396 43.60 -72.82 + 2136 2450 387 44.59 -72.92 + 2137 2449 393 43.98 -73.03 + 2138 2327 479 33.90 -87.31 + 2139 2339 487 32.91 -85.96 + 2140 2390 489 32.70 -80.00 + 2141 2409 405 42.57 -77.71 + 2142 2388 522 28.85 -80.23 + 2143 2388 512 30.02 -80.25 + 2144 2326 514 29.80 -87.50 + 2145 2358 525 28.50 -83.75 + 2146 2354 518 29.25 -84.20 + 2147 2385 514 29.80 -80.60 + 2148 2338 514 29.77 -86.03 + 2149 2022 387 44.64 -123.06 + 2150 2035 379 45.52 -121.59 + 2151 2010 374 46.10 -124.50 + 2152 2010 387 44.60 -124.50 + 2153 1999 376 45.90 -125.80 + 2154 2353 447 37.63 -84.33 + 2155 2167 437 38.81 -106.12 + 2156 2172 423 40.38 -105.52 + 2157 2176 440 38.43 -105.10 + 2158 2340 413 41.53 -85.79 + 2159 2346 413 41.64 -85.08 + 2160 2204 444 38.00 -101.75 + 2161 2465 408 42.18 -71.17 + 2162 2460 405 42.55 -71.76 + 2163 2453 404 42.59 -72.60 + 2164 2469 404 42.62 -70.67 + 2165 2466 411 41.87 -71.02 + 2166 2453 407 42.33 -72.63 + 2167 2412 432 39.41 -77.37 + 2168 2414 434 39.17 -77.17 + 2169 2467 392 43.99 -70.95 + 2170 2361 396 43.56 -83.34 + 2171 2358 408 42.22 -83.75 + 2172 2274 365 47.22 -93.52 + 2173 2279 378 45.70 -92.95 + 2174 2272 383 45.16 -93.84 + 2175 2277 408 42.15 -93.22 + 2176 2301 360 47.84 -90.38 + 2177 2146 371 46.48 -108.54 + 2178 2462 392 44.03 -71.49 + 2179 2171 457 36.45 -105.67 + 2180 2166 462 35.88 -106.27 + 2181 2379 416 41.21 -81.25 + 2182 2351 425 40.20 -84.53 + 2183 2416 427 39.93 -76.88 + 2184 2428 425 40.23 -75.55 + 2185 2449 402 42.89 -73.04 + 2186 2297 371 46.55 -90.92 + 2187 2399 441 38.27 -78.90 + 2188 2027 448 37.51 -122.50 + 2189 2117 421 40.62 -111.99 + 2190 2119 471 34.85 -111.79 + 2191 2122 476 34.26 -111.34 + 2192 2140 477 34.13 -109.31 + 2193 2115 467 35.31 -112.19 + 2194 2142 464 35.65 -109.07 + 2195 2123 460 36.14 -111.24 + 2196 2138 459 36.15 -109.55 + 2197 2115 455 36.71 -112.22 + 2198 2132 454 36.73 -110.25 + 2199 2111 459 36.24 -112.69 + 2200 2105 465 35.53 -113.43 + 2201 2107 473 34.58 -113.18 + 2202 2129 462 35.79 -110.51 + 2203 2142 453 36.92 -109.09 + 2204 2141 457 36.47 -109.15 + 2205 205 444 37.94 23.94 + 2206 2456 582 21.77 -72.27 + 2207 1287 257 59.91 150.72 + 2208 1353 314 53.17 158.45 + 2209 653 248 60.95 76.48 + 2210 3019 312 53.43 -6.25 + 2211 3000 363 47.48 -8.54 + 2212 241 991 -26.14 28.23 + 2213 22 350 49.02 2.53 + 2214 62 395 43.65 7.21 + 2215 3042 423 40.47 -3.58 + 2216 2858 452 36.97 -25.17 + 2217 2995 437 38.77 -9.13 + 2218 75 378 45.63 8.72 + 2219 569 200 66.53 66.67 + 2220 869 288 56.28 101.75 + 2221 891 322 52.27 104.32 + 2222 1154 354 48.53 135.19 + 2223 298 495 32.00 34.90 + 2224 1079 448 37.47 126.43 + 2225 1088 455 36.72 127.50 + 2226 1080 482 33.50 126.48 + 2227 1114 481 33.58 130.45 + 2228 895 669 11.60 104.86 + 2229 2956 686 9.57 -13.62 + 2230 474 946 -20.89 55.53 + 2231 2339 496 31.86 -86.01 + 2232 2286 439 38.59 -92.16 + 2233 2016 380 45.42 -123.82 + 2234 2552 651 13.75 -60.95 + 2235 2545 666 12.00 -61.78 + 2236 2436 839 -8.37 -74.57 diff --git a/parm/product/bufr_ij_gfs_C96.txt b/parm/product/bufr_ij_gfs_C96.txt index c005cc3170..53dc3f2f9a 100644 --- a/parm/product/bufr_ij_gfs_C96.txt +++ b/parm/product/bufr_ij_gfs_C96.txt @@ -111,9 +111,9 @@ 111 292 46 46.42 -86.65 112 289 54 39.16 -89.67 113 276 49 44.05 -101.60 - 114 293 49 43.58 -86.24 - 115 294 50 43.43 -85.30 - 116 294 50 43.37 -84.44 + 114 292 49 43.96 -86.41 + 115 293 49 43.73 -85.51 + 116 294 49 43.62 -84.74 117 273 59 34.38 -104.23 118 333 46 46.70 -48.00 119 326 46 47.20 -55.10 @@ -2113,3 +2113,124 @@ 2113 137 113 -15.51 128.15 2114 377 35 57.48 -7.36 2115 11 38 54.38 10.13 + 2116 308 53 41.24 -72.12 + 2117 299 61 32.70 -80.00 + 2118 289 45 47.91 -89.14 + 2119 290 46 46.67 -88.35 + 2120 254 48 45.24 -122.77 + 2121 218 74 20.79 -156.01 + 2122 218 74 20.71 -156.25 + 2123 219 75 19.82 -155.47 + 2124 214 72 22.04 -159.78 + 2125 218 72 22.27 -155.86 + 2126 219 75 19.47 -155.59 + 2127 195 66 28.20 -177.38 + 2128 214 72 22.21 -159.44 + 2129 216 73 21.45 -157.77 + 2130 219 76 19.10 -155.57 + 2131 299 55 38.43 -79.84 + 2132 305 48 44.85 -74.33 + 2133 304 50 44.56 -73.33 + 2134 304 50 45.00 -73.33 + 2135 307 49 43.60 -72.82 + 2136 307 48 44.59 -72.92 + 2137 307 49 43.98 -73.03 + 2138 291 60 33.90 -87.31 + 2139 293 61 32.91 -85.96 + 2140 299 61 32.70 -80.00 + 2141 302 50 42.57 -77.71 + 2142 299 65 28.85 -80.23 + 2143 299 64 30.02 -80.25 + 2144 291 65 29.80 -87.50 + 2145 295 66 28.50 -83.75 + 2146 295 65 29.25 -84.20 + 2147 299 64 29.80 -80.60 + 2148 293 65 29.77 -86.03 + 2149 253 48 44.64 -123.06 + 2150 255 47 45.52 -121.59 + 2151 252 47 46.10 -124.50 + 2152 252 48 44.60 -124.50 + 2153 250 47 45.90 -125.80 + 2154 295 56 37.63 -84.33 + 2155 271 54 38.81 -106.12 + 2156 272 53 40.38 -105.52 + 2157 272 55 38.43 -105.10 + 2158 293 52 41.53 -85.79 + 2159 294 51 41.64 -85.08 + 2160 276 55 38.00 -101.75 + 2161 309 51 42.18 -71.17 + 2162 308 50 42.55 -71.76 + 2163 307 50 42.59 -72.60 + 2164 309 50 42.62 -70.67 + 2165 309 51 41.87 -71.02 + 2166 307 51 42.33 -72.63 + 2167 302 54 39.41 -77.37 + 2168 302 54 39.17 -77.17 + 2169 309 49 43.99 -70.95 + 2170 296 49 43.56 -83.34 + 2171 295 51 42.22 -83.75 + 2172 285 46 47.22 -93.52 + 2173 285 47 45.70 -92.95 + 2174 284 48 45.16 -93.84 + 2175 285 51 42.15 -93.22 + 2176 288 45 47.84 -90.38 + 2177 269 46 46.48 -108.54 + 2178 308 49 44.03 -71.49 + 2179 272 57 36.45 -105.67 + 2180 271 58 35.88 -106.27 + 2181 298 52 41.21 -81.25 + 2182 294 53 40.20 -84.53 + 2183 302 53 39.93 -76.88 + 2184 304 53 40.23 -75.55 + 2185 307 50 42.89 -73.04 + 2186 288 46 46.55 -90.92 + 2187 300 55 38.27 -78.90 + 2188 254 56 37.51 -122.50 + 2189 265 53 40.62 -111.99 + 2190 265 59 34.85 -111.79 + 2191 266 59 34.26 -111.34 + 2192 268 59 34.13 -109.31 + 2193 265 58 35.31 -112.19 + 2194 268 58 35.65 -109.07 + 2195 266 57 36.14 -111.24 + 2196 268 57 36.15 -109.55 + 2197 265 57 36.71 -112.22 + 2198 267 57 36.73 -110.25 + 2199 264 57 36.24 -112.69 + 2200 264 58 35.53 -113.43 + 2201 264 59 34.58 -113.18 + 2202 267 58 35.79 -110.51 + 2203 268 57 36.92 -109.09 + 2204 268 57 36.47 -109.15 + 2205 26 55 37.94 23.94 + 2206 307 73 21.77 -72.27 + 2207 161 32 59.91 150.72 + 2208 170 39 53.17 158.45 + 2209 82 31 60.95 76.48 + 2210 378 39 53.43 -6.25 + 2211 375 45 47.48 -8.54 + 2212 31 124 -26.14 28.23 + 2213 3 44 49.02 2.53 + 2214 8 49 43.65 7.21 + 2215 381 53 40.47 -3.58 + 2216 358 56 36.97 -25.17 + 2217 375 55 38.77 -9.13 + 2218 10 47 45.63 8.72 + 2219 72 25 66.53 66.67 + 2220 109 36 56.28 101.75 + 2221 112 40 52.27 104.32 + 2222 145 44 48.53 135.19 + 2223 38 62 32.00 34.90 + 2224 135 56 37.47 126.43 + 2225 136 57 36.72 127.50 + 2226 135 60 33.50 126.48 + 2227 140 60 33.58 130.45 + 2228 112 84 11.60 104.86 + 2229 370 86 9.57 -13.62 + 2230 60 118 -20.89 55.53 + 2231 293 62 31.86 -86.01 + 2232 286 55 38.59 -92.16 + 2233 252 47 45.42 -123.82 + 2234 319 81 13.75 -60.95 + 2235 319 83 12.00 -61.78 + 2236 305 105 -8.37 -74.57 diff --git a/parm/product/bufr_stalist.meteo.gfs b/parm/product/bufr_stalist.meteo.gfs index 51d4108bac..0f3f76f23b 100755 --- a/parm/product/bufr_stalist.meteo.gfs +++ b/parm/product/bufr_stalist.meteo.gfs @@ -111,9 +111,9 @@ 000240 46.42N 86.65W KP53 11 MUNISING MI 187 Usr Rqst 10-11 000241 39.16N 89.67W K3LF 11 LITCHFIELD IL 210 Usr Rqst 11-11 000247 44.05N 101.60W PHP 10 PHILIP_AIRPORT, SD 673 Weiss 2-99 -000251 43.58N 86.24W KLDM 12 LUDINGTON MI 244 KGRR 1-02 -000252 43.43N 85.30W KRQB 12 BIG RAPIDS MI 276 KGRR 1-02 -000253 43.37N 84.44W KMOP 12 MOUNT PLEASANT MI 213 KGRR 1-02 +000251 43.96N 86.41W KLDM 12 LUDINGTON MI 244 KGRR 1-02 +000252 43.73N 85.51W KRQB 12 BIG RAPIDS MI 276 KGRR 1-02 +000253 43.62N 84.74W KMOP 12 MOUNT PLEASANT MI 213 KGRR 1-02 000254 34.38N 104.23W FTSR 12 FORT SUMNER NM 1231 Usr Rqst 4-02 000255 46.70N 48.00W WTRO 21 WHITE ROSE OIL RIG 0 Usr Rqst 12-03 000256 47.20N 55.10W MRYS 00 MARYSTOWN NF 0 Usr Rqst 12-03 @@ -2113,3 +2113,124 @@ 999913 15.51S 128.15E WYN 00 Wyndham aerodrome Australia 14 Usr Rqst 1-19 999914 57.48N 7.36W EGPL 00 Benbecula, Scotland, UK 6 Usr Rqst 1-19 999918 54.38N 10.13E EDHK 11 KIEL-HOLTENAU AIRPORT -999 Usr Rqst 1-17 +000530 41.24N 72.12W LIW 21 NEAR LONG ISL TWIN FK NY 9999 Usr Rqst 5-23 +000531 32.70N 80.00W CEA 11 CHARLESTON EXEC ARPT SC 6 Usr Rqst 5-23 +000532 47.91N 89.14W WINM 11 WINDIGO MI 293 Usr Rqst 5-23 +000533 46.67N 88.35W HERM 11 HERMAN MI 530 Usr Rqst 5-23 +000534 45.24N 122.77W KUAO 11 AURORA AIRPORT OR 59 Usr Rqst 5-23 +000535 20.79N 156.01W PHHN 11 HANA AIRPORT HI 24 Usr Rqst 5-23 +000536 20.71N 156.25W HLKL 11 HALEAKALA SUMMIT HI 3055 Usr Rqst 5-23 +000537 19.82N 155.47W MKEA 11 MAUNA KEA SUMMIT HI 4207 Usr Rqst 5-23 +000538 22.04N 159.78W PHBK 11 BARKING SANDS HI 7 Usr Rqst 5-23 +000539 22.27N 155.86W PHUP 11 UPOLU AIRPORT HI 29 Usr Rqst 5-23 +000540 19.47N 155.59W MLOA 11 MAUNA LOA SUMMIT HI 4169 Usr Rqst 5-23 +000541 28.20N 177.38W PMDY 11 HENDERSON FIELD HI 5 Usr Rqst 5-23 +000542 22.21N 159.44W HI01 11 PRINCEVILLE AIRPORT HI 105 Usr Rqst 5-23 +000543 21.45N 157.77W PHNG 11 KANEOHE BAY MCAS HI 7 Usr Rqst 5-23 +000544 19.10N 155.57W SCPE 11 SOUTH CAPE HI 355 Usr Rqst 5-23 +000545 38.43N 79.84W GBO 11 GREEN BANK OBSERVATORY WV 807 Usr Rqst 5-23 +000546 44.85N 74.33W MALO 11 MALONE NY 240 Usr Rqst 5-23 +000547 44.56N 73.33W CLCR 21 COLCHESTER REEF VT 29 Usr Rqst 5-23 +000548 45.00N 73.33W ALBU 21 ALBAUGH VT 38 Usr Rqst 5-23 +000549 43.60N 72.82W KLGN 11 KILLINGTON PEAK VT 1289 Usr Rqst 5-23 +000550 44.59N 72.92W NUND 11 NORTH UNDERHILL VT 315 Usr Rqst 5-23 +000551 43.98N 73.03W RIPT 11 RIPTON VT 512 Usr Rqst 5-23 +000552 33.90N 87.31W KJFX 11 JASPER AL 100 Usr Rqst 5-23 +000553 32.91N 85.96W KALX 11 ALEXANDER CITY AL 209 Usr Rqst 5-23 +000554 32.70N 80.00W KJZI 11 CHARLESTON EXEC ARPT SC 6 Usr Rqst 5-23 +000555 42.57N 77.71W KDSV 11 DANSVILLE NY 215 Usr Rqst 5-23 +000556 28.85N 80.23W NCPE 21 NORTH CAPE FL 9999 Usr Rqst 5-23 +000557 30.02N 80.25W JAXX 21 OFF JACKSONVILLE FL 9999 Usr Rqst 5-23 +000558 29.80N 87.50W PENX 21 OFF PENSACOLA FL 9999 Usr Rqst 5-23 +000559 28.50N 83.75W TPXX 21 OFF TAMPA FL 9999 Usr Rqst 5-23 +000560 29.25N 84.20W TLHX 21 OFF TALLAHASSEE FL 9999 Usr Rqst 5-23 +000561 29.80N 80.60W DABX 21 OFF DAYTONA BEACH FL 9999 Usr Rqst 5-23 +000562 29.77N 86.03W PANX 21 OFF PANAMA CITY FL 9999 Usr Rqst 5-23 +000563 44.64N 123.06W KS12 11 ALBANY OR 60 Usr Rqst 5-23 +000564 45.52N 121.59W PRKD 11 PARKDALE OR 526 Usr Rqst 5-23 +000565 46.10N 124.50W OC29 21 BUOY 29 OR 9999 Usr Rqst 5-23 +000566 44.60N 124.50W OC50 21 BUOY 50 OR 9999 Usr Rqst 5-23 +000567 45.90N 125.80W OC89 21 BUOY 89 OR 9999 Usr Rqst 5-23 +000568 37.63N 84.33W KRGA 10 CENTAL KY REGION ARPT KY 305 Usr Rqst 8-24 +000569 38.81N 106.12W AEJ 10 BUENA VISTA AIRPORT CO 2422 Usr Rqst 8-24 +000570 40.38N 105.52W ESTP 10 ESTES PARK CO 2295 Usr Rqst 8-24 +000571 38.43N 105.10W CANO 10 CANON CITY CO 1658 Usr Rqst 8-24 +000572 41.53N 85.79W GSH 10 GOSHEN MUNICIPAL ARPT IN 252 Usr Rqst 8-24 +000573 41.64N 85.08W ANQ 10 ANGOLA AIRPORT IN 303 Usr Rqst 8-24 +000574 38.00N 101.75W SYSE 10 SYRACUSE, KS KS 1013 Usr Rqst 8-24 +000575 42.18N 71.17W OWD 10 NORWOOD MEMORIAL ARPT MA 15 Usr Rqst 8-24 +000576 42.55N 71.76W FIT 10 FITCHBURG_(ASOS) MA 106 Usr Rqst 8-24 +000577 42.59N 72.60W GRNF 10 GREENFIELD MA 74 Usr Rqst 8-24 +000578 42.62N 70.67W GLOU 10 GLOUCESTER MA 16 Usr Rqst 8-24 +000579 41.87N 71.02W TAN 10 TAUNTON_(ASOS) MA 13 Usr Rqst 8-24 +000580 42.33N 72.63W NTHH 10 NORTHHAMPTON MA 41 Usr Rqst 8-24 +000581 39.41N 77.37W FDK 10 FREDERICK_MUNI_APT MD 92 Usr Rqst 8-24 +000582 39.17N 77.17W GAI 10 GAITHERSBURG MD 116 Usr Rqst 8-24 +000583 43.99N 70.95W IZG 10 FRYEBERG_(ASOS) ME 135 Usr Rqst 8-24 +000584 43.56N 83.34W NERA 10 NEW ERA, MI MI 230 Usr Rqst 8-24 +000585 42.22N 83.75W ARB 10 ANN_ARBOR_MUNI_(ASOS) MI 256 Usr Rqst 8-24 +000586 47.22N 93.52W GPZ 10 GRAND_RAPIDS(AWOS) MN 413 Usr Rqst 8-24 +000587 45.70N 92.95W ROS 10 RUSH_CITY MN 281 Usr Rqst 8-24 +000588 45.16N 93.84W 8Y2 10 BUFFALO_MUNI_ARPT MN 295 Usr Rqst 8-24 +000589 42.15N 93.22W ANE 10 MINNEAPOLIS/BLAINE MN 278 Usr Rqst 8-24 +000590 47.84N 90.38W CKC 10 GRAND_MARAIS/COOK_CO MN 548 Usr Rqst 8-24 +000591 46.48N 108.54W KRPX 10 ROUNDUP MT 1064 Usr Rqst 8-24 +000592 44.03N 71.49W KCMA 10 KANCAMAGUS PASS NH 872 Usr Rqst 8-24 +000593 36.45N 105.67W SKX 10 TAOS_MUNI_APT(AWOS) NM 2161 Usr Rqst 8-24 +000594 35.88N 106.27W LAM 10 LOS_ALAMOS_AIRPORT NM 2186 Usr Rqst 8-24 +000595 41.21N 81.25W 29G 10 RAVENNA_PORTAGE_CO_APT NM 365 Usr Rqst 8-24 +000596 40.20N 84.53W VES 10 VERSAILLES_DARK_CO_APT OH 307 Usr Rqst 8-24 +000597 39.93N 76.88W THV 10 YORK_AIRPORT_(ASOS) PA 148 Usr Rqst 8-24 +000598 40.23N 75.55W PTTO 10 POTTSTOWN, PA PA 92 Usr Rqst 8-24 +000599 42.89N 73.04W WDFD 10 WOODFORD ST PARK VT 703 Usr Rqst 8-24 +000600 46.55N 90.92W ASWI 10 ASHLAND AIRPORT WI 251 Usr Rqst 8-24 +000605 38.27N 78.90W SHD 10 STAUNTON/SHENANDOAH VA 366 Usr Rqst 8-24 +000615 37.51N 122.50W KHAF 10 HALF MOON BAY APT CA 20 Usr Rqst 8-24 +000616 40.62N 111.99W KSVR 10 SOUTH VALLEY APT UT 1404 Usr Rqst 8-24 +001007 34.85N 111.79W KSEZ 10 SEDONA_AIRPORT AZ 1472 Usr Rqst 8/24 +001008 34.26N 111.34W KPAN 10 PAYSON_AIRPORT AZ 1572 Usr Rqst 8/24 +001009 34.13N 109.31W KJTC 10 SPRINGERVILLE MUNI APT AZ 2150 Usr Rqst 8/24 +001010 35.31N 112.19W KCMR 10 WILLIAMS AZ 2039 Usr Rqst 8/24 +001011 35.65N 109.07W KRQE 10 WINDOW_ROCK_(ASOS) AZ 2054 Usr Rqst 8/24 +001012 36.14N 111.24W TUBA 10 TUBA CITY AZ 1499 Usr Rqst 8/24 +001013 36.15N 109.55W CHN 10 CHINLE AZ 1687 Usr Rqst 8/24 +001014 36.71N 112.22W JLK 10 JACOB LAKE AZ 2897 Usr Rqst 8/24 +001015 36.73N 110.25W KAYA 10 KAYENTA AZ 1716 Usr Rqst 8/24 +001016 36.24N 112.69W SUP 10 SUPAI AZ 1012 Usr Rqst 8/24 +001017 35.53N 113.43W PCHS 10 PEACH SPRINGS AZ 1478 Usr Rqst 8/24 +001018 34.58N 113.18W BDD 10 BAGDAD AZ 1189 Usr Rqst 8/24 +001019 35.79N 110.51W SMA 10 SECOND MESA AZ 1737 Usr Rqst 8/24 +001020 36.92N 109.09W TNP 10 TEEC NOS POS AZ 1592 Usr Rqst 8/24 +001021 36.47N 109.15W BOP 10 BUFFALO PASS AZ 2496 Usr Rqst 8/24 +001500 37.94N 23.94E LGAV 11 ATHENS, GREECE 94 Usr Rqst 5-23 +001501 21.77N 72.27W MBPV 11 PROVIDENCIALES, T_C 9 Usr Rqst 5-23 +001502 59.91N 150.72E UHMM 11 MAGADAN, RUSSIA 175 Usr Rqst 5-23 +001503 53.17N 158.45E UHPP 11 PETROPAVLOVSK, RUSSIA 39 Usr Rqst 5-23 +001504 60.95N 76.48E USNN 11 NIZHNEVARTOVSK, RUSSIA 54 Usr Rqst 5-23 +039690 53.43N 6.25W EIDW 11 DUBLIN, IRELAND 68 Usr Rqst 5-23 +066700 47.48N 8.54W LSZH 11 ZURICH, SWITZERLAND 426 Usr Rqst 5-23 +068368 26.14S 28.23E FAJS 11 JOHANNESBURG / TAMBO 1695 Usr Rqst 5-23 +071570 49.02N 2.53E LFPG 11 PARIS/ROISSY-EN-FRANCE 119 Usr Rqst 5-23 +076900 43.65N 7.21E LFMN 11 NICE 4 Usr Rqst 5-23 +082210 40.47N 3.58W LEMD 11 MADRID 631 Usr Rqst 5-23 +085150 36.97N 25.17W LPAZ 11 SANTA MARIA ISL, PORTUGAL 96 Usr Rqst 5-23 +085360 38.77N 9.13W LPPT 11 LISBON, PORTUGAL 114 Usr Rqst 5-23 +160660 45.63N 8.72E LIMC 11 MILANO/MILAN 234 Usr Rqst 5-23 +233300 66.53N 66.67E USDD 11 SALEKHARD, RUSSIA 15 Usr Rqst 5-23 +303090 56.28N 101.75E UIBB 11 BRATSK, RUSSIA 411 Usr Rqst 5-23 +307100 52.27N 104.32E UIII 11 DZERZHINSK, RUSSIA 467 Usr Rqst 5-23 +317350 48.53N 135.19E UHHH 11 KHABAROVSK, RUSSIA 75 Usr Rqst 5-23 +401800 32.00N 34.90E LLBG 11 TEL AVIV, ISRAEL 40 Usr Rqst 5-23 +471130 37.47N 126.43E RKSI 11 SEOUL INCHEON, SOUTH KOREA 7 Usr Rqst 5-23 +471280 36.72N 127.50E RKTU 11 CHONGIU, SOUTH KOREA 53 Usr Rqst 5-23 +471820 33.50N 126.48E RKPC 11 CHEJU, SOUTH KOREA 36 Usr Rqst 5-23 +478080 33.58N 130.45E RJFF 11 FUKUOKA, JAPAN 9 Usr Rqst 5-23 +489910 11.60N 104.86E VDPP 11 PHNOM-PENH, CAMBODIA 10 Usr Rqst 5-23 +618320 9.57N 13.62W GUCY 11 CONAKRY, GUINEA 26 Usr Rqst 5-23 +619800 20.89S 55.53E FMEE 11 ROLAND GARROS, REUNION 20 Usr Rqst 5-23 +722267 31.86N 86.01W KTOI 11 TROY_MUNICIPAL AL 121 Usr Rqst 5-23 +724458 38.59N 92.16W KJEF 11 JEFFERSON CITY ARPT MO 169 Usr Rqst 5-23 +726963 45.42N 123.82W KTMK 11 TILLAMOOK OR 11 Usr Rqst 5-23 +789480 13.75N 60.95W TLPL 11 HEWANORRA, ST LUCIA 3 Usr Rqst 5-23 +789580 12.00N 61.78W TGPY 11 ST GEORGES, GRENADA 6 Usr Rqst 5-23 +845150 8.37S 74.57W SPCL 11 PUCALLPA, PERU 148 Usr Rqst 5-23