From 3396c402a114a16cc69fc5757caee6a5e96b4ef9 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 1 Jul 2024 13:17:38 -0700 Subject: [PATCH 01/14] Add option to assert spectrum is background for Simple MDA tool. This option only applies to Currie style limits, for now. We could probably apply to "Deconvolution" style limits, but I need more thought. --- InterSpec/DetectionLimitSimple.h | 17 +- InterSpec_resources/DetectionLimitSimple.css | 9 + InterSpec_resources/GridLayoutHelpers.css | 2 +- src/DetectionLimitCalc.cpp | 169 +++++---- src/DetectionLimitSimple.cpp | 298 ++++++++++++---- src/DetectionLimitTool.cpp | 353 ++++++++++++------- 6 files changed, 578 insertions(+), 270 deletions(-) diff --git a/InterSpec/DetectionLimitSimple.h b/InterSpec/DetectionLimitSimple.h index a6802a20..bf73490b 100644 --- a/InterSpec/DetectionLimitSimple.h +++ b/InterSpec/DetectionLimitSimple.h @@ -43,6 +43,7 @@ namespace Wt { class WMenu; class WText; + class WCheckBox; class WComboBox; class WLineEdit; class WTabWidget; @@ -168,6 +169,7 @@ class DetectionLimitSimple : public Wt::WContainerWidget void handleUserChangedFwhm(); void handleDeconPriorChange(); + void handleNoSignalPresentChanged(); void handleDeconContinuumTypeChange(); void updateSpectrumDecorationsAndResultText(); @@ -227,7 +229,18 @@ class DetectionLimitSimple : public Wt::WContainerWidget Wt::WLineEdit *m_distance; - enum ConfidenceLevel { OneSigma, TwoSigma, ThreeSigma, FourSigma, FiveSigma, NumConfidenceLevel }; + enum ConfidenceLevel + { + NinetyFivePercent, //0.95 + NinetyNinePercent, //0.99 + OneSigma, //0.682689492137086 + TwoSigma, //0.954499736103642 + ThreeSigma, //0.997300203936740 + FourSigma, //0.999936657516334 + FiveSigma, //0.999999426696856 + NumConfidenceLevel + };//enum ConfidenceLevel + Wt::WComboBox *m_confidenceLevel; DetectorDisplay *m_detectorDisplay; @@ -254,6 +267,8 @@ class DetectionLimitSimple : public Wt::WContainerWidget Wt::WPushButton *m_addFwhmBtn; Wt::WPushButton *m_selectDetectorBtn; + WContainerWidget *m_isBackgroundDiv; + Wt::WCheckBox *m_isBackgroundSpectrum; Wt::WLabel *m_continuumPriorLabel; Wt::WComboBox *m_continuumPrior; Wt::WLabel *m_continuumTypeLabel; diff --git a/InterSpec_resources/DetectionLimitSimple.css b/InterSpec_resources/DetectionLimitSimple.css index 9932072d..43b3ebda 100644 --- a/InterSpec_resources/DetectionLimitSimple.css +++ b/InterSpec_resources/DetectionLimitSimple.css @@ -112,6 +112,14 @@ } +.DetectionLimitSimple .BackCbDiv { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + column-gap: 5px; +} + .DeconvMoreInfo { display: flex; flex-direction: column; @@ -138,3 +146,4 @@ border-collapse: separate; border-spacing: 5px 5px; } + diff --git a/InterSpec_resources/GridLayoutHelpers.css b/InterSpec_resources/GridLayoutHelpers.css index 2f66e72a..e04ba988 100644 --- a/InterSpec_resources/GridLayoutHelpers.css +++ b/InterSpec_resources/GridLayoutHelpers.css @@ -13,7 +13,7 @@ justify-self: stretch; } -.GridJustifyCenter +.GridJustifyCenter, .GridHorCenter { justify-self: center; } diff --git a/src/DetectionLimitCalc.cpp b/src/DetectionLimitCalc.cpp index 78a62039..e2f9c538 100644 --- a/src/DetectionLimitCalc.cpp +++ b/src/DetectionLimitCalc.cpp @@ -800,10 +800,14 @@ CurrieMdaResult currie_mda_calc( const CurrieMdaInput &input ) || (input.gamma_energy > input.roi_upper_energy) ) throw runtime_error( "mda_counts_calc: gamma energy must be between lower and upper ROI." ); - if( input.num_lower_side_channels < 1 || (input.num_lower_side_channels >= nchannel) ) + if( ((input.num_lower_side_channels == 0) || (input.num_upper_side_channels == 0)) + && (input.num_lower_side_channels != input.num_upper_side_channels) ) + throw runtime_error( "mda_counts_calc: lower or upper side channels was zero, but not both." ); + + if( input.num_lower_side_channels >= nchannel ) throw runtime_error( "mda_counts_calc: invalid num_lower_side_channels." ); - if( input.num_upper_side_channels < 1 || (input.num_upper_side_channels >= nchannel) ) + if( input.num_upper_side_channels >= nchannel ) throw runtime_error( "mda_counts_calc: invalid num_upper_side_channels." ); if( input.detection_probability <= 0.05 || input.detection_probability >= 1.0 ) @@ -828,18 +832,36 @@ CurrieMdaResult currie_mda_calc( const CurrieMdaInput &input ) if( result.first_peak_region_channel < (input.num_lower_side_channels + 1) ) throw std::runtime_error( "mda_counts_calc: lower peak region is outside spectrum energy range" ); - result.last_lower_continuum_channel = result.first_peak_region_channel - 1; - result.first_lower_continuum_channel = result.last_lower_continuum_channel - input.num_lower_side_channels + 1; - - result.first_upper_continuum_channel = result.last_peak_region_channel + 1; - result.last_upper_continuum_channel = result.first_upper_continuum_channel + input.num_upper_side_channels - 1; - - if( result.last_upper_continuum_channel >= nchannel ) - throw std::runtime_error( "mda_counts_calc: upper peak region is outside spectrum energy range" ); + if( input.num_lower_side_channels == 0 ) + { + result.first_lower_continuum_channel = 0; + result.last_lower_continuum_channel = 0; + result.lower_continuum_counts_sum = 0; + }else + { + result.last_lower_continuum_channel = result.first_peak_region_channel - 1; + result.first_lower_continuum_channel = result.last_lower_continuum_channel - input.num_lower_side_channels + 1; + result.lower_continuum_counts_sum = spec->gamma_channels_sum(result.first_lower_continuum_channel, result.last_lower_continuum_channel); + } - result.lower_continuum_counts_sum = spec->gamma_channels_sum(result.first_lower_continuum_channel, result.last_lower_continuum_channel); + if( input.num_upper_side_channels == 0 ) + { + result.first_upper_continuum_channel = 0; + result.last_upper_continuum_channel = 0; + result.upper_continuum_counts_sum = 0; + }else + { + result.first_upper_continuum_channel = result.last_peak_region_channel + 1; + result.last_upper_continuum_channel = result.first_upper_continuum_channel + input.num_upper_side_channels - 1; + + if( result.last_upper_continuum_channel >= nchannel ) + throw std::runtime_error( "mda_counts_calc: upper peak region is outside spectrum energy range" ); + + result.upper_continuum_counts_sum = spec->gamma_channels_sum(result.first_upper_continuum_channel, result.last_upper_continuum_channel); + } + result.peak_region_counts_sum = spec->gamma_channels_sum(result.first_peak_region_channel, result.last_peak_region_channel); - result.upper_continuum_counts_sum = spec->gamma_channels_sum(result.first_upper_continuum_channel, result.last_upper_continuum_channel); + /* cout << "Lower region:\n\tChan\tEne\tCounts" << endl; @@ -858,65 +880,80 @@ CurrieMdaResult currie_mda_calc( const CurrieMdaInput &input ) cout << "\tSum: " << result.upper_continuum_counts_sum << endl; */ - const double lower_cont_counts = spec->gamma_channels_sum(result.first_lower_continuum_channel, result.last_lower_continuum_channel); - const double upper_cont_counts = spec->gamma_channels_sum(result.first_upper_continuum_channel, result.last_upper_continuum_channel); - const double lower_cont_width = spec->gamma_channel_upper(result.last_lower_continuum_channel) - - spec->gamma_channel_lower(result.first_lower_continuum_channel); - const double upper_cont_width = spec->gamma_channel_upper(result.last_upper_continuum_channel) - - spec->gamma_channel_lower(result.first_upper_continuum_channel); - - const double lower_cont_density = lower_cont_counts / lower_cont_width; - const double lower_cont_density_uncert = ((lower_cont_counts <= 0.0) ? 0.0 : (lower_cont_density / sqrt(lower_cont_counts))); - - const double upper_cont_density = upper_cont_counts / upper_cont_width; - const double upper_cont_density_uncert = ((upper_cont_counts <= 0.0) ? 0.0 : (upper_cont_density / sqrt(upper_cont_counts))); - - const double peak_cont_density = 0.5*(lower_cont_density + upper_cont_density); - const double peak_cont_density_uncert = 0.5*sqrt( upper_cont_density_uncert*upper_cont_density_uncert - + lower_cont_density_uncert*lower_cont_density_uncert ); - const double peak_cont_frac_uncert = ((peak_cont_density > 0.0) ? (peak_cont_density_uncert / peak_cont_density) : 1.0); - - - const double peak_area_width = spec->gamma_channel_upper(result.last_peak_region_channel) - - spec->gamma_channel_lower(result.first_peak_region_channel); - const double peak_cont_sum = peak_cont_density * peak_area_width; - const double peak_cont_sum_uncert = peak_cont_sum * peak_cont_frac_uncert; - - result.estimated_peak_continuum_counts = static_cast( peak_cont_sum ); - result.estimated_peak_continuum_uncert = static_cast( peak_cont_sum_uncert ); + double peak_cont_sum_uncert = -999.9f, peak_cont_sum = -999.9f; - - // The equation is centered around the input.gamma_energy with the density of counts at normal - // value at that point. The Slope will be through the midpoints of each continuum. - // TODO: should do a proper least-squares fit to the continuum; I think this will give us a slightly true-er answer - - const double lower_cont_mid_energy = spec->gamma_channel_lower(result.first_lower_continuum_channel) + 0.5*lower_cont_width; - const double upper_cont_mid_energy = spec->gamma_channel_lower(result.first_upper_continuum_channel) + 0.5*upper_cont_width; - - result.continuum_eqn[1] = (upper_cont_density - lower_cont_density) / (upper_cont_mid_energy - lower_cont_mid_energy); - result.continuum_eqn[0] = lower_cont_density - result.continuum_eqn[1]*(lower_cont_mid_energy - input.gamma_energy); - -#if( PERFORM_DEVELOPER_CHECKS ) - {// begin sanity check on continuum eqn - const double peak_start_eq = spec->gamma_channel_lower(result.first_peak_region_channel) - input.gamma_energy; - const double peak_end_eq = spec->gamma_channel_upper(result.last_peak_region_channel) - input.gamma_energy; + if( input.num_upper_side_channels == 0 ) + { + peak_cont_sum = result.peak_region_counts_sum; + peak_cont_sum_uncert = sqrt( peak_cont_sum ); + + const double peak_area_width = spec->gamma_channel_upper(result.last_peak_region_channel) + - spec->gamma_channel_lower(result.first_peak_region_channel); + result.continuum_eqn[1] = 0.0; + result.continuum_eqn[0] = peak_cont_sum / peak_area_width; + }else + { + const double lower_cont_counts = spec->gamma_channels_sum(result.first_lower_continuum_channel, result.last_lower_continuum_channel); + const double upper_cont_counts = spec->gamma_channels_sum(result.first_upper_continuum_channel, result.last_upper_continuum_channel); + const double lower_cont_width = spec->gamma_channel_upper(result.last_lower_continuum_channel) + - spec->gamma_channel_lower(result.first_lower_continuum_channel); + const double upper_cont_width = spec->gamma_channel_upper(result.last_upper_continuum_channel) + - spec->gamma_channel_lower(result.first_upper_continuum_channel); + + const double lower_cont_density = lower_cont_counts / lower_cont_width; + const double lower_cont_density_uncert = ((lower_cont_counts <= 0.0) ? 0.0 : (lower_cont_density / sqrt(lower_cont_counts))); + + const double upper_cont_density = upper_cont_counts / upper_cont_width; + const double upper_cont_density_uncert = ((upper_cont_counts <= 0.0) ? 0.0 : (upper_cont_density / sqrt(upper_cont_counts))); - const double peak_cont_eq_integral = result.continuum_eqn[0] * (peak_end_eq - peak_start_eq) - + result.continuum_eqn[1] * 0.5 * (peak_end_eq*peak_end_eq - peak_start_eq*peak_start_eq); - const double upper_cont_eq = result.continuum_eqn[0] + (upper_cont_mid_energy - input.gamma_energy)*result.continuum_eqn[1]; + const double peak_cont_density = 0.5*(lower_cont_density + upper_cont_density); + const double peak_cont_density_uncert = 0.5*sqrt( upper_cont_density_uncert*upper_cont_density_uncert + + lower_cont_density_uncert*lower_cont_density_uncert ); + const double peak_cont_frac_uncert = ((peak_cont_density > 0.0) ? (peak_cont_density_uncert / peak_cont_density) : 1.0); - // Precision tests, for development - if we go down to a precision of 1E-4, instead of 1E-3, - // then these tests fail for NaI systems - I'm not sure if its something actually wrong, or - // just really bad numerical accuracy (although its hard to imagine going down to only 4 - // or so, significant figures) - const double eq_dens = fabs(peak_cont_eq_integral - peak_cont_sum); - assert( (eq_dens < 0.1) - || (eq_dens < 1.0E-3*std::max(peak_cont_eq_integral, peak_cont_sum)) ); + const double peak_area_width = spec->gamma_channel_upper(result.last_peak_region_channel) + - spec->gamma_channel_lower(result.first_peak_region_channel); - const double eq_diff = fabs(peak_cont_eq_integral - peak_cont_sum); - assert( eq_diff < 0.1 || eq_diff < 1.0E-3*std::max(peak_cont_eq_integral, peak_cont_sum) ); - }// end sanity check on continuum eqn + peak_cont_sum = peak_cont_density * peak_area_width; + peak_cont_sum_uncert = peak_cont_sum * peak_cont_frac_uncert; + + // The equation is centered around the input.gamma_energy with the density of counts at normal + // value at that point. The Slope will be through the midpoints of each continuum. + // TODO: should do a proper least-squares fit to the continuum; I think this will give us a slightly true-er answer + + const double lower_cont_mid_energy = spec->gamma_channel_lower(result.first_lower_continuum_channel) + 0.5*lower_cont_width; + const double upper_cont_mid_energy = spec->gamma_channel_lower(result.first_upper_continuum_channel) + 0.5*upper_cont_width; + + result.continuum_eqn[1] = (upper_cont_density - lower_cont_density) / (upper_cont_mid_energy - lower_cont_mid_energy); + result.continuum_eqn[0] = lower_cont_density - result.continuum_eqn[1]*(lower_cont_mid_energy - input.gamma_energy); + +#if( PERFORM_DEVELOPER_CHECKS ) + {// begin sanity check on continuum eqn + const double peak_start_eq = spec->gamma_channel_lower(result.first_peak_region_channel) - input.gamma_energy; + const double peak_end_eq = spec->gamma_channel_upper(result.last_peak_region_channel) - input.gamma_energy; + + const double peak_cont_eq_integral = result.continuum_eqn[0] * (peak_end_eq - peak_start_eq) + + result.continuum_eqn[1] * 0.5 * (peak_end_eq*peak_end_eq - peak_start_eq*peak_start_eq); + const double upper_cont_eq = result.continuum_eqn[0] + (upper_cont_mid_energy - input.gamma_energy)*result.continuum_eqn[1]; + + // Precision tests, for development - if we go down to a precision of 1E-4, instead of 1E-3, + // then these tests fail for NaI systems - I'm not sure if its something actually wrong, or + // just really bad numerical accuracy (although its hard to imagine going down to only 4 + // or so, significant figures) + const double eq_dens = fabs(peak_cont_eq_integral - peak_cont_sum); + assert( (eq_dens < 0.1) + || (eq_dens < 1.0E-3*std::max(peak_cont_eq_integral, peak_cont_sum)) ); + + const double eq_diff = fabs(peak_cont_eq_integral - peak_cont_sum); + assert( eq_diff < 0.1 || eq_diff < 1.0E-3*std::max(peak_cont_eq_integral, peak_cont_sum) ); + }// end sanity check on continuum eqn #endif //PERFORM_DEVELOPER_CHECKS + }//if( input.num_upper_side_channels == 0 ) / else + + assert( peak_cont_sum_uncert != -999.9f ); + assert( peak_cont_sum != -999.9f ); + result.estimated_peak_continuum_counts = static_cast( peak_cont_sum ); + result.estimated_peak_continuum_uncert = static_cast( peak_cont_sum_uncert ); typedef boost::math::policies::policy > my_pol_6; const boost::math::normal_distribution gaus_dist( 0.0, 1.0 ); diff --git a/src/DetectionLimitSimple.cpp b/src/DetectionLimitSimple.cpp index b20ca3f3..97fb0e2b 100644 --- a/src/DetectionLimitSimple.cpp +++ b/src/DetectionLimitSimple.cpp @@ -30,9 +30,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -211,6 +213,8 @@ DetectionLimitSimple::DetectionLimitSimple( MaterialDB *materialDB, m_fwhmSuggestTxt( nullptr ), m_addFwhmBtn( nullptr ), m_selectDetectorBtn( nullptr ), + m_isBackgroundDiv( nullptr ), + m_isBackgroundSpectrum( nullptr ), m_continuumPriorLabel( nullptr ), m_continuumPrior( nullptr ), m_continuumTypeLabel( nullptr ), @@ -384,18 +388,20 @@ void DetectionLimitSimple::init() switch( cl ) { - case ConfidenceLevel::OneSigma: txt = "68%"; break; - case ConfidenceLevel::TwoSigma: txt = "95%"; break; - case ConfidenceLevel::ThreeSigma: txt = "99%"; break; - case ConfidenceLevel::FourSigma: txt = "4-sigma"; break; - case ConfidenceLevel::FiveSigma: txt = "5-sigma"; break; - case ConfidenceLevel::NumConfidenceLevel: break; + case ConfidenceLevel::NinetyFivePercent: txt = "95%"; break; + case ConfidenceLevel::NinetyNinePercent: txt = "99%"; break; + case ConfidenceLevel::OneSigma: txt = "1σ (68.2%)"; break; + case ConfidenceLevel::TwoSigma: txt = "2σ (95.4%)"; break; + case ConfidenceLevel::ThreeSigma: txt = "3σ (99.7%)"; break; + case ConfidenceLevel::FourSigma: txt = "4σ"; break; //1-6.3E-5 + case ConfidenceLevel::FiveSigma: txt = "5σ"; break; //1-5.7E-7 + case ConfidenceLevel::NumConfidenceLevel: assert( 0 ); break; }//switch( cl ) m_confidenceLevel->addItem( txt ); }//for( loop over confidence levels ) - m_confidenceLevel->setCurrentIndex( ConfidenceLevel::TwoSigma ); + m_confidenceLevel->setCurrentIndex( ConfidenceLevel::NinetyFivePercent ); m_confidenceLevel->activated().connect(this, &DetectionLimitSimple::handleConfidenceLevelChanged ); @@ -464,7 +470,24 @@ void DetectionLimitSimple::init() m_addFwhmBtn->setHidden( !drf || !drf->isValid() || drf->hasResolutionInfo() ); m_selectDetectorBtn->setHidden( drf && drf->isValid() ); - m_continuumPriorLabel = new WLabel( WString::tr("dls-deon-cont-norm-label"), generalInput ); + m_isBackgroundDiv = new WContainerWidget( generalInput ); + m_isBackgroundDiv->addStyleClass( "BackCbDiv GridFirstCol GridSeventhRow GridVertCenter GridSpanTwoCol" ); + m_isBackgroundSpectrum = new WCheckBox( WString::tr("dls-is-background-spectrum-cb"), m_isBackgroundDiv ); + m_isBackgroundSpectrum->checked().connect( this, &DetectionLimitSimple::handleNoSignalPresentChanged ); + m_isBackgroundSpectrum->unChecked().connect( this, &DetectionLimitSimple::handleNoSignalPresentChanged ); + + { + m_isBackgroundSpectrum->setWordWrap( false ); + WImage *img = new WImage( m_isBackgroundDiv ); + img->setImageLink(Wt::WLink("InterSpec_resources/images/help_minimal.svg") ); + img->resize( 16, 16 ); //setStyleClass("Wt-icon"); + img->decorationStyle().setCursor( Wt::Cursor::WhatsThisCursor ); + WString tt = WString::tr("dls-is-background-spectrum-tt"); + HelpSystem::attachToolTipOn( img, tt, true, HelpSystem::ToolTipPosition::Right, + HelpSystem::ToolTipPrefOverride::InstantAlways ); + } + + m_continuumPriorLabel = new WLabel( WString::tr("dls-decon-cont-norm-label"), generalInput ); m_continuumPriorLabel->addStyleClass( "GridFirstCol GridSeventhRow GridVertCenter" ); m_continuumPrior = new WComboBox( generalInput ); m_continuumPrior->addItem( WString::tr("dls-cont-norm-unknown") ); @@ -474,8 +497,10 @@ void DetectionLimitSimple::init() m_continuumPrior->activated().connect( this, &DetectionLimitSimple::handleDeconPriorChange ); m_continuumPrior->addStyleClass( "ContTypeCombo GridSecondCol GridSeventhRow" ); - m_continuumPriorLabel->setHiddenKeepsGeometry( true ); - m_continuumPrior->setHiddenKeepsGeometry( true ); + //m_continuumPriorLabel->setHiddenKeepsGeometry( true ); + //m_continuumPrior->setHiddenKeepsGeometry( true ); + m_continuumPriorLabel->hide(); + m_continuumPrior->hide(); m_continuumTypeLabel = new WLabel( "Continuum Type:", generalInput ); m_continuumTypeLabel->addStyleClass( "GridFourthCol GridSeventhRow GridVertCenter" ); @@ -485,11 +510,8 @@ void DetectionLimitSimple::init() m_continuumType->setCurrentIndex( 0 ); m_continuumType->activated().connect( this, &DetectionLimitSimple::handleDeconContinuumTypeChange ); m_continuumType->addStyleClass( "GridFifthCol GridSeventhRow" ); - - m_continuumPriorLabel->hide(); - m_continuumPrior->hide(); - m_continuumTypeLabel->hide(); - m_continuumType->hide(); + m_continuumTypeLabel->setHidden( true ); + m_continuumType->setHidden( true ); WContainerWidget *container = new WContainerWidget( generalInput ); container->addStyleClass( "MethodSelect GridFirstCol GridEighthRow GridSpanFiveCol" ); @@ -720,6 +742,9 @@ void DetectionLimitSimple::handleDeconPriorChange() m_numSideChannelLabel->setHidden( !currieMethod && !useSideChan ); m_numSideChannel->setHidden( !currieMethod && !useSideChan ); + m_continuumTypeLabel->setHidden( useSideChan ); + m_continuumType->setHidden( useSideChan ); + m_renderFlags |= DetectionLimitSimple::RenderActions::UpdateLimit; m_renderFlags |= DetectionLimitSimple::RenderActions::AddUndoRedoStep; m_renderFlags |= DetectionLimitSimple::RenderActions::UpdateSpectrumDecorations; @@ -727,6 +752,22 @@ void DetectionLimitSimple::handleDeconPriorChange() }//void handleDeconPriorChange() +void DetectionLimitSimple::handleNoSignalPresentChanged() +{ + const bool noSignal = m_isBackgroundSpectrum->isChecked(); + const bool currieMethod = (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)); + assert( currieMethod ); + + m_numSideChannelLabel->setHidden( !currieMethod || noSignal ); + m_numSideChannel->setHidden( !currieMethod || noSignal ); + + m_renderFlags |= DetectionLimitSimple::RenderActions::UpdateLimit; + m_renderFlags |= DetectionLimitSimple::RenderActions::AddUndoRedoStep; + m_renderFlags |= DetectionLimitSimple::RenderActions::UpdateSpectrumDecorations; + scheduleRender(); +}//void handleNoSignalPresentChanged() + + void DetectionLimitSimple::handleDeconContinuumTypeChange() { m_renderFlags |= DetectionLimitSimple::RenderActions::UpdateLimit; @@ -745,17 +786,26 @@ void DetectionLimitSimple::handleMethodChanged() { const bool currieMethod = (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)); - m_numSideChannelLabel->setHidden( !currieMethod ); - m_numSideChannel->setHidden( !currieMethod ); - - const bool useSideChan = (m_continuumPrior->currentIndex() == 2); - m_numSideChannelLabel->setHidden( !currieMethod && !useSideChan ); - m_numSideChannel->setHidden( !currieMethod && !useSideChan ); - + m_isBackgroundDiv->setHidden( !currieMethod ); m_continuumPriorLabel->setHidden( currieMethod ); m_continuumPrior->setHidden( currieMethod ); - m_continuumTypeLabel->setHidden( currieMethod ); - m_continuumType->setHidden( currieMethod ); + + if( currieMethod ) + { + const bool noSignal = m_isBackgroundSpectrum->isChecked(); + m_numSideChannelLabel->setHidden( noSignal ); + m_numSideChannel->setHidden( noSignal ); + + m_continuumTypeLabel->setHidden( true ); + m_continuumType->setHidden( true ); + }else + { + const bool useSideChan = (m_continuumPrior->currentIndex() == 2); + m_numSideChannelLabel->setHidden( !useSideChan ); + m_numSideChannel->setHidden( !useSideChan ); + m_continuumTypeLabel->setHidden( useSideChan ); + m_continuumType->setHidden( useSideChan ); + }//if( currieMethod ) / else m_methodDescription->setText( WString::tr(currieMethod ? "dls-currie-desc" : "dls-decon-desc") ); @@ -1156,6 +1206,9 @@ void DetectionLimitSimple::updateSpectrumDecorationsAndResultText() })(); const bool currieMethod = (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)); + + assert( m_isBackgroundDiv->isVisible() == currieMethod ); + if( currieMethod ) { // Currie method limit @@ -1273,8 +1326,17 @@ void DetectionLimitSimple::updateSpectrumDecorationsAndResultText() const bool use_curie = use_curie_units(); const DetectorPeakResponse::EffGeometryType det_geom = drf ? drf->geometryType() : DetectorPeakResponse::EffGeometryType::FarField; + assert( (result->input.num_lower_side_channels != 0) + || (result->input.num_upper_side_channels != 0) + || (result->input.num_lower_side_channels == result->input.num_upper_side_channels) ); + + const bool assertedIsBackground = ((result->input.num_lower_side_channels == 0) + && (result->input.num_upper_side_channels == 0)); + if( result->source_counts > result->decision_threshold ) { + assert( !assertedIsBackground ); + // There is enough excess counts that we would reliably detect this activity, so we will // give the activity range. string lowerstr, upperstr, nomstr; @@ -1303,6 +1365,7 @@ void DetectionLimitSimple::updateSpectrumDecorationsAndResultText() } }else if( result->upper_limit < 0 ) { + assert( !assertedIsBackground ); // This can happen when there are a lot fewer counts in the peak region than predicted // from the sides - since this is non-sensical, we'll just say zero. const string unitstr = use_curie ? "Ci" : "Bq"; @@ -1331,8 +1394,16 @@ void DetectionLimitSimple::updateSpectrumDecorationsAndResultText() result_txt = WString::tr("dls-det-upper-bound").arg(mdastr).arg(cl_str); }//if( detected ) / else if( ....) - WString full_result_txt( "{1}
{2}" ); - full_result_txt.arg(result_txt); + WString full_result_txt; + + if( assertedIsBackground ) + { + full_result_txt = WString( "{1}" ); + }else + { + full_result_txt = WString( "{1}
{2}" ); + full_result_txt.arg(result_txt); + }//if( assertedIsBackground ) / else if( gammas_per_bq > 0.0 ) { @@ -1495,12 +1566,14 @@ double DetectionLimitSimple::currentConfidenceLevel() const switch( confidence ) { - case OneSigma: confidenceLevel = 0.682689492137086; break; - case TwoSigma: confidenceLevel = 0.954499736103642; break; - case ThreeSigma: confidenceLevel = 0.997300203936740; break; - case FourSigma: confidenceLevel = 0.999936657516334; break; - case FiveSigma: confidenceLevel = 0.999999426696856; break; - case NumConfidenceLevel: assert(0); break; + case ConfidenceLevel::NinetyFivePercent: confidenceLevel = 0.95; break; + case ConfidenceLevel::NinetyNinePercent: confidenceLevel = 0.99; break; + case ConfidenceLevel::OneSigma: confidenceLevel = 0.682689492137086; break; + case ConfidenceLevel::TwoSigma: confidenceLevel = 0.954499736103642; break; + case ConfidenceLevel::ThreeSigma: confidenceLevel = 0.997300203936740; break; + case ConfidenceLevel::FourSigma: confidenceLevel = 0.999936657516334; break; + case ConfidenceLevel::FiveSigma: confidenceLevel = 0.999999426696856; break; + case ConfidenceLevel::NumConfidenceLevel: assert(0); break; }//switch( confidence ) return confidenceLevel; @@ -1938,6 +2011,8 @@ void DetectionLimitSimple::updateResult() if( !hist || (hist->num_gamma_channels() < 7) ) throw runtime_error( "No foreground spectrum loaded." ); + const bool currieMethod = (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)); + const float roi_lower_energy = m_lowerRoi->value(); const float roi_upper_energy = m_upperRoi->value(); @@ -1962,6 +2037,12 @@ void DetectionLimitSimple::updateResult() currie_input->roi_upper_energy = m_upperRoi->value(); currie_input->num_lower_side_channels = static_cast( m_numSideChannel->value() ); currie_input->num_upper_side_channels = currie_input->num_lower_side_channels; + if( currieMethod && m_isBackgroundSpectrum->isChecked() ) + { + currie_input->num_lower_side_channels = 0; + currie_input->num_upper_side_channels = 0; + } + currie_input->detection_probability = confidenceLevel; currie_input->additional_uncertainty = 0.0f; // TODO: can we get the DRFs contribution to form this? @@ -1972,7 +2053,6 @@ void DetectionLimitSimple::updateResult() // Calculating the deconvolution-style limit is fairly CPU intensive, so we will only computer // it when its what the user actually wants. - const bool currieMethod = (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)); if( !currieMethod ) { const float live_time = m_currentCurrieInput->spectrum->live_time(); @@ -2033,7 +2113,6 @@ void DetectionLimitSimple::updateResult() throw std::logic_error( "Invalid continuuuum type selected" ); }//switch( continuumTypeIndex ) - const int continuumPriorIndex = m_continuumPrior->currentIndex(); switch( continuumPriorIndex ) { @@ -2046,6 +2125,7 @@ void DetectionLimitSimple::updateResult() break; case 2: // "Cont. from sides" + roiInfo.continuum_type = PeakContinuum::OffsetType::Linear; roiInfo.cont_norm_method = DetectionLimitCalc::DeconContinuumNorm::FixedByEdges; break; @@ -2054,6 +2134,11 @@ void DetectionLimitSimple::updateResult() throw std::logic_error( "Invalid continuuuum prior selected" ); }//switch( continuumPriorIndex ) + //if( assertNoSignal ) + // roiInfo.cont_norm_method = DetectionLimitCalc::DeconContinuumNorm::FixedByFullRange; // "Not Present" + //else + // roiInfo.cont_norm_method = DetectionLimitCalc::DeconContinuumNorm::Floating; // "Unknown or Present" + if( roiInfo.cont_norm_method == DetectionLimitCalc::DeconContinuumNorm::FixedByEdges ) { // `roiInfo.num_*_side_channels` only used if `cont_norm_method` is `DeconContinuumNorm::FixedByEdges`. @@ -2178,6 +2263,13 @@ void DetectionLimitSimple::updateResult() const double simple_mda = currie_result.upper_limit / gammas_per_bq; min_act = 0.0; max_act = diff_multiple*simple_mda; + + //if( assertNoSignal ) + //{ + // assert( currie_result.source_counts == 0.0f ); + // const double niave_max = currie_result.peak_region_counts_sum / gammas_per_bq; + // max_act = diff_multiple * std::max( simple_mda, niave_max ); + //} }//if( detected signal ) / else / else }// end estimate range we should search for deconvolution @@ -2244,6 +2336,8 @@ void DetectionLimitSimple::handleAppUrl( std::string uri ) handleMethodChanged(); }//if( m_methodGroup->checkedId() != static_cast(methodIndex) ) + const bool currieMethod = (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)); + auto qpos = values.find( "VER" ); const string ver = ((qpos != end(values)) && !qpos->second.empty()) ? qpos->second : "1"; @@ -2368,43 +2462,63 @@ void DetectionLimitSimple::handleAppUrl( std::string uri ) if( qpos != end(values) ) { int nsigma; - if( (stringstream(qpos->second) >> nsigma) && (nsigma >= 1) && (nsigma <= 5) ) + if( (stringstream(qpos->second) >> nsigma) ) { - m_confidenceLevel->setCurrentIndex( nsigma - 1 ); + ConfidenceLevel cl = ConfidenceLevel::NinetyFivePercent; + + switch( nsigma ) + { + case 1: cl = ConfidenceLevel::OneSigma; break; + case 2: cl = ConfidenceLevel::TwoSigma; break; + case 3: cl = ConfidenceLevel::ThreeSigma; break; + case 4: cl = ConfidenceLevel::FourSigma; break; + case 5: cl = ConfidenceLevel::FiveSigma; break; + case 95: cl = ConfidenceLevel::NinetyFivePercent; break; + case 99: cl = ConfidenceLevel::NinetyNinePercent; break; + default: + cerr << "Invalid URI CL, '" << qpos->second << "' to a valid CL." << endl; + break; + }//switch( nsigma ) + + m_confidenceLevel->setCurrentIndex( static_cast(cl) ); }else { - cerr << "Failed to convert URI CL, '" << qpos->second << "' to a to an int between 1 and 5" << endl; + cerr << "Failed to convert URI CL, '" << qpos->second << "' to a to an integer." << endl; } }//if( URI has CL ) - - int continuumNormIndex = -1; - qpos = values.find( "CONTNORM" ); - if( qpos != end(values) ) - { - if( SpecUtils::istarts_with(qpos->second, "UNKN") ) - continuumNormIndex = 0; - else if( SpecUtils::istarts_with(qpos->second, "NOS") ) - continuumNormIndex = 1; - else if( SpecUtils::istarts_with(qpos->second, "FIX") ) - continuumNormIndex = 2; - else - cerr << "Unexpected 'CONTNORM' value: '" << qpos->second << "'" << endl; - }//if( URI had continuum norm value ) - - if( (m_methodGroup->checkedId() == static_cast(MethodIds::Currie)) - || (continuumNormIndex >= 0) ) + if( currieMethod ) { + bool noSignal = false; + qpos = values.find( "ISBACK" ); + if( qpos != end(values) ) + { + if( (qpos->second == "0") || SpecUtils::iequals_ascii(qpos->second, "NO") || SpecUtils::iequals_ascii(qpos->second, "FALSE") ) + noSignal = false; + else if( (qpos->second == "1") || SpecUtils::iequals_ascii(qpos->second, "YES") || SpecUtils::iequals_ascii(qpos->second, "TRUE") ) + noSignal = true; + else + cerr << "Unexpected 'ISBACK' value: '" << qpos->second << "' (non-bool)" << endl; + }//if( URI had is background value ) + + m_isBackgroundSpectrum->setChecked( noSignal ); + m_numSideChannelLabel->setHidden( noSignal ); + m_numSideChannel->setHidden( noSignal ); + qpos = values.find( "NSIDE" ); if( qpos != end(values) ) { + // In principle we shouldnt have the NSIDE argument if the "is background" checkbox is checked + // but whatever. + assert( !noSignal ); + int nside; if( (stringstream(qpos->second) >> nside) && (nside >= 1) && (nside <= 64) ) m_numSideChannel->setValue( nside ); else cerr << "Invalid 'NSIDE' value: '" << qpos->second << "'" << endl; }//if( URI has NSIDE ) - }//if( want value of number of side channels ) + }//if( currieMethod ) bool setFwhm = false; qpos = values.find( "FWHM" ); @@ -2427,10 +2541,32 @@ void DetectionLimitSimple::handleAppUrl( std::string uri ) handleUserChangedFwhm(); } - if( m_methodGroup->checkedId() == static_cast(MethodIds::Deconvolution) ) + if( !currieMethod ) { - if( continuumNormIndex >= 0 ) - m_continuumPrior->setCurrentIndex( continuumNormIndex ); + qpos = values.find( "CONTNORM" ); + if( qpos != end(values) ) + { + int priorTypeIndex = -1; + if( SpecUtils::istarts_with(qpos->second, "UNK") ) + priorTypeIndex = 0; + else if( SpecUtils::istarts_with(qpos->second, "NOS") ) + priorTypeIndex = 1; + else if( SpecUtils::istarts_with(qpos->second, "FIX") ) + priorTypeIndex = 2; + else + cerr << "Invalid 'CONTNORM' value: '" << qpos->second << "'" << endl; + + if( priorTypeIndex >= 0 ) + { + m_continuumPrior->setCurrentIndex( priorTypeIndex ); + + m_numSideChannelLabel->setHidden( priorTypeIndex != 2 ); + m_numSideChannel->setHidden( priorTypeIndex != 2 ); + + m_continuumTypeLabel->setHidden( priorTypeIndex == 2 ); + m_continuumType->setHidden( priorTypeIndex == 2 ); + }//if( prior type provided ) + }//if( continuum prior provided ) qpos = values.find( "CONTTYPE" ); @@ -2536,19 +2672,20 @@ std::string DetectionLimitSimple::encodeStateToUrl() const switch( confidence ) { - case OneSigma: answer += "1"; break; - case TwoSigma: answer += "2"; break; - case ThreeSigma: answer += "3"; break; - case FourSigma: answer += "4"; break; - case FiveSigma: answer += "5"; break; - case NumConfidenceLevel: assert(0); break; + case ConfidenceLevel::NinetyFivePercent: answer += "95"; break; + case ConfidenceLevel::NinetyNinePercent: answer += "99"; break; + case ConfidenceLevel::OneSigma: answer += "1"; break; + case ConfidenceLevel::TwoSigma: answer += "2"; break; + case ConfidenceLevel::ThreeSigma: answer += "3"; break; + case ConfidenceLevel::FourSigma: answer += "4"; break; + case ConfidenceLevel::FiveSigma: answer += "5"; break; + case ConfidenceLevel::NumConfidenceLevel: assert(0); break; }//switch( confidence ) const bool useSideChan = (m_continuumPrior->currentIndex() == 2); - if( currieMethod || useSideChan ) + if( (currieMethod && !m_isBackgroundSpectrum->isChecked()) || useSideChan ) answer += "&NSIDE=" + std::to_string(m_numSideChannel->value()); - shared_ptr drf = m_detectorDisplay->detector(); if( drf && (!drf->isValid() || !drf->hasResolutionInfo()) ) drf.reset(); @@ -2558,7 +2695,11 @@ std::string DetectionLimitSimple::encodeStateToUrl() const if( !drf || (fabs(m_fwhm->value() - drf->peakResolutionFWHM(energy)) > 0.1) ) answer += "&FWHM=" + m_fwhm->valueText().toUTF8(); - if( !currieMethod ) + if( currieMethod ) + { + const bool noSignal = m_isBackgroundSpectrum->isChecked(); + answer += "&ISBACK=" + string(noSignal ? "1" : "0"); + }else { answer += "&CONTNORM="; switch( m_continuumPrior->currentIndex() ) @@ -2572,18 +2713,19 @@ std::string DetectionLimitSimple::encodeStateToUrl() const throw logic_error( "Invalid m_continuumPrior" ); }//switch( m_continuumPrior->currentIndex() ) - answer += "&CONTTYPE="; - - const int continuumTypeIndex = m_continuumType->currentIndex(); - switch( continuumTypeIndex ) + if( m_continuumPrior->currentIndex() != 2 ) { - case 0: answer += "LIN"; break; - case 1: answer += "QUAD"; break; - default: - assert( 0 ); - throw std::logic_error( "Invalid continuuuum type selected" ); - }//switch( continuumTypeIndex ) - }//if( !currieMethod ) + answer += "&CONTTYPE="; + switch( m_continuumType->currentIndex() ) + { + case 0: answer += "LIN"; break; + case 1: answer += "QUAD"; break; + default: + assert( 0 ); + throw std::logic_error( "Invalid continuum type selected" ); + }//switch( continuumTypeIndex ) + }//if( m_continuumPrior->currentIndex() != 2 ) + }//if( currieMethod ) / else if( !m_allGammasInRoi ) answer += "&ALLGAMMA=0"; diff --git a/src/DetectionLimitTool.cpp b/src/DetectionLimitTool.cpp index 64d855dc..c80f5365 100644 --- a/src/DetectionLimitTool.cpp +++ b/src/DetectionLimitTool.cpp @@ -1486,18 +1486,44 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv double lower_continuum_counts_sum, peak_region_counts_sum, upper_continuum_counts_sum; double lower_lower_energy, lower_upper_energy, upper_lower_energy, upper_upper_energy; + // If number of of side channels is zero, then we expect the following + // variables to all be zero + assert( (result->input.num_lower_side_channels != 0) + || ((result->first_lower_continuum_channel == 0) + && (result->last_lower_continuum_channel == 0) + && (result->lower_continuum_counts_sum == 0) + && (result->first_upper_continuum_channel == 0) + && (result->last_upper_continuum_channel == 0) + && (result->upper_continuum_counts_sum == 0)) + ); + + const bool assertedNoSignal = (result->input.num_lower_side_channels == 0); + // We will prefer to set the highlight regions from the results, but if we dont have results // we'll do it from the input. if( result ) { - lower_lower_energy = spectrum->gamma_channel_lower( result->first_lower_continuum_channel ); - lower_upper_energy = spectrum->gamma_channel_lower( result->first_upper_continuum_channel ); - upper_lower_energy = spectrum->gamma_channel_upper( result->last_lower_continuum_channel ); - upper_upper_energy = spectrum->gamma_channel_upper( result->last_upper_continuum_channel ); - - lower_continuum_counts_sum = result->lower_continuum_counts_sum; - peak_region_counts_sum = result->peak_region_counts_sum; - upper_continuum_counts_sum = result->upper_continuum_counts_sum; + if( assertedNoSignal ) + { + lower_upper_energy = spectrum->gamma_channel_upper( result->last_peak_region_channel ); + upper_upper_energy = lower_upper_energy; + upper_lower_energy = spectrum->gamma_channel_lower( result->first_peak_region_channel ); + lower_lower_energy = upper_lower_energy; + + lower_continuum_counts_sum = 0; + peak_region_counts_sum = result->peak_region_counts_sum; + upper_continuum_counts_sum = 0; + }else + { + lower_lower_energy = spectrum->gamma_channel_lower( result->first_lower_continuum_channel ); + lower_upper_energy = spectrum->gamma_channel_lower( result->first_upper_continuum_channel ); //need + upper_lower_energy = spectrum->gamma_channel_upper( result->last_lower_continuum_channel ); //need + upper_upper_energy = spectrum->gamma_channel_upper( result->last_upper_continuum_channel ); + + lower_continuum_counts_sum = result->lower_continuum_counts_sum; + peak_region_counts_sum = result->peak_region_counts_sum; + upper_continuum_counts_sum = result->upper_continuum_counts_sum; + } }else { const pair channels @@ -1506,28 +1532,46 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv const size_t first_peak_region_channel = channels.first; const size_t last_peak_region_channel = channels.second; - if( first_peak_region_channel < (input.num_lower_side_channels + 1) ) - throw std::runtime_error( "mda_counts_calc: lower peak region is outside spectrum energy range" ); - - const size_t last_lower_continuum_channel = first_peak_region_channel - 1; - const size_t first_lower_continuum_channel = last_lower_continuum_channel - input.num_lower_side_channels + 1; - - const size_t first_upper_continuum_channel = last_peak_region_channel + 1; - const size_t last_upper_continuum_channel = first_upper_continuum_channel + input.num_upper_side_channels - 1; - - if( last_upper_continuum_channel >= spectrum->num_gamma_channels() ) - throw std::runtime_error( "mda_counts_calc: upper peak region is outside spectrum energy range" ); - - lower_lower_energy = spectrum->gamma_channel_lower( first_lower_continuum_channel ); - lower_upper_energy = spectrum->gamma_channel_lower( first_upper_continuum_channel ); - upper_lower_energy = spectrum->gamma_channel_upper( last_lower_continuum_channel ); - upper_upper_energy = spectrum->gamma_channel_upper( last_upper_continuum_channel ); - - lower_continuum_counts_sum = spectrum->gamma_channels_sum(first_lower_continuum_channel, last_lower_continuum_channel); - peak_region_counts_sum = spectrum->gamma_channels_sum(first_peak_region_channel, last_peak_region_channel); - upper_continuum_counts_sum = spectrum->gamma_channels_sum(first_upper_continuum_channel, last_upper_continuum_channel); + if( assertedNoSignal ) + { + lower_upper_energy = spectrum->gamma_channel_upper( last_peak_region_channel ); + upper_upper_energy = lower_upper_energy; + upper_lower_energy = spectrum->gamma_channel_lower( first_peak_region_channel ); + lower_lower_energy = upper_lower_energy; + + lower_continuum_counts_sum = 0; + peak_region_counts_sum = spectrum->gamma_channels_sum(first_peak_region_channel, last_peak_region_channel); + upper_continuum_counts_sum = 0; + }else + { + if( first_peak_region_channel < (input.num_lower_side_channels + 1) ) + throw std::runtime_error( "mda_counts_calc: lower peak region is outside spectrum energy range" ); + + const size_t last_lower_continuum_channel = first_peak_region_channel - 1; + const size_t first_lower_continuum_channel = last_lower_continuum_channel - input.num_lower_side_channels + 1; + + const size_t first_upper_continuum_channel = last_peak_region_channel + 1; + const size_t last_upper_continuum_channel = first_upper_continuum_channel + input.num_upper_side_channels - 1; + + if( last_upper_continuum_channel >= spectrum->num_gamma_channels() ) + throw std::runtime_error( "mda_counts_calc: upper peak region is outside spectrum energy range" ); + + lower_lower_energy = spectrum->gamma_channel_lower( first_lower_continuum_channel ); + lower_upper_energy = spectrum->gamma_channel_lower( first_upper_continuum_channel ); //need + upper_lower_energy = spectrum->gamma_channel_upper( last_lower_continuum_channel ); //need + upper_upper_energy = spectrum->gamma_channel_upper( last_upper_continuum_channel ); + + lower_continuum_counts_sum = spectrum->gamma_channels_sum(first_lower_continuum_channel, last_lower_continuum_channel); + peak_region_counts_sum = spectrum->gamma_channels_sum(first_peak_region_channel, last_peak_region_channel); + upper_continuum_counts_sum = spectrum->gamma_channels_sum(first_upper_continuum_channel, last_upper_continuum_channel); + }//if( assertedNoSignal ) }//if( result ) / else + assert( !assertedNoSignal + || ((lower_continuum_counts_sum == 0) + && (upper_continuum_counts_sum == 0)) + ); + const double dx = upper_upper_energy - lower_lower_energy; chart->setXAxisRange( lower_lower_energy - 0.5*dx, upper_upper_energy + 0.5*dx ); @@ -1535,29 +1579,34 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv const int mid_ndec = 1 + static_cast( std::ceil( fabs( std::log10( fabs(peak_region_counts_sum) ) ) ) ); const int upper_ndec = 1 + static_cast( std::ceil( fabs( std::log10( fabs(upper_continuum_counts_sum) ) ) ) ); - const string lower_txt = SpecUtils::printCompact( lower_continuum_counts_sum, lower_ndec ); - const string mid_txt = SpecUtils::printCompact( peak_region_counts_sum, mid_ndec ); - const string upper_txt = SpecUtils::printCompact( upper_continuum_counts_sum, upper_ndec ); - shared_ptr theme = viewer->getColorTheme(); assert( theme ); if( !theme ) throw runtime_error( "Invalid color theme" ); - chart->addDecorativeHighlightRegion( lower_lower_energy, upper_lower_energy, - theme->timeHistoryBackgroundHighlight, - D3SpectrumDisplayDiv::HighlightRegionFill::BelowData, - lower_txt ); + if( !assertedNoSignal ) + { + const string lower_txt = SpecUtils::printCompact( lower_continuum_counts_sum, lower_ndec ); + chart->addDecorativeHighlightRegion( lower_lower_energy, upper_lower_energy, + theme->timeHistoryBackgroundHighlight, + D3SpectrumDisplayDiv::HighlightRegionFill::BelowData, + lower_txt ); + }//if( !assertedNoSignal ) + const string mid_txt = SpecUtils::printCompact( peak_region_counts_sum, mid_ndec ); chart->addDecorativeHighlightRegion( upper_lower_energy, lower_upper_energy, theme->timeHistoryForegroundHighlight, D3SpectrumDisplayDiv::HighlightRegionFill::BelowData, mid_txt ); - chart->addDecorativeHighlightRegion( lower_upper_energy, upper_upper_energy, - theme->timeHistoryBackgroundHighlight, - D3SpectrumDisplayDiv::HighlightRegionFill::BelowData, - upper_txt ); + if( !assertedNoSignal ) + { + const string upper_txt = SpecUtils::printCompact( upper_continuum_counts_sum, upper_ndec ); + chart->addDecorativeHighlightRegion( lower_upper_energy, upper_upper_energy, + theme->timeHistoryBackgroundHighlight, + D3SpectrumDisplayDiv::HighlightRegionFill::BelowData, + upper_txt ); + }//if( !assertedNoSignal ) // We will only put the peak on the chart if there is a result if( result && pmodel ) @@ -1599,6 +1648,8 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv { if( result->source_counts > result->decision_threshold ) { + assert( !assertedNoSignal ); + // There is enough excess counts that we would reliably detect this activity, so we will // give the activity range. string lowerstr, upperstr, nomstr; @@ -1637,6 +1688,8 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv } }else if( result->upper_limit < 0 ) { + assert( !assertedNoSignal ); + // This can happen when there are a lot fewer counts in the peak region than predicted // from the sides - since this is non-sensical, we'll just say zero. const string unitstr = useCuries ? "Ci" : "Bq"; @@ -1648,26 +1701,51 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv specific_peaks[i].setAmplitude( 0.0 ); }else { - // We will provide the upper bound on activity. - string mdastr; - if( gammas_per_bq > 0.0 ) + if( assertedNoSignal ) { - const double simple_mda = result->upper_limit / gammas_per_bq; - mdastr = PhysicalUnits::printToBestActivityUnits( simple_mda, 2, useCuries ) - + DetectorPeakResponse::det_eff_geom_type_postfix( det_geom ); + // We will provide minimum counts reliably detectable + string mdastr; + if( gammas_per_bq > 0.0 ) + { + const double simple_mda = result->detection_limit / gammas_per_bq; + mdastr = PhysicalUnits::printToBestActivityUnits( simple_mda, 2, useCuries ) + + DetectorPeakResponse::det_eff_geom_type_postfix( det_geom ); + }else + { + mdastr = SpecUtils::printCompact( result->detection_limit, 4 ) + " counts"; + } + + chart_title = "Data + peak for " + mdastr; + + generic_peak.setPeakArea( result->detection_limit ); + for( size_t i = 0; i < peaks.size(); ++i ) + { + const double area = result->detection_limit * peaks[i].counts_4pi / sum_counts_4pi; + specific_peaks[i].setAmplitude( area ); + } }else { - mdastr = SpecUtils::printCompact( result->upper_limit, 4 ) + " counts"; - } - - chart_title = "Peak for upper bound of " + mdastr + " @" + cl_str + " CL"; - - generic_peak.setPeakArea( result->upper_limit ); - for( size_t i = 0; i < peaks.size(); ++i ) - { - const double area = result->upper_limit * peaks[i].counts_4pi / sum_counts_4pi; - specific_peaks[i].setAmplitude( area ); - } + // We will provide the upper bound on activity. + string mdastr; + if( gammas_per_bq > 0.0 ) + { + const double simple_mda = result->upper_limit / gammas_per_bq; + mdastr = PhysicalUnits::printToBestActivityUnits( simple_mda, 2, useCuries ) + + DetectorPeakResponse::det_eff_geom_type_postfix( det_geom ); + }else + { + mdastr = SpecUtils::printCompact( result->upper_limit, 4 ) + " counts"; + } + + chart_title = "Peak for upper bound of " + mdastr + " @" + cl_str + " CL"; + + generic_peak.setPeakArea( result->upper_limit ); + for( size_t i = 0; i < peaks.size(); ++i ) + { + const double area = result->upper_limit * peaks[i].counts_4pi / sum_counts_4pi; + specific_peaks[i].setAmplitude( area ); + } + }//if( assertedNoSignal ) / else }//if( detected ) / else if( ....) break; @@ -1682,10 +1760,20 @@ void DetectionLimitTool::update_spectrum_for_currie_result( D3SpectrumDisplayDiv chart->setChartTitle( chart_title ); - if( specific_peaks.size() > 0 ) - pmodel->addPeaks( specific_peaks ); - else - pmodel->addPeaks( {generic_peak} ); + vector peaks = (specific_peaks.size() > 0) ? std::move(specific_peaks) : vector{generic_peak}; + + if( assertedNoSignal ) + { + for( PeakDef &p : peaks ) + { + p.continuum()->setType( PeakContinuum::OffsetType::External ); + p.continuum()->setExternalContinuum( spectrum ); + } + }//if( specIsBackground ) + + set_chi2_dof( spectrum, peaks, 0, peaks.size() ); // Compute Chi2 for peaks + + pmodel->addPeaks( peaks ); }//if( result ) }catch( std::exception &e ) { @@ -1761,10 +1849,12 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec const string confidence_level = buffer; - const double lower_lower_energy = input.spectrum->gamma_channel_lower( result.first_lower_continuum_channel ); - const double lower_upper_energy = input.spectrum->gamma_channel_lower( result.first_upper_continuum_channel ); - const double upper_lower_energy = input.spectrum->gamma_channel_upper( result.last_lower_continuum_channel ); - const double upper_upper_energy = input.spectrum->gamma_channel_upper( result.last_upper_continuum_channel ); + const bool assertedNoSignal = (result.input.num_lower_side_channels == 0); + + const double lower_upper_energy = input.spectrum->gamma_channel_lower( result.first_peak_region_channel ); + const double lower_lower_energy = assertedNoSignal ? lower_upper_energy : input.spectrum->gamma_channel_lower( result.first_lower_continuum_channel ); + const double upper_lower_energy = input.spectrum->gamma_channel_upper( result.last_peak_region_channel ); + const double upper_upper_energy = assertedNoSignal ? upper_lower_energy : input.spectrum->gamma_channel_upper( result.last_upper_continuum_channel ); // Add chart InterSpec *viewer = InterSpec::instance(); @@ -1811,7 +1901,7 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec img->decorationStyle().setCursor( Wt::Cursor::WhatsThisCursor ); HelpSystem::attachToolTipOn( img, tt, true, HelpSystem::ToolTipPosition::Right, - HelpSystem::ToolTipPrefOverride::AlwaysShow ); + HelpSystem::ToolTipPrefOverride::InstantAlways ); };//addTooltipToRow @@ -1821,6 +1911,8 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec { if( result.source_counts > result.decision_threshold ) { + assert( !assertedNoSignal ); + // There is enough excess counts that we would reliably detect this activity, so we will // give the activity range. WString obs_label, range_label; @@ -1865,6 +1957,8 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec addTooltipToRow( "The activity range estimate, to the " + confidence_level + " confidence level." ); }else if( result.upper_limit < 0 ) { + assert( !assertedNoSignal ); + // This can happen when there are a lot fewer counts in the peak region than predicted // from the sides - since this is non-sensical, we'll just say zero. const string unitstr = drf ? (useCuries ? "Ci" : "Bq") : " counts"; @@ -1920,74 +2014,82 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec }//switch( limitType ) // Add a blank row + string val; cell = table->elementAt( table->rowCount(), 0 ); WText *txt = new WText( " ", TextFormat::XHTMLText, cell ); - cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Lower region channels", cell ); - string val = "[" + std::to_string(result.first_lower_continuum_channel) + ", " - + std::to_string(result.last_lower_continuum_channel) + "]"; - cell = table->elementAt( table->rowCount() - 1, 1 ); - txt = new WText( val, cell ); - - - snprintf( buffer, sizeof(buffer), "The region above the peak in energy, that is being used" - " to estimate the expected continuum counts in the peak region;" - " corresponds to %.2f to %.2f keV", lower_lower_energy, lower_upper_energy ); - addTooltipToRow( buffer ); - - - cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Lower region counts", cell ); - val = SpecUtils::printCompact( result.lower_continuum_counts_sum, 5 ); - cell = table->elementAt( table->rowCount() - 1, 1 ); - txt = new WText( val, cell ); - addTooltipToRow( "The number of counts observed in the region below the peak region," - " that is being used to estimate expected peak-region expected counts" ); - - cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Upper region channels", cell ); - val = "[" + std::to_string(result.first_upper_continuum_channel) + ", " - + std::to_string(result.last_upper_continuum_channel) + "]"; - cell = table->elementAt( table->rowCount() - 1, 1 ); - txt = new WText( val, cell ); - snprintf( buffer, sizeof(buffer), "The region above the peak in energy, that is being used" - " to estimate the expected continuum counts in the peak region;" - " corresponds to %.2f to %.2f keV", upper_lower_energy, upper_upper_energy ); - addTooltipToRow( buffer ); - - cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Upper region counts", cell ); - val = SpecUtils::printCompact( result.upper_continuum_counts_sum, 5 ); - cell = table->elementAt( table->rowCount() - 1, 1 ); - txt = new WText( val, cell ); - addTooltipToRow( "The number of counts observed in the region above the peak region," - " that is being used to estimate expected peak-region expected counts" ); - + if( !assertedNoSignal ) + { + cell = table->elementAt( table->rowCount(), 0 ); + txt = new WText( "Lower region channels", cell ); + val = "[" + std::to_string(result.first_lower_continuum_channel) + ", " + + std::to_string(result.last_lower_continuum_channel) + "]"; + cell = table->elementAt( table->rowCount() - 1, 1 ); + txt = new WText( val, cell ); + + + snprintf( buffer, sizeof(buffer), "The region above the peak in energy, that is being used" + " to estimate the expected continuum counts in the peak region;" + " corresponds to %.2f to %.2f keV", lower_lower_energy, lower_upper_energy ); + addTooltipToRow( buffer ); + + + cell = table->elementAt( table->rowCount(), 0 ); + txt = new WText( "Lower region counts", cell ); + val = SpecUtils::printCompact( result.lower_continuum_counts_sum, 5 ); + cell = table->elementAt( table->rowCount() - 1, 1 ); + txt = new WText( val, cell ); + addTooltipToRow( "The number of counts observed in the region below the peak region," + " that is being used to estimate expected peak-region expected counts" ); + + cell = table->elementAt( table->rowCount(), 0 ); + txt = new WText( "Upper region channels", cell ); + val = "[" + std::to_string(result.first_upper_continuum_channel) + ", " + + std::to_string(result.last_upper_continuum_channel) + "]"; + cell = table->elementAt( table->rowCount() - 1, 1 ); + txt = new WText( val, cell ); + snprintf( buffer, sizeof(buffer), "The region above the peak in energy, that is being used" + " to estimate the expected continuum counts in the peak region;" + " corresponds to %.2f to %.2f keV", upper_lower_energy, upper_upper_energy ); + addTooltipToRow( buffer ); + + cell = table->elementAt( table->rowCount(), 0 ); + txt = new WText( "Upper region counts", cell ); + val = SpecUtils::printCompact( result.upper_continuum_counts_sum, 5 ); + cell = table->elementAt( table->rowCount() - 1, 1 ); + txt = new WText( val, cell ); + addTooltipToRow( "The number of counts observed in the region above the peak region," + " that is being used to estimate expected peak-region expected counts" ); + }else + { + cell = table->elementAt( table->rowCount(), 0 ); + cell->setColumnSpan( 2 ); + txt = new WText( "Using ROI area as background estimate", cell ); + }//if( !assertedNoSignal ) / else cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Peak area channels", cell ); - val = "[" + std::to_string(result.last_lower_continuum_channel + 1) + ", " - + std::to_string(result.first_upper_continuum_channel - 1) + "]"; + txt = new WText( assertedNoSignal ? "ROI area channels" : "Peak area channels", cell ); + val = "[" + std::to_string(result.first_peak_region_channel) + ", " + + std::to_string(result.last_peak_region_channel) + "]"; cell = table->elementAt( table->rowCount() - 1, 1 ); txt = new WText( val, cell ); - const double peak_lower_energy = input.spectrum->gamma_channel_lower( result.last_lower_continuum_channel + 1 ); - const double peak_upper_energy = input.spectrum->gamma_channel_lower( result.first_upper_continuum_channel - 1 ); + const double peak_lower_energy = input.spectrum->gamma_channel_lower( result.first_peak_region_channel ); + const double peak_upper_energy = input.spectrum->gamma_channel_upper( result.last_peak_region_channel ); snprintf( buffer, sizeof(buffer), "The region the peak is being assumed to be within;" " corresponds to %.2f to %.2f keV", peak_lower_energy, peak_upper_energy ); addTooltipToRow( buffer ); cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Peak region counts", cell ); + txt = new WText( assertedNoSignal ? "ROI region counts" : "Peak region counts", cell ); val = SpecUtils::printCompact( result.peak_region_counts_sum, 5 ); cell = table->elementAt( table->rowCount() - 1, 1 ); txt = new WText( val, cell ); addTooltipToRow( "The observed number of counts in the peak region" ); cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Peak region null est.", cell ); + txt = new WText( assertedNoSignal ? "ROI region null est.": "Peak region null est.", cell ); val = SpecUtils::printCompact( result.estimated_peak_continuum_counts, 5 ) + " ± " + SpecUtils::printCompact( result.estimated_peak_continuum_uncert, 5 ); cell = table->elementAt( table->rowCount() - 1, 1 ); @@ -1999,7 +2101,7 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec // the net signal level (instrument response) above which an observed signal may be // reliably recognized as "detected" cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Peak critical limit", cell ); + txt = new WText( "Peak critical limit (Lc)", cell ); if( drf && (distance >= 0.0) && (gammas_per_bq > 0.0) ) { const double decision_threshold_act = result.decision_threshold / gammas_per_bq; @@ -2023,7 +2125,7 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec // Note: I believe this quantity corresponds to Currie's "detection limit" (L_d) that // is the “true” net signal level which may be a priori expected to lead to detection. cell = table->elementAt( table->rowCount(), 0 ); - txt = new WText( "Peak detection limit", cell ); + txt = new WText( "Peak detection limit (Ld)", cell ); if( drf && (distance >= 0.0) && (gammas_per_bq > 0.0) ) { @@ -2074,14 +2176,17 @@ SimpleDialog *DetectionLimitTool::createCurrieRoiMoreInfoWindow( const SandiaDec nomstr = "< 0 counts"; }//if( drf ) / else - nomstr += " (below Lc)"; - - cell = table->elementAt( table->rowCount(), 0 ); - new WText( obs_label, cell ); - cell = table->elementAt( table->rowCount() - 1, 1 ); - new WText( nomstr, cell ); - addTooltipToRow( "The observed signal counts is less than the "critical level", Lc," - " so a detection can not be declared, but this is the excess over expected counts/activity." ); + if( !assertedNoSignal ) + { + nomstr += " (below Lc)"; + + cell = table->elementAt( table->rowCount(), 0 ); + new WText( obs_label, cell ); + cell = table->elementAt( table->rowCount() - 1, 1 ); + new WText( nomstr, cell ); + addTooltipToRow( "The observed signal counts is less than the "critical level", Lc," + " so a detection can not be declared, but this is the excess over expected counts/activity." ); + }//if( !assertedNoSignal ) }//if( result.source_counts <= result.decision_threshold ) }//case DetectionLimitTool::LimitType::Activity: From 8172108085f3d72b3c907813516d9dfd46a08712 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 1 Jul 2024 13:20:01 -0700 Subject: [PATCH 02/14] Add some options for showing tooltips. Added being able to show tool tips instantly when moused over, and in this case dont hide the TT when user clicks. Also, removed the "focus" event from showing TT, unless it is a TT we are showing, despite the user preference. --- InterSpec/HelpSystem.h | 3 ++- .../app_text/DetectionLimitSimple.xml | 16 ++++++++++++---- src/ExportSpecFile.cpp | 2 +- src/GammaCountDialog.cpp | 3 ++- src/HelpSystem.cpp | 6 ++++-- src/InterSpec.cpp | 2 +- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/InterSpec/HelpSystem.h b/InterSpec/HelpSystem.h index 51071c12..bf2d69f4 100644 --- a/InterSpec/HelpSystem.h +++ b/InterSpec/HelpSystem.h @@ -98,7 +98,8 @@ namespace HelpSystem enum class ToolTipPrefOverride { AlwaysShow, - RespectPreference + RespectPreference, + InstantAlways }; enum class ToolTipPosition diff --git a/InterSpec_resources/app_text/DetectionLimitSimple.xml b/InterSpec_resources/app_text/DetectionLimitSimple.xml index 99d3fbdf..b291d56c 100644 --- a/InterSpec_resources/app_text/DetectionLimitSimple.xml +++ b/InterSpec_resources/app_text/DetectionLimitSimple.xml @@ -10,10 +10,18 @@ ROI Upper: Num Side Chan.: FWHM: - Prior: - Unknown/Present - Not Present - Cont. from sides + Cont. Norm: + Background spectrum + + Checking this option asserts that there is no signal present in this spectrum. +

+ The peak-region itself will be used to estimate the expected background. + The option to choose side-channels adjacent to the peak-region becomes no-longer relevant, so will be hidden. +

+
+ Floating + As no signal + From sides Changing the ROI excluded primary gamma - not changing Detector FWHM: {1} keV Rough Est. FWHM: {1} keV diff --git a/src/ExportSpecFile.cpp b/src/ExportSpecFile.cpp index 290a986c..d6225e85 100644 --- a/src/ExportSpecFile.cpp +++ b/src/ExportSpecFile.cpp @@ -827,7 +827,7 @@ void ExportSpecFileTool::init() HelpSystem::attachToolTipOn( img, description, true, HelpSystem::ToolTipPosition::Right, - HelpSystem::ToolTipPrefOverride::AlwaysShow ); + HelpSystem::ToolTipPrefOverride::InstantAlways ); }//if( we have the description of the file ) }//if( !isMobile ) };//addFormatItem lambda diff --git a/src/GammaCountDialog.cpp b/src/GammaCountDialog.cpp index 5bf4bac4..c57dd064 100644 --- a/src/GammaCountDialog.cpp +++ b/src/GammaCountDialog.cpp @@ -230,7 +230,8 @@ void GammaCountDialog::init() m_nsigmaHelp->setHidden( true ); HelpSystem::attachToolTipOn( m_nsigmaHelp, WString::tr("gcd-tt-n-sigma"), true, - HelpSystem::ToolTipPosition::Right ); + HelpSystem::ToolTipPosition::Right, + HelpSystem::ToolTipPrefOverride::InstantAlways ); m_liveTimeScaleNote = new WText( "", XHTMLText, answers ); m_liveTimeScaleNote->addStyleClass( "GridFifthRow GridFirstCol GridJustifyCenter GridSpanThreeCol line-above LiveTimeScaleNote" ); //"" diff --git a/src/HelpSystem.cpp b/src/HelpSystem.cpp index abff8463..0c13e26b 100644 --- a/src/HelpSystem.cpp +++ b/src/HelpSystem.cpp @@ -771,6 +771,7 @@ namespace HelpSystem //the tooltip when they start typing. const bool overrideShow = (forceShowing == ToolTipPrefOverride::AlwaysShow); + const bool instantShow = (forceShowing == ToolTipPrefOverride::InstantAlways); //Create popup notifications Wt::WStringStream strm; @@ -808,8 +809,9 @@ namespace HelpSystem "method: 'flipinvert flipinvert', " "x:5} " "}," - "show: { event: '" << string(enableShowing ? "mouseenter focus" : "") << "', delay: 500 }," - "hide: { fixed: true, event: 'mouseleave focusout keypress click' }," + "show: { event: '" << (enableShowing ? (string("mouseenter") + (overrideShow ? " focus" : "")) : string("")) + << "', delay: " << string(instantShow ? "0" : "500") << " }," + "hide: { fixed: true, event: 'mouseleave focusout" << string(instantShow ? "" : " keypress click") << "' }," "style: { classes: 'qtip-rounded qtip-shadow" << string(overrideShow ? "" : " canDisableTt") << "'," "tip: {" "corner: true, " diff --git a/src/InterSpec.cpp b/src/InterSpec.cpp index 201334d3..efa5548b 100644 --- a/src/InterSpec.cpp +++ b/src/InterSpec.cpp @@ -7638,7 +7638,7 @@ void InterSpec::toggleToolTip( const bool showToolTips ) //update all existing qtips if( showToolTips ) { - wApp->doJavaScript( "$('.qtip-rounded.canDisableTt').qtip('option', 'show.event', 'mouseenter focus');" ); + wApp->doJavaScript( "$('.qtip-rounded.canDisableTt').qtip('option', 'show.event', 'mouseenter');" ); }else { wApp->doJavaScript( "$('.qtip-rounded.canDisableTt').qtip('option', 'show.event', '');" ); From 4768fed6c05a8a44fd23012396263bddc73ae2c7 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 1 Jul 2024 13:20:50 -0700 Subject: [PATCH 03/14] Add check for current value to be NaN when fitting for activity. --- src/ShieldingSourceDisplay.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ShieldingSourceDisplay.cpp b/src/ShieldingSourceDisplay.cpp index 52dde518..0b23e58d 100644 --- a/src/ShieldingSourceDisplay.cpp +++ b/src/ShieldingSourceDisplay.cpp @@ -3665,6 +3665,15 @@ pair, ROOT::Minuit2::Mn }//case ShieldingSourceFitCalc::ModelSourceType::Trace: }//switch( m_sourceModel->sourceType(ison) ) + // If we are fitting activity or age, we can possibly get into a situation where the + // values have become NaN - if this is the case, lets put in a number, to hopefully + // help get out of this badness + if( srcdef.fitAge && (IsInf(srcdef.age) || IsNan(srcdef.age)) ) + srcdef.age = PeakDef::defaultDecayTime( nuclide, nullptr ); + + if( srcdef.fitActivity && (IsInf(srcdef.activity) || IsNan(srcdef.activity)) ) + srcdef.activity = 1.0E-6 * PhysicalUnits::curie; + src_definitions.push_back( srcdef ); }//for( const SandiaDecay::Nuclide *nuc : nuclides ) From dfdcb36a2066c3594492306f18e576dde39b2055 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 08:43:18 -0700 Subject: [PATCH 04/14] Remove reference to D3 chart CSS file no longer needed --- CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7dbf1ce4..1f7a6819 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -322,7 +322,6 @@ set(sources src/D3SpectrumDisplayDiv.cpp external_libs/SpecUtils/d3_resources/SpectrumChartD3.js external_libs/SpecUtils/d3_resources/SpectrumChartD3.css - external_libs/SpecUtils/d3_resources/SpectrumChartD3StandAlone.css src/D3TimeChart.cpp src/ZipArchive.cpp src/MultimediaDisplay.cpp @@ -570,10 +569,7 @@ deploy_js_resource("${CMAKE_CURRENT_SOURCE_DIR}/external_libs/SpecUtils/d3_resou ) -set(CSS_RESOURCES_TO_COPY - "SpectrumChartD3.css" - "SpectrumChartD3StandAlone.css" -) +set( CSS_RESOURCES_TO_COPY "SpectrumChartD3.css" ) foreach(_file ${CSS_RESOURCES_TO_COPY}) deploy_css_resource("${CMAKE_CURRENT_SOURCE_DIR}/external_libs/SpecUtils/d3_resources/${_file}" From 3560f039acf94423a8b61b33f7cbabb3f997ae6d Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 10:37:38 -0700 Subject: [PATCH 05/14] Bump version of SpecUtils --- external_libs/SpecUtils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external_libs/SpecUtils b/external_libs/SpecUtils index 074524d9..a64d3354 160000 --- a/external_libs/SpecUtils +++ b/external_libs/SpecUtils @@ -1 +1 @@ -Subproject commit 074524d9371f4b73765f6982418827c36ecb03a1 +Subproject commit a64d3354a4319f2d054b71186f7463974146a111 From c031c88816c21ad4e777c798af28080f40601bf5 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 10:39:06 -0700 Subject: [PATCH 06/14] Handle y-axis type toggling when axis title is double clicked. --- InterSpec/D3SpectrumDisplayDiv.h | 4 ++++ src/D3SpectrumDisplayDiv.cpp | 38 +++++++++++++++++++++++++++----- src/RelActCalcAuto.cpp | 2 -- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/InterSpec/D3SpectrumDisplayDiv.h b/InterSpec/D3SpectrumDisplayDiv.h index 80f74241..4b6f2aac 100644 --- a/InterSpec/D3SpectrumDisplayDiv.h +++ b/InterSpec/D3SpectrumDisplayDiv.h @@ -476,6 +476,7 @@ class D3SpectrumDisplayDiv : public Wt::WContainerWidget std::unique_ptr > m_legendClosedJS; std::unique_ptr > m_sliderDisplayed; + std::unique_ptr > m_yAxisTypeChanged; // Wt Signals //for all the bellow, the doubles are all the coordinated of the action @@ -535,6 +536,9 @@ class D3SpectrumDisplayDiv : public Wt::WContainerWidget /** Called when user shows or closes the slider chart, by using the SVG buttons */ void sliderChartDisplayedCallback( const bool madeVisisble ); + /** Called when the user double-clicks on y-axis title, which toggles between linear and log y-axis */ + void yAxisTypeChangedCallback( const std::string &type ); + /** The javascript variable name used to refer to the SpecrtumChartD3 object. Currently is `jsRef() + ".chart"`. */ diff --git a/src/D3SpectrumDisplayDiv.cpp b/src/D3SpectrumDisplayDiv.cpp index e8fa81d0..5b4fb6ea 100644 --- a/src/D3SpectrumDisplayDiv.cpp +++ b/src/D3SpectrumDisplayDiv.cpp @@ -258,10 +258,9 @@ D3SpectrumDisplayDiv::D3SpectrumDisplayDiv( WContainerWidget *parent ) // Cancel right-click events for the div, we handle it all in JS setAttributeValue( "oncontextmenu", - "event.cancelBubble = true; event.returnValue = false; return false;" - ); + "event.cancelBubble = true; event.returnValue = false; return false;" ); - InterSpec::instance()->useMessageResourceBundle( "D3SpectrumDisplayDiv" ); + InterSpec::instance()->useMessageResourceBundle( "D3SpectrumDisplayDiv" ); //For development it may be useful to directly use the original JS/CSS files, // but normally we should use the resources CMake will copy into @@ -434,11 +433,15 @@ void D3SpectrumDisplayDiv::defineJavaScript() m_legendEnabled = false; m_legendDisabledSignal.emit(); }) ); + + + m_sliderDisplayed.reset( new Wt::JSignal(this, "sliderChartDisplayed") ); + m_sliderDisplayed->connect( boost::bind( &D3SpectrumDisplayDiv::sliderChartDisplayedCallback, this, boost::placeholders::_1 ) ); + + m_yAxisTypeChanged.reset( new Wt::JSignal(this, "yAxisTypeChanged") ); + m_yAxisTypeChanged->connect( boost::bind( &D3SpectrumDisplayDiv::yAxisTypeChangedCallback, this, boost::placeholders::_1 ) ); }//if( !m_xRangeChangedJS ) - m_sliderDisplayed.reset( new Wt::JSignal(this, "sliderChartDisplayed") ); - m_sliderDisplayed->connect( boost::bind( &D3SpectrumDisplayDiv::sliderChartDisplayedCallback, this, boost::placeholders::_1 ) ); - for( const string &js : m_pendingJs ) doJavaScript( js ); m_pendingJs.clear(); @@ -867,6 +870,9 @@ bool D3SpectrumDisplayDiv::yAxisIsLog() const void D3SpectrumDisplayDiv::setYAxisLog( bool log ) { + if( m_yAxisIsLog == log ) + return; + m_yAxisIsLog = log; if( isRendered() ) doJavaScript( m_jsgraph + (log ? ".setLogY();" : ".setLinearY();") ); @@ -2583,6 +2589,26 @@ void D3SpectrumDisplayDiv::sliderChartDisplayedCallback( const bool madeVisisble }//void sliderChartDisplayedCallback( const bool madeVisisble ); +void D3SpectrumDisplayDiv::yAxisTypeChangedCallback( const std::string &type ) +{ + const bool isLogY = (type == "log"); + if( isLogY == m_yAxisIsLog ) + return; + + m_yAxisIsLog = isLogY; + InterSpec *interspec = InterSpec::instance(); + interspec->setLogY( m_yAxisIsLog ); //toggles menu items, but wont put in undo/redo step + + UndoRedoManager *undoRedo = UndoRedoManager::instance(); + if( undoRedo && undoRedo->canAddUndoRedoNow() ) + { + undoRedo->addUndoRedoStep( [isLogY](){ InterSpec::instance()->setLogY(!isLogY); }, + [isLogY](){ InterSpec::instance()->setLogY(isLogY); }, + "Toggle log-y axis" ); + } +}//void yAxisTypeChanged( const std::string &type ) + + D3SpectrumDisplayDiv::~D3SpectrumDisplayDiv() { //doJavaScript( "try{" + m_jsgraph + "=null;}catch(){}" ); diff --git a/src/RelActCalcAuto.cpp b/src/RelActCalcAuto.cpp index efed4303..2844bbac 100644 --- a/src/RelActCalcAuto.cpp +++ b/src/RelActCalcAuto.cpp @@ -4806,8 +4806,6 @@ void RelActAutoSolution::print_html_report( std::ostream &out ) const const string d3_js = load_file_contents( "d3.v3.min.js" ); const string spectrum_chart_d3_js = load_file_contents( "SpectrumChartD3.js" ); const string spectrum_chart_d3_css = load_file_contents( "SpectrumChartD3.css" ); - //const string spectrum_chart_d3_standalone_css = load_file_contents( "SpectrumChartD3StandAlone.css" ); - SpecUtils::ireplace_all( html, "\\;", ";" ); From cfc91bb34ca25917439c3d2b9a2fad114504a01b Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 11:44:17 -0700 Subject: [PATCH 07/14] Bump version of SpecUtils --- external_libs/SpecUtils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external_libs/SpecUtils b/external_libs/SpecUtils index a64d3354..f0949dea 160000 --- a/external_libs/SpecUtils +++ b/external_libs/SpecUtils @@ -1 +1 @@ -Subproject commit a64d3354a4319f2d054b71186f7463974146a111 +Subproject commit f0949dea5c1379d1ee5f58ab5e3f6dfff82e81bd From 38153484926957bc4c5578dd297d7358404d1aa1 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 11:44:40 -0700 Subject: [PATCH 08/14] Rest of chart colors to CSS variables. --- src/ColorThemeWidget.cpp | 6 +++--- src/D3SpectrumDisplayDiv.cpp | 22 +++++++++------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/ColorThemeWidget.cpp b/src/ColorThemeWidget.cpp index b2b9b90d..010a51be 100644 --- a/src/ColorThemeWidget.cpp +++ b/src/ColorThemeWidget.cpp @@ -215,7 +215,7 @@ ColorThemeWidget::ColorThemeWidget(WContainerWidget *parent) ++row; cell = table->elementAt(row, 0); cell->addStyleClass( "CTRowLabel" ); - new WLabel("Spectrum Chart Background", cell); + new WLabel("Spectrum Area", cell); cell = table->elementAt(row, 1); if( nativeColorSelect ) @@ -232,7 +232,7 @@ ColorThemeWidget::ColorThemeWidget(WContainerWidget *parent) ++row; cell = table->elementAt(row, 0); cell->addStyleClass( "CTRowLabel" ); - new WLabel("Spectrum Chart Margins", cell); + new WLabel("Spectrum Chart Bckgrnd", cell); cell = table->elementAt(row, 1); cell->addStyleClass( "CTSelect" ); if( nativeColorSelect ) @@ -244,7 +244,7 @@ ColorThemeWidget::ColorThemeWidget(WContainerWidget *parent) cell = table->elementAt(row, 2); cell->addStyleClass( "CTRowDesc" ); new WText("Color for spectrum chart margins", cell); - m_specMarginSameAsBackground = new WCheckBox("Same as background", cell); + m_specMarginSameAsBackground = new WCheckBox("Same as spectrum area", cell); m_specMarginSameAsBackground->setInline(false); diff --git a/src/D3SpectrumDisplayDiv.cpp b/src/D3SpectrumDisplayDiv.cpp index 5b4fb6ea..86aef108 100644 --- a/src/D3SpectrumDisplayDiv.cpp +++ b/src/D3SpectrumDisplayDiv.cpp @@ -455,9 +455,8 @@ void D3SpectrumDisplayDiv::defineJavaScript() void D3SpectrumDisplayDiv::initChangeableCssRules() { WCssStyleSheet &style = wApp->styleSheet(); - - m_cssRules["GridColor"] = style.addRule( ".xgrid > .tick, .ygrid > .tick", "stroke: #b3b3b3" ); - m_cssRules["MinorGridColor"] = style.addRule( ".minorgrid", "stroke: #e6e6e6" ); + m_cssRules["GridColor"] = style.addRule( ":root", "--d3spec-grid-major-color: #b3b3b3;" ); //Not actually needed, as CSS will default to this + m_cssRules["MinorGridColor"] = style.addRule( ":root", "--d3spec-grid-minor-color: #e6e6e6;" ); //Not actually needed, as CSS will default to this }//void initChangeableCssRules() @@ -1664,14 +1663,9 @@ void D3SpectrumDisplayDiv::setAxisLineColor( const Wt::WColor &color ) if( m_cssRules.count(rulename) ) style.removeRule( m_cssRules[rulename] ); - m_cssRules[rulename] = style.addRule( ":root", "--d3spec-axis-color: " + m_axisColor.cssText() ); - + // Sets axis colors, and ".peakLine, .escapeLineForward, .mouseLine, .secondaryMouseLine" - //ToDo: is setting feature line colors okay like this - rulename = "FeatureLinesColor"; - if( m_cssRules.count(rulename) ) - style.removeRule( m_cssRules[rulename] ); - m_cssRules[rulename] = style.addRule( ".peakLine, .escapeLineForward, .mouseLine, .secondaryMouseLine", "stroke: " + m_axisColor.cssText() ); + m_cssRules[rulename] = style.addRule( ":root", "--d3spec-axis-color: " + m_axisColor.cssText() ); //ToDo: figure out how to make grid lines look okay. //rulename = "GridColor"; @@ -1707,8 +1701,9 @@ void D3SpectrumDisplayDiv::setChartMarginColor( const Wt::WColor &color ) if( m_cssRules.count(rulename) ) style.removeRule( m_cssRules[rulename] ); + m_cssRules[rulename] = style.addRule( ":root", "--d3spec-background-color: " + color.cssText() + ";" ); //Actually this will set the background for the entire chart... - m_cssRules[rulename] = style.addRule( "#" + id() + " > svg", "background: " + color.cssText() ); + //m_cssRules[rulename] = style.addRule( "#" + id() + " > svg", "background: " + color.cssText() ); // to set background color for just this chart }//setChartMarginColor(...) @@ -1723,8 +1718,9 @@ void D3SpectrumDisplayDiv::setChartBackgroundColor( const Wt::WColor &color ) if( m_cssRules.count(rulename) ) style.removeRule( m_cssRules[rulename] ); - - m_cssRules[rulename] = style.addRule( "#chartarea" + id(), "fill: " + c ); + + m_cssRules[rulename] = style.addRule( ":root", "--d3spec-chart-area-color: " + c + ";" ); + //m_cssRules[rulename] = style.addRule( "#chartarea" + id(), "fill: " + c ); //If we wanted to apply this color to only this chart } void D3SpectrumDisplayDiv::setDefaultPeakColor( const Wt::WColor &color ) From 6a6abe65ab5ac23c859f3f9ca17a3e4cf0adb475 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 20:42:18 -0700 Subject: [PATCH 09/14] Add uncertainty to peak info table for the peak area column. Figuring out how many significant figures to show could probably still use some refinement. --- src/PeakInfoDisplay.cpp | 2 +- src/PeakModel.cpp | 108 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/src/PeakInfoDisplay.cpp b/src/PeakInfoDisplay.cpp index 2fe080bd..f1697e0b 100644 --- a/src/PeakInfoDisplay.cpp +++ b/src/PeakInfoDisplay.cpp @@ -708,7 +708,7 @@ void PeakInfoDisplay::init() dblDelagate = new ItemDelegate( m_infoView ); dblDelagate->setTextFormat( "%.0f" ); - m_infoView->setItemDelegateForColumn( PeakModel::kAmplitude, dblDelagate ); + //m_infoView->setItemDelegateForColumn( PeakModel::kAmplitude, dblDelagate ); // We'll actually return a string for peak amplitude m_infoView->setItemDelegateForColumn( PeakModel::kRoiCounts, dblDelagate ); //tweak column widths diff --git a/src/PeakModel.cpp b/src/PeakModel.cpp index 5a018db9..822850c0 100644 --- a/src/PeakModel.cpp +++ b/src/PeakModel.cpp @@ -1921,7 +1921,38 @@ boost::any PeakModel::data( const WModelIndex &index, int role ) const }//switch( peak->type() ) case kAmplitude: - return getPeakArea(); + { + boost::any areaAny = getPeakArea(); + if( areaAny.empty() ) + return areaAny; + + const double area = boost::any_cast(areaAny); + double uncert = peak->amplitudeUncert(); + + switch( peak->type() ) + { + case PeakDef::GaussianDefined: + break; + case PeakDef::DataDefined: + uncert = -1.0; // JIC + break; + }//switch( peak->type() ) + + if( uncert <= 0.0 ) + { + char text[64]; + snprintf( text, sizeof(text), "%.2f", area ); + return WString::fromUTF8(text); + } + + // TODO: Figure out how many significant figures to show - this is kinda a guess for the moment + const int numValLeft = 1 + static_cast( std::floor( std::log10(area) ) ); //1.2 will give 1, 10.1 will give 2 + //const int numUncertLeft = 1 + static_cast( std::floor( std::log10(uncert) ) ); //0.11 will give 0, 0.011 will give -1 + + const int nsigfig = 1 + ((area > 1.0) ? std::min(std::max(3,numValLeft), 6) : 4); //we'll print out one place past decimal point + const string txt = PhysicalUnits::printValueWithUncertainty( area, uncert, nsigfig ); + return WString::fromUTF8(txt); + }//case kAmplitude: case PeakModel::kCps: { @@ -2570,7 +2601,7 @@ bool PeakModel::setData( const WModelIndex &index, return false; }//switch( section ) - double dbl_val = 0.0; + double dbl_val = 0.0, uncert_val = -1.0; WString txt_val; try @@ -2584,7 +2615,67 @@ bool PeakModel::setData( const WModelIndex &index, case kMean: case kFwhm: case kAmplitude: try { - dbl_val = std::stod( txt_val.toUTF8() ); + const string strval = txt_val.toUTF8(); + + const auto parseValWithUncert = []( const string &input, string &value, string &uncert ){ + string::size_type pos = input.find( "\xC2\xB1" ); + if( pos == string::npos ) + pos = input.find( "+-" ); + if( pos == string::npos ) + pos = input.find( "-+" ); + if( pos == string::npos ) + { + value = input; + SpecUtils::trim(value); + return; + } + + value = input.substr(0, pos); + uncert = input.substr(pos + 2); //All "+-" strings are two bytes long + SpecUtils::trim(value); + SpecUtils::trim(uncert); + }; + + string valstr, uncertstr; + parseValWithUncert( strval, valstr, uncertstr ); + + dbl_val = std::stod( valstr ); + if( !uncertstr.empty() ) + uncert_val = std::stod( uncertstr ); + + // There could be some rounding in the string representation of the uncertainty, or the + // area so lets avoid this if the user hasnt changed that part of it + const boost::any prevData = data( index, role ); + if( !prevData.empty() ) + { + WString prevDataWstr = boost::any_cast( prevData ); + + assert( row < m_sortedPeaks.size() ); + const PeakShrdPtr &peak = m_sortedPeaks[row]; + + string prevValStr, prevUncertStr; + parseValWithUncert( prevDataWstr.toUTF8(), prevValStr, prevUncertStr ); + + if( prevValStr == valstr ) + { + if( column == kMean ) + dbl_val = peak->mean(); + else if( column == kFwhm ) + dbl_val = peak->fwhm(); + else if( column == kAmplitude && (peak->type() == PeakDef::GaussianDefined) ) + dbl_val = peak->amplitude(); + }//if( prevValStr == valstr ) + + if( (uncert_val > 0.0) && (prevUncertStr == uncertstr) ) + { + if( column == kMean ) + uncert_val = peak->meanUncert(); + else if( column == kFwhm ) + uncert_val = 2.3548201 * peak->sigmaUncert(); + else if( column == kAmplitude && (peak->type() == PeakDef::GaussianDefined) ) + uncert_val = peak->amplitudeUncert(); + }//if( prevValStr == valstr ) + }//if( prevData, so we can potentially avoid rounding values ) }catch(...) { cerr << "PeakModel::setData(...)\n\tUnable to convert '" << txt_val @@ -2611,6 +2702,8 @@ bool PeakModel::setData( const WModelIndex &index, { case PeakDef::GaussianDefined: new_peak.setMean( dbl_val ); + if( uncert_val > 0.0 ) + new_peak.setMeanUncert( uncert_val ); break; case PeakDef::DataDefined: @@ -2624,6 +2717,8 @@ bool PeakModel::setData( const WModelIndex &index, { case PeakDef::GaussianDefined: new_peak.setSigma( dbl_val / 2.3548201 ); + if( uncert_val > 0.0 ) + new_peak.setSigmaUncert( uncert_val / 2.3548201 ); break; case PeakDef::DataDefined: @@ -2637,6 +2732,8 @@ bool PeakModel::setData( const WModelIndex &index, { case PeakDef::GaussianDefined: new_peak.setAmplitude( dbl_val ); + if( uncert_val > 0.0 ) + new_peak.setAmplitudeUncert( uncert_val ); break; case PeakDef::DataDefined: @@ -2962,10 +3059,11 @@ bool PeakModel::setData( const WModelIndex &index, if( column != kIsotope && column != kPhotoPeakEnergy - && column != kMean ) + && column != kMean + && column != kAmplitude ) { dataChanged().emit( index, index ); - }else if( column == kPhotoPeakEnergy ) + }else if( (column == kPhotoPeakEnergy) || (column == kAmplitude) ) { if( changedFit ) dataChanged().emit( PeakModel::index(row, kIsotope), PeakModel::index(row, kNumColumns-1) ); //just update whole row, jic From 5abd3e9fe112a98e90173a1f4cde9777b288ff86 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 21:13:03 -0700 Subject: [PATCH 10/14] Make area and fwhm not editable for non-gaussian peaks. --- src/PeakModel.cpp | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/PeakModel.cpp b/src/PeakModel.cpp index 822850c0..48e8e843 100644 --- a/src/PeakModel.cpp +++ b/src/PeakModel.cpp @@ -2583,7 +2583,7 @@ bool PeakModel::setData( const WModelIndex &index, switch( column ) { - case kMean: case kFwhm: case kAmplitude: + case kMean: case kIsotope: case kPhotoPeakEnergy: case kCandidateIsotopes: @@ -2593,6 +2593,21 @@ bool PeakModel::setData( const WModelIndex &index, case kUserLabel: case kPeakLineColor: break; + + case kFwhm: + case kAmplitude: + { + switch( m_sortedPeaks[row]->type() ) + { + case PeakDef::GaussianDefined: + break; + case PeakDef::DataDefined: + cerr << "PeakModel::setData(...)\n\tCant set area or FWHM for non-Gaussian peak" << endl; + return false; + }//switch( m_sortedPeaks[row]->type() ) + + break; + }//case kFwhm or kAmplitude case kCps: case kHasSkew: case kSkewAmount: case kType: case kLowerX: case kUpperX: case kRoiCounts: case kContinuumType: case kNumColumns: case kDifference: @@ -3097,13 +3112,30 @@ WFlags PeakModel::flags( const WModelIndex &index ) const { switch( index.column() ) { - case kMean: case kFwhm: case kAmplitude: case kUserLabel: + case kMean: case kUserLabel: case kIsotope: case kPhotoPeakEnergy: #if( ALLOW_PEAK_COLOR_DELEGATE ) case kPeakLineColor: #endif return ItemIsEditable | ItemIsSelectable; //ItemIsSelectabl + case kFwhm: case kAmplitude: + { + const int row = index.row(); + if( row < static_cast(m_sortedPeaks.size()) ) + { + switch( m_sortedPeaks[row]->type() ) + { + case PeakDef::GaussianDefined: + return ItemIsEditable | ItemIsSelectable; + case PeakDef::DataDefined: + return ItemIsSelectable; + }//switch( peak type ) + }//if( a valid row of a peak ) + + return ItemIsEditable | ItemIsSelectable; + }//case kFwhm: case kAmplitude: + case kUseForShieldingSourceFit: case kUseForCalibration: case kUseForManualRelEff: From 7c56ec4a33a2a993bf09de0511014e09d8de2a4f Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 8 Jul 2024 21:15:36 -0700 Subject: [PATCH 11/14] Remove static CSS file from install, we no longer have/need --- target/wxWidgets/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/target/wxWidgets/CMakeLists.txt b/target/wxWidgets/CMakeLists.txt index 06eddf6b..640836f9 100644 --- a/target/wxWidgets/CMakeLists.txt +++ b/target/wxWidgets/CMakeLists.txt @@ -184,7 +184,6 @@ install( ${CMAKE_CURRENT_SOURCE_DIR}/../../external_libs/SpecUtils/d3_resources/d3.v3.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../../external_libs/SpecUtils/d3_resources/SpectrumChartD3.css ${CMAKE_CURRENT_SOURCE_DIR}/../../external_libs/SpecUtils/d3_resources/SpectrumChartD3.js - ${CMAKE_CURRENT_SOURCE_DIR}/../../external_libs/SpecUtils/d3_resources/SpectrumChartD3StandAlone.css DESTINATION "${RESOURCE_DEST_DIR}/InterSpec_resources" ) From 1c5b4acdb07d0bb17122d7b42e65e5e2257437cd Mon Sep 17 00:00:00 2001 From: William Johnson Date: Fri, 12 Jul 2024 10:48:01 -0700 Subject: [PATCH 12/14] Bump version of SpecUtils --- external_libs/SpecUtils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external_libs/SpecUtils b/external_libs/SpecUtils index f0949dea..b2b7a174 160000 --- a/external_libs/SpecUtils +++ b/external_libs/SpecUtils @@ -1 +1 @@ -Subproject commit f0949dea5c1379d1ee5f58ab5e3f6dfff82e81bd +Subproject commit b2b7a174135c712bd5ffac7d5453dff6d1fa7a1e From a0aafd2a6e9f8ce70fff10206eed77aa5efc9835 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Fri, 12 Jul 2024 11:51:37 -0700 Subject: [PATCH 13/14] Fix printing out distance in RID result dialog. --- src/ShowRiidInstrumentsAna.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ShowRiidInstrumentsAna.cpp b/src/ShowRiidInstrumentsAna.cpp index 4d62bb79..2e53103e 100644 --- a/src/ShowRiidInstrumentsAna.cpp +++ b/src/ShowRiidInstrumentsAna.cpp @@ -292,7 +292,7 @@ class AnaResultDisplay : public WContainerWidget + ""; if( res.distance_ > 0.0 ) result += "" + WString::tr("Distance").toUTF8() - + "" + PhysicalUnits::printToBestLengthUnits(0.1*res.distance_) + ""; + + "" + PhysicalUnits::printToBestLengthUnits(res.distance_/PhysicalUnits::mm,4) + ""; if( res.activity_ > 0.0 ) { const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); From 79f24fc2a7a7d9e26572b08c0848c2716ec048c1 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Fri, 12 Jul 2024 11:59:41 -0700 Subject: [PATCH 14/14] Fix to mult. instead of divide mm units. (not that it matters, `PhysicalUnits::mm == 1`. --- src/ShowRiidInstrumentsAna.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ShowRiidInstrumentsAna.cpp b/src/ShowRiidInstrumentsAna.cpp index 2e53103e..2bb88987 100644 --- a/src/ShowRiidInstrumentsAna.cpp +++ b/src/ShowRiidInstrumentsAna.cpp @@ -292,7 +292,7 @@ class AnaResultDisplay : public WContainerWidget + ""; if( res.distance_ > 0.0 ) result += "" + WString::tr("Distance").toUTF8() - + "" + PhysicalUnits::printToBestLengthUnits(res.distance_/PhysicalUnits::mm,4) + ""; + + "" + PhysicalUnits::printToBestLengthUnits(res.distance_ * PhysicalUnits::mm,4) + ""; if( res.activity_ > 0.0 ) { const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() );