diff --git a/met/data/config/TCGenConfig_default b/met/data/config/TCGenConfig_default index db38b1302b..1a135491f5 100644 --- a/met/data/config/TCGenConfig_default +++ b/met/data/config/TCGenConfig_default @@ -150,18 +150,34 @@ dland_thresh = NA; // //////////////////////////////////////////////////////////////////////////////// +// +// Genesis matching logic. Compare the forecast genesis point to all points in +// the Best track (TRUE) or the single Best track genesis point (FALSE). +// +genesis_match_point_to_track = TRUE; + // // Radius in km to search for a matching genesis event // genesis_match_radius = 500; +// +// Time window in hours, relative to the model genesis time, to search for a +// matching Best track point +// +genesis_match_window = { + beg = 0; + end = 0; +} + // // Radius in km for a development scoring method hit // dev_hit_radius = 500; // -// Time window in hours for a development scoring method hit +// Time window in hours, relative to the model genesis time, for a development +// scoring method hit // dev_hit_window = { beg = -24; @@ -169,10 +185,13 @@ dev_hit_window = { } // -// Maximum Best track genesis minus model initialization time difference for an -// operational scoring method hit +// Time window in hours for the Best track genesis minus model initialization +// time difference for an operational scoring method hit // -ops_hit_tdiff = 48; +ops_hit_window = { + beg = 0; + end = 48; +} // // Discard genesis forecasts for initializations at or after the matching diff --git a/met/data/wrappers/read_tmp_dataplane.py b/met/data/wrappers/read_tmp_dataplane.py index e21c17ba3f..c6f8f57a9c 100644 --- a/met/data/wrappers/read_tmp_dataplane.py +++ b/met/data/wrappers/read_tmp_dataplane.py @@ -27,7 +27,6 @@ grid[grid_attr] = attr_val else: met_attrs[attr] = attr_val -grid['nx'], grid['ny'] = int(grid['nx']), int(grid['ny']) met_attrs['grid'] = grid met_attrs['name'] = met_attrs['name_str'] del met_attrs['name_str'] diff --git a/met/docs/Users_Guide/tc-gen.rst b/met/docs/Users_Guide/tc-gen.rst index 45bd9e7404..763c5663b4 100644 --- a/met/docs/Users_Guide/tc-gen.rst +++ b/met/docs/Users_Guide/tc-gen.rst @@ -6,7 +6,9 @@ TC-Gen Tool Introduction ____________ -The TC-Gen tool provides verification of tropical cyclone genesis forecasts in ATCF file format. Producing reliable tropical cyclone genesis forecasts is an important metric for global numerical weather prediction models. This tool ingests deterministic model output post-processed by a genesis tracking software (e.g. GFDL vortex tracker) and ATCF format reference dataset(s) (e.g. Best Track analysis and CARQ operational tracks) and outputs categorical counts and statistics. The capability to modify the spatial and temporal tolerances that define a “hit” forecast is included to give users the ability to condition the criteria based on model performance and/or conduct sensitivity analyses. Statistical aspects are outlined in Section 21.2 and practical aspects of the TC-Gen tool are described in Section 21.3. +The TC-Gen tool provides verification of tropical cyclone genesis forecasts in ATCF file format. Producing reliable tropical cyclone genesis forecasts is an important metric for global numerical weather prediction models. This tool ingests deterministic model output post-processed by a genesis tracking software (e.g. GFDL vortex tracker) and ATCF format reference dataset(s) (e.g. Best Track analysis and CARQ operational tracks) and outputs categorical counts and statistics. The capability to modify the spatial and temporal tolerances that define a “hit” forecast is included to give users the ability to condition the criteria based on model performance and/or conduct sensitivity analyses. Statistical aspects are outlined in :numref:`tc-gen_stat_aspects` and practical aspects of the TC-Gen tool are described in :numref:`tc-gen_practical_info`. + +.. _tc-gen_stat_aspects: Statistical aspects ___________________ @@ -15,7 +17,9 @@ The TC-Gen tool populates a contingency tables with hits, misses, and false alar Other considerations for interpreting the output of the TC-Gen tool involve the size of the contingency table output. The size of the contingency table will change depending on the number of matches. Additionally, the number of misses is based on the forecast duration and interval (specified in the configuration file). This change is due to the number of model opportunities to forecast the event, which is determined by the specified duration/interval. -Care should be taken when interpreting the statistics for filtered data. In some cases, variables (e.g. storm name) are only available in either the forecast or reference datasets, rather than both. When filtering on a field that is only present in one dataset, the contingency table counts will be impacted. Similarly, the initialization field only impacts the model forecast data. If the valid time (which will impact the reference dataset) isn't also specified, the forecasts will be filtered and matched such that the number of misses will erroneously increase. See section 21.3 for more detail. +Care should be taken when interpreting the statistics for filtered data. In some cases, variables (e.g. storm name) are only available in either the forecast or reference datasets, rather than both. When filtering on a field that is only present in one dataset, the contingency table counts will be impacted. Similarly, the initialization field only impacts the model forecast data. If the valid time (which will impact the reference dataset) isn't also specified, the forecasts will be filtered and matched such that the number of misses will erroneously increase. See :numref:`tc-gen_practical_info` for more detail. + +.. _tc-gen_practical_info: Practical information _____________________ @@ -61,15 +65,15 @@ The TC-Gen tool implements the following logic: * Parse the forecast genesis data and identify forecast genesis events separately for each model present. -* Parse the Best and operational track data, and identify Best track genesis events. +* Parse the Best and operational track data, and identify Best track genesis events. Note that Best tracks with a cyclone number greater than 50 are automatically discarded from the analysis. Large cyclone numbers are used for pre-season testing or to track invests prior to a storm actually forming. Running this tool at verbosity level 6 (-v 6) prints details about which tracks are discarded. * Loop over the filters defined in the configuration file and apply the following logic for each. * For each Best track genesis event meeting the filter critera, determine the initialization and lead times for which the model had an opportunity to forecast that genesis event. Store an unmatched genesis pair for each case. - * For each forecast genesis event, search for a matching Best track. A Best track matches if the valid time of one of its track points matches the forecast genesis time and is within a configurable radius of the forecast genesis location. If a Best track match is found, store the storm ID. + * For each forecast genesis event, search for a matching Best track. A configurable boolean option controls whether all Best track points are considered for a match or only the single Best track genesis point. A match occurs if the Best track point valid time is within a configurable window around the forecast genesis time and the Best track point location is within a configurable radius of the forecast genesis location. If a Best track match is found, store the storm ID. - * In no Best track match is found, apply the same logic to search the 0-hour operational track points. If an operational match is found, store the storm ID. + * In no Best track match is found, apply the same logic to search the operational track points with lead time of 0 hours. If an operational match is found, store the storm ID. * If a matching storm ID is found, match the forecast genesis event to the Best track genesis event for that storm ID. @@ -251,11 +255,30 @@ The **dland_thresh** entry is a threshold defining whether the genesis event sho ______________________ +.. code-block:: none + + genesis_match_point_to_track = TRUE; + +The **genesis_match_point_to_track** entry is a boolean which controls the matching logic. When set to its default value of TRUE, for each forecast genesis event, all Best track points are searched for a match. This logic implements the method used by the NOAA National Hurricane Center. When set to FALSE, only the single Best track genesis point is considered for a match. When selecting FALSE, users are encouraged to adjust the **genesis_match_radius** and/or **gensesis_match_window** options, described below, to enable matches to be found. + +______________________ + .. code-block:: none genesis_match_radius = 500; -The **genesis_match_radius** entry defines a search radius, in km, relative to the forecast genesis location. When searching for a match, only those Best genesis events which occur within this radius will be considered. Increasing this search radius should lead to an increase in the number of matched genesis pairs. +The **genesis_match_radius** entry defines a search radius, in km, relative to the forecast genesis location. When searching for a match, only Best or operational tracks with a track point within this radius will be considered. Increasing this search radius should lead to an increase in the number of matched genesis pairs. + +______________________ + +.. code-block:: none + + genesis_match_window = { + beg = 0; + end = 0; + } + +The **genesis_match_window** entry defines a time window, in hours, relative to the forecast genesis time. When searching for a match, only Best or operational tracks with a track point falling within this time window will be considered. The default time window of 0 requires a Best or operational track to exist at the forecast genesis time for a match to be found. Increasing this time window should lead to an increase in the number matched genesis pairs. For example, setting *end = 12;* would allow forecast genesis events to match Best tracks up to 12 hours prior to their existence. ______________________ @@ -274,15 +297,18 @@ ______________________ end = 24; } -The **dev_hit_window** entry defines a time window, in hours, relative to the forecast genesis time. The Best track genesis event must occur within this time window for the pair to be counted as contingency table HIT for the development scoring method. Tightening this window may cause development method HITS to become FALSE ALARMS. +The **dev_hit_window** entry defines a time window, in hours, relative to the forecast genesis time. The Best track genesis event must occur within this time window for the pair to be counted as a contingency table HIT for the development scoring method. Tightening this window may cause development method HITS to become FALSE ALARMS. ______________________ .. code-block:: none - ops_hit_tdiff = 48; + ops_hit_window = { + beg = 0; + end = 48; + } -The **ops_hit_tdiff** entry is an integer which defines a maximum allowable time difference in hours. For each matching forecast and Best track genesis event, if the difference between the Best track genesis time and the forecast initialization time is less than or equal to this value, then the pair is counted as a contingency table HIT for the operational scoring method. Otherwise, it is counted as a FALSE ALARM. +The **ops_hit_window** entry defines a time window, in hours, relative to the Best track genesis time. The model initialization time for the forecast genesis event must occur within this time window for the pairs to be counted as a contingency table HIT for the operationl scoring method. Otherwise, the pair is counted as a FALSE ALARM. ______________________ diff --git a/met/src/basic/vx_config/config_constants.h b/met/src/basic/vx_config/config_constants.h index bc82d55625..a6431fca54 100644 --- a/met/src/basic/vx_config/config_constants.h +++ b/met/src/basic/vx_config/config_constants.h @@ -1076,10 +1076,12 @@ static const char conf_key_category[] = "category"; static const char conf_key_vmax_thresh[] = "vmax_thresh"; static const char conf_key_mslp_thresh[] = "mslp_thresh"; static const char conf_key_basin_mask[] = "basin_mask"; +static const char conf_key_genesis_match_point_to_track[] = "genesis_match_point_to_track"; static const char conf_key_genesis_match_radius[] = "genesis_match_radius"; +static const char conf_key_genesis_match_window[] = "genesis_match_window"; static const char conf_key_dev_hit_radius[] = "dev_hit_radius"; static const char conf_key_dev_hit_window[] = "dev_hit_window"; -static const char conf_key_ops_hit_tdiff[] = "ops_hit_tdiff"; +static const char conf_key_ops_hit_window[] = "ops_hit_window"; static const char conf_key_discard_init_post_genesis_flag[] = "discard_init_post_genesis_flag"; static const char conf_key_dev_method_flag[] = "dev_method_flag"; static const char conf_key_ops_method_flag[] = "ops_method_flag"; diff --git a/met/src/libcode/vx_python3_utils/python3_dict.cc b/met/src/libcode/vx_python3_utils/python3_dict.cc index 6d38599aa4..99a0d5fc93 100644 --- a/met/src/libcode/vx_python3_utils/python3_dict.cc +++ b/met/src/libcode/vx_python3_utils/python3_dict.cc @@ -164,6 +164,14 @@ if ( ! a ) { } +// If not a Long, try interpreting as LongLong for numpy.int64 values + +if ( ! PyLong_Check(a) ) { + + a = PyLong_FromLongLong(PyLong_AsLongLong(a)); + +} + if ( ! PyLong_Check(a) ) { mlog << Error << "\nPython3_Dict::lookup_int(const char *) -> " diff --git a/met/src/libcode/vx_tc_util/atcf_line_base.cc b/met/src/libcode/vx_tc_util/atcf_line_base.cc index 5ae638f44f..aa970dc117 100644 --- a/met/src/libcode/vx_tc_util/atcf_line_base.cc +++ b/met/src/libcode/vx_tc_util/atcf_line_base.cc @@ -211,9 +211,12 @@ ConcatString ATCFLineBase::get_item(int i) const { int i_col = i; // For ATCFLineType_GenTrack: - // Columns 1 and 2 are consistent, use offsets 0 and 1 - // Columns 4-20 are the same as columns 3-19 of ATCFLineType_Track - // Shift those column indices by 1. + // Columns 1 and 2 are consistent: + // Use offsets 0 and 1 + // Column 3 for is an EXTRA column for this line type: + // Add special handling in storm_id() + // Columns 4-20 are the same as columns 3-19 of ATCFLineType_Track: + // Shift those column indices by 1. if(Type == ATCFLineType_GenTrack && i >= 2 && i <= 18) i_col++; cs = DataLine::get_item(i_col); @@ -252,7 +255,8 @@ ConcatString ATCFLineBase::basin() const { //////////////////////////////////////////////////////////////////////// ConcatString ATCFLineBase::cyclone_number() const { - return(get_item(CycloneNumberOffset)); } + return(get_item(CycloneNumberOffset)); +} //////////////////////////////////////////////////////////////////////// @@ -357,8 +361,10 @@ int ATCFLineBase::lead() const { ConcatString ATCFLineBase::storm_id() const { ConcatString cs; + // For ATCFLineType_GenTrack, use the contents of the extra 3rd column + // Call DataLine::get_item() to avoid the column shifting logic if(Type == ATCFLineType_GenTrack) { - cs = get_item(GenStormIdOffset); + cs = DataLine::get_item(GenStormIdOffset); } else { unixtime ut = valid(); diff --git a/met/src/libcode/vx_tc_util/genesis_info.cc b/met/src/libcode/vx_tc_util/genesis_info.cc index 8cc5ea36d9..91c708ff82 100644 --- a/met/src/libcode/vx_tc_util/genesis_info.cc +++ b/met/src/libcode/vx_tc_util/genesis_info.cc @@ -259,6 +259,7 @@ void GenesisInfo::set_dland(double d) { bool GenesisInfo::set(const TrackInfo &ti, const GenesisEventInfo &event_info) { + // Initialize clear(); @@ -298,14 +299,32 @@ int GenesisInfo::genesis_fhr() const { //////////////////////////////////////////////////////////////////////// -bool GenesisInfo::is_match(const TrackPoint &p, - const double rad) const { +const TrackPoint * GenesisInfo::genesis() const { + return(is_bad_data(GenesisIndex) ? 0 : &(Point[GenesisIndex])); +} + +//////////////////////////////////////////////////////////////////////// + +bool GenesisInfo::is_match(const TrackPoint &p, const double rad, + const int beg, const int end) const { // Check for matching in time and space - return(GenesisTime == p.valid() && + return(p.valid() >= (GenesisTime + beg) && + p.valid() <= (GenesisTime + end) && gc_dist(Lat, Lon, p.lat(), p.lon()) <= rad); } +//////////////////////////////////////////////////////////////////////// + +bool GenesisInfo::is_match(const GenesisInfo &gi, const double rad, + const int beg, const int end) const { + + // Input genesis point + const TrackPoint *p = gi.genesis(); + + return(p ? is_match(*p, rad, beg, end) : false); +} + //////////////////////////////////////////////////////////////////////// // // Code for class GenesisInfoArray diff --git a/met/src/libcode/vx_tc_util/genesis_info.h b/met/src/libcode/vx_tc_util/genesis_info.h index bc4a511020..9ef8afb742 100644 --- a/met/src/libcode/vx_tc_util/genesis_info.h +++ b/met/src/libcode/vx_tc_util/genesis_info.h @@ -55,11 +55,8 @@ class GenesisInfo : public TrackInfo { bool IsSet; - // TrackInfo for this Genesis event - TrackInfo Track; - int GenesisIndex; - // Genesis Information + int GenesisIndex; unixtime GenesisTime; int GenesisLead; double Lat; @@ -93,6 +90,8 @@ class GenesisInfo : public TrackInfo { // get stuff // + const TrackPoint *genesis() const; + double lat() const; double lon() const; double dland() const; @@ -105,7 +104,11 @@ class GenesisInfo : public TrackInfo { // bool is_match(const TrackPoint &, - const double) const; + const double, + const int, const int) const; + bool is_match(const GenesisInfo &, + const double, + const int, const int) const; }; //////////////////////////////////////////////////////////////////////// @@ -161,7 +164,6 @@ class GenesisInfoArray { const GenesisInfo & operator[](int) const; int n() const; int n_technique() const; - }; //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/track_info.cc b/met/src/libcode/vx_tc_util/track_info.cc index 312d9aa620..215f0c3b38 100644 --- a/met/src/libcode/vx_tc_util/track_info.cc +++ b/met/src/libcode/vx_tc_util/track_info.cc @@ -299,7 +299,7 @@ void TrackInfo::initialize(const ATCFTrackLine &l, bool check_anly) { MinValidTime = MaxValidTime = l.valid(); // Create the storm id - set_storm_id(); + set_storm_id(l.storm_id().c_str()); return; } diff --git a/met/src/tools/tc_utils/tc_gen/tc_gen.cc b/met/src/tools/tc_utils/tc_gen/tc_gen.cc index f3b614960c..743db7fd44 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen.cc +++ b/met/src/tools/tc_utils/tc_gen/tc_gen.cc @@ -19,6 +19,7 @@ // 002 12/15/20 Halley Gotway Matching logic for MET #1448 // 003 12/31/20 Halley Gotway Add NetCDF output for MET #1430 // 004 01/14/21 Halley Gotway Add GENMPR output for MET #1597 +// 005 04/02/21 Halley Gotway Refinements for MET #1714 // //////////////////////////////////////////////////////////////////////// @@ -82,7 +83,7 @@ static void do_genesis_ctc (const TCGenVxOpt &, static int find_genesis_match (const GenesisInfo &, const GenesisInfoArray &, const TrackInfoArray &, - double); + bool, double, int, int); static void setup_txt_files (int, int); static void setup_table (AsciiTable &); @@ -370,14 +371,17 @@ void get_genesis_pairs(const TCGenVxOpt &vx_opt, conf_info.InitFreqHr*sec_per_hour, vx_opt.InitBeg, vx_opt.InitEnd, vx_opt.InitInc, vx_opt.InitExc); - } + } // end for i bga // Loop over the model genesis events looking for pairs. for(i=0; igenesis_time() - fgi->init(); offset_cs << cs_erase << "with an init vs genesis time offset of " << diff.OpsDSec/sec_per_hour << " hours.\n"; - if(diff.OpsDSec <= vx_opt.OpsHitDSec) { + // Ops Method: + // HIT if forecast init time is close enough to + // the BEST genesis time. + if(diff.OpsDSec >= vx_opt.OpsHitBeg && + diff.OpsDSec <= vx_opt.OpsHitEnd) { mlog << Debug(4) << case_cs << " is an ops method HIT " << offset_cs; @@ -591,10 +595,9 @@ void do_genesis_ctc(const TCGenVxOpt &vx_opt, int find_genesis_match(const GenesisInfo &fcst_gi, const GenesisInfoArray &bga, const TrackInfoArray &ota, - const double rad) { - int i, j; - int i_best = bad_data_int; - int i_oper = bad_data_int; + bool point2track, double rad, + int beg, int end) { + int i, j, i_best, i_oper; ConcatString case_cs; case_cs << fcst_gi.technique() << " " @@ -605,20 +608,36 @@ int find_genesis_match(const GenesisInfo &fcst_gi, << " forecast genesis at (" << fcst_gi.lat() << ", " << fcst_gi.lon() << ")"; - // Search the BEST track points for a match + // Search for a BEST track genesis match for(i=0, i_best=bad_data_int; i conf_info.FcstSecEnd) { - mlog << Debug(6) << "Skipping genesis event for forecast hour " - << fcst_gi.genesis_fhr() << ".\n"; + mlog << Debug(6) + << "Skipping forecast genesis event for forecast hour " + << fcst_gi.genesis_fhr() << " not between " + << conf_info.FcstSecBeg/sec_per_hour << " and " + << conf_info.FcstSecEnd/sec_per_hour << ".\n"; continue; } // Check the forecast track minimum duration if(fcst_gi.duration() < conf_info.MinDur*sec_per_hour) { - mlog << Debug(6) << "Skipping genesis event for track duration of " - << fcst_gi.duration()/sec_per_hour << ".\n"; + mlog << Debug(6) + << "Skipping forecast genesis event for track duration of " + << fcst_gi.duration()/sec_per_hour << " < " + << conf_info.MinDur << ".\n"; continue; } @@ -899,6 +925,15 @@ void process_best_tracks(const StringArray &files, continue; } + // Skip invest tracks with a large cyclone number + if(atof(best_ta[i].cyclone().c_str()) > max_best_cyclone_number) { + mlog << Debug(6) + << "Skipping Best track genesis event for cyclone number " + << best_ta[i].cyclone() << " > " << max_best_cyclone_number + << ".\n"; + continue; + } + // Check for duplicates if(best_ga.has_storm(best_gi, i_bga)) { diff --git a/met/src/tools/tc_utils/tc_gen/tc_gen.h b/met/src/tools/tc_utils/tc_gen/tc_gen.h index 3d6ff17e9c..10118842c6 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen.h +++ b/met/src/tools/tc_utils/tc_gen/tc_gen.h @@ -77,6 +77,10 @@ const ConcatString genesis_name ("GENESIS"); const ConcatString genesis_dev_name("GENESIS_DEV"); const ConcatString genesis_ops_name("GENESIS_OPS"); +// Maximum Best track cyclone number to be processed +// Cyclone numbers > 50 are for testing or invests +static const int max_best_cyclone_number = 50; + //////////////////////////////////////////////////////////////////////// // // Variables for Command Line Arguments diff --git a/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.cc b/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.cc index cdab8c11da..e2046c0c13 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.cc +++ b/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.cc @@ -156,10 +156,12 @@ void TCGenVxOpt::clear() { VxBasinMask.clear(); VxAreaMask.clear(); DLandThresh.clear(); + GenesisMatchPointTrack = false; GenesisMatchRadius = bad_data_double; + GenesisMatchBeg = GenesisMatchEnd = bad_data_int; DevHitRadius = bad_data_double; DevHitBeg = DevHitEnd = bad_data_int; - OpsHitDSec = bad_data_int; + OpsHitBeg = OpsHitEnd = bad_data_int; DiscardFlag = false; DevFlag = OpsFlag = false; CIAlpha = bad_data_double; @@ -242,24 +244,34 @@ void TCGenVxOpt::process_config(Dictionary &dict) { // Conf: dland_thresh DLandThresh = dict.lookup_thresh(conf_key_dland_thresh); + // Conf: genesis_match_point_to_track + GenesisMatchPointTrack = + dict.lookup_bool(conf_key_genesis_match_point_to_track); + // Conf: genesis_match_radius GenesisMatchRadius = dict.lookup_double(conf_key_genesis_match_radius); + // Conf: genesis_match_window + dict2 = dict.lookup_dictionary(conf_key_genesis_match_window); + parse_conf_range_int(dict2, beg, end); + GenesisMatchBeg = beg*sec_per_hour; + GenesisMatchEnd = end*sec_per_hour; + // Conf: dev_hit_radius - DevHitRadius = - dict.lookup_double(conf_key_dev_hit_radius); + DevHitRadius = dict.lookup_double(conf_key_dev_hit_radius); - // Conf: genesis_hit_window + // Conf: dev_hit_window dict2 = dict.lookup_dictionary(conf_key_dev_hit_window); parse_conf_range_int(dict2, beg, end); DevHitBeg = beg*sec_per_hour; DevHitEnd = end*sec_per_hour; - // Conf: ops_hit_tdiff - OpsHitDSec = nint( - dict.lookup_double(conf_key_ops_hit_tdiff) * - sec_per_hour); + // Conf: ops_hit_window + dict2 = dict.lookup_dictionary(conf_key_ops_hit_window); + parse_conf_range_int(dict2, beg, end); + OpsHitBeg = beg*sec_per_hour; + OpsHitEnd = end*sec_per_hour; // Conf: discard_init_post_genesis_flag DiscardFlag = diff --git a/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h b/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h index de7d14c1d9..0769f64f8f 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h +++ b/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h @@ -131,16 +131,18 @@ class TCGenVxOpt { // Distance to land threshold SingleThresh DLandThresh; + // Matching logic + bool GenesisMatchPointTrack; + // Temporal and spatial matching criteria double GenesisMatchRadius; + int GenesisMatchBeg, GenesisMatchEnd; + + // Temporal and spatial scoring options double DevHitRadius; int DevHitBeg, DevHitEnd; - int OpsHitDSec; - - // Scoring methods - bool DiscardFlag; - bool DevFlag; - bool OpsFlag; + int OpsHitBeg, OpsHitEnd; + bool DiscardFlag, DevFlag, OpsFlag; // Output file options double CIAlpha; diff --git a/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc b/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc index a86b1a3d49..6fd095d299 100644 --- a/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc +++ b/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc @@ -1740,7 +1740,7 @@ void compute_track_err(const TrackInfo &adeck, const TrackInfo &bdeck, NumArray &altk_err, NumArray &crtk_err) { int i, i_adeck, i_bdeck, status; unixtime ut, ut_min, ut_max; - int ut_inc, n_ut; + int bd_inc, ut_inc, n_ut; float alat[mxp], alon[mxp], blat[mxp], blon[mxp]; float crtk[mxp], altk[mxp]; double x, y, tk, lon_min, lon_max; @@ -1760,8 +1760,10 @@ void compute_track_err(const TrackInfo &adeck, const TrackInfo &bdeck, // Determine the valid increment // For BEST tracks, use a constant time step // For non-BEST tracks, select the most common BDECK time step - if(bdeck.is_best_track()) ut_inc = best_track_time_step; - else ut_inc = bdeck.valid_inc(); + // Check for 0 and bad data + bd_inc = bdeck.valid_inc(); + ut_inc = (bdeck.is_best_track() || bd_inc == 0 || is_bad_data(bd_inc) ? + best_track_time_step : bd_inc); // Round the valid times to the nearest valid increment if(ut_min%ut_inc != 0) ut_min = (ut_min/ut_inc + 1)*ut_inc; diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index 9a80592f6b..330e0bbadd 100644 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -53,7 +53,7 @@ RUN yum -y update \ && yum -y install gv ncview wgrib wgrib2 ImageMagick ps2pdf \ && yum -y install python3 python3-devel python3-pip \ && pip3 install --upgrade pip \ - && python3 -m pip install numpy xarray + && python3 -m pip install numpy xarray netCDF4 # # Set the working directory. diff --git a/test/config/TCGenConfig_2016 b/test/config/TCGenConfig_2016 index 6d46d9f528..28ec263e85 100644 --- a/test/config/TCGenConfig_2016 +++ b/test/config/TCGenConfig_2016 @@ -90,6 +90,17 @@ filter = [ nc_pairs_flag = TRUE; output_flag = { genmpr = BOTH; } }, + { + desc = "AL_MATCH24HR"; + basin_mask = "AL"; + genesis_match_window = { beg = 0; end = 24; }; + }, + { + desc = "AL_POINT2POINT"; + basin_mask = "AL"; + genesis_match_window = { beg = 0; end = 24; }; + genesis_match_point_to_track = FALSE; + }, { desc = "AL_DLAND_300NM"; basin_mask = "AL"; @@ -119,7 +130,7 @@ filter = [ { desc = "ALL_MATCH_600KM_OPS60HR"; genesis_match_radius = 600; - ops_hit_tdiff = 60; + ops_hit_window = { beg = -60; end = 60; }; dev_method_flag = FALSE; ops_method_flag = TRUE; } @@ -193,18 +204,34 @@ dland_thresh = NA; // //////////////////////////////////////////////////////////////////////////////// +// +// Genesis matching logic. Compare the forecast genesis point to all points in +// the Best track (TRUE) or the single Best track genesis point (FALSE). +// +genesis_match_point_to_track = TRUE; + // // Radius in km to search for a matching genesis event // genesis_match_radius = 500; +// +// Time window in hours, relative to the model genesis time, to search for a +// matching Best track point +// +genesis_match_window = { + beg = 0; + end = 0; +} + // // Radius in km for a development scoring method hit // dev_hit_radius = 500; // -// Time window in hours for a development scoring method hit +// Time window in hours, relative to the model genesis time, for a development +// scoring method hit // dev_hit_window = { beg = -24; @@ -212,10 +239,13 @@ dev_hit_window = { } // -// Maximum Best track genesis minus model initialization time difference for an -// operational scoring method hit +// Time window in hours for the Best track genesis minus model initialization +// time difference for an operational scoring method hit // -ops_hit_tdiff = 48; +ops_hit_window = { + beg = 0; + end = 48; +} // // Discard genesis forecasts for initializations at or after the matching