diff --git a/DataModel/ADCPulse.cpp b/DataModel/ADCPulse.cpp index 84a3e3247..7b85a2c53 100755 --- a/DataModel/ADCPulse.cpp +++ b/DataModel/ADCPulse.cpp @@ -7,9 +7,9 @@ ADCPulse::ADCPulse(int TubeId, double start_time, double peak_time, double baseline, double sigma_baseline, unsigned long area, unsigned short raw_amplitude, double calibrated_amplitude, - double charge) : Hit(TubeId, start_time, charge), + double charge, double stop_time) : Hit(TubeId, start_time, charge), start_time_(start_time), peak_time_(peak_time), baseline_(baseline), sigma_baseline_(sigma_baseline), raw_area_(area), - raw_amplitude_(raw_amplitude), calibrated_amplitude_(calibrated_amplitude) + raw_amplitude_(raw_amplitude), calibrated_amplitude_(calibrated_amplitude), stop_time_(stop_time) { } diff --git a/DataModel/ADCPulse.h b/DataModel/ADCPulse.h index 891e840d0..4176fbdf9 100755 --- a/DataModel/ADCPulse.h +++ b/DataModel/ADCPulse.h @@ -23,7 +23,7 @@ class ADCPulse : public Hit { ADCPulse(int TubeId, double start_time, double peak_time, double baseline, double sigma_baseline, unsigned long raw_area, unsigned short raw_amplitude, double calibrated_amplitude, - double charge); + double charge, double stop_time = 0); // @brief Returns the start time (ns) of the pulse relative to the // start of its minibuffer @@ -33,6 +33,10 @@ class ADCPulse : public Hit { // start of its minibuffer inline double peak_time() const { return peak_time_; } + // @brief Returns the stop time (ns) of the pulse relative to the + // start of its minibuffer + inline double stop_time() const { return stop_time_; } + // @brief Returns the approximate baseline (ADC) used to calibrate the // pulse inline double baseline() const { return baseline_; } @@ -71,16 +75,24 @@ class ADCPulse : public Hit { ar & raw_area_; ar & raw_amplitude_; ar & calibrated_amplitude_; + if (version > 0) + ar & stop_time_; } protected: double start_time_; // ns since beginning of minibuffer double peak_time_; // ns since beginning of minibuffer + double stop_time_; // ns since beginning of minibuffer double baseline_; // mean (ADC) double sigma_baseline_; // standard deviation (ADC) unsigned long raw_area_; // (ADC * samples) unsigned short raw_amplitude_; // ADC double calibrated_amplitude_; // V + }; + +// Need to increment the class version since we added time as a new variable +// the version number ensures backward compatibility when serializing +BOOST_CLASS_VERSION(ADCPulse, 1) diff --git a/UserTools/BackTracker/BackTracker.cpp b/UserTools/BackTracker/BackTracker.cpp index ce80ada9b..bf72fc03d 100644 --- a/UserTools/BackTracker/BackTracker.cpp +++ b/UserTools/BackTracker/BackTracker.cpp @@ -27,6 +27,13 @@ bool BackTracker::Initialise(std::string configfile, DataModel &data){ Log(logmessage, v_error, verbosity); } + bool gotMCWaveforms = m_variables.Get("MCWaveforms", fMCWaveforms); + if (!gotMCWaveforms) { + fMCWaveforms = false; + logmessage = "BackTracker::Initialize: \"MCWaveforms\" not set in the config, defaulting to false"; + Log(logmessage, v_error, verbosity); + } + // Set up the pointers we're going to save. No need to // delete them at Finalize, the store will handle it @@ -42,9 +49,11 @@ bool BackTracker::Initialise(std::string configfile, DataModel &data){ //------------------------------------------------------------------------------ bool BackTracker::Execute() { - if (!LoadFromStores()) - return false; - + int load_status = LoadFromStores(); + if (load_status == 0) return false; + if (load_status == 2) return true; + + fClusterToBestParticleID ->clear(); fClusterToBestParticlePDG->clear(); fClusterEfficiency ->clear(); @@ -52,25 +61,79 @@ bool BackTracker::Execute() fClusterTotalCharge ->clear(); fParticleToTankTotalCharge.clear(); + SumParticleTankCharge(); - - // Loop over the clusters and do the things - for (std::pair>&& apair : *fClusterMapMC) { - int prtId = -5; - int prtPdg = -5; - double eff = -5; - double pur = -5; - double totalCharge = 0; - - MatchMCParticle(apair.second, prtId, prtPdg, eff, pur, totalCharge); - - fClusterToBestParticleID ->emplace(apair.first, prtId); - fClusterToBestParticlePDG->emplace(apair.first, prtPdg); - fClusterEfficiency ->emplace(apair.first, eff); - fClusterPurity ->emplace(apair.first, pur); - fClusterTotalCharge ->emplace(apair.first, totalCharge); - } + if (fMCWaveforms) { // using clusters of Hits made from simulated PMT pulses + + // Produce the map from channel ID to pulse time to MCHit index + //std::cout << "BT: Calling MapPulsesToParentIdxs" << std::endl; + bool gotPulseMap = MapPulsesToParentIdxs(); + if (!gotPulseMap) { + logmessage = "BackTracker: No good pulse map."; + Log(logmessage, v_error, verbosity); + return false; + } + + // Loop over the clusters + for (std::pair>&& apair : *fClusterMap) { + // Create a vector of MCHits associated with the vector of Hits + std::vector mcHits; + for (auto hit : apair.second) { + int channel_key = hit.GetTubeId(); + double hitTime = hit.GetTime(); + + // Catches if something goes wrong + // First make sure that we have the PMT in the outer map + // Then make sure we have the hit time in the inner map + if (fMapChannelToPulseTimeToMCHitIdx.find(channel_key) == fMapChannelToPulseTimeToMCHitIdx.end()) { + std::cout << "BackTracker: No hit on this PMT: " << channel_key << std::endl; + return false; + } + if (fMapChannelToPulseTimeToMCHitIdx.at(channel_key).find(hitTime) == fMapChannelToPulseTimeToMCHitIdx.at(channel_key).end()) { + std::cout << "BackTracker: No hit on this PMT: " << channel_key << " at this time: " << hitTime << std::endl; + return false; + } + + std::vector mcHitIdxVec = fMapChannelToPulseTimeToMCHitIdx[channel_key][hitTime]; + for (auto mcHitIdx : mcHitIdxVec) + mcHits.push_back((fMCHitsMap->at(channel_key)).at(mcHitIdx)); + }// end loop over cluster hits + + int prtId = -5; + int prtPdg = -5; + double eff = -5; + double pur = -5; + double totalCharge = 0; + + MatchMCParticle(mcHits, prtId, prtPdg, eff, pur, totalCharge); + + fClusterToBestParticleID ->emplace(apair.first, prtId); + fClusterToBestParticlePDG->emplace(apair.first, prtPdg); + fClusterEfficiency ->emplace(apair.first, eff); + fClusterPurity ->emplace(apair.first, pur); + fClusterTotalCharge ->emplace(apair.first, totalCharge); + + m_data->Stores.at("ANNIEEvent")->Set("MapChannelToPulseTimeToMCHitIdx", fMapChannelToPulseTimeToMCHitIdx ); + }// end loop over the cluster map + } else { // using clusters of MCHits + // Loop over the MC clusters and do the things + for (std::pair>&& apair : *fClusterMapMC) { + int prtId = -5; + int prtPdg = -5; + double eff = -5; + double pur = -5; + double totalCharge = 0; + + MatchMCParticle(apair.second, prtId, prtPdg, eff, pur, totalCharge); + + fClusterToBestParticleID ->emplace(apair.first, prtId); + fClusterToBestParticlePDG->emplace(apair.first, prtPdg); + fClusterEfficiency ->emplace(apair.first, eff); + fClusterPurity ->emplace(apair.first, pur); + fClusterTotalCharge ->emplace(apair.first, totalCharge); + }// end loop over the MC cluster map + }// end if/else fMCWaveforms m_data->Stores.at("ANNIEEvent")->Set("ClusterToBestParticleID", fClusterToBestParticleID ); m_data->Stores.at("ANNIEEvent")->Set("ClusterToBestParticlePDG", fClusterToBestParticlePDG); @@ -91,8 +154,8 @@ bool BackTracker::Finalise() //------------------------------------------------------------------------------ void BackTracker::SumParticleTankCharge() { - for (auto mcHitsIt : *fMCHitsMap) { - std::vector mcHits = mcHitsIt.second; + for (auto apair : *fMCHitsMap) { + std::vector mcHits = apair.second; for (uint mcHitIdx = 0; mcHitIdx < mcHits.size(); ++mcHitIdx) { // technically a MCHit could have multiple parents, but they don't appear to in practice @@ -101,8 +164,8 @@ void BackTracker::SumParticleTankCharge() if (parentIdxs.size() != 1) continue; int particleId = -5; - for (auto it : *fMCParticleIndexMap) { - if (it.second == parentIdxs[0]) particleId = it.first; + for (auto bpair : *fMCParticleIndexMap) { + if (bpair.second == parentIdxs[0]) particleId = bpair.first; } if (particleId == -5) continue; @@ -133,8 +196,8 @@ void BackTracker::MatchMCParticle(std::vector const &mchits, int &prtId, } int particleId = -5; - for (auto it : *fMCParticleIndexMap) { - if (it.second == parentIdxs[0]) particleId = it.first; + for (auto apair : *fMCParticleIndexMap) { + if (apair.second == parentIdxs[0]) particleId = apair.first; } if (particleId == -5) continue; @@ -175,39 +238,129 @@ void BackTracker::MatchMCParticle(std::vector const &mchits, int &prtId, } //------------------------------------------------------------------------------ -bool BackTracker::LoadFromStores() +bool BackTracker::MapPulsesToParentIdxs() { - // Grab the stuff we need from the stores - bool goodMCClusters = m_data->CStore.Get("ClusterMapMC", fClusterMapMC); - if (!goodMCClusters) { - std::cerr<<"BackTracker: no ClusterMapMC in the CStore!"<> > adcPulseMap; + bool goodADCPulses = m_data->Stores.at("ANNIEEvent")->Get("RecoADCHits", adcPulseMap); + if (!goodADCPulses) { + logmessage = "BackTracker: no RecoADCHits in the ANNIEEvent!"; + Log(logmessage, v_error, verbosity); return false; } + // Loop over the ADCPulses and find MCHits that fall within the start and stop times + // also record the pulse time to match the MCHits to the reco Hits + // Pulses are indexed by PMT id then stacked in a two-deep vector + + for (auto apair: adcPulseMap) { + int channel_key = apair.first; + + std::map> mapHitTimeToParents; + bool goodPulses = false; + for (auto pulseVec : apair.second) { + for (auto pulse : pulseVec) { + goodPulses = true; + double pulseTime = pulse.peak_time(); + + // Record the hit index if it occurred within the pulse window + // If there is only one hit the no need to check the times + std::vector mcHits = fMCHitsMap->at(channel_key); + if (mcHits.size() == 1) + mapHitTimeToParents[pulseTime].push_back(0); + else { + for (uint mcHitIdx = 0; mcHitIdx < mcHits.size(); ++mcHitIdx) { + double hitTime = mcHits[mcHitIdx].GetTime(); + // The hit finding has to contend with noise so allow for a 10 ns + // slew in the pulse start time (I know it seems large) + if ( hitTime + 10 >= pulse.start_time() && hitTime <= pulse.stop_time()) + mapHitTimeToParents[pulseTime].push_back(mcHitIdx); + }// end loop over MCHits + } + }// end loop over inner pulse vector + }// end loop over outer pulse vector + + if (mapHitTimeToParents.size() == 0 && goodPulses) { + logmessage = "BackTracker::MapPulsesToParentIdxs: No MCHits match with this pulse! PMT channel: "; + logmessage += std::to_string(channel_key); + Log(logmessage, v_error, verbosity); + return false; + } + + fMapChannelToPulseTimeToMCHitIdx.emplace(channel_key, std::move(mapHitTimeToParents)); + }// end loop over pulse map + + return true; +} + +//------------------------------------------------------------------------------ +int BackTracker::LoadFromStores() +{ + // Grab the stuff we need from the stores + bool goodAnnieEvent = m_data->Stores.count("ANNIEEvent"); if (!goodAnnieEvent) { - std::cerr<<"BackTracker: no ANNIEEvent store!"<Stores.at("ANNIEEvent")->Get("SkipExecute", skip); + if (goodSkipStatus && skip) { + logmessage = "BackTracker: An upstream tool told me to skip this event."; + Log(logmessage, v_warning, verbosity); + return 2; + } + bool goodMCHits = m_data->Stores.at("ANNIEEvent")->Get("MCHits", fMCHitsMap); if (!goodMCHits) { - std::cerr<<"BackTracker: no MCHits in the ANNIEEvent!"<CStore.Get("ClusterMap", fClusterMap); + if (!goodClusters) { + logmessage = "BackTracker: no ClusterMap in the CStore!"; + Log(logmessage, v_error, verbosity); + return 0; + } + + bool goodRecoHits = m_data->Stores.at("ANNIEEvent")->Get("Hits", fRecoHitsMap); + if (!goodRecoHits) { + logmessage = "BackTracker: no Hits in the ANNIEEvent!"; + Log(logmessage, v_error, verbosity); + return 0; + } + + } else { + bool goodMCClusters = m_data->CStore.Get("ClusterMapMC", fClusterMapMC); + if (!goodMCClusters) { + logmessage = "BackTracker: no ClusterMapMC in the CStore!"; + Log(logmessage, v_error, verbosity); + return 0; + } + }// end if/else fMCWaveforms bool goodMCParticles = m_data->Stores.at("ANNIEEvent")->Get("MCParticles", fMCParticles); if (!goodMCParticles) { - std::cerr<<"BackTracker: no MCParticles in the ANNIEEvent!"<Stores.at("ANNIEEvent")->Get("TrackId_to_MCParticleIndex", fMCParticleIndexMap); if (!goodMCParticleIndexMap) { - std::cerr<<"BackTracker: no TrackId_to_MCParticleIndex in the ANNIEEvent!"< const &mchits, int &prtId, int &prtPdg, double &eff, double &pur, double &totalCharge); ///< The meat and potatoes + + bool MapPulsesToParentIdxs(); private: // Things we need to pull out of the store std::map> *fMCHitsMap = nullptr; ///< All of the MCHits keyed by channel number - std::map> *fClusterMapMC = nullptr; ///< Clusters that we will be linking MCParticles to + std::map> *fRecoHitsMap = nullptr; ///< All of the reco Hits keyed by channel number + std::map> *fClusterMapMC = nullptr; ///< MCClusters that we will be linking MCParticles to + std::map> *fClusterMap = nullptr; ///< Clusters that we will be linking MCParticles to std::vector *fMCParticles = nullptr; ///< The true particles from the event std::map *fMCParticleIndexMap = nullptr; ///< Map between the particle Id and it's position in MCParticles vector // We'll calculate this map from MCHit parent particle to the total charge deposited throughout the tank // technically a MCHit could have multiple parents, but they don't appear to in practice // the key is particle Id and value is total tank charge - std::map fParticleToTankTotalCharge; + std::map fParticleToTankTotalCharge; + + // Used in the case of MC Waveforms + // Outer map: PMT channel ID to inner map + // Inner map: pulse time to vector of MCHit indexes that contribute + // The peak times of ADCPulses should match the time associated with reco Hits + std::map>> fMapChannelToPulseTimeToMCHitIdx; // We'll save out maps between the local cluster time and // the ID and PDG of the particle that contributed the most energy @@ -57,6 +68,8 @@ class BackTracker: public Tool { std::map *fClusterPurity = nullptr; std::map *fClusterTotalCharge = nullptr; + bool fMCWaveforms; + /// \brief verbosity levels: if 'verbosity' < this level, the message type will be logged. int verbosity; int v_error=0; diff --git a/UserTools/ClusterFinder/ClusterFinder.cpp b/UserTools/ClusterFinder/ClusterFinder.cpp index 5c002b119..8ce1966b8 100644 --- a/UserTools/ClusterFinder/ClusterFinder.cpp +++ b/UserTools/ClusterFinder/ClusterFinder.cpp @@ -164,6 +164,16 @@ bool ClusterFinder::Execute(){ //---------------------------------------------------------------------------- //---------------get the members of the ANNIEEvent---------------------------- //---------------------------------------------------------------------------- + + // An upstream tool may opt to skip this execution stage + // For example the PMTWaveformSim tool will skip events with no MCHits or if + // no waveforms are produced. + bool skip = false; + bool got_skip_status = m_data->Stores["ANNIEEvent"]->Get("SkipExecute", skip); + if (got_skip_status && skip) { + Log("ClusterFinder: An upstream tool told me to skip this event.",v_warning,verbose); + return true; + } m_data->Stores["ANNIEEvent"]->Get("EventNumber", evnum); //m_data->Stores["ANNIEEvent"]->Get("BeamStatus", BeamStatus); diff --git a/UserTools/Factory/Factory.cpp b/UserTools/Factory/Factory.cpp index 0651246b9..21157af0a 100644 --- a/UserTools/Factory/Factory.cpp +++ b/UserTools/Factory/Factory.cpp @@ -1,7 +1,7 @@ #include "Factory.h" Tool* Factory(std::string tool) { -Tool* ret=0; +Tool* ret = 0; // if (tool=="Type") tool=new Type; if (tool=="DummyTool") ret=new DummyTool; @@ -173,5 +173,6 @@ if (tool=="BeamQuality") ret=new BeamQuality; if (tool=="BackTracker") ret=new BackTracker; if (tool=="PrintDQ") ret=new PrintDQ; if (tool=="AssignBunchTimingMC") ret=new AssignBunchTimingMC; +if (tool=="PMTWaveformSim") ret=new PMTWaveformSim; return ret; } diff --git a/UserTools/PMTWaveformSim/PMTWaveformSim.cpp b/UserTools/PMTWaveformSim/PMTWaveformSim.cpp new file mode 100644 index 000000000..83ad4c5c7 --- /dev/null +++ b/UserTools/PMTWaveformSim/PMTWaveformSim.cpp @@ -0,0 +1,387 @@ +#include +#include +#include + +// ANNIE includes +#include "ANNIEconstants.h" +#include "PMTWaveformSim.h" + +// ROOT includes +#include "TGraph.h" + +PMTWaveformSim::PMTWaveformSim():Tool(){} + + +bool PMTWaveformSim::Initialise(std::string configfile, DataModel &data) +{ + + /////////////////// Useful header /////////////////////// + if(configfile!="") m_variables.Initialise(configfile); // loading config file + //m_variables.Print(); + + m_data= &data; //assigning transient data pointer + ///////////////////////////////////////////////////////////////// + + // get config variables + bool gotVerbosity = m_variables.Get("verbosity", verbosity); + if (!gotVerbosity) verbosity = 1; + + bool gotPMTParamFile = m_variables.Get("PMTParameterFile", fPMTParameterFile); + if (!gotPMTParamFile) { + logmessage = "PMTWaveformSim: No PMTParameterFile specified! Aborting!"; + Log(logmessage, v_error, verbosity); + return false; + } + + if (!LoadPMTParameters()) + return false; + + bool gotPrewindow = m_variables.Get("Prewindow", fPrewindow); + if (!gotPrewindow) { + logmessage = "PMTWaveformSim: Prewindow not defined. Using default of 10."; + Log(logmessage, v_warning, verbosity); + fPrewindow = 10; + } + + bool gotReadoutWindow = m_variables.Get("ReadoutWindow", fReadoutWindow); + if (!gotReadoutWindow) { + logmessage = "PMTWaveformSim: ReadoutWindow not defined. Using default of 35."; + Log(logmessage, v_warning, verbosity); + fReadoutWindow = 35; + } + + bool gotT0Offset = m_variables.Get("T0Offset", fT0Offset); + if (!gotT0Offset) { + logmessage = "PMTWaveformSim: T0Offset not defined. Using default of 7."; + Log(logmessage, v_warning, verbosity); + fT0Offset = 7; + } + + bool gotDebug = m_variables.Get("MakeDebugFile", fDebug); + if (!gotDebug) fDebug = 0; + if (fDebug) + fOutFile = new TFile("PMTWaveforms.root", "RECREATE"); + + bool gotGeometry = m_data->Stores.at("ANNIEEvent")->Header->Get("AnnieGeometry", fGeo); + if(!gotGeometry){ + logmessage = "PMTWaveformSim: Error retrieving Geometry from ANNIEEvent! Aborting!"; + Log(logmessage, v_error, verbosity); + return false; + } + + // Could set a seed here for repeatability + // Though would probably want to seed it in the Execute function based on the run/part/event numbers + fRandom = new TRandom3(); + + return true; +} + +//------------------------------------------------------------------------------ +bool PMTWaveformSim::Execute() +{ + int load_status = LoadFromStores(); + if (load_status == 0) { + ++fEvtNum; + return false; + } + if (load_status == 2) { + m_data->Stores.at("ANNIEEvent")->Set("SkipExecute", true); + ++fEvtNum; + return true; + } + + + // The container for the data that we'll put into the ANNIEEvent + std::map> > RawADCDataMC; + std::map> > CalADCDataMC; + + for (auto mcHitsIt : *fMCHits) { // Loop over the hit PMTs + int PMTID = mcHitsIt.first; + + std::vector mcHits = mcHitsIt.second; + + // Generate waveform samples from the MC hits + // samples from hits that are close in time will be added together + // key is hit time in clock ticks, value is amplitude + std::map sample_map; + for (const auto& mcHit : mcHits) {// Loop through each MCHit in the vector + + // skip negative hit times, what does that even mean if we're not using the smeared digit time? + // skip hit times past 70 us since that's our longest readout + if (mcHit.GetTime() < 0) continue; + if (mcHit.GetTime() > 70000) continue; + + // convert PMT hit time to clock ticks and "digitize" by converting to an int + // MCHit time is in ns, but we're going to sample in clock ticks + uint16_t hit_t0 = uint16_t(mcHit.GetTime() / NS_PER_ADC_SAMPLE); + double hit_charge = mcHit.GetCharge(); + + // Set the readout window but don't allow negative times + uint16_t start_clocktick = (hit_t0 > fPrewindow)? hit_t0 - fPrewindow : 0; + uint16_t end_clocktick = start_clocktick + fReadoutWindow; + + // Randomly Sample the PMT parameters for each MCHit + SampleFitParameters(PMTID); + + // if (mcHits.size() < 5) + //std::cout << "PMTWaveformSim: " << fEvtNum << ", " << hit_t0 << ", " << start_clocktick << ", " << end_clocktick << std::endl; + + // loop over clock ticks + for (uint16_t clocktick = start_clocktick; clocktick <= end_clocktick; clocktick += 1) { + uint16_t sample = CustomLogNormalPulse(hit_t0, clocktick, hit_charge); + + // check if this hit time has been recorded + // either set it or add to it + if (sample_map.find(clocktick) == sample_map.end()) + sample_map[clocktick] = sample; + else + sample_map[clocktick] += sample; + }// end loop over clock ticks + }// end loop over mcHits + // If there are no samples for this PMT then no need to do the rest + if (sample_map.empty()) continue; + + + // Set the noise envelope and baseline for this PMT + // The noise std dev appears to be normally distributed around 1 with sigma 0.25 + double noiseSigma = fRandom->Gaus(1, 0.25); + int basline = fRandom->Uniform(300, 350); + + // convert the sample map into a vector of Waveforms and put them into the container + std::vector> rawWaveforms; + std::vector> calWaveforms; + ConvertMapToWaveforms(sample_map, rawWaveforms, calWaveforms, noiseSigma, basline); + + RawADCDataMC.emplace(PMTID, rawWaveforms); + CalADCDataMC.emplace(PMTID, calWaveforms); + }// end loop over PMTs + + // Publish the waveforms to the ANNIEEvent store if we have them + if (RawADCDataMC.size()) { + m_data->Stores.at("ANNIEEvent")->Set("RawADCDataMC", RawADCDataMC); + m_data->Stores.at("ANNIEEvent")->Set("CalibratedADCData", CalADCDataMC); + } else { + logmessage = "PMTWaveformSim: No waveforms produced. Skipping!"; + Log(logmessage, v_warning, verbosity); + m_data->Stores.at("ANNIEEvent")->Set("SkipExecute", true); + return true; + } + + + if (fDebug) + FillDebugGraphs(RawADCDataMC); + + ++fEvtNum; + m_data->Stores.at("ANNIEEvent")->Set("SkipExecute", false); + return true; +} + +//------------------------------------------------------------------------------ +bool PMTWaveformSim::Finalise() +{ + if (fDebug) + fOutFile->Close(); + + return true; +} + +//------------------------------------------------------------------------------ +bool PMTWaveformSim::LoadPMTParameters() +{ + + std::ifstream infile(fPMTParameterFile); + if (!infile.is_open()) { + logmessage = "PMTWaveformSim: Error opening CSV file: "; + logmessage += fPMTParameterFile + "!"; + Log(logmessage, v_error, verbosity); + return false; + } + + int pmtid; + double p0, p1, p2, u00, u10, u11, u20, u21, u22; + std::string comma; + std::string line; + while (std::getline(infile, line)) { + if (infile.fail()) { + logmessage = "PMTWaveformSim: Error using CSV file: "; + logmessage += fPMTParameterFile + "!"; + Log(logmessage, v_error, verbosity); + + return false; + } + + // Skip the header line + if (line.find("PMT") != std::string::npos) continue; + + // Skip any commented lines + if(line.rfind("#",0) != std::string::npos) continue; + + // Turn the line into a stringstream to extract the values + std::stringstream ss(line); + ss >> pmtid >> comma >> p0 >> comma >> p1 >> comma >> p2 >> comma + >> u00 >> comma + >> u10 >> comma >> u11 >> comma + >> u20 >> comma >> u21 >> comma >> u22; + + fPMTParamMap[pmtid] = {p0, p1, p2, u00, u10, u11, u20, u21, u22}; + + logmessage = "PMTWaveformSim: Loaded parameters for PMTID " + std::to_string(pmtid) + ": "; + logmessage += "p0 = " + std::to_string(p0); + logmessage += "p1 = " + std::to_string(p1); + logmessage += "p2 = " + std::to_string(p2); + Log(logmessage, v_message, verbosity); + } + + infile.close(); + + return true; +} + +//------------------------------------------------------------------------------ +bool PMTWaveformSim::SampleFitParameters(int pmtid) +{ + PMTFitParams pmtParams; + if (fPMTParamMap.find(pmtid) != fPMTParamMap.end()) { + pmtParams = fPMTParamMap[pmtid]; + } else { + logmessage = "PMTWaveformSim: PMTParameters not found for " + std::to_string(pmtid); + logmessage += ", using defaults: p0 = 17.49, p1 = 3.107, p2 = 0.1492"; + Log(logmessage, v_warning, verbosity); + + // TODO make this a random sample as well + fP0 = 17.49; + fP1 = 3.107; + fP2 = 0.1492; + + return true; + } + + // First sample a Gaussian with mean 0 and deviation 1 + double r0 = fRandom->Gaus(); + double r1 = fRandom->Gaus(); + double r2 = fRandom->Gaus(); + + // Convert to parameters that follow the fitted covariance matrix + fP0 = r0*pmtParams.u00 + pmtParams.p0; + fP1 = r0*pmtParams.u10 + r1*pmtParams.u11 + pmtParams.p1; + fP2 = r0*pmtParams.u20 + r1*pmtParams.u21 + r2*pmtParams.u22 + pmtParams.p2; + + return true; +} + +//------------------------------------------------------------------------------ +uint16_t PMTWaveformSim::CustomLogNormalPulse(uint16_t hit_t0, uint16_t clocktick, double hit_charge) +{ + //p0*exp( -0.5 * (log(x/p1)/p2)^2) + + // The fit was performed in time units of ns, but we pass samples in clock ticks + double x = (double(clocktick) + fT0Offset - hit_t0) * NS_PER_ADC_SAMPLE; + + double numerator = pow(log(x/fP1), 2); + double denom = (pow(fP2, 2)); + double amplitude = fP0 * exp(-0.5 * numerator/denom) * hit_charge; + + // Clip at 4095 and digitize to an integer + return uint16_t((amplitude > 4095) ? 4095 : amplitude); +} + +//------------------------------------------------------------------------------ +void PMTWaveformSim::ConvertMapToWaveforms(const std::map &sample_map, + std::vector> &rawWaveforms, + std::vector> &calWaveforms, + double noiseSigma, int baseline) +{ + // Clear the output waveforms just in case + rawWaveforms.clear(); + calWaveforms.clear(); + + // All MC has extended readout, but it's time consuming to draw 35000 noise samples + // Instead, only sample up to the maximum tick time. Then start with all baseline and + // add noise only to relvant ticks. The intermediate space between pulses will have no noise + + uint16_t maxTick = sample_map.rbegin()->first; + std::vector rawSamples(maxTick+1, baseline); + std::vector calSamples(maxTick+1, 0); + for (auto sample_pair : sample_map) { + uint16_t tick = sample_pair.first; + + // Generate noise for each sample based on the std dev of the noise envelope + double noise = fRandom->Gaus(0, noiseSigma); + int sample = std::round(noise + baseline); + + sample += sample_pair.second; + + rawSamples[tick] = (sample > 4095) ? 4095 : sample; + calSamples[tick] = (rawSamples[tick] - baseline) * ADC_TO_VOLT; + } + + // The start time in data is a trigger timestamp. Don't have that for MC so just set to 0. + rawWaveforms.emplace_back(0, rawSamples); + calWaveforms.emplace_back(0, calSamples, baseline, noiseSigma); +} + +//------------------------------------------------------------------------------ +int PMTWaveformSim::LoadFromStores() +{ + bool goodAnnieEvent = m_data->Stores.count("ANNIEEvent"); + if (!goodAnnieEvent) { + logmessage = "PMTWaveformSim: no ANNIEEvent store!"; + Log(logmessage, v_error, verbosity); + return 0; + } + + bool goodMCHits = m_data->Stores.at("ANNIEEvent")->Get("MCHits", fMCHits); + if (!goodMCHits) { + logmessage = "PMTWaveformSim: no MCHits in the ANNIEEvent! "; + Log(logmessage, v_error, verbosity); + return 0; + } + + if (fMCHits->empty()) { + logmessage = "PMTWaveformSim: The MCHits map is empty! Skipping!"; + Log(logmessage, v_warning, verbosity); + return 2; + } + + + return 1; +} + +//------------------------------------------------------------------------------ +void PMTWaveformSim::FillDebugGraphs(const std::map> > &RawADCDataMC) +{ + for (auto itpair : RawADCDataMC) { + std::string chanString = std::to_string(itpair.first); + + // Get/make the directory for this PMT + TDirectory* dir = fOutFile->GetDirectory(chanString.c_str()); + if (!dir) + dir = fOutFile->mkdir(chanString.c_str()); + + // Hop into that directory and save the graph + dir->cd(); + + // loop over waveforms and make graphs + for (uint wfIdx = 0; wfIdx < itpair.second.size(); ++wfIdx) { + auto waveform = itpair.second.at(wfIdx); + + std::string grName = ("wf_" + std::to_string(fEvtNum) + "_" + std::to_string(wfIdx)); + + // Make the graph + std::vector samples = waveform.Samples(); + TGraph grTemp = TGraph(); + double sampleX = waveform.GetStartTime(); + for(auto sample : samples) { + grTemp.AddPoint(sampleX, sample); + ++sampleX; + } + + grTemp.Write(grName.c_str()); + }// end loop over waveforms + }// end loop over PMTs +} + + + + + diff --git a/UserTools/PMTWaveformSim/PMTWaveformSim.h b/UserTools/PMTWaveformSim/PMTWaveformSim.h new file mode 100644 index 000000000..50b5b6aa8 --- /dev/null +++ b/UserTools/PMTWaveformSim/PMTWaveformSim.h @@ -0,0 +1,86 @@ +#ifndef PMTWaveformSim_H +#define PMTWaveformSim_H + +#include +#include + +// ANNIE includes +#include "Tool.h" +#include "CalibratedADCWaveform.h" +#include "Waveform.h" + +// ROOT includes +#include "TFile.h" +#include "TRandom3.h" + +struct PMTFitParams +{ + double p0; double p1; double p2; + double u00; + double u10; double u11; + double u20; double u21; double u22; +}; + + +/** + * \class PMTWaveformSim + * + * This is a blank template for a Tool used by the script to generate a new custom tool. Please fill out the description and author information. +* +* $Author: D. Ajana $ +* $Date: 2024/11/05 10:44:00 $ +* Contact: dja23@fsu.edu +*/ +class PMTWaveformSim: public Tool { + + + public: + + PMTWaveformSim(); ///< Simple constructor + bool Initialise(std::string configfile,DataModel &data); ///< Initialise Function for setting up Tool resources. @param configfile The path and name of the dynamic configuration file to read in. @param data A reference to the transient data class used to pass information between Tools. + bool Execute(); ///< Execute function used to perform Tool purpose. + bool Finalise(); ///< Finalise function used to clean up resources. + int LoadFromStores(); + + bool LoadPMTParameters(); + bool SampleFitParameters(int pmtid); + uint16_t CustomLogNormalPulse(uint16_t hit_t0, uint16_t t0_clocktick, double hit_charge); + void ConvertMapToWaveforms(const std::map &sample_map, + std::vector> &rawWaveforms, + std::vector> &calWaveforms, + double noiseSigma, int baseline); + + void FillDebugGraphs(const std::map> > &RawADCDataMC); + + private: + + // To load from the ANNIEEvent + std::map> *fMCHits = nullptr; + + Geometry *fGeo = nullptr; + + // Config variables + uint16_t fPrewindow; + uint16_t fReadoutWindow; + uint16_t fT0Offset; + std::string fPMTParameterFile; + + TRandom3 *fRandom; + + std::map fPMTParamMap; + double fP0, fP1, fP2; + + bool fDebug; + TFile *fOutFile; + int fEvtNum = 0; + + int verbosity; + int v_error=0; + int v_warning=1; + int v_message=2; + int v_debug=3; + std::string logmessage; +}; + + +#endif diff --git a/UserTools/PMTWaveformSim/README.md b/UserTools/PMTWaveformSim/README.md new file mode 100644 index 000000000..ba457de91 --- /dev/null +++ b/UserTools/PMTWaveformSim/README.md @@ -0,0 +1,22 @@ +# PMTWaveformSim + +PMTWaveformSim generates PMT waveforms based on the simulated PMT hits. For each MCHit a lognorm waveform is sampled based on fit parameters extracted from SPE waveforms in data. The fit parameters are randomly sampled in a way that conserved the covariance between parameters and captures the random behavior of a PMT's response. A full extended readout window (70 us) is sampled and waveforms from overlapping hits are added. A random baseline between 300 and 350 ADC is added. Random noise is applied, where the noise sigma is sampled from a gaussian with mean 1 and std dev of 0.25. The baseline and noise envelope values are hardcoded at the moment. Finally, any ADC counts above 4095 are clipped to mimick saturation. + +## Data + +**RawADCDataMC** `std::map> >` +* The raw simulated waveforms. The key is PMT channel number, then inner vector should always have size == 1 + +**CalibratedADCData** `std::map> >` +* The "calibrated" simulated waveforms. The baseline and sigma are the true, randomly-generated values. The key is PMT channel number, then inner vector should always have size == 1 + + +## Configuration + +``` +PMTParameterFile configfiles/PMTWaveformSim/PMTWaveformLognormFit.csv # file containing the fit parameters and triangular Cholesky decomposed covariance matrix +Prewindow 10 # number of clock ticks before the MC hit time to begin sampling the fit function +ReadoutWindow 35 # number of clock ticks around the MC hit time over which waveforms are sampled +T0Offset 0 # A timing offset (in clock ticks) that can be used to align the pulse start time +MakeDebugFile 0 # Produce a root file containing all the simulated waveforms +``` diff --git a/UserTools/PhaseIIADCCalibrator/PhaseIIADCCalibrator.cpp b/UserTools/PhaseIIADCCalibrator/PhaseIIADCCalibrator.cpp index a85341ac1..34b0fda62 100644 --- a/UserTools/PhaseIIADCCalibrator/PhaseIIADCCalibrator.cpp +++ b/UserTools/PhaseIIADCCalibrator/PhaseIIADCCalibrator.cpp @@ -723,20 +723,27 @@ PhaseIIADCCalibrator::make_calibrated_waveforms_ze3ra_multi( // come from the same readout for the same channel) std::vector< CalibratedADCWaveform > calibrated_waveforms; for (const auto& raw_waveform : raw_waveforms) { - std::vector baselines; + std::vector baselines; + std::vector sigma_baselines; std::vector RepresentationRegion; - double first_baseline=0; - double first_sigma_baseline; + bool isFirst = true; + double first_baseline, first_sigma_baseline; double baseline, sigma_baseline; const size_t nsamples = raw_waveform.Samples().size(); for(size_t starting_sample = 0; starting_sample < nsamples; starting_sample += baseline_rep_samples){ double baseline, sigma_baseline; ze3ra_baseline(raw_waveform, baseline, sigma_baseline, num_baseline_samples,starting_sample); - if(sigma_baseline4) std::cout << "BASELINE UNCERTAINTY BEYOND SET THRESHOLD. IGNORING SAMPLE" << std::endl; } } @@ -746,6 +753,7 @@ PhaseIIADCCalibrator::make_calibrated_waveforms_ze3ra_multi( if(verbosity>4) std::cout << "NO BASLINE FOUND WITHIN TOLERANCE. USING FIRST AS BEST ESTIMATE" << std::endl; RepresentationRegion.push_back(baseline_rep_samples); baselines.push_back(first_baseline); + sigma_baselines.push_back(first_sigma_baseline); } std::vector cal_data; const std::vector& raw_data = raw_waveform.Samples(); @@ -762,10 +770,23 @@ PhaseIIADCCalibrator::make_calibrated_waveforms_ze3ra_multi( } } } - double bl_estimates_mean, bl_estimates_var; - ComputeMeanAndVariance(baselines, bl_estimates_mean, bl_estimates_var); + double bl_estimates_mean = 0; + double bl_estimates_sigma = 0; + + // When averaging multiple means you need to average the variances as well + // We don't want to use the variance of the mean of all the baselines. + // If there is only one baseline then the variance is 0 if you do that... + for (size_t idx = 0; idx < baselines.size(); ++idx) { + bl_estimates_mean += baselines[idx]; + bl_estimates_sigma += pow(sigma_baselines[idx],2); + } + bl_estimates_mean = bl_estimates_mean / baselines.size(); + bl_estimates_sigma = sqrt(bl_estimates_sigma / double(baselines.size())); + + // ComputeMeanAndVariance(baselines, bl_estimates_mean, bl_estimates_var, std::numeric_limits::max(), 0, 7); + calibrated_waveforms.emplace_back(raw_waveform.GetStartTime(), - cal_data, bl_estimates_mean, bl_estimates_var); + cal_data, bl_estimates_mean, bl_estimates_sigma); } return calibrated_waveforms; } diff --git a/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp b/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp old mode 100755 new mode 100644 index 902a25016..b6bc414b7 --- a/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp +++ b/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp @@ -36,6 +36,7 @@ bool PhaseIIADCHitFinder::Initialise(std::string config_filename, DataModel& dat m_variables.Get("PulseWindowEnd", pulse_window_end_shift); m_variables.Get("WindowIntegrationDB", adc_window_db); m_variables.Get("EventBuilding",eventbuilding_mode); + m_variables.Get("MCWaveforms",mc_waveforms); if ((pulse_window_start_shift > 0) || (pulse_window_end_shift) < 0){ Log("PhaseIIADCHitFinder Tool: WARNING... trigger threshold crossing will not be inside pulse window. Threshold" @@ -65,6 +66,11 @@ bool PhaseIIADCHitFinder::Initialise(std::string config_filename, DataModel& dat eventbuilding_mode = false; } + if (mc_waveforms && (eventbuilding_mode || use_led_waveforms)) { + Log("PhaseIIADCCalibrator: Cannot use MCWaveforms in EventBuilding mode or while using LED waveforms. Aborting!", v_error, verbosity); + return false; + } + //Set in CStore for tools to know and log this later m_data->CStore.Set("ADCThreshold",default_adc_threshold); @@ -72,7 +78,9 @@ bool PhaseIIADCHitFinder::Initialise(std::string config_filename, DataModel& dat m_data->CStore.Get("AuxChannelNumToTypeMap",AuxChannelNumToTypeMap); // Get the timing offsets - m_data->CStore.Get("ChannelNumToTankPMTTimingOffsetMap",ChannelKeyToTimingOffsetMap); + // don't need them for MC + if (!mc_waveforms) + m_data->CStore.Get("ChannelNumToTankPMTTimingOffsetMap",ChannelKeyToTimingOffsetMap); //Recreate maps that were deleted with ANNIEEvent->Delete() ANNIEEventBuilder tool hit_map = new std::map>; @@ -129,21 +137,40 @@ bool PhaseIIADCHitFinder::Execute() { } else { got_raw_data = annie_event->Get("RawADCData", raw_waveform_map); got_rawaux_data = annie_event->Get("RawADCAuxData", raw_aux_waveform_map); + + if (mc_waveforms) { + got_raw_data = annie_event->Get("RawADCDataMC", raw_waveform_map); + + // Some executes are skipped if there are no MCHits or waveforms produced + // Put the cleared maps into the ANNIEEvent to ensure that a downstream + // tool doesn't grab a map from a previous event + bool skip = false; + bool got_skip_status = annie_event->Get("SkipExecute", skip); + if (got_skip_status && skip) { + Log("PhaseIIADCHitFinder: An upstream tool told me to skip this event.",v_warning,verbosity); + + m_data->Stores.at("ANNIEEvent")->Set("RecoADCHits", pulse_map); + m_data->Stores.at("ANNIEEvent")->Set("RecoADCAuxHits", aux_pulse_map); + + return true; + } + }// end if mc_waveforms } + // Check for problems if ( !got_raw_data ) { Log("Error: The PhaseIIADCHitFinder tool could not find the RawADCData entry", v_error, verbosity); return false; } - if ( !got_rawaux_data ) { + if ( !got_rawaux_data && !(use_led_waveforms || mc_waveforms)) { Log("Error: The PhaseIIADCHitFinder tool could not find the RawADCAuxData entry", v_error, - verbosity); + verbosity); return false; } else if ( raw_waveform_map.empty() ) { - Log("Error: The PhaseIIADCHitFinder tool found an empty RawADCData entry", v_error, - verbosity); + Log("Error: The PhaseIIADCHitFinder tool found an empty RawADCData entry.", v_error, + verbosity); return false; } @@ -165,13 +192,13 @@ bool PhaseIIADCHitFinder::Execute() { " entry", v_error, verbosity); return false; } - if ( !got_calibratedaux_data ) { + if ( !got_calibratedaux_data && !(use_led_waveforms || mc_waveforms)) { Log("Error: The PhaseIIADCHitFinder tool could not find the CalibratedADCAuxData" " entry", v_error, verbosity); return false; } else if ( calibrated_waveform_map.empty() ) { - Log("Error: The PhaseIIADCHitFinder tool found an empty CalibratedADCData entry", + Log("Error: The PhaseIIADCHitFinder tool found an empty CalibratedADCData entry.", v_error, verbosity); return false; } @@ -712,7 +739,7 @@ std::vector PhaseIIADCHitFinder::find_pulses_bywindow( if(it != ChannelKeyToTimingOffsetMap.end()){ //Timing offset is available timing_offset = ChannelKeyToTimingOffsetMap.at(channel_key); } else { - if(verbosity>2){ + if(verbosity>2 && !mc_waveforms){ std::cout << "Didn't find Timing offset for channel " << channel_key << std::endl; } } @@ -720,10 +747,11 @@ std::vector PhaseIIADCHitFinder::find_pulses_bywindow( // Store the freshly made pulse in the vector of found pulses pulses.emplace_back(channel_key, ( wmin * NS_PER_SAMPLE )-timing_offset, - (peak_sample * NS_PER_SAMPLE)-timing_offset, + ( peak_sample * NS_PER_SAMPLE )-timing_offset, calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + ( wmax * NS_PER_SAMPLE )-timing_offset); } return pulses; } @@ -826,7 +854,7 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( if(it != ChannelKeyToTimingOffsetMap.end()){ //Timing offset is available timing_offset = ChannelKeyToTimingOffsetMap.at(channel_key); } else { - if(verbosity>v_error){ + if(verbosity>v_error && !mc_waveforms){ std::cout << "PhaseIIADCHitFinder: Didn't find Timing offset for channel... setting this channel's offset to 0ns" << channel_key << std::endl; } } @@ -834,10 +862,11 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( // Store the freshly made pulse in the vector of found pulses pulses.emplace_back(channel_key, ( pulse_start_sample * NS_PER_SAMPLE )-timing_offset, - (peak_sample * NS_PER_SAMPLE)-timing_offset, + ( peak_sample * NS_PER_SAMPLE )-timing_offset, calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + ( pulse_end_sample * NS_PER_SAMPLE )-timing_offset); } @@ -903,7 +932,7 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( if(it != ChannelKeyToTimingOffsetMap.end()){ //Timing offset is available timing_offset = ChannelKeyToTimingOffsetMap.at(channel_key); } else { - if(verbosity>v_error){ + if(verbosity>v_error && !mc_waveforms){ std::cout << "PhaseIIADCHitFinder: Didn't find Timing offset for channel... setting this channel's offset to 0ns" << channel_key << std::endl; } } @@ -953,10 +982,11 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( // Store the freshly made pulse in the vector of found pulses pulses.emplace_back(channel_key, ( pulse_start_sample * NS_PER_ADC_SAMPLE )-timing_offset, - (hit_time * NS_PER_ADC_SAMPLE)-timing_offset, // interpolated hit time + ( hit_time * NS_PER_ADC_SAMPLE )-timing_offset, // interpolated hit time calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + ( pulse_end_sample * NS_PER_ADC_SAMPLE )-timing_offset); } } } else { diff --git a/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.h b/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.h old mode 100755 new mode 100644 index b3471d58e..eae2a98e8 --- a/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.h +++ b/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.h @@ -58,7 +58,8 @@ class PhaseIIADCHitFinder : public Tool { int pulse_window_end_shift; std::map channel_threshold_map; std::map>> channel_window_map; - bool eventbuilding_mode; + bool eventbuilding_mode; + bool mc_waveforms; std::map ChannelKeyToTimingOffsetMap; diff --git a/UserTools/Unity.h b/UserTools/Unity.h index 5f43815d1..10b5839b8 100644 --- a/UserTools/Unity.h +++ b/UserTools/Unity.h @@ -181,3 +181,4 @@ #include "BackTracker.h" #include "PrintDQ.h" #include "AssignBunchTimingMC.h" +#include "PMTWaveformSim.h" diff --git a/configfiles/PMTWaveformSim/LoadGeometryConfig b/configfiles/PMTWaveformSim/LoadGeometryConfig new file mode 100644 index 000000000..b52737aa4 --- /dev/null +++ b/configfiles/PMTWaveformSim/LoadGeometryConfig @@ -0,0 +1,9 @@ +verbosity 0 +LAPPDChannelCount 60 +FACCMRDGeoFile ./configfiles/LoadGeometry/FullMRDGeometry.csv +DetectorGeoFile ./configfiles/LoadGeometry/DetectorGeometrySpecs.csv +LAPPDGeoFile ./configfiles/LoadGeometry/LAPPDGeometry.csv +TankPMTGeoFile ./configfiles/LoadGeometry/FullTankPMTGeometry.csv +TankPMTGainFile ./configfiles/LoadGeometry/ChannelSPEGains_BeamRun20192020.csv +AuxiliaryChannelFile ./configfiles/LoadGeometry/AuxChannels.csv +TankPMTTimingOffsetFile ./configfiles/LoadGeometry/TankPMTTimingOffsets.csv \ No newline at end of file diff --git a/configfiles/PMTWaveformSim/LoadWCSimConfig b/configfiles/PMTWaveformSim/LoadWCSimConfig new file mode 100644 index 000000000..5f6f6b0a9 --- /dev/null +++ b/configfiles/PMTWaveformSim/LoadWCSimConfig @@ -0,0 +1,16 @@ +#LoadWCSim Config File +# all variables retrieved with m_variables.Get() must be defined here! +verbose 0 + +InputFile /pnfs/annie/persistent/simulations/wcsim/G1810a0211a/standard/tank/pmt/wcsim_0.18.10.root + +WCSimVersion 3 ## should reflect the WCSim version of the files being loaded +HistoricTriggeroffset 0 ## time offset of digits relative to the trigger +UseDigitSmearedTime 0 ## whether to use smeared digit time (T), or true time of first photon (F) +LappdNumStrips 56 ## num channels to construct from each LAPPD +LappdStripLength 100 ## relative x position of each LAPPD strip, for dual-sided readout [mm] +LappdStripSeparation 10 ## stripline separation, for calculating relative y position of each LAPPD strip [mm] +PMTMask ./configfiles/LoadWCSim/DeadPMTIDs_p2v7.txt ## Which PMTs should be masked out? / are dead? +ChankeyToPMTIDMap ./configfiles/LoadWCSim/Chankey_WCSimID_v7.txt +ChankeyToMRDIDMap ./configfiles/LoadWCSim/MRD_Chankey_WCSimID.dat +ChankeyToFMVIDMap ./configfiles/LoadWCSim/FMV_Chankey_WCSimID.dat diff --git a/configfiles/PMTWaveformSim/PMTWaveformLognormFit.csv b/configfiles/PMTWaveformSim/PMTWaveformLognormFit.csv new file mode 100644 index 000000000..0e7dc0969 --- /dev/null +++ b/configfiles/PMTWaveformSim/PMTWaveformLognormFit.csv @@ -0,0 +1,122 @@ +PMT, p0, p1, p2, U00, U10, U11, U20, U21, U22, +332, 16.1815, 11.7835, 0.224469, 1.09912, 0.0012609, 0.20212, -0.0117725, -0.00387271, 0.0145493 +334, 16.9673, 11.8651, 0.2489, 2.9381, 0, 0.2207, 0, 0, 0.0472 +335, 16.4180, 11.8651, 0.2489, 2.9381, 0, 0.2207, 0, 0, 0.0472 +336, 13.1547, 11.5986, 0.256954, 1.0054, 0.00256062, 0.250369, -0.0162861, -0.00559374, 0.0188073 +338, 16.5613, 11.8651, 0.2489, 2.9381, 0, 0.2207, 0, 0, 0.0472 +339, 12.3941, 11.8651, 0.2489, 2.9381, 0, 0.2207, 0, 0, 0.0472 +340, 10.5316, 11.852, 0.358028, 0.937849, -0.0359459, 0.410756, -0.0258826, -0.00849393, 0.0305087 +341, 17.2763, 12.0659, 0.237495, 1.2176, -0.00123625, 0.222479, -0.0135174, -0.00415835, 0.0160533 +#343, 3.1881, 11.8651, 0.2489, 2.9381, 0, 0.2207, 0, 0, 0.0472 +344, 11.145, 11.6382, 0.270989, 2.01325, -0.0741067, 0.521478, -0.0524951, -0.00598261, 0.0463826 +347, 17.066, 12.3058, 0.252206, 0.52976, -0.000135767, 0.111231, -0.00561469, -0.00219789, 0.00733403 +348, 16.6618, 11.9753, 0.235212, 1.21115, 0.0036329, 0.227827, -0.013784, -0.00467008, 0.0163823 +350, 17.4545, 11.778, 0.208261, 0.992566, 0.00912863, 0.161067, -0.00902673, -0.00359592, 0.0112417 +351, 18.4885, 11.7882, 0.196192, 1.21593, 0.0026219, 0.170627, -0.00999694, -0.00300509, 0.0123515 +#353, 3.7712, 11.6034, 0.3268, 2.4673, 0, 0.7434, 0, 0, 0.0241 +354, 14.7095, 12.3313, 0.298, 0.962958, -0.0163942, 0.26598, -0.0149568, -0.00475438, 0.0184653 +355, 10.255, 11.6652, 0.341621, 0.596657, -0.0118375, 0.262788, -0.0149194, -0.00620281, 0.0186959 +#356, 4.7437, 11.6034, 0.3268, 2.4673, 0, 0.7434, 0, 0, 0.024 +357, 14.79, 12.6458, 0.321597, 0.905763, -0.0193769, 0.278233, -0.0150576, -0.00505714, 0.018792 +358, 9.09274, 11.0971, 0.314893, 0.706831, -0.0231053, 0.299926, -0.0182626, -0.00595521, 0.0228697 +#359, 2.7887, 11.6034, 0.3268, 2.4673, 0, 0.7434, 0, 0, 0.0241 +360, 11.8003, 11.9891, 0.33628, 0.672946, -0.0155954, 0.258834, -0.0144044, -0.00548394, 0.0181108 +361, 8.3358, 10.5122, 0.329485, 0.582343, -0.00468082, 0.274878, -0.01737, -0.00772567, 0.0214473 +362, 12.5612, 11.7437, 0.291085, 0.815987, -0.00616299, 0.245508, -0.0149959, -0.00529733, 0.0179324 +363, 12.808, 11.9107, 0.289881, 0.692459, -0.0070296, 0.20929, -0.0118531, -0.00427004, 0.0147672 +364, 10.4694, 11.5381, 0.349791, 0.45393, 0.00594671, 0.209203, -0.0113758, -0.00645827, 0.0141728 +365, 8.96467, 10.8896, 0.38109, 0.509903, 0.00752423, 0.28113, -0.0167097, -0.00980058, 0.0202394 +366, 7.54604, 10.3277, 0.320294, 0.76851, -0.0255958, 0.358688, -0.0268422, -0.00812207, 0.030787 +367, 13.4516, 12.2728, 0.315725, 0.726559, -0.0103248, 0.235225, -0.0129952, -0.00486405, 0.0161507 +368, 13.7834, 12.4673, 0.323781, 0.625156, -0.00136558, 0.211405, -0.0112286, -0.00505615, 0.0139609 +369, 8.02919, 10.5823, 0.360006, 0.4664, -0.00974797, 0.257012, -0.0146779, -0.00726211, 0.0192091 +370, 10.2022, 11.356, 0.330295, 0.554959, 0.00188158, 0.236099, -0.0136587, -0.00666618, 0.0168183 +371, 13.6898, 12.3247, 0.324826, 0.638444, -0.00147777, 0.215787, -0.0115014, -0.00523692, 0.0143534 +372, 18.6865, 11.9436, 0.226671, 0.945701, -0.00285972, 0.152502, -0.00885944, -0.00257133, 0.010953 +373, 17.2248, 11.7201, 0.214925, 1.19781, 0.0196466, 0.208605, -0.0112031, -0.00547322, 0.0140196 +374, 17.653, 11.9499, 0.225296, 0.692605, 0.000785657, 0.120219, -0.00660593, -0.00228146, 0.00838178 +375, 17.3329, 11.626, 0.205215, 0.953283, 0.00644134, 0.149391, -0.00876697, -0.00316025, 0.0107825 +376, 16.3353, 11.7771, 0.234094, 1.10946, -0.01189, 0.203196, -0.0126647, -0.00287989, 0.0152296 +377, 16.4613, 11.7395, 0.228804, 1.33221, -0.0104825, 0.231954, -0.015608, -0.00349413, 0.0178923 +378, 15.5484, 11.894, 0.248814, 0.998359, -0.00496052, 0.207982, -0.0128779, -0.00379268, 0.0152893 +379, 18.4251, 11.8365, 0.216831, 1.4908, -0.00562787, 0.221649, -0.0150193, -0.00348075, 0.0170231 +380, 16.8922, 11.7073, 0.213033, 0.811405, 0.0104526, 0.142227, -0.00746291, -0.00346849, 0.00958732 +381, 15.8199, 11.8887, 0.247267, 0.886527, -0.00300425, 0.1825, -0.0109067, -0.00342333, 0.0132167 +382, 12.1736, 11.5981, 0.288064, 0.64403, 0.00322319, 0.203865, -0.0114588, -0.00514149, 0.01429 +383, 17.3771, 11.7654, 0.225753, 1.0952, -0.00326426, 0.183473, -0.011478, -0.00314874, 0.0136821 +384, 18.4667, 12.0561, 0.228487, 0.714233, 0.00195128, 0.122002, -0.00658039, -0.00242593, 0.00836149 +385, 16.6384, 11.8575, 0.223512, 1.42805, -0.0194552, 0.241996, -0.0158588, -0.00274704, 0.0184799 +386, 16.531, 11.9013, 0.231433, 0.915276, 0.00100232, 0.17039, -0.0100904, -0.00331765, 0.0122502 +387, 17.8784, 12.0554, 0.237911, 0.894593, -0.0030315, 0.160264, -0.00912539, -0.00281532, 0.0113281 +388, 15.2968, 11.6789, 0.240434, 1.38132, -0.0312206, 0.266723, -0.0179417, -0.0025785, 0.0207715 +389, 18.6947, 11.9855, 0.225579, 0.785306, 6.95106e-05, 0.128372, -0.00716647, -0.00236321, 0.0090084 +390, 16.193, 11.6874, 0.214387, 1.07159, -0.0034306, 0.182333, -0.0113206, -0.00296363, 0.0136509 +391, 14.3684, 11.5356, 0.239436, 0.885146, -0.00791972, 0.186319, -0.0116311, -0.00304685, 0.0140958 +392, 15.5213, 11.8151, 0.232703, 0.846511, -0.00272054, 0.166728, -0.00987177, -0.00296001, 0.0121191 +393, 14.4607, 11.9787, 0.276474, 0.851475, -0.0152815, 0.214403, -0.0123445, -0.0034262, 0.0153607 +394, 17.989, 12.0286, 0.230265, 0.800926, -0.00400728, 0.138462, -0.00763204, -0.00222846, 0.00972108 +395, 20.956, 11.916, 0.209918, 1.4117, -0.00115246, 0.184391, -0.0115401, -0.00307273, 0.0136481 +396, 19.4333, 12.0084, 0.2152, 1.20421, 0.00214268, 0.177116, -0.0107999, -0.00328542, 0.0128342 +397, 16.8615, 11.9109, 0.238467, 1.04534, -0.00730006, 0.192558, -0.011761, -0.00311293, 0.0141518 +398, 16.1561, 11.671, 0.224238, 0.987765, 0.00258986, 0.178236, -0.0109789, -0.00357651, 0.0131557 +399, 16.3963, 11.8413, 0.227827, 1.42694, -0.0291131, 0.24974, -0.0159717, -0.00210604, 0.0189558 +400, 17.1704, 11.7925, 0.214773, 1.01624, -0.00315399, 0.165391, -0.0100399, -0.00266275, 0.0122081 +401, 15.3736, 11.7643, 0.239775, 0.719101, -0.00404763, 0.147793, -0.00844082, -0.00256813, 0.0106377 +402, 16.4304, 11.5953, 0.213623, 0.891156, -0.0033035, 0.149204, -0.00896757, -0.00238849, 0.0110989 +403, 14.4771, 11.315, 0.214186, 0.770042, -3.11814e-05, 0.145043, -0.00872536, -0.00269165, 0.0108905 +404, 11.6303, 12.0387, 0.337009, 0.573103, -0.0126925, 0.226651, -0.0123472, -0.00486966, 0.0156723 +405, 7.82446, 10.8991, 0.339089, 0.707243, -0.0470227, 0.356936, -0.0235469, -0.00608166, 0.0285459 +406, 17.1667, 11.9454, 0.237854, 0.88653, -0.00666752, 0.162609, -0.00938048, -0.00256132, 0.0116844 +407, 17.7687, 11.9023, 0.212032, 0.766962, -0.000433398, 0.122873, -0.00689259, -0.00209822, 0.0087139 +408, 16.4457, 11.7988, 0.2254, 1.4540, 0, 0.1640, 0, 0, 0.0136 +409, 16.9692, 11.7387, 0.220713, 1.12227, -0.00308527, 0.187484, -0.0118391, -0.00317149, 0.0140611 +410, 14.9763, 11.9452, 0.264532, 0.964051, -0.0144364, 0.2222, -0.0132197, -0.00349243, 0.0161554 +411, 14.3011, 11.5125, 0.233957, 0.980853, -0.0170196, 0.199695, -0.0126145, -0.00239796, 0.0153161 +412, 17.4909, 11.8264, 0.216318, 1.03942, -0.00252854, 0.167459, -0.010243, -0.00276921, 0.012358 +413, 18.4095, 11.7611, 0.208316, 0.9201, 0.00638486, 0.140023, -0.00802475, -0.00299365, 0.00992059 +414, 19.0506, 11.7197, 0.200956, 1.04819, 0.00567565, 0.146363, -0.00868709, -0.00295905, 0.0105898 +415, 16.429, 11.5389, 0.207206, 0.924626, -0.00151963, 0.150925, -0.00887943, -0.0025144, 0.0111488 +417, 16.6145, 11.5931, 0.219003, 1.91434, -0.0116268, 0.303973, -0.0228075, -0.00461274, 0.0245922 +418, 18.1754, 11.5369, 0.1972, 2.26999, 0.00690851, 0.308702, -0.0209887, -0.00577008, 0.023937 +419, 16.7176, 11.4496, 0.197993, 1.79447, 0.0115074, 0.264508, -0.0184788, -0.0054933, 0.0206809 +420, 22.7669, 11.6322, 0.1959, 2.3697, 0, 0.1129, 0, 0, 0.0240 +421, 16.5196, 11.5699, 0.206677, 1.66467, 0.00636307, 0.263751, -0.017533, -0.00517831, 0.020186 +422, 17.5206, 11.6951, 0.20264, 2.09836, 0.00128097, 0.300455, -0.0216512, -0.00519984, 0.0236865 +423, 17.3143, 11.7567, 0.205548, 1.74809, 0.00302894, 0.266245, -0.0173402, -0.00481607, 0.020097 +424, 18.8025, 11.6723, 0.192618, 2.25663, 0.00946812, 0.290165, -0.0202556, -0.00548813, 0.022506 +425, 15.4372, 11.6983, 0.246771, 2.1073, -0.0447784, 0.386148, -0.0326742, -0.00422519, 0.0324745 +426, 21.4214, 11.7092, 0.162415, 2.10618, 0.0157944, 0.216375, -0.0115835, -0.00436897, 0.0148912 +427, 18.3197, 11.5901, 0.185997, 1.94919, 0.0205332, 0.255456, -0.0166436, -0.00578503, 0.0190691 +428, 15.5427, 11.7414, 0.228688, 1.81489, -0.0129791, 0.322544, -0.0245963, -0.0050347, 0.0259873 +429, 19.8808, 11.5239, 0.169535, 2.25705, 0.0277746, 0.254673, -0.0150648, -0.00606648, 0.0181274 +430, 18.5201, 11.6785, 0.192243, 1.97639, 0.0124438, 0.271381, -0.0160084, -0.00547, 0.019617 +432, 16.2194, 11.5495, 0.21082, 1.79304, -0.00449995, 0.285874, -0.0203824, -0.00471571, 0.0227028 +433, 20.3546, 11.5811, 0.169168, 1.77143, 0.0203762, 0.197991, -0.0111087, -0.00461118, 0.0137774 +434, 16.8308, 11.6776, 0.204703, 1.90792, -0.00138537, 0.289198, -0.0201872, -0.0048371, 0.0225977 +435, 16.1769, 11.5845, 0.212605, 1.83411, 0.00378384, 0.296374, -0.0216042, -0.00565352, 0.0235066 +436, 20.6799, 11.6408, 0.167326, 2.08367, 0.0235311, 0.226964, -0.0128406, -0.00525132, 0.0157995 +437, 21.9742, 11.7023, 0.170203, 2.23103, 0.015895, 0.229293, -0.0133747, -0.00465486, 0.0163782 +438, 17.244, 11.5206, 0.194149, 1.94546, 0.0164538, 0.278271, -0.0186331, -0.00603257, 0.0212209 +439, 17.598, 11.5839, 0.20636, 1.92805, 0.00501941, 0.281054, -0.0198236, -0.00534138, 0.0220233 +440, 17.244, 11.8343, 0.22008, 2.05148, -0.00242051, 0.324049, -0.0238332, -0.00568393, 0.02552 +441, 16.7511, 11.6316, 0.212069, 1.98284, 0.00564093, 0.310735, -0.0224549, -0.00603168, 0.0244694 +442, 20.8555, 11.5429, 0.16751, 1.79699, 0.0185962, 0.193175, -0.0107737, -0.00436848, 0.0134829 +443, 16.8946, 11.3716, 0.191988, 1.61059, 0.0200682, 0.237307, -0.0148217, -0.00573007, 0.017558 +446, 17.0102, 11.8694, 0.220794, 1.88812, -0.00140432, 0.306529, -0.0219894, -0.00545122, 0.0238593 +447, 15.6919, 11.671, 0.22151, 1.93993, -0.0192759, 0.324627, -0.0256407, -0.00439161, 0.0266903 +448, 20.2622, 11.6803, 0.171468, 1.59172, 0.0152281, 0.185896, -0.00947263, -0.00403246, 0.0124752 +449, 21.0339, 11.5684, 0.165837, 1.96438, 0.0238181, 0.208036, -0.0117683, -0.00499108, 0.0144516 +450, 26.2343, 11.4588, 0.14365, 3.19853, 0.0102589, 0.233484, -0.0108197, -0.00385624, 0.0154123 +451, 17.1911, 11.5185, 0.194249, 1.88346, 0.0205039, 0.271199, -0.0182804, -0.00626395, 0.0206045 +452, 15.4282, 11.6637, 0.250749, 2.10438, -0.0259987, 0.39473, -0.0334018, -0.00610021, 0.0330829 +453, 20.0478, 11.6385, 0.175812, 1.86005, 0.0192341, 0.217135, -0.0127603, -0.00488615, 0.015464 +454, 19.5356, 11.5196, 0.161747, 1.91231, 0.0219558, 0.211209, -0.0117655, -0.00481501, 0.0146956 +455, 19.3469, 11.6801, 0.180487, 2.33217, 0.0243363, 0.28719, -0.0175967, -0.00644664, 0.0207875 +456, 24.8544, 11.7885, 0.158929, 2.13574, 0.010196, 0.189276, -0.00922597, -0.00348487, 0.0125871 +457, 17.9635, 11.9083, 0.215111, 2.04839, 0.00328706, 0.307679, -0.022277, -0.0057057, 0.0239149 +458, 16.5816, 11.775, 0.216282, 1.91451, 0.00494447, 0.311505, -0.0225166, -0.00601306, 0.0243323 +459, 19.0233, 11.6407, 0.190107, 1.84959, 0.00795076, 0.234735, -0.015644, -0.00443225, 0.0179219 +460, 18.1027, 11.6885, 0.20582, 1.95369, 0.00432308, 0.278251, -0.0194696, -0.00516732, 0.0216305 +461, 17.2489, 11.6514, 0.212362, 1.70302, 0.00220343, 0.2621, -0.0182581, -0.00486691, 0.0203933 +462, 16.3098, 11.5501, 0.212058, 2.27866, -0.00407155, 0.345676, -0.0289145, -0.0058746, 0.0290091 +463, 18.1769, 11.479, 0.192802, 1.82787, 0.0127039, 0.24785, -0.0159712, -0.00520418, 0.018721 diff --git a/configfiles/PMTWaveformSim/PMTWaveformSimConfig b/configfiles/PMTWaveformSim/PMTWaveformSimConfig new file mode 100644 index 000000000..1cfd33811 --- /dev/null +++ b/configfiles/PMTWaveformSim/PMTWaveformSimConfig @@ -0,0 +1,6 @@ +verbosity 0 +PMTParameterFile configfiles/PMTWaveformSim/PMTWaveformLognormFit.csv +Prewindow 10 +ReadoutWindow 35 +T0Offset 0 +MakeDebugFile 0 diff --git a/configfiles/PMTWaveformSim/PhaseIIADCHitFinderConfig b/configfiles/PMTWaveformSim/PhaseIIADCHitFinderConfig new file mode 100644 index 000000000..d05287eff --- /dev/null +++ b/configfiles/PMTWaveformSim/PhaseIIADCHitFinderConfig @@ -0,0 +1,11 @@ +verbosity 0 + +UseLEDWaveforms 0 + +PulseFindingApproach threshold +PulseWindowType dynamic +DefaultADCThreshold 7 +DefaultThresholdType relative + +EventBuilding 0 +MCWaveforms 1 diff --git a/configfiles/PMTWaveformSim/README.md b/configfiles/PMTWaveformSim/README.md new file mode 100644 index 000000000..78d3e2015 --- /dev/null +++ b/configfiles/PMTWaveformSim/README.md @@ -0,0 +1,25 @@ +# Configure files + +*********************** +#Description +********************** + +Configure files are simple text files for passing variables to the Tools. + +Text files are read by the Store class (src/Store) and automatically assigned to an internal map for the relevant Tool to use. + + +************************ +#Usage +************************ + +Any line starting with a "#" will be ignored by the Store, as will blank lines. + +Variables should be stored one per line as follows: + + +Name Value #Comments + + +Note: Only one value is permitted per name and they are stored in a string stream and template cast back to the type given. + diff --git a/configfiles/PMTWaveformSim/ToolChainConfig b/configfiles/PMTWaveformSim/ToolChainConfig new file mode 100644 index 000000000..5c69603a2 --- /dev/null +++ b/configfiles/PMTWaveformSim/ToolChainConfig @@ -0,0 +1,26 @@ +#ToolChain dynamic setup file + +##### Runtime Parameters ##### +verbose 0 ## Verbosity level of ToolChain +error_level 0 # 0= do not exit, 1= exit on unhandled errors only, 2= exit on unhandled errors and handled errors +attempt_recover 1 ## 1= will attempt to finalise if an execute fails +remote_port 24002 +IO_Threads 1 ## Number of threads for network traffic (~ 1/Gbps) + +###### Logging ##### +log_mode Interactive # Interactive=cout , Remote= remote logging system "serservice_name Remote_Logging" , Local = local file log; +log_local_path ./log +log_service LogStore + + +###### Service discovery ##### Ignore these settings for local analysis +service_publish_sec -1 +service_kick_sec -1 + +##### Tools To Add ##### +Tools_File configfiles/PMTWaveformSim/ToolsConfig ## list of tools to run and their config files + +##### Run Type ##### +Inline 2 ## number of Execute steps in program, -1 infinite loop that is ended by user +Interactive 0 ## set to 1 if you want to run the code interactively + diff --git a/configfiles/PMTWaveformSim/ToolsConfig b/configfiles/PMTWaveformSim/ToolsConfig new file mode 100644 index 000000000..42261ac88 --- /dev/null +++ b/configfiles/PMTWaveformSim/ToolsConfig @@ -0,0 +1,4 @@ +LoadGeometry LoadGeometry configfiles/PMTWaveformSim/LoadGeometryConfig +LoadWCSim LoadWCSim configfiles/PMTWaveformSim/LoadWCSimConfig +PMTWaveformSim PMTWaveformSim configfiles/PMTWaveformSim/PMTWaveformSimConfig +PhaseIIADCHitFinder PhaseIIADCHitFinder configfiles/PMTWaveformSim/PhaseIIADCHitFinderConfig