diff --git a/met/data/config/TCGenConfig_default b/met/data/config/TCGenConfig_default index 004bdbe7c5..db38b1302b 100644 --- a/met/data/config/TCGenConfig_default +++ b/met/data/config/TCGenConfig_default @@ -13,25 +13,30 @@ //////////////////////////////////////////////////////////////////////////////// // -// Genesis event definition criteria. +// Genesis event definition criteria // //////////////////////////////////////////////////////////////////////////////// // -// Model initialization frequency in hours, starting at 0. +// Model initialization frequency in hours, starting at 0 // init_freq = 6; // -// Lead times in hours to be searched for genesis events. +// Valid hour frequency to be analyzed in hours, starting at 0 // -lead_window = { - beg = 24; +valid_freq = 6; + +// +// Forecast hours to be searched for genesis events +// +fcst_hr_window = { + beg = 6; end = 120; } // -// Minimum track duration for genesis event in hours. +// Minimum track duration for genesis event in hours // min_duration = 12; @@ -60,36 +65,27 @@ best_genesis = { } // -// Operational track genesis event criteria. Defined as tracks reaching the -// specified intensity category, maximum wind speed threshold, and minimum -// sea-level pressure threshold. The operational track genesis time is valid -// time of the first track point where all of these criteria are met. +// Operational track technique name // -oper_genesis = { - technique = "CARQ"; - category = [ "DB", "LO", "WV" ]; - vmax_thresh = NA; - mslp_thresh = NA; -} +oper_technique = "CARQ"; //////////////////////////////////////////////////////////////////////////////// // -// Track filtering options which may be specified separately in each filter -// array entry. +// Track filtering options +// May be specified separately in each filter array entry. // //////////////////////////////////////////////////////////////////////////////// // -// Filter is an array of dictionaries containing the track filtering options -// listed below. If empty, a single filter is defined using the top-level -// settings. +// Array of dictionaries containing the track filtering options +// If empty, a single filter is defined using the top-level settings. // filter = []; // // Description written to output DESC column // -desc = "NA"; +desc = "ALL"; // // Forecast ATCF ID's @@ -109,10 +105,12 @@ storm_id = []; storm_name = []; // -// Forecast and operational initialization time window +// Forecast and operational initialization times to include or exclude // init_beg = ""; init_end = ""; +init_inc = []; +init_exc = []; // // Forecast, BEST, and operational valid time window @@ -135,27 +133,63 @@ lead = []; // vx_mask = ""; +// +// Spatial masking of hurricane basin names from the basin_file +// +basin_mask = []; + // // Distance to land threshold // dland_thresh = NA; +//////////////////////////////////////////////////////////////////////////////// // -// Genesis matching time window, in hours relative to the forecast genesis time +// Matching and scoring options +// May be specified separately in each filter array entry. // -genesis_window = { +//////////////////////////////////////////////////////////////////////////////// + +// +// Radius in km to search for a matching genesis event +// +genesis_match_radius = 500; + +// +// Radius in km for a development scoring method hit +// +dev_hit_radius = 500; + +// +// Time window in hours for a development scoring method hit +// +dev_hit_window = { beg = -24; end = 24; } // -// Genesis matching search radius in km. +// Maximum Best track genesis minus model initialization time difference for an +// operational scoring method hit +// +ops_hit_tdiff = 48; + +// +// Discard genesis forecasts for initializations at or after the matching +// BEST track genesis time // -genesis_radius = 300; +discard_init_post_genesis_flag = TRUE; + +// +// Scoring methods to be applied +// +dev_method_flag = TRUE; +ops_method_flag = TRUE; //////////////////////////////////////////////////////////////////////////////// // -// Global settings. +// Output options +// May be specified separately in each filter array entry. // //////////////////////////////////////////////////////////////////////////////// @@ -171,14 +205,60 @@ output_flag = { fho = NONE; ctc = BOTH; cts = BOTH; + genmpr = NONE; +} + +// +// NetCDF genesis pair counts +// +nc_pairs_flag = { + latlon = TRUE; + fcst_genesis = TRUE; + fcst_tracks = TRUE; + fcst_fy_oy = TRUE; + fcst_fy_on = TRUE; + best_genesis = TRUE; + best_tracks = TRUE; + best_fy_oy = TRUE; + best_fn_oy = TRUE; } +// +// Specify which track points should be counted by thresholding the track point +// valid time minus genesis time difference. +// +valid_minus_genesis_diff_thresh = NA; + +// +// Count unique BEST track genesis event locations (TRUE) versus counting the +// location for all pairs (FALSE). +// +best_unique_flag = TRUE; + +//////////////////////////////////////////////////////////////////////////////// +// +// Global settings +// May only be specified once. +// +//////////////////////////////////////////////////////////////////////////////// + // // Specify the NetCDF output of the gen_dland tool containing a gridded // representation of the minimum distance to land. // dland_file = "MET_BASE/tc_data/dland_global_tenth_degree.nc"; +// +// Specify the NetCDF file containing a gridded representation of the +// global basins. +// +basin_file = "MET_BASE/tc_data/basin_global_tenth_degree.nc"; + +// +// NetCDF genesis pairs grid +// +nc_pairs_grid = "G003"; + // // Indicate a version number for the contents of this configuration file. // The value should generally not be modified. diff --git a/met/data/table_files/met_header_columns_V10.0.txt b/met/data/table_files/met_header_columns_V10.0.txt index 5a9cdc56e5..851ae5e3ee 100644 --- a/met/data/table_files/met_header_columns_V10.0.txt +++ b/met/data/table_files/met_header_columns_V10.0.txt @@ -28,6 +28,7 @@ V10.0 : STAT : SSVAR : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID V10.0 : STAT : VAL1L2 : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL UFABAR VFABAR UOABAR VOABAR UVFOABAR UVFFABAR UVOOABAR V10.0 : STAT : VL1L2 : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL UFBAR VFBAR UOBAR VOBAR UVFOBAR UVFFBAR UVOOBAR F_SPEED_BAR O_SPEED_BAR V10.0 : STAT : VCNT : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL FBAR FBAR_BCL FBAR_BCU OBAR OBAR_BCL OBAR_BCU FS_RMS FS_RMS_BCL FS_RMS_BCU OS_RMS OS_RMS_BCL OS_RMS_BCU MSVE MSVE_BCL MSVE_BCU RMSVE RMSVE_BCL RMSVE_BCU FSTDEV FSTDEV_BCL FSTDEV_BCU OSTDEV OSTDEV_BCL OSTDEV_BCU FDIR FDIR_BCL FDIR_BCU ODIR ODIR_BCL ODIR_BCU FBAR_SPEED FBAR_SPEED_BCL FBAR_SPEED_BCU OBAR_SPEED OBAR_SPEED_BCL OBAR_SPEED_BCU VDIFF_SPEED VDIFF_SPEED_BCL VDIFF_SPEED_BCU VDIFF_DIR VDIFF_DIR_BCL VDIFF_DIR_BCU SPEED_ERR SPEED_ERR_BCL SPEED_ERR_BCU SPEED_ABSERR SPEED_ABSERR_BCL SPEED_ABSERR_BCU DIR_ERR DIR_ERR_BCL DIR_ERR_BCU DIR_ABSERR DIR_ABSERR_BCL DIR_ABSERR_BCU +V10.0 : STAT : GENMPR : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL INDEX STORM_ID AGEN_INIT AGEN_FHR AGEN_LAT AGEN_LON AGEN_DLAND BGEN_LAT BGEN_LON BGEN_DLAND GEN_DIST GEN_TDIFF INIT_TDIFF DEV_CAT OPS_CAT V10.0 : MODE : OBJ : VERSION MODEL N_VALID GRID_RES DESC FCST_LEAD FCST_VALID FCST_ACCUM OBS_LEAD OBS_VALID OBS_ACCUM FCST_RAD FCST_THR OBS_RAD OBS_THR FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE OBJECT_ID OBJECT_CAT CENTROID_X CENTROID_Y CENTROID_LAT CENTROID_LON AXIS_ANG LENGTH WIDTH AREA AREA_THRESH CURVATURE CURVATURE_X CURVATURE_Y COMPLEXITY INTENSITY_10 INTENSITY_25 INTENSITY_50 INTENSITY_75 INTENSITY_90 INTENSITY_USER INTENSITY_SUM CENTROID_DIST BOUNDARY_DIST CONVEX_HULL_DIST ANGLE_DIFF ASPECT_DIFF AREA_RATIO INTERSECTION_AREA UNION_AREA SYMMETRIC_DIFF INTERSECTION_OVER_AREA CURVATURE_RATIO COMPLEXITY_RATIO PERCENTILE_INTENSITY_RATIO INTEREST V10.0 : MODE : CTS : VERSION MODEL N_VALID GRID_RES DESC FCST_LEAD FCST_VALID FCST_ACCUM OBS_LEAD OBS_VALID OBS_ACCUM FCST_RAD FCST_THR OBS_RAD OBS_THR FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE FIELD TOTAL FY_OY FY_ON FN_OY FN_ON BASER FMEAN ACC FBIAS PODY PODN POFD FAR CSI GSS HK HSS ODDS diff --git a/met/data/tc_data/basin_global_tenth_degree.nc b/met/data/tc_data/basin_global_tenth_degree.nc index fe50c30629..82dea4c0e1 100644 Binary files a/met/data/tc_data/basin_global_tenth_degree.nc and b/met/data/tc_data/basin_global_tenth_degree.nc differ diff --git a/met/docs/Users_Guide/tc-gen.rst b/met/docs/Users_Guide/tc-gen.rst index 33b2dd4099..45bd9e7404 100644 --- a/met/docs/Users_Guide/tc-gen.rst +++ b/met/docs/Users_Guide/tc-gen.rst @@ -11,7 +11,7 @@ The TC-Gen tool provides verification of tropical cyclone genesis forecasts in A Statistical aspects ___________________ -The TC-Gen tool populates a contingency table with hits, misses, and false alarms. As with other extreme events (where the event occurs much less frequently than the non-event), the correct negative category is not computed the non-events would dominate the contingency table. Therefore, only statistics that do not include correct negatives should be considered for this tool. The following CTS statistics are relevant: Base rate (BASER), Mean forecast (FMEAN), Frequency Bias (FBIAS), Probability of Detection (PODY), False Alarm Ratio (FAR), Critical Success Index (CSI), Gilbert Skill Score (GSS), Extreme Dependency Score (EDS), Symmetric Extreme Dependency Score (SEDS), Bias Adjusted Gilbert Skill Score (BAGSS). +The TC-Gen tool populates a contingency tables with hits, misses, and false alarms. As with other extreme events (where the event occurs much less frequently than the non-event), the correct negative category is not computed the non-events would dominate the contingency table. Therefore, only statistics that do not include correct negatives should be considered for this tool. The following CTS statistics are relevant: Base rate (BASER), Mean forecast (FMEAN), Frequency Bias (FBIAS), Probability of Detection (PODY), False Alarm Ratio (FAR), Critical Success Index (CSI), Gilbert Skill Score (GSS), Extreme Dependency Score (EDS), Symmetric Extreme Dependency Score (SEDS), Bias Adjusted Gilbert Skill Score (BAGSS). 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. @@ -44,7 +44,7 @@ Required arguments for tc_gen 1. The **-genesis path** argument is the path to one or more ATCF or fort.66 (see documentation listed below) files generated by the Geophysical Fluid Dynamics Laboratory (GFDL) Vortex Tracker when run in tcgen mode or an ASCII file list or a top-level directory containing them. The **-genesis** option must be used at least once. The required file format is described in the "Output formats" section of the `GFDL Vortex Tracker users guide. `_ -2. The **-track path** argument is one or more ATCF reference track files or an ASCII file list or top-level directory containing them, with files ending in “.dat”. This tool processes either BEST track data from bdeck files, or operational track data (e.g. CARQ) from adeck files, or both. Providing both bdeck and adeck files will result in a richer dataset to match with the **-genesis** files. Both adeck and bdeck data should be provided using the **-track** option. The **-track** option must be used at least once. +2. The **-track path** argument is one or more ATCF reference track files or an ASCII file list or top-level directory containing them, with files ending in “.dat”. This tool processes either Best track data from bdeck files, or operational track data (e.g. CARQ) from adeck files, or both. Providing both bdeck and adeck files will result in a richer dataset to match with the **-genesis** files. Both adeck and bdeck data should be provided using the **-track** option. The **-track** option must be used at least once. 3. The **-config** file argument indicates the name of the configuration file to be used. The contents of the configuration file are discussed below. @@ -59,19 +59,33 @@ Optional arguments for tc_gen The TC-Gen tool implements the following logic: -* Parse the genesis data and identify forecast genesis events separately for each model present. +* Parse the forecast genesis data and identify forecast genesis events separately for each model present. -* Parse the BEST and operational track data and identify observed genesis events. +* Parse the Best and operational track data, and identify Best track genesis events. * Loop over the filters defined in the configuration file and apply the following logic for each. - * For each forecast genesis event, search the BEST genesis events for a match that is close enough in time and space. If not found, search the operational genesis events for a match. If a match is found, classify the forecast genesis event as a **hit**. Otherwise, classify it as a **false alarm**. + * 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. + + * 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. + + * If a matching storm ID is found, match the forecast genesis event to the Best track genesis event for that storm ID. + + * If no matching storm ID is found, store an unmatched pair for the genesis forecast. - * For each BEST track genesis event, determine the initialization and lead times for which the model had an opportunity to forecast that genesis event. If the model opportunity is not classified in the previous step, then classify as a **miss**. + * Loop through the genesis pairs and populate contingency tables using two methods, the developement (dev) and operational (ops) methods. For each pair, if the forecast genesis event is unmatched, score it as a dev and ops FALSE ALARM. If the Best track genesis event is unmatched, score it as a dev and ops MISS. Score each matched genesis pair as follows: - * Do not count any correct negatives. + * If the forecast initialization time is at or after the Best track genesis event, DISCARD this case and exclude it from the statistics. + + * Compute the difference between the forecast and Best track genesis events in time and space. If they are both within the configurable tolerance, score it as a dev HIT. If not, score it as a dev FALSE ALARM. + + * Compute the difference between the Best track genesis time and model initialization time. If it is within the configurable tolerance, score it as an ops HIT. If not, score it as an ops FALSE ALARM. -* Report the contingency table hits, misses, and false alarms separately for each forecast model and configuration file filter. + * Do not count any CORRECT NEGATIVES. + +* Report the contingency table hits, misses, and false alarms separately for each forecast model and configuration file filter. The development (dev) scoring method is indicated in the output as *GENESIS_DEV* while the operational (ops) scoring method is indicated as *GENESIS_OPS*. tc_gen configuration file ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -86,18 +100,26 @@ ______________________ init_freq = 6; -The **init_freq** variable is an integer specifying the model initialization frequency in hours, starting at 00Z. The default value of 6 indicates that the model is initialized every day at 00Z, 06Z, 12Z, and 18Z. The same frequency is applied to all models processed. Models initialized at different frequencies should be processed with separate calls to tc_gen. The initialization frequency is used when defining the model opportunities to forecast the BEST track genesis events. +The **init_freq** variable is an integer specifying the model initialization frequency in hours, starting at 00Z. The default value of 6 indicates that the model is initialized every day at 00Z, 06Z, 12Z, and 18Z. The same frequency is applied to all models processed. Models initialized at different frequencies should be processed with separate calls to tc_gen. The initialization frequency is used when defining the model opportunities to forecast the Best track genesis events. ______________________ .. code-block:: none - lead_window = { + valid_freq = 6; + +The **valid_freq** variable is an integer specifying the valid time of the track points to be analyzed in hours, starting at 00Z. The default value of 6 indicates that only track points with valid times of 00Z, 06Z, 12Z, and 18Z will be checked for genesis events. Since Best and operational tracks are typically only available at those times, a match to a forecast genesis event is only possible for those hours. + +______________________ + +.. code-block:: none + + fcst_hr_window = { beg = 24; end = 120; } -The **lead_window** option is a dictionary defining the beginning (**beg**) and ending (**end**) model forecast hours to be searched for genesis events. Model genesis events occurring outside of this window are ignored. This lead window is also used when defining the model opportunities to forecast the BEST track genesis events. +The **fcst_hr_window** option is a dictionary defining the beginning (**beg**) and ending (**end**) model forecast hours to be searched for genesis events. Model genesis events occurring outside of this window are ignored. This forecast hour window is also used when defining the model opportunities to forecast the Best track genesis events. ______________________ @@ -128,14 +150,16 @@ ______________________ vmax_thresh = NA; mslp_thresh = NA; } - oper_genesis = { - technique = "CARQ"; - category = [ "DB", "LO", "WV" ]; - vmax_thresh = NA; - mslp_thresh = NA; - } -The **best_genesis** and **oper_genesis** dictionaries define genesis criteria for the BEST and operational tracks, respectively. Like the **fcst_genesis** dictionary, the **vmax_thresh** and **mslp_thresh** thresholds define required genesis criteria. In addition, the **category** array defines the ATCF storm categories that should qualify as genesis events. The **technique** string defines the ATCF ID for the BEST and operational tracks. +The **best_genesis** dictionary defines genesis criteria for the Best tracks. Like the **fcst_genesis** dictionary, the **vmax_thresh** and **mslp_thresh** thresholds define required genesis criteria. In addition, the **category** array defines the ATCF storm categories that should qualify as genesis events. The **technique** string defines the ATCF ID for the Best track. + +______________________ + +.. code-block:: none + + oper_technique = "CARQ"; + +The **oper_technique** entry is a string which defines the ATCF ID for the operational track data that should be used. For each forecast genesis event, the Best tracks are searched for a track point valid at the time of forecast genesis and within the search radius. If no match is found, the 0-hour operational track points are searched for a match. ______________________ @@ -143,13 +167,13 @@ ______________________ filter = []; -The **filter** entry is an array of dictionaries defining genesis filtering criteria to be applied. Each of the entries listed below (from **desc** to **genesis_radius**) may be specified separately within each filter dictionary. If left empty, the default setting, a single filter is applied using the top-level filtering criteria. If multiple filtering dictionaries are defined, the **desc** entry must be specified for each to differentiate the output data. Output is written for each combination of filter dictionary and model ATCF ID encountered in the data. +The **filter** entry is an array of dictionaries defining genesis filtering criteria to be applied. Each of the entries listed below (from **desc** to **best_unique_flag**) may be specified separately within each filter dictionary. If left empty, the default setting, a single filter is applied using the top-level filtering criteria. If multiple filtering dictionaries are defined, the **desc** entry must be specified for each to differentiate the output data. Output is written for each combination of filter dictionary and model ATCF ID encountered in the data. ______________________ .. code-block:: none - desc = "NA"; + desc = "ALL"; The **desc** configuration option is common to many MET tools and is described in :numref:`config_options`. @@ -168,7 +192,7 @@ ______________________ storm_id = []; storm_name = []; -The **storm_id** and **storm_name** entries are arrays indicating the ATCF storm ID's and storm names to be processed. If left empty, all tracks will be processed. Otherwise, only those tracks which meet these criteria will be included. Note that these strings only appear in the BEST and operational tracks, not the forecast genesis data. Therefore, these filters only apply to the BEST and operational tracks. Care should be given when interpreting the contingency table results for filtered data. +The **storm_id** and **storm_name** entries are arrays indicating the ATCF storm ID's and storm names to be processed. If left empty, all tracks will be processed. Otherwise, only those tracks which meet these criteria will be included. Note that these strings only appear in the Best and operational tracks, not the forecast genesis data. Therefore, these filters only apply to the Best and operational tracks. Care should be given when interpreting the contingency table results for filtered data. ______________________ @@ -176,8 +200,10 @@ ______________________ init_beg = ""; init_end = ""; + init_inc = []; + init_exc = []; -The **init_beg** and **init_end** entries are strings in YYYYMMDD[_HH[MMSS]] format which defines which forecast and operational tracks initializations to be processed. If left empty, all tracks will be used. Otherwise, only those tracks whose initialization time falls within the window will be included. Note that these settings only apply to the forecast and operational tracks, not the BEST tracks, for which the initialization time is undefined. Care should be given when interpreting the contingency table results for filtered data. +The **init_beg**, **init_end**, **init_inc**, and **init_exc** entries define strings in YYYYMMDD[_HH[MMSS]] format which defines which forecast and operational tracks initializations to be processed. If left empty, all tracks will be used. Otherwise, only those tracks whose initialization time meets all the criteria will be processed. The initialization time must fall between **init_beg**, and **init_end**, must appear in **init_inc** inclusion list, and must not appear in the **init_exc** exclusion list. Note that these settings only apply to the forecast and operational tracks, not the Best tracks, for which the initialization time is undefined. Care should be given when interpreting the contingency table results for filtered data. ______________________ @@ -195,16 +221,25 @@ ______________________ init_hour = []; lead = []; -The **init_hour** and **lead** entries are arrays of strings in HH[MMSS] format defining which forecast and operational tracks should be included. If left empty, all tracks will be used. Otherwise, only those forecast and operational tracks whose initialization hour and lead times appear in the list will be used. Note that these settings only apply to the forecast and operational tracks, not the BEST tracks, for which the initialization time is undefined. Care should be given when interpreting the contingency table results for filtered data. +The **init_hour** and **lead** entries are arrays of strings in HH[MMSS] format defining which forecast tracks should be included. If left empty, all tracks will be used. Otherwise, only those forecast tracks whose initialization hour and lead times appear in the list will be used. Note that these settings only apply to the forecast tracks, not the Best tracks, for which the initialization time is undefined. Care should be given when interpreting the contingency table results for filtered data. ______________________ .. code-block:: none - vx_mask = "MET_BASE/tc_data/basin_global_tenth_degree.nc \ - { 'name=\”basin\”;level=\”(*,*)\”; } ==1"; + vx_mask = ""; -The **vx_mask** entry is a string defining the path to a Lat/Lon polyline file or a gridded data file that MET can read to subset the results spatially. If specified, only those genesis events whose Lat/Lon location falls within the specified area will be included. The MET code includes the file **basin_global_tenth_degree.nc**, which contains a global definition of the Regional Specialized Meteorology Centers (RSMC) and hurricane basin regions. The above example uses this file to stratify genesis results for the Atlantic Basin, where the **basin** variable equals ones. +The **vx_mask** entry is a string defining the path to a Lat/Lon polyline file or a gridded data file that MET can read to subset the results spatially. If specified, only those genesis events whose Lat/Lon location falls within the specified area will be included. + +______________________ + +.. code-block:: none + + basin_mask = []; + +The **basin_mask** entry is an array of strings listing tropical cycline basin abbreviations (e.g. AL, EP, CP, WP, NI, SI, AU, and SP). The configuration entry **basin_file** defines the path to a NetCDF file which defines these regions. The default file (**basin_global_tenth_degree.nc**) is bundled with MET. If **basin_mask** is left empty, genesis events for all basins will be included. If non-empty, the union of specified basins will be used. If **vx_mask** is also specified, the analysis is done on the intersection of those masking areas. + +The **vx_mask** and **basin_mask** names are concatenated and written to the **VX_MASK** output column. ______________________ @@ -212,26 +247,111 @@ ______________________ dland_thresh = NA; -The **dland_thresh** entry is a threshold defining whether the genesis event should be included based on it's distance to land. The default threshold (**NA**) always evaluates to true. +The **dland_thresh** entry is a threshold defining whether the genesis event should be included based on its distance to land. The default threshold (**NA**) always evaluates to true. + +______________________ + +.. 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. + +______________________ + +.. code-block:: none + + dev_hit_radius = 500; + +The **dev_hit_radius** entry defines the maximum distance, in km, that the forecast and Best track genesis events may be separated in order for them to be counted as a contingency table HIT for the development scoring method. Users should set this hit radius less than or equal to the genesis match radius. Reducing this radius may cause development method HITS to become FALSE ALARMS. ______________________ .. code-block:: none - genesis_window = { + dev_hit_window = { beg = -24; end = 24; } -The **genesis_window** entry defines a matching time window, in hours, relative to the forecast genesis time. When searching for a match, only those BEST/operational genesis events which occur within this time window will be considered. Increasing this time window should lead to an increase in hits. +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. + +______________________ + +.. code-block:: none + + ops_hit_tdiff = 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. ______________________ .. code-block:: none - genesis_radius = 300; + discard_init_post_genesis_flag = TRUE; -The **genesis_radius** entry defines a search radius, in km, relative to the forecast genesis location. When searching for a match, only those BEST/operational genesis events which occur within this radius will be considered. Increasing this search radius should lead to an increase in hits. +The **discard_init_post_genesis_flag** entry is a boolean which indicates whether or not forecast genesis events from model intializations occurring at or after the matching Best track genesis time should be discarded. If true, those cases are not scored in the contingency table. If false, they are included in the counts. + +______________________ + +.. code-block:: none + + dev_method_flag = TRUE; + ops_method_flag = TRUE; + +The **dev_method_flag** and **ops_method_flag** entries are booleans which indicate whether the development and operational scoring methods should be applied and written to the output. At least one of these flags must be set to true. + +______________________ + +.. code-block:: none + + nc_pairs_flag = { + latlon = TRUE; + fcst_genesis = TRUE; + fcst_tracks = TRUE; + fcst_fy_oy = TRUE; + fcst_fy_on = TRUE; + best_genesis = TRUE; + best_tracks = TRUE; + best_fy_oy = TRUE; + best_fn_oy = TRUE; + } + +The **nc_pairs_flag** entry is a dictionary of booleans indicating which fields should be written to the NetCDF genesis pairs output file. Each type of output is enabled by setting it to TRUE and disabled by setting it to FALSE. The **latlon** option writes the latitude and longitude values of the output grid. The remaining options write a count of the number of points occuring within each grid cell. The **fcst_genesis** and **best_genesis** options write counts of the forecast and Best track genesis locations. The **fcst_track** and **best_track** options write counts of the full set of track point locations, which can be refined by the **valid_minus_genesis_diff_thresh** option, described below. The **fcst_fy_oy** and **fcst_fy_on** options write counts for the locations of forecast genesis event HITS and FALSE ALARMS. The **best_fy_oy** and **best_fn_oy** options write counts for the locations of Best track genesis event HITS and MISSES. Note that since matching forecast and Best track genesis events may occur in different grid cells, their counts are reported separately. + +______________________ + + +.. code-block:: none + + valid_minus_genesis_diff_thresh = NA; + +The **valid_minus_genesis_diff_thresh** is a threshold which affects the counts in the NetCDF pairs output file. The fcst_tracks and best_tracks options, described above, turn on counts for the forecast and Best track points. This option defines which of those track points should be counted by thresholding the track point valid time minus genesis time difference. If set to NA, the default threshold which always evaluates to true, all track points will be counted. Setting <=0 would count the genesis point and all track points prior. Setting >0 would count all points after genesis. And setting >=-12||<=12 would could all points within 12 hours of the genesis time. + +______________________ + + +.. code-block:: none + + best_unique_flag = TRUE; + +The **best_unique_flag** entry is a boolean which affects the counts in the NetCDF pairs output file. If true, the Best track HIT and MISS locations are counted for each genesis pair. If false, each Best track genesis event is counted only once. If it is a HIT in at least one genesis pair, it is counted as a HIT in the output. Otherwise, it is counted as a MISS. + +______________________ + +.. code-block:: none + + basin_file = "MET_BASE/tc_data/basin_global_tenth_degree.nc"; + +The **basin_file** entry defines the path to the NetCDF basin data file that is included with MET. When a Best track storm moves from one basin to another, the Best track dataset can include two tracks for the same storm, one for each basin. However, both tracks have the same genesis point. When this occurs, this basin data file is read and used to determine the basin in which genesis actually occurred. The corresponding Best track is retained and the other is discarded. + +______________________ + +.. code-block:: none + + nc_pairs_grid = "G001"; + +The **nc_pairs_grid** entry is a string which defines the grid to be used for the NetCDF genesis pairs output file. It can be specified as a named grid, the path to a gridded data file, or a grid specification string. ______________________ @@ -251,8 +371,198 @@ The configuration options listed above are common to many MET tools and are desc tc_gen output ~~~~~~~~~~~~~ -TC-Gen produces output in STAT and, optionally, ASCII format. The ASCII output duplicates the STAT output but has the data organized by line type. The output files are created based on the **-out** command line argument. The default output base name, **./tc_gen** writes output files in the current working directory named **tc_gen.stat** and, optionally, **tc_gen_fho.txt, tc_gen_ctc.txt**, and **tc_gen_cts.txt**. The contents of these output files are described in section :numref:`point_stat-output`. +TC-Gen produces output in STAT and, optionally, ASCII and NetCDF formats. The ASCII output duplicates the STAT output but has the data organized by line type. The output files are created based on the **-out** command line argument. The default output base name, **./tc_gen** writes output files in the current working directory named **tc_gen.stat** and, optionally, **tc_gen_fho.txt, tc_gen_ctc.txt**, **tc_gen_cts.txt**, **tc_gen_genmpr.txt**, and **tc_gen_pairs.nc**. The format of the STAT and ASCII output of the TC-Gen tool matches the output of other MET tools with the exception of the genesis matched pair line type. Please refer to the tables in :numref:`point_stat-output` for a description of the common output line types. The genesis matched pair line type and NetCDF output file are described below. + +.. _table_TG_header_info_tg_outputs: + +.. list-table:: Header information for each file tc-gen outputs + :widths: auto + :header-rows: 2 + + * - HEADER + - + - + * - Column Number + - Header Column Name + - Description + * - 1 + - VERSION + - Version number + * - 2 + - MODEL + - Current ATCF Technique name + * - 3 + - DESC + - User provided text string describing the "filter" options + * - 4 + - FCST_LEAD + - Forecast lead time in HHMMSS format + * - 5 + - FCST_VALID_BEG + - Minimum forecast valid time in YYYYMMDD_HHMMSS format + * - 6 + - FCST_VALID_END + - Maximum forecast valid time in YYYYMMDD_HHMMSS format + * - 7 + - OBS_LEAD + - Does not apply and is set to NA + * - 8 + - OBS_VALID_BEG + - Minimum Best track valid time in YYYYMMDD_HHMMSS format + * - 9 + - OBS_VALID_END + - Maximum Best track valid time in YYYYMMDD_HHMMSS format + * - 10 + - FCST_VAR + - Genesis methodology + * - 11 + - FCST_UNITS + - Does not apply and is set to NA + * - 12 + - FCST_LEV + - Does not apply and is set to NA + * - 13 + - OBS_VAR + - Genesis methodology + * - 14 + - OBS_UNITS + - Does not apply and is set to NA + * - 15 + - OBS_LEV + - Does not apply and is set to NA + * - 16 + - OBTYPE + - Verifying Best track technique name + * - 17 + - VX_MASK + - Verifying masking region + * - 18 + - INTERP_MTHD + - Does not apply and is set to NA + * - 19 + - INTERP_PNTS + - Does not apply and is set to NA + * - 20 + - FCST_THRESH + - Does not apply and is set to NA + * - 21 + - OBS_THRESH + - Does not apply and is set to NA + * - 22 + - COV_THRESH + - Does not apply and is set to NA + * - 23 + - ALPHA + - Error percent value used in confidence intervals + * - 24 + - LINE_TYPE + - Various line type options, refer to :numref:`point_stat-output` and the tables below. + +.. _table_TG_format_info_GENMPR: + +.. list-table:: Format information for GENMPR (Genesis Matched Pairs) output line type + :widths: auto + :header-rows: 2 + + * - GENMPR OUTPUT FORMAT + - + - + * - Column Number + - GENMPR Column Name + - Description + * - 5, 6 + - FCST_VALID_BEG, FCST_VALID_END + - Forecast genesis time in YYYYMMDD_HHMMSS format + * - 8, 9 + - OBS_VALID_BEG, OBS_VALID_END + - Best track genesis time in YYYYMMDD_HHMMSS format + * - 24 + - GENMPR + - Genesis Matched Pairs line type + * - 25 + - TOTAL + - Total number of genesis pairs + * - 26 + - INDEX + - Index for the current matched pair + * - 27 + - STORM_ID + - BBCCYYYY designation of storm (basin, cyclone number, and year) + * - 28 + - AGEN_INIT + - Forecast initialization time + * - 29 + - AGEN_FHR + - Forecast hour of genesis event + * - 30 + - AGEN_LAT + - Latitude position of the forecast genesis event + * - 31 + - AGEN_LON + - Longitude position of the forecast genesis event + * - 32 + - AGEN_DLAND + - Forecast genesis event distance to land (nm) + * - 33 + - BGEN_LAT + - Latitude position of the verifying Best track genesis event + * - 34 + - BGEN_LON + - Longitude position of the verifying Best track genesis event + * - 35 + - BGEN_DLAND + - Best track genesis event distance to land (nm) + * - 36 + - GEN_DIST + - Distance between the forecast and Best track genesis events (km) + * - 37 + - GEN_TDIFF + - Forecast minus Best track genesis time in HHMMSS format + * - 38 + - INIT_TDIFF + - Best track genesis minus forecast initialization time in HHMMSS format + * - 39 + - DEV_CAT + - Development methodology category (FYOY, FYON, FNOY, or DISCARD) + * - 40 + - OPS_CAT + - Operational methodology category (FYOY, FYON, FNOY, or DISCARD) + +.. _table_TG_var_NetCDF_matched_pair_out: + +.. list-table:: A selection of variables that can appear in the NetCDF matched pair output which can be controlled by the nc_pairs_flag configuration option. + :widths: auto + :header-rows: 2 + + * - tc_gen NETCDF VARIABLES + - + - + * - NetCDF Variable + - Dimension + - Description + * - DESC_MODEL_GENESIS + - lat, lon + - For each filter entry (DESC) and forecast ATCF ID (MODEL), count the number of forecast genesis events within each grid box. + * - DESC_MODEL_TRACKS + - lat, lon + - For each filter entry (DESC) and forecast ATCF ID (MODEL), count the number of track points within each grid box. + * - DESC_BEST_GENESIS + - lat, lon + - For each filter entry (DESC), count the number of Best track genesis events within each grid box. + * - DESC_BEST_GENESIS + - lat, lon + - For each filter entry (DESC), count the number of Best track points within each grid box. + * - DESC_MODEL_[DEV|OPS]_FY_OY + - lat, lon + - For each filter entry (DESC) and forecast ATCF ID (MODEL), count the number of forecast genesis events classified as hits by the development (DEV) or operational (OPS) methodology. + * - DESC_MODEL_[DEV|OPS]_FY_ON + - lat, lon + - For each filter entry (DESC) and forecast ATCF ID (MODEL), count the number of forecast genesis events classified as false alarms by the development (DEV) or operational (OPS) methodology. + * - DESC_MODEL_BEST_[DEV|OPS]_FY_OY + - lat, lon + - For each filter entry (DESC) and forecast ATCF ID (MODEL), count the number of Best track genesis events classified as hits by the development (DEV) or operational (OPS) methodology. + * - DESC_MODEL_BEST_[DEV|OPS]_FN_OY + - lat, lon + - For each filter entry (DESC) and forecast ATCF ID (MODEL), count the number of Best track genesis events classified as misses by the development (DEV) or operational (OPS) methodology. Like all STAT output, the output of TC-Gen may be further processed using the Stat-Analysis tool, described in :numref:`stat-analysis`. - -Future development will include writing out a NetCDF file with total counts of events, hits, misses, false alarms, and pertinent information for plotting the density of genesis events. diff --git a/met/docs/Users_Guide/tc-pairs.rst b/met/docs/Users_Guide/tc-pairs.rst index 9c85349f35..2df76d0506 100644 --- a/met/docs/Users_Guide/tc-pairs.rst +++ b/met/docs/Users_Guide/tc-pairs.rst @@ -288,7 +288,7 @@ TC-Pairs produces output in TCST format. The default output file name can be ove - User provided text string designating model name * - 4 - STORM_ID - - BBCCYYY designation of storm + - BBCCYYYY designation of storm * - 5 - BASIN - Basin (BB in STORM_ID) diff --git a/met/internal_tests/libcode/vx_tc_util/test_read.cc b/met/internal_tests/libcode/vx_tc_util/test_read.cc index 08746b6507..568fead189 100644 --- a/met/internal_tests/libcode/vx_tc_util/test_read.cc +++ b/met/internal_tests/libcode/vx_tc_util/test_read.cc @@ -63,10 +63,10 @@ int main(int argc, char *argv[]) { << "Read " << count << " lines from input file \"" << input_filename << "\"\n"; mlog << Debug(1) - << "TrackInfoArray contains " << t_array.n_tracks() + << "TrackInfoArray contains " << t_array.n() << " tracks.\n"; - for(i=0; i " << "unexpected stat line type \"" << statlinetype_to_string(cur_lt) @@ -1834,6 +1835,7 @@ void STATAnalysisJob::setup_stat_file(int n_row, int n) { case stat_relp: write_relp_header_row (1, n, stat_at, 0, 0); break; case stat_orank: write_header_row (orank_columns, n_orank_columns, 1, stat_at, 0, 0); break; case stat_ssvar: write_header_row (ssvar_columns, n_ssvar_columns, 1, stat_at, 0, 0); break; + case stat_genmpr: write_header_row (genmpr_columns, n_genmpr_columns, 1, stat_at, 0, 0); break; // // Write only header columns for unspecified line type @@ -1979,6 +1981,7 @@ void STATAnalysisJob::dump_stat_line(const STATLine &line) { case(stat_phist): case(stat_relp): case(stat_orank): + case(stat_genmpr): write_header_row((const char **) 0, 0, 1, dump_at, 0, 0); break; diff --git a/met/src/libcode/vx_nc_util/Makefile.am b/met/src/libcode/vx_nc_util/Makefile.am index 405e588dc5..900ec9bd1e 100644 --- a/met/src/libcode/vx_nc_util/Makefile.am +++ b/met/src/libcode/vx_nc_util/Makefile.am @@ -16,7 +16,7 @@ libvx_nc_util_a_SOURCES = \ nc_utils.cc nc_utils.h \ write_netcdf.cc write_netcdf.h \ grid_output.cc grid_output.h \ - load_dland.cc load_dland.h \ + load_tc_data.cc load_tc_data.h \ nc_constants.h \ vx_nc_util.h libvx_nc_util_a_CPPFLAGS = ${MET_CPPFLAGS} diff --git a/met/src/libcode/vx_nc_util/load_dland.cc b/met/src/libcode/vx_nc_util/load_dland.cc deleted file mode 100644 index a475359793..0000000000 --- a/met/src/libcode/vx_nc_util/load_dland.cc +++ /dev/null @@ -1,80 +0,0 @@ -// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* -// ** Copyright UCAR (c) 1992 - 2020 -// ** University Corporation for Atmospheric Research (UCAR) -// ** National Center for Atmospheric Research (NCAR) -// ** Research Applications Lab (RAL) -// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA -// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* - -//////////////////////////////////////////////////////////////////////// - -using namespace std; - -#include - -#include "vx_data2d_nc_met.h" -#include "load_dland.h" - -//////////////////////////////////////////////////////////////////////// - -void load_dland(const ConcatString &dland_file, Grid &grid, - DataPlane &dp) { - ConcatString file_name; - LongArray dim; - int i; - - // Get the path for the distance to land file - file_name = replace_path(dland_file); - - mlog << Debug(1) - << "Distance to land file: " << file_name << "\n"; - - // Check for no file provided - if(file_name.empty()) return; - - // Open the NetCDF output of the tc_dland tool - MetNcFile MetNc; - if(!MetNc.open(file_name.c_str())) { - mlog << Error - << "\nload_dland() -> " - << "problem reading file \"" << file_name << "\"\n\n"; - exit(1); - } - - // Find the first non-lat/lon variable - for(i=0; i " - << "can't find non-lat/lon variable in file \"" - << file_name << "\"\n\n"; - exit(1); - } - - // Store the grid - grid = MetNc.grid; - - // Set the dimension to (*,*) - dim.add(vx_data2d_star); - dim.add(vx_data2d_star); - - // Read the data - if(!MetNc.data(MetNc.Var[i].var, dim, dp)) { - mlog << Error - << "\nload_dland() -> " - << "can't read data from file \"" - << file_name << "\"\n\n"; - exit(1); - } - - return; -} - -//////////////////////////////////////////////////////////////////////// - diff --git a/met/src/libcode/vx_nc_util/load_tc_data.cc b/met/src/libcode/vx_nc_util/load_tc_data.cc new file mode 100644 index 0000000000..39710f912e --- /dev/null +++ b/met/src/libcode/vx_nc_util/load_tc_data.cc @@ -0,0 +1,136 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2020 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + +//////////////////////////////////////////////////////////////////////// + +using namespace std; + +#include + +#include "vx_data2d_nc_met.h" +#include "vx_nc_util.h" +#include "load_tc_data.h" + +//////////////////////////////////////////////////////////////////////// + +void load_tc_dland(const ConcatString &dland_file, Grid &grid, + DataPlane &dp) { + ConcatString file_name; + LongArray dim; + int i; + + // Get the path for the distance to land file + file_name = replace_path(dland_file); + + mlog << Debug(1) + << "Distance to land file: " << file_name << "\n"; + + // Check for no file provided + if(file_name.empty()) return; + + // Open the NetCDF output of the tc_dland tool + MetNcFile MetNc; + if(!MetNc.open(file_name.c_str())) { + mlog << Error << "\nload_tc_dland() -> " + << "problem reading file \"" << file_name << "\"\n\n"; + exit(1); + } + + // Find the first non-lat/lon variable + for(i=0; i " + << "can't find non-lat/lon variable in file \"" + << file_name << "\"\n\n"; + exit(1); + } + + // Store the grid + grid = MetNc.grid; + + // Set the dimension to (*,*) + dim.add(vx_data2d_star); + dim.add(vx_data2d_star); + + // Read the data + if(!MetNc.data(MetNc.Var[i].var, dim, dp)) { + mlog << Error << "\nload_tc_dland() -> " + << "can't read data from file \"" + << file_name << "\"\n\n"; + exit(1); + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + +static const char nc_var_basin [] = "basin"; +static const char nc_var_basin_abbr [] = "basin_abbr"; + +//////////////////////////////////////////////////////////////////////// + +void load_tc_basin(const ConcatString &basin_file, Grid &grid, + DataPlane &dp, StringArray &abbr) { + ConcatString file_name; + LongArray dim; + NcVarInfo *vi; + int i; + + // Get the path for the distance to land file + file_name = replace_path(basin_file); + + mlog << Debug(1) + << "Basin definition file: " << file_name << "\n"; + + // Check for no file provided + if(file_name.empty()) return; + + // Open the NetCDF basin definition file + MetNcFile MetNc; + if(!MetNc.open(file_name.c_str())) { + mlog << Error << "\nload_tc_basin() -> " + << "problem reading file \"" << file_name << "\"\n\n"; + exit(1); + } + + // Store the grid + grid = MetNc.grid; + + // Set the dimension to (*,*) + dim.add(vx_data2d_star); + dim.add(vx_data2d_star); + + // Read the basin data + if(!MetNc.data(nc_var_basin, dim, dp, vi)) { + mlog << Error << "\nload_tc_basin() -> " + << "can't read \"" << nc_var_basin + << "\" data from file \"" + << file_name << "\"\n\n"; + exit(1); + } + + // Read the basin abbrevations + if(!get_nc_data_to_array(MetNc.Nc, nc_var_basin_abbr, &abbr)) { + mlog << Error << "\nload_tc_basin() -> " + << "can't read \"" << nc_var_basin_abbr + << "\" abbreviations from file \"" + << file_name << "\"\n\n"; + exit(1); + } + + return; +} + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_nc_util/load_dland.h b/met/src/libcode/vx_nc_util/load_tc_data.h similarity index 75% rename from met/src/libcode/vx_nc_util/load_dland.h rename to met/src/libcode/vx_nc_util/load_tc_data.h index 1a407f1dfe..bb7a6172f0 100644 --- a/met/src/libcode/vx_nc_util/load_dland.h +++ b/met/src/libcode/vx_nc_util/load_tc_data.h @@ -8,8 +8,8 @@ //////////////////////////////////////////////////////////////////////// -#ifndef __LOAD_DLAND_H__ -#define __LOAD_DLAND_H__ +#ifndef __LOAD_TC_DATA_H__ +#define __LOAD_TC_DATA_H__ //////////////////////////////////////////////////////////////////////// @@ -18,12 +18,16 @@ #include "vx_log.h" #include "vx_grid.h" #include "vx_data2d.h" + //////////////////////////////////////////////////////////////////////// -extern void load_dland(const ConcatString &, Grid &, DataPlane &); +extern void load_tc_dland(const ConcatString &, Grid &, DataPlane &); + +extern void load_tc_basin(const ConcatString &, Grid &, DataPlane &, + StringArray &); //////////////////////////////////////////////////////////////////////// -#endif /* __LOAD_DLAND_H__ */ +#endif /* __LOAD_TC_DATA_H__ */ //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_nc_util/vx_nc_util.h b/met/src/libcode/vx_nc_util/vx_nc_util.h index b8d4a9e74a..208cc46b03 100644 --- a/met/src/libcode/vx_nc_util/vx_nc_util.h +++ b/met/src/libcode/vx_nc_util/vx_nc_util.h @@ -23,7 +23,7 @@ #include "nc_utils.h" #include "nc_var_info.h" #include "write_netcdf.h" -#include "load_dland.h" +#include "load_tc_data.h" //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_nc_util/write_netcdf.h b/met/src/libcode/vx_nc_util/write_netcdf.h index dd64f1425c..49fac996e5 100644 --- a/met/src/libcode/vx_nc_util/write_netcdf.h +++ b/met/src/libcode/vx_nc_util/write_netcdf.h @@ -21,13 +21,9 @@ using namespace netCDF; #include "nc_utils.h" #include "observation.h" -//////////////////////////////////////////////////////////////////////// -static const float FILL_VALUE = -9999.f; - -//////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// +static const float FILL_VALUE = -9999.f; //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_regrid/vx_regrid.cc b/met/src/libcode/vx_regrid/vx_regrid.cc index c492c73f9d..188ef51057 100644 --- a/met/src/libcode/vx_regrid/vx_regrid.cc +++ b/met/src/libcode/vx_regrid/vx_regrid.cc @@ -92,6 +92,24 @@ return ( out ); //////////////////////////////////////////////////////////////////////// +DataPlane met_regrid_nearest (const DataPlane & from_data, const Grid & from_grid, const Grid & to_grid) + +{ + +RegridInfo ri; +ri.enable = true; +ri.method = InterpMthd_Nearest; +ri.width = 1; +ri.shape = GridTemplateFactory::GridTemplate_Square; + +return ( met_regrid_generic(from_data, from_grid, to_grid, ri) ); + +} + + +//////////////////////////////////////////////////////////////////////// + + DataPlane met_regrid_generic (const DataPlane & from_data, const Grid & from_grid, const Grid & to_grid, const RegridInfo & info) { diff --git a/met/src/libcode/vx_regrid/vx_regrid.h b/met/src/libcode/vx_regrid/vx_regrid.h index 0946605daf..11f7a65ba7 100644 --- a/met/src/libcode/vx_regrid/vx_regrid.h +++ b/met/src/libcode/vx_regrid/vx_regrid.h @@ -31,7 +31,7 @@ extern DataPlane met_regrid (const DataPlane & in, const Grid & from_grid, const //////////////////////////////////////////////////////////////////////// - +extern DataPlane met_regrid_nearest (const DataPlane & in, const Grid & from_grid, const Grid & to_grid); extern DataPlane met_regrid_generic (const DataPlane & in, const Grid & from_grid, const Grid & to_grid, const RegridInfo & info); extern DataPlane met_regrid_budget (const DataPlane & in, const Grid & from_grid, const Grid & to_grid, const RegridInfo & info); extern DataPlane met_regrid_area_weighted (const DataPlane & in, const Grid & from_grid, const Grid & to_grid, const RegridInfo & info); diff --git a/met/src/libcode/vx_statistics/apply_mask.cc b/met/src/libcode/vx_statistics/apply_mask.cc index f3b3d6b7c3..f5ea562630 100644 --- a/met/src/libcode/vx_statistics/apply_mask.cc +++ b/met/src/libcode/vx_statistics/apply_mask.cc @@ -57,10 +57,6 @@ Grid parse_vx_grid(const RegridInfo info, const Grid *fgrid, const Grid *ogrid) // Otherwise, process regridding logic else { - // Parse info.name as a white-space separated string - StringArray sa; - sa.parse_wsss(info.name); - // Verify on the forecast grid if(info.field == FieldType_Fcst) { mlog << Debug(3) @@ -73,33 +69,10 @@ Grid parse_vx_grid(const RegridInfo info, const Grid *fgrid, const Grid *ogrid) << "Use the observation grid.\n"; vx_grid = *ogrid; } - // Search for a named grid - else if(sa.n() == 1 && find_grid_by_name(info.name.c_str(), vx_grid)) { - mlog << Debug(3) - << "Use the grid named \"" << info.name << "\".\n"; - } - // Parse grid definition - else if(sa.n() > 1 && parse_grid_def(sa, vx_grid)) { - mlog << Debug(3) - << "Use the grid defined by string \"" << info.name << "\".\n"; - } - // Extract the grid from a gridded data file + // Parse a named grid, grid specification string, + // or gridded data file else { - - mlog << Debug(3) - << "Use the grid defined by file \"" << info.name << "\".\n"; - - Met2dDataFileFactory mtddf_factory; - Met2dDataFile *mtddf = (Met2dDataFile *) 0; - - // Attempt to open the data file - if(!(mtddf = mtddf_factory.new_met_2d_data_file(info.name.c_str()))) { - mlog << Error << "\nparse_vx_grid() -> " - << "can't open file \"" << info.name << "\"\n\n"; - exit(1); - } - vx_grid = mtddf->grid(); - delete mtddf; + parse_grid_mask(info.name, vx_grid); } } @@ -209,25 +182,40 @@ void parse_grid_mask(const ConcatString &mask_grid_str, Grid &grid) { // Check for empty input string if(mask_grid_str.empty()) return; - Met2dDataFileFactory factory; - Met2dDataFile * datafile = (Met2dDataFile *) 0; - - // First, try to find the grid by name. - if(!find_grid_by_name(mask_grid_str.c_str(), grid)) { + // Parse mask_grid_str as a white-space separated string + StringArray sa; + sa.parse_wsss(mask_grid_str); + + // Named grid + if(sa.n() == 1 && find_grid_by_name(mask_grid_str.c_str(), grid)) { + mlog << Debug(3) + << "Use the grid named \"" << mask_grid_str << "\".\n"; + } + // Grid specification string + else if(sa.n() > 1 && parse_grid_def(sa, grid)) { + mlog << Debug(3) + << "Use the grid defined by string \"" << mask_grid_str + << "\".\n"; + } + // Extract the grid from a gridded data file + else { + + mlog << Debug(3) + << "Use the grid defined by file \"" + << mask_grid_str << "\".\n"; - // If that doesn't work, try to open a data file. - datafile = factory.new_met_2d_data_file(replace_path(mask_grid_str.c_str()).c_str()); + Met2dDataFileFactory mtddf_factory; + Met2dDataFile *mtddf = (Met2dDataFile *) 0; - if(!datafile) { - mlog << Error << "\nparse_grid_mask() -> " - << "can't open data file \"" << mask_grid_str << "\"\n\n"; - exit(1); + // Attempt to open the data file + if(!(mtddf = mtddf_factory.new_met_2d_data_file( + replace_path(mask_grid_str.c_str()).c_str()))) { + mlog << Error << "\nparse_grid_mask() -> " + << "can't open file \"" << mask_grid_str << "\"\n\n"; + exit(1); } - - // Store the data file's grid - grid = datafile->grid(); - - delete datafile; datafile = (Met2dDataFile *) 0; + grid = mtddf->grid(); + delete mtddf; } return; @@ -281,18 +269,12 @@ void parse_poly_mask(const ConcatString &mask_poly_str, const Grid &grid, // Regrid, if necessary, using nearest neighbor. if(!(mask_grid == grid)) { - RegridInfo ri; - ri.enable = true; - ri.method = InterpMthd_Nearest; - ri.width = 1; - ri.shape = GridTemplateFactory::GridTemplate_Square; - mlog << Debug(2) << "Regridding mask grid to the verification grid using nearest " << "neighbor interpolation:\n" << mask_grid.serialize() << "!=\n" << grid.serialize() << "\n"; - mask_dp = met_regrid(mask_dp, mask_grid, grid, ri); + mask_dp = met_regrid_nearest(mask_dp, mask_grid, grid); } } @@ -346,7 +328,7 @@ void process_poly_mask(const ConcatString &file_name, const Grid &grid, //////////////////////////////////////////////////////////////////////// // -// Parse the poly mask information. If it's a gridded data file, +// Parse the poly mask information. If it's a gridded data file, // return the masking grid, mask plane, and the name of the mask. // If not, return the polyline object. // diff --git a/met/src/libcode/vx_tc_util/Makefile.am b/met/src/libcode/vx_tc_util/Makefile.am index e9d0bd2e00..4ff97d485f 100644 --- a/met/src/libcode/vx_tc_util/Makefile.am +++ b/met/src/libcode/vx_tc_util/Makefile.am @@ -23,6 +23,7 @@ libvx_tc_util_a_SOURCES = \ prob_rirw_info.h prob_rirw_info.cc \ prob_rirw_pair_info.h prob_rirw_pair_info.cc \ genesis_info.cc genesis_info.h \ + pair_data_genesis.cc pair_data_genesis.h \ tc_hdr_columns.cc tc_hdr_columns.h \ tc_columns.cc tc_columns.h \ tc_stat_line.cc tc_stat_line.h \ diff --git a/met/src/libcode/vx_tc_util/genesis_info.cc b/met/src/libcode/vx_tc_util/genesis_info.cc index f32ac50f6e..218d33770b 100644 --- a/met/src/libcode/vx_tc_util/genesis_info.cc +++ b/met/src/libcode/vx_tc_util/genesis_info.cc @@ -20,6 +20,77 @@ using namespace std; #include "nav.h" #include "genesis_info.h" +#include "vx_config.h" + +//////////////////////////////////////////////////////////////////////// +// +// Code for struct GenesisEventInfo +// +//////////////////////////////////////////////////////////////////////// + +void GenesisEventInfo::clear() { + Technique.clear(); + Category.clear(); + VMaxThresh.clear(); + MSLPThresh.clear(); +} + +//////////////////////////////////////////////////////////////////////// + +bool GenesisEventInfo::is_genesis(const TrackPoint &p) const { + bool status = true; + + // Check event category + if(Category.size() > 0 && + std::count(Category.begin(), Category.end(), p.level()) == 0) { + status = false; + } + + // Check VMax threshold + if(VMaxThresh.get_type() != thresh_na && + !VMaxThresh.check(p.v_max())) { + status = false; + } + + // Check MSLP threshold + if(MSLPThresh.get_type() != thresh_na && + !MSLPThresh.check(p.mslp())) { + status = false; + } + + return(status); +} + +//////////////////////////////////////////////////////////////////////// + +GenesisEventInfo parse_conf_genesis_event_info(Dictionary *dict) { + GenesisEventInfo info; + StringArray sa; + int i; + + if(!dict) { + mlog << Error << "\nparse_conf_genesis_event_info() -> " + << "empty dictionary!\n\n"; + exit(1); + } + + // Conf: technique (optional) + info.Technique = dict->lookup_string(conf_key_technique, false); + + // Conf: category (optional) + sa = dict->lookup_string_array(conf_key_category, false); + for(i=0; ilookup_thresh(conf_key_vmax_thresh); + + // Conf: mslp_thresh + info.MSLPThresh = dict->lookup_thresh(conf_key_mslp_thresh); + + return(info); +} //////////////////////////////////////////////////////////////////////// // @@ -28,23 +99,19 @@ using namespace std; //////////////////////////////////////////////////////////////////////// GenesisInfo::GenesisInfo() { - clear(); } //////////////////////////////////////////////////////////////////////// GenesisInfo::~GenesisInfo() { - clear(); } //////////////////////////////////////////////////////////////////////// GenesisInfo::GenesisInfo(const GenesisInfo &g) { - clear(); - assign(g); } @@ -67,15 +134,7 @@ GenesisInfo & GenesisInfo::operator=(const GenesisInfo &g) { //////////////////////////////////////////////////////////////////////// bool GenesisInfo::operator==(const GenesisInfo & g) const { - - return(StormId == g.StormId && - Technique == g.Technique && - TechniqueNumber == g.TechniqueNumber && - GenesisTime == g.GenesisTime && - InitTime == g.InitTime && - LeadTime == g.LeadTime && - is_eq(Lat, g.Lat) && - is_eq(Lon, g.Lon)); + return(StormId == g.StormId && is_storm(g)); } //////////////////////////////////////////////////////////////////////// @@ -86,12 +145,11 @@ bool GenesisInfo::operator==(const GenesisInfo & g) const { //////////////////////////////////////////////////////////////////////// bool GenesisInfo::is_storm(const GenesisInfo & g) const { - return(Technique == g.Technique && TechniqueNumber == g.TechniqueNumber && - GenesisTime == g.GenesisTime && InitTime == g.InitTime && - LeadTime == g.LeadTime && + GenesisTime == g.GenesisTime && + GenesisLead == g.GenesisLead && is_eq(Lat, g.Lat) && is_eq(Lon, g.Lon)); } @@ -100,68 +158,32 @@ bool GenesisInfo::is_storm(const GenesisInfo & g) const { void GenesisInfo::clear() { - IsSet = false; - IsBestTrack = false; - IsOperTrack = false; - IsAnlyTrack = false; - - StormId.clear(); - Basin.clear(); - Cyclone.clear(); - StormName.clear(); - TechniqueNumber = bad_data_int; - Technique.clear(); - Initials.clear(); - - GenesisTime = (unixtime) 0; - InitTime = (unixtime) 0; - LeadTime = bad_data_int; - - Lat = bad_data_double; - Lon = bad_data_double; - DLand = bad_data_double; + TrackInfo::clear(); - NPoints = 0; - MinValidTime = (unixtime) 0; - MaxValidTime = (unixtime) 0; - MinWarmCoreTime = (unixtime) 0; - MaxWarmCoreTime = (unixtime) 0; + GenesisIndex = bad_data_int; + GenesisTime = (unixtime) 0; + GenesisLead = bad_data_int; + Lat = bad_data_double; + Lon = bad_data_double; + DLand = bad_data_double; return; } //////////////////////////////////////////////////////////////////////// -void GenesisInfo::assign(const GenesisInfo &g) { +void GenesisInfo::assign(const GenesisInfo &gi) { clear(); - IsSet = true; - IsBestTrack = g.IsBestTrack; - IsOperTrack = g.IsOperTrack; - IsAnlyTrack = g.IsAnlyTrack; - - StormId = g.StormId; - Basin = g.Basin; - Cyclone = g.Cyclone; - StormName = g.StormName; - TechniqueNumber = g.TechniqueNumber; - Technique = g.Technique; - Initials = g.Initials; - - GenesisTime = g.GenesisTime; - InitTime = g.InitTime; - LeadTime = g.LeadTime; - - Lat = g.Lat; - Lon = g.Lon; - DLand = g.DLand; - - NPoints = g.NPoints; - MinValidTime = g.MinValidTime; - MaxValidTime = g.MaxValidTime; - MinWarmCoreTime = g.MinWarmCoreTime; - MaxWarmCoreTime = g.MaxWarmCoreTime; + TrackInfo::assign(gi); + + GenesisIndex = gi.GenesisIndex; + GenesisTime = gi.GenesisTime; + GenesisLead = gi.GenesisLead; + Lat = gi.Lat; + Lon = gi.Lon; + DLand = gi.DLand; return; } @@ -171,47 +193,21 @@ void GenesisInfo::assign(const GenesisInfo &g) { void GenesisInfo::dump(ostream &out, int indent_depth) const { Indent prefix(indent_depth); - out << prefix << "IsSet = " << bool_to_string(IsSet) << "\n"; - out << prefix << "IsBest = " << bool_to_string(IsBestTrack) << "\n"; - out << prefix << "IsOper = " << bool_to_string(IsOperTrack) << "\n"; - out << prefix << "IsAnly = " << bool_to_string(IsAnlyTrack) << "\n"; - out << prefix << "StormId = \"" << StormId.contents() << "\"\n"; - out << prefix << "Basin = \"" << Basin.contents() << "\"\n"; - out << prefix << "Cyclone = \"" << Cyclone.contents() << "\"\n"; - out << prefix << "StormName = \"" << StormName.contents() << "\"\n"; - out << prefix << "TechniqueNumber = " << TechniqueNumber << "\n"; - out << prefix << "Technique = \"" << Technique.contents() << "\"\n"; - out << prefix << "Initials = \"" << Initials.contents() << "\"\n"; - out << prefix << "GenesisTime = \"" + out << prefix << "StormId = \""<< StormId << "\"\n"; + out << prefix << "Technique = \""<< Technique << "\"\n"; + out << prefix << "GenesisTime = \"" << (GenesisTime > 0 ? unix_to_yyyymmdd_hhmmss(GenesisTime).text() : na_str) << "\"\n"; - out << prefix << "InitTime = \"" + out << prefix << "InitTime = \"" << (InitTime > 0 ? unix_to_yyyymmdd_hhmmss(InitTime).text() : na_str) << "\"\n"; - out << prefix << "LeadTime = \"" - << sec_to_hhmmss(LeadTime).text() << "\"\n"; - out << prefix << "Lat = " << Lat << "\n"; - out << prefix << "Lon = " << Lon << "\n"; - out << prefix << "DLand = " << DLand << "\n"; - out << prefix << "NPoints = " << NPoints << "\n"; - out << prefix << "MinValidTime = \"" - << (MinValidTime > 0 ? - unix_to_yyyymmdd_hhmmss(MinValidTime).text() : - na_str) << "\"\n"; - out << prefix << "MaxValidTime = \"" - << (MaxValidTime > 0 ? - unix_to_yyyymmdd_hhmmss(MaxValidTime).text() : - na_str) << "\"\n"; - out << prefix << "MinWarmCoreTime = \"" - << (MinWarmCoreTime > 0 ? - unix_to_yyyymmdd_hhmmss(MinWarmCoreTime).text() : - na_str) << "\"\n"; - out << prefix << "MaxWarmCoreTime = \"" - << (MaxWarmCoreTime > 0 ? - unix_to_yyyymmdd_hhmmss(MaxWarmCoreTime).text() : - na_str) << "\"\n"; + out << prefix << "LeadTime = \"" + << sec_to_hhmmss(GenesisLead).text() << "\"\n"; + out << prefix << "Lat = " << Lat << "\n"; + out << prefix << "Lon = " << Lon << "\n"; + out << prefix << "DLand = " << DLand << "\n"; out << flush; @@ -225,34 +221,16 @@ ConcatString GenesisInfo::serialize() const { ConcatString s; s << "GenesisInfo: " - << "IsSet = " << bool_to_string(IsSet) - << ", IsBest = " << bool_to_string(IsBestTrack) - << ", IsOper = " << bool_to_string(IsOperTrack) - << ", IsAnly = " << bool_to_string(IsAnlyTrack) - << ", StormId = \"" << StormId.contents() << "\"" - << ", Basin = \"" << Basin.contents() << "\"" - << ", Cyclone = \"" << Cyclone.contents() << "\"" - << ", StormName = \"" << StormName.contents() << "\"" - << ", TechniqueNumber = " << TechniqueNumber - << ", Technique = \"" << Technique.contents() << "\"" - << ", Initials = \"" << Initials.contents() << "\"" + << "StormId = \"" << StormId << "\"" + << ", Technique = \"" << Technique << "\"" << ", GenesisTime = \"" << (GenesisTime > 0 ? unix_to_yyyymmdd_hhmmss(GenesisTime).text() : na_str) << "\"" << ", InitTime = \"" << (InitTime > 0 ? unix_to_yyyymmdd_hhmmss(InitTime).text() : na_str) << "\"" - << ", LeadTime = \"" << sec_to_hhmmss(LeadTime).text() << "\"" + << ", LeadTime = \"" << sec_to_hhmmss(GenesisLead).text() << "\"" << ", Lat = " << Lat << ", Lon = " << Lon - << ", DLand = " << DLand - << ", NPoints = " << NPoints - << ", MinValidTime = \"" << (MinValidTime > 0 ? - unix_to_yyyymmdd_hhmmss(MinValidTime).text() : na_str) << "\"" - << ", MaxValidTime = \"" << (MaxValidTime > 0 ? - unix_to_yyyymmdd_hhmmss(MaxValidTime).text() : na_str) << "\"" - << ", MinWarmCoreTime = \"" << (MinWarmCoreTime > 0 ? - unix_to_yyyymmdd_hhmmss(MinWarmCoreTime).text() : na_str) << "\"" - << ", MaxWarmCoreTime = \"" << (MaxWarmCoreTime > 0 ? - unix_to_yyyymmdd_hhmmss(MaxWarmCoreTime).text() : na_str) << "\""; + << ", DLand = " << DLand; return(s); @@ -272,16 +250,6 @@ ConcatString GenesisInfo::serialize_r(int n, int indent_depth) const { //////////////////////////////////////////////////////////////////////// -void GenesisInfo::set_storm_id() { - - StormId = define_storm_id(InitTime, MinValidTime, MaxValidTime, - Basin, Cyclone); - - return; -} - -//////////////////////////////////////////////////////////////////////// - void GenesisInfo::set_dland(double d) { DLand = d; return; @@ -289,86 +257,53 @@ void GenesisInfo::set_dland(double d) { //////////////////////////////////////////////////////////////////////// -bool GenesisInfo::set(const TrackInfo &t, int i_point) { - int i; - +bool GenesisInfo::set(const TrackInfo &ti, + const GenesisEventInfo &event_info) { // Initialize clear(); - // Store track type information - IsBestTrack = t.is_best_track(); - IsOperTrack = t.is_oper_track(); - IsAnlyTrack = t.is_anly_track(); - - // Initialize - IsSet = true; - Basin = t.basin(); - Cyclone = t.cyclone(); - StormName = t.storm_name(); - TechniqueNumber = t.technique_number(); - Technique = t.technique(); - Initials = t.initials(); - - // Store genesis time and location. - GenesisTime = t[i_point].valid(); - Lat = t[i_point].lat(); - Lon = t[i_point].lon(); - - // For analysis tracks, keep InitTime = LeadTime = 0. - if(IsAnlyTrack) { - InitTime = (unixtime) 0; - LeadTime = 0; - } - else { - InitTime = t.init(); - LeadTime = t[i_point].lead(); + // Find genesis point + for(int i=0; i MaxValidTime) MaxValidTime = t[i].valid(); + // Store the TrackInfo + TrackInfo::assign(ti); - if(t[i].warm_core()) { - if(MinWarmCoreTime == 0 || t[i].valid() < MinWarmCoreTime) { - MinWarmCoreTime = t[i].valid(); - } - if(MaxWarmCoreTime == 0 || t[i].valid() > MaxWarmCoreTime) { - MaxWarmCoreTime = t[i].valid(); - } - } - } + // Store genesis time and location. + GenesisTime = ti[GenesisIndex].valid(); + Lat = ti[GenesisIndex].lat(); + Lon = ti[GenesisIndex].lon(); - // Create the storm id - set_storm_id(); + // For analysis tracks, set GenesisLead = 0 + if(IsAnlyTrack) GenesisLead = 0; + else GenesisLead = ti[GenesisIndex].lead(); return(true); } //////////////////////////////////////////////////////////////////////// -bool GenesisInfo::is_match(const GenesisInfo &g, - const double rad, const int beg_sec, const int end_sec, - double &dist, int &diff) const { - bool keep = true; - - // Check for a match in time - diff = GenesisTime - g.GenesisTime; - if(diff < beg_sec || diff > end_sec) keep = false; +int GenesisInfo::genesis_fhr() const { + return(is_bad_data(GenesisLead) ? + bad_data_int : + nint((double) GenesisLead/sec_per_hour)); +} - // Store the absolute value of the difference - diff = labs(diff); +//////////////////////////////////////////////////////////////////////// - // Check for a match in space - dist = gc_dist(Lat, Lon, g.Lat, g.Lon); - if(dist > rad) keep = false; +bool GenesisInfo::is_match(const TrackPoint &p, + const double rad) const { - return(keep); + // Check for matching in time and space + return(GenesisTime == p.valid() && + gc_dist(Lat, Lon, p.lat(), p.lon()) <= rad); } //////////////////////////////////////////////////////////////////////// @@ -506,45 +441,26 @@ const GenesisInfo & GenesisInfoArray::operator[](int n) const { //////////////////////////////////////////////////////////////////////// -void GenesisInfoArray::add(const GenesisInfo &g) { - - Genesis.push_back(g); - - return; -} - -//////////////////////////////////////////////////////////////////////// - -bool GenesisInfoArray::add(const TrackInfo &t, int i_point) { - GenesisInfo g; - - // Attempt to create a genesis object - if(!g.set(t, i_point)) return(false); +bool GenesisInfoArray::add(const GenesisInfo &gi) { + int i; - // Ignore true duplicates - if(has(g)) { + // Skip true duplicates + if(has(gi)) { mlog << Warning << "\nGenesisInfoArray::add() -> " - << "Skipping duplicate genesis event:\n" << g.serialize() - << "\n\n"; + << "Skipping duplicate genesis event:\n" + << gi.serialize() << "\n\n"; return(false); } - // Print warning for matches - if(has_storm(g)) { - mlog << Warning << "\nGenesisInfoArray::add() -> " - << "Including multiple genesis events for the same storm:\n" - << g.serialize() << "\n\n"; - } - // Store the genesis object - add(g); + Genesis.push_back(gi); return(true); } //////////////////////////////////////////////////////////////////////// -bool GenesisInfoArray::has(const GenesisInfo &g) { +bool GenesisInfoArray::has(const GenesisInfo &g) const { for(int i=0; i= Genesis.size())) { - mlog << Error - << "\nGenesisInfoArray::set_dland() -> " - << "range check error for index value " << n << "\n\n"; - exit(1); + for(i=0; i Category; + SingleThresh VMaxThresh; + SingleThresh MSLPThresh; + + bool is_genesis(const TrackPoint &) const; + void clear(); +}; + +extern GenesisEventInfo parse_conf_genesis_event_info(Dictionary *dict); + //////////////////////////////////////////////////////////////////////// // // GenesisInfo class stores information about genesis events. // //////////////////////////////////////////////////////////////////////// -class GenesisInfo { +class GenesisInfo : public TrackInfo { private: void assign(const GenesisInfo &); - // Storm and model identification - - bool IsSet; - bool IsBestTrack; - bool IsOperTrack; - bool IsAnlyTrack; + bool IsSet; - ConcatString StormId; - ConcatString Basin; - ConcatString Cyclone; - ConcatString StormName; - int TechniqueNumber; - ConcatString Technique; - ConcatString Initials; + // TrackInfo for this Genesis event + TrackInfo Track; + int GenesisIndex; - // Genesis Timing + // Genesis Information unixtime GenesisTime; - unixtime InitTime; - int LeadTime; - - // Genesis Location + int GenesisLead; double Lat; double Lon; double DLand; - // Track Summary - int NPoints; - unixtime MinValidTime; - unixtime MaxValidTime; - unixtime MinWarmCoreTime; - unixtime MaxWarmCoreTime; - public: GenesisInfo(); ~GenesisInfo(); GenesisInfo(const GenesisInfo &); + GenesisInfo & operator=(const GenesisInfo &); bool operator==(const GenesisInfo &) const; bool is_storm(const GenesisInfo &) const; @@ -86,77 +86,35 @@ class GenesisInfo { // set stuff // - void set_storm_id(); void set_dland(double); - bool set(const TrackInfo &, int); + bool set(const TrackInfo &, const GenesisEventInfo &); // // get stuff // - - bool is_best_track() const; - bool is_oper_track() const; - bool is_anly_track() const; - - const ConcatString & storm_id() const; - const ConcatString & basin() const; - const ConcatString & cyclone() const; - const ConcatString & storm_name() const; - int technique_number() const; - const ConcatString & technique() const; - const ConcatString & initials() const; - double lat() const; - double lon() const; - double dland() const; - unixtime genesis_time() const; - unixtime init() const; - int init_hour() const; - int lead_time() const; - unixtime valid_min() const; - unixtime valid_max() const; - int valid_dur() const; - int n_points() const; - unixtime warm_core_min() const; - unixtime warm_core_max() const; - int warm_core_dur() const; + + double lat() const; + double lon() const; + double dland() const; + unixtime genesis_time() const; + int genesis_lead() const; + int genesis_fhr() const; // // do stuff // - bool is_match(const GenesisInfo &, - const double, const int, const int, - double &, int &) const; + bool is_match(const TrackPoint &, + const double) const; }; //////////////////////////////////////////////////////////////////////// -inline bool GenesisInfo::is_best_track() const { return(IsBestTrack); } -inline bool GenesisInfo::is_oper_track() const { return(IsOperTrack); } -inline bool GenesisInfo::is_anly_track() const { return(IsAnlyTrack); } -inline const ConcatString & GenesisInfo::storm_id() const { return(StormId); } -inline const ConcatString & GenesisInfo::basin() const { return(Basin); } -inline const ConcatString & GenesisInfo::cyclone() const { return(Cyclone); } -inline const ConcatString & GenesisInfo::storm_name() const { return(StormName); } -inline int GenesisInfo::technique_number() const { return(TechniqueNumber); } -inline const ConcatString & GenesisInfo::technique() const { return(Technique); } -inline const ConcatString & GenesisInfo::initials() const { return(Initials); } -inline double GenesisInfo::lat() const { return(Lat); } -inline double GenesisInfo::lon() const { return(Lon); } -inline double GenesisInfo::dland() const { return(DLand); } -inline unixtime GenesisInfo::genesis_time() const { return(GenesisTime); } -inline unixtime GenesisInfo::init() const { return(InitTime); } -inline int GenesisInfo::init_hour() const { return(unix_to_sec_of_day(InitTime)); } -inline int GenesisInfo::lead_time() const { return(LeadTime); } -inline int GenesisInfo::n_points() const { return(NPoints); } -inline unixtime GenesisInfo::valid_min() const { return(MinValidTime); } -inline unixtime GenesisInfo::valid_max() const { return(MaxValidTime); } -inline int GenesisInfo::valid_dur() const { return((MinValidTime == 0 || MaxValidTime == 0 ? - bad_data_int : MaxValidTime - MinValidTime)); } -inline unixtime GenesisInfo::warm_core_min() const { return(MinWarmCoreTime); } -inline unixtime GenesisInfo::warm_core_max() const { return(MaxWarmCoreTime); } -inline int GenesisInfo::warm_core_dur() const { return((MinWarmCoreTime == 0 || MaxWarmCoreTime == 0 ? - bad_data_int : MaxWarmCoreTime - MinWarmCoreTime)); } +inline double GenesisInfo::lat() const { return(Lat); } +inline double GenesisInfo::lon() const { return(Lon); } +inline double GenesisInfo::dland() const { return(DLand); } +inline unixtime GenesisInfo::genesis_time() const { return(GenesisTime); } +inline int GenesisInfo::genesis_lead() const { return(GenesisLead); } //////////////////////////////////////////////////////////////////////// // @@ -190,11 +148,11 @@ class GenesisInfoArray { // set stuff // - void add(const GenesisInfo &); - bool add(const TrackInfo &, int); - bool has(const GenesisInfo &); - bool has_storm(const GenesisInfo &); - void set_dland(int, double); + bool add(const GenesisInfo &); + bool has(const GenesisInfo &) const; + bool has_storm(const GenesisInfo &, int &) const; + bool has_storm_id(const ConcatString &, int &) const; + bool erase_storm_id(const ConcatString &); // // get stuff @@ -204,13 +162,6 @@ class GenesisInfoArray { int n() const; int n_technique() const; - // - // do stuff - // - - int find_match(const GenesisInfo &, - const double, const int, const int) const; - }; //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/pair_data_genesis.cc b/met/src/libcode/vx_tc_util/pair_data_genesis.cc new file mode 100644 index 0000000000..ba1f564b44 --- /dev/null +++ b/met/src/libcode/vx_tc_util/pair_data_genesis.cc @@ -0,0 +1,347 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2020 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + +//////////////////////////////////////////////////////////////////////// + +using namespace std; + +#include +#include +#include +#include +#include +#include + +#include "nav.h" + +#include "pair_data_genesis.h" + +//////////////////////////////////////////////////////////////////////// +// +// Code for enum GenesisPairCategory +// +//////////////////////////////////////////////////////////////////////// + +ConcatString genesispaircategory_to_string(const GenesisPairCategory c) { + const char *s = (const char *) 0; + + switch(c) { + case FYOYGenesis: s = "FYOY"; break; + case FYONGenesis: s = "FYON"; break; + case FNOYGenesis: s = "FNOY"; break; + case DiscardGenesis: s = "DISCARD"; break; + default: s = na_str; break; + } + + return(ConcatString(s)); +} + +//////////////////////////////////////////////////////////////////////// +// +// Code for struct Genesis Pair Differences +// +//////////////////////////////////////////////////////////////////////// + +GenesisPairDiff::GenesisPairDiff() { + + clear(); +} + +//////////////////////////////////////////////////////////////////////// + +void GenesisPairDiff::clear() { + + DevDist = bad_data_double; + DevDSec = bad_data_int; + OpsDSec = bad_data_int; + DevCategory = NoGenesisPairCategory; + OpsCategory = NoGenesisPairCategory; + + return; +} + +//////////////////////////////////////////////////////////////////////// +// +// Code for class PairDataGenesis +// +//////////////////////////////////////////////////////////////////////// + +PairDataGenesis::PairDataGenesis() { + + clear(); +} + +//////////////////////////////////////////////////////////////////////// + +PairDataGenesis::~PairDataGenesis() { + + clear(); +} + +//////////////////////////////////////////////////////////////////////// + +PairDataGenesis::PairDataGenesis(const PairDataGenesis &g) { + + clear(); + + assign(g); +} + +//////////////////////////////////////////////////////////////////////// + +PairDataGenesis & PairDataGenesis::operator=(const PairDataGenesis &g) { + + if(this == &g) return(*this); + + assign(g); + + return(*this); +} + +//////////////////////////////////////////////////////////////////////// + +void PairDataGenesis::clear() { + + Desc.clear(); + Mask.clear(); + Model.clear(); + + NPair = 0; + + BestStormId.clear(); + InitTime.clear(); + LeadTime.clear(); + + FcstGen.clear(); + BestGen.clear(); + GenDiff.clear(); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void PairDataGenesis::assign(const PairDataGenesis &g) { + + clear(); + + Desc = g.Desc; + Mask = g.Mask; + Model = g.Model; + + NPair = g.NPair; + + BestStormId = g.BestStormId; + InitTime = g.InitTime; + LeadTime = g.LeadTime; + + FcstGen = g.FcstGen; + BestGen = g.BestGen; + GenDiff = g.GenDiff; + + return; +} + +//////////////////////////////////////////////////////////////////////// + +const GenesisInfo * PairDataGenesis::fcst_gen(int i) const { + + if(i < 0 || i > NPair) { + mlog << Error << "\nPairBase::fcst_gen() -> " + << "range check error: " << i << " not in (0, " + << NPair << ").\n\n"; + exit(1); + } + + return(FcstGen[i]); +} + +//////////////////////////////////////////////////////////////////////// + +const GenesisInfo * PairDataGenesis::best_gen(int i) const { + + if(i < 0 || i > NPair) { + mlog << Error << "\nPairBase::best_gen() -> " + << "range check error: " << i << " not in (0, " + << NPair << ").\n\n"; + exit(1); + } + + return(BestGen[i]); +} + +//////////////////////////////////////////////////////////////////////// + +const GenesisPairDiff & PairDataGenesis::gen_diff(int i) const { + + if(i < 0 || i > NPair) { + mlog << Error << "\nPairBase::gen_diff() -> " + << "range check error: " << i << " not in (0, " + << NPair << ").\n\n"; + exit(1); + } + + return(GenDiff[i]); +} + +//////////////////////////////////////////////////////////////////////// + +bool PairDataGenesis::has_gen(const vector& gi_list, + const GenesisInfo *gi, int &i) const { + + if(!gi) return(false); + + // Search for a match + for(i=0; iinit()); + LeadTime.add(fgi->genesis_lead()); + FcstGen.push_back(fgi); + BestGen.push_back((GenesisInfo *) 0); + GenDiff.push_back(diff); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void PairDataGenesis::add_best_gen(const GenesisInfo *bgi, + const int fcst_beg, const int fcst_end, const int init_add, + const unixtime init_beg, const unixtime init_end, + const TimeArray &init_inc, const TimeArray &init_exc) { + + if(!bgi) return; + + int i_case; + GenesisPairDiff diff; + unixtime init_ut; + + // Define opportunities to forecast this event + unixtime init_start = bgi->genesis_time() - fcst_end; + unixtime init_stop = bgi->genesis_time() - fcst_beg; + + // Add unmatched pair for each forecast opportunity + for(init_ut=init_start; init_ut<=init_stop; init_ut+=init_add) { + + // Check if this initialization time should be used + if((init_beg > 0 && init_beg > init_ut) || + (init_end > 0 && init_beg < init_ut) || + (init_inc.n() > 0 && !init_inc.has(init_ut)) || + (init_exc.n() > 0 && init_exc.has(init_ut))) + continue; + + // Check if this case already exists + if(!has_case(bgi->storm_id(), init_ut, i_case)) { + + // Add a new unmatched pair + NPair++; + BestStormId.add(bgi->storm_id()); + InitTime.add(init_ut); + LeadTime.add(bgi->genesis_time() - init_ut); + FcstGen.push_back((GenesisInfo *) 0); + BestGen.push_back(bgi); + GenDiff.push_back(diff); + } + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void PairDataGenesis::add_gen_pair(const GenesisInfo *fgi, + const GenesisInfo *bgi) { + + if(!fgi || !bgi) return; + + int i_case; + GenesisPairDiff diff; + + // Update an existing case + if(has_case(bgi->storm_id(), fgi->init(), i_case)) { + FcstGen[i_case] = fgi; + GenDiff[i_case].clear(); + } + // Add a new case + else { + NPair++; + BestStormId.add(bgi->storm_id()); + InitTime.add(fgi->init()); + LeadTime.add(fgi->genesis_lead()); + FcstGen.push_back(fgi); + BestGen.push_back(bgi); + GenDiff.push_back(diff); + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void PairDataGenesis::set_gen_diff(int i, const GenesisPairDiff &diff) { + + if(i < 0 || i > NPair) { + mlog << Error << "\nPairBase::set_gen_diff() -> " + << "range check error: " << i << " not in (0, " + << NPair << ").\n\n"; + exit(1); + } + + GenDiff[i] = diff; + + return; +} + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/pair_data_genesis.h b/met/src/libcode/vx_tc_util/pair_data_genesis.h new file mode 100644 index 0000000000..81843fce09 --- /dev/null +++ b/met/src/libcode/vx_tc_util/pair_data_genesis.h @@ -0,0 +1,156 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2020 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + +//////////////////////////////////////////////////////////////////////// + +#ifndef __PAIR_DATA_GENESIS_H__ +#define __PAIR_DATA_GENESIS_H__ + +//////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "vx_cal.h" +#include "vx_math.h" +#include "vx_util.h" + +#include "genesis_info.h" + +//////////////////////////////////////////////////////////////////////// +// +// Genesis Pair Categories +// +//////////////////////////////////////////////////////////////////////// + +enum GenesisPairCategory { + FYOYGenesis, // Hit + FYONGenesis, // False Alarm + FNOYGenesis, // Miss + DiscardGenesis, // Discard + NoGenesisPairCategory +}; + +extern ConcatString genesispaircategory_to_string(const GenesisPairCategory); + +//////////////////////////////////////////////////////////////////////// +// +// Structure for Genesis Pair Differences +// +//////////////////////////////////////////////////////////////////////// + +struct GenesisPairDiff { + double DevDist; // Distance between genesis events + int DevDSec; // Fcst - Best genesis time + int OpsDSec; // Best genesis - fcst initalization + GenesisPairCategory DevCategory; // Contingency table category + GenesisPairCategory OpsCategory; // Concingency table category + + GenesisPairDiff(); + void clear(); +}; + +//////////////////////////////////////////////////////////////////////// +// +// Class to store matched genesis pairs. +// +//////////////////////////////////////////////////////////////////////// + +class PairDataGenesis { + + private: + + void init_from_scratch(); + void assign(const PairDataGenesis &); + + // Describe this verification task + ConcatString Desc; + ConcatString Mask; + ConcatString Model; + + // Number of pairs + int NPair; + + // Arrays of info for each pair + StringArray BestStormId; + TimeArray InitTime; + IntArray LeadTime; + + vector FcstGen; + vector BestGen; + vector GenDiff; + + ////////////////////////////////////////////////////////////////// + + bool has_gen (const vector&, + const GenesisInfo *, int &) const; + bool has_case(const ConcatString &, const unixtime, + int &) const; + + public: + + PairDataGenesis(); + ~PairDataGenesis(); + PairDataGenesis(const PairDataGenesis &); + PairDataGenesis & operator=(const PairDataGenesis &); + + ////////////////////////////////////////////////////////////////// + + void clear(); + + // Set stuff + void set_desc (const ConcatString &); + void set_mask (const ConcatString &); + void set_model(const ConcatString &); + + + // Get stuff + ConcatString desc() const; + ConcatString mask() const; + ConcatString model() const; + int n_pair() const; + const std::string best_storm_id(int) const; + unixtime init (int) const; + int lead_time (int) const; + const GenesisInfo * fcst_gen (int) const; + const GenesisInfo * best_gen (int) const; + const GenesisPairDiff &gen_diff (int) const; + + // Do stuff + bool has_fcst_gen(const GenesisInfo *, int &) const; + bool has_best_gen(const GenesisInfo *, int &) const; + + void add_fcst_gen(const GenesisInfo *); + void add_best_gen(const GenesisInfo *, + const int, const int, const int, + const unixtime, const unixtime, + const TimeArray &, const TimeArray &); + + void add_gen_pair(const GenesisInfo *, const GenesisInfo *); + void set_gen_diff(int, const GenesisPairDiff &); +}; + +//////////////////////////////////////////////////////////////////////// + +inline void PairDataGenesis::set_desc (const ConcatString &s) { Desc = s; } +inline void PairDataGenesis::set_mask (const ConcatString &s) { Mask = s; } +inline void PairDataGenesis::set_model(const ConcatString &s) { Model = s; } + +inline ConcatString PairDataGenesis::desc() const { return(Desc); } +inline ConcatString PairDataGenesis::mask() const { return(Mask); } +inline ConcatString PairDataGenesis::model() const { return(Model); } +inline int PairDataGenesis::n_pair() const { return(NPair); } +inline const std::string PairDataGenesis::best_storm_id(int i) const { return(BestStormId[i]); } +inline unixtime PairDataGenesis::init(int i) const { return(InitTime[i]); } +inline int PairDataGenesis::lead_time(int i) const { return(LeadTime[i]); } + +//////////////////////////////////////////////////////////////////////// + +#endif /* __PAIR_DATA_GENESIS_H__ */ + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/track_info.cc b/met/src/libcode/vx_tc_util/track_info.cc index 4fbb349150..b7c443c0f3 100644 --- a/met/src/libcode/vx_tc_util/track_info.cc +++ b/met/src/libcode/vx_tc_util/track_info.cc @@ -90,6 +90,8 @@ void TrackInfo::clear() { InitTime = (unixtime) 0; MinValidTime = (unixtime) 0; MaxValidTime = (unixtime) 0; + MinWarmCore = (unixtime) 0; + MaxWarmCore = (unixtime) 0; TrackLines.clear(); clear_points(); @@ -124,9 +126,11 @@ void TrackInfo::dump(ostream &out, int indent_depth) const { out << prefix << "TechniqueNumber = " << TechniqueNumber << "\n"; out << prefix << "Technique = \"" << Technique.contents() << "\"\n"; out << prefix << "Initials = \"" << Initials.contents() << "\"\n"; - out << prefix << "InitTime = \"" << (InitTime > 0 ? unix_to_yyyymmdd_hhmmss(InitTime).text() : na_str) << "\n"; + out << prefix << "InitTime = \"" << (InitTime > 0 ? unix_to_yyyymmdd_hhmmss(InitTime).text() : na_str) << "\n"; out << prefix << "MinValidTime = \"" << (MinValidTime > 0 ? unix_to_yyyymmdd_hhmmss(MinValidTime).text() : na_str) << "\n"; out << prefix << "MaxValidTime = \"" << (MaxValidTime > 0 ? unix_to_yyyymmdd_hhmmss(MaxValidTime).text() : na_str) << "\n"; + out << prefix << "MinWarmCore = \"" << (MinWarmCore > 0 ? unix_to_yyyymmdd_hhmmss(MinWarmCore).text() : na_str) << "\n"; + out << prefix << "MaxWarmCore = \"" << (MaxWarmCore > 0 ? unix_to_yyyymmdd_hhmmss(MaxWarmCore).text() : na_str) << "\n"; out << prefix << "NPoints = " << NPoints << "\n"; out << prefix << "NAlloc = " << NAlloc << "\n"; out << prefix << "NTrackLines = " << TrackLines.n_elements() << "\n"; @@ -162,6 +166,8 @@ ConcatString TrackInfo::serialize() const { << ", InitTime = " << (InitTime > 0 ? unix_to_yyyymmdd_hhmmss(InitTime).text() : na_str) << ", MinValidTime = " << (MinValidTime > 0 ? unix_to_yyyymmdd_hhmmss(MinValidTime).text() : na_str) << ", MaxValidTime = " << (MaxValidTime > 0 ? unix_to_yyyymmdd_hhmmss(MaxValidTime).text() : na_str) + << ", MinWarmCore = " << (MinWarmCore > 0 ? unix_to_yyyymmdd_hhmmss(MinWarmCore).text() : na_str) + << ", MaxWarmCore = " << (MaxWarmCore > 0 ? unix_to_yyyymmdd_hhmmss(MaxWarmCore).text() : na_str) << ", NPoints = " << NPoints << ", NAlloc = " << NAlloc << ", NTrackLines = " << TrackLines.n_elements(); @@ -209,6 +215,8 @@ void TrackInfo::assign(const TrackInfo &t) { InitTime = t.InitTime; MinValidTime = t.MinValidTime; MaxValidTime = t.MaxValidTime; + MinWarmCore = t.MinWarmCore; + MaxWarmCore = t.MaxWarmCore; TrackLines = t.TrackLines; if(t.NPoints == 0) return; @@ -377,6 +385,13 @@ int TrackInfo::duration() const { //////////////////////////////////////////////////////////////////////// +int TrackInfo::warm_core_dur() const { + return(MaxWarmCore == 0 || MinWarmCore == 0 ? bad_data_int : + MaxWarmCore - MinWarmCore); +} + +//////////////////////////////////////////////////////////////////////// + int TrackInfo::valid_inc() const { int i; NumArray ut_inc; @@ -402,6 +417,14 @@ void TrackInfo::add(const TrackPoint &p) { if(MaxValidTime == (unixtime) 0 || p.valid() > MaxValidTime) MaxValidTime = p.valid(); + // Check the warm core time range + if(p.warm_core()) { + if(MinWarmCore == (unixtime) 0 || p.valid() < MinWarmCore) + MinWarmCore = p.valid(); + if(MaxWarmCore == (unixtime) 0 || p.valid() > MaxWarmCore) + MaxWarmCore = p.valid(); + } + return; } @@ -424,8 +447,8 @@ bool TrackInfo::add(const ATCFTrackLine &l, bool check_dup, bool check_anly) { else if(StormName.length() > 0 && name.length() > 0 && StormName != name) { mlog << Debug(4) - << "Updating storm name from \"" << StormName << "\" to \"" - << name << "\" for " << StormId << ".\n"; + << "Updating " << StormId << " storm name from \"" + << StormName << "\" to \"" << name << "\".\n"; StormName = name; } @@ -463,6 +486,14 @@ bool TrackInfo::add(const ATCFTrackLine &l, bool check_dup, bool check_anly) { if(MaxValidTime == (unixtime) 0 || l.valid() > MaxValidTime) MaxValidTime = l.valid(); + // Check the warm core time range + if(l.warm_core()) { + if(MinWarmCore == (unixtime) 0 || l.valid() < MinWarmCore) + MinWarmCore = l.valid(); + if(MaxWarmCore == (unixtime) 0 || l.valid() > MaxWarmCore) + MaxWarmCore = l.valid(); + } + // Store the ATCFTrackLine that was just added if(check_dup) TrackLines.add(l.get_line()); @@ -624,8 +655,6 @@ TrackInfoArray & TrackInfoArray::operator=(const TrackInfoArray & t) { void TrackInfoArray::init_from_scratch() { - Track = (TrackInfo *) 0; - clear(); return; @@ -635,8 +664,7 @@ void TrackInfoArray::init_from_scratch() { void TrackInfoArray::clear() { - if(Track) { delete [] Track; Track = (TrackInfo *) 0; } - NTracks = NAlloc = 0; + Track.clear(); return; } @@ -647,10 +675,9 @@ void TrackInfoArray::dump(ostream &out, int indent_depth) const { Indent prefix(indent_depth); int i; - out << prefix << "NTracks = " << NTracks << "\n"; - out << prefix << "NAlloc = " << NAlloc << "\n"; + out << prefix << "NTracks = " << Track.size() << "\n"; - for(i=0; i= n) return; - - // Compute the allocation size - if(!exact) { - k = n/TrackInfoArrayAllocInc; - if(n%TrackInfoArrayAllocInc) k++; - n = k*TrackInfoArrayAllocInc; - } - - // Allocate a new TrackInfo array of the required length - new_info = new TrackInfo [n]; - - if(!new_info) { - mlog << Error - << "\nvoid TrackInfoArray::extend(int, bool) -> " - << "memory allocation error\n\n"; - exit(1); - } - - // Copy the array contents and delete the old one - if(Track) { - for(j=0; j= NTracks)) { + if((n < 0) || (n >= Track.size())) { mlog << Error << "\nTrackInfoArray::operator[](int) -> " << "range check error for index value " << n << "\n\n"; @@ -769,8 +747,7 @@ const TrackInfo & TrackInfoArray::operator[](int n) const { void TrackInfoArray::add(const TrackInfo &t) { - extend(NTracks + 1, false); - Track[NTracks++] = t; + Track.push_back(t); return; } @@ -780,7 +757,7 @@ void TrackInfoArray::add(const TrackInfo &t) { void TrackInfoArray::set(int n, const TrackInfo &t) { // Check range - if((n < 0) || (n >= NTracks)) { + if((n < 0) || (n >= Track.size())) { mlog << Error << "\nTrackInfoArray::set(int, const TrackInfo &) -> " << "range check error for index value " << n << "\n\n"; @@ -811,7 +788,7 @@ bool TrackInfoArray::add(const ATCFTrackLine &l, bool check_dup, bool check_anly } // Add ATCFTrackLine to an existing track if possible - for(i=NTracks-1; i>=0; i--) { + for(i=Track.size()-1; i>=0; i--) { if(Track[i].is_match(l)) { found = true; status = Track[i].add(l, check_dup, check_anly); @@ -821,8 +798,9 @@ bool TrackInfoArray::add(const ATCFTrackLine &l, bool check_dup, bool check_anly // Otherwise, create a new track if(!found) { - extend(NTracks + 1, false); - status = Track[NTracks++].add(l, check_dup, check_anly); + TrackInfo t; + t.add(l, check_dup, check_anly); + Track.push_back(t); } return(status); @@ -835,7 +813,7 @@ bool TrackInfoArray::has(const ATCFTrackLine &l) const { int i; // Check if the TrackInfo data matches - for(i=NTracks-1; i>=0; i--) { + for(i=Track.size()-1; i>=0; i--) { if(Track[i].has(l)) { found = true; break; @@ -846,6 +824,24 @@ bool TrackInfoArray::has(const ATCFTrackLine &l) const { return(found); } +//////////////////////////////////////////////////////////////////////// + +bool TrackInfoArray::erase_storm_id(const ConcatString &s) { + bool status = false; + int i; + + // Erase all tracks with this storm id + for(i=0; i " << "cannot compute a consensus for zero tracks!\n\n"; @@ -885,7 +881,7 @@ TrackInfo consensus(const TrackInfoArray &tracks, tavg.set_storm_id(); // Loop through the tracks and build a list of lead times - for(i=0; i Track; public: @@ -221,29 +225,24 @@ class TrackInfoArray { // set stuff // + void add(const TrackInfo &); + void set(int, const TrackInfo &); + bool add(const ATCFTrackLine &, bool check_dup = false, bool check_anly = false); + bool has(const ATCFTrackLine &) const; + bool erase_storm_id(const ConcatString &); + // // get stuff // const TrackInfo & operator[](int) const; - int n_tracks() const; int n() const; - // - // do stuff - // - - void add(const TrackInfo &); - void set(int, const TrackInfo &); - bool add(const ATCFTrackLine &, bool check_dup = false, bool check_anly = false); - bool has(const ATCFTrackLine &) const; - }; //////////////////////////////////////////////////////////////////////// -inline int TrackInfoArray::n_tracks() const { return(NTracks); } -inline int TrackInfoArray::n() const { return(NTracks); } +inline int TrackInfoArray::n() const { return(Track.size()); } //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/vx_tc_util.h b/met/src/libcode/vx_tc_util/vx_tc_util.h index 1d84408d57..2b3d74d0c9 100644 --- a/met/src/libcode/vx_tc_util/vx_tc_util.h +++ b/met/src/libcode/vx_tc_util/vx_tc_util.h @@ -18,6 +18,7 @@ #include "track_info.h" #include "track_pair_info.h" #include "atcf_prob_line.h" +#include "pair_data_genesis.h" #include "prob_info_base.h" #include "prob_info_array.h" #include "prob_rirw_info.h" diff --git a/met/src/tools/core/grid_stat/grid_stat.cc b/met/src/tools/core/grid_stat/grid_stat.cc index 31c0a042b7..46e579f765 100644 --- a/met/src/tools/core/grid_stat/grid_stat.cc +++ b/met/src/tools/core/grid_stat/grid_stat.cc @@ -328,8 +328,8 @@ void setup_first_pass(const DataPlane &dp) { setup_txt_files(dp.valid(), dp.lead()); } - // If requested, create a NetCDF file to store the matched pairs and - // difference fields for each GRIB code and masking region + // If requested, create a NetCDF file to store the gridded + // verification data if(conf_info.get_output_nc_flag()) { setup_nc_file(conf_info.nc_info, dp.valid(), dp.lead()); } diff --git a/met/src/tools/tc_utils/rmw_analysis/rmw_analysis.cc b/met/src/tools/tc_utils/rmw_analysis/rmw_analysis.cc index 6fd30e62da..084ea2edeb 100644 --- a/met/src/tools/tc_utils/rmw_analysis/rmw_analysis.cc +++ b/met/src/tools/tc_utils/rmw_analysis/rmw_analysis.cc @@ -345,7 +345,7 @@ void process_files() { // Filter tracks filter_tracks(adeck_tracks); - if (adeck_tracks.n_tracks() > 0) { + if (adeck_tracks.n() > 0) { for(int i_var = 0; i_var < data_names.size(); i_var++) { NcVar var = get_nc_var(nc_in, data_names[i_var].c_str()); @@ -620,7 +620,7 @@ void process_track_file(const ConcatString& filename, // Dump out the track information mlog << Debug(3) - << "Identified " << tracks.n_tracks() << " track(s).\n"; + << "Identified " << tracks.n() << " track(s).\n"; remove(adeck_source.c_str()); @@ -673,7 +673,7 @@ void filter_tracks(TrackInfoArray& tracks) { tracks.clear(); // Loop over tracks - for(int i = 0; i < t.n_tracks(); i++) { + for(int i = 0; i < t.n(); i++) { // Valid time window if((conf_info.ValidBeg > 0 && conf_info.ValidBeg > t[i].valid_min()) || 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 f50937b8bd..4b5acaf56e 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen.cc +++ b/met/src/tools/tc_utils/tc_gen/tc_gen.cc @@ -15,7 +15,10 @@ // Mod# Date Name Description // ---- ---- ---- ----------- // 000 05/17/19 Halley Gotway New -// 001 10/21/21 Halley Gotway Fix for MET #1465 +// 001 10/21/20 Halley Gotway Fix for MET #1465 +// 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 // //////////////////////////////////////////////////////////////////////// @@ -36,15 +39,18 @@ using namespace std; #include #include "tc_gen.h" +#include "pair_data_genesis.h" #include "vx_data2d_factory.h" #include "vx_statistics.h" #include "vx_tc_util.h" +#include "vx_nc_util.h" #include "vx_grid.h" #include "vx_util.h" #include "vx_log.h" #include "met_file.h" +#include "nav.h" //////////////////////////////////////////////////////////////////////// @@ -54,18 +60,44 @@ static void get_atcf_files (const StringArray &, const StringArray &, const char *, StringArray &, StringArray &); -static void process_track_files (const StringArray &, +static void process_fcst_tracks (const StringArray &, const StringArray &, - GenesisInfoArray &, bool); -static void process_genesis_pair (int, const ConcatString &, - const GenesisInfoArray &, + GenesisInfoArray &); +static void process_best_tracks (const StringArray &, + const StringArray &, + GenesisInfoArray &, + TrackInfoArray &); + +static void get_genesis_pairs (const TCGenVxOpt &, + const ConcatString &, const GenesisInfoArray &, const GenesisInfoArray &, + const TrackInfoArray &, + PairDataGenesis &); + +static void do_genesis_ctc (const TCGenVxOpt &, + PairDataGenesis &, GenCTCInfo &); -static void setup_txt_files (int); +static int find_genesis_match (const GenesisInfo &, + const GenesisInfoArray &, + const TrackInfoArray &, + double); + +static void setup_txt_files (int, int); static void setup_table (AsciiTable &); -static void write_cts (int, GenCTCInfo &); +static void setup_nc_file (); + +static void write_stats (const PairDataGenesis &, + GenCTCInfo &); +static void write_genmpr_row (StatHdrColumns &, + const PairDataGenesis &, + STATOutputType, + AsciiTable &, int &, + AsciiTable &, int &); +static void write_genmpr_cols (const PairDataGenesis &, int, + AsciiTable &, int, int); +static void write_nc (GenCTCInfo &); static void finish_txt_files (); static void usage (); @@ -126,9 +158,9 @@ void process_command_line(int argc, char **argv) { } // Check for the minimum number of arguments - if(genesis_source.n() == 0 || - track_source.n() == 0 || - config_file.length() == 0) { + if(genesis_source.n() == 0 || + track_source.n() == 0 || + config_file.length() == 0) { mlog << Error << "\nprocess_command_line(int argc, char **argv) -> " << "the \"-genesis\", \"-track\", and \"-config\" command " @@ -173,12 +205,13 @@ void process_genesis() { int i, j; StringArray genesis_files, genesis_files_model_suffix; StringArray track_files, track_files_model_suffix; - GenesisInfoArray fcst_ga, anly_ga, cur_ga, best_ga, oper_ga; - map fcst_ga_map; + GenesisInfoArray fcst_ga, best_ga, empty_ga; + TrackInfoArray oper_ta; + ConcatString model, cs; + map model_ga_map; map::iterator it; - ConcatString atcf_id, cs; - - GenCTCInfo cur_info; + PairDataGenesis pairs; + GenCTCInfo ctc_info; // Get the list of genesis track files get_atcf_files(genesis_source, genesis_model_suffix, atcf_gen_reg_exp, @@ -187,8 +220,8 @@ void process_genesis() { mlog << Debug(2) << "Processing " << genesis_files.n() << " forecast genesis track files.\n"; - process_track_files(genesis_files, genesis_files_model_suffix, - fcst_ga, false); + process_fcst_tracks(genesis_files, genesis_files_model_suffix, + fcst_ga); // Get the list of verifing track files get_atcf_files(track_source, track_model_suffix, atcf_reg_exp, @@ -197,77 +230,82 @@ void process_genesis() { mlog << Debug(2) << "Processing " << track_files.n() << " verifying track files.\n"; - process_track_files(track_files, track_files_model_suffix, - anly_ga, true); + process_best_tracks(track_files, track_files_model_suffix, + best_ga, oper_ta); // Setup output files based on the number of techniques present - setup_txt_files(fcst_ga.n_technique()); + // and possible pairs. + int n_time = (conf_info.FcstSecEnd - conf_info.FcstSecBeg) / + (conf_info.InitFreqHr*sec_per_hour) + 1; + int n_pair = best_ga.n() * n_time + fcst_ga.n(); + setup_txt_files(fcst_ga.n_technique(), n_pair); + + // If requested, setup the NetCDF output file + if(!conf_info.NcInfo.all_false()) setup_nc_file(); // Process each verification filter for(i=0; i 0 && + !conf_info.VxOpt[i].Model.has(model)) continue; - // Add a new map entry, if necessary - if(fcst_ga_map.count(atcf_id) == 0) { - cur_ga.clear(); - fcst_ga_map[atcf_id] = cur_ga; - } - - // Store the current object - fcst_ga_map[atcf_id].add(fcst_ga[j]); - } + // Add a new map entry, if necessary + if(model_ga_map.count(model) == 0) { + empty_ga.clear(); + model_ga_map[model] = empty_ga; } - } // end j - // Subset the BEST and operational genesis events - best_ga.clear(); - oper_ga.clear(); - for(j=0; jfirst; + ctc_info.set_vx_opt(&conf_info.VxOpt[i], + &conf_info.NcOutGrid); - // Loop through and process the genesis event pairs - for(j=0,it=fcst_ga_map.begin(); it!=fcst_ga_map.end(); it++,j++) { mlog << Debug(2) << "[Filter " << i+1 << " (" << conf_info.VxOpt[i].Desc << ") " << ": Model " << j+1 << "] " << "For " << it->first << " model, comparing " << it->second.n() << " genesis forecasts to " << best_ga.n() << " " << conf_info.BestEventInfo.Technique << " and " - << oper_ga.n() << " " << conf_info.OperEventInfo.Technique - << " genesis events.\n"; - process_genesis_pair(i, it->first, it->second, - best_ga, oper_ga, cur_info); + << oper_ta.n() << " " << conf_info.OperTechnique + << " tracks.\n"; + + // Get the pairs + get_genesis_pairs(conf_info.VxOpt[i], it->first, it->second, + best_ga, oper_ta, pairs); + + // Do the categorical verification + do_genesis_ctc(conf_info.VxOpt[i], pairs, ctc_info); - // Write output for the current model - cur_info.model = it->first; - write_cts(i, cur_info); + // Write the statistics output + write_stats(pairs, ctc_info); + // Write NetCDF output fields + if(!conf_info.VxOpt[i].NcInfo.all_false()) { + write_nc(ctc_info); + } + } // end for j } // end for i n_vx @@ -275,164 +313,347 @@ void process_genesis() { // Finish output files finish_txt_files(); + // Close the NetCDF output file + if(nc_out) { + + // List the NetCDF file after it is finished + mlog << Debug(1) << "Output file: " << out_nc_file << "\n"; + + delete nc_out; + nc_out = (NcFile *) 0; + } + + return; } //////////////////////////////////////////////////////////////////////// // -// For each genesis forecast, find the matching BEST tracks genesis -// event that is the closest in space and time, where: -// (1) The difference between the BEST and forecast track genesis falls -// within the configurable genesis_window. -// (2) The distance between the BEST and forecast genesis locations is -// less than the configurable genesis_radius. -// (3) For multiple BEST track matches, choose the one closest in space. -// -// If there are no BEST track matches, apply to same logic with the -// operational baseline genesis events. -// -// Any genesis forecast with a BEST or operational match is a HIT. -// -// Any genesis forecast with no BEST or operational match is a FALSE -// ALARM. -// -// For each BEST track genesis event, define the timing information for -// the model's opportunities to forecast that genesis event using the -// configurable init_freq and lead_window options. Any case for which -// no match exists is a MISS. +// Match the forecast and BEST track genesis events: +// (1) Subset the BEST genesis events based on the current filters. +// (2) For each BEST genesis event, add an unmatched PairDataGenesis +// entry for each model opportunity to forecast that genesis event. +// (3) Loop over the forecast genesis events. For each, search for a +// BEST track point valid at that time and within the search radius. +// (4) If found, store the matching BEST track storm id. +// (5) If not found, search the CARQ points for a matching storm id. +// (6) If a storm id was found, update the existing PairDataGenesis +// entry for that storm id with the forecast genesis event. +// (7) If no match was found, add an unmatched PairDataGenesis +// entry for that forecast genesis event. // //////////////////////////////////////////////////////////////////////// -void process_genesis_pair(int i_vx, const ConcatString &model, - const GenesisInfoArray &fga, - const GenesisInfoArray &bga, - const GenesisInfoArray &oga, - GenCTCInfo &info) { - int i, j, i_match; - unixtime ut, init_beg, init_end; - map info_map; - vector fga_match; - const GenesisInfo *g_ptr; - bool match; +void get_genesis_pairs(const TCGenVxOpt &vx_opt, + const ConcatString &model, + const GenesisInfoArray &fga, + const GenesisInfoArray &bga, + const TrackInfoArray &ota, + PairDataGenesis &gpd) { + int i, i_bga; // Initialize - info.clear(); + gpd.clear(); + gpd.set_desc(vx_opt.Desc); + gpd.set_mask(vx_opt.VxMaskName); + gpd.set_model(model); + + // Filter the BEST genesis events and define model opportunities + for(i=0; itechnique() << " " - << unix_to_yyyymmdd_hhmmss(g_ptr->genesis_time()) - << " genesis at (" << g_ptr->lat() << ", " - << g_ptr->lon() << ").\n"; - - // Increment the HIT count - info.cts_info.cts.inc_fy_oy(); + diff.clear(); + + case_cs << cs_erase + << gpd.model() << " " + << unix_to_yyyymmdd_hhmmss(gpd.init(i)) + << " initialization, " + << gpd.lead_time(i)/sec_per_hour << " lead"; + + // Count the genesis and track points + if(fgi) gci.add_fcst_gen(*fgi); + if(bgi) gci.add_best_gen(*bgi); + + // Unmatched forecast genesis (FALSE ALARM) + if(fgi && !bgi) { + + mlog << Debug(4) << case_cs << ", " + << unix_to_yyyymmdd_hhmmss(fgi->genesis_time()) + << " forecast genesis at (" + << fgi->lat() << ", " << fgi->lon() + << ") is a dev and ops FALSE ALARM.\n"; + + // FALSE ALARM for both methods + diff.DevCategory = FYONGenesis; + diff.OpsCategory = FYONGenesis; + } + + // Unmatched BEST genesis (MISS) + else if(!fgi && bgi) { + + mlog << Debug(4) << case_cs << ", " + << unix_to_yyyymmdd_hhmmss(bgi->genesis_time()) + << " BEST track " << bgi->storm_id() << " genesis at (" + << bgi->lat() << ", " << bgi->lon() + << ") is a dev and ops MISS.\n"; + + // MISS for both methods + diff.DevCategory = FNOYGenesis; + diff.OpsCategory = FNOYGenesis; } + // Matched genesis pairs (DISCARD, HIT, or FALSE ALARM) else { - mlog << Debug(4) << fga[i].technique() << " " - << unix_to_yyyymmdd_hhmmss(fga[i].init()) - << " initialization, " - << fga[i].lead_time()/sec_per_hour << " lead, " - << unix_to_yyyymmdd_hhmmss(fga[i].genesis_time()) - << " genesis at (" << fga[i].lat() << ", " - << fga[i].lon() << ") is a FALSE ALARM.\n"; - // Increment the FALSE ALARM count - info.cts_info.cts.inc_fy_on(); + case_cs << ", " << unix_to_yyyymmdd_hhmmss(bgi->genesis_time()) + << " BEST track " << bgi->storm_id() << " genesis at (" + << bgi->lat() << ", " << bgi->lon() << ") and " + << unix_to_yyyymmdd_hhmmss(fgi->genesis_time()) + << " forecast hour " << fgi->genesis_fhr() + << " genesis at (" << fgi->lat() << ", " << fgi->lon() << ")"; + + // Discard if the forecast init >= BEST genesis + if(vx_opt.DiscardFlag && + fgi->init() >= bgi->genesis_time()) { + mlog << Debug(4) << "DISCARD " << case_cs + << " since the model initialization time is at or " + << "after the matching BEST track " + << unix_to_yyyymmdd_hhmmss(bgi->genesis_time()) + << " genesis time.\n"; + + // DISCARD for both methods + diff.DevCategory = DiscardGenesis; + diff.OpsCategory = DiscardGenesis; + } + // Check for a HIT + else { + + // Compute time and space offsets + diff.DevDSec = fgi->genesis_time() - bgi->genesis_time(); + diff.DevDist = gc_dist(bgi->lat(), bgi->lon(), + fgi->lat(), fgi->lon()); + + ConcatString offset_cs; + offset_cs << "with a genesis time offset of " << diff.DevDSec/sec_per_hour + << " hours and location offset of " << diff.DevDist << " km.\n"; + + // Dev Method: + // HIT if forecast genesis time and location + // are within the temporal and spatial windows. + if(diff.DevDSec >= vx_opt.DevHitBeg && + diff.DevDSec <= vx_opt.DevHitEnd && + diff.DevDist <= vx_opt.DevHitRadius) { + + mlog << Debug(4) << case_cs + << " is a dev method HIT " << offset_cs; + + // HIT for the development method + diff.DevCategory = FYOYGenesis; + } + else { + mlog << Debug(4) << case_cs + << " is a dev method FALSE ALARM " << offset_cs; + + // FALSE ALARM for the development method + diff.DevCategory = FYONGenesis; + } + + // Ops Method: + // HIT if forecast init time is close enough to + // the BEST genesis time. + + // Compute time offset + diff.OpsDSec = bgi->genesis_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) { + + mlog << Debug(4) << case_cs + << " is an ops method HIT " << offset_cs; + + // HIT for the operational method + diff.OpsCategory = FYOYGenesis; + } + else { + mlog << Debug(4) << case_cs + << " is an ops method FALSE ALARM " << offset_cs; + + // FALSE ALARM for the operational method + diff.OpsCategory = FYONGenesis; + } + } } - } // end for i fga - // Loop over the BEST track genesis events to find MISSES - for(i=0; i " + mlog << Error << "\nprocess_fcst_tracks() -> " << "unable to open file \"" << files[i] << "\"\n\n"; exit(1); } // Initialize - tracks.clear(); + fcst_ta.clear(); n_lines = 0; // Set metadata pointer suffix = model_suffix[i]; line.set_tech_suffix(&suffix); - // Read each input line. Track lines may be interleaved - // within a file but cannot span multiple files. + // Process the input track lines while(f >> line) { - // Increment the line counts + // Skip off-hour track points + if((line.valid_hour() % valid_freq_sec) != 0) continue; + n_lines++; - - // Only process the specified BEST and operational track - if(is_anly && !line.is_best_track() && !line.is_oper_track()) { - continue; - } - - // Add the current line checking for analysis tracks - tracks.add(line, false, true); + fcst_ta.add(line); } // Increment counts tot_lines += n_lines; - tot_tracks += tracks.n(); + tot_tracks += fcst_ta.n(); // Close the current file f.close(); // Search the tracks for genesis events - for(j=0; jis_keeper(tracks[j][k])) { - keep = true; - break; - } - } // end for k - - if(!keep) continue; - - // Check the model genesis event criteria - if(!is_anly) { - - // Check the lead time window - if(tracks[j][k].lead() < conf_info.LeadSecBeg || - tracks[j][k].lead() > conf_info.LeadSecEnd) { - mlog << Debug(6) << "Skipping genesis event for lead time " - << tracks[j][k].lead()/sec_per_hour << ".\n"; - continue; - } + // Check the forecast lead time window + if(fcst_gi.genesis_lead() < conf_info.FcstSecBeg || + fcst_gi.genesis_lead() > conf_info.FcstSecEnd) { + mlog << Debug(6) << "Skipping genesis event for forecast hour " + << fcst_gi.genesis_fhr() << ".\n"; + continue; + } - // Check the minimum duration - if(tracks[j].duration() < conf_info.MinDur*sec_per_hour) { - mlog << Debug(6) << "Skipping genesis event for track duration of " - << tracks[j].duration()/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"; + continue; } + // Compute the distance to land + fcst_gi.set_dland(conf_info.compute_dland( + fcst_gi.lat(), -1.0*fcst_gi.lon())); + // Store the genesis event - genesis.add(tracks[j], k); + fcst_ga.add(fcst_gi); } // end for j // Dump out the current number of lines - mlog << Debug(4) - << "[File " << i+1 << " of " << files.n() << "] Found " - << genesis.n() - n_genesis << " " << atcf_ids - << " genesis events, from " << tracks.n() - << " tracks, from " << n_lines - << " input lines, from file \"" << files[i] << "\".\n"; - n_genesis = genesis.n(); + mlog << Debug(4) << "[File " << i+1 << " of " << files.n() + << "] Found " << fcst_ga.n() - n_genesis + << " forecast genesis events, from " << fcst_ta.n() + << " tracks, from " << n_lines << " input lines, from file \"" + << files[i] << "\".\n"; + n_genesis = fcst_ga.n(); } // end for i // Dump out the total number of lines - mlog << Debug(3) - << "Found a total of " << genesis.n() << " " << atcf_ids - << " genesis events, from " << tot_tracks << " tracks, from " - << tot_lines << " input lines, from " << files.n() - << " input files.\n"; + mlog << Debug(3) << "Found a total of " << fcst_ga.n() + << " forecast genesis events, from " << tot_tracks + << " tracks, from " << tot_lines << " input lines, from " + << files.n() << " input files.\n"; - // Compute the distance to land - for(i=0; i= 6) { - mlog << Debug(6) - << genesis.serialize_r() << "\n"; + if(mlog.verbosity_level() > 6) { + mlog << Debug(6) << fcst_ga.serialize_r() << "\n"; } // Dump out track info else { - for(i=0; i " + << "unable to open file \"" << files[i] << "\"\n\n"; + exit(1); } + + // Set metadata pointer + suffix = model_suffix[i]; + line.set_tech_suffix(&suffix); + + // Process the input track lines + while(f >> line) { + + // Skip off-hour track points + if((line.valid_hour() % valid_freq_sec) != 0) continue; + + // Increment the line counter + n_lines++; + + // Store all BEST track lines + if(line.is_best_track()) { + best_ta.add(line, false, true); + } + // Store only 0-hour operational track lines + else if(line.is_oper_track() && line.lead() == 0) { + oper_ta.add(line); + } + } + + // Close the current file + f.close(); + + } // end for i + + // Dump out the total number of lines + mlog << Debug(3) + << "Found a total of " << best_ta.n() << " " + << conf_info.BestEventInfo.Technique + << " tracks and " << oper_ta.n() << " " + << conf_info.OperTechnique + << " operational tracks, from " << n_lines + << " track lines, from " << files.n() + << " input files.\n"; + + // Dump out very verbose output + if(mlog.verbosity_level() >= 6) { + mlog << Debug(6) << "BEST tracks:\n" + << best_ta.serialize_r() << "\n" + << "Operational tracks:\n" + << oper_ta.serialize_r() << "\n"; } + // Search the BEST tracks for genesis events + for(i=0; i " + << case_cs << "neither " << best_ga[i_bga].storm_id() + << " nor " << best_gi.storm_id() + << " matches the basin!\n\n"; + continue; + } + } + + // Compute the distance to land + best_gi.set_dland(conf_info.compute_dland( + best_gi.lat(), -1.0*best_gi.lon())); + + // Store the genesis event + best_ga.add(best_gi); + + } // end for i + + // Dump out the number of genesis events + mlog << Debug(2) << "Found " << best_ga.n() + << " BEST genesis events.\n"; + return; } @@ -644,9 +965,12 @@ void process_track_files(const StringArray &files, // //////////////////////////////////////////////////////////////////////// -void setup_txt_files(int n_model) { +void setup_txt_files(int n_model, int n_pair) { int i, n_rows, n_cols; + // Check to see if the text files have already been set up + if(stat_at.nrows() > 0 || stat_at.ncols() > 0) return; + // Initialize file stream stat_out = (ofstream *) 0; @@ -657,7 +981,10 @@ void setup_txt_files(int n_model) { open_txt_file(stat_out, stat_file.c_str()); // Setup the STAT AsciiTable - n_rows = 1 + 3 * n_model * conf_info.n_vx(); + n_rows = 1 + 6 * n_model * conf_info.n_vx(); + if(conf_info.OutputMap[stat_genmpr] != STATOutputType_None) { + n_rows += n_model * conf_info.n_vx() * n_pair; + } n_cols = 1 + n_header_columns + n_cts_columns; stat_at.set_size(n_rows, n_cols); setup_table(stat_at); @@ -685,7 +1012,12 @@ void setup_txt_files(int n_model) { open_txt_file(txt_out[i], txt_file[i].c_str()); // Setup the text AsciiTable - n_rows = 1 + n_model * conf_info.n_vx(); + if(txt_file_type[i] == stat_genmpr) { + n_rows = 1 + n_model * conf_info.n_vx() * n_pair; + } + else { + n_rows = 1 + 2 * n_model * conf_info.n_vx(); + } n_cols = 1 + n_header_columns + n_txt_columns[i]; txt_at[i].set_size(n_rows, n_cols); setup_table(txt_at[i]); @@ -726,63 +1058,415 @@ void setup_table(AsciiTable &at) { //////////////////////////////////////////////////////////////////////// -void write_cts(int i_vx, GenCTCInfo &info) { - ConcatString var_name("GENESIS"); +void setup_nc_file() { - // Setup header columns - shc.set_model(info.model.c_str()); - shc.set_desc(conf_info.VxOpt[i_vx].Desc.c_str()); - if(conf_info.VxOpt[i_vx].Lead.n() == 1) { - shc.set_fcst_lead_sec(conf_info.VxOpt[i_vx].Lead[0]); + // Build the output NetCDF file name + out_nc_file << cs_erase << out_base << "_pairs.nc"; + + // Create a new NetCDF file and open it + nc_out = open_ncfile(out_nc_file.c_str(), true); + + if(IS_INVALID_NC_P(nc_out)) { + mlog << Error << "\nsetup_nc_file() -> " + << "trouble opening output NetCDF file " + << out_nc_file << "\n\n"; + exit(1); } - shc.set_fcst_valid_beg(conf_info.VxOpt[i_vx].ValidBeg != 0 ? - conf_info.VxOpt[i_vx].ValidBeg : info.fbeg); - shc.set_fcst_valid_end(conf_info.VxOpt[i_vx].ValidEnd != 0 ? - conf_info.VxOpt[i_vx].ValidEnd : info.fend); - shc.set_obs_valid_beg(conf_info.VxOpt[i_vx].ValidBeg != 0 ? - conf_info.VxOpt[i_vx].ValidBeg : info.obeg); - shc.set_obs_valid_end(conf_info.VxOpt[i_vx].ValidEnd != 0 ? - conf_info.VxOpt[i_vx].ValidEnd : info.oend); - shc.set_fcst_var(var_name); - shc.set_obs_var(var_name); - shc.set_obtype(conf_info.BestEventInfo.Technique.c_str()); - if(!conf_info.VxOpt[i_vx].VxMaskName.empty()) { - shc.set_mask(conf_info.VxOpt[i_vx].VxMaskName.c_str()); + + // Add global attributes + write_netcdf_global(nc_out, out_nc_file.c_str(), program_name); + + // Add the projection information + Grid grid = conf_info.NcOutGrid; + write_netcdf_proj(nc_out, grid); + + // Define Dimensions + lat_dim = add_dim(nc_out, "lat", (long) grid.ny()); + lon_dim = add_dim(nc_out, "lon", (long) grid.nx()); + + // Add the lat/lon variables + if(conf_info.NcInfo.do_latlon) { + write_netcdf_latlon(nc_out, &lat_dim, &lon_dim, grid); } + return; +} + +//////////////////////////////////////////////////////////////////////// + +void write_stats(const PairDataGenesis &gpd, + GenCTCInfo &gci) { + + // Setup header columns + shc.set_model(gci.Model.c_str()); + shc.set_desc(gci.VxOpt->Desc.c_str()); + shc.set_fcst_lead_sec(gci.VxOpt->Lead.n() == 1 ? + gci.VxOpt->Lead[0] : bad_data_int); + shc.set_fcst_valid_beg(gci.VxOpt->ValidBeg != 0 ? + gci.VxOpt->ValidBeg : gci.FcstBeg); + shc.set_fcst_valid_end(gci.VxOpt->ValidEnd != 0 ? + gci.VxOpt->ValidEnd : gci.FcstEnd); + shc.set_obs_lead_sec(bad_data_int); + shc.set_obs_valid_beg(gci.VxOpt->ValidBeg != 0 ? + gci.VxOpt->ValidBeg : gci.BestBeg); + shc.set_obs_valid_end(gci.VxOpt->ValidEnd != 0 ? + gci.VxOpt->ValidEnd : gci.BestEnd); + shc.set_obtype(conf_info.BestEventInfo.Technique.c_str()); + shc.set_mask(gci.VxOpt->VxMaskName.empty() ? + na_str : gci.VxOpt->VxMaskName.c_str()); + // Write out FHO - if(conf_info.OutputMap[stat_fho] != STATOutputType_None) { - write_fho_row(shc, info.cts_info, - conf_info.OutputMap[stat_fho], - stat_at, i_stat_row, - txt_at[i_fho], i_txt_row[i_fho]); + if(gci.VxOpt->output_map(stat_fho) != STATOutputType_None) { + + if(gci.VxOpt->DevFlag) { + shc.set_fcst_var(genesis_dev_name); + shc.set_obs_var (genesis_dev_name); + write_fho_row(shc, gci.CTSDev, + gci.VxOpt->OutputMap.at(stat_fho), + stat_at, i_stat_row, + txt_at[i_fho], i_txt_row[i_fho]); + } + + if(gci.VxOpt->OpsFlag) { + shc.set_fcst_var(genesis_ops_name); + shc.set_obs_var (genesis_ops_name); + write_fho_row(shc, gci.CTSOps, + gci.VxOpt->OutputMap.at(stat_fho), + stat_at, i_stat_row, + txt_at[i_fho], i_txt_row[i_fho]); + } } // Write out CTC - if(conf_info.OutputMap[stat_ctc] != STATOutputType_None) { - write_ctc_row(shc, info.cts_info, - conf_info.OutputMap[stat_ctc], - stat_at, i_stat_row, - txt_at[i_ctc], i_txt_row[i_ctc]); + if(gci.VxOpt->output_map(stat_ctc) != STATOutputType_None) { + + if(gci.VxOpt->DevFlag) { + shc.set_fcst_var(genesis_dev_name); + shc.set_obs_var (genesis_dev_name); + write_ctc_row(shc, gci.CTSDev, + gci.VxOpt->OutputMap.at(stat_ctc), + stat_at, i_stat_row, + txt_at[i_ctc], i_txt_row[i_ctc]); + } + + if(gci.VxOpt->OpsFlag) { + shc.set_fcst_var(genesis_ops_name); + shc.set_obs_var (genesis_ops_name); + write_ctc_row(shc, gci.CTSOps, + gci.VxOpt->OutputMap.at(stat_ctc), + stat_at, i_stat_row, + txt_at[i_ctc], i_txt_row[i_ctc]); + } } // Write out CTS - if(conf_info.OutputMap[stat_cts] != STATOutputType_None) { - - // Compute the statistics - info.cts_info.allocate_n_alpha(1); - info.cts_info.alpha[0] = conf_info.CIAlpha; - info.cts_info.compute_stats(); - info.cts_info.compute_ci(); - - write_cts_row(shc, info.cts_info, - conf_info.OutputMap[stat_cts], - stat_at, i_stat_row, - txt_at[i_cts], i_txt_row[i_cts]); + if(gci.VxOpt->output_map(stat_cts) != STATOutputType_None) { + + if(gci.VxOpt->DevFlag) { + gci.CTSDev.compute_stats(); + gci.CTSDev.compute_ci(); + + shc.set_fcst_var(genesis_dev_name); + shc.set_obs_var (genesis_dev_name); + write_cts_row(shc, gci.CTSDev, + gci.VxOpt->OutputMap.at(stat_cts), + stat_at, i_stat_row, + txt_at[i_cts], i_txt_row[i_cts]); + } + + if(gci.VxOpt->OpsFlag) { + gci.CTSOps.compute_stats(); + gci.CTSOps.compute_ci(); + + shc.set_fcst_var(genesis_ops_name); + shc.set_obs_var (genesis_ops_name); + write_cts_row(shc, gci.CTSOps, + gci.VxOpt->OutputMap.at(stat_cts), + stat_at, i_stat_row, + txt_at[i_cts], i_txt_row[i_cts]); + } + } + + // Write out GENMPR + if(gci.VxOpt->output_map(stat_genmpr) != STATOutputType_None) { + shc.set_fcst_var(genesis_name); + shc.set_obs_var (genesis_name); + write_genmpr_row(shc, gpd, + gci.VxOpt->OutputMap.at(stat_genmpr), + stat_at, i_stat_row, + txt_at[i_genmpr], i_txt_row[i_genmpr]); } return; } +//////////////////////////////////////////////////////////////////////// + +void write_genmpr_row(StatHdrColumns &shc, + const PairDataGenesis &gpd, + STATOutputType out_type, + AsciiTable &stat_at, int &stat_row, + AsciiTable &txt_at, int &txt_row) { + int i; + unixtime ut; + + // GENMPR line type + shc.set_line_type(stat_genmpr_str); + + // Not Applicable + shc.set_alpha(bad_data_double); + + // Write a line for each matched pair + for(i=0; igenesis_time() : bgi->genesis_time()); + shc.set_fcst_valid_beg(ut); + shc.set_fcst_valid_end(ut); + shc.set_obs_lead_sec(bad_data_int); + ut = (bgi ? bgi->genesis_time() : fgi->genesis_time()); + shc.set_obs_valid_beg(ut); + shc.set_obs_valid_end(ut); + + // Write the header columns + write_header_cols(shc, stat_at, stat_row); + + // Write the data columns + write_genmpr_cols(gpd, i, stat_at, stat_row, n_header_columns); + + // If requested, copy row to the text file + if(out_type == STATOutputType_Both) { + copy_ascii_table_row(stat_at, stat_row, txt_at, txt_row); + + // Increment the text row counter + txt_row++; + } + + // Increment the STAT row counter + stat_row++; + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + + void write_genmpr_cols(const PairDataGenesis &gpd, int i, + AsciiTable &at, int r, int c) { + + // Pointers for current case + const GenesisInfo* fgi = gpd.fcst_gen(i); + const GenesisInfo* bgi = gpd.best_gen(i); + + // + // Genesis Matched Pairs (GENMPR): + // TOTAL, INDEX, STORM_ID, + // AGEN_INIT, AGEN_FHR, + // AGEN_LAT, AGEN_LON, AGEN_DLAND, + // BGEN_LAT, BGEN_LON, BGEN_DLAND, + // GEN_DIST, GEN_TDIFF, INIT_TDIFF, + // DEV_CAT, OPS_CAT + // + + at.set_entry(r, c+0, // Total number of pairs + gpd.n_pair()); + + at.set_entry(r, c+1, // Index of current pair + i+1); + + at.set_entry(r, c+2, // Best track Storm ID + gpd.best_storm_id(i)); + + at.set_entry(r, c+3, // Fcst genesis initialization time + fgi ? unix_to_yyyymmdd_hhmmss(fgi->init()) : na_str); + + at.set_entry(r, c+4, // Fcst genesis hour + fgi ? fgi->genesis_fhr() : bad_data_int); + + at.set_entry(r, c+5, // Fcst track latitude + fgi ? fgi->lat() : bad_data_double); + + at.set_entry(r, c+6, // Fcst track longitude + fgi ? fgi->lon() : bad_data_double); + + at.set_entry(r, c+7, // Fcst track distance to land + fgi ? fgi->dland() : bad_data_double); + + at.set_entry(r, c+8, // Best track latitude + bgi ? bgi->lat() : bad_data_double); + + at.set_entry(r, c+9, // Best track longitude + bgi ? bgi->lon() : bad_data_double); + + at.set_entry(r, c+10, // Best track distance to land + bgi ? bgi->dland() : bad_data_double); + + at.set_entry(r, c+11, // Genesis distance + gpd.gen_diff(i).DevDist); + + at.set_entry(r, c+12, // Genesis time difference + sec_to_hhmmss(gpd.gen_diff(i).DevDSec)); + + at.set_entry(r, c+13, // Genesis - Init time + sec_to_hhmmss(gpd.gen_diff(i).OpsDSec)); + + at.set_entry(r, c+14, // Development category + genesispaircategory_to_string(gpd.gen_diff(i).DevCategory)); + + at.set_entry(r, c+15, // Operational category + genesispaircategory_to_string(gpd.gen_diff(i).OpsCategory)); + + return; + } + +//////////////////////////////////////////////////////////////////////// + +void write_nc(GenCTCInfo &gci) { + int i; + ConcatString var_name, long_name; + unixtime valid_beg, valid_end; + + // Allocate memory + float *data = (float *) 0; + int nx = gci.NcOutGrid->nx(); + int ny = gci.NcOutGrid->ny(); + data = new float [nx*ny]; + + // Loop over vector of output types + for(i=0; iDesc << "_" << gci.Model << "_GENESIS"; + long_name = "Forecast genesis events"; + valid_beg = gci.FcstBeg; + valid_end = gci.FcstEnd; + } + else if(ncout_str[i] == ftrk_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_TRACKS"; + long_name = "Forecast track points"; + valid_beg = gci.FcstBeg; + valid_end = gci.FcstEnd; + } + else if(ncout_str[i] == bgen_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_BEST_GENESIS"; + long_name = "Best track genesis events"; + valid_beg = gci.BestBeg; + valid_end = gci.BestEnd; + } + else if(ncout_str[i] == btrk_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_BEST_TRACKS"; + long_name = "Best track points"; + valid_beg = gci.BestBeg; + valid_end = gci.BestEnd; + } + else if(ncout_str[i] == fdev_fyoy_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_DEV_FY_OY"; + long_name = "Forecast genesis development method hits"; + valid_beg = gci.FcstBeg; + valid_end = gci.FcstEnd; + } + else if(ncout_str[i] == fdev_fyon_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_DEV_FY_ON"; + long_name = "Forecast genesis development method false alarms"; + valid_beg = gci.FcstBeg; + valid_end = gci.FcstEnd; + } + else if(ncout_str[i] == bdev_fyoy_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_BEST_DEV_FY_OY"; + long_name = "Best track genesis development method hits"; + valid_beg = gci.BestBeg; + valid_end = gci.BestEnd; + } + else if(ncout_str[i] == bdev_fnoy_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_BEST_DEV_FN_OY"; + long_name = "Best track genesis development method misses"; + valid_beg = gci.BestBeg; + valid_end = gci.BestEnd; + } + else if(ncout_str[i] == fops_fyoy_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_OPS_FY_OY"; + long_name = "Forecast genesis operational method hits"; + valid_beg = gci.FcstBeg; + valid_end = gci.FcstEnd; + } + else if(ncout_str[i] == fops_fyon_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_OPS_FY_ON"; + long_name = "Forecast genesis operational method false alarms"; + valid_beg = gci.FcstBeg; + valid_end = gci.FcstEnd; + } + else if(ncout_str[i] == bops_fyoy_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_BEST_OPS_FY_OY"; + long_name = "Best track genesis operational method hits"; + valid_beg = gci.BestBeg; + valid_end = gci.BestEnd; + } + else if(ncout_str[i] == bops_fnoy_str) { + var_name << cs_erase << gci.VxOpt->Desc << "_" << gci.Model << "_BEST_OPS_FN_OY"; + long_name = "Best track genesis operational method misses"; + valid_beg = gci.BestBeg; + valid_end = gci.BestEnd; + } + + // Skip variable names that have already been written + if(nc_var_sa.has(var_name)) continue; + + mlog << Debug(4) << "Writing output variable \"" + << var_name << "\".\n"; + + int n, x, y; + ConcatString cs; + NcVar nc_var; + + // Otherwise, add to the list of previously defined variables + nc_var_sa.add(var_name); + + // Define the variable + nc_var = add_var(nc_out, (string) var_name, + ncFloat, lat_dim, lon_dim, + conf_info.compression_level()); + + // Add variable attributes + add_att(&nc_var, "name", nc_var.getName()); + add_att(&nc_var, "long_name", long_name); + add_att(&nc_var, "model", gci.Model); + add_att(&nc_var, "desc", gci.VxOpt->Desc); + add_att(&nc_var, "valid_beg", unix_to_yyyymmdd_hhmmss(valid_beg)); + add_att(&nc_var, "valid_end", unix_to_yyyymmdd_hhmmss(valid_end)); + + // Reset memory + memset(data, 0, nx*ny); + + // Store the data + for(x=0; x " + << "error writing NetCDF variable name " << var_name + << "\n\n"; + exit(1); + } + } + + // Deallocate and clean up + if(data) { delete [] data; data = (float *) 0; } + + return; +} //////////////////////////////////////////////////////////////////////// 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 536964568f..3d6ff17e9c 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen.h +++ b/met/src/tools/tc_utils/tc_gen/tc_gen.h @@ -29,6 +29,9 @@ using namespace std; #include #include +#include +using namespace netCDF; + #include "tc_gen_conf_info.h" #include "vx_tc_util.h" @@ -57,19 +60,23 @@ static const char * default_config_filename = // Header columns static const char **txt_columns[n_txt] = { - fho_columns, ctc_columns, cts_columns + fho_columns, ctc_columns, cts_columns, genmpr_columns }; // Length of header columns static const int n_txt_columns[n_txt] = { - n_fho_columns, n_ctc_columns, n_cts_columns + n_fho_columns, n_ctc_columns, n_cts_columns, n_genmpr_columns }; // Text file abbreviations static const char *txt_file_abbr[n_txt] = { - "fho", "ctc", "cts" + "fho", "ctc", "cts", "genmpr" }; +const ConcatString genesis_name ("GENESIS"); +const ConcatString genesis_dev_name("GENESIS_DEV"); +const ConcatString genesis_ops_name("GENESIS_OPS"); + //////////////////////////////////////////////////////////////////////// // // Variables for Command Line Arguments @@ -91,6 +98,15 @@ static ConcatString out_base; // //////////////////////////////////////////////////////////////////////// +// Output NetCDF file +static ConcatString out_nc_file; +static NcFile *nc_out = (NcFile *) 0; +static NcDim lat_dim; +static NcDim lon_dim; + +// List of output NetCDF variable names +static StringArray nc_var_sa; + // Output STAT file static ConcatString stat_file; static ofstream *stat_out = (ofstream *) 0; 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 50be7fd2c0..13bcab8455 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 @@ -22,99 +22,89 @@ using namespace std; #include "vx_nc_util.h" #include "apply_mask.h" +#include "vx_regrid.h" #include "vx_log.h" //////////////////////////////////////////////////////////////////////// // -// Code for struct GenesisInfo +// Code for struct TCGenNcOutInfo // //////////////////////////////////////////////////////////////////////// -void GenesisEventInfo::clear() { - Technique.clear(); - Category.clear(); - VMaxThresh.clear(); - MSLPThresh.clear(); +TCGenNcOutInfo::TCGenNcOutInfo() { + clear(); } //////////////////////////////////////////////////////////////////////// -bool GenesisEventInfo::is_keeper(const TrackPoint &p) { - bool keep = true; - - // Check event category - if(Category.size() > 0 && - std::count(Category.begin(), Category.end(), p.level()) == 0) { - keep = false; - } +TCGenNcOutInfo & TCGenNcOutInfo::operator+=(const TCGenNcOutInfo &t) { - // Check VMax threshold - if(VMaxThresh.get_type() != thresh_na && - !VMaxThresh.check(p.v_max())) { - keep = false; - } + if(t.do_latlon) do_latlon = true; + if(t.do_fcst_genesis) do_fcst_genesis = true; + if(t.do_fcst_tracks) do_fcst_tracks = true; + if(t.do_fcst_fy_oy) do_fcst_fy_oy = true; + if(t.do_fcst_fy_on) do_fcst_fy_on = true; + if(t.do_best_genesis) do_best_genesis = true; + if(t.do_best_tracks) do_best_tracks = true; + if(t.do_best_fy_oy) do_best_fy_oy = true; + if(t.do_best_fn_oy) do_best_fn_oy = true; - // Check MSLP threshold - if(MSLPThresh.get_type() != thresh_na && - !MSLPThresh.check(p.mslp())) { - keep = false; - } - return(keep); + return(*this); } -//////////////////////////////////////////////////////////////////////// -// -// Code for struct GenCTCInfo -// //////////////////////////////////////////////////////////////////////// -GenCTCInfo::GenCTCInfo() { - clear(); -} +void TCGenNcOutInfo::clear() { -//////////////////////////////////////////////////////////////////////// + set_all_false(); -void GenCTCInfo::clear() { - model.clear(); - cts_info.clear(); - fbeg = fend = obeg = oend = (unixtime) 0; + return; } //////////////////////////////////////////////////////////////////////// -GenCTCInfo & GenCTCInfo::operator+=(const GenCTCInfo &g) { - - // Increment counts - cts_info.cts += g.cts_info.cts; +bool TCGenNcOutInfo::all_false() const { - // Keep track of the minimum and maximum times - if(fbeg == 0 || g.fbeg < fbeg) fbeg = g.fbeg; - if(fend == 0 || g.fend > fend) fend = g.fend; - if(obeg == 0 || g.obeg < obeg) obeg = g.obeg; - if(oend == 0 || g.oend > oend) oend = g.oend; + bool status = do_latlon || + do_fcst_genesis || do_fcst_tracks || + do_fcst_fy_oy || do_fcst_fy_on || + do_best_genesis || do_best_tracks || + do_best_fy_oy || do_best_fn_oy; - return(*this); + return(!status); } //////////////////////////////////////////////////////////////////////// -void GenCTCInfo::add_fcst_valid(const unixtime beg, - const unixtime end) { +void TCGenNcOutInfo::set_all_false() { - if(fbeg == 0 || fbeg > beg) fbeg = beg; - if(fend == 0 || fend < end) fend = end; + do_latlon = false; + do_fcst_genesis = false; + do_fcst_tracks = false; + do_fcst_fy_oy = false; + do_fcst_fy_on = false; + do_best_genesis = false; + do_best_tracks = false; + do_best_fy_oy = false; + do_best_fn_oy = false; return; } //////////////////////////////////////////////////////////////////////// -void GenCTCInfo::add_obs_valid(const unixtime beg, - const unixtime end) { +void TCGenNcOutInfo::set_all_true() { - if(obeg == 0 || obeg > beg) obeg = beg; - if(oend == 0 || oend < end) oend = end; + do_latlon = true; + do_fcst_genesis = true; + do_fcst_tracks = true; + do_fcst_fy_oy = true; + do_fcst_fy_on = true; + do_best_genesis = true; + do_best_tracks = true; + do_best_fy_oy = true; + do_best_fn_oy = true; return; } @@ -155,16 +145,28 @@ void TCGenVxOpt::clear() { StormId.clear(); StormName.clear(); InitBeg = InitEnd = (unixtime) 0; + InitInc.clear(); + InitExc.clear(); ValidBeg = ValidEnd = (unixtime) 0; InitHour.clear(); Lead.clear(); VxMaskName.clear(); VxPolyMask.clear(); VxGridMask.clear(); + VxBasinMask.clear(); VxAreaMask.clear(); DLandThresh.clear(); - GenesisSecBeg = GenesisSecEnd = bad_data_int; - GenesisRadius = bad_data_double; + GenesisMatchRadius = bad_data_double; + DevHitRadius = bad_data_double; + DevHitBeg = DevHitEnd = bad_data_int; + OpsHitDSec = bad_data_int; + DiscardFlag = false; + DevFlag = OpsFlag = false; + CIAlpha = bad_data_double; + OutputMap.clear(); + NcInfo.clear(); + ValidGenesisDHrThresh.clear(); + BestUniqueFlag = false; return; } @@ -172,10 +174,11 @@ void TCGenVxOpt::clear() { //////////////////////////////////////////////////////////////////////// void TCGenVxOpt::process_config(Dictionary &dict) { - int i; + int i, beg, end; Dictionary *dict2 = (Dictionary *) 0; ConcatString file_name; StringArray sa; + bool status; // Conf: desc Desc = parse_conf_string(&dict, conf_key_desc); @@ -193,19 +196,31 @@ void TCGenVxOpt::process_config(Dictionary &dict) { InitBeg = dict.lookup_unixtime(conf_key_init_beg); InitEnd = dict.lookup_unixtime(conf_key_init_end); + // Conf: InitInc + sa = dict.lookup_string_array(conf_key_init_inc); + for(i=0; i 0) { + mlog << Debug(2) << "Basin Mask: " + << write_css(VxBasinMask) << "\n"; + } + // Conf: dland_thresh DLandThresh = dict.lookup_thresh(conf_key_dland_thresh); - // Conf: genesis_window - int beg, end; - dict2 = dict.lookup_dictionary(conf_key_genesis_window); + // Conf: genesis_match_radius + GenesisMatchRadius = + dict.lookup_double(conf_key_genesis_match_radius); + + // Conf: dev_hit_radius + DevHitRadius = + dict.lookup_double(conf_key_dev_hit_radius); + + // Conf: genesis_hit_window + dict2 = dict.lookup_dictionary(conf_key_dev_hit_window); parse_conf_range_int(dict2, beg, end); - GenesisSecBeg = beg*sec_per_hour; - GenesisSecEnd = end*sec_per_hour; + 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: discard_init_post_genesis_flag + DiscardFlag = + dict.lookup_bool(conf_key_discard_init_post_genesis_flag); + + // Conf: dev_method_flag and ops_method_flag + DevFlag = dict.lookup_bool(conf_key_dev_method_flag); + OpsFlag = dict.lookup_bool(conf_key_ops_method_flag); + + if(!DevFlag && !OpsFlag) { + mlog << Error << "\nTCGenVxOpt::process_config() -> " + << "at least one of " << conf_key_dev_method_flag + << " or " << conf_key_ops_method_flag + << " must be set to true!\n\n"; + exit(1); + } + + // Conf: ci_alpha + CIAlpha = dict.lookup_double(conf_key_ci_alpha); + + // Conf: output_flag + OutputMap = parse_conf_output_flag(&dict, txt_file_type, n_txt); + + for(i=0, status=false; i " + << "at least one output line type must be requested in \"" + << conf_key_output_flag << "\"!\n\n"; + exit(1); + } + + // Conf: nc_pairs_flag + parse_nc_info(dict); + + // Conf: valid_minus_genesis_diff_thresh + ValidGenesisDHrThresh = + dict.lookup_thresh(conf_key_valid_minus_genesis_diff_thresh); + + // Conf: bset_unique_flag + BestUniqueFlag = + dict.lookup_bool(conf_key_best_unique_flag); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void TCGenVxOpt::process_basin_mask(const Grid &basin_grid, + const DataPlane &basin_data, + const StringArray &basin_abbr) { + + // Nothing to do for an empty list + if(VxBasinMask.n() == 0) return; + + int i, j; + DataPlane dp; + ConcatString cs; + SingleThresh st; + MaskPlane mp; + + // If no grid has been defined, use the basin grid + if(VxGridMask.nxy() == 0) VxGridMask = basin_grid; + + // Regrid the basin data, if necessary + dp = (VxGridMask == basin_grid ? basin_data : + met_regrid_nearest(basin_data, basin_grid, VxGridMask)); + + // Construct the threshold + for(i=0; i " + << "\"" << VxBasinMask[i] + << "\" is not a valid basin name!\n\n"; + exit(1); + } + + // Build the threshold string + if(cs.nonempty()) cs << "||"; + cs << "==" << j; + } + st.set(cs.c_str()); + + // Apply the threshold and create the mask + dp.threshold(st); + mp = dp.mask_plane(); + + // Set the area mask + if(VxAreaMask.is_empty()) VxAreaMask = mp; + else apply_mask(VxAreaMask, mp); - // Conf: genesis_radius - GenesisRadius = dict.lookup_double(conf_key_genesis_radius); + // Append to the mask name + if(VxMaskName.nonempty()) VxMaskName << ","; + VxMaskName << write_css(VxBasinMask); return; } //////////////////////////////////////////////////////////////////////// -bool TCGenVxOpt::is_keeper(const GenesisInfo &g) { +void TCGenVxOpt::parse_nc_info(Dictionary &odict) { + const DictionaryEntry * e = (const DictionaryEntry *) 0; + + e = odict.lookup(conf_key_nc_pairs_flag); + + if(!e) { + mlog << Error << "\nTCGenVxOpt::parse_nc_info() -> " + << "lookup failed for key \"" << conf_key_nc_pairs_flag + << "\"\n\n"; + exit(1); + } + + const ConfigObjectType type = e->type(); + + if(type == BooleanType) { + bool value = e->b_value(); + + if(value) NcInfo.set_all_true(); + else NcInfo.set_all_false(); + + return; + } + + // It should be a dictionary + if(type != DictionaryType) { + mlog << Error << "\nTCGenVxOpt::parse_nc_info() -> " + << "bad type for key \"" << conf_key_nc_pairs_flag + << "\"\n\n"; + exit(1); + } + + // Parse the various entries + Dictionary * d = e->dict_value(); + + NcInfo.do_latlon = d->lookup_bool(conf_key_latlon_flag); + NcInfo.do_fcst_genesis = d->lookup_bool(conf_key_fcst_genesis); + NcInfo.do_fcst_tracks = d->lookup_bool(conf_key_fcst_tracks); + NcInfo.do_fcst_fy_oy = d->lookup_bool(conf_key_fcst_fy_oy); + NcInfo.do_fcst_fy_on = d->lookup_bool(conf_key_fcst_fy_on); + NcInfo.do_best_genesis = d->lookup_bool(conf_key_best_genesis); + NcInfo.do_best_tracks = d->lookup_bool(conf_key_best_tracks); + NcInfo.do_best_fy_oy = d->lookup_bool(conf_key_best_fy_oy); + NcInfo.do_best_fn_oy = d->lookup_bool(conf_key_best_fn_oy); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +bool TCGenVxOpt::is_keeper(const GenesisInfo &gi) const { bool keep = true; // ATCF ID processed elsewhere @@ -243,15 +425,15 @@ bool TCGenVxOpt::is_keeper(const GenesisInfo &g) { // Only check basin, storm ID, cyclone number, and storm name for // BEST and operational tracks. - if(g.is_best_track() || g.is_oper_track()) { + if(gi.is_best_track() || gi.is_oper_track()) { // Check storm id if(StormId.n() > 0 && - !has_storm_id(StormId, g.basin(), g.cyclone(), g.init())) + !has_storm_id(StormId, gi.basin(), gi.cyclone(), gi.init())) keep = false; // Check storm name - if(StormName.n() > 0 && !StormName.has(g.storm_name())) + if(StormName.n() > 0 && !StormName.has(gi.storm_name())) keep = false; } @@ -260,38 +442,40 @@ bool TCGenVxOpt::is_keeper(const GenesisInfo &g) { // Only check intialization and lead times for forecast and // operational tracks. - if(!g.is_best_track() || g.is_oper_track()) { + if(!gi.is_best_track() || gi.is_oper_track()) { - // Initialization time window - if((InitBeg > 0 && InitBeg > g.init()) || - (InitEnd > 0 && InitEnd < g.init())) + // Initialization times + if((InitBeg > 0 && InitBeg > gi.init()) || + (InitEnd > 0 && InitEnd < gi.init()) || + (InitInc.n() > 0 && !InitInc.has(gi.init())) || + (InitExc.n() > 0 && InitExc.has(gi.init()))) keep = false; // Initialization hours - if(InitHour.n() > 0 && !InitHour.has(g.init_hour())) + if(InitHour.n() > 0 && !InitHour.has(gi.init_hour())) keep = false; // Lead times - if(Lead.n() > 0 && !Lead.has(g.lead_time())) + if(Lead.n() > 0 && !Lead.has(gi.genesis_lead())) keep = false; } if(!keep) return(keep); // Valid time window - if((ValidBeg > 0 && ValidBeg > g.valid_min()) || - (ValidEnd > 0 && ValidEnd < g.valid_max())) + if((ValidBeg > 0 && ValidBeg > gi.valid_min()) || + (ValidEnd > 0 && ValidEnd < gi.valid_max())) keep = false; // Poly masking if(VxPolyMask.n_points() > 0 && - !VxPolyMask.latlon_is_inside(g.lat(), g.lon())) + !VxPolyMask.latlon_is_inside(gi.lat(), gi.lon())) keep = false; // Area masking if(!VxAreaMask.is_empty()) { double x, y; - VxGridMask.latlon_to_xy(g.lat(), -1.0*g.lon(), x, y); + VxGridMask.latlon_to_xy(gi.lat(), -1.0*gi.lon(), x, y); if(x < 0 || x >= VxGridMask.nx() || y < 0 || y >= VxGridMask.ny()) { keep = false; @@ -303,13 +487,19 @@ bool TCGenVxOpt::is_keeper(const GenesisInfo &g) { // Distance to land if((DLandThresh.get_type() != no_thresh_type) && - (is_bad_data(g.dland()) || !DLandThresh.check(g.dland()))) + (is_bad_data(gi.dland()) || !DLandThresh.check(gi.dland()))) keep = false; // Return the keep status return(keep); } +//////////////////////////////////////////////////////////////////////// + +STATOutputType TCGenVxOpt::output_map(STATLineType t) const { + return(OutputMap.at(t)); +} + //////////////////////////////////////////////////////////////////////// // // Code for class TCGenConfInfo @@ -342,19 +532,25 @@ void TCGenConfInfo::init_from_scratch() { void TCGenConfInfo::clear() { for(size_t i=0; i " << "\"" << conf_key_init_freq << "\" must be greater than " << "zero!\n\n"; exit(1); } - // Conf: lead_window - dict = Conf.lookup_dictionary(conf_key_lead_window); + // Conf: valid_freq + ValidFreqHr = Conf.lookup_int(conf_key_valid_freq); + + if(ValidFreqHr <= 0) { + mlog << Error << "\nTCGenConfInfo::process_config() -> " + << "\"" << conf_key_valid_freq << "\" must be greater than " + << "zero!\n\n"; + exit(1); + } + + // Conf: fcst_hr_window + dict = Conf.lookup_dictionary(conf_key_fcst_hr_window); parse_conf_range_int(dict, beg, end); - LeadSecBeg = beg*sec_per_hour; - LeadSecEnd = end*sec_per_hour; + FcstSecBeg = beg*sec_per_hour; + FcstSecEnd = end*sec_per_hour; // Conf: min_duration MinDur = Conf.lookup_int(conf_key_min_duration); @@ -414,38 +620,23 @@ void TCGenConfInfo::process_config() { dict = Conf.lookup_dictionary(conf_key_best_genesis); BestEventInfo = parse_conf_genesis_event_info(dict); - // Conf: oper_genesis - dict = Conf.lookup_dictionary(conf_key_oper_genesis); - OperEventInfo = parse_conf_genesis_event_info(dict); + // Conf: oper_technique + OperTechnique = Conf.lookup_string(conf_key_oper_technique); // Conf: DLandFile DLandFile = Conf.lookup_string(conf_key_dland_file); + // Conf: BasinFile + BasinFile = Conf.lookup_string(conf_key_basin_file); + + // Conf: NcOutGrid + parse_grid_mask(Conf.lookup_string(conf_key_nc_pairs_grid), + NcOutGrid); + // Conf: Version Version = Conf.lookup_string(conf_key_version); check_met_version(Version.c_str()); - // Conf: CIAlpha - CIAlpha = Conf.lookup_double(conf_key_ci_alpha); - - // Conf: OutputMap - OutputMap = parse_conf_output_flag(dict, txt_file_type, n_txt); - - for(i=0, status=false; i " - << "at least one output line type must be requested in \"" - << conf_key_output_flag << "\"!\n\n"; - exit(1); - } - // Conf: Filter dict = Conf.lookup_array(conf_key_filter, false); @@ -480,34 +671,46 @@ void TCGenConfInfo::process_config() { } // end for i } - // If not already set, define the valid time window relative to the - // initialization time window. + // Loop through the filters for(size_t j=0; j 0) { + + // Load the basin data, if needed. + if(BasinData.is_empty()) { + load_tc_basin(BasinFile, BasinGrid, BasinData, BasinAbbr); + } + + // Apply the basin mask + VxOpt[j].process_basin_mask(BasinGrid, BasinData, BasinAbbr); + } + + // Update the summary OutputMap and NcInfo + process_flags(VxOpt[j].OutputMap, VxOpt[j].NcInfo); + + // If not already set, define the valid time window relative to the + // initialization time window. if(VxOpt[j].InitBeg != 0 && VxOpt[j].ValidBeg == 0) { - VxOpt[j].ValidBeg = VxOpt[j].InitBeg + LeadSecBeg + VxOpt[j].GenesisSecBeg; + VxOpt[j].ValidBeg = VxOpt[j].InitBeg + FcstSecBeg; mlog << Debug(3) << "For filter " << j+1 << " setting " << conf_key_valid_beg << " (" << unix_to_yyyymmdd_hhmmss(VxOpt[j].ValidBeg) << ") = " << conf_key_init_beg << " (" << unix_to_yyyymmdd_hhmmss(VxOpt[j].InitBeg) - << ") + " << conf_key_lead_window << ".beg (" - << LeadSecBeg/sec_per_hour - << ") + " << conf_key_genesis_window << ".beg (" - << VxOpt[j].GenesisSecBeg/sec_per_hour << ").\n"; + << ") + " << conf_key_fcst_hr_window << ".beg (" + << FcstSecBeg/sec_per_hour << ").\n"; } if(VxOpt[j].InitEnd != 0 && VxOpt[j].ValidEnd == 0) { - VxOpt[j].ValidEnd = VxOpt[j].InitEnd + LeadSecEnd + VxOpt[j].GenesisSecEnd; + VxOpt[j].ValidEnd = VxOpt[j].InitEnd + FcstSecEnd; mlog << Debug(3) << "For filter " << j+1 << " setting " << conf_key_valid_end << " (" << unix_to_yyyymmdd_hhmmss(VxOpt[j].ValidEnd) << ") = " << conf_key_init_end << " (" << unix_to_yyyymmdd_hhmmss(VxOpt[j].InitEnd) - << ") + " << conf_key_lead_window << ".end (" - << LeadSecEnd/sec_per_hour - << ") + " << conf_key_genesis_window << ".end (" - << VxOpt[j].GenesisSecEnd/sec_per_hour << ").\n"; + << ") + " << conf_key_fcst_hr_window << ".end (" + << FcstSecEnd/sec_per_hour << ").\n"; } } @@ -516,13 +719,39 @@ void TCGenConfInfo::process_config() { //////////////////////////////////////////////////////////////////////// +void TCGenConfInfo::process_flags( + const map &m, + const TCGenNcOutInfo &n) { + int i; + + // Initialize output map + if(OutputMap.empty()) OutputMap = m; + + // Update output map + for(i=0; i= BasinGrid.nx() || + y < 0 || y >= BasinGrid.ny()) ? + bad_data_int : + nint(BasinData.get(x, y))); + + // Convert to string + if(i < 0 || i >= BasinAbbr.n()) { + mlog << Error << "\nTCGenConfInfo::compute_basin() -> " + << "unexpected basin id value (" << i + << ") found in basin file \"" << BasinFile + << "\"\n\n"; + exit(1); + } + + return(BasinAbbr[i]); +} + +//////////////////////////////////////////////////////////////////////// + +STATOutputType TCGenConfInfo::output_map(STATLineType t) const { + return(OutputMap.at(t)); +} + //////////////////////////////////////////////////////////////////////// // -// Miscellaneous utility functions. +// Code for class GenCTCInfo // //////////////////////////////////////////////////////////////////////// -GenesisEventInfo parse_conf_genesis_event_info(Dictionary *dict) { - GenesisEventInfo info; - StringArray sa; - int i; +GenCTCInfo::GenCTCInfo() { - if(!dict) { - mlog << Error << "\nparse_conf_genesis_event_info() -> " - << "empty dictionary!\n\n"; - exit(1); + init_from_scratch(); +} + +//////////////////////////////////////////////////////////////////////// + +GenCTCInfo::~GenCTCInfo() { + + clear(); +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::init_from_scratch() { + + clear(); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::clear() { + + Model.clear(); + FcstBeg = FcstEnd = (unixtime) 0; + BestBeg = BestEnd = (unixtime) 0; + + CTSDev.clear(); + CTSOps.clear(); + + VxOpt = (const TCGenVxOpt *) 0; + NcOutGrid = (const Grid *) 0; + + ValidGenesisDHrThresh.clear(); + BestUniqueFlag = false; + + BestDevHitMap.clear(); + BestOpsHitMap.clear(); + + DpMap.clear(); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::set_vx_opt(const TCGenVxOpt *vx_opt, + const Grid *nc_out_grid) { + + if(!vx_opt) return; + + // Store pointer + VxOpt = vx_opt; + + // Store config options + ValidGenesisDHrThresh = VxOpt->ValidGenesisDHrThresh; + BestUniqueFlag = VxOpt->BestUniqueFlag; + + // Setup alpha value + if(VxOpt->DevFlag) { + CTSDev.allocate_n_alpha(1); + CTSDev.alpha[0] = VxOpt->CIAlpha; + } + if(VxOpt->OpsFlag) { + CTSOps.allocate_n_alpha(1); + CTSOps.alpha[0] = VxOpt->CIAlpha; + } + + // Setup NetCDF pairs output fields + if(!VxOpt->NcInfo.all_false()) { + NcOutGrid = nc_out_grid; + + // Initialize data plane of all zeros + DataPlane dp; + dp.set_size(NcOutGrid->nx(), NcOutGrid->ny(), 0.0); + + // Add map entries for requested outputs + if(VxOpt->NcInfo.do_fcst_genesis) DpMap[fgen_str] = dp; + if(VxOpt->NcInfo.do_fcst_tracks) DpMap[ftrk_str] = dp; + if(VxOpt->NcInfo.do_best_genesis) DpMap[bgen_str] = dp; + if(VxOpt->NcInfo.do_best_tracks) DpMap[btrk_str] = dp; + if(VxOpt->DevFlag) { + if(VxOpt->NcInfo.do_fcst_fy_oy) DpMap[fdev_fyoy_str] = dp; + if(VxOpt->NcInfo.do_fcst_fy_on) DpMap[fdev_fyon_str] = dp; + if(VxOpt->NcInfo.do_best_fy_oy) DpMap[bdev_fyoy_str] = dp; + if(VxOpt->NcInfo.do_best_fn_oy) DpMap[bdev_fnoy_str] = dp; + } + if(VxOpt->OpsFlag) { + if(VxOpt->NcInfo.do_fcst_fy_oy) DpMap[fops_fyoy_str] = dp; + if(VxOpt->NcInfo.do_fcst_fy_on) DpMap[fops_fyon_str] = dp; + if(VxOpt->NcInfo.do_best_fy_oy) DpMap[bops_fyoy_str] = dp; + if(VxOpt->NcInfo.do_best_fn_oy) DpMap[bops_fnoy_str] = dp; + } } - // Conf: technique (optional) - info.Technique = dict->lookup_string(conf_key_technique, false); + return; +} + +//////////////////////////////////////////////////////////////////////// - // Conf: category (optional) - sa = dict->lookup_string_array(conf_key_category, false); - for(i=0; ilat(), fgi->lon(), fdev_fyoy_str); + BestDevHitMap[bgi] += 1; + + // Count all BEST track genesis pairs + if(!BestUniqueFlag) { + inc_pnt(bgi->lat(), bgi->lon(), bdev_fyoy_str); + } + } + // False Alarms + else if(c == FYONGenesis) { + CTSDev.cts.inc_fy_on(); + inc_pnt(fgi->lat(), fgi->lon(), fdev_fyon_str); + } + // Misses + else if(c == FNOYGenesis) { + CTSDev.cts.inc_fn_oy(); - // Conf: vmax_thresh - info.VMaxThresh = dict->lookup_thresh(conf_key_vmax_thresh); + // Count all BEST track genesis pairs + if(!BestUniqueFlag) { + inc_pnt(bgi->lat(), bgi->lon(), bdev_fnoy_str); + } + } + // Correct Negatives (should be none) + else { + CTSDev.cts.inc_fn_on(); + } - // Conf: mslp_thresh - info.MSLPThresh = dict->lookup_thresh(conf_key_mslp_thresh); + return; +} - return(info); +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::inc_ops(GenesisPairCategory c, + const GenesisInfo *fgi, + const GenesisInfo *bgi) { + + // Discard + if(c == DiscardGenesis) { + return; + } + // Hits + else if(c == FYOYGenesis) { + CTSOps.cts.inc_fy_oy(); + inc_pnt(fgi->lat(), fgi->lon(), fops_fyoy_str); + BestOpsHitMap[bgi] += 1; + + // Count all BEST track genesis pairs + if(!BestUniqueFlag) { + inc_pnt(bgi->lat(), bgi->lon(), bops_fyoy_str); + } + } + // False Alarms + else if(c == FYONGenesis) { + CTSOps.cts.inc_fy_on(); + inc_pnt(fgi->lat(), fgi->lon(), fops_fyon_str); + } + // Misses + else if(c == FNOYGenesis) { + CTSOps.cts.inc_fn_oy(); + + // Count all BEST track genesis pairs + if(!BestUniqueFlag) { + inc_pnt(bgi->lat(), bgi->lon(), bops_fnoy_str); + } + } + // Correct Negatives (should be none) + else { + CTSOps.cts.inc_fn_on(); + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::inc_best_unique() { + + // Only process when the flag is set + if(!BestUniqueFlag) return; + + map::iterator it; + + // Count the dev BEST track hits and false alarms + for(it=BestDevHitMap.begin(); it!=BestDevHitMap.end(); it++) { + + // Zero hits is a miss + if(it->second == 0) { + inc_pnt(it->first->lat(), it->first->lon(), bdev_fnoy_str); + } + // Otherwise, it's a hit + else { + inc_pnt(it->first->lat(), it->first->lon(), bdev_fyoy_str); + } + } + + // Count the ops BEST track hits and false alarms + for(it=BestOpsHitMap.begin(); it!=BestOpsHitMap.end(); it++) { + + // Zero hits is a miss + if(it->second == 0) { + inc_pnt(it->first->lat(), it->first->lon(), bops_fnoy_str); + } + // Otherwise, it's a hit + else { + inc_pnt(it->first->lat(), it->first->lon(), bops_fyoy_str); + } + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::add_fcst_gen(const GenesisInfo &gi) { + + // Track the range of valid times + if(FcstBeg == 0 || FcstBeg > gi.valid_min()) FcstBeg = gi.valid_min(); + if(FcstEnd == 0 || FcstEnd < gi.valid_max()) FcstEnd = gi.valid_max(); + + // Count the genesis and track points + inc_pnt(gi.lat(), gi.lon(), fgen_str); + inc_trk(gi, ftrk_str); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::add_best_gen(const GenesisInfo &gi) { + + // Nothing to do if this genesis has already been counted + if(BestDevHitMap.count(&gi) > 0 || + BestOpsHitMap.count(&gi) > 0) return; + + // Add hit counter entries for this storm + BestDevHitMap[&gi] = 0; + BestOpsHitMap[&gi] = 0; + + // Track the range of valid times + if(BestBeg == 0 || BestBeg > gi.valid_min()) BestBeg = gi.valid_min(); + if(BestEnd == 0 || BestEnd < gi.valid_max()) BestEnd = gi.valid_max(); + + // Count the genesis and track points + inc_pnt(gi.lat(), gi.lon(), bgen_str); + inc_trk(gi, btrk_str); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::inc_pnt(double lat, double lon, const string &s) { + + // Nothing to do if there is no DataPlane map entry + if(DpMap.count(s) == 0) return; + + int x, y; + double x_dbl, y_dbl; + + NcOutGrid->latlon_to_xy(lat, -1.0*lon, x_dbl, y_dbl); + x = nint(x_dbl); + y = nint(y_dbl); + + // Only increment points on the grid + if(x >= 0 && x < NcOutGrid->nx() && + y >= 0 && y < NcOutGrid->ny()) { + DpMap[s].set((DpMap[s])(x, y) + 1, x, y); + } + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void GenCTCInfo::inc_trk(const GenesisInfo &gi, const string &s) { + + // Nothing to do if there is no DataPlane map entry + if(DpMap.count(s) == 0) return; + + // Loop through the track points + for(int i=0; i Category; - SingleThresh VMaxThresh; - SingleThresh MSLPThresh; +struct TCGenNcOutInfo { - bool is_keeper(const TrackPoint &); - void clear(); -}; + bool do_latlon; + bool do_fcst_genesis; + bool do_fcst_tracks; + bool do_fcst_fy_oy; + bool do_fcst_fy_on; + bool do_best_genesis; + bool do_best_tracks; + bool do_best_fy_oy; + bool do_best_fn_oy; -extern GenesisEventInfo parse_conf_genesis_event_info(Dictionary *dict); -//////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////// -struct GenCTCInfo { - ConcatString model; - CTSInfo cts_info; - unixtime fbeg, fend, obeg, oend; + TCGenNcOutInfo(); + TCGenNcOutInfo & operator+=(const TCGenNcOutInfo &); - GenCTCInfo(); + void clear(); // sets everything to true - void clear(); - GenCTCInfo & operator+=(const GenCTCInfo &); + bool all_false() const; - void add_fcst_valid(const unixtime, const unixtime); - void add_obs_valid (const unixtime, const unixtime); + void set_all_false(); + void set_all_true(); }; //////////////////////////////////////////////////////////////////////// @@ -97,30 +116,51 @@ class TCGenVxOpt { // Timing information unixtime InitBeg, InitEnd; + TimeArray InitInc, InitExc; unixtime ValidBeg, ValidEnd; NumArray InitHour; NumArray Lead; - // Polyline masking region + // Spatial masking information ConcatString VxMaskName; MaskPoly VxPolyMask; Grid VxGridMask; + StringArray VxBasinMask; MaskPlane VxAreaMask; // Distance to land threshold SingleThresh DLandThresh; // Temporal and spatial matching criteria - int GenesisSecBeg, GenesisSecEnd; - double GenesisRadius; + double GenesisMatchRadius; + double DevHitRadius; + int DevHitBeg, DevHitEnd; + int OpsHitDSec; + + // Scoring methods + bool DiscardFlag; + bool DevFlag; + bool OpsFlag; + + // Output file options + double CIAlpha; + map OutputMap; + TCGenNcOutInfo NcInfo; + SingleThresh ValidGenesisDHrThresh; + bool BestUniqueFlag; ////////////////////////////////////////////////////////////////// void clear(); void process_config(Dictionary &); + void process_basin_mask(const Grid &, const DataPlane &, + const StringArray &); + void parse_nc_info(Dictionary &); - bool is_keeper(const GenesisInfo &); + bool is_keeper(const GenesisInfo &) const; + + STATOutputType output_map(STATLineType) const; }; //////////////////////////////////////////////////////////////////////// @@ -138,17 +178,20 @@ class TCGenConfInfo { ////////////////////////////////////////////////////////////////// - // TCPairs configuration object + // TCGen configuration object MetConfig Conf; // Vector of vx task filtering options [n_vx] std::vector VxOpt; // Forecast initialization frequency in hours - int InitFreqSec; + int InitFreqHr; + + // Forecast lead time frequency in hours + int ValidFreqHr; // Begin and end forecast hours for genesis - int LeadSecBeg, LeadSecEnd; + int FcstSecBeg, FcstSecEnd; // Minimum track duration int MinDur; @@ -156,19 +199,28 @@ class TCGenConfInfo { // Genesis event criteria GenesisEventInfo FcstEventInfo; GenesisEventInfo BestEventInfo; - GenesisEventInfo OperEventInfo; + ConcatString OperTechnique; // Gridded data file containing distances to land ConcatString DLandFile; Grid DLandGrid; DataPlane DLandData; - // Config file version - ConcatString Version; + // Gridded data file containing TC basins + ConcatString BasinFile; + Grid BasinGrid; + DataPlane BasinData; + StringArray BasinAbbr; - // Output file options - double CIAlpha; + // Grid for NetCDF output file + Grid NcOutGrid; + + // Summary of output file options across all filters map OutputMap; + TCGenNcOutInfo NcInfo; + + // Config file version + ConcatString Version; ////////////////////////////////////////////////////////////////// @@ -177,15 +229,75 @@ class TCGenConfInfo { void read_config(const char *, const char *); void process_config(); + void process_flags(const map &, + const TCGenNcOutInfo &); double compute_dland(double lat, double lon); + ConcatString compute_basin(double lat, double lon); + int n_vx() const; + int compression_level(); + + STATOutputType output_map(STATLineType) const; }; //////////////////////////////////////////////////////////////////////// -inline int TCGenConfInfo::n_vx() const { return(VxOpt.size()); } +inline int TCGenConfInfo::n_vx() const { return(VxOpt.size()); } +inline int TCGenConfInfo::compression_level() { return(Conf.nc_compression()); } + +//////////////////////////////////////////////////////////////////////// + +class GenCTCInfo { + + private: + + void init_from_scratch(); + + public: + + GenCTCInfo(); + ~GenCTCInfo(); + + ////////////////////////////////////////////////////////////////// + + ConcatString Model; + unixtime FcstBeg, FcstEnd; + unixtime BestBeg, BestEnd; + CTSInfo CTSDev, CTSOps; + + const TCGenVxOpt* VxOpt; + const Grid *NcOutGrid; + + SingleThresh ValidGenesisDHrThresh; + bool BestUniqueFlag; + + // Number of hits per BEST track genesis event + map BestDevHitMap; + map BestOpsHitMap; + + // Output DataPlane variables + map DpMap; + + ////////////////////////////////////////////////////////////////// + + void clear(); + + void set_vx_opt(const TCGenVxOpt *, const Grid *); + + void inc_dev(GenesisPairCategory, + const GenesisInfo *, const GenesisInfo *); + void inc_ops(GenesisPairCategory, + const GenesisInfo *, const GenesisInfo *); + void inc_best_unique(); + + void add_fcst_gen(const GenesisInfo &); + void add_best_gen(const GenesisInfo &); + + void inc_pnt(double, double, const string &); + void inc_trk(const GenesisInfo &, const string &); +}; //////////////////////////////////////////////////////////////////////// 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 8046c74fa1..a86b1a3d49 100644 --- a/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc +++ b/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc @@ -251,7 +251,7 @@ void process_command_line(int argc, char **argv) { // Load the distance to land data file if(dland_dp.is_empty()) { - load_dland(conf_info.DLandFile, dland_grid, dland_dp); + load_tc_dland(conf_info.DLandFile, dland_grid, dland_dp); } return; @@ -296,7 +296,7 @@ void process_bdecks(TrackInfoArray &bdeck_tracks) { (conf_info.AnlyTrack == TrackType_BDeck || conf_info.AnlyTrack == TrackType_Both)); mlog << Debug(2) - << "Found " << bdeck_tracks.n_tracks() << " BDECK track(s).\n"; + << "Found " << bdeck_tracks.n() << " BDECK track(s).\n"; return; } @@ -357,7 +357,7 @@ void process_adecks(const TrackInfoArray &bdeck_tracks) { // Filter the ADECK tracks using the config file information mlog << Debug(2) - << "Filtering " << adeck_tracks.n_tracks() + << "Filtering " << adeck_tracks.n() << " ADECK tracks based on config file settings.\n"; filter_tracks(adeck_tracks); @@ -366,12 +366,12 @@ void process_adecks(const TrackInfoArray &bdeck_tracks) { // mlog << Debug(2) - << "Matching " << adeck_tracks.n_tracks() << " ADECK tracks to " - << bdeck_tracks.n_tracks() << " BDECK tracks.\n"; + << "Matching " << adeck_tracks.n() << " ADECK tracks to " + << bdeck_tracks.n() << " BDECK tracks.\n"; - for(i=0; iis_match(bdeck_tracks[j])) { @@ -599,7 +599,7 @@ void process_track_files(const StringArray &files, // Dump out the track information mlog << Debug(3) - << "Identified " << tracks.n_tracks() << " track(s).\n"; + << "Identified " << tracks.n() << " track(s).\n"; // Dump out very verbose output if(mlog.verbosity_level() >= 5) { @@ -608,9 +608,9 @@ void process_track_files(const StringArray &files, } // Dump out track info else { - for(i=0; i 0 && @@ -880,13 +880,13 @@ void filter_tracks(TrackInfoArray &tracks) { // Print summary filtering info mlog << Debug(3) - << "Total tracks read = " << t.n_tracks() << "\n" - << "Total tracks kept = " << tracks.n_tracks() << "\n" - << "Rejected for storm name = " << n_name << "\n" - << "Rejected for valid time = " << n_vld << "\n" - << "Rejected for required lead times = " << n_req_lead << "\n" - << "Rejected for init mask = " << n_mask_init << "\n" - << "Rejected for valid mask = " << n_mask_vld << "\n"; + << "Total tracks read = " << t.n() << "\n" + << "Total tracks kept = " << tracks.n() << "\n" + << "Rejected for storm name = " << n_name << "\n" + << "Rejected for valid time = " << n_vld << "\n" + << "Rejected for required lead times = " << n_req_lead << "\n" + << "Rejected for init mask = " << n_mask_init << "\n" + << "Rejected for valid mask = " << n_mask_vld << "\n"; return; } @@ -1032,7 +1032,7 @@ void derive_interp12(TrackInfoArray &tracks) { if(conf_info.Interp12 == Interp12Type_None) return; // Loop through the track array and store case information - for(i=0; i 1 - if(tracks.n_tracks() == 0) { + if(tracks.n() == 0) { mlog << Error << "\nprocess_track_files() -> " << "no tracks retained! Adjust the configuration file " << "filtering options to select a single track.\n\n"; exit(1); } // Issue warning if more than one track found - else if(tracks.n_tracks() > 1) { + else if(tracks.n() > 1) { mlog << Warning << "\nprocess_track_files() -> " - << "multiple tracks found (" << tracks.n_tracks() + << "multiple tracks found (" << tracks.n() << ")! Using the first one. Adjust the configuration file " << "filtering options to select a single track.\n\n"; } diff --git a/test/config/TCGenConfig_2016 b/test/config/TCGenConfig_2016 index 832ede390d..6d46d9f528 100644 --- a/test/config/TCGenConfig_2016 +++ b/test/config/TCGenConfig_2016 @@ -13,25 +13,30 @@ //////////////////////////////////////////////////////////////////////////////// // -// Genesis event definition criteria. +// Genesis event definition criteria // //////////////////////////////////////////////////////////////////////////////// // -// Model initialization frequency in hours, starting at 0. +// Model initialization frequency in hours, starting at 0 // init_freq = 6; // -// Lead times in hours to be searched for genesis events. +// Valid hour frequency to be analyzed in hours, starting at 0 // -lead_window = { - beg = 24; +valid_freq = 6; + +// +// Forecast hours to be searched for genesis events +// +fcst_hr_window = { + beg = 6; end = 120; } // -// Minimum track duration for genesis event in hours. +// Minimum track duration for genesis event in hours // min_duration = 12; @@ -39,7 +44,7 @@ min_duration = 12; // Forecast genesis event criteria. Defined as tracks reaching the specified // intensity category, maximum wind speed threshold, and minimum sea-level // pressure threshold. The forecast genesis time is the valid time of the first -// point of tracks which meet this criteria. +// track point where all of these criteria are met. // fcst_genesis = { vmax_thresh = NA; @@ -49,8 +54,8 @@ fcst_genesis = { // // BEST track genesis event criteria. Defined as tracks reaching the specified // intensity category, maximum wind speed threshold, and minimum sea-level -// pressure threshold. The BEST track genesis time is the time of the first -// track point where all of these criteria are met. +// pressure threshold. The BEST track genesis time is the valid time of the +// first track point where all of these criteria are met. // best_genesis = { technique = "BEST"; @@ -60,57 +65,70 @@ best_genesis = { } // -// Operational track genesis event criteria. Defined as tracks reaching the -// specified intensity category, maximum wind speed threshold, and minimum -// sea-level pressure threshold. The operational track genesis time is the time -// of the first track point where all of these criteria are met. +// Operational track technique name // -oper_genesis = { - technique = "CARQ"; - category = [ "DB", "LO", "WV" ]; - vmax_thresh = NA; - mslp_thresh = NA; -} +oper_technique = "CARQ"; //////////////////////////////////////////////////////////////////////////////// // -// Track filtering options which may be specified separately in each filter -// array entry. +// Track filtering options +// May be specified separately in each filter array entry. // //////////////////////////////////////////////////////////////////////////////// // -// Filter is an array of dictionaries containing the track filtering options -// listed below. If empty, a single filter is defined using the top-level -// settings. +// Array of dictionaries containing the track filtering options +// If empty, a single filter is defined using the top-level settings. // filter = [ - { desc = "AL_BASIN"; - vx_mask = "MET_BASE/tc_data/basin_global_tenth_degree.nc \ - { name=\"basin\"; level=\"(*,*)\"; } ==1"; }, - { desc = "AL_DLAND_300"; - vx_mask = "MET_BASE/tc_data/basin_global_tenth_degree.nc \ - { name=\"basin\"; level=\"(*,*)\"; } ==1"; - dland_thresh = >0&&<300; }, - { desc = "EP_CP_BASIN"; - vx_mask = "MET_BASE/tc_data/basin_global_tenth_degree.nc \ - { name=\"basin\"; level=\"(*,*)\";} ==2||==3"; }, - { desc = "EP_BASIN"; - genesis_window = { beg = -3*24; end = 3*24; }; genesis_radius = 300; }, - { desc = "3DAY_300KM"; - genesis_window = { beg = -3*24; end = 3*24; }; genesis_radius = 300; }, - { desc = "3DAY_600KM"; - genesis_window = { beg = -3*24; end = 3*24; }; genesis_radius = 600; }, - { desc = "5DAY_300KM"; - genesis_window = { beg = -5*24; end = 5*24; }; genesis_radius = 300; }, - { desc = "5DAY_600KM"; - genesis_window = { beg = -5*24; end = 5*24; }; genesis_radius = 600; } + { + desc = "ALL"; + }, + { + desc = "AL_BASIN"; + basin_mask = "AL"; + nc_pairs_flag = TRUE; + output_flag = { genmpr = BOTH; } + }, + { + desc = "AL_DLAND_300NM"; + basin_mask = "AL"; + dland_thresh = >0&&<300; + }, + { + desc = "EP_CP_BASIN"; + basin_mask = [ "EP", "CP" ]; + nc_pairs_flag = TRUE; + output_flag = { genmpr = BOTH; } + }, + { + desc = "ALL_MATCH_600KM"; + genesis_match_radius = 600; + dev_hit_radius = 600; + dev_method_flag = TRUE; + ops_method_flag = TRUE; + }, + { + desc = "ALL_MATCH_600KM_DEV60HR"; + genesis_match_radius = 600; + dev_hit_radius = 600; + dev_hit_window = { beg = -60; end = 60; }; + dev_method_flag = TRUE; + ops_method_flag = FALSE; + }, + { + desc = "ALL_MATCH_600KM_OPS60HR"; + genesis_match_radius = 600; + ops_hit_tdiff = 60; + dev_method_flag = FALSE; + ops_method_flag = TRUE; + } ]; // // Description written to output DESC column // -desc = "NA"; +desc = "ALL"; // // Forecast ATCF ID's @@ -130,10 +148,12 @@ storm_id = []; storm_name = []; // -// Forecast and operational initialization time window +// Forecast and operational initialization times to include or exclude // init_beg = ""; init_end = ""; +init_inc = []; +init_exc = []; // // Forecast, BEST, and operational valid time window @@ -156,27 +176,63 @@ lead = []; // vx_mask = ""; +// +// Spatial masking of hurricane basin names from the basin_file +// +basin_mask = []; + // // Distance to land threshold // dland_thresh = NA; +//////////////////////////////////////////////////////////////////////////////// +// +// Matching and scoring options +// May be specified separately in each filter array entry. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Radius in km to search for a matching genesis event +// +genesis_match_radius = 500; + +// +// Radius in km for a development scoring method hit +// +dev_hit_radius = 500; + // -// Genesis matching time window, in hours relative to the forecast genesis time +// Time window in hours for a development scoring method hit // -genesis_window = { +dev_hit_window = { beg = -24; end = 24; } // -// Genesis matching search radius in km. +// Maximum Best track genesis minus model initialization time difference for an +// operational scoring method hit +// +ops_hit_tdiff = 48; + +// +// Discard genesis forecasts for initializations at or after the matching +// BEST track genesis time +// +discard_init_post_genesis_flag = TRUE; + +// +// Scoring methods to be applied // -genesis_radius = 300; +dev_method_flag = TRUE; +ops_method_flag = TRUE; //////////////////////////////////////////////////////////////////////////////// // -// Global settings. +// Output options +// May be specified separately in each filter array entry. // //////////////////////////////////////////////////////////////////////////////// @@ -190,16 +246,52 @@ ci_alpha = 0.05; // output_flag = { fho = NONE; - ctc = BOTH; + ctc = BOTH; cts = BOTH; + genmpr = NONE; } +// +// NetCDF genesis pair counts +// +nc_pairs_flag = FALSE; + +// +// Specify which track points should be counted by thresholding the track point +// valid time minus genesis time difference. +// +valid_minus_genesis_diff_thresh = NA; + +// +// Count unique BEST track genesis event locations (TRUE) versus counting the +// location for all pairs (FALSE). +// +best_unique_flag = TRUE; + +//////////////////////////////////////////////////////////////////////////////// +// +// Global settings +// May only be specified once. +// +//////////////////////////////////////////////////////////////////////////////// + // // Specify the NetCDF output of the gen_dland tool containing a gridded // representation of the minimum distance to land. // dland_file = "MET_BASE/tc_data/dland_global_tenth_degree.nc"; +// +// Specify the NetCDF file containing a gridded representation of the +// global basins. +// +basin_file = "MET_BASE/tc_data/basin_global_tenth_degree.nc"; + +// +// NetCDF genesis pairs grid +// +nc_pairs_grid = "G003"; + // // Indicate a version number for the contents of this configuration file. // The value should generally not be modified. diff --git a/test/hdr/met_10_0.hdr b/test/hdr/met_10_0.hdr index bdf4d0f92b..1c3004e680 100644 --- a/test/hdr/met_10_0.hdr +++ b/test/hdr/met_10_0.hdr @@ -28,6 +28,7 @@ SSVAR : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_L VL1L2 : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL UFBAR VFBAR UOBAR VOBAR UVFOBAR UVFFBAR UVOOBAR F_SPEED_BAR O_SPEED_BAR VAL1L2 : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL UFABAR VFABAR UOABAR VOABAR UVFOABAR UVFFABAR UVOOABAR VCNT : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL FBAR FBAR_BCL FBAR_BCU OBAR OBAR_BCL OBAR_BCU FS_RMS FS_RMS_BCL FS_RMS_BCU OS_RMS OS_RMS_BCL OS_RMS_BCU MSVE MSVE_BCL MSVE_BCU RMSVE RMSVE_BCL RMSVE_BCU FSTDEV FSTDEV_BCL FSTDEV_BCU OSTDEV OSTDEV_BCL OSTDEV_BCU FDIR FDIR_BCL FDIR_BCU ODIR ODIR_BCL ODIR_BCU FBAR_SPEED FBAR_SPEED_BCL FBAR_SPEED_BCU OBAR_SPEED OBAR_SPEED_BCL OBAR_SPEED_BCU VDIFF_SPEED VDIFF_SPEED_BCL VDIFF_SPEED_BCU VDIFF_DIR VDIFF_DIR_BCL VDIFF_DIR_BCU SPEED_ERR SPEED_ERR_BCL SPEED_ERR_BCU SPEED_ABSERR SPEED_ABSERR_BCL SPEED_ABSERR_BCU DIR_ERR DIR_ERR_BCL DIR_ERR_BCU DIR_ABSERR DIR_ABSERR_BCL DIR_ABSERR_BCU +GENMPR : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE TOTAL INDEX STORM_ID AGEN_INIT AGEN_FHR AGEN_LAT AGEN_LON AGEN_DLAND BGEN_LAT BGEN_LON BGEN_DLAND GEN_DIST GEN_TDIFF INIT_TDIFF DEV_CAT OPS_CAT MODE_SOA : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE N_VALID GRID_RES OBJECT_ID OBJECT_CAT CENTROID_X CENTROID_Y CENTROID_LAT CENTROID_LON AXIS_ANG LENGTH WIDTH AREA AREA_THRESH CURVATURE CURVATURE_X CURVATURE_Y COMPLEXITY INTENSITY_10 INTENSITY_25 INTENSITY_50 INTENSITY_75 INTENSITY_90 INTENSITY_50 INTENSITY_SUM MODE_POA : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE N_VALID GRID_RES OBJECT_ID OBJECT_CAT CENTROID_DIST BOUNDARY_DIST CONVEX_HULL_DIST ANGLE_DIFF ASPECT_DIFF AREA_RATIO INTERSECTION_AREA UNION_AREA SYMMETRIC_DIFF INTERSECTION_OVER_AREA CURVATURE_RATIO COMPLEXITY_RATIO PERCENTILE_INTENSITY_RATIO INTEREST MODE_CTS : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID_END OBS_LEAD OBS_VALID_BEG OBS_VALID_END FCST_VAR FCST_UNITS FCST_LEV OBS_VAR OBS_UNITS OBS_LEV OBTYPE VX_MASK INTERP_MTHD INTERP_PNTS FCST_THRESH OBS_THRESH COV_THRESH ALPHA LINE_TYPE N_VALID GRID_RES FIELD TOTAL FY_OY FY_ON FN_OY FN_ON BASER FMEAN ACC FBIAS PODY PODN POFD FAR CSI GSS HK HSS ODDS