10 #include <utl/config.h>
15 #include <fwk/CentralConfig.h>
16 #include <fwk/CoordinateSystemRegistry.h>
17 #include <fwk/RandomEngineRegistry.h>
19 #include <evt/Event.h>
20 #include <evt/ShowerSimData.h>
22 #include <sevt/SEvent.h>
23 #include <sevt/Station.h>
24 #include <sevt/StationSimData.h>
26 #include <det/Detector.h>
27 #include <sdet/SDetector.h>
28 #include <sdet/Station.h>
29 #include <sdet/Scintillator.h>
31 #include <mdet/MDetector.h>
33 #include <cdet/CDetector.h>
34 #include <cdet/Station.h>
36 #include <utl/ErrorLogger.h>
37 #include <utl/GeometryUtilities.h>
38 #include <utl/MathConstants.h>
39 #include <utl/Particle.h>
40 #include <utl/ParticleCases.h>
41 #include <utl/PhysicalConstants.h>
42 #include <utl/Point.h>
43 #include <utl/RandomEngine.h>
44 #include <utl/TimeStamp.h>
46 #include <utl/Reader.h>
47 #include <utl/TabularStream.h>
48 #include <utl/TabulatedFunction.h>
50 #include <CLHEP/Random/RandFlat.h>
51 #include <CLHEP/Random/RandPoisson.h>
65 using CLHEP::RandFlat;
66 using CLHEP::RandPoisson;
86 # define DUMP(_pStationDistance_, _pStationDistanceXY_, _timeShift_, _injectionZ_) \
89 << Point(0,0,0, dStationCS).GetX(showerCS) << ' ' \
90 << Point(0,0,0, dStationCS).GetY(showerCS) << ' ' \
91 << RadiusXY(corePosition - Point(0,0,0, dStationCS), showerCS) / meter << ' ' \
92 << particle.GetType() << ' ' \
93 << particle.GetKineticEnergy() / GeV << ' ' \
97 << pPosition.GetX(showerCS) << ' ' \
98 << pPosition.GetY(showerCS) << ' ' \
99 << sqrt(exp(pLnR2)) / meter << ' ' \
100 << (_pStationDistance_) / meter << ' ' \
101 << (_pStationDistanceXY_) / meter << ' ' \
102 << (_timeShift_) / nanosecond << ' ' \
103 << (_injectionZ_) / meter \
105 # define DUMP_REJECT DUMP(0, 0, 0, 0)
109 # define DUMP(_pStationDistance_, _pStationDistanceXY_, _timeShift_, _injectionZ_)
115 template<
typename Map,
typename T>
120 const auto sIt = map.find(sId);
121 if (sIt != map.end())
124 map.insert(make_pair(sId,
typename Map::mapped_type(value)));
130 Round(
const double div,
const double val)
132 return round(div * val) / div;
151 const Point& position)
177 topB.
GetChild(
"LimitParticlesPerCycle").
GetData(fLimitParticlesPerCycle);
178 fParticlesPerCycle = maxUI;
179 if (fLimitParticlesPerCycle)
182 topB.
GetChild(
"UseWeightedStationSimulation").
GetData(fUseWeightedStationSimulation);
183 fWeightedStationSimulationParticleLimit = maxUI;
184 if (fUseWeightedStationSimulation) {
185 topB.
GetChild(
"WeightedStationSimulationParticleLimit").
GetData(fWeightedStationSimulationParticleLimit);
186 Branch wtFactorB = topB.
GetChild(
"WeightedStationSimulationThinningFactor");
188 wtFactorB.
GetData(fWeightedStationSimulationThinningFactor);
191 if (fParticlesPerCycle != maxUI && fWeightedStationSimulationParticleLimit != maxUI) {
192 ERROR(
"The <LimitParticlesPerCycle> and <UseWeightedStationSimulation> options are incompatible, "
193 "only one should be enabled.");
204 fOuterRadiusCut = 1e6*
km;
219 algoB.
GetChild(
"HorizontalParticleCosThetaCut").
GetData(fHorizontalParticleCut);
220 fHorizontalParticleCut =
std::abs(fHorizontalParticleCut);
221 algoB.
GetChild(
"UseStationPositionMatrix").
GetData(fUseStationPositionMatrix);
224 algoB.
GetChild(
"LogGaussSmearingWidth").
GetData(fLogGaussSmearingWidth);
225 algoB.
GetChild(
"UseWeightDependentResamplingArea").
GetData(fUseWeightDependentResamplingArea);
227 fMuonWeightScale = 1;
229 if (muonWeightScaleB)
230 muonWeightScaleB.
GetData(fMuonWeightScale);
232 fSimulateUMD =
false;
239 INFO(
"UMD tank+ground G4 simulation has been activated");
240 if (fUseWeightedStationSimulation)
241 WARNING(
"UMD does not handle weighted particles. The LimitParticlesPerCycle option should be used instead.");
245 fSimulateMARTA =
false;
251 INFO(
"MARTA tank+RPC G4 simulation has been activated");
254 fRandomEngine = &RandomEngineRegistry::GetInstance().Get(RandomEngineRegistry::eDetector).GetEngine();
256 if (!fUseStationPositionMatrix)
257 WARNING(
"Not using StationPositionMatrix. "
258 "All stations will be searched for injection.");
260 #ifdef DUMP_PARTICLES
262 new ofstream(
"CachedShowerRegenerator-particle_dump-"
263 "wdr" + to_string(
int(fUseWeightDependentResamplingArea)) +
279 return energy < fPhotonEnergyCut;
281 return energy < fElectronEnergyCut;
283 return energy < fMuonEnergyCut;
285 return energy < fHadronEnergyCut;
287 return energy < fMesonEnergyCut;
299 fShowerData->fWeightCounterMap.clear();
301 if (fLogGaussSmearingWidth)
302 fShowerData->fLogGauss =
305 auto& simShower =
event.GetSimShower();
306 simShower.SetMuonWeightScale(fMuonWeightScale);
308 if (simShower.GetMinRadiusCut() < fInnerRadiusCut) {
310 msg <<
"Setting new value for fMinRadiusCut: " << fInnerRadiusCut;
312 simShower.SetMinRadiusCut(fInnerRadiusCut);
315 fShowerData->fParticleIt = simShower.GroundParticlesBegin();
316 fShowerData->fParticlesEnd = simShower.GroundParticlesEnd();
318 const auto localCS = simShower.GetLocalCoordinateSystem();
319 const double showerZenith = (-simShower.GetDirection()).GetTheta(localCS);
321 const double samplingAreaFactor = fDeltaPhi / cos(showerZenith);
325 auto& sEvent =
event.GetSEvent();
327 const auto showerCS = simShower.GetShowerCoordinateSystem();
328 const auto& corePosition = simShower.GetPosition();
329 const auto& coreTime = simShower.GetTimeStamp();
330 const auto& sDetector = det::Detector::GetInstance().GetSDetector();
336 for (
const auto& sd : sDetector.StationsRange()) {
340 const int sId = sd.GetId();
342 if (!sEvent.HasStation(sId))
343 sEvent.MakeStation(sId);
345 auto& station = sEvent.GetStation(sId);
350 if (!station.HasSimData())
351 station.MakeSimData();
352 auto& sSim = station.GetSimData();
353 if (station.HasScintillator() && !station.GetScintillator().HasSimData())
354 station.GetScintillator().MakeSimData();
356 sSim.SetMaxNParticles(fWeightedStationSimulationParticleLimit);
357 sSim.SetThinningFactor(fWeightedStationSimulationThinningFactor);
359 const auto& sPosition = sd.GetPosition();
363 sSim.SetPlaneFrontTime(planeTime);
365 const double sR = sPosition.GetRho(showerCS);
367 if (sR < fInnerRadiusCut) {
369 sSim.SetIsInsideMinRadius();
373 if (sR > fOuterRadiusCut) {
379 const double r1 =
max(sR*(1 - fDeltaROverR), fInnerRadiusCut);
380 const double r2 = min(sR*(1 + fDeltaROverR), fOuterRadiusCut);
381 const double lnSqrR1 = 2 * log(r1/
meter);
382 const double lnSqrR2 = 2 * log(r2/
meter);
393 const double samplingArea = samplingAreaFactor * (
Sqr(r2) -
Sqr(r1));
395 fShowerData->fStationMatrix.PushBack(sd, station,
396 sPosition.GetPhi(showerCS), fDeltaPhi,
397 sR, lnSqrR1, lnSqrR2,
402 fShowerData->fStationMatrix.CreateMatrix(fUseStationPositionMatrix);
403 fShowerData->fMinSqrR = exp(fShowerData->fStationMatrix.GetMinLnSqrR())*
meter2;
406 info <<
"Out of " << nStations <<
" stations: inner cut " << nInner <<
", outer " << nOuter;
415 ERROR(
"Current event does not have a simulated shower.");
419 const auto& simShower =
event.GetSimShower();
422 if (fShowerData && fShowerData->fParticleIt == fShowerData->fParticlesEnd) {
423 fShowerData =
nullptr;
429 InitNewShower(event);
433 if (fParticlesPerCycle < maxUI) {
435 info <<
"Regenerating batch of " << fParticlesPerCycle <<
" particles.";
439 if (fWeightedStationSimulationParticleLimit < maxUI) {
441 info <<
"Will use weighted simulation for more than " << fWeightedStationSimulationParticleLimit
442 <<
" particles per station.";
446 const auto& sDetector = det::Detector::GetInstance().GetSDetector();
447 auto& sEvent =
event.GetSEvent();
450 for (
const auto& sd : sDetector.StationsRange())
451 sEvent.GetStation(sd.GetId()).GetSimData().ClearParticleList();
453 const auto showerCS = simShower.GetShowerCoordinateSystem();
454 const auto& corePosition = simShower.GetPosition();
455 const auto grParticleCS = simShower.GetLocalCoordinateSystem();
464 unsigned int nParticlesInjected = 0;
466 auto& particleIt = fShowerData->fParticleIt;
467 for (
auto pEnd = fShowerData->fParticlesEnd; particleIt != pEnd; ++particleIt) {
470 if (fParticlesPerCycle && nParticlesInjected >= fParticlesPerCycle) {
472 info << nParticlesInjected <<
" particles processed.";
479 const auto& pPosition =
particle.GetPosition();
480 const double pR2ShowerFrame = pPosition.GetRho2(showerCS);
482 if (pR2ShowerFrame <= fShowerData->fMinSqrR)
488 const double pPhi = pPosition.GetPhi(showerCS);
489 const double pLnSqrR = log(pR2ShowerFrame);
492 fShowerData->fStationMatrix.GetInjectionStations(pPhi, pLnSqrR);
498 const double pTime =
particle.GetTime().GetInterval();
506 const double pPlaneFrontDelay = pTime -
PlaneFrontTime(showerCS, corePosition, pPosition);
507 if (pPlaneFrontDelay < -0.1*
ns)
513 for (
const auto sInfo : sList) {
515 const auto& dStation = sInfo->GetDetStation();
516 const auto stationCS = dStation.GetLocalCoordinateSystem();
517 const unsigned int sId = dStation.GetId();
521 const double pStationCosine = -pDirection.
GetZ(stationCS);
523 if (pStationCosine <= 0) {
524 ++fShowerData->fUpwardSideParticles[sId];
529 const double pPlaneCosine = -pDirection.GetZ(grParticleCS);
531 if (pPlaneCosine < fHorizontalParticleCut) {
532 ++fShowerData->fNHorizontalParticles;
536 const double sThickness = dStation.GetThickness();
537 const double sRadius = dStation.GetRadius() + sThickness;
538 const double sHeight = dStation.GetHeight() + 2*sThickness;
540 double injectionRadius = sRadius;
541 double injectionHeight = sHeight;
544 if (fSimulateMARTA) {
545 const auto& cStation =
546 det::Detector::GetInstance().GetCDetector().GetStation(sId);
548 cStation.GetTankSupportTopSlabDimensions()[2] +
549 cStation.GetTankSupportCentralFootDimensions()[2] +
550 cStation.GetTankSupportCentralFootBaseDimensions()[2];
551 injectionRadius =
max(injectionRadius, fMARTARadius);
554 if (dStation.HasScintillator()) {
556 const double scintRadius = dStation.GetScintillator().GetMaxRadius();
557 const double scintHeight = dStation.GetScintillator().GetMaxHeight();
558 injectionRadius =
max(injectionRadius, scintRadius);
559 injectionHeight =
max(injectionHeight, scintHeight);
562 if (fSimulateUMD && dStation.ExistsAssociatedCounter()) {
563 const auto& mDet = det::Detector::GetInstance().GetMDetector();
564 const auto& dCounter = mDet.GetCounter(dStation.GetAssociatedCounterId());
565 const auto&
mod = *dCounter.ModulesBegin();
566 const auto& modPos =
mod.GetPosition();
567 const double depth = -modPos.GetZ(stationCS);
568 const double pPlaneSine = pDirection.GetRho(grParticleCS);
570 const double umdRadius = fUMDTightRadius + depth * pPlaneSine/pPlaneCosine;
571 injectionRadius = min(fUMDMaxRadius,
max(injectionRadius, umdRadius));
574 const double pStationSine = pDirection.GetRho(stationCS);
577 const double effAreaTop =
kPi *
Sqr(injectionRadius) * pStationCosine;
578 const double effAreaSide = 2 * injectionRadius * injectionHeight * pStationSine;
579 const double effArea = effAreaTop + effAreaSide;
584 const double effResArea = sInfo->GetResamplingArea() * pPlaneCosine;
589 const double avgN = pWeight * effArea / effResArea;
592 if (fUseWeightDependentResamplingArea && avgN < 1) {
594 if (!sInfo->IsInScaledArea(avgN, pPhi, pLnSqrR)) {
604 n = RandPoisson::shoot(fRandomEngine, avgN);
607 auto& station = sInfo->GetEvtStation();
609 for (
unsigned int i = 0; i < n; ++i) {
611 if (RandFlat::shoot(fRandomEngine, 0, effArea) < effAreaTop) {
613 const double r = injectionRadius *
sqrt(RandFlat::shoot(fRandomEngine, 0, 1));
614 const double phi = RandFlat::shoot(fRandomEngine, 0,
kTwoPi);
615 newParticle.
SetPosition(
Point(r, phi, injectionHeight, stationCS, Point::kCylindrical));
616 ++fShowerData->fTopParticles[sId];
619 const double pPhi = pDirection.GetPhi(stationCS) +
kPiOnTwo;
623 const double phi = pPhi + acos(RandFlat::shoot(fRandomEngine, -1, 1));
624 const double z = RandFlat::shoot(fRandomEngine, 0, injectionHeight);
625 newParticle.
SetPosition(
Point(injectionRadius, phi, z, stationCS, Point::kCylindrical));
626 ++fShowerData->fSideParticles[sId];
636 const auto& pShiftedPosition = newParticle.
GetPosition();
637 double pShiftedTime = pTime +
PlaneFrontTime(showerCS, pPosition, pShiftedPosition);
638 if (i && fShowerData->fLogGauss) {
639 const double planeFrontTime =
PlaneFrontTime(showerCS, corePosition, pShiftedPosition);
640 pShiftedTime = fShowerData->fLogGauss->GetSmearedTime(planeFrontTime, pShiftedTime);
642 newParticle.
SetTime(pShiftedTime);
644 InsertValue(fShowerData->fTimeStat, sId, pShiftedTime);
645 station.AddParticle(newParticle);
648 (pPosition -
Point(0,0,0, dStationCS)).GetMag(),
649 RadiusXY(pPosition -
Point(0,0,0, dStationCS), showerCS),
650 pShiftedTime - pTime,
656 nParticlesInjected += n;
663 info << nParticlesInjected <<
" particles processed.";
666 if (fShowerData->fParticleIt == fShowerData->fParticlesEnd)
669 #ifdef DUMP_PARTICLES
670 *gParticleDump <<
"\n\n\n---\n\n\n" << endl;
680 const auto& sList = fShowerData->fStationMatrix.GetStationList();
685 INFO(
"\nStation particle statistics:");
689 <<
"wght" <<
endc <<
"wght" <<
endc <<
"wght" <<
endc <<
"station"
692 <<
"min" <<
endc <<
"avg" <<
endc <<
"max" <<
endc <<
"thinning"
695 for (
const auto&
s : sList) {
697 const unsigned sId =
s.GetDetStation().GetId();
699 if (fShowerData->fTopParticles[sId] ||
700 fShowerData->fSideParticles[sId] ||
701 fShowerData->fUpwardSideParticles[sId]) {
703 const auto& w = *fShowerData->fWeightStat.find(sId);
704 tab << sId <<
' ' <<
endc
705 << setprecision(4) <<
s.GetR()/
km <<
endc
706 <<
' ' << fShowerData->fTopParticles[sId] <<
' ' <<
endc
707 <<
' ' << fShowerData->fSideParticles[sId] <<
' ' <<
endc
708 <<
' ' << fShowerData->fUpwardSideParticles[sId] <<
' ' <<
endc
709 <<
' ' << setprecision(4) << w.second.GetMin() <<
endc
710 <<
' ' << setprecision(4) << w.second.GetAverage() <<
endc
711 <<
' ' << setprecision(4) << w.second.GetMax() <<
endc
712 <<
s.GetEvtStation().GetSimData().GetThinning()
724 cerr <<
"No stations were hit." << endl;
728 INFO(
"\nStation timing statistics:\n"
729 "abs = absolute time difference to core time\n"
730 "rel = relative time to station plane-front arrival");
733 <<
"min rel" <<
endc <<
"max rel" <<
endr
735 <<
"station" <<
endc <<
"[km]" <<
endc <<
"time [ns]" <<
endc <<
"time [ns]" <<
endc
738 const auto& simShower =
event.GetSimShower();
739 const auto& coreTime = simShower.GetTimeStamp();
741 for (
const auto&
s : sList) {
743 const unsigned sId =
s.GetDetStation().GetId();
745 if (fShowerData->fTopParticles[sId] ||
746 fShowerData->fSideParticles[sId] ||
747 fShowerData->fUpwardSideParticles[sId]) {
750 s.GetEvtStation().GetSimData().GetPlaneFrontTime() - coreTime;
752 const auto&
mm = *fShowerData->fTimeStat.find(sId);
753 tab << sId <<
' ' <<
endc
754 << setprecision(4) <<
s.GetR()/
km <<
endc
755 <<
' ' <<
Round(10,
mm.second.GetMin().GetInterval()/
ns) <<
endc
756 <<
' ' <<
Round(10,
mm.second.GetMax().GetInterval()/
ns) <<
endc
757 <<
' ' <<
Round(10, (
mm.second.GetMin() - sTime).GetInterval()/
ns) <<
endc
758 <<
' ' <<
Round(10, (
mm.second.GetMax() - sTime).GetInterval()/
ns) <<
endr;
772 info << fShowerData->fNHorizontalParticles <<
" horizontal particles rejected.";
void SetTime(const TimeInterval &time)
Branch GetTopBranch() const
pointer with built-in initialization, deletion, deep copying
constexpr T Sqr(const T &x)
void OutputStats(evt::Event &event)
void SetPosition(const utl::Point &position)
Describes a particle for Simulation.
bool HasSimShower() const
std::map< std::string, std::string > AttributeMap
VModule::ResultFlag Init()
Initialize: invoked at beginning of run (NOT beginning of event)
#define INFO(message)
Macro for logging informational messages.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
double Round(const double div, const double val)
A TimeStamp holds GPS second and nanosecond for some event.
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
Class representing a document branch.
double GetX(const CoordinateSystemPtr &coordinateSystem) const
double abs(const SVector< n, T > &v)
bool IsParticleEnergyLow(const int type, const double energy) const
constexpr double kPiOnTwo
void SetWeight(const double weight)
class to format data in tabular form
#define WARNING(message)
Macro for logging warning messages.
void GetData(bool &b) const
Overloads of the GetData member template function.
void InsertValue(Map &map, const int sId, const T &value)
bool IsMuonic(const Particle &p)
double GetY(const CoordinateSystemPtr &coordinateSystem) const
constexpr double kSpeedOfLight
A TimeInterval is used to represent time elapsed between two events.
ResultFlag
Flag returned by module methods to the RunController.
Regenerate thinned MC showers.
Source GetSource() const
Source of the particle (eg. shower or background)
struct particle_info particle[80]
const Point & GetPosition() const
Position of the particle.
#define OFFLINE_ELECTRONS
VModule::ResultFlag Run(evt::Event &event)
Run: invoked once per event.
double GetZ(const CoordinateSystemPtr &coordinateSystem) const
void InitNewShower(evt::Event &event)
double mod(const double d, const double periode)
#define ERROR(message)
Macro for logging error messages.
const Vector & GetDirection() const
Unit vector giving particle direction.
#define DUMP(_pStationDistance_, _pStationDistanceXY_, _timeShift_, _injectionZ_)
double PlaneFrontTime(const CoordinateSystemPtr &showerCS, const Point &corePosition, const Point &position)
Calculate time of arrival of the plane front at point position x.