|
| 1 | +package org.jlab.detector.qadb; |
| 2 | + |
| 3 | +import java.util.List; |
| 4 | +import org.jlab.detector.scalers.DaqScalers; |
| 5 | +import org.jlab.detector.scalers.DaqScalersSequence; |
| 6 | + |
| 7 | +/** |
| 8 | + * A single bin for the Quality Assurance Database (QADB). |
| 9 | + * It may hold arbitrary data, such as a class instance, accessible by public member {@link QadbBin#data}; |
| 10 | + * its type is set by a generic type parameter. |
| 11 | + * <p> |
| 12 | + * A bin contains a (sub)sequence of scaler readouts, and therefore extends {@link DaqScalersSequence}. |
| 13 | + * @see QadbBinSequence |
| 14 | + * @author dilks |
| 15 | + */ |
| 16 | +public class QadbBin<T> extends DaqScalersSequence { |
| 17 | + |
| 18 | + private int binNum; |
| 19 | + private BinType binType; |
| 20 | + private int evnumMin; |
| 21 | + private int evnumMax; |
| 22 | + private long timestampMin; |
| 23 | + private long timestampMax; |
| 24 | + private double charge; // ungated |
| 25 | + private double chargeGated; |
| 26 | + |
| 27 | + /** arbitrary data that may be held by this bin; it is just public so the user can do anything with it */ |
| 28 | + public T data; |
| 29 | + |
| 30 | + // ---------------------------------------------------------------------------------- |
| 31 | + |
| 32 | + /** lambda type to print each bin's generic data as a string */ |
| 33 | + public interface DataPrinter<T> { |
| 34 | + /** |
| 35 | + * @param data the public member {@link QadbBin#data} |
| 36 | + * @return a String representation of {@link QadbBin#data} |
| 37 | + */ |
| 38 | + String run(T data); |
| 39 | + } |
| 40 | + |
| 41 | + /** bin type */ |
| 42 | + public enum BinType { |
| 43 | + /** the first bin, for events before the first scaler readout */ |
| 44 | + FIRST, |
| 45 | + /** any bin between two scaler readouts */ |
| 46 | + INTERMEDIATE, |
| 47 | + /** the last bin, for events after the last scaler readout */ |
| 48 | + LAST, |
| 49 | + } |
| 50 | + |
| 51 | + /** charge type */ |
| 52 | + public enum ChargeType { |
| 53 | + /** full charge, DAQ-ungated */ |
| 54 | + UNGATED, |
| 55 | + /** DAQ-gated charge */ |
| 56 | + GATED, |
| 57 | + } |
| 58 | + |
| 59 | + // ---------------------------------------------------------------------------------- |
| 60 | + |
| 61 | + /** |
| 62 | + * construct a single bin |
| 63 | + * @param binNum the bin number, in the {@link QadbBinSequence} which contains this bin |
| 64 | + * @param binType the bin type (see {@link BinType}) |
| 65 | + * @param inputScalers the scaler sequence for this bin |
| 66 | + * @param initData the initial data for this bin (sets public member {@link data}) |
| 67 | + */ |
| 68 | + public QadbBin(int binNum, BinType binType, List<DaqScalers> inputScalers, T initData) { |
| 69 | + super(inputScalers); |
| 70 | + this.binNum = binNum; |
| 71 | + this.binType = binType; |
| 72 | + this.data = initData; |
| 73 | + switch(this.binType) { |
| 74 | + case INTERMEDIATE -> { |
| 75 | + this.timestampMin = this.scalers.get(0).getTimestamp(); |
| 76 | + this.timestampMax = this.scalers.get(scalers.size()-1).getTimestamp(); |
| 77 | + this.evnumMin = this.scalers.get(0).getEventNum(); |
| 78 | + this.evnumMax = this.scalers.get(scalers.size()-1).getEventNum(); |
| 79 | + this.charge = this.getInterval().getBeamCharge(); |
| 80 | + this.chargeGated = this.getInterval().getBeamChargeGated(); |
| 81 | + } |
| 82 | + case FIRST -> { |
| 83 | + if(this.scalers.size() != 1) |
| 84 | + throw new RuntimeException("a FIRST bin may only have ONE scaler readout"); |
| 85 | + this.timestampMin = 0; // user may correct this using `correctLowerBound` |
| 86 | + this.timestampMax = this.scalers.get(0).getTimestamp(); |
| 87 | + this.evnumMin = 0; // user may correct this using `correctLowerBound` |
| 88 | + this.evnumMax = this.scalers.get(0).getEventNum(); |
| 89 | + this.charge = 0; // since no lower bound |
| 90 | + this.chargeGated = 0; // since no lower bound |
| 91 | + } |
| 92 | + case LAST -> { |
| 93 | + if(this.scalers.size() != 1) |
| 94 | + throw new RuntimeException("a LAST bin may only have ONE scaler readout"); |
| 95 | + this.timestampMin = this.scalers.get(0).getTimestamp(); |
| 96 | + this.timestampMax = 10 * this.timestampMin; // user may correct this using `correctUpperBound` |
| 97 | + this.evnumMin = this.scalers.get(0).getEventNum(); |
| 98 | + this.evnumMax = 10 * this.evnumMin; // user may correct this using `correctUpperBound` |
| 99 | + this.charge = 0; // since no upper bound |
| 100 | + this.chargeGated = 0; // since no upper bound |
| 101 | + } |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + // ---------------------------------------------------------------------------------- |
| 106 | + |
| 107 | + /** @return the bin number for this bin */ |
| 108 | + public int getBinNum() { return this.binNum; } |
| 109 | + |
| 110 | + /** @return minimum timestamp for this bin */ |
| 111 | + public long getTimestampMin() { return this.timestampMin; } |
| 112 | + |
| 113 | + /** @return maximum timestamp for this bin */ |
| 114 | + public long getTimestampMax() { return this.timestampMax; } |
| 115 | + |
| 116 | + /** @return minimum event number for this bin */ |
| 117 | + public long getEventNumMin() { return this.evnumMin; } |
| 118 | + |
| 119 | + /** @return maximum event number for this bin */ |
| 120 | + public long getEventNumMax() { return this.evnumMax; } |
| 121 | + |
| 122 | + /** @return the beam charge, not gated by DAQ, for this bin */ |
| 123 | + public double getBeamCharge() { return this.charge; } |
| 124 | + |
| 125 | + /** @return the beam charge, gated by DAQ, for this bin */ |
| 126 | + public double getBeamChargeGated() { return this.chargeGated; } |
| 127 | + |
| 128 | + /** |
| 129 | + * @return the beam charge, gated or ungated |
| 130 | + * @param chargeType the type of charge |
| 131 | + */ |
| 132 | + public double getBeamCharge(ChargeType chargeType) { |
| 133 | + return switch(chargeType) { |
| 134 | + case UNGATED -> this.getBeamCharge(); |
| 135 | + case GATED -> this.getBeamChargeGated(); |
| 136 | + }; |
| 137 | + } |
| 138 | + |
| 139 | + // ---------------------------------------------------------------------------------- |
| 140 | + |
| 141 | + /** @return the mean livetime for this bin */ |
| 142 | + public double getMeanLivetime() { |
| 143 | + double sumLivetime = 0; |
| 144 | + int numLivetimeEvents = 0; |
| 145 | + for(int i=1; i<scalers.size(); i++) { // skip the first scaler, since that's for the previous bin |
| 146 | + var livetime = scalers.get(i).dsc2.getLivetime(); |
| 147 | + if(livetime >= 0) { // filter out livetime = -1 |
| 148 | + sumLivetime += livetime; |
| 149 | + numLivetimeEvents++; |
| 150 | + } |
| 151 | + } |
| 152 | + return numLivetimeEvents > 0 ? sumLivetime / numLivetimeEvents : 0; |
| 153 | + } |
| 154 | + |
| 155 | + /** @return the duration of the bin, in seconds */ |
| 156 | + public double getDuration() { |
| 157 | + return (this.getTimestampMax() - this.getTimestampMin()) * 4e-9; // convert timestamp units [4ns] -> [s] |
| 158 | + } |
| 159 | + |
| 160 | + // ---------------------------------------------------------------------------------- |
| 161 | + |
| 162 | + /** charge correction method */ |
| 163 | + public enum ChargeCorrectionMethod { |
| 164 | + /** interchange the DAQ-gated and ungated charges */ |
| 165 | + BY_FLIP, |
| 166 | + /** calculate the DAQ-gated charge as the mean livetime multiplied by the ungated charge */ |
| 167 | + BY_MEAN_LIVETIME, |
| 168 | + } |
| 169 | + |
| 170 | + /** |
| 171 | + * correct the beam charge for this bin, using a correction method |
| 172 | + * @param method the correction method to use |
| 173 | + * @see ChargeCorrectionMethod |
| 174 | + */ |
| 175 | + public void correctCharge(ChargeCorrectionMethod method) { |
| 176 | + logger.fine("correcting beam charge for bin " + this.binNum + " using method " + method); |
| 177 | + logger.fine(" before: gated = " + this.chargeGated); |
| 178 | + logger.fine(" ungated = " + this.charge); |
| 179 | + switch(method) { |
| 180 | + case BY_FLIP -> { // interchange the gated and ungated charge |
| 181 | + var tmp = this.charge; |
| 182 | + this.charge = this.chargeGated; |
| 183 | + this.chargeGated = tmp; |
| 184 | + } |
| 185 | + case BY_MEAN_LIVETIME -> { // gated = <livetime> * ungated |
| 186 | + var meanLivetime = this.getMeanLivetime(); |
| 187 | + logger.fine(" mean livetime = " + meanLivetime); |
| 188 | + this.chargeGated = meanLivetime * this.charge; |
| 189 | + } |
| 190 | + } |
| 191 | + logger.fine(" after: gated = " + this.chargeGated); |
| 192 | + logger.fine(" ungated = " + this.charge); |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * correct the beam charge for this bin, using specific values from the caller |
| 197 | + * @param charge the charge, not gated by the DAQ |
| 198 | + * @param chargeGated the DAQ-gated charge |
| 199 | + */ |
| 200 | + public void correctCharge(double charge, double chargeGated) { |
| 201 | + logger.fine("correcting beam charge for bin " + this.binNum + " using user-specified values"); |
| 202 | + logger.fine(" before: gated = " + this.chargeGated); |
| 203 | + logger.fine(" ungated = " + this.charge); |
| 204 | + this.charge = charge; |
| 205 | + this.chargeGated = chargeGated; |
| 206 | + logger.fine(" after: gated = " + this.chargeGated); |
| 207 | + logger.fine(" ungated = " + this.charge); |
| 208 | + } |
| 209 | + |
| 210 | + // ---------------------------------------------------------------------------------- |
| 211 | + |
| 212 | + /** |
| 213 | + * correct the first bin's lower bound, if you know it from tag-0 events |
| 214 | + * @param evnumMin the correct minimum event number |
| 215 | + * @param timestampMin the correct minimum timestamp |
| 216 | + */ |
| 217 | + public void correctLowerBound(int evnumMin, long timestampMin) { |
| 218 | + if(this.binType == BinType.FIRST) { |
| 219 | + this.evnumMin = evnumMin; |
| 220 | + this.timestampMin = timestampMin; |
| 221 | + } |
| 222 | + else logger.warning("not allowed to correct the lower bound of a bin with type " + this.binType); |
| 223 | + } |
| 224 | + |
| 225 | + /** |
| 226 | + * correct the last bin's upper bound, if you know it from tag-0 events |
| 227 | + * @param evnumMax the correct maximum event number |
| 228 | + * @param timestampMax the correct maximum timestamp |
| 229 | + */ |
| 230 | + public void correctUpperBound(int evnumMax, long timestampMax) { |
| 231 | + if(this.binType == BinType.LAST) { |
| 232 | + this.evnumMax = evnumMax; |
| 233 | + this.timestampMax = timestampMax; |
| 234 | + } |
| 235 | + else logger.warning("not allowed to correct the upper bound of a bin with type " + this.binType); |
| 236 | + } |
| 237 | + |
| 238 | + // ---------------------------------------------------------------------------------- |
| 239 | + |
| 240 | + /** extremum type, used with {@link QadbBin#getChargeExtremum} */ |
| 241 | + public enum ExtremumType { |
| 242 | + /** from the first scaler readout */ |
| 243 | + FIRST, |
| 244 | + /** from the last scaler readout */ |
| 245 | + LAST, |
| 246 | + /** the maximum */ |
| 247 | + MAX, |
| 248 | + /** the minimum */ |
| 249 | + MIN, |
| 250 | + } |
| 251 | + |
| 252 | + /** |
| 253 | + * Get the min/max or initial/final charge. |
| 254 | + * <p> |
| 255 | + * WARNING: this is likely NOT corrected by {@link correctCharge} |
| 256 | + * @param extremumType the type of extremum |
| 257 | + * @param chargeType the type of charge |
| 258 | + * @return the charge for the given extremum |
| 259 | + */ |
| 260 | + public double getChargeExtremum(ExtremumType extremumType, ChargeType chargeType) { |
| 261 | + return switch(extremumType) { |
| 262 | + case FIRST -> getDsc2Charge(this.scalers.get(0), chargeType); |
| 263 | + case LAST -> getDsc2Charge(this.scalers.get(this.scalers.size()-1), chargeType); |
| 264 | + case MIN -> getDsc2Charge(this.scalers.stream().reduce((a,b) -> getDsc2Charge(a, chargeType) < getDsc2Charge(b, chargeType) ? a : b).get(), chargeType); |
| 265 | + case MAX -> getDsc2Charge(this.scalers.stream().reduce((a,b) -> getDsc2Charge(a, chargeType) > getDsc2Charge(b, chargeType) ? a : b).get(), chargeType); |
| 266 | + }; |
| 267 | + } |
| 268 | + |
| 269 | + /** helper method for {@link getChargeExtremum} |
| 270 | + * @param ds the scaler readout |
| 271 | + * @param chargeType the charge type |
| 272 | + * @return the charge from this scaler object |
| 273 | + */ |
| 274 | + private static double getDsc2Charge(DaqScalers ds, ChargeType chargeType) { |
| 275 | + return switch(chargeType) { |
| 276 | + case UNGATED -> ds.dsc2.getBeamCharge(); |
| 277 | + case GATED -> ds.dsc2.getBeamChargeGated(); |
| 278 | + }; |
| 279 | + } |
| 280 | + |
| 281 | + // ---------------------------------------------------------------------------------- |
| 282 | + |
| 283 | + /** print a QA bin, and some basic information */ |
| 284 | + public void print() { |
| 285 | + System.out.printf("BIN %d", this.getBinNum()); |
| 286 | + System.out.printf(" -----------\n"); |
| 287 | + System.out.printf("%30s %d to %d\n", "timestamp interval:", this.getTimestampMin(), this.getTimestampMax()); |
| 288 | + System.out.printf("%30s %d to %d\n", "event number interval:", this.getEventNumMin(), this.getEventNumMax()); |
| 289 | + System.out.printf("%30s %f s\n", "duration:", this.getDuration()); |
| 290 | + System.out.printf("%30s %d events\n", "event number range:", this.getEventNumMax() - this.getEventNumMin()); |
| 291 | + System.out.printf("%30s %f / %f\n", "beam charge gated / ungated:", this.getBeamChargeGated(), this.getBeamCharge()); |
| 292 | + } |
| 293 | + |
| 294 | + /** |
| 295 | + * print a QA bin's stored {@link data} |
| 296 | + * @param dataPrinter a lambda which resolves {@link data} as a {@code String} |
| 297 | + */ |
| 298 | + public void print(DataPrinter<T> dataPrinter) { |
| 299 | + System.out.printf("BIN %d :: ", this.getBinNum()); |
| 300 | + System.out.println(dataPrinter.run(this.data)); |
| 301 | + } |
| 302 | + |
| 303 | + /** |
| 304 | + * print a QA bin's stored {@link data}, and optionally the bin's basic information |
| 305 | + * @param dataPrinter a lambda which resolves {@link data} as a {@code String} |
| 306 | + * @param verbose if {@code true}, print more |
| 307 | + */ |
| 308 | + public void print(DataPrinter<T> dataPrinter, boolean verbose) { |
| 309 | + if(verbose) { |
| 310 | + this.print(); |
| 311 | + System.out.println(dataPrinter.run(this.data)); |
| 312 | + } |
| 313 | + else |
| 314 | + this.print(dataPrinter); |
| 315 | + } |
| 316 | + |
| 317 | +} |
0 commit comments