From 7871489d0c0afbc33e08acdb6bfecaeec1ce48f3 Mon Sep 17 00:00:00 2001 From: jiaruidong2017 Date: Sat, 27 Dec 2025 15:49:15 -0500 Subject: [PATCH 1/4] Enable to read IMS data in netcdf format --- parm/snow/snow_stage_ims_scf2ioda.yaml.j2 | 2 +- utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc | 46 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 b/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 index 9636d2ff7..97273342c 100644 --- a/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 +++ b/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 @@ -1,2 +1,2 @@ -- ['{{ ims_file }}', '{{ DATA }}/obs/ims{{ current_cycle | to_julian }}_4km_v1.3.asc'] +- ['{{ ims_file }}', '{{ DATA }}/obs/ims{{ current_cycle | to_julian }}_4km_v1.3.dat'] - ['{{ FIXgfs }}/gdas/obs/ims/IMS_4km_to_{{ CASE }}.mx{{ OCNRES }}.nc', '{{ DATA }}/obs/IMS4km_to_FV3_mapping.{{ CASE }}.mx{{ OCNRES }}_oro_data.nc'] diff --git a/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc b/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc index aafe2e02a..244be6c70 100644 --- a/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc +++ b/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc @@ -393,9 +393,51 @@ void gdasapp::CalcSCFtoIODA::IMSscf::readIMS() { if (ncfile.isNull() == false) { isNetCDF = true; oops::Log::info() << "Opened IMS file as netCDF: " << imspath_ << std::endl; - // TODO(CoryMartin-NOAA) ... process netCDF file here ... + + // define and get dimensions + netCDF::NcDim xDim = ncfile.getDim("x"); + netCDF::NcDim yDim = ncfile.getDim("y"); + netCDF::NcDim tDim = ncfile.getDim("time"); + + size_t nx = xDim.getSize(); + size_t ny = yDim.getSize(); + size_t nt = tDim.getSize(); + + oops::Log::info() << "IMS dimensions: " + << nx << " x " << ny + << " time=" << nt << std::endl; + + // Get IMS variable + netCDF::NcVar imsVar = ncfile.getVar("IMS_Surface_Values"); + if (imsVar.isNull()) { + throw eckit::UserError("IMS_Surface_Values variable not found", Here()); + } + + // Allocate storage + this->IMS_flag.resize(ny, std::vector(nx)); + this->IMS_index.resize(ny, + std::vector>(nx, std::vector(3))); + + // IMS_Surface_Values is: byte(time, y, x) + std::vector buffer(nx * ny); + + // Read only time index 0 + std::vector start = {0, 0, 0}; + std::vector count = {1, ny, nx}; + + imsVar.getVar(start, count, buffer.data()); + + // Copy into IMS_flag (row-major: y, x) + for (size_t j = 0; j < ny; ++j) { + for (size_t i = 0; i < nx; ++i) { + this->IMS_flag[j][i] = static_cast(buffer[j * nx + i]); + } + } + + oops::Log::info() << "IMS NetCDF data successfully read" << std::endl; } - } catch (...) { + ncfile.close(); + } catch (netCDF::exceptions::NcException &e) { oops::Log::info() << "Failed to open as netCDF, will try ASCII: " << imspath_ << std::endl; } From 6d83db96320d266520b053281b90cc6dddb6bd0d Mon Sep 17 00:00:00 2001 From: jiaruidong2017 Date: Mon, 29 Dec 2025 14:13:20 -0500 Subject: [PATCH 2/4] Add missing null checks for netCDF dimensions --- utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc b/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc index 244be6c70..4b345ceca 100644 --- a/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc +++ b/utils/land/gdas_fv3jedi_calc_scf_to_ioda.cc @@ -399,6 +399,16 @@ void gdasapp::CalcSCFtoIODA::IMSscf::readIMS() { netCDF::NcDim yDim = ncfile.getDim("y"); netCDF::NcDim tDim = ncfile.getDim("time"); + if (xDim.isNull()) { + throw eckit::UserError("Dimension 'x' not found in IMS netCDF file: " + imspath_, Here()); + } + if (yDim.isNull()) { + throw eckit::UserError("Dimension 'y' not found in IMS netCDF file: " + imspath_, Here()); + } + if (tDim.isNull()) { + throw eckit::UserError("Dimension 'time' not found in IMS netCDF file: " + imspath_, Here()); + } + size_t nx = xDim.getSize(); size_t ny = yDim.getSize(); size_t nt = tDim.getSize(); From d377cc5baac52650d14b430aad75efbf66224fe2 Mon Sep 17 00:00:00 2001 From: jiaruidong2017 Date: Tue, 30 Dec 2025 16:25:03 -0500 Subject: [PATCH 3/4] Use manually defined IMS input suffix --- parm/snow/jcb-base.yaml.j2 | 1 + parm/snow/snow_stage_ims_scf2ioda.yaml.j2 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/parm/snow/jcb-base.yaml.j2 b/parm/snow/jcb-base.yaml.j2 index f8da16f45..1067a721e 100644 --- a/parm/snow/jcb-base.yaml.j2 +++ b/parm/snow/jcb-base.yaml.j2 @@ -87,6 +87,7 @@ snow_obsdataout_path: "{{DATA}}/diags" snow_prepobs_path: "{{DATA}}/prep" snow_obsdataout_prefix: diag_ snow_obsdataout_suffix: "_{{ current_cycle | to_YMDH }}.nc" +ims_scf_obs_suffix: "{{ ims_scf_obs_suffix }}" # Naming conventions for bias correction files snow_obsbiasin_path: "{{DATA}}/obs/" diff --git a/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 b/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 index 97273342c..168702a18 100644 --- a/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 +++ b/parm/snow/snow_stage_ims_scf2ioda.yaml.j2 @@ -1,2 +1,2 @@ -- ['{{ ims_file }}', '{{ DATA }}/obs/ims{{ current_cycle | to_julian }}_4km_v1.3.dat'] +- ['{{ ims_file }}', '{{ DATA }}/obs/ims{{ current_cycle | to_julian }}_4km_v1.3.{{ ims_scf_obs_suffix }}'] - ['{{ FIXgfs }}/gdas/obs/ims/IMS_4km_to_{{ CASE }}.mx{{ OCNRES }}.nc', '{{ DATA }}/obs/IMS4km_to_FV3_mapping.{{ CASE }}.mx{{ OCNRES }}_oro_data.nc'] From 5972feb67ee7c3f7e5bc1452a27a17f6ee1808ad Mon Sep 17 00:00:00 2001 From: jiaruidong2017 Date: Fri, 2 Jan 2026 13:19:32 -0500 Subject: [PATCH 4/4] Update the jcb-gdas repo --- parm/jcb-gdas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parm/jcb-gdas b/parm/jcb-gdas index 49a8c56ac..b83769de9 160000 --- a/parm/jcb-gdas +++ b/parm/jcb-gdas @@ -1 +1 @@ -Subproject commit 49a8c56ac2730fe6e93da88815a37b48a9e14ca5 +Subproject commit b83769de9a8fe2e7b20cf92fd9dc1e86da504448