// PCACal.cc // Contact person: Freija Descamps // See PCACal.hh for more details //———————————————————————// #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace RAT { // Time is set to DS::INVALID if calibration fails const double kFailValue = DS::INVALID; const double kFailCheck = DS::INVALID; const int kPCABitOffset = 32; PCACal::PCACal() { // Make sure these are all zero fQHS = 0.; fQHL = 0.; fQLX = 0.; fTime = 0.; // Set all Booleans fTimeWalkResultOK=false; fGainFitresultOK=false; fDoTimeWalk = false; fDoGainFit = false; } PCACal::~PCACal() { } void PCACal::BeginOfRun( DS::Run& run ) { PCABits pcabits; pcabits.BeginOfRun(run); // Set up the necessary links to the database fCalbank = DB::Get()->GetLink("PMTCAL"); // The requested PCA calibration fPCAtype = fCalbank->GetI("pca_type"); // The requested tolerance level fTolLevel = fCalbank->GetI("pca_validation"); // These are the TimeWalk PCA calibration constants fTimeWalkBank = DB::Get()->GetLink("PCA_TW"); //Check if we are calibrating MC fIsMC = run.GetMCFlag(); if(fIsMC) info<<" PCACal::BeginOfRun: Analyzing MC. PCA calibration won't be applied. "<((fPCAtype%100)/10) == 1)) { fDoGainFit = true; // No npe is extracted at this time PCACal::DontDie(GAINFIT,0,0); } if(fDoTimeWalk) { // Find out how many interpolation points there are fPCANpointsTimeWalk=fTimeWalkBank->GetI("tw_npoints"); // Read in the array with the timewalk interpolation points fTimeWalkPoints=fTimeWalkBank->GetIArray("twinter"); // Read in the array with the timewalk status words fTimeWalkStatus=fTimeWalkBank->GetIArray("PCATW_status"); // Find out if these are SNO calibration constants fIsSNO=fTimeWalkBank->GetI("is_sno"); if( fIsSNO ) { // Warn the user that we are using SNO constants PCACal::DontDie(SNOCONST,0,0); } // In order to save time during the calibration, extract all the // interpolation points at this point and store them! Trust me, // this makes everything much faster. // Loop through all channels and extract the interpolation points and the // fit parameters. for( unsigned i=0; i Do not correct by delays // For this case PCA time = ECA time // Flag this as a failed hit due to no TimeWalk calibration data fStatus.Set(kPCABitOffset + 2); PCACal::DontDie(FIRSTINT,lcn,0); return; // there is no calibration data available } // Set thisq to the first interpolation point thisq=fTimeWalkCharge[lcn][0]; thist=fTimeWalkTime[lcn][0]; // If the first interpolation point is defined, go ahead and // retrieve it! if( fTimeWalkCharge[lcn].size()<2 || fTimeWalkTime[lcn].size()<2 ) { // No interpolation points available -> Do not correct by delays // For this case PCA time = ECA time // Flag this as a failed hit: only 1 calibration data-point available fStatus.Set(kPCABitOffset + 3); PCACal::DontDie(SECONDINT,lcn,0); // One single interpolation point is not enough // to perform a time calibration return; } nextq=fTimeWalkCharge[lcn][1]; nextt=fTimeWalkTime[lcn][1]; // We now have two valid interpolation points, there is much rejoicing // Define qstep as the difference between these two points qstep=nextq-thisq; // Distance in QHS between the two interpolation points /* Calculate TW effect based on PCA constants. There are 3 cases: * 1. QHS too low (> */ if( fQHS= static_cast(i+1) && fTimeWalkTime[lcn].size() >= static_cast(i+1)){ // we have not yet exhausted the available interpolation points thisq=nextq; thist=nextt; nextq=fTimeWalkCharge[lcn][i+1]; nextt=fTimeWalkTime[lcn][i+1]; } // This loop is only entered if no more valid interpolation points are available else { // The next point is invalid. Therefore, in the current scheme // we will assume that there are no more valid interpolation points if( fQHS0 && fTimeWalkGradient.count(lcn)>0 ) { double gradient=fTimeWalkGradient[lcn]; double intercept=fTimeWalkIntercept[lcn]; // Now check the sanity of the intercept and gradient if( gradient>0 || intercept==0 ) { // It is pathological behaviour that the time // correction would go up with charge, // flag and use twalk=thist; fStatus.Set(kPCABitOffset + 6); twalk=thist; // Overwrite with the last time point break; // twalk found } if( gradient<-0.10 ) { // FIXME when real data comes in, // set this limit correctly. // It is pathological behaviour that the time // correction would go down so quickly with charge, // flag and use twalk=thist fStatus.Set(kPCABitOffset + 7); twalk=thist; break; // twalk found } // The correction in time to be applied to data-point: twalk = intercept+gradient*fQHS; break; // twalk found } else { // Else: last resort! There is no fit to the last // interpolation points saved. Use the last stored time, // this seems sloppy. Being in the high charge tail, // however, this should not have a big effect (<0.5ns). // Still, would be nice to make a better decision here. // FIXME when real data comes in, no more 'should' // The correction in time to be applied to data-point: twalk=thist; // Flag this as a failed high-charge hit fStatus.Set(kPCABitOffset + 5); } continue; } } qstep=nextq-thisq; } } // else fQHS > thisq // use the extracted twalk to correct the time // (twalk contains the electronic delay and the time walk // from PCA calibration data). It does not contain the transit // time for this event. fTime=fTime-twalk; return; } void PCACal::ValidateTimeWalk(int lcn){ // First, if we are using old SNO calibration constants, // set the flag and exit, no further validation is done. if( fIsSNO ) { fStatus.Set(kPCABitOffset + 31); // The SNO status word is as follows (see thesis J. Cameron): // (100 x tube_type) + (10 x tube_status) + fit_quality // The tube type is 1 for a normal tube and > 1 for all other tube types. // Tube status: // 0 passed // 1 offline // 2 zero occupancy // 3 low occupancy // 4 low ECA // Fit Quality // 0 not fitted // 1 passed // 2 no points // 3 failed cut // 4 failed RMS // // So '101' is a perfect score in SNO-lingo // Anything else should be indicated in the statusword. if( fTimeWalkStatus[lcn] != 101 ) { fStatus.Set(kPCABitOffset + 30); } } else // Only set the status word bits if we are using SNO+ // calibration constants. If one uses SNO calibration // constants, only one bit will be set: 31. { // Check the PCA TimeWalk status words and flag accordingly // 1. Check RMS // RMS way too high if( BitManip::TestBit(fTimeWalkStatus[lcn],7) ) { fStatus.Set(kPCABitOffset + 8); return; } // RMS too high if( BitManip::TestBit(fTimeWalkStatus[lcn],8) ) { fStatus.Set(kPCABitOffset + 9); } // 2. High Q tail not fitted if( BitManip::TestBit(fTimeWalkStatus[lcn],9) ) { fStatus.Set(kPCABitOffset + 5); } // 3. Check TStep if( BitManip::TestBit(fTimeWalkStatus[lcn],10) ) { fStatus.Set(kPCABitOffset + 10); } // 4. Check FLate if( BitManip::TestBit(fTimeWalkStatus[lcn],11) ) { fStatus.Set(kPCABitOffset + 11); } // 5. Check FOut if( BitManip::TestBit(fTimeWalkStatus[lcn],12) ) { fStatus.Set(kPCABitOffset + 12); } // 6. Set general TimeWalk test bit if( fStatus.GetBits(kPCABitOffset + 8,kPCABitOffset + 12) != 0){ fStatus.Set(kPCABitOffset + 13); } } return; } void PCACal::ValidateCalibrate(int lcn){ // Check the PCA GainFit status words and flag if(fDoTimeWalk) { PCACal::ValidateTimeWalk(lcn); } // Set general FAIL bit if( fStatus.GetBits(kPCABitOffset + 2,kPCABitOffset + 4) != 0){ fStatus.Set(kPCABitOffset + 0); } // Set general WARN bit if( fStatus.GetBits(kPCABitOffset + 5,kPCABitOffset + 7) != 0){ fStatus.Set(kPCABitOffset + 1); } return; } void PCACal::CalStatus(){ // This function looks at the tolerance level that was set by the user in // PMTCalibration.ratdb and sets the Q/T values to DS::INVALID accordingly. if( fDoTimeWalk ) { if( fStatus.Get( kPCABitOffset + 30 ) ) { // SNO calibration failed fTime = kFailValue; } if( fTolLevel==1 ) { if( fStatus.Get( kPCABitOffset + 1 ) ) { // WARN level fTime = kFailValue; } } if( fTolLevel==2 ) { if( fStatus.Get( kPCABitOffset + 13 ) ) { // TEST level fTime = kFailValue; } } if( fTolLevel==3 ) { if( (fStatus.Get( kPCABitOffset + 13 )) || (fStatus.Get( kPCABitOffset+ 1 )) ) { // TEST or WARN level fTime = kFailValue; return; } } } return; } // Handle non-fatal errors void PCACal::DontDie(LiveFlagErr flagErr, int info1, int info2) { switch(flagErr) { case NOPCA: // PCA selected but no PCA calibs requested warn <