/* This file is part of MAUS: http://micewww.pp.rl.ac.uk:8080/projects/maus * * MAUS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MAUS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MAUS. If not, see . * */ #include "src/map/MapCppTOFMCDigitizer/MapCppTOFMCDigitizer.hh" #include #include "src/common_cpp/Utils/TOFChannelMap.hh" #include "Config/MiceModule.hh" #include "DataStructure/MCEvent.hh" #include "DataStructure/Spill.hh" #include "Utils/Exception.hh" #include "API/PyWrapMapBase.hh" #include "src/common_cpp/DataStructure/RunHeaderData.hh" #include "src/common_cpp/DataStructure/RunHeader.hh" namespace MAUS { PyMODINIT_FUNC init_MapCppTOFMCDigitizer(void) { PyWrapMapBase::PyWrapMapBaseModInit ("MapCppTOFMCDigitizer", "", "", "", ""); } MapCppTOFMCDigitizer::MapCppTOFMCDigitizer() : MapBase("MapCppTOFMCDigitizer") { } ////////////////////////////////////////////////////////////////////// void MapCppTOFMCDigitizer::_birth(const std::string& argJsonConfigDocument) { // print level fDebug = false; Json::Reader reader; // Check if the JSON document can be parsed, else return error only bool parsingSuccessful = reader.parse(argJsonConfigDocument, _configJSON); if (!parsingSuccessful) { throw MAUS::Exceptions::Exception(Exceptions::recoverable, "Failed to parse Json Configuration", "MapCppTOFMCDigitizer::_birth"); } // get the geometry if (!_configJSON.isMember("reconstruction_geometry_filename")) throw(Exceptions::Exception(Exceptions::recoverable, "Could not find geometry file", "MapCppTOFMCDigitizer::birth")); std::string filename; filename = _configJSON["reconstruction_geometry_filename"].asString(); // get the tof geometry modules geo_module = new MiceModule(filename); tof_modules = geo_module->findModulesByPropertyString("SensitiveDetector", "TOF"); // load the TOF calibration map bool loaded = _map.InitializeFromCards(_configJSON, 0); if (!loaded) throw(Exceptions::Exception(Exceptions::recoverable, "Could not find TOF calibration map", "MapCppTOFMCDigitizer::birth")); } ////////////////////////////////////////////////////////////////////// void MapCppTOFMCDigitizer::_death() { } ////////////////////////////////////////////////////////////////////// void MapCppTOFMCDigitizer::_process(MAUS::Data* data) const { // Get spill, break if there's no DAQ data Spill *spill = data->GetSpill(); if (spill->GetMCEvents() == NULL) return; MCEventPArray *mcEvts = spill->GetMCEvents(); int nPartEvents = spill->GetMCEventSize(); if (nPartEvents == 0) return; ReconEventPArray *recEvts = spill->GetReconEvents(); // Resize the recon event to hold mc events if (spill->GetReconEventSize() == 0) { // No recEvts yet for (int iPe = 0; iPe < nPartEvents; iPe++) { recEvts->push_back(new ReconEvent); } } // all_tof_digits store all the tof hits // then we'll weed out multiple hits, add up yields, and store the event // loop over events if (fDebug) std::cout << "mc numevts = " << mcEvts->size() << std::endl; // Construct digits from hits for each MC event for (int iPe = 0; iPe < nPartEvents; iPe++) { // Get tof hits for this event TOFHitArray *hits = spill->GetAnMCEvent(iPe).GetTOFHits(); // process only if there are tof hits if (hits) { // Get the time of the primary // for digitizing, hit time will be taken as relative to the primary Primary *primary = spill->GetAnMCEvent(iPe).GetPrimary(); double gentime = primary->GetTime(); // ns // pick out tof hits, digitize and store TofTmpDigits tmpDigits = make_tof_digits(hits, gentime); // go through tof hits and find the hit in the trig station (TOF1) std::string strig = findTriggerPixel(tmpDigits); // now go through the stations and fill tof events // real data tof digits look like so: // "digits":{"tof1":[ [evt0..{pmt0},{pmt1}..],[evt1..]],"tof0":[[evt0]..],tof2..} TOFEvent *evt = new TOFEvent(); (*recEvts)[iPe]->SetTOFEvent(evt); fDigsArrayPtr digitArrayPtr(0); TOF0DigitArray *tof0_digits = evt->GetTOFEventDigitPtr()->GetTOF0DigitArrayPtr(); TOF1DigitArray *tof1_digits = evt->GetTOFEventDigitPtr()->GetTOF1DigitArrayPtr(); TOF2DigitArray *tof2_digits = evt->GetTOFEventDigitPtr()->GetTOF2DigitArrayPtr(); digitArrayPtr.push_back(tof0_digits); digitArrayPtr.push_back(tof1_digits); digitArrayPtr.push_back(tof2_digits); fill_tof_evt(iPe, 0, tmpDigits, digitArrayPtr, strig); } } // end loop over events } ////////////////////////////////////////////////////////////////////// TofTmpDigits MapCppTOFMCDigitizer::make_tof_digits(TOFHitArray* hits, double gentime) const { TofTmpDigits tmpDigits(0); if (hits->size() == 0) return tmpDigits; for ( unsigned int j = 0; j < hits->size(); j++ ) { // j-th hit TOFHit hit = hits->at(j); if (fDebug) std::cout << "=================== hit# " << j << std::endl; // make sure we can get the station/slab info if (!hit.GetChannelId()) throw(Exceptions::Exception(Exceptions::recoverable, "No channel_id in hit", "MapCppTOFMCDigitizer::make_tof_digits")); ThreeVector mom = hit.GetMomentum(); double hTime = hit.GetTime(); TOFChannelId* channel_id = hit.GetChannelId(); double edep = hit.GetEnergyDeposited(); if (fDebug) { std::cout << "tofhit: stn: " << channel_id->GetStation() << " slab: " << channel_id->GetSlab() << " plane: " << channel_id->GetPlane() << " xyz: " << hit.GetPosition().x() << " " << hit.GetPosition().y() << " " << hit.GetPosition().z() << " px,py,pz: " << hit.GetMomentum().x() << " " << hit.GetMomentum().y() << " " << hit.GetMomentum().z() << " time: " << hit.GetTime() << std::endl; } int stn = channel_id->GetStation(); int pln = channel_id->GetPlane(); int slb = channel_id->GetSlab(); // set daq_event_type. TofSlabHits expects this. // NOTE: In principle, should be done globally /* if (!root.isMember("daq_event_type")) { root["daq_event_type"] = "physics_event"; } */ // find the geo module corresponding to this hit const MiceModule* hit_module = NULL; for ( unsigned int jj = 0; !hit_module && jj < tof_modules.size(); ++jj ) { if ( tof_modules[jj]->propertyExists("Station", "int") && tof_modules[jj]->propertyInt("Station") == stn && tof_modules[jj]->propertyExists("Slab", "int") && tof_modules[jj]->propertyInt("Slab") == slb && tof_modules[jj]->propertyExists("Plane", "int") && tof_modules[jj]->propertyInt("Plane") == pln ) { // got it hit_module = tof_modules[jj]; if (fDebug) { std::cout << "mod: " << jj << " " << tof_modules[jj]->propertyInt("Station") << " " << tof_modules[jj]->propertyInt("Plane") << " " << tof_modules[jj]->propertyInt("Slab") << std::endl; } } // end check on module } // end loop over tof_modules // make sure we actually found a tof module corresponding to this hit if (hit_module == NULL) throw(Exceptions::Exception(Exceptions::recoverable, "No TOF module for hit", "MapCppTOFMCDigitizer::make_tof_digits")); // now get the position of the hit ThreeVector tpos = hit.GetPosition(); Hep3Vector hpos(tpos.x(), tpos.y(), tpos.z()); // get the slab and pmt positions from the geometry // slab pos Hep3Vector slabPos = hit_module->globalPosition(); // pmt positions std::string pmtName; // pmt 1 pmtName = "Pmt1Pos"; Hep3Vector pmt1Pos = hit_module->propertyHep3Vector(pmtName); // pmt 2 pmtName = "Pmt2Pos"; Hep3Vector pmt2Pos = hit_module->propertyHep3Vector(pmtName); if (fDebug) { std::cout << "hitpos: " << hpos << std::endl; std::cout << "pmt1pos: " << pmt1Pos << " slabPos: " << slabPos << std::endl; std::cout << "pmt2pos: " << pmt2Pos << " slabPos: " << slabPos << std::endl; } // find distances from hit to pmt geom double dist1 = ( hpos - (slabPos + pmt1Pos) ).mag(); double dist2 = ( hpos - (slabPos + pmt2Pos) ).mag(); double dist_average = (dist1 + dist2)/2; // convert edep to photoelectrons for this slab/pmt // can't convert to adc yet since we need to add up ph.el's // from other hits if any if (fDebug) std::cout << "edep= " << edep << std::endl; double npe1 = get_npe(dist1, edep); double npe2 = get_npe(dist2, edep); if (fDebug) printf("npe# %3.15f %3.4f %3.4f\n", edep, npe1, npe2); // get the hit time double csp = _configJSON["TOFscintLightSpeed"].asDouble(); double tres = _configJSON["TOFpmtTimeResolution"].asDouble(); double htime = hit.GetTime() - gentime; // propagate time to pmt & smear by the resolution double time1 = CLHEP::RandGauss::shoot((htime + (dist1-dist_average)/csp) , tres); double time2 = CLHEP::RandGauss::shoot((htime + (dist2-dist_average)/csp) , tres); double tdc2time = _configJSON["TOFtdcConversionFactor"].asDouble(); // convert to tdc int tdc1 = static_cast(std::round(time1 / tdc2time)); int tdc2 = static_cast(std::round(time2 / tdc2time)); if (fDebug) { std::cout << "time: " << hit.GetTime() << " now: " << time1 << " " << time2 << " " << (time1+time2)/2 << std::endl; std::cout << "dist: " << dist1 << " " << dist2 << std::endl; std::cout << "tdc: " << tdc1 << " " << tdc2 << std::endl; } tmpThisDigit thisDigit; // set the hits for PMTs at both ends for (int p = 0; p < 2; ++p) { thisDigit.fChannelId = channel_id; thisDigit.fMom = mom; thisDigit.fTime = hTime; thisDigit.fPos = hpos; thisDigit.fIsUsed = 0; // set the key for this channel std::stringstream ss; ss << "TOFChannelKey " << stn << " " << pln << " " << slb << " " << p << " tof" << stn; thisDigit.fTofKey = ss.str(); thisDigit.fStation = stn; thisDigit.fPlane = pln; thisDigit.fSlab = slb; thisDigit.fPmt = p; if (p == 0) { thisDigit.fNpe = npe1; thisDigit.fLeadingTime = tdc1; thisDigit.fRawTime = time1; } else if (p == 1) { thisDigit.fNpe = npe2; thisDigit.fLeadingTime = tdc2; thisDigit.fRawTime = time2; } // this stuff needs tweaking // need to make sure the trigger leading time is reasonable // as is this requires a mod -- // --- ConfigDefaults triggerpixelcut has to be modified to // allow for the trigger pixel to be found // ideally if all was well the triggerpixel after calib // will give a time ~= 0. // but without the correct trigger leading time, this will no // longer be the case. hence the pixelcut mod. // need a more elegant way to handle this -- DR 3/15 thisDigit.fTrailingTime = 0; thisDigit.fTriggerRequestLeadingTime = 0; thisDigit.fTriggerTimeTag = 0; thisDigit.fTimeStamp = 0; if (fDebug) std::cout << "digit " << j << " key: " << ss.str() << std::endl; tmpDigits.push_back(thisDigit); } // end loop over pmts } // ends 'for' loop over hits return tmpDigits; } ////////////////////////////////////////////////////////////////////// double MapCppTOFMCDigitizer::get_npe(double dist, double edep) const { double nphot = 0; double nptmp = 0.; if (fDebug) std::cout << "get_npe::edep= " << edep << std::endl; if (!_configJSON.isMember("TOFattenuationLength")) throw(Exceptions::Exception(Exceptions::recoverable, "Could not find TOFattenauationLength in config", "MapCppTOFMCDigitizer::get_npe")); if (!_configJSON.isMember("reconstruction_geometry_filename")) throw(Exceptions::Exception(Exceptions::recoverable, "Could not find TOFpmtQuantumEfficiency in config", "MapCppTOFMCDigitizer::get_npe")); if (!_configJSON.isMember("TOFconversionFactor")) throw(Exceptions::Exception(Exceptions::recoverable, "Could not find TOFconversionFactor in config", "MapCppTOFMCDigitizer::birth")); if (fDebug) printf("nphot0: %3.12f %3.12f\n", edep, _configJSON["TOFconversionFactor"].asDouble()); // convert energy deposited to number of photoelectrons nphot = edep / (_configJSON["TOFconversionFactor"].asDouble()); TRandom* rnd = new TRandom(); rnd = new TRandom(); nptmp = rnd->Poisson(nphot); nphot = nptmp; // attenuate the yield nphot *= exp(-dist / (_configJSON["TOFattenuationLength"].asDouble())); rnd = new TRandom(); nptmp = rnd->Poisson(nphot); nphot = nptmp; // correct for phototube quantum efficiency nphot *= (_configJSON["TOFpmtQuantumEfficiency"].asDouble()); rnd = new TRandom(); nptmp = rnd->Poisson(nphot); nphot = nptmp; return nphot; } ////////////////////////////////////////////////////////////////////// std::string MapCppTOFMCDigitizer::findTriggerPixel(TofTmpDigits& tmpDigits) const { int tstn = 1; int tsx = 99, tsy = 99; for (unsigned int digit_i = 0; digit_i < tmpDigits.size(); digit_i++) { if (tmpDigits[digit_i].fStation == tstn) { if (tsx == 99 || tsy == 99) { if (tmpDigits[digit_i].fPlane == 0) tsx = tmpDigits[digit_i].fSlab; else tsy = tmpDigits[digit_i].fSlab; } } } std::stringstream strig_str(""); // set trigger to be "unknown" if we could not find the hit in TOF1 if (tsx == 99 || tsy == 99) strig_str << "unknown"; else strig_str << "TOFPixelKey " << tstn << " " << tsx << " " << tsy << " tof" << tstn; if (fDebug) std::cout << "pixelKey: " << strig_str.str() << std::endl; return strig_str.str(); } ////////////////////////////////////////////////////////////////////// void MapCppTOFMCDigitizer::fill_tof_evt(int evnum, int snum, TofTmpDigits& tmpDigits, fDigsArrayPtr& tofDigits, std::string pixStr) const { // return null if this evt had no tof hits if (tmpDigits.size() == 0) return; double npe; int ndigs = 0; // throw out multihits and add up the light yields from multihits in slabs for (unsigned int i = 0; i < tmpDigits.size(); ++i) { int thisStation = tmpDigits[i].fStation; // check if the digit has been used if (!tmpDigits[i].fIsUsed) { ndigs++; npe = tmpDigits[i].fNpe; // loop over all the other digits to find multihit slabs for ( unsigned int j = i+1; j < tmpDigits.size(); j++ ) { if ( check_param(tmpDigits[i], tmpDigits[j]) ) { // add up light yields if same bar was hit npe += tmpDigits[j].fNpe; // mark this hit as used so we don't multicount tmpDigits[j].fIsUsed = true; } // end check for used } // end loop over secondary digits // convert light yield to adc & set the charge int adc = static_cast (std::round(npe / (_configJSON["TOFadcConversionFactor"].asDouble()))); /* double adcs = npe / (_configJSON["TOFadcConversionFactor"].asDouble()); double adcw = 0.5; TRandom* rnd = new TRandom(); double adcsm = rnd->Landau(adcs,adcw); int adc = static_cast(adcsm); */ if (fDebug) std::cout << "npe-adc: " << npe << " " << adc << std::endl; // now set the digit TOFDigit theDigit; theDigit.SetChargePm(adc); // NOTE: needs tweaking/verifying -- DR 3/15 theDigit.SetChargeMm(adc); theDigit.SetTofKey(tmpDigits[i].fTofKey); theDigit.SetStation(tmpDigits[i].fStation); theDigit.SetPlane(tmpDigits[i].fPlane); theDigit.SetSlab(tmpDigits[i].fSlab); theDigit.SetPmt(tmpDigits[i].fPmt); // add the calibration corrections to the time std::string keyStr = tmpDigits[i].fTofKey; TOFChannelKey xChannelKey(keyStr); double dT = 0.; if (pixStr != "unknown") { if (fDebug) std::cout << "MapCppTOFMCDigitizer::Correcting pixel: " << keyStr << std::endl; TOFPixelKey xTriggerPixelKey(pixStr); dT = _map.dT(xChannelKey, xTriggerPixelKey, adc); if (fDebug) std::cout << "dT= " << dT << std::endl; } if (fDebug) std::cout << "calibCorr: " << dT << std::endl; double raw = tmpDigits[i].fRawTime; double rt = raw + dT; // convert to tdc int tdc = static_cast(std::round(rt/(_configJSON["TOFtdcConversionFactor"].asDouble()))); if (fDebug) { std::cout << "times: raw= " << raw << " dT= " << dT << " corr= " << rt << " tdc= " << tdc << std::endl; } // store the digitized times // NOTE: trigger_req... has to be tweaked -- see earlier note -- DR/march15 theDigit.SetLeadingTime(tdc); theDigit.SetTriggerRequestLeadingTime(tmpDigits[i].fTriggerRequestLeadingTime); // ROGERS addition to maintain data integrity 03-May-2012 // - trigger_leading_time // - trigger_trailing_time // - trigger_request_trailing_timr theDigit.SetTriggerLeadingTime(0); theDigit.SetTriggerTrailingTime(0); theDigit.SetTriggerRequestTrailingTime(0); theDigit.SetTrailingTime(0); theDigit.SetTimeStamp(0); theDigit.SetTriggerTimeTag(0); // store event number theDigit.SetPhysEventNumber(evnum); theDigit.SetPartEventNumber(evnum); // store this digit in the digits array for this station tofDigits[thisStation]->push_back(theDigit); if (fDebug) std::cout << "digit #" << ndigs << " " << theDigit.GetTofKey() << std::endl; // done - mark digit as used tmpDigits[i].fIsUsed = true; } // ends check of used hit } // ends loop over all digits } ////////////////////////////////////////////////////////////////////// bool MapCppTOFMCDigitizer::check_param(tmpThisDigit& hit1, tmpThisDigit& hit2) const { int station1 = hit1.fStation; int station2 = hit2.fStation; int plane1 = hit1.fPlane; int plane2 = hit2.fPlane; int slab1 = hit1.fSlab; int slab2 = hit2.fSlab; int pmt1 = hit1.fPmt; int pmt2 = hit2.fPmt; bool hit_is_used = hit2.fIsUsed; if ( station1 == station2 && plane1 == plane2 && slab1 == slab2 && pmt1 == pmt2 && !hit_is_used ) { return true; } else { return false; } } //===================================================================== }