/* 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 "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::Exception(Exception::recoverable, "Failed to parse Json Configuration", "MapCppTOFMCDigitizer::_birth"); } // get the geometry if (!_configJSON.isMember("reconstruction_geometry_filename")) throw(Exception(Exception::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"); _stationKeys.push_back("tof0"); _stationKeys.push_back("tof1"); _stationKeys.push_back("tof2"); // load the TOF calibration map bool loaded = _map.InitializeFromCards(_configJSON, 0); if (!loaded) throw(Exception(Exception::recoverable, "Could not find TOF calibration map", "MapCppTOFMCDigitizer::birth")); } ////////////////////////////////////////////////////////////////////// void MapCppTOFMCDigitizer::_death() { } ////////////////////////////////////////////////////////////////////// void MapCppTOFMCDigitizer::_process(Json::Value* document) const { Json::Value& root = *document; // check sanity of json input file and mc brach Json::Value mc = check_sanity_mc(*document); // all_tof_digits store all the tof hits // then we'll weed out multiple hits, add up yields, and store the event std::vector all_tof_digits; all_tof_digits.push_back(Json::Value()); Json::Value tof_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]; double gentime = particle["primary"]["time"].asDouble(); if (fDebug) std::cout << "evt: " << i << " pcle= " << particle << std::endl; // hits for this event Json::Value _hits = particle["tof_hits"]; // if _hits.size() = 0, the make_digits and fill_tof_evt functions will // return null. The TOF recon expects something - either null or data, can't just skip all_tof_digits.clear(); // pick out tof hits, digitize and store all_tof_digits = make_tof_digits(_hits, gentime, *document); // go through tof hits and find the hit in the trig station (TOF1) // if (all_tof_digits.size() != 0) findTriggerPixel(all_tof_digits); std::string strig = findTriggerPixel(all_tof_digits); // 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..} // loop over tof stations for (int snum = 0; snum < 3; ++snum) { tof_evt.clear(); tof_evt[i] = fill_tof_evt(i, snum, all_tof_digits, strig); if (fDebug) { std::cout << "mcevt: " << i << " tof" << snum << " " << _hits.size() << " hits, " << all_tof_digits.size() << std::endl; } Json::Value tof_digs = fill_tof_evt(i, snum, all_tof_digits, strig); root["recon_events"][i]["tof_event"]["tof_digits"][_stationKeys[snum]] = tof_digs; } // end loop over stations } // end loop over events } ////////////////////////////////////////////////////////////////////// std::vector MapCppTOFMCDigitizer::make_tof_digits( Json::Value hits, double gentime, Json::Value& root) const { std::vector tof_digits; tof_digits.clear(); if (hits.size() == 0) return tof_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; if (fDebug) std::cout << "=================== hit# " << j << std::endl; // make sure we can get the station/slab info if (!hit.isMember("channel_id")) throw(Exception(Exception::recoverable, "No channel_id in hit", "MapCppTOFMCDigitizer::make_tof_digits")); if (!hit.isMember("momentum")) throw(Exception(Exception::recoverable, "No momentum in hit", "MapCppTOFMCDigitizer::make_tof_digits")); if (!hit.isMember("time")) throw(Exception(Exception::recoverable, "No time in hit", "MapCppTOFMCDigitizer::make_tof_digits")); Json::Value channel_id = hit["channel_id"]; if (!hit.isMember("energy_deposited")) throw(Exception(Exception::recoverable, "No energy_deposited in hit", "MapCppTOFMCDigitizer::make_tof_digits")); double edep = hit["energy_deposited"].asDouble(); if (fDebug) { std::cout << "tofhit: " << hit["channel_id"] << " " << hit["position"] << " " << hit["momentum"] << " " << hit["time"] << " " << hit["energy_deposited"] << std::endl; } int stn = hit["channel_id"]["station_number"].asInt(); int pln = hit["channel_id"]["plane"].asInt(); int slb = hit["channel_id"]["slab"].asInt(); // 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(Exception(Exception::recoverable, "No TOF module for hit", "MapCppTOFMCDigitizer::make_tof_digits")); // now get the position of the hit Hep3Vector hpos = JsonWrapper::JsonToThreeVector(hit["position"]); // 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(); // 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["time"].asDouble() - gentime; // propagate time to pmt & smear by the resolution double time1 = CLHEP::RandGauss::shoot((htime + dist1/csp) , tres); double time2 = CLHEP::RandGauss::shoot((htime + dist2/csp) , tres); double tdc2time = _configJSON["TOFtdcConversionFactor"].asDouble(); // convert to tdc int tdc1 = static_cast(time1 / tdc2time); int tdc2 = static_cast(time2 / tdc2time); if (fDebug) { std::cout << "time: " << hit["time"].asDouble() << " now: " << time1 << " " << time2 << " " << (time1+time2)/2 << std::endl; std::cout << "dist: " << dist1 << " " << dist2 << std::endl; std::cout << "tdc: " << tdc1 << " " << tdc2 << 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 << "TOFChannelKey " << stn << " " << pln << " " << slb << " " << p << " tof" << stn; tmpDigit["tof_key"] = ss.str(); tmpDigit["station"] = stn; tmpDigit["plane"] = pln; tmpDigit["slab"] = slb; tmpDigit["pmt"] = p; if (p == 0) { tmpDigit["npe"] = npe1; tmpDigit["leading_time"] = tdc1; tmpDigit["raw_time"] = time1; } else if (p == 1) { tmpDigit["npe"] = npe2; tmpDigit["leading_time"] = tdc2; tmpDigit["raw_time"] = 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 tmpDigit["trailing_time"] = 0; tmpDigit["trigger_request_leading_time"] = 0; tmpDigit["trigger_time_tag"] = 0; // can't put a time_t into json??? // std::time_t tstamp = std::time(0); tmpDigit["time_stamp"] = 0; if (fDebug) std::cout << "digit " << j << " key: " << ss.str() << std::endl; tof_digits.push_back(tmpDigit); } // end loop over pmts } // ends 'for' loop over hits return tof_digits; } ////////////////////////////////////////////////////////////////////// Json::Value MapCppTOFMCDigitizer::check_sanity_mc(Json::Value& root) const { // Check if the JSON document can be parsed, else return error only // Check if the JSON document has a 'mc' branch, else return error if (!root.isMember("mc_events")) { throw MAUS::Exception(Exception::recoverable, "Could not find an MC branch to simulate.", "MapCppTOFMCDigitizer::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 was not an array.", "MapCppTOFMCDigitizer::check_sanity_mc"); } return mc; } ////////////////////////////////////////////////////////////////////// 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(Exception(Exception::recoverable, "Could not find TOFattenauationLength in config", "MapCppTOFMCDigitizer::get_npe")); if (!_configJSON.isMember("reconstruction_geometry_filename")) throw(Exception(Exception::recoverable, "Could not find TOFpmtQuantumEfficiency in config", "MapCppTOFMCDigitizer::get_npe")); if (!_configJSON.isMember("TOFconversionFactor")) throw(Exception(Exception::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(std::vector all_tof_digits) const { int tstn = 1; int tsx = 99, tsy = 99; for (unsigned int digit_i = 0; digit_i < all_tof_digits.size(); digit_i++) { if (all_tof_digits[digit_i]["station"] == tstn) { if (tsx == 99 || tsy == 99) { if (all_tof_digits[digit_i]["plane"].asInt() == 0) tsx = all_tof_digits[digit_i]["slab"].asInt(); else tsy = all_tof_digits[digit_i]["slab"].asInt(); } } } 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(); } ////////////////////////////////////////////////////////////////////// Json::Value MapCppTOFMCDigitizer::fill_tof_evt(int evnum, int snum, std::vector all_tof_digits, std::string pixStr) const { Json::Value tof_digit(Json::arrayValue); // return null if this evt had no tof hits if (all_tof_digits.size() == 0) return tof_digit; double npe; int ndigs = 0; // throw out multihits and add up the light yields from multihits in slabs for (unsigned int i = 0; i < all_tof_digits.size(); ++i) { // check that this digit belongs to the station we are trying to fill if (all_tof_digits[i]["station"].asInt() != snum) continue; // check if the digit has been used if ( all_tof_digits[i]["isUsed"].asInt() == 0 ) { ndigs++; npe = all_tof_digits[i]["npe"].asDouble(); // loop over all the other digits to find multihit slabs for ( unsigned int j = i+1; j < all_tof_digits.size(); j++ ) { if ( check_param(&(all_tof_digits[i]), &(all_tof_digits[j])) ) { // add up light yields if same bar was hit npe += all_tof_digits[j]["npe"].asDouble(); // mark this hit as used so we don't multicount all_tof_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["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; // ROGERS = changed from "charge" to "charge_pm" for data integrity Json::Value digit; digit["charge_pm"] = adc; // NOTE: needs tweaking/verifying -- DR 3/15 digit["charge_mm"] = adc; digit["tof_key"] = all_tof_digits[i]["tof_key"].asString(); digit["station"] = all_tof_digits[i]["station"].asInt(); digit["plane"] = all_tof_digits[i]["plane"].asInt(); digit["slab"] = all_tof_digits[i]["slab"].asInt(); digit["pmt"] = all_tof_digits[i]["pmt"].asInt(); // add the calibration corrections to the time std::string keyStr = all_tof_digits[i]["tof_key"].asString(); TOFChannelKey xChannelKey(keyStr); double dT = 0.; if (pixStr != "unknown") { 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 = all_tof_digits[i]["raw_time"].asDouble(); double rt = raw + dT; // convert to tdc int tdc = static_cast(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 digit["leading_time"] = tdc; digit["trigger_request_leading_time"] = all_tof_digits[i]["trigger_request_leading_time"].asInt(); // ROGERS addition to maintain data integrity 03-May-2012 // - trigger_leading_time // - trigger_trailing_time // - trigger_request_trailing_timr digit["trigger_leading_time"] = 0; digit["trigger_trailing_time"] = 0; digit["trigger_request_trailing_time"] = 0; digit["trailing_time"] = 0; digit["time_stamp"] = 0; digit["trigger_time_tag"] = 0; // store event number digit["phys_event_number"] = evnum; digit["part_event_number"] = evnum; tof_digit.append(digit); if (fDebug) std::cout << "digit #" << ndigs << " " << digit["tof_key"].asString() << std::endl; all_tof_digits[i]["isUsed"]=1; } // ends if-statement } // ends k-loop return tof_digit; } ////////////////////////////////////////////////////////////////////// bool MapCppTOFMCDigitizer::check_param(Json::Value* hit1, Json::Value* hit2) const { int station1 = (*hit1)["station"].asInt(); int station2 = (*hit2)["station"].asInt(); int plane1 = (*hit1)["plane"].asInt(); int plane2 = (*hit2)["plane"].asInt(); int slab1 = (*hit1)["slab"].asInt(); int slab2 = (*hit2)["slab"].asInt(); int pmt1 = (*hit1)["pmt"].asInt(); int pmt2 = (*hit2)["pmt"].asInt(); int hit_is_used = (*hit2)["isUsed"].asInt(); if ( station1 == station2 && plane1 == plane2 && slab1 == slab2 && pmt1 == pmt2 && !hit_is_used ) { return true; } else { return false; } } //===================================================================== }