diff --git a/ci/scripts/utils/ci_utils.sh b/ci/scripts/utils/ci_utils.sh index d49acb5762..2a51467d38 100755 --- a/ci/scripts/utils/ci_utils.sh +++ b/ci/scripts/utils/ci_utils.sh @@ -122,7 +122,8 @@ function create_experiment () { source "${HOMEgfs}/ci/platforms/config.${MACHINE_ID}" source "${HOMEgfs}/workflow/gw_setup.sh" - # system=$(grep "system:" "${yaml_config}" | cut -d":" -f2 | tr -d " ") || true + # Remove RUNDIRS dir incase this is a retry + rm -Rf "${STMP}/RUNDIRS/${pslot}" "${HOMEgfs}/${system}/workflow/create_experiment.py" --overwrite --yaml "${yaml_config}" diff --git a/jobs/JGDAS_ENKF_ARCHIVE b/jobs/JGDAS_ENKF_ARCHIVE index 07e171d668..5ac46a73e7 100755 --- a/jobs/JGDAS_ENKF_ARCHIVE +++ b/jobs/JGDAS_ENKF_ARCHIVE @@ -18,7 +18,7 @@ MEMDIR="ensstat" YMD=${PDY} HH=${cyc} declare_from_tmpl -rx \ # Run archive script ############################################################### -"${SCRgfs}/exgdas_enkf_earc.sh" +"${SCRgfs}/exgdas_enkf_earc.py" status=$? [[ ${status} -ne 0 ]] && exit "${status}" diff --git a/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_POST b/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_POST index ef2f07cfe5..c87dc6b34f 100755 --- a/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_POST +++ b/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_POST @@ -13,9 +13,10 @@ export CDATE=${CDATE:-${PDY}${cyc}} export GDUMP=${GDUMP:-"gdas"} # Generate COM variables from templates -YMD=${PDY} HH=${cyc} declare_from_tmpl -rx COM_OCEAN_ANALYSIS COM_ICE_RESTART +YMD=${PDY} HH=${cyc} declare_from_tmpl -rx COM_OCEAN_ANALYSIS COM_ICE_ANALYSIS COM_ICE_RESTART mkdir -p "${COM_OCEAN_ANALYSIS}" +mkdir -p "${COM_ICE_ANALYSIS}" mkdir -p "${COM_ICE_RESTART}" ############################################## diff --git a/jobs/JGLOBAL_ARCHIVE b/jobs/JGLOBAL_ARCHIVE index 2410b49732..cec3505000 100755 --- a/jobs/JGLOBAL_ARCHIVE +++ b/jobs/JGLOBAL_ARCHIVE @@ -17,9 +17,9 @@ YMD=${PDY} HH=${cyc} declare_from_tmpl -rx COM_ATMOS_ANALYSIS COM_ATMOS_BUFR COM COM_SNOW_ANALYSIS \ COM_ICE_HISTORY COM_ICE_INPUT COM_ICE_RESTART COM_ICE_GRIB \ COM_OBS COM_TOP \ - COM_OCEAN_HISTORY COM_OCEAN_INPUT COM_OCEAN_RESTART COM_OCEAN_GRIB COM_OCEAN_NETCDF \ + COM_OCEAN_HISTORY COM_OCEAN_RESTART COM_OCEAN_GRIB COM_OCEAN_NETCDF \ COM_OCEAN_ANALYSIS \ - COM_WAVE_GRID COM_WAVE_HISTORY COM_WAVE_STATION \ + COM_WAVE_GRID COM_WAVE_HISTORY COM_WAVE_STATION COM_WAVE_RESTART \ COM_ATMOS_OZNMON COM_ATMOS_RADMON COM_ATMOS_MINMON COM_CONF for grid in "0p25" "0p50" "1p00"; do @@ -30,7 +30,7 @@ done # Run archive script ############################################################### -${GLOBALARCHIVESH:-${SCRgfs}/exglobal_archive.sh} +${GLOBALARCHIVESH:-${SCRgfs}/exglobal_archive.py} status=$? [[ ${status} -ne 0 ]] && exit "${status}" diff --git a/jobs/JGLOBAL_FORECAST b/jobs/JGLOBAL_FORECAST index 8d91be8a57..9ae247cdca 100755 --- a/jobs/JGLOBAL_FORECAST +++ b/jobs/JGLOBAL_FORECAST @@ -36,7 +36,7 @@ declare -rx gcyc="${GDATE:8:2}" # Construct COM variables from templates (see config.com) YMD="${PDY}" HH="${cyc}" declare_from_tmpl -rx COM_ATMOS_RESTART COM_ATMOS_INPUT COM_ATMOS_ANALYSIS \ - COM_ATMOS_HISTORY COM_ATMOS_MASTER COM_TOP COM_CONF + COM_ICE_ANALYSIS COM_ATMOS_HISTORY COM_ATMOS_MASTER COM_TOP COM_CONF RUN="${rCDUMP}" YMD="${gPDY}" HH="${gcyc}" declare_from_tmpl -rx \ COM_ATMOS_RESTART_PREV:COM_ATMOS_RESTART_TMPL diff --git a/jobs/rocoto/arch.sh b/jobs/rocoto/arch.sh index d949b7d76f..083e319bf5 100755 --- a/jobs/rocoto/arch.sh +++ b/jobs/rocoto/arch.sh @@ -8,6 +8,11 @@ source "${HOMEgfs}/ush/preamble.sh" status=$? [[ ${status} -ne 0 ]] && exit "${status}" +############################################################### +# setup python path for workflow utilities and tasks +PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}${HOMEgfs}/ush/python" +export PYTHONPATH + export job="arch" export jobid="${job}.$$" diff --git a/jobs/rocoto/earc.sh b/jobs/rocoto/earc.sh index c4c7341698..4a9263b509 100755 --- a/jobs/rocoto/earc.sh +++ b/jobs/rocoto/earc.sh @@ -8,6 +8,11 @@ source "${HOMEgfs}/ush/preamble.sh" status=$? [[ ${status} -ne 0 ]] && exit "${status}" +############################################################### +# setup python path for workflow utilities and tasks +PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}${HOMEgfs}/ush/python" +export PYTHONPATH + export job="earc" export jobid="${job}.$$" @@ -16,5 +21,4 @@ export jobid="${job}.$$" "${HOMEgfs}/jobs/JGDAS_ENKF_ARCHIVE" status=$? - exit "${status}" diff --git a/modulefiles/module_base.hera.lua b/modulefiles/module_base.hera.lua index c47a1bfd70..f75cf886e7 100644 --- a/modulefiles/module_base.hera.lua +++ b/modulefiles/module_base.hera.lua @@ -41,6 +41,10 @@ load(pathJoin("metplus", (os.getenv("metplus_ver") or "None"))) load(pathJoin("py-xarray", (os.getenv("py_xarray_ver") or "None"))) setenv("WGRIB2","wgrib2") + +-- Stop gap fix for wgrib with spack-stack 1.6.0 +-- TODO Remove this when spack-stack issue #1097 is resolved +setenv("WGRIB","wgrib") setenv("UTILROOT",(os.getenv("prod_util_ROOT") or "None")) --prepend_path("MODULEPATH", pathJoin("/scratch1/NCEPDEV/global/glopara/git/prepobs/v" .. (os.getenv("prepobs_run_ver") or "None"), "modulefiles")) diff --git a/modulefiles/module_base.hercules.lua b/modulefiles/module_base.hercules.lua index 5835d013d7..998803f246 100644 --- a/modulefiles/module_base.hercules.lua +++ b/modulefiles/module_base.hercules.lua @@ -37,6 +37,10 @@ load(pathJoin("metplus", (os.getenv("metplus_ver") or "None"))) load(pathJoin("py-xarray", (os.getenv("py_xarray_ver") or "None"))) setenv("WGRIB2","wgrib2") + +-- Stop gap fix for wgrib with spack-stack 1.6.0 +-- TODO Remove this when spack-stack issue #1097 is resolved +setenv("WGRIB","wgrib") setenv("UTILROOT",(os.getenv("prod_util_ROOT") or "None")) prepend_path("MODULEPATH", pathJoin("/work/noaa/global/glopara/git/prepobs/feature-GFSv17_com_reorg_log_update/modulefiles")) diff --git a/modulefiles/module_base.jet.lua b/modulefiles/module_base.jet.lua index 31f8aa676d..e53132fd6a 100644 --- a/modulefiles/module_base.jet.lua +++ b/modulefiles/module_base.jet.lua @@ -43,6 +43,10 @@ load(pathJoin("py-xarray", (os.getenv("py_xarray_ver") or "None"))) load(pathJoin("perl", (os.getenv("perl_ver") or "None"))) setenv("WGRIB2","wgrib2") + +-- Stop gap fix for wgrib with spack-stack 1.6.0 +-- TODO Remove this when spack-stack issue #1097 is resolved +setenv("WGRIB","wgrib") setenv("UTILROOT",(os.getenv("prod_util_ROOT") or "None")) --prepend_path("MODULEPATH", pathJoin("/lfs4/HFIP/hfv3gfs/glopara/git/prepobs/v" .. (os.getenv("prepobs_run_ver") or "None"), "modulefiles")) diff --git a/modulefiles/module_base.orion.lua b/modulefiles/module_base.orion.lua index 72a480a946..4d747512db 100644 --- a/modulefiles/module_base.orion.lua +++ b/modulefiles/module_base.orion.lua @@ -38,6 +38,10 @@ load(pathJoin("metplus", (os.getenv("metplus_ver") or "None"))) load(pathJoin("py-xarray", (os.getenv("py_xarray_ver") or "None"))) setenv("WGRIB2","wgrib2") + +-- Stop gap fix for wgrib with spack-stack 1.6.0 +-- TODO Remove this when spack-stack issue #1097 is resolved +setenv("WGRIB","wgrib") setenv("UTILROOT",(os.getenv("prod_util_ROOT") or "None")) --prepend_path("MODULEPATH", pathJoin("/work/noaa/global/glopara/git/prepobs/v" .. (os.getenv("prepobs_run_ver") or "None"), "modulefiles")) diff --git a/modulefiles/module_base.s4.lua b/modulefiles/module_base.s4.lua index 6f0602c3eb..835249fb85 100644 --- a/modulefiles/module_base.s4.lua +++ b/modulefiles/module_base.s4.lua @@ -37,6 +37,10 @@ load(pathJoin("metplus", (os.getenv("metplus_ver") or "None"))) load(pathJoin("py-xarray", (os.getenv("py_xarray_ver") or "None"))) setenv("WGRIB2","wgrib2") + +-- Stop gap fix for wgrib with spack-stack 1.6.0 +-- TODO Remove this when spack-stack issue #1097 is resolved +setenv("WGRIB","wgrib") setenv("UTILROOT",(os.getenv("prod_util_ROOT") or "None")) --prepend_path("MODULEPATH", pathJoin("/data/prod/glopara/git/prepobs/v" .. (os.getenv("prepobs_run_ver") or "None"), "modulefiles")) diff --git a/parm/archive/arcdir.yaml.j2 b/parm/archive/arcdir.yaml.j2 new file mode 100644 index 0000000000..6321f6fc41 --- /dev/null +++ b/parm/archive/arcdir.yaml.j2 @@ -0,0 +1,85 @@ +{% set cycle_HH = current_cycle | strftime("%H") %} +{% set cycle_YMDH = current_cycle | to_YMDH %} +{% set cycle_YMD = current_cycle | to_YMD %} +{% set head = RUN + ".t" + cycle_HH + "z." %} +{% if RUN == "gdas" or RUN == "gfs" %} +deterministic: + mkdir: + - "{{ ARCDIR }}" + {% if RUN == "gfs" %} + - "{{ ARCDIR }}/tracker.{{ cycle_YMDH }}/{{ RUN }}" + {% endif %} + {% if FITSARC %} + {% set VFYARC = ROTDIR + "/vrfyarch" %} + - "{{ VFYARC }}/{{ RUN }}.{{ cycle_YMD }}/{{ cycle_HH }}" + {% endif %} + copy: + {% if current_cycle != SDATE and MODE == "cycled" %} + {% if DO_JEDIATMVAR %} + - ["{{ COM_ATMOS_ANALYSIS }}/{{ head }}atmstat", "{{ ARCDIR }}/atmstat.{{ RUN }}.{{ cycle_YMDH }}"] + {% else %} + - ["{{ COM_ATMOS_ANALYSIS }}/{{ head }}gsistat", "{{ ARCDIR }}/gsistat.{{ RUN }}.{{ cycle_YMDH }}"] + {% endif %} + {% if DO_JEDISNOWDA %} + - ["{{ COM_SNOW_ANALYSIS }}/{{ head }}snowstat.tgz", "{{ ARCDIR }}/snowstat.{{ RUN }}.{{ cycle_YMDH }}.tgz"] + {% endif %} + {% if AERO_ANL_CDUMP == RUN or AERO_ANL_CDUMP == "both" %} + - ["{{ COM_CHEM_ANALYSIS }}/{{ head }}aerostat", "{{ ARCDIR }}/aerostat.{{ RUN }}.{{ cycle_YMDH }}"] + {% endif %} + - ["{{ COM_ATMOS_GRIB_1p00 }}/{{ head }}pgrb2.1p00.anl", "{{ ARCDIR }}/pgbanl.{{ RUN }}.{{ cycle_YMDH }}.grib2"] + {% endif %} # Full cycle + {% if RUN == "gfs" %} + {% set fhmax, fhout = FHMAX_GFS, FHOUT_GFS %} + {% elif RUN == "gdas" %} + {% set fhmax, fhout = FHMAX, FHOUT %} + {% endif %} + {% for fhr in range(0, fhmax + fhout, fhout) %} + - ["{{ COM_ATMOS_GRIB_1p00 }}/{{ head }}pgrb2.1p00.f{{ '%03d' % fhr }}", "{{ ARCDIR }}/pgbf{{ '%02d' % fhr }}.{{ RUN }}.{{ cycle_YMDH }}.grib2"] + {% endfor %} + {% if RUN == "gfs" %} + {% if FITSARC %} + {% if FHMAX_FITS is defined %} + {% set fhmax = FHMAX_FITS %} + {% else %} + {% set fhmax = FHMAX_GFS %} + {% endif %} + {% for fhr in range(0, fhmax + 6, 6) %} + {% set sfcfile = "/" + head + "sfcf" + '%03d'|format(fhr) + ".nc" %} + {% set sigfile = "/" + head + "atmf" + '%03d'|format(fhr) + ".nc" %} + - ["{{COM_ATMOS_HISTORY}}/{{ sfcfile }}", "{{ VFYARC }}/{{ RUN }}.{{ cycle_YMD }}/{{ cycle_HH }}/{{ sfcfile }}"] + - ["{{COM_ATMOS_HISTORY}}/{{ sigfile }}", "{{ VFYARC }}/{{ RUN }}.{{ cycle_YMD }}/{{ cycle_HH }}/{{ sigfile }}"] + {% endfor %} + {% endif %} ## FITSARC + {% if path_exists(COM_ATMOS_GENESIS ~ "/storms.gfso.atcf_gen." ~ cycle_YMDH) %} + - ["{{ COM_ATMOS_GENESIS }}/storms.gfso.atcf_gen.{{ cycle_YMDH }}", "{{ ARCDIR }}/storms.gfso.atcf_gen.{{ cycle_YMDH }}"] + - ["{{ COM_ATMOS_GENESIS }}/storms.gfso.atcf_gen.altg.{{ cycle_YMDH }}", "{{ ARCDIR }}/storms.gfso.atcf_gen.altg.{{ cycle_YMDH }}"] + {% endif %} + {% if path_exists(COM_ATMOS_GENESIS ~ "/trak.gfso.atcfunix." ~ cycle_YMDH) %} + - ["{{ COM_ATMOS_GENESIS }}/trak.gfso.atcfunix.{{ cycle_YMDH }}", "{{ ARCDIR }}/trak.gfso.atcfunix.{{ cycle_YMDH }}"] + - ["{{ COM_ATMOS_GENESIS }}/trak.gfso.atcfunix.altg.{{ cycle_YMDH }}", "{{ ARCDIR }}/trak.gfso.atcfunix.altg.{{ cycle_YMDH }}"] + {% endif %} + ## Only created if tracking is on and there were systems to track + {% for basin in ["epac", "natl"] %} + {% if path_exists(COM_ATMOS_TRACK + "/" + basin) %} + - ["{{ COM_ATMOS_TRACK }}/{{ basin }}", "{{ ARCDIR }}/{{ basin }}"] + {% endif %} + {% endfor %} + {% endif %} ## RUN == "gfs" + {% if path_exists(COM_ATMOS_TRACK ~ "/atcfunix." ~ RUN ~ "." ~ cycle_YMDH) %} + - ["{{ COM_ATMOS_TRACK }}/atcfunix.{{ RUN }}.{{ cycle_YMDH }}", "{{ ARCDIR }}/atcfunix.{{ RUN }}.{{ cycle_YMDH }}"] + - ["{{ COM_ATMOS_TRACK }}/atcfunixp.{{ RUN }}.{{ cycle_YMDH }}", "{{ ARCDIR }}/atcfunixp.{{ RUN }}.{{ cycle_YMDH }}"] + {% endif %} +{% endif %} # gfs or gdas +{% if current_cycle != SDATE and (RUN == "enkfgdas" or RUN == "enkfgfs") %} +ensemble: + mkdir: + - "{{ ARCDIR }}" + copy: + {% if DO_JEDIATMENS %} + - ["{{ COM_ATMOS_ANALYSIS_ENSSTAT }}/{{ head }}atmensstat", "{{ ARCDIR }}/atmensstat.{{ RUN }}.{{ cycle_YMDH }}"] + - ["{{ COM_ATMOS_ANALYSIS_ENSSTAT }}/{{ head }}atminc.ensmean.nc", "{{ ARCDIR }}/atmensstat.{{ RUN }}.{{ cycle_YMDH }}.ensmean.nc"] + {% else %} + - ["{{ COM_ATMOS_ANALYSIS_ENSSTAT }}/{{ head }}enkfstat", "{{ ARCDIR }}/enkfstat.{{ RUN }}.{{ cycle_YMDH }}"] + - ["{{ COM_ATMOS_ANALYSIS_ENSSTAT }}/{{ head }}gsistat.ensmean", "{{ ARCDIR }}/gsistat.{{ RUN }}.{{ cycle_YMDH }}.ensmean"] + {% endif %} +{% endif %} # enkfgdas or enkfgfs diff --git a/parm/archive/chem.yaml.j2 b/parm/archive/chem.yaml.j2 new file mode 100644 index 0000000000..7796912b1a --- /dev/null +++ b/parm/archive/chem.yaml.j2 @@ -0,0 +1,7 @@ +chem: + {% set head = "gocart" %} + name: "CHEM" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/chem.tar" + required: + # TODO explicitize this set + - "{{ COM_CHEM_HISTORY | relpath(ROTDIR) }}/{{ head }}*" diff --git a/parm/archive/enkf.yaml.j2 b/parm/archive/enkf.yaml.j2 new file mode 100644 index 0000000000..10aee51a19 --- /dev/null +++ b/parm/archive/enkf.yaml.j2 @@ -0,0 +1,74 @@ +enkf: + name: "ENKF" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/{{ RUN }}.tar" + required: + {% for fhr in range(3, fhmax + 1, 3) %} + - "{{ COM_ATMOS_HISTORY_ENSSTAT | relpath(ROTDIR) }}/{{ head }}atmf{{ '%03d' % fhr }}.ensmean.nc" + - "{{ COM_ATMOS_HISTORY_ENSSTAT | relpath(ROTDIR) }}/{{ head }}sfcf{{ '%03d' % fhr }}.ensmean.nc" + {% if ENKF_SPREAD %} + - "{{ COM_ATMOS_HISTORY_ENSSTAT | relpath(ROTDIR) }}/{{ head }}atmf{{ '%03d' % fhr }}.ensspread.nc" + {% endif %} + {% endfor %} + {% for mem in range(1, nmem_ens + 1) %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}fcst_mem{{ '%03d' % mem }}.log" + {% endfor %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}epos*.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}echgres.log" + + {% if current_cycle != SDATE %} + # TODO archive ecen logs based on actual groups. Will need to emulate numpy.array_split to do so. + - "logs/{{ cycle_YMDH }}/{{ RUN }}ecen*.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}esfc.log" + + {% if not DO_JEDIATMENS %} + {% set da_files = ["enkfstat", + "gsistat.ensmean", + "cnvstat.ensmean", + "oznstat.ensmean", + "radstat.ensmean"] %} + {% else %} + {% set da_files = ["atmens.yaml", + "atminc.ensmean.nc", + "atmensstat"] %} + {% endif %} + {% for file in da_files %} + - "{{ COM_ATMOS_ANALYSIS_ENSSTAT | relpath(ROTDIR) }}/{{ head }}{{ file }}" + {% endfor %} + {% if DOIAU %} + {% for fhr in iaufhrs %} + {% if fhr == IAU_OFFSET %} + {% if do_calc_increment %} + - "{{ COM_ATMOS_ANALYSIS_ENSSTAT | relpath(ROTDIR) }}/{{ head }}atmanl.ensmean.nc" + {% endif %} # calc increment + {% else %} # fhr != IAU_OFFSET + - "{{ COM_ATMOS_ANALYSIS_ENSSTAT | relpath(ROTDIR) }}/{{ head }}atmi{{ '%03d' % fhr }}.ensmean.nc" + {% endif %} # fhr == IAU_OFFSET + {% endfor %} # IAUFHRS + {% endif %} # DOIAU + + {% if DO_JEDIATMENS %} + {% set steps = ["atmensanlinit", "atmensanlrun", "atmensanlfinal"] %} + {% else %} + {% set steps = ["eobs", "eupd"] %} + {% if lobsdiag_forenkf %} + {% do steps.append("ediag") %} + {% else %} + {% for mem in range(1, nmem_ens + 1) %} + {% do steps.append("eomg_mem{{ '%03d' % mem }}") %} + {% endfor %} + {% endif %} + {% endif %} + + {% for step in steps %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}{{ step }}.log" + {% endfor %} + {% endif %} # not the first cycle + optional: + {% if current_cycle != SDATE and DOIAU %} + {% for fhr in iaufhrs %} + {% if fhr != IAU_OFFSET %} + - "{{ COM_ATMOS_ANALYSIS_ENSSTAT | relpath(ROTDIR) }}/{{ head }}atma{{ '%03d' % fhr }}.ensmean.nc" + - "{{ COM_ATMOS_ANALYSIS_ENSSTAT | relpath(ROTDIR) }}/{{ head }}atmi{{ '%03d' % fhr }}.ensmean.nc" + {% endif %} # fhr == IAU_OFFSET + {% endfor %} # IAUFHRS + {% endif %} diff --git a/parm/archive/enkf_grp.yaml.j2 b/parm/archive/enkf_grp.yaml.j2 new file mode 100644 index 0000000000..cf7933ef0e --- /dev/null +++ b/parm/archive/enkf_grp.yaml.j2 @@ -0,0 +1,29 @@ +enkf_grp: + name: "ENKF_GRP" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/{{ RUN }}_grp{{ ENSGRP }}.tar" + required: + {% for mem in range(first_group_mem, last_group_mem + 1) %} + {% set imem = mem - first_group_mem %} + {% set COM_ATMOS_ANALYSIS_MEM = COM_ATMOS_ANALYSIS_MEM_list[imem] %} + {% set COM_ATMOS_HISTORY_MEM = COM_ATMOS_HISTORY_MEM_list[imem] %} + {% set COM_ATMOS_RESTART_MEM = COM_ATMOS_RESTART_MEM_list[imem] %} + + {% for iaufhr in iaufhrs_enkf %} + - "{{ COM_ATMOS_HISTORY_MEM | relpath(ROTDIR) }}/{{ head }}atmf{{ "%03d" % iaufhr }}.nc" + {% endfor %} + + {% if 6 in iaufhrs_enkf %} + - "{{ COM_ATMOS_HISTORY_MEM | relpath(ROTDIR) }}/{{ head }}sfcf006.nc" + {% endif %} + + {% if current_cycle != SDATE %} + {% if not lobsdiag_forenkf %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ head }}gsistat" + {% endif %} + {% if do_calc_increment %} + - "{{ COM_ATMOS_ANALYSIS_MEM | relpath(ROTDIR) }}/{{ head }}atmanl.nc" + {% else %} + - "{{ COM_ATMOS_ANALYSIS_MEM | relpath(ROTDIR) }}/{{ head }}ratminc.nc" + {% endif %} + {% endif %} + {% endfor %} # first_group_mem to last_group_mem diff --git a/parm/archive/enkf_restarta_grp.yaml.j2 b/parm/archive/enkf_restarta_grp.yaml.j2 new file mode 100644 index 0000000000..ee768a1f92 --- /dev/null +++ b/parm/archive/enkf_restarta_grp.yaml.j2 @@ -0,0 +1,48 @@ +enkf_restarta_grp: + name: "ENKF_RESTARTA_GRP" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/{{ RUN }}_restarta_grp{{ ENSGRP }}.tar" + required: + {% for mem in range(first_group_mem, last_group_mem + 1) %} + {% set imem = mem - first_group_mem %} + {% set COM_ATMOS_ANALYSIS_MEM = COM_ATMOS_ANALYSIS_MEM_list[imem] %} + {% set COM_ATMOS_HISTORY_MEM = COM_ATMOS_HISTORY_MEM_list[imem] %} + {% set COM_ATMOS_RESTART_MEM = COM_ATMOS_RESTART_MEM_list[imem] %} + + {% if not lobsdiag_forenkf %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ head }}abias" + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ head }}abias_air" + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ head }}abias_int" + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ head }}abias_pc" + {% endif %} + + {% if DOIAU_ENKF %} + {% set anl_delta = "-3H" | to_timedelta %} + {% else %} + {% set anl_delta = "0H" | to_timedelta %} + {% endif %} + {% set anl_time = current_cycle | add_to_datetime(anl_delta) %} + {% for itile in range(1, 7) %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ anl_time | to_YMD }}.{{ anl_time | strftime("%H") }}0000.sfcanl_data.tile{{ itile }}.nc" + {% endfor %} + {% if do_calc_increment %} + - "{{ COM_ATMOS_ANALYSIS_MEM | relpath(ROTDIR) }}/{{ head }}atmanl.nc" + {% else %} + - "{{ COM_ATMOS_ANALYSIS_MEM | relpath(ROTDIR) }}/{{ head }}ratminc.nc" + {% endif %} + {% endfor %} # first_group_mem to last_group_mem + optional: + {% for mem in range(first_group_mem, last_group_mem + 1) %} + {% set imem = mem - first_group_mem %} + {% set COM_ATMOS_RESTART_MEM = COM_ATMOS_RESTART_MEM_list[imem] %} + {% set COM_ATMOS_ANALYSIS_MEM = COM_ATMOS_ANALYSIS_MEM_list[imem] %} + {% if not lobsdiag_forenkf and not DO_JEDIATMENS %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ head }}cnvstat" + {% endif %} + {% for iaufhr in iaufhrs if iaufhr != 6 %} + {% set iaufhr = iaufhr %} + {% if do_calc_increment %} + - "{{ COM_ATMOS_ANALYSIS_MEM | relpath(ROTDIR) }}/{{ head }}atma{{ '%03d' % iaufhr }}.nc" + - "{{ COM_ATMOS_ANALYSIS_MEM | relpath(ROTDIR) }}/{{ head }}ratmi{{ '%03d' % iaufhr }}.nc" + {% endif %} + {% endfor %} # iaufhr in iaufhrs + {% endfor %} diff --git a/parm/archive/enkf_restartb_grp.yaml.j2 b/parm/archive/enkf_restartb_grp.yaml.j2 new file mode 100644 index 0000000000..34fde9d7ca --- /dev/null +++ b/parm/archive/enkf_restartb_grp.yaml.j2 @@ -0,0 +1,37 @@ +enkf_restartb_grp: + name: "ENKF_RESTARTB_GRP" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/{{ RUN }}_restartb_grp{{ ENSGRP }}.tar" + required: + {% for mem in range(first_group_mem, last_group_mem + 1) %} + {% set imem = mem - first_group_mem %} + {% set COM_ATMOS_RESTART_MEM = COM_ATMOS_RESTART_MEM_list[imem] %} + + # Grab surface analysis data. + # If IAU is on, grab the beginning of the window, otherwise grab the center. + {% if DOIAU_ENKF %} + {% set offset_td = "-3H" | to_timedelta %} + {% else %} + {% set offset_td = "0H" | to_timedelta %} + {% endif %} + {% set offset_dt = current_cycle | add_to_datetime(offset_td) %} + {% set offset_YMD = offset_dt | to_YMD %} + {% set offset_HH = offset_dt | strftime("%H") %} + {% set prefix = offset_YMD + "." + offset_HH + "0000" %} + {% for itile in range(1, 7) %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ prefix }}.sfcanl_data.tile{{ itile }}.nc" + {% endfor %} + + # Now get the restart files. + {% for r_time in range(restart_interval, fhmax + 1, restart_interval) %} + {% set r_timedelta = (r_time | string + "H") | to_timedelta %} + {% set r_dt = current_cycle | add_to_datetime(r_timedelta) %} + {% set r_prefix = r_dt | to_YMD + "." + r_dt | strftime("%H") + "0000" %} + {% for itile in range(1, 7) %} + {% for datatype in ["ca_data", "fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"] %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ r_prefix }}.{{datatype}}.tile{{ itile }}.nc" + {% endfor %} + {% endfor %} + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ r_prefix }}.coupler.res" + - "{{ COM_ATMOS_RESTART_MEM | relpath(ROTDIR) }}/{{ r_prefix }}.fv_core.res.nc" + {% endfor %} + {% endfor %} diff --git a/parm/archive/gdas.yaml.j2 b/parm/archive/gdas.yaml.j2 new file mode 100644 index 0000000000..3c7709cfac --- /dev/null +++ b/parm/archive/gdas.yaml.j2 @@ -0,0 +1,128 @@ +gdas: + {% set head = "gdas.t" + cycle_HH + "z." %} + name: "GDAS" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdas.tar" + required: + {% if current_cycle != SDATE and MODE == "cycled" %} + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.anl" + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.anl.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.anl" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.anl.idx" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmanl.nc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}sfcanl.nc" + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlprod.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}prep.log" + {% if DO_JEDIATMVAR %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}prepatmiodaobs.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlinit.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlprod.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlfinal.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlfv3inc.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlvar.log" + {% else %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}anal.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}analdiag.log" + {% endif %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlupp.log" + {% if DO_JEDIOCNVAR %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}prepoceanobs.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalprep.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalbmat.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalrun.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalpost.log" + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalchkpt.log" + {% if DOHYBVAR %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalecen.log" + {% endif %} + {% endif %} + {% if DO_VRFY_OCEANDA %} + - "logs/{{ cycle_YMDH }}/{{ RUN }}ocnanalvrfy.log" + {% endif %} + {% if DOHYBVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmanl.ensres.nc" + {% endif %} + {% if DO_JEDIATMVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmvar.yaml" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmstat" + {% else %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}gsistat" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}cnvstat" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}oznstat" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}radstat" + {% endif %} + {% if DO_VERFOZN %} + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/time/bad_cnt.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/time/bad_diag.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/time/bad_pen.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/time/stdout.time.tar.gz" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/horiz/stdout.horiz.tar.gz" + - "logs/{{ cycle_YMDH }}/{{ RUN }}verfozn.log" + {% endif %} + {% if DO_VERFRAD %} + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/radmon_angle.tar.gz" + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/radmon_bcoef.tar.gz" + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/radmon_bcor.tar.gz" + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/radmon_time.tar.gz" + - "logs/{{ cycle_YMDH }}/{{ RUN }}verfrad.log" + {% endif %} + {% if DO_VMINMON %} + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.costs.txt" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.cost_terms.txt" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.gnorms.ieee_d" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.reduction.ieee_d" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/gnorm_data.txt" + - "logs/{{ cycle_YMDH }}/{{ RUN }}vminmon.log" + {% endif %} + {% if AERO_ANL_CDUMP == "gdas" or AERO_ANL_CDUMP == "both" %} + - "{{ COM_CHEM_ANALYSIS | relpath(ROTDIR) }}/{{ head }}aerostat" + {% endif %} + {% if DO_JEDISNOWDA %} + - "{{ COM_SNOW_ANALYSIS | relpath(ROTDIR) }}/{{ head }}snowstat.tgz" + {% endif %} + {% endif %} # Full cycle + - "logs/{{ cycle_YMDH }}/{{ RUN }}fcst.log" + # TODO explicitly name the atmos_prod log files to archive + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmos_prod_f*.log" + {% for fhr in range(0, FHMAX + 1, 3) %} + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.f{{ '%03d' % fhr }}.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.f{{ '%03d' % fhr }}.idx" + - "{{ COM_ATMOS_HISTORY | relpath(ROTDIR) }}/{{ head }}atm.logf{{ '%03d' % fhr }}.txt" + - "{{ COM_ATMOS_HISTORY | relpath(ROTDIR) }}/{{ head }}atmf{{ '%03d' % fhr }}.nc" + - "{{ COM_ATMOS_HISTORY | relpath(ROTDIR) }}/{{ head }}sfcf{{ '%03d' % fhr }}.nc" + - "{{ COM_ATMOS_MASTER | relpath(ROTDIR) }}/{{ head }}sfluxgrbf{{ '%03d' % fhr }}.grib2" + - "{{ COM_ATMOS_MASTER | relpath(ROTDIR) }}/{{ head }}sfluxgrbf{{ '%03d' % fhr }}.grib2.idx" + {% endfor %} + optional: + {% if current_cycle != SDATE and MODE == "cycled" %} + {% if DOHYBVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atma003.ensres.nc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atma009.ensres.nc" + {% endif %} + {% if DO_VERFRAD %} + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/bad_diag.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/bad_pen.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/low_count.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_RADMON | relpath(ROTDIR) }}/warning.{{ cycle_YMDH }}" + {% endif %} + {% if DO_VERFOZN %} + {% set oznmon_types = [ + "gome_metop-b", "omi_aura", "ompslp_npp", "ompsnp_n20", + "ompsnp_npp", "ompstc8_n20", "ompstc8_npp", "sbuv2_n19" + ] %} + {% for group in [ "horiz", "time" ] %} + {% if group == "horiz" %} {% set suffix = ".gz" %} {% else %} {% set suffix = "" %} {% endif %} + {% for type in oznmon_types %} + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/{{ group }}/{{ type }}.anl.ctl" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/{{ group }}/{{ type }}.anl.{{ cycle_YMDH }}.ieee_d{{ suffix }}" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/{{ group }}/{{ type }}.ges.ctl" + - "{{ COM_ATMOS_OZNMON | relpath(ROTDIR) }}/{{ group }}/{{ type }}.ges.{{ cycle_YMDH }}.ieee_d{{ suffix }}" + {% endfor %} + {% endfor %} + {% endif %} + {% endif %} + {% if not WRITE_DOPOST %} + # TODO set the forecast hours explicitly. This will require emulating numpy.array_split + - "logs/{{ cycle_YMDH }}/{{ RUN }}atmos_upp_f*.log" + {% endif %} ## not WRITE_DOPOST diff --git a/parm/archive/gdas_restarta.yaml.j2 b/parm/archive/gdas_restarta.yaml.j2 new file mode 100644 index 0000000000..9c4aa1244f --- /dev/null +++ b/parm/archive/gdas_restarta.yaml.j2 @@ -0,0 +1,49 @@ +gdas_restarta: + {% set head = "gdas.t" + cycle_HH + "z." %} + name: "GDAS_RESTARTA" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdas_restarta.tar" + required: + {% if current_cycle != SDATE and MODE == "cycled" %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atminc.nc" + {% for iaufhr in iaufhrs if iaufhr != 6 %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmi{{ "%03d" % iaufhr }}.nc" + {% endfor %} + {% if DOHYBVAR and DOIAU %} + {% set anl_offset = "-3H" %} + {% else %} + {% set anl_offset = "0H" %} + {% endif %} + {% set anl_timedelta = anl_offset | to_timedelta %} + {% set anl_time = current_cycle | add_to_datetime(anl_timedelta) %} + {% for itile in range(1,7) %} + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ anl_time | to_YMD }}.{{ anl_time | strftime("%H") }}0000.sfcanl_data.tile{{ itile }}.nc" + {% endfor %} + {% if not DO_JEDIATMVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}abias" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}abias_air" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}abias_pc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}radstat" + {% endif %} + {% if DO_JEDISNOWDA %} + {% for itile in range(1,7) %} + # Snow analysis is 3dvar + - "{{ COM_SNOW_ANALYSIS | relpath(ROTDIR) }}/snowinc.{{ cycle_YMD }}.{{ cycle_HH }}0000.sfc_data.tile{{ itile }}.nc" + - "{{ COM_SNOW_ANALYSIS | relpath(ROTDIR) }}/{{ cycle_YMD }}.{{ cycle_HH }}0000.sfc_data.tile{{ itile }}.nc" + {% endfor %} + {% endif %} + {% endif %} + optional: + {% if current_cycle != SDATE and MODE == "cycled" %} + - "{{ COM_OBS | relpath(ROTDIR) }}/{{ head }}nsstbufr" + - "{{ COM_OBS | relpath(ROTDIR) }}/{{ head }}prepbufr" + - "{{ COM_OBS | relpath(ROTDIR) }}/{{ head }}prepbufr.acft_profiles" + {% if not DO_JEDIATMVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}cnvstat" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}abias_int" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}dtfanl.nc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}loginc.txt" + {% endif %} + {% if DO_JEDISNOWDA %} + - "{{ COM_CONF | relpath(ROTDIR) }}/{{ head }}letkfoi.yaml" + {% endif %} + {% endif %} diff --git a/parm/archive/gdas_restartb.yaml.j2 b/parm/archive/gdas_restartb.yaml.j2 new file mode 100644 index 0000000000..c5cb29329f --- /dev/null +++ b/parm/archive/gdas_restartb.yaml.j2 @@ -0,0 +1,39 @@ +gdas_restartb: + {% set head = "gdas.t" + cycle_HH + "z." %} + name: "GDAS_RESTARTB" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdas_restartb.tar" + required: + # Grab the surface analysis data. + # If IAU is on, grab the beginning of the window. + {% if DOIAU %} + {% set offset_td = "-3H" | to_timedelta %} + {% set offset_dt = current_cycle | add_to_datetime(offset_td) %} + {% set offset_YMD = offset_dt | to_YMD %} + {% set offset_HH = offset_dt | strftime("%H") %} + {% set prefix = offset_YMD + "." + offset_HH + "0000" %} + {% for itile in range(1, 7) %} + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ prefix }}.sfcanl_data.tile{{ itile }}.nc" + {% endfor %} + {% endif %} + + # Regardless, always grab the center surface analysis data. + {% set prefix = cycle_YMD + "." + cycle_HH + "0000" %} + {% for itile in range(1, 7) %} + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ prefix }}.sfcanl_data.tile{{ itile }}.nc" + {% endfor %} + + # Now get the restart files. + {% for r_time in range(restart_interval_gdas, FHMAX + 1, restart_interval_gdas) %} + {% set r_timedelta = (r_time | string + "H") | to_timedelta %} + {% set r_dt = current_cycle | add_to_datetime(r_timedelta) %} + {% set r_YMD = r_dt | to_YMD %} + {% set r_HH = r_dt | strftime("%H") %} + {% set r_prefix = r_YMD + "." + r_HH + "0000" %} + {% for itile in range(1, 7) %} + {% for datatype in ["ca_data", "fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"] %} + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ r_prefix }}.{{datatype}}.tile{{ itile }}.nc" + {% endfor %} + {% endfor %} + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ r_prefix }}.coupler.res" + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ r_prefix }}.fv_core.res.nc" + {% endfor %} diff --git a/parm/archive/gdasice.yaml.j2 b/parm/archive/gdasice.yaml.j2 new file mode 100644 index 0000000000..4cfa1eb9af --- /dev/null +++ b/parm/archive/gdasice.yaml.j2 @@ -0,0 +1,10 @@ +gdasice: + {% set head = "gdas.ice.t" + cycle_HH + "z." %} + name: "GDASICE" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdasice.tar" + required: + - "{{ COM_ICE_HISTORY | relpath(ROTDIR) }}/{{ head }}ic.nc" + {% for fhr in range(FHOUT_OCNICE, FHMAX+1, FHOUT_OCNICE) %} + - "{{ COM_ICE_HISTORY | relpath(ROTDIR) }}/{{ head }}inst.f{{ '%03d' % fhr }}.nc" + {% endfor %} + - '{{ COM_CONF | relpath(ROTDIR) }}/ufs.ice_in' diff --git a/parm/archive/gdasice_restart.yaml.j2 b/parm/archive/gdasice_restart.yaml.j2 new file mode 100644 index 0000000000..39877674fb --- /dev/null +++ b/parm/archive/gdasice_restart.yaml.j2 @@ -0,0 +1,7 @@ +gdasice_restart: + {% set head = "gdas.ice.t" + cycle_HH + "z." %} + name: "GDASICE_RESTART" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdasice_restart.tar" + required: + # TODO explicitly name the restart files to archive + - '{{ COM_ICE_RESTART | relpath(ROTDIR) }}/*' diff --git a/parm/archive/gdasocean.yaml.j2 b/parm/archive/gdasocean.yaml.j2 new file mode 100644 index 0000000000..9791709319 --- /dev/null +++ b/parm/archive/gdasocean.yaml.j2 @@ -0,0 +1,9 @@ +gdasocean: + {% set head = "gdas.ocean.t" + cycle_HH + "z." %} + name: "GDASOCEAN" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdasocean.tar" + required: + {% for fhr in range(FHMIN, FHMAX + 1, FHOUT_OCNICE) %} + - "{{ COM_OCEAN_HISTORY | relpath(ROTDIR) }}/{{ head }}inst.f{{ '%03d' % fhr }}.nc" + {% endfor %} + - '{{ COM_CONF | relpath(ROTDIR) }}/ufs.MOM_input' diff --git a/parm/archive/gdasocean_analysis.yaml.j2 b/parm/archive/gdasocean_analysis.yaml.j2 new file mode 100644 index 0000000000..0c43cd40ba --- /dev/null +++ b/parm/archive/gdasocean_analysis.yaml.j2 @@ -0,0 +1,27 @@ +gdasocean_analysis: + {% set head = "gdas.t" + cycle_HH + "z." %} + name: "GDASOCEAN_ANALYSIS" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdasocean_analysis.tar" + required: + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocninc.nc' + {% set ocngrid_cycle = '%02d' % (((cycle_HH | int) - 3) % 24) %} + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/gdas.t{{ ocngrid_cycle }}z.ocngrid.nc' + {% for domain in ["ocn", "ice"] %} + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}{{domain}}.bkgerr_stddev.nc' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}{{domain}}.incr.nc' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}{{domain}}ana.nc' + {% if NMEM_ENS > 2 %} + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}{{domain}}.recentering_error.nc' + {% endif %} + {% endfor %} + {% if NMEM_ENS > 2 %} + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.ssh_steric_stddev.nc' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.ssh_unbal_stddev.nc' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.ssh_total_stddev.nc' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.steric_explained_variance.nc' + {% endif %} + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.adt_rads_all.stats.csv' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.icec_amsr2_north.stats.csv' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/{{ head }}ocn.icec_amsr2_south.stats.csv' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/diags/*.nc4' + - '{{ COM_OCEAN_ANALYSIS | relpath(ROTDIR) }}/yaml/*.yaml' diff --git a/parm/archive/gdasocean_restart.yaml.j2 b/parm/archive/gdasocean_restart.yaml.j2 new file mode 100644 index 0000000000..21bfc3955f --- /dev/null +++ b/parm/archive/gdasocean_restart.yaml.j2 @@ -0,0 +1,8 @@ +gdasocean_restart: + {% set head = "gdas.ocean.t" + cycle_HH + "z." %} + name: "GDASOCEAN_RESTART" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdasocean_restart.tar" + required: + # TODO explicitly name the restart files to archive + - '{{ COM_OCEAN_RESTART | relpath(ROTDIR) }}/*' + - '{{ COM_MED_RESTART | relpath(ROTDIR) }}/*' diff --git a/parm/archive/gdaswave.yaml.j2 b/parm/archive/gdaswave.yaml.j2 new file mode 100644 index 0000000000..74a5a64dbf --- /dev/null +++ b/parm/archive/gdaswave.yaml.j2 @@ -0,0 +1,8 @@ +gdaswave: + {% set head = "gdas.wave.t" + cycle_HH + "z." %} + name: "GDASWAVE" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdaswave.tar" + required: + # TODO explicitly name the wave grid/station files to archive + - "{{ COM_WAVE_GRID | relpath(ROTDIR) }}/{{ head }}*" + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}*" diff --git a/parm/archive/gdaswave_restart.yaml.j2 b/parm/archive/gdaswave_restart.yaml.j2 new file mode 100644 index 0000000000..8387d48616 --- /dev/null +++ b/parm/archive/gdaswave_restart.yaml.j2 @@ -0,0 +1,6 @@ +gdaswave_restart: + name: "GDASWAVE_RESTART" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gdaswave_restart.tar" + required: + # TODO explicitly name the wave restart files to archive + - "{{ COM_WAVE_RESTART | relpath(ROTDIR) }}/*" diff --git a/parm/archive/gfs_downstream.yaml.j2 b/parm/archive/gfs_downstream.yaml.j2 new file mode 100644 index 0000000000..23c9383d28 --- /dev/null +++ b/parm/archive/gfs_downstream.yaml.j2 @@ -0,0 +1,12 @@ +gfs_downstream: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_DOWNSTREAM" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_downstream.tar" + required: + - "{{ COM_ATMOS_GEMPAK | relpath(ROTDIR) }}/gfs_{{ cycle_YMDH }}.sfc" + - "{{ COM_ATMOS_GEMPAK | relpath(ROTDIR) }}/gfs_{{ cycle_YMDH }}.snd" + {% for i in range(1, NUM_SND_COLLECTIVES) %} + - "{{ COM_ATMOS_WMO | relpath(ROTDIR) }}/gfs_collective{{ i }}.postsnd_{{ cycle_HH }}" + {% endfor %} + - "{{ COM_ATMOS_BUFR | relpath(ROTDIR) }}/bufr.t{{ cycle_HH }}z" + - "{{ COM_ATMOS_BUFR | relpath(ROTDIR) }}/gfs.t{{ cycle_HH }}z.bufrsnd.tar.gz" diff --git a/parm/archive/gfs_flux.yaml.j2 b/parm/archive/gfs_flux.yaml.j2 new file mode 100644 index 0000000000..66c8221f60 --- /dev/null +++ b/parm/archive/gfs_flux.yaml.j2 @@ -0,0 +1,9 @@ +gfs_flux: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_FLUX" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_flux.tar" + required: + {% for fhr in range(FHMIN_GFS, FHMAX_GFS + FHOUT_GFS, FHOUT_GFS) %} + - "{{ COM_ATMOS_MASTER | relpath(ROTDIR) }}/{{ head }}sfluxgrbf{{ '%03d' % fhr }}.grib2" + - "{{ COM_ATMOS_MASTER | relpath(ROTDIR) }}/{{ head }}sfluxgrbf{{ '%03d' % fhr }}.grib2.idx" + {% endfor %} diff --git a/parm/archive/gfs_flux_1p00.yaml.j2 b/parm/archive/gfs_flux_1p00.yaml.j2 new file mode 100644 index 0000000000..2f5c9c8910 --- /dev/null +++ b/parm/archive/gfs_flux_1p00.yaml.j2 @@ -0,0 +1,9 @@ +gfs_flux_1p00: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_FLUX_1P00" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_flux_1p00.tar" + required: + {% for fhr in range(FHMIN_GFS, FHMAX_GFS + FHOUT_GFS, FHOUT_GFS) %} + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}flux.1p00.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}flux.1p00.f{{ '%03d' % fhr }}.idx" + {% endfor %} diff --git a/parm/archive/gfs_netcdfa.yaml.j2 b/parm/archive/gfs_netcdfa.yaml.j2 new file mode 100644 index 0000000000..6bcafe1b89 --- /dev/null +++ b/parm/archive/gfs_netcdfa.yaml.j2 @@ -0,0 +1,16 @@ +gfs_netcdfa: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_NETCDFA" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_netcdfa.tar" + required: + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmanl.nc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}sfcanl.nc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atminc.nc" + {% for iauhr in iaufhrs if iauhr != 6 %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmi{{ "%03d" % iauhr }}.nc" + {% endfor %} + optional: + {% if not DO_JEDIATMVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}dtfanl.nc" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}loginc.txt" + {% endif %} diff --git a/parm/archive/gfs_netcdfb.yaml.j2 b/parm/archive/gfs_netcdfb.yaml.j2 new file mode 100644 index 0000000000..b0393d63b6 --- /dev/null +++ b/parm/archive/gfs_netcdfb.yaml.j2 @@ -0,0 +1,9 @@ +gfs_netcdfb: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_NETCDFB" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_netcdfb.tar" + required: + {% for fhr in range(0, ARCH_GAUSSIAN_FHMAX + ARCH_GAUSSIAN_FHINC, ARCH_GAUSSIAN_FHINC) %} + - "{{ COM_ATMOS_HISTORY | relpath(ROTDIR) }}/{{ head }}atmf{{ '%03d' % fhr }}.nc" + - "{{ COM_ATMOS_HISTORY | relpath(ROTDIR) }}/{{ head }}sfcf{{ '%03d' % fhr }}.nc" + {% endfor %} diff --git a/parm/archive/gfs_pgrb2b.yaml.j2 b/parm/archive/gfs_pgrb2b.yaml.j2 new file mode 100644 index 0000000000..b06dd14b73 --- /dev/null +++ b/parm/archive/gfs_pgrb2b.yaml.j2 @@ -0,0 +1,19 @@ +gfs_pgrb2b: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_PGRB2B" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_pgrb2b.tar" + required: + {% if MODE == "cycled" %} + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2b.0p25.anl" + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2b.0p25.anl.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2b.1p00.anl" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2b.1p00.anl.idx" + {% endif %} + {% if ARCH_GAUSSIAN %} + {% for fhr in range(0, FHMAX_GFS + FHOUT_GFS, FHOUT_GFS) %} + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2b.0p25.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2b.0p25.f{{ '%03d' % fhr }}.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2b.1p00.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2b.1p00.f{{ '%03d' % fhr }}.idx" + {% endfor %} + {% endif %} diff --git a/parm/archive/gfs_restarta.yaml.j2 b/parm/archive/gfs_restarta.yaml.j2 new file mode 100644 index 0000000000..c2ad717484 --- /dev/null +++ b/parm/archive/gfs_restarta.yaml.j2 @@ -0,0 +1,23 @@ +gfs_restarta: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFS_RESTARTA" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfs_restarta.tar" + required: + {% if MODE == "cycled" %} + {% if DOHYBVAR and DOIAU %} + {% set anl_offset = "-3H" %} + {% else %} + {% set anl_offset = "0H" %} + {% endif %} + {% set anl_timedelta = anl_offset | to_timedelta %} + {% set anl_time = current_cycle | add_to_datetime(anl_timedelta) %} + {% for i_tile in range(1, 7) %} + - "{{ COM_ATMOS_RESTART | relpath(ROTDIR) }}/{{ anl_time | to_YMD }}.{{ anl_time | strftime("%H") }}0000.sfcanl_data.tile{{ i_tile }}.nc" + {% endfor %} + {% elif MODE == "forecast-only" %} + - "{{ COM_ATMOS_INPUT | relpath(ROTDIR) }}/gfs_ctrl.nc" + {% for i_tile in range(1, 7) %} + - "{{ COM_ATMOS_INPUT | relpath(ROTDIR) }}/gfs_data.tile{{ i_tile }}.nc" + - "{{ COM_ATMOS_INPUT | relpath(ROTDIR) }}/sfc_data.tile{{ i_tile }}.nc" + {% endfor %} + {% endif %} diff --git a/parm/archive/gfsa.yaml.j2 b/parm/archive/gfsa.yaml.j2 new file mode 100644 index 0000000000..7ed12819a0 --- /dev/null +++ b/parm/archive/gfsa.yaml.j2 @@ -0,0 +1,62 @@ +gfsa: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFSA" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfsa.tar" + required: + # TODO explicitly name all logs to include + {% for log in glob("logs/" ~ cycle_YMDH ~ "/gfs*.log") %} + {% if not "gfsarch.log" in log %} + - "{{ log }}" + {% endif %} + {% endfor %} + # - "logs/{{ cycle_YMDH }}/{{ RUN }}fcst.log" + # - "logs/{{ cycle_YMDH }}/{{ RUN }}atmos_prod_f*.log" + - "{{ COM_CONF | relpath(ROTDIR) }}/ufs.input.nml" + {% if MODE == "cycled" %} + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.anl" + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.anl.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.anl" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.anl.idx" + # - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlprod.log" + # - "logs/{{ cycle_YMDH }}/{{ RUN }}prep.log" + # - "logs/{{ cycle_YMDH }}/{{ RUN }}anal.log" + # - "logs/{{ cycle_YMDH }}/{{ RUN }}atmanlupp.log" + {% if DO_VMINMON %} + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.costs.txt" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.cost_terms.txt" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.gnorms.ieee_d" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/{{ cycle_YMDH }}.reduction.ieee_d" + - "{{ COM_ATMOS_MINMON | relpath(ROTDIR) }}/gnorm_data.txt" + # - "logs/{{ cycle_YMDH }}/{{ RUN }}vminmon.log" + {% endif %} + {% if AERO_ANL_CDUMP == "gfs" or AERO_ANL_CDUMP == "both" %} + - "{{ COM_CHEM_ANALYSIS | relpath(ROTDIR) }}/{{ head }}aerostat" + {% endif %} + {% if DO_JEDIATMVAR %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmvar.yaml" + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}atmstat" + {% else %} + - "{{ COM_ATMOS_ANALYSIS | relpath(ROTDIR) }}/{{ head }}gsistat" + {% endif %} + {% endif %} # Full cycle + {% for fhr in range(FHMIN_GFS, FHMAX_GFS + FHOUT_GFS, FHOUT_GFS) %} + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_0p25 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p25.f{{ '%03d' % fhr }}.idx" + - "{{ COM_ATMOS_HISTORY | relpath(ROTDIR) }}/{{ head }}atm.logf{{ '%03d' % fhr }}.txt" + {% endfor %} + optional: + {% if MODE == "cycled" %} + - "{{ COM_OBS | relpath(ROTDIR) }}/{{ head }}nsstbufr" + - "{{ COM_OBS | relpath(ROTDIR) }}/{{ head }}prepbufr" + - "{{ COM_OBS | relpath(ROTDIR) }}/{{ head }}prepbufr.acft_profiles" + {% endif %} # cycled + - "{{ COM_ATMOS_TRACK | relpath(ROTDIR) }}/avno.t{{ cycle_HH }}z.cyclone.trackatcfunix" + - "{{ COM_ATMOS_TRACK | relpath(ROTDIR) }}/avnop.t{{ cycle_HH }}z.cyclone.trackatcfunix" + - "{{ COM_ATMOS_GENESIS | relpath(ROTDIR) }}/trak.gfso.atcfunix.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_GENESIS | relpath(ROTDIR) }}/trak.gfso.atcfunix.altg.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_GENESIS | relpath(ROTDIR) }}/storms.gfso.atcf_gen.{{ cycle_YMDH }}" + - "{{ COM_ATMOS_GENESIS | relpath(ROTDIR) }}/storms.gfso.atcf_gen.altg.{{ cycle_YMDH }}" + # {% if not WRITE_DOPOST %} + # # TODO set the forecast hours explicitly. This will require emulating numpy.array_split + # - "logs/{{ cycle_YMDH }}/{{ RUN }}atmos_upp_f*.log" + # {% endif %} ## not WRITE_DOPOST diff --git a/parm/archive/gfsb.yaml.j2 b/parm/archive/gfsb.yaml.j2 new file mode 100644 index 0000000000..721b529278 --- /dev/null +++ b/parm/archive/gfsb.yaml.j2 @@ -0,0 +1,17 @@ +gfsb: + {% set head = "gfs.t" + cycle_HH + "z." %} + name: "GFSB" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfsb.tar" + required: + {% if MODE == "cycled" %} + - "{{ COM_ATMOS_GRIB_0p50 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p50.anl" + - "{{ COM_ATMOS_GRIB_0p50 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p50.anl.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.anl" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.anl.idx" + {% endif %} + {% for fhr in range(FHMIN_GFS, FHMAX_GFS + FHOUT_GFS, FHOUT_GFS) %} + - "{{ COM_ATMOS_GRIB_0p50 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p50.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_0p50 | relpath(ROTDIR) }}/{{ head }}pgrb2.0p50.f{{ '%03d' % fhr }}.idx" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.f{{ '%03d' % fhr }}" + - "{{ COM_ATMOS_GRIB_1p00 | relpath(ROTDIR) }}/{{ head }}pgrb2.1p00.f{{ '%03d' % fhr }}.idx" + {% endfor %} diff --git a/parm/archive/gfswave.yaml.j2 b/parm/archive/gfswave.yaml.j2 new file mode 100644 index 0000000000..8542afac0c --- /dev/null +++ b/parm/archive/gfswave.yaml.j2 @@ -0,0 +1,23 @@ +gfswave: + {% set head = "gfswave.t" + cycle_HH + "z." %} + name: "GFSWAVE" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/gfswave.tar" + required: + {% for fh in range(0, FHMAX_HF_WAV + FHOUT_HF_WAV, FHOUT_HF_WAV) %} + # NOTE This is as explicit as possible without major logic to parse wavepostGRD. + # Matches files of the form "gfswave.tCCz...fHHH.grib2". + - "{{ COM_WAVE_GRID | relpath(ROTDIR) }}/{{ head }}*.*.f{{ '%03d' % fh }}.grib2" + - "{{ COM_WAVE_GRID | relpath(ROTDIR) }}/{{ head }}*.*.f{{ '%03d' % fh }}.grib2.idx" + {% endfor %} + {% for fh in range(FHMAX_HF_WAV + FHOUT_WAV, FHMAX_WAV_GFS + FHOUT_WAV, FHOUT_WAV) %} + - "{{ COM_WAVE_GRID | relpath(ROTDIR) }}/{{ head }}*.*.f{{ '%03d' % fh }}.grib2" + - "{{ COM_WAVE_GRID | relpath(ROTDIR) }}/{{ head }}*.*.f{{ '%03d' % fh }}.grib2.idx" + {% endfor %} + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}bull_tar" + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}cbull_tar" + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}spec_tar.gz" + {% if DOIBP_WAV %} + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}ibpbull_tar" + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}ibpcbull_tar" + - "{{ COM_WAVE_STATION | relpath(ROTDIR) }}/{{ head }}ibp_tar" + {% endif %} diff --git a/parm/archive/ice_6hravg.yaml.j2 b/parm/archive/ice_6hravg.yaml.j2 new file mode 100644 index 0000000000..251e51b110 --- /dev/null +++ b/parm/archive/ice_6hravg.yaml.j2 @@ -0,0 +1,9 @@ +ice_6hravg: + {% set head = "gfs.ice.t" + cycle_HH + "z." %} + name: "ICE_6HRAVG" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/ice_6hravg.tar" + required: + - "{{ COM_ICE_HISTORY | relpath(ROTDIR) }}/{{ head }}ic.nc" + {% for fhr in range(6, FHMAX_GFS + 6, 6) %} + - "{{ COM_ICE_HISTORY | relpath(ROTDIR) }}/{{ head }}6hr_avg.f{{ '%03d' % fhr }}.nc" + {% endfor %} diff --git a/parm/archive/ice_grib2.yaml.j2 b/parm/archive/ice_grib2.yaml.j2 new file mode 100644 index 0000000000..42e6910a16 --- /dev/null +++ b/parm/archive/ice_grib2.yaml.j2 @@ -0,0 +1,18 @@ +ice_grib2: + {% set head = "gfs.ice.t" + cycle_HH + "z." %} + name: "ICE_GRIB2" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/ice_grib2.tar" + required: + {% for fhr in range(FHOUT_OCNICE_GFS, FHMAX_GFS + FHOUT_OCNICE_GFS, FHOUT_OCNICE_GFS) %} + {% set fhr3 = '%03d' % fhr %} + {% if ICERES == 500 %} + - "{{ COM_ICE_GRIB | relpath(ROTDIR) }}/5p00/{{ head }}5p00.f{{ fhr3 }}.grib2" + - "{{ COM_ICE_GRIB | relpath(ROTDIR) }}/5p00/{{ head }}5p00.f{{ fhr3 }}.grib2.idx" + {% elif ICERES == 100 %} + - "{{ COM_ICE_GRIB | relpath(ROTDIR) }}/1p00/{{ head }}1p00.f{{ fhr3 }}.grib2" + - "{{ COM_ICE_GRIB | relpath(ROTDIR) }}/1p00/{{ head }}1p00.f{{ fhr3 }}.grib2.idx" + {% elif ICERES == 25 or ICERES == "025" %} + - "{{ COM_ICE_GRIB | relpath(ROTDIR) }}/0p25/{{ head }}0p25.f{{ fhr3 }}.grib2" + - "{{ COM_ICE_GRIB | relpath(ROTDIR) }}/0p25/{{ head }}0p25.f{{ fhr3 }}.grib2.idx" + {% endif %} + {% endfor %} diff --git a/parm/archive/master_enkf.yaml.j2 b/parm/archive/master_enkf.yaml.j2 new file mode 100644 index 0000000000..7ab7f45e30 --- /dev/null +++ b/parm/archive/master_enkf.yaml.j2 @@ -0,0 +1,100 @@ +# Set variables/lists needed to parse the enkf templates +{% set cycle_HH = current_cycle | strftime("%H") %} +{% set cycle_YMD = current_cycle | to_YMD %} +{% set cycle_YMDH = current_cycle | to_YMDH %} +{% set head = RUN + ".t" + cycle_HH + "z." %} + +{% if IAUFHRS is string %} +{% set iaufhrs = [] %} +{% for iaufhr in IAUFHRS.split(",") %} +{% do iaufhrs.append(iaufhr | int) %} +{% endfor %} +{% else %} +{% set iaufhrs = [IAUFHRS] %} +{% endif %} + +{% if IAUFHRS_ENKF is string %} +{% set iaufhrs_enkf = [] %} +{% for iaufhr in IAUFHRS_ENKF.split(",") %} +{% do iaufhrs_enkf.append(iaufhr | int) %} +{% endfor %} +{% else %} +{% set iaufhrs_enkf = [IAUFHRS_ENKF] %} +{% endif %} + +datasets: +{% if ENSGRP == 0 %} +{% filter indent(width=4) %} +{% include "enkf.yaml.j2" %} +{% endfilter %} +{% else %} + +# Declare to-be-filled lists of member COM directories +{% set COM_ATMOS_ANALYSIS_MEM_list = [] %} +{% set COM_ATMOS_RESTART_MEM_list = [] %} +{% set COM_ATMOS_HISTORY_MEM_list = [] %} + +{% set first_group_mem = (ENSGRP - 1) * NMEM_EARCGRP + 1 %} +{% set last_group_mem = [ ENSGRP * NMEM_EARCGRP, nmem_ens ] | min %} + +# Construct member COM directories +{% for mem in range(first_group_mem, last_group_mem + 1) %} + +# Declare a dict of search and replace terms to run on each template +{% set tmpl_dict = {'ROTDIR':ROTDIR, + 'RUN':RUN, + 'YMD':cycle_YMD, + 'HH':cycle_HH, + 'MEMDIR':"mem" + '%03d' % mem} %} + +# Replace template variables with tmpl_dict, one key at a time +# This must be done in a namespace to overcome jinja scoping +# Variables set inside of a for loop are lost at the end of the loop +# unless they are part of a namespace +{% set com_ns = namespace(COM_ATMOS_ANALYSIS_MEM = COM_ATMOS_ANALYSIS_TMPL, + COM_ATMOS_HISTORY_MEM = COM_ATMOS_HISTORY_TMPL, + COM_ATMOS_RESTART_MEM = COM_ATMOS_RESTART_TMPL) %} + +{% for key in tmpl_dict.keys() %} +{% set search_term = '${' + key + '}' %} +{% set replace_term = tmpl_dict[key] %} +{% set com_ns.COM_ATMOS_ANALYSIS_MEM = com_ns.COM_ATMOS_ANALYSIS_MEM.replace(search_term, replace_term) %} +{% set com_ns.COM_ATMOS_HISTORY_MEM = com_ns.COM_ATMOS_HISTORY_MEM.replace(search_term, replace_term) %} +{% set com_ns.COM_ATMOS_RESTART_MEM = com_ns.COM_ATMOS_RESTART_MEM.replace(search_term, replace_term) %} +{% endfor %} + +{% do COM_ATMOS_ANALYSIS_MEM_list.append(com_ns.COM_ATMOS_ANALYSIS_MEM)%} +{% do COM_ATMOS_HISTORY_MEM_list.append(com_ns.COM_ATMOS_HISTORY_MEM)%} +{% do COM_ATMOS_RESTART_MEM_list.append(com_ns.COM_ATMOS_RESTART_MEM)%} + +{% endfor %} + +# Determine which members to archive +{% filter indent(width=4) %} +{% include "enkf_grp.yaml.j2" %} +{% endfilter %} + +# Determine if restarts should be saved +{% set save_warm_start_forecast, save_warm_start_cycled = ( False, False ) %} + +# Save the increments and restarts every ARCH_WARMICFREQ days +# The ensemble increments (group a) should be saved on the ARCH_CYC +{% if (current_cycle - SDATE).days % ARCH_WARMICFREQ == 0 %} +{% if ARCH_CYC == cycle_HH | int and current_cycle != SDATE %} +{% filter indent(width=4) %} +{% include "enkf_restarta_grp.yaml.j2" %} +{% endfilter %} +{% endif %} +{% endif %} + +# The ensemble ICs (group b) are restarts and always lag increments by assim_freq +{% set ics_offset = (assim_freq | string + "H") | to_timedelta %} +{% if (current_cycle | add_to_datetime(ics_offset) - SDATE).days % ARCH_WARMICFREQ == 0 %} +{% if (ARCH_CYC - assim_freq) % 24 == cycle_HH | int and current_cycle != SDATE %} +{% filter indent(width=4) %} +{% include "enkf_restartb_grp.yaml.j2" %} +{% endfilter %} +{% endif %} +{% endif %} + +{% endif %} # ENSGRP != 0 diff --git a/parm/archive/master_enkfgdas.yaml.j2 b/parm/archive/master_enkfgdas.yaml.j2 new file mode 100644 index 0000000000..e21f6a381b --- /dev/null +++ b/parm/archive/master_enkfgdas.yaml.j2 @@ -0,0 +1,6 @@ +# Set variables specific to gdasenkf runs then parse the master_enkf template +{% set (fhmin, fhmax, fhout) = (FHMIN_ENKF, FHMAX_ENKF, FHOUT_ENKF) %} +{% set do_calc_increment = DO_CALC_INCREMENT %} +{% set nmem_ens = NMEM_ENS %} +{% set restart_interval = restart_interval_enkfgdas %} +{% include "master_enkf.yaml.j2" %} diff --git a/parm/archive/master_enkfgfs.yaml.j2 b/parm/archive/master_enkfgfs.yaml.j2 new file mode 100644 index 0000000000..93ec38b660 --- /dev/null +++ b/parm/archive/master_enkfgfs.yaml.j2 @@ -0,0 +1,6 @@ +# Set variables specific to gfsenkf runs then parse the master_enkf template +{% set (fhmin, fhmax, fhout) = (FHMIN_ENKF, FHMAX_ENKF_GFS, FHOUT_ENKF_GFS) %} +{% set do_calc_increment = DO_CALC_INCREMENT_ENKF_GFS %} +{% set nmem_ens = NMEM_ENS_GFS %} +{% set restart_interval = restart_interval_enkfgfs %} +{% include "master_enkf.yaml.j2" %} diff --git a/parm/archive/master_gdas.yaml.j2 b/parm/archive/master_gdas.yaml.j2 new file mode 100644 index 0000000000..6813209e6a --- /dev/null +++ b/parm/archive/master_gdas.yaml.j2 @@ -0,0 +1,87 @@ +{% set cycle_HH = current_cycle | strftime("%H") %} +{% set cycle_YMD = current_cycle | to_YMD %} +{% set cycle_YMDH = current_cycle | to_YMDH %} +{% set head = "gdas.t" + cycle_HH + "z." %} + +{% if IAUFHRS is string %} +{% set iaufhrs = [] %} +{% for iaufhr in IAUFHRS.split(",") %} +{% do iaufhrs.append(iaufhr | int) %} +{% endfor %} +{% else %} +{% set iaufhrs = [IAUFHRS] %} +{% endif %} + +datasets: +{% filter indent(width=4) %} +{% include "gdas.yaml.j2" %} +{% endfilter %} + +{% if DO_ICE %} +{% filter indent(width=4) %} +{% include "gdasice.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if DO_OCN %} +{% filter indent(width=4) %} +{% include "gdasocean.yaml.j2" %} +{% endfilter %} +{% if DO_JEDIOCNVAR and current_cycle != SDATE and MODE == "cycled" %} +{% filter indent(width=4) %} +{% include "gdasocean_analysis.yaml.j2" %} +{% endfilter %} +{% endif %} +{% endif %} + +{% if DO_WAVE %} +{% filter indent(width=4) %} +{% include "gdaswave.yaml.j2" %} +{% endfilter %} +{% endif %} + +# Determine if we will save restart ICs or not +{% set save_warm_start_forecast, save_warm_start_cycled = ( False, False ) %} + +{% if ARCH_CYC == cycle_HH | int and current_cycle != SDATE%} +# Save the warm and forecast-only cycle ICs every ARCH_WARMICFREQ days +{% if (current_cycle - SDATE).days % ARCH_WARMICFREQ == 0 %} +{% set save_warm_start_forecast = True %} +{% set save_warm_start_cycled = True %} +# Save the forecast-only restarts every ARCH_FCSTICFREQ days +{% elif (current_cycle - SDATE).days % ARCH_FCSTICFREQ == 0 %} +{% set save_warm_start_forecast = True %} +{% endif %} +{% endif %} + +{% if save_warm_start_forecast %} +{% filter indent(width=4) %} +{% include "gdas_restarta.yaml.j2" %} +{% endfilter %} + +{% if DO_WAVE %} +{% filter indent(width=4) %} +{% include "gdaswave_restart.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if DO_OCN %} +{% filter indent(width=4) %} +{% include "gdasocean_restart.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if DO_ICE %} +{% filter indent(width=4) %} +{% include "gdasice_restart.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% endif %} # Save forecast-only restarts + +# Save cycled restarts +{% if save_warm_start_cycled %} +{% filter indent(width=4) %} +{% include "gdas_restartb.yaml.j2" %} +{% endfilter %} +{% endif %} diff --git a/parm/archive/master_gfs.yaml.j2 b/parm/archive/master_gfs.yaml.j2 new file mode 100644 index 0000000000..5340ddb721 --- /dev/null +++ b/parm/archive/master_gfs.yaml.j2 @@ -0,0 +1,95 @@ +{% set cycle_HH = current_cycle | strftime("%H") %} +{% set cycle_YMD = current_cycle | to_YMD %} +{% set cycle_YMDH = current_cycle | to_YMDH %} + +{% if IAUFHRS is string %} +{% set iaufhrs = [] %} +{% for iaufhr in IAUFHRS.split(",") %} +{% do iaufhrs.append(iaufhr | int) %} +{% endfor %} +{% else %} +{% set iaufhrs = [IAUFHRS] %} +{% endif %} +datasets: +{% filter indent(width=4) %} +{% include "gfsa.yaml.j2" %} +{% include "gfsb.yaml.j2" %} +{% endfilter %} + +{% if ARCH_GAUSSIAN %} +{% filter indent(width=4) %} +{% include "gfs_flux.yaml.j2" %} +{% include "gfs_netcdfb.yaml.j2" %} +{% include "gfs_pgrb2b.yaml.j2" %} +{% endfilter %} +{% if MODE == "cycled" %} +{% filter indent(width=4) %} +{% include "gfs_netcdfa.yaml.j2" %} +{% endfilter %} +{% endif %} +{% endif %} + +{% if DO_WAVE %} +{% filter indent(width=4) %} +{% include "gfswave.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if AERO_FCST_CDUMP == "gfs" or AERO_FCST_CDUMP == "both" %} +{% filter indent(width=4) %} +{% include "chem.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if DO_BUFRSND %} +{% filter indent(width=4) %} +{% include "gfs_downstream.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if DO_OCN %} +{% filter indent(width=4) %} +{% include "ocean_6hravg.yaml.j2" %} +{% include "ocean_daily.yaml.j2" %} +{% include "ocean_grib2.yaml.j2" %} +{% include "gfs_flux_1p00.yaml.j2" %} +{% endfilter %} +{% endif %} + +{% if DO_ICE %} +{% filter indent(width=4) %} +{% include "ice_6hravg.yaml.j2" %} +{% include "ice_grib2.yaml.j2" %} +{% endfilter %} +{% endif %} + +# Determine whether to save the MOS tarball +{% if DO_MOS and cycle_HH == "18" %} +{% if not REALTIME %} +{% filter indent(width=4) %} +{% include "gfsmos.yaml.j2" %} +{% endfilter %} +{% else %} +{% set td_from_sdate = current_cycle - SDATE %} +{% set td_one_day = "+1D" | to_timedelta %} +{% if td_from_sdate > td_one_day %} +{% filter indent(width=4) %} +{% include "gfsmos.yaml.j2" %} +{% endfilter %} +{% endif %} +{% endif %} +{% endif %} + +# Determine if we will save restart ICs or not +{% if ARCH_CYC == cycle_HH | int and current_cycle != SDATE %} +# Save the forecast-only cycle ICs every ARCH_WARMICFREQ or ARCH_FCSTICFREQ days +{% if (current_cycle - SDATE).days % ARCH_WARMICFREQ == 0 %} +{% filter indent(width=4) %} +{% include "gfs_restarta.yaml.j2" %} +{% endfilter %} +{% elif (current_cycle - SDATE).days % ARCH_FCSTICFREQ == 0 %} +{% filter indent(width=4) %} +{% include "gfs_restarta.yaml.j2" %} +{% endfilter %} +{% endif %} +{% endif %} diff --git a/parm/archive/ocean_6hravg.yaml.j2 b/parm/archive/ocean_6hravg.yaml.j2 new file mode 100644 index 0000000000..dac3ce262a --- /dev/null +++ b/parm/archive/ocean_6hravg.yaml.j2 @@ -0,0 +1,8 @@ +ocean_6hravg: + {% set head = "gfs.ocean.t" + cycle_HH + "z." %} + name: "OCEAN_6HRAVG" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/ocean_6hravg.tar" + required: + {% for fhr in range(6, FHMAX_GFS + 6, 6) %} + - "{{ COM_OCEAN_HISTORY | relpath(ROTDIR) }}/{{ head }}6hr_avg.f{{ '%03d' % fhr }}.nc" + {% endfor %} diff --git a/parm/archive/ocean_daily.yaml.j2 b/parm/archive/ocean_daily.yaml.j2 new file mode 100644 index 0000000000..0f45264973 --- /dev/null +++ b/parm/archive/ocean_daily.yaml.j2 @@ -0,0 +1,8 @@ +ocean_daily: + {% set head = "gfs.ocean.t" + cycle_HH + "z." %} + name: "OCEAN_DAILY" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/ocean_daily.tar" + required: + {% for fhr in range(24, FHMAX_GFS + 24, 24) %} + - "{{ COM_OCEAN_HISTORY | relpath(ROTDIR) }}/{{ head }}daily.f{{ '%03d' % fhr }}.nc" + {% endfor %} diff --git a/parm/archive/ocean_grib2.yaml.j2 b/parm/archive/ocean_grib2.yaml.j2 new file mode 100644 index 0000000000..2e63c0ca98 --- /dev/null +++ b/parm/archive/ocean_grib2.yaml.j2 @@ -0,0 +1,18 @@ +ocean_grib2: + {% set head = "gfs.ocean.t" + cycle_HH + "z." %} + name: "OCEAN_GRIB2" + target: "{{ ATARDIR }}/{{ cycle_YMDH }}/ocean_grib2.tar" + required: + {% for fhr in range(FHOUT_OCNICE_GFS, FHMAX_GFS + FHOUT_OCNICE_GFS, FHOUT_OCNICE_GFS) %} + {% set fhr3 = '%03d' % fhr %} + {% if OCNRES == 500 %} + - "{{ COM_OCEAN_GRIB | relpath(ROTDIR) }}/5p00/{{ head }}5p00.f{{ fhr3 }}.grib2" + - "{{ COM_OCEAN_GRIB | relpath(ROTDIR) }}/5p00/{{ head }}5p00.f{{ fhr3 }}.grib2.idx" + {% elif OCNRES == 100 %} + - "{{ COM_OCEAN_GRIB | relpath(ROTDIR) }}/1p00/{{ head }}1p00.f{{ fhr3 }}.grib2" + - "{{ COM_OCEAN_GRIB | relpath(ROTDIR) }}/1p00/{{ head }}1p00.f{{ fhr3 }}.grib2.idx" + {% elif OCNRES == 25 or OCNRES == "025" %} + - "{{ COM_OCEAN_GRIB | relpath(ROTDIR) }}/0p25/{{ head }}0p25.f{{ fhr3 }}.grib2" + - "{{ COM_OCEAN_GRIB | relpath(ROTDIR) }}/0p25/{{ head }}0p25.f{{ fhr3 }}.grib2.idx" + {% endif %} + {% endfor %} diff --git a/parm/config/gefs/config.base b/parm/config/gefs/config.base index 90a75e3639..e98c942dcf 100644 --- a/parm/config/gefs/config.base +++ b/parm/config/gefs/config.base @@ -134,8 +134,11 @@ export DO_WAVE="NO" export DO_OCN="NO" export DO_ICE="NO" export DO_AERO="NO" +export AERO_FCST_CDUMP="" # When to run aerosol forecast: gdas, gfs, or both +export AERO_ANL_CDUMP="" # When to run aerosol analysis: gdas, gfs, or both export WAVE_CDUMP="" # When to include wave suite: gdas, gfs, or both 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." export DO_NEST="NO" # Whether to run a global-nested domain if [[ "${DO_NEST:-NO}" == "YES" ]] ; then @@ -179,6 +182,8 @@ case "${APP}" in ;; ATMA) export DO_AERO="YES" + export AERO_ANL_CDUMP="both" + export AERO_FCST_CDUMP="gdas" ;; ATMW) export DO_COUPLED="YES" @@ -197,6 +202,8 @@ case "${APP}" in if [[ "${APP}" =~ A$ ]]; then export DO_AERO="YES" + export AERO_ANL_CDUMP="both" + export AERO_FCST_CDUMP="gdas" fi if [[ "${APP}" =~ ^S2SW ]]; then @@ -222,6 +229,11 @@ export FHOUT_GFS=6 export FHMAX_HF_GFS=0 export FHOUT_HF_GFS=1 export FHOUT_OCNICE_GFS=6 +export FHMIN_WAV=0 +export FHOUT_WAV=3 +export FHMAX_HF_WAV=120 +export FHOUT_HF_WAV=1 +export FHMAX_WAV=${FHMAX_GFS} if (( gfs_cyc != 0 )); then export STEP_GFS=$(( 24 / gfs_cyc )) else @@ -235,6 +247,7 @@ export FHOUT_ENKF=${FHOUT_GFS} # GFS restart interval in hours export restart_interval_gfs=12 +export restart_interval_enkfgfs=12 # NOTE: Do not set this to zero. Instead set it to $FHMAX_GFS # TODO: Remove this variable from config.base and reference from config.fcst # TODO: rework logic in config.wave and push it to parsing_nameslist_WW3.sh where it is actually used @@ -313,4 +326,7 @@ export ARCH_FCSTICFREQ=1 # Archive frequency in days for gdas and gfs foreca export DELETE_COM_IN_ARCHIVE_JOB="YES" # NO=retain ROTDIR. YES default in arch.sh and earc.sh. +# Number of regional collectives to create soundings for +export NUM_SND_COLLECTIVES=${NUM_SND_COLLECTIVES:-9} + echo "END: config.base" diff --git a/parm/config/gefs/config.efcs b/parm/config/gefs/config.efcs index ad90fa864c..915726b974 100644 --- a/parm/config/gefs/config.efcs +++ b/parm/config/gefs/config.efcs @@ -78,6 +78,6 @@ if [[ "${USE_OCN_PERTURB_FILES:-false}" == "true" ]]; then else export ODA_INCUPD="False" fi -export restart_interval="${restart_interval_gfs}" +export restart_interval="${restart_interval_enkfgfs:-12}" echo "END: config.efcs" diff --git a/parm/config/gefs/config.fcst b/parm/config/gefs/config.fcst index f91316c7d2..12e461cef8 100644 --- a/parm/config/gefs/config.fcst +++ b/parm/config/gefs/config.fcst @@ -8,12 +8,18 @@ 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 CDUMP +# Turn off waves if not used for this RUN case ${WAVE_CDUMP} in - both | "${CDUMP/enkf}" ) ;; # Don't change + 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_CDUMP} 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}" diff --git a/parm/config/gefs/config.wave b/parm/config/gefs/config.wave index 38ad959eee..66f19296cb 100644 --- a/parm/config/gefs/config.wave +++ b/parm/config/gefs/config.wave @@ -79,12 +79,7 @@ export WAVEWND_DID= export WAVEWND_FID= # The start time reflects the number of hindcast hours prior to the cycle initial time -export FHMAX_WAV=${FHMAX_GFS} export WAVHINDH=0 -export FHMIN_WAV=0 -export FHOUT_WAV=3 -export FHMAX_HF_WAV=120 -export FHOUT_HF_WAV=1 export FHMAX_WAV_IBP=180 if (( FHMAX_WAV < FHMAX_WAV_IBP )); then export FHMAX_WAV_IBP=${FHMAX_GFS} ; fi diff --git a/parm/config/gefs/config.wavepostsbs b/parm/config/gefs/config.wavepostsbs index b3c5902e3c..82cec321da 100644 --- a/parm/config/gefs/config.wavepostsbs +++ b/parm/config/gefs/config.wavepostsbs @@ -13,7 +13,6 @@ export WAV_SUBGRBSRC="" export WAV_SUBGRB="" # Options for point output (switch on/off boundary point output) -export DOIBP_WAV='NO' # Input boundary points export DOFLD_WAV='YES' # Field data export DOPNT_WAV='YES' # Station data export DOGRB_WAV='YES' # Create grib2 files diff --git a/parm/config/gfs/config.base b/parm/config/gfs/config.base index 8ee1a2c17e..f1e25750ef 100644 --- a/parm/config/gfs/config.base +++ b/parm/config/gfs/config.base @@ -163,9 +163,6 @@ export APP=@APP@ shopt -s extglob # Adjust APP based on RUN case "${RUN}" in - gfs) # Turn off aerosols - APP="${APP/%A}" - ;; enkf*) # Turn off aerosols and waves APP="${APP/%+([WA])}" ;; @@ -181,8 +178,11 @@ export DO_WAVE="NO" export DO_OCN="NO" export DO_ICE="NO" export DO_AERO="NO" +export AERO_FCST_CDUMP="" # When to run aerosol forecast: gdas, gfs, or both +export AERO_ANL_CDUMP="" # When to run aerosol analysis: gdas, gfs, or both export WAVE_CDUMP="" # When to include wave suite: gdas, gfs, or both export DOBNDPNT_WAVE="NO" +export DOIBP_WAV="NO" # Option to create point outputs from input boundary points export FRAC_GRID=".true." export DO_NEST="NO" # Whether to run a global-nested domain if [[ "${DO_NEST:-NO}" == "YES" ]] ; then @@ -227,6 +227,8 @@ case "${APP}" in ;; ATMA) export DO_AERO="YES" + export AERO_ANL_CDUMP="both" + export AERO_FCST_CDUMP="gdas" ;; ATMW) export DO_COUPLED="YES" @@ -245,6 +247,8 @@ case "${APP}" in if [[ "${APP}" =~ A$ ]]; then export DO_AERO="YES" + export AERO_ANL_CDUMP="both" + export AERO_FCST_CDUMP="gdas" fi if [[ "${APP}" =~ ^S2SW ]]; then @@ -285,6 +289,12 @@ export FHOUT_GFS=3 # Must be 6 for S2S until #1629 is addressed; 3 for ops export FHMAX_HF_GFS=0 export FHOUT_HF_GFS=1 export FHOUT_OCNICE_GFS=6 +export FHMIN_WAV=0 +export FHOUT_WAV=3 +export FHMAX_HF_WAV=120 +export FHOUT_HF_WAV=1 +export FHMAX_WAV=${FHMAX:-9} +export FHMAX_WAV_GFS=${FHMAX_GFS} if (( gfs_cyc != 0 )); then export STEP_GFS=$(( 24 / gfs_cyc )) else @@ -342,6 +352,7 @@ export NMEM_ENS=@NMEM_ENS@ export SMOOTH_ENKF="NO" export l4densvar=".true." export lwrite4danl=".true." +export DO_CALC_INCREMENT="NO" # Early-cycle EnKF parameters export NMEM_ENS_GFS=30 @@ -370,6 +381,9 @@ if [[ ${DOHYBVAR} == "NO" && ${DOIAU} == "YES" ]]; then export IAUFHRS_ENKF="6" fi +# Generate post-processing ensemble spread files +export ENKF_SPREAD="YES" + # Check if cycle is cold starting, DOIAU off, or free-forecast mode if [[ "${MODE}" = "cycled" && "${SDATE}" = "${PDY}${cyc}" && ${EXP_WARM_START} = ".false." ]] || [[ "${DOIAU}" = "NO" ]] || [[ "${MODE}" = "forecast-only" && ${EXP_WARM_START} = ".false." ]] ; then export IAU_OFFSET=0 @@ -379,6 +393,22 @@ fi if [[ "${DOIAU_ENKF}" = "NO" ]]; then export IAUFHRS_ENKF="6"; fi +# Determine restart intervals +# For IAU, write restarts at beginning of window also +if [[ "${DOIAU_ENKF:-}" == "YES" ]]; then + export restart_interval_enkfgdas="3" +else + export restart_interval_enkfgdas="6" +fi + +export restart_interval_enkfgfs=${restart_interval_enkfgdas} + +if [[ "${DOIAU}" == "YES" ]]; then + export restart_interval_gdas="3" +else + export restart_interval_gdas="6" +fi + export GSI_SOILANAL=@GSI_SOILANAL@ # turned on nsst in anal and/or fcst steps, and turn off rtgsst @@ -435,4 +465,7 @@ if [[ ${DO_JEDIATMVAR} = "YES" ]]; then export DO_VMINMON="NO" # GSI minimization monitoring fi +# Number of regional collectives to create soundings for +export NUM_SND_COLLECTIVES=${NUM_SND_COLLECTIVES:-9} + echo "END: config.base" diff --git a/parm/config/gfs/config.com b/parm/config/gfs/config.com index 5d63a499ed..004ca1affb 100644 --- a/parm/config/gfs/config.com +++ b/parm/config/gfs/config.com @@ -84,6 +84,7 @@ declare -rx COM_OCEAN_NETCDF_TMPL=${COM_BASE}'/products/ocean/netcdf' declare -rx COM_OCEAN_GRIB_TMPL=${COM_BASE}'/products/ocean/grib2' declare -rx COM_OCEAN_GRIB_GRID_TMPL=${COM_OCEAN_GRIB_TMPL}'/${GRID}' +declare -rx COM_ICE_ANALYSIS_TMPL=${COM_BASE}'/analysis/ice' declare -rx COM_ICE_INPUT_TMPL=${COM_BASE}'/model_data/ice/input' declare -rx COM_ICE_HISTORY_TMPL=${COM_BASE}'/model_data/ice/history' declare -rx COM_ICE_RESTART_TMPL=${COM_BASE}'/model_data/ice/restart' diff --git a/parm/config/gfs/config.earc b/parm/config/gfs/config.earc index de73a93731..00a2fa95bd 100644 --- a/parm/config/gfs/config.earc +++ b/parm/config/gfs/config.earc @@ -8,7 +8,25 @@ echo "BEGIN: config.earc" # Get task specific resources . $EXPDIR/config.resources earc -export NMEM_EARCGRP=10 +# Set the number of ensemble members to archive per earc job +case "${CASE_ENS}" in + "C48" | "C96") + export NMEM_EARCGRP=80 + ;; + "C192") + export NMEM_EARCGRP=20 + ;; + "C384" | "C768") + export NMEM_EARCGRP=10 + ;; + "C1152") + export NMEM_EARCGRP=4 + ;; + *) + echo "FATAL ERROR: Unknown ensemble resolution ${CASE_ENS}, ABORT!" + exit 1 + ;; +esac #--starting and ending hours of previous cycles to be removed from rotating directory export RMOLDSTD_ENKF=144 diff --git a/parm/config/gfs/config.efcs b/parm/config/gfs/config.efcs index 402ba64087..1837cf0619 100644 --- a/parm/config/gfs/config.efcs +++ b/parm/config/gfs/config.efcs @@ -85,11 +85,10 @@ if [[ ${RECENTER_ENKF:-"YES"} == "YES" ]]; then export PREFIX_ATMINC="r" fi -# For IAU, write restarts at beginning of window also -if [[ "${DOIAU_ENKF:-}" == "YES" ]]; then - export restart_interval="3" -else - export restart_interval="6" +# Set restart interval to enable restarting forecasts after failures +export restart_interval=${restart_interval_enkfgdas:-6} +if [[ ${RUN} == "enkfgfs" ]]; then + export restart_interval=${restart_interval_enkfgfs:-12} fi echo "END: config.efcs" diff --git a/parm/config/gfs/config.epos b/parm/config/gfs/config.epos index 8026a2ba2e..f1da929b62 100644 --- a/parm/config/gfs/config.epos +++ b/parm/config/gfs/config.epos @@ -14,7 +14,4 @@ if [ $l4densvar = ".false." ]; then export NEPOSGRP=3 fi -# Generate ensemble spread files -export ENKF_SPREAD="YES" - echo "END: config.epos" diff --git a/parm/config/gfs/config.fcst b/parm/config/gfs/config.fcst index db7306d2e8..f45874a31a 100644 --- a/parm/config/gfs/config.fcst +++ b/parm/config/gfs/config.fcst @@ -8,12 +8,18 @@ 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 CDUMP +# Turn off waves if not used for this RUN case ${WAVE_CDUMP} in - both | "${CDUMP/enkf}" ) ;; # Don't change + 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_CDUMP} 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}" @@ -271,11 +277,8 @@ if [[ "${CDUMP}" =~ "gdas" ]] ; then # GDAS cycle specific parameters # Variables used in DA cycling export DIAG_TABLE="${PARMgfs}/ufs/fv3/diag_table_da" - if [[ "${DOIAU}" == "YES" ]]; then - export restart_interval="3" - else - export restart_interval="6" - fi + # Write gfs restart files to rerun fcst from any break point + export restart_interval=${restart_interval_gdas:-6} # Turn on dry mass adjustment in GDAS export adjust_dry_mass=".true." diff --git a/parm/config/gfs/config.wave b/parm/config/gfs/config.wave index 5efceeeacf..3c643c7996 100644 --- a/parm/config/gfs/config.wave +++ b/parm/config/gfs/config.wave @@ -91,16 +91,10 @@ export WAVEWND_DID= export WAVEWND_FID= # The start time reflects the number of hindcast hours prior to the cycle initial time -if [[ "${CDUMP}" = "gdas" ]]; then - export FHMAX_WAV=${FHMAX:-9} -else - export FHMAX_WAV=${FHMAX_GFS} +if [[ "${RUN}" == "gfs" ]]; then + export FHMAX_WAV=${FHMAX_WAV_GFS} fi export WAVHINDH=0 -export FHMIN_WAV=0 -export FHOUT_WAV=3 -export FHMAX_HF_WAV=120 -export FHOUT_HF_WAV=1 export FHMAX_WAV_IBP=180 if (( FHMAX_WAV < FHMAX_WAV_IBP )); then export FHMAX_WAV_IBP=${FHMAX_GFS} ; fi @@ -113,7 +107,7 @@ export FHINCP_WAV=$(( DTPNT_WAV / 3600 )) export OUTPARS_WAV="WND CUR ICE HS T01 T02 DIR FP DP PHS PTP PDIR CHA" # Restart file config -if [[ "${CDUMP}" = "gdas" ]]; then +if [[ "${RUN}" == "gdas" ]]; then export WAVNCYC=4 export WAVHCYC=${assim_freq:-6} export FHMAX_WAV_CUR=48 # RTOFS forecasts only out to 8 days @@ -128,7 +122,7 @@ fi # Restart timing business export RSTTYPE_WAV='T' # generate second tier of restart files -if [[ "${CDUMP}" != gfs ]]; then # Setting is valid for GDAS and GEFS +if [[ "${RUN}" != gfs ]]; then # Setting is valid for GDAS and GEFS export DT_1_RST_WAV=10800 # time between restart files, set to DTRST=1 for a single restart file export DT_2_RST_WAV=43200 # restart stride for checkpointing restart export RSTIOFF_WAV=0 # first restart file offset relative to model start diff --git a/parm/config/gfs/config.wavepostsbs b/parm/config/gfs/config.wavepostsbs index b3c5902e3c..82cec321da 100644 --- a/parm/config/gfs/config.wavepostsbs +++ b/parm/config/gfs/config.wavepostsbs @@ -13,7 +13,6 @@ export WAV_SUBGRBSRC="" export WAV_SUBGRB="" # Options for point output (switch on/off boundary point output) -export DOIBP_WAV='NO' # Input boundary points export DOFLD_WAV='YES' # Field data export DOPNT_WAV='YES' # Station data export DOGRB_WAV='YES' # Create grib2 files diff --git a/scripts/exgdas_enkf_earc.py b/scripts/exgdas_enkf_earc.py new file mode 100755 index 0000000000..2febbc27f5 --- /dev/null +++ b/scripts/exgdas_enkf_earc.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import os + +from pygfs.task.archive import Archive +from wxflow import AttrDict, Logger, cast_strdict_as_dtypedict, chdir, logit + +# initialize root logger +logger = Logger(level=os.environ.get("LOGGING_LEVEL", "DEBUG"), colored_log=True) + + +@logit(logger) +def main(): + + config = cast_strdict_as_dtypedict(os.environ) + + # Instantiate the Archive object + archive = Archive(config) + + # Pull out all the configuration keys needed to run the rest of archive steps + keys = ['ATARDIR', 'current_cycle', 'IAUFHRS', 'RUN', 'PDY', + 'PSLOT', 'HPSSARCH', 'LOCALARCH', 'ROTDIR', 'PARMgfs', + 'ARCDIR', 'SDATE', 'MODE', 'ENSGRP', 'NMEM_EARCGRP', + 'NMEM_ENS', 'DO_CALC_INCREMENT_ENKF_GFS', 'DO_JEDIATMENS', + 'lobsdiag_forenkf', 'FHMIN_ENKF', 'FHMAX_ENKF_GFS', + 'FHOUT_ENKF_GFS', 'FHMAX_ENKF', 'FHOUT_ENKF', 'ENKF_SPREAD', + 'restart_interval_enkfgdas', 'restart_interval_enkfgfs', + 'DOHYBVAR', 'DOIAU_ENKF', 'IAU_OFFSET', 'DOIAU', + 'DO_CALC_INCREMENT', 'assim_freq', 'ARCH_CYC', + 'ARCH_WARMICFREQ', 'ARCH_FCSTICFREQ', + 'IAUFHRS_ENKF'] + + archive_dict = AttrDict() + for key in keys: + archive_dict[key] = archive.task_config[key] + + # Also import all COM* directory and template variables + for key in archive.task_config.keys(): + if key.startswith("COM"): + archive_dict[key] = archive.task_config[key] + + cwd = os.getcwd() + + os.chdir(config.ROTDIR) + + # Determine which archives to create + arcdir_set, atardir_sets = archive.configure(archive_dict) + + # Populate the product archive (ARCDIR) + archive.execute_store_products(arcdir_set) + + # Create the backup tarballs and store in ATARDIR + for atardir_set in atardir_sets: + archive.execute_backup_dataset(atardir_set) + + os.chdir(cwd) + + +if __name__ == '__main__': + main() diff --git a/scripts/exgdas_enkf_earc.sh b/scripts/exgdas_enkf_earc.sh deleted file mode 100755 index 3e54c658e9..0000000000 --- a/scripts/exgdas_enkf_earc.sh +++ /dev/null @@ -1,163 +0,0 @@ -#! /usr/bin/env bash - -source "${USHgfs}/preamble.sh" - -############################################## -# Begin JOB SPECIFIC work -############################################## -export n=$((10#${ENSGRP})) -export CDUMP_ENKF="${EUPD_CYC:-"gdas"}" - -# ICS are restarts and always lag INC by $assim_freq hours. -EARCINC_CYC=${ARCH_CYC} -EARCICS_CYC=$((ARCH_CYC-assim_freq)) -if [ "${EARCICS_CYC}" -lt 0 ]; then - EARCICS_CYC=$((EARCICS_CYC+24)) -fi - -"${USHgfs}/hpssarch_gen.sh" "${RUN}" -status=$? -if [ "${status}" -ne 0 ]; then - echo "${USHgfs}/hpssarch_gen.sh ${RUN} failed, ABORT!" - exit "${status}" -fi - -cd "${ROTDIR}" || exit 2 - -source "${USHgfs}/file_utils.sh" - -################################################################### -# ENSGRP > 0 archives a group of ensemble members -firstday=$(${NDATE} +24 "${SDATE}") -if (( 10#${ENSGRP} > 0 )) && [[ ${HPSSARCH} = "YES" || ${LOCALARCH} = "YES" ]]; then - -#--set the archiving command and create local directories, if necessary - TARCMD="htar" - if [[ ${LOCALARCH} = "YES" ]]; then - TARCMD="tar" - if [[ ! -d "${ATARDIR}/${PDY}${cyc}" ]]; then mkdir -p "${ATARDIR}/${PDY}${cyc}"; fi - fi - -#--determine when to save ICs for warm start - SAVEWARMICA="NO" - SAVEWARMICB="NO" - mm="${PDY:4:2}" - dd="${PDY:6:2}" - nday=$(( (10#${mm}-1)*30+10#${dd} )) - mod=$((nday % ARCH_WARMICFREQ)) - if [ "${PDY}${cyc}" -eq "${firstday}" ] && [ "${cyc}" -eq "${EARCINC_CYC}" ]; then SAVEWARMICA="YES" ; fi - if [ "${PDY}${cyc}" -eq "${firstday}" ] && [ "${cyc}" -eq "${EARCICS_CYC}" ]; then SAVEWARMICB="YES" ; fi - if [ "${mod}" -eq 0 ] && [ "${cyc}" ] && [ "${EARCINC_CYC}" ]; then SAVEWARMICA="YES" ; fi - if [ "${mod}" -eq 0 ] && [ "${cyc}" ] && [ "${EARCICS_CYC}" ]; then SAVEWARMICB="YES" ; fi - - if [ "${EARCICS_CYC}" -eq 18 ]; then - nday1=$((nday+1)) - mod1=$((nday1 % ARCH_WARMICFREQ)) - if [ "${mod1}" -eq 0 ] && [ "${cyc}" -eq "${EARCICS_CYC}" ] ; then SAVEWARMICB="YES" ; fi - if [ "${mod1}" -ne 0 ] && [ "${cyc}" -eq "${EARCICS_CYC}" ] ; then SAVEWARMICB="NO" ; fi - if [ "${PDY}${cyc}" -eq "${SDATE}" ] && [ "${cyc}" -eq "${EARCICS_CYC}" ] ; then SAVEWARMICB="YES" ; fi - fi - - if [ "${PDY}${cyc}" -gt "${SDATE}" ]; then # Don't run for first half cycle - - ${TARCMD} -P -cvf "${ATARDIR}/${PDY}${cyc}/${RUN}_grp${ENSGRP}.tar" $(cat "${DATA}/${RUN}_grp${n}.txt") - status=$? - if [ "${status}" -ne 0 ] && [ "${PDY}${cyc}" -ge "${firstday}" ]; then - echo "FATAL ERROR: ${TARCMD} ${PDY}${cyc} ${RUN}_grp${ENSGRP}.tar failed" - exit "${status}" - fi - - if [ "${SAVEWARMICA}" = "YES" ] && [ "${cyc}" -eq "${EARCINC_CYC}" ]; then - ${TARCMD} -P -cvf "${ATARDIR}/${PDY}${cyc}/${RUN}_restarta_grp${ENSGRP}.tar" $(cat "${DATA}/${RUN}_restarta_grp${n}.txt") - status=$? - if [ "${status}" -ne 0 ]; then - echo "FATAL ERROR: ${TARCMD} ${PDY}${cyc} ${RUN}_restarta_grp${ENSGRP}.tar failed" - exit "${status}" - fi - fi - - if [ "${SAVEWARMICB}" = "YES" ] && [ "${cyc}" -eq "${EARCICS_CYC}" ]; then - ${TARCMD} -P -cvf "${ATARDIR}/${PDY}${cyc}/${RUN}_restartb_grp${ENSGRP}.tar" $(cat "${DATA}/${RUN}_restartb_grp${n}.txt") - status=$? - if [ "${status}" -ne 0 ]; then - echo "FATAL ERROR: ${TARCMD} ${PDY}${cyc} ${RUN}_restartb_grp${ENSGRP}.tar failed" - exit "${status}" - fi - fi - - fi # CDATE>SDATE - -fi - - -################################################################### -# ENSGRP 0 archives ensemble means and copy data to online archive -if [ "${ENSGRP}" -eq 0 ]; then - - if [[ ${HPSSARCH} = "YES" || ${LOCALARCH} = "YES" ]]; then - - #--set the archiving command and create local directories, if necessary - TARCMD="htar" - HSICMD="hsi" - if [[ ${LOCALARCH} = "YES" ]]; then - TARCMD="tar" - HSICMD="" - if [[ ! -d "${ATARDIR}/${PDY}${cyc}" ]]; then mkdir -p "${ATARDIR}/${PDY}${cyc}"; fi - fi - - set +e - # Check if the tarball will have rstprod in it - has_rstprod="NO" - while IFS= read -r file; do - if [[ -f ${file} ]]; then - group=$( stat -c "%G" "${file}" ) - if [[ "${group}" == "rstprod" ]]; then - has_rstprod="YES" - break - fi - fi - done < "${DATA}/${RUN}.txt" - - # Create the tarball - tar_fl=${ATARDIR}/${PDY}${cyc}/${RUN}.tar - ${TARCMD} -P -cvf "${tar_fl}" $(cat "${DATA}/${RUN}.txt") - status=$? - if [[ "${status}" -ne 0 ]]; then - echo "FATAL ERROR: Tarring of ${tar_fl} failed" - exit "${status}" - fi - - # If rstprod was found, change the group of the tarball - if [[ "${has_rstprod}" == "YES" ]]; then - ${HSICMD} chgrp rstprod "${tar_fl}" - stat_chgrp=$? - ${HSICMD} chmod 640 "${tar_fl}" - stat_chgrp=$((stat_chgrp+$?)) - if [[ "${stat_chgrp}" -gt 0 ]]; then - echo "FATAL ERROR: Unable to properly restrict ${tar_fl}!" - echo "Attempting to delete ${tar_fl}" - ${HSICMD} rm "${tar_fl}" - echo "Please verify that ${tar_fl} was deleted!" - exit "${stat_chgrp}" - fi - fi - - # For safety, test if the htar/tar command failed only after changing groups - if (( status != 0 && ${PDY}${cyc} >= firstday )); then - echo "FATAL ERROR: ${TARCMD} ${tar_fl} failed" - exit "${status}" - fi - set_strict - fi - - #-- Archive online for verification and diagnostics - [[ ! -d ${ARCDIR} ]] && mkdir -p "${ARCDIR}" - cd "${ARCDIR}" || exit 2 - - nb_copy "${COM_ATMOS_ANALYSIS_ENSSTAT}/${RUN}.t${cyc}z.enkfstat" \ - "enkfstat.${RUN}.${PDY}${cyc}" - nb_copy "${COM_ATMOS_ANALYSIS_ENSSTAT}/${RUN}.t${cyc}z.gsistat.ensmean" \ - "gsistat.${RUN}.${PDY}${cyc}.ensmean" -fi - -exit 0 diff --git a/scripts/exgfs_atmos_postsnd.sh b/scripts/exgfs_atmos_postsnd.sh index 7aa97f3644..23c41157fe 100755 --- a/scripts/exgfs_atmos_postsnd.sh +++ b/scripts/exgfs_atmos_postsnd.sh @@ -113,7 +113,7 @@ fi # add appropriate WMO Headers. ######################################## rm -rf poe_col -for (( m = 1; m <10 ; m++ )); do +for (( m = 1; m <= NUM_SND_COLLECTIVES ; m++ )); do echo "sh ${USHgfs}/gfs_sndp.sh ${m} " >> poe_col done diff --git a/scripts/exglobal_archive.py b/scripts/exglobal_archive.py new file mode 100755 index 0000000000..31b5eb1186 --- /dev/null +++ b/scripts/exglobal_archive.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import os + +from pygfs.task.archive import Archive +from wxflow import AttrDict, Logger, cast_strdict_as_dtypedict, chdir, logit + +# initialize root logger +logger = Logger(level=os.environ.get("LOGGING_LEVEL", "DEBUG"), colored_log=True) + + +@logit(logger) +def main(): + + config = cast_strdict_as_dtypedict(os.environ) + + # Instantiate the Archive object + archive = Archive(config) + + # 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', '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', + 'FHMIN_GFS', 'FHMAX_GFS', 'FHOUT_GFS', 'ARCH_GAUSSIAN', 'MODE', + 'FHOUT_OCNICE', 'FHOUT_OCNICE_GFS', 'DO_BUFRSND', 'DOHYBVAR', + 'ARCH_GAUSSIAN_FHMAX', 'ARCH_GAUSSIAN_FHINC', 'ARCH_GAUSSIAN_FHINC', + '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_CDUMP', 'AERO_FCST_CDUMP', 'DOIBP_WAV', 'DO_JEDIOCNVAR', + 'NMEM_ENS', 'DO_JEDIATMVAR', 'DO_VRFY_OCEANDA', 'FHMAX_FITS', + 'FITSARC', 'IAUFHRS'] + + archive_dict = AttrDict() + for key in keys: + archive_dict[key] = archive.task_config[key] + + # Also import all COM* directory and template variables + for key in archive.task_config.keys(): + if key.startswith("COM"): + archive_dict[key] = archive.task_config[key] + + cwd = os.getcwd() + + os.chdir(config.ROTDIR) + + # Determine which archives to create + arcdir_set, atardir_sets = archive.configure(archive_dict) + + # Populate the product archive (ARCDIR) + archive.execute_store_products(arcdir_set) + + # Create the backup tarballs and store in ATARDIR + for atardir_set in atardir_sets: + archive.execute_backup_dataset(atardir_set) + + os.chdir(cwd) + + +if __name__ == '__main__': + main() diff --git a/scripts/exglobal_archive.sh b/scripts/exglobal_archive.sh deleted file mode 100755 index acb926d0e6..0000000000 --- a/scripts/exglobal_archive.sh +++ /dev/null @@ -1,321 +0,0 @@ -#! /usr/bin/env bash - -source "${USHgfs}/preamble.sh" - -############################################## -# Begin JOB SPECIFIC work -############################################## - -# ICS are restarts and always lag INC by $assim_freq hours -ARCHINC_CYC=${ARCH_CYC} -ARCHICS_CYC=$((ARCH_CYC-assim_freq)) -if [ "${ARCHICS_CYC}" -lt 0 ]; then - ARCHICS_CYC=$((ARCHICS_CYC+24)) -fi - -# CURRENT CYCLE -APREFIX="${RUN}.t${cyc}z." - -# Realtime parallels run GFS MOS on 1 day delay -# If realtime parallel, back up CDATE_MOS one day -# Ignore possible spelling error (nothing is misspelled) -# shellcheck disable=SC2153 -CDATE_MOS=${PDY}${cyc} -if [ "${REALTIME}" = "YES" ]; then - CDATE_MOS=$(${NDATE} -24 "${PDY}${cyc}") -fi -PDY_MOS="${CDATE_MOS:0:8}" - -############################################################### -# Archive online for verification and diagnostics -############################################################### -source "${USHgfs}/file_utils.sh" - -[[ ! -d ${ARCDIR} ]] && mkdir -p "${ARCDIR}" -nb_copy "${COM_ATMOS_ANALYSIS}/${APREFIX}gsistat" "${ARCDIR}/gsistat.${RUN}.${PDY}${cyc}" -nb_copy "${COM_SNOW_ANALYSIS}/${APREFIX}snowstat" "${ARCDIR}/snowstat.${RUN}.${PDY}${cyc}" -if [[ ${DO_AERO} = "YES" ]]; then - nb_copy "${COM_CHEM_ANALYSIS}/${APREFIX}aerostat" "${ARCDIR}/aerostat.${RUN}.${PDY}${cyc}" -fi -nb_copy "${COM_ATMOS_GRIB_1p00}/${APREFIX}pgrb2.1p00.anl" "${ARCDIR}/pgbanl.${RUN}.${PDY}${cyc}.grib2" - -# Archive 1 degree forecast GRIB2 files for verification -if [[ "${RUN}" == "gfs" ]]; then - fhmax=${FHMAX_GFS} - fhr=0 - while [ "${fhr}" -le "${fhmax}" ]; do - fhr2=$(printf %02i "${fhr}") - fhr3=$(printf %03i "${fhr}") - nb_copy "${COM_ATMOS_GRIB_1p00}/${APREFIX}pgrb2.1p00.f${fhr3}" "${ARCDIR}/pgbf${fhr2}.${RUN}.${PDY}${cyc}.grib2" - fhr=$((10#${fhr} + 10#${FHOUT_GFS} )) - done -fi -if [[ "${RUN}" == "gdas" ]]; then - flist="000 003 006 009" - for fhr in ${flist}; do - fname="${COM_ATMOS_GRIB_1p00}/${APREFIX}pgrb2.1p00.f${fhr}" - # TODO Shouldn't the archived files also use three-digit tags? - fhr2=$(printf %02i $((10#${fhr}))) - nb_copy "${fname}" "${ARCDIR}/pgbf${fhr2}.${RUN}.${PDY}${cyc}.grib2" - done -fi - -if [[ -s "${COM_ATMOS_TRACK}/avno.t${cyc}z.cyclone.trackatcfunix" ]]; then - # shellcheck disable=2153 - PSLOT4=${PSLOT:0:4} - # shellcheck disable= - PSLOT4=${PSLOT4^^} - sed "s:AVNO:${PSLOT4}:g" < "${COM_ATMOS_TRACK}/avno.t${cyc}z.cyclone.trackatcfunix" \ - > "${ARCDIR}/atcfunix.${RUN}.${PDY}${cyc}" - sed "s:AVNO:${PSLOT4}:g" < "${COM_ATMOS_TRACK}/avnop.t${cyc}z.cyclone.trackatcfunix" \ - > "${ARCDIR}/atcfunixp.${RUN}.${PDY}${cyc}" -fi - -if [[ "${RUN}" == "gdas" ]] && [[ -s "${COM_ATMOS_TRACK}/gdas.t${cyc}z.cyclone.trackatcfunix" ]]; then - # shellcheck disable=2153 - PSLOT4=${PSLOT:0:4} - # shellcheck disable= - PSLOT4=${PSLOT4^^} - sed "s:AVNO:${PSLOT4}:g" < "${COM_ATMOS_TRACK}/gdas.t${cyc}z.cyclone.trackatcfunix" \ - > "${ARCDIR}/atcfunix.${RUN}.${PDY}${cyc}" - sed "s:AVNO:${PSLOT4}:g" < "${COM_ATMOS_TRACK}/gdasp.t${cyc}z.cyclone.trackatcfunix" \ - > "${ARCDIR}/atcfunixp.${RUN}.${PDY}${cyc}" -fi - -if [ "${RUN}" = "gfs" ]; then - nb_copy "${COM_ATMOS_GENESIS}/storms.gfso.atcf_gen.${PDY}${cyc}" "${ARCDIR}/." - nb_copy "${COM_ATMOS_GENESIS}/storms.gfso.atcf_gen.altg.${PDY}${cyc}" "${ARCDIR}/." - nb_copy "${COM_ATMOS_TRACK}/trak.gfso.atcfunix.${PDY}${cyc}" "${ARCDIR}/." - nb_copy "${COM_ATMOS_TRACK}/trak.gfso.atcfunix.altg.${PDY}${cyc}" "${ARCDIR}/." - - mkdir -p "${ARCDIR}/tracker.${PDY}${cyc}/${RUN}" - blist="epac natl" - for basin in ${blist}; do - if [[ -f ${basin} ]]; then - cp -rp "${COM_ATMOS_TRACK}/${basin}" "${ARCDIR}/tracker.${PDY}${cyc}/${RUN}" - fi - done -fi - -# Archive required gaussian gfs forecast files for Fit2Obs -if [[ "${RUN}" == "gfs" ]] && [[ "${FITSARC}" = "YES" ]]; then - VFYARC=${VFYARC:-${ROTDIR}/vrfyarch} - [[ ! -d ${VFYARC} ]] && mkdir -p "${VFYARC}" - mkdir -p "${VFYARC}/${RUN}.${PDY}/${cyc}" - prefix="${RUN}.t${cyc}z" - fhmax=${FHMAX_FITS:-${FHMAX_GFS}} - fhr=0 - while [[ ${fhr} -le ${fhmax} ]]; do - fhr3=$(printf %03i "${fhr}") - sfcfile="${COM_ATMOS_HISTORY}/${prefix}.sfcf${fhr3}.nc" - sigfile="${COM_ATMOS_HISTORY}/${prefix}.atmf${fhr3}.nc" - nb_copy "${sfcfile}" "${VFYARC}/${RUN}.${PDY}/${cyc}/" - nb_copy "${sigfile}" "${VFYARC}/${RUN}.${PDY}/${cyc}/" - (( fhr = 10#${fhr} + 6 )) - done -fi - - -############################################################### -# Archive data either to HPSS or locally -if [[ ${HPSSARCH} = "YES" || ${LOCALARCH} = "YES" ]]; then -############################################################### - - # --set the archiving command and create local directories, if necessary - TARCMD="htar" - HSICMD="hsi" - if [[ ${LOCALARCH} = "YES" ]]; then - TARCMD="tar" - HSICMD='' - [[ ! -d "${ATARDIR}/${PDY}${cyc}" ]] && mkdir -p "${ATARDIR}/${PDY}${cyc}" - [[ ! -d "${ATARDIR}/${CDATE_MOS}" ]] && [[ -d "${ROTDIR}/gfsmos.${PDY_MOS}" ]] && [[ "${cyc}" -eq 18 ]] && mkdir -p "${ATARDIR}/${CDATE_MOS}" - fi - - #--determine when to save ICs for warm start and forecast-only runs - SAVEWARMICA="NO" - SAVEWARMICB="NO" - SAVEFCSTIC="NO" - firstday=$(${NDATE} +24 "${SDATE}") - mm="${PDY:2:2}" - dd="${PDY:4:2}" - # TODO: This math yields multiple dates sharing the same nday - nday=$(( (10#${mm}-1)*30+10#${dd} )) - mod=$((nday % ARCH_WARMICFREQ)) - if [[ "${PDY}${cyc}" -eq "${firstday}" ]] && [[ "${cyc}" -eq "${ARCHINC_CYC}" ]]; then SAVEWARMICA="YES" ; fi - if [[ "${PDY}${cyc}" -eq "${firstday}" ]] && [[ "${cyc}" -eq "${ARCHICS_CYC}" ]]; then SAVEWARMICB="YES" ; fi - if [[ "${mod}" -eq 0 ]] && [[ "${cyc}" -eq "${ARCHINC_CYC}" ]]; then SAVEWARMICA="YES" ; fi - if [[ "${mod}" -eq 0 ]] && [[ "${cyc}" -eq "${ARCHICS_CYC}" ]]; then SAVEWARMICB="YES" ; fi - - if [[ "${ARCHICS_CYC}" -eq 18 ]]; then - nday1=$((nday+1)) - mod1=$((nday1 % ARCH_WARMICFREQ)) - if [[ "${mod1}" -eq 0 ]] && [[ "${cyc}" -eq "${ARCHICS_CYC}" ]] ; then SAVEWARMICB="YES" ; fi - if [[ "${mod1}" -ne 0 ]] && [[ "${cyc}" -eq "${ARCHICS_CYC}" ]] ; then SAVEWARMICB="NO" ; fi - if [[ "${PDY}${cyc}" -eq "${SDATE}" ]] && [[ "${cyc}" -eq "${ARCHICS_CYC}" ]] ; then SAVEWARMICB="YES" ; fi - fi - - mod=$((nday % ARCH_FCSTICFREQ)) - if [[ "${mod}" -eq 0 ]] || [[ "${PDY}${cyc}" -eq "${firstday}" ]]; then SAVEFCSTIC="YES" ; fi - - cd "${DATA}" || exit 2 - - "${USHgfs}/hpssarch_gen.sh" "${RUN}" - status=$? - if [ "${status}" -ne 0 ]; then - echo "${USHgfs}/hpssarch_gen.sh ${RUN} failed, ABORT!" - exit "${status}" - fi - - cd "${ROTDIR}" || exit 2 - - if [[ "${RUN}" = "gfs" ]]; then - - targrp_list="gfsa gfsb" - - if [ "${ARCH_GAUSSIAN:-"NO"}" = "YES" ]; then - targrp_list="${targrp_list} gfs_flux gfs_netcdfb gfs_pgrb2b" - if [ "${MODE}" = "cycled" ]; then - targrp_list="${targrp_list} gfs_netcdfa" - fi - fi - - if [ "${DO_WAVE}" = "YES" ]; then - targrp_list="${targrp_list} gfswave" - fi - - if [[ "${DO_OCN}" == "YES" ]]; then - targrp_list="${targrp_list} ocean_6hravg ocean_daily ocean_grib2 gfs_flux_1p00" - fi - - if [[ "${DO_ICE}" == "YES" ]]; then - targrp_list="${targrp_list} ice_6hravg ice_grib2" - fi - - # Aerosols - if [ "${DO_AERO}" = "YES" ]; then - for targrp in chem; do - # TODO: Why is this tar being done here instead of being added to the list? - ${TARCMD} -P -cvf "${ATARDIR}/${PDY}${cyc}/${targrp}.tar" $(cat "${DATA}/${targrp}.txt") - status=$? - if [[ "${status}" -ne 0 ]] && [[ "${PDY}${cyc}" -ge "${firstday}" ]]; then - echo "HTAR ${PDY}${cyc} ${targrp}.tar failed" - exit "${status}" - fi - done - fi - - #for restarts - if [ "${SAVEFCSTIC}" = "YES" ]; then - targrp_list="${targrp_list} gfs_restarta" - fi - - #for downstream products - if [ "${DO_BUFRSND}" = "YES" ]; then - targrp_list="${targrp_list} gfs_downstream" - fi - - #--save mdl gfsmos output from all cycles in the 18Z archive directory - if [[ -d "gfsmos.${PDY_MOS}" ]] && [[ "${cyc}" -eq 18 ]]; then - set +e - # TODO: Why is this tar being done here instead of being added to the list? - ${TARCMD} -P -cvf "${ATARDIR}/${CDATE_MOS}/gfsmos.tar" "./gfsmos.${PDY_MOS}" - status=$? - if [[ "${status}" -ne 0 ]] && [[ "${PDY}${cyc}" -ge "${firstday}" ]]; then - echo "${TARCMD^^} ${PDY}${cyc} gfsmos.tar failed" - exit "${status}" - fi - set_strict - fi - elif [[ "${RUN}" = "gdas" ]]; then - - targrp_list="gdas" - - #gdaswave - if [ "${DO_WAVE}" = "YES" ]; then - targrp_list="${targrp_list} gdaswave" - fi - - #gdasocean - if [ "${DO_OCN}" = "YES" ]; then - targrp_list="${targrp_list} gdasocean" - if [[ "${DO_JEDIOCNVAR}" == "YES" ]]; then - targrp_list="${targrp_list} gdasocean_analysis" - fi - fi - - #gdasice - if [ "${DO_ICE}" = "YES" ]; then - targrp_list="${targrp_list} gdasice" - fi - - if [ "${SAVEWARMICA}" = "YES" ] || [ "${SAVEFCSTIC}" = "YES" ]; then - targrp_list="${targrp_list} gdas_restarta" - if [ "${DO_WAVE}" = "YES" ]; then targrp_list="${targrp_list} gdaswave_restart"; fi - if [ "${DO_OCN}" = "YES" ]; then targrp_list="${targrp_list} gdasocean_restart"; fi - if [ "${DO_ICE}" = "YES" ]; then targrp_list="${targrp_list} gdasice_restart"; fi - fi - - if [ "${SAVEWARMICB}" = "YES" ] || [ "${SAVEFCSTIC}" = "YES" ]; then - targrp_list="${targrp_list} gdas_restartb" - fi - fi - - # Turn on extended globbing options - shopt -s extglob - for targrp in ${targrp_list}; do - set +e - - # Test whether gdas.tar or gdas_restarta.tar will have rstprod data - has_rstprod="NO" - case ${targrp} in - 'gdas'|'gdas_restarta') - # Test for rstprod in each archived file - while IFS= read -r file; do - if [[ -f ${file} ]]; then - group=$( stat -c "%G" "${file}" ) - if [[ "${group}" == "rstprod" ]]; then - has_rstprod="YES" - break - fi - fi - done < "${DATA}/${targrp}.txt" - - ;; - *) ;; - esac - - # Create the tarball - tar_fl="${ATARDIR}/${PDY}${cyc}/${targrp}.tar" - ${TARCMD} -P -cvf "${tar_fl}" $(cat "${DATA}/${targrp}.txt") - status=$? - - # Change group to rstprod if it was found even if htar/tar failed in case of partial creation - if [[ "${has_rstprod}" == "YES" ]]; then - ${HSICMD} chgrp rstprod "${tar_fl}" - stat_chgrp=$? - ${HSICMD} chmod 640 "${tar_fl}" - stat_chgrp=$((stat_chgrp+$?)) - if [[ "${stat_chgrp}" -gt 0 ]]; then - echo "FATAL ERROR: Unable to properly restrict ${tar_fl}!" - echo "Attempting to delete ${tar_fl}" - ${HSICMD} rm "${tar_fl}" - echo "Please verify that ${tar_fl} was deleted!" - exit "${stat_chgrp}" - fi - fi - - # For safety, test if the htar/tar command failed after changing groups - if [[ "${status}" -ne 0 ]] && [[ "${PDY}${cyc}" -ge "${firstday}" ]]; then - echo "FATAL ERROR: ${TARCMD} ${tar_fl} failed" - exit "${status}" - fi - set_strict - done - # Turn extended globbing back off - shopt -u extglob - -############################################################### -fi ##end of HPSS archive -############################################################### - -exit 0 diff --git a/sorc/build_all.sh b/sorc/build_all.sh index 0797092a37..e3040fc0fa 100755 --- a/sorc/build_all.sh +++ b/sorc/build_all.sh @@ -183,7 +183,7 @@ echo "Building ${build_list}" # Go through all builds and adjust CPU counts up if possible if [[ ${requested_cpus} -lt ${_build_job_max} && ${big_jobs} -gt 0 ]]; then - # Add cores to the gdas, ufs, and gsi build jobs + # Add cores to the gdas and ufs build jobs extra_cores=$(( _build_job_max - requested_cpus )) extra_cores=$(( extra_cores / big_jobs )) for build in "${!build_jobs[@]}"; do diff --git a/sorc/gdas.cd b/sorc/gdas.cd index 2b2d417a96..249e242e33 160000 --- a/sorc/gdas.cd +++ b/sorc/gdas.cd @@ -1 +1 @@ -Subproject commit 2b2d417a96528527d7d3e7eedaccf150dc075d92 +Subproject commit 249e242e33a33feeb1c81bedd51198309f669de0 diff --git a/sorc/wxflow b/sorc/wxflow index 942b90bfaa..71f6b10f76 160000 --- a/sorc/wxflow +++ b/sorc/wxflow @@ -1 +1 @@ -Subproject commit 942b90bfaa14f6b6d7374310dbdfd421ddb30548 +Subproject commit 71f6b10f76a440993580027ba1183d61277d1299 diff --git a/ush/check_netcdf.sh b/ush/check_netcdf.sh index e115ae8ae3..5f56a38aba 100755 --- a/ush/check_netcdf.sh +++ b/ush/check_netcdf.sh @@ -8,7 +8,7 @@ source "${HOMEgfs}/ush/load_fv3gfs_modules.sh" 1>/dev/null 2>&1 ncfile=${1?} -ncdump -h "${ncfile}" 1>/dev/null 2>&1 # redirect stdout and stderr to /dev/null to suppress output in cron +(( $(ncdump "${ncfile}" 2> /dev/null | grep -Po '(?<=time = UNLIMITED ; // \()\d+(?= currently)' || echo 0) > 0 )) # redirect stdout and stderr to /dev/null to suppress output in cron rc=$? # If there is no error, rc=0, else rc!=0 diff --git a/ush/forecast_det.sh b/ush/forecast_det.sh index e4b9ded3d3..5e2f277592 100755 --- a/ush/forecast_det.sh +++ b/ush/forecast_det.sh @@ -87,7 +87,7 @@ UFS_det(){ # Check for CICE6 restart availability if [[ "${cplice}" == ".true." ]]; then if [[ ! -f "${DATArestart}/CICE_RESTART/cice_model.res.${rdate:0:4}-${rdate:4:2}-${rdate:6:2}-${seconds}.nc" ]]; then - cice_rst_ok="NO" + cice6_rst_ok="NO" fi fi diff --git a/ush/forecast_postdet.sh b/ush/forecast_postdet.sh index 9c8858ec3d..f5a2bec07c 100755 --- a/ush/forecast_postdet.sh +++ b/ush/forecast_postdet.sh @@ -239,7 +239,7 @@ FV3_out() { if [[ "${RUN}" =~ "gdas" || "${RUN}" == "enkfgfs" ]]; then local restart_date restart_date=$(date --utc -d "${current_cycle:0:8} ${current_cycle:8:2} + ${restart_interval} hours" +%Y%m%d%H) - while (( restart_date < forecast_end_cycle )); do + while (( restart_date <= forecast_end_cycle )); do echo "Copying FV3 restarts for 'RUN=${RUN}' at ${restart_date}" for fv3_restart_file in "${fv3_restart_files[@]}"; do restart_file="${restart_date:0:8}.${restart_date:8:2}0000.${fv3_restart_file}" @@ -253,15 +253,15 @@ FV3_out() { # Copy the final restart files at the end of the forecast segment # The final restart written at the end of the forecast does not include the valid date # TODO: verify the above statement since RM found that it did! - # TODO: For other components, this is only for gfs/gefs - check to see if this should also have this - if [[ "${COPY_FINAL_RESTARTS}" == "YES" ]]; then + # TODO: For other components, this is only for gfs/gefs - check to see if this should also have this + if [[ "${COPY_FINAL_RESTARTS}" == "YES" ]]; then echo "Copying FV3 restarts for 'RUN=${RUN}' at the end of the forecast segment: ${forecast_end_cycle}" for fv3_restart_file in "${fv3_restart_files[@]}"; do restart_file="${forecast_end_cycle:0:8}.${forecast_end_cycle:8:2}0000.${fv3_restart_file}" ${NCP} "${DATArestart}/FV3_RESTART/${restart_file}" \ "${COM_ATMOS_RESTART}/${restart_file}" done - fi + fi echo "SUB ${FUNCNAME[0]}: Output data for FV3 copied" } @@ -471,7 +471,7 @@ MOM6_nml() { MOM6_out() { echo "SUB ${FUNCNAME[0]}: Copying output data for MOM6" - # Copy MOM_input from DATA to COM_OCEAN_INPUT after the forecast is run (and successfull) + # Copy MOM_input from DATA to COM_CONF after the forecast is run (and successfull) ${NCP} "${DATA}/INPUT/MOM_input" "${COM_CONF}/ufs.MOM_input" # Create a list of MOM6 restart files @@ -499,7 +499,7 @@ MOM6_out() { ${NCP} "${DATArestart}/MOM6_RESTART/${restart_file}" \ "${COM_OCEAN_RESTART}/${restart_file}" done - fi + fi fi # Copy restarts for the next cycle for RUN=gdas|enkfgdas|enkfgfs @@ -527,6 +527,9 @@ CICE_postdet() { else # "${RERUN}" == "NO" restart_date="${model_start_date_current_cycle}" cice_restart_file="${COM_ICE_RESTART_PREV}/${restart_date:0:8}.${restart_date:8:2}0000.cice_model.res.nc" + if [[ "${DO_JEDIOCNVAR:-NO}" == "YES" ]]; then + cice_restart_file="${COM_ICE_ANALYSIS}/${restart_date:0:8}.${restart_date:8:2}0000.cice_model_anl.res.nc" + fi fi # Copy CICE ICs @@ -592,7 +595,7 @@ CICE_out() { target_file="${forecast_end_cycle:0:8}.${forecast_end_cycle:8:2}0000.cice_model.res.nc" ${NCP} "${DATArestart}/CICE_RESTART/${source_file}" \ "${COM_ICE_RESTART}/${target_file}" - fi + fi fi # Copy restarts for next cycle for RUN=gdas|enkfgdas|enkfgfs @@ -728,7 +731,7 @@ CMEPS_out() { else echo "Mediator restart '${DATArestart}/CMEPS_RESTART/${source_file}' not found." fi - fi + fi # Copy restarts for the next cycle to COM for RUN=gdas|enkfgdas|enkfgfs if [[ "${RUN}" =~ "gdas" || "${RUN}" == "enkfgfs" ]]; then diff --git a/ush/hpssarch_gen.sh b/ush/hpssarch_gen.sh deleted file mode 100755 index 101745da8e..0000000000 --- a/ush/hpssarch_gen.sh +++ /dev/null @@ -1,798 +0,0 @@ -#! /usr/bin/env bash - -################################################### -# Fanglin Yang, 20180318 -# --create bunches of files to be archived to HPSS -################################################### -source "${USHgfs}/preamble.sh" - -type=${1:-gfs} ##gfs, gdas, enkfgdas or enkfggfs - -ARCH_GAUSSIAN=${ARCH_GAUSSIAN:-"YES"} -ARCH_GAUSSIAN_FHMAX=${ARCH_GAUSSIAN_FHMAX:-36} -ARCH_GAUSSIAN_FHINC=${ARCH_GAUSSIAN_FHINC:-6} - -# Set whether to archive downstream products -DO_DOWN=${DO_DOWN:-"NO"} -if [[ ${DO_BUFRSND} = "YES" ]]; then - export DO_DOWN="YES" -fi - -#----------------------------------------------------- -if [[ ${type} = "gfs" ]]; then -#----------------------------------------------------- - FHMIN_GFS=${FHMIN_GFS:-0} - FHMAX_GFS=${FHMAX_GFS:-384} - FHOUT_GFS=${FHOUT_GFS:-3} - FHMAX_HF_GFS=${FHMAX_HF_GFS:-120} - FHOUT_HF_GFS=${FHOUT_HF_GFS:-1} - - rm -f "${DATA}/gfsa.txt" - rm -f "${DATA}/gfsb.txt" - rm -f "${DATA}/gfs_restarta.txt" - touch "${DATA}/gfsa.txt" - touch "${DATA}/gfsb.txt" - touch "${DATA}/gfs_restarta.txt" - - if [[ ${ARCH_GAUSSIAN} = "YES" ]]; then - rm -f "${DATA}/gfs_pgrb2b.txt" - rm -f "${DATA}/gfs_netcdfb.txt" - rm -f "${DATA}/gfs_flux.txt" - touch "${DATA}/gfs_pgrb2b.txt" - touch "${DATA}/gfs_netcdfb.txt" - touch "${DATA}/gfs_flux.txt" - - if [[ ${MODE} = "cycled" ]]; then - rm -f "${DATA}/gfs_netcdfa.txt" - touch "${DATA}/gfs_netcdfa.txt" - fi - fi - - if [[ ${DO_DOWN} = "YES" ]]; then - rm -f "${DATA}/gfs_downstream.txt" - touch "${DATA}/gfs_downstream.txt" - fi - - head="gfs.t${cyc}z." - - if [[ ${ARCH_GAUSSIAN} = "YES" ]]; then - { - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2b.0p25.anl" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2b.0p25.anl.idx" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2b.1p00.anl" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2b.1p00.anl.idx" - } >> "${DATA}/gfs_pgrb2b.txt" - - if [[ ${MODE} = "cycled" ]]; then - { - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmanl.nc" - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}sfcanl.nc" - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmi*.nc" - gsida_files=("dtfanl.nc" - "loginc.txt") - for file in "${gsida_files[@]}"; do - [[ -s ${COM_ATMOS_ANALYSIS}/${head}${file} ]] && echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}${file}" - done - } >> "${DATA}/gfs_netcdfa.txt" - fi - - fh=0 - while (( fh <= ARCH_GAUSSIAN_FHMAX )); do - fhr=$(printf %03i "${fh}") - { - echo "${COM_ATMOS_HISTORY/${ROTDIR}\//}/${head}atmf${fhr}.nc" - echo "${COM_ATMOS_HISTORY/${ROTDIR}\//}/${head}sfcf${fhr}.nc" - } >> "${DATA}/gfs_netcdfb.txt" - fh=$((fh+ARCH_GAUSSIAN_FHINC)) - done - fi - - #.................. - # Exclude the gfsarch.log file, which will change during the tar operation - # This uses the bash extended globbing option - { - echo "./logs/${PDY}${cyc}/gfs!(arch).log" - echo "${COM_CONF/${ROTDIR}\//}/ufs.input.nml" - - if [[ ${MODE} = "cycled" ]]; then - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}gsistat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}gsistat" - fi - gsiob_files=("nsstbufr" - "prepbufr" - "prepbufr.acft_profiles") - for file in "${gsiob_files[@]}"; do - [[ -s ${COM_OBS}/${head}${file} ]] && echo "${COM_OBS/${ROTDIR}\//}/${head}${file}" - done - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atmvar.yaml" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmvar.yaml" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atmstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmstat" - fi - fi - - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.anl" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.anl.idx" - - #Only generated if there are cyclones to track - cyclone_files=("avno.t${cyc}z.cyclone.trackatcfunix" - "avnop.t${cyc}z.cyclone.trackatcfunix" - "trak.gfso.atcfunix.${PDY}${cyc}" - "trak.gfso.atcfunix.altg.${PDY}${cyc}") - - for file in "${cyclone_files[@]}"; do - [[ -s ${COM_ATMOS_TRACK}/${file} ]] && echo "${COM_ATMOS_TRACK/${ROTDIR}\//}/${file}" - done - - genesis_files=("storms.gfso.atcf_gen.${PDY}${cyc}" - "storms.gfso.atcf_gen.altg.${PDY}${cyc}") - for file in "${genesis_files[@]}"; do - [[ -s ${COM_ATMOS_GENESIS}/${file} ]] && echo "${COM_ATMOS_GENESIS/${ROTDIR}\//}/${file}" - done - - # GSI Monitor job output - - if [[ ${DO_VMINMON} = "YES" ]]; then - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.costs.txt" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.cost_terms.txt" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.gnorms.ieee_d" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.reduction.ieee_d" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/gnorm_data.txt" - fi - - } >> "${DATA}/gfsa.txt" - - { - if [[ ${DO_DOWN} = "YES" ]]; then - if [[ ${DO_BUFRSND} = "YES" ]]; then - echo "${COM_ATMOS_GEMPAK/${ROTDIR}\//}/gfs_${PDY}${cyc}.sfc" - echo "${COM_ATMOS_GEMPAK/${ROTDIR}\//}/gfs_${PDY}${cyc}.snd" - echo "${COM_ATMOS_WMO/${ROTDIR}\//}/gfs_collective*.postsnd_${cyc}" - echo "${COM_ATMOS_BUFR/${ROTDIR}\//}/bufr.t${cyc}z" - echo "${COM_ATMOS_BUFR/${ROTDIR}\//}/gfs.t${cyc}z.bufrsnd.tar.gz" - fi - fi - } >> "${DATA}/gfs_downstream.txt" - - { - echo "${COM_ATMOS_GRIB_0p50/${ROTDIR}\//}/${head}pgrb2.0p50.anl" - echo "${COM_ATMOS_GRIB_0p50/${ROTDIR}\//}/${head}pgrb2.0p50.anl.idx" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.anl" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.anl.idx" - } >> "${DATA}/gfsb.txt" - - - fh=0 - while (( fh <= FHMAX_GFS )); do - fhr=$(printf %03i "${fh}") - if [[ ${ARCH_GAUSSIAN} = "YES" ]]; then - { - echo "${COM_ATMOS_MASTER/${ROTDIR}\//}/${head}sfluxgrbf${fhr}.grib2" - echo "${COM_ATMOS_MASTER/${ROTDIR}\//}/${head}sfluxgrbf${fhr}.grib2.idx" - } >> "${DATA}/gfs_flux.txt" - - { - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2b.0p25.f${fhr}" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2b.0p25.f${fhr}.idx" - if [[ -s "${COM_ATMOS_GRIB_1p00}/${head}pgrb2b.1p00.f${fhr}" ]]; then - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2b.1p00.f${fhr}" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2b.1p00.f${fhr}.idx" - fi - } >> "${DATA}/gfs_pgrb2b.txt" - fi - - { - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.f${fhr}" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.f${fhr}.idx" - echo "${COM_ATMOS_HISTORY/${ROTDIR}\//}/${head}atm.logf${fhr}.txt" - } >> "${DATA}/gfsa.txt" - - - { - if [[ -s "${COM_ATMOS_GRIB_0p50}/${head}pgrb2.0p50.f${fhr}" ]]; then - echo "${COM_ATMOS_GRIB_0p50/${ROTDIR}\//}/${head}pgrb2.0p50.f${fhr}" - echo "${COM_ATMOS_GRIB_0p50/${ROTDIR}\//}/${head}pgrb2.0p50.f${fhr}.idx" - fi - if [[ -s "${COM_ATMOS_GRIB_1p00}/${head}pgrb2.1p00.f${fhr}" ]]; then - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.f${fhr}" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.f${fhr}.idx" - fi - } >> "${DATA}/gfsb.txt" - - inc=${FHOUT_GFS} - if (( FHMAX_HF_GFS > 0 && FHOUT_HF_GFS > 0 && fh < FHMAX_HF_GFS )); then - inc=${FHOUT_HF_GFS} - fi - - fh=$((fh+inc)) - done - - #.................. - { - if [[ ${MODE} = "cycled" ]]; then - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile1.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile2.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile3.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile4.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile5.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile6.nc" - elif [[ ${MODE} = "forecast-only" ]]; then - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_ctrl.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_data.tile1.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_data.tile2.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_data.tile3.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_data.tile4.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_data.tile5.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/gfs_data.tile6.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/sfc_data.tile1.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/sfc_data.tile2.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/sfc_data.tile3.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/sfc_data.tile4.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/sfc_data.tile5.nc" - echo "${COM_ATMOS_INPUT/${ROTDIR}\//}/sfc_data.tile6.nc" - fi - } >> "${DATA}/gfs_restarta.txt" - - - #.................. - if [[ ${DO_WAVE} = "YES" ]]; then - - rm -rf "${DATA}/gfswave.txt" - touch "${DATA}/gfswave.txt" - - head="gfswave.t${cyc}z." - - #........................... - { - echo "${COM_WAVE_HISTORY/${ROTDIR}\//}/ww3_multi*" - echo "${COM_WAVE_GRID/${ROTDIR}\//}/${head}*" - echo "${COM_WAVE_STATION/${ROTDIR}\//}/${head}*" - } >> "${DATA}/gfswave.txt" - fi - - if [[ "${DO_OCN}" == "YES" ]]; then - - head="gfs.ocean.t${cyc}z." - rm -f "${DATA}/ocean_6hravg.txt"; touch "${DATA}/ocean_6hravg.txt" - rm -f "${DATA}/ocean_daily.txt"; touch "${DATA}/ocean_daily.txt" - rm -f "${DATA}/ocean_grib2.txt"; touch "${DATA}/ocean_grib2.txt" - - echo "${COM_OCEAN_HISTORY/${ROTDIR}\//}/${head}6hr_avg.f*.nc" >> "${DATA}/ocean_6hravg.txt" - echo "${COM_OCEAN_HISTORY/${ROTDIR}\//}/${head}daily.f*.nc" >> "${DATA}/ocean_daily.txt" - - { - if [[ -d "${COM_OCEAN_GRIB}/5p00" ]]; then - echo "${COM_OCEAN_GRIB/${ROTDIR}\//}/5p00/${head}5p00.f*.grib2" - echo "${COM_OCEAN_GRIB/${ROTDIR}\//}/5p00/${head}5p00.f*.grib2.idx" - fi - if [[ -d "${COM_OCEAN_GRIB}/1p00" ]]; then - echo "${COM_OCEAN_GRIB/${ROTDIR}\//}/1p00/${head}1p00.f*.grib2" - echo "${COM_OCEAN_GRIB/${ROTDIR}\//}/1p00/${head}1p00.f*.grib2.idx" - fi - if [[ -d "${COM_OCEAN_GRIB}/0p25" ]]; then - echo "${COM_OCEAN_GRIB/${ROTDIR}\//}/0p25/${head}0p25.f*.grib2" - echo "${COM_OCEAN_GRIB/${ROTDIR}\//}/0p25/${head}0p25.f*.grib2.idx" - fi - } >> "${DATA}/ocean_grib2.txt" - - # Also save fluxes from atmosphere - head="gfs.t${cyc}z." - rm -f "${DATA}/gfs_flux_1p00.txt"; touch "${DATA}/gfs_flux_1p00.txt" - { - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}flux.1p00.f???" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}flux.1p00.f???.idx" - } >> "${DATA}/gfs_flux_1p00.txt" - fi - - if [[ "${DO_ICE}" == "YES" ]]; then - head="gfs.ice.t${cyc}z." - rm -f "${DATA}/ice_6hravg.txt"; touch "${DATA}/ice_6hravg.txt" - rm -f "${DATA}/ice_grib2.txt"; touch "${DATA}/ice_grib2.txt" - - { - echo "${COM_ICE_HISTORY/${ROTDIR}\//}/${head}ic.nc" - echo "${COM_ICE_HISTORY/${ROTDIR}\//}/${head}6hr_avg.f*.nc" - } >> "${DATA}/ice_6hravg.txt" - - { - if [[ -d "${COM_ICE_GRIB}/5p00" ]]; then - echo "${COM_ICE_GRIB/${ROTDIR}\//}/5p00/${head}5p00.f*.grib2" - echo "${COM_ICE_GRIB/${ROTDIR}\//}/5p00/${head}5p00.f*.grib2.idx" - fi - if [[ -d "${COM_ICE_GRIB}/1p00" ]]; then - echo "${COM_ICE_GRIB/${ROTDIR}\//}/1p00/${head}1p00.f*.grib2" - echo "${COM_ICE_GRIB/${ROTDIR}\//}/1p00/${head}1p00.f*.grib2.idx" - fi - if [[ -d "${COM_ICE_GRIB}/0p25" ]]; then - echo "${COM_ICE_GRIB/${ROTDIR}\//}/0p25/${head}0p25.f*.grib2" - echo "${COM_ICE_GRIB/${ROTDIR}\//}/0p25/${head}0p25.f*.grib2.idx" - fi - } >> "${DATA}/ice_grib2.txt" - fi - - if [[ ${DO_AERO} = "YES" ]]; then - head="gocart" - - rm -f "${DATA}/chem.txt" - touch "${DATA}/chem.txt" - - echo "${COM_CHEM_HISTORY/${ROTDIR}\//}/${head}*" >> "${DATA}/chem.txt" - fi - -#----------------------------------------------------- -fi ##end of gfs -#----------------------------------------------------- - - - -#----------------------------------------------------- -if [[ ${type} == "gdas" ]]; then -#----------------------------------------------------- - - rm -f "${DATA}/gdas.txt" - rm -f "${DATA}/gdas_restarta.txt" - rm -f "${DATA}/gdas_restartb.txt" - touch "${DATA}/gdas.txt" - touch "${DATA}/gdas_restarta.txt" - touch "${DATA}/gdas_restartb.txt" - - head="gdas.t${cyc}z." - - #.................. - { - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.anl" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.anl.idx" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.anl" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.anl.idx" - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmanl.nc" - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}sfcanl.nc" - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atmvar.yaml" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmvar.yaml" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atmstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmstat" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}gsistat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}gsistat" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atmanl.ensres.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmanl.ensres.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atma003.ensres.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atma003.ensres.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}atma009.ensres.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atma009.ensres.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}cnvstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}cnvstat" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}oznstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}oznstat" - fi - if [[ ${DO_AERO} = "YES" ]]; then - if [[ -s "${COM_CHEM_ANALYSIS}/${head}aerostat" ]]; then - echo "${COM_CHEM_ANALYSIS/${ROTDIR}\//}/${head}aerostat" - fi - fi - if [[ -s "${COM_SNOW_ANALYSIS}/${head}snowstat.tgz" ]]; then - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/${head}snowstat.tgz" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}radstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}radstat" - fi - for fstep in prep anal fcst verfozn verfrad vminmon; do - if [[ -s "${ROTDIR}/logs/${PDY}${cyc}/gdas${fstep}.log" ]]; then - echo "./logs/${PDY}${cyc}/gdas${fstep}.log" - fi - done - echo "./logs/${PDY}${cyc}/gdas*prod*.log" - if [[ "${WRITE_DOPOST}" == ".false." ]]; then - echo "./logs/${PDY}${cyc}/gdas*upp*.log" - fi - - fh=0 - while [[ ${fh} -le 9 ]]; do - fhr=$(printf %03i "${fh}") - echo "${COM_ATMOS_MASTER/${ROTDIR}\//}/${head}sfluxgrbf${fhr}.grib2" - echo "${COM_ATMOS_MASTER/${ROTDIR}\//}/${head}sfluxgrbf${fhr}.grib2.idx" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.f${fhr}" - echo "${COM_ATMOS_GRIB_0p25/${ROTDIR}\//}/${head}pgrb2.0p25.f${fhr}.idx" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.f${fhr}" - echo "${COM_ATMOS_GRIB_1p00/${ROTDIR}\//}/${head}pgrb2.1p00.f${fhr}.idx" - echo "${COM_ATMOS_HISTORY/${ROTDIR}\//}/${head}atm.logf${fhr}.txt" - echo "${COM_ATMOS_HISTORY/${ROTDIR}\//}/${head}atmf${fhr}.nc" - echo "${COM_ATMOS_HISTORY/${ROTDIR}\//}/${head}sfcf${fhr}.nc" - fh=$((fh+3)) - done - flist="001 002 004 005 007 008" - for fhr in ${flist}; do - file="${COM_ATMOS_MASTER/${ROTDIR}\//}/${head}sfluxgrbf${fhr}.grib2" - if [[ -s "${file}" ]]; then - echo "${file}" - echo "${file}.idx" - fi - done - - # GSI Monitor jobs output - - if [[ ${DO_VERFOZN} = "YES" ]]; then - for type in horiz time; do - if [[ ${type} = "horiz" ]]; then - suffix=".gz" - elif [[ ${type} = "time" ]]; then - suffix="" - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/bad_cnt.${PDY}${cyc}" - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/bad_diag.${PDY}${cyc}" - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/bad_pen.${PDY}${cyc}" - fi - subtyplist="gome_metop-b omi_aura ompslp_npp ompsnp_n20 ompsnp_npp ompstc8_n20 ompstc8_npp sbuv2_n19" - for subtype in ${subtyplist}; do - # On occassion, data is not available for some of these satellites. Check for existence. - if [[ -s "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/${subtype}.ges.${PDY}${cyc}.ieee_d${suffix}" ]]; then - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/${subtype}.anl.${PDY}${cyc}.ieee_d${suffix}" - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/${subtype}.anl.ctl" - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/${subtype}.ges.${PDY}${cyc}.ieee_d${suffix}" - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/${subtype}.ges.ctl" - fi - done - echo "${COM_ATMOS_OZNMON/${ROTDIR}\//}/${type}/stdout.${type}.tar.gz" - done - fi - - if [[ ${DO_VERFRAD} = "YES" ]]; then - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/bad_diag.${PDY}${cyc}" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/bad_pen.${PDY}${cyc}" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/low_count.${PDY}${cyc}" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/radmon_angle.tar.gz" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/radmon_bcoef.tar.gz" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/radmon_bcor.tar.gz" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/radmon_time.tar.gz" - echo "${COM_ATMOS_RADMON/${ROTDIR}\//}/warning.${PDY}${cyc}" - fi - - if [[ ${DO_VMINMON} = "YES" ]]; then - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.costs.txt" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.cost_terms.txt" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.gnorms.ieee_d" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/${PDY}${cyc}.reduction.ieee_d" - echo "${COM_ATMOS_MINMON/${ROTDIR}\//}/gnorm_data.txt" - fi - - } >> "${DATA}/gdas.txt" - - #.................. - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}cnvstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}cnvstat" >> "${DATA}/gdas_restarta.txt" - fi - if [[ -s "${COM_ATMOS_ANALYSIS}/${head}radstat" ]]; then - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}radstat" >> "${DATA}/gdas_restarta.txt" - fi - - { - gsiob_files=("nsstbufr" - "prepbufr" - "prepbufr.acft_profiles") - for file in "${gsiob_files[@]}"; do - [[ -s ${COM_OBS}/${head}${file} ]] && echo "${COM_OBS/${ROTDIR}\//}/${head}${file}" - done - - gsida_files=("abias" - "abias_air" - "abias_int" - "abias_pc" - "dtfanl.nc" - "loginc.txt") - for file in "${gsida_files[@]}"; do - [[ -s ${COM_ATMOS_ANALYSIS}/${head}${file} ]] && echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}${file}" - done - - ufsda_files=("amsua_n19.satbias.nc4" - "amsua_n19.satbias_cov.nc4" - "amsua_n19.tlapse.txt") - for file in "${ufsda_files[@]}"; do - [[ -s ${COM_ATMOS_ANALYSIS}/${head}${file} ]] && echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}${file}" - done - - echo "${COM_ATMOS_ANALYSIS/${ROTDIR}\//}/${head}atmi*nc" - - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile1.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile2.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile3.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile4.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile5.nc" - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}/*0000.sfcanl_data.tile6.nc" - - [[ -s "${COM_CONF}/${head}letkfoi.yaml" ]] && echo "${COM_CONF/${ROTDIR}\//}/${head}letkfoi.yaml" - - if [[ "${DO_JEDISNOWDA:-}" == "YES" ]]; then - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/*0000.sfc_data.tile1.nc" - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/*0000.sfc_data.tile2.nc" - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/*0000.sfc_data.tile3.nc" - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/*0000.sfc_data.tile4.nc" - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/*0000.sfc_data.tile5.nc" - echo "${COM_SNOW_ANALYSIS/${ROTDIR}\//}/*0000.sfc_data.tile6.nc" - fi - } >> "${DATA}/gdas_restarta.txt" - - #.................. - echo "${COM_ATMOS_RESTART/${ROTDIR}\//}" >> "${DATA}/gdas_restartb.txt" - - #.................. - if [[ ${DO_WAVE} = "YES" ]]; then - - rm -rf "${DATA}/gdaswave.txt" - touch "${DATA}/gdaswave.txt" - rm -rf "${DATA}/gdaswave_restart.txt" - touch "${DATA}/gdaswave_restart.txt" - - head="gdaswave.t${cyc}z." - - #........................... - { - echo "${COM_WAVE_GRID/${ROTDIR}\//}/${head}*" - echo "${COM_WAVE_STATION/${ROTDIR}\//}/${head}*" - } >> "${DATA}/gdaswave.txt" - - echo "${COM_WAVE_RESTART/${ROTDIR}\//}/*" >> "${DATA}/gdaswave_restart.txt" - - fi - - #.................. - if [[ ${DO_OCN} = "YES" ]]; then - - rm -rf "${DATA}/gdasocean.txt" - touch "${DATA}/gdasocean.txt" - rm -rf "${DATA}/gdasocean_restart.txt" - touch "${DATA}/gdasocean_restart.txt" - - head="gdas.t${cyc}z." - - #........................... - { - echo "${COM_OCEAN_HISTORY/${ROTDIR}\//}/${head}*" - echo "${COM_OCEAN_INPUT/${ROTDIR}\//}" - } >> "${DATA}/gdasocean.txt" - - { - echo "${COM_OCEAN_RESTART/${ROTDIR}\//}/*" - echo "${COM_MED_RESTART/${ROTDIR}\//}/*" - } >> "${DATA}/gdasocean_restart.txt" - - if [[ ${DO_JEDIOCNVAR} = "YES" ]]; then - { - echo "${COM_OCEAN_ANALYSIS/${ROTDIR}\//}/${head}*" - echo "${COM_OCEAN_ANALYSIS/${ROTDIR}\//}/gdas.t??z.ocngrid.nc" - echo "${COM_OCEAN_ANALYSIS/${ROTDIR}\//}/diags" - echo "${COM_OCEAN_ANALYSIS/${ROTDIR}\//}/yaml" - } >> "${DATA}/gdasocean_analysis.txt" - fi - fi - - if [[ ${DO_ICE} = "YES" ]]; then - - rm -rf "${DATA}/gdasice.txt" - touch "${DATA}/gdasice.txt" - rm -rf "${DATA}/gdasice_restart.txt" - touch "${DATA}/gdasice_restart.txt" - - head="gdas.ice.t${cyc}z." - - #........................... - { - echo "${COM_ICE_HISTORY/${ROTDIR}\//}/${head}*" - } >> "${DATA}/gdasice.txt" - - echo "${COM_ICE_RESTART/${ROTDIR}\//}/*" >> "${DATA}/gdasice_restart.txt" - - fi - - -#----------------------------------------------------- -fi ##end of gdas -#----------------------------------------------------- - - -#----------------------------------------------------- -if [[ ${type} == "enkfgdas" || ${type} == "enkfgfs" ]]; then -#----------------------------------------------------- - - IAUFHRS_ENKF=${IAUFHRS_ENKF:-6} - lobsdiag_forenkf=${lobsdiag_forenkf:-".false."} - IFS=',' read -ra nfhrs <<< ${IAUFHRS_ENKF} - NMEM_ENS=${NMEM_ENS:-80} - NMEM_EARCGRP=${NMEM_EARCGRP:-10} ##number of ens memebers included in each tarball - NTARS=$((NMEM_ENS/NMEM_EARCGRP)) - [[ ${NTARS} -eq 0 ]] && NTARS=1 - [[ $((NTARS*NMEM_EARCGRP)) -lt ${NMEM_ENS} ]] && NTARS=$((NTARS+1)) - ##NTARS2=$((NTARS/2)) # number of earc groups to include analysis/increments - NTARS2=${NTARS} - - head="${RUN}.t${cyc}z." - - #.................. - rm -f "${DATA}/${RUN}.txt" - touch "${DATA}/${RUN}.txt" - - { - gsida_files=("enkfstat" - "gsistat.ensmean" - "cnvstat.ensmean" - "oznstat.ensmean" - "radstat.ensmean") - for file in "${gsida_files[@]}"; do - [[ -s ${COM_ATMOS_ANALYSIS_ENSSTAT}/${head}${file} ]] && echo "${COM_ATMOS_ANALYSIS_ENSSTAT/${ROTDIR}\//}/${head}${file}" - done - - ufsda_files=("atmens.yaml" - "atmensstat") - for file in "${ufsda_files[@]}"; do - [[ -s ${COM_ATMOS_ANALYSIS_ENSSTAT}/${head}${file} ]] && echo "${COM_ATMOS_ANALYSIS_ENSSTAT/${ROTDIR}\//}/${head}${file}" - done - - for FHR in "${nfhrs[@]}"; do # loop over analysis times in window - if [[ ${FHR} -eq 6 ]]; then - if [[ -s "${COM_ATMOS_ANALYSIS_ENSSTAT}/${head}atmanl.ensmean.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS_ENSSTAT/${ROTDIR}\//}/${head}atmanl.ensmean.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS_ENSSTAT}/${head}atminc.ensmean.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS_ENSSTAT/${ROTDIR}\//}/${head}atminc.ensmean.nc" - fi - else - if [[ -s "${COM_ATMOS_ANALYSIS_ENSSTAT}/${head}atma00${FHR}.ensmean.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS_ENSSTAT/${ROTDIR}\//}/${head}atma00${FHR}.ensmean.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS_ENSSTAT}/${head}atmi00${FHR}.ensmean.nc" ]]; then - echo "${COM_ATMOS_ANALYSIS_ENSSTAT/${ROTDIR}\//}/${head}atmi00${FHR}.ensmean.nc" - fi - fi - done # loop over FHR - for fstep in fcst epos ; do - echo "logs/${PDY}${cyc}/${RUN}${fstep}*.log" - done - - # eobs, ecen, esfc, and eupd are not run on the first cycle - for fstep in eobs ecen esfc eupd ; do - for log in "${ROTDIR}/logs/${PDY}${cyc}/${RUN}${fstep}"*".log"; do - if [[ -s "${log}" ]]; then - echo "logs/${PDY}${cyc}/${RUN}${fstep}*.log" - fi - done - done - - # eomg* are optional jobs - for log in "${ROTDIR}/logs/${PDY}${cyc}/${RUN}eomg"*".log"; do - if [[ -s "${log}" ]]; then - echo "logs/${PDY}${cyc}/${RUN}eomg*.log" - fi - break - done - - # Ensemble spread file only available with netcdf output - fh=3 - while [ $fh -le 9 ]; do - fhr=$(printf %03i $fh) - echo "${COM_ATMOS_HISTORY_ENSSTAT/${ROTDIR}\//}/${head}atmf${fhr}.ensmean.nc" - echo "${COM_ATMOS_HISTORY_ENSSTAT/${ROTDIR}\//}/${head}sfcf${fhr}.ensmean.nc" - if [[ -s "${COM_ATMOS_HISTORY_ENSSTAT}/${head}atmf${fhr}.ensspread.nc" ]]; then - echo "${COM_ATMOS_HISTORY_ENSSTAT/${ROTDIR}\//}/${head}atmf${fhr}.ensspread.nc" - fi - fh=$((fh+3)) - done - } >> "${DATA}/${RUN}.txt" - - #........................... - n=1 - while (( n <= NTARS )); do - #........................... - - rm -f "${DATA}/${RUN}_grp${n}.txt" - rm -f "${DATA}/${RUN}_restarta_grp${n}.txt" - rm -f "${DATA}/${RUN}_restartb_grp${n}.txt" - touch "${DATA}/${RUN}_grp${n}.txt" - touch "${DATA}/${RUN}_restarta_grp${n}.txt" - touch "${DATA}/${RUN}_restartb_grp${n}.txt" - - m=1 - while (( m <= NMEM_EARCGRP && (n-1)*NMEM_EARCGRP+m <= NMEM_ENS )); do - nm=$(((n-1)*NMEM_EARCGRP+m)) - mem=$(printf %03i ${nm}) - head="${RUN}.t${cyc}z." - - MEMDIR="mem${mem}" YMD=${PDY} HH=${cyc} declare_from_tmpl \ - COM_ATMOS_ANALYSIS_MEM:COM_ATMOS_ANALYSIS_TMPL \ - COM_ATMOS_RESTART_MEM:COM_ATMOS_RESTART_TMPL \ - COM_ATMOS_HISTORY_MEM:COM_ATMOS_HISTORY_TMPL - - #--- - for FHR in "${nfhrs[@]}"; do # loop over analysis times in window - if [ "${FHR}" -eq 6 ]; then - { - if (( n <= NTARS2 )); then - if [[ -s "${COM_ATMOS_ANALYSIS_MEM}/${head}atmanl.nc" ]] ; then - echo "${COM_ATMOS_ANALYSIS_MEM/${ROTDIR}\//}/${head}atmanl.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS_MEM}/${head}ratminc.nc" ]] ; then - echo "${COM_ATMOS_ANALYSIS_MEM/${ROTDIR}\//}/${head}ratminc.nc" - fi - fi - } >> "${DATA}/${RUN}_grp${n}.txt" - - if [[ -s "${COM_ATMOS_ANALYSIS_MEM}/${head}ratminc.nc" ]] ; then - echo "${COM_ATMOS_ANALYSIS_MEM/${ROTDIR}\//}/${head}ratminc.nc" \ - >> "${DATA}/${RUN}_restarta_grp${n}.txt" - fi - - else - { - if (( n <= NTARS2 )); then - if [[ -s "${COM_ATMOS_ANALYSIS_MEM}/${head}atma00${FHR}.nc" ]] ; then - echo "${COM_ATMOS_ANALYSIS_MEM/${ROTDIR}\//}/${head}atma00${FHR}.nc" - fi - if [[ -s "${COM_ATMOS_ANALYSIS_MEM}/${head}ratmi00${FHR}.nc" ]] ; then - echo "${COM_ATMOS_ANALYSIS_MEM/${ROTDIR}\//}/${head}ratmi00${FHR}.nc" - fi - fi - } >> "${DATA}/${RUN}_grp${n}.txt" - if [[ -s "${COM_ATMOS_ANALYSIS_MEM}/${head}ratmi00${FHR}.nc" ]] ; then - echo "${COM_ATMOS_ANALYSIS_MEM/${ROTDIR}\//}/${head}ratmi00${FHR}.nc" \ - >> "${DATA}/${RUN}_restarta_grp${n}.txt" - fi - fi - { - echo "${COM_ATMOS_HISTORY_MEM/${ROTDIR}\//}/${head}atmf00${FHR}.nc" - if (( FHR == 6 )); then - echo "${COM_ATMOS_HISTORY_MEM/${ROTDIR}\//}/${head}sfcf00${FHR}.nc" - fi - } >> "${DATA}/${RUN}_grp${n}.txt" - done # loop over FHR - - if [[ ${lobsdiag_forenkf} == ".false." ]] ; then - { - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}gsistat" - if [[ -s "${COM_ATMOS_RESTART_MEM}/${head}cnvstat" ]] ; then - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}cnvstat" - fi - } >> "${DATA}/${RUN}_grp${n}.txt" - - { - if [[ -s "${COM_ATMOS_RESTART_MEM}/${head}radstat" ]]; then - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}radstat" - fi - if [[ -s "${COM_ATMOS_RESTART_MEM}/${head}cnvstat" ]]; then - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}cnvstat" - fi - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}abias" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}abias_air" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}abias_int" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/${head}abias_pc" - } >> "${DATA}/${RUN}_restarta_grp${n}.txt" - fi - #--- - { - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/*0000.sfcanl_data.tile1.nc" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/*0000.sfcanl_data.tile2.nc" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/*0000.sfcanl_data.tile3.nc" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/*0000.sfcanl_data.tile4.nc" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/*0000.sfcanl_data.tile5.nc" - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}/*0000.sfcanl_data.tile6.nc" - } >> "${DATA}/${RUN}_restarta_grp${n}.txt" - #--- - echo "${COM_ATMOS_RESTART_MEM/${ROTDIR}\//}" >> "${DATA}/${RUN}_restartb_grp${n}.txt" - - m=$((m+1)) - done - - - #........................... - n=$((n+1)) - done - #........................... - - -#----------------------------------------------------- -fi ##end of enkfgdas or enkfgfs -#----------------------------------------------------- - -exit 0 diff --git a/ush/python/pygfs/task/analysis.py b/ush/python/pygfs/task/analysis.py index 5464c25370..b668ac3980 100644 --- a/ush/python/pygfs/task/analysis.py +++ b/ush/python/pygfs/task/analysis.py @@ -335,6 +335,7 @@ def tgz_diags(statfile: str, diagdir: str) -> None: # get list of diag files to put in tarball diags = glob.glob(os.path.join(diagdir, 'diags', 'diag*nc')) + diags.extend(glob.glob(os.path.join(diagdir, 'diags', 'diag*nc4'))) logger.info(f"Compressing {len(diags)} diag files to {statfile}") diff --git a/ush/python/pygfs/task/archive.py b/ush/python/pygfs/task/archive.py new file mode 100644 index 0000000000..d66b02d7bc --- /dev/null +++ b/ush/python/pygfs/task/archive.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python3 + +import glob +import os +import shutil +import tarfile +from logging import getLogger +from typing import Any, Dict, List + +from wxflow import (AttrDict, FileHandler, Hsi, Htar, Task, cast_strdict_as_dtypedict, + chgrp, get_gid, logit, mkdir_p, parse_j2yaml, rm_p, strftime, + to_YMD, to_YMDH, Template, TemplateConstants) + +logger = getLogger(__name__.split('.')[-1]) + + +class Archive(Task): + """Task to archive ROTDIR data to HPSS (or locally) + """ + + @logit(logger, name="Archive") + def __init__(self, config: Dict[str, Any]) -> None: + """Constructor for the Archive task + The constructor is responsible for collecting necessary yamls based on + the runtime options and RUN. + + Parameters + ---------- + config : Dict[str, Any] + Incoming configuration for the task from the environment + + Returns + ------- + None + """ + super().__init__(config) + + rotdir = self.config.ROTDIR + os.sep + + # Find all absolute paths in the environment and get their relative paths from ${ROTDIR} + path_dict = self._gen_relative_paths(rotdir) + + self.task_config = AttrDict(**self.config, **self.runtime_config, **path_dict) + + @logit(logger) + def configure(self, arch_dict: Dict[str, Any]) -> (Dict[str, Any], List[Dict[str, Any]]): + """Determine which tarballs will need to be created. + + Parameters + ---------- + arch_dict : Dict[str, Any] + Task specific keys, e.g. runtime options (DO_AERO, DO_ICE, etc) + + Return + ------ + arcdir_set : Dict[str, Any] + Set of FileHandler instructions to copy files to the ARCDIR + atardir_sets : List[Dict[str, Any]] + List of tarballs and instructions for creating them via tar or htar + """ + + archive_parm = os.path.join(arch_dict.PARMgfs, "archive") + + # Collect the dataset to archive locally + arcdir_filename = os.path.join(archive_parm, "arcdir.yaml.j2") + + # Add the glob.glob function for capturing log filenames + # TODO remove this kludge once log filenames are explicit + arch_dict['glob'] = glob.glob + + # Add the os.path.exists function to the dict for yaml parsing + arch_dict['path_exists'] = os.path.exists + + # Parse the input jinja yaml template + arcdir_set = parse_j2yaml(arcdir_filename, arch_dict) + + # Collect datasets that need to be archived + # Each dataset represents one tarball + + if arch_dict.HPSSARCH: + self.tar_cmd = "htar" + self.hsi = Hsi() + self.htar = Htar() + self.cvf = self.htar.cvf + self.rm_cmd = self.hsi.rm + self.chgrp_cmd = self.hsi.chgrp + self.chmod_cmd = self.hsi.chmod + elif arch_dict.LOCALARCH: + self.tar_cmd = "tar" + self.cvf = Archive._create_tarball + self.chgrp_cmd = chgrp + self.chmod_cmd = os.chmod + self.rm_cmd = rm_p + else: # Only perform local archiving. Do not create tarballs. + self.tar_cmd = "" + return arcdir_set, [] + + 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 == "gdas" or arch_dict.RUN == "gfs": + + # Copy the cyclone track files and rename the experiments + Archive._rename_cyclone_expt(arch_dict) + + if arch_dict.RUN == "gefs": + raise NotImplementedError("FATAL ERROR: Archiving is not yet set up for GEFS runs") + + master_yaml = "master_" + arch_dict.RUN + ".yaml.j2" + + parsed_sets = parse_j2yaml(os.path.join(archive_parm, master_yaml), arch_dict) + + atardir_sets = [] + + for dataset in parsed_sets.datasets.values(): + + dataset["fileset"] = Archive._create_fileset(dataset) + dataset["has_rstprod"] = Archive._has_rstprod(dataset.fileset) + + atardir_sets.append(dataset) + + return arcdir_set, atardir_sets + + @logit(logger) + def execute_store_products(self, arcdir_set: Dict[str, Any]) -> None: + """Perform local archiving of data products to ARCDIR. + + Parameters + ---------- + arcdir_set : Dict[str, Any] + FileHandler instructions to populate ARCDIR with + + Return + ------ + None + """ + + # Copy files to the local ARCDIR + for key in arcdir_set.keys(): + FileHandler(arcdir_set[key]).sync() + + @logit(logger) + def execute_backup_dataset(self, atardir_set: Dict[str, Any]) -> None: + """Create a backup tarball from a yaml dict. + + Parameters + ---------- + atardir_set: Dict[str, Any] + Dict defining set of files to backup and the target tarball. + + Return + ------ + None + """ + + # Generate tarball + if len(atardir_set.fileset) == 0: + logger.warning(f"WARNING: skipping would-be empty archive {atardir_set.target}.") + return + + if atardir_set.has_rstprod: + + try: + self.cvf(atardir_set.target, atardir_set.fileset) + # Regardless of exception type, attempt to remove the target + except Exception: + self.rm_cmd(atardir_set.target) + raise RuntimeError(f"FATAL ERROR: Failed to create restricted archive {atardir_set.target}, deleting!") + + self._protect_rstprod(atardir_set) + + else: + self.cvf(atardir_set.target, atardir_set.fileset) + + @staticmethod + @logit(logger) + def _create_fileset(atardir_set: Dict[str, Any]) -> List: + """ + Collect the list of all available files from the parsed yaml dict. + Globs are expanded and if required files are missing, an error is + raised. + + TODO: expand all globs in the jinja yaml files instead of expanding + them here and issue errors here if globbing patterns (*, ?, []) + are found. + + Parameters + ---------- + atardir_set: Dict + Contains full paths for required and optional files to be archived. + """ + + fileset = [] + if "required" in atardir_set: + if atardir_set.required is not None: + for item in atardir_set.required: + glob_set = glob.glob(item) + if len(glob_set) == 0: + raise FileNotFoundError(f"FATAL ERROR: Required file, directory, or glob {item} not found!") + for entry in glob_set: + fileset.append(entry) + + if "optional" in atardir_set: + if atardir_set.optional is not None: + for item in atardir_set.optional: + glob_set = glob.glob(item) + if len(glob_set) == 0: + logger.warning(f"WARNING: optional file/glob {item} not found!") + else: + for entry in glob_set: + fileset.append(entry) + + return fileset + + @staticmethod + @logit(logger) + def _has_rstprod(fileset: List) -> bool: + """ + Checks if any files in the input fileset belongs to rstprod. + + Parameters + ---------- + fileset : List + List of filenames to check. + """ + + try: + rstprod_gid = get_gid("rstprod") + except KeyError: + # rstprod does not exist on this machine + return False + + # Expand globs and check each file for group ownership + for file_or_glob in fileset: + glob_set = glob.glob(file_or_glob) + for filename in glob_set: + if os.stat(filename).st_gid == rstprod_gid: + return True + + return False + + @logit(logger) + def _protect_rstprod(self, atardir_set: Dict[str, any]) -> None: + """ + Changes the group of the target tarball to rstprod and the permissions to + 640. If this fails for any reason, attempt to delete the file before exiting. + + """ + + try: + self.chgrp_cmd("rstprod", atardir_set.target) + if self.tar_cmd == "htar": + self.chmod_cmd("640", atardir_set.target) + else: + self.chmod_cmd(atardir_set.target, 0o640) + # Regardless of exception type, attempt to remove the target + except Exception: + try: + self.rm_cmd(atardir_set.target) + finally: + raise RuntimeError(f"FATAL ERROR: Failed to protect {atardir_set.target}!\n" + f"Please verify that it has been deleted!!") + + @staticmethod + @logit(logger) + def _create_tarball(target: str, fileset: List) -> None: + """Method to create a local tarball. + + Parameters + ---------- + target : str + Tarball to create + + file_list : List + List of files to add to an archive + """ + + # TODO create a set of tar helper functions in wxflow + # Attempt to create the parent directory if it does not exist + mkdir_p(os.path.dirname(os.path.realpath(target))) + + # Create the archive + with tarfile.open(target, "w") as tarball: + for filename in fileset: + tarball.add(filename) + + @logit(logger) + def _gen_relative_paths(self, root_path: str) -> Dict: + """Generate a dict of paths in self.config relative to root_path + + Parameters + ---------- + root_path : str + Path to base all relative paths off of + + Return + ------ + rel_path_dict : Dict + Dictionary of paths relative to root_path. Members will be named + based on the dict names in self.config. For COM paths, the names will + follow COM_ --> _dir. For all other directories, the + names will follow --> _dir. + """ + + rel_path_dict = {} + for key, value in self.config.items(): + if isinstance(value, str): + if root_path in value: + rel_path = value.replace(root_path, "") + rel_key = (key[4:] if key.startswith("COM_") else key).lower() + "_dir" + rel_path_dict[rel_key] = rel_path + + return rel_path_dict + + @staticmethod + @logit(logger) + def _rename_cyclone_expt(arch_dict) -> None: + + # Rename the experiment in the tracker files from "AVNO" to the + # first 4 letters of PSLOT. + pslot4 = arch_dict.PSLOT.upper() + if len(arch_dict.PSLOT) > 4: + pslot4 = arch_dict.PSLOT[0:4].upper() + + track_dir = arch_dict.COM_ATMOS_TRACK + run = arch_dict.RUN + cycle_HH = strftime(arch_dict.current_cycle, "%H") + + if run == "gfs": + in_track_file = (track_dir + "/avno.t" + + cycle_HH + "z.cycle.trackatcfunix") + in_track_p_file = (track_dir + "/avnop.t" + + cycle_HH + "z.cycle.trackatcfunixp") + elif run == "gdas": + in_track_file = (track_dir + "/gdas.t" + + cycle_HH + "z.cycle.trackatcfunix") + in_track_p_file = (track_dir + "/gdasp.t" + + cycle_HH + "z.cycle.trackatcfunixp") + + if not os.path.isfile(in_track_file): + # Do not attempt to archive the outputs + return + + out_track_file = track_dir + "/atcfunix." + run + "." + to_YMDH(arch_dict.current_cycle) + out_track_p_file = track_dir + "/atcfunixp." + run + "." + to_YMDH(arch_dict.current_cycle) + + def replace_string_from_to_file(filename_in, filename_out, search_str, replace_str): + + """Write a new file from the contents of an input file while searching + and replacing ASCII strings. To prevent partial file creation, a + temporary file is created and moved to the final location only + after the search/replace is finished. + + Parameters + ---------- + filename_in : str + Input filename + + filename_out : str + Output filename + + search_str : str + ASCII string to search for + + replace_str : str + ASCII string to replace the search_str with + """ + with open(filename_in) as old_file: + lines = old_file.readlines() + + out_lines = [line.replace(search, replace) for line in lines] + + with open("/tmp/track_file", "w") as new_file: + new_file.writelines(out_lines) + + 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) + + return diff --git a/workflow/applications/applications.py b/workflow/applications/applications.py index 3a8d23f744..adfab16496 100644 --- a/workflow/applications/applications.py +++ b/workflow/applications/applications.py @@ -77,6 +77,14 @@ def __init__(self, conf: Configuration) -> None: elif wave_cdump in ['gfs', 'gdas']: self.wave_cdumps = [wave_cdump] + self.aero_anl_cdumps = None + if self.do_aero: + aero_anl_cdump = _base.get('AERO_ANL_CDUMP', 'BOTH').lower() + if aero_anl_cdump in ['both']: + self.aero_anl_cdumps = ['gfs', 'gdas'] + elif aero_anl_cdump in ['gfs', 'gdas']: + self.aero_anl_cdumps = [aero_anl_cdump] + def _init_finalize(self, conf: Configuration): print("Finalizing initialize") diff --git a/workflow/applications/gfs_cycled.py b/workflow/applications/gfs_cycled.py index 4d785bc4da..ad13876528 100644 --- a/workflow/applications/gfs_cycled.py +++ b/workflow/applications/gfs_cycled.py @@ -150,9 +150,6 @@ def get_task_names(self): gdas_gfs_common_tasks_before_fcst += ['sfcanl', 'analcalc'] - if self.do_aero: - gdas_gfs_common_tasks_before_fcst += ['aeroanlinit', 'aeroanlrun', 'aeroanlfinal'] - if self.do_jedisnowda: gdas_gfs_common_tasks_before_fcst += ['prepsnowobs', 'snowanl'] @@ -179,6 +176,9 @@ def get_task_names(self): if self.do_wave and 'gdas' in self.wave_cdumps: gdas_tasks += wave_prep_tasks + if self.do_aero and 'gdas' in self.aero_anl_cdumps: + gdas_tasks += ['aeroanlinit', 'aeroanlrun', 'aeroanlfinal'] + gdas_tasks += ['atmanlupp', 'atmanlprod', 'fcst'] if self.do_upp: @@ -213,6 +213,9 @@ def get_task_names(self): if self.do_wave and 'gfs' in self.wave_cdumps: gfs_tasks += wave_prep_tasks + if self.do_aero and 'gfs' in self.aero_anl_cdumps: + gfs_tasks += ['aeroanlinit', 'aeroanlrun', 'aeroanlfinal'] + gfs_tasks += ['atmanlupp', 'atmanlprod', 'fcst'] if self.do_ocean: diff --git a/workflow/applications/gfs_forecast_only.py b/workflow/applications/gfs_forecast_only.py index 89881af8c9..e219032551 100644 --- a/workflow/applications/gfs_forecast_only.py +++ b/workflow/applications/gfs_forecast_only.py @@ -87,7 +87,9 @@ def get_task_names(self): tasks = ['stage_ic'] if self.do_aero: - tasks += ['aerosol_init'] + aero_fcst_cdump = _base.get('AERO_FCST_CDUMP', 'BOTH').lower() + if self._base['CDUMP'] in aero_fcst_cdump or aero_fcst_cdump == "both": + tasks += ['aerosol_init'] if self.do_wave: tasks += ['waveinit'] diff --git a/workflow/rocoto/gfs_tasks.py b/workflow/rocoto/gfs_tasks.py index 6125a33dec..d61060a596 100644 --- a/workflow/rocoto/gfs_tasks.py +++ b/workflow/rocoto/gfs_tasks.py @@ -829,7 +829,7 @@ def _fcst_forecast_only(self): dep_dict = {'type': 'task', 'name': f'{self.cdump}{wave_job}'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_aero: + if self.app_config.do_aero and self.cdump in self.app_config.aero_fcst_cdumps: # Calculate offset based on CDUMP = gfs | gdas interval = None if self.cdump in ['gfs']: @@ -873,7 +873,7 @@ def _fcst_cycled(self): dep_dict = {'type': 'task', 'name': f'{self.cdump}ocnanalpost'} dependencies.append(rocoto.add_dependency(dep_dict)) - if self.app_config.do_aero: + if self.app_config.do_aero and self.cdump in self.app_config.aero_anl_cdumps: dep_dict = {'type': 'task', 'name': f'{self.cdump}aeroanlfinal'} dependencies.append(rocoto.add_dependency(dep_dict)) diff --git a/workflow/setup_expt.py b/workflow/setup_expt.py index 224f182343..19235f9486 100755 --- a/workflow/setup_expt.py +++ b/workflow/setup_expt.py @@ -29,6 +29,235 @@ def makedirs_if_missing(dirname): os.makedirs(dirname) +def fill_ROTDIR(host, inputs): + """ + Method to populate the ROTDIR for supported modes. + INPUTS: + host: host object from class Host + inputs: user inputs to setup_expt.py + """ + + fill_modes = { + 'cycled': fill_ROTDIR_cycled, + 'forecast-only': fill_ROTDIR_forecasts + } + + try: + fill_modes[inputs.mode](host, inputs) + except KeyError: + raise NotImplementedError(f'{inputs.mode} is not a supported mode.\n' + + 'Currently supported modes are:\n' + + f'{" | ".join(fill_modes.keys())}') + + return + + +def fill_ROTDIR_cycled(host, inputs): + """ + Implementation of 'fill_ROTDIR' for cycled mode + """ + + rotdir = os.path.join(inputs.comroot, inputs.pslot) + + do_ocean = do_ice = do_med = False + + if 'S2S' in inputs.app: + do_ocean = do_ice = do_med = True + + if inputs.icsdir is None: + warnings.warn("User did not provide '--icsdir' to stage initial conditions") + return + + rdatestr = datetime_to_YMDH(inputs.idate - to_timedelta('T06H')) + idatestr = datetime_to_YMDH(inputs.idate) + + # Test if we are using the new COM structure or the old flat one for ICs + if inputs.start in ['warm']: + pathstr = os.path.join(inputs.icsdir, f'{inputs.cdump}.{rdatestr[:8]}', + rdatestr[8:], 'model_data', 'atmos') + else: + pathstr = os.path.join(inputs.icsdir, f'{inputs.cdump}.{idatestr[:8]}', + idatestr[8:], 'model_data', 'atmos') + + if os.path.isdir(pathstr): + flat_structure = False + else: + flat_structure = True + + # Destination always uses the new COM structure + # These should match the templates defined in config.com + if inputs.start in ['warm']: + dst_atm_dir = os.path.join('model_data', 'atmos', 'restart') + dst_med_dir = os.path.join('model_data', 'med', 'restart') + else: + dst_atm_dir = os.path.join('model_data', 'atmos', 'input') + dst_med_dir = '' # no mediator files for a "cold start" + do_med = False + dst_ocn_rst_dir = os.path.join('model_data', 'ocean', 'restart') + dst_ocn_anl_dir = os.path.join('analysis', 'ocean') + dst_ice_rst_dir = os.path.join('model_data', 'ice', 'restart') + dst_ice_anl_dir = os.path.join('analysis', 'ice') + dst_atm_anl_dir = os.path.join('analysis', 'atmos') + + if flat_structure: + # ICs are in the old flat COM structure + if inputs.start in ['warm']: # This is warm start experiment + src_atm_dir = os.path.join('atmos', 'RESTART') + src_med_dir = os.path.join('med', 'RESTART') + elif inputs.start in ['cold']: # This is a cold start experiment + src_atm_dir = os.path.join('atmos', 'INPUT') + src_med_dir = '' # no mediator files for a "cold start" + do_med = False + # ocean and ice have the same filenames for warm and cold + src_ocn_rst_dir = os.path.join('ocean', 'RESTART') + src_ocn_anl_dir = 'ocean' + src_ice_rst_dir = os.path.join('ice', 'RESTART') + src_ice_anl_dir = dst_ice_anl_dir + src_atm_anl_dir = 'atmos' + else: + src_atm_dir = dst_atm_dir + src_med_dir = dst_med_dir + src_ocn_rst_dir = dst_ocn_rst_dir + src_ocn_anl_dir = dst_ocn_anl_dir + src_ice_rst_dir = dst_ice_rst_dir + src_ice_anl_dir = dst_ice_anl_dir + src_atm_anl_dir = dst_atm_anl_dir + + def link_files_from_src_to_dst(src_dir, dst_dir): + files = os.listdir(src_dir) + for fname in files: + os.symlink(os.path.join(src_dir, fname), + os.path.join(dst_dir, fname)) + return + + # Link ensemble member initial conditions + if inputs.nens > 0: + previous_cycle_dir = f'enkf{inputs.cdump}.{rdatestr[:8]}/{rdatestr[8:]}' + current_cycle_dir = f'enkf{inputs.cdump}.{idatestr[:8]}/{idatestr[8:]}' + + for ii in range(1, inputs.nens + 1): + memdir = f'mem{ii:03d}' + # Link atmospheric files + if inputs.start in ['warm']: + dst_dir = os.path.join(rotdir, previous_cycle_dir, memdir, dst_atm_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, memdir, src_atm_dir) + elif inputs.start in ['cold']: + dst_dir = os.path.join(rotdir, current_cycle_dir, memdir, dst_atm_dir) + src_dir = os.path.join(inputs.icsdir, current_cycle_dir, memdir, src_atm_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # Link ocean files + if do_ocean: + dst_dir = os.path.join(rotdir, previous_cycle_dir, memdir, dst_ocn_rst_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, memdir, src_ocn_rst_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # First 1/2 cycle needs a MOM6 increment + incfile = f'enkf{inputs.cdump}.t{idatestr[8:]}z.ocninc.nc' + src_file = os.path.join(inputs.icsdir, current_cycle_dir, memdir, src_ocn_anl_dir, incfile) + dst_file = os.path.join(rotdir, current_cycle_dir, memdir, dst_ocn_anl_dir, incfile) + makedirs_if_missing(os.path.join(rotdir, current_cycle_dir, memdir, dst_ocn_anl_dir)) + os.symlink(src_file, dst_file) + + # Link ice files + if do_ice: + dst_dir = os.path.join(rotdir, previous_cycle_dir, memdir, dst_ice_rst_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, memdir, src_ice_rst_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # Link mediator files + if do_med: + dst_dir = os.path.join(rotdir, previous_cycle_dir, memdir, dst_med_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, memdir, src_med_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # Link deterministic initial conditions + previous_cycle_dir = f'{inputs.cdump}.{rdatestr[:8]}/{rdatestr[8:]}' + current_cycle_dir = f'{inputs.cdump}.{idatestr[:8]}/{idatestr[8:]}' + + # Link atmospheric files + if inputs.start in ['warm']: + dst_dir = os.path.join(rotdir, previous_cycle_dir, dst_atm_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, src_atm_dir) + elif inputs.start in ['cold']: + dst_dir = os.path.join(rotdir, current_cycle_dir, dst_atm_dir) + src_dir = os.path.join(inputs.icsdir, current_cycle_dir, src_atm_dir) + + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # Link ocean files + if do_ocean: + dst_dir = os.path.join(rotdir, previous_cycle_dir, dst_ocn_rst_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, src_ocn_rst_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # First 1/2 cycle needs a MOM6 increment + incfile = f'{inputs.cdump}.t{idatestr[8:]}z.ocninc.nc' + src_file = os.path.join(inputs.icsdir, current_cycle_dir, src_ocn_anl_dir, incfile) + dst_file = os.path.join(rotdir, current_cycle_dir, dst_ocn_anl_dir, incfile) + makedirs_if_missing(os.path.join(rotdir, current_cycle_dir, dst_ocn_anl_dir)) + os.symlink(src_file, dst_file) + + # Link ice files + if do_ice: + # First 1/2 cycle needs a CICE6 analysis restart + src_dir = os.path.join(inputs.icsdir, current_cycle_dir, src_ice_anl_dir) + dst_dir = os.path.join(rotdir, current_cycle_dir, src_ice_anl_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # Link mediator files + if do_med: + dst_dir = os.path.join(rotdir, previous_cycle_dir, dst_med_dir) + src_dir = os.path.join(inputs.icsdir, previous_cycle_dir, src_med_dir) + makedirs_if_missing(dst_dir) + link_files_from_src_to_dst(src_dir, dst_dir) + + # Link bias correction and radiance diagnostics files + src_dir = os.path.join(inputs.icsdir, current_cycle_dir, src_atm_anl_dir) + dst_dir = os.path.join(rotdir, current_cycle_dir, dst_atm_anl_dir) + makedirs_if_missing(dst_dir) + for ftype in ['abias', 'abias_pc', 'abias_air', 'radstat']: + fname = f'{inputs.cdump}.t{idatestr[8:]}z.{ftype}' + src_file = os.path.join(src_dir, fname) + if os.path.exists(src_file): + os.symlink(src_file, os.path.join(dst_dir, fname)) + # First 1/2 cycle also needs a atmos increment if doing warm start + if inputs.start in ['warm']: + for ftype in ['atmi003.nc', 'atminc.nc', 'atmi009.nc']: + fname = f'{inputs.cdump}.t{idatestr[8:]}z.{ftype}' + src_file = os.path.join(src_dir, fname) + if os.path.exists(src_file): + os.symlink(src_file, os.path.join(dst_dir, fname)) + if inputs.nens > 0: + current_cycle_dir = f'enkf{inputs.cdump}.{idatestr[:8]}/{idatestr[8:]}' + for ii in range(1, inputs.nens + 1): + memdir = f'mem{ii:03d}' + src_dir = os.path.join(inputs.icsdir, current_cycle_dir, memdir, src_atm_anl_dir) + dst_dir = os.path.join(rotdir, current_cycle_dir, memdir, dst_atm_anl_dir) + makedirs_if_missing(dst_dir) + for ftype in ['ratmi003.nc', 'ratminc.nc', 'ratmi009.nc']: + fname = f'enkf{inputs.cdump}.t{idatestr[8:]}z.{ftype}' + src_file = os.path.join(src_dir, fname) + if os.path.exists(src_file): + os.symlink(src_file, os.path.join(dst_dir, fname)) + + return + + +def fill_ROTDIR_forecasts(host, inputs): + """ + Implementation of 'fill_ROTDIR' for forecast-only mode + """ + print('forecast-only mode treats ICs differently and cannot be staged here') + + def fill_EXPDIR(inputs): """ Method to copy config files from workflow to experiment directory