Skip to content

Commit

Permalink
Feature 1714 tc_gen (#1750)
Browse files Browse the repository at this point in the history
* Per #1714, add tc_gen genesis_match_window configuration option to define a search window relative to the forecast genesis time.

* Per #1714, clarify docs to state the genesis_match_window.end = 12 allows for matches for early forecasts. Also add an example of this option to the tc_gen unit test.

* Per #1714, switch ops_hit_tdiff to ops_hit_window.

* Per #1714, skip genesis events for tracks where the cyclone number is > 50.

* Per #1714, only discard cyclone numbers > 50 from the Best track, not the forecast tracks.

* Per #1716, add note to the tc_gen chapter about skipping Best tracks with cyclone number > 50.

* Per #1714, adding genesis_match_point_to_track config file option for TC-Gen. Note that this version of the code is close but doesn't actually compile yet. I still need to figure out exactly how to process the operational tracks. Should this logic also apply to the matching for those tracks?

* Per #1714, the logic for checking the operational tracks is pretty simple. We only store/check operational track points for lead time = 0. So applying the genesis_match_point_to_track boolean config option does not make sense.

* Per #1714, update the tc-gen user's guide chapter to describe the updated logic and new config file option.

* Per #1714, fix the logic of the is_match() function.

* Per #1714, reconfigure the call to tc_gen to exercise the new genesis_match_track_to_point option.

* Per #1714, just fixing spacing in source code.

* Committing 2 small changes not specifically related to #1714, but related the processing of genesis tracks. When getting items from ATCFGenLines, the columns to be shifted are off by one. We had been shifting offset 2 up to 3, but it should have remained at 2. Also when initializing a TrackInfo object, set the StormID by calling ATCFLineBase::storm_id() instead of constructing it from BASIN:CYCLONE:YYYY. For ATCFGenLines we want to set the Storm ID equal to the 3rd column rather than constructing it!

* Per #1714, fix an error in the logic of GenesisInfo::is_match(const GenesisInfo &,...). I was using the index of the current GenesisInfo object instead of the one from the input argument. Fix this by adding GenesisInfo::genesis() member function to return a reference the TrackPoint for Genesis.

* Per #1714, correcting logic for parsing the storm_id and warning_time columns for ATCFGen and regular ATCF line types. For ATCFGen line types, the code was incorrectly using the 3rd column when it should have used the 4th column!

Co-authored-by: John Halley Gotway <[email protected]>
  • Loading branch information
JohnHalleyGotway and John Halley Gotway authored Apr 9, 2021
1 parent b92a6f6 commit 11866b3
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 76 deletions.
27 changes: 23 additions & 4 deletions met/data/config/TCGenConfig_default
Original file line number Diff line number Diff line change
Expand Up @@ -150,29 +150,48 @@ 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;
end = 24;
}

//
// 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
Expand Down
36 changes: 29 additions & 7 deletions met/docs/Users_Guide/tc-gen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,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.

Expand Down Expand Up @@ -255,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.

______________________

Expand All @@ -278,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.

______________________

Expand Down
4 changes: 3 additions & 1 deletion met/src/basic/vx_config/config_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
16 changes: 11 additions & 5 deletions met/src/libcode/vx_tc_util/atcf_line_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -252,7 +255,8 @@ ConcatString ATCFLineBase::basin() const {
////////////////////////////////////////////////////////////////////////

ConcatString ATCFLineBase::cyclone_number() const {
return(get_item(CycloneNumberOffset)); }
return(get_item(CycloneNumberOffset));
}

////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -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();
Expand Down
25 changes: 22 additions & 3 deletions met/src/libcode/vx_tc_util/genesis_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ void GenesisInfo::set_dland(double d) {

bool GenesisInfo::set(const TrackInfo &ti,
const GenesisEventInfo &event_info) {

// Initialize
clear();

Expand Down Expand Up @@ -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
Expand Down
14 changes: 8 additions & 6 deletions met/src/libcode/vx_tc_util/genesis_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -93,6 +90,8 @@ class GenesisInfo : public TrackInfo {
// get stuff
//

const TrackPoint *genesis() const;

double lat() const;
double lon() const;
double dland() const;
Expand All @@ -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;
};

////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -161,7 +164,6 @@ class GenesisInfoArray {
const GenesisInfo & operator[](int) const;
int n() const;
int n_technique() const;

};

////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion met/src/libcode/vx_tc_util/track_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit 11866b3

Please sign in to comment.