From b14df7c2a82e8633b41ccb772e3d504c082cea3b Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 29 Jul 2026 20:22:51 +0000 Subject: [PATCH 01/15] #3410 Added get_init_time(netCDF::NcVar *) --- src/libcode/vx_nc_util/nc_utils.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libcode/vx_nc_util/nc_utils.h b/src/libcode/vx_nc_util/nc_utils.h index 1817626e74..dae2a76494 100644 --- a/src/libcode/vx_nc_util/nc_utils.h +++ b/src/libcode/vx_nc_util/nc_utils.h @@ -340,16 +340,17 @@ extern int get_index_at_nc_data(netCDF::NcVar *var, double value_min, double extern netCDF::NcFile* open_ncfile(const char * nc_name, bool write = false); // Moved from nc_cf_file.cc -extern unixtime get_init_time(netCDF::NcFile *nc_file); +extern unixtime get_init_time(netCDF::NcVar *var); +extern unixtime get_init_time(netCDF::NcFile *nc_file, const char *time_var_name=nullptr); extern unixtime get_reference_unixtime(netCDF::NcVar *time_var, int &sec_per_unit, - bool &no_leap_year); + bool &no_leap_year, const char *caller=nullptr); extern bool is_nc_unit_time(const char *units); extern bool is_nc_unit_longitude(const char *units); extern bool is_nc_unit_latitude(const char *units); -extern void parse_cf_time_string(const char *str, unixtime &ref_ut, +extern bool parse_cf_time_string(const char *str, unixtime &ref_ut, int &sec_per_unit); extern void parse_time_string(const char *str, unixtime &ut); From 5a2c8c5fd6afb2ba57cef6667ddd70f05c74e131 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 29 Jul 2026 20:23:57 +0000 Subject: [PATCH 02/15] #3410 Added get_init_time(netCDF::NcVar *) --- src/libcode/vx_nc_util/nc_utils.cc | 126 ++++++++++++++++++----------- 1 file changed, 79 insertions(+), 47 deletions(-) diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index 5a8b6e8ccb..3fea46e70e 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -1157,12 +1157,11 @@ int get_int_var(NcVar * var, const int index) { //////////////////////////////////////////////////////////////////////// double get_nc_time(NcVar * var, const int index) { - double k; vector start; vector count; const char *method_name = "get_nc_time() -> "; - k = bad_data_double; + double k = bad_data_double; if (IS_VALID_NC_P(var)) { int dim_idx = 0; int dim_size = get_dim_size(var, dim_idx); @@ -1183,19 +1182,24 @@ double get_nc_time(NcVar * var, const int index) { exit(1); } + int buf_len = 512; + int dataType = GET_NC_TYPE_ID_P(var); + int dim_count = get_dim_count(var); + if (dataType == NC_CHAR) { + buf_len = get_dim_size(var, (dim_count-1)); + } + int vi; short vs; float vf; ncbyte vb; long long vl; unixtime ref_ut; - int buf_len = 512; - vector tmp_buf(buf_len); - int dataType = GET_NC_TYPE_ID_P(var); + vector tmp_buf(buf_len+1); start.emplace_back(index); count.emplace_back(1); - tmp_buf[0] = 0; + tmp_buf[0] = tmp_buf[buf_len] = 0; switch (dataType) { case NC_DOUBLE: @@ -1214,12 +1218,13 @@ double get_nc_time(NcVar * var, const int index) { k = (double)vb; break; case NC_CHAR: - if (2 == get_dim_count(var)) { - buf_len = get_dim_size(var, 1); + if (2 == dim_count) { start.emplace_back(0); count.emplace_back(buf_len); } + else count[0] = buf_len; for (int i=0; igetVar(start, count, tmp_buf.data()); parse_time_string(tmp_buf.data(), ref_ut); k = ref_ut; @@ -3617,61 +3622,87 @@ int get_data_size(NcVar *var) { return data_size; } +//////////////////////////////////////////////////////////////////////// + +unixtime get_init_time(NcVar *time_var) { + unixtime init_time = 0; + const char *method_name = "get_init_time(NcVar *) -> "; + + if (IS_INVALID_NC_P(time_var)) { + mlog << Debug(4) << method_name + << "could not extract init time\n"; + return init_time; + } + + int data_type = GET_NC_TYPE_ID_P(time_var); + bool is_string_time = (NC_UBYTE == data_type || NC_BYTE == data_type + || NC_CHAR == data_type || NC_STRING == data_type); + double time_value = get_nc_time(time_var, 0); + init_time = time_value; + if (!is_string_time) { + int sec_per_unit = 0; + bool no_leap_year = true; + unixtime ref_ut = get_reference_unixtime(time_var, sec_per_unit, + no_leap_year, method_name); + init_time = ref_ut + (unixtime)(sec_per_unit * time_value); + } + + mlog << Debug(4) << method_name + << "get InitTime (" << unix_to_yyyymmdd_hhmmss(init_time) + << ") from \"" << GET_NC_NAME_P(time_var) << "\" variable (value=" + << time_value << ").\n"; + + return init_time; +} //////////////////////////////////////////////////////////////////////// // Moved from nc_cf_file.cc -// init_time or valid_time from filename` +// init_time from NetCDF, default variable name is forecast_reference_time +// Can get valid_time id byte or char type with variable name string init_time_var_name = "forecast_reference_time"; -unixtime get_init_time(NcFile *nc_file) { + +unixtime get_init_time(NcFile *nc_file, const char *time_var_name) { unixtime init_time = 0; - NcVar init_time_var = get_var(nc_file, init_time_var_name.c_str()); const char *method_name = "get_init_time(NcFile *, string &) -> "; - if (IS_INVALID_NC(init_time_var)) { + if (time_var_name == nullptr) time_var_name = init_time_var_name.c_str(); + + NcVar time_var = get_var(nc_file, time_var_name); + if (IS_INVALID_NC(time_var)) { mlog << Debug(4) << method_name << "could not extract init time from the " - << "\"" << init_time_var_name << "\" variable.\n"; + << "\"" << time_var_name << "\" variable.\n"; } else { - unixtime ut = 0; - int sec_per_unit = 0; - ConcatString units; - - // Parse the units for the time variable. - if (get_var_units(&init_time_var, units)) { - if (units.empty()) { - mlog << Warning << "\n" << method_name - << "the \"" << init_time_var_name << "\" variable must contain a \"units\" attribute.\n\n"; - } - else { - parse_cf_time_string(units.c_str(), ut, sec_per_unit); - } - } - - double time_value = get_nc_time(&init_time_var,0); - init_time = ut + (unixtime)(sec_per_unit * time_value); - mlog << Debug(4) << method_name - << "get InitTime (" << unix_to_yyyymmdd_hhmmss(init_time) - << ") from \"" << init_time_var_name << "\" variable (value=" << time_value<< ").\n"; + init_time = get_init_time(&time_var); } return init_time; } //////////////////////////////////////////////////////////////////////// +// retrurn 0 if units attribute is missing unixtime get_reference_unixtime(NcVar *time_var, int &sec_per_unit, - bool &no_leap_year) { + bool &no_leap_year, const char *caller) { unixtime ref_ut = 0; ConcatString time_unit_str; static const char *method_name = "get_reference_unixtime() -> "; + sec_per_unit = 1; if (get_var_units(time_var, time_unit_str)) { - parse_cf_time_string(time_unit_str.c_str(), ref_ut, sec_per_unit); - no_leap_year = (sec_per_day == sec_per_unit) ? get_att_no_leap_year(time_var) : false; - } - else { - sec_per_unit = 1; + if (time_unit_str.empty()) { + int data_type = GET_NC_TYPE_ID_P(time_var); + if (NC_UBYTE != data_type && NC_BYTE != data_type) { + mlog << Warning << "\n" << (caller != nullptr ? caller : method_name) + << "the \"" << GET_NC_NAME_P(time_var) + << "\" variable must contain a \"units\" attribute.\n\n"; + } + } + else { + parse_cf_time_string(time_unit_str.c_str(), ref_ut, sec_per_unit); + } } + no_leap_year = (sec_per_day == sec_per_unit) ? get_att_no_leap_year(time_var) : false; return ref_ut; } @@ -3708,7 +3739,7 @@ bool is_nc_unit_time(const char *units) { //////////////////////////////////////////////////////////////////////// -void parse_cf_time_string(const char *str, unixtime &ref_ut, +bool parse_cf_time_string(const char *str, unixtime &ref_ut, int &sec_per_unit) { static const char *method_name = "parse_cf_time_string() -> "; @@ -3721,7 +3752,7 @@ void parse_cf_time_string(const char *str, unixtime &ref_ut, mlog << Warning << "\n" << method_name << "unexpected NetCDF CF convention time unit \"" << str << "\"\n\n"; - return; + return false; } else { // Tokenize the input string @@ -3756,7 +3787,7 @@ void parse_cf_time_string(const char *str, unixtime &ref_ut, mlog << Warning << "\n" << method_name << "Unsupported time step in the CF convention time unit \"" << str << "\"\n\n"; - return; + return false; } // Parse the reference time @@ -3775,7 +3806,7 @@ void parse_cf_time_string(const char *str, unixtime &ref_ut, << "\"\n\t\t as a reference time of " << unix_to_yyyymmdd_hhmmss(ref_ut) << " and " << sec_per_unit << " second(s) per time step.\n"; - return; + return true; } //////////////////////////////////////////////////////////////////////// @@ -3800,16 +3831,17 @@ void parse_time_string(const char *str, unixtime &ut) { // 2016-01-28T12:00:00Z // 1977-08-07 12:00:00Z StringArray tok; + StringArray hms; + StringArray ymd; tok.parse_delim(str, " _T"); // Parse the reference time - StringArray ymd, hms; ymd.parse_delim(tok[0], "-"); if(tok.n_elements() > 1) hms.parse_delim(tok[1], ":"); else hms.parse_delim("00:00:00", ":"); ut = mdyhms_to_unix(atoi(ymd[1].c_str()), atoi(ymd[2].c_str()), - atoi(ymd[0].c_str()), atoi(hms[0].c_str()), - hms.n_elements() > 1 ? atoi(hms[1].c_str()) : 0, - hms.n_elements() > 2 ? atoi(hms[2].c_str()) : 0); + atoi(ymd[0].c_str()), atoi(hms[0].c_str()), + hms.n_elements() > 1 ? atoi(hms[1].c_str()) : 0, + hms.n_elements() > 2 ? atoi(hms[2].c_str()) : 0); } mlog << Debug(4) << method_name From c0c8e41a3dd251005bc277218525b43bc0969a4a Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 29 Jul 2026 20:28:41 +0000 Subject: [PATCH 03/15] #3410 Added init_time --- data/config/UGridConfig_mpas | 3 ++- docs/Users_Guide/appendixH.rst | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/data/config/UGridConfig_mpas b/data/config/UGridConfig_mpas index 189eb3a959..73d8046c1b 100644 --- a/data/config/UGridConfig_mpas +++ b/data/config/UGridConfig_mpas @@ -18,7 +18,8 @@ ugrid_metadata_map = [ { key = "lat_face"; val = "latCell"; }, { key = "lon_face"; val = "lonCell"; }, { key = "vert_face"; val = "zCell"; }, // optional - { key = "time"; val = "xtime"; } + { key = "time"; val = "xtime"; }, + { key = "init_time"; val = "initial_time"; } // optional ]; // diff --git a/docs/Users_Guide/appendixH.rst b/docs/Users_Guide/appendixH.rst index 49669c5bad..1a78d00231 100644 --- a/docs/Users_Guide/appendixH.rst +++ b/docs/Users_Guide/appendixH.rst @@ -43,6 +43,9 @@ To correctly parse the UGRID topology, MET needs the following information that * - time - Coordinate variable for time - Required + * - init_time + - Coordinate variable for initial time (for the lead time) + - Optional * - dim_vert - Dimension name for the vertical coordinate, if present - Optional From e42205bf96f69a9c4948355a20096d860c78fc61 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 29 Jul 2026 21:23:10 +0000 Subject: [PATCH 04/15] #3410 Support InitTime from the init_time configuration --- src/libcode/vx_data2d_ugrid/ugrid_file.cc | 322 ++++++++++++---------- src/libcode/vx_data2d_ugrid/ugrid_file.h | 9 +- 2 files changed, 177 insertions(+), 154 deletions(-) diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.cc b/src/libcode/vx_data2d_ugrid/ugrid_file.cc index e2597d3c58..bbb81458f7 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.cc +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.cc @@ -23,7 +23,6 @@ #include #include "vx_math.h" -#include "vx_cal.h" #include "vx_log.h" #include "config_util.h" @@ -46,7 +45,7 @@ array DIM_KEYS = { array COORD_VAR_KEYS = { "time", "lat_face", "lon_face", "vert_face", "lat_edge", - "lon_edge", "lat_node", "lon_node", "cell_id" + "lon_edge", "lat_node", "lon_node", "cell_id", "init_time" }; static double get_nc_var_att_double(const NcVar *nc_var, const char *att_name, @@ -97,6 +96,9 @@ void UGridFile::init_from_scratch() _tDim = (NcDim *)nullptr; _latVar = (NcVar *)nullptr; _lonVar = (NcVar *)nullptr; + _zVar = (NcVar *)nullptr; + _tVar = (NcVar *)nullptr; + _init_time_var = (NcVar *)nullptr; // Close any existing file @@ -219,18 +221,18 @@ bool UGridFile::open(const char * filepath) bool UGridFile::open_metadata(const char * filepath) { - unixtime ut = 0; const char *method_name = "UGridFile::open_metadata() -> "; // Open the file _ncMetaFile = open_ncfile(filepath); + mlog << Debug(7) << method_name << "open " << filepath << "\n"; + if (IS_INVALID_NC_P(_ncMetaFile)) { close(); exit(1); } - NcDim dim; StringArray dim_names; get_dim_names(_ncMetaFile, &dim_names); @@ -256,152 +258,23 @@ bool UGridFile::open_metadata(const char * filepath) assign_dim_from_metadata(_ncFile, _tDim, DIM_KEYS[3], dim_names); // Time dimension assign_dim_from_metadata(_ncFile, _virtDim, DIM_KEYS[4], dim_names); // Vertical dimension - int max_dim_count = 0; - StringArray var_names; - ConcatString att_value; - auto z_var = (NcVar *)nullptr; - auto valid_time_var = (NcVar *)nullptr; - string time_dim_name = find_metadata_name(DIM_KEYS[3], dim_names); - string vert_dim_name = find_metadata_name(DIM_KEYS[4], dim_names); + metadata_coord_variables(); - StringArray time_names = get_metadata_names(COORD_VAR_KEYS[0]); - StringArray lat_names = get_metadata_names(COORD_VAR_KEYS[1]); - StringArray lon_names = get_metadata_names(COORD_VAR_KEYS[2]); - StringArray z_names = get_metadata_names(COORD_VAR_KEYS[3]); - for (int j=0; j max_dim_count) max_dim_count = dim_count; - - // parse the variable attributes - get_att_str( MetaVar[j], long_name_att_name, MetaVar[j].long_name_att ); - get_att_str( MetaVar[j], units_att_name, MetaVar[j].units_att ); - - if (0 == j && nullptr == _time_var_info) { - valid_time_var = MetaVar[j].var; - _time_var_info = &MetaVar[j]; - } - else if (1 == j && nullptr == _latVar) _latVar = MetaVar[j].var; - else if (2 == j && nullptr == _lonVar) _lonVar = MetaVar[j].var; - else if (3 == j && nullptr == z_var) z_var = MetaVar[j].var; - } - } // for j - - - // Pull out the valid and init times - if (IS_INVALID_NC_P(valid_time_var)) { + InitTime = 0; + if (!metadata_time()) { mlog << Debug(4) << method_name << "could not extract valid time from the " << "time variable from " << filepath << "\n"; - - ValidTime.add(ut); } - else { - int sec_per_unit; - - // Store the dimension for the time variable as the time dimension - ConcatString units; - bool use_bounds_var = false; - int time_dim_count = get_dim_count(valid_time_var); - if (time_dim_count == 1 || time_dim_count == 2) { - NcDim tDim = get_nc_dim(valid_time_var, 0); - if (IS_VALID_NC(tDim)) { - _tDim = new NcDim(tDim); - } - } - - // Parse the units for the time variable. - ut = sec_per_unit = 0; - if (get_var_units(valid_time_var, units) && (time_dim_count < 2)) { - if (units.empty()) { - mlog << Warning << "\n" << method_name - << "the \"time\" variable must contain a \"units\" attribute. " - << "Using valid time of 0\n\n"; - } - else { - mlog << Debug(4) << method_name - << "parsing units for the time variable \"" << units << "\"\n"; - parse_cf_time_string(units.c_str(), ut, sec_per_unit); - } - } - // Determine the number of times present. - int n_times = IS_VALID_NC_P(_tDim) ? get_dim_size(_tDim) - : get_data_size(valid_time_var); - int tim_buf_size = n_times; - vector time_values(tim_buf_size); - if(2 == time_dim_count) { - for(int i=0; i 1 ) { - double latest_time = bad_data_double; - for(int i=0; ivar; + if (info) _zVar = info->var; } // Pull out the vertical levels - if (IS_VALID_NC_P(z_var)) { - - int z_count = get_data_size(z_var); + if (IS_VALID_NC_P(_zVar)) { + int z_count = get_data_size(_zVar); vector z_values(z_count); - if( get_nc_data(z_var, z_values.data()) ) { + if( get_nc_data(_zVar, z_values.data()) ) { for(int i=0; i max_dim_count) max_dim_count = dim_count; Var[j].Dims = new NcDim * [dim_count]; @@ -864,6 +737,151 @@ int UGridFile::lead_time() const //////////////////////////////////////////////////////////////////////// +void UGridFile::metadata_coord_variables() { + static const string method_name + = "UGridFile::metadata_coord_variables() => "; + + //Variables at the data file first + StringArray time_names = get_metadata_names(COORD_VAR_KEYS[0]); + StringArray lat_names = get_metadata_names(COORD_VAR_KEYS[1]); + StringArray lon_names = get_metadata_names(COORD_VAR_KEYS[2]); + StringArray z_names = get_metadata_names(COORD_VAR_KEYS[3]); + StringArray init_time_names = get_metadata_names(COORD_VAR_KEYS[9]); + for (int j=0; j time_values(n_times); + if(is_string_time) { // String type: YYYY-MM-DD HH:MM:SS + mlog << Debug(7) << method_name + << "from " << GET_NC_NAME_P(_tVar) << "\n"; + for(int i=0; i _lat(face_count); vector _lon(face_count); - + if (IS_INVALID_NC_P(_latVar)) { mlog << Error << "\n" << method_name << "latitude variable is missing\n\n"; exit(1); diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.h b/src/libcode/vx_data2d_ugrid/ugrid_file.h index a0d1828d60..a73f4d07c9 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.h +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.h @@ -30,7 +30,7 @@ static const int UG_DIM_COUNT = 5; -static const int UG_META_VAR_COUNT = 9; +static const int UG_META_VAR_COUNT = 10; //////////////////////////////////////////////////////////////////////// @@ -65,7 +65,7 @@ class UGridFile { return 1; } - NcVarInfo *get_time_var_info() const { return _time_var_info; } + //NcVarInfo *get_time_var_info() const { return _time_var_info; } // // time @@ -146,6 +146,9 @@ class UGridFile { netCDF::NcVar *_latVar; netCDF::NcVar *_lonVar; + netCDF::NcVar *_zVar; + netCDF::NcVar *_tVar; + netCDF::NcVar *_init_time_var; NcVarInfo *_time_var_info; int face_count; @@ -165,6 +168,8 @@ class UGridFile { std::string find_metadata_name(const std::string &key, const StringArray &available_names); StringArray get_metadata_names(const std::string &key); + void metadata_coord_variables(); + bool metadata_time(); void radian_to_degree(std::vector &lat_values, const int lat_count) const; void read_config(const ConcatString &config_filename); void read_netcdf_grid(); From bdc18f5c30df3618abcf26a9ca64d8fc6c0ea342 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 29 Jul 2026 21:43:39 +0000 Subject: [PATCH 05/15] #3410 Cleanup --- src/libcode/vx_data2d_ugrid/ugrid_file.cc | 3 +-- src/libcode/vx_nc_util/nc_utils.cc | 12 ++++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.cc b/src/libcode/vx_data2d_ugrid/ugrid_file.cc index bbb81458f7..c81fb0461d 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.cc +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.cc @@ -824,8 +824,7 @@ bool UGridFile::metadata_time() { } int data_type = GET_NC_TYPE_ID_P(_tVar); - bool is_string_time = (NC_UBYTE == data_type || NC_BYTE == data_type - || NC_CHAR == data_type || NC_STRING == data_type); + bool is_string_time = (NC_CHAR == data_type || NC_STRING == data_type); // Determine the number of times present. int n_times = IS_VALID_NC_P(_tDim) ? get_dim_size(_tDim) diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index 3fea46e70e..1046c85776 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -3635,8 +3635,7 @@ unixtime get_init_time(NcVar *time_var) { } int data_type = GET_NC_TYPE_ID_P(time_var); - bool is_string_time = (NC_UBYTE == data_type || NC_BYTE == data_type - || NC_CHAR == data_type || NC_STRING == data_type); + bool is_string_time = (NC_CHAR == data_type || NC_STRING == data_type); double time_value = get_nc_time(time_var, 0); init_time = time_value; if (!is_string_time) { @@ -3691,12 +3690,9 @@ unixtime get_reference_unixtime(NcVar *time_var, int &sec_per_unit, sec_per_unit = 1; if (get_var_units(time_var, time_unit_str)) { if (time_unit_str.empty()) { - int data_type = GET_NC_TYPE_ID_P(time_var); - if (NC_UBYTE != data_type && NC_BYTE != data_type) { - mlog << Warning << "\n" << (caller != nullptr ? caller : method_name) - << "the \"" << GET_NC_NAME_P(time_var) - << "\" variable must contain a \"units\" attribute.\n\n"; - } + mlog << Warning << "\n" << (caller != nullptr ? caller : method_name) + << "the \"" << GET_NC_NAME_P(time_var) + << "\" variable does not have a \"units\" attribute.\n\n"; } else { parse_cf_time_string(time_unit_str.c_str(), ref_ut, sec_per_unit); From f44cf8139443100f10a4e501fe3a456ac5502955 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Tue, 4 Aug 2026 17:40:12 +0000 Subject: [PATCH 06/15] #3410 Initialisze result --- internal/test_unit/python/unit.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/test_unit/python/unit.py b/internal/test_unit/python/unit.py index 198d0d052d..54cabf3f2d 100755 --- a/internal/test_unit/python/unit.py +++ b/internal/test_unit/python/unit.py @@ -146,6 +146,7 @@ def unit(test_xml, file_log=None, cmd_only=False, noexit=False, memchk=False, ca ret_ok = (cmd_return.returncode == test['retval']) if ret_ok: out_ok = True + result = None for filepath in test['out_pnc']: result = subprocess.run([mpnc, '-v', filepath], @@ -173,7 +174,10 @@ def unit(test_xml, file_log=None, cmd_only=False, noexit=False, memchk=False, ca break except FileNotFoundError: cmd_outs += (f"\nERROR: stat file missing {filepath}\n") - logger.debug(result.stdout) + if result: + logger.debug(result.stdout) + else: + logger.debug(f"No result object, stat file missing {filepath}") out_ok = False break # check stat file has non-header lines From 9758540453e9c3c4372ff26a27c3daddb9c17578 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Tue, 4 Aug 2026 17:41:40 +0000 Subject: [PATCH 07/15] #3410 Added const to DIM_KEYS and COORD_VAR_KEYS --- src/libcode/vx_data2d_ugrid/ugrid_file.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.cc b/src/libcode/vx_data2d_ugrid/ugrid_file.cc index c81fb0461d..58e6536ec0 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.cc +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.cc @@ -39,11 +39,11 @@ constexpr char def_config_prefix[] = "UGridConfig_"; constexpr char def_config_prefix2[] = "MET_BASE/config/UGridConfig_"; constexpr double lat_epsilon = 0.00001; -array DIM_KEYS = { +const array DIM_KEYS = { "dim_face", "dim_node", "dim_edge", "dim_time", "dim_vert" }; -array COORD_VAR_KEYS = { +const array COORD_VAR_KEYS = { "time", "lat_face", "lon_face", "vert_face", "lat_edge", "lon_edge", "lat_node", "lon_node", "cell_id", "init_time" }; From 80d0b35621a4f38ec726c392f99dc547bb0d6d23 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Tue, 4 Aug 2026 17:42:02 +0000 Subject: [PATCH 08/15] #3410 Added const to init_time_var_name --- src/libcode/vx_nc_util/nc_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index 1046c85776..4d681710e8 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -3658,7 +3658,7 @@ unixtime get_init_time(NcVar *time_var) { // init_time from NetCDF, default variable name is forecast_reference_time // Can get valid_time id byte or char type with variable name -string init_time_var_name = "forecast_reference_time"; +const string init_time_var_name = "forecast_reference_time"; unixtime get_init_time(NcFile *nc_file, const char *time_var_name) { unixtime init_time = 0; From f5ba8e269564e77a9a5e03c9336ce69f41876bcc Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Mon, 17 Aug 2026 22:27:09 +0000 Subject: [PATCH 09/15] #3410 Added init_hour 12 into putput filenames --- internal/test_unit/xml/unit_ugrid.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/test_unit/xml/unit_ugrid.xml b/internal/test_unit/xml/unit_ugrid.xml index 52c3d4b619..255ff94760 100644 --- a/internal/test_unit/xml/unit_ugrid.xml +++ b/internal/test_unit/xml/unit_ugrid.xml @@ -35,8 +35,8 @@ -outdir &OUTPUT_DIR;/grid_stat_ugrid -v 1 - &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_OUT_TO_GRID_000000L_20120409_120000V.stat - &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_OUT_TO_GRID_000000L_20120409_120000V_pairs.nc + &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_OUT_TO_GRID_120000L_20120409_120000V.stat + &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_OUT_TO_GRID_120000L_20120409_120000V_pairs.nc @@ -53,8 +53,8 @@ -outdir &OUTPUT_DIR;/grid_stat_ugrid -v 1 - &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_DIAG_000000L_20120409_120000V.stat - &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_DIAG_000000L_20120409_120000V_pairs.nc + &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_DIAG_120000L_20120409_120000V.stat + &OUTPUT_DIR;/grid_stat_ugrid/grid_stat_UGRID_MPAS_DIAG_120000L_20120409_120000V_pairs.nc @@ -79,7 +79,7 @@ -outdir &OUTPUT_DIR;/point_stat_ugrid -v 1 - &OUTPUT_DIR;/point_stat_ugrid/point_stat_UGRID_MPAS_DIAG_TEMP_000000L_20120409_120000V.stat + &OUTPUT_DIR;/point_stat_ugrid/point_stat_UGRID_MPAS_DIAG_TEMP_120000L_20120409_120000V.stat @@ -98,7 +98,7 @@ -outdir &OUTPUT_DIR;/point_stat_ugrid -v 1 - &OUTPUT_DIR;/point_stat_ugrid/point_stat_UGRID_MPAS_OUT_TEMP_000000L_20120409_120000V.stat + &OUTPUT_DIR;/point_stat_ugrid/point_stat_UGRID_MPAS_OUT_TEMP_120000L_20120409_120000V.stat @@ -118,7 +118,7 @@ -outdir &OUTPUT_DIR;/point_stat_ugrid -v 1 - &OUTPUT_DIR;/point_stat_ugrid/point_stat_UGRID_MPAS_CFG_OUT_TEMP_000000L_20120409_120000V.stat + &OUTPUT_DIR;/point_stat_ugrid/point_stat_UGRID_MPAS_CFG_OUT_TEMP_120000L_20120409_120000V.stat From d599213ad8d002e1222db82bc61a0fc4326dc3de Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Tue, 18 Aug 2026 18:31:18 +0000 Subject: [PATCH 10/15] #3410 Removed _time_var_info --- src/libcode/vx_data2d_ugrid/ugrid_file.cc | 7 ++----- src/libcode/vx_data2d_ugrid/ugrid_file.h | 3 --- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.cc b/src/libcode/vx_data2d_ugrid/ugrid_file.cc index 58e6536ec0..4ffd683a66 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.cc +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.cc @@ -87,7 +87,6 @@ void UGridFile::init_from_scratch() _ncFile = (NcFile *) nullptr; _ncMetaFile = (NcFile *) nullptr; Var = (NcVarInfo *) nullptr; - _time_var_info = (NcVarInfo *)nullptr; _faceDim = (NcDim *)nullptr; _edgeDim = (NcDim *)nullptr; @@ -750,7 +749,6 @@ void UGridFile::metadata_coord_variables() { for (int j=0; j Date: Tue, 18 Aug 2026 18:32:00 +0000 Subject: [PATCH 11/15] #3410 data type conversion to init_time --- src/libcode/vx_nc_util/nc_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index 4d681710e8..bee4e7b44c 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -3637,7 +3637,7 @@ unixtime get_init_time(NcVar *time_var) { int data_type = GET_NC_TYPE_ID_P(time_var); bool is_string_time = (NC_CHAR == data_type || NC_STRING == data_type); double time_value = get_nc_time(time_var, 0); - init_time = time_value; + init_time = (unixtime)time_value; if (!is_string_time) { int sec_per_unit = 0; bool no_leap_year = true; From f0f0c1dbc7c3e06c7f6573127182f16fc7f2d5ca Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Tue, 18 Aug 2026 18:47:11 +0000 Subject: [PATCH 12/15] #3410 Type conversion for SonarQube findings --- src/libcode/vx_data2d_ugrid/ugrid_file.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.cc b/src/libcode/vx_data2d_ugrid/ugrid_file.cc index 4ffd683a66..c1fd8efcfd 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.cc +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.cc @@ -511,7 +511,7 @@ double UGridFile::getData(NcVar * var, const LongArray & a) const // done mlog << Debug(6) << method_name << "took " - << (clock()-start_clock)/double(CLOCKS_PER_SEC) << " seconds\n"; + << (clock()-start_clock)/CLOCKS_PER_SEC << " seconds\n"; return d; } @@ -589,7 +589,7 @@ bool UGridFile::getData(NcVar * v, const LongArray & a, DataPlane & plane) const } else { offsets.add(a[k]); - if (k != var->t_slot && k != var->z_slot) length = plane_size - a[k]; + if (k != var->t_slot && k != var->z_slot) length = (int)(plane_size - a[k]); } lengths.add(length); dim_size = v->getDim(k).getSize(); @@ -626,7 +626,7 @@ bool UGridFile::getData(NcVar * v, const LongArray & a, DataPlane & plane) const log_message << " " << (a[idx] == vx_data2d_star ? "*" : std::to_string(a[idx])); } mlog << Debug(6) << method_name << "took " - << (clock()-start_clock)/double(CLOCKS_PER_SEC) << " seconds. " + << (clock()-start_clock)/CLOCKS_PER_SEC << " seconds. " << GET_NC_NAME_P(v) << ": levels: (" << log_message << " )" << " min=" << min_value << ", max_value=" << max_value<< "\n"; @@ -647,7 +647,7 @@ bool UGridFile::getData(const char *var_name, // store the times unixtime valid_ut; - if(info->t_slot >= 0) valid_ut = ValidTime[a[info->t_slot]]; + if(info->t_slot >= 0) valid_ut = ValidTime[(int)a[info->t_slot]]; else valid_ut = ValidTime[0]; // if unset, set the init time to the valid time @@ -664,8 +664,8 @@ bool UGridFile::getData(const char *var_name, plane.set_init(init_ut); plane.set_valid(valid_ut); - plane.set_lead(valid_ut - init_ut); - plane.set_accum(accum_time); + plane.set_lead((int)(valid_ut - init_ut)); + plane.set_accum((int)accum_time); // done From 91deca968deeb6c09ae4052a0f5e0773ec10b4bb Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 19 Aug 2026 00:43:00 +0000 Subject: [PATCH 13/15] #3410 Separated assignments --- src/libcode/vx_nc_util/nc_utils.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index bee4e7b44c..cf16298f97 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -1199,7 +1199,8 @@ double get_nc_time(NcVar * var, const int index) { start.emplace_back(index); count.emplace_back(1); - tmp_buf[0] = tmp_buf[buf_len] = 0; + tmp_buf[0] = 0; + tmp_buf[buf_len] = 0; switch (dataType) { case NC_DOUBLE: From 1644826399874709e5280b7d946f74edaba2f707 Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 26 Aug 2026 10:44:41 -0600 Subject: [PATCH 14/15] Update docs/Users_Guide/appendixH.rst Co-authored-by: Dan Adriaansen --- docs/Users_Guide/appendixH.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Users_Guide/appendixH.rst b/docs/Users_Guide/appendixH.rst index 1a78d00231..32242fcec4 100644 --- a/docs/Users_Guide/appendixH.rst +++ b/docs/Users_Guide/appendixH.rst @@ -44,7 +44,7 @@ To correctly parse the UGRID topology, MET needs the following information that - Coordinate variable for time - Required * - init_time - - Coordinate variable for initial time (for the lead time) + - Coordinate variable for initialization time (used to compute the lead time) - Optional * - dim_vert - Dimension name for the vertical coordinate, if present From ddeff76b20868b6bd0961507062bab8cb5f28ccb Mon Sep 17 00:00:00 2001 From: Howard Soh Date: Wed, 26 Aug 2026 10:44:50 -0600 Subject: [PATCH 15/15] Update docs/Users_Guide/appendixH.rst Co-authored-by: Dan Adriaansen --- docs/Users_Guide/appendixH.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Users_Guide/appendixH.rst b/docs/Users_Guide/appendixH.rst index 32242fcec4..52a33ea903 100644 --- a/docs/Users_Guide/appendixH.rst +++ b/docs/Users_Guide/appendixH.rst @@ -41,7 +41,7 @@ To correctly parse the UGRID topology, MET needs the following information that - Dimension name for the time coordinate - Required * - time - - Coordinate variable for time + - Coordinate variable for valid time - Required * - init_time - Coordinate variable for initialization time (used to compute the lead time)