/* 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::Exception(Exception::recoverable,
"Failed to parse Json Configuration",
"MapCppTOFMCDigitizer::_birth");
}
// get the geometry
if (!_configJSON.isMember("simulation_geometry_filename"))
throw(Exception(Exception::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(Json::Value* data) const {
Json::Value& root = *data;
Json::Value mc = check_sanity_mc(root);
// all_kl_digits store all the kl hits
// then we'll weed out multiple hits, add up yields, and store the event
std::vector all_kl_digits;
all_kl_digits.push_back(Json::Value());
Json::Value kl_evt;
// loop over events
if (fDebug) std::cout << "mc numevts = " << mc.size() << std::endl;
for ( unsigned int i = 0; i < mc.size(); i++ ) {
Json::Value particle = mc[i];
if (fDebug) std::cout << "evt: " << i << " particle= " << particle << std::endl;
// hits for this event
Json::Value _hits = particle["kl_hits"];
// if _hits.size() = 0, the make_digits and fill_kl_evt functions will
// return null.
all_kl_digits.clear();
// pick out kl hits, digitize and store
all_kl_digits = make_kl_digits(_hits, root);
// fill kl events
kl_evt.clear();
kl_evt[i] = fill_kl_evt(i, all_kl_digits);
if (fDebug) {
std::cout << "mcevt: " << i << " kl " << _hits.size()
<< " hits, " << all_kl_digits.size() << std::endl;
}
Json::Value kl_digs = fill_kl_evt(i, all_kl_digits);
root["recon_events"][i]["kl_event"]["kl_digits"]["kl"] = kl_digs;
} // end loop over events
}
//////////////////////////////////////////////////////////////////////
std::vector MapCppKLMCDigitizer::make_kl_digits(Json::Value hits,
Json::Value& root) const {
std::vector kl_digits;
kl_digits.clear();
if (hits.size() == 0) return kl_digits;
for ( unsigned int j = 0; j < hits.size(); j++ ) { // j-th hit
Json::Value hit = hits[j];
if (fDebug) std::cout << "hit# " << j << hit << std::endl;
// make sure we can get the cell info
if (!hit.isMember("channel_id"))
throw(Exception(Exception::recoverable,
"No channel_id in hit",
"MapCppKLMCDigitizer::make_kl_digits"));
if (!hit.isMember("momentum"))
throw(Exception(Exception::recoverable,
"No momentum in hit",
"MapCppKLMCDigitizer::make_kl_digits"));
if (!hit.isMember("time"))
throw(Exception(Exception::recoverable,
"No time in hit",
"MapCppKLMCDigitizer::make_kl_digits"));
Json::Value channel_id = hit["channel_id"];
if (!hit.isMember("energy_deposited"))
throw(Exception(Exception::recoverable,
"No energy_deposited in hit",
"MapCppKLMCDigitizer::make_kl_digits"));
double edep = hit["energy_deposited"].asDouble();
if (fDebug) {
std::cout << "klhit: " << hit["channel_id"] << " "
<< hit["position"] << " " << hit["momentum"]
<< " " << hit["time"] << " " << hit["energy_deposited"] << std::endl;
}
int cll = hit["channel_id"]["cell"].asInt();
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(Exception(Exception::recoverable,
"No KL module for hit",
"MapCppKLMCDigitizer::make_kl_digits"));
// now get the position of the hit
Hep3Vector hpos = JsonWrapper::JsonToThreeVector(hit["position"]);
// 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;
Json::Value tmpDigit;
// set the hits for PMTs at both ends
for (int p = 0; p < 2; ++p) {
tmpDigit.clear();
tmpDigit["channel_id"] = hit["channel_id"];
tmpDigit["mc_mom"] = hit["momentum"];
tmpDigit["time"] = hit["time"];
tmpDigit["mc_position"]=hit["position"];
tmpDigit["isUsed"] = 0;
// set the key for this channel
std::stringstream ss;
ss << "KLChannelKey " << cll << " " << p << " kl";
tmpDigit["kl_key"] = ss.str();
tmpDigit["cell"] = cll;
tmpDigit["pmt"] = p;
if (p == 0) {
tmpDigit["npe"] = npe1;
} else if (p == 1) {
tmpDigit["npe"] = npe2;
}
if (fDebug)
std::cout << "digit " << j << " key: " << ss.str() << std::endl;
kl_digits.push_back(tmpDigit);
} // end loop over pmts
} // ends 'for' loop over hits
return kl_digits;
}
//////////////////////////////////////////////////////////////////////
Json::Value MapCppKLMCDigitizer::check_sanity_mc(const Json::Value& root) const {
// Check if the JSON document has a 'mc' branch, else return error
if (!root.isMember("mc_events")) {
throw MAUS::Exception(Exception::recoverable,
"Missing MC branch from data",
"MapCppKLDigitizer::check_sanity_mc");
}
Json::Value mc = root.get("mc_events", 0);
// Check if JSON document is of the right type, else return error
if (!mc.isArray()) {
throw MAUS::Exception(Exception::recoverable,
"MC branch isn't an array",
"MapCppKLDigitizer::check_sanity_mc");
}
return mc;
}
//////////////////////////////////////////////////////////////////////
int MapCppKLMCDigitizer::calculate_nphe_at_pmt(double dist, double edep) const {
if (fDebug) std::cout << "edep= " << edep << std::endl;
if (!_configJSON.isMember("KLattLengthLong"))
throw(Exception(Exception::recoverable,
"Could not find KLattLengthLong in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLattLengthShort"))
throw(Exception(Exception::recoverable,
"Could not find KLattLengthShort in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLattLengthLongNorm"))
throw(Exception(Exception::recoverable,
"Could not find KLattLengthLongNorm in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLattLengthShortNorm"))
throw(Exception(Exception::recoverable,
"Could not find KLattLengthShortNorm in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLconversionFactor"))
throw(Exception(Exception::recoverable,
"Could not find KLconversionFactor in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLlightCollectionEff"))
throw(Exception(Exception::recoverable,
"Could not find KLlightCollectionEff in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLquantumEff"))
throw(Exception(Exception::recoverable,
"Could not find KLquantumEff in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLlightGuideEff"))
throw(Exception(Exception::recoverable,
"Could not find KLlightGuideEff in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLpmtGain"))
throw(Exception(Exception::recoverable,
"Could not find KLpmtGain in config",
"MapCppKLMCDigitizer::calculate_nphe_at_pmt"));
if (!_configJSON.isMember("KLpmtSigmaGain"))
throw(Exception(Exception::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;
}
//////////////////////////////////////////////////////////////////////
Json::Value MapCppKLMCDigitizer::fill_kl_evt(int evnum,
std::vector all_kl_digits) const {
if (!_configJSON.isMember("Do_V1724_Zero_Suppression"))
throw(Exception(Exception::recoverable,
"Could not find Do_V1724_Zero_Suppression",
"MapCppKLMCDigitizer::fill_kl_evt"));
if (!_configJSON.isMember("V1724_Zero_Suppression_Threshold"))
throw(Exception(Exception::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 (all_kl_digits.size() == 0) return kl_digit;
int npe;
int ndigs = 0;
// throw out multihits and add up the light yields from multihits in slabs
for (unsigned int i = 0; i < all_kl_digits.size(); ++i) {
// check if the digit has been used
if ( all_kl_digits[i]["isUsed"].asInt() == 0 ) {
ndigs++;
npe = all_kl_digits[i]["npe"].asInt();
// loop over all the other digits to find multihit slabs
for ( unsigned int j = i+1; j < all_kl_digits.size(); j++ ) {
if ( check_param(&(all_kl_digits[i]), &(all_kl_digits[j])) ) {
// add up light yields if same bar was hit
npe += all_kl_digits[j]["npe"].asInt();
// mark this hit as used so we don't multicount
all_kl_digits[j]["isUsed"]=1;
} // 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;
Json::Value digit(Json::objectValue);
if (adc > adc_thresh) {
digit["charge_pm"] = adc;
digit["charge_mm"] = adc;
digit["kl_key"] = all_kl_digits[i]["kl_key"].asString();
digit["cell"] = all_kl_digits[i]["cell"].asInt();
digit["pmt"] = all_kl_digits[i]["pmt"].asInt();
// store event number
digit["phys_event_number"] = evnum;
digit["part_event_number"] = evnum;
if (fDebug) std::cout << "adc over thresh: "<< adc << std::endl;
kl_digit.append(digit);
}
if (fDebug)
std::cout << "digit #" << ndigs << " " << digit["kl_key"].asString() << std::endl;
all_kl_digits[i]["isUsed"]=1;
} // ends if-statement
} // ends k-loop
return kl_digit;
}
//////////////////////////////////////////////////////////////////////
bool MapCppKLMCDigitizer::check_param(Json::Value* hit1, Json::Value* hit2) const {
int cell1 = (*hit1)["cell"].asInt();
int cell2 = (*hit2)["cell"].asInt();
int pmt1 = (*hit1)["pmt"].asInt();
int pmt2 = (*hit2)["pmt"].asInt();
int hit_is_used = (*hit2)["isUsed"].asInt();
if ( cell1 == cell2 && pmt1 == pmt2 && !hit_is_used ) {
return true;
} else {
return false;
}
}
//=====================================================================
}