diff --git a/env/HERA.env b/env/HERA.env index db63f0bfa5..b743a19a62 100755 --- a/env/HERA.env +++ b/env/HERA.env @@ -140,13 +140,13 @@ elif [[ "${step}" = "ocnanalecen" ]]; then [[ ${NTHREADS_OCNANALECEN} -gt ${nth_max} ]] && export NTHREADS_OCNANALECEN=${nth_max} export APRUN_OCNANALECEN="${launcher} -n ${npe_ocnanalecen} --cpus-per-task=${NTHREADS_OCNANALECEN}" -elif [[ "${step}" = "ocnanalletkf" ]]; then +elif [[ "${step}" = "marineanalletkf" ]]; then - nth_max=$((npe_node_max / npe_node_ocnanalletkf)) + nth_max=$((npe_node_max / npe_node_marineanalletkf)) - export NTHREADS_OCNANALLETKF=${nth_ocnanalletkf:-${nth_max}} - [[ ${NTHREADS_OCNANALLETKF} -gt ${nth_max} ]] && export NTHREADS_OCNANALLETKF=${nth_max} - export APRUN_OCNANALLETKF="${launcher} -n ${npe_ocnanalletkf} --cpus-per-task=${NTHREADS_OCNANALLETKF}" + export NTHREADS_MARINEANALLETKF=${nth_marineanalletkf:-${nth_max}} + [[ ${NTHREADS_MARINEANALLETKF} -gt ${nth_max} ]] && export NTHREADS_MARINEANALLETKF=${nth_max} + export APRUN_MARINEANALLETKF="${launcher} -n ${npe_marineanalletkf} --cpus-per-task=${NTHREADS_MARINEANALLETKF}" elif [[ "${step}" = "anal" ]] || [[ "${step}" = "analcalc" ]]; then diff --git a/env/ORION.env b/env/ORION.env index 502e99e192..c203acae48 100755 --- a/env/ORION.env +++ b/env/ORION.env @@ -148,13 +148,13 @@ elif [[ "${step}" = "ocnanalecen" ]]; then [[ ${NTHREADS_OCNANALECEN} -gt ${nth_max} ]] && export NTHREADS_OCNANALECEN=${nth_max} export APRUN_OCNANALECEN="${launcher} -n ${npe_ocnanalecen} --cpus-per-task=${NTHREADS_OCNANALECEN}" -elif [[ "${step}" = "ocnanalletkf" ]]; then +elif [[ "${step}" = "marineanalletkf" ]]; then - nth_max=$((npe_node_max / npe_node_ocnanalletkf)) + nth_max=$((npe_node_max / npe_node_marineanalletkf)) - export NTHREADS_OCNANALLETKF=${nth_ocnanalletkf:-${nth_max}} - [[ ${NTHREADS_OCNANALLETKF} -gt ${nth_max} ]] && export NTHREADS_OCNANALLETKF=${nth_max} - export APRUN_OCNANALLETKF="${launcher} -n ${npe_ocnanalletkf} --cpus-per-task=${NTHREADS_OCNANALLETKF}" + export NTHREADS_MARINEANALLETKF=${nth_marineanalletkf:-${nth_max}} + [[ ${NTHREADS_MARINEANALLETKF} -gt ${nth_max} ]] && export NTHREADS_MARINEANALLETKF=${nth_max} + export APRUN_MARINEANALLETKF="${launcher} -n ${npe_marineanalletkf} --cpus-per-task=${NTHREADS_MARINEANALLETKF}" elif [[ "${step}" = "anal" ]] || [[ "${step}" = "analcalc" ]]; then diff --git a/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_LETKF b/jobs/JGLOBAL_MARINE_ANALYSIS_LETKF similarity index 82% rename from jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_LETKF rename to jobs/JGLOBAL_MARINE_ANALYSIS_LETKF index d03ddfc19a..38dc3049f9 100755 --- a/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_LETKF +++ b/jobs/JGLOBAL_MARINE_ANALYSIS_LETKF @@ -1,6 +1,6 @@ #!/bin/bash source "${HOMEgfs}/ush/preamble.sh" -source "${HOMEgfs}/ush/jjob_header.sh" -e "ocnanalletkf" -c "base ocnanal ocnanalletkf" +source "${HOMEgfs}/ush/jjob_header.sh" -e "marineanalletkf" -c "base ocnanal marineanalletkf" ############################################## # Set variables used in the script @@ -13,8 +13,10 @@ gPDY=${GDATE:0:8} gcyc=${GDATE:8:2} YMD=${gPDY} HH=${gcyc} declare_from_tmpl -rx \ - COM_OCEAN_HISTORY_PREV:COM_OCEAN_HISTORY_TMPL \ - COM_ICE_HISTORY_PREV:COM_ICE_HISTORY_TMPL + COMIN_OCEAN_HISTORY_PREV:COM_OCEAN_HISTORY_TMPL \ + COMIN_ICE_HISTORY_PREV:COM_ICE_HISTORY_TMPL + +YMD=${PDY} HH=${cyc} declare_from_tmpl -rx COMIN_OBS:COM_OBS_TMPL ############################################## # Begin JOB SPECIFIC work diff --git a/jobs/rocoto/ocnanalletkf.sh b/jobs/rocoto/marineanalletkf.sh similarity index 87% rename from jobs/rocoto/ocnanalletkf.sh rename to jobs/rocoto/marineanalletkf.sh index f710be5710..f2bfb9f70c 100755 --- a/jobs/rocoto/ocnanalletkf.sh +++ b/jobs/rocoto/marineanalletkf.sh @@ -8,7 +8,7 @@ source "${HOMEgfs}/ush/preamble.sh" status=$? [[ ${status} -ne 0 ]] && exit "${status}" -export job="ocnanalletkf" +export job="marineanalletkf" export jobid="${job}.$$" ############################################################### @@ -18,6 +18,6 @@ export PYTHONPATH ############################################################### # Execute the JJOB -"${HOMEgfs}/jobs/JGDAS_GLOBAL_OCEAN_ANALYSIS_LETKF" +"${HOMEgfs}/jobs/JGLOBAL_MARINE_ANALYSIS_LETKF" status=$? exit "${status}" diff --git a/parm/archive/master_enkf.yaml.j2 b/parm/archive/master_enkf.yaml.j2 index f663d02895..70f8a2ad89 100644 --- a/parm/archive/master_enkf.yaml.j2 +++ b/parm/archive/master_enkf.yaml.j2 @@ -6,108 +6,111 @@ # Split IAUFHRS into a list; typically either "3,6,9" or 6 (integer) {% if IAUFHRS is string %} -# "3,6,9" -{% set iaufhrs = [] %} -{% for iaufhr in IAUFHRS.split(",") %} -{% do iaufhrs.append(iaufhr | int) %} -{% endfor %} + # "3,6,9" + {% set iaufhrs = [] %} + {% for iaufhr in IAUFHRS.split(",") %} + {% do iaufhrs.append(iaufhr | int) %} + {% endfor %} {% else %} -# 6 (integer) -{% set iaufhrs = [IAUFHRS] %} + # 6 (integer) + {% set iaufhrs = [IAUFHRS] %} {% endif %} # Repeat for IAUFHRS_ENKF {% if IAUFHRS_ENKF is string %} -{% set iaufhrs_enkf = [] %} -{% for iaufhr in IAUFHRS_ENKF.split(",") %} -{% do iaufhrs_enkf.append(iaufhr | int) %} -{% endfor %} + {% set iaufhrs_enkf = [] %} + {% for iaufhr in IAUFHRS_ENKF.split(",") %} + {% do iaufhrs_enkf.append(iaufhr | int) %} + {% endfor %} {% else %} -{% set iaufhrs_enkf = [IAUFHRS_ENKF] %} + {% set iaufhrs_enkf = [IAUFHRS_ENKF] %} {% endif %} # Determine which data to archive datasets: {% if ENSGRP == 0 %} -{% filter indent(width=4) %} -# Archive the ensemble means and spreads + {% filter indent(width=4) %} + # Archive the ensemble means and spreads {% include "enkf.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% else %} -# Archive individual member data -# First, construct individual member directories from templates -# COMIN_ATMOS_ANALYSIS_MEM, COMIN_ATMOS_HISTORY_MEM, and COMIN_ATMOS_RESTART_MEM - -# Declare to-be-filled lists of member COM directories -{% set COMIN_ATMOS_ANALYSIS_MEM_list = [] %} -{% set COMIN_ATMOS_RESTART_MEM_list = [] %} -{% set COMIN_ATMOS_HISTORY_MEM_list = [] %} - -# Determine which ensemble members belong to this group -{% set first_group_mem = (ENSGRP - 1) * NMEM_EARCGRP + 1 %} -{% set last_group_mem = [ ENSGRP * NMEM_EARCGRP, nmem_ens ] | min %} - -# Construct member COM directories for the group -{% 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(COMIN_ATMOS_ANALYSIS_MEM = COM_ATMOS_ANALYSIS_TMPL, - COMIN_ATMOS_HISTORY_MEM = COM_ATMOS_HISTORY_TMPL, - COMIN_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.COMIN_ATMOS_ANALYSIS_MEM = com_ns.COMIN_ATMOS_ANALYSIS_MEM.replace(search_term, replace_term) %} -{% set com_ns.COMIN_ATMOS_HISTORY_MEM = com_ns.COMIN_ATMOS_HISTORY_MEM.replace(search_term, replace_term) %} -{% set com_ns.COMIN_ATMOS_RESTART_MEM = com_ns.COMIN_ATMOS_RESTART_MEM.replace(search_term, replace_term) %} -{% endfor %} - -# Append the member COM directories -{% do COMIN_ATMOS_ANALYSIS_MEM_list.append(com_ns.COMIN_ATMOS_ANALYSIS_MEM)%} -{% do COMIN_ATMOS_HISTORY_MEM_list.append(com_ns.COMIN_ATMOS_HISTORY_MEM)%} -{% do COMIN_ATMOS_RESTART_MEM_list.append(com_ns.COMIN_ATMOS_RESTART_MEM)%} - -{% endfor %} - -# Archive member data -{% filter indent(width=4) %} + # Archive individual member data + # First, construct individual member directories from templates + # COMIN_ATMOS_ANALYSIS_MEM, COMIN_ATMOS_HISTORY_MEM, and COMIN_ATMOS_RESTART_MEM + + # Declare to-be-filled lists of member COM directories + {% set COMIN_ATMOS_ANALYSIS_MEM_list = [] %} + {% set COMIN_ATMOS_RESTART_MEM_list = [] %} + {% set COMIN_ATMOS_HISTORY_MEM_list = [] %} + + # Determine which ensemble members belong to this group + {% set first_group_mem = (ENSGRP - 1) * NMEM_EARCGRP + 1 %} + {% set last_group_mem = [ ENSGRP * NMEM_EARCGRP, nmem_ens ] | min %} + + # Construct member COM directories for the group + {% 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(COMIN_ATMOS_ANALYSIS_MEM = COM_ATMOS_ANALYSIS_TMPL, + COMIN_ATMOS_HISTORY_MEM = COM_ATMOS_HISTORY_TMPL, + COMIN_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.COMIN_ATMOS_ANALYSIS_MEM = + com_ns.COMIN_ATMOS_ANALYSIS_MEM.replace(search_term, replace_term) %} + {% set com_ns.COMIN_ATMOS_HISTORY_MEM = + com_ns.COMIN_ATMOS_HISTORY_MEM.replace(search_term, replace_term) %} + {% set com_ns.COMIN_ATMOS_RESTART_MEM = + com_ns.COMIN_ATMOS_RESTART_MEM.replace(search_term, replace_term) %} + {% endfor %} + + # Append the member COM directories + {% do COMIN_ATMOS_ANALYSIS_MEM_list.append(com_ns.COMIN_ATMOS_ANALYSIS_MEM)%} + {% do COMIN_ATMOS_HISTORY_MEM_list.append(com_ns.COMIN_ATMOS_HISTORY_MEM)%} + {% do COMIN_ATMOS_RESTART_MEM_list.append(com_ns.COMIN_ATMOS_RESTART_MEM)%} + + {% endfor %} + + # Archive member data + {% filter indent(width=4) %} {% include "enkf_grp.yaml.j2" %} -{% endfilter %} + {% endfilter %} -# Determine if restarts should be saved -{% set save_warm_start_forecast, save_warm_start_cycled = ( False, False ) %} + # 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 %} -{% filter indent(width=4) %} + # 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 %} + {% 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 %} -{% filter indent(width=4) %} + {% 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 %} + {% filter indent(width=4) %} {% include "enkf_restartb_grp.yaml.j2" %} -{% endfilter %} -{% endif %} -{% endif %} + {% endfilter %} + {% endif %} + {% endif %} -# End of individual member archiving + # End of individual member archiving {% endif %} diff --git a/parm/archive/master_gdas.yaml.j2 b/parm/archive/master_gdas.yaml.j2 index f25fd9de40..30a2175653 100644 --- a/parm/archive/master_gdas.yaml.j2 +++ b/parm/archive/master_gdas.yaml.j2 @@ -5,12 +5,12 @@ # Split IAUFHRS into a list; typically either "3,6,9" or 6 (integer) {% if IAUFHRS is string %} -{% set iaufhrs = [] %} -{% for iaufhr in IAUFHRS.split(",") %} -{% do iaufhrs.append(iaufhr | int) %} -{% endfor %} + {% set iaufhrs = [] %} + {% for iaufhr in IAUFHRS.split(",") %} + {% do iaufhrs.append(iaufhr | int) %} + {% endfor %} {% else %} -{% set iaufhrs = [IAUFHRS] %} + {% set iaufhrs = [IAUFHRS] %} {% endif %} datasets: @@ -20,84 +20,90 @@ datasets: {% endfilter %} {% if DO_ICE %} -# Ice data -{% filter indent(width=4) %} + # Ice data + {% filter indent(width=4) %} {% include "gdasice.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} {% if DO_OCN %} -# Ocean forecast products -{% filter indent(width=4) %} + # Ocean forecast products + {% filter indent(width=4) %} {% include "gdasocean.yaml.j2" %} -{% endfilter %} -{% if DO_JEDIOCNVAR and MODE == "cycled" %} -# Ocean analysis products -{% filter indent(width=4) %} + {% endfilter %} + {% if DO_JEDIOCNVAR and MODE == "cycled" %} + # Ocean analysis products + {% filter indent(width=4) %} {% include "gdasocean_analysis.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} {% endif %} {% if DO_WAVE %} -# Wave products -{% filter indent(width=4) %} + # Wave products + {% filter indent(width=4) %} {% include "gdaswave.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} {% if MODE == "cycled" %} -# Determine if we will save restart ICs or not (only valid for cycled) -{% set save_warm_start_forecast, save_warm_start_cycled = ( False, False ) %} + # Determine if we will save restart ICs or not (only valid for cycled) + {% set save_warm_start_forecast, save_warm_start_cycled = ( False, False ) %} -{% if ARCH_CYC == cycle_HH | int%} -# 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 ARCH_CYC == cycle_HH | int%} + # Save the forecast-only cycle ICs every ARCH_WARMICFREQ or ARCH_FCSTICFREQ days + {% if (current_cycle - SDATE).days % ARCH_WARMICFREQ == 0 %} + {% set save_warm_start_forecast = True %} + {% elif (current_cycle - SDATE).days % ARCH_FCSTICFREQ == 0 %} + {% set save_warm_start_forecast = True %} + {% endif %} + {% endif %} -{% if save_warm_start_forecast %} -# Save warm start forecast-only data -# Atmosphere restarts -{% filter indent(width=4) %} + # The GDAS ICs (group b) are restarts and always lag increments by assim_freq + {% if (ARCH_CYC - assim_freq) % 24 == cycle_HH | int %} + {% set ics_offset = (assim_freq | string + "H") | to_timedelta %} + {% if (current_cycle | add_to_datetime(ics_offset) - SDATE).days % ARCH_WARMICFREQ == 0 %} + {% set save_warm_start_cycled = True %} + {% endif %} + {% endif %} + + {% if save_warm_start_forecast %} + # Save warm start forecast-only data + # Atmosphere restarts + {% filter indent(width=4) %} {% include "gdas_restarta.yaml.j2" %} -{% endfilter %} + {% endfilter %} -{% if DO_WAVE %} -# Wave restarts -{% filter indent(width=4) %} + {% if DO_WAVE %} + # Wave restarts + {% filter indent(width=4) %} {% include "gdaswave_restart.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} -{% if DO_OCN %} -# Ocean restarts -{% filter indent(width=4) %} + {% if DO_OCN %} + # Ocean restarts + {% filter indent(width=4) %} {% include "gdasocean_restart.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} -{% if DO_ICE %} -# Ice restarts -{% filter indent(width=4) %} + {% if DO_ICE %} + # Ice restarts + {% filter indent(width=4) %} {% include "gdasice_restart.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} -# End of forecast-only restarts -{% endif %} + # End of forecast-only restarts + {% endif %} -{% if save_warm_start_cycled %} -# Save warm start cycled restarts -{% filter indent(width=4) %} + {% if save_warm_start_cycled %} + # Save warm start cycled restarts + {% filter indent(width=4) %} {% include "gdas_restartb.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} -# End of restart checking + # End of restart checking {% endif %} diff --git a/parm/archive/master_gfs.yaml.j2 b/parm/archive/master_gfs.yaml.j2 index 67cde482a2..14178f3e7e 100644 --- a/parm/archive/master_gfs.yaml.j2 +++ b/parm/archive/master_gfs.yaml.j2 @@ -5,14 +5,14 @@ # Split IAUFHRS into a list; typically either "3,6,9" or 6 (integer) {% if IAUFHRS is string %} -# "3,6,9" -{% set iaufhrs = [] %} -{% for iaufhr in IAUFHRS.split(",") %} -{% do iaufhrs.append(iaufhr | int) %} -{% endfor %} + # "3,6,9" + {% set iaufhrs = [] %} + {% for iaufhr in IAUFHRS.split(",") %} + {% do iaufhrs.append(iaufhr | int) %} + {% endfor %} {% else %} -# 6 (integer) -{% set iaufhrs = [IAUFHRS] %} + # 6 (integer) + {% set iaufhrs = [IAUFHRS] %} {% endif %} # Determine which data to archive @@ -24,89 +24,89 @@ datasets: {% endfilter %} {% if ARCH_GAUSSIAN %} -# Archive Gaussian data -{% filter indent(width=4) %} + # Archive Gaussian data + {% filter indent(width=4) %} {% include "gfs_flux.yaml.j2" %} {% include "gfs_netcdfb.yaml.j2" %} {% include "gfs_pgrb2b.yaml.j2" %} -{% endfilter %} -{% if MODE == "cycled" %} -# Archive Gaussian analysis data -{% filter indent(width=4) %} + {% endfilter %} + {% if MODE == "cycled" %} + # Archive Gaussian analysis data + {% filter indent(width=4) %} {% include "gfs_netcdfa.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} {% endif %} {% if DO_WAVE %} -# Wave forecasts -{% filter indent(width=4) %} + # Wave forecasts + {% filter indent(width=4) %} {% include "gfswave.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} {% if AERO_FCST_CDUMP == "gfs" or AERO_FCST_CDUMP == "both" %} -# Aerosol forecasts -{% filter indent(width=4) %} + # Aerosol forecasts + {% filter indent(width=4) %} {% include "chem.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} {% if DO_OCN %} -# Ocean forecasts -{% filter indent(width=4) %} + # Ocean forecasts + {% filter indent(width=4) %} {% include "ocean_6hravg.yaml.j2" %} {% include "ocean_grib2.yaml.j2" %} {% include "gfs_flux_1p00.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} {% if DO_ICE %} -# Ice forecasts -{% filter indent(width=4) %} + # Ice forecasts + {% filter indent(width=4) %} {% include "ice_6hravg.yaml.j2" %} {% include "ice_grib2.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} {% if DO_BUFRSND %} -# Downstream BUFR soundings -{% filter indent(width=4) %} + # Downstream BUFR soundings + {% filter indent(width=4) %} {% include "gfs_downstream.yaml.j2" %} -{% endfilter %} + {% endfilter %} {% endif %} # Determine whether to save the MOS tarball {% if DO_MOS and cycle_HH == "18" %} -{% if not REALTIME %} -{% filter indent(width=4) %} + {% if not REALTIME %} + {% filter indent(width=4) %} {% include "gfsmos.yaml.j2" %} -{% endfilter %} + {% endfilter %} -{% else %} + {% 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) %} + {% 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 %} + {% endfilter %} + {% endif %} -{% endif %} + {% endif %} {% endif %} # Determine if we will save restart ICs or not {% if ARCH_CYC == cycle_HH | int %} -# 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) %} + # 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) %} + {% endfilter %} + {% elif (current_cycle - SDATE).days % ARCH_FCSTICFREQ == 0 %} + {% filter indent(width=4) %} {% include "gfs_restarta.yaml.j2" %} -{% endfilter %} -{% endif %} + {% endfilter %} + {% endif %} {% endif %} diff --git a/parm/config/gfs/config.marineanalletkf b/parm/config/gfs/config.marineanalletkf new file mode 100644 index 0000000000..fde3433a13 --- /dev/null +++ b/parm/config/gfs/config.marineanalletkf @@ -0,0 +1,18 @@ +#!/bin/bash + +########## config.marineanalletkf ########## +# Ocn Analysis specific + +echo "BEGIN: config.marineanalletkf" + +# Get task specific resources +. "${EXPDIR}/config.resources" marineanalletkf + +export MARINE_LETKF_EXEC="${JEDI_BIN}/gdas.x" +export MARINE_LETKF_YAML_TMPL="${PARMgfs}/gdas/soca/letkf/letkf.yaml.j2" +export MARINE_LETKF_STAGE_YAML_TMPL="${PARMgfs}/gdas/soca/letkf/letkf_stage.yaml.j2" + +export GRIDGEN_EXEC="${JEDI_BIN}/gdas_soca_gridgen.x" +export GRIDGEN_YAML="${PARMgfs}/gdas/soca/gridgen/gridgen.yaml" + +echo "END: config.marineanalletkf" diff --git a/parm/config/gfs/config.ocnanal b/parm/config/gfs/config.ocnanal index 38a6cbd52a..367e570ec8 100644 --- a/parm/config/gfs/config.ocnanal +++ b/parm/config/gfs/config.ocnanal @@ -16,8 +16,8 @@ export SOCA_NINNER=@SOCA_NINNER@ export CASE_ANL=@CASE_ANL@ export DOMAIN_STACK_SIZE=116640000 #TODO: Make the stack size resolution dependent export JEDI_BIN=${HOMEgfs}/sorc/gdas.cd/build/bin - -export COMIN_OBS=@COMIN_OBS@ +export SOCA_FIX_STAGE_YAML_TMPL="${PARMgfs}/gdas/soca/soca_fix_stage.yaml.j2" +export SOCA_ENS_BKG_STAGE_YAML_TMPL="${PARMgfs}/gdas/soca/soca_ens_bkg_stage.yaml.j2" # NICAS export NICAS_RESOL=@NICAS_RESOL@ diff --git a/parm/config/gfs/config.ocnanalletkf b/parm/config/gfs/config.ocnanalletkf deleted file mode 100644 index b67f37152e..0000000000 --- a/parm/config/gfs/config.ocnanalletkf +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -########## config.ocnanalletkf ########## -# Ocn Analysis specific - -echo "BEGIN: config.ocnanalletkf" - -# Get task specific resources -. "${EXPDIR}/config.resources" ocnanalletkf - -echo "END: config.ocnanalletkf" diff --git a/parm/config/gfs/config.resources b/parm/config/gfs/config.resources index 5c3a100880..e16524ecd3 100644 --- a/parm/config/gfs/config.resources +++ b/parm/config/gfs/config.resources @@ -25,7 +25,7 @@ if (( $# != 1 )); then echo "waveinit waveprep wavepostsbs wavepostbndpnt wavepostbndpntbll wavepostpnt" echo "wavegempak waveawipsbulls waveawipsgridded" echo "postsnd awips gempak npoess" - echo "ocnanalprep prepoceanobs ocnanalbmat ocnanalrun ocnanalecen ocnanalletkf ocnanalchkpt ocnanalpost ocnanalvrfy" + echo "ocnanalprep prepoceanobs ocnanalbmat ocnanalrun ocnanalecen marineanalletkf ocnanalchkpt ocnanalpost ocnanalvrfy" exit 1 fi @@ -557,32 +557,32 @@ case ${step} in export memory_ocnanalecen ;; - "ocnanalletkf") + "marineanalletkf") npes=16 case ${OCNRES} in "025") npes=480 - memory_ocnanalletkf="96GB" + memory_marineanalletkf="96GB" ;; "050") npes=16 - memory_ocnanalletkf="96GB" + memory_marineanalletkf="96GB" ;; "500") npes=16 - memory_ocnanalletkf="24GB" + memory_marineanalletkf="24GB" ;; *) echo "FATAL ERROR: Resources not defined for job ${step} at resolution ${OCNRES}" exit 4 esac - export wtime_ocnanalletkf="00:10:00" - export npe_ocnanalletkf=${npes} - export nth_ocnanalletkf=1 + export wtime_marineanalletkf="00:10:00" + export npe_marineanalletkf=${npes} + export nth_marineanalletkf=1 export is_exclusive=True - export npe_node_ocnanalletkf=$(( npe_node_max / nth_ocnanalletkf )) - export memory_ocnanalletkf + export npe_node_marineanalletkf=$(( npe_node_max / nth_marineanalletkf )) + export memory_marineanalletkf ;; diff --git a/ush/python/pygfs/task/marine_letkf.py b/ush/python/pygfs/task/marine_letkf.py index 0ae5bea98d..0fdd3d9aba 100644 --- a/ush/python/pygfs/task/marine_letkf.py +++ b/ush/python/pygfs/task/marine_letkf.py @@ -1,11 +1,16 @@ #!/usr/bin/env python3 +import f90nml from logging import getLogger +import os from pygfs.task.analysis import Analysis from typing import Dict -from wxflow import (chdir, +from wxflow import (AttrDict, + FileHandler, logit, - Task) + parse_j2yaml, + to_timedelta, + to_YMDH) logger = getLogger(__name__.split('.')[-1]) @@ -30,6 +35,21 @@ def __init__(self, config: Dict) -> None: logger.info("init") super().__init__(config) + _half_assim_freq = to_timedelta(f"{self.task_config.assim_freq}H") / 2 + _letkf_yaml_file = 'letkf.yaml' + _letkf_exec_args = [self.task_config.MARINE_LETKF_EXEC, + 'soca', + 'localensembleda', + _letkf_yaml_file] + + self.task_config.WINDOW_MIDDLE = self.task_config.current_cycle + self.task_config.WINDOW_BEGIN = self.task_config.current_cycle - _half_assim_freq + self.task_config.letkf_exec_args = _letkf_exec_args + self.task_config.letkf_yaml_file = _letkf_yaml_file + self.task_config.mom_input_nml_tmpl = os.path.join(self.task_config.DATA, 'mom_input.nml.tmpl') + self.task_config.mom_input_nml = os.path.join(self.task_config.DATA, 'mom_input.nml') + self.task_config.obs_dir = os.path.join(self.task_config.DATA, 'obs') + @logit(logger) def initialize(self): """Method initialize for ocean and sea ice LETKF task @@ -43,6 +63,63 @@ def initialize(self): logger.info("initialize") + # make directories and stage ensemble background files + ensbkgconf = AttrDict() + keys = ['previous_cycle', 'current_cycle', 'DATA', 'NMEM_ENS', + 'PARMgfs', 'ROTDIR', 'COM_OCEAN_HISTORY_TMPL', 'COM_ICE_HISTORY_TMPL'] + for key in keys: + ensbkgconf[key] = self.task_config[key] + ensbkgconf.RUN = 'enkfgdas' + soca_ens_bkg_stage_list = parse_j2yaml(self.task_config.SOCA_ENS_BKG_STAGE_YAML_TMPL, ensbkgconf) + FileHandler(soca_ens_bkg_stage_list).sync() + soca_fix_stage_list = parse_j2yaml(self.task_config.SOCA_FIX_STAGE_YAML_TMPL, self.task_config) + FileHandler(soca_fix_stage_list).sync() + letkf_stage_list = parse_j2yaml(self.task_config.MARINE_LETKF_STAGE_YAML_TMPL, self.task_config) + FileHandler(letkf_stage_list).sync() + + obs_list = parse_j2yaml(self.task_config.OBS_YAML, self.task_config) + + # get the list of observations + obs_files = [] + for ob in obs_list['observers']: + obs_name = ob['obs space']['name'].lower() + obs_filename = f"{self.task_config.RUN}.t{self.task_config.cyc}z.{obs_name}.{to_YMDH(self.task_config.current_cycle)}.nc" + obs_files.append((obs_filename, ob)) + + obs_files_to_copy = [] + obs_to_use = [] + # copy obs from COMIN_OBS to DATA/obs + for obs_file, ob in obs_files: + obs_src = os.path.join(self.task_config.COMIN_OBS, obs_file) + obs_dst = os.path.join(self.task_config.DATA, self.task_config.obs_dir, obs_file) + if os.path.exists(obs_src): + obs_files_to_copy.append([obs_src, obs_dst]) + obs_to_use.append(ob) + else: + logger.warning(f"{obs_file} is not available in {self.task_config.COMIN_OBS}") + + # stage the desired obs files + FileHandler({'copy': obs_files_to_copy}).sync() + + # make the letkf.yaml + letkfconf = AttrDict() + keys = ['WINDOW_BEGIN', 'WINDOW_MIDDLE', 'RUN', 'gcyc', 'NMEM_ENS'] + for key in keys: + letkfconf[key] = self.task_config[key] + letkfconf.RUN = 'enkfgdas' + letkf_yaml = parse_j2yaml(self.task_config.MARINE_LETKF_YAML_TMPL, letkfconf) + letkf_yaml.observations.observers = obs_to_use + letkf_yaml.save(self.task_config.letkf_yaml_file) + + # swap date and stack size in mom_input.nml + domain_stack_size = self.task_config.DOMAIN_STACK_SIZE + ymdhms = [int(s) for s in self.task_config.WINDOW_BEGIN.strftime('%Y,%m,%d,%H,%M,%S').split(',')] + with open(self.task_config.mom_input_nml_tmpl, 'r') as nml_file: + nml = f90nml.read(nml_file) + nml['ocean_solo_nml']['date_init'] = ymdhms + nml['fms_nml']['domains_stack_size'] = int(domain_stack_size) + nml.write(self.task_config.mom_input_nml, force=True) # force to overwrite if necessary + @logit(logger) def run(self): """Method run for ocean and sea ice LETKF task @@ -56,8 +133,6 @@ def run(self): logger.info("run") - chdir(self.runtime_config.DATA) - @logit(logger) def finalize(self): """Method finalize for ocean and sea ice LETKF task diff --git a/workflow/rocoto/gfs_tasks.py b/workflow/rocoto/gfs_tasks.py index 55fa5a2475..530ea465c4 100644 --- a/workflow/rocoto/gfs_tasks.py +++ b/workflow/rocoto/gfs_tasks.py @@ -659,7 +659,9 @@ def ocnanalprep(self): deps = [] dep_dict = {'type': 'task', 'name': f'{self.cdump}prepoceanobs'} deps.append(rocoto.add_dependency(dep_dict)) - dependencies = rocoto.create_dependency(dep=deps) + dep_dict = {'type': 'task', 'name': 'gdasfcst', 'offset': f"-{timedelta_to_HMS(self._base['cycle_interval'])}"} + deps.append(rocoto.add_dependency(dep_dict)) + dependencies = rocoto.create_dependency(dep_condition='and', dep=deps) resources = self.get_resource('ocnanalprep') task_name = f'{self.cdump}ocnanalprep'