diff --git a/docs/Users_Guide/tc-pairs.rst b/docs/Users_Guide/tc-pairs.rst index 36b7d86ab5..c7a56a05ff 100644 --- a/docs/Users_Guide/tc-pairs.rst +++ b/docs/Users_Guide/tc-pairs.rst @@ -7,7 +7,7 @@ TC-Pairs Tool Introduction ============ -The TC-Pairs tool provides verification for tropical cyclone forecasts in ATCF file format. It matches an ATCF format tropical cyclone (TC) forecast with a second ATCF format reference TC dataset (most commonly the Best Track analysis). The TC-Pairs tool processes both track and intensity adeck data and probabilistic edeck data. The adeck matched pairs contain position errors, as well as wind, sea level pressure, and distance to land values for each TC dataset. The edeck matched pairs contain probabilistic forecast values and the verifying observation values. The pair generation can be subset based on user-defined filtering criteria. Practical aspects of the TC-Pairs tool are described in :numref:`TC-Pairs_Practical-information`. +The TC-Pairs tool provides verification for tropical cyclone forecasts in ATCF file format. It matches an ATCF format tropical cyclone (TC) forecast with a second ATCF format reference TC dataset (most commonly the Best Track analysis). The TC-Pairs tool processes both track and intensity adeck data and probabilistic edeck data. The adeck matched pairs contain position errors, as well as values for each TC dataset, including wind speed, wind radii, minimum sea level pressure, and distance to land. The edeck matched pairs contain probabilistic forecast values and the verifying observation values. The pair generation can be subset based on user-defined filtering criteria. Practical aspects of the TC-Pairs tool are described in :numref:`TC-Pairs_Practical-information`. Scientific and Statistical Aspects ================================== @@ -21,7 +21,7 @@ TC diagnostics provide information about a TC's structure or its environment. Ea * Inner core diagnostics provide information about the structure of the storm near the storm center. Examples include the intensity of the storm and the radius of maximum winds. - * Large scale diagnostics provide information about quantities that characterize its environment. Examples include environmental vertical wind shear, total precipitable water, the average relative humidity, measures of convective instability, and the upper bound of intensity that a storm may be expected to achieve in its current environment. These diagnostics are typically derived from model fields as an average of the quantity of interest over either a circular area or an annulus centered on the storm center. Often, the storm center is taken to be the underlying model's storm center. In other cases, the diagnostics may be computed along some other specified track. + * Large scale diagnostics provide information about quantities that characterize its environment. Examples include environmental vertical wind shear, total precipitable water, relative humidity, convective instability, and the upper bound of intensity that a storm may be expected to achieve in its current environment. These diagnostics are typically derived from model fields as an average of the quantity of interest over either a circular area or an annulus centered on the storm center. Often, the storm center is taken to be the underlying model's storm center. In other cases, the diagnostics may be computed along some other specified track. * Ocean-based diagnostics provide information about the sea's thermal characteristics in the vicinity of the storm center. Examples include the sea surface temperature, ocean heat content, and the depth of warm water of a given temperature. @@ -163,7 +163,7 @@ ____________________ model = [ "DSHP", "LGEM", "HWRF" ]; -The **model** variable contains a list of comma-separated models to be used. Each model is identified with an ATCF TECH ID (normally four unique characters). This model identifier should match the model column in the ATCF format input file. An empty list indicates that all models in the input file(s) will be processed. Note that when reading ATCF track data, all instances of the string AVN are automatically replaced with GFS. +The **model** variable contains a list of comma-separated models to be used. Each model is identified with an ATCF TECH ID (normally four unique characters). This model identifier should match the model column in the ATCF format input file. An empty list indicates that all models in the input file(s) will be processed. Note that when reading ATCF track data, all instances of the string **AVN** are automatically replaced with **GFS**. ____________________ @@ -195,15 +195,26 @@ ____________________ consensus = [ { - name = "CON1"; - members = [ "MOD1", "MOD2", "MOD3" ]; - required = [ true, false, false ]; - min_req = 2; + name = "CON1"; + members = [ "MOD1", "MOD2", "MOD3" ]; + required = [ true, false, false ]; + min_req = 2; + diag_required = [ false, false, false ]; + min_diag_req = 0; write_members = TRUE; } ]; -The **consensus** field allows the user to generate a user-defined consensus forecasts from any number of models. All models used in the consensus forecast need to be included in the **model** field (first entry in **TCPairsConfig_default**). The name field is the desired consensus model name. The **members** field is a comma-separated list of model IDs that make up the members of the consensus. The **required** field is a comma-separated list of true/false values associated with each consensus member. If a member is designated as true, the member is required to be present in order for the consensus to be generated. If a member is false, the consensus will be generated regardless of whether the member is present. The length of the required array must be the same length as the members array. The **min_req** field is the number of members required in order for the consensus to be computed. The required and min_req field options are applied at each forecast lead time. If any member of the consensus has a non-valid position or intensity value, the consensus for that valid time will not be generated. The **write_members** field is a boolean that indicates whether or not to write output for the individual consensus members. If set to true, standard output will show up for all members. If set to false, output for the consensus members is excluded from the output, even if they are used to define other consensus tracks in the configuration file. If a consensus model is defined in the configuration file, there will be non-missing output for the consensus track variables in the output file (NUM_MEMBERS, TRACK_SPREAD, TRACK_STDEV, MSLP_STDEV, MAX_WIND_STDEV). See the TCMPR line type definitions below. +The **consensus** array allows users to derive consensus forecasts from any number of models. A consensus forecast is computed as the average intensity and location of the members which comprise it. TC-Pairs attempts to derive consensus forecasts for each unique storm ID and initialization time found in the input track data. Each array entry is a dictionary which defines the consensus name, membership, and requirements: + +- The **name** field is a string defining the consensus model name to be written. +- The **members** field is a comma-separated array of model ID stings which define the members of the consensus. +- The **required** field is a comma-separated array of true/false values associated with each consensus member. If a member is designated as true, that member must be present in order for the consensus to be generated. If a member is false, the consensus will be generated regardless of whether or not the member is present. The required array can either be empty or have the same length as the members array. If empty, it defaults to all false. +- The **min_req** field is the number of members required in order for the consensus to be computed. The **required** and **min_req** field options are applied at each forecast lead time. If any member of the consensus has a non-valid position or intensity value, the consensus for that valid time will not be generated. +- Tropical cyclone diagnostics, if provided on the command line, are included in the computation of consensus tracks. The consensus diagnostics are computed as the mean of the diagnostics for the members. The **diag_required** and **min_diag_req** entries apply the same logic described above, but to the computation of each consensus diagnostic value rather than the consensus track location and intensity. If **diag_required** is missing or an empty list, it defaults to all false. If **min_diag_req** is missing, it default to 0. +- The **write_members** field is a boolean that indicates whether or not to write track output for the individual consensus members. If set to true, standard output will show up for all members. If set to false, output for the consensus members is excluded from the output, even if they are used to define other consensus tracks in the configuration file. + +Users should take care to avoid filtering out track data for the consensus members with the **model** field, described above. Either set **model** to an empty list to process all input track data or include all of the consensus members in the **model** list. Use the **write_members** field, not the **model** field, to suppress track output for consensus members. ____________________ @@ -420,13 +431,13 @@ TC-Pairs produces output in TCST format. The default output file name can be ove - Version number * - 2 - AMODEL - - User provided text string designating model name + - User-provided text string designating the forecast ATCF ID * - 3 - BMODEL - - User provided text string designating model name + - User-provided text string designating the reference ATCF ID * - 4 - DESC - - User provided description text string + - User-provided description text string * - 5 - STORM_ID - BBCCYYYY designation of storm diff --git a/internal/test_unit/config/TCPairsConfig_CONSENSUS b/internal/test_unit/config/TCPairsConfig_CONSENSUS index d5f6a644b0..01e4e53453 100644 --- a/internal/test_unit/config/TCPairsConfig_CONSENSUS +++ b/internal/test_unit/config/TCPairsConfig_CONSENSUS @@ -96,59 +96,73 @@ interp12 = NONE; // consensus = [ { - name = "UEMN_CONS"; - members = [ "UE00", "UE01", "UE02", "UE03", "UE04", - "UE05", "UE06", "UE07", "UE08", "UE09", - "UE10", "UE11", "UE12", "UE13", "UE14", - "UE15", "UE16", "UE17", "UE18", "UE19", - "UE20", "UE21", "UE22", "UE23", "UE24", - "UE25", "UE26", "UE27", "UE28", "UE29", - "UE30", "UE31", "UE32", "UE33", "UE34", - "UE35" ]; - required = []; - min_req = 36; + name = "UEMN_CONS"; + members = [ "UE00", "UE01", "UE02", "UE03", "UE04", + "UE05", "UE06", "UE07", "UE08", "UE09", + "UE10", "UE11", "UE12", "UE13", "UE14", + "UE15", "UE16", "UE17", "UE18", "UE19", + "UE20", "UE21", "UE22", "UE23", "UE24", + "UE25", "UE26", "UE27", "UE28", "UE29", + "UE30", "UE31", "UE32", "UE33", "UE34", + "UE35" ]; + required = []; + min_req = 36; + diag_required = []; + diag_min_req = 0; write_members = FALSE; }, { - name = "HCCA_CONS"; - members = [ "AEMI", "GFSI", "CTCI", "DSHP", "EGRI", "EMN2", "EMXI", "HWFI", "LGEM" ]; - required = []; - min_req = 8; + name = "HCCA_CONS"; + members = [ "AEMI", "GFSI", "CTCI", "DSHP", "EGRI", "EMN2", "EMXI", "HWFI", "LGEM" ]; + required = []; + min_req = 8; + diag_required = []; + diag_min_req = 0; write_members = TRUE; }, { - name = "GFEX_CONS"; - members = [ "GFSI", "EMXI" ]; - required = []; - min_req = 2; + name = "GFEX_CONS"; + members = [ "GFSI", "EMXI" ]; + required = []; + min_req = 2; + diag_required = []; + diag_min_req = 0; write_members = TRUE; }, { - name = "TVCA_CONS"; - members = [ "GFSI", "EGRI", "HWFI", "EMHI", "CTCI", "EMNI" ]; - required = []; - min_req = 2; + name = "TVCA_CONS"; + members = [ "GFSI", "EGRI", "HWFI", "EMHI", "CTCI", "EMNI" ]; + required = []; + min_req = 2; + diag_required = []; + diag_min_req = 0; write_members = TRUE; }, { - name = "TVCX_CONS"; - members = [ "GFSI", "EMXI", "EMXI", "HWFI", "CTCI", "EGRI" ]; - required = [ TRUE, TRUE, FALSE, FALSE, FALSE, FALSE ]; - min_req = 2; + name = "TVCX_CONS"; + members = [ "GFSI", "EMXI", "EMXI", "HWFI", "CTCI", "EGRI" ]; + required = [ TRUE, TRUE, FALSE, FALSE, FALSE, FALSE ]; + min_req = 2; + diag_required = []; + diag_min_req = 0; write_members = TRUE; }, { - name = "ICON_CONS"; - members = [ "DSHP", "LGEM", "HWFI", "HMNI" ]; - required = [ TRUE, TRUE, TRUE, TRUE ]; - min_req = 4; + name = "ICON_CONS"; + members = [ "DSHP", "LGEM", "HWFI", "HMNI" ]; + required = [ TRUE, TRUE, TRUE, TRUE ]; + min_req = 4; + diag_required = []; + diag_min_req = 0; write_members = TRUE; }, { - name = "IVCN_CONS"; - members = [ "DSHP", "LGEM", "HWFI", "HMNI", "CTCI" ]; - required = []; - min_req = 2; + name = "IVCN_CONS"; + members = [ "DSHP", "LGEM", "HWFI", "HMNI", "CTCI" ]; + required = []; + min_req = 2; + diag_required = []; + diag_min_req = 0; write_members = TRUE; } ]; diff --git a/internal/test_unit/config/TCPairsConfig_DIAGNOSTICS_CONSENSUS b/internal/test_unit/config/TCPairsConfig_DIAGNOSTICS_CONSENSUS index 49d3963dd1..e4bcc5e95e 100644 --- a/internal/test_unit/config/TCPairsConfig_DIAGNOSTICS_CONSENSUS +++ b/internal/test_unit/config/TCPairsConfig_DIAGNOSTICS_CONSENSUS @@ -14,7 +14,7 @@ // // Models // -model = [ "GFSI", "EMXI" ]; +model = [ "GFSO", "HWRF", "HMON" ]; // // Description @@ -96,10 +96,12 @@ interp12 = NONE; // consensus = [ { - name = "GFEX_CONS"; - members = [ "GFSI", "EMXI" ]; - required = []; - min_req = 2; + name = "GFSO_HWRF_HMON_CONS"; + members = [ "GFSO", "HWRF", "HMON" ]; + required = []; + min_req = 2; + diag_required = [ TRUE, FALSE, FALSE ]; + diag_min_req = 2; write_members = TRUE; } ]; diff --git a/internal/test_unit/xml/unit_tc_pairs.xml b/internal/test_unit/xml/unit_tc_pairs.xml index fde2193181..4dd547e2a6 100644 --- a/internal/test_unit/xml/unit_tc_pairs.xml +++ b/internal/test_unit/xml/unit_tc_pairs.xml @@ -216,8 +216,9 @@ \ -adeck &DATA_DIR;/adeck/aal092022.dat \ -bdeck &DATA_DIR;/bdeck/bal092022.dat \ - -diag CIRA_DIAG_RT &DATA_DIR;/diag/cira_diag_rt/2022/GFSI/sal092022_avni_doper_20220926*_diag.dat \ - -diag CIRA_DIAG_RT &DATA_DIR;/diag/cira_diag_rt/2022/EMXI/sal092022_emxi_doper_20220926*_diag.dat \ + -diag CIRA_DIAG_RT &DATA_DIR;/diag/cira_diag_rt/2022/sal092022_avno_doper_20220926*_diag.dat \ + -diag CIRA_DIAG_RT &DATA_DIR;/diag/cira_diag_rt/2022/sal092022_hmon_doper_20220926*_diag.dat \ + -diag CIRA_DIAG_RT &DATA_DIR;/diag/cira_diag_rt/2022/sal092022_hwrf_doper_20220926*_diag.dat \ -config &CONFIG_DIR;/TCPairsConfig_DIAGNOSTICS_CONSENSUS \ -out &OUTPUT_DIR;/tc_pairs/al092022_20220926_DIAGNOSTICS_CONSENSUS \ -log &OUTPUT_DIR;/tc_pairs/tc_pairs_DIAGNOSTICS_CONSENSUS.log \ diff --git a/src/basic/vx_config/config_constants.h b/src/basic/vx_config/config_constants.h index bd3ffe0219..8937a43d82 100644 --- a/src/basic/vx_config/config_constants.h +++ b/src/basic/vx_config/config_constants.h @@ -1099,6 +1099,8 @@ static const char conf_key_consensus[] = "consensus"; static const char conf_key_members[] = "members"; static const char conf_key_required[] = "required"; static const char conf_key_min_req[] = "min_req"; +static const char conf_key_diag_required[] = "diag_required"; +static const char conf_key_min_diag_req[] = "diag_min_req"; static const char conf_key_write_members[] = "write_members"; static const char conf_key_lag_time[] = "lag_time"; static const char conf_key_best_technique[] = "best_technique"; diff --git a/src/libcode/vx_tc_util/track_info.cc b/src/libcode/vx_tc_util/track_info.cc index 86a39b15ed..ec1fdd00c8 100644 --- a/src/libcode/vx_tc_util/track_info.cc +++ b/src/libcode/vx_tc_util/track_info.cc @@ -972,12 +972,15 @@ int TrackInfoArray::add_diag_data(DiagFile &diag_file, const StringArray &diag_n //////////////////////////////////////////////////////////////////////// TrackInfo consensus(const TrackInfoArray &tracks, - const ConcatString &model, int req, - const StringArray &req_list) { + const ConcatString &model, + const StringArray &req_cons_mem, const int min_cons_req, + const StringArray &req_diag_mem, const int min_diag_req, + const bool skip_all_diag) { int i, j, k, i_pnt; - bool skip; + bool skip_cons, skip_diag; TrackInfo tavg; NumArray lead_list; + // Variables for average TrackPoint int pcnt; TrackPoint pavg, psum; @@ -1001,6 +1004,12 @@ TrackInfo consensus(const TrackInfoArray &tracks, tavg.set_technique(model.c_str()); tavg.set_init(tracks[0].init()); tavg.set_storm_id(); + + // Diagnostic information + tavg.set_diag_source(tracks[0].diag_source()); + tavg.set_track_source(tracks[0].track_source().c_str()); + tavg.set_field_source(tracks[0].field_source().c_str()); + // Loop through the tracks and build a list of lead times for(i=0; i " @@ -1023,10 +1032,20 @@ TrackInfo consensus(const TrackInfoArray &tracks, << tracks[i].technique_number() << ").\n\n"; } - // Get unique diag names - // Store diag_names in tavg object - for(j=0; j " + << "the diagnostic type has changed (" + << diagtype_to_string(tavg.diag_source()) << "!=" + << diagtype_to_string(tracks[i].diag_source()) << ").\n\n"; + } + + // Store unique diag_names in tavg object, unless they're all skipped + if(!skip_all_diag) { + for(j=0; j 0) n_diag_inputs++; found = true; mlog << Debug(5) << "[Case " << i+1 << "] For case \"" @@ -1336,38 +1348,64 @@ int derive_consensus(TrackInfoArray &tracks) { << conf_info.Consensus[j].Members[k] << "\" was not found.\n"; - // Check if it was a required model - if(conf_info.Consensus[j].Required[k]) { + // Check if it was a required diagnostics model + if(conf_info.Consensus[j].DiagRequired[k]) { mlog << Debug(4) << "[Case " << i+1 << "] For case \"" << case_list[i] - << "\" skipping consensus model \"" + << "\" skipping diagnostics for consensus model \"" + << conf_info.Consensus[j].Name + << "\" since required diagnostics member \"" + << conf_info.Consensus[j].Members[k] + << "\" was not found.\n"; + skip_diag = true; + } + + // Check if it was a required consensus model + if(conf_info.Consensus[j].ConsRequired[k]) { + mlog << Debug(4) + << "[Case " << i+1 << "] For case \"" << case_list[i] + << "\" skipping derivation of consensus model \"" << conf_info.Consensus[j].Name << "\" since required member \"" << conf_info.Consensus[j].Members[k] << "\" was not found.\n"; - skip = true; + skip_cons = true; break; } } + } // end for k // If a required member was missing, continue to the next case - if(skip) continue; + if(skip_cons) continue; + + // Check that the required number of diagnostics inputs were found + if(n_diag_inputs < conf_info.Consensus[j].MinDiagReq) { + mlog << Debug(4) + << "[Case " << i+1 << "] For case \"" << case_list[i] + << "\" skipping diagnostics for consensus model \"" + << conf_info.Consensus[j].Name + << "\" since the minimum number of required members were not found (" + << n_diag_inputs << " < " << conf_info.Consensus[j].MinDiagReq << ").\n"; + skip_diag = true; + } - // Check that the required number of tracks were found - if(con_tracks.n() < conf_info.Consensus[j].MinReq) { + // Check that the required number of consensus tracks were found + if(con_tracks.n() < conf_info.Consensus[j].MinConsReq) { mlog << Debug(4) << "[Case " << i+1 << "] For case \"" << case_list[i] - << "\" skipping consensus model \"" << conf_info.Consensus[j].Name + << "\" skipping derivation of consensus model \"" + << conf_info.Consensus[j].Name << "\" since the minimum number of required members were not found (" - << con_tracks.n() << " < " - << conf_info.Consensus[j].MinReq << ").\n"; + << con_tracks.n() << " < " << conf_info.Consensus[j].MinConsReq << ").\n"; continue; } // Derive the consensus model from the TrackInfoArray new_track = consensus(con_tracks, conf_info.Consensus[j].Name, - conf_info.Consensus[j].MinReq, req_list); + req_cons_mem, conf_info.Consensus[j].MinConsReq, + req_diag_mem, conf_info.Consensus[j].MinDiagReq, + skip_diag); if(mlog.verbosity_level() >= 5) { mlog << Debug(5) diff --git a/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.cc b/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.cc index d30b490228..14cf82ae83 100644 --- a/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.cc +++ b/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.cc @@ -169,17 +169,17 @@ void TCPairsConfInfo::process_config() { // Conf: InitInc sa = Conf.lookup_string_array(conf_key_init_inc); - for(i=0; idict_value()->lookup_string(conf_key_name); - Consensus[i].Members = (*dict)[i]->dict_value()->lookup_string_array(conf_key_members); - Consensus[i].Required = (*dict)[i]->dict_value()->lookup_num_array(conf_key_required); - Consensus[i].MinReq = (*dict)[i]->dict_value()->lookup_int(conf_key_min_req); + // Conf: Consensus: name, members, required (error if missing) + Consensus[i].Name = (*dict)[i]->dict_value()->lookup_string(conf_key_name); + Consensus[i].Members = (*dict)[i]->dict_value()->lookup_string_array(conf_key_members); + Consensus[i].ConsRequired = (*dict)[i]->dict_value()->lookup_bool_array(conf_key_required); - // If write_members is missing, print warning message rather than error - Consensus[i].WriteMembers = (*dict)[i]->dict_value()->lookup_bool(conf_key_write_members, false); + // If ConsRequired is empty, default to false. + if(Consensus[i].ConsRequired.n() == 0) { + for(j=0; j " + << "\"consensus." << conf_key_required + << "\" must either be empty or the same length as \"consensus." + << conf_key_members << "\".\n\n"; + exit(1); + } + + // Conf: Consensus: min_req (error if missing) + Consensus[i].MinConsReq = (*dict)[i]->dict_value()->lookup_int(conf_key_min_req); + + // Conf: Consensus: diag_required (warn if missing) + Consensus[i].DiagRequired = (*dict)[i]->dict_value()->lookup_bool_array(conf_key_diag_required, false); if(!(*dict)[i]->dict_value()->last_lookup_status()) { - mlog << Warning - << "\nTCPairsConfInfo::process_config() -> " - << "\"consensus.write_members\" is missing. Using default value of true.\n\n"; - Consensus[i].WriteMembers = true; + mlog << Warning << "\nTCPairsConfInfo::process_config() -> " + << "\"consensus." << conf_key_diag_required + << "\" is missing. Using default values of false.\n\n"; } - // If required is empty, default to 0 - if(Consensus[i].Required.n_elements() == 0) { - for(j=0; j " - << "\"consensus.required\" must either be empty " - << "or the same length as \"consensus.members\".\n\n"; + else if(Consensus[i].DiagRequired.n() != + Consensus[i].Members.n()) { + mlog << Error << "\nTCPairsConfInfo::process_config() -> " + << "\"consensus." << conf_key_diag_required + << "\" must either be empty or the same length as \"consensus." + << conf_key_members << "\".\n\n"; exit(1); } - // If WriteMembers is false, save the Members to skip output for + // Conf: Consensus: min_diag_req (warn if missing) + Consensus[i].MinDiagReq = (*dict)[i]->dict_value()->lookup_int(conf_key_min_diag_req, false); + if(!(*dict)[i]->dict_value()->last_lookup_status()) { + mlog << Warning << "\nTCPairsConfInfo::process_config() -> " + << "\"consensus." << conf_key_min_diag_req + << "\" is missing. Using default value of 0.\n\n"; + Consensus[i].MinDiagReq = 0; + } + + // Conf: Consensus: write_members (warn if missing) + Consensus[i].WriteMembers = (*dict)[i]->dict_value()->lookup_bool(conf_key_write_members, false); + if(!(*dict)[i]->dict_value()->last_lookup_status()) { + mlog << Warning << "\nTCPairsConfInfo::process_config() -> " + << "\"consensus." << conf_key_write_members + << "\" is missing. Using default value of true.\n\n"; + Consensus[i].WriteMembers = true; + } + + // If WriteMembers is false, add the Members to the skip output list if(!Consensus[i].WriteMembers) { SkipConsensusMembers.add(Consensus[i].Members); } @@ -279,8 +311,9 @@ void TCPairsConfInfo::process_config() { // Conf: LagTime sa = Conf.lookup_string_array(conf_key_lag_time); - for(i=0; i 1 && BestBaseline.n() > 0) { - mlog << Warning - << "\nTCPairsConfInfo::process_config() -> " - << "deriving \"best_baseline\" models from multiple " - << "\"best_technique\" models may cause the baseline " - << "technique names to be used multiple times.\n\n"; + mlog << Warning << "\nTCPairsConfInfo::process_config() -> " + << "deriving \"" << conf_key_best_baseline + << "\" models from multiple \"" << conf_key_best_technique + << "\" models may cause the baseline technique names " + << "to be used multiple times.\n\n"; } // Conf: OperTechnique @@ -305,11 +338,11 @@ void TCPairsConfInfo::process_config() { OperBaseline = Conf.lookup_string_array(conf_key_oper_baseline); if(OperTechnique.n() > 1 && OperBaseline.n() > 0) { - mlog << Warning - << "\nTCPairsConfInfo::process_config() -> " - << "deriving \"oper_baseline\" models from multiple " - << "\"oper_technique\" models may cause the baseline " - << "technique names to be used multiple times.\n\n"; + mlog << Warning << "\nTCPairsConfInfo::process_config() -> " + << "deriving \"" << conf_key_oper_baseline + << "\" models from multiple \"" << conf_key_oper_technique + << "\" models may cause the baseline technique names " + << "ßto be used multiple times.\n\n"; } // Conf: AnlyTrack diff --git a/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.h b/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.h index 2cff235adc..8354c3405a 100644 --- a/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.h +++ b/src/tools/tc_utils/tc_pairs/tc_pairs_conf_info.h @@ -27,8 +27,10 @@ struct ConsensusInfo { ConcatString Name; StringArray Members; - NumArray Required; - int MinReq; + BoolArray ConsRequired; + int MinConsReq; + BoolArray DiagRequired; + int MinDiagReq; bool WriteMembers; };