From e90bae31f0b59e809f8980a9fe8399e5cbbc6646 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 22 Oct 2021 16:05:26 -0600 Subject: [PATCH] Per #1761, updates to Simple_Node::set_perc() to handle variable frequency bias amounts. Changes include: - Reverting the formatting of this back to how it originally was in the develop branch. In general, just match the formatting of the existing file, so as the minimize the number of difference lines. - Add logic to adjust the percentile to be found based on the requested FBIAS value. Multiplying or dividing the percentile by the FBIAS value depends on the inequality type and whether we're bias adjusting the forecast or observation data. - Adjust the log messages slightly. Please be aware that I'm not totally confident in these changes. They warrant much more testing. This logic is very, very confusing. --- met/src/basic/vx_config/threshold.cc | 354 +++++++++++++++------------ 1 file changed, 194 insertions(+), 160 deletions(-) diff --git a/met/src/basic/vx_config/threshold.cc b/met/src/basic/vx_config/threshold.cc index c2c508669f..d991b97568 100644 --- a/met/src/basic/vx_config/threshold.cc +++ b/met/src/basic/vx_config/threshold.cc @@ -933,240 +933,274 @@ void Simple_Node::set_perc(const NumArray *fptr, const NumArray *optr, const Num const SingleThresh *fthr, const SingleThresh *othr) { - - int i, count; - double ptile, diff; - NumArray data; - const NumArray * ptr = 0; - + +int i, count; +double ptile, diff; +NumArray data; +const NumArray * ptr = 0; +bool fbias_fcst = false; + // // handle sample percentile types // - - if ( Ptype == perc_thresh_sample_fcst ) ptr = fptr; - else if ( Ptype == perc_thresh_sample_obs ) ptr = optr; - else if ( Ptype == perc_thresh_sample_climo ) ptr = cptr; - + + if ( Ptype == perc_thresh_sample_fcst ) ptr = fptr; +else if ( Ptype == perc_thresh_sample_obs ) ptr = optr; +else if ( Ptype == perc_thresh_sample_climo ) ptr = cptr; + // // handle bias-correction type // - - else if ( Ptype == perc_thresh_freq_bias ) { - - if ( !fptr || !optr || !fthr || !othr ) { - - mlog << Error << "\nSimple_Node::set_perc() -> " - << "not enough information provided to define the " - << perc_thresh_info[Ptype].long_name - << " threshold \"" << s << "\".\n\n"; - - exit ( 1 ); - - } + +else if ( Ptype == perc_thresh_freq_bias ) { + + if ( !fptr || !optr || !fthr || !othr ) { + + mlog << Error << "\nSimple_Node::set_perc() -> " + << "not enough information provided to define the " + << perc_thresh_info[Ptype].long_name + << " threshold \"" << s << "\".\n\n"; + + exit ( 1 ); + + } // // bias-correct the observation // - - if ( othr->get_ptype() == perc_thresh_freq_bias && - fthr->get_ptype() == no_perc_thresh_type && - fthr->get_type() != thresh_complex ) { - - ptr = optr; - op = fthr->get_type(); - PT = fptr->compute_percentile(fthr->get_value(), - is_inclusive(fthr->get_type())); - - mlog << Debug(3) - << "The forecast threshold \"" << fthr->get_str() - << "\" includes " << PT * 100.0 << "% of the data.\n"; - - } + + if ( othr->get_ptype() == perc_thresh_freq_bias && + fthr->get_ptype() == no_perc_thresh_type && + fthr->get_type() != thresh_complex ) { + + fbias_fcst = false; + + ptr = optr; + op = fthr->get_type(); + PT = fptr->compute_percentile(fthr->get_value(), + is_inclusive(fthr->get_type())); + + mlog << Debug(3) + << "The forecast threshold value \"" << fthr->get_str() + << "\" represents the " << PT * 100.0 << "-th percentile.\n"; + + } // // bias-correct the forecast // - - else if ( fthr->get_ptype() == perc_thresh_freq_bias && - othr->get_ptype() == no_perc_thresh_type && - othr->get_type() != thresh_complex ) { - - ptr = fptr; - op = othr->get_type(); - PT = optr->compute_percentile(othr->get_value(), - is_inclusive(othr->get_type())); - - mlog << Debug(3) - << "The observation threshold \"" << othr->get_str() - << "\" includes " << PT * 100.0 << "% of the data.\n"; - } - - else { - - mlog << Error << "\nSimple_Node::set_perc() -> " - << "unsupported options for computing the " - << perc_thresh_info[Ptype].long_name - << " threshold \"" << s << "\".\n\n"; + else if ( fthr->get_ptype() == perc_thresh_freq_bias && + othr->get_ptype() == no_perc_thresh_type && + othr->get_type() != thresh_complex ) { - exit ( 1 ); + fbias_fcst = true; - } + ptr = fptr; + op = othr->get_type(); + PT = optr->compute_percentile(othr->get_value(), + is_inclusive(othr->get_type())); - // - // multiple percentile by 100 - // + mlog << Debug(3) + << "The observation threshold value \"" << othr->get_str() + << "\" represents the " << PT * 100.0 << "-th percentile.\n"; - if ( is_bad_data(PT) ) { + } - mlog << Error << "\nSimple_Node::set_perc() -> " - << "unable to compute the percentile for the " - << perc_thresh_info[Ptype].long_name - << " threshold \"" << s << "\".\n\n"; + else { - exit ( 1 ); - } + mlog << Error << "\nSimple_Node::set_perc() -> " + << "unsupported options for computing the " + << perc_thresh_info[Ptype].long_name + << " threshold \"" << s << "\".\n\n"; - PT *= 100.0; + exit ( 1 ); + + } - } // end else if PT == perc_thresh_freq_bias - // - // nothing to do + // multiple percentile by 100 // - - else { - return; + if ( is_bad_data(PT) ) { - } - - if ( !ptr ) { - mlog << Error << "\nSimple_Node::set_perc() -> " + << "unable to compute the percentile for the " << perc_thresh_info[Ptype].long_name - << " threshold \"" << s - << "\" requested but no data provided.\n\n"; - - exit ( 1 ); + << " threshold \"" << s << "\".\n\n"; + exit ( 1 ); } + PT *= 100.0; + +} // end else if PT == perc_thresh_freq_bias + + // + // nothing to do + // + +else { + + return; + +} + +if ( !ptr ) { + + mlog << Error << "\nSimple_Node::set_perc() -> " + << perc_thresh_info[Ptype].long_name + << " threshold \"" << s + << "\" requested but no data provided.\n\n"; + + exit ( 1 ); + +} + // // remove bad data, if necessary // - if ( ptr->has(bad_data_double) ) { +if ( ptr->has(bad_data_double) ) { - data.extend(ptr->n()); + data.extend(ptr->n()); - for ( i=0; in(); i++ ) { + for ( i=0; in(); i++ ) { + + if ( !is_bad_data(ptr->vals()[i]) ) data.add(ptr->vals()[i]); - if ( !is_bad_data(ptr->vals()[i]) ) data.add(ptr->vals()[i]); - - } - } - else { - - data = *ptr; - } - +} +else { + + data = *ptr; + +} + // // compute the percentile threshold // - if ( data.n() == 0 ) { +if ( data.n() == 0 ) { - mlog << Error << "\nSimple_Node::set_perc() -> " - << "can't compute " << perc_thresh_info[Ptype].long_name - << " threshold \"" << s - << "\" because no valid data was provided.\n\n"; + mlog << Error << "\nSimple_Node::set_perc() -> " + << "can't compute " << perc_thresh_info[Ptype].long_name + << " threshold \"" << s + << "\" because no valid data was provided.\n\n"; - exit ( 1 ); + exit ( 1 ); - } - else { - - // - // strip previous definition from strings - // +} +else { - s.strip_paren(); - abbr_s.strip_paren(); + // + // strip previous definition from strings + // + + s.strip_paren(); + abbr_s.strip_paren(); + + // + // parse the frequency bias value from the threshold string + // + + if ( Ptype == perc_thresh_freq_bias ) { - // - // Get the FBIAS value - // Convert the numeric value to a double (float) - // ConcatString fs = s; + fs.replace("==FBIAS", " ", false); + double fbias_val = atof(fs.c_str()); - + // - // compute the percentile and update the strings + // range check requested bias value // - T = data.percentile_array((double) PT / 100.0); - - if ( !is_bad_data(T) ) { + if ( fbias_val <= 0.0 ) { - ConcatString cs; - - cs << T; - - fix_float(cs); - - s << "(" << cs << ")"; - abbr_s << "(" << cs << ")"; + mlog << Error << "\nSimple_Node::set_perc() -> " + << "the requested frequency bias value (" << fbias_val + << ") must be > 0 in threshold \"" << s << "\".\n\n"; } // - // compute the actual percentile and check tolerance + // adjust PT by the requested frequency bias amount // - if ( op == thresh_le || op == thresh_ge || op == thresh_eq ) { - - for ( i=count=0; i 100.0 ) PT_new = 100.0; - } + mlog << Debug(3) + << "For " << (fbias_fcst ? "forecast" : "observation" ) + << " threshold \"" << s << "\" with type \"" << thresh_type_str[op] + << "\" update the requested percentile from " << PT << " to " + << PT_new << ".\n"; - if(fbias_val > 0) - ptile = (double) (fbias_val *count) / data.n(); - else - ptile = (double) count / data.n(); - - diff = abs(PT / 100.0 - ptile); - - if ( !is_eq(PT / 100.0, ptile, perc_thresh_default_tol) ) { - - mlog << Warning << "\nSimple_Node::set_perc() -> " - << "the requested percentile (" << PT - << "%) for threshold \"" << s - << "\" differs from the actual percentile (" - << ptile * 100.0 << ") by " << diff * 100.0 << "%.\n" - << "This is common for small samples or data that contains " - << "ties.\n\n"; + PT = PT_new; + } - } - else { + // + // compute the percentile and update the strings + // - mlog << Debug(3) - << "The requested percentile (" << PT - << "%) for threshold threshold \"" << s - << "\" includes " << ptile * 100.0 << "% of the data.\n"; + T = data.percentile_array((double) PT / 100.0); - } + if ( !is_bad_data(T) ) { + + ConcatString cs; + + cs << T; + + fix_float(cs); + + s << "(" << cs << ")"; + abbr_s << "(" << cs << ")"; } - return; + // + // compute the actual percentile and check tolerance + // + + for ( i=count=0; i " + << "the requested percentile (" << PT + << ") for threshold \"" << s + << "\" differs from the actual percentile (" + << ptile * 100.0 << ") by " << diff * 100.0 << ".\n" + << "This is common for small samples or data that contains " + << "ties.\n\n"; + + } + else { + + mlog << Debug(3) + << "The requested percentile (" << PT + << ") for threshold \"" << s << "\" includes " + << ptile * 100.0 << "% of the data.\n"; + + } + +} + +return; }