1
+ #include < string>
2
+
1
3
// ToolAnalysis includes
2
4
#include " ADCCalibrator.h"
3
5
#include " ANNIEalgorithms.h"
@@ -56,6 +58,9 @@ bool ADCCalibrator::Execute() {
56
58
const auto & channel_key = temp_pair.first ;
57
59
const auto & raw_waveforms = temp_pair.second ;
58
60
61
+ Log (" Making calibrated waveforms for ADC channel " +
62
+ std::to_string (channel_key.GetDetectorElementIndex ()), 3 , verbosity);
63
+
59
64
calibrated_waveform_map[channel_key] = make_calibrated_waveforms (
60
65
raw_waveforms);
61
66
}
@@ -74,17 +79,22 @@ void ADCCalibrator::ze3ra_baseline(
74
79
const std::vector< Waveform<unsigned short > >& raw_data,
75
80
double & baseline, double & sigma_baseline, size_t num_baseline_samples)
76
81
{
77
- // All F-distribution probabilities below this value will pass the
78
- // variance consistency test in ze3ra_baseline()
79
- double q_critical;
80
- m_variables.Get (" QCritical" , q_critical);
82
+ int verbosity;
83
+ m_variables.Get (" verbose" , verbosity);
84
+
85
+ // All F-distribution probabilities above this value will pass the
86
+ // variance consistency test in ze3ra_baseline(). That is, p_critical
87
+ // is the maximum p-value for which we will reject the null hypothesis
88
+ // of equal variances.
89
+ double p_critical;
90
+ m_variables.Get (" PCritical" , p_critical);
81
91
82
92
// Signal ADC means, variances, and F-distribution probability values
83
- // ("Q ") for the first num_baseline_samples from each minibuffer
93
+ // ("P ") for the first num_baseline_samples from each minibuffer
84
94
// (in Hefty mode) or from each sub-minibuffer (in non-Hefty mode)
85
95
std::vector<double > means;
86
96
std::vector<double > variances;
87
- std::vector<double > Qs ;
97
+ std::vector<double > Ps ;
88
98
89
99
// Hefty mode uses multiple minibuffers, non-Hefty mode uses a single
90
100
// minibuffer
@@ -136,11 +146,20 @@ void ADCCalibrator::ze3ra_baseline(
136
146
else F = sigma2_jp1 / sigma2_j;
137
147
138
148
double nu = (num_baseline_samples - 1 ) / 2 .;
139
- double Q = std::tgamma (2 *nu)
140
- * annie_math::Incomplete_Beta_Function (1 . / (1 . + F), nu, nu)
141
- / (2 . * std::tgamma (nu));
149
+ double P = annie_math::Regularized_Beta_Function (1 . / (1 . + F), nu, nu);
150
+
151
+ // Two-tailed hypothesis test (we need to exclude unusually small values
152
+ // as well as unusually large ones). The tails have equal sizes, so we
153
+ // may use symmetry and simply multiply our earlier result by 2.
154
+ P *= 2 .;
155
+
156
+ // I've never seen this problem (the numerical values for the regularized
157
+ // beta function that I've checked all fall within [0,1]), but the book
158
+ // Numerical Recipes includes this check in a similar block of code,
159
+ // so I'll add it just in case.
160
+ if (P > 1 .) P = 2 . - P;
142
161
143
- Qs .push_back (Q );
162
+ Ps .push_back (P );
144
163
}
145
164
146
165
// Compute the mean and standard deviation of the baseline signal
@@ -149,30 +168,61 @@ void ADCCalibrator::ze3ra_baseline(
149
168
// the critical value.
150
169
baseline = 0 .;
151
170
sigma_baseline = 0 .;
171
+ double variance_baseline = 0 .;
152
172
size_t num_passing = 0 ;
153
- for (size_t k = 0 ; k < Qs .size (); ++k) {
154
- if (Qs .at (k) < q_critical ) {
173
+ for (size_t k = 0 ; k < Ps .size (); ++k) {
174
+ if (Ps .at (k) > p_critical ) {
155
175
++num_passing;
156
176
baseline += means.at (k);
157
- sigma_baseline += std::sqrt ( variances.at (k) );
177
+ variance_baseline += variances.at (k);
158
178
}
159
179
}
160
180
161
- if (num_passing > 0 ) {
181
+ if (num_passing > 1 ) {
162
182
baseline /= num_passing;
163
- sigma_baseline /= num_passing;
183
+
184
+ variance_baseline *= static_cast <double >(num_baseline_samples - 1 )
185
+ / (num_passing*num_baseline_samples - 1 );
186
+ // Now that we've combined the sample variances correctly, take the
187
+ // square root to get the standard deviation
188
+ sigma_baseline = std::sqrt ( variance_baseline );
189
+ }
190
+ else if (num_passing == 1 ) {
191
+ // We only have one set of sample statistics, so all we need to
192
+ // do is take the square root of the variance to get the standard
193
+ // deviation.
194
+ sigma_baseline = std::sqrt ( variance_baseline );
164
195
}
165
196
else {
166
197
// If none of the minibuffers passed the F-distribution test,
167
- // choose the one closest to passing and adopt its baseline statistics
198
+ // choose the one closest to passing (i.e., the one with the largest
199
+ // P-value) and adopt its baseline statistics. For a sufficiently large
200
+ // number of minibuffers (e.g., 40), such a situation should be very rare.
168
201
// TODO: consider changing this approach
169
- auto min_iter = std::min_element (Qs .cbegin (), Qs .cend ());
170
- int min_index = std::distance (Qs .cbegin (), min_iter );
202
+ auto max_iter = std::max_element (Ps .cbegin (), Ps .cend ());
203
+ int max_index = std::distance (Ps .cbegin (), max_iter );
171
204
172
- baseline = means.at (min_index );
173
- sigma_baseline = std::sqrt ( variances.at (min_index ) );
205
+ baseline = means.at (max_index );
206
+ sigma_baseline = std::sqrt ( variances.at (max_index ) );
174
207
}
175
208
209
+ std::string mb_temp_string = " minibuffer" ;
210
+ if ( !hefty_mode ) mb_temp_string = " sub-minibuffer" ;
211
+
212
+ if (verbosity >= 4 ) {
213
+ for ( size_t x = 0 ; x < Ps.size (); ++x ) {
214
+ Log (" " + mb_temp_string + " " + std::to_string (x) + " , mean = "
215
+ + std::to_string (means.at (x)) + " , var = "
216
+ + std::to_string (variances.at (x)) + " , p-value = "
217
+ + std::to_string (Ps.at (x)), 4 , verbosity);
218
+ }
219
+ }
220
+
221
+ Log (std::to_string (num_passing) + " " + mb_temp_string + " pairs passed the"
222
+ " F-test" , 3 , verbosity);
223
+ Log (" Baseline estimate: " + std::to_string (baseline) + " ± "
224
+ + std::to_string (sigma_baseline) + " ADC counts" , 3 , verbosity);
225
+
176
226
}
177
227
178
228
std::vector< CalibratedADCWaveform<double > >
0 commit comments