/* 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/MapCppKLMCDigitizer/MapCppKLMCDigitizer.hh"
#include
#include "src/common_cpp/Utils/KLChannelMap.hh"
#include "Config/MiceModule.hh"
#include "Utils/Exception.hh"
#include "API/PyWrapMapBase.hh"
namespace MAUS {
PyMODINIT_FUNC init_MapCppKLMCDigitizer(void) {
PyWrapMapBase::PyWrapMapBaseModInit
("MapCppKLMCDigitizer", "", "", "", "");
}
MapCppKLMCDigitizer::MapCppKLMCDigitizer()
: MapBase("MapCppKLMCDigitizer") {
}
//////////////////////////////////////////////////////////////////////
void MapCppKLMCDigitizer::_birth(const std::string& argJsonConfigDocument) {
// print level
fDebug = false;
// Check if the JSON document can be parsed, else return error only
Json::Reader reader;
bool parsingSuccessful = reader.parse(argJsonConfigDocument, _configJSON);
if (!parsingSuccessful) {
throw MAUS::Exceptions::Exception(Exceptions::recoverable,
"Failed to parse Json Configuration",
"MapCppKLMCDigitizer::_birth");
}
// get the geometry
if (!_configJSON.isMember("simulation_geometry_filename"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find geometry file",
"MapCppKLMCDigitizer::birth"));
std::string filename;
filename = _configJSON["simulation_geometry_filename"].asString();
// get the kl geometry modules
geo_module = new MiceModule(filename);
kl_modules = geo_module->findModulesByPropertyString("SensitiveDetector", "KL");
}
//////////////////////////////////////////////////////////////////////
void MapCppKLMCDigitizer::_death() {
}
//////////////////////////////////////////////////////////////////////
void MapCppKLMCDigitizer::_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_kl_digits store all the kl 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 kl hits for this event
KLHitArray *hits = spill->GetAnMCEvent(iPe).GetKLHits();
KLEvent *evt = new KLEvent();
(*recEvts)[iPe]->SetKLEvent(evt);
// process only if there are kl hits
if (hits) {
// pick out kl hits, digitize and store
KLTmpDigits tmpDigits = make_kl_digits(hits);
// fill kl events
if (fDebug) {
std::cout << "mcevt: " << iPe << " kl " << hits->size()
<< " hits, " << tmpDigits.size() << std::endl;
}
KLDigitArray* kl_digits = evt->GetKLEventDigitPtr()->GetKLDigitArrayPtr();
fill_kl_evt(iPe, tmpDigits, kl_digits);
} // end if-hits
} // end loop over events
}
//////////////////////////////////////////////////////////////////////
KLTmpDigits MapCppKLMCDigitizer::make_kl_digits(KLHitArray* hits) const {
KLTmpDigits tmpDigits(0);
if (hits->size() == 0) return tmpDigits;
for ( unsigned int j = 0; j < hits->size(); j++ ) { // j-th hit
KLHit hit = hits->at(j);
// if (fDebug) std::cout << "hit# " << j << hit << std::endl;
// make sure we can get the cell info
if (!hit.GetChannelId())
throw(Exceptions::Exception(Exceptions::recoverable,
"No channel_id in hit",
"MapCppKLMCDigitizer::make_kl_digits"));
double edep = hit.GetEnergyDeposited();
if (fDebug) {
std::cout << "klhit: " << hit.GetChannelId()->GetCell() << " "
<< hit.GetPosition().x() << " " << hit.GetMomentum().x()
<< " " << hit.GetEnergyDeposited() << std::endl;
}
int cll = hit.GetChannelId()->GetCell();
/*
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 < kl_modules.size(); ++jj ) {
if ( kl_modules[jj]->propertyExists("Cell", "int") &&
kl_modules[jj]->propertyInt("Cell") == cll) {
// got it
hit_module = kl_modules[jj];
if (fDebug) {
std::cout << kl_modules[jj]->propertyInt("Cell") << std::endl;
std::cout << kl_modules[jj]->fullName() << std::endl;
}
} // end check on module
} // end loop over kl_modules
// make sure we actually found a kl module corresponding to this hit
if (hit_module == NULL)
throw(Exceptions::Exception(Exceptions::recoverable,
"No KL module for hit",
"MapCppKLMCDigitizer::make_kl_digits"));
// now get the position of the hit
ThreeVector tpos = hit.GetPosition();
Hep3Vector hpos(tpos.x(), tpos.y(), tpos.z());
// get the cell and pmt positions from the geometry
// cell pos
Hep3Vector cellPos = 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 << " cellPos: " << cellPos << std::endl;
std::cout << "pmt2pos: " << pmt2Pos << " cellPos: " << cellPos << std::endl;
}
// find distances from hit to pmt geom
double dist1 = ( hpos - (cellPos + pmt1Pos) ).mag();
double dist2 = ( hpos - (cellPos + pmt2Pos) ).mag();
if (fDebug) std::cout << "distances to pmt: " << dist1 << " " << dist2 << std::endl;
// convert edep to photoelectrons for this cell/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;
int npe1 = calculate_nphe_at_pmt(dist1, edep);
int npe2 = calculate_nphe_at_pmt(dist2, edep);
if (fDebug) std::cout << "npe1: "<< npe1 << ", npe2: "<< npe2 << std::endl;
tmpThisDigit thisDigit;
// set the hits for PMTs at both ends
for (int p = 0; p < 2; ++p) {
thisDigit.fChannelId = hit.GetChannelId();
thisDigit.fMom = hit.GetMomentum();
thisDigit.fTime = hit.GetTime();
thisDigit.fPos = hpos;
thisDigit.fIsUsed = false;
// set the key for this channel
std::stringstream ss;
ss << "KLChannelKey " << cll << " " << p << " kl";
thisDigit.fKLKey = ss.str();
thisDigit.fCell = cll;
thisDigit.fPmt = p;
if (p == 0) {
thisDigit.fNpe = npe1;
} else if (p == 1) {
thisDigit.fNpe = npe2;
}
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;
}
//////////////////////////////////////////////////////////////////////
int MapCppKLMCDigitizer::calculate_nphe_at_pmt(double dist, double edep) const {
if (fDebug) std::cout << "edep= " << edep << std::endl;
if (!_configJSON.isMember("KLattLengthLong"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLattLengthLong in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLattLengthShort"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLattLengthShort in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLattLengthLongNorm"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLattLengthLongNorm in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLattLengthShortNorm"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLattLengthShortNorm in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLconversionFactor"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLconversionFactor in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLlightCollectionEff"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLlightCollectionEff in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLquantumEff"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLquantumEff in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLlightGuideEff"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLlightGuideEff in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLpmtGain"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLpmtGain in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLpmtSigmaGain"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find KLpmtSigmaGain in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
// convert energy deposited to number of photoelectrons
double attLengthL = (_configJSON["KLattLengthLong"].asDouble());
double attLengthS = (_configJSON["KLattLengthShort"].asDouble());
double nL = (_configJSON["KLattLengthLongNorm"].asDouble());
double nS = (_configJSON["KLattLengthShortNorm"].asDouble());
double energyW = (_configJSON["KLconversionFactor"].asDouble());
double effColl = (_configJSON["KLlightCollectionEff"].asDouble());
double qEff = (_configJSON["KLquantumEff"].asDouble());
double lgEff = (_configJSON["KLlightGuideEff"].asDouble());
double meanPhN;
int numberOfPh, collectedPh, pe;
// Photons in hit
meanPhN = edep/energyW;
numberOfPh = floor(RandPoisson::shoot(meanPhN));
// At the photomultiplier, after attenuation. Formula is from KLOE
double attenuation = nL*exp(-dist/attLengthL)+nS*exp(-dist/attLengthS);
collectedPh = floor(numberOfPh*effColl*attenuation*lgEff+0.5);
// Poisson smearing of photoelctrons at pmt cathode
pe = floor(RandPoisson::shoot(collectedPh*qEff));
// Now calculate amplified photoelectrons at the pmt anode and smear them with gaussian
int gain = (_configJSON["KLpmtGain"].asInt());
int sigma_gain = (_configJSON["KLpmtSigmaGain"].asInt());
int peAmplified = static_cast (floor(pe*RandGauss::shoot(gain, sigma_gain)));
// int peAmplified = static_cast (pe*gain);
if (fDebug) {
std::cout << "created photons: " << meanPhN << std::endl;
std::cout << "smeared photons: " << numberOfPh << std::endl;
std::cout << "attenuated photons at pmt: " << collectedPh << std::endl;
std::cout << "photoelectrons at pmt cathode: " << collectedPh*qEff << std::endl;
std::cout << "smeared photoelectrons at pmt cathode: " << pe << std::endl;
std::cout << "amplified photoelectrons: " << peAmplified<< std::endl;
}
return peAmplified;
}
//////////////////////////////////////////////////////////////////////
void MapCppKLMCDigitizer::fill_kl_evt(int evnum,
KLTmpDigits& tmpDigits,
KLDigitArray* klDigits) const {
if (!_configJSON.isMember("Do_V1724_Zero_Suppression"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find Do_V1724_Zero_Suppression",
"MapCppKLMCDigitizer::fill_kl_evt"));
if (!_configJSON.isMember("V1724_Zero_Suppression_Threshold"))
throw(Exceptions::Exception(Exceptions::recoverable,
"Could not find V1724_Zero_Suppression_Threshold",
"MapCppKLMCDigitizer::fill_kl_evt"));
bool zero_supp = (_configJSON["Do_V1724_Zero_Suppression"].asBool());
int adc_thresh;
if (zero_supp) {
adc_thresh = (_configJSON["V1724_Zero_Suppression_Threshold"].asInt());
} else {
adc_thresh = 0;
}
Json::Value kl_digit(Json::arrayValue);
// return null if this evt had no kl hits
if (tmpDigits.size() == 0) return;
int 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) {
// 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(npe / (_configJSON["KLadcConversionFactor"].asDouble()));
if (fDebug) std::cout << "npe-adc: " << npe << " " << adc << std::endl;
// now set the digit
KLDigit theDigit;
if (adc > adc_thresh) {
theDigit.SetChargePm(adc);
theDigit.SetChargeMm(adc);
theDigit.SetKlKey(tmpDigits[i].fKLKey);
theDigit.SetCell(tmpDigits[i].fCell);
theDigit.SetPmt(tmpDigits[i].fPmt);
// store event number
theDigit.SetPhysEventNumber(evnum);
theDigit.SetPartEventNumber(evnum);
if (fDebug) std::cout << "adc over thresh: "<< adc << std::endl;
klDigits->push_back(theDigit);
}
if (fDebug)
std::cout << "digit #" << ndigs << " " << theDigit.GetKlKey() << std::endl;
tmpDigits[i].fIsUsed = true;
} // ends if-statement
} // ends k-loop
}
//////////////////////////////////////////////////////////////////////
bool MapCppKLMCDigitizer::check_param(tmpThisDigit& hit1, tmpThisDigit& hit2) const {
int cell1 = hit1.fCell;
int cell2 = hit2.fCell;
int pmt1 = hit1.fPmt;
int pmt2 = hit2.fPmt;
int hit_is_used = hit2.fIsUsed;
if ( cell1 == cell2 && pmt1 == pmt2 && !hit_is_used ) {
return true;
} else {
return false;
}
}
//=====================================================================
}