diff --git a/parm/jcb-gdas b/parm/jcb-gdas index 77fdc329d..c9f2d7983 160000 --- a/parm/jcb-gdas +++ b/parm/jcb-gdas @@ -1 +1 @@ -Subproject commit 77fdc329d45b54529499f4033851e50e1dee0697 +Subproject commit c9f2d79833de83f342055140cac679e6e7ec9ff7 diff --git a/parm/soca/berror/soca_ensrecenter.yaml b/parm/soca/berror/soca_ensrecenter.yaml index e3e63b7a1..b70060140 100644 --- a/parm/soca/berror/soca_ensrecenter.yaml +++ b/parm/soca/berror/soca_ensrecenter.yaml @@ -4,104 +4,60 @@ geometry: mom6_input_nml: mom_input.nml fields metadata: ./fields_metadata.yaml -date: '{{ ATM_WINDOW_BEGIN }}' +date: '{{ MARINE_WINDOW_END_ISO }}' -layers variable: [hocn] +layers variable: [sea_water_cell_thickness] # TODO(AFE) fix ice recentering in cycled da -#increment variables: [tocn, socn, uocn, vocn, ssh, hocn, cicen, hicen, hsnon] -increment variables: [tocn, socn, uocn, vocn, ssh, hocn] - -set increment variables to zero: [ssh] +increment variables: +- sea_water_potential_temperature +- sea_water_salinity +- eastward_sea_water_velocity +- northward_sea_water_velocity +set increment variables to zero: +- eastward_sea_water_velocity +- northward_sea_water_velocity vertical geometry: - date: '{{ ATM_WINDOW_BEGIN }}' + date: '{{ MARINE_WINDOW_BEGIN_ISO }}' basename: ./INPUT/ ocn_filename: MOM.res.nc read_from_file: 3 -add recentering increment: false +add recentering increment: true +recentering around deterministic: true soca increments: # Could also be states, but they are read as increments number of increments: {{ NMEM_ENS }} pattern: '%mem%' template: - date: '{{ ATM_WINDOW_BEGIN }}' + date: '{{ MARINE_WINDOW_END_ISO }}' basename: ./ens/ ocn_filename: 'ocean.%mem%.nc' -# TODO(AFE) fix ice recentering in cycled da + # TODO(AFE) fix ice recentering in cycled da # ice_filename: 'ice.%mem%.nc' read_from_file: 3 -steric height: - linear variable changes: - - linear variable change name: BalanceSOCA # Only the steric balance is applied - -#ensemble mean output: -# datadir: ./static_ens -# date: '{{ ATM_WINDOW_BEGIN }}' -# exp: ens_mean -# type: incr - -ssh output: - unbalanced: - datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' - exp: ssh_unbal_stddev - type: incr - - steric: - datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' - exp: ssh_steric_stddev - type: incr - - total: - datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' - exp: ssh_total_stddev - type: incr - - explained variance: - datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' - exp: steric_explained_variance - type: incr - - recentering error: - datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' - exp: ssh_recentering_error - type: incr - -background error output: - datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' - exp: bkgerr_stddev - type: incr - -#linear variable change: -# linear variable changes: -# - linear variable change name: BkgErrFILT -# ocean_depth_min: 500 # zero where ocean is shallower than 500m -# rescale_bkgerr: 1.0 # rescale perturbation -# efold_z: 1500.0 # Apply exponential decay -# - linear variable change name: BalanceSOCA - trajectory: # TODO(AFE) fix ice recentering in cycled da - # state variables: [tocn, socn, uocn, vocn, ssh, hocn, layer_depth, mld, cicen, hicen, hsnon] - state variables: [tocn, socn, uocn, vocn, ssh, hocn, layer_depth, mld] - date: '{{ ATM_WINDOW_BEGIN }}' - basename: ./INPUT/ - ocn_filename: MOM.res.nc + state variables: + - sea_water_potential_temperature + - sea_water_salinity + - eastward_sea_water_velocity + - northward_sea_water_velocity + - sea_water_cell_thickness + - sea_water_depth + - ocean_mixed_layer_thickness + date: '{{ MARINE_WINDOW_END_ISO }}' + basename: ./bkg/ + ocn_filename: ocean.bkg.f009.nc # TODO(AFE) fix ice recentering in cycled da #ice_filename: cice.res.nc read_from_file: 1 output increment: datadir: ./ - date: '{{ ATM_WINDOW_BEGIN }}' + date: '{{ MARINE_WINDOW_END_ISO }}' exp: trash type: incr output file: 'ocn.recenter.incr.%mem%.nc' diff --git a/parm/soca/berror/soca_hybrid_bmat.yaml b/parm/soca/berror/soca_hybrid_bmat.yaml deleted file mode 100644 index 0e2340da8..000000000 --- a/parm/soca/berror/soca_hybrid_bmat.yaml +++ /dev/null @@ -1,92 +0,0 @@ -covariance model: hybrid -components: -- covariance: - covariance model: SABER - saber central block: - saber block name: diffusion - active variables: [tocn, socn, ssh, cicen] - geometry: - mom6_input_nml: mom_input.nml - fields metadata: ./fields_metadata.yaml - group mapping: - - name: ocean - variables: - - tocn - - socn - - ssh - - name: ice - variables: - - cicen - read: - groups: - - name: ocean - horizontal: - filename: hz_ocean.nc - vertical: - filename: vt_ocean.nc - - name: ice - horizontal: - filename: hz_ice.nc - - saber outer blocks: - - saber block name: StdDev - read: - model file: - date: '{{ATM_WINDOW_MIDDLE}}' - basename: ./ - ocn_filename: 'ocean.bkgerr_stddev.nc' - ice_filename: 'ice.bkgerr_stddev.nc' - read_from_file: 3 - - linear variable change: - input variables: [cicen, hicen, hsnon, socn, tocn, uocn, vocn, ssh] - output variables: [cicen, hicen, hsnon, socn, tocn, uocn, vocn, ssh] - linear variable changes: - - linear variable change name: BalanceSOCA - - weight: - value: 1.00 - -- covariance: - covariance model: ensemble - members from template: - template: - read_from_file: 1 - date: '{{ATM_WINDOW_MIDDLE}}' - basename: ./ens/ - ocn_filename: 'ocn.pert.steric.%mem%.nc' - ice_filename: 'ice.%mem%.nc' - state variables: [tocn, socn, ssh, uocn, vocn, cicen, hicen, hsnon] - pattern: '%mem%' - nmembers: ${ENS_SIZE} - localization: - localization method: SABER - saber central block: - saber block name: diffusion - active variables: [tocn, socn, ssh] - geometry: - mom6_input_nml: mom_input.nml - fields metadata: ./fields_metadata.yaml - group mapping: - - name: ocean - variables: [tocn, socn, ssh, uocn, vocn] - - name: ice - variables: [cicen, hicen, hsnon] - read: - groups: - - name: ocean - multivariate strategy: duplicated - horizontal: - filename: hz_ocean.nc - vertical: - strategy: duplicated - - name: ice - horizontal: - filename: hz_ice.nc - - weight: - read_from_file: 3 - basename: ./ - ocn_filename: 'ocean.ens_weights.nc' - ice_filename: 'ice.ens_weights.nc' - date: '{{ATM_WINDOW_MIDDLE}}' diff --git a/parm/soca/ensda/stage_ens_mem.yaml.j2 b/parm/soca/ensda/stage_ens_mem.yaml.j2 index d0dca6e18..af47348a6 100644 --- a/parm/soca/ensda/stage_ens_mem.yaml.j2 +++ b/parm/soca/ensda/stage_ens_mem.yaml.j2 @@ -9,7 +9,8 @@ # create working directories ###################################### mkdir: - - "{{ ENSPERT_RELPATH }}/ens" +- "{{ DATAens }}/ens" + ###################################### # copy ensemble background files ###################################### @@ -21,6 +22,6 @@ copy: '${YMD}':gPDY, '${HH}':gcyc, '${MEMDIR}':"mem" + '%03d' % mem} %} - - ["{{ COM_OCEAN_HISTORY_TMPL | replace_tmpl(tmpl_dict) }}/{{ GDUMP_ENS }}.ocean.t{{ gcyc }}z.inst.f006.nc", "{{ ENSPERT_RELPATH }}/ens/ocean.{{ mem }}.nc"] - - ["{{ COM_ICE_HISTORY_TMPL | replace_tmpl(tmpl_dict) }}/{{ GDUMP_ENS }}.ice.t{{ gcyc }}z.inst.f006.nc", "{{ ENSPERT_RELPATH }}/ens/ice.{{ mem }}.nc"] + - ["{{ COM_OCEAN_HISTORY_TMPL | replace_tmpl(tmpl_dict) }}/{{ GDUMP_ENS }}.ocean.t{{ gcyc }}z.inst.f006.nc", "{{ DATAens }}/ens/ocean.{{ mem }}.nc"] + - ["{{ COM_ICE_HISTORY_TMPL | replace_tmpl(tmpl_dict) }}/{{ GDUMP_ENS }}.ice.t{{ gcyc }}z.inst.f006.nc", "{{ DATAens }}/ens/ice.{{ mem }}.nc"] {% endfor %} diff --git a/parm/soca/obsprep/obsprep_config.yaml b/parm/soca/obsprep/obsprep_config.yaml index 0b3c56764..3af8565e3 100644 --- a/parm/soca/obsprep/obsprep_config.yaml +++ b/parm/soca/obsprep/obsprep_config.yaml @@ -160,7 +160,7 @@ observations: name: sst_viirs_npp_l3u provider: GHRSST dmpdir subdir: ocean/sst - type: nc + type: nc dmpdir regex: '*-L3U_GHRSST-SSTsubskin-VIIRS_NPP-ACSPO_V*.nc' bounds: units: C @@ -230,7 +230,7 @@ observations: stride: 15 min number of obs: 10 ocean basin: RECCAP2_region_masks_all_v20221025.nc - + # in situ: monthly - obs space: name: insitu_profile_bathy @@ -285,7 +285,7 @@ observations: name: insitu_surface_trkob provider: GTS dmpdir subdir: atmos - type: bufr + type: bufr dmpdir regex: 'gdas.*.trkob.*.bufr_d' # in situ: daily @@ -316,5 +316,3 @@ observations: dmpdir subdir: atmos type: bufr dmpdir regex: 'gdas.*.mbuoyb.*.bufr_d' - - diff --git a/test/gw-ci/CMakeLists.txt b/test/gw-ci/CMakeLists.txt index 5a07090d0..a248310fa 100644 --- a/test/gw-ci/CMakeLists.txt +++ b/test/gw-ci/CMakeLists.txt @@ -39,12 +39,12 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo elseif("${task_name}" STREQUAL "enkfgdas_ecmn") if ("${pslot}" STREQUAL "C96C48_ufs_hybatmDA") set(subtask_names_list - "enkfgdas_ecen000") + "enkfgdas_ecen000") else() set(subtask_names_list "enkfgdas_ecen000" "enkfgdas_ecen001" - "enkfgdas_ecen002") + "enkfgdas_ecen002") endif() else() set(subtask_names_list ${task_name}) @@ -80,7 +80,7 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_epmn_${HALF_CYCLE}") list(APPEND TEST_DEPENDS "${test_prefix}_gdas_prep_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "gdas_prepatmiodaobs") - list(APPEND TEST_DEPENDS "${test_prefix}_gdas_prep_${FULL_CYCLE}") + list(APPEND TEST_DEPENDS "${test_prefix}_gdas_prep_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "gdas_atmanlinit") list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_epmn_${HALF_CYCLE}") list(APPEND TEST_DEPENDS "${test_prefix}_gdas_prepatmiodaobs_${FULL_CYCLE}") @@ -106,7 +106,7 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo list(APPEND TEST_DEPENDS "${test_prefix}_gdas_anal_${FULL_CYCLE}") list(APPEND TEST_DEPENDS "${test_prefix}_snowanal_${FULL_CYCLE}") else() - list(APPEND TEST_DEPENDS "${test_prefix}_gdas_anal_${FULL_CYCLE}") + list(APPEND TEST_DEPENDS "${test_prefix}_gdas_anal_${FULL_CYCLE}") endif() elseif("${task_name}" STREQUAL "gdas_analcalc") list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_echgres_${HALF_CYCLE}") @@ -127,7 +127,7 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo elseif("${task_name}" STREQUAL "enkfgdas_atmensanlobs") list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_atmensanlinit_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "enkfgdas_atmensanlsol") - list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_atmensanlobs_${FULL_CYCLE}") + list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_atmensanlobs_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "enkfgdas_atmensanlfv3inc") list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_atmensanlsol_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "enkfgdas_atmensanlfinal") @@ -139,7 +139,7 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo elseif("${task_name}" STREQUAL "enkfgdas_ediag") list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_eobs_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "enkfgdas_eupd") - list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_ediag_${FULL_CYCLE}") + list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_ediag_${FULL_CYCLE}") elseif("${task_name}" STREQUAL "enkfgdas_ecmn") if("${pslot}" STREQUAL "C96C48_ufs_hybatmDA") list(APPEND TEST_DEPENDS "${test_prefix}_gdas_analcalc_${FULL_CYCLE}") @@ -184,9 +184,9 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_fcst_${HALF_CYCLE}") list(APPEND TEST_DEPENDS "${test_prefix}_gdas_prepoceanobs_${FULL_CYCLE}") else() - list(APPEND TEST_DEPENDS "${test_prefix}") + list(APPEND TEST_DEPENDS "${test_prefix}") endif() - endif() + endif() # Set cycle if(${is_full_cycle}) @@ -194,7 +194,7 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo else() set(cycle ${HALF_CYCLE}) endif() - + # Add Task set(test_name ${test_prefix}_${task_name}_${cycle}) message(STATUS "preparing ${subtask_names} for ${test_prefix} ctest") @@ -233,162 +233,179 @@ function(add_cycling_tests pslot YAML_PATH HOMEgfs WORKING_DIRECTORY PROJECT_SOU foreach(task_name ${HALF_CYCLE_TASKS}) add_task(${task_name} ${test_prefix} ${is_full_cycle} ${HALF_CYCLE} ${FULL_CYCLE} ${pslot} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR}) endforeach() - + # Select the list of tasks to run for the full cycle message(STATUS "Full-cycle tasks ${FULL_CYCLE_TASKS}") - set(is_full_cycle TRUE) + set(is_full_cycle TRUE) foreach(task_name ${FULL_CYCLE_TASKS}) add_task(${task_name} ${test_prefix} ${is_full_cycle} ${HALF_CYCLE} ${FULL_CYCLE} ${pslot} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR}) endforeach() endfunction() if (WORKFLOW_TESTS) + # List of tests to run + option(TEST_GSI "Enable the GFSv17 GSI tests" ON) + option(TEST_GFS18 "Enable the GFSv18 Atmos JEDI tests" ON) + option(TEST_AERO_LAND "Enable the GFSv17 Aero-Land JEDI tests" ON) + option(TEST_MARINE "Enable the GFSv17 Marine JEDI tests" ON) + option(TEST_GFS17 "Enable the GFSv17 WCDA tests" ON) + # Setup the environement set(HOMEgfs ${CMAKE_SOURCE_DIR}/../../..) set(WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../test/gw-ci) - - # GSI Atm DA C96/C48 - # ------------------ - set(pslot "C96C48_hybatmDA") - set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) - set(HALF_CYCLE_TASKS - "gdas_stage_ic" - "gdas_fcst" - "gdas_atmos_prod" - "enkfgdas_stage_ic" - "enkfgdas_fcst" - "enkfgdas_echgres" - "enkfgdas_epmn") - set(FULL_CYCLE_TASKS - "gdas_prep" - "gdas_anal" - "gdas_sfcanl" - "gdas_analcalc" - "gdas_fcst" - "enkfgdas_eobs" - "enkfgdas_ediag" - "enkfgdas_eupd" - "enkfgdas_ecmn" - "enkfgdas_esfc" - "enkfgdas_fcst" - ) - add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + if(${TEST_GSI}) + # GSI Atm DA C96/C48 + # ------------------ + set(pslot "C96C48_hybatmDA") + set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) + set(HALF_CYCLE_TASKS + "gdas_stage_ic" + "gdas_fcst" + "gdas_atmos_prod" + "enkfgdas_stage_ic" + "enkfgdas_fcst" + "enkfgdas_echgres" + "enkfgdas_epmn") + set(FULL_CYCLE_TASKS + "gdas_prep" + "gdas_anal" + "gdas_sfcanl" + "gdas_analcalc" + "gdas_fcst" + "enkfgdas_eobs" + "enkfgdas_ediag" + "enkfgdas_eupd" + "enkfgdas_ecmn" + "enkfgdas_esfc" + "enkfgdas_fcst" + ) + + add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + endif() # TEST_GSI + # JEDI Atm DA C96/C48 # ------------------- - set(pslot "C96C48_ufs_hybatmDA") - set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) - set(HALF_CYCLE_TASKS - "gdas_stage_ic" - "gdas_fcst" - "gdas_atmos_prod" - "enkfgdas_stage_ic" - "enkfgdas_fcst" - "enkfgdas_echgres" - "enkfgdas_epmn") - set(FULL_CYCLE_TASKS - "gdas_prep" - "gdas_prepatmiodaobs" - "gdas_atmanlinit" - "gdas_atmanlvar" - "gdas_atmanlfv3inc" - "gdas_atmanlfinal" - "gdas_sfcanl" - "gdas_analcalc" - "gdas_fcst" - "enkfgdas_atmensanlinit" - "enkfgdas_atmensanlobs" - "enkfgdas_atmensanlsol" - "enkfgdas_atmensanlfv3inc" - "enkfgdas_atmensanlfinal" - "enkfgdas_ecmn" - "enkfgdas_esfc" - "enkfgdas_fcst" - ) - add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + if(${TEST_GFS18}) + set(pslot "C96C48_ufs_hybatmDA") + set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) + set(HALF_CYCLE_TASKS + "gdas_stage_ic" + "gdas_fcst" + "gdas_atmos_prod" + "enkfgdas_stage_ic" + "enkfgdas_fcst" + "enkfgdas_echgres" + "enkfgdas_epmn") + set(FULL_CYCLE_TASKS + "gdas_prep" + "gdas_prepatmiodaobs" + "gdas_atmanlinit" + "gdas_atmanlvar" + "gdas_atmanlfv3inc" + "gdas_atmanlfinal" + "gdas_sfcanl" + "gdas_analcalc" + "gdas_fcst" + "enkfgdas_atmensanlinit" + "enkfgdas_atmensanlobs" + "enkfgdas_atmensanlsol" + "enkfgdas_atmensanlfv3inc" + "enkfgdas_atmensanlfinal" + "enkfgdas_ecmn" + "enkfgdas_esfc" + "enkfgdas_fcst" + ) + add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + endif() # TEST_V18 # Aero-Land DA C96 # ---------------- - set(pslot "C96C48_hybatmaerosnowDA") - set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) - set(HALF_CYCLE_TASKS - "gdas_stage_ic" - "gdas_fcst" - "gdas_atmos_prod" - "gdas_aeroanlgenb" - "enkfgdas_stage_ic" - "enkfgdas_fcst" - "enkfgdas_echgres" - "enkfgdas_epmn") - set(FULL_CYCLE_TASKS - "gdas_prep" - "gdas_anal" - "gdas_aeroanlinit" - "gdas_aeroanlvar" - "gdas_aeroanlfinal" - "gdas_snowanl" - "gdas_sfcanl" - "gdas_analcalc" - "gdas_fcst" - "enkfgdas_eobs" - "enkfgdas_ediag" - "enkfgdas_eupd" - "enkfgdas_ecmn" - "enkfgdas_esnowrecen" - "enkfgdas_esfc" - "enkfgdas_fcst" - ) - add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") - - # GSI Atm DA C48, JEDI Marine DA 500 - # ---------------------------------- - set(pslot "C48mx500_3DVarAOWCDA") - set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) - set(HALF_CYCLE_TASKS - "gdas_stage_ic" - "gdas_fcst") - set(FULL_CYCLE_TASKS - "gdas_prepoceanobs" - "gdas_marinebmat" - "gdas_marineanlinit" - "gdas_marineanlvar" - "gdas_marineanlchkpt" - "gdas_marineanlfinal" - "gdas_prep" - "gdas_anal" - "gdas_sfcanl" - "gdas_fcst" + if(${TEST_AERO_LAND}) + set(pslot "C96C48_hybatmaerosnowDA") + set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) + set(HALF_CYCLE_TASKS + "gdas_stage_ic" + "gdas_fcst" + "gdas_atmos_prod" + "gdas_aeroanlgenb" + "enkfgdas_stage_ic" + "enkfgdas_fcst" + "enkfgdas_echgres" + "enkfgdas_epmn") + set(FULL_CYCLE_TASKS + "gdas_prep" + "gdas_anal" + "gdas_aeroanlinit" + "gdas_aeroanlvar" + "gdas_aeroanlfinal" + "gdas_snowanl" + "gdas_sfcanl" + "gdas_analcalc" + "gdas_fcst" + "enkfgdas_eobs" + "enkfgdas_ediag" + "enkfgdas_eupd" + "enkfgdas_ecmn" + "enkfgdas_esnowrecen" + "enkfgdas_esfc" + "enkfgdas_fcst" ) - add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + endif() # TEST_AERO_LAND - # WCDA, low-res, ensemble da - # ------------- - set(pslot "C48mx500_hybAOWCDA") - set(letkf TRUE) - set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) - set(HALF_CYCLE_TASKS - "gdas_stage_ic" - "gdas_fcst" - "enkfgdas_stage_ic" - "enkfgdas_fcst") - set(FULL_CYCLE_TASKS - "gdas_prepoceanobs" - "gdas_marineanlletkf" - # TODO(AFE) waiting until these are working for hybrid - # "gdas_marinebmat" - # "gdas_marineanlinit" - # "gdas_marineanlvar" - # "gdas_marineanlchkpt" - # "gdas_marineanlfinal" - ) - add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") - set(letkf FALSE) + if(${TEST_MARINE}) + # GSI Atm DA C48, JEDI Marine DA 500 + # ---------------------------------- + set(pslot "C48mx500_3DVarAOWCDA") + set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) + set(HALF_CYCLE_TASKS + "gdas_stage_ic" + "gdas_fcst") + set(FULL_CYCLE_TASKS + "gdas_prepoceanobs" + "gdas_marinebmat" + "gdas_marineanlinit" + "gdas_marineanlvar" + "gdas_marineanlchkpt" + "gdas_marineanlfinal" + # TODO: run the below task once we have a C24/C12 config + #"gdas_prep" + #"gdas_anal" + #"gdas_sfcanl" + #"gdas_fcst" + ) + add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + + + # WCDA, low-res, ensemble da + # ------------- + + set(pslot "C48mx500_hybAOWCDA") + set(letkf TRUE) + set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) + set(HALF_CYCLE_TASKS + "gdas_stage_ic" + "gdas_fcst" + "enkfgdas_stage_ic" + "enkfgdas_fcst") + set(FULL_CYCLE_TASKS + "gdas_prepoceanobs" + "gdas_marineanlletkf" + "gdas_marinebmat" + "gdas_marineanlinit" + "gdas_marineanlvar" + "gdas_marineanlchkpt" + "gdas_marineanlfinal" + ) + add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + set(letkf FALSE) + endif() # TEST_MARINE # GFSv17, 3DVAR prototype # ----------------------- - option(TEST_GFS17 "Enable the GFSv17 prototype CI tests" OFF) if(TEST_GFS17) - set(pslot "GFSv17-3DVAR-C384mx025") + set(pslot "C384mx025_3DVarAOWCDA") set(YAML_PATH ${HOMEgfs}/ci/cases/gfsv17/${pslot}.yaml) set(HALF_CYCLE_TASKS "gdas_stage_ic" @@ -406,6 +423,6 @@ if (WORKFLOW_TESTS) "gdas_sfcanl" "gdas_fcst" ) - add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") + add_cycling_tests(${pslot} ${YAML_PATH} ${HOMEgfs} ${WORKING_DIRECTORY} ${PROJECT_SOURCE_DIR} "${HALF_CYCLE_TASKS}" "${FULL_CYCLE_TASKS}") endif() endif() diff --git a/test/testreference/C48mx500_3DVarAOWCDA_3dfgat.ref b/test/testreference/C48mx500_3DVarAOWCDA_3dfgat.ref index b28df9aec..da694e64f 100644 --- a/test/testreference/C48mx500_3DVarAOWCDA_3dfgat.ref +++ b/test/testreference/C48mx500_3DVarAOWCDA_3dfgat.ref @@ -1,54 +1,41 @@ -Norm of input parameter StdDev: 4.1916150456516459e+02 +Norm of input parameter StdDev: 1.8173379285780410e+02 CostJb : Nonlinear Jb = 0.0000000000000000e+00 -CostJo : Nonlinear Jo(adt_rads_all) = 3.6538107293180053e+02, nobs = 154056, Jo/n = 2.3717419180804417e-03, err = 3.5902523361909755e+00 -CostJo : Nonlinear Jo(sst_avhrr_ma_l3u) = 2.2674500789797807e+05, nobs = 80795, Jo/n = 2.8064237625840467e+00, err = 3.1933209964416054e-01 -CostJo : Nonlinear Jo(sst_avhrr_mb_l3u) = 2.4093341994206450e+05, nobs = 87590, Jo/n = 2.7506955125249970e+00, err = 3.1303879303248278e-01 -CostJo : Nonlinear Jo(sst_viirs_npp_l3u) = 3.5095100144345453e+05, nobs = 86464, Jo/n = 4.0589262750214488e+00, err = 2.7352638780435701e-01 -CostJo : Nonlinear Jo(sst_viirs_n20_l3u) = 3.4617882174260722e+05, nobs = 82328, Jo/n = 4.2048734542635220e+00, err = 2.7610924066693887e-01 +CostJo : Nonlinear Jo(adt_rads_all) = 8.9472206004908480e+02, nobs = 305088, Jo/n = 2.9326688039158695e-03, err = 3.6584285974238564e+00 +CostJo : Nonlinear Jo(sst_avhrr_ma_l3u) = 1.6529491726590766e+05, nobs = 75382, Jo/n = 2.1927637534943045e+00, err = 3.1025085244688677e-01 +CostJo : Nonlinear Jo(sst_avhrr_mb_l3u) = 2.0157960412025612e+05, nobs = 85824, Jo/n = 2.3487556408493675e+00, err = 2.9608803521703780e-01 +CostJo : Nonlinear Jo(sst_viirs_npp_l3u) = 2.5707963369798398e+05, nobs = 108952, Jo/n = 2.3595678252623538e+00, err = 2.7810665818752978e-01 +CostJo : Nonlinear Jo(sst_viirs_n20_l3u) = 2.1906632422822388e+05, nobs = 107011, Jo/n = 2.0471383710854387e+00, err = 2.8307574214768672e-01 CostJo : Nonlinear Jo(sst_abi_g16_l3c) = 0.0000000000000000e+00 --- No Observations CostJo : Nonlinear Jo(sst_abi_g17_l3c) = 0.0000000000000000e+00 --- No Observations CostJo : Nonlinear Jo(sst_ahi_h08_l3c) = 0.0000000000000000e+00 --- No Observations -CostJo : Nonlinear Jo(icec_amsr2_north) = 1.3776003730013903e+05, nobs = 238447, Jo/n = 5.7773860564460455e-01, err = 1.1973958453579239e-01 -CostJo : Nonlinear Jo(icec_amsr2_south) = 1.3525377877554770e+06, nobs = 294824, Jo/n = 4.5876108720981907e+00, err = 1.6104199583000647e-01 -CostJo : Nonlinear Jo(insitu_profile_argo) = 1.2371155464032217e+03, nobs = 74525, Jo/n = 1.6600007331811092e-02, err = 1.0000000000000000e+01 -CostFunction: Nonlinear J = 2.6567085727010556e+06 -RPCGMinimizer: reduction in residual norm = 3.3320242450517824e-02 -CostFunction::addIncrement: Analysis: - Valid time: 2021-03-24T18:00:00Z - sea_ice_area_fraction min=-0.3191478642087981 max=1.9094064942717854 mean=0.1380060365165569 - sea_ice_thickness min=0.0000000000000000 max=4.7237067222595215 mean=0.1716933516882944 - sea_ice_snow_thickness min=0.0000000000000000 max=0.5992891192436218 mean=0.0227558953028715 - sea_water_salinity min=-0.0004127016278705 max=40.0432853698730469 mean=33.6826135810349925 -sea_water_potential_temperature min=-13.5424639498294965 max=31.5094358260619565 mean=11.4876284245572435 - eastward_sea_water_velocity min=-0.9187383651733398 max=0.6746135354042053 mean=-0.0008460743757450 - northward_sea_water_velocity min=-0.6511899232864380 max=0.9435366392135620 mean=0.0030693165578485 - sea_surface_height_above_geoid min=-1.9814533174212232 max=0.8682830617264085 mean=-0.3311748439341126 - sea_water_cell_thickness min=0.0000000000000000 max=5416.6992187500000000 mean=128.6267671163319903 - ocean_mixed_layer_thickness min=2.3304977416992188 max=2417.2331242561340332 mean=148.3113220148310063 - sea_water_depth min=2.3304977416992188 max=2949.9484505653381348 mean=150.4446913535238650 - - - - - - - - - - - - - -CostJb : Nonlinear Jb = 319.2500686210223648 -CostJo : Nonlinear Jo(adt_rads_all) = 269.8785811470986573, nobs = 154056, Jo/n = 0.0017518212932122, err = 3.5902523361909755 -CostJo : Nonlinear Jo(sst_avhrr_ma_l3u) = 97733.3440574856213061, nobs = 80795, Jo/n = 1.2096459441485936, err = 0.3193320996441605 -CostJo : Nonlinear Jo(sst_avhrr_mb_l3u) = 102482.6442049633478746, nobs = 87590, Jo/n = 1.1700267633858128, err = 0.3130387930324828 -CostJo : Nonlinear Jo(sst_viirs_npp_l3u) = 179681.9646639756683726, nobs = 86464, Jo/n = 2.0781130258139302, err = 0.2735263878043570 -CostJo : Nonlinear Jo(sst_viirs_n20_l3u) = 165441.5019567124545574, nobs = 82328, Jo/n = 2.0095411276444519, err = 0.2761092406669389 +CostJo : Nonlinear Jo(icec_amsr2_north) = 4.5008964311717841e+05, nobs = 223886, Jo/n = 2.0103518894311319e+00, err = 1.1069397449281068e-01 +CostJo : Nonlinear Jo(icec_amsr2_south) = 2.0473344657993333e+06, nobs = 381832, Jo/n = 5.3618724093301067e+00, err = 1.5601541685395720e-01 +CostJo : Nonlinear Jo(insitu_profile_argo) = 1.0286658130801611e+03, nobs = 75513, Jo/n = 1.3622367182871308e-02, err = 1.0000000000000000e+01 +CostFunction: Nonlinear J = 3.3423679761020122e+06 +RPCGMinimizer: reduction in residual norm = 1.0352198587033008e+00 +CostFunction::addIncrement: Analysis: + Valid time: 2021-03-25T00:00:00Z + sea_ice_area_fraction min=-0.3375955741044348 max=1.0701686282805594 mean=0.1111281998821976 + sea_ice_thickness min=0.0000000000000000 max=48.1358184814453125 mean=0.2073979630505945 + sea_ice_snow_thickness min=0.0000000000000000 max=0.5993193387985229 mean=0.0200216847118031 + sea_water_salinity min=-0.0000000397062482 max=43.5831925004053033 mean=33.6886249686437580 +sea_water_potential_temperature min=-1.9124325513839722 max=30.8660448750331859 mean=11.7945041333814515 + eastward_sea_water_velocity min=-0.9171831607818604 max=1.5397396087646484 mean=-0.0003884267695896 + northward_sea_water_velocity min=-0.5532888770103455 max=1.0238031148910522 mean=0.0046988454914240 + sea_surface_height_above_geoid min=-11.2607626229298159 max=1.9775081835545605 mean=-0.3558952181999109 + sea_water_cell_thickness min=0.0000000000000000 max=5416.7958984375000000 mean=128.6249924829840268 + ocean_mixed_layer_thickness min=2.3822515010833740 max=2241.9723339080810547 mean=127.4101581065958015 + sea_water_depth min=2.3822515010833740 max=2949.8905668258666992 mean=150.4069206023627032 +CostJb : Nonlinear Jb = 1.8140984503493731 +CostJo : Nonlinear Jo(adt_rads_all) = 736.2157130227589050, nobs = 305088, Jo/n = 0.0024131257637887, err = 3.6584285974238564 +CostJo : Nonlinear Jo(sst_avhrr_ma_l3u) = 165802.2057433615264017, nobs = 75382, Jo/n = 2.1994933239150134, err = 0.3102508524468868 +CostJo : Nonlinear Jo(sst_avhrr_mb_l3u) = 199058.6476532369852066, nobs = 85824, Jo/n = 2.3193820802250769, err = 0.2960880352170378 +CostJo : Nonlinear Jo(sst_viirs_npp_l3u) = 257785.8077833042480052, nobs = 108952, Jo/n = 2.3660493408409597, err = 0.2781066581875298 +CostJo : Nonlinear Jo(sst_viirs_n20_l3u) = 230228.7910482294391841, nobs = 107011, Jo/n = 2.1514497672970951, err = 0.2830757421476867 CostJo : Nonlinear Jo(sst_abi_g16_l3c) = 0.0000000000000000 --- No Observations CostJo : Nonlinear Jo(sst_abi_g17_l3c) = 0.0000000000000000 --- No Observations CostJo : Nonlinear Jo(sst_ahi_h08_l3c) = 0.0000000000000000 --- No Observations -CostJo : Nonlinear Jo(icec_amsr2_north) = 96049.0640872453514021, nobs = 238447, Jo/n = 0.4028109562596525, err = 0.1197395845357924 -CostJo : Nonlinear Jo(icec_amsr2_south) = 507683.7047332451329567, nobs = 294824, Jo/n = 1.7219890671493676, err = 0.1610419958300065 -CostJo : Nonlinear Jo(insitu_profile_argo) = 608.7904107136805578, nobs = 74525, Jo/n = 0.0081689421095428, err = 10.0000000000000000 -CostFunction: Nonlinear J = 1150270.1427641094196588 +CostJo : Nonlinear Jo(icec_amsr2_north) = 442351.0648206729674712, nobs = 223886, Jo/n = 1.9757870738709564, err = 0.1106939744928107 +CostJo : Nonlinear Jo(icec_amsr2_south) = 1688092.4695333424024284, nobs = 381832, Jo/n = 4.4210345637174004, err = 0.1560154168539572 +CostJo : Nonlinear Jo(insitu_profile_argo) = 1027.3563725773119586, nobs = 75513, Jo/n = 0.0136050265858503, err = 10.0000000000000000 +CostFunction: Nonlinear J = 2985084.3727661976590753 diff --git a/ush/soca/marine_recenter.py b/ush/soca/marine_recenter.py index a3814db85..4c4a0c285 100644 --- a/ush/soca/marine_recenter.py +++ b/ush/soca/marine_recenter.py @@ -8,6 +8,7 @@ from typing import Dict import ufsda from ufsda.stage import soca_fix +import pygfs.utils.marine_da_utils as mdau from wxflow import (AttrDict, chdir, Executable, @@ -15,10 +16,8 @@ logit, parse_j2yaml, Task, - Template, - TemplateConstants, - WorkflowException, - YAMLFile) + add_to_datetime, to_timedelta, + WorkflowException) logger = getLogger(__name__.split('.')[-1]) @@ -59,32 +58,40 @@ def __init__(self, config: Dict) -> None: half_assim_freq = timedelta(hours=int(config['assim_freq'])/2) window_begin = cdate - half_assim_freq + window_end = cdate + half_assim_freq window_begin_iso = window_begin.strftime('%Y-%m-%dT%H:%M:%SZ') window_middle_iso = cdate.strftime('%Y-%m-%dT%H:%M:%SZ') - - self.recen_config = AttrDict( - {'window_begin': f"{window_begin.strftime('%Y-%m-%dT%H:%M:%SZ')}", - 'ATM_WINDOW_BEGIN': window_begin_iso, - 'ATM_WINDOW_MIDDLE': window_middle_iso, - 'DATA': DATA, - 'dump': self.task_config.RUN, - 'stage_dir': DATA, - 'soca_input_fix_dir': self.task_config.SOCA_INPUT_FIX_DIR, - 'NMEM_ENS': self.task_config.NMEM_ENS, - 'ATM_WINDOW_LENGTH': f"PT{config['assim_freq']}H"}) - berror_yaml_dir = os.path.join(gdas_home, 'parm', 'soca', 'berror') - self.task_config['recen_yaml_template'] = os.path.join(berror_yaml_dir, 'soca_ensrecenter.yaml') - self.task_config['recen_yaml_file'] = os.path.join(DATA, 'soca_ensrecenter.yaml') - self.task_config['gridgen_yaml'] = os.path.join(gdas_home, 'parm', 'soca', 'gridgen', 'gridgen.yaml') - self.task_config['BKG_LIST'] = 'bkg_list.yaml' - self.task_config['window_begin'] = window_begin - self.task_config['mom_input_nml_src'] = os.path.join(gdas_home, 'parm', 'soca', 'fms', 'input.nml') - self.task_config['mom_input_nml_tmpl'] = os.path.join(DATA, 'mom_input.nml.tmpl') - self.task_config['mom_input_nml'] = os.path.join(DATA, 'mom_input.nml') - self.task_config['bkg_dir'] = os.path.join(DATA, 'bkg') - self.task_config['INPUT'] = os.path.join(DATA, 'INPUT') - self.task_config['ens_dir'] = os.path.join(DATA, 'ens') + + _window_begin = add_to_datetime(self.task_config.current_cycle, -to_timedelta(f"{self.task_config.assim_freq}H") / 2) + _window_end = add_to_datetime(self.task_config.current_cycle, to_timedelta(f"{self.task_config.assim_freq}H") / 2) + + local_dict = AttrDict({'window_begin': f"{window_begin.strftime('%Y-%m-%dT%H:%M:%SZ')}", + 'PARMsoca': os.path.join(self.task_config.PARMgfs, 'gdas', 'soca'), + 'MARINE_WINDOW_BEGIN': _window_begin, + 'MARINE_WINDOW_BEGIN_ISO': _window_begin.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'MARINE_WINDOW_END': _window_end, + 'MARINE_WINDOW_END_ISO': _window_end.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'MARINE_WINDOW_LENGTH': f"PT{self.task_config['assim_freq']}H", + 'MARINE_WINDOW_MIDDLE': self.task_config.current_cycle, + 'MARINE_WINDOW_MIDDLE_ISO': self.task_config.current_cycle.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'DATA': DATA, + 'dump': self.task_config.RUN, + 'stage_dir': DATA, + 'soca_input_fix_dir': self.task_config.SOCA_INPUT_FIX_DIR, + 'NMEM_ENS': self.task_config.NMEM_ENS, + 'MARINE_WINDOW_LENGTH': f"PT{config['assim_freq']}H", + 'recen_yaml_template': os.path.join(berror_yaml_dir, 'soca_ensrecenter.yaml'), + 'recen_yaml_file': os.path.join(DATA, 'soca_ensrecenter.yaml'), + 'gridgen_yaml': os.path.join(gdas_home, 'parm', 'soca', 'gridgen', 'gridgen.yaml'), + 'BKG_LIST': 'bkg_list.yaml', + 'window_begin': window_begin, + 'bkg_dir': os.path.join(DATA, 'bkg'), + 'INPUT': os.path.join(DATA, 'INPUT'), + 'ens_dir': os.path.join(DATA, 'ens')}) + + # Extend task_config with local_dict + self.task_config.update(local_dict) @logit(logger) def initialize(self): @@ -101,33 +108,28 @@ def initialize(self): RUN = self.task_config.RUN gcyc = self.task_config.gcyc - ufsda.stage.soca_fix(self.recen_config) + # stage fix files + logger.info(f"Staging SOCA fix files from {self.task_config.SOCA_INPUT_FIX_DIR}") + soca_fix_list = parse_j2yaml(self.task_config.SOCA_FIX_YAML_TMPL, self.task_config) + FileHandler(soca_fix_list).sync() - ################################################################################ - # prepare input.nml - FileHandler({'copy': [[self.task_config.mom_input_nml_src, self.task_config.mom_input_nml_tmpl]]}).sync() - - # swap date and stack size - 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) - ufsda.disk_utils.removefile(self.task_config.mom_input_nml) - nml.write(self.task_config.mom_input_nml) - - FileHandler({'mkdir': [self.task_config.bkg_dir]}).sync() - bkg_utils.gen_bkg_list(bkg_path=self.task_config.COM_OCEAN_HISTORY_PREV, - out_path=self.task_config.bkg_dir, - window_begin=self.task_config.window_begin, - yaml_name=self.task_config.BKG_LIST) + # prepare the MOM6 input.nml + mdau.prep_input_nml(self.task_config) - ################################################################################ - # Copy initial condition + # stage the soca utility yamls (gridgen, fields and ufo mapping yamls) + logger.info(f"Staging SOCA utility yaml files from {self.task_config.PARMsoca}") + soca_utility_list = parse_j2yaml(self.task_config.MARINE_UTILITY_YAML_TMPL, self.task_config) + FileHandler(soca_utility_list).sync() - bkg_utils.stage_ic(self.task_config.bkg_dir, self.task_config.DATA, gcyc) + # stage backgrounds + bkg_list = parse_j2yaml(self.task_config.MARINE_DET_STAGE_BKG_YAML_TMPL, self.task_config) + FileHandler(bkg_list).sync() +# ################################################################################ +# # Copy initial condition +# +# bkg_utils.stage_ic(self.task_config.bkg_dir, self.task_config.DATA, gcyc) +# ################################################################################ # stage ensemble members logger.info("---------------- Stage ensemble members") @@ -145,9 +147,9 @@ def initialize(self): domain, 'history') mem_dir_real = os.path.realpath(mem_dir) - f009 = f'enkf{RUN}.{domain}.t{gcyc}z.inst.f009.nc' + f00 = f"enkf{RUN}.{domain}.t{gcyc}z.inst.f009.nc" - fname_in = os.path.abspath(os.path.join(mem_dir_real, f009)) + fname_in = os.path.abspath(os.path.join(mem_dir_real, f00)) fname_out = os.path.realpath(os.path.join(self.task_config.ens_dir, domain+"."+str(mem)+".nc")) ens_member_list.append([fname_in, fname_out]) @@ -159,7 +161,7 @@ def initialize(self): logger.info(f"---------------- generate soca_ensrecenter.yaml") - recen_yaml = parse_j2yaml(self.task_config.recen_yaml_template, self.recen_config) + recen_yaml = parse_j2yaml(self.task_config.recen_yaml_template, self.task_config) recen_yaml.save(self.task_config.recen_yaml_file) @logit(logger) @@ -178,7 +180,7 @@ def run(self): chdir(self.task_config.DATA) exec_cmd_gridgen = Executable(self.task_config.APRUN_OCNANALECEN) - exec_name_gridgen = os.path.join(self.task_config.JEDI_BIN, 'gdas_soca_gridgen.x') + exec_name_gridgen = os.path.join(self.task_config.EXECgfs, 'gdas_soca_gridgen.x') exec_cmd_gridgen.add_default_arg(exec_name_gridgen) exec_cmd_gridgen.add_default_arg(self.task_config.gridgen_yaml) @@ -192,7 +194,7 @@ def run(self): pass exec_cmd_recen = Executable(self.task_config.APRUN_OCNANALECEN) - exec_name_recen = os.path.join(self.task_config.JEDI_BIN, 'gdas_ens_handler.x') + exec_name_recen = os.path.join(self.task_config.EXECgfs, 'gdas_ens_handler.x') exec_cmd_recen.add_default_arg(exec_name_recen) exec_cmd_recen.add_default_arg(os.path.basename(self.task_config.recen_yaml_file)) @@ -219,13 +221,18 @@ def finalize(self): logger.info("finalize") RUN = self.task_config.RUN - cyc = self.task_config.cyc + cyc = str(self.task_config.cyc).zfill(2) incr_file = f'enkf{RUN}.t{cyc}z.ocninc.nc' nmem_ens = self.task_config.NMEM_ENS PDYstr = self.task_config.PDY.strftime("%Y%m%d") mem_dir_list = [] copy_list = [] + # Skip the analysis insertion into the CICE restart for now + # TODO (G): Add this back in when we have hardened the soca to cice + # change of variable + + # Copy the recentering increment files to the member COMROOT directories for mem in range(1, nmem_ens+1): mem_dir = os.path.join(self.task_config.ROTDIR, f'enkf{RUN}.{PDYstr}', diff --git a/utils/soca/gdas_ens_handler.h b/utils/soca/gdas_ens_handler.h index 019076fcb..34ac8113e 100644 --- a/utils/soca/gdas_ens_handler.h +++ b/utils/soca/gdas_ens_handler.h @@ -118,13 +118,9 @@ namespace gdasapp { for (size_t i = 0; i < postProcIncr.ensSize_; ++i) { oops::Log::info() << " demean member " << i << std::endl; ensMembers[i] -= ensMean; + oops::Log::info() << "incr " << i << ":" << ensMembers[i] << std::endl; } - // Get the steric variable change configuration - eckit::LocalConfiguration stericVarChangeConfig; - fullConfig.get("steric height", stericVarChangeConfig); - oops::Log::info() << "steric config 0000: " << stericVarChangeConfig << std::endl; - // Initialize the trajectories used in the linear variable changes const eckit::LocalConfiguration trajConfig(fullConfig, "trajectory"); soca::State determTraj(geom, trajConfig); // deterministic trajectory @@ -136,19 +132,55 @@ namespace gdasapp { // the ensemble mean and the deterministic soca::Increment recenteringIncr(geom, postProcIncr.socaIncrVar_, postProcIncr.dt_); recenteringIncr.diff(determTraj, ensMeanTraj); - eckit::LocalConfiguration sshRecErrOutputConfig(fullConfig, "ssh output.recentering error"); - recenteringIncr = postProcIncr.setToZero(recenteringIncr); + postProcIncr.setToZero(recenteringIncr); + + // Check if we're only re-centering the ensemble fcst around the det. + bool recenterOnly = fullConfig.getBool("recentering around deterministic", false); + + // Save increments and exit if all we're doing is re-centering + // the ensemble fcst around the det. + if (recenterOnly) { + oops::Log::info() << "Only recentering " << std::endl; + int result = 0; + for (size_t i = 0; i < postProcIncr.ensSize_; ++i) { + // make a copy of the ens. member + soca::Increment incr(ensMembers[i]); + + // Add the recentering increment + incr += recenteringIncr; + oops::Log::info() << "recentered incr " << i << ":" << incr << std::endl; + + // Append the vertical geometry (for MOM6 IAU) + soca::Increment mom6_incr = postProcIncr.appendLayer(incr); + oops::Log::info() << "incr " << i << ":" << mom6_incr << std::endl; + + // Set variables to zero if specified in the configuration + postProcIncr.setToZero(incr); + + // Save the increments used to initialize the ensemble forecast + result = postProcIncr.save(mom6_incr, i+1); + } + return result; + } + + // Get the steric variable change configuration + eckit::LocalConfiguration stericVarChangeConfig; + fullConfig.get("steric height", stericVarChangeConfig); + oops::Log::info() << "steric config 0000: " << stericVarChangeConfig << std::endl; + + // Re-balance the perturbations around the deterministic oops::Log::info() << "steric config : " << stericVarChangeConfig << std::endl; postProcIncr.applyLinVarChange(recenteringIncr, stericVarChangeConfig, determTraj); oops::Log::info() << "ensemble mean: " << ensMeanTraj << std::endl; oops::Log::info() << "deterministic: " << determTraj << std::endl; oops::Log::info() << "error: " << recenteringIncr << std::endl; + eckit::LocalConfiguration sshRecErrOutputConfig(fullConfig, "ssh output.recentering error"); recenteringIncr.write(sshRecErrOutputConfig); // Re-process the ensemble of perturbations int result = 0; oops::Variables socaSshVar; - socaSshVar.push_back("ssh"); + socaSshVar.push_back("sea_surface_height_above_geoid"); std::vector sshTotal; std::vector sshSteric; std::vector sshNonSteric; @@ -167,7 +199,7 @@ namespace gdasapp { // Zero out ssh and other specified fields // TODO(G): - assert that at least ssh is in the list // - assert that Temperature is NOT in the list - incr = postProcIncr.setToZero(incr); + postProcIncr.setToZero(incr); // Compute the original steric height perturbation from T and S eckit::LocalConfiguration stericConfig(fullConfig, "steric height"); @@ -186,7 +218,7 @@ namespace gdasapp { oops::Log::info() << "--- ssh non-steric --- " << i << sshNonSteric[i] << std::endl; // Zero out specified fields (again, steric heigh is now in ssh from the previous step) - incr = postProcIncr.setToZero(incr); + postProcIncr.setToZero(incr); // Filter ensemble member and recompute steric ssh, recentering around // the deterministic trajectory @@ -264,7 +296,7 @@ namespace gdasapp { oops::Log::info() << "filtered std: " << ensStd << std::endl; // Prepare D (diag of the static B): replace sigma ssh with sigma ssh_u - ensStd = postProcIncr.setToZero(ensStd); // Set ssh (and other specified fields to zero) + postProcIncr.setToZero(ensStd); // Set ssh (and other specified fields to zero) atlas::FieldSet ensStdFs; ensStd.toFieldSet(ensStdFs); atlas::FieldSet sshNonStericStdFs; diff --git a/utils/soca/gdas_incr_handler.h b/utils/soca/gdas_incr_handler.h index 409f8acbe..bb2eccdc6 100644 --- a/utils/soca/gdas_incr_handler.h +++ b/utils/soca/gdas_incr_handler.h @@ -56,7 +56,7 @@ namespace gdasapp { oops::Log::debug() << incrWithLayer << std::endl; // Zero out specified fields - incrWithLayer = postProcIncr.setToZero(incrWithLayer); + postProcIncr.setToZero(incrWithLayer); // Save final increment result = postProcIncr.save(incrWithLayer, i); diff --git a/utils/soca/gdas_postprocincr.h b/utils/soca/gdas_postprocincr.h index 4fe241e27..5245412ad 100644 --- a/utils/soca/gdas_postprocincr.h +++ b/utils/soca/gdas_postprocincr.h @@ -94,11 +94,13 @@ class PostProcIncr { // ----------------------------------------------------------------------------- // Append layer thicknesses to increment // TODO(guillaume): There's got to be a better way to append a variable. - - soca::Increment appendLayer(soca::Increment& socaIncr) { + soca::Increment appendLayer(const soca::Increment& socaIncr) { oops::Log::info() << "==========================================" << std::endl; oops::Log::info() << "====== Append Layers" << std::endl; + // make a copy of the input increment + soca::Increment socaIncrOut(socaIncr); + // concatenate variables oops::Variables outputIncrVar(socaIncrVar_); outputIncrVar += layerVar_; @@ -107,8 +109,8 @@ class PostProcIncr { // append layer variable to the soca increment atlas::FieldSet socaIncrFs; - socaIncr.toFieldSet(socaIncrFs); - socaIncr.updateFields(outputIncrVar); + socaIncrOut.toFieldSet(socaIncrFs); + socaIncrOut.updateFields(outputIncrVar); // pad layer increment with zeros soca::Increment layerThick(Layers_); @@ -119,21 +121,21 @@ class PostProcIncr { layerThick.updateFields(outputIncrVar); // append layer thinckness to increment - socaIncr += layerThick; + socaIncrOut += layerThick; oops::Log::debug() << "-------------------- output increment: " << std::endl; - oops::Log::debug() << socaIncr << std::endl; + oops::Log::debug() << socaIncrOut << std::endl; - return socaIncr; + return socaIncrOut; } // ----------------------------------------------------------------------------- // Set specified variables to 0 - soca::Increment setToZero(soca::Increment& socaIncr) { + void setToZero(soca::Increment& socaIncr) { oops::Log::info() << "==========================================" << std::endl; if (!this->setToZero_) { oops::Log::info() << "====== no variables to set to 0.0" << std::endl; - return socaIncr; + return; } oops::Log::info() << "====== Set specified increment variables to 0.0" << std::endl; std::cout << socaZeroIncrVar_ << std::endl; @@ -155,7 +157,6 @@ class PostProcIncr { socaIncr.fromFieldSet(socaIncrFs); oops::Log::debug() << "-------------------- increment with zero'ed out fields: " << std::endl; oops::Log::debug() << socaIncr << std::endl; - return socaIncr; } // ----------------------------------------------------------------------------- diff --git a/utils/soca/gdas_soca_diagb.h b/utils/soca/gdas_soca_diagb.h index 563d303b9..9ca6c8a9b 100644 --- a/utils/soca/gdas_soca_diagb.h +++ b/utils/soca/gdas_soca_diagb.h @@ -343,7 +343,7 @@ namespace gdasapp { stdDevFilt(jnode, level, nbz, configD.depthMin, neighbors, nzMld, viewHocn, bkg, viewBathy, stdDevBkg, true, 6); - } + } // end level } // end 3D case } // end jnode } // end var @@ -412,11 +412,32 @@ namespace gdasapp { tmpArray(jnode, level+1)) / 3.0; } stdDevBkg(jnode, 0) = stdDevBkg(jnode, 1); + } // end jnode + } // end iter (vertical) + } // end vertical smoothing case + } // end var (loop through variables) + } // end simple smoothing + + /// Impose an exponential decay to the background error + // ---------------------------------------------------- + if (fullConfig.has("vertical e-folding scale")) { + double efold = 0.0; + fullConfig.get("vertical e-folding scale", efold); + for (auto & var : configD.socaVars.variables()) { + oops::Log::info() + << "====================== apply exponential decay to the background error. " + << " e-folding scale: " << efold << " m for " << var << std::endl; + auto stdDevBkg = atlas::array::make_view(bkgErrFs[var]); + auto numLevels = xbFs["sea_water_potential_temperature"].shape(0); + for (atlas::idx_t jnode = 0; jnode < numLevels; ++jnode) { + for (atlas::idx_t level = 0; level < xbFs[var].shape(1); ++level) { + if (viewDepth(jnode, level) >= 0.0) { + stdDevBkg(jnode, level) *= std::exp(-viewDepth(jnode, level) / efold); } - } - } - } - } + } // end level + } // end jnode + } // end var (loop through variables) + } // end exponential decay /// Use explicit diffusion to smooth the background error // ------------------------------------------------------ @@ -454,7 +475,7 @@ namespace gdasapp { // Apply the diffusion filtering diffuse.setParameters(scalesFs); diffuse.multiply(bkgErrFs, oops::Diffusion::Mode::HorizontalOnly); - } + } // end explicit diffusion // Rescale util::multiplyFieldSet(bkgErrFs, configD.rescale);