From fa5e6513bc6cc8e6a1235b8a48bd2a94e94549b3 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 20 Jun 2024 23:25:18 +0000 Subject: [PATCH 01/13] Per #2841, port over fixes from bugfix_2841_main_v11.1_tang_rad_winds for the develop branch --- src/basic/vx_util/main.cc | 2 +- src/libcode/vx_grid/earth_rotation.cc | 8 +- src/libcode/vx_grid/tcrmw_grid.cc | 98 +++--------- src/libcode/vx_grid/tcrmw_grid.h | 23 +-- src/libcode/vx_tc_util/vx_tc_nc_util.cc | 28 +++- src/libcode/vx_tc_util/vx_tc_nc_util.h | 2 + src/tools/tc_utils/tc_diag/tc_diag.cc | 2 +- src/tools/tc_utils/tc_rmw/tc_rmw.cc | 96 ++++++------ src/tools/tc_utils/tc_rmw/tc_rmw.h | 8 +- .../tc_utils/tc_rmw/tc_rmw_wind_converter.cc | 146 +++++++++--------- .../tc_utils/tc_rmw/tc_rmw_wind_converter.h | 27 ++-- 11 files changed, 197 insertions(+), 243 deletions(-) diff --git a/src/basic/vx_util/main.cc b/src/basic/vx_util/main.cc index f75b648e3f..f9b55fa203 100644 --- a/src/basic/vx_util/main.cc +++ b/src/basic/vx_util/main.cc @@ -1,5 +1,5 @@ // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* -// ** Copyright UCAR (c) 2022 - 2023 +// ** Copyright UCAR (c) 2022 - 2024 // ** University Corporation for Atmospheric Research (UCAR) // ** National Center for Atmospheric Research (NCAR) // ** Research Applications Lab (RAL) diff --git a/src/libcode/vx_grid/earth_rotation.cc b/src/libcode/vx_grid/earth_rotation.cc index 3da1347f23..a904fb9615 100644 --- a/src/libcode/vx_grid/earth_rotation.cc +++ b/src/libcode/vx_grid/earth_rotation.cc @@ -179,7 +179,13 @@ M23 = clat*clon; M33 = slat; */ -set_np(lat_center, lon_center, lon_center - 180.0); + // + // MET #2841 define the rotation by subtracting 90 degrees + // instead of 180 to define TCRMW grids as pointing east + // instead of north. + // + +set_np(lat_center, lon_center, lon_center - 90.0); // // diff --git a/src/libcode/vx_grid/tcrmw_grid.cc b/src/libcode/vx_grid/tcrmw_grid.cc index a2e1738e1b..3e1e188b18 100644 --- a/src/libcode/vx_grid/tcrmw_grid.cc +++ b/src/libcode/vx_grid/tcrmw_grid.cc @@ -10,6 +10,8 @@ //////////////////////////////////////////////////////////////////////// +using namespace std; + #include #include #include @@ -19,8 +21,6 @@ #include "trig.h" -using namespace std; - //////////////////////////////////////////////////////////////////////// @@ -136,7 +136,7 @@ Ir.set_xyz(1.0, 0.0, 0.0); Jr.set_xyz(0.0, 1.0, 0.0); Kr.set_xyz(0.0, 0.0, 1.0); -Range_n = 0; +Range_n = 0; Azimuth_n = 0; Range_max_km = 0.0; @@ -324,54 +324,23 @@ return; //////////////////////////////////////////////////////////////////////// -void TcrmwGrid::wind_ne_to_ra (const double lat, const double lon, - const double east_component, const double north_component, - double & radial_component, double & azimuthal_component) const +void TcrmwGrid::wind_ne_to_rt (const double azi_deg, + const double u_wind, const double v_wind, + double & radial_wind, double & tangential_wind) const { -Vector E, N, V; -Vector B_range, B_azi; -double azi_deg, range_deg, range_km; - - -latlon_to_range_azi(lat, lon, range_km, azi_deg); - -range_deg = deg_per_km*range_km; - -E = latlon_to_east (lat, lon); -N = latlon_to_north (lat, lon); - -V = east_component*E + north_component*N; - - -range_azi_to_basis(range_deg, azi_deg, B_range, B_azi); - - - - radial_component = dot(V, B_range); - -azimuthal_component = dot(V, B_azi); - - - - - -return; +double rcos = cosd(azi_deg); +double rsin = sind(azi_deg); +if (is_bad_data(u_wind) || is_bad_data(v_wind)) { + radial_wind = bad_data_double; + tangential_wind = bad_data_double; +} +else { + radial_wind = rcos*u_wind + rsin*v_wind; + tangential_wind = -1.0*rsin*u_wind + rcos*v_wind; } - - -//////////////////////////////////////////////////////////////////////// - - -void TcrmwGrid::wind_ne_to_ra_conventional (const double lat, const double lon, - const double east_component, const double north_component, - double & radial_component, double & azimuthal_component) const - -{ - -wind_ne_to_ra(lat, lon, east_component, north_component, radial_component, azimuthal_component); return; @@ -381,33 +350,17 @@ return; //////////////////////////////////////////////////////////////////////// -void TcrmwGrid::range_azi_to_basis(const double range_deg, const double azi_deg, Vector & B_range, Vector & B_azi) const +void TcrmwGrid::wind_ne_to_rt (const double lat, const double lon, + const double u_wind, const double v_wind, + double & radial_wind, double & tangential_wind) const { -double u, v, w; - - -u = cosd(range_deg)*sind(azi_deg); - -v = cosd(range_deg)*cosd(azi_deg); - -w = -sind(range_deg); - - - -B_range = u*Ir + v*Jr + w*Kr; - +double range_km, azi_deg; -u = cosd(azi_deg); - -v = -sind(azi_deg); - -w = 0.0; - - -B_azi = u*Ir + v*Jr + w*Kr; +latlon_to_range_azi(lat, lon, range_km, azi_deg); +wind_ne_to_rt(azi_deg, u_wind, v_wind, radial_wind, tangential_wind); return; @@ -425,8 +378,7 @@ RotatedLatLonGrid::latlon_to_xy(true_lat, true_lon, x, y); x -= Nx*floor(x/Nx); -x -= Nx*floor(x/Nx); - +y -= Ny*floor(y/Ny); return; @@ -442,7 +394,7 @@ void TcrmwGrid::xy_to_latlon(double x, double y, double & true_lat, double & tru x -= Nx*floor(x/Nx); -x -= Nx*floor(x/Nx); +y -= Ny*floor(y/Ny); RotatedLatLonGrid::xy_to_latlon(x, y, true_lat, true_lon); @@ -500,7 +452,3 @@ return; //////////////////////////////////////////////////////////////////////// - - - - diff --git a/src/libcode/vx_grid/tcrmw_grid.h b/src/libcode/vx_grid/tcrmw_grid.h index 8af34d9261..b7304b614e 100644 --- a/src/libcode/vx_grid/tcrmw_grid.h +++ b/src/libcode/vx_grid/tcrmw_grid.h @@ -35,11 +35,8 @@ class TcrmwGrid : public RotatedLatLonGrid { void calc_ijk(); // calculate rotated basis vectors - void range_azi_to_basis(const double range_deg, const double azi_deg, Vector & B_range, Vector & B_azi) const; - TcrmwData TData; - Vector Ir, Jr, Kr; int Range_n, Azimuth_n; // # of points in the radial and azimuthal directions @@ -89,23 +86,15 @@ class TcrmwGrid : public RotatedLatLonGrid { void xy_to_latlon(double x, double y, double & true_lat, double & true_lon) const; + void wind_ne_to_rt(const double azi_deg, + const double u_wind, const double v_wind, + double & radial_wind, double & tangential_wind) const; + void wind_ne_to_rt(const double lat, const double lon, + const double u_wind, const double v_wind, + double & radial_wind, double & tangential_wind) const; - void wind_ne_to_ra(const double lat, const double lon, - const double east_component, const double north_component, - double & radial_component, double & azimuthal_component) const; - - - // - // possibly toggles the signs of the radial and/or azimuthal components - // - // to align with the conventions used in the TC community - // - void wind_ne_to_ra_conventional (const double lat, const double lon, - const double east_component, const double north_component, - double & radial_component, double & azimuthal_component) const; - }; diff --git a/src/libcode/vx_tc_util/vx_tc_nc_util.cc b/src/libcode/vx_tc_util/vx_tc_nc_util.cc index d601d48caa..4a8d6fa12f 100644 --- a/src/libcode/vx_tc_util/vx_tc_nc_util.cc +++ b/src/libcode/vx_tc_util/vx_tc_nc_util.cc @@ -8,14 +8,13 @@ //////////////////////////////////////////////////////////////////////// +using namespace std; #include +using namespace netCDF; #include "vx_tc_nc_util.h" -using namespace std; -using namespace netCDF; - //////////////////////////////////////////////////////////////////////// void write_tc_track_lines(NcFile* nc_out, @@ -159,6 +158,21 @@ void write_tc_rmw(NcFile* nc_out, //////////////////////////////////////////////////////////////////////// +bool has_pressure_level(vector levels) { + + bool status = false; + + for (int j = 0; j < levels.size(); j++) { + if (levels[j].substr(0, 1) == "P") { + status = true; + break; + } + } + + return status; +} + +//////////////////////////////////////////////////////////////////////// set get_pressure_level_strings( map > variable_levels) { @@ -289,7 +303,7 @@ void def_tc_pressure(NcFile* nc_out, put_nc_data(&pressure_var, &pressure_data[0]); // Cleanup - if(pressure_data) { delete [] pressure_data; pressure_data = (double *) nullptr; } + if(pressure_data) { delete [] pressure_data; pressure_data = (double *) 0; } return; } @@ -342,8 +356,8 @@ void def_tc_range_azimuth(NcFile* nc_out, put_nc_data(&azimuth_var, &azimuth_data[0]); // Cleanup - if(range_data) { delete [] range_data; range_data = (double *) nullptr; } - if(azimuth_data) { delete [] azimuth_data; azimuth_data = (double *) nullptr; } + if(range_data) { delete [] range_data; range_data = (double *) 0; } + if(azimuth_data) { delete [] azimuth_data; azimuth_data = (double *) 0; } return; } @@ -539,7 +553,7 @@ void def_tc_variables(NcFile* nc_out, string long_name = variable_long_names[i->first]; string units = variable_units[i->first]; - if (levels.size() > 1) { + if (has_pressure_level(levels)) { data_var = nc_out->addVar( var_name, ncDouble, dims_3d); add_att(&data_var, "long_name", long_name); diff --git a/src/libcode/vx_tc_util/vx_tc_nc_util.h b/src/libcode/vx_tc_util/vx_tc_nc_util.h index bd9911e4d0..174319888c 100644 --- a/src/libcode/vx_tc_util/vx_tc_nc_util.h +++ b/src/libcode/vx_tc_util/vx_tc_nc_util.h @@ -35,6 +35,8 @@ extern void write_tc_track_point(netCDF::NcFile*, extern void write_tc_rmw(netCDF::NcFile*, const netCDF::NcDim&, const TrackInfo&); +extern bool has_pressure_level(std::vector); + extern std::set get_pressure_level_strings( std::map >); diff --git a/src/tools/tc_utils/tc_diag/tc_diag.cc b/src/tools/tc_utils/tc_diag/tc_diag.cc index e5815d30cb..0b551fd1f2 100644 --- a/src/tools/tc_utils/tc_diag/tc_diag.cc +++ b/src/tools/tc_utils/tc_diag/tc_diag.cc @@ -867,7 +867,7 @@ void compute_lat_lon(TcrmwGrid& grid, ia * grid.azimuth_delta_deg(), lat, lon); lat_arr[i] = lat; - lon_arr[i] = -lon; // degrees east to west + lon_arr[i] = -lon; // degrees west to east } } diff --git a/src/tools/tc_utils/tc_rmw/tc_rmw.cc b/src/tools/tc_utils/tc_rmw/tc_rmw.cc index d32bc62d81..2dfe8afd6e 100644 --- a/src/tools/tc_utils/tc_rmw/tc_rmw.cc +++ b/src/tools/tc_utils/tc_rmw/tc_rmw.cc @@ -23,7 +23,6 @@ // //////////////////////////////////////////////////////////////////////// - #include #include #include @@ -57,7 +56,6 @@ using namespace std; using namespace netCDF; - //////////////////////////////////////////////////////////////////////// static void usage(); @@ -69,22 +67,21 @@ static bool file_is_ok(const ConcatString &, const GrdFileType); static void process_rmw(); static void process_tracks(TrackInfoArray&); static void get_atcf_files(const StringArray&, - const StringArray&, StringArray&, StringArray&); + const StringArray&, StringArray&, StringArray&); static void process_track_files(const StringArray&, - const StringArray&, TrackInfoArray&); + const StringArray&, TrackInfoArray&); static bool is_keeper(const ATCFLineBase *); static void set_deck(const StringArray&); static void set_atcf_source(const StringArray&, - StringArray&, StringArray&); + StringArray&, StringArray&); static void set_data_files(const StringArray&); static void set_config(const StringArray&); static void set_out(const StringArray&); static void setup_grid(); static void setup_nc_file(); static void build_outfile_name(const ConcatString&, - const char*, ConcatString&); -static void compute_lat_lon(TcrmwGrid&, - double*, double*); + const char*, ConcatString&); +static void compute_lat_lon(TcrmwGrid&, double*, double*); static void process_fields(const TrackInfoArray&); //////////////////////////////////////////////////////////////////////// @@ -552,19 +549,19 @@ void set_out(const StringArray& a) { void setup_grid() { - grid_data.name = "TCRMW"; - grid_data.range_n = conf_info.n_range; - grid_data.azimuth_n = conf_info.n_azimuth; + tcrmw_data.name = "TCRMW"; + tcrmw_data.range_n = conf_info.n_range; + tcrmw_data.azimuth_n = conf_info.n_azimuth; // Define the maximum range in km based on the fixed increment if(is_bad_data(conf_info.rmw_scale)) { - grid_data.range_max_km = + tcrmw_data.range_max_km = conf_info.delta_range_km * (conf_info.n_range - 1); } - tcrmw_grid.set_from_data(grid_data); - grid.set(grid_data); + tcrmw_grid.set_from_data(tcrmw_data); + grid_out.set(tcrmw_data); } //////////////////////////////////////////////////////////////////////// @@ -651,8 +648,8 @@ void compute_lat_lon(TcrmwGrid& tcrmw_grid, ir * tcrmw_grid.range_delta_km(), ia * tcrmw_grid.azimuth_delta_deg(), lat, lon); - lat_arr[i] = lat; - lon_arr[i] = -lon; + lat_arr[i] = lat; + lon_arr[i] = -lon; // degrees west to east } } } @@ -693,12 +690,12 @@ void process_fields(const TrackInfoArray& tracks) { << point.lon() << ").\n"; // Set grid center - grid_data.lat_center = point.lat(); - grid_data.lon_center = -1.0*point.lon(); // internal sign change + tcrmw_data.lat_center = point.lat(); + tcrmw_data.lon_center = -1.0*point.lon(); // internal sign change // Define the maximum range in km relative to the radius of maximum winds if(!is_bad_data(conf_info.rmw_scale)) { - grid_data.range_max_km = + tcrmw_data.range_max_km = conf_info.rmw_scale * point.mrd() * tc_km_per_nautical_miles * (conf_info.n_range - 1); @@ -706,9 +703,9 @@ void process_fields(const TrackInfoArray& tracks) { // Re-define the range/azimuth grid tcrmw_grid.clear(); - tcrmw_grid.set_from_data(grid_data); - grid.clear(); - grid.set(grid_data); + tcrmw_grid.set_from_data(tcrmw_data); + grid_out.clear(); + grid_out.set(tcrmw_data); // Compute lat and lon coordinate arrays compute_lat_lon(tcrmw_grid, lat_arr, lon_arr); @@ -727,7 +724,7 @@ void process_fields(const TrackInfoArray& tracks) { for(int i_var = 0; i_var < conf_info.get_n_data(); i_var++) { - // Update with the valid time of the track point + // Update the variable info with the valid time of the track point data_info = conf_info.data_info[i_var]; string sname = data_info->name_attr().string(); @@ -736,38 +733,33 @@ void process_fields(const TrackInfoArray& tracks) { data_info->set_valid(valid_time); // Find data for this track point - get_series_entry(i_point, data_info, data_files, ftype, data_dp, latlon_arr); - - // Check data range - double data_min, data_max; - data_dp.data_range(data_min, data_max); - mlog << Debug(4) << "data_min:" << data_min << "\n"; - mlog << Debug(4) << "data_max:" << data_max << "\n"; - - // Regrid data - data_dp = met_regrid(data_dp, latlon_arr, grid, - data_info->regrid()); - data_dp.data_range(data_min, data_max); - mlog << Debug(4) << "data_min:" << data_min << "\n"; - mlog << Debug(4) << "data_max:" << data_max << "\n"; - - // If this is "U", setup everything for matching "V" - // and compute the radial/tangential winds - if(wind_converter.compute_winds_if_input_is_u( - i_point, sname, slevel, valid_time, data_files, ftype, - latlon_arr, lat_arr, lon_arr, grid, data_dp, tcrmw_grid)) { - write_tc_pressure_level_data(nc_out, tcrmw_grid, - pressure_level_indices, data_info->level_attr(), i_point, - data_3d_vars[conf_info.radial_velocity_field_name.string()], - wind_converter.get_wind_r_arr()); - write_tc_pressure_level_data(nc_out, tcrmw_grid, - pressure_level_indices, data_info->level_attr(), i_point, - data_3d_vars[conf_info.tangential_velocity_field_name.string()], - wind_converter.get_wind_t_arr()); + get_series_entry(i_point, data_info, data_files, ftype, data_dp, grid_in); + + // Regrid data and log the range of values before and after + double dmin, dmax, dmin_rgd, dmax_rgd; + data_dp.data_range(dmin, dmax); + data_dp = met_regrid(data_dp, grid_in, grid_out, data_info->regrid()); + data_dp.data_range(dmin_rgd, dmax_rgd); + + mlog << Debug(4) << data_info->magic_str() + << " input range (" << dmin << ", " << dmax + << "), regrid range (" << dmin_rgd << ", " << dmax_rgd << ")\n"; + + // if this is "U", setup everything for matching "V" and compute the radial/tangential + if(wind_converter.compute_winds_if_input_is_u(i_point, sname, slevel, valid_time, data_files, ftype, + grid_in, grid_out, data_dp, tcrmw_grid)) { + write_tc_pressure_level_data(nc_out, tcrmw_grid, + pressure_level_indices, data_info->level_attr(), i_point, + data_3d_vars[conf_info.radial_velocity_field_name.string()], + wind_converter.get_wind_r_arr()); + write_tc_pressure_level_data(nc_out, tcrmw_grid, + pressure_level_indices, data_info->level_attr(), i_point, + data_3d_vars[conf_info.tangential_velocity_field_name.string()], + wind_converter.get_wind_t_arr()); } // Write data - if(variable_levels[data_info->name_attr()].size() > 1) { + if(has_pressure_level(variable_levels[data_info->name_attr()])) { write_tc_pressure_level_data(nc_out, tcrmw_grid, pressure_level_indices, data_info->level_attr(), i_point, data_3d_vars[data_info->name_attr()], data_dp.data()); diff --git a/src/tools/tc_utils/tc_rmw/tc_rmw.h b/src/tools/tc_utils/tc_rmw/tc_rmw.h index 7691a2c012..3bd0bfae40 100644 --- a/src/tools/tc_utils/tc_rmw/tc_rmw.h +++ b/src/tools/tc_utils/tc_rmw/tc_rmw.h @@ -84,7 +84,6 @@ static TCRMWConfInfo conf_info; static GrdFileType ftype; static TCRMW_WindConverter wind_converter; - // Optional arguments static ConcatString out_dir; static ConcatString out_prefix; @@ -136,11 +135,10 @@ static std::map pressure_level_indices; // //////////////////////////////////////////////////////////////////////// -static DataPlane dp; -static Grid latlon_arr; -static TcrmwData grid_data; +static Grid grid_in; +static TcrmwData tcrmw_data; static TcrmwGrid tcrmw_grid; -static Grid grid; +static Grid grid_out; // Grid coordinate arrays static double* lat_arr; diff --git a/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.cc b/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.cc index b82f8e30da..58e4b9ea48 100644 --- a/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.cc +++ b/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.cc @@ -17,27 +17,25 @@ // ---- ---- ---- ----------- // 000 05/11/22 Albo Pulled the wind conversion into a class // 001 09/28/22 Prestopnik MET #2227 Remove namespace std from header files +// 002 05/03/24 Halley Gotway MET #2841 Fix radial and tangential winds // //////////////////////////////////////////////////////////////////////// - #include "tc_rmw_wind_converter.h" #include "series_data.h" #include "vx_regrid.h" using namespace std; - //////////////////////////////////////////////////////////////////////// -static void wind_ne_to_ra(const TcrmwGrid&, +static void wind_ne_to_rt(const TcrmwGrid&, const DataPlane&, const DataPlane&, - const double*, const double*, double*, double*); + double*, double*); //////////////////////////////////////////////////////////////////////// -void TCRMW_WindConverter::_free_winds_arrays(void) -{ +void TCRMW_WindConverter::_free_winds_arrays(void) { if (_windR != nullptr) { delete [] _windR; _windR = nullptr; @@ -96,6 +94,7 @@ void TCRMW_WindConverter::init(const TCRMWConfInfo *conf) { _vIndexMap[varlevel] = i_var; } } + // test for consistency if (_uIndexMap.size() != _vIndexMap.size()) { mlog << Warning << "Uneven number of u/v wind inputs, no wind conversion will be done:\n" @@ -103,12 +102,16 @@ void TCRMW_WindConverter::init(const TCRMWConfInfo *conf) { << _conf->v_wind_field_name.string() << " has " << _vIndexMap.size() << " inputs\n"; _computeWinds = false; } - map::const_iterator iu, iv; - for (iu=_uIndexMap.begin(), iv=_vIndexMap.begin(); iu!=_uIndexMap.end(); ++iu, ++iv) { - if (iu->first != iv->first) { - mlog << Warning << "Ordering of u/v wind input levels not the same, not implemented, no wind conversions will be done:\n" - << " " << iu->first << " " << iv->first << "\n"; - _computeWinds = false; + if (_computeWinds) { + map::const_iterator iu, iv; + for (iu=_uIndexMap.begin(), iv=_vIndexMap.begin(); iu!=_uIndexMap.end(); ++iu, ++iv) { + if (iu->first != iv->first) { + mlog << Warning << "Ordering of u/v wind input levels not the same, " + << "not implemented, no wind conversions will be done:\n" + << " " << iu->first << " " << iv->first << "\n"; + _computeWinds = false; + break; + } } } } @@ -132,9 +135,7 @@ void TCRMW_WindConverter::update_input(const string &variableName, const string void TCRMW_WindConverter::append_nc_output_vars(map > &variable_levels, map &variable_long_names, map &variable_units) { - if (!_computeWinds) { - return; - } + if (!_computeWinds) return; if (_foundUInInput && _foundVInInput) { variable_levels[_conf->tangential_velocity_field_name] = variable_levels[_conf->u_wind_field_name.string()]; @@ -146,14 +147,15 @@ void TCRMW_WindConverter::append_nc_output_vars(map > &va } else { if (!_foundUInInput) { - mlog << Warning << "\nTCWRMW_WindConverter::checkInputs() -> " + mlog << Warning << "\nTCWRMW_WindConverter::append_nc_output_vars() -> " << "field not found in input \"" << _conf->u_wind_field_name << "\"\n\n"; } if (!_foundVInInput) { - mlog << Warning << "\nTCWRMW_WindConverter::checkInputs() -> " + mlog << Warning << "\nTCWRMW_WindConverter::append_nc_output_vars() -> " << "field not found in input \"" << _conf->v_wind_field_name << "\"\n\n"; } - mlog << Warning << "\nNot computing radial and tangential winds\n\n"; + mlog << Warning << "\nTCWRMW_WindConverter::append_nc_output_vars() -> " + << "Not computing radial and tangential winds\n\n"; _computeWinds = false; } } @@ -166,11 +168,9 @@ bool TCRMW_WindConverter::compute_winds_if_input_is_u(int i_point, unixtime valid_time, const StringArray &data_files, const GrdFileType &ftype, - const Grid &latlon_arr, - const double *lat_arr, - const double *lon_arr, - const Grid &grid, - const DataPlane &data_dp, + const Grid &grid_in, + const Grid &grid_out, + const DataPlane &u_wind_dp, const TcrmwGrid &tcrmw_grid) { if (!_computeWinds) { return false; @@ -178,67 +178,75 @@ bool TCRMW_WindConverter::compute_winds_if_input_is_u(int i_point, int uIndex = -1; int vIndex = -1; - VarInfo *data_infoV = (VarInfo *) nullptr; + VarInfo *v_wind_info = (VarInfo *) nullptr; if (varName == _conf->u_wind_field_name.string()) { uIndex = _uIndexMap[varLevel]; vIndex = _vIndexMap[varLevel]; - data_infoV = _conf->data_info[vIndex]; - data_infoV->set_valid(valid_time); + v_wind_info = _conf->data_info[vIndex]; + v_wind_info->set_valid(valid_time); } else { // not the U input return false; } - DataPlane data_dpV; - Grid latlon_arrV; - get_series_entry(i_point, data_infoV, data_files, ftype, data_dpV, - latlon_arrV); - double data_min, data_max; - data_dpV.data_range(data_min, data_max); - mlog << Debug(4) << "V data_min:" << data_min << "\n"; - mlog << Debug(4) << "V data_max:" << data_max << "\n"; - data_dpV = met_regrid(data_dpV, latlon_arr, grid, data_infoV->regrid()); - data_dpV.data_range(data_min, data_max); - mlog << Debug(4) << "V data_min:" << data_min << "\n"; - mlog << Debug(4) << "V data_max:" << data_max << "\n"; - - // here's the conversion, at last - wind_ne_to_ra(tcrmw_grid, data_dp, data_dpV, lat_arr, lon_arr, - _windR, _windT); - // _windR and _windT now set + DataPlane v_wind_dp; + Grid v_wind_grid; + get_series_entry(i_point, v_wind_info, data_files, ftype, + v_wind_dp, v_wind_grid); + double dmin, dmax, dmin_rgd, dmax_rgd; + v_wind_dp.data_range(dmin, dmax); + v_wind_dp = met_regrid(v_wind_dp, v_wind_grid, grid_out, v_wind_info->regrid()); + v_wind_dp.data_range(dmin_rgd, dmax_rgd); + + mlog << Debug(4) << v_wind_info->magic_str() + << " input range (" << dmin << ", " << dmax + << "), regrid range (" << dmin_rgd << ", " << dmax_rgd << ")\n"; + + // Compute the radial and tangential winds and store in _windR and _windT + wind_ne_to_rt(tcrmw_grid, u_wind_dp, v_wind_dp, _windR, _windT); + return true; } - //////////////////////////////////////////////////////////////////////// -void wind_ne_to_ra(const TcrmwGrid& tcrmw_grid, +void wind_ne_to_rt(const TcrmwGrid& tcrmw_grid, const DataPlane& u_dp, const DataPlane& v_dp, - const double* lat_arr, const double* lon_arr, double* wind_r_arr, double* wind_t_arr) { - // Transform (u, v) to (radial, azimuthal) - for(int ir = 0; ir < tcrmw_grid.range_n(); ir++) { - for(int ia = 0; ia < tcrmw_grid.azimuth_n(); ia++) { - int i = ir * tcrmw_grid.azimuth_n() + ia; - double lat = lat_arr[i]; - double lon = - lon_arr[i]; - double u = u_dp.data()[i]; - double v = v_dp.data()[i]; - double wind_r; - double wind_t; - if(is_bad_data(u) || is_bad_data(v)) { - mlog << Debug(4) << "wind_ne_to_ra: latlon:" << lat << "," << lon << " winds are missing\n"; - wind_r = bad_data_double; - wind_t = bad_data_double; - } else { - tcrmw_grid.wind_ne_to_ra(lat, lon, u, v, wind_r, wind_t); - mlog << Debug(4) << "wind_ne_to_ra: latlon:" << lat << "," << lon << " uv:" << u << "," - << v << ", rt:" << wind_r << "," << wind_t <<"\n"; - } - wind_r_arr[i] = wind_r; - wind_t_arr[i] = wind_t; - } - } + + int n_rng = tcrmw_grid.range_n(); + int n_azi = tcrmw_grid.azimuth_n(); + + // Transform (u, v) to (radial, tangential) winds + for(int ir = 0; ir < n_rng; ir++) { + for(int ia = 0; ia < n_azi; ia++) { + + // Store data in reverse order + int i_rev = (n_rng - ir - 1) * n_azi + ia; + + double azi_deg = ia * tcrmw_grid.azimuth_delta_deg(); + double range_km = ir * tcrmw_grid.range_delta_km(); + + double lat, lon; + tcrmw_grid.range_azi_to_latlon(range_km, azi_deg, lat, lon); + + tcrmw_grid.wind_ne_to_rt(azi_deg, u_dp.data()[i_rev], v_dp.data()[i_rev], + wind_r_arr[i_rev], wind_t_arr[i_rev]); + + mlog << Debug(4) << "wind_ne_to_rt() -> " + << "center lat/lon (" << tcrmw_grid.lat_center_deg() + << ", " << tcrmw_grid.lon_center_deg() + << "), range (km): " << range_km + << ", azimuth (deg): " << azi_deg + << ", point lat/lon (" << lat << ", " << lon + << "), uv (" << u_dp.data()[i_rev] << ", " << v_dp.data()[i_rev] + << "), radial wind: " << wind_r_arr[i_rev] + << ", tangential wind: " << wind_t_arr[i_rev] << "\n"; + } // end for ia + } // end for ir + + return; } +//////////////////////////////////////////////////////////////////////// diff --git a/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.h b/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.h index 86dbc0d802..819dcb999a 100644 --- a/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.h +++ b/src/tools/tc_utils/tc_rmw/tc_rmw_wind_converter.h @@ -15,7 +15,7 @@ // // Mod# Date Name Description // ---- ---- ---- ----------- -// 000 05/11/22 DaveAlbo New +// 000 05/11/22 Albo New // //////////////////////////////////////////////////////////////////////// @@ -32,7 +32,6 @@ using std::map; using std::string; - //////////////////////////////////////////////////////////////////////// // // Constants @@ -98,8 +97,8 @@ class TCRMW_WindConverter { // if configured to compute winds, but didn't find U or V, turn off // the wind computations and report an error void append_nc_output_vars(std::map > &variable_levels, - std::map &variable_long_names, - std::map &variable_units); + std::map &variable_long_names, + std::map &variable_units); // Check input varName against U, and if it's a match, lookup V using the // map members, and then compute tangential and radial winds if it is so @@ -109,17 +108,15 @@ class TCRMW_WindConverter { // If true if returned, the winds can be accessed by calls to // get_wind_t_arr() and get_wind_r_arr() bool compute_winds_if_input_is_u(int i_point, - const string &varName, - const string &varLevel, - unixtime valid_time, - const StringArray &data_files, - const GrdFileType &ftype, - const Grid &latlon_arr, - const double *lat_arr, - const double *lon_arr, - const Grid &grid, - const DataPlane &data_dp, - const TcrmwGrid &tcrmw_grid); + const string &varName, + const string &varLevel, + unixtime valid_time, + const StringArray &data_files, + const GrdFileType &ftype, + const Grid &grid_in, + const Grid &grid_out, + const DataPlane &u_wind_dp, + const TcrmwGrid &tcrmw_grid); }; From 3873181c89cf6eec4bfaf8d47d40b0c5dc5c38ac Mon Sep 17 00:00:00 2001 From: MET Tools Test Account Date: Fri, 21 Jun 2024 00:04:05 +0000 Subject: [PATCH 02/13] Per #2841, clarify in the docs that azimuths are defined in degrees counter-clockwise from due east. --- docs/Users_Guide/tc-diag.rst | 2 +- docs/Users_Guide/tc-rmw.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Users_Guide/tc-diag.rst b/docs/Users_Guide/tc-diag.rst index 4f1d4630ee..89d267b2cc 100644 --- a/docs/Users_Guide/tc-diag.rst +++ b/docs/Users_Guide/tc-diag.rst @@ -15,7 +15,7 @@ Originally developed for the Statistical Hurricane Intensity Prediction Scheme ( TC-Diag is run once for each initialization time to produce diagnostics for each user-specified combination of TC tracks and model fields. The user provides track data (such as one or more ATCF a-deck track files), along with track filtering criteria as needed, to select one or more tracks to be processed. The user also provides gridded model data from which diagnostics should be computed. Gridded data can be provided for multiple concurrent storms, multiple models, and/or multiple domains (i.e. parent and nest) in a single run. -TC-Diag first determines the list of valid times that appear in any one of the tracks. For each valid time, it processes all track points for that time. For each track point, it reads the gridded model fields requested in the configuration file and transforms the gridded data to a range-azimuth cylindrical coordinates grid. For each domain, it writes the range-azimuth data to a temporary NetCDF file, as described in :numref:`Contributor's Guide Section %s `. +TC-Diag first determines the list of valid times that appear in any one of the tracks. For each valid time, it processes all track points for that time. For each track point, it reads the gridded model fields requested in the configuration file and transforms the gridded data to a range-azimuth cylindrical coordinates grid, as described in :numref:`tc-rmw`. For each domain, it writes the range-azimuth data to a temporary NetCDF file, as described in :numref:`Contributor's Guide Section %s `. Once the input data have been processed into the temporary NetCDF files, TC-Diag then calls one or more Python diagnostics scripts, as specified in the configuration file, to compute tropical cyclone diagnostic values. The computed diagnostics values are retrieved from the Python script and stored in memory. diff --git a/docs/Users_Guide/tc-rmw.rst b/docs/Users_Guide/tc-rmw.rst index 82628c087c..5676a6ae70 100644 --- a/docs/Users_Guide/tc-rmw.rst +++ b/docs/Users_Guide/tc-rmw.rst @@ -7,7 +7,7 @@ TC-RMW Tool Introduction ============ -The TC-RMW tool regrids tropical cyclone model data onto a moving range-azimuth grid centered on points along the storm track provided in ATCF format, most likely the adeck generated from the file. The radial grid spacing may be set as a factor of the radius of maximum winds (RMW). If wind fields are specified in the configuration file the radial and tangential wind components will be computed. Any regridding method available in MET can be used to interpolate data on the model output grid to the specified range-azimuth grid. The regridding will be done separately on each vertical level. The model data files must coincide with track points in a user provided ATCF formatted track file. +The TC-RMW tool regrids tropical cyclone model data onto a moving range-azimuth grid centered on points along the storm track provided in ATCF format, most likely the adeck generated from the file. The radial grid spacing can be defined in kilometers or as a factor of the radius of maximum winds (RMW). The azimuthal grid spacing is defined in degrees counter-clockwise from due east. If wind fields are specified in the configuration file, the radial and tangential wind components will be computed. Any regridding method available in MET can be used to interpolate data on the model output grid to the specified range-azimuth grid. The regridding will be done separately on each vertical level. The model data files must coincide with track points in a user provided ATCF formatted track file. Practical Information ===================== From 0321e43b5555f68d8a7bc448c0a5be5603199511 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 21 Jun 2024 15:50:39 +0000 Subject: [PATCH 03/13] Per #2841, just updating with output from enum_to_string. --- src/libcode/vx_gis/shapetype_to_string.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libcode/vx_gis/shapetype_to_string.cc b/src/libcode/vx_gis/shapetype_to_string.cc index 55e8f318c4..e292ffe878 100644 --- a/src/libcode/vx_gis/shapetype_to_string.cc +++ b/src/libcode/vx_gis/shapetype_to_string.cc @@ -28,7 +28,6 @@ #include "shapetype_to_string.h" - using namespace std; From 419c6ef7ced79a790f56250d4b2db223e62cce19 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 21 Jun 2024 16:03:45 +0000 Subject: [PATCH 04/13] Per #2841, tweak the documentation. --- docs/Users_Guide/tc-diag.rst | 2 +- docs/Users_Guide/tc-rmw.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Users_Guide/tc-diag.rst b/docs/Users_Guide/tc-diag.rst index 89d267b2cc..edcafa62dd 100644 --- a/docs/Users_Guide/tc-diag.rst +++ b/docs/Users_Guide/tc-diag.rst @@ -15,7 +15,7 @@ Originally developed for the Statistical Hurricane Intensity Prediction Scheme ( TC-Diag is run once for each initialization time to produce diagnostics for each user-specified combination of TC tracks and model fields. The user provides track data (such as one or more ATCF a-deck track files), along with track filtering criteria as needed, to select one or more tracks to be processed. The user also provides gridded model data from which diagnostics should be computed. Gridded data can be provided for multiple concurrent storms, multiple models, and/or multiple domains (i.e. parent and nest) in a single run. -TC-Diag first determines the list of valid times that appear in any one of the tracks. For each valid time, it processes all track points for that time. For each track point, it reads the gridded model fields requested in the configuration file and transforms the gridded data to a range-azimuth cylindrical coordinates grid, as described in :numref:`tc-rmw`. For each domain, it writes the range-azimuth data to a temporary NetCDF file, as described in :numref:`Contributor's Guide Section %s `. +TC-Diag first determines the list of valid times that appear in any one of the tracks. For each valid time, it processes all track points for that time. For each track point, it reads the gridded model fields requested in the configuration file and transforms the gridded data to a range-azimuth cylindrical coordinates grid, as described for the TC-RMW tool in :numref:`tc-rmw`. For each domain, it writes the range-azimuth data to a temporary NetCDF file, as described in :numref:`Contributor's Guide Section %s `. Once the input data have been processed into the temporary NetCDF files, TC-Diag then calls one or more Python diagnostics scripts, as specified in the configuration file, to compute tropical cyclone diagnostic values. The computed diagnostics values are retrieved from the Python script and stored in memory. diff --git a/docs/Users_Guide/tc-rmw.rst b/docs/Users_Guide/tc-rmw.rst index 5676a6ae70..2808728b80 100644 --- a/docs/Users_Guide/tc-rmw.rst +++ b/docs/Users_Guide/tc-rmw.rst @@ -7,7 +7,7 @@ TC-RMW Tool Introduction ============ -The TC-RMW tool regrids tropical cyclone model data onto a moving range-azimuth grid centered on points along the storm track provided in ATCF format, most likely the adeck generated from the file. The radial grid spacing can be defined in kilometers or as a factor of the radius of maximum winds (RMW). The azimuthal grid spacing is defined in degrees counter-clockwise from due east. If wind fields are specified in the configuration file, the radial and tangential wind components will be computed. Any regridding method available in MET can be used to interpolate data on the model output grid to the specified range-azimuth grid. The regridding will be done separately on each vertical level. The model data files must coincide with track points in a user provided ATCF formatted track file. +The TC-RMW tool regrids tropical cyclone model data onto a moving range-azimuth grid centered on points along the storm track provided in ATCF format, most likely the adeck generated from the file. The radial grid spacing can be defined in kilometers or as a factor of the radius of maximum winds (RMW). The azimuthal grid spacing is defined in degrees counterclockwise from due east. If wind vector fields are specified in the configuration file, the radial and tangential wind components will be computed. Any regridding method available in MET can be used to interpolate data on the model output grid to the specified range-azimuth grid. The regridding will be done separately on each vertical level. The model data files must coincide with track points in a user provided ATCF formatted track file. Practical Information ===================== From 42d6b4d37c2ab406608186db3d22f568d7835923 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 21 Jun 2024 16:08:28 +0000 Subject: [PATCH 05/13] Per #2841, correct the location of using namespace lines. --- src/libcode/vx_grid/tcrmw_grid.cc | 4 ++-- src/libcode/vx_tc_util/vx_tc_nc_util.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcode/vx_grid/tcrmw_grid.cc b/src/libcode/vx_grid/tcrmw_grid.cc index 3e1e188b18..fe01b273f5 100644 --- a/src/libcode/vx_grid/tcrmw_grid.cc +++ b/src/libcode/vx_grid/tcrmw_grid.cc @@ -10,8 +10,6 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include @@ -21,6 +19,8 @@ using namespace std; #include "trig.h" +using namespace std; + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_tc_util/vx_tc_nc_util.cc b/src/libcode/vx_tc_util/vx_tc_nc_util.cc index 4a8d6fa12f..ec075f5c90 100644 --- a/src/libcode/vx_tc_util/vx_tc_nc_util.cc +++ b/src/libcode/vx_tc_util/vx_tc_nc_util.cc @@ -8,13 +8,13 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include -using namespace netCDF; #include "vx_tc_nc_util.h" +using namespace std; +using namespace netCDF; + //////////////////////////////////////////////////////////////////////// void write_tc_track_lines(NcFile* nc_out, From c0402151b038c59efab99c060cc5c390edf002f6 Mon Sep 17 00:00:00 2001 From: MET Tools Test Account Date: Fri, 21 Jun 2024 19:51:13 +0000 Subject: [PATCH 06/13] Per #2841, update compute_tc_diag.py to no longer skip writing the radial and tangential wind diagnostics. --- scripts/python/tc_diag/compute_tc_diag.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/python/tc_diag/compute_tc_diag.py b/scripts/python/tc_diag/compute_tc_diag.py index 6a0523784e..bd30464a58 100644 --- a/scripts/python/tc_diag/compute_tc_diag.py +++ b/scripts/python/tc_diag/compute_tc_diag.py @@ -24,9 +24,7 @@ ] # Diagnostic names to be skipped -SKIP_DATA_NAMES = [ - "850RADIAL", "850DVRG", "200VORT" -] +SKIP_DATA_NAMES = [] # Bad data integer value BAD_DATA_INT = 9999 From f097345bedcfcca663e8fb4322eed5b5e00e19fd Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 21 Jun 2024 19:53:32 +0000 Subject: [PATCH 07/13] Per #2841, update compute_tc_diag.py to no longer skip writing radial and tangential wind diagnostics. --- scripts/python/tc_diag/compute_tc_diag.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/python/tc_diag/compute_tc_diag.py b/scripts/python/tc_diag/compute_tc_diag.py index 6a0523784e..bd30464a58 100644 --- a/scripts/python/tc_diag/compute_tc_diag.py +++ b/scripts/python/tc_diag/compute_tc_diag.py @@ -24,9 +24,7 @@ ] # Diagnostic names to be skipped -SKIP_DATA_NAMES = [ - "850RADIAL", "850DVRG", "200VORT" -] +SKIP_DATA_NAMES = [] # Bad data integer value BAD_DATA_INT = 9999 From 0e6b43d8d29995299ead1d8cdb0cc764ba0c7aab Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 21 Jun 2024 19:54:16 +0000 Subject: [PATCH 08/13] Revert "Per #2841, update compute_tc_diag.py to no longer skip writing radial and tangential wind diagnostics." This reverts commit f097345bedcfcca663e8fb4322eed5b5e00e19fd. --- scripts/python/tc_diag/compute_tc_diag.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/python/tc_diag/compute_tc_diag.py b/scripts/python/tc_diag/compute_tc_diag.py index bd30464a58..6a0523784e 100644 --- a/scripts/python/tc_diag/compute_tc_diag.py +++ b/scripts/python/tc_diag/compute_tc_diag.py @@ -24,7 +24,9 @@ ] # Diagnostic names to be skipped -SKIP_DATA_NAMES = [] +SKIP_DATA_NAMES = [ + "850RADIAL", "850DVRG", "200VORT" +] # Bad data integer value BAD_DATA_INT = 9999 From cafe439760f7771d2c830ef9491b94e031dfc636 Mon Sep 17 00:00:00 2001 From: MET Tools Test Account Date: Fri, 21 Jun 2024 20:07:58 +0000 Subject: [PATCH 09/13] Revert "Per #2841, update compute_tc_diag.py to no longer skip writing the radial and tangential wind diagnostics." This reverts commit c0402151b038c59efab99c060cc5c390edf002f6. --- scripts/python/tc_diag/compute_tc_diag.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/python/tc_diag/compute_tc_diag.py b/scripts/python/tc_diag/compute_tc_diag.py index bd30464a58..6a0523784e 100644 --- a/scripts/python/tc_diag/compute_tc_diag.py +++ b/scripts/python/tc_diag/compute_tc_diag.py @@ -24,7 +24,9 @@ ] # Diagnostic names to be skipped -SKIP_DATA_NAMES = [] +SKIP_DATA_NAMES = [ + "850RADIAL", "850DVRG", "200VORT" +] # Bad data integer value BAD_DATA_INT = 9999 From 4a2d032c17e836814bea3d76d16e78864c09c9ba Mon Sep 17 00:00:00 2001 From: MET Tools Test Account Date: Fri, 21 Jun 2024 20:30:08 +0000 Subject: [PATCH 10/13] Per #2841, update comp_dir.sh logic to include .dat in the files that are diffed --- internal/test_unit/R_test/comp_dir.R | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/test_unit/R_test/comp_dir.R b/internal/test_unit/R_test/comp_dir.R index a4453459c3..c94d8bae38 100644 --- a/internal/test_unit/R_test/comp_dir.R +++ b/internal/test_unit/R_test/comp_dir.R @@ -51,9 +51,9 @@ strDir1 = gsub("/$", "", listArgs[1]); strDir2 = gsub("/$", "", listArgs[2]); # build a list of files in each stat folder -listTest1 = system(paste("find", strDir1, "| egrep '\\.stat$|\\.txt$|\\.tcst|\\.nc$|\\.out$|\\.ps$|\\.png$' | sort"), intern=T); +listTest1 = system(paste("find", strDir1, "| egrep '\\.stat$|\\.txt$|\\.tcst|\\.nc$|\\.out$|\\.ps$|\\.png$|\\.dat$' | sort"), intern=T); listTest1Files = gsub(paste(strDir1, "/", sep=""), "", listTest1); -listTest2 = system(paste("find", strDir2, "| egrep '\\.stat$|\\.txt$|\\.tcst|\\.nc$|\\.out$|\\.ps$|\\.png$' | sort"), intern=T); +listTest2 = system(paste("find", strDir2, "| egrep '\\.stat$|\\.txt$|\\.tcst|\\.nc$|\\.out$|\\.ps$|\\.png$|\\.dat$' | sort"), intern=T); listTest2Files = gsub(paste(strDir2, "/", sep=""), "", listTest2); if( 1 <= verb ){ cat("dir1:", strDir1, "contains", length(listTest1Files), "files\n"); @@ -103,10 +103,11 @@ for(strFile in listTest1Files[ listTest1Files %in% listTest2Files ]){ compareNc(strFile1, strFile2, verb, strict, file_size_delta, compare_nc_var); } - # if the files are PostScript, PNG, or end in .out, compare accordingly + # if the files are PostScript, PNG, or end in .out or .dat, compare accordingly else if( TRUE == grepl("\\.out$", strFile1, perl=T) || TRUE == grepl("\\.ps$", strFile1, perl=T) || - TRUE == grepl("\\.png$", strFile1, perl=T) ){ + TRUE == grepl("\\.png$", strFile1, perl=T) || + TRUE == grepl("\\.dat$", strFile1, perl=T) ){ if( 1 <= verb ){ cat("file1: ", strFile1, "\nfile2: ", strFile2, "\n", sep=""); } compareDiff(strFile1, strFile2, verb); } From 728b9f15d298c7078a8b274e739e57c9bfbd5de5 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 21 Jun 2024 21:56:37 +0000 Subject: [PATCH 11/13] Replace tab with spaces --- src/libcode/vx_tc_util/vx_tc_nc_util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcode/vx_tc_util/vx_tc_nc_util.cc b/src/libcode/vx_tc_util/vx_tc_nc_util.cc index ec075f5c90..829fec05c3 100644 --- a/src/libcode/vx_tc_util/vx_tc_nc_util.cc +++ b/src/libcode/vx_tc_util/vx_tc_nc_util.cc @@ -165,7 +165,7 @@ bool has_pressure_level(vector levels) { for (int j = 0; j < levels.size(); j++) { if (levels[j].substr(0, 1) == "P") { status = true; - break; + break; } } From 531a584574ed33e24876ff237d42d0c880b0806d Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Sat, 22 Jun 2024 02:40:09 +0000 Subject: [PATCH 12/13] Per #2841, correct the units for the azimuth netcdf output variable --- src/libcode/vx_tc_util/vx_tc_nc_util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcode/vx_tc_util/vx_tc_nc_util.cc b/src/libcode/vx_tc_util/vx_tc_nc_util.cc index 829fec05c3..bc835eb4e2 100644 --- a/src/libcode/vx_tc_util/vx_tc_nc_util.cc +++ b/src/libcode/vx_tc_util/vx_tc_nc_util.cc @@ -338,7 +338,7 @@ void def_tc_range_azimuth(NcFile* nc_out, add_att(&range_var, "_FillValue", bad_data_double); add_att(&azimuth_var, "long_name", "azimuth"); - add_att(&azimuth_var, "units", "degrees_clockwise_from_north"); + add_att(&azimuth_var, "units", "degrees_clockwise_from_east"); add_att(&azimuth_var, "standard_name", "azimuth"); add_att(&azimuth_var, "_FillValue", bad_data_double); From 2afeccd43b23591b20bcc10cedd2b3dfde1f24b7 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Sat, 22 Jun 2024 11:21:48 +0000 Subject: [PATCH 13/13] Per #2841, reverse the x dimension of the rotated latlon grid to effectively switch from counterclockwise rotation to clockwise. --- docs/Users_Guide/tc-rmw.rst | 2 +- src/libcode/vx_grid/tcrmw_grid.cc | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/Users_Guide/tc-rmw.rst b/docs/Users_Guide/tc-rmw.rst index 2808728b80..5f226cc76a 100644 --- a/docs/Users_Guide/tc-rmw.rst +++ b/docs/Users_Guide/tc-rmw.rst @@ -7,7 +7,7 @@ TC-RMW Tool Introduction ============ -The TC-RMW tool regrids tropical cyclone model data onto a moving range-azimuth grid centered on points along the storm track provided in ATCF format, most likely the adeck generated from the file. The radial grid spacing can be defined in kilometers or as a factor of the radius of maximum winds (RMW). The azimuthal grid spacing is defined in degrees counterclockwise from due east. If wind vector fields are specified in the configuration file, the radial and tangential wind components will be computed. Any regridding method available in MET can be used to interpolate data on the model output grid to the specified range-azimuth grid. The regridding will be done separately on each vertical level. The model data files must coincide with track points in a user provided ATCF formatted track file. +The TC-RMW tool regrids tropical cyclone model data onto a moving range-azimuth grid centered on points along the storm track provided in ATCF format, most likely the adeck generated from the file. The radial grid spacing can be defined in kilometers or as a factor of the radius of maximum winds (RMW). The azimuthal grid spacing is defined in degrees clockwise from due east. If wind vector fields are specified in the configuration file, the radial and tangential wind components will be computed. Any regridding method available in MET can be used to interpolate data on the model output grid to the specified range-azimuth grid. The regridding will be done separately on each vertical level. The model data files must coincide with track points in a user provided ATCF formatted track file. Practical Information ===================== diff --git a/src/libcode/vx_grid/tcrmw_grid.cc b/src/libcode/vx_grid/tcrmw_grid.cc index fe01b273f5..e695e1c49d 100644 --- a/src/libcode/vx_grid/tcrmw_grid.cc +++ b/src/libcode/vx_grid/tcrmw_grid.cc @@ -288,6 +288,7 @@ y = (lat_rot - RData.rot_lat_ll)/(RData.delta_rot_lat); x = lon_rot/(RData.delta_rot_lon); +x = Nx - x; // MET #2841 switch from counterclockwise to clockwise RotatedLatLonGrid::xy_to_latlon(x, y, lat, lon); @@ -310,6 +311,8 @@ const double range_max_deg = deg_per_km*Range_max_km; RotatedLatLonGrid::latlon_to_xy(lat, lon, x, y); +x = Nx - x; // MET #2841 switch from counterclockwise to clockwise + azi_deg = x*(RData.delta_rot_lon); range_deg = range_max_deg - y*(RData.delta_rot_lat); @@ -378,6 +381,8 @@ RotatedLatLonGrid::latlon_to_xy(true_lat, true_lon, x, y); x -= Nx*floor(x/Nx); +x = Nx - x; // MET #2841 switch from counterclockwise to clockwise + y -= Ny*floor(y/Ny); return; @@ -394,6 +399,8 @@ void TcrmwGrid::xy_to_latlon(double x, double y, double & true_lat, double & tru x -= Nx*floor(x/Nx); +x = Nx - x; // MET #2841 switch from counterclockwise to clockwise + y -= Ny*floor(y/Ny); RotatedLatLonGrid::xy_to_latlon(x, y, true_lat, true_lon);