diff --git a/internal/test_unit/xml/unit_plot_data_plane.xml b/internal/test_unit/xml/unit_plot_data_plane.xml index 999773a57d..e84a5bcb66 100644 --- a/internal/test_unit/xml/unit_plot_data_plane.xml +++ b/internal/test_unit/xml/unit_plot_data_plane.xml @@ -657,7 +657,7 @@ \ &DATA_DIR_MODEL;/wrf/wrfout_solarwind_d02_2019-08-31_12:00:00 \ &OUTPUT_DIR;/plot_data_plane/wrf_num_z_levels_stag.ps \ - 'name="RH_ZL"; level="(0,0,*,*)";' \ + 'name="RH_ZL"; level="(0,@-78,*,*)";' \ -title "Relative Humidity Z-level (num_z_levels_stag)" \ -v 1 diff --git a/src/libcode/vx_data2d/Makefile.am b/src/libcode/vx_data2d/Makefile.am index 9c43d52286..8204de2c7b 100644 --- a/src/libcode/vx_data2d/Makefile.am +++ b/src/libcode/vx_data2d/Makefile.am @@ -15,6 +15,7 @@ libvx_data2d_a_SOURCES = \ leveltype_to_string.cc leveltype_to_string.h \ level_info.cc level_info.h \ var_info.cc var_info.h \ + var_info_nc.cc var_info_nc.h \ var_info_pairs.cc var_info_pairs.h \ data_class.cc data_class.h \ data2d_utils.cc data2d_utils.h \ diff --git a/src/libcode/vx_data2d/Makefile.in b/src/libcode/vx_data2d/Makefile.in index 52e5a7b78e..8a1a1eb83c 100644 --- a/src/libcode/vx_data2d/Makefile.in +++ b/src/libcode/vx_data2d/Makefile.in @@ -111,6 +111,7 @@ am_libvx_data2d_a_OBJECTS = \ libvx_data2d_a-leveltype_to_string.$(OBJEXT) \ libvx_data2d_a-level_info.$(OBJEXT) \ libvx_data2d_a-var_info.$(OBJEXT) \ + libvx_data2d_a-var_info_nc.$(OBJEXT) \ libvx_data2d_a-var_info_pairs.$(OBJEXT) \ libvx_data2d_a-data_class.$(OBJEXT) \ libvx_data2d_a-data2d_utils.$(OBJEXT) \ @@ -139,6 +140,7 @@ am__depfiles_remade = ./$(DEPDIR)/libvx_data2d_a-data2d_utils.Po \ ./$(DEPDIR)/libvx_data2d_a-mask_filters.Po \ ./$(DEPDIR)/libvx_data2d_a-table_lookup.Po \ ./$(DEPDIR)/libvx_data2d_a-var_info.Po \ + ./$(DEPDIR)/libvx_data2d_a-var_info_nc.Po \ ./$(DEPDIR)/libvx_data2d_a-var_info_pairs.Po am__mv = mv -f AM_V_lt = $(am__v_lt_@AM_V@) @@ -357,6 +359,7 @@ libvx_data2d_a_SOURCES = \ leveltype_to_string.cc leveltype_to_string.h \ level_info.cc level_info.h \ var_info.cc var_info.h \ + var_info_nc.cc var_info_nc.h \ var_info_pairs.cc var_info_pairs.h \ data_class.cc data_class.h \ data2d_utils.cc data2d_utils.h \ @@ -420,6 +423,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_data2d_a-mask_filters.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_data2d_a-table_lookup.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_data2d_a-var_info.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_data2d_a-var_info_nc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_data2d_a-var_info_pairs.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @@ -484,6 +488,20 @@ libvx_data2d_a-var_info.obj: var_info.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_data2d_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libvx_data2d_a-var_info.obj `if test -f 'var_info.cc'; then $(CYGPATH_W) 'var_info.cc'; else $(CYGPATH_W) '$(srcdir)/var_info.cc'; fi` +libvx_data2d_a-var_info_nc.o: var_info_nc.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_data2d_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libvx_data2d_a-var_info_nc.o -MD -MP -MF $(DEPDIR)/libvx_data2d_a-var_info_nc.Tpo -c -o libvx_data2d_a-var_info_nc.o `test -f 'var_info_nc.cc' || echo '$(srcdir)/'`var_info_nc.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libvx_data2d_a-var_info_nc.Tpo $(DEPDIR)/libvx_data2d_a-var_info_nc.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='var_info_nc.cc' object='libvx_data2d_a-var_info_nc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_data2d_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libvx_data2d_a-var_info_nc.o `test -f 'var_info_nc.cc' || echo '$(srcdir)/'`var_info_nc.cc + +libvx_data2d_a-var_info_nc.obj: var_info_nc.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_data2d_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libvx_data2d_a-var_info_nc.obj -MD -MP -MF $(DEPDIR)/libvx_data2d_a-var_info_nc.Tpo -c -o libvx_data2d_a-var_info_nc.obj `if test -f 'var_info_nc.cc'; then $(CYGPATH_W) 'var_info_nc.cc'; else $(CYGPATH_W) '$(srcdir)/var_info_nc.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libvx_data2d_a-var_info_nc.Tpo $(DEPDIR)/libvx_data2d_a-var_info_nc.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='var_info_nc.cc' object='libvx_data2d_a-var_info_nc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_data2d_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libvx_data2d_a-var_info_nc.obj `if test -f 'var_info_nc.cc'; then $(CYGPATH_W) 'var_info_nc.cc'; else $(CYGPATH_W) '$(srcdir)/var_info_nc.cc'; fi` + libvx_data2d_a-var_info_pairs.o: var_info_pairs.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_data2d_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libvx_data2d_a-var_info_pairs.o -MD -MP -MF $(DEPDIR)/libvx_data2d_a-var_info_pairs.Tpo -c -o libvx_data2d_a-var_info_pairs.o `test -f 'var_info_pairs.cc' || echo '$(srcdir)/'`var_info_pairs.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libvx_data2d_a-var_info_pairs.Tpo $(DEPDIR)/libvx_data2d_a-var_info_pairs.Po @@ -687,6 +705,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libvx_data2d_a-mask_filters.Po -rm -f ./$(DEPDIR)/libvx_data2d_a-table_lookup.Po -rm -f ./$(DEPDIR)/libvx_data2d_a-var_info.Po + -rm -f ./$(DEPDIR)/libvx_data2d_a-var_info_nc.Po -rm -f ./$(DEPDIR)/libvx_data2d_a-var_info_pairs.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ @@ -740,6 +759,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libvx_data2d_a-mask_filters.Po -rm -f ./$(DEPDIR)/libvx_data2d_a-table_lookup.Po -rm -f ./$(DEPDIR)/libvx_data2d_a-var_info.Po + -rm -f ./$(DEPDIR)/libvx_data2d_a-var_info_nc.Po -rm -f ./$(DEPDIR)/libvx_data2d_a-var_info_pairs.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/src/libcode/vx_data2d/var_info_nc.cc b/src/libcode/vx_data2d/var_info_nc.cc new file mode 100644 index 0000000000..ac072e2eed --- /dev/null +++ b/src/libcode/vx_data2d/var_info_nc.cc @@ -0,0 +1,389 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2026 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + +/////////////////////////////////////////////////////////////////////////////// +// +// Filename: var_info_nc.cc +// +// Description: +// +// Mod# Date Name Description +// ---- ---- ---- ----------- +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "var_info_nc.h" +#include "nc_constants.h" + +using namespace std; + +/////////////////////////////////////////////////////////////////////////////// + +static void check_dim_offset(const char *); + +/////////////////////////////////////////////////////////////////////////////// +// +// Code for class VarInfoNC +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::clear_dimension() { + Dimension.clear(); + Is_offset.clear(); + Dim_value.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::add_dimension(long dim, bool as_offset, double dim_value) { + Dimension.add(dim); + Is_offset.add(as_offset); + Dim_value.add(dim_value); + return; +} + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::parse_level(const ConcatString &level_str) { + const char *method_name = "VarInfoNC::parse_level() -> "; + + if(level_str.empty() + || level_str.string().find_first_of("(") == std::string::npos) { + // Should be handled by the derived class + clear_dimension(); + set_default_levels(level_str); + return; + } + + char *ptr = nullptr; + char *ptr2 = nullptr; + char *ptr3 = nullptr; + char *save_ptr = nullptr; + ConcatString tmp_str; + + // Initialize the temp string + tmp_str = level_str; + + // Parse the level specification + // Retreive the NetCDF level specification + ptr = strtok_r((char*)tmp_str.c_str(), "()", &save_ptr); + + // Set the level name + Level.set_req_name(ptr); + Level.set_name(ptr); + + // If dimensions are specified, clear the default value + if (strchr(ptr, ',') != nullptr) clear_dimension(); + + // Parse the dimensions + bool as_offset = true; + while ((ptr2 = strtok_r(ptr, ",", &save_ptr)) != nullptr) { + // Check for wildcards + if (strchr(ptr2, '*') != nullptr) { + add_dimension(vx_data2d_star); + } + else { + as_offset = (*ptr2 != '@'); + if (!as_offset) ptr2++; + + // Check for a range of levels + ptr3 = strchr(ptr2, '-'); + + //skip negative sign of the negative value to check the range + if (ptr3 != nullptr && ptr3 == ptr2) ptr3 = strchr((ptr2+1), '-'); + + if (ptr3 != nullptr && ptr3 != ptr2) { + // Store the dimension of the range and limits + parse_vertical_range(ptr2, ptr3, as_offset, method_name); + } + // Check for a range of times + else if ((ptr3 = strchr(ptr2, ':')) != nullptr) { + parse_time_range(ptr2, ptr3, as_offset, method_name); + } + else { + parse_single_level(ptr2, as_offset, method_name); + } + } + + // Set ptr to nullptr for next call to strtok + ptr = nullptr; + + } // end while + +} + + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::parse_single_level(const char *lstr, bool as_offset, const char *caller) { + // Single level + long level = 0; + double level_value = bad_data_double; + + if (is_datestring(lstr)) { + unixtime unix_time = timestring_to_unix(lstr); + level = vx_data2d_dim_by_value; + level_value = (double) unix_time; + as_offset = false; + } + else if (is_number(lstr)) { + if (as_offset) { + check_dim_offset(lstr); + level = atoi(lstr); + if (level < 0) { + mlog << Warning << "\n" << caller + << " negative number is not accepted as an offset: " + << level << "\n\n"; + } + } + else { + level = vx_data2d_dim_by_value; + level_value = atof(lstr); + } + } + else { + mlog << Error << "\n" << caller + << "trouble parsing NetCDF dimension value \"" + << lstr << "\"!\n\n"; + exit(1); + } + if (as_offset) add_dimension(level, as_offset); + else add_dimension(level, as_offset, level_value); +} + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::parse_time_range(const char *ptr_lower, char *ptr_upper, + bool as_offset, const char *caller) { + + // Check if a range has already been supplied + if (Dimension.has(range_flag)) { + mlog << Error << "\n" << caller + << "only one dimension can have a range for NetCDF variable \"" + << MagicStr << "\".\n\n"; + exit(1); + } + + int increment = 0; + // Store the dimension of the range and limits + *ptr_upper = 0; + ptr_upper++; + + if(*ptr_upper == '@') { + if (as_offset) { + mlog << Error << "\n" << caller + << "Can not mix an offset and a value for NetCDF variable \"" + << MagicStr << "\".\n\n"; + exit(1); + } + else ptr_upper++; // to support @time1:@time2 + } + + char *ptr_inc = strchr(ptr_upper, ':'); + if (ptr_inc != nullptr) { + *ptr_inc = 0; + ptr_inc++; + } + mlog << Debug(7) << caller + << " start: " << ptr_lower << ", end: " << ptr_upper + << ", as_offset: " << as_offset << "\n"; + + bool datestring_start = is_datestring(ptr_lower); + bool datestring_end = is_datestring(ptr_upper); + if (datestring_start != datestring_end) { + mlog << Error << "\n" << caller + << "the time value and an index/offset can not be mixed for NetCDF variable \"" + << MagicStr << "\".\n\n"; + exit(1); + } + + // Parse the lower and upper time limits + unixtime time_lower = 0; + unixtime time_upper = 0; + + if (datestring_start && datestring_end) { + as_offset = false; + time_lower = timestring_to_unix(ptr_lower); + time_upper = timestring_to_unix(ptr_upper); + } + else if (as_offset) { + + // Check for integer dimension offsets + check_dim_offset(ptr_lower); + check_dim_offset(ptr_upper); + + time_lower = (unixtime) atoi(ptr_lower); + time_upper = (unixtime) atoi(ptr_upper); + } + else { + time_lower = (unixtime) nint(atof(ptr_lower)); + time_upper = (unixtime) nint(atof(ptr_upper)); + } + + if (ptr_inc != nullptr) { + if (as_offset) { + increment = atoi(ptr_inc); + } + else { + increment = is_float(ptr_inc) + ? nint(atof(ptr_inc)) + : timestring_to_sec(ptr_inc); + mlog << Debug(7) << caller + << "increment: \"" << ptr_inc << "\" to " + << increment << " seconds.\n"; + } + } + + add_dimension(range_flag, as_offset); + Level.set_lower((double) time_lower); + Level.set_upper((double) time_upper); + Level.set_increment(increment); + + // Assume time level type for a range of levels + Level.set_type(LevelType_Time); + Level.set_is_offset(as_offset); +} + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::parse_vertical_range(const char *ptr_lower, char *ptr_upper, + bool as_offset, const char *caller) { + const char *method_name = "VarInfoNC::parse_vertical_range() -> "; + + // Check if a range has already been supplied + if (Dimension.has(range_flag)) { + mlog << Error << "\n" << caller + << "only one dimension can have a range for NetCDF variable \"" + << MagicStr << "\".\n\n"; + exit(1); + } + + *ptr_upper = 0; + ptr_upper++; + mlog << Debug(7) << method_name + << " input: start: " << ptr_lower << ", end: " << ptr_upper + << ", as_offset: " << as_offset << "\n"; + + add_dimension(range_flag, as_offset); + if (*ptr_upper == '@') { + if (as_offset) { + mlog << Warning << "\n" << caller + << "Can not mix an offset and a value for NetCDF variable \"" + << MagicStr << "\".\n\n"; + } + ptr_upper++; // to support @vlevel_lower-@vlevel_upper + } + if(as_offset) { + // Check for integer dimension offsets + check_dim_offset(ptr_lower); + check_dim_offset(ptr_upper); + + int lower = atoi(ptr_lower); + int upper = atoi(ptr_upper); + if (lower > upper) { + int tmp = lower; + lower = upper; + upper = tmp; + } + Level.set_lower(lower); + Level.set_upper(upper); + mlog << Debug(7) << method_name + << " offset: lower: " << lower << ", upper: " << upper << "\n"; + if (lower < 0 || upper < 0) { + mlog << Warning << "\n" << method_name + << " negative number is not accepted as an offset: lower=" + << lower << ", upper=" << upper << "\n\n"; + } + } + else { + float lower = atof(ptr_lower); + float upper = atof(ptr_upper); + if (lower > upper) { + float tmp = lower; + lower = upper; + upper = tmp; + } + Level.set_lower(lower); + Level.set_upper(upper); + mlog << Debug(7) << method_name + << " value: lower: " << lower << ", upper: " << upper << "\n"; + } + + // Assume pressure level type for a range of levels + Level.set_type(LevelType_Pres); + Level.set_is_offset(as_offset); +} + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::set_dimension(int i_dim, long dim) const { + Dimension[i_dim] = dim; + return; +} + + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::set_magic_pre(const ConcatString &nstr, const ConcatString &lstr) { + + // Store the magic string + VarInfo::set_magic(nstr, lstr); + + // Set the requested name and default output name + set_req_name(nstr.c_str()); + set_name(nstr); + +} + + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfoNC::set_magic_post(const ConcatString &req_name, const ConcatString &level_name) { + ConcatString tmp_str; + + // Check for "/PROB" to indicate a probability forecast + if (strstr(MagicStr.c_str(), "/PROB") != nullptr) PFlag = true; + + // Set the long name + tmp_str.format("%s(%s)", req_name.text(), level_name.text()); + set_long_name(tmp_str.c_str()); + + // Set the units + set_units(na_str); + +} + + +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// +// Begin miscellaneous utility functions +// +/////////////////////////////////////////////////////////////////////////////// + +static void check_dim_offset(const char *ptr) { + + if(!is_eq(atof(ptr), (double) atoi(ptr))) { + mlog << Warning << "\ncheck_dim_offset() -> " + << "Found non-integer NetCDF dimension index (" + << ptr << " != " << atoi(ptr) << ").\n" + << "Did you intend to use \"@" << ptr + << "\" to specify the value for that dimension instead?\n\n"; + } + + return; +} + +//////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d/var_info_nc.h b/src/libcode/vx_data2d/var_info_nc.h new file mode 100644 index 0000000000..6454c7b9fe --- /dev/null +++ b/src/libcode/vx_data2d/var_info_nc.h @@ -0,0 +1,73 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2026 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef __VAR_INFO_NC_H__ +#define __VAR_INFO_NC_H__ + +/////////////////////////////////////////////////////////////////////////////// + +#include "var_info.h" + +/////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// + +class VarInfoNC : public VarInfo +{ + protected: + + LongArray Dimension; // Dimension values for extracting 2D field + BoolArray Is_offset; // boolean for Dimension value (true: offset, false: value to be an offset (false for value) + NumArray Dim_value; // Dimension values as float for extracting 2D field + + virtual void set_default_levels(const ConcatString &lstr) = 0; + + void clear_dimension(); + void parse_level(const ConcatString &level_str); + void parse_single_level(const char *lstr, bool as_offset, const char *caller); + void parse_time_range(const char *ptr_lower, char *ptr_upper, bool as_offset, const char *caller); + void parse_vertical_range(const char *ptr_lower, char *ptr_upper, bool as_offset, const char *caller); + void set_magic_pre(const ConcatString &nstr, const ConcatString &lstr); + void set_magic_post(const ConcatString &req_name, const ConcatString &level_name); + + public: + + virtual ~VarInfoNC() = default; + + const LongArray & dimension() const; + long dimension(int i) const; + const NumArray & dim_value() const; + double dim_value(int i) const; + const BoolArray & is_offset() const; + bool is_offset(int i) const; + int n_dimension() const; + + void add_dimension(long dim, bool as_offset=true, double dim_value=bad_data_double); + void set_dimension(int i_dim, long dim) const; + +}; + +/////////////////////////////////////////////////////////////////////////////// + + +inline const LongArray & VarInfoNC::dimension() const { return Dimension; } +inline long VarInfoNC::dimension(int i) const { return (int)Dimension[i]; } +inline int VarInfoNC::n_dimension() const { return Dimension.n_elements(); } +inline const NumArray & VarInfoNC::dim_value() const { return Dim_value; } +inline double VarInfoNC::dim_value(int i) const { return Dim_value[i]; } +inline const BoolArray & VarInfoNC::is_offset() const { return Is_offset; } +inline bool VarInfoNC::is_offset(int i) const { return Is_offset[i]; } + + +/////////////////////////////////////////////////////////////////////////////// + +#endif // __VAR_INFO_NC_H__ + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc index c91443ab57..022cd6d3c8 100644 --- a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc +++ b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc @@ -270,6 +270,37 @@ Grid MetNcCFDataFile::build_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_va //////////////////////////////////////////////////////////////////////// +bool MetNcCFDataFile::get_real_dimension(VarInfo &vinfo, NcVarInfo *data_var, + LongArray &dimension) { + static const string method_name + = "MetNcCFDataFile::get_real_dimension() ->"; + + int time_dim_slot = data_var->t_slot; + int zdim_slot = data_var->z_slot; + + // set vlevels if needed + _file->set_vlevels(data_var); + + for (int idx=0; idxdimension(); NcVarInfo *data_var = get_data_var(vinfo); if (nullptr != data_var) { - int time_dim_slot = data_var->t_slot; - int zdim_slot = data_var->z_slot; - - // set vlevels if needed - _file->set_vlevels(data_var); - - for (int idx=0; idx values); long convert_time_to_offset(double time_value) const; long convert_z_to_offset(double z_value, const NcVarInfo* data_var); + bool get_real_dimension(VarInfo &vinfo, NcVarInfo *data_var, + LongArray &dimension); + bool read_data_plane(VarInfo &, DataPlane &, const LongArray &dimension, bool do_winds); void error_message(const bool is_dim_time, const int error_code, const double _lower, const double _upper, diff --git a/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc b/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc index 42bea02689..bccc274f11 100644 --- a/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc +++ b/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc @@ -945,7 +945,7 @@ bool NcCfFile::getData(NcVar * v, const LongArray & a, DataPlane & plane) const { mlog << Error << "\n" << method_name << "variable " << GET_NC_NAME_P(v) << " not found!\n\n"; - exit(1); + return false; } // check star positions and count diff --git a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc index 76ebc3adf9..ee17b95257 100644 --- a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc +++ b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc @@ -34,8 +34,6 @@ using namespace std; /////////////////////////////////////////////////////////////////////////////// -static void check_dim_offset(const char *); - /////////////////////////////////////////////////////////////////////////////// // // Code for class VarInfoNcCF @@ -123,14 +121,6 @@ void VarInfoNcCF::clear() { /////////////////////////////////////////////////////////////////////////////// -void VarInfoNcCF::clear_dimension() { - Dimension.clear(); - Is_offset.clear(); - Dim_value.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// - void VarInfoNcCF::dump(ostream &out) const { // Dump out the contents @@ -147,215 +137,24 @@ void VarInfoNcCF::dump(ostream &out) const { /////////////////////////////////////////////////////////////////////////////// -void VarInfoNcCF::add_dimension(long dim, bool as_offset, double dim_value) { - Dimension.add(dim); - Is_offset.add(as_offset); - Dim_value.add(dim_value); - return; +void VarInfoNcCF::set_default_levels(const ConcatString &lstr) { + Level.set_req_name("*,*"); + Level.set_name("*,*"); + add_dimension(vx_data2d_star); + add_dimension(vx_data2d_star); } /////////////////////////////////////////////////////////////////////////////// - void VarInfoNcCF::set_magic(const ConcatString &nstr, const ConcatString &lstr) { - ConcatString tmp_str; const char *method_name = "VarInfoNcCF::set_magic() -> "; // Store the magic string - VarInfo::set_magic(nstr, lstr); - - // Set the requested name and default output name - set_req_name(nstr.c_str()); - set_name(nstr); + set_magic_pre(nstr, lstr); // If there's no level specification, assume (*, *) - if(strchr(lstr.c_str(), '(') == nullptr) { - Level.set_req_name("*,*"); - Level.set_name("*,*"); - clear_dimension(); - add_dimension(vx_data2d_star); - add_dimension(vx_data2d_star); - } - else { - char *ptr = nullptr; - char *ptr2 = nullptr; - char *ptr3 = nullptr; - char *save_ptr = nullptr; - - // Initialize the temp string - tmp_str = lstr; - - // Parse the level specification - // Retreive the NetCDF level specification - ptr = strtok_r((char*)tmp_str.c_str(), "()", &save_ptr); - - // Set the level name - Level.set_req_name(ptr); - Level.set_name(ptr); - - // If dimensions are specified, clear the default value - if (strchr(ptr, ',') != nullptr) clear_dimension(); - - // Parse the dimensions - bool as_offset = true; - while ((ptr2 = strtok_r(ptr, ",", &save_ptr)) != nullptr) { - // Check for wildcards - if (strchr(ptr2, '*') != nullptr) { add_dimension(vx_data2d_star); - } - else { - as_offset = (*ptr2 != '@'); - if (!as_offset) ptr2++; - - // Check for a range of levels - if ((ptr3 = strchr(ptr2, '-')) != nullptr) { - - // Check if a range has already been supplied - if (Dimension.has(range_flag)) { - mlog << Error << "\n" << method_name - << "only one dimension can have a range for NetCDF variable \"" - << MagicStr << "\".\n\n"; - exit(1); - } - else { - // Store the dimension of the range and limits - *ptr3++ = 0; - add_dimension(range_flag, as_offset); - // Check for integer dimension offsets - if(as_offset) { - check_dim_offset(ptr2); - check_dim_offset(ptr3); - } - else if (*ptr3 == '@') ptr3++; // to support @vlevel1-@vlevel2 - Level.set_lower(as_offset ? atoi(ptr2) : atof(ptr2)); - Level.set_upper(as_offset ? atoi(ptr3) : atof(ptr3)); - - // Assume pressure level type for a range of levels - Level.set_type(LevelType_Pres); - Level.set_is_offset(as_offset); - } - } - // Check for a range of times - else if ((ptr3 = strchr(ptr2, ':')) != nullptr) { - // Check if a range has already been supplied - if (Dimension.has(range_flag)) { - mlog << Error << "\n" << method_name - << "only one dimension can have a range for NetCDF variable \"" - << MagicStr << "\".\n\n"; - exit(1); - } - else { - int increment = 0; - // Store the dimension of the range and limits - *ptr3++ = 0; - if(!as_offset && *ptr3 == '@') ptr3++; // to support @time1:@time2 - - char *ptr_inc = strchr(ptr3, ':'); - if (ptr_inc != nullptr) *ptr_inc++ = 0; - mlog << Debug(7) << method_name - << " start: " << ptr2 << ", end: " << ptr3 << "\n"; - - bool datestring_start = is_datestring(ptr2); - bool datestring_end = is_datestring(ptr3); - if (datestring_start != datestring_end) { - mlog << Error << "\n" << method_name - << "the time value and an index/offset can not be mixed for NetCDF variable \"" - << MagicStr << "\".\n\n"; - exit(1); - } - - // Parse the lower and upper time limits - unixtime time_lower = 0; - unixtime time_upper = 0; - - if (datestring_start && datestring_end) { - as_offset = false; - time_lower = timestring_to_unix(ptr2); - time_upper = timestring_to_unix(ptr3); - } - else if (as_offset) { - - // Check for integer dimension offsets - check_dim_offset(ptr2); - check_dim_offset(ptr3); - - time_lower = (unixtime) atoi(ptr2); - time_upper = (unixtime) atoi(ptr3); - } - else { - time_lower = (unixtime) nint(atof(ptr2)); - time_upper = (unixtime) nint(atof(ptr3)); - } - - if (ptr_inc != nullptr) { - if (as_offset) { - increment = atoi(ptr_inc); - } - else { - increment = is_float(ptr_inc) - ? nint(atof(ptr_inc)) - : timestring_to_sec(ptr_inc); - mlog << Debug(7) << method_name - << "increment: \"" << ptr_inc << "\" to " - << increment << " seconds.\n"; - } - } - - add_dimension(range_flag, as_offset); - Level.set_lower((double) time_lower); - Level.set_upper((double) time_upper); - Level.set_increment(increment); - - // Assume time level type for a range of levels - Level.set_type(LevelType_Time); - Level.set_is_offset(as_offset); - } - } - else { - // Single level - long level = 0; - double level_value = bad_data_double; - if (is_datestring(ptr2)) { - unixtime unix_time = timestring_to_unix(ptr2); - level = vx_data2d_dim_by_value; - level_value = (double) unix_time; - as_offset = false; - } - else if (is_number(ptr2)) { - if (as_offset) { - check_dim_offset(ptr2); - level = atoi(ptr2); - } - else { - level = vx_data2d_dim_by_value; - level_value = atof(ptr2); - } - } - else { - mlog << Error << "\n" << method_name - << "trouble parsing NetCDF dimension value \"" - << ptr2 << "\"!\n\n"; - exit(1); - } - if (as_offset) add_dimension(level, as_offset); - else add_dimension(level, as_offset, level_value); - } - } - - // Set ptr to nullptr for next call to strtok - ptr = nullptr; - - } // end while - - } // end else - - // Check for "/PROB" to indicate a probability forecast - if (strstr(MagicStr.c_str(), "/PROB") != nullptr) PFlag = 1; - - // Set the long name - tmp_str.format("%s(%s)", req_name().text(), Level.req_name().text()); - set_long_name(tmp_str.c_str()); - - // Set the units - set_units(na_str); + parse_level(lstr); + + set_magic_post(req_name(), Level.req_name()); return; } @@ -469,17 +268,5 @@ bool VarInfoNcCF::is_wind_direction() const { // /////////////////////////////////////////////////////////////////////////////// -static void check_dim_offset(const char *ptr) { - - if(!is_eq(atof(ptr), (double) atoi(ptr))) { - mlog << Warning << "\ncheck_dim_offset() -> " - << "Found non-integer NetCDF dimension index (" - << ptr << " != " << atoi(ptr) << ").\n" - << "Did you intend to use \"@" << ptr - << "\" to specify the value for that dimension instead?\n\n"; - } - - return; -} /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h index 514ad11356..f93959259a 100644 --- a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h +++ b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h @@ -14,7 +14,7 @@ /////////////////////////////////////////////////////////////////////////////// -#include "var_info.h" +#include "var_info_nc.h" #include "data_file_type.h" #include "long_array.h" @@ -24,21 +24,15 @@ /////////////////////////////////////////////////////////////////////////////// -class VarInfoNcCF : public VarInfo +class VarInfoNcCF : public VarInfoNC { private: - // - // NetCDF-specific parameters - // - - LongArray Dimension; // Dimension values for extracting 2D field - BoolArray Is_offset; // boolean for Dimension value (true: offset, false: value to be an offset (false for value) - NumArray Dim_value; // Dimension values as float for extracting 2D field - void init_from_scratch(); void assign(const VarInfoNcCF &); - void clear_dimension(); + + protected: + void set_default_levels(const ConcatString &lstr) override; public: VarInfoNcCF(); @@ -55,13 +49,6 @@ class VarInfoNcCF : public VarInfo // GrdFileType file_type() const override; - const LongArray & dimension() const; - long dimension(int i) const; - const NumArray & dim_value() const; - double dim_value(int i) const; - const BoolArray & is_offset() const; - bool is_offset(int i) const; - int n_dimension() const; // // set stuff @@ -70,8 +57,6 @@ class VarInfoNcCF : public VarInfo void set_magic(const ConcatString &, const ConcatString &) override; bool set_dict(Dictionary &s, bool do_exit=true) override; - void add_dimension(long dim, bool as_offset=true, double dim_value=bad_data_double); - // // do stuff // @@ -87,13 +72,6 @@ class VarInfoNcCF : public VarInfo /////////////////////////////////////////////////////////////////////////////// inline GrdFileType VarInfoNcCF::file_type() const { return FileType_NcCF; } -inline const LongArray & VarInfoNcCF::dimension() const { return Dimension; } -inline long VarInfoNcCF::dimension(int i) const { return (int)Dimension[i]; } -inline int VarInfoNcCF::n_dimension() const { return Dimension.n_elements(); } -inline const NumArray & VarInfoNcCF::dim_value() const { return Dim_value; } -inline double VarInfoNcCF::dim_value(int i) const { return Dim_value[i]; } -inline const BoolArray & VarInfoNcCF::is_offset() const { return Is_offset; } -inline bool VarInfoNcCF::is_offset(int i) const { return Is_offset[i]; } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_met/met_file.cc b/src/libcode/vx_data2d_nc_met/met_file.cc index 9bbe9f839a..5758954a9d 100644 --- a/src/libcode/vx_data2d_nc_met/met_file.cc +++ b/src/libcode/vx_data2d_nc_met/met_file.cc @@ -505,9 +505,7 @@ if ( dimCount >= max_met_args ) { } -int j, count; -bool found = false; -NcVarInfo * var = (NcVarInfo *) nullptr; +auto var = (NcVarInfo *) nullptr; const int Nx = grid.nx(); const int Ny = grid.ny(); LongArray b = a; @@ -518,54 +516,37 @@ const int cell_count = Nx * Ny; // find varinfo's // -found = false; - -for (j=0; jx_slot) && (j != var->y_slot) ) ) { - mlog << Error << "\n" << method_name << "star found in bad slot\n\n"; - exit ( 1 ); - } - } - } if ( count != 2 ) { - mlog << Error << "\n" << method_name << " bad star count ... " << count << "\n\n"; - exit ( 1 ); - } // @@ -575,11 +556,8 @@ if ( count != 2 ) { int x_slot_tmp = 0; int y_slot_tmp = 0; if ( var == nullptr || (var->x_slot < 0) || (var->y_slot < 0) ) { - mlog << Error << "\n" << method_name << "bad x|y|z slot\n\n"; - exit ( 1 ); - } else { x_slot_tmp = var->x_slot; diff --git a/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc b/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc index 2fa0e82c0c..6c08df6118 100644 --- a/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc +++ b/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc @@ -111,7 +111,7 @@ void VarInfoNcMet::clear() { VarInfo::clear(); // Initialize - Dimension.clear(); + clear_dimension(); return; } @@ -132,113 +132,42 @@ void VarInfoNcMet::dump(ostream &out) const { /////////////////////////////////////////////////////////////////////////////// -void VarInfoNcMet::add_dimension(long dim) { - Dimension.add(dim); - return; +void VarInfoNcMet::set_default_levels(const ConcatString &lstr) { + // MET #3087 Set the level name string: + // - If empty or '*' from Point2Grid, set to *,* to indicate gridded output + // - If nonempty, use the input level string to support U/V vector level matching + if(lstr.empty() || lstr == "*") { + Level.set_req_name("*,*"); + Level.set_name("*,*"); + } + else { + Level.set_req_name(lstr.c_str()); + Level.set_name(lstr.c_str()); + } + Dimension.add(vx_data2d_star); + Dimension.add(vx_data2d_star); } /////////////////////////////////////////////////////////////////////////////// void VarInfoNcMet::set_magic(const ConcatString &nstr, const ConcatString &lstr) { - ConcatString tmp_str; - auto ptr = (char *) nullptr; - auto ptr2 = (char *) nullptr; - auto ptr3 = (char *) nullptr; - auto save_ptr = (char *) nullptr; // Store the magic string - VarInfo::set_magic(nstr, lstr); - - // Set the requested name and default output name - set_req_name(nstr.c_str()); - set_name(nstr); + set_magic_pre(nstr, lstr); // Parse the level dimensions, if specified - if(lstr.string().find_first_of("(") != std::string::npos) { - - // Initialize the temp string - tmp_str = lstr; - - // Retreive the NetCDF level specification - ptr = strtok_r((char*)tmp_str.c_str(), "()", &save_ptr); - - // Set the level name - Level.set_req_name(ptr); - Level.set_name(ptr); - - // If dimensions are specified, clear the default value - if(strchr(ptr, ',') != nullptr) Dimension.clear(); - - // Parse the dimensions - while((ptr2 = strtok_r(ptr, ",", &save_ptr)) != nullptr) { - - // Check for wildcards - if(strchr(ptr2, '*') != nullptr) Dimension.add(vx_data2d_star); - else { - - // @value notation not supported in vx_data_nc_met - if(*ptr2 == '@') { - mlog << Warning << "\nVarInfoNcMet::set_magic() -> " - << "problem parsing \"" << MagicStr << "\" for the vx_data2d_nc_met library.\n" - << "NetCDF dimensions must be specified as 0-based integer indices rather " - << "than using the NetCDF dimension \"@value\" notation.\n\n"; - } - - // Check for a range of levels - if((ptr3 = strchr(ptr2, '-')) != nullptr) { - - // Check if a range has already been supplied - if(Dimension.has(range_flag)) { - mlog << Error << "\nVarInfoNcMet::set_magic() -> " - << "only one dimension can have a range for NetCDF variable \"" - << MagicStr << "\".\n\n"; - exit(1); - } - // Store the dimension of the range and limits - else { - Dimension.add(range_flag); - Level.set_lower(atoi(ptr2)); - Level.set_upper(atoi(++ptr3)); - } - } - // Single level - else { - Dimension.add(atoi(ptr2)); - } - } - - // Set ptr to nullptr for next call to strtok - ptr = nullptr; - } // end while - } - // Otherwise, assume (*,*) level dimensions - else { - - // MET #3087 Set the level name string: - // - If empty or '*' from Point2Grid, set to *,* to indicate gridded output - // - If nonempty, use the input level string to support U/V vector level matching - if(lstr.empty() || lstr == "*") { - Level.set_req_name("*,*"); - Level.set_name("*,*"); - } - else { - Level.set_req_name(lstr.c_str()); - Level.set_name(lstr.c_str()); - } - Dimension.clear(); - Dimension.add(vx_data2d_star); - Dimension.add(vx_data2d_star); + if(lstr.string().find_first_of("(") != std::string::npos + && lstr.string().find_first_of("@") != std::string::npos) { + mlog << Warning << "\nVarInfoNcMet::set_magic() -> " + << "problem parsing \"" << MagicStr << "\" for the vx_data2d_nc_met library.\n" + << "NetCDF dimensions must be specified as 0-based integer indices rather " + << "than using the NetCDF dimension \"@value\" notation.\n\n"; + exit(1); } - // Check for "/PROB" to indicate a probability forecast - if(strstr(MagicStr.c_str(), "/PROB") != nullptr) PFlag = 1; - - // Set the long name - tmp_str.format("%s(%s)", req_name().text(), Level.req_name().text()); - set_long_name(tmp_str.c_str()); + parse_level(lstr); - // Set the units - set_units(na_str); + set_magic_post(req_name(), Level.req_name()); return; } diff --git a/src/libcode/vx_data2d_nc_met/var_info_nc_met.h b/src/libcode/vx_data2d_nc_met/var_info_nc_met.h index 42dba7d00b..bc013a4cab 100644 --- a/src/libcode/vx_data2d_nc_met/var_info_nc_met.h +++ b/src/libcode/vx_data2d_nc_met/var_info_nc_met.h @@ -14,7 +14,7 @@ /////////////////////////////////////////////////////////////////////////////// -#include "var_info.h" +#include "var_info_nc.h" #include "data_file_type.h" #include "long_array.h" @@ -23,7 +23,7 @@ /////////////////////////////////////////////////////////////////////////////// -class VarInfoNcMet : public VarInfo +class VarInfoNcMet : public VarInfoNC { private: @@ -31,11 +31,12 @@ class VarInfoNcMet : public VarInfo // NetCDF-specific parameters // - LongArray Dimension; // Dimension values for extracting 2D field - void init_from_scratch(); void assign(const VarInfoNcMet &); + protected: + void set_default_levels(const ConcatString &lstr) override; + public: VarInfoNcMet(); ~VarInfoNcMet() override; @@ -51,9 +52,6 @@ class VarInfoNcMet : public VarInfo // GrdFileType file_type() const override; - const LongArray & dimension() const; - long dimension(int i) const; - int n_dimension() const; // // set stuff @@ -62,8 +60,6 @@ class VarInfoNcMet : public VarInfo void set_magic(const ConcatString &, const ConcatString &) override; bool set_dict(Dictionary &s, bool do_exit=true) override; - void add_dimension(long dim); - // // do stuff // @@ -79,9 +75,6 @@ class VarInfoNcMet : public VarInfo /////////////////////////////////////////////////////////////////////////////// inline GrdFileType VarInfoNcMet::file_type() const { return FileType_NcMet; } -inline const LongArray & VarInfoNcMet::dimension() const { return Dimension; } -inline long VarInfoNcMet::dimension(int i) const { return Dimension[i]; } -inline int VarInfoNcMet::n_dimension() const { return Dimension.n_elements(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc index 20130822e3..64dd818ceb 100644 --- a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc +++ b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc @@ -84,7 +84,7 @@ bool MetNcWrfDataFile::open(const char * _filename) { close(); - WrfNc = new WrfFile; + WrfNc = new WrfFile; if(!WrfNc->open(_filename)) { mlog << Error << "\nMetNcWrfDataFile::open(const char *) -> " @@ -118,13 +118,67 @@ void MetNcWrfDataFile::dump(ostream & out, int depth) const { //////////////////////////////////////////////////////////////////////// +bool MetNcWrfDataFile::get_real_dimension(const VarInfoNcWrf *vinfo_nc, + const NcVarInfo *info, LongArray &dimension) const { + static const string method_name + = "MetNcWrfDataFile::get_real_dimension() ->"; + int dim_count = dimension.n_elements(); + for (int k=0; kis_offset(k)) { + dimension[k] = nint(vinfo_nc->level().lower()); + continue; + } + + if (dimension[k] != vx_data2d_dim_by_value && dimension[k] != range_flag) continue; + + string dim_name = GET_NC_NAME(get_nc_dim(info->var, k)); + NcVarInfo *var_info = find_var_info_by_dim_name(WrfNc->Var, dim_name, + WrfNc->Nvars); + if (var_info == nullptr) continue; + + if (dimension[k] == vx_data2d_dim_by_value) { + long new_offset = get_index_at_nc_data(var_info->var, + vinfo_nc->dim_value(k), + dim_name, (k == info->t_slot)); + if (new_offset != bad_data_int) dimension[k] = new_offset; + else { + mlog << Warning << "\n" << method_name + << "for \"" << vinfo_nc->req_name() + << "\" variable, the dimension value " + << vinfo_nc->dim_value(k) << " for " << dim_name + << " does not exist\n\n"; + return false; + } + } + else if (dimension[k] == range_flag) { + double lower = vinfo_nc->level().lower(); + double upper = vinfo_nc->level().upper(); + long new_offset = get_index_at_nc_data(var_info->var, lower, upper, + dim_name, (k == info->t_slot)); + if (new_offset != bad_data_int) dimension[k] = new_offset; + else { + mlog << Warning << "\n" << method_name + << "for \"" << vinfo_nc->req_name() + << "\" variable, the dimension value between " + << lower << " and " << upper << " for " << dim_name + << " does not exist\n\n"; + return false; + } + } + } + return true; + } + +//////////////////////////////////////////////////////////////////////// + + bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, bool do_winds) { bool status = false; double pressure = bad_data_double; - ConcatString level_str; - VarInfoNcWrf * vinfo_nc = (VarInfoNcWrf *) &vinfo; - NcVarInfo *info = (NcVarInfo *) nullptr; + auto vinfo_nc = (VarInfoNcWrf *) &vinfo; + auto info = (NcVarInfo *) nullptr; + static const string method_name = "MetNcWrfDataFile::data_plane() ->"; // Initialize the data plane plane.clear(); @@ -133,27 +187,21 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, vinfo_nc->set_grid_relative_flag(true); // Read the data if found - WrfNc->get_nc_var_info(vinfo_nc->req_name().c_str(), info); - if(info) { + status = WrfNc->get_nc_var_info(vinfo_nc->req_name().c_str(), info); + + if (status) { LongArray dimension = vinfo_nc->dimension(); - int dim_count = dimension.n_elements(); - for (int k=0; kvar, k)); - NcVarInfo *var_info = find_var_info_by_dim_name(WrfNc->Var, dim_name, - WrfNc->Nvars); - if (var_info) { - long new_offset = get_index_at_nc_data(var_info->var, - vinfo_nc->dim_value(k), - dim_name, (k == info->t_slot)); - if (new_offset != bad_data_int) dimension[k] = new_offset; - } - } - } + if (! get_real_dimension(vinfo_nc, info, dimension)) + return false; status = WrfNc->data(vinfo_nc->req_name().c_str(), dimension, plane, pressure, info); } + else if (! do_winds) { + mlog << Error << "\n" << method_name + << "\"" << vinfo.req_name() << "\" variable does not exist\n\n"; + return false; + } // Attempt to derive the data if(!status && do_winds) { @@ -166,7 +214,7 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, // Check that the valid time matches the request if(vinfo.valid() > 0 && vinfo.valid() != plane.valid()) { - mlog << Warning << "\nMetNcWrfDataFile::data_plane() -> " + mlog << Warning << "\n" << method_name << "for \"" << vinfo.req_name() << "\" variable, the valid " << "time does not match the requested valid time: (" << unix_to_yyyymmdd_hhmmss(plane.valid()) << " != " @@ -177,7 +225,7 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, // Check that the lead time matches the request if(vinfo.lead() > 0 && vinfo.lead() != plane.lead()) { - mlog << Warning << "\nMetNcWrfDataFile::data_plane() -> " + mlog << Warning << "\n" << method_name << "for \"" << vinfo.req_name() << "\" variable, the lead " << "time does not match the requested lead time: (" << sec_to_hhmmss(plane.lead()) << " != " @@ -201,6 +249,7 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, // Set the VarInfo object's level string for pressure levels if(!is_bad_data(pressure)) { + ConcatString level_str; level_str << cs_erase << "P" << nint(pressure); vinfo.set_level_name(level_str.c_str()); } @@ -215,9 +264,9 @@ int MetNcWrfDataFile::data_plane_array(VarInfo &vinfo, DataPlaneArray &plane_array, bool do_winds) { double pressure; - VarInfoNcWrf *vinfo_nc = (VarInfoNcWrf *) &vinfo; + auto vinfo_nc = (VarInfoNcWrf *) &vinfo; LongArray dim = vinfo_nc->dimension(); - NcVarInfo *info = (NcVarInfo *) nullptr; + auto info = (NcVarInfo *) nullptr; LongArray cur_dim; DataPlane cur_plane; diff --git a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h index 4512d49a61..545bb9fb76 100644 --- a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h +++ b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h @@ -34,6 +34,8 @@ class MetNcWrfDataFile : public Met2dDataFile { private: void nc_wrf_init_from_scratch(); + bool get_real_dimension(const VarInfoNcWrf *vinfo_nc, const NcVarInfo *info, + LongArray &dimension) const; MetNcWrfDataFile(const MetNcWrfDataFile &); MetNcWrfDataFile & operator=(const MetNcWrfDataFile &); diff --git a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc index 2dfe83dcd1..139aa9ee86 100644 --- a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc +++ b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc @@ -132,150 +132,27 @@ void VarInfoNcWrf::dump(ostream &out) const { /////////////////////////////////////////////////////////////////////////////// -void VarInfoNcWrf::add_dimension(long dim, bool as_offset, double dim_value) { - Dimension.add(dim); - Is_offset.add(as_offset); - Dim_value.add(dim_value); -} - -/////////////////////////////////////////////////////////////////////////////// - -void VarInfoNcWrf::clear_dimension() { - Dimension.clear(); - Is_offset.clear(); - Dim_value.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void VarInfoNcWrf::set_dimension(int i_dim, long dim) { - Dimension[i_dim] = dim; - return; +void VarInfoNcWrf::set_default_levels(const ConcatString &lstr) { + Level.set_req_name("0,*,*"); + Level.set_name("0,*,*"); + add_dimension(0); + add_dimension(vx_data2d_star); + add_dimension(vx_data2d_star); } /////////////////////////////////////////////////////////////////////////////// void VarInfoNcWrf::set_magic(const ConcatString &nstr, const ConcatString &lstr) { - ConcatString tmp_str; - char *ptr = (char *) nullptr; - char *ptr2 = (char *) nullptr; - char *ptr3 = (char *) nullptr; - char *save_ptr = (char *) nullptr; const char *method_name = "VarInfoNcWrf::set_magic() -> "; // Store the magic string - VarInfo::set_magic(nstr, lstr); - - // Set the requested name and default output name - set_req_name(nstr.c_str()); - set_name(nstr); + set_magic_pre(nstr, lstr); // If there's no level specification, assume (0,*,*) - if(strchr(lstr.c_str(), '(') == nullptr) { - Level.set_req_name("0,*,*"); - Level.set_name("0,*,*"); - clear_dimension(); - add_dimension(0); - add_dimension(vx_data2d_star); - add_dimension(vx_data2d_star); - } // Parse the level specification - else { - - // Initialize the temp string - tmp_str = lstr; - - // Retreive the NetCDF level specification - ptr = strtok_r((char*)tmp_str.c_str(), "()", &save_ptr); - - // Set the level name - Level.set_req_name(ptr); - Level.set_name(ptr); - - // If dimensions are specified, clear the default value - if(strchr(ptr, ',') != nullptr) clear_dimension(); - - // Parse the dimensions - bool as_offset = true; - while((ptr2 = strtok_r(ptr, ",", &save_ptr)) != nullptr) { - - // Check for wildcards - if(strchr(ptr2, '*') != nullptr) add_dimension(vx_data2d_star); - else { - - as_offset = (*ptr2 != '@'); - if (!as_offset) ptr2++; - - // Check for a range of levels - if((ptr3 = strchr(ptr2, '-')) != nullptr) { - - // Check if a range has already been supplied - if(Dimension.has(range_flag)) { - mlog << Error << "\n" << method_name - << "only one dimension can have a range for NetCDF variable \"" - << MagicStr << "\".\n\n"; - exit(1); - } - // Store the dimension of the range and limits - else { - add_dimension(range_flag); - Level.set_lower(atoi(ptr2)); - Level.set_upper(atoi(++ptr3)); - - // Assume pressure level type for a range of levels - Level.set_type(LevelType_Pres); - } - } - // Single level - else { - long level = 0; - double level_value = bad_data_double; - if (is_datestring(ptr2)) { - unixtime unix_time = timestring_to_unix(ptr2); - level = vx_data2d_dim_by_value; - level_value = unix_time; - as_offset = false; - } - else if (is_number(ptr2)) { - if (as_offset) level = atoi(ptr2); - else { - level = vx_data2d_dim_by_value; - level_value = atof(ptr2); - } - } - else if (is_datestring(ptr2)) { - unixtime unix_time = timestring_to_unix(ptr2); - level = vx_data2d_dim_by_value; - level_value = unix_time; - as_offset = false; - } - else { - mlog << Error << "\n" << method_name - << "trouble parsing NetCDF dimension value \"" - << ptr2 << "\"!\n\n"; - exit(1); - } - if (as_offset) add_dimension(level, as_offset); - else add_dimension(level, as_offset, level_value); - } - } - - // Set ptr to nullptr for next call to strtok - ptr = nullptr; - - } // end while - - } // end else - - // Check for "/PROB" to indicate a probability forecast - if(strstr(MagicStr.c_str(), "/PROB") != nullptr) PFlag = 1; - - // Set the long name - tmp_str.format("%s(%s)", name().text(), Level.name().text()); - set_long_name(tmp_str.c_str()); - - // Set the units - set_units(na_str); + parse_level(lstr); + + set_magic_post(req_name(), Level.req_name()); return; } diff --git a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h index 10b1f00178..0592affbe6 100644 --- a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h +++ b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h @@ -14,7 +14,7 @@ /////////////////////////////////////////////////////////////////////////////// -#include "var_info.h" +#include "var_info_nc.h" #include "data_file_type.h" #include "long_array.h" @@ -157,7 +157,7 @@ static const int n_pinterp_wind_speed_names = /////////////////////////////////////////////////////////////////////////////// -class VarInfoNcWrf : public VarInfo +class VarInfoNcWrf : public VarInfoNC { private: @@ -165,13 +165,11 @@ class VarInfoNcWrf : public VarInfo // NetCDF-specific parameters // - LongArray Dimension; // Dimension values for extracting 2D field - BoolArray Is_offset; // boolean for Dimension value (true: offset, false: value to be an offset (false for value) - NumArray Dim_value; // Dimension values as float for extracting 2D field - void init_from_scratch(); void assign(const VarInfoNcWrf &); - void clear_dimension(); + + protected: + void set_default_levels(const ConcatString &lstr) override; public: VarInfoNcWrf(); @@ -188,13 +186,6 @@ class VarInfoNcWrf : public VarInfo // GrdFileType file_type() const override; - const LongArray & dimension() const; - long dimension(int i) const; - const NumArray & dim_value() const; - double dim_value(int i) const; - const BoolArray & is_offset() const; - bool is_offset(int i) const; - int n_dimension() const; // // set stuff @@ -203,9 +194,6 @@ class VarInfoNcWrf : public VarInfo void set_magic(const ConcatString &, const ConcatString &) override; bool set_dict(Dictionary &, bool do_exit=true) override; - void add_dimension(long dim, bool as_index=true, double dim_value=bad_data_double); - void set_dimension(int i_dim, long dim); - // // do stuff // @@ -221,13 +209,6 @@ class VarInfoNcWrf : public VarInfo /////////////////////////////////////////////////////////////////////////////// inline GrdFileType VarInfoNcWrf::file_type() const { return FileType_NcWrf; } -inline const LongArray & VarInfoNcWrf::dimension() const { return Dimension; } -inline long VarInfoNcWrf::dimension(int i) const { return Dimension[i]; } -inline int VarInfoNcWrf::n_dimension() const { return Dimension.n_elements(); } -inline const NumArray & VarInfoNcWrf::dim_value() const { return Dim_value; } -inline double VarInfoNcWrf::dim_value(int i) const { return Dim_value[i]; } -inline const BoolArray & VarInfoNcWrf::is_offset() const { return Is_offset; } -inline bool VarInfoNcWrf::is_offset(int i) const { return Is_offset[i]; } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_wrf/wrf_file.cc b/src/libcode/vx_data2d_nc_wrf/wrf_file.cc index 9c2901333e..46e859af14 100644 --- a/src/libcode/vx_data2d_nc_wrf/wrf_file.cc +++ b/src/libcode/vx_data2d_nc_wrf/wrf_file.cc @@ -186,8 +186,12 @@ bool WrfFile::open(const char * filename) { -int j, k; -int month, day, year, hour, minute, second, str_len; +int month; +int day; +int year; +int hour; +int minute; +int second; char time_str[max_str_len]; string c; NcVar v; @@ -215,7 +219,7 @@ Dim = new NcDim*[Ndims]; get_dim_names(Nc, &DimNames); -for (j=0; jgetDimCount(); @@ -587,13 +594,9 @@ bool WrfFile::data(NcVar * v, const LongArray & a, DataPlane & plane, double & p const char *method_name = "WrfFile::data(NcVar *, const LongArray &, DataPlane &, double &) const -> "; if ( !args_ok(a) ) { - mlog << Warning << "\n" << method_name << "bad arguments:\n"; - a.dump(cerr); - return false; - } string var_name = GET_NC_NAME_P(v); @@ -671,7 +674,7 @@ if ( !found ) { LongArray c; if(time_in_pressure) c.add(a[var->t_slot]); - + c.add(a[var->z_slot]); pressure = data(P->var, c) * pressure_unit_conversion; @@ -682,7 +685,10 @@ if ( !found ) { } -void WrfFile::setup_dataplane(NcVar* v, const LongArray& a, DataPlane& plane, int dim_count, const NcVarInfo* var) const +//////////////////////////////////////////////////////////////////////// + +void WrfFile::setup_dataplane(NcVar* v, const LongArray& a, DataPlane& plane, + int dim_count, const NcVarInfo* var) const { double value; @@ -727,46 +733,38 @@ void WrfFile::setup_dataplane(NcVar* v, const LongArray& a, DataPlane& plane, in plane.set(value, x, y); } // for y - } // for x // de-stagger the DataPlane if necessary plane.destagger(var->x_stag, var->y_stag); } +//////////////////////////////////////////////////////////////////////// + bool WrfFile::check_star_position_and_count(const LongArray& a, const NcVarInfo* var) { const char *method_name = "WrfFile::check_star_position_and_count(const LongArray& a, const NcVarInfo* var) const -> "; int count = 0; for (int j =0; j<(a.n_elements()); ++j) { - if ( a[j] == vx_data2d_star ) { - ++count; - if ( (j != var->x_slot) && (j != var->y_slot) ) { - mlog << Warning << "\n" << method_name << " star found in bad slot\n\n"; - return false; - } - } - } if ( count != 2 ) { - mlog << Warning << "\n" << method_name << " bad star count ... " << count << "\n\n"; - return false; - } return true; } +//////////////////////////////////////////////////////////////////////// + void WrfFile::handle_pressure(const NcVarInfo* var, const string& z_name, NcVarInfo*& P, bool& time_in_pressure, double& pressure_unit_conversion) const { @@ -817,6 +815,8 @@ void WrfFile::handle_pressure(const NcVarInfo* var, const string& z_name, NcVarI } } +//////////////////////////////////////////////////////////////////////// + bool WrfFile::parse_dims_for_var(const string& var_name, NcVarInfo* var, string& z_name) { const char *method_name = "WrfFile::parse_dims_for_var(const string& var_name, NcVarInfo* var, string& z_name) const -> "; @@ -828,15 +828,11 @@ bool WrfFile::parse_dims_for_var(const string& var_name, NcVarInfo* var, string& // X dimension if ( c == x_dim_name || c == x_dim_stag_name || c == x_dim_subgrid_name ) { - if(!parse_dim_x(var_name, var, c, k)) return false; - } // Y dimension else if ( c == y_dim_name || c == y_dim_stag_name || c == y_dim_subgrid_name ) { - parse_dim_y(var, c, k); - } // Z dimension else if ( c == z_dim_p_interp_name || @@ -855,11 +851,8 @@ bool WrfFile::parse_dims_for_var(const string& var_name, NcVarInfo* var, string& } // T dimension else if ( c == t_dim_name ) { - var->t_slot = k; - } - } // end if k // @@ -867,18 +860,17 @@ bool WrfFile::parse_dims_for_var(const string& var_name, NcVarInfo* var, string& // if ( var->x_slot < 0 || var->y_slot < 0 ) { - mlog << Error << "\n" << method_name << "can't find needed dimensions(s) for variable \"" << var_name << "\" ... one of the dimensions may be staggered.\n\n"; - return false; - } return true; } +//////////////////////////////////////////////////////////////////////// + void WrfFile::parse_z_dim(NcVarInfo* var, string& z_name, const string& c, int k) { var->z_slot = k; @@ -895,21 +887,19 @@ void WrfFile::parse_z_dim(NcVarInfo* var, string& z_name, const string& c, int k // track fields that need to be de-staggered in the Z dimension if ( c == z_dim_wrf_stag_name ) { - var->z_stag = true; - } } +//////////////////////////////////////////////////////////////////////// + void WrfFile::parse_dim_y(NcVarInfo* var, const string& c, int k) { var->y_slot = k; // track fields that need to be de-staggered in the Y dimension if ( c == y_dim_stag_name ) { - var->y_stag = true; - } // track fields that are on a subgrid @@ -918,6 +908,8 @@ void WrfFile::parse_dim_y(NcVarInfo* var, const string& c, int k) } } +//////////////////////////////////////////////////////////////////////// + bool WrfFile::parse_dim_x(const string& var_name, NcVarInfo* var, const string& c, int k) { const char *method_name = "WrfFile::parse_dim_x(const string& var_name, NcVarInfo* var, const string& c, int k) -> "; @@ -954,7 +946,6 @@ bool WrfFile::parse_dim_x(const string& var_name, NcVarInfo* var, const string& //////////////////////////////////////////////////////////////////////// - bool WrfFile::data(const char * var_name, const LongArray & a, DataPlane & plane, double & pressure, NcVarInfo *&info) const { @@ -1009,13 +1000,11 @@ bool WrfFile::get_nc_var_info(const char *var_name, NcVarInfo *&info) const { if (nullptr == info) { for (int j=0; j " << "bad time string ... \"" << s << "\"\n\n"; - exit ( 1 ); - } // // done // -t = mdyhms_to_unix(month, day, year, hour, minute, second); +unixtime t = mdyhms_to_unix(month, day, year, hour, minute, second); return t; @@ -1069,11 +1056,10 @@ return t; bool is_bad_data_wrf(double v) - { -if (v < wrf_missing ) return false; -else return true; +if (v < wrf_missing ) return false; +else return true; } @@ -1082,16 +1068,12 @@ else return true; bool is_accumulation(const char * s) - { -int j; bool found = false; -for ( j=0; j " - << "bad status for var->get()\n\n"; + mlog << Error << "\n" << method_name << "bad status for var->get()\n\n"; exit(1); } @@ -679,7 +678,7 @@ bool UGridFile::getData(NcVar * v, const LongArray & a, DataPlane & plane) const if (nullptr == var) { mlog << Error << "\n" << method_name << "variable " << GET_NC_NAME_P(v) << " not found!\n\n"; - exit(1); + return true;; } // check star positions and count diff --git a/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc b/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc index f4d0178b86..3641d364e9 100644 --- a/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc +++ b/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc @@ -118,14 +118,6 @@ void VarInfoUGrid::clear() { /////////////////////////////////////////////////////////////////////////////// -void VarInfoUGrid::clear_dimension() { - Dimension.clear(); - Is_offset.clear(); - Dim_value.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// - void VarInfoUGrid::dump(ostream &out) const { // Dump out the contents @@ -142,136 +134,27 @@ void VarInfoUGrid::dump(ostream &out) const { /////////////////////////////////////////////////////////////////////////////// -void VarInfoUGrid::add_dimension(long dim, bool as_offset, double dim_value) { - Dimension.add(dim); - Is_offset.add(as_offset); - Dim_value.add(dim_value); - return; +void VarInfoUGrid::set_default_levels(const ConcatString &lstr) { + Level.set_req_name("0,*"); + Level.set_name("0,*"); + add_dimension(0); + add_dimension(vx_data2d_star); } /////////////////////////////////////////////////////////////////////////////// void VarInfoUGrid::set_magic(const ConcatString &nstr, const ConcatString &lstr) { - ConcatString tmp_str; const char *method_name = "VarInfoUGrid::set_magic() -> "; // Store the magic string - VarInfo::set_magic(nstr, lstr); - - // Set the requested name and default output name - set_req_name(nstr.c_str()); - set_name(nstr); - - // If there's no level specification, assume (*, *) - if(lstr.empty() || strchr(lstr.c_str(), '(') == nullptr) { - Level.set_req_name("0,*"); - Level.set_name("0,*"); - clear_dimension(); - add_dimension(0); - add_dimension(vx_data2d_star); - } - else { - char *ptr = nullptr; - char *ptr2 = nullptr; - char *ptr3 = nullptr; - char *save_ptr = nullptr; - - // Initialize the temp string - tmp_str = lstr; - - // Parse the level specification - // Retreive the NetCDF level specification - ptr = strtok_r((char*)tmp_str.c_str(), "()", &save_ptr); - - // Set the level name - Level.set_req_name(ptr); - Level.set_name(ptr); - - // If dimensions are specified, clear the default value - if (strchr(ptr, ',') != nullptr) clear_dimension(); - - // Parse the dimensions - bool as_offset = true; - while ((ptr2 = strtok_r(ptr, ",", &save_ptr)) != nullptr) { - // Check for wildcards - if (strchr(ptr2, '*') != nullptr) { add_dimension(vx_data2d_star); - } - else { - as_offset = (*ptr2 != '@'); - if (!as_offset) ptr2++; - - // Check for a range of levels - if ((ptr3 = strchr(ptr2, '-')) != nullptr) { - - // Check if a range has already been supplied - if (Dimension.has(range_flag)) { - mlog << Error << "\n" << method_name - << "only one dimension can have a range for NetCDF variable \"" - << MagicStr << "\".\n\n"; - exit(1); - } - else { - // Store the dimension of the range and limits - *ptr3++ = 0; - add_dimension(range_flag, as_offset); - Level.set_lower(as_offset ? atoi(ptr2) : atof(ptr2)); - Level.set_upper(as_offset ? atoi(ptr3) : atof(ptr3)); - - // Assume None type (offset instead of pressure level) for a range of levels - Level.set_type(LevelType_None); // like Ldd-dd - Level.set_is_offset(as_offset); - } - } - else { - // Single level - int level = 0; - double level_value = bad_data_double; - if (is_datestring(ptr2)) { - unixtime unix_time = timestring_to_unix(ptr2); - level = vx_data2d_dim_by_value; - level_value = unix_time; - as_offset = false; - } - else if (is_number(ptr2)) { - if (as_offset) level = atoi(ptr2); - else { - level = vx_data2d_dim_by_value; - level_value = atof(ptr2); - } - } - else if (is_datestring(ptr2)) { - unixtime unix_time = timestring_to_unix(ptr2); - level = vx_data2d_dim_by_value; - level_value = unix_time; - as_offset = false; - } - else { - mlog << Error << "\n" << method_name - << "trouble parsing NetCDF dimension value \"" - << ptr2 << "\"!\n\n"; - exit(1); - } - if (as_offset) add_dimension(level, as_offset); - else add_dimension(level, as_offset, level_value); - } - } - - // Set ptr to nullptr for next call to strtok - ptr = nullptr; - - } // end while - - } // end else - - // Check for "/PROB" to indicate a probability forecast - if (strstr(MagicStr.c_str(), "/PROB") != nullptr) PFlag = true; - - // Set the long name - tmp_str.format("%s(%s)", req_name().text(), Level.req_name().text()); - set_long_name(tmp_str.c_str()); - - // Set the units - set_units(na_str); + set_magic_pre(nstr, lstr); + + parse_level(lstr); + + // Assume None type (offset instead of pressure level) for a range of levels + if (Level.type() == LevelType_Pres) Level.set_type(LevelType_None); // like Ldd-dd + + set_magic_post(req_name(), Level.req_name()); return; } diff --git a/src/libcode/vx_data2d_ugrid/var_info_ugrid.h b/src/libcode/vx_data2d_ugrid/var_info_ugrid.h index 1725956fd5..c2ea276fed 100644 --- a/src/libcode/vx_data2d_ugrid/var_info_ugrid.h +++ b/src/libcode/vx_data2d_ugrid/var_info_ugrid.h @@ -14,7 +14,7 @@ /////////////////////////////////////////////////////////////////////////////// -#include "var_info.h" +#include "var_info_nc.h" #include "data_file_type.h" #include "long_array.h" @@ -24,7 +24,7 @@ /////////////////////////////////////////////////////////////////////////////// -class VarInfoUGrid : public VarInfo +class VarInfoUGrid : public VarInfoNC { private: @@ -32,13 +32,11 @@ class VarInfoUGrid : public VarInfo // NetCDF-specific parameters // - LongArray Dimension; // Dimension values for extracting 2D field - BoolArray Is_offset; // boolean for Dimension value (true: offset, false: value to be an offset (false for value) - NumArray Dim_value; // Dimension values as float for extracting 2D field - void init_from_scratch(); void assign(const VarInfoUGrid &); - void clear_dimension(); + + protected: + void set_default_levels(const ConcatString &lstr) override; public: VarInfoUGrid(); @@ -55,13 +53,6 @@ class VarInfoUGrid : public VarInfo // GrdFileType file_type() const override; - const LongArray & dimension() const; - long dimension(int i) const; - const NumArray & dim_value() const; - double dim_value(int i) const; - const BoolArray & is_offset() const; - bool is_offset(int i) const; - int n_dimension() const; // // set stuff @@ -70,8 +61,6 @@ class VarInfoUGrid : public VarInfo void set_magic(const ConcatString &, const ConcatString &) override; bool set_dict(Dictionary &s, bool do_exit=true) override; - void add_dimension(long dim, bool as_offset=true, double dim_value=bad_data_double); - // // do stuff // @@ -87,13 +76,6 @@ class VarInfoUGrid : public VarInfo /////////////////////////////////////////////////////////////////////////////// inline GrdFileType VarInfoUGrid::file_type() const { return FileType_UGrid; } -inline const LongArray & VarInfoUGrid::dimension() const { return Dimension; } -inline long VarInfoUGrid::dimension(int i) const { return Dimension[i]; } -inline int VarInfoUGrid::n_dimension() const { return Dimension.n_elements(); } -inline const NumArray & VarInfoUGrid::dim_value() const { return Dim_value; } -inline double VarInfoUGrid::dim_value(int i) const { return Dim_value[i]; } -inline const BoolArray & VarInfoUGrid::is_offset() const { return Is_offset; } -inline bool VarInfoUGrid::is_offset(int i) const { return Is_offset[i]; } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index 6afcdfdf8e..5a8b6e8ccb 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -1888,10 +1888,19 @@ bool get_nc_data(NcVar *var, ncbyte *data, const LongArray &dims, const LongArra return return_status; } +//////////////////////////////////////////////////////////////////////// + +ConcatString get_value_string(double value, bool is_time) { + ConcatString value_str; + if (is_time && (value > 10000000.)) value_str << unix_to_yyyymmdd_hhmmss(value); + else value_str << value; + return value_str; +} + //////////////////////////////////////////////////////////////////////// // returns matching offset or bad_data_int if not found -int get_index_at_nc_data(NcVar *var, double value, const string dim_name, bool is_time) { +int get_index_at_nc_data(NcVar *var, double value, const string &dim_name, bool is_time) { int offset = bad_data_int; static const char *method_name = "get_index_at_nc_data() -> "; if (IS_VALID_NC_P(var)) { @@ -1915,23 +1924,24 @@ int get_index_at_nc_data(NcVar *var, double value, const string dim_name, bool i << units_att_name << "\" attribute.\n\n"; } } + bool found = false; for (int idx=0; idx 10000000.)) value_str << unix_to_yyyymmdd_hhmmss(value); - else value_str << value; + ConcatString value_str = get_value_string(value, is_time); if (offset == bad_data_int) mlog << Debug(7) << method_name << "Not found value " << value_str << " at " << GET_NC_NAME_P(var) @@ -1948,6 +1958,78 @@ int get_index_at_nc_data(NcVar *var, double value, const string dim_name, bool i return offset; } +//////////////////////////////////////////////////////////////////////// +// returns matching offset or bad_data_int if not found + +int get_index_at_nc_data(NcVar *var, double value_min, double value_max, + const string &dim_name, bool is_time) { + int offset = bad_data_int; + static const char *method_name = "get_index_at_nc_data(min,max) -> "; + if (IS_VALID_NC_P(var)) { + int data_size = get_data_size(var); + vector values(data_size); + + if (get_nc_data(var, values.data())) { + unixtime ut; + int sec_per_unit; + bool no_leap_year = get_att_no_leap_year(var); + ut = sec_per_unit = 0; + if (is_time) { + ConcatString units; + bool has_attr = get_var_units(var, units); + if (has_attr && (!units.empty())) + parse_cf_time_string(units.c_str(), ut, sec_per_unit); + else { + mlog << Warning << "\n" << method_name + << "the time variable \"" << GET_NC_NAME_P(var) + << "\" must contain a \"" + << units_att_name << "\" attribute.\n\n"; + } + } + bool found = false; + // Select the first offset between value_min and value_max + for (int idx=0; idx= value_min && values[idx] <= value_max)) { + found = true; + } + if (!found && is_time) { + unixtime time_value = add_to_unixtime(ut, sec_per_unit, + values[idx], no_leap_year); + if (is_eq(time_value, value_min) + || is_eq(time_value, value_max) + || (time_value >= value_min && time_value <= value_max)) { + found = true; + } + } + if (found) { + offset = idx; + break; + } + } + } + + ConcatString value_max_str = get_value_string(value_max, is_time); + ConcatString value_min_str = get_value_string(value_min, is_time); + if (offset == bad_data_int) + mlog << Debug(7) << method_name << "Not found value between " << value_min_str + << " and " << value_max_str << " at " << GET_NC_NAME_P(var) + << " by dimension name \"" << dim_name << "\"\n"; + else { + ConcatString value_str = get_value_string(values[offset], is_time); + mlog << Debug(7) << method_name << "Found value " << value_str + << " (index=" << offset << ") at " << GET_NC_NAME_P(var) + << " by dimension name \"" << dim_name << "\"\n"; + } + } + else { + mlog << Debug(7) << method_name << "Not found a dimension variable for \"" + << dim_name << "\"\n"; + } + return offset; +} + //////////////////////////////////////////////////////////////////////// bool get_nc_data_to_array(NcVar *var, StringArray *array_buf) { diff --git a/src/libcode/vx_nc_util/nc_utils.h b/src/libcode/vx_nc_util/nc_utils.h index 294b851ec4..1817626e74 100644 --- a/src/libcode/vx_nc_util/nc_utils.h +++ b/src/libcode/vx_nc_util/nc_utils.h @@ -335,7 +335,8 @@ extern bool get_dim_names(const netCDF::NcFile *nc, StringArray *dimNames); extern netCDF::NcVar get_nc_var_lat(const netCDF::NcFile *nc); extern netCDF::NcVar get_nc_var_lon(const netCDF::NcFile *nc); extern netCDF::NcVar get_nc_var_time(const netCDF::NcFile *nc); -extern int get_index_at_nc_data(netCDF::NcVar *var, double value, const std::string dim_name, bool is_time=false); +extern int get_index_at_nc_data(netCDF::NcVar *var, double value, const std::string &dim_name, bool is_time=false); +extern int get_index_at_nc_data(netCDF::NcVar *var, double value_min, double value_max, const std::string &dim_name, bool is_time=false); extern netCDF::NcFile* open_ncfile(const char * nc_name, bool write = false); // Moved from nc_cf_file.cc