2 #include <sevt/SEvent.h>
4 #include <sevt/PMTSimData.h>
5 #include <utl/ErrorLogger.h>
6 #include <utl/RandomEngine.h>
7 #include <fwk/CentralConfig.h>
8 #include <fwk/RandomEngineRegistry.h>
9 #include <utl/Reader.h>
11 #include <utl/SaveCurrentTDirectory.h>
12 #include <utl/config.h>
14 #include <sdet/SDetector.h>
15 #include <sdet/Station.h>
17 #include <sdet/PMTConstants.h>
21 using namespace SdPMTSimulatorOG;
27 using CLHEP::RandGeneral;
33 auto topB = CentralConfig::GetInstance()->GetTopBranch(
"SdPMTSimulator");
34 vector<int> components;
35 if (topB.GetChild(
"fundamentalComponents"))
36 topB.GetChild(
"fundamentalComponents").GetData(components);
37 for (
const auto c : components) {
42 err <<
"Invalid signal component: " <<
c;
47 if (fFundamentalComponents.empty())
48 INFO(
"Will not use any fundamental components.");
51 info <<
"Will use " << fFundamentalComponents.size() <<
" fundamental components:";
52 for (
const auto c : fFundamentalComponents)
57 auto elecB = topB.GetChild(
"electronicsParameters");
59 elecB.GetChild(
"kFactor").GetData(fKFactor);
61 fRandomEngine = &RandomEngineRegistry::GetInstance().Get(RandomEngineRegistry::eDetector);
66 auto satB = elecB.GetChild(
"saturationCurve", useAtt);
68 auto vIn = satB.GetChild(
"inputVoltage").Get<vector<double>>();
69 auto vOut = satB.GetChild(
"outputVoltage").Get<vector<double>>();
70 satB.GetChild(
"Current2VoltageMultiplier").GetData(fCurrent2VoltageMultiplier);
71 if (vIn.size() != vOut.size()) {
72 ERROR(
"Lists of input/output voltages for saturation curve must have same size");
76 info <<
"saturation curve [V]: ";
77 for (
unsigned int i = 0, n = vIn.size(); i < n; ++i)
78 info << vIn[i] /
volt <<
" -> " << vOut[i] /
volt <<
" ";
81 const double v2c = 1 / fCurrent2VoltageMultiplier;
82 for (
unsigned int i = 0, n = vIn.size(); i < n; ++i) {
89 INFO(
"No PMT saturation simulation");
112 const double start = rawPulse.
XFront();
113 const double stop = rawPulse.
XBack();
116 for (
unsigned int i = 1; i < n; ++i)
117 minTimeDifference(rawPulse[i].X() - rawPulse[i-1].X());
119 const double timeBinning = minTimeDifference.
GetMin() / 10;
122 for (
double t = start; t < stop; t += timeBinning)
125 const double norm = sum * timeBinning;
129 fMeanPulseShape.Clear();
130 for (
const auto& xy : rawPulse)
131 fMeanPulseShape.PushBack(xy.X() - start, xy.Y() /
norm);
135 double currentMax = 0;
136 for (
const auto& th : fMeanPulseShape) {
137 const double h = th.Y();
138 if (h > currentMax) {
140 fPulseMaxTime = th.X();
156 const double pulseStopTime = fMeanPulseShape.XBack();
157 vector<double> meanPulseShape;
158 meanPulseShape.reserve(
int(pulseStopTime / dt) + 1);
159 double meanPulseShapeNorm = 0;
161 for (
double time = 0; time < pulseStopTime; time += dt) {
162 const double pulse = fMeanPulseShape.Y(time);
163 meanPulseShapeNorm += pulse;
164 meanPulseShape.push_back(pulse);
167 const double normGain = fPMTGain / meanPulseShapeNorm;
168 const double scaleFactorMin = normGain * fMinCharge;
173 const double scaleFactorMax = normGain * fMaxCharge * scaling;
175 for (
const auto& tn : pe.SparseRange()) {
178 for (
int i = 0, n = tn.get<1>(); i < n; ++i)
179 charge += fChargeDistribution->fire();
181 const double realCharge = scaleFactorMin + scaleFactorMax * charge;
184 const double offset = tn.get<0>() * dt - fPulseMaxTime;
188 for (
double time = 0; time <= pulseStopTime; time += dt) {
191 base.
AddTime(time + offset, realCharge * meanPulseShape[++i]);
202 for (
const auto& ts : baseSignal.SparseRange()) {
203 const auto t = ts.get<0>();
204 auto& sig = baseSignal[t];
205 const double tot = totalBaseSignal.
At(t);
206 const double maxDist =
max(sig, tot);
207 const double scaling = (maxDist > 0) ? fSaturationFunction->Y(maxDist) / maxDist : 1;
217 ERROR(
"Event does not have an SEvent");
221 const bool haveFund = !fFundamentalComponents.empty();
223 auto& sEvent =
event.GetSEvent();
225 for (
auto& station : sEvent.StationsRange()) {
227 if (!station.HasSimData())
230 auto& sSim = station.GetSimData();
232 const auto& simulatorSignature = sSim.GetSimulatorSignature();
239 const double kFactor =
240 (simulatorSignature ==
"G4StationSimulatorFullTrackOG") ? 1 : fKFactor;
242 const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
244 const bool isUUB = dStation.IsUUB();
247 const double scaling =
248 (sSim.GetThinning() == 1) ?
249 1 :
double(sSim.GetTotalSimCandidateParticleCount()) / sSim.GetTotalSimParticleCount();
253 if (!pmt.HasSimData())
256 auto& pSim = pmt.GetSimData();
257 if (!pSim.HasPETimeDistribution() || pSim.HasBaseSignal())
261 const auto& dPMT = dStation.GetPMT(pmt.GetId());
263 fPMTGain = dPMT.GetWorkingGain();
268 const auto& rawPulse = dPMT.GetPulseShape();
269 const auto& rawChargeProbDist = dPMT.GetChargeProbabilityDistribution();
270 fMinCharge = dPMT.GetMinCharge();
271 fMaxCharge = dPMT.GetMaxCharge();
274 FillPulseShape(rawPulse);
275 delete fChargeDistribution;
276 fChargeDistribution =
277 new RandGeneral(fRandomEngine->GetEngine(), &rawChargeProbDist.front(), rawChargeProbDist.size());
279 pSim.MakeBaseSignal();
280 auto& totalSignal = pSim.GetBaseSignal();
282 for (
const auto peComp : pSim.PETimeDistributionsRange()) {
290 if (!pSim.HasBaseSignal(comp))
291 pSim.MakeBaseSignal(comp);
292 auto& base = pSim.GetBaseSignal(comp);
294 ConvertPEToBaseSignal(scaling, peComp.GetTimeDistribution(), base);
296 if (haveFund && fFundamentalComponents.count(comp))
305 totalNoSaturationSignal = totalSignal;
307 if (fSaturationFunction)
308 SimulateSaturation(totalSignal, totalNoSaturationSignal);
312 sSim.SetUsedWeight(scaling);
323 delete fChargeDistribution;
324 fChargeDistribution =
nullptr;
325 delete fSaturationFunction;
326 fSaturationFunction =
nullptr;
T At(const double time) const
unsigned int GetNPoints() const
ArrayConstReference XBack() const
read-only reference to back of array of X
fwk::VModule::ResultFlag Finish() override
Finish: invoked at end of the run (NOT end of the event)
Base class for exceptions arising because configuration data are not valid.
Class to hold collection (x,y) points and provide interpolation between them.
std::map< std::string, std::string > AttributeMap
Histogram class for time distributions with suppressed empty bins.
#define INFO(message)
Macro for logging informational messages.
std::string GetSignalComponentName(const SignalComponent comp)
double GetBinning() const
Size of one slot.
total FADC trace, with no saturation applied by FADC/baseline simulator
ArrayConstReference XFront() const
read-only reference to front of array of X
fwk::VModule::ResultFlag Init() override
Initialize: invoked at beginning of run (NOT beginning of event)
fwk::VModule::ResultFlag Run(evt::Event &event) override
Run: invoked once per event.
void ConvertPEToBaseSignal(const double scaling, const utl::TimeDistributionI &pe, utl::TimeDistributionD &baseSignal)
void FillPulseShape(const utl::TabulatedFunction &rawPulse)
ResultFlag
Flag returned by module methods to the RunController.
double InterpolateY(const double x, const unsigned int polyDegree) const
Interpolate the Y value with a polyDegree polynomial.
total (shower and background)
#define ERROR(message)
Macro for logging error messages.
void AddTime(const double time, const T weight=T(1))
Add an entry (optionally weighted) for the given time. Slot will be computed.
void SimulateSaturation(utl::TimeDistributionD &baseSignal, const utl::TimeDistributionD &totalBaseSignal)