diff --git a/.github/workflows/pw_aws_ci.yaml b/.github/workflows/pw_aws_ci.yaml index 245e219dd4..6225b0503a 100644 --- a/.github/workflows/pw_aws_ci.yaml +++ b/.github/workflows/pw_aws_ci.yaml @@ -15,7 +15,7 @@ on: workflow_dispatch: inputs: pr_number: - description: 'Pull Request Number (use 0 for non-PR)' + description: 'PR Number (use 0 for non-PR)' required: true default: '0' os: @@ -31,14 +31,44 @@ env: MACHINE_ID: noaacloud jobs: + + run-start-clusters: + runs-on: ubuntu-latest + env: + PW_PLATFORM_HOST: noaa.parallel.works + steps: + - name: Checkout pw-cluster-automation repository + uses: actions/checkout@v4 + with: + repository: TerrenceMcGuinness-NOAA/pw-cluster-automation + path: pw-cluster-automation + ref: pw_cluster_noaa + + - name: Run startClusters + run: | + mkdir -p ~/.ssh + echo "${{ secrets.ID_RSA_AWS }}" > ~/.ssh/id_rsa + echo "${{ secrets.PW_API_KEY }}" > ~/.ssh/pw_api.key + chmod 700 ~/.ssh + chmod 600 ~/.ssh/id_rsa + chmod 600 ~/.ssh/pw_api.key + if [ "${{ github.event.inputs.os }}" == "rocky" ]; then + clustername="globalworkflowciplatformrocky8" + elif [ "${{ github.event.inputs.os }}" == "centos" ]; then + clustername="awsemctmcgc7i48xlargeciplatform" + fi + python3 pw-cluster-automation/startClusters.py $clustername + fetch-branch: + needs: run-start-clusters runs-on: ubuntu-latest env: GH_TOKEN: ${{ secrets.GITHUBTOKEN }} outputs: branch: ${{ steps.get-branch.outputs.branch }} + repo: ${{ steps.get-branch.outputs.repo }} steps: - - name: Fetch branch name for PR + - name: Fetch branch name and repo for PR id: get-branch run: | pr_number=${{ github.event.inputs.pr_number }} @@ -47,8 +77,14 @@ jobs: branch=${{ github.event.inputs.ref }} 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') + repo_name=$(gh pr view $pr_number --repo $repo --json headRepository --jq '.headRepository.name') + repo="$repo_owner/$repo_name" fi - echo "::set-output name=branch::$branch" + { + echo "branch=$branch" + echo "repo=$repo" + } >> $GITHUB_OUTPUT checkout: needs: fetch-branch @@ -64,6 +100,7 @@ jobs: with: path: ${{ github.run_id }}/HOMEgfs submodules: 'recursive' + repository: ${{ needs.fetch-branch.outputs.repo }} ref: ${{ needs.fetch-branch.outputs.branch }} build-link: diff --git a/.gitignore b/.gitignore index 8fc6d0b20b..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 @@ -171,11 +172,6 @@ ush/bufr2ioda_insitu* versions/build.ver versions/run.ver -# wxflow checkout and symlinks -ush/python/wxflow -workflow/wxflow -ci/scripts/wxflow - # jcb checkout and symlinks ush/python/jcb workflow/jcb 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/cases/gfsv17/ocnanal.yaml b/ci/cases/gfsv17/ocnanal.yaml index d559f544e4..b0605c9c16 100644 --- a/ci/cases/gfsv17/ocnanal.yaml +++ b/ci/cases/gfsv17/ocnanal.yaml @@ -15,6 +15,7 @@ base: DO_VRFY_OCEANDA: "NO" FHMAX_GFS: 240 ACCOUNT: {{ 'HPC_ACCOUNT' | getenv }} + DO_TEST_MODE: "YES" marineanl: SOCA_INPUT_FIX_DIR: {{ HOMEgfs }}/fix/gdas/soca/1440x1080x75/soca diff --git a/ci/cases/pr/C48mx500_3DVarAOWCDA.yaml b/ci/cases/pr/C48mx500_3DVarAOWCDA.yaml index 2de5fea7ff..762d2c3fbe 100644 --- a/ci/cases/pr/C48mx500_3DVarAOWCDA.yaml +++ b/ci/cases/pr/C48mx500_3DVarAOWCDA.yaml @@ -9,7 +9,7 @@ arguments: resdetocean: 5.0 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C48mx500/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C48mx500/20241120 idate: 2021032412 edate: 2021032418 nens: 0 diff --git a/ci/cases/pr/C48mx500_hybAOWCDA.yaml b/ci/cases/pr/C48mx500_hybAOWCDA.yaml index 036aa8ca60..ca477b5fba 100644 --- a/ci/cases/pr/C48mx500_hybAOWCDA.yaml +++ b/ci/cases/pr/C48mx500_hybAOWCDA.yaml @@ -10,7 +10,7 @@ arguments: resensatmos: 48 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C48mx500/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C48mx500/20241120 idate: 2021032412 edate: 2021032418 nens: 3 diff --git a/ci/cases/pr/C96C48_hybatmDA.yaml b/ci/cases/pr/C96C48_hybatmDA.yaml index b527903d69..c0833acf14 100644 --- a/ci/cases/pr/C96C48_hybatmDA.yaml +++ b/ci/cases/pr/C96C48_hybatmDA.yaml @@ -10,7 +10,7 @@ arguments: resensatmos: 48 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20241120 idate: 2021122018 edate: 2021122106 nens: 2 diff --git a/ci/cases/pr/C96C48_hybatmaerosnowDA.yaml b/ci/cases/pr/C96C48_hybatmaerosnowDA.yaml index 57d0989ae3..93abda95e5 100644 --- a/ci/cases/pr/C96C48_hybatmaerosnowDA.yaml +++ b/ci/cases/pr/C96C48_hybatmaerosnowDA.yaml @@ -9,7 +9,7 @@ arguments: resensatmos: 48 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20241120 idate: 2021122012 edate: 2021122100 nens: 2 diff --git a/ci/cases/pr/C96C48_ufs_hybatmDA.yaml b/ci/cases/pr/C96C48_ufs_hybatmDA.yaml index 41a8baa725..240f04c090 100644 --- a/ci/cases/pr/C96C48_ufs_hybatmDA.yaml +++ b/ci/cases/pr/C96C48_ufs_hybatmDA.yaml @@ -9,7 +9,7 @@ arguments: resensatmos: 48 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20241120 idate: 2024022318 edate: 2024022406 nens: 2 @@ -21,4 +21,4 @@ skip_ci_on_hosts: - gaea - orion - hercules - + - wcoss2 diff --git a/ci/cases/pr/C96_atm3DVar.yaml b/ci/cases/pr/C96_atm3DVar.yaml index fc09beeacf..6286d74dc4 100644 --- a/ci/cases/pr/C96_atm3DVar.yaml +++ b/ci/cases/pr/C96_atm3DVar.yaml @@ -8,7 +8,7 @@ arguments: resdetatmos: 96 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20241120 idate: 2021122018 edate: 2021122106 nens: 0 diff --git a/ci/cases/pr/C96_atm3DVar_extended.yaml b/ci/cases/pr/C96_atm3DVar_extended.yaml index 8ab67a750e..45a0cdd6d2 100644 --- a/ci/cases/pr/C96_atm3DVar_extended.yaml +++ b/ci/cases/pr/C96_atm3DVar_extended.yaml @@ -8,7 +8,7 @@ arguments: resdetatmos: 96 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20240610 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C96C48/20241120 idate: 2021122018 edate: 2021122118 nens: 0 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/weekly/C384C192_hybatmda.yaml b/ci/cases/weekly/C384C192_hybatmda.yaml index 6053f73124..ecaea3757f 100644 --- a/ci/cases/weekly/C384C192_hybatmda.yaml +++ b/ci/cases/weekly/C384C192_hybatmda.yaml @@ -10,7 +10,7 @@ arguments: resensatmos: 192 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C384C192 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C384C192/20240610 idate: 2023040118 edate: 2023040200 nens: 2 diff --git a/ci/cases/weekly/C384_atm3DVar.yaml b/ci/cases/weekly/C384_atm3DVar.yaml index 1a14059ab1..4409a73ac1 100644 --- a/ci/cases/weekly/C384_atm3DVar.yaml +++ b/ci/cases/weekly/C384_atm3DVar.yaml @@ -10,7 +10,7 @@ arguments: resensatmos: 192 comroot: {{ 'RUNTESTS' | getenv }}/COMROOT expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR - icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C384C192 + icsdir: {{ 'ICSDIR_ROOT' | getenv }}/C384C192/20240610 idate: 2023040118 edate: 2023040200 nens: 0 diff --git a/ci/cases/yamls/atmaerosnowDA_defaults_ci.yaml b/ci/cases/yamls/atmaerosnowDA_defaults_ci.yaml index 6d978e25ef..24f629128a 100644 --- a/ci/cases/yamls/atmaerosnowDA_defaults_ci.yaml +++ b/ci/cases/yamls/atmaerosnowDA_defaults_ci.yaml @@ -3,3 +3,4 @@ defaults: base: DO_JEDISNOWDA: "YES" ACCOUNT: {{ 'HPC_ACCOUNT' | getenv }} + DO_TEST_MODE: "YES" 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/gfs_defaults_ci.yaml b/ci/cases/yamls/gfs_defaults_ci.yaml index d09f78b8b8..65d440ac93 100644 --- a/ci/cases/yamls/gfs_defaults_ci.yaml +++ b/ci/cases/yamls/gfs_defaults_ci.yaml @@ -2,3 +2,4 @@ defaults: !INC {{ HOMEgfs }}/parm/config/gfs/yaml/defaults.yaml base: ACCOUNT: {{ 'HPC_ACCOUNT' | getenv }} + DO_TEST_MODE: "YES" diff --git a/ci/cases/yamls/gfs_extended_ci.yaml b/ci/cases/yamls/gfs_extended_ci.yaml index 8caa942eed..e60350ad8a 100644 --- a/ci/cases/yamls/gfs_extended_ci.yaml +++ b/ci/cases/yamls/gfs_extended_ci.yaml @@ -12,3 +12,4 @@ base: FCST_BREAKPOINTS: 192 FHMAX_GFS: 384 FHMAX_HF_GFS: 120 + DO_TEST_MODE: "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/ci/cases/yamls/soca_gfs_defaults_ci.yaml b/ci/cases/yamls/soca_gfs_defaults_ci.yaml index 3d75cc911a..38d55e3574 100644 --- a/ci/cases/yamls/soca_gfs_defaults_ci.yaml +++ b/ci/cases/yamls/soca_gfs_defaults_ci.yaml @@ -3,3 +3,4 @@ defaults: base: ACCOUNT: {{ 'HPC_ACCOUNT' | getenv }} DO_JEDIOCNVAR: "YES" + DO_TEST_MODE: "YES" 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/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)) diff --git a/ci/scripts/wxflow b/ci/scripts/wxflow new file mode 120000 index 0000000000..9dbee42bc8 --- /dev/null +++ b/ci/scripts/wxflow @@ -0,0 +1 @@ +../../sorc/wxflow/src/wxflow \ No newline at end of file diff --git a/docs/source/_static/noaacsp_cluster_1.png b/docs/source/_static/noaacsp_cluster_1.png deleted file mode 100644 index 3fdc0e68b8..0000000000 Binary files a/docs/source/_static/noaacsp_cluster_1.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_cluster_2.png b/docs/source/_static/noaacsp_cluster_2.png deleted file mode 100644 index 0fc3b2896d..0000000000 Binary files a/docs/source/_static/noaacsp_cluster_2.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_cluster_3.png b/docs/source/_static/noaacsp_cluster_3.png deleted file mode 100644 index bf3991b7ff..0000000000 Binary files a/docs/source/_static/noaacsp_cluster_3.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_cluster_4.png b/docs/source/_static/noaacsp_cluster_4.png deleted file mode 100644 index 9294d40bbe..0000000000 Binary files a/docs/source/_static/noaacsp_cluster_4.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_cluster_5.png b/docs/source/_static/noaacsp_cluster_5.png deleted file mode 100644 index 9fd7a96e40..0000000000 Binary files a/docs/source/_static/noaacsp_cluster_5.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_cluster_6.png b/docs/source/_static/noaacsp_cluster_6.png deleted file mode 100644 index 79287bc1e7..0000000000 Binary files a/docs/source/_static/noaacsp_cluster_6.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_instance_1.png b/docs/source/_static/noaacsp_instance_1.png deleted file mode 100644 index 0e06fe345b..0000000000 Binary files a/docs/source/_static/noaacsp_instance_1.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_instance_2.png b/docs/source/_static/noaacsp_instance_2.png deleted file mode 100644 index 7c74d32853..0000000000 Binary files a/docs/source/_static/noaacsp_instance_2.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_instance_3.png b/docs/source/_static/noaacsp_instance_3.png deleted file mode 100644 index f1031fb576..0000000000 Binary files a/docs/source/_static/noaacsp_instance_3.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_instance_4.png b/docs/source/_static/noaacsp_instance_4.png deleted file mode 100644 index f4aedb27d1..0000000000 Binary files a/docs/source/_static/noaacsp_instance_4.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_login.png b/docs/source/_static/noaacsp_login.png deleted file mode 100644 index fd2ea73144..0000000000 Binary files a/docs/source/_static/noaacsp_login.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_using_1.png b/docs/source/_static/noaacsp_using_1.png deleted file mode 100644 index 68550db050..0000000000 Binary files a/docs/source/_static/noaacsp_using_1.png and /dev/null differ diff --git a/docs/source/_static/noaacsp_using_2.png b/docs/source/_static/noaacsp_using_2.png deleted file mode 100644 index 4d1899f8f5..0000000000 Binary files a/docs/source/_static/noaacsp_using_2.png and /dev/null differ diff --git a/docs/source/conf.py b/docs/source/conf.py index 81f231f6b0..2c462a2675 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,6 +25,7 @@ # The full version, including alpha/beta/rc tags release = '0.1' +numfig = True # -- General configuration --------------------------------------------------- @@ -42,7 +43,7 @@ 'sphinx.ext.viewcode', 'sphinx.ext.githubpages', 'sphinx.ext.napoleon', - 'sphinxcontrib.bibtex' + 'sphinxcontrib.bibtex', ] bibtex_bibfiles = ['references.bib'] @@ -120,3 +121,4 @@ def setup(app): # Output file base name for HTML help builder. htmlhelp_basename = 'Global-Workflow' + diff --git a/docs/source/index.rst b/docs/source/index.rst index 637a4ef70a..e6513b743a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -41,5 +41,6 @@ Table of Contents hpc.rst output.rst run.rst + wave.rst noaa_csp.rst errors_faq.rst diff --git a/docs/source/noaa_csp.rst b/docs/source/noaa_csp.rst index 66317efe92..98754335e2 100644 --- a/docs/source/noaa_csp.rst +++ b/docs/source/noaa_csp.rst @@ -4,12 +4,11 @@ Configuring NOAA Cloud Service Providers ######################################## -The NOAA Cloud Service Providers (CSP) support the forecast-only -configurations for the global workflow. Once a suitable CSP instance -and cluster is defined/created, the global workflow may be executed as -on the other platforms discussed in the previous sections. In order -successfully execute the global-workflow, a suitable CSP cluster must -be created. Currently the global-workflow supports the following +The NOAA Cloud Service Providers (CSPs) support the forecast-only, +coupled, and GEFS configurations for global-workflow. +Once a suitable CSP instance and cluster is defined/created, +the global-workflow may be executed similarly to the on-premises (on-prem) machines. +Currently, the global-workflow supports the following instance and storage types as a function of CSP and forecast resolution. @@ -24,177 +23,398 @@ resolution. - **Instance Type** - **Partition** - **File System** - * - Amazon Web Services Parallel Works - - C48 - - ``ATM`` - - ``c5.9xlarge (36 vCPUs, 72 GB Memory, amd64)`` + * - Amazon Web Services ParallelWorks + - C48, C96, C192, C384 + - ``ATM``, ``GEFS`` + - ``c5.18xlarge (72 vCPUs, 144 GB Memory, amd64)`` - ``compute`` - - ``/lustre`` + - ``/lustre``, ``/bucket`` + * - Azure ParallelWorks + - C48, C96, C192, C384 + - ``ATM``, ``GEFS`` + - ``Standard_F48s_v2 (48 vCPUs, 96 GB Memory, amd64)`` + - ``compute`` + - ``/lustre``, ``/bucket`` + * - GCP ParallelWorks + - C48, C96, C192, C384 + - ``ATM``, ``GEFS`` + - ``c3d-standard-60-lssd (60 vCPUs, 240 GB Memory, amd64)`` + - ``compute`` + - ``/lustre``, ``/bucket`` -Instructions regarding configuring the respective CSP instance and -cluster follows. +Instructions regarding configuring the respective CSP instances and +clusters follow. ********************* Login to the NOAA CSP ********************* -Log in to the `NOAA CSP `_ and into +Log in to the `NOAA cloud `_, and into the resources configuration. The user should arrive at the following -screen. +screen as in :numref:`Figure %s `. +Click the blue box indicated by the red arrow to login. + +.. _pw-home: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_login_1.png + :name: noaacsp_login_1 + :class: with-border + :align: center + + NOAA-PARALLELWORKS Home Page -Note that the ``Username or email`` query is case-sensitive. The user -will then be prompted for their respective RSA token key using the -same application use for the other RDHPCS machines (i.e., Hera, Jet, -etc.,). +As shown in :numref:`Figure %s `, Fill the ``Username / Email`` box with your username or NOAA email (usually in "FirstName.LastName" format). +Note that the ``Username or email`` query field is case-sensitive. +Then enter the respective ``Pin + RSA`` combination using the same RSA token application used +for access to other RDHPCS machines (e.g., Hera, Gaea). -.. image:: _static/noaacsp_login.png +.. _login2: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_login_2.png + :name: noaacsp_login_2 + :class: with-border + :align: center + + NOAA-SSO-PARALLELWORKS Login Page ******************************* Configure the NOAA CSP Instance ******************************* -Once logged into the NOAA CSP, navigate to the :red-text:`RESOURCES` section -and click the ``+ Add Resource`` button in the upper-right corner as -illustrated below. +Once logged into the NOAA CSP, navigate to the ``Marketplace`` section +in the left sidebar as indicated by the red arrow in :numref:`Figure %s `, and click. +Scroll down to select "AWS EPIC Wei CentOS", circled in red. +Note that the current global-workflow is still using CentOS-built spack-stack, +but it will be updated to Rocky 8 soon. + +.. _pw-marketplace: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_instance_1.png + :name: noaacsp_instance_1 + :class: with-border + :align: center + + ParallelWorks Marketplace + +Next, click "Fork latest" as shown in the red-circle in :numref:`Figure %s`. + +.. _fork-latest: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_instance_2.png + :name: noaacsp_instance_2 + :class: with-border + :align: center -.. image:: _static/noaacsp_instance_1.png + Fork Instance From Marketplace -Next, the mandatory attributes for the respective instance must be -defined as shown in the illustration below. +Please provide a unique name in the "New compute node" field for the instance +(see the box pointer by the red arrow in :numref:`Figure %s `). +Best practices suggest one that is clear, concise, and relevant to the application. +Click ``Fork`` (in the red-circle) to fork an instance. -.. image:: _static/noaacsp_instance_2.png +.. _create-fork: -The annotated attributes and their respective descriptions are as -follows. +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_instance_3.png + :name: noaacsp_instance_3 + :class: with-border + :align: center + + Create the Fork -1. A unique name for the instance. Best practices suggest one that is - clear, concise, and relevant to the application. -2. A short description of the instance, i.e., ``This instance supports - this task.`` -3. Tag(s) describing and identifying the respective instance. These - allow for improved bookkeeping, especially when a user has multiple - or concurrent instance types. +Now, an instance is forked, and it is time to configure the cluster. Follow these steps as shown in :numref:`Figure %s `: -Next, the cluster is defined as shown in the following illustration. +#. Select a *Resource Account*; usually it is *NOAA AWS Commercial Vault*. +#. Select a *Group*, which will be something like: ``ca-epic``, ``ca-sfs-emc``, etc. +#. Copy and paste your public key (e.g., ``.ssh/id_rsa.pub``, ``.ssh/id_dsa.pub`` from your laptop). +#. Modify *User Bootstrap*. If you are not using the ``ca-epic`` group, please UNCOMMENT line 2. +#. Keep *Health Check* as it is. -.. image:: _static/noaacsp_instance_3.png +Click *Save Changes* at the top-right as shown in the red circle. -The NOAA Parallel Works initiative currently provides 2 CSPs for the -global-workflow; **AWS** (Amazon Web Services) and **Azure** -(Microsoft Azure). Existing clusters may also be modified. However -this is neither recommended or supported. +.. _config-cluster: -Finally, when satisfied with the CSP instance configure, click ``Add -Resource`` as illustrated below. +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_instance_4.png + :name: noaacsp_instance_4 + :class: with-border + :align: center -.. image:: _static/noaacsp_instance_4.png + Configure & Save the Instance + +NOAA ParallelWorks (PW) currently provides 3 CSPs: +**AWS** (Amazon Web Services), **Azure** (Microsoft Azure), +and **GCP** (Google Cloud Platform). +Existing clusters may also be modified. +However, it is best practice to fork from Marketplace with something similar to your requests +(rather than modifying an existing cluster). ****************************** -Configure the NOAA CSP Cluster +Add CSP Lustre Filesystem ****************************** -Navigate to the tab and locate the CSP instance configured in the -previous section and click on the link, `globalworkflowdemo` for this -example. +To run global-workflow on CSPs, we need to attach the ``/lustre`` filesystem as a run directory. +First, we need to add/define our ``/lustre`` filesystem. +To do so, navigate to the middle of the NOAA PW website left side panel and select *Lustre* +(see the red arrow in :numref:`Figure %s `), and then click *Add Storage* +at the top right, as shown in the red circle. + +.. _select-lustre: -.. image:: _static/noaacsp_cluster_1.png +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_lustre_1.png + :name: noaacsp_lustre_1 + :class: with-border + :align: center + + Add Lustre Storage + +Select `FSx` for the AWS FSx ``/lustre`` filesystem as shown in the red circle. -The respective CSP cluster maybe then be configured. The mandatory -configuration attributes are as follows. +Define ``/lustre`` with steps in :numref:`Figure %s `: -- Availability zone; -- Disk size and storage type(s); -- Available compute and resource partitions. +#. Provide a clear and meaningful *Resource name*, as shown by the first red arrow +#. Provide a short sentence for *Description*, as shown in the second red arrow +#. Choose **linux** for *Tag*, as shown by red arrow #3 -The following image describes the general settings for the respective -cluster. These attributes are specific to the user and the respective -user's group allocation. The right-most panel provides a breakdown of -the costs related to the requested compute and storage -resources. While there is a space to place an SSH key here, RDHPCS -recommends adding any SSH keys under the respective user's -``Accountâž¡Authentication instead``. This will allow you to connect -from another machine instead of using the Parallel Works web terminal. +Click *Add Storage* as in the red box at the top right corner. -.. image:: _static/noaacsp_cluster_2.png +This will create a ``/lustre`` filesystem template after clicking on the red square shown in :numref:`Figure %s `. + +.. _define-lustre: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_lustre_2.png + :name: noaacsp_lustre_2 + :class: with-border + :align: center + + Define Lustre Attributes -The following image describes the controller settings for a cluster -created for a C48 atmosphere forecast-only configuration. Here the -user must define the instance type (see the table above), the number -of image disks and the image disk sizes. +After creating the template, we need to fill in information for this ``/lustre`` filesystem. +To do so, go to the NOAA PW website, and click *Lustre* on the left side panel, as +indicated by red arrow 1 in :numref:`Figure %s `. Then select the filesystem defined by *Resource name* in :numref:`Figure %s above `, +as shown in the red box. Here, the user can delete this resource if not needed by +clicking the trash can (indicated by red arrow 2 in :numref:`Figure %s `). + +.. _check-lustre: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_lustre_3.png + :name: noaacsp_lustre_3 + :class: with-border + :align: center + + Show Lustre on the PW page -.. image:: _static/noaacsp_cluster_3.png +By clicking the filesystem in the red box of the image above, +users will be led to the ``/lustre`` definition page. -Next the partitions for the cluster may be defined. A partition -configuration for the aforementioned C48 atmosphere forecast-only -application is illustrated in the figure below. Note that the instance -type beneath ``Controller Settings`` and ``Partitions`` must be -identical. Other configurations are not supported by the -global-workflow team. Once the partitions are configured, click the -``+ Add Partition`` button in the upper-right corner. +Then follow the steps illustrated in :numref:`Figure %s ` below: -.. image:: _static/noaacsp_cluster_4.png +#. Choose a size in the *Storage Capacity (GB)* box, as indicated by red arrow 1. + There is a minimum of 1200 for AWS. For the C48 ATM/GEFS case this will be enough. + For SFS-C96 case or C768 ATM/S2S case, it should probably be increased to 12000. +#. For *File System Deployment*, choose "SCRATCH_2" for now as indicated by red arrow 2. + Do not use SCRATCH_1, as it is used for testing by PW. +#. Choose **NONE** for *File System Compression* as pointed by red arrow 3. + Only choose LZ4 if you understand what it means. +#. Leave *S3 Import Path* and *S3 Export Path* blank for now. +#. Click **Save Changes** in the red circle to save the definition/changes made. -For the storage do be allocated for the global-workflow application it -is suggested that the ``Mount Point`` be ``/lustre``. Once the storage -has been configured, click the ``+ Add Attached Storage`` button in -the upper-right corner. This is illustrated in the following image. +.. _config-lustre: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_lustre_4.png + :name: noaacsp_lustre_4 + :class: with-border + :align: center + + Defining the Lustre Filesystem Capacity + +For the storage to be allocated for the global-workflow application, +it is suggested that the ``Mount Point`` be ``/lustre``. Once the storage +has been configured, follow the steps below to attach the ``/lustre`` filesystem. + +****************************** +Attach CSP Lustre Filesystem +****************************** + +Now we need to attach the defined filesystem to our cluster. +Go back to our the NOAA PW website (https://noaa.parallel.works), and click *Cluster* +as shown in :numref:`Figure %s ` below, then select the cluster you made (e.g., ``AWS EPIC Wei CentOS example`` cluster, as shown in the red box below). +Note, one can remove/delete this cluster if no longer needed by +clicking the trash can shown in the red circle at right. + +.. _select-cluster: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_filesystem_1.png + :name: noaacsp_filesystem_1 + :class: with-border + :align: center -.. image:: _static/noaacsp_cluster_5.png + Add Attached Filesystems -Finally, the following illustrates a JSON version of the cluster -configuration created from the steps above. When opening issues -related to the NOAA CSP global-workflow applications please include -the JSON content. +When we get into the cluster page, click the *Definition* in the top menu as +in the red-box in :numref:`Figure %s `. Then we can attach the defined filesystems. +When finished, remeber to click *Save Changes* to save the changes. -.. image:: _static/noaacsp_cluster_6.png +.. _add-filesystem: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_filesystem_2.png + :name: noaacsp_filesystem_2 + :class: with-border + :align: center + + Add Attached ``/lustre`` and/or ``/bucket`` Filesystems + +Scroll down to the bottom as show in :numref:`Figure %s `, and click *Add Attached Filesystems* as in the red circle. + +.. _click-add-fs: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_filesystem_3.png + :name: noaacsp_filesystem_3 + :class: with-border + :align: center + + Attach ``/lustre`` and/or ``/bucket`` Filesystems + +After clicking *Add Attached Filesystems*, go to *Attached Filesystems settings*, and follow the steps listed here, +which are also shown in :numref:`Figure %s `. + +#. In the *Storage* box, select the lustre filesystem defined above, as in red arrow 1. +#. In the *Mount Point* box, name it ``/lustre`` (the common and default choice), as indicated by red arrow 2. + If you choose a different name, make sure that the name chosen here uses the name from the global-workflow setup step. + +If you have a S3 bucket, one can attached as: + +#. In the *Storage* box, select the bucket you want to use, as in red arrow 3. +#. In the *Mount Point* box, name it ``/bucket`` (the common and default choice) as indicated by red arrow 4. + +.. _change-settings: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_filesystem_4.png + :name: noaacsp_filesystem_4 + :class: with-border + :align: center + + Adjust Attached ``/lustre`` and/or ``/bucket`` Filesystem Settings + +Always remember to click *Save Changes* after making any changes to the cluster. ************************** Using the NOAA CSP Cluster ************************** -To activate the cluster, click the button circled in -:red-text:red. The cluster status is denoted by the color-coded button -on the right. The amount of time required to start the cluster is -variable and not immediate and may take several minutes for the -cluster to become. +To activate the cluster, click *Clusters* on the left panel of the NOAA PW website shown in :numref:`Figure %s `, +as indicated by the red arrow. Then click the *Sessions* button in the red square, and click the power +button in the red circle. The cluster status is denoted by the color-coded button +on the right: red means stopped; orange means requested; green means active. The amount of time required to start +the cluster varies and is not immediate; it may take several minutes (often 10-20) for the cluster to become active. + +.. _activate-cluster: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_using_1.png + :name: noaacsp_using_1 + :class: with-border + :align: center + + Activate the Cluster + +When the cluster is activated, users will see the following indicators of success listed below as seen in :numref:`Figure %s `: -.. image:: _static/noaacsp_using_1.png +#. A green dot on the left beside the AWS logo means that the cluster is active (indicated by red arrow 1). +#. A green dot on the right labeled "active" means that the cluster is active (indicated by red arrow 2). +#. A green power button means the cluster is active (indicated by red arrow 3). +#. Clicking the clipboard icon (blue square with arrow inside), indicated by red arrow 4 will copy the cluster's IP address into the clipboard. Then, + you can open a laptop terminal window (such as xterm), and run ``ssh username@the-ip-address``. This will connect you + to the AWS cluster, and you can do your work there. +#. Alternatively, users can click directly on the ``username@the-ip-address``, and a PW web terminal will appear at the + bottom of the website. Users can work through this terminal to use their AWS cluster. -For instances where a NOAA CSP cluster does not initialize, useful -output can be found beneath the ``Logs`` section beneath the -``Provision`` tab as illustrated below. Once again, when opening -issues related to the NOAA CSP cluster initialization please include -this information. +Please note, as soon as the cluster is activated, AWS/PW starts charging you for use of the cluster. +As this cluster is exclusive for yourself, AWS keeps charging you as long as the cluster is active. +For running global-workflow, one needs to keep the cluster active if there are any Rocoto jobs running +because Rocoto uses `crontab`, which needs the cluster active all the time, or the crontab job will be terminated. -.. image:: _static/noaacsp_using_2.png +.. _cluster-on: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_using_2.png + :name: noaacsp_using_2 + :class: with-border + :align: center + + Knowing the Cluster + +After finishing your work on the AWS cluster, you should terminate/stop the cluster, unless you have reasons to keep it active. +To stop/terminate the cluster, go to the cluster session, and click the green power button as show in :numref:`Figure %s `. +A window will pop up; click the red *Turn Off* button to switch off the cluster. + +.. _stop-cluster: + +.. figure:: https://raw.githubusercontent.com/wiki/NOAA-EMC/global-workflow/images/noaacsp_using_3.png + :name: noaacsp_using_3 + :class: with-border + :align: center + + Terminating the Cluster *************************** Running the Global Workflow *************************** -The global-workflow configuration currently requires that all initial -conditions, observations, and fixed-files, are staged in the -appropriate paths prior to running the global-workflow. As suggested -above, it is strongly recommended the the user configure their -respective experiments to use the ``/lustre`` file system for the -``EXPDIR`` and ``ROTDIR`` contents. The ``/contrib`` file system is -suitable for compiling and linking the workflow components required of -the global-workflow. +Assuming you have an AWS cluster running, after logging in to the cluster through ``ssh`` from your laptop terminal +or accessing the cluster from your web terminal, you can start to clone, compile, and run global-workflow. -The software stack supporting the ``develop`` branch of the -global-workflow is provided for the user and is located beneath -``/contrib/emc_static/spack-stack``. The modules required for the -global-workflow execution may be loaded as follows. +#. Clone global-workflow (assumes you have set up access to GitHub): -.. code-block:: bash + .. code-block:: console - user@host:$ module unuse /opt/cray/craype/default/modulefiles - user@host:$ module unuse /opt/cray/modulefiles - user@host:$ module use /contrib/emc_static/spack-stack/miniconda/modulefiles/miniconda - user@host:$ module load py39_4.12.0 - user@host:$ module load rocoto/1.3.3 + cd /contrib/$USER #you should have a username and have a directory at /contrib, where we save our permanent files. + git clone --recursive git@github.com:NOAA-EMC/global-workflow.git global-workflow + #or the develop fork at EPIC: + git clone --recursive git@github.com:NOAA-EPIC/global-workflow-cloud.git global-workflow-cloud -The execution of the global-workflow should now follow the same steps -as those for the RDHPCS on-premise hosts. +#. Compile global-workflow: + + .. code-block:: console + + cd /contrib/$USER/global-workflow # or cd /contrib/$USER/global-workflow-cloud depending on which one you cloned + cd sorc + build_all.sh # or similar command to compile for gefs, or others. + link_workflow.sh # after build_all.sh finished successfully +#. As users may define a very small cluster as controller, one may use the script below to compile in compute node. + Save the this script in a file, say, ``com.slurm``, and submit this job with command ``sbatch com.slurm``: + + .. code-block:: console + + #!/bin/bash + #SBATCH --job-name=compile + #SBATCH --account=$USER + #SBATCH --qos=batch + #SBATCH --partition=compute + #SBATCH -t 01:15:00 + #SBATCH --nodes=1 + #SBATCH -o compile.%J.log + #SBATCH --exclusive + + gwhome=/contrib/Wei.Huang/src/global-workflow-cloud # Change this to your own "global-workflow" source directory + cd ${gwhome}/sorc + source ${gwhome}/workflow/gw_setup.sh + #build_all.sh + build_all.sh -w + link_workflow.sh + +#. Run global-workflow C48 ATM test case (assumes user has ``/lustre`` filesystem attached): + + .. code-block:: console + + cd /contrib/$USER/global-workflow + + HPC_ACCOUNT=${USER} pslot=c48atm RUNTESTS=/lustre/$USER/run \ + ./workflow/create_experiment.py \ + --yaml ci/cases/pr/C48_ATM.yaml + + cd /lustre/$USER/run/EXPDIR/c48atm + crontab c48atm.crontab + +EPIC has copied the C48 and C96 ATM, GEFS, and some other data to AWS, and the current code has been set up to use those data. +If users want to run their own case, they need to make changes to the IC path and others to make it work. +The execution of the global-workflow should now follow the same steps +as those for the RDHPCS on-premises hosts. diff --git a/docs/source/references.bib b/docs/source/references.bib new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/source/setup.rst b/docs/source/setup.rst index 0916033cbd..1f94610f65 100644 --- a/docs/source/setup.rst +++ b/docs/source/setup.rst @@ -35,7 +35,7 @@ The following command examples include variables for reference but users should cd workflow ./setup_expt.py gfs forecast-only --idate $IDATE --edate $EDATE [--app $APP] [--start $START] [--interval $INTERVAL_GFS] [--resdetatmos $RESDETATMOS] [--resdetocean $RESDETOCEAN] - [--pslot $PSLOT] [--configdir $CONFIGDIR] [--comroot $COMROOT] [--expdir $EXPDIR] + [--pslot $PSLOT] [--configdir $CONFIGDIR] [--comroot $COMROOT] [--expdir $EXPDIR] [--account $ACCOUNT] where: @@ -61,6 +61,7 @@ where: * ``$INTERVAL_GFS`` is the forecast interval in hours [default: 6] * ``$COMROOT`` is the path to your experiment output directory. Your ``ROTDIR`` (rotating com directory) will be created using ``COMROOT`` and ``PSLOT``. [default: $HOME (but do not use default due to limited space in home directories normally, provide a path to a larger scratch space)] * ``$EXPDIR`` is the path to your experiment directory where your configs will be placed and where you will find your workflow monitoring files (i.e. rocoto database and xml file). DO NOT include PSLOT folder at end of path, it will be built for you. [default: $HOME] + * ``$ACCOUNT`` is the HPC (i.e. slurm or PBS) account to use for the experiment. [default: $HPC_ACCOUNT; if $HPC_ACCOUNT is not set, then the default in the host file (workflow/hosts/.yaml) will be used] Examples: 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..fccc2f87a5 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}" @@ -147,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}" @@ -155,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/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..27001bebd7 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}" @@ -109,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/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/jobs/JGLOBAL_FORECAST b/jobs/JGLOBAL_FORECAST index e64a91d21c..0572f1d2d9 100755 --- a/jobs/JGLOBAL_FORECAST +++ b/jobs/JGLOBAL_FORECAST @@ -77,7 +77,7 @@ if [[ "${DO_ICE}" == "YES" ]]; then COMIN_ICE_RESTART_PREV:COM_ICE_RESTART_TMPL fi -if [[ "${DO_AERO}" == "YES" ]]; then +if [[ "${DO_AERO_FCST}" == "YES" ]]; then YMD="${PDY}" HH="${cyc}" declare_from_tmpl -rx \ COMOUT_CHEM_HISTORY:COM_CHEM_HISTORY_TMPL fi 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/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/gdas.yaml.j2 b/parm/archive/gdas.yaml.j2 index 1e9597ba1c..fa8919a589 100644 --- a/parm/archive/gdas.yaml.j2 +++ b/parm/archive/gdas.yaml.j2 @@ -67,7 +67,7 @@ gdas: - "{{ COMIN_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}oznstat" - "{{ COMIN_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}radstat" {% endif %} - {% if AERO_ANL_RUN == "gdas" or AERO_ANL_RUN == "both" %} + {% if DO_AERO_ANL %} - "{{ COMIN_CHEM_ANALYSIS | relpath(ROTDIR) }}/{{ head }}aerostat" {% endif %} {% if DO_PREP_OBS_AERO %} diff --git a/parm/archive/gfs_arcdir.yaml.j2 b/parm/archive/gfs_arcdir.yaml.j2 index 57dbc78885..98803b473c 100644 --- a/parm/archive/gfs_arcdir.yaml.j2 +++ b/parm/archive/gfs_arcdir.yaml.j2 @@ -50,7 +50,7 @@ ARCDIR ~ "/snowstat." ~ RUN ~ "." ~ cycle_YMDH ~ ".tgz"]) %} {% endif %} - {% if AERO_ANL_RUN == RUN or AERO_ANL_RUN == "both" %} + {% if DO_AERO_ANL %} {% do det_anl_files.append([COMIN_CHEM_ANALYSIS ~ "/" ~ head ~ "aerostat", ARCDIR ~ "/aerostat." ~ RUN ~ "." ~ cycle_YMDH ]) %} {% endif %} 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/archive/gfsa.yaml.j2 b/parm/archive/gfsa.yaml.j2 index dcf059d871..4efe281120 100644 --- a/parm/archive/gfsa.yaml.j2 +++ b/parm/archive/gfsa.yaml.j2 @@ -38,7 +38,7 @@ gfsa: {% else %} - "{{ COMIN_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}gsistat" {% endif %} - {% if AERO_ANL_RUN == "gfs" or AERO_ANL_RUN == "both" %} + {% if DO_AERO_ANL %} - "{{ COMIN_CHEM_ANALYSIS | relpath(ROTDIR) }}/{{ head }}aerostat" {% endif %} {% if DO_PREP_OBS_AERO %} diff --git a/parm/archive/master_gfs.yaml.j2 b/parm/archive/master_gfs.yaml.j2 index ab9a00c95e..e7187d70d5 100644 --- a/parm/archive/master_gfs.yaml.j2 +++ b/parm/archive/master_gfs.yaml.j2 @@ -33,7 +33,7 @@ datasets: {% endfilter %} {% endif %} -{% if AERO_FCST_RUN == "gfs" or AERO_FCST_RUN == "both" %} +{% if DO_AERO_FCST %} # Aerosol forecasts {% filter indent(width=4) %} {% include "chem.yaml.j2" %} 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 261dcb021b..ec0fc3e402 100644 --- a/parm/config/gefs/config.base +++ b/parm/config/gefs/config.base @@ -64,6 +64,8 @@ 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 +export DO_TEST_MODE="@DO_TEST_MODE@" # option to change configuration for automated testing #################################################### # DO NOT ADD MACHINE DEPENDENT STUFF BELOW THIS LINE @@ -133,11 +135,8 @@ export DO_COUPLED="NO" export DO_WAVE="NO" export DO_OCN="NO" export DO_ICE="NO" -export DO_AERO="NO" export DO_EXTRACTVARS="@DO_EXTRACTVARS@" # Option to process and extract a subset of products to save on disk -export AERO_FCST_RUN="" # When to run aerosol forecast: gdas, gfs, or both -export AERO_ANL_RUN="" # When to run aerosol analysis: gdas, gfs, or both -export WAVE_RUN="" # When to include wave suite: gdas, gfs, or both +export DO_AERO_FCST="NO" export DOBNDPNT_WAVE="NO" # The GEFS buoys file does not currently have any boundary points export DOIBP_WAV="NO" # Option to create point outputs from input boundary points export FRAC_GRID=".true." @@ -182,14 +181,11 @@ case "${APP}" in ATM) ;; ATMA) - export DO_AERO="YES" - export AERO_ANL_RUN="both" - export AERO_FCST_RUN="gdas" + export DO_AERO_FCST="YES" ;; ATMW) export DO_COUPLED="YES" export DO_WAVE="YES" - export WAVE_RUN="both" ;; NG-GODAS) export DO_ATM="NO" @@ -202,20 +198,16 @@ case "${APP}" in export DO_ICE="YES" if [[ "${APP}" =~ A$ ]]; then - export DO_AERO="YES" - export AERO_ANL_RUN="both" - export AERO_FCST_RUN="gdas" + export DO_AERO_FCST="YES" fi 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 @@ -225,6 +217,7 @@ export FHMAX=9 export FHOUT=3 # Will be changed to 1 in config.base if (DOHYBVAR set to NO and l4densvar set to false) export FHOUT_OCN=3 export FHOUT_ICE=3 +export FHOUT_AERO=3 # GFS cycle info export INTERVAL_GFS=@INTERVAL_GFS@ # Frequency of GFS forecast @@ -238,7 +231,6 @@ else export OFFSET_START_HOUR=0 fi - # GFS output and frequency export FHMIN_GFS=0 export FHMAX_GFS="@FHMAX_GFS@" @@ -246,7 +238,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@ @@ -256,6 +248,10 @@ export FHOUT_WAV=3 export FHMAX_HF_WAV=120 export FHOUT_HF_WAV=1 export FHMAX_WAV=${FHMAX_GFS} +export FHMAX_WAV_GFS=${FHMAX_GFS} +export FHMAX_HF_GFS=$(( FHMAX_HF_GFS > FHMAX_GFS ? FHMAX_GFS : FHMAX_HF_GFS )) +export FHMAX_WAV_GFS=$(( FHMAX_WAV_GFS > FHMAX_GFS ? FHMAX_GFS : FHMAX_WAV_GFS )) +export FHMAX_HF_WAV=$(( FHMAX_HF_WAV > FHMAX_WAV_GFS ? FHMAX_WAV_GFS : FHMAX_HF_WAV )) export ILPOST=1 # gempak output frequency up to F120 export FHMIN_ENKF=${FHMIN_GFS} @@ -296,6 +292,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 @@ -327,7 +324,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 +335,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/gefs/config.efcs b/parm/config/gefs/config.efcs index 0086121450..6bf0ed0a18 100644 --- a/parm/config/gefs/config.efcs +++ b/parm/config/gefs/config.efcs @@ -6,7 +6,7 @@ echo "BEGIN: config.efcs" # Turn off components in ensemble -# export DO_AERO="NO" +# export DO_AERO_FCST="NO" # export DO_OCN="NO" # export DO_ICE="NO" # export DO_WAVE="NO" @@ -19,7 +19,7 @@ string="--fv3 ${CASE}" [[ "${DO_OCN}" == "YES" ]] && string="${string} --mom6 ${OCNRES}" [[ "${DO_ICE}" == "YES" ]] && string="${string} --cice6 ${ICERES}" [[ "${DO_WAVE}" == "YES" ]] && string="${string} --ww3 ${waveGRD// /;}" -[[ "${DO_AERO}" == "YES" ]] && string="${string} --gocart" +[[ "${DO_AERO_FCST}" == "YES" ]] && string="${string} --gocart" # shellcheck disable=SC2086 source "${EXPDIR}/config.ufs" ${string} @@ -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 d17a6d3177..2a15056ec9 100644 --- a/parm/config/gefs/config.fcst +++ b/parm/config/gefs/config.fcst @@ -8,24 +8,12 @@ echo "BEGIN: config.fcst" export USE_ESMF_THREADING="YES" # Toggle to use ESMF-managed threading or traditional threading in UFSWM export COPY_FINAL_RESTARTS="NO" # Toggle to copy restarts from the end of GFS/GEFS Run (GDAS is handled seperately) -# Turn off waves if not used for this RUN -case ${WAVE_RUN} in - both | "${RUN/enkf}" ) ;; # Don't change - *) DO_WAVE="NO" ;; # Turn waves off -esac - -# Turn off aerosols if not used for this RUN -case ${AERO_FCST_RUN} in - both | "${RUN/enkf}" ) ;; # Don't change - *) DO_AERO="NO" ;; # Turn waves off -esac - # Source model specific information that is resolution dependent string="--fv3 ${CASE}" [[ "${DO_OCN}" == "YES" ]] && string="${string} --mom6 ${OCNRES}" [[ "${DO_ICE}" == "YES" ]] && string="${string} --cice6 ${ICERES}" [[ "${DO_WAVE}" == "YES" ]] && string="${string} --ww3 ${waveGRD// /;}" -[[ "${DO_AERO}" == "YES" ]] && string="${string} --gocart" +[[ "${DO_AERO_FCST}" == "YES" ]] && string="${string} --gocart" # We are counting on $string being multiple arguments # shellcheck disable=SC2086 source "${EXPDIR}/config.ufs" ${string} @@ -44,6 +32,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 @@ -68,8 +57,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." @@ -143,7 +132,7 @@ tbp="" if [[ "${progsigma}" == ".true." ]]; then tbp="_progsigma" ; fi # Radiation options -if [[ "${DO_AERO}" == "YES" ]]; then +if [[ "${DO_AERO_FCST}" == "YES" ]]; then export IAER=2011 # spectral band mapping method for aerosol optical properties else export IAER=1011 @@ -203,6 +192,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 ;; @@ -223,7 +217,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 @@ -270,9 +268,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.resources b/parm/config/gefs/config.resources index dc0a1f02bb..87f10b6cb9 100644 --- a/parm/config/gefs/config.resources +++ b/parm/config/gefs/config.resources @@ -67,7 +67,7 @@ case ${step} in export ntasks=1 export tasks_per_node=1 export threads_per_task=1 - export is_exclusive=True + export memory="4096M" ;; "waveinit") @@ -146,7 +146,7 @@ case ${step} in echo "MEDIATOR using (threads, PETS) = (${MEDTHREADS}, ${MEDPETS})" CHMPETS=0; CHMTHREADS=0 - if [[ "${DO_AERO}" == "YES" ]]; then + if [[ "${DO_AERO_FCST}" == "YES" ]]; then # GOCART shares the same grid and forecast tasks as FV3 (do not add write grid component tasks). (( CHMTHREADS = ATMTHREADS )) (( CHMPETS = FV3PETS )) diff --git a/parm/config/gefs/config.stage_ic b/parm/config/gefs/config.stage_ic index cac65c74b9..5822f2e794 100644 --- a/parm/config/gefs/config.stage_ic +++ b/parm/config/gefs/config.stage_ic @@ -12,12 +12,11 @@ export BASE_IC="@BASE_IC@" # Platform home for staged ICs export STAGE_IC_YAML_TMPL="${PARMgfs}/stage/master_gefs.yaml.j2" -# Set ICSDIR +source "${HOMEgfs}/versions/ic.ver" +# Set ICSDIR (if not defined) if [[ -z "${ICSDIR}" ]] ; then - ic_ver="20240610" - if (( NMEM_ENS > 0 )) ; then ensic="${CASE_ENS}" fi @@ -26,8 +25,15 @@ if [[ -z "${ICSDIR}" ]] ; then ocnic="mx${OCNRES}" fi - export ICSDIR="${BASE_IC}/${CASE}${ensic:-}${ocnic:-}/${ic_ver}" + dir_name="${CASE}${ensic:-}${ocnic:-}" + ic_ver="${ic_versions[${dir_name}]}" + + export ICSDIR="${BASE_IC}/${dir_name}/${ic_ver}" 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 aac97ef651..6d5e0ad062 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='n' + 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..48cf912dcb 100644 --- a/parm/config/gefs/yaml/defaults.yaml +++ b/parm/config/gefs/yaml/defaults.yaml @@ -15,7 +15,23 @@ 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" + DO_TEST_MODE: "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" +# 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/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.anal b/parm/config/gfs/config.anal index 27ff8742e4..123bd6decd 100644 --- a/parm/config/gfs/config.anal +++ b/parm/config/gfs/config.anal @@ -29,6 +29,11 @@ fi export lobsdiag_forenkf=".false." # anal does not need to write out jacobians # set to .true. in config.eobs and config.eupd +# Reduce number of iterations for testing mode +if [[ ${DO_TEST_MODE} = "YES" ]]; then + export SETUP="${SETUP:-}niter(1)=5,niter(2)=5," +fi + # Do not process the following datasets export GSNDBF=${GSNDBF:-/dev/null} export AMSREBF=${AMSREBF:-/dev/null} 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.base b/parm/config/gfs/config.base index ccb05abe88..46e27b4541 100644 --- a/parm/config/gfs/config.base +++ b/parm/config/gfs/config.base @@ -88,6 +88,7 @@ export REALTIME="YES" # Experiment mode (cycled or forecast-only) export MODE="@MODE@" # cycled/forecast-only +export DO_TEST_MODE="@DO_TEST_MODE@" # option to change configuration for automated testing #################################################### # DO NOT ADD MACHINE DEPENDENT STUFF BELOW THIS LINE @@ -159,6 +160,7 @@ export APP=@APP@ shopt -s extglob # Adjust APP based on RUN +# If a component (WAVES, etc) needs to be turned off by RUN, set it here case "${RUN}" in enkf*) # Turn off aerosols and waves APP="${APP/%+([WA])}" @@ -174,11 +176,12 @@ export DO_COUPLED="NO" export DO_WAVE="NO" export DO_OCN="NO" export DO_ICE="NO" -export DO_AERO="NO" +DO_AERO="NO" export DO_PREP_OBS_AERO="NO" -export AERO_FCST_RUN="" # When to run aerosol forecast: gdas, gfs, or both -export AERO_ANL_RUN="" # When to run aerosol analysis: gdas, gfs, or both -export WAVE_RUN="" # When to include wave suite: gdas, gfs, or both +aero_fcst_runs="gdas" # When to run aerosol forecast: gdas, gfs, or both +aero_anl_runs="gdas gfs" # When to run aerosol analysis: gdas, gfs, or both +export DO_AERO_FCST="NO" +export DO_AERO_ANL="NO" export DOBNDPNT_WAVE="NO" export DOIBP_WAV="NO" # Option to create point outputs from input boundary points export FRAC_GRID=".true." @@ -220,7 +223,7 @@ case "${CASE}" in ;; *) echo "FATAL ERROR: Unrecognized CASE ${CASE}, ABORT!" - exit 1 + exit 2 ;; esac @@ -228,14 +231,11 @@ case "${APP}" in ATM) ;; ATMA) - export DO_AERO="YES" - export AERO_ANL_RUN="both" - export AERO_FCST_RUN="gdas" + DO_AERO="YES" ;; ATMW) export DO_COUPLED="YES" export DO_WAVE="YES" - export WAVE_RUN="both" ;; NG-GODAS) export DO_ATM="NO" @@ -248,22 +248,29 @@ case "${APP}" in export DO_ICE="YES" if [[ "${APP}" =~ A$ ]]; then - export DO_AERO="YES" - export AERO_ANL_RUN="both" - export AERO_FCST_RUN="gdas" + DO_AERO="YES" fi if [[ "${APP}" =~ ^S2SW ]]; then export DO_WAVE="YES" - export WAVE_RUN="both" fi ;; *) - echo "Unrecognized APP: '${APP}'" - exit 1 + echo "FATAL ERROR: Unrecognized APP: '${APP}'" + exit 3 ;; esac +# Aerosol forecasts and analyses may be RUN-dependent +if [[ "${DO_AERO}" == "YES" ]]; then + if [[ ${aero_anl_runs} =~ ${RUN} ]]; then + export DO_AERO_ANL="YES" + fi + if [[ ${aero_fcst_runs} =~ ${RUN} ]]; then + export DO_AERO_FCST="YES" + fi +fi + # Surface cycle update frequency if [[ "${RUN}" =~ "gdas" ]] ; then export FHCYC=1 @@ -278,6 +285,7 @@ export FHMAX=9 export FHOUT=3 # Will be changed to 1 in config.base if (DOHYBVAR set to NO and l4densvar set to false) export FHOUT_OCN=3 export FHOUT_ICE=3 +export FHOUT_AERO=3 # Cycle to run EnKF (set to BOTH for both gfs and gdas) export EUPD_CYC="@EUPD_CYC@" @@ -303,6 +311,10 @@ export FHMAX_HF_WAV=120 export FHOUT_HF_WAV=1 export FHMAX_WAV=${FHMAX:-9} export FHMAX_WAV_GFS=${FHMAX_GFS} +export FHMAX_HF_GFS=$(( FHMAX_HF_GFS > FHMAX_GFS ? FHMAX_GFS : FHMAX_HF_GFS )) +export FHMAX_WAV=$(( FHMAX_WAV > FHMAX ? FHMAX : FHMAX_WAV )) +export FHMAX_WAV_GFS=$(( FHMAX_WAV_GFS > FHMAX_GFS ? FHMAX_GFS : FHMAX_WAV_GFS )) +export FHMAX_HF_WAV=$(( FHMAX_HF_WAV > FHMAX_WAV_GFS ? FHMAX_WAV_GFS : FHMAX_HF_WAV )) # TODO: Change gempak to use standard out variables (#2348) export ILPOST=${FHOUT_HF_GFS} # gempak output frequency up to F120 @@ -461,8 +473,8 @@ export FHMAX_FITS=132 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 + echo "FATAL ERROR: Both HPSS and local archiving selected. Please choose one or the other." + exit 4 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 +495,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.efcs b/parm/config/gfs/config.efcs index 1837cf0619..d27fd13cfa 100644 --- a/parm/config/gfs/config.efcs +++ b/parm/config/gfs/config.efcs @@ -13,7 +13,7 @@ string="--fv3 ${CASE}" [[ "${DO_OCN}" == "YES" ]] && string="${string} --mom6 ${OCNRES}" [[ "${DO_ICE}" == "YES" ]] && string="${string} --cice6 ${ICERES}" [[ "${DO_WAVE}" == "YES" ]] && string="${string} --ww3 ${waveGRD// /;}" -[[ "${DO_AERO}" == "YES" ]] && string="${string} --gocart" +[[ "${DO_AERO_FCST}" == "YES" ]] && string="${string} --gocart" # We are counting on $string being multiple arguments # shellcheck disable=SC2086 source "${EXPDIR}/config.ufs" ${string} 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/parm/config/gfs/config.fcst b/parm/config/gfs/config.fcst index 571e6cafb5..b154d37114 100644 --- a/parm/config/gfs/config.fcst +++ b/parm/config/gfs/config.fcst @@ -8,24 +8,12 @@ echo "BEGIN: config.fcst" export USE_ESMF_THREADING="YES" # Toggle to use ESMF-managed threading or traditional threading in UFSWM export COPY_FINAL_RESTARTS="NO" # Toggle to copy restarts from the end of GFS/GEFS Run (GDAS is handled seperately) -# Turn off waves if not used for this RUN -case ${WAVE_RUN} in - both | "${RUN/enkf}" ) ;; # Don't change - *) DO_WAVE="NO" ;; # Turn waves off -esac - -# Turn off aerosols if not used for this RUN -case ${AERO_FCST_RUN} in - both | "${RUN/enkf}" ) ;; # Don't change - *) DO_AERO="NO" ;; # Turn aerosols off -esac - # Source model specific information that is resolution dependent string="--fv3 ${CASE}" [[ "${DO_OCN}" == "YES" ]] && string="${string} --mom6 ${OCNRES}" [[ "${DO_ICE}" == "YES" ]] && string="${string} --cice6 ${ICERES}" [[ "${DO_WAVE}" == "YES" ]] && string="${string} --ww3 ${waveGRD// /;}" -[[ "${DO_AERO}" == "YES" ]] && string="${string} --gocart" +[[ "${DO_AERO_FCST}" == "YES" ]] && string="${string} --gocart" # We are counting on $string being multiple arguments # shellcheck disable=SC2086 source "${EXPDIR}/config.ufs" ${string} @@ -157,7 +145,7 @@ tbp="" if [[ "${progsigma}" == ".true." ]]; then tbp="_progsigma" ; fi # Radiation options -if [[ "${DO_AERO}" == "YES" ]]; then +if [[ "${DO_AERO_FCST}" == "YES" ]]; then export IAER=2011 # spectral band mapping method for aerosol optical properties else export IAER=1011 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.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.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/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 4a0c105243..9122d1db68 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" @@ -154,13 +154,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 @@ -818,7 +811,7 @@ case ${step} in echo "MEDIATOR using (threads, PETS) = (${MEDTHREADS}, ${MEDPETS})" CHMPETS=0; CHMTHREADS=0 - if [[ "${DO_AERO}" == "YES" ]]; then + if [[ "${DO_AERO_FCST}" == "YES" ]]; then # GOCART shares the same grid and forecast tasks as FV3 (do not add write grid component tasks). (( CHMTHREADS = ATMTHREADS )) (( CHMPETS = FV3PETS )) @@ -1045,7 +1038,7 @@ case ${step} in ntasks=1 tasks_per_node=1 threads_per_task=1 - export is_exclusive=True + memory="4096M" ;; "atmensanlinit") 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/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/parm/config/gfs/config.stage_ic b/parm/config/gfs/config.stage_ic index 7aa0c25f32..f30e402c93 100644 --- a/parm/config/gfs/config.stage_ic +++ b/parm/config/gfs/config.stage_ic @@ -12,11 +12,11 @@ export BASE_IC="@BASE_IC@" # Platform home for staged ICs export STAGE_IC_YAML_TMPL="${PARMgfs}/stage/master_gfs.yaml.j2" +source "${HOMEgfs}/versions/ic.ver" + # Set ICSDIR (if not defined) if [[ -z "${ICSDIR}" ]] ; then - ic_ver="20240610" - if (( NMEM_ENS > 0 )) ; then ensic="${CASE_ENS}" fi @@ -25,8 +25,15 @@ if [[ -z "${ICSDIR}" ]] ; then ocnic="mx${OCNRES}" fi - export ICSDIR="${BASE_IC}/${CASE}${ensic:-}${ocnic:-}/${ic_ver}" + dir_name="${CASE}${ensic:-}${ocnic:-}" + ic_ver="${ic_versions[${dir_name}]}" + + export ICSDIR="${BASE_IC}/${dir_name}/${ic_ver}" 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/config/gfs/yaml/defaults.yaml b/parm/config/gfs/yaml/defaults.yaml index dfc67d1237..05dfc90332 100644 --- a/parm/config/gfs/yaml/defaults.yaml +++ b/parm/config/gfs/yaml/defaults.yaml @@ -21,9 +21,10 @@ base: GSI_SOILANAL: "NO" EUPD_CYC: "gdas" FHMAX_ENKF_GFS: 12 + DO_TEST_MODE: "NO" 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 +34,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 @@ -61,4 +57,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/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/parm/post/oceanice_products_gefs.yaml b/parm/post/oceanice_products_gefs.yaml index fea88df2bb..f961fab83f 100644 --- a/parm/post/oceanice_products_gefs.yaml +++ b/parm/post/oceanice_products_gefs.yaml @@ -33,7 +33,7 @@ ocean: {% elif model_grid == 'mx500' %} ocean_levels: [5, 15, 25, 35, 45, 55, 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175, 185, 195, 205, 215, 226, 241, 267] {% endif %} - subset: ['SSH', 'SST', 'SSS', 'speed', 'MLD_003', 'latent', 'sensible', 'SW', 'LW', 'LwLatSens', 'Heat_PmE', 'SSU', 'SSV', 'taux', 'tauy', 'temp', 'so', 'uo', 'vo'] + subset: ['SSH', 'SST', 'SSS', 'speed', 'MLD_003', 'latent', 'sensible', 'SW', 'LW', 'LwLatSens', 'Heat_PmE', 'SSU', 'SSV', 'taux', 'tauy', 'temp', 'tob', 'so', 'uo', 'vo'] data_in: copy: - ["{{ COM_OCEAN_HISTORY }}/{{ RUN }}.ocean.t{{ current_cycle | strftime('%H') }}z.{{ interval }}hr_avg.f{{ '%03d' % forecast_hour }}.nc", "{{ DATA }}/ocean.nc"] 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 diff --git a/parm/product/gefs.0p25.fFFF.paramlist.a.txt b/parm/product/gefs.0p25.fFFF.paramlist.a.txt index 303752ac17..4bb87c32ff 100644 --- a/parm/product/gefs.0p25.fFFF.paramlist.a.txt +++ b/parm/product/gefs.0p25.fFFF.paramlist.a.txt @@ -19,6 +19,7 @@ :CIN:180-0 mb above ground: :CIN:surface: :HLCY:3000-0 m above ground: +:PEVPR:surface: :TCDC:entire atmosphere (considered as a single layer): :WEASD:surface: :SNOD:surface: diff --git a/parm/product/gefs.0p25.fFFF.paramlist.b.txt b/parm/product/gefs.0p25.fFFF.paramlist.b.txt index ccad9da4d0..5c406ce34d 100644 --- a/parm/product/gefs.0p25.fFFF.paramlist.b.txt +++ b/parm/product/gefs.0p25.fFFF.paramlist.b.txt @@ -151,7 +151,6 @@ :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: 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/parm/stage/master_gfs.yaml.j2 b/parm/stage/master_gfs.yaml.j2 index 5204221c9b..e862005094 100644 --- a/parm/stage/master_gfs.yaml.j2 +++ b/parm/stage/master_gfs.yaml.j2 @@ -94,11 +94,6 @@ '${YMD}':previous_cycle_YMD, '${HH}':previous_cycle_HH, '${MEMDIR}': mem_char }) %} - {% set previous_cycle_and_run_dict = ({ '${ROTDIR}':ROTDIR, - '${RUN}':RUN, - '${YMD}':previous_cycle_YMD, - '${HH}':previous_cycle_HH, - '${MEMDIR}': mem_char }) %} {% set COMOUT_ATMOS_INPUT_MEM = COM_ATMOS_INPUT_TMPL | replace_tmpl(current_cycle_dict) %} {% set COMOUT_ATMOS_RESTART_PREV_MEM = COM_ATMOS_RESTART_TMPL | replace_tmpl(previous_cycle_dict) %} @@ -108,7 +103,7 @@ {% set COMOUT_OCEAN_RESTART_PREV_MEM = COM_OCEAN_RESTART_TMPL | replace_tmpl(previous_cycle_dict) %} {% set COMOUT_OCEAN_ANALYSIS_MEM = COM_OCEAN_ANALYSIS_TMPL | replace_tmpl(current_cycle_dict) %} {% set COMOUT_MED_RESTART_PREV_MEM = COM_MED_RESTART_TMPL | replace_tmpl(previous_cycle_dict) %} - {% set COMOUT_WAVE_RESTART_PREV_MEM = COM_WAVE_RESTART_TMPL | replace_tmpl(previous_cycle_and_run_dict) %} + {% set COMOUT_WAVE_RESTART_PREV_MEM = COM_WAVE_RESTART_TMPL | replace_tmpl(previous_cycle_dict) %} # Append the member COM directories {% do COMOUT_ATMOS_INPUT_MEM_list.append(COMOUT_ATMOS_INPUT_MEM)%} diff --git a/parm/ufs/gocart/AERO_HISTORY.rc b/parm/ufs/gocart/AERO_HISTORY.rc index 4c7df15b2a..db1b934f5f 100644 --- a/parm/ufs/gocart/AERO_HISTORY.rc +++ b/parm/ufs/gocart/AERO_HISTORY.rc @@ -58,7 +58,7 @@ PC720x361-DC.LM: 72 inst_du_ss.mode: 'instantaneous', inst_du_ss.grid_label: PC720x361-DC , inst_du_ss.splitField: 1, - inst_du_ss.frequency: 120000 , + inst_du_ss.frequency: @[inst_du_ss_freq] , inst_du_ss.duration: 010000 , inst_du_ss.ref_time: 000000 , inst_du_ss.nbits: 10, @@ -72,7 +72,7 @@ PC720x361-DC.LM: 72 tavg_du_ss.mode: 'time-averaged', tavg_du_ss.grid_label: PC720x361-DC , tavg_du_ss.splitField: 1, - tavg_du_ss.frequency: 120000 , + tavg_du_ss.frequency: @[tavg_du_ss_freq] , tavg_du_ss.duration: 010000 , tavg_du_ss.ref_time: 000000 , tavg_du_ss.nbits: 10, @@ -85,7 +85,7 @@ PC720x361-DC.LM: 72 inst_ca.template: '%y4%m2%d2_%h2%n2z.nc4', inst_ca.mode: 'instantaneous', inst_ca.grid_label: PC720x361-DC , - inst_ca.frequency: 120000 , + inst_ca.frequency: @[inst_ca_freq] , inst_ca.duration: 010000 , inst_ca.ref_time: 000000 , inst_ca.nbits: 10, @@ -100,7 +100,7 @@ PC720x361-DC.LM: 72 inst_ni.template: '%y4%m2%d2_%h2%n2z.nc4', inst_ni.mode: 'instantaneous', inst_ni.grid_label: PC720x361-DC , - inst_ni.frequency: 120000 , + inst_ni.frequency: @[inst_ni_freq] , inst_ni.duration: 010000 , inst_ni.ref_time: 000000 , inst_ni.nbits: 10, @@ -116,7 +116,7 @@ PC720x361-DC.LM: 72 inst_su.template: '%y4%m2%d2_%h2%n2z.nc4', inst_su.mode: 'instantaneous', inst_su.grid_label: PC720x361-DC , - inst_su.frequency: 120000 , + inst_su.frequency: @[inst_su_freq] , inst_su.duration: 010000 , inst_su.ref_time: 000000 , inst_su.nbits: 10, @@ -135,7 +135,7 @@ PC720x361-DC.LM: 72 inst_du_bin.mode: 'instantaneous' inst_du_bin.grid_label: PC720x361-DC , inst_du_bin.splitField: 1, - inst_du_bin.frequency: 010000 , + inst_du_bin.frequency: @[inst_du_bin_freq] , inst_du_bin.duration: 010000 , inst_du_bin.ref_time: 000000 , inst_du_bin.nbits: 10, @@ -152,7 +152,7 @@ PC720x361-DC.LM: 72 tavg_du_bin.mode: 'time-averaged' tavg_du_bin.grid_label: PC720x361-DC , tavg_du_bin.splitField: 1, - tavg_du_bin.frequency: 030000 , + tavg_du_bin.frequency: @[tavg_du_bin_freq] , tavg_du_bin.duration: 010000 , tavg_du_bin.ref_time: 000000 , tavg_du_bin.nbits: 10, @@ -169,7 +169,7 @@ PC720x361-DC.LM: 72 inst_ss_bin.mode: 'instantaneous' inst_ss_bin.grid_label: PC720x361-DC , inst_ss_bin.splitField: 1, - inst_ss_bin.frequency: 060000 , + inst_ss_bin.frequency: @[inst_ss_bin_freq] , inst_ss_bin.duration: 010000 , inst_ss_bin.ref_time: 000000 , inst_ss_bin.nbits: 10, @@ -186,7 +186,7 @@ PC720x361-DC.LM: 72 inst_ca_bin.mode: 'instantaneous' inst_ca_bin.grid_label: PC720x361-DC , inst_ca_bin.splitField: 1, - inst_ca_bin.frequency: 120000 , + inst_ca_bin.frequency: @[inst_ca_bin_freq] , inst_ca_bin.duration: 010000 , inst_ca_bin.ref_time: 000000 , inst_ca_bin.nbits: 10, @@ -208,7 +208,7 @@ PC720x361-DC.LM: 72 inst_ni_bin.mode: 'instantaneous', inst_ni_bin.grid_label: PC720x361-DC , inst_ni_bin.splitField: 1, - inst_ni_bin.frequency: 120000 , + inst_ni_bin.frequency: @[inst_ni_bin_freq] , inst_ni_bin.duration: 010000 , inst_ni_bin.ref_time: 000000 , inst_ni_bin.nbits: 10, @@ -225,7 +225,7 @@ PC720x361-DC.LM: 72 inst_su_bin.mode: 'instantaneous', inst_su_bin.grid_label: PC720x361-DC , inst_su_bin.splitField: 1, - inst_su_bin.frequency: 120000 , + inst_su_bin.frequency: @[inst_su_bin_freq] , inst_su_bin.duration: 010000 , inst_su_bin.ref_time: 000000 , inst_su_bin.nbits: 10, @@ -244,7 +244,7 @@ PC720x361-DC.LM: 72 inst_2d.template: '%y4%m2%d2_%h2%n2z.nc4', inst_2d.archive: '%c/Y%y4', inst_2d.mode: 'instantaneous' - inst_2d.frequency: 030000, + inst_2d.frequency: @[inst_2d_freq], inst_2d.duration: 030000, inst_2d.ref_time: 000000, inst_2d.grid_label: PC720x361-DC @@ -343,7 +343,7 @@ PC720x361-DC.LM: 72 inst_3d.template: '%y4%m2%d2_%h2%n2z.nc4' , inst_3d.archive: '%c/Y%y4' , inst_3d.mode: 'instantaneous' - inst_3d.frequency: 060000, + inst_3d.frequency: @[inst_3d_freq], inst_3d.duration: 010000, inst_3d.ref_time: 000000, inst_3d.grid_label: PC720x361-DC @@ -381,7 +381,7 @@ PC720x361-DC.LM: 72 inst_aod.template: '%y4%m2%d2_%h2%n2z.nc4' , inst_aod.archive: '%c/Y%y4' , inst_aod.mode: 'instantaneous' - inst_aod.frequency: 060000, + inst_aod.frequency: @[inst_aod_freq], inst_aod.duration: 010000, inst_aod.ref_time: 000000, inst_aod.grid_label: PC720x361-DC @@ -398,7 +398,7 @@ PC720x361-DC.LM: 72 tavg_2d_rad.template: '%y4%m2%d2_%h2%n2z.nc4', tavg_2d_rad.archive: '%c/Y%y4', tavg_2d_rad.mode: 'time-averaged', - tavg_2d_rad.frequency: 120000, + tavg_2d_rad.frequency: @[tavg_2d_rad_freq], tavg_2d_rad.duration: 120000, tavg_2d_rad.ref_time: 000000, tavg_2d_rad.grid_label: PC720x361-DC @@ -432,7 +432,7 @@ PC720x361-DC.LM: 72 tavg_3d_rad.template: '%y4%m2%d2_%h2%n2z.nc4', tavg_3d_rad.archive: '%c/Y%y4', tavg_3d_rad.mode: 'time-averaged', - tavg_3d_rad.frequency: 120000, + tavg_3d_rad.frequency: @[tavg_3d_rad_freq], tavg_3d_rad.duration: 120000, tavg_3d_rad.ref_time: 000000, tavg_3d_rad.grid_label: PC720x361-DC 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 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 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}, diff --git a/scripts/exgfs_wave_post_gridded_sbs.sh b/scripts/exgfs_wave_post_gridded_sbs.sh index b0cca34bd1..a241a00d88 100755 --- a/scripts/exgfs_wave_post_gridded_sbs.sh +++ b/scripts/exgfs_wave_post_gridded_sbs.sh @@ -217,219 +217,189 @@ source "${USHgfs}/preamble.sh" set +x echo ' Making command file for sbs grib2 and GRID Interpolation ' set_trace - -# 1.a.2 Loop over forecast time to generate post files -# When executed side-by-side, serial mode (cfp when run after the fcst step) -# Contingency for RERUN=YES - if [ "${RERUN-NO}" = "YES" ]; then - fhr=$((FHRUN + FHMIN_WAV)) - if [ $FHMAX_HF_WAV -gt 0 ] && [ $FHOUT_HF_WAV -gt 0 ] && [ $fhr -lt $FHMAX_HF_WAV ]; then - FHINCG=$FHOUT_HF_WAV - else - FHINCG=$FHOUT_WAV - fi - fhr=$((fhr + FHINCG)) - else - fhr=$FHMIN_WAV - fi + fhr=$(( 10#${FHR3} )) fhrg=$fhr - sleep_interval=10 - iwaitmax=120 # Maximum loop cycles for waiting until wave component output file is ready (fails after max) - while [ $fhr -le $FHMAX_WAV ]; do + ymdh=$($NDATE $fhr ${PDY}${cyc}) + YMD=$(echo $ymdh | cut -c1-8) + HMS="$(echo $ymdh | cut -c9-10)0000" + YMDHMS=${YMD}${HMS} + FH3=$(printf %03i $fhr) - ymdh=$($NDATE $fhr ${PDY}${cyc}) - YMD=$(echo $ymdh | cut -c1-8) - HMS="$(echo $ymdh | cut -c9-10)0000" - YMDHMS=${YMD}${HMS} - FH3=$(printf %03i $fhr) - - fcmdnow=cmdfile.${FH3} - fcmdigrd=icmdfile.${FH3} - mkdir output_$YMDHMS - cd output_$YMDHMS - rm -f ${fcmdnow} ${fcmdigrd} - touch ${fcmdnow} ${fcmdigrd} + fcmdnow=cmdfile.${FH3} + fcmdigrd=icmdfile.${FH3} + mkdir output_$YMDHMS + cd output_$YMDHMS + rm -f ${fcmdnow} ${fcmdigrd} + touch ${fcmdnow} ${fcmdigrd} # Create instances of directories for gridded output - export GRIBDATA=${DATA}/output_$YMDHMS - export GRDIDATA=${DATA}/output_$YMDHMS + export GRIBDATA=${DATA}/output_$YMDHMS + export GRDIDATA=${DATA}/output_$YMDHMS # Gridded data (main part, need to be run side-by-side with forecast - - if [ $fhr = $fhrg ] - then - for wavGRD in ${waveGRD}; do - gfile="${COMIN_WAVE_HISTORY}/${WAV_MOD_TAG}.out_grd.${wavGRD}.${YMD}.${HMS}" - if ! wait_for_file "${gfile}" "${sleep_interval}" "${iwaitmax}"; then - echo " FATAL ERROR : NO RAW FIELD OUTPUT FILE out_grd.${grdID}" - echo "${WAV_MOD_TAG} post ${grdID} ${PDY} ${cycle} : field output missing." + + if [ $fhr = $fhrg ] + then + for wavGRD in ${waveGRD}; do + gfile="${COMIN_WAVE_HISTORY}/${WAV_MOD_TAG}.out_grd.${wavGRD}.${YMD}.${HMS}" + if [[ ! -s "${gfile}" ]]; then + echo " FATAL ERROR : NO RAW FIELD OUTPUT FILE ${gfile}" err=3; export err; "${errchk}" exit "${err}" fi - ${NLN} "${gfile}" "./out_grd.${wavGRD}" - done - - if [ "$DOGRI_WAV" = 'YES' ] - then - nigrd=1 - for grdID in $waveinterpGRD - do - ymdh_int=$($NDATE -${WAVHINDH} $ymdh); dt_int=3600.; n_int=9999 ; - echo "${USHgfs}/wave_grid_interp_sbs.sh $grdID $ymdh_int $dt_int $n_int > grint_$grdID.out 2>&1" >> ${fcmdigrd}.${nigrd} - if [ "$DOGRB_WAV" = 'YES' ] - then - gribFL=\'$(echo ${OUTPARS_WAV})\' - case $grdID in - glo_15mxt) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - reg025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - glo_025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - glo_100) GRDNAME='global' ; GRDRES=1p00 ; GRIDNR=255 ; MODNR=11 ;; - glo_200) GRDNAME='global' ; GRDRES=2p00 ; GRIDNR=255 ; MODNR=11 ;; - glo_500) GRDNAME='global' ; GRDRES=5p00 ; GRIDNR=255 ; MODNR=11 ;; - glo_30mxt) GRDNAME='global' ; GRDRES=0p50 ; GRIDNR=255 ; MODNR=11 ;; - glo_30m) GRDNAME='global' ; GRDRES=0p50 ; GRIDNR=255 ; MODNR=11 ;; - at_10m) GRDNAME='atlocn' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; - ep_10m) GRDNAME='epacif' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; - wc_10m) GRDNAME='wcoast' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; - ak_10m) GRDNAME='alaska' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; - esac - echo "${USHgfs}/wave_grib2_sbs.sh $grdID $GRIDNR $MODNR $ymdh $fhr $GRDNAME $GRDRES $gribFL > grib_$grdID.out 2>&1" >> ${fcmdigrd}.${nigrd} - fi - echo "${GRIBDATA}/${fcmdigrd}.${nigrd}" >> ${fcmdnow} - chmod 744 ${fcmdigrd}.${nigrd} - nigrd=$((nigrd+1)) - done - fi + ${NLN} "${gfile}" "./out_grd.${wavGRD}" + done - if [ "$DOGRB_WAV" = 'YES' ] - then - for grdID in ${wavepostGRD} # First concatenate grib files for sbs grids - do + if [ "$DOGRI_WAV" = 'YES' ] + then + nigrd=1 + for grdID in $waveinterpGRD + do + ymdh_int=$($NDATE -${WAVHINDH} $ymdh); dt_int=3600.; n_int=9999 ; + echo "${USHgfs}/wave_grid_interp_sbs.sh $grdID $ymdh_int $dt_int $n_int > grint_$grdID.out 2>&1" >> ${fcmdigrd}.${nigrd} + if [ "$DOGRB_WAV" = 'YES' ] + then gribFL=\'$(echo ${OUTPARS_WAV})\' case $grdID in - aoc_9km) GRDNAME='arctic' ; GRDRES=9km ; GRIDNR=255 ; MODNR=11 ;; - ant_9km) GRDNAME='antarc' ; GRDRES=9km ; GRIDNR=255 ; MODNR=11 ;; - glo_10m) GRDNAME='global' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; - gnh_10m) GRDNAME='global' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; - gsh_15m) GRDNAME='gsouth' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - glo_15m) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - ao_20m) GRDNAME='arctic' ; GRDRES=0p33 ; GRIDNR=255 ; MODNR=11 ;; - so_20m) GRDNAME='antarc' ; GRDRES=0p33 ; GRIDNR=255 ; MODNR=11 ;; - glo_15mxt) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - reg025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - glo_025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; - glo_100) GRDNAME='global' ; GRDRES=1p00 ; GRIDNR=255 ; MODNR=11 ;; - glo_200) GRDNAME='global' ; GRDRES=2p00 ; GRIDNR=255 ; MODNR=11 ;; - glo_500) GRDNAME='global' ; GRDRES=5p00 ; GRIDNR=255 ; MODNR=11 ;; - gwes_30m) GRDNAME='global' ; GRDRES=0p50 ; GRIDNR=255 ; MODNR=10 ;; + glo_15mxt) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + reg025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + glo_025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + glo_100) GRDNAME='global' ; GRDRES=1p00 ; GRIDNR=255 ; MODNR=11 ;; + glo_200) GRDNAME='global' ; GRDRES=2p00 ; GRIDNR=255 ; MODNR=11 ;; + glo_500) GRDNAME='global' ; GRDRES=5p00 ; GRIDNR=255 ; MODNR=11 ;; + glo_30mxt) GRDNAME='global' ; GRDRES=0p50 ; GRIDNR=255 ; MODNR=11 ;; + glo_30m) GRDNAME='global' ; GRDRES=0p50 ; GRIDNR=255 ; MODNR=11 ;; + at_10m) GRDNAME='atlocn' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; + ep_10m) GRDNAME='epacif' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; + wc_10m) GRDNAME='wcoast' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; + ak_10m) GRDNAME='alaska' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; esac - echo "${USHgfs}/wave_grib2_sbs.sh $grdID $GRIDNR $MODNR $ymdh $fhr $GRDNAME $GRDRES $gribFL > grib_$grdID.out 2>&1" >> ${fcmdnow} - done - fi - + echo "${USHgfs}/wave_grib2_sbs.sh $grdID $GRIDNR $MODNR $ymdh $fhr $GRDNAME $GRDRES $gribFL > grib_$grdID.out 2>&1" >> ${fcmdigrd}.${nigrd} + fi + echo "${GRIBDATA}/${fcmdigrd}.${nigrd}" >> ${fcmdnow} + chmod 744 ${fcmdigrd}.${nigrd} + nigrd=$((nigrd+1)) + done fi - if [ ${CFP_MP:-"NO"} = "YES" ]; then - nfile=0 - ifile=0 - iline=1 - ifirst='yes' - nlines=$( wc -l ${fcmdnow} | awk '{print $1}' ) - while [ $iline -le $nlines ]; do - line=$( sed -n ''$iline'p' ${fcmdnow} ) - if [ -z "$line" ]; then - break - else - if [ "$ifirst" = 'yes' ]; then - echo "#!/bin/sh" > cmdmfile.$nfile - echo "$nfile cmdmfile.$nfile" >> cmdmprog - chmod 744 cmdmfile.$nfile - fi - echo $line >> cmdmfile.$nfile - nfile=$(( nfile + 1 )) - if [ $nfile -eq $NTASKS ]; then - nfile=0 - ifirst='no' - fi - iline=$(( iline + 1 )) - fi + if [ "$DOGRB_WAV" = 'YES' ] + then + for grdID in ${wavepostGRD} # First concatenate grib files for sbs grids + do + gribFL=\'$(echo ${OUTPARS_WAV})\' + case $grdID in + aoc_9km) GRDNAME='arctic' ; GRDRES=9km ; GRIDNR=255 ; MODNR=11 ;; + ant_9km) GRDNAME='antarc' ; GRDRES=9km ; GRIDNR=255 ; MODNR=11 ;; + glo_10m) GRDNAME='global' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; + gnh_10m) GRDNAME='global' ; GRDRES=0p16 ; GRIDNR=255 ; MODNR=11 ;; + gsh_15m) GRDNAME='gsouth' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + glo_15m) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + ao_20m) GRDNAME='arctic' ; GRDRES=0p33 ; GRIDNR=255 ; MODNR=11 ;; + so_20m) GRDNAME='antarc' ; GRDRES=0p33 ; GRIDNR=255 ; MODNR=11 ;; + glo_15mxt) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + reg025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + glo_025) GRDNAME='global' ; GRDRES=0p25 ; GRIDNR=255 ; MODNR=11 ;; + glo_100) GRDNAME='global' ; GRDRES=1p00 ; GRIDNR=255 ; MODNR=11 ;; + glo_200) GRDNAME='global' ; GRDRES=2p00 ; GRIDNR=255 ; MODNR=11 ;; + glo_500) GRDNAME='global' ; GRDRES=5p00 ; GRIDNR=255 ; MODNR=11 ;; + gwes_30m) GRDNAME='global' ; GRDRES=0p50 ; GRIDNR=255 ; MODNR=10 ;; + esac + echo "${USHgfs}/wave_grib2_sbs.sh $grdID $GRIDNR $MODNR $ymdh $fhr $GRDNAME $GRDRES $gribFL > grib_$grdID.out 2>&1" >> ${fcmdnow} done fi - wavenproc=$(wc -l ${fcmdnow} | awk '{print $1}') - wavenproc=$(echo $((${wavenproc}<${NTASKS}?${wavenproc}:${NTASKS}))) + fi + + if [ ${CFP_MP:-"NO"} = "YES" ]; then + nfile=0 + ifile=0 + iline=1 + ifirst='yes' + nlines=$( wc -l ${fcmdnow} | awk '{print $1}' ) + while [ $iline -le $nlines ]; do + line=$( sed -n ''$iline'p' ${fcmdnow} ) + if [ -z "$line" ]; then + break + else + if [ "$ifirst" = 'yes' ]; then + echo "#!/bin/sh" > cmdmfile.$nfile + echo "$nfile cmdmfile.$nfile" >> cmdmprog + chmod 744 "cmdmfile.$nfile" + fi + echo $line >> "cmdmfile.$nfile" + nfile=$(( nfile + 1 )) + if [ "$nfile" -eq "$NTASKS" ]; then + nfile=0 + ifirst='no' + fi + iline=$(( iline + 1 )) + fi + done + fi + + wavenproc=$(wc -l ${fcmdnow} | awk '{print $1}') + wavenproc=$(echo $((${wavenproc}<${NTASKS}?${wavenproc}:${NTASKS}))) + + set +x + echo ' ' + echo " Executing the grib2_sbs scripts at : $(date)" + echo ' ------------------------------------' + echo ' ' + set_trace + + if [ "$wavenproc" -gt '1' ] + then + if [ ${CFP_MP:-"NO"} = "YES" ]; then + ${wavempexec} -n ${wavenproc} ${wave_mpmd} cmdmprog + else + ${wavempexec} ${wavenproc} ${wave_mpmd} ${fcmdnow} + fi + exit=$? + else + chmod 744 ${fcmdnow} + ./${fcmdnow} + exit=$? + fi + if [ "$exit" != '0' ] + then set +x echo ' ' - echo " Executing the grib2_sbs scripts at : $(date)" - echo ' ------------------------------------' + echo '*************************************' + echo '*** FATAL ERROR: CMDFILE FAILED ***' + echo '*************************************' + echo ' See Details Below ' echo ' ' set_trace + err=4; export err;${errchk} + exit "$err" + fi - if [ "$wavenproc" -gt '1' ] - then - if [ ${CFP_MP:-"NO"} = "YES" ]; then - ${wavempexec} -n ${wavenproc} ${wave_mpmd} cmdmprog - else - ${wavempexec} ${wavenproc} ${wave_mpmd} ${fcmdnow} - fi - exit=$? - else - chmod 744 ${fcmdnow} - ./${fcmdnow} - exit=$? - fi + rm -f out_grd.* # Remove large binary grid output files - if [ "$exit" != '0' ] - then + cd $DATA + + + if [ "$fhr" = "$fhrg" ] + then +# Check if grib2 file created + ENSTAG="" + if [ ${waveMEMB} ]; then ENSTAG=".${membTAG}${waveMEMB}" ; fi + gribchk="${RUN}wave.${cycle}${ENSTAG}.${GRDNAME}.${GRDRES}.f${FH3}.grib2" + if [ ! -s ${COMOUT_WAVE_GRID}/${gribchk} ]; then set +x echo ' ' - echo '*************************************' - echo '*** FATAL ERROR: CMDFILE FAILED ***' - echo '*************************************' + echo '********************************************' + echo "*** FATAL ERROR: $gribchk not generated " + echo '********************************************' echo ' See Details Below ' echo ' ' set_trace - err=4; export err;${errchk} - exit $err - fi - - rm -f out_grd.* # Remove large binary grid output files - - cd $DATA - - FHINCG=$(( DTFLD_WAV / 3600 )) - if [ $fhr = $fhrg ] - then -# Check if grib2 file created - ENSTAG="" - if [ ${waveMEMB} ]; then ENSTAG=".${membTAG}${waveMEMB}" ; fi - gribchk="${RUN}wave.${cycle}${ENSTAG}.${GRDNAME}.${GRDRES}.f${FH3}.grib2" - if [ ! -s ${COMOUT_WAVE_GRID}/${gribchk} ]; then - set +x - echo ' ' - echo '********************************************' - echo "*** FATAL ERROR: $gribchk not generated " - echo '********************************************' - echo ' See Details Below ' - echo ' ' - set_trace - err=5; export err;${errchk} - exit $err - fi - if [ $FHMAX_HF_WAV -gt 0 ] && [ $FHOUT_HF_WAV -gt 0 ] && [ $fhr -lt $FHMAX_HF_WAV ]; then - FHINCG=$FHOUT_HF_WAV - else - FHINCG=$FHOUT_WAV - fi - fhrg=$((fhr+FHINCG)) + err=5; export err;${errchk} + exit "$err" fi - echo $fhrg - - fhr=$fhrg #loop with out_grd stride - - done + fi # --------------------------------------------------------------------------- # # 7. Ending output diff --git a/scripts/exglobal_archive.py b/scripts/exglobal_archive.py index 4ee9e5ed0e..2d3fa58313 100755 --- a/scripts/exglobal_archive.py +++ b/scripts/exglobal_archive.py @@ -3,7 +3,7 @@ import os from pygfs.task.archive import Archive -from wxflow import AttrDict, Logger, cast_strdict_as_dtypedict, chdir, logit +from wxflow import AttrDict, Logger, cast_strdict_as_dtypedict, logit # initialize root logger logger = Logger(level=os.environ.get("LOGGING_LEVEL", "DEBUG"), colored_log=True) @@ -19,7 +19,7 @@ def main(): # Pull out all the configuration keys needed to run the rest of archive steps keys = ['ATARDIR', 'current_cycle', 'FHMIN', 'FHMAX', 'FHOUT', 'RUN', 'PDY', - 'DO_VERFRAD', 'DO_VMINMON', 'DO_VERFOZN', 'DO_ICE', 'DO_AERO', 'DO_PREP_OBS_AERO', + 'DO_VERFRAD', 'DO_VMINMON', 'DO_VERFOZN', 'DO_ICE', 'DO_PREP_OBS_AERO', 'PARMgfs', 'DO_OCN', 'DO_WAVE', 'WRITE_DOPOST', 'PSLOT', 'HPSSARCH', 'DO_MOS', 'DO_JEDISNOWDA', 'LOCALARCH', 'REALTIME', 'ROTDIR', 'ARCH_WARMICFREQ', 'ARCH_FCSTICFREQ', 'ARCH_CYC', 'assim_freq', 'ARCDIR', 'SDATE', @@ -29,7 +29,7 @@ def main(): 'DOIAU', 'OCNRES', 'ICERES', 'NUM_SND_COLLECTIVES', 'FHOUT_WAV', 'FHOUT_HF_WAV', 'FHMAX_WAV', 'FHMAX_HF_WAV', 'FHMAX_WAV_GFS', 'restart_interval_gdas', 'restart_interval_gfs', - 'AERO_ANL_RUN', 'AERO_FCST_RUN', 'DOIBP_WAV', 'DO_JEDIOCNVAR', + 'DO_AERO_ANL', 'DO_AERO_FCST', 'DOIBP_WAV', 'DO_JEDIOCNVAR', 'NMEM_ENS', 'DO_JEDIATMVAR', 'DO_VRFY_OCEANDA', 'FHMAX_FITS', 'waveGRD', 'IAUFHRS', 'DO_FIT2OBS', 'NET', 'FHOUT_HF_GFS', 'FHMAX_HF_GFS', 'REPLAY_ICS', 'OFFSET_START_HOUR'] 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/scripts/exglobal_marine_analysis_checkpoint.py b/scripts/exglobal_marine_analysis_checkpoint.py index 84b180b287..c47a2e3a0e 100755 --- a/scripts/exglobal_marine_analysis_checkpoint.py +++ b/scripts/exglobal_marine_analysis_checkpoint.py @@ -24,6 +24,5 @@ # Prepare the SOCA increment for MOM6 IAU MarineAnl.checkpoint_mom6_iau('socaincr2mom6.yaml') - # Insert the seaice analysis into the CICE6 restarts in 2 sequential stages - MarineAnl.checkpoint_cice6('soca_2cice_arctic.yaml') - MarineAnl.checkpoint_cice6('soca_2cice_antarctic.yaml') + # Insert the seaice analysis into the CICE6 restart + MarineAnl.checkpoint_cice6('soca_2cice_global.yaml') 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/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/gdas.cd b/sorc/gdas.cd index 764f58cebd..9ab7994a0c 160000 --- a/sorc/gdas.cd +++ b/sorc/gdas.cd @@ -1 +1 @@ -Subproject commit 764f58cebdf64f3695d89538994a50183e5884d9 +Subproject commit 9ab7994a0caf6b201613dd7e7ceae482ffa600e0 diff --git a/sorc/gfs_utils.fd b/sorc/gfs_utils.fd index a00cc0949e..856a42076a 160000 --- a/sorc/gfs_utils.fd +++ b/sorc/gfs_utils.fd @@ -1 +1 @@ -Subproject commit a00cc0949e2f901e73b58d54834517743916c69a +Subproject commit 856a42076a65256aaae9b29f4891532cb4a3fbca diff --git a/sorc/link_workflow.sh b/sorc/link_workflow.sh index 870ddc5eba..b35b7ff35a 100755 --- a/sorc/link_workflow.sh +++ b/sorc/link_workflow.sh @@ -86,15 +86,6 @@ esac # Source fix version file source "${HOMEgfs}/versions/fix.ver" -# Link python pacakges in ush/python -# TODO: This will be unnecessary when these are part of the virtualenv -packages=("wxflow") -for package in "${packages[@]}"; do - cd "${HOMEgfs}/ush/python" || exit 1 - [[ -s "${package}" ]] && rm -f "${package}" - ${LINK} "${HOMEgfs}/sorc/${package}/src/${package}" . -done - # Link GDASapp python packages in ush/python packages=("jcb") for package in "${packages[@]}"; do @@ -103,15 +94,6 @@ for package in "${packages[@]}"; do ${LINK} "${HOMEgfs}/sorc/gdas.cd/sorc/${package}/src/${package}" . done -# Link wxflow in workflow and ci/scripts -# TODO: This will be unnecessary when wxflow is part of the virtualenv -cd "${HOMEgfs}/workflow" || exit 1 -[[ -s "wxflow" ]] && rm -f wxflow -${LINK} "${HOMEgfs}/sorc/wxflow/src/wxflow" . -cd "${HOMEgfs}/ci/scripts" || exit 1 -[[ -s "wxflow" ]] && rm -f wxflow -${LINK} "${HOMEgfs}/sorc/wxflow/src/wxflow" . - # Link fix directories if [[ -n "${FIX_DIR}" ]]; then if [[ ! -d "${HOMEgfs}/fix" ]]; then mkdir "${HOMEgfs}/fix" || exit 1; fi @@ -166,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_postdet.sh b/ush/forecast_postdet.sh index 25b2e28d75..aade469ddc 100755 --- a/ush/forecast_postdet.sh +++ b/ush/forecast_postdet.sh @@ -328,39 +328,33 @@ WW3_postdet() { local ww3_grid first_ww3_restart_out ww3_restart_file # Copy initial condition files: - if [[ "${warm_start}" == ".true." ]]; then - local restart_date restart_dir + local restart_date restart_dir + if [[ "${RERUN}" == "YES" ]]; then + restart_date="${RERUN_DATE}" + restart_dir="${DATArestart}/WW3_RESTART" + else + restart_date="${model_start_date_current_cycle}" + restart_dir="${COMIN_WAVE_RESTART_PREV}" + fi + + echo "Copying WW3 restarts for 'RUN=${RUN}' at '${restart_date}' from '${restart_dir}'" + ww3_restart_file="${restart_dir}/${restart_date:0:8}.${restart_date:8:2}0000.restart.ww3" + if [[ -s "${ww3_restart_file}" ]]; then + ${NCP} "${ww3_restart_file}" "${DATA}/restart.ww3" \ + || ( echo "FATAL ERROR: Unable to copy WW3 IC, ABORT!"; exit 1 ) + first_ww3_restart_out=$(date --utc -d "${restart_date:0:8} ${restart_date:8:2} + ${restart_interval} hours" +%Y%m%d%H) + else if [[ "${RERUN}" == "YES" ]]; then - restart_date="${RERUN_DATE}" - restart_dir="${DATArestart}/WW3_RESTART" + # In the case of a RERUN, the WW3 restart file is required + echo "FATAL ERROR: WW3 restart file '${ww3_restart_file}' not found for RERUN='${RERUN}', ABORT!" + exit 1 else - restart_date="${model_start_date_current_cycle}" - restart_dir="${COMIN_WAVE_RESTART_PREV}" + echo "WARNING: WW3 restart file '${ww3_restart_file}' not found for warm_start='${warm_start}', will start from rest!" + first_ww3_restart_out=${model_start_date_current_cycle} fi - echo "Copying WW3 restarts for 'RUN=${RUN}' at '${restart_date}' from '${restart_dir}'" - ww3_restart_file="${restart_dir}/${restart_date:0:8}.${restart_date:8:2}0000.restart.ww3" - if [[ -f "${ww3_restart_file}" ]]; then - ${NCP} "${ww3_restart_file}" "${DATA}/restart.ww3" \ - || ( echo "FATAL ERROR: Unable to copy WW3 IC, ABORT!"; exit 1 ) - else - if [[ "${RERUN}" == "YES" ]]; then - # In the case of a RERUN, the WW3 restart file is required - echo "FATAL ERROR: WW3 restart file '${ww3_restart_file}' not found for RERUN='${RERUN}', ABORT!" - exit 1 - else - echo "WARNING: WW3 restart file '${ww3_restart_file}' not found for warm_start='${warm_start}', will start from rest!" - fi - fi - - first_ww3_restart_out=$(date --utc -d "${restart_date:0:8} ${restart_date:8:2} + ${restart_interval} hours" +%Y%m%d%H) - else # cold start - echo "WW3 will start from rest!" - first_ww3_restart_out="${model_start_date_current_cycle}" - fi # [[ "${warm_start}" == ".true." ]] + fi # Link restart files - local ww3_restart_file - # Use restart_date if it was determined above, otherwise use initialization date for (( vdate = first_ww3_restart_out; vdate <= forecast_end_cycle; vdate = $(date --utc -d "${vdate:0:8} ${vdate:8:2} + ${restart_interval} hours" +%Y%m%d%H) )); do ww3_restart_file="${vdate:0:8}.${vdate:8:2}0000.restart.ww3" @@ -689,30 +683,15 @@ GOCART_rc() { [[ ${status} -ne 0 ]] && exit "${status}" fi - # copying GOCART configuration files - if [[ -n "${AERO_CONFIG_DIR}" ]]; then - ${NCP} "${AERO_CONFIG_DIR}"/*.rc "${DATA}" - status=$? - [[ ${status} -ne 0 ]] && exit "${status}" - # attempt to generate ExtData configuration file if not provided - if [[ ! -f "${DATA}/AERO_ExtData.rc" ]]; then - { \ - echo "PrimaryExports%%" ; \ - cat "${AERO_CONFIG_DIR}/ExtData.other" ; \ - cat "${AERO_CONFIG_DIR}/ExtData.${AERO_EMIS_FIRE:-none}" ; \ - echo "%%" ; \ - } > "${DATA}/AERO_ExtData.rc" - status=$? - if (( status != 0 )); then exit "${status}"; fi - fi - fi + source "${USHgfs}/parsing_namelists_GOCART.sh" + GOCART_namelists } GOCART_postdet() { echo "SUB ${FUNCNAME[0]}: Linking output data for GOCART" local vdate - for fhr in ${GOCART_OUTPUT_FH}; do + for fhr in $(GOCART_output_fh); do vdate=$(date --utc -d "${current_cycle:0:8} ${current_cycle:8:2} + ${fhr} hours" +%Y%m%d%H) # Temporarily delete existing files due to noclobber in GOCART @@ -726,6 +705,18 @@ GOCART_postdet() { done } +GOCART_output_fh() { + # This has to be called during postdet after FHROT has been set + local aero_min + local gocart_output_fh + # GOCART produces no AOD files at the initial forecast time, so start the time + # after the forecast start (accounting for FHROT) + aero_min=$(( ${IAU_FHROT:-0} > FHMIN ? IAU_FHROT + FHOUT_AERO : FHMIN + FHOUT_AERO )) + gocart_output_fh=$(seq -s ' ' "$(( aero_min ))" "${FHOUT_AERO}" "${GOCART_MAX}") + + echo "${gocart_output_fh}" +} + GOCART_out() { echo "SUB ${FUNCNAME[0]}: Copying output data for GOCART" @@ -733,8 +724,8 @@ GOCART_out() { # TODO: this should be linked but there are issues where gocart crashing if it is linked local fhr local vdate - for fhr in ${GOCART_OUTPUT_FH}; do - if (( fhr == 0 )); then continue; fi + + for fhr in $(GOCART_output_fh); do vdate=$(date --utc -d "${current_cycle:0:8} ${current_cycle:8:2} + ${fhr} hours" +%Y%m%d%H) ${NCP} "${DATA}/gocart.inst_aod.${vdate:0:8}_${vdate:8:2}00z.nc4" \ "${COMOUT_CHEM_HISTORY}/gocart.inst_aod.${vdate:0:8}_${vdate:8:2}00z.nc4" diff --git a/ush/forecast_predet.sh b/ush/forecast_predet.sh index 0b7072d80b..512d185bb9 100755 --- a/ush/forecast_predet.sh +++ b/ush/forecast_predet.sh @@ -342,9 +342,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."} @@ -352,9 +354,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 @@ -553,9 +561,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 } @@ -723,6 +734,8 @@ GOCART_predet(){ if [[ ! -d "${COMOUT_CHEM_HISTORY}" ]]; then mkdir -p "${COMOUT_CHEM_HISTORY}"; fi - GOCART_OUTPUT_FH=$(seq -s ' ' "${FHMIN}" "6" "${FHMAX}") - # TODO: AERO_HISTORY.rc has hardwired output frequency to 6 hours + # FHMAX gets modified when IAU is on, so keep origianl value for GOCART output + GOCART_MAX=${FHMAX} + + # GOCART output times can't be computed here because they may depend on FHROT } 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 <> "${DATA}/${base_in}" + status=$? + [[ ${status} -ne 0 ]] && exit "${status}" + done + + # attempt to generate ExtData configuration file if not provided + if [[ ! -f "${DATA}/AERO_ExtData.rc" ]]; then + { \ + echo "PrimaryExports%%" ; \ + cat "${AERO_CONFIG_DIR}/ExtData.other" ; \ + cat "${AERO_CONFIG_DIR}/ExtData.${AERO_EMIS_FIRE:-none}" ; \ + echo "%%" ; \ + } > "${DATA}/AERO_ExtData.rc" + # shellcheck disable=SC2320 + status=$? + if (( status != 0 )); then exit "${status}"; fi + fi + fi +} 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/archive.py b/ush/python/pygfs/task/archive.py index d138474e9a..f1d8cdf865 100644 --- a/ush/python/pygfs/task/archive.py +++ b/ush/python/pygfs/task/archive.py @@ -50,7 +50,7 @@ def configure(self, arch_dict: Dict[str, Any]) -> (Dict[str, Any], List[Dict[str Parameters ---------- arch_dict : Dict[str, Any] - Task specific keys, e.g. runtime options (DO_AERO, DO_ICE, etc) + Task specific keys, e.g. runtime options (DO_AERO_FCST, DO_ICE, etc) Return ------ @@ -88,11 +88,6 @@ def configure(self, arch_dict: Dict[str, Any]) -> (Dict[str, Any], List[Dict[str if not os.path.isdir(arch_dict.ROTDIR): raise FileNotFoundError(f"FATAL ERROR: The ROTDIR ({arch_dict.ROTDIR}) does not exist!") - if arch_dict.RUN in ["gdas", "gfs"]: - - # Copy the cyclone track files and rename the experiments - Archive._rename_cyclone_expt(arch_dict) - # Collect datasets that need to be archived # Each dataset represents one tarball @@ -371,14 +366,14 @@ def _rename_cyclone_expt(arch_dict) -> None: if run == "gfs": in_track_file = (track_dir_in + "/avno.t" + - cycle_HH + "z.cycle.trackatcfunix") + cycle_HH + "z.cyclone.trackatcfunix") in_track_p_file = (track_dir_in + "/avnop.t" + - cycle_HH + "z.cycle.trackatcfunixp") + cycle_HH + "z.cyclone.trackatcfunix") elif run == "gdas": in_track_file = (track_dir_in + "/gdas.t" + - cycle_HH + "z.cycle.trackatcfunix") + cycle_HH + "z.cyclone.trackatcfunix") in_track_p_file = (track_dir_in + "/gdasp.t" + - cycle_HH + "z.cycle.trackatcfunixp") + cycle_HH + "z.cyclone.trackatcfunix") if not os.path.isfile(in_track_file): # Do not attempt to archive the outputs @@ -416,7 +411,7 @@ def replace_string_from_to_file(filename_in, filename_out, search_str, replace_s with open("/tmp/track_file", "w") as new_file: new_file.writelines(out_lines) - shutil.move("tmp/track_file", filename_out) + shutil.move("/tmp/track_file", filename_out) replace_string_from_to_file(in_track_file, out_track_file, "AVNO", pslot4) replace_string_from_to_file(in_track_p_file, out_track_p_file, "AVNO", pslot4) 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..4f8fa760c0 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) @@ -281,7 +284,7 @@ def _prep_checkpoint(self: Task) -> None: # render the SOCA to CICE YAML file for the Arctic and Antarctic logger.info("render the SOCA to CICE YAML file for the Arctic and Antarctic") - varchgyamls = ['soca_2cice_arctic.yaml', 'soca_2cice_antarctic.yaml'] + varchgyamls = ['soca_2cice_global.yaml'] for varchgyaml in varchgyamls: soca2cice_config = parse_j2yaml(path=os.path.join(self.task_config.MARINE_JCB_GDAS_ALGO, f'{varchgyaml}.j2'), data=soca2cice_param) 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/ush/python/wxflow b/ush/python/wxflow new file mode 120000 index 0000000000..9dbee42bc8 --- /dev/null +++ b/ush/python/wxflow @@ -0,0 +1 @@ +../../sorc/wxflow/src/wxflow \ No newline at end of file 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 diff --git a/versions/ic.ver b/versions/ic.ver new file mode 100644 index 0000000000..e7bd8358ef --- /dev/null +++ b/versions/ic.ver @@ -0,0 +1,22 @@ +#!/bin/bash +# Initial condition subfolder versions + +#shellcheck disable=SC2034 +declare -A ic_versions + +ic_versions['C48']=20241120 +ic_versions['C48mx500']=20241120 +ic_versions['C48C48mx500']=20241120 + +ic_versions['C96']=20241120 +ic_versions['C96C48']=20241120 +ic_versions['C96mx100']=20240610 + +ic_versions['C384C192']=20240610 +ic_versions['C384mx025']=20241120 + +ic_versions['C768']=20241120 +ic_versions['C768mx025']=20241120 + +ic_versions['C1152']=20241120 +ic_versions['C1152mx025']=20241120 diff --git a/workflow/applications/applications.py b/workflow/applications/applications.py index ecd320d708..22e299df20 100644 --- a/workflow/applications/applications.py +++ b/workflow/applications/applications.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from typing import Dict, List, Any -from datetime import timedelta from hosts import Host -from wxflow import Configuration, to_timedelta +from wxflow import Configuration from abc import ABC, ABCMeta, abstractmethod __all__ = ['AppConfig'] @@ -31,94 +30,83 @@ def __init__(self, conf: Configuration) -> None: self.scheduler = Host().scheduler + # Get the most basic settings from config.base to determine + # experiment type ({NET}_{MODE}) base = conf.parse_config('config.base') self.mode = base['MODE'] - if self.mode not in self.VALID_MODES: raise NotImplementedError(f'{self.mode} is not a valid application mode.\n' f'Valid application modes are:\n' f'{", ".join(self.VALID_MODES)}\n') self.net = base['NET'] - self.model_app = base.get('APP', 'ATM') - self.do_atm = base.get('DO_ATM', True) - self.do_wave = base.get('DO_WAVE', False) - self.do_wave_bnd = base.get('DOBNDPNT_WAVE', False) - self.do_ocean = base.get('DO_OCN', False) - self.do_ice = base.get('DO_ICE', False) - self.do_aero = base.get('DO_AERO', False) - self.do_prep_obs_aero = base.get('DO_PREP_OBS_AERO', False) - self.do_bufrsnd = base.get('DO_BUFRSND', False) - self.do_gempak = base.get('DO_GEMPAK', False) - self.do_awips = base.get('DO_AWIPS', False) - self.do_verfozn = base.get('DO_VERFOZN', True) - self.do_verfrad = base.get('DO_VERFRAD', True) - self.do_vminmon = base.get('DO_VMINMON', True) - self.do_tracker = base.get('DO_TRACKER', True) - self.do_genesis = base.get('DO_GENESIS', True) - self.do_genesis_fsu = base.get('DO_GENESIS_FSU', False) - self.do_metp = base.get('DO_METP', False) - self.do_upp = not base.get('WRITE_DOPOST', True) - self.do_goes = base.get('DO_GOES', False) - self.do_mos = base.get('DO_MOS', False) - self.do_extractvars = base.get('DO_EXTRACTVARS', False) - - self.do_hpssarch = base.get('HPSSARCH', False) - - self.nens = base.get('NMEM_ENS', 0) - self.fcst_segments = base.get('FCST_SEGMENTS', None) - self.interval_gfs = to_timedelta(f"{base.get('INTERVAL_GFS')}H") - - if not AppConfig.is_monotonic(self.fcst_segments): - raise ValueError(f'Forecast segments do not increase monotonically: {",".join(self.fcst_segments)}') - - self.wave_runs = None - if self.do_wave: - wave_run = base.get('WAVE_RUN', 'BOTH').lower() - if wave_run in ['both']: - self.wave_runs = ['gfs', 'gdas'] - elif wave_run in ['gfs', 'gdas']: - self.wave_runs = [wave_run] - - self.aero_anl_runs = None - self.aero_fcst_runs = None - if self.do_aero: - aero_anl_run = base.get('AERO_ANL_RUN', 'BOTH').lower() - if aero_anl_run in ['both']: - self.aero_anl_runs = ['gfs', 'gdas'] - elif aero_anl_run in ['gfs', 'gdas']: - self.aero_anl_runs = [aero_anl_run] - aero_fcst_run = base.get('AERO_FCST_RUN', None).lower() - if aero_fcst_run in ['both']: - self.aero_fcst_runs = ['gfs', 'gdas'] - elif aero_fcst_run in ['gfs', 'gdas']: - self.aero_fcst_runs = [aero_fcst_run] + print(f"Generating the XML for a {self.mode}_{self.net} case") def _init_finalize(self, conf: Configuration): - print("Finalizing initialize") - - # Get a list of all possible config_files that would be part of the application - self.configs_names = self._get_app_configs() - - # Source the config files for the jobs in the application without specifying a RUN - self.configs = {'_no_run': self._source_configs(conf)} - - # Update the base config dictionary based on application - self.configs['_no_run']['base'] = self._update_base(self.configs['_no_run']['base']) + ''' + Finalize object initialization calling subclass methods + ''' - # Save base in the internal state since it is often needed - base = self.configs['_no_run']['base'] + # Get run-, net-, and mode-based options + self.run_options = self._get_run_options(conf) - # Get task names for the application + # Get task names and runs for the application self.task_names = self.get_task_names() - # Finally, source the configuration files for each valid `RUN` - for run in self.task_names.keys(): + # Initialize the configs and model_apps dictionaries + self.configs = dict.fromkeys(self.runs) + + # Now configure the experiment for each valid run + for run in self.runs: self.configs[run] = self._source_configs(conf, run=run, log=False) - # Update the base config dictionary based on application and RUN - self.configs[run]['base'] = self._update_base(self.configs[run]['base']) + def _get_run_options(self, conf: Configuration) -> Dict[str, Any]: + ''' + Determine the do_* and APP options for each RUN by sourcing config.base + for each RUN and collecting the flags into self.run_options. Note that + this method is overloaded so additional NET- and MODE-dependent flags + can be set. + ''' + + run_options = {run: {} for run in dict.fromkeys(self.runs)} + for run in self.runs: + # Read config.base with RUN specified + run_base = conf.parse_config('config.base', RUN=run) + + run_options[run]['app'] = run_base.get('APP', 'ATM') + run_options[run]['do_wave_bnd'] = run_base.get('DOBNDPNT_WAVE', False) + run_options[run]['do_bufrsnd'] = run_base.get('DO_BUFRSND', False) + run_options[run]['do_gempak'] = run_base.get('DO_GEMPAK', False) + run_options[run]['do_awips'] = run_base.get('DO_AWIPS', False) + run_options[run]['do_verfozn'] = run_base.get('DO_VERFOZN', True) + run_options[run]['do_verfrad'] = run_base.get('DO_VERFRAD', True) + run_options[run]['do_vminmon'] = run_base.get('DO_VMINMON', True) + run_options[run]['do_tracker'] = run_base.get('DO_TRACKER', True) + run_options[run]['do_genesis'] = run_base.get('DO_GENESIS', True) + run_options[run]['do_genesis_fsu'] = run_base.get('DO_GENESIS_FSU', False) + run_options[run]['do_metp'] = run_base.get('DO_METP', False) + run_options[run]['do_upp'] = not run_base.get('WRITE_DOPOST', True) + run_options[run]['do_goes'] = run_base.get('DO_GOES', False) + run_options[run]['do_mos'] = run_base.get('DO_MOS', False) + run_options[run]['do_extractvars'] = run_base.get('DO_EXTRACTVARS', False) + + run_options[run]['do_atm'] = run_base.get('DO_ATM', True) + run_options[run]['do_wave'] = run_base.get('DO_WAVE', False) + run_options[run]['do_ocean'] = run_base.get('DO_OCN', False) + run_options[run]['do_ice'] = run_base.get('DO_ICE', False) + run_options[run]['do_prep_obs_aero'] = run_base.get('DO_PREP_OBS_AERO', False) + run_options[run]['do_aero_anl'] = run_base.get('DO_AERO_ANL', False) + run_options[run]['do_aero_fcst'] = run_base.get('DO_AERO_FCST', False) + + run_options[run]['do_hpssarch'] = run_base.get('HPSSARCH', False) + run_options[run]['fcst_segments'] = run_base.get('FCST_SEGMENTS', None) + + if not AppConfig.is_monotonic(run_options[run]['fcst_segments']): + raise ValueError(f'Forecast segments do not increase monotonically: {",".join(self.fcst_segments)}') + + # Return the dictionary of run options + return run_options @abstractmethod def _get_app_configs(self): @@ -150,13 +138,12 @@ def _source_configs(self, conf: Configuration, run: str = "gfs", log: bool = Tru Every config depends on "config.base" """ - configs = dict() - - # Return config.base as well - configs['base'] = conf.parse_config('config.base', RUN=run) + # Include config.base by its lonesome and update it + configs = {'base': conf.parse_config('config.base', RUN=run)} + configs['base'] = self._update_base(configs['base']) # Source the list of all config_files involved in the application - for config in self.configs_names: + for config in self._get_app_configs(run): # All must source config.base first files = ['config.base'] @@ -182,9 +169,9 @@ def _source_configs(self, conf: Configuration, run: str = "gfs", log: bool = Tru return configs @abstractmethod - def get_task_names(self, run="_no_run") -> Dict[str, List[str]]: + def get_task_names(self, run: str) -> Dict[str, List[str]]: ''' - Create a list of task names for each RUN valid for the configuation. + Create a list of valid RUNs and a dict of task names for each RUN valid for the configuation. Parameters ---------- @@ -192,7 +179,7 @@ def get_task_names(self, run="_no_run") -> Dict[str, List[str]]: Returns ------- - Dict[str, List[str]]: Lists of tasks for each RUN. + Dict[str, List[str]]: Lists of all tasks for each RUN. ''' pass diff --git a/workflow/applications/gefs.py b/workflow/applications/gefs.py index 9d1d5c3dc4..33545eb2ec 100644 --- a/workflow/applications/gefs.py +++ b/workflow/applications/gefs.py @@ -1,4 +1,5 @@ from applications.applications import AppConfig +from typing import Dict, Any from wxflow import Configuration @@ -12,28 +13,38 @@ def __init__(self, conf: Configuration): base = conf.parse_config('config.base') self.run = base.get('RUN', 'gefs') + self.runs = [self.run] - def _get_app_configs(self): + def _get_run_options(self, conf: Configuration) -> Dict[str, Any]: + + run_options = super()._get_run_options(conf) + + run_options[self.run]['nens'] = conf.parse_config('config.base').get('NMEM_ENS', 0) + + return run_options + + def _get_app_configs(self, run): """ Returns the config_files that are involved in gefs """ + options = self.run_options[run] configs = ['stage_ic', 'fcst', 'atmos_products', 'arch', 'cleanup'] - if self.nens > 0: + if options['nens'] > 0: configs += ['efcs', 'atmos_ensstat'] - if self.do_wave: + if options['do_wave']: configs += ['waveinit', 'wavepostsbs', 'wavepostpnt'] - if self.do_wave_bnd: + if options['do_wave_bnd']: configs += ['wavepostbndpnt', 'wavepostbndpntbll'] - if self.do_ocean or self.do_ice: + if options['do_ocean'] or options['do_ice']: configs += ['oceanice_products'] - if self.do_aero: + if options['do_aero_fcst']: configs += ['prep_emissions'] - if self.do_extractvars: + if options['do_extractvars']: configs += ['extractvars'] return configs @@ -48,37 +59,38 @@ def _update_base(base_in): def get_task_names(self): + options = self.run_options[self.run] tasks = ['stage_ic'] - if self.do_wave: + if options['do_wave']: tasks += ['waveinit'] - if self.do_aero: + if options['do_aero_fcst']: tasks += ['prep_emissions'] tasks += ['fcst'] - if self.nens > 0: + if options['nens'] > 0: tasks += ['efcs'] tasks += ['atmos_prod'] - if self.nens > 0: + if options['nens'] > 0: tasks += ['atmos_ensstat'] - if self.do_ocean: + if options['do_ocean']: tasks += ['ocean_prod'] - if self.do_ice: + if options['do_ice']: tasks += ['ice_prod'] - if self.do_wave: + if options['do_wave']: tasks += ['wavepostsbs'] - if self.do_wave_bnd: + if options['do_wave_bnd']: tasks += ['wavepostbndpnt', 'wavepostbndpntbll'] tasks += ['wavepostpnt'] - if self.do_extractvars: + if options['do_extractvars']: tasks += ['extractvars', 'arch'] tasks += ['cleanup'] diff --git a/workflow/applications/gfs_cycled.py b/workflow/applications/gfs_cycled.py index f92bf95fba..2d16b6a59c 100644 --- a/workflow/applications/gfs_cycled.py +++ b/workflow/applications/gfs_cycled.py @@ -1,7 +1,6 @@ -from typing import Dict, Any from applications.applications import AppConfig -from wxflow import Configuration, to_timedelta -from datetime import timedelta +from typing import Dict, Any +from wxflow import Configuration class GFSCycledAppConfig(AppConfig): @@ -11,113 +10,136 @@ class GFSCycledAppConfig(AppConfig): def __init__(self, conf: Configuration): super().__init__(conf) + # Re-read config.base without RUN specified to get the basic settings for + # cycled cases to be able to determine valid runs base = conf.parse_config('config.base') - self.do_hybvar = base.get('DOHYBVAR', False) - self.do_fit2obs = base.get('DO_FIT2OBS', True) - self.do_jediatmvar = base.get('DO_JEDIATMVAR', False) - self.do_jediatmens = base.get('DO_JEDIATMENS', False) - self.do_jediocnvar = base.get('DO_JEDIOCNVAR', False) - self.do_jedisnowda = base.get('DO_JEDISNOWDA', False) - self.do_mergensst = base.get('DO_MERGENSST', False) - self.do_vrfy_oceanda = base.get('DO_VRFY_OCEANDA', False) - - self.lobsdiag_forenkf = False - self.eupd_runs = None - if self.do_hybvar: - self.lobsdiag_forenkf = base.get('lobsdiag_forenkf', False) - eupd_run = base.get('EUPD_CYC', 'gdas').lower() - if eupd_run in ['both']: - self.eupd_runs = ['gfs', 'gdas'] - elif eupd_run in ['gfs', 'gdas']: - self.eupd_runs = [eupd_run] - - def _get_app_configs(self): + + self.ens_runs = [] + + if base.get('DOHYBVAR', False): + ens_run = base.get('EUPD_CYC', 'gdas').lower() + if ens_run in ['both']: + self.ens_runs = ['gfs', 'gdas'] + elif ens_run in ['gfs', 'gdas']: + self.ens_runs = [ens_run] + + # Now construct self.runs the desired XML order (gdas, enkfgdas, gfs, enkfgfs) + self.runs = ["gdas"] # We always have a 'gdas' run + self.runs.append('enkfgdas') if 'gdas' in self.ens_runs else 0 + self.runs.append("gfs") if base['INTERVAL_GFS'] > 0 else 0 + self.runs.append('enkfgfs') if 'gfs' in self.ens_runs and "gfs" in self.runs else 0 + + def _get_run_options(self, conf: Configuration) -> Dict[str, Any]: + + run_options = super()._get_run_options(conf) + + for run in self.runs: + base = conf.parse_config('config.base', RUN=run) + + run_options[run]['do_hybvar'] = base.get('DOHYBVAR', False) + run_options[run]['nens'] = base.get('NMEM_ENS', 0) + if run_options[run]['do_hybvar']: + run_options[run]['lobsdiag_forenkf'] = base.get('lobsdiag_forenkf', False) + + run_options[run]['do_fit2obs'] = base.get('DO_FIT2OBS', True) + run_options[run]['do_jediatmvar'] = base.get('DO_JEDIATMVAR', False) + run_options[run]['do_jediatmens'] = base.get('DO_JEDIATMENS', False) + run_options[run]['do_jediocnvar'] = base.get('DO_JEDIOCNVAR', False) + run_options[run]['do_jedisnowda'] = base.get('DO_JEDISNOWDA', False) + run_options[run]['do_mergensst'] = base.get('DO_MERGENSST', False) + run_options[run]['do_vrfy_oceanda'] = base.get('DO_VRFY_OCEANDA', False) + + return run_options + + def _get_app_configs(self, run): """ - Returns the config_files that are involved in the cycled app + Returns the config files that are involved in the cycled app """ + options = self.run_options[run] configs = ['prep'] - if self.do_jediatmvar: + if options['do_jediatmvar']: configs += ['prepatmiodaobs', 'atmanlinit', 'atmanlvar', 'atmanlfv3inc', 'atmanlfinal'] else: configs += ['anal', 'analdiag'] - if self.do_jediocnvar: + if options['do_jediocnvar']: configs += ['prepoceanobs', 'marineanlinit', 'marinebmat', 'marineanlvar'] - if self.do_hybvar: + if options['do_hybvar']: configs += ['marineanlletkf', 'ocnanalecen'] configs += ['marineanlchkpt', 'marineanlfinal'] - if self.do_vrfy_oceanda: + if options['do_vrfy_oceanda']: configs += ['ocnanalvrfy'] - if self.do_ocean or self.do_ice: + if options['do_ocean'] or options['do_ice']: configs += ['oceanice_products'] configs += ['stage_ic', 'sfcanl', 'analcalc', 'fcst', 'upp', 'atmos_products', 'arch', 'cleanup'] - if self.do_hybvar: - if self.do_jediatmens: - configs += ['atmensanlinit', 'atmensanlobs', 'atmensanlsol', 'atmensanlletkf', 'atmensanlfv3inc', 'atmensanlfinal'] + if options['do_hybvar']: + if options['do_jediatmens']: + configs += ['atmensanlinit', 'atmensanlobs', 'atmensanlsol', + 'atmensanlletkf', 'atmensanlfv3inc', 'atmensanlfinal'] else: configs += ['eobs', 'eomg', 'ediag', 'eupd'] configs += ['ecen', 'esfc', 'efcs', 'echgres', 'epos', 'earc'] - if self.do_fit2obs: + if options['do_fit2obs']: configs += ['fit2obs'] - if self.do_verfozn: + if options['do_verfozn']: configs += ['verfozn'] - if self.do_verfrad: + if options['do_verfrad']: configs += ['verfrad'] - if self.do_vminmon: + if options['do_vminmon']: configs += ['vminmon'] - if self.do_tracker: + if options['do_tracker']: configs += ['tracker'] - if self.do_genesis: + if options['do_genesis']: configs += ['genesis'] - if self.do_genesis_fsu: + if options['do_genesis_fsu']: configs += ['genesis_fsu'] - if self.do_metp: + if options['do_metp']: configs += ['metp'] - if self.do_gempak: + if options['do_gempak']: configs += ['gempak'] - if self.do_goes: + if options['do_goes']: configs += ['npoess'] - if self.do_bufrsnd: + if options['do_bufrsnd']: configs += ['postsnd'] - if self.do_awips: + if options['do_awips']: configs += ['awips'] - if self.do_wave: + if options['do_wave']: configs += ['waveinit', 'waveprep', 'wavepostsbs', 'wavepostpnt'] - if self.do_wave_bnd: + if options['do_wave_bnd']: configs += ['wavepostbndpnt', 'wavepostbndpntbll'] - if self.do_gempak: + if options['do_gempak']: configs += ['wavegempak'] - if self.do_awips: + if options['do_awips']: configs += ['waveawipsbulls', 'waveawipsgridded'] - if self.do_aero: + if options['do_aero_anl']: configs += ['aeroanlgenb', 'aeroanlinit', 'aeroanlvar', 'aeroanlfinal'] - if self.do_prep_obs_aero: + if options['do_prep_obs_aero']: configs += ['prepobsaero'] - if self.do_jedisnowda: - configs += ['prepsnowobs', 'snowanl'] - if self.do_hybvar: + if options['do_jedisnowda']: + configs += ['snowanl'] + if options['do_hybvar']: configs += ['esnowrecen'] - if self.do_mos: + if options['do_mos']: configs += ['mos_stn_prep', 'mos_grd_prep', 'mos_ext_stn_prep', 'mos_ext_grd_prep', 'mos_stn_fcst', 'mos_grd_fcst', 'mos_ext_stn_fcst', 'mos_ext_grd_fcst', 'mos_stn_prdgen', 'mos_grd_prdgen', 'mos_ext_stn_prdgen', 'mos_ext_grd_prdgen', @@ -132,178 +154,169 @@ def _update_base(base_in): def get_task_names(self): """ - Get the task names for all the tasks in the cycled application. - Note that the order of the task names matters in the XML. - This is the place where that order is set. + Get the task names for each valid run in this cycled configuration. + NOTE: The order of the task names matters in the XML. + This is the place where that order is set. """ - gdas_gfs_common_tasks_before_fcst = ['prep'] - gdas_gfs_common_cleanup_tasks = ['arch', 'cleanup'] - - if self.do_jediatmvar: - gdas_gfs_common_tasks_before_fcst += ['prepatmiodaobs', 'atmanlinit', 'atmanlvar', 'atmanlfv3inc', 'atmanlfinal'] - else: - gdas_gfs_common_tasks_before_fcst += ['anal'] - - if self.do_jediocnvar: - gdas_gfs_common_tasks_before_fcst += ['prepoceanobs', 'marineanlinit', 'marinebmat', 'marineanlvar'] - if self.do_hybvar: - gdas_gfs_common_tasks_before_fcst += ['marineanlletkf', 'ocnanalecen'] - gdas_gfs_common_tasks_before_fcst += ['marineanlchkpt', 'marineanlfinal'] - if self.do_vrfy_oceanda: - gdas_gfs_common_tasks_before_fcst += ['ocnanalvrfy'] - - gdas_gfs_common_tasks_before_fcst += ['sfcanl', 'analcalc'] - - if self.do_jedisnowda: - gdas_gfs_common_tasks_before_fcst += ['prepsnowobs', 'snowanl'] - - wave_prep_tasks = ['waveinit', 'waveprep'] - wave_bndpnt_tasks = ['wavepostbndpnt', 'wavepostbndpntbll'] - wave_post_tasks = ['wavepostsbs', 'wavepostpnt'] - - hybrid_tasks = [] - hybrid_after_eupd_tasks = [] - if self.do_hybvar: - if self.do_jediatmens: - hybrid_tasks += ['atmensanlinit', 'atmensanlfv3inc', 'atmensanlfinal', 'echgres'] - hybrid_tasks += ['atmensanlobs', 'atmensanlsol'] if self.lobsdiag_forenkf else ['atmensanlletkf'] - else: - hybrid_tasks += ['eobs', 'eupd', 'echgres'] - hybrid_tasks += ['ediag'] if self.lobsdiag_forenkf else ['eomg'] - if self.do_jedisnowda: - hybrid_tasks += ['esnowrecen'] - hybrid_after_eupd_tasks += ['stage_ic', 'ecen', 'esfc', 'efcs', 'epos', 'earc', 'cleanup'] - - # Collect all "gdas" cycle tasks - gdas_tasks = gdas_gfs_common_tasks_before_fcst.copy() - - if not self.do_jediatmvar: - gdas_tasks += ['analdiag'] - - if self.do_wave and 'gdas' in self.wave_runs: - gdas_tasks += wave_prep_tasks - - if self.do_aero and 'gdas' in self.aero_anl_runs: - gdas_tasks += ['aeroanlgenb', 'aeroanlinit', 'aeroanlvar', 'aeroanlfinal'] - if self.do_prep_obs_aero: - gdas_tasks += ['prepobsaero'] - - gdas_tasks += ['stage_ic', 'atmanlupp', 'atmanlprod', 'fcst'] - - if self.do_upp: - gdas_tasks += ['atmupp'] - gdas_tasks += ['atmos_prod'] - - if self.do_wave and 'gdas' in self.wave_runs: - if self.do_wave_bnd: - gdas_tasks += wave_bndpnt_tasks - gdas_tasks += wave_post_tasks - - if self.do_fit2obs: - gdas_tasks += ['fit2obs'] - - if self.do_verfozn: - gdas_tasks += ['verfozn'] - - if self.do_verfrad: - gdas_tasks += ['verfrad'] - - if self.do_vminmon: - gdas_tasks += ['vminmon'] - - if self.do_gempak: - gdas_tasks += ['gempak', 'gempakmetancdc'] - - gdas_tasks += gdas_gfs_common_cleanup_tasks - - # Collect "gfs" cycle tasks - gfs_tasks = gdas_gfs_common_tasks_before_fcst.copy() - - if self.do_wave and 'gfs' in self.wave_runs: - gfs_tasks += wave_prep_tasks - - if self.do_aero and 'gfs' in self.aero_anl_runs: - gfs_tasks += ['aeroanlinit', 'aeroanlvar', 'aeroanlfinal'] - if self.do_prep_obs_aero: - gfs_tasks += ['prepobsaero'] - - gfs_tasks += ['atmanlupp', 'atmanlprod', 'fcst'] - - if self.do_ocean: - gfs_tasks += ['ocean_prod'] - - if self.do_ice: - gfs_tasks += ['ice_prod'] - - if self.do_upp: - gfs_tasks += ['atmupp'] - gfs_tasks += ['atmos_prod'] - - if self.do_goes: - gfs_tasks += ['goesupp'] - - if self.do_vminmon: - gfs_tasks += ['vminmon'] - - if self.do_tracker: - gfs_tasks += ['tracker'] - - if self.do_genesis: - gfs_tasks += ['genesis'] - - if self.do_genesis_fsu: - gfs_tasks += ['genesis_fsu'] - - if self.do_metp: - gfs_tasks += ['metp'] - - if self.do_wave and 'gfs' in self.wave_runs: - if self.do_wave_bnd: - gfs_tasks += wave_bndpnt_tasks - gfs_tasks += wave_post_tasks - if self.do_gempak: - gfs_tasks += ['wavegempak'] - if self.do_awips: - gfs_tasks += ['waveawipsbulls', 'waveawipsgridded'] - - if self.do_bufrsnd: - gfs_tasks += ['postsnd'] - - if self.do_gempak: - gfs_tasks += ['gempak'] - gfs_tasks += ['gempakmeta'] - gfs_tasks += ['gempakncdcupapgif'] - if self.do_goes: - gfs_tasks += ['npoess_pgrb2_0p5deg'] - gfs_tasks += ['gempakpgrb2spec'] - - if self.do_awips: - gfs_tasks += ['awips_20km_1p0deg', 'fbwind'] - - if self.do_mos: - gfs_tasks += ['mos_stn_prep', 'mos_grd_prep', 'mos_ext_stn_prep', 'mos_ext_grd_prep', - 'mos_stn_fcst', 'mos_grd_fcst', 'mos_ext_stn_fcst', 'mos_ext_grd_fcst', - 'mos_stn_prdgen', 'mos_grd_prdgen', 'mos_ext_stn_prdgen', 'mos_ext_grd_prdgen', - 'mos_wx_prdgen', 'mos_wx_ext_prdgen'] - - gfs_tasks += gdas_gfs_common_cleanup_tasks - - tasks = dict() - tasks['gdas'] = gdas_tasks - - if self.do_hybvar and 'gdas' in self.eupd_runs: - enkfgdas_tasks = hybrid_tasks + hybrid_after_eupd_tasks - tasks['enkfgdas'] = enkfgdas_tasks - - # Add RUN=gfs tasks if running early cycle - if self.interval_gfs > to_timedelta("0H"): - tasks['gfs'] = gfs_tasks + # Start with a dictionary of empty task lists for each valid run + task_names = {run: [] for run in self.runs} - if self.do_hybvar and 'gfs' in self.eupd_runs: - enkfgfs_tasks = hybrid_tasks + hybrid_after_eupd_tasks - enkfgfs_tasks.remove("echgres") - enkfgfs_tasks.remove("esnowrecen") - tasks['enkfgfs'] = enkfgfs_tasks + for run in self.runs: + options = self.run_options[run] - return tasks + # Common gdas and gfs tasks before fcst + if run in ['gdas', 'gfs']: + task_names[run] += ['prep'] + if options['do_jediatmvar']: + task_names[run] += ['prepatmiodaobs', 'atmanlinit', 'atmanlvar', 'atmanlfv3inc', 'atmanlfinal'] + else: + task_names[run] += ['anal'] + + if options['do_jediocnvar']: + task_names[run] += ['prepoceanobs', 'marineanlinit', 'marinebmat', 'marineanlvar'] + if options['do_hybvar']: + task_names[run] += ['marineanlletkf', 'ocnanalecen'] + task_names[run] += ['marineanlchkpt', 'marineanlfinal'] + if options['do_vrfy_oceanda']: + task_names[run] += ['ocnanalvrfy'] + + task_names[run] += ['sfcanl', 'analcalc'] + + if options['do_jedisnowda']: + task_names[run] += ['snowanl'] + + wave_prep_tasks = ['waveinit', 'waveprep'] + wave_bndpnt_tasks = ['wavepostbndpnt', 'wavepostbndpntbll'] + wave_post_tasks = ['wavepostsbs', 'wavepostpnt'] + + # gdas- and gfs-specific analysis tasks + if run == 'gdas': + if not options['do_jediatmvar']: + task_names[run] += ['analdiag'] + + if options['do_wave']: + task_names[run] += wave_prep_tasks + + if options['do_aero_anl']: + task_names[run] += ['aeroanlgenb'] + + else: + if options['do_wave']: + task_names[run] += wave_prep_tasks + + if options['do_aero_anl']: + task_names[run] += ['aeroanlinit', 'aeroanlvar', 'aeroanlfinal'] + + if options['do_prep_obs_aero']: + task_names[run] += ['prepobsaero'] + + # Staging is gdas-specific + if run == 'gdas': + task_names[run] += ['stage_ic'] + + task_names[run] += ['atmanlupp', 'atmanlprod', 'fcst'] + + # gfs-specific products + if run == 'gfs': + if options['do_ocean']: + task_names[run] += ['ocean_prod'] + + if options['do_ice']: + task_names[run] += ['ice_prod'] + + if options['do_upp']: + task_names[run] += ['atmupp'] + task_names[run] += ['atmos_prod'] + + # GOES post-processing (gfs only) + if run == 'gfs': + if options['do_goes']: + task_names[run] += ['goesupp'] + + # Only fit to obs and verify ozone and radiance during gdas cycles + if run == "gdas": + if options['do_fit2obs']: + task_names[run] += ['fit2obs'] + if options['do_verfozn']: + task_names[run] += ['verfozn'] + if options['do_verfrad']: + task_names[run] += ['verfrad'] + + if options['do_vminmon']: + task_names[run] += ['vminmon'] + + # gfs-only verification/tracking + if run == 'gfs': + if options['do_tracker']: + task_names[run] += ['tracker'] + + if options['do_genesis']: + task_names[run] += ['genesis'] + + if options['do_genesis_fsu']: + task_names[run] += ['genesis_fsu'] + + if options['do_metp']: + task_names[run] += ['metp'] + + if options['do_wave']: + if options['do_wave_bnd']: + task_names[run] += wave_bndpnt_tasks + task_names[run] += wave_post_tasks + # wave gempak and awips jobs are gfs-specific + if run == 'gfs': + if options['do_gempak']: + task_names[run] += ['wavegempak'] + if options['do_awips']: + task_names[run] += ['waveawipsbulls', 'waveawipsgridded'] + + # gdas- and gfs-specific downstream products + if run == 'gdas': + if options['do_gempak']: + task_names[run] += ['gempak', 'gempakmetancdc'] + else: + if options['do_bufrsnd']: + task_names[run] += ['postsnd'] + + if options['do_gempak']: + task_names[run] += ['gempak'] + task_names[run] += ['gempakmeta'] + task_names[run] += ['gempakncdcupapgif'] + if options['do_goes']: + task_names[run] += ['npoess_pgrb2_0p5deg'] + task_names[run] += ['gempakpgrb2spec'] + + if options['do_awips']: + task_names[run] += ['awips_20km_1p0deg', 'fbwind'] + + if options['do_mos']: + task_names[run] += ['mos_stn_prep', 'mos_grd_prep', 'mos_ext_stn_prep', 'mos_ext_grd_prep', + 'mos_stn_fcst', 'mos_grd_fcst', 'mos_ext_stn_fcst', 'mos_ext_grd_fcst', + 'mos_stn_prdgen', 'mos_grd_prdgen', 'mos_ext_stn_prdgen', + 'mos_ext_grd_prdgen', 'mos_wx_prdgen', 'mos_wx_ext_prdgen'] + + # Last two items + task_names[run] += ['arch', 'cleanup'] + + # Ensemble tasks + elif 'enkf' in run: + + if options['do_jediatmens']: + task_names[run] += ['atmensanlinit', 'atmensanlfv3inc', 'atmensanlfinal'] + # Only run echgres for the gdas cycle + task_names[run] += ['echgres'] if 'gdas' in run else 0 + if options['lobsdiag_forenkf']: + task_names[run] += ['atmensanlobs', 'atmensanlsol'] + else: + task_names[run] += ['atmensanlletkf'] + + else: + task_names[run] += ['eobs', 'eupd'] + task_names[run].append('echgres') if 'gdas' in run else 0 + task_names[run] += ['ediag'] if options['lobsdiag_forenkf'] else ['eomg'] + task_names[run].append('esnowrecen') if options['do_jedisnowda'] and 'gdas' in run else 0 + + task_names[run] += ['stage_ic', 'ecen', 'esfc', 'efcs', 'epos', 'earc', 'cleanup'] + + return task_names diff --git a/workflow/applications/gfs_forecast_only.py b/workflow/applications/gfs_forecast_only.py index fb1d2cdb8f..fffdab6ef9 100644 --- a/workflow/applications/gfs_forecast_only.py +++ b/workflow/applications/gfs_forecast_only.py @@ -1,5 +1,6 @@ from applications.applications import AppConfig from wxflow import Configuration +from typing import Dict, Any class GFSForecastOnlyAppConfig(AppConfig): @@ -11,62 +12,70 @@ def __init__(self, conf: Configuration): super().__init__(conf) base = conf.parse_config('config.base') - self.aero_fcst_run = base.get('AERO_FCST_RUN', 'BOTH').lower() self.run = base.get('RUN', 'gfs') - self.exp_warm_start = base.get('EXP_WARM_START', False) + self.runs = [self.run] - def _get_app_configs(self): + def _get_run_options(self, conf: Configuration) -> Dict[str, Any]: + + run_options = super()._get_run_options(conf) + + run_options[self.run]['exp_warm_start'] = conf.parse_config('config.base').get('EXP_WARM_START', False) + + return run_options + + def _get_app_configs(self, run): """ Returns the config_files that are involved in the forecast-only app """ + options = self.run_options[run] configs = ['stage_ic', 'fcst', 'arch', 'cleanup'] - if self.do_atm: + if options['do_atm']: - if self.do_upp or self.do_goes: + if options['do_upp'] or options['do_goes']: configs += ['upp'] configs += ['atmos_products'] - if self.do_aero: - if not self.exp_warm_start: + if options['do_aero_fcst']: + if not options['exp_warm_start']: configs += ['aerosol_init'] - if self.do_tracker: + if options['do_tracker']: configs += ['tracker'] - if self.do_genesis: + if options['do_genesis']: configs += ['genesis'] - if self.do_genesis_fsu: + if options['do_genesis_fsu']: configs += ['genesis_fsu'] - if self.do_metp: + if options['do_metp']: configs += ['metp'] - if self.do_bufrsnd: + if options['do_bufrsnd']: configs += ['postsnd'] - if self.do_gempak: + if options['do_gempak']: configs += ['gempak'] - if self.do_awips: + if options['do_awips']: configs += ['awips'] - if self.do_ocean or self.do_ice: + if options['do_ocean'] or options['do_ice']: configs += ['oceanice_products'] - if self.do_wave: + if options['do_wave']: configs += ['waveinit', 'waveprep', 'wavepostsbs', 'wavepostpnt'] - if self.do_wave_bnd: + if options['do_wave_bnd']: configs += ['wavepostbndpnt', 'wavepostbndpntbll'] - if self.do_gempak: + if options['do_gempak']: configs += ['wavegempak'] - if self.do_awips: + if options['do_awips']: configs += ['waveawipsbulls', 'waveawipsgridded'] - if self.do_mos: + if options['do_mos']: configs += ['mos_stn_prep', 'mos_grd_prep', 'mos_ext_stn_prep', 'mos_ext_grd_prep', 'mos_stn_fcst', 'mos_grd_fcst', 'mos_ext_stn_fcst', 'mos_ext_grd_fcst', 'mos_stn_prdgen', 'mos_grd_prdgen', 'mos_ext_stn_prdgen', 'mos_ext_grd_prdgen', @@ -90,66 +99,64 @@ def get_task_names(self): """ tasks = ['stage_ic'] + options = self.run_options[self.run] - if self.do_aero: - aero_fcst_run = self.aero_fcst_run - if self.run in aero_fcst_run or aero_fcst_run == "both": - if not self.exp_warm_start: - tasks += ['aerosol_init'] + if options['do_aero_fcst'] and not options['exp_warm_start']: + tasks += ['aerosol_init'] - if self.do_wave: + if options['do_wave']: tasks += ['waveinit'] # tasks += ['waveprep'] # TODO - verify if waveprep is executed in forecast-only mode when APP=ATMW|S2SW tasks += ['fcst'] - if self.do_atm: + if options['do_atm']: - if self.do_upp: + if options['do_upp']: tasks += ['atmupp'] tasks += ['atmos_prod'] - if self.do_goes: + if options['do_goes']: tasks += ['goesupp'] - if self.do_tracker: + if options['do_tracker']: tasks += ['tracker'] - if self.do_genesis: + if options['do_genesis']: tasks += ['genesis'] - if self.do_genesis_fsu: + if options['do_genesis_fsu']: tasks += ['genesis_fsu'] - if self.do_metp: + if options['do_metp']: tasks += ['metp'] - if self.do_bufrsnd: + if options['do_bufrsnd']: tasks += ['postsnd'] - if self.do_gempak: + if options['do_gempak']: tasks += ['gempak', 'gempakmeta', 'gempakncdcupapgif', 'gempakpgrb2spec'] - if self.do_awips: + if options['do_awips']: tasks += ['awips_20km_1p0deg', 'fbwind'] - if self.do_ocean: + if options['do_ocean']: tasks += ['ocean_prod'] - if self.do_ice: + if options['do_ice']: tasks += ['ice_prod'] - if self.do_wave: - if self.do_wave_bnd: + if options['do_wave']: + if options['do_wave_bnd']: tasks += ['wavepostbndpnt', 'wavepostbndpntbll'] tasks += ['wavepostsbs', 'wavepostpnt'] - if self.do_gempak: + if options['do_gempak']: tasks += ['wavegempak'] - if self.do_awips: + if options['do_awips']: tasks += ['waveawipsbulls', 'waveawipsgridded'] - if self.do_mos: + if options['do_mos']: tasks += ['mos_stn_prep', 'mos_grd_prep', 'mos_ext_stn_prep', 'mos_ext_grd_prep', 'mos_stn_fcst', 'mos_grd_fcst', 'mos_ext_stn_fcst', 'mos_ext_grd_fcst', 'mos_stn_prdgen', 'mos_grd_prdgen', 'mos_ext_stn_prdgen', 'mos_ext_grd_prdgen', diff --git a/workflow/generate_workflows.sh b/workflow/generate_workflows.sh index c2565140d3..c98fa3028a 100755 --- a/workflow/generate_workflows.sh +++ b/workflow/generate_workflows.sh @@ -145,15 +145,18 @@ while [[ $# -gt 0 && "$1" != "--" ]]; do t) _tag="_${OPTARG}" ;; v) _verbose=true ;; V) _very_verbose=true && _verbose=true && _verbose_flag="-v" ;; + A) _set_account=true && _hpc_account="${OPTARG}" ;; d) _debug=true && _very_verbose=true && _verbose=true && _verbose_flag="-v" && PS4='${LINENO}: ' ;; h) _usage && exit 0 ;; :) 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 99c9c3becf..487fe04f3b 100644 --- a/workflow/hosts/awspw.yaml +++ b/workflow/hosts/awspw.yaml @@ -25,4 +25,8 @@ LOCALARCH: 'NO' ATARDIR: '' # TODO: This will not yet work from AWS. MAKE_NSSTBUFR: 'NO' MAKE_ACFTBUFR: 'NO' -SUPPORTED_RESOLUTIONS: ['C48', 'C96', 'C768'] # TODO: Test and support all cubed-sphere resolutions. +DO_TRACKER: 'NO' +DO_GENESIS: 'NO' +DO_METP: 'NO' +SUPPORT_WAVES: 'NO' +SUPPORTED_RESOLUTIONS: ['C48', 'C96', 'C192', 'C384', 'C768'] # 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/rocoto/gefs_tasks.py b/workflow/rocoto/gefs_tasks.py index e9338c90df..ca29bcdf1e 100644 --- a/workflow/rocoto/gefs_tasks.py +++ b/workflow/rocoto/gefs_tasks.py @@ -1,7 +1,6 @@ from applications.applications import AppConfig from rocoto.tasks import Tasks import rocoto.rocoto as rocoto -from datetime import datetime, timedelta class GEFSTasks(Tasks): @@ -44,10 +43,6 @@ def waveinit(self): return task def prep_emissions(self): - deps = [] - dep_dict = {'type': 'task', 'name': f'gefs_stage_ic'} - deps.append(rocoto.add_dependency(dep_dict)) - dependencies = rocoto.create_dependency(dep=deps) resources = self.get_resource('prep_emissions') task_name = 'gefs_prep_emissions' @@ -69,17 +64,17 @@ def fcst(self): dep_dict = {'type': 'task', 'name': f'gefs_stage_ic'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave: + if self.options['do_wave']: dep_dict = {'type': 'task', 'name': f'gefs_wave_init'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_aero: + if self.options['do_aero_fcst']: dep_dict = {'type': 'task', 'name': f'gefs_prep_emissions'} dependencies.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=dependencies) - num_fcst_segments = len(self.app_config.fcst_segments) - 1 + num_fcst_segments = len(self.options['fcst_segments']) - 1 fcst_vars = self.envars.copy() fcst_envars_dict = {'FCST_SEGMENT': '#seg#'} @@ -115,17 +110,17 @@ def efcs(self): dep_dict = {'type': 'task', 'name': f'gefs_stage_ic'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave: + if self.options['do_wave']: dep_dict = {'type': 'task', 'name': f'gefs_wave_init'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_aero: + if self.options['do_aero_fcst']: dep_dict = {'type': 'task', 'name': f'gefs_prep_emissions'} dependencies.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=dependencies) - num_fcst_segments = len(self.app_config.fcst_segments) - 1 + num_fcst_segments = len(self.options['fcst_segments']) - 1 resources = self.get_resource('efcs') # Kludge to work around bug in rocoto with serial metatasks nested @@ -321,13 +316,14 @@ def wavepostsbs(self): wave_post_envars = self.envars.copy() postenvar_dict = {'ENSMEM': '#member#', 'MEMDIR': 'mem#member#', + 'FHR3': '#fhr#', } for key, value in postenvar_dict.items(): wave_post_envars.append(rocoto.create_envar(name=key, value=str(value))) resources = self.get_resource('wavepostsbs') - task_name = f'gefs_wave_post_grid_mem#member#' + task_name = f'gefs_wave_post_grid_mem#member#_f#fhr#' task_dict = {'task_name': task_name, 'resources': resources, 'dependency': dependencies, @@ -339,11 +335,21 @@ def wavepostsbs(self): 'maxtries': '&MAXTRIES;' } - member_var_dict = {'member': ' '.join([str(mem).zfill(3) for mem in range(0, self.nmem + 1)])} - member_metatask_dict = {'task_name': 'gefs_wave_post_grid', - 'task_dict': task_dict, - 'var_dict': member_var_dict - } + fhrs = self._get_forecast_hours('gefs', self._configs['wavepostsbs'], 'wave') + is_replay = self._configs['wavepostsbs']['REPLAY_ICS'] + if is_replay: + fhrs = [fhr for fhr in fhrs if fhr not in [0, 1, 2]] + + fhr_var_dict = {'fhr': ' '.join([f"{fhr:03d}" for fhr in fhrs])} + + fhr_metatask_dict = {'task_name': f'gefs_wave_post_grid_#member#', + 'task_dict': task_dict, + 'var_dict': fhr_var_dict} + + member_var_dict = {'member': ' '.join([f"{mem:03d}" for mem in range(0, self.nmem + 1)])} + member_metatask_dict = {'task_name': f'gefs_wave_post_grid', + 'task_dict': fhr_metatask_dict, + 'var_dict': member_var_dict} task = rocoto.create_task(member_metatask_dict) @@ -434,7 +440,7 @@ def wavepostpnt(self): deps = [] dep_dict = {'type': 'metatask', 'name': f'gefs_fcst_mem#member#'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave_bnd: + if self.options['do_wave_bnd']: dep_dict = {'type': 'task', 'name': f'gefs_wave_post_bndpnt_bull_mem#member#'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -471,16 +477,16 @@ def wavepostpnt(self): def extractvars(self): deps = [] - if self.app_config.do_wave: - dep_dict = {'type': 'task', 'name': 'gefs_wave_post_grid_mem#member#'} + if self.options['do_wave']: + dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_grid_#member#'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ocean: + if self.options['do_ocean']: dep_dict = {'type': 'metatask', 'name': 'gefs_ocean_prod_#member#'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ice: + if self.options['do_ice']: dep_dict = {'type': 'metatask', 'name': 'gefs_ice_prod_#member#'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_atm: + if self.options['do_atm']: dep_dict = {'type': 'metatask', 'name': 'gefs_atmos_prod_#member#'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -520,23 +526,23 @@ def arch(self): deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': 'gefs_atmos_ensstat'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ice: + if self.options['do_ice']: dep_dict = {'type': 'metatask', 'name': 'gefs_ice_prod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ocean: + if self.options['do_ocean']: dep_dict = {'type': 'metatask', 'name': 'gefs_ocean_prod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave: + if self.options['do_wave']: dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_grid'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_pnt'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave_bnd: + if self.options['do_wave_bnd']: dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_bndpnt'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_bndpnt_bull'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_extractvars: + if self.options['do_extractvars']: dep_dict = {'type': 'metatask', 'name': 'gefs_extractvars'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep=deps, dep_condition='and') @@ -560,7 +566,7 @@ def arch(self): def cleanup(self): deps = [] - if self.app_config.do_extractvars: + if self.options['do_extractvars']: dep_dict = {'type': 'task', 'name': 'arch'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep=deps) @@ -569,18 +575,18 @@ def cleanup(self): deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': 'gefs_atmos_ensstat'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ice: + if self.options['do_ice']: dep_dict = {'type': 'metatask', 'name': 'gefs_ice_prod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ocean: + if self.options['do_ocean']: dep_dict = {'type': 'metatask', 'name': 'gefs_ocean_prod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave: + if self.options['do_wave']: dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_grid'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_pnt'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave_bnd: + if self.options['do_wave_bnd']: dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_bndpnt'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'metatask', 'name': 'gefs_wave_post_bndpnt_bull'} diff --git a/workflow/rocoto/gefs_xml.py b/workflow/rocoto/gefs_xml.py index a5dfd5140e..f0ea407e34 100644 --- a/workflow/rocoto/gefs_xml.py +++ b/workflow/rocoto/gefs_xml.py @@ -16,7 +16,7 @@ def __init__(self, app_config: AppConfig, rocoto_config: Dict) -> None: def get_cycledefs(self): sdate = self._base['SDATE_GFS'] edate = self._base['EDATE'] - interval = self._app_config.interval_gfs + interval = self._base['interval_gfs'] sdate_str = sdate.strftime("%Y%m%d%H%M") edate_str = edate.strftime("%Y%m%d%H%M") interval_str = timedelta_to_HMS(interval) diff --git a/workflow/rocoto/gfs_cycled_xml.py b/workflow/rocoto/gfs_cycled_xml.py index eef77ba7fc..dfeefd1402 100644 --- a/workflow/rocoto/gfs_cycled_xml.py +++ b/workflow/rocoto/gfs_cycled_xml.py @@ -24,7 +24,7 @@ def get_cycledefs(self): sdate_str = sdate.strftime("%Y%m%d%H%M") strings.append(f'\t{sdate_str} {edate_str} {interval_str}') - interval_gfs = self._app_config.interval_gfs + interval_gfs = self._base['interval_gfs'] if interval_gfs > to_timedelta("0H"): sdate_gfs = self._base['SDATE_GFS'] diff --git a/workflow/rocoto/gfs_forecast_only_xml.py b/workflow/rocoto/gfs_forecast_only_xml.py index a4d5b0878b..018bdfaef2 100644 --- a/workflow/rocoto/gfs_forecast_only_xml.py +++ b/workflow/rocoto/gfs_forecast_only_xml.py @@ -14,15 +14,15 @@ def __init__(self, app_config: AppConfig, rocoto_config: Dict) -> None: def get_cycledefs(self): sdate_gfs = self._base['SDATE_GFS'] edate_gfs = self._base['EDATE'] - interval_gfs = self._app_config.interval_gfs + interval_gfs = self._base['interval_gfs'] strings = [] sdate_gfs_str = sdate_gfs.strftime("%Y%m%d%H%M") edate_gfs_str = edate_gfs.strftime("%Y%m%d%H%M") interval_gfs_str = timedelta_to_HMS(interval_gfs) strings.append(f'\t{sdate_gfs_str} {edate_gfs_str} {interval_gfs_str}') - date2 = sdate_gfs + interval_gfs - if date2 <= edate_gfs: + date2_gfs = sdate_gfs + interval_gfs + if date2_gfs <= edate_gfs: date2_gfs_str = date2_gfs.strftime("%Y%m%d%H%M") strings.append(f'\t{date2_gfs_str} {edate_gfs_str} {interval_gfs_str}') diff --git a/workflow/rocoto/gfs_tasks.py b/workflow/rocoto/gfs_tasks.py index d3bb68a6b8..535f5ce844 100644 --- a/workflow/rocoto/gfs_tasks.py +++ b/workflow/rocoto/gfs_tasks.py @@ -44,7 +44,7 @@ def prep(self): dump_path = self._template_to_rocoto_cycstring(self._base["COM_OBSDMP_TMPL"], {'DMPDIR': dmpdir, 'DUMP_SUFFIX': dump_suffix}) - gfs_enkf = True if self.app_config.do_hybvar and 'gfs' in self.app_config.eupd_runs else False + gfs_enkf = True if self.options['do_hybvar'] and 'gfs' in self.app_config.ens_runs else False deps = [] dep_dict = {'type': 'metatask', 'name': 'gdas_atmos_prod', 'offset': f"-{timedelta_to_HMS(self._base['interval_gdas'])}"} @@ -58,7 +58,7 @@ def prep(self): dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) cycledef = self.run - if self.run in ['gfs'] and gfs_enkf and self.app_config.interval_gfs != 6: + if self.run in ['gfs'] and gfs_enkf and self._base['INTERVAL_GFS'] != 6: cycledef = 'gdas' resources = self.get_resource('prep') @@ -148,9 +148,9 @@ def aerosol_init(self): # Calculate offset based on RUN = gfs | gdas interval = None if self.run in ['gfs']: - interval = self._base['INTERVAL_GFS'] + interval = self._base['interval_gfs'] elif self.run in ['gdas']: - interval = self._base['INTERVAL'] + interval = self._base['interval'] offset = timedelta_to_HMS(-interval) # Files from previous cycle @@ -187,7 +187,7 @@ def anal(self): deps = [] dep_dict = {'type': 'task', 'name': f'{self.run}_prep'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_hybvar: + if self.options['do_hybvar']: dep_dict = {'type': 'metatask', 'name': 'enkfgdas_epmn', 'offset': f"-{timedelta_to_HMS(self._base['interval_gdas'])}"} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -214,12 +214,12 @@ def anal(self): def sfcanl(self): deps = [] - if self.app_config.do_jediatmvar: + if self.options['do_jediatmvar']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmanlfinal'} else: dep_dict = {'type': 'task', 'name': f'{self.run}_anal'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_jedisnowda: + if self.options['do_jedisnowda']: dep_dict = {'type': 'task', 'name': f'{self.run}_snowanl'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -246,14 +246,14 @@ def sfcanl(self): def analcalc(self): deps = [] - if self.app_config.do_jediatmvar: + if self.options['do_jediatmvar']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmanlfinal'} else: dep_dict = {'type': 'task', 'name': f'{self.run}_anal'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'task', 'name': f'{self.run}_sfcanl'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_hybvar and self.run in ['gdas']: + if self.options['do_hybvar'] and self.run in ['gdas']: dep_dict = {'type': 'task', 'name': 'enkfgdas_echgres', 'offset': f"-{timedelta_to_HMS(self._base['interval_gdas'])}"} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -328,7 +328,7 @@ def atmanlinit(self): deps = [] dep_dict = {'type': 'task', 'name': f'{self.run}_prepatmiodaobs'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_hybvar: + if self.options['do_hybvar']: dep_dict = {'type': 'metatask', 'name': 'enkfgdas_epmn', 'offset': f"-{timedelta_to_HMS(self._base['interval_gdas'])}"} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -336,7 +336,7 @@ def atmanlinit(self): dependencies = rocoto.create_dependency(dep=deps) interval_gfs = self._base["INTERVAL_GFS"] - gfs_enkf = True if self.app_config.do_hybvar and 'gfs' in self.app_config.eupd_runs else False + gfs_enkf = True if self.options['do_hybvar'] and 'gfs' in self.app_config.ens_runs else False cycledef = self.run if self.run in ['gfs'] and gfs_enkf and interval_gfs != 6: @@ -486,7 +486,7 @@ def aeroanlinit(self): dep_dict = {'type': 'task', 'name': f'{self.run}_prep'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_prep_obs_aero: + if self.options['do_prep_obs_aero']: dep_dict = {'type': 'task', 'name': f'{self.run}_prepobsaero'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -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'])}"} @@ -796,12 +770,12 @@ def ocnanalecen(self): def marineanlchkpt(self): deps = [] - if self.app_config.do_hybvar: + if self.options['do_hybvar']: dep_dict = {'type': 'task', 'name': f'{self.run}_ocnanalecen'} else: dep_dict = {'type': 'task', 'name': f'{self.run}_marineanlvar'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_mergensst: + if self.options['do_mergensst']: data = f'&ROTDIR;/{self.run}.@Y@m@d/@H/atmos/{self.run}.t@Hz.sfcanl.nc' dep_dict = {'type': 'data', 'data': data} deps.append(rocoto.add_dependency(dep_dict)) @@ -892,18 +866,16 @@ def _fcst_forecast_only(self): dep_dict = {'type': 'task', 'name': f'{self.run}_stage_ic'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave and self.run in self.app_config.wave_runs: - wave_job = 'waveprep' if self.app_config.model_app in ['ATMW'] else 'waveinit' + if self.options['do_wave']: + wave_job = 'waveprep' if self.options['app'] in ['ATMW'] else 'waveinit' dep_dict = {'type': 'task', 'name': f'{self.run}_{wave_job}'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_aero and \ - self.run in self.app_config.aero_fcst_runs and \ - not self._base['EXP_WARM_START']: + if self.options['do_aero_fcst'] and not self._base['EXP_WARM_START']: # Calculate offset based on RUN = gfs | gdas interval = None if self.run in ['gfs']: - interval = to_timedelta(f"{self._base['INTERVAL_GFS']}H") + interval = self._base['interval_gfs'] elif self.run in ['gdas']: interval = self._base['assim_freq'] offset = timedelta_to_HMS(-interval) @@ -917,7 +889,7 @@ def _fcst_forecast_only(self): dependencies = rocoto.create_dependency(dep_condition='and', dep=dependencies) if self.run in ['gfs']: - num_fcst_segments = len(self.app_config.fcst_segments) - 1 + num_fcst_segments = len(self.options['fcst_segments']) - 1 else: num_fcst_segments = 1 @@ -956,15 +928,15 @@ def _fcst_cycled(self): dep = rocoto.add_dependency(dep_dict) dependencies = rocoto.create_dependency(dep=dep) - if self.app_config.do_jediocnvar: + if self.options['do_jediocnvar']: dep_dict = {'type': 'task', 'name': f'{self.run}_marineanlfinal'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_aero and self.run in self.app_config.aero_anl_runs: + if self.options['do_aero_anl']: dep_dict = {'type': 'task', 'name': f'{self.run}_aeroanlfinal'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_jedisnowda: + if self.options['do_jedisnowda']: dep_dict = {'type': 'task', 'name': f'{self.run}_snowanl'} dependencies.append(rocoto.add_dependency(dep_dict)) @@ -975,7 +947,7 @@ def _fcst_cycled(self): dependencies.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='or', dep=dependencies) - if self.app_config.do_wave and self.run in self.app_config.wave_runs: + if self.options['do_wave']: dep_dict = {'type': 'task', 'name': f'{self.run}_waveprep'} dependencies.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=dependencies) @@ -983,7 +955,7 @@ def _fcst_cycled(self): cycledef = 'gdas_half,gdas' if self.run in ['gdas'] else self.run if self.run in ['gfs']: - num_fcst_segments = len(self.app_config.fcst_segments) - 1 + num_fcst_segments = len(self.options['fcst_segments']) - 1 else: num_fcst_segments = 1 @@ -1200,6 +1172,7 @@ def _atmosoceaniceprod(self, component: str): fhrs.remove(0) fhr_var_dict = {'fhr': ' '.join([f"{fhr:03d}" for fhr in fhrs])} + if component in ['ocean']: fhrs_next = fhrs[1:] + [fhrs[-1] + (fhrs[-1] - fhrs[-2])] fhr_var_dict['fhr_next'] = ' '.join([f"{fhr:03d}" for fhr in fhrs_next]) @@ -1212,17 +1185,23 @@ def _atmosoceaniceprod(self, component: str): return task def wavepostsbs(self): + deps = [] dep_dict = {'type': 'metatask', 'name': f'{self.run}_fcst'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep=deps) + wave_post_envars = self.envars.copy() + postenvar_dict = {'FHR3': '#fhr#'} + for key, value in postenvar_dict.items(): + wave_post_envars.append(rocoto.create_envar(name=key, value=str(value))) + resources = self.get_resource('wavepostsbs') - task_name = f'{self.run}_wavepostsbs' + task_name = f'{self.run}_wavepostsbs_f#fhr#' task_dict = {'task_name': task_name, 'resources': resources, 'dependency': dependencies, - 'envars': self.envars, + 'envars': wave_post_envars, 'cycledef': self.run.replace('enkf', ''), 'command': f'{self.HOMEgfs}/jobs/rocoto/wavepostsbs.sh', 'job_name': f'{self.pslot}_{task_name}_@H', @@ -1230,7 +1209,14 @@ def wavepostsbs(self): 'maxtries': '&MAXTRIES;' } - task = rocoto.create_task(task_dict) + fhrs = self._get_forecast_hours('gfs', self._configs['wavepostsbs'], 'wave') + + fhr_metatask_dict = {'fhr': ' '.join([f"{fhr:03d}" for fhr in fhrs])} + metatask_dict = {'task_name': f'{self.run}_wavepostsbs', + 'task_dict': task_dict, + 'var_dict': fhr_metatask_dict} + + task = rocoto.create_task(metatask_dict) return task @@ -1290,7 +1276,7 @@ def wavepostpnt(self): deps = [] dep_dict = {'type': 'metatask', 'name': f'{self.run}_fcst'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave_bnd: + if self.options['do_wave_bnd']: dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostbndpntbll'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) @@ -1314,7 +1300,7 @@ def wavepostpnt(self): def wavegempak(self): deps = [] - dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostsbs'} + dep_dict = {'type': 'metatask', 'name': f'{self.run}_wavepostsbs'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep=deps) @@ -1337,7 +1323,7 @@ def wavegempak(self): def waveawipsbulls(self): deps = [] - dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostsbs'} + dep_dict = {'type': 'metatask', 'name': f'{self.run}_wavepostsbs'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostpnt'} deps.append(rocoto.add_dependency(dep_dict)) @@ -1362,7 +1348,7 @@ def waveawipsbulls(self): def waveawipsgridded(self): deps = [] - dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostsbs'} + dep_dict = {'type': 'metatask', 'name': f'{self.run}_wavepostsbs'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep=deps) @@ -1860,8 +1846,8 @@ def metp(self): deps = [] dep_dict = {'type': 'task', 'name': f'{self.run}_arch'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.interval_gfs < to_timedelta('24H'): - n_lookback = self.app_config.interval_gfs // to_timedelta('6H') + if self._base["interval_gfs"] < to_timedelta("24H"): + n_lookback = self._base["interval_gfs"] // to_timedelta("6H") for lookback in range(1, n_lookback + 1): deps2 = [] dep_dict = {'type': 'taskvalid', 'name': f'{self.run}_arch', 'condition': 'not'} @@ -2268,54 +2254,54 @@ def arch(self): if self.run in ['gfs']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmanlprod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_vminmon: + if self.options['do_vminmon']: dep_dict = {'type': 'task', 'name': f'{self.run}_vminmon'} deps.append(rocoto.add_dependency(dep_dict)) elif self.run in ['gdas']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmanlprod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_fit2obs: + if self.options['do_fit2obs']: dep_dict = {'type': 'task', 'name': f'{self.run}_fit2obs'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_verfozn: + if self.options['do_verfozn']: dep_dict = {'type': 'task', 'name': f'{self.run}_verfozn'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_verfrad: + if self.options['do_verfrad']: dep_dict = {'type': 'task', 'name': f'{self.run}_verfrad'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_vminmon: + if self.options['do_vminmon']: dep_dict = {'type': 'task', 'name': f'{self.run}_vminmon'} deps.append(rocoto.add_dependency(dep_dict)) - if self.run in ['gfs'] and self.app_config.do_tracker: + if self.run in ['gfs'] and self.options['do_tracker']: dep_dict = {'type': 'task', 'name': f'{self.run}_tracker'} deps.append(rocoto.add_dependency(dep_dict)) - if self.run in ['gfs'] and self.app_config.do_genesis: + if self.run in ['gfs'] and self.options['do_genesis']: dep_dict = {'type': 'task', 'name': f'{self.run}_genesis'} deps.append(rocoto.add_dependency(dep_dict)) - if self.run in ['gfs'] and self.app_config.do_genesis_fsu: + if self.run in ['gfs'] and self.options['do_genesis_fsu']: dep_dict = {'type': 'task', 'name': f'{self.run}_genesis_fsu'} deps.append(rocoto.add_dependency(dep_dict)) # Post job dependencies dep_dict = {'type': 'metatask', 'name': f'{self.run}_atmos_prod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave: - dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostsbs'} + if self.options['do_wave']: + dep_dict = {'type': 'metatask', 'name': f'{self.run}_wavepostsbs'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostpnt'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_wave_bnd: + if self.options['do_wave_bnd']: dep_dict = {'type': 'task', 'name': f'{self.run}_wavepostbndpnt'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ocean: + if self.options['do_ocean']: if self.run in ['gfs']: dep_dict = {'type': 'metatask', 'name': f'{self.run}_ocean_prod'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_ice: + if self.options['do_ice']: if self.run in ['gfs']: dep_dict = {'type': 'metatask', 'name': f'{self.run}_ice_prod'} deps.append(rocoto.add_dependency(dep_dict)) # MOS job dependencies - if self.run in ['gfs'] and self.app_config.do_mos: + if self.run in ['gfs'] and self.options['do_mos']: mos_jobs = ["stn_prep", "grd_prep", "ext_stn_prep", "ext_grd_prep", "stn_fcst", "grd_fcst", "ext_stn_fcst", "ext_grd_fcst", "stn_prdgen", "grd_prdgen", "ext_stn_prdgen", "ext_grd_prdgen", @@ -2353,7 +2339,7 @@ def cleanup(self): dep_dict = {'type': 'task', 'name': f'{self.run}_arch'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_gempak: + if self.options['do_gempak']: if self.run in ['gdas']: dep_dict = {'type': 'task', 'name': f'{self.run}_gempakmetancdc'} deps.append(rocoto.add_dependency(dep_dict)) @@ -2362,13 +2348,13 @@ def cleanup(self): deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'task', 'name': f'{self.run}_gempakncdcupapgif'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_goes: + if self.options['do_goes']: dep_dict = {'type': 'metatask', 'name': f'{self.run}_gempakgrb2spec'} deps.append(rocoto.add_dependency(dep_dict)) dep_dict = {'type': 'task', 'name': f'{self.run}_npoess_pgrb2_0p5deg'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_metp and self.run in ['gfs']: + if self.options['do_metp'] and self.run in ['gfs']: deps2 = [] # taskvalid only handles regular tasks, so just check the first metp job exists dep_dict = {'type': 'taskvalid', 'name': f'{self.run}_metpg2g1', 'condition': 'not'} @@ -2483,7 +2469,7 @@ def ediag(self): def eupd(self): deps = [] - if self.app_config.lobsdiag_forenkf: + if self.options['lobsdiag_forenkf']: dep_dict = {'type': 'task', 'name': f'{self.run}_ediag'} else: dep_dict = {'type': 'metatask', 'name': f'{self.run}_eomg'} @@ -2614,7 +2600,7 @@ def atmensanlletkf(self): def atmensanlfv3inc(self): deps = [] - if self.app_config.lobsdiag_forenkf: + if self.options['lobsdiag_forenkf']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmensanlsol'} else: dep_dict = {'type': 'task', 'name': f'{self.run}_atmensanlletkf'} @@ -2692,7 +2678,7 @@ def _get_ecengroups(): deps = [] dep_dict = {'type': 'task', 'name': f'{self.run.replace("enkf","")}_analcalc'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_jediatmens: + if self.options['do_jediatmens']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmensanlfinal'} else: dep_dict = {'type': 'task', 'name': f'{self.run}_eupd'} @@ -2733,17 +2719,15 @@ def _get_ecengroups(): def esfc(self): - # eupd_run = 'gdas' if 'gdas' in self.app_config.eupd_runs else 'gfs' - deps = [] dep_dict = {'type': 'task', 'name': f'{self.run.replace("enkf","")}_analcalc'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_jediatmens: + if self.options['do_jediatmens']: dep_dict = {'type': 'task', 'name': f'{self.run}_atmensanlfinal'} else: dep_dict = {'type': 'task', 'name': f'{self.run}_eupd'} deps.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_jedisnowda: + if self.options['do_jedisnowda']: dep_dict = {'type': 'task', 'name': f'{self.run}_esnowrecen'} deps.append(rocoto.add_dependency(dep_dict)) dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) diff --git a/workflow/rocoto/tasks.py b/workflow/rocoto/tasks.py index b989def13f..c0496a4996 100644 --- a/workflow/rocoto/tasks.py +++ b/workflow/rocoto/tasks.py @@ -10,7 +10,7 @@ class Tasks: - SERVICE_TASKS = ['arch', 'earc'] + SERVICE_TASKS = ['arch', 'earc', 'stage_ic', 'cleanup'] VALID_TASKS = ['aerosol_init', 'stage_ic', 'prep', 'anal', 'sfcanl', 'analcalc', 'analdiag', 'arch', "cleanup", 'prepatmiodaobs', 'atmanlinit', 'atmanlvar', 'atmanlfv3inc', 'atmanlfinal', @@ -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', @@ -44,6 +44,9 @@ def __init__(self, app_config: AppConfig, run: str) -> None: # Get the configs for the specified RUN self._configs = self.app_config.configs[run] + # Get the workflow options for the specified RUN + self.options = self.app_config.run_options[run] + # Update the base config for the application self._configs['base'] = self.app_config._update_base(self._configs['base']) @@ -149,6 +152,12 @@ def _get_forecast_hours(run, config, component='atmos') -> List[str]: local_config['FHOUT_GFS'] = config['FHOUT_ICE_GFS'] local_config['FHOUT'] = config['FHOUT_ICE'] + if component in ['wave']: + local_config['FHOUT_HF_GFS'] = config['FHOUT_HF_WAV'] + local_config['FHMAX_HF_GFS'] = config['FHMAX_HF_WAV'] + local_config['FHOUT_GFS'] = config['FHOUT_WAV'] + local_config['FHOUT'] = config['FHOUT_WAV'] + fhmin = local_config['FHMIN'] # Get a list of all forecast hours @@ -160,8 +169,8 @@ def _get_forecast_hours(run, config, component='atmos') -> List[str]: elif run in ['gfs', 'gefs']: fhmax = local_config['FHMAX_GFS'] fhout = local_config['FHOUT_GFS'] - fhmax_hf = local_config['FHMAX_HF_GFS'] fhout_hf = local_config['FHOUT_HF_GFS'] + fhmax_hf = local_config['FHMAX_HF_GFS'] fhrs_hf = range(fhmin, fhmax_hf + fhout_hf, fhout_hf) fhrs = list(fhrs_hf) + list(range(fhrs_hf[-1] + fhout, fhmax + fhout, fhout)) @@ -245,6 +254,6 @@ def get_task(self, task_name, *args, **kwargs): try: return getattr(self, task_name, *args, **kwargs)() except AttributeError: - raise AttributeError(f'"{task_name}" is not a valid task.\n' + - 'Valid tasks are:\n' + + raise AttributeError(f'"{task_name}" is not a valid task.\n' + f'Valid tasks are:\n' f'{", ".join(Tasks.VALID_TASKS)}') diff --git a/workflow/rocoto/workflow_xml.py b/workflow/rocoto/workflow_xml.py index 3ad7c4bd91..bed19ad5ee 100644 --- a/workflow/rocoto/workflow_xml.py +++ b/workflow/rocoto/workflow_xml.py @@ -7,6 +7,7 @@ from typing import Dict from applications.applications import AppConfig from rocoto.workflow_tasks import get_wf_tasks +from wxflow import to_timedelta import rocoto.rocoto as rocoto from abc import ABC, abstractmethod @@ -18,8 +19,10 @@ def __init__(self, app_config: AppConfig, rocoto_config: Dict) -> None: self._app_config = app_config self.rocoto_config = rocoto_config - # Use the generic config.base (without RUN specified) - self._base = self._app_config.configs['_no_run']['base'] + # Use the first config.base (sourced with an arbitrary RUN) + self._base = self._app_config.configs[next(iter(self._app_config.configs))]['base'] + self._base['interval_gdas'] = to_timedelta(f'{self._base["assim_freq"]}H') + self._base['interval_gfs'] = to_timedelta(f'{self._base["INTERVAL_GFS"]}H') self.preamble = self._get_preamble() self.definitions = self._get_definitions() diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index f32203e600..574dc0d91a 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 @@ -214,6 +235,7 @@ def _common_args(parser): parser.add_argument('--idate', help='starting date of experiment, initial conditions must exist!', required=True, type=lambda dd: to_datetime(dd)) parser.add_argument('--edate', help='end date experiment', required=False, type=lambda dd: to_datetime(dd)) + parser.add_argument('--account', help='HPC account to use; default is host-dependent', required=False, default=os.getenv('HPC_ACCOUNT')) parser.add_argument('--interval', help='frequency of forecast (in hours); must be a multiple of 6', type=_validate_interval, required=False, default=6) parser.add_argument('--icsdir', help='full path to user initial condition directory', type=str, required=False, default='') parser.add_argument('--overwrite', help='overwrite previously created experiment (if it exists)', @@ -350,6 +372,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 +382,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): """ @@ -382,6 +408,10 @@ def main(*argv): validate_user_request(host, user_inputs) + # Update the default host account if the user supplied one + if user_inputs.account is not None: + host.info.ACCOUNT = user_inputs.account + # Determine ocean resolution if not provided if user_inputs.resdetocean <= 0: user_inputs.resdetocean = get_ocean_resolution(user_inputs.resdetatmos) @@ -397,7 +427,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) diff --git a/workflow/wxflow b/workflow/wxflow new file mode 120000 index 0000000000..7ea96a12bf --- /dev/null +++ b/workflow/wxflow @@ -0,0 +1 @@ +../sorc/wxflow/src/wxflow \ No newline at end of file