From bd6d61648c3b36318ab728af494f3d39025716ff Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA <26926959+RussTreadon-NOAA@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:02:58 -0500 Subject: [PATCH 1/6] add gdas_waveinit to C48mx500_3DVarAOWCDA ctest suite (#1470) --- test/gw-ci/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/gw-ci/CMakeLists.txt b/test/gw-ci/CMakeLists.txt index f9aece46b..4c08c8a83 100644 --- a/test/gw-ci/CMakeLists.txt +++ b/test/gw-ci/CMakeLists.txt @@ -359,6 +359,7 @@ if (WORKFLOW_TESTS) set(YAML_PATH ${HOMEgfs}/ci/cases/pr/${pslot}.yaml) set(HALF_CYCLE_TASKS "gdas_stage_ic" + "gdas_waveinit" "gdas_fcst") set(FULL_CYCLE_TASKS "gdas_prepoceanobs" From fcaf0ada9b68d01179c1e7d829942036612b04f6 Mon Sep 17 00:00:00 2001 From: Ed Givelberg Date: Thu, 30 Jan 2025 08:17:27 -0500 Subject: [PATCH 2/6] fixed glider output filename (#1469) --- .../marine/b2i/bufr2ioda_insitu_profile_glider.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ush/ioda/bufr2ioda/marine/b2i/bufr2ioda_insitu_profile_glider.py b/ush/ioda/bufr2ioda/marine/b2i/bufr2ioda_insitu_profile_glider.py index 627bf0292..4eb1bd4e9 100755 --- a/ush/ioda/bufr2ioda/marine/b2i/bufr2ioda_insitu_profile_glider.py +++ b/ush/ioda/bufr2ioda/marine/b2i/bufr2ioda_insitu_profile_glider.py @@ -10,11 +10,16 @@ platform_description = 'GLIDER profiles from subpfl: temperature and salinity' +class GliderConfig(Bufr2iodaConfig): + def ioda_filename(self): + return f"{self.cycle_type}.t{self.hh}z.insitu_profile_glider.{self.cycle_datetime}.nc4" + + if __name__ == '__main__': script_name, config_file, log_file, test_file = parse_arguments() - bufr2ioda_config = Bufr2iodaConfig( + bufr2ioda_config = GliderConfig( script_name, config_file, platform_description) From 74297a3fa06965f1de8fb6b2e7427f9cd7175b7d Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA <26926959+RussTreadon-NOAA@users.noreply.github.com> Date: Thu, 30 Jan 2025 08:31:32 -0500 Subject: [PATCH 3/6] update spack-stack path in Hera modulefiles (#1471) --- modulefiles/EVA/hera.lua | 2 +- modulefiles/GDAS/hera.intel.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modulefiles/EVA/hera.lua b/modulefiles/EVA/hera.lua index 865676c9f..2003cff7b 100644 --- a/modulefiles/EVA/hera.lua +++ b/modulefiles/EVA/hera.lua @@ -8,7 +8,7 @@ local pkgNameVer = myModuleFullName() conflict(pkgName) -prepend_path("MODULEPATH", "/scratch1/NCEPDEV/nems/role.epic/spack-stack/spack-stack-1.6.0/envs/unified-env-rocky8/install/modulefiles/Core") +prepend_path("MODULEPATH", "/contrib/spack-stack//spack-stack-1.6.0/envs/unified-env-rocky8/install/modulefiles/Core") load("stack-intel/2021.5.0") load("python/3.10.13") load("proj/9.2.1") diff --git a/modulefiles/GDAS/hera.intel.lua b/modulefiles/GDAS/hera.intel.lua index 7ef752a8e..07be6a18a 100644 --- a/modulefiles/GDAS/hera.intel.lua +++ b/modulefiles/GDAS/hera.intel.lua @@ -6,7 +6,7 @@ local pkgName = myModuleName() local pkgVersion = myModuleVersion() local pkgNameVer = myModuleFullName() -prepend_path("MODULEPATH", '/scratch1/NCEPDEV/nems/role.epic/spack-stack/spack-stack-1.6.0/envs/unified-env-rocky8/install/modulefiles/Core') +prepend_path("MODULEPATH", '/contrib/spack-stack//spack-stack-1.6.0/envs/unified-env-rocky8/install/modulefiles/Core') --prepend_path("MODULEPATH", '/scratch1/NCEPDEV/da/python/opt/modulefiles/stack') -- below two lines get us access to the spack-stack modules From fa0932686a63f99c6038b7d52c37e6ef5ea49d70 Mon Sep 17 00:00:00 2001 From: Guillaume Vernieres Date: Tue, 4 Feb 2025 11:46:42 -0500 Subject: [PATCH 4/6] Add code changes for the implementation of the low-resolution B (#1441) As the title says. ## Dependencies - https://github.com/NOAA-EMC/jcb-gdas/pull/66 - https://github.com/NOAA-EMC/global-workflow/pull/3238 - Update of the static files in glopara: https://github.com/NOAA-EMC/global-workflow/issues/3239 ## Work done in this PR Interp to low-resolution: - [ ] variance partitioning - [ ] preparation of the perturbations for the envar - [ ] hybrid weights - [ ] ... ## Testing Visual check showing the variance explained by the steric height (30 members) ![steic-explained-variance](https://github.com/user-attachments/assets/274c3fdb-94c3-42fc-ad6e-90867b39a778) left is native resolution, right is on the 1/2 deg grid. **"Science" note:** The explained variance above is almost the opposite to what I would have expected, and what we saw in the previous offline ensemble, or even what we see in tendencies. Check if there's a bug in the explained variance calculation. # Automated CI tests to run in Global Workflow - [ ] atm_jjob - [ ] C96C48_ufs_hybatmDA - [ ] C96C48_hybatmaerosnowDA - [x] C48mx500_3DVarAOWCDA - [x] C48mx500_hybAOWCDA - [ ] C96C48_hybatmDA --- parm/jcb-gdas | 2 +- parm/soca/fms/input.nml.j2 | 16 ++-- parm/soca/soca_fix_stage_025.yaml.j2 | 5 ++ parm/soca/soca_fix_stage_100.yaml.j2 | 5 ++ parm/soca/soca_fix_stage_500.yaml.j2 | 5 ++ utils/soca/gdas_ens_handler.h | 45 ++++++---- utils/soca/gdas_incr_handler.h | 9 +- utils/soca/gdas_postprocincr.h | 118 +++++++++++++++++---------- utils/soca/gdas_soca_diagb.h | 11 ++- utils/soca/gdas_socahybridweights.h | 28 +++++-- 10 files changed, 162 insertions(+), 82 deletions(-) diff --git a/parm/jcb-gdas b/parm/jcb-gdas index de8f25503..de1fd083c 160000 --- a/parm/jcb-gdas +++ b/parm/jcb-gdas @@ -1 +1 @@ -Subproject commit de8f25503e271ceeff9ad253310036351d14764b +Subproject commit de1fd083ccdf637470f23157b3d694dc331f0ef8 diff --git a/parm/soca/fms/input.nml.j2 b/parm/soca/fms/input.nml.j2 index 506860d03..6d87fa558 100644 --- a/parm/soca/fms/input.nml.j2 +++ b/parm/soca/fms/input.nml.j2 @@ -1,22 +1,16 @@ &MOM_input_nml output_directory = './', - input_filename = 'r' + input_filename = 'r', + {% if not simple_geom %} restart_input_dir = 'INPUT/', restart_output_dir = 'RESTART/', - parameter_filename = 'MOM_input' / + {% endif %} + parameter_filename = '{{ mom_input }}' + / &diag_manager_nml / - &ocean_solo_nml - months = 0 - days = 1 - date_init = {{ date_init }}, - hours = 0 - minutes = 0 - seconds = 0 - calendar = 'NOLEAP' / - &fms_io_nml max_files_w=100 checksum_required=.false. diff --git a/parm/soca/soca_fix_stage_025.yaml.j2 b/parm/soca/soca_fix_stage_025.yaml.j2 index 4f8e398f9..8158b6a3c 100644 --- a/parm/soca/soca_fix_stage_025.yaml.j2 +++ b/parm/soca/soca_fix_stage_025.yaml.j2 @@ -1,9 +1,11 @@ mkdir: - "{{ DATA }}/INPUT" +- "{{ DATA }}/anl_geom" ###################################### # fix files to copy ###################################### copy: +# Deterministic resource files - ["{{ SOCA_INPUT_FIX_DIR }}/rossrad.nc", "{{ DATA }}/rossrad.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/field_table", "{{ DATA }}/field_table"] - ["{{ SOCA_INPUT_FIX_DIR }}/diag_table", "{{ DATA }}/diag_table"] @@ -23,3 +25,6 @@ copy: - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/geothermal_davies2013_v1.nc", "{{ DATA }}/INPUT/geothermal_davies2013_v1.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/interpolate_zgrid_46L.nc", "{{ DATA }}/INPUT/interpolate_zgrid_46L.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/C384_mosaic.nc", "{{ DATA }}/INPUT/C384_mosaic.nc"] +# Analysis resource files +- ["{{ SOCA_ANL_GEOM }}/soca_gridspec.nc", "{{ DATA }}/anl_geom/soca_gridspec.nc"] +- ["{{ SOCA_ANL_GEOM }}/MOM_input", "{{ DATA }}/anl_geom/MOM_input"] \ No newline at end of file diff --git a/parm/soca/soca_fix_stage_100.yaml.j2 b/parm/soca/soca_fix_stage_100.yaml.j2 index 37b9498b7..71785b59c 100644 --- a/parm/soca/soca_fix_stage_100.yaml.j2 +++ b/parm/soca/soca_fix_stage_100.yaml.j2 @@ -1,9 +1,11 @@ mkdir: - "{{ DATA }}/INPUT" +- "{{ DATA }}/anl_geom" ###################################### # fix files to copy ###################################### copy: +# Deterministic resource files - ["{{ SOCA_INPUT_FIX_DIR }}/rossrad.nc", "{{ DATA }}/rossrad.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/field_table", "{{ DATA }}/field_table"] - ["{{ SOCA_INPUT_FIX_DIR }}/diag_table", "{{ DATA }}/diag_table"] @@ -23,3 +25,6 @@ copy: - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/topo_edits_011818.nc", "{{ DATA }}/INPUT/topo_edits_011818.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/MOM_channels_SPEAR", "{{ DATA }}/INPUT/MOM_channels_SPEAR"] - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/seawifs_1998-2006_smoothed_2X.nc", "{{ DATA }}/INPUT/seawifs_1998-2006_smoothed_2X.nc"] +# Analysis resource files +- ["{{ SOCA_ANL_GEOM }}/soca_gridspec.nc", "{{ DATA }}/anl_geom/soca_gridspec.nc"] +- ["{{ SOCA_ANL_GEOM }}/MOM_input", "{{ DATA }}/anl_geom/MOM_input"] diff --git a/parm/soca/soca_fix_stage_500.yaml.j2 b/parm/soca/soca_fix_stage_500.yaml.j2 index 6d6930e0b..82a6e9947 100644 --- a/parm/soca/soca_fix_stage_500.yaml.j2 +++ b/parm/soca/soca_fix_stage_500.yaml.j2 @@ -1,9 +1,11 @@ mkdir: - "{{ DATA }}/INPUT" +- "{{ DATA }}/anl_geom" ###################################### # fix files to copy ###################################### copy: +# Deterministic resource files - ["{{ SOCA_INPUT_FIX_DIR }}/rossrad.nc", "{{ DATA }}/rossrad.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/field_table", "{{ DATA }}/field_table"] - ["{{ SOCA_INPUT_FIX_DIR }}/diag_table", "{{ DATA }}/diag_table"] @@ -15,3 +17,6 @@ copy: - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/ocean_hgrid.nc", "{{ DATA }}/INPUT/ocean_hgrid.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/ocean_mosaic.nc", "{{ DATA }}/INPUT/ocean_mosaic.nc"] - ["{{ SOCA_INPUT_FIX_DIR }}/INPUT/ocean_topog.nc", "{{ DATA }}/INPUT/ocean_topog.nc"] +# Analysis resource files +- ["{{ SOCA_ANL_GEOM }}/soca_gridspec.nc", "{{ DATA }}/anl_geom/soca_gridspec.nc"] +- ["{{ SOCA_ANL_GEOM }}/MOM_input", "{{ DATA }}/anl_geom/MOM_input"] \ No newline at end of file diff --git a/utils/soca/gdas_ens_handler.h b/utils/soca/gdas_ens_handler.h index 89c6e2a2c..741055ca1 100644 --- a/utils/soca/gdas_ens_handler.h +++ b/utils/soca/gdas_ens_handler.h @@ -75,14 +75,22 @@ namespace gdasapp { // ----------------------------------------------------------------------------- int execute(const eckit::Configuration & fullConfig) const { - // Setup the soca geometry + // Setup the native geometry of the ens. members, + // currently assumed to be the same as the deterministic const eckit::LocalConfiguration geomConfig(fullConfig, "geometry"); oops::Log::info() << "geometry: " << std::endl << geomConfig << std::endl; const soca::Geometry geom(geomConfig, this->getComm()); + // Setup the ensemble B (and output) soca geometry + oops::Log::info() << "====================== ens B geometry" << std::endl; + const std::string outputGeometryKey = fullConfig.has("output geometry") + ? "output geometry" // keep things backward compatible for now + : "geometry"; // and default to the input geometry + const eckit::LocalConfiguration geomOutConfig(fullConfig, outputGeometryKey); + const soca::Geometry geomOut(geomOutConfig, this->getComm()); // Initialize the post processing - PostProcIncr postProcIncr(fullConfig, geom, this->getComm()); + PostProcIncr postProcIncr(fullConfig, geom, this->getComm(), geomOut); oops::Log::info() << "soca increments: " << std::endl << postProcIncr.inputIncrConfig_ << std::endl; @@ -93,7 +101,9 @@ namespace gdasapp { oops::Log::info() << postProcIncr.inputIncrConfig_ << std::endl; // Assume z* output is the same for the trajectory and the state - ensMembers.push_back(postProcIncr.read(i)); + soca::Increment fullResIncr = postProcIncr.read(i); + soca::Increment lowResIncr(geomOut, fullResIncr); // interp to low resolution geometry + ensMembers.push_back(lowResIncr); } // Check if we need to recenter the increment around the deterministic @@ -103,9 +113,9 @@ namespace gdasapp { } // Compute ensemble moments - soca::Increment ensMean(geom, postProcIncr.socaIncrVar_, postProcIncr.dt_); - soca::Increment ensStd(geom, postProcIncr.socaIncrVar_, postProcIncr.dt_); - soca::Increment ensVariance(geom, postProcIncr.socaIncrVar_, postProcIncr.dt_); + soca::Increment ensMean(geomOut, postProcIncr.socaIncrVar_, postProcIncr.dt_); + soca::Increment ensStd(geomOut, postProcIncr.socaIncrVar_, postProcIncr.dt_); + soca::Increment ensVariance(geomOut, postProcIncr.socaIncrVar_, postProcIncr.dt_); gdasapp_ens_utils::ensMoments(ensMembers, ensMean, ensStd, ensVariance); oops::Log::info() << "mean: " << ensMean << std::endl; oops::Log::info() << "std: " << ensStd << std::endl; @@ -123,14 +133,15 @@ namespace gdasapp { // Initialize the trajectories used in the linear variable changes const eckit::LocalConfiguration trajConfig(fullConfig, "trajectory"); - soca::State determTraj(geom, trajConfig); // deterministic trajectory + soca::State determTrajNative(geom, trajConfig); // deterministic trajectory at full res + soca::State determTraj(geomOut, determTrajNative); // interpolated deterministic trajectory soca::State ensMeanTraj(determTraj); // trajectory defined as the ens. mean ensMeanTraj.zero(); ensMeanTraj += ensMean; // Compute the recentering increment as the difference between // the ensemble mean and the deterministic - soca::Increment recenteringIncr(geom, postProcIncr.socaIncrVar_, postProcIncr.dt_); + soca::Increment recenteringIncr(geomOut, postProcIncr.socaIncrVar_, postProcIncr.dt_); recenteringIncr.diff(determTraj, ensMeanTraj); postProcIncr.setToZero(recenteringIncr); @@ -192,7 +203,7 @@ namespace gdasapp { // Save total ssh oops::Log::info() << "ssh ensemble member " << i << std::endl; - soca::Increment ssh_tmp(geom, socaSshVar, postProcIncr.dt_); + soca::Increment ssh_tmp(geomOut, socaSshVar, postProcIncr.dt_); ssh_tmp = ensMembers[i]; sshTotal.push_back(ssh_tmp); @@ -257,9 +268,9 @@ namespace gdasapp { } // Compute ensemble moments for total ssh - soca::Increment sshMean(geom, socaSshVar, postProcIncr.dt_); - soca::Increment sshStd(geom, socaSshVar, postProcIncr.dt_); - soca::Increment sshTotalVariance(geom, socaSshVar, postProcIncr.dt_); + soca::Increment sshMean(geomOut, socaSshVar, postProcIncr.dt_); + soca::Increment sshStd(geomOut, socaSshVar, postProcIncr.dt_); + soca::Increment sshTotalVariance(geomOut, socaSshVar, postProcIncr.dt_); gdasapp_ens_utils::ensMoments(sshTotal, sshMean, sshStd, sshTotalVariance); oops::Log::info() << "mean ssh total: " << sshMean << std::endl; oops::Log::info() << "std ssh total: " << sshStd << std::endl; @@ -269,7 +280,7 @@ namespace gdasapp { // Compute ensemble moments for steric ssh sshMean.zero(); sshStd.zero(); - soca::Increment sshStericVariance(geom, socaSshVar, postProcIncr.dt_); + soca::Increment sshStericVariance(geomOut, socaSshVar, postProcIncr.dt_); gdasapp_ens_utils::ensMoments(sshSteric, sshMean, sshStd, sshStericVariance); oops::Log::info() << "mean steric ssh: " << sshMean << std::endl; oops::Log::info() << "std steric ssh: " << sshStd << std::endl; @@ -278,8 +289,8 @@ namespace gdasapp { // Compute ensemble moments for non-steric ssh sshMean.zero(); - soca::Increment sshNonStericVariance(geom, socaSshVar, postProcIncr.dt_); - soca::Increment sshNonStericStd(geom, socaSshVar, postProcIncr.dt_); + soca::Increment sshNonStericVariance(geomOut, socaSshVar, postProcIncr.dt_); + soca::Increment sshNonStericStd(geomOut, socaSshVar, postProcIncr.dt_); sshNonStericStd.zero(); gdasapp_ens_utils::ensMoments(sshNonSteric, sshMean, sshNonStericStd, sshNonStericVariance); oops::Log::info() << "mean non-steric ssh: " << sshMean << std::endl; @@ -308,7 +319,7 @@ namespace gdasapp { ensStd.write(bkgErrOutputConfig); // Explained variance by steric height R=1-SS(non-steric ssh)/SS(total ssh) - soca::Increment varianceRatio(geom, socaSshVar, postProcIncr.dt_); + soca::Increment varianceRatio(geomOut, socaSshVar, postProcIncr.dt_); varianceRatio = sshNonStericVariance; atlas::FieldSet varianceRatioFs; varianceRatio.toFieldSet(varianceRatioFs); @@ -319,7 +330,7 @@ namespace gdasapp { util::divideFieldSets(varianceRatioFs, sshTotalVarianceFs, sshTotalVarianceFs); varianceRatio.fromFieldSet(varianceRatioFs); - soca::Increment stericExplainedVariance(geom, socaSshVar, postProcIncr.dt_); + soca::Increment stericExplainedVariance(geomOut, socaSshVar, postProcIncr.dt_); stericExplainedVariance.ones(); stericExplainedVariance -= varianceRatio; diff --git a/utils/soca/gdas_incr_handler.h b/utils/soca/gdas_incr_handler.h index 18cd19d90..998d1d15d 100644 --- a/utils/soca/gdas_incr_handler.h +++ b/utils/soca/gdas_incr_handler.h @@ -2,6 +2,7 @@ #include #include +#include #include "eckit/config/LocalConfiguration.h" @@ -35,6 +36,12 @@ namespace gdasapp { oops::Log::info() << "geometry: " << std::endl << geomConfig << std::endl; const soca::Geometry geom(geomConfig, this->getComm()); + // Domains to save + std::vector domains = {"ocn", "ice"}; + if (fullConfig.has("domains")) { + fullConfig.get("domains", domains); + } + // Check that we are using at least 2 mpi tasks if (this->getComm().size() < 2) { throw eckit::BadValue("This application requires at least 2 MPI tasks", Here()); @@ -65,7 +72,7 @@ namespace gdasapp { oops::Log::debug() << incr_mom6 << std::endl; // Save final increment - result = postProcIncr.save(incr_mom6, i); + result = postProcIncr.save(incr_mom6, i, domains); oops::Log::debug() << "========= after appending layer and after saving:" << std::endl; oops::Log::debug() << incr_mom6 << std::endl; } diff --git a/utils/soca/gdas_postprocincr.h b/utils/soca/gdas_postprocincr.h index b8638a7b8..0c0550366 100644 --- a/utils/soca/gdas_postprocincr.h +++ b/utils/soca/gdas_postprocincr.h @@ -4,6 +4,7 @@ #include #include +#include #include "eckit/config/LocalConfiguration.h" @@ -22,17 +23,38 @@ namespace gdasapp { +// ----------------------------------------------------------------------------- +/*! \class PostProcIncr + \brief This class handles the processing of increments in the GDAS application. + + The PostProcIncr class is responsible for managing the configuration and processing + of increments, including reading configuration parameters, initializing variables, + applying specified variable changes and handling input and output. +*/ +// ----------------------------------------------------------------------------- class PostProcIncr { public: // ----------------------------------------------------------------------------- - // Constructor - + // Constructors + + /** + * @brief Constructor for the PostProcIncr class. + * + * This constructor initializes the PostProcIncr object using the provided configuration, geometry, and MPI communicator. + * It sets up various parameters and configurations required for post-processing increments. + * + * @param fullConfig The full configuration object containing various settings. + * @param geom The native geometry of the increments output. + * @param comm The MPI communicator. + * @param geomProc The geometry to perform the processing on. + */ PostProcIncr(const eckit::Configuration & fullConfig, const soca::Geometry& geom, - const eckit::mpi::Comm & comm) + const eckit::mpi::Comm & comm, const soca::Geometry& geomProc) : dt_(getDate(fullConfig)), layerVar_(getLayerVar(fullConfig)), geom_(geom), - Layers_(getLayerThickness(fullConfig, geom)), + geomProc_(geomProc), + Layers_(getLayerThickness(fullConfig, geom, geomProc)), comm_(comm), ensSize_(1), pattern_() { @@ -66,6 +88,20 @@ class PostProcIncr { } } + /** + * @brief Constructor for the PostProcIncr class when the compute/processing geometry + * is the same as the native geometry. + * + * This constructor delegates the initialization to the main constructor + * + * @param fullConfig The full configuration object. + * @param geom The geometry object. + * @param comm The MPI communicator. + */ + PostProcIncr(const eckit::Configuration & fullConfig, const soca::Geometry & geom, + const eckit::mpi::Comm & comm) + : PostProcIncr(fullConfig, geom, comm, geom) {} + // ----------------------------------------------------------------------------- // Read ensemble member n @@ -75,7 +111,7 @@ class PostProcIncr { // initialize the soca increment soca::Increment socaIncr(geom_, socaIncrVar_, dt_); - eckit::LocalConfiguration memberConfig; // inputIncrConfig_); + eckit::LocalConfiguration memberConfig; memberConfig = inputIncrConfig_; // replace templated string if necessary @@ -88,8 +124,10 @@ class PostProcIncr { oops::Log::debug() << "-------------------- input increment: " << std::endl; oops::Log::debug() << socaIncr << std::endl; - return socaIncr; - } + + soca::Increment socaIncrOut(geomProc_, socaIncr); + return socaIncrOut; + } // ----------------------------------------------------------------------------- // Append variable to increment @@ -174,7 +212,7 @@ class PostProcIncr { const soca::State& xTraj) { oops::Log::info() << "==========================================" << std::endl; oops::Log::info() << "====== applying specified change of variables" << std::endl; - soca::LinearVariableChange lvc(this->geom_, lvcConfig); + soca::LinearVariableChange lvc(this->geomProc_, lvcConfig); lvc.changeVarTraj(xTraj, socaIncrVar_); lvc.changeVarTL(socaIncr, socaIncrVar_); oops::Log::info() << " in var change:" << socaIncr << std::endl; @@ -183,7 +221,8 @@ class PostProcIncr { // ----------------------------------------------------------------------------- // Save increment - int save(soca::Increment& socaIncr, int ensMem = 1) { + int save(soca::Increment& socaIncr, int ensMem = 1, + const std::vector& domains = {"ocn", "ice"}) { oops::Log::info() << "==========================================" << std::endl; oops::Log::info() << "-------------------- save increment: " << std::endl; oops::Log::info() << socaIncr << std::endl; @@ -201,19 +240,23 @@ class PostProcIncr { // get the output file name std::string outputFileName; outputIncrConfig_.get("output file", outputFileName); - outputFileName = dataDir + "/" + outputFileName; - if (outputIncrConfig_.has("pattern")) { - std::string pattern; - outputIncrConfig_.get("pattern", pattern); - outputFileName = this->swapPattern(outputFileName, pattern, std::to_string(ensMem)); - } - const char* charPtrOut = outputFileName.c_str(); - - std::string incrFname = this->socaFname(); - const char* charPtr = incrFname.c_str(); - - oops::Log::info() << "rename: " << incrFname << " to " << outputFileName << std::endl; - result = std::rename(charPtr, charPtrOut); + + for (const std::string& domain : domains) { + std::string outputDomain = dataDir + "/" + domain + "." +outputFileName; + if (outputIncrConfig_.has("pattern")) { + std::string pattern; + outputIncrConfig_.get("pattern", pattern); + outputDomain = this->swapPattern(outputDomain, pattern, std::to_string(ensMem)); + } + const char* charPtrOut = outputDomain.c_str(); + + // rename the file + std::string incrFname = this->socaFname(domain); + const char* charPtr = incrFname.c_str(); + oops::Log::info() << "domain: " << domain <<" rename: " + << incrFname << " to " << outputDomain << std::endl; + result += std::rename(charPtr, charPtrOut); + } } return result; } @@ -236,27 +279,15 @@ class PostProcIncr { } // Read the layer thickness from the relevant background soca::Increment getLayerThickness(const eckit::Configuration& fullConfig, - const soca::Geometry& geom) const { + const soca::Geometry& geom, + const soca::Geometry& geomProc) const { soca::Increment layerThick(geom, getLayerVar(fullConfig), getDate(fullConfig)); const eckit::LocalConfiguration vertGeomConfig(fullConfig, "vertical geometry"); layerThick.read(vertGeomConfig); - oops::Log::debug() << "layerThick: " << std::endl << layerThick << std::endl; - return layerThick; - } - // Initialize the trajectory - soca::State getTraj(const eckit::Configuration& fullConfig, - const soca::Geometry& geom) const { - if ( fullConfig.has("linear variable change") ) { - const eckit::LocalConfiguration trajConfig(fullConfig, "trajectory"); - soca::State traj(geom, trajConfig); - oops::Log::debug() << "traj:" << traj << std::endl; - return traj; - } else { - oops::Variables tmpVar(fullConfig, "layers variable"); - util::DateTime tmpDate(getDate(fullConfig)); - soca::State traj(geom, tmpVar, tmpDate); - return traj; - } + soca::Increment layerThickOut(geomProc, layerThick); + + oops::Log::debug() << "layerThickOut: " << std::endl << layerThickOut << std::endl; + return layerThickOut; } // ----------------------------------------------------------------------------- @@ -266,7 +297,7 @@ class PostProcIncr { // Recreate the soca filename from the configuration // TODO(guillaume): Change this in soca? // TODO(guillaume): Hard-coded for ocean, implement for seaice as well - std::string socaFname() { + std::string socaFname(const std::string& domain = "ocn") { std::string datadir; outputIncrConfig_.get("datadir", datadir); std::experimental::filesystem::path pathToResolve(datadir); @@ -275,7 +306,7 @@ class PostProcIncr { std::string outputType; outputIncrConfig_.get("type", outputType); std::string incrFname = std::experimental::filesystem::canonical(pathToResolve); - incrFname += "/ocn." + exp + "." + outputType + "." + dt_.toString() + ".nc"; + incrFname += "/" + domain + "." + exp + "." + outputType + "." + dt_.toString() + ".nc"; return incrFname; } @@ -300,7 +331,8 @@ class PostProcIncr { util::DateTime dt_; // valid date of increment oops::Variables layerVar_; // layer variable const soca::Increment Layers_; // layer thicknesses - const soca::Geometry & geom_; + const soca::Geometry & geom_; // Native geometry + const soca::Geometry & geomProc_; // Geometry to perform processing on const eckit::mpi::Comm & comm_; // std::vector inputIncrConfig_; eckit::LocalConfiguration inputIncrConfig_; diff --git a/utils/soca/gdas_soca_diagb.h b/utils/soca/gdas_soca_diagb.h index 5a765355c..fc1ec08da 100644 --- a/utils/soca/gdas_soca_diagb.h +++ b/utils/soca/gdas_soca_diagb.h @@ -204,6 +204,14 @@ namespace gdasapp { oops::Log::info() << "Background:" << std::endl; oops::Log::info() << xb << std::endl; + // Setup the output soca geometry + oops::Log::info() << "====================== output geometry" << std::endl; + const std::string outputGeometryKey = fullConfig.has("output geometry") + ? "output geometry" // keep things backward compatible for now + : "geometry"; // and default to the input geometry + const eckit::LocalConfiguration geomOutConfig(fullConfig, outputGeometryKey); + const soca::Geometry geomOut(geomOutConfig, this->getComm()); + /// Create the mesh connectivity (Copy/paste of Francois's stuff) // -------------------------------------------------------------- // Build edges, then connections between nodes and edges @@ -485,7 +493,8 @@ namespace gdasapp { // Save the background error const eckit::LocalConfiguration bkgErrorConfig(fullConfig, "background error"); - bkgErr.write(bkgErrorConfig); + soca::Increment bkgErrOut(geomOut, bkgErr); + bkgErrOut.write(bkgErrorConfig); return 0; } diff --git a/utils/soca/gdas_socahybridweights.h b/utils/soca/gdas_socahybridweights.h index 8b90dcaa5..55d0b913e 100644 --- a/utils/soca/gdas_socahybridweights.h +++ b/utils/soca/gdas_socahybridweights.h @@ -66,6 +66,14 @@ namespace gdasapp { oops::Log::info() << "geometry: " << std::endl << geomConfig << std::endl; const soca::Geometry geom(geomConfig, this->getComm()); + // Setup the output soca geometry + oops::Log::info() << "====================== ens B geometry" << std::endl; + const std::string outputGeometryKey = fullConfig.has("output geometry") + ? "output geometry" // keep things backward compatible for now + : "geometry"; // and default to the input geometry + const eckit::LocalConfiguration geomOutConfig(fullConfig, outputGeometryKey); + const soca::Geometry geomOut(geomOutConfig, this->getComm()); + /// Get the date std::string strdt; fullConfig.get("date", strdt); @@ -94,6 +102,7 @@ namespace gdasapp { /// Create fields of weights for seaice soca::Increment socaIceHW(geom, socaVars, dt); // ocean field is mandatory for writting socaIceHW.ones(); + // TODO(Guillaume): set the weights based on the ice extent socaIceHW *= wIce; oops::Log::info() << "socaIceHW: " << std::endl << socaIceHW << std::endl; const eckit::LocalConfiguration socaHWOutConfig(fullConfig, "output"); @@ -104,18 +113,21 @@ namespace gdasapp { socaOcnHW.ones(); /// Apply localized gaussians to the weights - eckit::LocalConfiguration localWeightsConfigs(fullConfig, "weights.ocean local weights"); - std::vector localWeightsList = - localWeightsConfigs.getSubConfigurations(); - for (auto & conf : localWeightsList) { - gaussianMask(geom, socaOcnHW, conf); - oops::Log::info() << "Local weights for socaOcnHW: " << std::endl << conf << std::endl; - oops::Log::info() << socaOcnHW << std::endl; + if (fullConfig.has("weights.ocean local weights")) { + eckit::LocalConfiguration localWeightsConfigs(fullConfig, "weights.ocean local weights"); + std::vector localWeightsList = + localWeightsConfigs.getSubConfigurations(); + for (auto & conf : localWeightsList) { + gaussianMask(geom, socaOcnHW, conf); + oops::Log::info() << "Local weights for socaOcnHW: " << std::endl << conf << std::endl; + oops::Log::info() << socaOcnHW << std::endl; + } } socaOcnHW *= wOcean; oops::Log::info() << "socaOcnHW: " << std::endl << socaOcnHW << std::endl; - socaOcnHW.write(socaHWOutConfig); + soca::Increment socaOcnHWOut(geomOut, socaOcnHW); // interp to output geometry + socaOcnHWOut.write(socaHWOutConfig); return 0; } From d300ebe96e8574e9cbd96d6e3444b5fbafb2f628 Mon Sep 17 00:00:00 2001 From: Anna Shlyaeva Date: Wed, 5 Feb 2025 07:12:32 -0700 Subject: [PATCH 5/6] Bugfix for ensemble recenter + add a test (#1477) # Description Bugfix for ensemble recenter; adds a gw-ci test for recenter as well. # Companion PRs Required for https://github.com/NOAA-EMC/global-workflow/pull/3238 # Automated CI tests to run in Global Workflow - [ ] atm_jjob - [ ] C96C48_ufs_hybatmDA - [ ] C96C48_hybatmaerosnowDA - [ ] C48mx500_3DVarAOWCDA - [x] C48mx500_hybAOWCDA - [ ] C96C48_hybatmDA Co-authored-by: Anna Shlyaeva --- parm/soca/berror/soca_ensrecenter.yaml | 2 +- test/gw-ci/CMakeLists.txt | 4 ++++ utils/soca/gdas_ens_handler.h | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/parm/soca/berror/soca_ensrecenter.yaml b/parm/soca/berror/soca_ensrecenter.yaml index b70060140..b7d8e2032 100644 --- a/parm/soca/berror/soca_ensrecenter.yaml +++ b/parm/soca/berror/soca_ensrecenter.yaml @@ -60,5 +60,5 @@ output increment: date: '{{ MARINE_WINDOW_END_ISO }}' exp: trash type: incr - output file: 'ocn.recenter.incr.%mem%.nc' + output file: 'recenter.incr.%mem%.nc' pattern: '%mem%' diff --git a/test/gw-ci/CMakeLists.txt b/test/gw-ci/CMakeLists.txt index 4c08c8a83..99dcfa865 100644 --- a/test/gw-ci/CMakeLists.txt +++ b/test/gw-ci/CMakeLists.txt @@ -180,6 +180,9 @@ function(add_task task_name test_prefix is_full_cycle HALF_CYCLE FULL_CYCLE pslo elseif("${task_name}" STREQUAL "gdas_marineanlletkf") list(APPEND TEST_DEPENDS "${test_prefix}_enkfgdas_fcst_${HALF_CYCLE}") list(APPEND TEST_DEPENDS "${test_prefix}_gdas_prepoceanobs_${FULL_CYCLE}") + elseif("${task_name}" STREQUAL "gdas_ocnanalecen") + list(APPEND TEST_DEPENDS "${test_prefix}_gdas_marineanlvar_${FULL_CYCLE}") + list(APPEND TEST_DEPENDS "${test_prefix}_gdas_marineanlletkf_${FULL_CYCLE}") else() list(APPEND TEST_DEPENDS "${test_prefix}") endif() @@ -394,6 +397,7 @@ if (WORKFLOW_TESTS) "gdas_marinebmat" "gdas_marineanlinit" "gdas_marineanlvar" + "gdas_ocnanalecen" "gdas_marineanlchkpt" "gdas_marineanlfinal" ) diff --git a/utils/soca/gdas_ens_handler.h b/utils/soca/gdas_ens_handler.h index 741055ca1..c60723db5 100644 --- a/utils/soca/gdas_ens_handler.h +++ b/utils/soca/gdas_ens_handler.h @@ -169,7 +169,7 @@ namespace gdasapp { postProcIncr.setToZero(incr); // Save the increments used to initialize the ensemble forecast - result = postProcIncr.save(mom6_incr, i+1); + result = postProcIncr.save(mom6_incr, i+1, {"ocn"}); } return result; } From 07ff8a0166db22ed417b7eb4fb26f01cb62729e9 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA <26926959+RussTreadon-NOAA@users.noreply.github.com> Date: Fri, 7 Feb 2025 12:10:15 -0500 Subject: [PATCH 6/6] add LD_LIBRARY_PATH patch for wcoss2 build (#1484) --- build.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.sh b/build.sh index 6445f80cf..c1f2e67d8 100755 --- a/build.sh +++ b/build.sh @@ -89,6 +89,11 @@ esac CMAKE_OPTS+=" -DCLONE_JCSDADATA=$CLONE_JCSDADATA -DMACHINE=$BUILD_TARGET" +# TODO: Remove LD_LIBRARY_PATH line as soon as permanent solution is available +if [[ $BUILD_TARGET == 'wcoss2' ]]; then + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/opt/cray/pe/mpich/8.1.19/ofi/intel/19.0/lib" +fi + BUILD_DIR=${BUILD_DIR:-$dir_root/build} if [[ $CLEAN_BUILD == 'YES' ]]; then [[ -d ${BUILD_DIR} ]] && rm -rf ${BUILD_DIR}