diff --git a/met/data/config/TCGenConfig_default b/met/data/config/TCGenConfig_default index 9ed75cc2c2..839d8bd21d 100644 --- a/met/data/config/TCGenConfig_default +++ b/met/data/config/TCGenConfig_default @@ -212,6 +212,11 @@ ops_method_flag = TRUE; // //////////////////////////////////////////////////////////////////////////////// +// +// Probability of genesis thresholds +// +prob_genesis_thresh = ==0.25; + // // Confidence interval alpha value // @@ -222,8 +227,12 @@ ci_alpha = 0.05; // output_flag = { fho = NONE; - ctc = BOTH; + ctc = BOTH; cts = BOTH; + pct = NONE; + pstd = NONE; + pjc = NONE; + prc = NONE; genmpr = NONE; } diff --git a/met/data/table_files/met_header_columns_V10.1.txt b/met/data/table_files/met_header_columns_V10.1.txt index c0218275ec..1dbd3c2849 100644 --- a/met/data/table_files/met_header_columns_V10.1.txt +++ b/met/data/table_files/met_header_columns_V10.1.txt @@ -28,7 +28,7 @@ V10.1 : STAT : SSVAR : VERSION MODEL DESC FCST_LEAD FCST_VALID_BEG FCST_VALID V10.1 : 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.1 : 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.1 : 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.1 : 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.1 : 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 PROB_LEAD PROB_VAL 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.1 : STAT : SSIDX : 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 FCST_MODEL REF_MODEL N_INIT N_TERM N_VLD SS_INDEX V10.1 : 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 diff --git a/met/docs/Users_Guide/tc-gen.rst b/met/docs/Users_Guide/tc-gen.rst index 61209566dc..b67f1a26b9 100644 --- a/met/docs/Users_Guide/tc-gen.rst +++ b/met/docs/Users_Guide/tc-gen.rst @@ -6,14 +6,18 @@ TC-Gen Tool Introduction ____________ -The TC-Gen tool provides verification of tropical cyclone genesis forecasts in ATCF file format. Producing reliable tropical cyclone genesis forecasts is an important metric for global numerical weather prediction models. This tool ingests deterministic model output post-processed by a genesis tracking software (e.g. GFDL vortex tracker) and ATCF format reference dataset(s) (e.g. Best Track analysis and CARQ operational tracks) and outputs categorical counts and statistics. The capability to modify the spatial and temporal tolerances that define a "hit" forecast is included to give users the ability to condition the criteria based on model performance and/or conduct sensitivity analyses. Statistical aspects are outlined in :numref:`tc-gen_stat_aspects` and practical aspects of the TC-Gen tool are described in :numref:`tc-gen_practical_info`. +The TC-Gen tool provides verification of deterministic and probabilistic tropical cyclone genesis forecasts in the ATCF file format. Producing reliable tropical cyclone genesis forecasts is an important metric for global numerical weather prediction models. This tool ingests deterministic model output post-processed by a genesis tracking software (e.g. GFDL vortex tracker), ATCF edeck files containing probability of genesis forecasts, and ATCF reference track dataset(s) (e.g. Best Track analysis and CARQ operational tracks). It writes categorical counts and statistics. The capability to modify the spatial and temporal tolerances when matching forecasts to reference genesis events, as well as scoring those matched pairs, gives users the ability to condition the criteria based on model performance and/or conduct sensitivity analyses. Statistical aspects are outlined in :numref:`tc-gen_stat_aspects` and practical aspects of the TC-Gen tool are described in :numref:`tc-gen_practical_info`. .. _tc-gen_stat_aspects: Statistical aspects ___________________ -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). +The TC-Gen tool processes both deterministic and probabilistic forecasts. For deterministic forecasts specified using the **-track** command line option, it identifies genesis events in both the forecasts and reference datasets, typically Best tracks. It applies user-specified configuration options to pair up the forecast and reference genesis events and categorize each pair as a hit, miss, or false alarm. + +As with other extreme events (where the event occurs much less frequently than the non-event), the correct negative category is not computed since 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). + +For probabilistic forecasts specified using the **-edeck** command line option, it identifies genesis events in the reference dataset. It applies user-specified configuration options to pair the forecast probabilities to the reference genesis events. These pairs are added to an Nx2 probabilistic contingency table. If the reference genesis event occurs within in the predicted time window, the pair is counted in the observation-yes column. Otherwise, it is added to the observation-no column. 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. @@ -34,8 +38,8 @@ The usage statement for tc_gen is shown below: .. code-block:: none Usage: tc_gen - -genesis path - -track path + -genesis source and/or -edeck source + -track source -config file [-out base] [-log file] @@ -46,50 +50,76 @@ TC-Gen has three required arguments and accepts optional ones. 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. `_ +1. The **-genesis source** 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 required file format is described in the "Output formats" section of the `GFDL Vortex Tracker users guide. `_ + +2. The **-edeck source** argument is the path to one or more ATCF edeck files, an ASCII file list containing them, or a top-level directory with files matching the regular expression ".dat". The probability of genesis are read from each edeck input file and verified against at the **-track** data. The **-genesis** or **-edeck** 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 **-track source** 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. +4. The **-config** file argument indicates the name of the configuration file to be used. The contents of the configuration file are discussed below. Optional arguments for tc_gen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -4. The **-out base** argument indicates the path of the output file base. This argument overrides the default output file base (./tc_gen) +5. The **-out base** argument indicates the path of the output file base. This argument overrides the default output file base (./tc_gen) -5. The **-log file** option directs output and errors to the specified log file. All messages will be written to that file as well as standard out and error. Thus, users can save the messages without having to redirect the output on the command line. The default behavior is no log file. +6. The **-log file** option directs output and errors to the specified log file. All messages will be written to that file as well as standard out and error. Thus, users can save the messages without having to redirect the output on the command line. The default behavior is no log file. -6. The **-v level** option indicates the desired level of verbosity. The contents of "level" will override the default setting of 2. Setting the verbosity to 0 will make the tool run with no log messages, while increasing the verbosity above 1 will increase the amount of logging. +7. The **-v level** option indicates the desired level of verbosity. The contents of "level" will override the default setting of 2. Setting the verbosity to 0 will make the tool run with no log messages, while increasing the verbosity above 1 will increase the amount of logging. -The TC-Gen tool implements the following logic: +Scoring Logic +^^^^^^^^^^^^^ -* Parse the forecast genesis data and identify forecast genesis events separately for each model present. +The TC-Gen tool implements the following logic: * Parse the Best and operational track data, and identify Best track genesis events. Note that Best tracks with a cyclone number greater than 50 are automatically discarded from the analysis. Large cyclone numbers are used for pre-season testing or to track invests prior to a storm actually forming. Running this tool at verbosity level 6 (-v 6) prints details about which tracks are discarded. -* Loop over the filters defined in the configuration file and apply the following logic for each. +* For **-track** inputs: + + * Parse the forecast genesis data and identify forecast genesis events separately for each model present. - * 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. + * Loop over the filters defined in the configuration file and apply the following logic for each. + + * For each Best track genesis event meeting the filter critera, determine the initialization and lead times for which the model had an opportunity to forecast that genesis event. Store an unmatched genesis pair for each case. - * For each forecast genesis event, search for a matching Best track. A configurable boolean option controls whether all Best track points are considered for a match or only the single Best track genesis point. A match occurs if the Best track point valid time is within a configurable window around the forecast genesis time and the Best track point location is within a configurable radius of the forecast genesis location. If a Best track match is found, store the storm ID. + * For each forecast genesis event, search for a matching Best track. A configurable boolean option controls whether all Best track points are considered for a match or only the single Best track genesis point. A match occurs if the Best track point valid time is within a configurable window around the forecast genesis time and the Best track point location is within a configurable radius of the forecast genesis location. If a Best track match is found, store the storm ID. - * In no Best track match is found, apply the same logic to search the operational track points with lead time of 0 hours. If an operational match is found, store the storm ID. + * If no Best track match is found, apply the same logic to search the operational track points with lead time of 0 hours. If an operational match is found, store the storm ID. - * If a matching storm ID is found, match the forecast genesis event to the Best track genesis event for that storm ID. + * 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. + * If no matching storm ID is found, store an unmatched pair for the genesis forecast. - * 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: + * Loop through the genesis pairs and populate contingency tables using two methods, the development (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: - * If the forecast initialization time is at or after the Best track genesis event, DISCARD this case and exclude it from the statistics. + * 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 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. + * 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. + + * 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*. + +* For **-edeck** inputs: + + * Parse the ATCF edeck files. Ignore any lines not containing "GN" and "genFcst", which indicate a genesis probability forecast. Also, ignore any lines which do not contain a predicted genesis location (latitude and longitude) or genesis time. + + * Loop over the filters defined in the configuration file and apply the following logic for each. - * Do not count any CORRECT NEGATIVES. + * Subset the genesis probability forecasts based on the current filter criteria. Typically, genesis probability forecast are provided for multiple lead times. Create separate Nx2 probabilistic contingency tables for each unique combination of predicted lead time and model name. + + * For each genesis probability forecast, search for a matching Best track. A configurable boolean option controls whether all Best track points are considered for a match or only the single Best track genesis point. A match occurs if the Best track point valid time is within a configurable window around the forecast genesis time and the Best track point location is within a configurable radius of the forecast genesis location. If a Best track match is found, store the storm ID. + + * If no Best track match is found, apply the same logic to search the operational track points with lead time of 0 hours. If an operational match is found, store the storm ID. + + * If no matching storm ID is found, add the unmatched forecast to the observation-no column of the Nx2 probabilistic contingency table. + + * If a matching storm ID is found, check whether that storm's genesis occurred within the predicted time window: between the forecast initialization time and the predicted lead time. If so, add the matched forecast to the observation-yes column. If not, add it to observation-no column. + + * Report the Nx2 probabilistic contingency table counts and statistics for each forecast model, lead time, and configuration file filter. These counts and statistics are identified in the output files as *PROB_GENESIS*. -* 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 ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -381,18 +411,31 @@ The **nc_pairs_grid** entry is a string which defines the grid to be used for th ______________________ +.. code-block:: none + + prob_genesis_thresh = ==0.25; + +The **prob_genesis_thresh** entry defines the probability thresholds used to create the output Nx2 contingency table when verifying edeck probability of genesis forecasts. The default is probability bins of width 0.25. These probabilities may be specified as a list (>0.00,>0.25,>0.50,>0.75,>1.00) or using shorthand notation (==0.25) for bins of equal width. + +______________________ + .. code-block:: none ci_alpha = 0.05; output_flag = { - fho = BOTH; - ctc = BOTH; - cts = BOTH; + fho = BOTH; + ctc = BOTH; + cts = BOTH; + pct = NONE; + pstd = NONE; + pjc = NONE; + prc = NONE; + genmpr = NONE; } dland_file = "MET_BASE/tc_data/dland_global_tenth_degree.nc"; version = "VN.N"; -The configuration options listed above are common to many MET tools and are described in :numref:`config_options`. Note that TC-Gen writes output for 2x2 contingency tables to the **FHO, CTC**, and **CTS** line types. +The configuration options listed above are common to many MET tools and are described in :numref:`config_options`. TC-Gen writes output for 2x2 contingency tables to the **FHO**, **CTC**, and **CTS** line types when verifying deterministic genesis forecasts specified using the **-track** command line option. TC-Gen writes output for Nx2 probabilistic contingency tables to the **PCT**, **PSTD**, **PJC**, and **PRC** line types when verifying the probability of genesis forecasts specified using the **-edeck** command line option. Note that the **genmpr** line type is specific to TC-Gen and describes individual genesis matched pairs. tc_gen output ~~~~~~~~~~~~~ @@ -440,7 +483,7 @@ TC-Gen produces output in STAT and, optionally, ASCII and NetCDF formats. The AS - Maximum Best track valid time in YYYYMMDD_HHMMSS format * - 10 - FCST_VAR - - Genesis methodology + - Genesis methodology (GENESIS_DEV, GENESIS_OPS, or PROB_GENESIS) * - 11 - FCST_UNITS - Does not apply and is set to NA @@ -449,7 +492,7 @@ TC-Gen produces output in STAT and, optionally, ASCII and NetCDF formats. The AS - Does not apply and is set to NA * - 13 - OBS_VAR - - Genesis methodology + - Genesis methodology (GENESIS_DEV, GENESIS_OPS, or PROB_GENESIS) * - 14 - OBS_UNITS - Does not apply and is set to NA @@ -515,44 +558,50 @@ TC-Gen produces output in STAT and, optionally, ASCII and NetCDF formats. The AS - STORM_ID - BBCCYYYY designation of storm (basin, cyclone number, and year) * - 28 + - PROB_LEAD + - Lead time in HHH format for the predicted probability of genesis (only for **-edeck** inputs) + * - 29 + - PROB_VAL + - Predicted probability of genesis (only for **-edeck** inputs) + * - 30 - AGEN_INIT - Forecast initialization time - * - 29 + * - 31 - AGEN_FHR - Forecast hour of genesis event - * - 30 + * - 32 - AGEN_LAT - Latitude position of the forecast genesis event - * - 31 + * - 33 - AGEN_LON - Longitude position of the forecast genesis event - * - 32 + * - 34 - AGEN_DLAND - Forecast genesis event distance to land (nm) - * - 33 + * - 35 - BGEN_LAT - Latitude position of the verifying Best track genesis event - * - 34 + * - 36 - BGEN_LON - Longitude position of the verifying Best track genesis event - * - 35 + * - 37 - BGEN_DLAND - Best track genesis event distance to land (nm) - * - 36 + * - 38 - GEN_DIST - - Distance between the forecast and Best track genesis events (km) - * - 37 + - Distance between the forecast and Best track genesis events (km) (only for **-track** inputs) + * - 39 - GEN_TDIFF - - Forecast minus Best track genesis time in HHMMSS format - * - 38 + - Forecast minus Best track genesis time in HHMMSS format (only for **-track** inputs) + * - 40 - INIT_TDIFF - - Best track genesis minus forecast initialization time in HHMMSS format - * - 39 + - Best track genesis minus forecast initialization time in HHMMSS format (only for **-track** inputs) + * - 41 - DEV_CAT - - Development methodology category (FYOY, FYON, FNOY, or DISCARD) - * - 40 + - Category for the development methodology (FYOY, FYON, FNOY, or DISCARD) (only for **-track** inputs) + * - 42 - OPS_CAT - - Operational methodology category (FYOY, FYON, FNOY, or DISCARD) + - Category for the operational methodology (FYOY, FYON, FNOY, or DISCARD for **-track** inputs and FYOY or FYON for **-edeck** inputs) .. _table_TG_var_NetCDF_matched_pair_out: diff --git a/met/internal_tests/libcode/vx_tc_util/Makefile.am b/met/internal_tests/libcode/vx_tc_util/Makefile.am index a5fc96c5d0..a1d0fed6c5 100644 --- a/met/internal_tests/libcode/vx_tc_util/Makefile.am +++ b/met/internal_tests/libcode/vx_tc_util/Makefile.am @@ -43,6 +43,7 @@ test_read_LDADD = -lvx_stat_out \ -lvx_config \ -lvx_gsl_prob \ -lvx_cal \ + -lvx_nav \ -lvx_util \ -lvx_math \ -lvx_color \ @@ -75,6 +76,7 @@ test_read_prob_LDADD = -lvx_stat_out \ -lvx_config \ -lvx_gsl_prob \ -lvx_cal \ + -lvx_nav \ -lvx_util \ -lvx_math \ -lvx_color \ diff --git a/met/internal_tests/libcode/vx_tc_util/test_read_prob.cc b/met/internal_tests/libcode/vx_tc_util/test_read_prob.cc index 402876f47c..eea9a30e2d 100644 --- a/met/internal_tests/libcode/vx_tc_util/test_read_prob.cc +++ b/met/internal_tests/libcode/vx_tc_util/test_read_prob.cc @@ -53,7 +53,7 @@ int main(int argc, char *argv[]) { while(f >> p_line) { // Add the current line to the array of probs - probs.add(p_line); + probs.add(p_line, bad_data_double, false); // Increment the line count count++; diff --git a/met/src/basic/vx_config/config_constants.h b/met/src/basic/vx_config/config_constants.h index a46559ca02..01c598bd72 100644 --- a/met/src/basic/vx_config/config_constants.h +++ b/met/src/basic/vx_config/config_constants.h @@ -1097,6 +1097,7 @@ static const char conf_key_ops_hit_window[] = "ops_hit_window"; static const char conf_key_discard_init_post_genesis_flag[] = "discard_init_post_genesis_flag"; static const char conf_key_dev_method_flag[] = "dev_method_flag"; static const char conf_key_ops_method_flag[] = "ops_method_flag"; +static const char conf_key_prob_genesis_thresh[] = "prob_genesis_thresh"; static const char conf_key_fcst_fy_oy[] = "fcst_fy_oy"; static const char conf_key_fcst_fy_on[] = "fcst_fy_on"; static const char conf_key_fcst_tracks[] = "fcst_tracks"; diff --git a/met/src/basic/vx_util/stat_column_defs.h b/met/src/basic/vx_util/stat_column_defs.h index b8ddc583bc..9b21762513 100644 --- a/met/src/basic/vx_util/stat_column_defs.h +++ b/met/src/basic/vx_util/stat_column_defs.h @@ -320,6 +320,7 @@ static const char * ssidx_columns [] = { static const char * genmpr_columns [] = { "TOTAL", "INDEX", "STORM_ID", + "PROB_LEAD", "PROB_VAL", "AGEN_INIT", "AGEN_FHR", "AGEN_LAT", "AGEN_LON", "AGEN_DLAND", "BGEN_LAT", "BGEN_LON", "BGEN_DLAND", diff --git a/met/src/libcode/vx_statistics/met_stats.cc b/met/src/libcode/vx_statistics/met_stats.cc index 2975b03432..3880ab204a 100644 --- a/met/src/libcode/vx_statistics/met_stats.cc +++ b/met/src/libcode/vx_statistics/met_stats.cc @@ -2424,6 +2424,27 @@ void PCTInfo::allocate_n_alpha(int i) { //////////////////////////////////////////////////////////////////////// +void PCTInfo::set_fthresh(const ThreshArray &ta) { + + // Expand the probability thresholds, as needed + fthresh = string_to_prob_thresh(ta.get_str().c_str()); + + // Validate the threshold settings + check_prob_thresh(fthresh, true); + + // Store the values in an array + NumArray prob_vals; + for(int i=0; i " << "skipping ATCF line type (" << atcflinetype_to_string(Type) << ")\n"; diff --git a/met/src/libcode/vx_tc_util/prob_gen_info.cc b/met/src/libcode/vx_tc_util/prob_gen_info.cc new file mode 100644 index 0000000000..c0db7ce9fb --- /dev/null +++ b/met/src/libcode/vx_tc_util/prob_gen_info.cc @@ -0,0 +1,241 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2021 +// ** 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 "prob_gen_info.h" +#include "atcf_offsets.h" + +//////////////////////////////////////////////////////////////////////// +// +// Code for class ProbGenInfo +// +//////////////////////////////////////////////////////////////////////// + +ProbGenInfo::ProbGenInfo() { + + init_from_scratch(); +} + +//////////////////////////////////////////////////////////////////////// + +ProbGenInfo::~ProbGenInfo() { + + clear(); +} + +//////////////////////////////////////////////////////////////////////// + +ProbGenInfo::ProbGenInfo(const ProbGenInfo & t) { + + init_from_scratch(); + + assign(t); +} + +//////////////////////////////////////////////////////////////////////// + +ProbGenInfo & ProbGenInfo::operator=(const ProbGenInfo & t) { + + if(this == &t) return(*this); + + assign(t); + + return(*this); +} + +//////////////////////////////////////////////////////////////////////// + +void ProbGenInfo::init_from_scratch() { + + clear(); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void ProbGenInfo::clear() { + + ProbInfoBase::clear(); + + Initials.clear(); + GenOrDis.clear(); + GenesisTime = (unixtime) 0; + GenesisLead = 0; + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void ProbGenInfo::dump(ostream &out, int indent_depth) const { + Indent prefix(indent_depth); + + ProbInfoBase::dump(out, indent_depth); + + out << prefix << "Initials = \"" << Initials.contents() << "\"\n"; + out << prefix << "GenOrDis = \"" << GenOrDis.contents() << "\"\n"; + out << prefix << "GenesisTime = " << unix_to_yyyymmdd_hhmmss(GenesisTime) << "\n"; + out << prefix << "GenesisLead = " << sec_to_hhmmss(GenesisLead) << "\n"; + + out << flush; + + return; + +} + +//////////////////////////////////////////////////////////////////////// + +ConcatString ProbGenInfo::serialize() const { + ConcatString s; + + s << ProbInfoBase::serialize() + << ", ProbGenInfo: " + << "Initials = \"" << Initials << "\"" + << ", GenOrDis = \"" << GenOrDis << "\"" + << ", GenesisTime = " << unix_to_yyyymmdd_hhmmss(GenesisTime) + << ", GenesisLead = " << sec_to_hhmmss(GenesisLead) << "\n"; + + return(s); +} + +//////////////////////////////////////////////////////////////////////// + +ConcatString ProbGenInfo::serialize_r(int n, int indent_depth) const { + ConcatString s; + + s << ProbInfoBase::serialize_r(n, indent_depth); + + return(s); +} + +//////////////////////////////////////////////////////////////////////// + +void ProbGenInfo::assign(const ProbGenInfo &p) { + + clear(); + + ProbInfoBase::assign(p); + + Initials = p.Initials; + GenOrDis = p.GenOrDis; + GenesisTime = p.GenesisTime; + GenesisLead = p.GenesisLead; + + return; +} + +//////////////////////////////////////////////////////////////////////// + +int ProbGenInfo::genesis_fhr() const { + return(is_bad_data(GenesisLead) ? + bad_data_int : + nint((double) GenesisLead/sec_per_hour)); +} + +//////////////////////////////////////////////////////////////////////// + +void ProbGenInfo::initialize(const ATCFProbLine &l, double dland) { + + clear(); + + ProbInfoBase::initialize(l, dland); + + Initials = l.get_item(ProbGenInitialsOffset); + GenOrDis = l.get_item(ProbGenOrDisOffset); + + // Store an empty string as unixtime 0 + GenesisTime = (l.get_item(ProbGenTimeOffset).empty() ? + (unixtime) 0 : + parse_time(l.get_item(ProbGenTimeOffset).c_str())); + GenesisLead = (GenesisTime == 0 ? bad_data_int : + GenesisTime - InitTime); + + return; +} + +//////////////////////////////////////////////////////////////////////// + +bool ProbGenInfo::is_match(const ATCFProbLine &l) const { + + if(!ProbInfoBase::is_match(l)) return(false); + + unixtime gen_ut = (l.get_item(ProbGenTimeOffset).empty() ? + (unixtime) 0 : + parse_time(l.get_item(ProbGenTimeOffset).c_str())); + + return(GenesisTime == gen_ut); +} + +//////////////////////////////////////////////////////////////////////// + +bool ProbGenInfo::add(const ATCFProbLine &l, double dland, bool check_dup) { + + // Check for duplicates + if(check_dup) { + if(has(l)) { + mlog << Warning + << "\nProbGenInfo::add(const ATCFProbLine &l, bool check_dup) -> " + << "skipping duplicate ATCF line:\n" + << l.get_line() << "\n\n"; + return(false); + } + } + + // Initialize the header information, if necessary + if(Type == NoATCFLineType) initialize(l, dland); + + // Check for matching header information + if(!is_match(l)) return(false); + + // Add probability information + NProb++; + Prob.add(l.prob()); + ProbItem.add(l.prob_item()); + + // Store the ATCFProbLine that was just added + if(check_dup) ProbLines.add(l.get_line()); + + return(true); +} + +//////////////////////////////////////////////////////////////////////// + +bool ProbGenInfo::is_match(const TrackPoint &p, const double rad, + const int beg, const int end) const { + + // Check for matching in time and space + return(p.valid() >= (GenesisTime + beg) && + p.valid() <= (GenesisTime + end) && + gc_dist(Lat, Lon, p.lat(), p.lon()) <= rad); +} + +//////////////////////////////////////////////////////////////////////// + +bool ProbGenInfo::is_match(const GenesisInfo &gi, const double rad, + const int beg, const int end) const { + + // Input genesis point + const TrackPoint *p = gi.genesis(); + + return(p ? is_match(*p, rad, beg, end) : false); +} + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/prob_gen_info.h b/met/src/libcode/vx_tc_util/prob_gen_info.h new file mode 100644 index 0000000000..af97bb03fa --- /dev/null +++ b/met/src/libcode/vx_tc_util/prob_gen_info.h @@ -0,0 +1,98 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2021 +// ** 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 __VX_PROB_GEN_INFO_H__ +#define __VX_PROB_GEN_INFO_H__ + +//////////////////////////////////////////////////////////////////////// + +#include + +#include "prob_info_base.h" +#include "genesis_info.h" + +#include "vx_util.h" + +//////////////////////////////////////////////////////////////////////// +// +// ProbGenInfo class stores probability of rapid intensification. +// +//////////////////////////////////////////////////////////////////////// + +class ProbGenInfo : public ProbInfoBase { + + private: + + void init_from_scratch(); + void assign(const ProbGenInfo &); + + // Probability of Genesis specific values + ConcatString Initials; + ConcatString GenOrDis; + unixtime GenesisTime; + int GenesisLead; + + public: + + ProbGenInfo(); + ~ProbGenInfo(); + ProbGenInfo(const ProbGenInfo &); + ProbGenInfo & operator=(const ProbGenInfo &); + + void clear(); + + void dump(ostream &, int = 0) const; + ConcatString serialize() const; + ConcatString serialize_r(int, int = 0) const; + + // + // set stuff + // + + void set_best_gen(const GenesisInfo *); + + // + // get stuff + // + + const ConcatString & initials() const; + const ConcatString & gen_or_dis() const; + unixtime genesis_time() const; + int genesis_lead() const; + int genesis_fhr() const; + const GenesisInfo * best_gen() const; + + // + // do stuff + // + + void initialize(const ATCFProbLine &, double); + bool is_match (const ATCFProbLine &) const; + bool add (const ATCFProbLine &, double, bool check_dup = false); + + bool is_match (const TrackPoint &, + const double, const int, const int) const; + bool is_match (const GenesisInfo &, + const double, const int, const int) const; + +}; + +//////////////////////////////////////////////////////////////////////// + +inline const ConcatString & ProbGenInfo::initials() const { return(Initials); } +inline const ConcatString & ProbGenInfo::gen_or_dis() const { return(GenOrDis); } +inline unixtime ProbGenInfo::genesis_time() const { return(GenesisTime); } +inline int ProbGenInfo::genesis_lead() const { return(GenesisLead); } + +//////////////////////////////////////////////////////////////////////// + +#endif /* __VX_PROB_GEN_INFO_H__ */ + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/prob_info_array.cc b/met/src/libcode/vx_tc_util/prob_info_array.cc index 5a8e91844b..64809049ec 100644 --- a/met/src/libcode/vx_tc_util/prob_info_array.cc +++ b/met/src/libcode/vx_tc_util/prob_info_array.cc @@ -72,8 +72,9 @@ void ProbInfoArray::init_from_scratch() { void ProbInfoArray::clear() { - // Erase the entire vector + // Erase the entire vectors ProbRIRW.erase(ProbRIRW.begin(), ProbRIRW.end()); + ProbGen.erase(ProbGen.begin(), ProbGen.end()); return; } @@ -82,15 +83,23 @@ void ProbInfoArray::clear() { void ProbInfoArray::dump(ostream &out, int indent_depth) const { Indent prefix(indent_depth); + int i; out << prefix << "ProbInfoArray:\n" - << prefix << "NProbRIRW = " << n_prob_rirw() << "\n"; + << prefix << "NProbRIRW = " << n_prob_rirw() << "\n"; - for(unsigned int i=0; i 0 ) { + s << prefix << serialize() << ", ProbRIRW:\n"; + } + for(i=0; i 0 ) { + s << prefix << serialize() << ", ProbGen:\n"; + } + + for(i=0; i= n_probs())) { - mlog << Error - << "\nProbInfoBase * ProbInfoArray::operator[] -> " + mlog << Error << "\nProbInfoBase * ProbInfoArray::operator[] -> " << "range check error for index value " << n << "\n\n"; exit(1); } - // Return a base pointer to the n-th probability - // Need to revise for each new probability vector + // Get a base pointer to the n-th probability + const ProbInfoBase *ptr; + + if(ProbRIRW.size() > 0 && n < ProbRIRW.size()) { + ptr = &ProbRIRW[n]; + } + else { + n -= ProbRIRW.size(); + ptr = &ProbGen[n]; + } - return(&ProbRIRW[n]); + return(ptr); } //////////////////////////////////////////////////////////////////////// -const ProbRIRWInfo & ProbInfoArray::prob_rirw(int n) const { +ProbRIRWInfo & ProbInfoArray::prob_rirw(int n) { // Check range if((n < 0) || (n >= (int) ProbRIRW.size())) { - mlog << Error - << "\nProbRIRWInfo & ProbInfoArray::prob_rirw(int) -> " + mlog << Error << "\nProbRIRWInfo & ProbInfoArray::prob_rirw(int) -> " << "range check error for index value " << n << "\n\n"; exit(1); } @@ -171,14 +195,49 @@ const ProbRIRWInfo & ProbInfoArray::prob_rirw(int n) const { //////////////////////////////////////////////////////////////////////// -bool ProbInfoArray::add(const ATCFProbLine &l, bool check_dup) { +ProbGenInfo & ProbInfoArray::prob_gen(int n) { + + // Check range + if((n < 0) || (n >= (int) ProbGen.size())) { + mlog << Error << "\nProbGenInfo & ProbInfoArray::prob_gen(int) -> " + << "range check error for index value " << n << "\n\n"; + exit(1); + } + + return(ProbGen[n]); +} + +//////////////////////////////////////////////////////////////////////// + +int ProbInfoArray::n_technique() const { + StringArray sa; + + // Count the number of unique technique names + for(int i=0; i 100) { - mlog << Debug(4) - << "\nbool ProbInfoArray::add() -> " + mlog << Debug(4) + << "bool ProbInfoArray::add() -> " << "skipping probability value (" << l.prob() - << ") outside of range (0, 100).\n\n"; + << ") outside of range (0, 100).\n"; return(false); } @@ -187,27 +246,61 @@ bool ProbInfoArray::add(const ATCFProbLine &l, bool check_dup) { case(ATCFLineType_ProbRI): - // Check for no entries or a mismatch with the latest entry - if( ProbRIRW.size() == 0 || - (ProbRIRW.size() > 0 && - !ProbRIRW[ProbRIRW.size()-1].add(l, check_dup))) { - - // Store new entry + // Add line to an existing entry + if(ProbRIRW.size() > 0 && + ProbRIRW[ProbRIRW.size()-1].add(l, dland, check_dup)) { + status = true; + } + // Add a new entry + else { ProbRIRWInfo ri; - ri.add(l, check_dup); + ri.add(l, dland, check_dup); ProbRIRW.push_back(ri); + status = true; + } + break; + + case(ATCFLineType_ProbGN): + + // Add line to an existing entry + if(ProbGen.size() > 0 && + ProbGen[ProbGen.size()-1].add(l, dland, check_dup)) { + status = true; + } + // Add a new entry + else { + ProbGenInfo gi; + gi.add(l, dland, check_dup); + + // Check for the expected genesis type and predicted location + if(gi.gen_or_dis() != "genFcst") { + mlog << Debug(4) + << "bool ProbInfoArray::add() -> " + << "skipping ATCF " << atcflinetype_to_string(ATCFLineType_ProbGN) + << " line with non-genesis probability type (" + << gi.gen_or_dis() << " != genFcst).\n"; + } + else if(is_bad_data(gi.lat()) || is_bad_data(gi.lon())) { + mlog << Debug(4) + << "bool ProbInfoArray::add() -> " + << "skipping ATCF " << atcflinetype_to_string(ATCFLineType_ProbGN) + << " line with no predicted genesis location.\n"; + } + else { + ProbGen.push_back(gi); + status = true; + } } break; default: - mlog << Warning - << "\nbool ProbInfoArray::add() -> " + mlog << Warning << "\nbool ProbInfoArray::add() -> " << "unexpected ATCF line type (" << atcflinetype_to_string(l.type()) << ")\n\n"; - return(false); + status = false; } - return(true); + return(status); } //////////////////////////////////////////////////////////////////////// @@ -218,3 +311,10 @@ void ProbInfoArray::add(const ProbRIRWInfo &rirw) { } //////////////////////////////////////////////////////////////////////// + +void ProbInfoArray::add(const ProbGenInfo &gi) { + ProbGen.push_back(gi); + return; +} + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/prob_info_array.h b/met/src/libcode/vx_tc_util/prob_info_array.h index 1228b91880..881cf3dcfb 100644 --- a/met/src/libcode/vx_tc_util/prob_info_array.h +++ b/met/src/libcode/vx_tc_util/prob_info_array.h @@ -19,6 +19,7 @@ #include "atcf_prob_line.h" #include "prob_info_base.h" #include "prob_rirw_info.h" +#include "prob_gen_info.h" #include "vx_util.h" @@ -36,6 +37,7 @@ class ProbInfoArray { void assign(const ProbInfoArray &); vector ProbRIRW; + vector ProbGen; public: @@ -58,21 +60,27 @@ class ProbInfoArray { const ProbInfoBase * operator[](int) const; int n_prob_rirw() const; - const ProbRIRWInfo & prob_rirw(int) const; + ProbRIRWInfo & prob_rirw(int); + + int n_prob_gen() const; + ProbGenInfo & prob_gen(int); + + int n_technique() const; // // do stuff // - bool add(const ATCFProbLine &, bool check_dup = false); + bool add(const ATCFProbLine &, double dland, bool check_dup = false); void add(const ProbRIRWInfo &); - + void add(const ProbGenInfo &); }; //////////////////////////////////////////////////////////////////////// -inline int ProbInfoArray::n_probs() const { return(ProbRIRW.size()); } -inline int ProbInfoArray::n_prob_rirw() const { return(ProbRIRW.size()); } +inline int ProbInfoArray::n_probs() const { return(ProbRIRW.size() + ProbGen.size()); } +inline int ProbInfoArray::n_prob_rirw() const { return(ProbRIRW.size()); } +inline int ProbInfoArray::n_prob_gen() const { return(ProbGen.size()); } //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_tc_util/prob_info_base.cc b/met/src/libcode/vx_tc_util/prob_info_base.cc index f8d3bb791e..fb4147226c 100644 --- a/met/src/libcode/vx_tc_util/prob_info_base.cc +++ b/met/src/libcode/vx_tc_util/prob_info_base.cc @@ -79,6 +79,7 @@ void ProbInfoBase::clear() { ValidTime = (unixtime) 0; Lat = bad_data_double; Lon = bad_data_double; + DLand = bad_data_double; NProb = 0; Prob.clear(); ProbItem.clear(); @@ -101,6 +102,7 @@ void ProbInfoBase::dump(ostream &out, int indent_depth) const { out << prefix << "ValidTime = \"" << (ValidTime > 0 ? unix_to_yyyymmdd_hhmmss(ValidTime).text() : na_str) << "\"\n"; out << prefix << "Lat = " << Lat << "\n"; out << prefix << "Lon = " << Lon << "\n"; + out << prefix << "DLand = " << DLand << "\n"; out << prefix << "NProb = " << NProb << "\n"; out << prefix << "Prob:" << "\n"; Prob.dump(out, indent_depth+1); @@ -130,6 +132,7 @@ ConcatString ProbInfoBase::serialize() const { << ", ValidTime = \"" << (ValidTime > 0 ? unix_to_yyyymmdd_hhmmss(ValidTime).text() : na_str) << "\"" << ", Lat = " << Lat << ", Lon = " << Lon + << ", DLand = " << DLand << ", NProb = " << NProb; return(s); @@ -155,6 +158,14 @@ ConcatString ProbInfoBase::serialize_r(int n, int indent_depth) const { //////////////////////////////////////////////////////////////////////// +void ProbInfoBase::set_dland(double d) { + DLand = d; + + return; +} + +//////////////////////////////////////////////////////////////////////// + void ProbInfoBase::assign(const ProbInfoBase &t) { clear(); @@ -168,6 +179,7 @@ void ProbInfoBase::assign(const ProbInfoBase &t) { ValidTime = t.ValidTime; Lat = t.Lat; Lon = t.Lon; + DLand = t.DLand; NProb = t.NProb; Prob = t.Prob; ProbItem = t.ProbItem; @@ -178,7 +190,7 @@ void ProbInfoBase::assign(const ProbInfoBase &t) { //////////////////////////////////////////////////////////////////////// -void ProbInfoBase::initialize(const ATCFProbLine &l) { +void ProbInfoBase::initialize(const ATCFProbLine &l, double dland) { clear(); @@ -192,6 +204,7 @@ void ProbInfoBase::initialize(const ATCFProbLine &l) { ValidTime = l.valid(); Lat = l.lat(); Lon = l.lon(); + DLand = dland; return; } @@ -206,7 +219,6 @@ bool ProbInfoBase::is_match(const ATCFProbLine &l) const { Cyclone == l.cyclone_number() && Technique == l.technique() && InitTime == l.warning_time() && - ValidTime == l.valid() && Lat == l.lat() && Lon == l.lon()); } @@ -239,7 +251,7 @@ bool ProbInfoBase::is_match(const TrackInfo &t) const { //////////////////////////////////////////////////////////////////////// -bool ProbInfoBase::add(const ATCFProbLine &l, bool check_dup) { +bool ProbInfoBase::add(const ATCFProbLine &l, double dland, bool check_dup) { // Check for duplicates if(check_dup) { @@ -253,7 +265,7 @@ bool ProbInfoBase::add(const ATCFProbLine &l, bool check_dup) { } // Initialize the header information, if necessary - if(Type == NoATCFLineType) initialize(l); + if(Type == NoATCFLineType) initialize(l, dland); // Check for matching header information if(!is_match(l)) return(false); @@ -297,6 +309,7 @@ void ProbInfoBase::set(const TCStatLine &l) { ValidTime = l.valid(); Lat = atof(l.get_item("ALAT")); Lon = atof(l.get_item("ALON")); + DLand = atof(l.get_item("ADLAND")); NProb = atoi(l.get_item("N_THRESH")); for(int i=1; i<=NProb; i++) { cs << cs_erase << "PROB_" << i; diff --git a/met/src/libcode/vx_tc_util/prob_info_base.h b/met/src/libcode/vx_tc_util/prob_info_base.h index 4f416db319..fff06def24 100644 --- a/met/src/libcode/vx_tc_util/prob_info_base.h +++ b/met/src/libcode/vx_tc_util/prob_info_base.h @@ -50,6 +50,7 @@ class ProbInfoBase { // Location information double Lat; double Lon; + double DLand; // Probability information int NProb; @@ -93,6 +94,7 @@ class ProbInfoBase { int valid_hour() const; double lat() const; double lon() const; + double dland() const; int n_prob() const; double prob(int i) const; double prob_item(int i) const; @@ -101,11 +103,11 @@ class ProbInfoBase { // do stuff // - virtual void initialize(const ATCFProbLine &); + virtual void initialize(const ATCFProbLine &, double); virtual bool is_match (const ATCFProbLine &) const; bool is_match (const TrackInfo &) const; bool has (const ATCFProbLine &) const; - virtual bool add (const ATCFProbLine &, bool check_dup = false); + virtual bool add (const ATCFProbLine &, double, bool check_dup = false); virtual void set (const TCStatLine &); }; @@ -123,6 +125,7 @@ inline unixtime ProbInfoBase::valid() const { return(ValidT inline int ProbInfoBase::valid_hour() const { return(unix_to_sec_of_day(ValidTime)); } inline double ProbInfoBase::lat() const { return(Lat); } inline double ProbInfoBase::lon() const { return(Lon); } +inline double ProbInfoBase::dland() const { return(DLand); } inline int ProbInfoBase::n_prob() const { return(NProb); } inline double ProbInfoBase::prob(int i) const { return(Prob[i]); } inline double ProbInfoBase::prob_item(int i) const { return(ProbItem[i]); } diff --git a/met/src/libcode/vx_tc_util/prob_rirw_info.cc b/met/src/libcode/vx_tc_util/prob_rirw_info.cc index edbd318334..7799e591d3 100644 --- a/met/src/libcode/vx_tc_util/prob_rirw_info.cc +++ b/met/src/libcode/vx_tc_util/prob_rirw_info.cc @@ -149,11 +149,11 @@ int ProbRIRWInfo::rirw_window() const { //////////////////////////////////////////////////////////////////////// -void ProbRIRWInfo::initialize(const ATCFProbLine &l) { +void ProbRIRWInfo::initialize(const ATCFProbLine &l, double dland) { clear(); - ProbInfoBase::initialize(l); + ProbInfoBase::initialize(l, dland); Value = parse_int(l.get_item(ProbRIRWValueOffset).c_str()); Initials = l.get_item(ProbRIRWInitialsOffset); @@ -169,14 +169,15 @@ bool ProbRIRWInfo::is_match(const ATCFProbLine &l) const { if(!ProbInfoBase::is_match(l)) return(false); - return(Value == parse_int(l.get_item(ProbRIRWValueOffset).c_str()) && - RIRWBeg == parse_int(l.get_item(ProbRIRWBegOffset).c_str()) && - RIRWEnd == parse_int(l.get_item(ProbRIRWEndOffset).c_str())); + return(ValidTime == l.valid() && + Value == parse_int(l.get_item(ProbRIRWValueOffset).c_str()) && + RIRWBeg == parse_int(l.get_item(ProbRIRWBegOffset).c_str()) && + RIRWEnd == parse_int(l.get_item(ProbRIRWEndOffset).c_str())); } //////////////////////////////////////////////////////////////////////// -bool ProbRIRWInfo::add(const ATCFProbLine &l, bool check_dup) { +bool ProbRIRWInfo::add(const ATCFProbLine &l, double dland, bool check_dup) { // Check for duplicates if(check_dup) { @@ -190,7 +191,7 @@ bool ProbRIRWInfo::add(const ATCFProbLine &l, bool check_dup) { } // Initialize the header information, if necessary - if(Type == NoATCFLineType) initialize(l); + if(Type == NoATCFLineType) initialize(l, dland); // Check for matching header information if(!is_match(l)) return(false); diff --git a/met/src/libcode/vx_tc_util/prob_rirw_info.h b/met/src/libcode/vx_tc_util/prob_rirw_info.h index 9f47615dbf..2f04fc4baf 100644 --- a/met/src/libcode/vx_tc_util/prob_rirw_info.h +++ b/met/src/libcode/vx_tc_util/prob_rirw_info.h @@ -69,9 +69,9 @@ class ProbRIRWInfo : public ProbInfoBase { // do stuff // - void initialize(const ATCFProbLine &); + void initialize(const ATCFProbLine &, double); bool is_match (const ATCFProbLine &) const; - bool add (const ATCFProbLine &, bool check_dup = false); + bool add (const ATCFProbLine &, double, bool check_dup = false); void set (const TCStatLine &); }; 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 bfb3960226..fb05b4c743 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen.cc +++ b/met/src/tools/tc_utils/tc_gen/tc_gen.cc @@ -20,6 +20,7 @@ // 003 12/31/20 Halley Gotway Add NetCDF output for MET #1430 // 004 01/14/21 Halley Gotway Add GENMPR output for MET #1597 // 005 04/02/21 Halley Gotway Refinements for MET #1714 +// 006 11/04/21 Halley Gotway Add -edeck for MET #1809 // //////////////////////////////////////////////////////////////////////// @@ -56,19 +57,24 @@ using namespace std; //////////////////////////////////////////////////////////////////////// static void process_command_line (int, char **); -static void process_genesis (); +static void score_track_genesis (const GenesisInfoArray &, + const TrackInfoArray &); +static void score_genesis_prob (const GenesisInfoArray &, + const TrackInfoArray &); static void get_atcf_files (const StringArray &, const StringArray &, const char *, StringArray &, StringArray &); -static void process_fcst_tracks (const StringArray &, +static void process_genesis (const StringArray &, const StringArray &, GenesisInfoArray &); -static void process_best_tracks (const StringArray &, +static void process_tracks (const StringArray &, const StringArray &, GenesisInfoArray &, TrackInfoArray &); - +static void process_edecks (const StringArray &, + const StringArray &, + ProbInfoArray &); static void get_genesis_pairs (const TCGenVxOpt &, const ConcatString &, const GenesisInfoArray &, @@ -79,25 +85,44 @@ static void get_genesis_pairs (const TCGenVxOpt &, static void do_genesis_ctc (const TCGenVxOpt &, PairDataGenesis &, GenCTCInfo &); +static void do_probgen_pct (const TCGenVxOpt &, + ProbInfoArray &, + const GenesisInfoArray &, + const TrackInfoArray &, + ProbGenPCTInfo &); static int find_genesis_match (const GenesisInfo &, const GenesisInfoArray &, const TrackInfoArray &, bool, double, int, int); +static int find_probgen_match (const ProbGenInfo &, + const GenesisInfoArray &, + const TrackInfoArray &, + bool, double, int, int); -static void setup_txt_files (int, int); +static void setup_txt_files (int, int, int); static void setup_table (AsciiTable &); static void setup_nc_file (); -static void write_stats (const PairDataGenesis &, +static void write_ctc_stats (const PairDataGenesis &, GenCTCInfo &); -static void write_genmpr_row (StatHdrColumns &, +static void write_ctc_genmpr_row (StatHdrColumns &, const PairDataGenesis &, STATOutputType, AsciiTable &, int &, AsciiTable &, int &); -static void write_genmpr_cols (const PairDataGenesis &, int, +static void write_ctc_genmpr_cols(const PairDataGenesis &, int, AsciiTable &, int, int); + +static void write_pct_stats (ProbGenPCTInfo &); +static void write_pct_genmpr_row (StatHdrColumns &, + ProbGenPCTInfo &, int, + STATOutputType, + AsciiTable &, int &, + AsciiTable &, int &); +static void write_pct_genmpr_cols(ProbGenPCTInfo &, int, int, + AsciiTable &, int, int); + static void write_nc (GenCTCInfo &); static void finish_txt_files (); @@ -105,6 +130,7 @@ static void usage (); static void set_source (const StringArray &, const char *, StringArray &, StringArray &); static void set_genesis (const StringArray &); +static void set_edeck (const StringArray &); static void set_track (const StringArray &); static void set_config (const StringArray &); static void set_out (const StringArray &); @@ -119,8 +145,43 @@ int main(int argc, char *argv[]) { // Process the command line arguments process_command_line(argc, argv); - // Identify and process genesis events and write output - process_genesis(); + // Process the verifying BEST and operational tracks + StringArray track_files, track_files_model_suffix; + GenesisInfoArray best_ga; + TrackInfoArray oper_ta; + + // Get the list of verifing track files + get_atcf_files(track_source, track_model_suffix, atcf_reg_exp, + track_files, track_files_model_suffix); + + mlog << Debug(2) + << "Processing " << track_files.n() + << " verifying track files.\n"; + process_tracks(track_files, track_files_model_suffix, + best_ga, oper_ta); + + // Score genesis events and write output + if(genesis_source.n() > 0) { + score_track_genesis(best_ga, oper_ta); + } + + // Score EDECK genesis probabilities and write output + if(edeck_source.n() > 0) { + score_genesis_prob(best_ga, oper_ta); + } + + // 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(0); } @@ -143,6 +204,7 @@ void process_command_line(int argc, char **argv) { // Add function calls for the arguments cline.add(set_genesis, "-genesis", -1); + cline.add(set_edeck, "-edeck", -1); cline.add(set_track, "-track", -1); cline.add(set_config, "-config", 1); cline.add(set_out, "-out", 1); @@ -154,21 +216,29 @@ void process_command_line(int argc, char **argv) { for(i=genesis_model_suffix.n(); i " - << "the \"-genesis\", \"-track\", and \"-config\" command " + if(genesis_source.n() == 0 && edeck_source.n() == 0) { + mlog << Error << "\nprocess_command_line(int argc, char **argv) -> " + << "at least one of the \"-genesis\" or \"-edeck\" command " << "line options are required\n\n"; usage(); } + // Check for the minimum number of arguments + if(track_source.n() == 0 || config_file.length() == 0) { + mlog << Error << "\nprocess_command_line(int argc, char **argv) -> " + << "the \"-track\" and \"-config\" command line options " + << "are required\n\n"; + usage(); + } + // List the input genesis track files for(i=0; i model_ga_map; map::iterator it; PairDataGenesis pairs; - GenCTCInfo ctc_info; + GenCTCInfo genesis_ctc; // Get the list of genesis track files get_atcf_files(genesis_source, genesis_model_suffix, atcf_gen_reg_exp, @@ -221,25 +298,15 @@ void process_genesis() { mlog << Debug(2) << "Processing " << genesis_files.n() << " forecast genesis track files.\n"; - 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, - track_files, track_files_model_suffix); - - mlog << Debug(2) - << "Processing " << track_files.n() - << " verifying track files.\n"; - process_best_tracks(track_files, track_files_model_suffix, - best_ga, oper_ta); + process_genesis(genesis_files, genesis_files_model_suffix, + fcst_ga); // Setup output files based on the number of techniques present // 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); + setup_txt_files(fcst_ga.n_technique(), 1, n_pair); // If requested, setup the NetCDF output file if(!conf_info.NcInfo.all_false()) setup_nc_file(); @@ -272,16 +339,16 @@ void process_genesis() { // Store the current genesis event model_ga_map[model].add(fcst_ga[j]); - } // end j + } // end for j - // Process the genesis events for each model. + // Process the genesis events for each model for(j=0,it=model_ga_map.begin(); it!=model_ga_map.end(); it++,j++) { // Initialize - ctc_info.clear(); - ctc_info.Model = it->first; - ctc_info.set_vx_opt(&conf_info.VxOpt[i], - &conf_info.NcOutGrid); + genesis_ctc.clear(); + genesis_ctc.Model = it->first; + genesis_ctc.set_vx_opt(&conf_info.VxOpt[i], + &conf_info.NcOutGrid); mlog << Debug(2) << "[Filter " << i+1 << " (" << conf_info.VxOpt[i].Desc @@ -297,33 +364,107 @@ void process_genesis() { best_ga, oper_ta, pairs); // Do the categorical verification - do_genesis_ctc(conf_info.VxOpt[i], pairs, ctc_info); + do_genesis_ctc(conf_info.VxOpt[i], pairs, genesis_ctc); // Write the statistics output - write_stats(pairs, ctc_info); + write_ctc_stats(pairs, genesis_ctc); // Write NetCDF output fields if(!conf_info.VxOpt[i].NcInfo.all_false()) { - write_nc(ctc_info); + write_nc(genesis_ctc); } } // end for j } // end for i n_vx - // Finish output files - finish_txt_files(); + return; +} - // 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"; +void score_genesis_prob(const GenesisInfoArray &best_ga, + const TrackInfoArray &oper_ta) { + int i, j, n, max_n_prob, n_pair; + StringArray edeck_files, edeck_files_model_suffix; + ProbInfoArray fcst_pa, empty_pa; + ConcatString model; + map model_pa_map; + map::iterator it; + ProbGenPCTInfo probgen_pct; - delete nc_out; - nc_out = (NcFile *) 0; + // Get the list of EDECK files + get_atcf_files(edeck_source, edeck_model_suffix, atcf_reg_exp, + edeck_files, edeck_files_model_suffix); + + mlog << Debug(2) + << "Processing " << edeck_files.n() + << " forecast EDECK files.\n"; + process_edecks(edeck_files, edeck_files_model_suffix, + fcst_pa); + + // Count up the total number of probabilities + for(i=0,max_n_prob=0,n_pair=0; i max_n_prob) max_n_prob = n; + n_pair += n; } + // Setup output files based on the number of techniques + setup_txt_files(fcst_pa.n_technique(), max_n_prob, n_pair); + + // 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(model_pa_map.count(model) == 0) { + empty_pa.clear(); + model_pa_map[model] = empty_pa; + } + + // Store the current genesis event + model_pa_map[model].add(fcst_pa.prob_gen(j)); + + } // end for j + + // Process the genesis probabilities for each model + for(j=0,it=model_pa_map.begin(); it!=model_pa_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_prob_gen() + << " probability of genesis forecasts to " << best_ga.n() << " " + << conf_info.BestEventInfo.Technique << " and " + << oper_ta.n() << " " << conf_info.OperTechnique + << " tracks.\n"; + + // Do the probabilistic verification + do_probgen_pct(conf_info.VxOpt[i], it->second, + best_ga, oper_ta, probgen_pct); + + // Write the statistics output + write_pct_stats(probgen_pct); + + } // end for j + } // end for i return; } @@ -598,9 +739,63 @@ void do_genesis_ctc(const TCGenVxOpt &vx_opt, //////////////////////////////////////////////////////////////////////// -int find_genesis_match(const GenesisInfo &fcst_gi, +void do_probgen_pct(const TCGenVxOpt &vx_opt, + ProbInfoArray &model_pa, + const GenesisInfoArray &best_ga, + const TrackInfoArray &oper_ta, + ProbGenPCTInfo &pgi) { + int i, i_bga, j, time_diff; + bool is_event; + const GenesisInfo *bgi; + + // Initialize + pgi.clear(); + pgi.set_vx_opt(&vx_opt); + + // Score each of the probability forecasts + for(i=0; igenesis_time() - + model_pa.prob_gen(i).init(); + is_event = time_diff >= 0 && + time_diff <= (model_pa.prob_gen(i).prob_item(j) * sec_per_hour); + } + else { + is_event = false; + } + + // Store pair info + pgi.add(model_pa.prob_gen(i), j, bgi, is_event); + + } // end for j + } // end for i + + return; +} + + +//////////////////////////////////////////////////////////////////////// + +int find_genesis_match(const GenesisInfo &fcst_gi, const GenesisInfoArray &bga, - const TrackInfoArray &ota, + const TrackInfoArray &ota, bool point2track, double rad, int beg, int end) { int i, j, i_best, i_oper; @@ -684,6 +879,94 @@ int find_genesis_match(const GenesisInfo &fcst_gi, return(i_best); } +//////////////////////////////////////////////////////////////////////// + +int find_probgen_match(const ProbGenInfo &prob_gi, + const GenesisInfoArray &bga, + const TrackInfoArray &ota, + bool point2track, double rad, + int beg, int end) { + int i, j, i_best, i_oper; + + ConcatString case_cs; + case_cs << prob_gi.technique() << " " + << unix_to_yyyymmdd_hhmmss(prob_gi.init()) + << " initialization, " + << unix_to_yyyymmdd_hhmmss(prob_gi.genesis_time()) + << " forecast genesis at (" << prob_gi.lat() << ", " + << prob_gi.lon() << ")"; + + // Search for a BEST track genesis match + for(i=0, i_best=bad_data_int; + i " + mlog << Error << "\nprocess_genesis() -> " << "unable to open file \"" << files[i] << "\"\n\n"; exit(1); } @@ -842,10 +1125,10 @@ void process_fcst_tracks(const StringArray &files, //////////////////////////////////////////////////////////////////////// -void process_best_tracks(const StringArray &files, - const StringArray &model_suffix, - GenesisInfoArray &best_ga, - TrackInfoArray &oper_ta) { +void process_tracks(const StringArray &files, + const StringArray &model_suffix, + GenesisInfoArray &best_ga, + TrackInfoArray &oper_ta) { int i, i_bga, n_lines; ConcatString suffix, gen_basin, case_cs, storm_id; StringArray best_tech, oper_tech; @@ -871,8 +1154,7 @@ void process_best_tracks(const StringArray &files, // Open the current file if(!f.open(files[i].c_str())) { - mlog << Error - << "\nprocess_best_tracks() -> " + mlog << Error << "\nprocess_tracks() -> " << "unable to open file \"" << files[i] << "\"\n\n"; exit(1); } @@ -976,7 +1258,7 @@ void process_best_tracks(const StringArray &files, i--; } else { - mlog << Warning << "\nprocess_best_tracks() -> " + mlog << Warning << "\nprocess_tracks() -> " << case_cs << "neither " << best_ga[i_bga].storm_id() << " nor " << best_gi.storm_id() << " matches the basin!\n\n"; @@ -997,6 +1279,82 @@ void process_best_tracks(const StringArray &files, mlog << Debug(2) << "Found " << best_ga.n() << " BEST genesis events.\n"; + // Dump out very verbose output + if(mlog.verbosity_level() > 6) { + mlog << Debug(6) << best_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; + + // Only process genesis probability lines + if(line.type() == ATCFLineType_ProbGN) { + dland = conf_info.compute_dland(line.lat(), -1.0*line.lon()); + if(probs.add(line, dland, false)) n_lines++; + } + } + + // Close the current file + f.close(); + + } // end for i + + // Dump out the total number of lines + mlog << Debug(3) + << "Read a total of " << n_lines << " " + << atcflinetype_to_string(ATCFLineType_ProbGN) + << " lines from " << files.n() << " input files.\n"; + + // Dump out very verbose output + if(mlog.verbosity_level() >= 6) { + mlog << Debug(6) << probs.serialize_r() << "\n"; + } + return; } @@ -1006,70 +1364,153 @@ void process_best_tracks(const StringArray &files, // //////////////////////////////////////////////////////////////////////// -void setup_txt_files(int n_model, int n_pair) { - int i, n_rows, n_cols; +void setup_txt_files(int n_model, int max_n_prob, int n_pair) { + int i, n_rows, n_cols, stat_rows, stat_cols, n_prob; - // Check to see if the text files have already been set up - if(stat_at.nrows() > 0 || stat_at.ncols() > 0) return; + // Check to see if the stat file stream has already been setup + bool init_from_scratch = (stat_out == (ofstream *) 0); - // Initialize file stream - stat_out = (ofstream *) 0; + // Get the maximum number of probability thresholds + n_prob = conf_info.get_max_n_prob_thresh(); - // Build the file name - stat_file << out_base << stat_file_ext; + // Compute the number of rows/cols needs for each file type + for(i=0, stat_rows=0, stat_cols=0; iOutputMap.at(stat_fho), + gci.VxOpt->output_map(stat_fho), stat_at, i_stat_row, txt_at[i_fho], i_txt_row[i_fho]); } @@ -1172,7 +1613,7 @@ void write_stats(const PairDataGenesis &gpd, 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), + gci.VxOpt->output_map(stat_fho), stat_at, i_stat_row, txt_at[i_fho], i_txt_row[i_fho]); } @@ -1185,7 +1626,7 @@ void write_stats(const PairDataGenesis &gpd, 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), + gci.VxOpt->output_map(stat_ctc), stat_at, i_stat_row, txt_at[i_ctc], i_txt_row[i_ctc]); } @@ -1194,7 +1635,7 @@ void write_stats(const PairDataGenesis &gpd, 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), + gci.VxOpt->output_map(stat_ctc), stat_at, i_stat_row, txt_at[i_ctc], i_txt_row[i_ctc]); } @@ -1210,7 +1651,7 @@ void write_stats(const PairDataGenesis &gpd, 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), + gci.VxOpt->output_map(stat_cts), stat_at, i_stat_row, txt_at[i_cts], i_txt_row[i_cts]); } @@ -1222,7 +1663,7 @@ void write_stats(const PairDataGenesis &gpd, 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), + gci.VxOpt->output_map(stat_cts), stat_at, i_stat_row, txt_at[i_cts], i_txt_row[i_cts]); } @@ -1232,21 +1673,22 @@ void write_stats(const PairDataGenesis &gpd, 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]); + write_ctc_genmpr_row(shc, gpd, + gci.VxOpt->output_map(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) { +void write_ctc_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; @@ -1254,6 +1696,8 @@ void write_genmpr_row(StatHdrColumns &shc, shc.set_line_type(stat_genmpr_str); // Not Applicable + shc.set_fcst_thresh(na_str); + shc.set_obs_thresh(na_str); shc.set_alpha(bad_data_double); // Write a line for each matched pair @@ -1262,7 +1706,7 @@ void write_genmpr_row(StatHdrColumns &shc, // Pointers for current case const GenesisInfo* fgi = gpd.fcst_gen(i); const GenesisInfo* bgi = gpd.best_gen(i); - + // Store timing info shc.set_fcst_lead_sec(gpd.lead_time(i)); ut = (fgi ? fgi->genesis_time() : bgi->genesis_time()); @@ -1277,7 +1721,7 @@ void write_genmpr_row(StatHdrColumns &shc, write_header_cols(shc, stat_at, stat_row); // Write the data columns - write_genmpr_cols(gpd, i, stat_at, stat_row, n_header_columns); + write_ctc_genmpr_cols(gpd, i, stat_at, stat_row, n_header_columns); // If requested, copy row to the text file if(out_type == STATOutputType_Both) { @@ -1296,16 +1740,17 @@ void write_genmpr_row(StatHdrColumns &shc, //////////////////////////////////////////////////////////////////////// - void write_genmpr_cols(const PairDataGenesis &gpd, int i, - AsciiTable &at, int r, int c) { + void write_ctc_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, + // PROB_LEAD, PROB_VAL, // AGEN_INIT, AGEN_FHR, // AGEN_LAT, AGEN_LON, AGEN_DLAND, // BGEN_LAT, BGEN_LON, BGEN_DLAND, @@ -1322,43 +1767,49 @@ void write_genmpr_row(StatHdrColumns &shc, 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 + at.set_entry(r, c+3, // Probability lead time + na_str); + + at.set_entry(r, c+4, // Probability value + na_str); + + at.set_entry(r, c+5, // Fcst genesis initialization time fgi ? unix_to_yyyymmdd_hhmmss(fgi->init()) : na_str); - at.set_entry(r, c+4, // Fcst genesis hour + at.set_entry(r, c+6, // Fcst genesis hour fgi ? fgi->genesis_fhr() : bad_data_int); - at.set_entry(r, c+5, // Fcst track latitude + at.set_entry(r, c+7, // Fcst track latitude fgi ? fgi->lat() : bad_data_double); - at.set_entry(r, c+6, // Fcst track longitude + at.set_entry(r, c+8, // Fcst track longitude fgi ? fgi->lon() : bad_data_double); - at.set_entry(r, c+7, // Fcst track distance to land + at.set_entry(r, c+9, // Fcst track distance to land fgi ? fgi->dland() : bad_data_double); - at.set_entry(r, c+8, // Best track latitude + at.set_entry(r, c+10, // Best track latitude bgi ? bgi->lat() : bad_data_double); - at.set_entry(r, c+9, // Best track longitude + at.set_entry(r, c+11, // Best track longitude bgi ? bgi->lon() : bad_data_double); - at.set_entry(r, c+10, // Best track distance to land + at.set_entry(r, c+12, // Best track distance to land bgi ? bgi->dland() : bad_data_double); - at.set_entry(r, c+11, // Genesis distance + at.set_entry(r, c+13, // Genesis distance gpd.gen_diff(i).DevDist); - at.set_entry(r, c+12, // Genesis time difference + at.set_entry(r, c+14, // Genesis time difference sec_to_hhmmss(gpd.gen_diff(i).DevDSec)); - at.set_entry(r, c+13, // Genesis - Init time + at.set_entry(r, c+15, // Genesis - Init time sec_to_hhmmss(gpd.gen_diff(i).OpsDSec)); - at.set_entry(r, c+14, // Development category + at.set_entry(r, c+16, // Development category genesispaircategory_to_string(gpd.gen_diff(i).DevCategory)); - at.set_entry(r, c+15, // Operational category + at.set_entry(r, c+17, // Operational category genesispaircategory_to_string(gpd.gen_diff(i).OpsCategory)); return; @@ -1366,6 +1817,222 @@ void write_genmpr_row(StatHdrColumns &shc, //////////////////////////////////////////////////////////////////////// +void write_pct_stats(ProbGenPCTInfo &pgi) { + int i, lead_hr, lead_sec; + + // Setup header columns + shc.set_model(pgi.Model.c_str()); + shc.set_desc(pgi.VxOpt->Desc.c_str()); + shc.set_obtype(conf_info.BestEventInfo.Technique.c_str()); + shc.set_mask(pgi.VxOpt->VxMaskName.empty() ? + na_str : pgi.VxOpt->VxMaskName.c_str()); + shc.set_fcst_var(prob_genesis_name); + shc.set_obs_var (prob_genesis_name); + + // Write results for each lead time + for(i=0; ioutput_map(stat_pct) != STATOutputType_None) { + write_pct_row(shc, pgi.PCTMap[lead_hr], + pgi.VxOpt->output_map(stat_pct), + 1, 1, stat_at, i_stat_row, + txt_at[i_pct], i_txt_row[i_pct]); + } + + // Write PSTD output + if(pgi.VxOpt->output_map(stat_pstd) != STATOutputType_None) { + pgi.PCTMap[lead_hr].compute_stats(); + pgi.PCTMap[lead_hr].compute_ci(); + write_pstd_row(shc, pgi.PCTMap[lead_hr], + pgi.VxOpt->output_map(stat_pstd), + 1, 1, stat_at, i_stat_row, + txt_at[i_pstd], i_txt_row[i_pstd]); + } + + // Write PJC output + if(pgi.VxOpt->output_map(stat_pjc) != STATOutputType_None) { + write_pct_row(shc, pgi.PCTMap[lead_hr], + pgi.VxOpt->output_map(stat_pjc), + 1, 1, stat_at, i_stat_row, + txt_at[i_pjc], i_txt_row[i_pjc]); + } + + // Write PRC output + if(pgi.VxOpt->output_map(stat_pjc) != STATOutputType_None) { + write_pct_row(shc, pgi.PCTMap[lead_hr], + pgi.VxOpt->output_map(stat_pjc), + 1, 1, stat_at, i_stat_row, + txt_at[i_prc], i_txt_row[i_prc]); + } + + // Write out GENMPR + if(pgi.VxOpt->output_map(stat_genmpr) != STATOutputType_None) { + shc.set_fcst_var(prob_genesis_name); + shc.set_obs_var (prob_genesis_name); + write_pct_genmpr_row(shc, pgi, lead_hr, + pgi.VxOpt->output_map(stat_genmpr), + stat_at, i_stat_row, + txt_at[i_genmpr], i_txt_row[i_genmpr]); + } + + } // end for i + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void write_pct_genmpr_row(StatHdrColumns &shc, + ProbGenPCTInfo &pgi, + int lead_hr, + 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_fcst_thresh(na_str); + shc.set_obs_thresh(na_str); + shc.set_alpha(bad_data_double); + + // Write a line for each matched pair + for(i=0; igenesis_lead()); + ut = fgi->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() : ut); + 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_pct_genmpr_cols(pgi, lead_hr, 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_pct_genmpr_cols(ProbGenPCTInfo &pgi, + int lead_hr, int index, + AsciiTable &at, int r, int c) { + + // Pointers for current case + const ProbGenInfo *fgi = pgi.FcstGenMap[lead_hr][index]; + const GenesisInfo *bgi = pgi.BestGenMap[lead_hr][index]; + + int i_prob = pgi.FcstIdxMap[lead_hr][index]; + + // + // Genesis Matched Pairs (GENMPR): + // TOTAL, INDEX, STORM_ID, + // PROB_LEAD, PROB_VAL, + // 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 + (int) pgi.FcstGenMap[lead_hr].size()); + + at.set_entry(r, c+1, // Index of current pair + index+1); + + at.set_entry(r, c+2, // Best track Storm ID + (bgi ? bgi->storm_id() : na_str)); + + at.set_entry(r, c+3, // Probability lead time + fgi->prob_item(i_prob)); + + at.set_entry(r, c+4, // Probability value + fgi->prob(i_prob)); + + at.set_entry(r, c+5, // Fcst genesis initialization time + unix_to_yyyymmdd_hhmmss(fgi->init())); + + at.set_entry(r, c+6, // Fcst genesis hour + fgi->genesis_fhr()); + + at.set_entry(r, c+7, // Fcst genesis latitude + fgi->lat()); + + at.set_entry(r, c+8, // Fcst genesis longitude + fgi->lon()); + + at.set_entry(r, c+9, // Fcst genesis distance to land + fgi->dland()); + + at.set_entry(r, c+10, // Best track latitude + bgi ? bgi->lat() : bad_data_double); + + at.set_entry(r, c+11, // Best track longitude + bgi ? bgi->lon() : bad_data_double); + + at.set_entry(r, c+12, // Best track distance to land + bgi ? bgi->dland() : bad_data_double); + + at.set_entry(r, c+13, // Genesis distance + na_str); + + at.set_entry(r, c+14, // Genesis time difference + na_str); + + at.set_entry(r, c+15, // Genesis - Init time + na_str); + + at.set_entry(r, c+16, // Development category + na_str); + + at.set_entry(r, c+17, // Operational category + (pgi.BestEvtMap[lead_hr][index] ? "FYOY" : "FYON" )); + + return; + } + +//////////////////////////////////////////////////////////////////////// + void write_nc(GenCTCInfo &gci) { int i; ConcatString var_name, long_name; @@ -1547,19 +2214,24 @@ void usage() { << ") ***\n\n" << "Usage: " << program_name << "\n" - << "\t-genesis path\n" - << "\t-track path\n" + << "\t-genesis source and/or -edeck source\n" + << "\t-track source\n" << "\t-config file\n" << "\t[-out base]\n" << "\t[-log file]\n" << "\t[-v level]\n\n" - << "\twhere\t\"-genesis path\" is one or more ATCF genesis " + << "\twhere\t\"-genesis source\" is one or more ATCF genesis " << "files, an ASCII file list containing them, or a top-level " << "directory with files matching the regular expression \"" - << atcf_gen_reg_exp << "\" (required).\n" + << atcf_gen_reg_exp << "\" (required if no -edeck).\n" - << "\t\t\"-track path\" is one or more ATCF track " + << "\t\t\"-edeck source\" is one or more ensemble model output " + << "files, an ASCII file list containing them, or a top-level " + << "directory with files matching the regular expression \"" + << atcf_reg_exp << "\" (required if no -genesis).\n" + + << "\t\t\"-track source\" is one or more ATCF track " << "files, an ASCII file list containing them, or a top-level " << "directory with files matching the regular expression \"" << atcf_reg_exp << "\" for the verifying BEST and operational " @@ -1648,6 +2320,12 @@ void set_genesis(const StringArray & a) { //////////////////////////////////////////////////////////////////////// +void set_edeck(const StringArray & a) { + set_source(a, "edeck", edeck_source, edeck_model_suffix); +} + +//////////////////////////////////////////////////////////////////////// + void set_track(const StringArray & a) { set_source(a, "track", track_source, track_model_suffix); } 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 a531652c32..19461c5941 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen.h +++ b/met/src/tools/tc_utils/tc_gen/tc_gen.h @@ -60,22 +60,27 @@ static const char * default_config_filename = // Header columns static const char **txt_columns[n_txt] = { - fho_columns, ctc_columns, cts_columns, genmpr_columns + fho_columns, ctc_columns, cts_columns, + pct_columns, pstd_columns, pjc_columns, + prc_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_genmpr_columns + n_fho_columns, n_ctc_columns, n_cts_columns, + n_pct_columns, n_pstd_columns, n_pjc_columns, + n_prc_columns, n_genmpr_columns }; // Text file abbreviations static const char *txt_file_abbr[n_txt] = { - "fho", "ctc", "cts", "genmpr" + "fho", "ctc", "cts", "pct", "pstd", "pjc", "prc", "genmpr" }; -const ConcatString genesis_name ("GENESIS"); -const ConcatString genesis_dev_name("GENESIS_DEV"); -const ConcatString genesis_ops_name("GENESIS_OPS"); +const ConcatString genesis_name ("GENESIS"); +const ConcatString genesis_dev_name ("GENESIS_DEV"); +const ConcatString genesis_ops_name ("GENESIS_OPS"); +const ConcatString prob_genesis_name("PROB_GENESIS"); // Maximum Best track cyclone number to be processed // Cyclone numbers > 50 are for testing or invests @@ -89,7 +94,8 @@ static const int max_best_cyclone_number = 50; // Input files static StringArray genesis_source, genesis_model_suffix; -static StringArray track_source, track_model_suffix; +static StringArray edeck_source, edeck_model_suffix; +static StringArray track_source, track_model_suffix; static ConcatString config_file; static TCGenConfInfo conf_info; 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 02f75d16d7..0592c667b4 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 @@ -164,6 +164,7 @@ void TCGenVxOpt::clear() { OpsHitBeg = OpsHitEnd = bad_data_int; DiscardFlag = false; DevFlag = OpsFlag = false; + ProbGenThresh.clear(); CIAlpha = bad_data_double; OutputMap.clear(); NcInfo.clear(); @@ -288,7 +289,12 @@ void TCGenVxOpt::process_config(Dictionary &dict) { << " must be set to true!\n\n"; exit(1); } - + + // Conf: prob_genesis_thresh + ProbGenThresh = dict.lookup_thresh_array(conf_key_prob_genesis_thresh); + ProbGenThresh = string_to_prob_thresh(ProbGenThresh.get_str().c_str()); + check_prob_thresh(ProbGenThresh); + // Conf: ci_alpha CIAlpha = dict.lookup_double(conf_key_ci_alpha); @@ -508,6 +514,62 @@ bool TCGenVxOpt::is_keeper(const GenesisInfo &gi) const { //////////////////////////////////////////////////////////////////////// +bool TCGenVxOpt::is_keeper(const ProbGenInfo &gi) const { + bool keep = true; + + // ATCF ID processed elsewhere + + // Check storm id + if(StormId.n() > 0 && + !has_storm_id(StormId, gi.basin(), gi.cyclone(), gi.init())) + keep = false; + + // Check storm name: no included in genesis probabilities + + // Initialization time + 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(gi.init_hour())) + keep = false; + + // Lead and valid times: + // ProbGenInfo objects can contain multiple lead/valid times. + // Do not filter by them here. + + // Poly masking + if(VxPolyMask.n_points() > 0 && + !VxPolyMask.latlon_is_inside(gi.lat(), gi.lon())) + keep = false; + + // Area masking + if(!VxAreaMask.is_empty()) { + double 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; + } + else { + keep = VxAreaMask(nint(x), nint(y)); + } + } + + // Distance to land + if((DLandThresh.get_type() != no_thresh_type) && + (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)); } @@ -823,6 +885,18 @@ STATOutputType TCGenConfInfo::output_map(STATLineType t) const { return(OutputMap.at(t)); } +//////////////////////////////////////////////////////////////////////// + +int TCGenConfInfo::get_max_n_prob_thresh() const { + int i, n; + + for(i=0,n=0; iProbGenThresh); + DefaultPCT.allocate_n_alpha(1); + DefaultPCT.alpha[0] = VxOpt->CIAlpha; + + return; +} + +//////////////////////////////////////////////////////////////////////// + +void ProbGenPCTInfo::add(const ProbGenInfo &fgi, int index, + const GenesisInfo *bgi, bool is_event) { + int i; + unixtime ut; + + // Store the model name + if(Model.empty()) Model = fgi.technique(); + + // Track the range of forecast initalization times + ut = fgi.init(); + if(InitBeg == 0 || InitBeg > ut) InitBeg = ut; + if(InitEnd == 0 || InitEnd < ut) InitEnd = ut; + + // Track the range of verifying BEST genesis events + if(bgi) { + ut = bgi->genesis_time(); + if(BestBeg == 0 || BestBeg > ut) BestBeg = ut; + if(BestEnd == 0 || BestEnd < ut) BestEnd = ut; + } + + // Current lead time and probability value + int lead_hr = nint(fgi.prob_item(index)); + double prob = fgi.prob(index) / 100.0; + + // Add new map entries, if needed + if(!LeadTimes.has(lead_hr)) { + + LeadTimes.add(lead_hr); + vector empty_fgi; + vector empty_idx; + vector empty_bgi; + vector empty_evt; + + PCTMap [lead_hr] = DefaultPCT; + FcstGenMap[lead_hr] = empty_fgi; + FcstIdxMap[lead_hr] = empty_idx; + BestGenMap[lead_hr] = empty_bgi; + BestEvtMap[lead_hr] = empty_evt; + } + + // Update map entries + FcstGenMap[lead_hr].push_back(&fgi); + FcstIdxMap[lead_hr].push_back(index); + BestGenMap[lead_hr].push_back(bgi); + BestEvtMap[lead_hr].push_back(is_event); + + // Increment counts + if(is_event) PCTMap[lead_hr].pct.inc_event (prob); + else PCTMap[lead_hr].pct.inc_nonevent(prob); + + return; +} + +//////////////////////////////////////////////////////////////////////// diff --git a/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h b/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h index a02a62e562..fe1b3c3f57 100644 --- a/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h +++ b/met/src/tools/tc_utils/tc_gen/tc_gen_conf_info.h @@ -29,16 +29,24 @@ static const int i_fho = 0; static const int i_ctc = 1; static const int i_cts = 2; -static const int i_genmpr = 3; +static const int i_pct = 3; +static const int i_pstd = 4; +static const int i_pjc = 5; +static const int i_prc = 6; +static const int i_genmpr = 7; -static const int n_txt = 4; +static const int n_txt = 8; // Text file type static const STATLineType txt_file_type[n_txt] = { stat_fho, // 0 stat_ctc, // 1 stat_cts, // 2 - stat_genmpr // 3 + stat_pct, // 3 + stat_pstd, // 4 + stat_pjc, // 5 + stat_prc, // 6 + stat_genmpr // 7 }; // Names for output data plane types @@ -78,7 +86,6 @@ struct TCGenNcOutInfo { bool do_best_fy_oy; bool do_best_fn_oy; - ////////////////////////////////////////////////////////////////// TCGenNcOutInfo(); @@ -145,6 +152,7 @@ class TCGenVxOpt { bool DiscardFlag, DevFlag, OpsFlag; // Output file options + ThreshArray ProbGenThresh; double CIAlpha; map OutputMap; TCGenNcOutInfo NcInfo; @@ -161,6 +169,7 @@ class TCGenVxOpt { void parse_nc_info(Dictionary &); bool is_keeper(const GenesisInfo &) const; + bool is_keeper(const ProbGenInfo &) const; STATOutputType output_map(STATLineType) const; }; @@ -242,6 +251,9 @@ class TCGenConfInfo { int compression_level(); STATOutputType output_map(STATLineType) const; + + // Maximum across all verification tasks + int get_max_n_prob_thresh() const; }; //////////////////////////////////////////////////////////////////////// @@ -303,6 +315,49 @@ class GenCTCInfo { //////////////////////////////////////////////////////////////////////// +class ProbGenPCTInfo { + + private: + + void init_from_scratch(); + + PCTInfo DefaultPCT; + + public: + + ProbGenPCTInfo(); + ~ProbGenPCTInfo(); + + ////////////////////////////////////////////////////////////////// + + ConcatString Model; + unixtime InitBeg, InitEnd; + unixtime BestBeg, BestEnd; + const TCGenVxOpt* VxOpt; + IntArray LeadTimes; + + // Map of lead times to PCT tables + map PCTMap; + + // Map of lead times to vectors of pair info + map> FcstGenMap; + map> FcstIdxMap; + map> BestGenMap; + map> BestEvtMap; + + ////////////////////////////////////////////////////////////////// + + void clear(); + + void set_vx_opt(const TCGenVxOpt *); + + void add(const ProbGenInfo &, int, + const GenesisInfo *, bool); + +}; + +//////////////////////////////////////////////////////////////////////// + #endif /* __TC_GEN_CONF_INFO_H__ */ //////////////////////////////////////////////////////////////////////// diff --git a/met/src/tools/tc_utils/tc_pairs/Makefile.am b/met/src/tools/tc_utils/tc_pairs/Makefile.am index d5d4436698..1dd9e1275f 100644 --- a/met/src/tools/tc_utils/tc_pairs/Makefile.am +++ b/met/src/tools/tc_utils/tc_pairs/Makefile.am @@ -38,6 +38,7 @@ tc_pairs_LDADD = -lvx_stat_out \ -lvx_gsl_prob \ -lvx_pb_util \ -lvx_cal \ + -lvx_nav \ -lvx_util \ -lvx_math \ -lvx_color \ 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 7a70c0736a..5bf073959e 100644 --- a/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc +++ b/met/src/tools/tc_utils/tc_pairs/tc_pairs.cc @@ -437,7 +437,7 @@ void process_edecks(const TrackInfoArray &bdeck_tracks) { // Filter the EDECK tracks using the config file information mlog << Debug(2) - << "Filtering " << edeck_probs.n_probs() + << "Filtering " << edeck_probs.n_prob_rirw() << " probabilities based on config file settings.\n"; filter_probs(edeck_probs); @@ -446,11 +446,11 @@ void process_edecks(const TrackInfoArray &bdeck_tracks) { // mlog << Debug(2) - << "Matching " << edeck_probs.n_probs() + << "Matching " << edeck_probs.n_prob_rirw() << " EDECK probabilities to " << bdeck_tracks.n() << " BDECK tracks.\n"; - for(i=0; idland()); + cur_ri.set_bdland(compute_dland(cur_ri.blat(), -1.0*cur_ri.blon())); // Store the current pair prob_rirw_pairs.add(cur_ri); @@ -640,6 +640,7 @@ void process_prob_files(const StringArray &files, const StringArray &model_suffix, ProbInfoArray &probs) { int i, cur_read, cur_add, tot_read, tot_add; + double dland; LineDataFile f; ConcatString suffix; ATCFProbLine line; @@ -683,10 +684,13 @@ void process_prob_files(const StringArray &files, // Check the keep status if(!is_keeper(&line)) continue; - // Attempt to add the current line to ProbInfoArray - if(probs.add(line, conf_info.CheckDup)) { - cur_add++; - tot_add++; + // Only process probability of RI lines + if(line.type() == ATCFLineType_ProbRI) { + dland = compute_dland(line.lat(), -1.0*line.lon()); + if(probs.add(line, dland, conf_info.CheckDup)) { + cur_add++; + tot_add++; + } } } @@ -708,7 +712,7 @@ void process_prob_files(const StringArray &files, // Dump out the track information mlog << Debug(3) - << "Identified " << probs.n_probs() << " probabilities.\n"; + << "Identified " << probs.n_prob_rirw() << " probabilities.\n"; // Dump out very verbose output if(mlog.verbosity_level() >= 5) { @@ -717,9 +721,9 @@ void process_prob_files(const StringArray &files, } // Dump out track info else { - for(i=0; iserialize() << "\n"; } } @@ -928,7 +932,7 @@ void filter_probs(ProbInfoArray &probs) { // Loop through the pairs and determine which should be retained // The is_keeper() function has already filtered by model, storm id, // basin, cyclone, initialization time, and initialization hour. - for(i=0; i 0 && @@ -977,11 +981,11 @@ void filter_probs(ProbInfoArray &probs) { // Print summary filtering info mlog << Debug(3) - << "Total probabilities read = " << p.n_probs() << "\n" - << "Total probabilities kept = " << probs.n_probs() << "\n" - << "Rejected for valid time = " << n_vld << "\n" - << "Rejected for init mask = " << n_mask_init << "\n" - << "Rejected for valid mask = " << n_mask_vld << "\n"; + << "Total probabilities read = " << p.n_prob_rirw() << "\n" + << "Total probabilities kept = " << probs.n_prob_rirw() << "\n" + << "Rejected for valid time = " << n_vld << "\n" + << "Rejected for init mask = " << n_mask_init << "\n" + << "Rejected for valid mask = " << n_mask_vld << "\n"; return; } diff --git a/test/config/TCGenConfig_prob b/test/config/TCGenConfig_prob new file mode 100644 index 0000000000..db075b4479 --- /dev/null +++ b/test/config/TCGenConfig_prob @@ -0,0 +1,288 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TC-Gen configuration file. +// +// For additional information, please see the MET User's Guide. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// ATCF file format reference: +// http://www.nrlmry.navy.mil/atcf_web/docs/database/new/abrdeck.html +// + +//////////////////////////////////////////////////////////////////////////////// +// +// Genesis event definition criteria +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Model initialization frequency in hours, starting at 0 +// +init_freq = 6; + +// +// Valid hour frequency to be analyzed in hours, starting at 0 +// +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 +// +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 +// track point where all of these criteria are met. +// +fcst_genesis = { + vmax_thresh = NA; + mslp_thresh = NA; +} + +// +// 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 valid time of the +// first track point where all of these criteria are met. +// +best_genesis = { + technique = "BEST"; + category = [ "TD", "TS" ]; + vmax_thresh = NA; + mslp_thresh = NA; +} + +// +// Operational track technique name +// +oper_technique = "CARQ"; + +//////////////////////////////////////////////////////////////////////////////// +// +// Track filtering options +// May be specified separately in each filter array entry. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Array of dictionaries containing the track filtering options +// If empty, a single filter is defined using the top-level settings. +// +filter = [ + { + desc = "ALL"; + } +]; + +// +// Description written to output DESC column +// +desc = "ALL"; + +// +// Forecast ATCF ID's +// If empty, all ATCF ID's found will be processed. +// Statistics will be generated separately for each ATCF ID. +// +model = []; + +// +// BEST and operational track storm identifiers +// +storm_id = []; + +// +// BEST and operational track storm names +// +storm_name = []; + +// +// Forecast and operational initialization times to include or exclude +// +init_beg = ""; +init_end = ""; +init_inc = []; +init_exc = []; + +// +// Forecast, BEST, and operational valid time window +// +valid_beg = ""; +valid_end = ""; + +// +// Forecast and operational initialization hours +// +init_hour = []; + +// +// Forecast and operational lead times in hours +// +lead = []; + +// +// Spatial masking region (path to gridded data file or polyline file) +// +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. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Genesis matching logic. Compare the forecast genesis point to all points in +// the Best track (TRUE) or the single Best track genesis point (FALSE). +// +genesis_match_point_to_track = FALSE; + +// +// Radius in km to search for a matching genesis event +// +genesis_match_radius = 500; + +// +// Time window in hours, relative to the model genesis time, to search for a +// matching Best track point +// +genesis_match_window = { + beg = -6; + end = 6; +} + +// +// Radius in km for a development scoring method hit +// +dev_hit_radius = 500; + +// +// Time window in hours, relative to the model genesis time, for a development +// scoring method hit +// +dev_hit_window = { + beg = -24; + end = 24; +} + +// +// Time window in hours for the Best track genesis minus model initialization +// time difference for an operational scoring method hit +// +ops_hit_window = { + beg = 0; + end = 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 +// +dev_method_flag = TRUE; +ops_method_flag = TRUE; + +//////////////////////////////////////////////////////////////////////////////// +// +// Output options +// May be specified separately in each filter array entry. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Probability of genesis thresholds +// +prob_genesis_thresh = ==0.25; + +// +// Confidence interval alpha value +// +ci_alpha = 0.05; + +// +// Statistical output types +// +output_flag = { + fho = NONE; + ctc = NONE; + cts = NONE; + pct = BOTH; + pstd = BOTH; + pjc = BOTH; + prc = BOTH; + genmpr = BOTH; +} + +// +// 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. +// +version = "V10.1.0"; diff --git a/test/hdr/met_10_1.hdr b/test/hdr/met_10_1.hdr index 641ac25813..84f5b5a950 100644 --- a/test/hdr/met_10_1.hdr +++ b/test/hdr/met_10_1.hdr @@ -28,7 +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 +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 PROB_LEAD PROB_VAL 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 SSIDX : 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 FCST_MODEL REF_MODEL N_INIT N_TERM N_VLD SS_INDEX 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 diff --git a/test/xml/unit_tc_gen.xml b/test/xml/unit_tc_gen.xml index d1aa664166..86245745d8 100644 --- a/test/xml/unit_tc_gen.xml +++ b/test/xml/unit_tc_gen.xml @@ -6,7 +6,7 @@ - + ]> @@ -17,8 +17,8 @@ &MET_BIN;/tc_gen \ - -genesis &DATA_DIR;/suite1/2016*/genesis*2016* \ - -track &DATA_DIR;/atcf/2016 \ + -genesis &DATA_DIR;/genesis/suite1/2016*/genesis*2016* \ + -track &DATA_DIR;/genesis/atcf/2016 \ -config &CONFIG_DIR;/TCGenConfig_2016 \ -out &OUTPUT_DIR;/tc_gen/tc_gen_2016 \ -log &OUTPUT_DIR;/tc_gen/tc_gen_2016.log \ @@ -31,4 +31,26 @@ + + + + &MET_BIN;/tc_gen + \ + -edeck &DATA_DIR;/edeck/eal152020*dat \ + -track &DATA_DIR;/bdeck/bal152020.dat \ + -config &CONFIG_DIR;/TCGenConfig_prob \ + -out &OUTPUT_DIR;/tc_gen/tc_gen_prob \ + -log &OUTPUT_DIR;/tc_gen/tc_gen_prob.log \ + -v 4 + + + &OUTPUT_DIR;/tc_gen/tc_gen_prob.stat + &OUTPUT_DIR;/tc_gen/tc_gen_prob_pct.txt + &OUTPUT_DIR;/tc_gen/tc_gen_prob_pstd.txt + &OUTPUT_DIR;/tc_gen/tc_gen_prob_pjc.txt + &OUTPUT_DIR;/tc_gen/tc_gen_prob_prc.txt + &OUTPUT_DIR;/tc_gen/tc_gen_prob_genmpr.txt + + +