diff --git a/Control/CalypsoExample/Generation/python/faser_parser.py b/Control/CalypsoExample/Generation/python/faser_parser.py index 4ac399e08ce06c47fc792e9f32d0c45806cabafc..7af8f3d219ce0095d580f92d98ddd9b1a52c3478 100644 --- a/Control/CalypsoExample/Generation/python/faser_parser.py +++ b/Control/CalypsoExample/Generation/python/faser_parser.py @@ -75,6 +75,8 @@ def faser_pgparser(): help="Write out full configuration") parser.add_argument("--noexec", action='store_true', help="Exit after parsing configuration (no execution)") + parser.add_argument('--filter',default="",type=str, + help='Specify "type" of filter, e.g. "muon_conversion" ,etc') pg_args = parser.parse_args() diff --git a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py index bf3cd11cb1e31e182b48372dc476c3766a2deee5..6a3c34308187d4407f3f01d63f88897a88006716 100755 --- a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py +++ b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py @@ -265,6 +265,16 @@ if __name__ == '__main__': # from G4FaserAlg.G4FaserAlgConfigNew import G4FaserAlgCfg cfg.merge(G4FaserAlgCfg(ConfigFlags)) + + +#Event Filtering? + if args.filter == 'muon_conversion': + import McParticleEvent.Pythonizations + from GeneratorUtils.MuonFilters import ConversionFilter + filt = ConversionFilter("MuonFilter") # , muonEnergyLoss = 5000, minConversionEnergy = 2000) # , muonEnergyLoss = 5000, maxRadius = 100) + cfg.addEventAlgo(filt, sequenceName = "AthAlgSeq") + OutputStreamHITS = cfg.getEventAlgo("OutputStreamHITS") + OutputStreamHITS.RequireAlgs = ["MuonFilter"] # # Dump config # diff --git a/Generators/GeneratorUtils/python/MuonFilters.py b/Generators/GeneratorUtils/python/MuonFilters.py new file mode 100644 index 0000000000000000000000000000000000000000..2540d3905ada00ccba8ffacf2f3f56e43d4b8248 --- /dev/null +++ b/Generators/GeneratorUtils/python/MuonFilters.py @@ -0,0 +1,132 @@ +from AthenaCommon.AppMgr import ServiceMgr as svcMgr +from AthenaPython import PyAthena +from AthenaPython.PyAthena import StatusCode, McEventCollection, CLHEP +from AthenaCommon.SystemOfUnits import cm, m, GeV +import ROOT +from math import sqrt + +try: + from AthenaPython.PyAthena import HepMC3 as HepMC +except ImportError: + from AthenaPython.PyAthena import HepMC as HepMC + +class ConversionFilter(PyAthena.Alg): + def __init__(self, name="SimFilter", InputMCEventKey="TruthEvent", minConversionEnergy = None, + maxRadius = None, muonEnergyLoss = None): + + super(ConversionFilter,self).__init__(name=name) + + self.InputMCEventKey = InputMCEventKey + self.minConversionEnergy = minConversionEnergy + self.maxRadius = maxRadius + self.muonEnergyLoss = muonEnergyLoss + + self.zEmulsionEnd = -1.6 * m + + return + + def findConversionFromMuon(self, evt): + + for i, p in enumerate(evt.particles): + + # Check incoming particle (muon) is within radius to + # avoid those conversions from muons coming in to magnet + if i == 1 and self.maxRadius is not None: + pvtx = p.production_vertex() + if pvtx: + ppos = pvtx.position() + if sqrt(ppos.x()**2 + ppos.y()**2) > self.maxRadius: + return False + + # Find photon + if not abs(p.pdg_id()) == 22: continue + + # Decaying to 2 children + ovtx = p.end_vertex() + if not ovtx: continue + if ovtx.particles_out_size() != 2: continue + + # That are e+e- pair + children = list(ovtx.particles_out) + if not (abs(children[0].pdg_id()) == 11 and children[0].pdg_id() == -children[1].pdg_id()): continue + self.msg.debug("Found Photon conversion") + + # Which comes from a single mother + ivtx = p.production_vertex() + if not ivtx: continue + if ivtx.particles_in_size() != 1: continue + mother = list(ivtx.particles_in)[0] + + # That is a muon (allowing for another photon inbetween) + isFromMuon = False + if abs(mother.pdg_id()) == 13: + isFromMuon = True + elif abs(mother.pdg_id()) == 22: + ivtx = mother.production_vertex() + if ivtx and ivtx.particles_in_size() == 1: + gran = list(ivtx.particles_in)[0] + if abs(gran.pdg_id()) == 13: + isFromMuon = True + + if isFromMuon: + # Optionally require a certain energy for at least one of the e+e- pair + if (self.minConversionEnergy is None + or children[0].momentum().e() > self.minConversionEnergy + or children[1].momentum().e() > self.minConversionEnergy): + + self.msg.debug("... from muon") + self.setFilterPassed(True) + return True + + return False + + def findLargeELossMuon(self, evt): + + lastMuonE = None + firstMuonE = None + + for p in evt.particles: + + # Find muons + if abs(p.pdg_id()) != 13: continue + + # With a vertex inside the specified radius + pvtx = p.production_vertex() + if not pvtx: continue + ppos = pvtx.position() + + if self.maxRadius is not None and sqrt(ppos.x()**2 + ppos.y()**2) > self.maxRadius: + continue + + # Find the final muon produced in the emulsion + if ppos.z() < self.zEmulsionEnd: + firstMuonE = p.momentum().e() + + # Find the final muon + lastMuonE = p.momentum().e() + + # Look for E loss between end of emulsion and final + if lastMuonE is not None and firstMuonE is not None: + if firstMuonE - lastMuonE > self.muonEnergyLoss: + self.msg.debug(f"Found muon with E loss = {firstMuonE - lastMuonE}") + self.setFilterPassed(True) + return True + + return False + + + + def execute(self): + self.msg.debug(f"Executing {self.getName()}") + + self.msg.debug(f"Reading {self.InputMCEventKey}") + evt = self.evtStore[self.InputMCEventKey][0] + + self.setFilterPassed(False) + + if self.muonEnergyLoss: + self.findLargeELossMuon(evt) + else: + self.findConversionFromMuon(evt) + + return StatusCode.Success diff --git a/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py b/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py index 79200c089a8c439300df6937ff62d4dee6e144f3..a88fd42c68121ffea75f92c551ee466125359935 100755 --- a/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py +++ b/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py @@ -61,6 +61,12 @@ parser.add_argument("--NoTrackFilt", action='store_true', parser.add_argument("--no_stable", action='store_true', help="Don't apply stable beam requirement (default: do)") +parser.add_argument("--randomTrigFilt", action='store_true', + help="Only store events that have a random trigger Events") + +parser.add_argument("--randomOnlyTrigFilt", action='store_true', + help="Only store events that only have a random trigger Events") + parser.add_argument("--unblind", action='store_true', help="Don't apply signal blinding (default: do)") parser.add_argument("--onlyblind", action='store_true', @@ -204,7 +210,7 @@ else: # Print out what we found if len(filelist) == 0: - printf("Found no files for {args.path}!") + print("Found no files for {args.path}!") sys.exit(1) elif len(filelist) == 1: print("Processing file:") @@ -228,6 +234,8 @@ print(f"OnlyBlinded = {args.onlyblind}") print(f"Stable Beams = {not args.no_stable}") print(f"Backward = {args.backward}") print(f"GRL = {args.grl}") +print(f"Random Filter = {args.randomTrigFilt}") +print(f"Random Only Filter = {args.randomOnlyTrigFilt}") # OK, lets run the job here from AthenaCommon.Logging import log, logging @@ -317,7 +325,7 @@ if args.isMC: acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, **mc_kwargs)) else: - acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, DoBlinding=(not args.unblind), OnlyBlinded=args.onlyblind, DoScintFilter = args.scintFilt, DoTrackFilter = (not args.NoTrackFilt), DoTrigFilter = args.trigFilt, StableOnly = (not args.no_stable), **grl_kwargs) ) + acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, DoBlinding=(not args.unblind), OnlyBlinded=args.onlyblind, DoScintFilter = args.scintFilt, DoTrackFilter = (not args.NoTrackFilt), DoTrigFilter = args.trigFilt, StableOnly = (not args.no_stable),DoRandomFilter = args.randomTrigFilt, DoRandomOnlyFilter=args.randomOnlyTrigFilt ,**grl_kwargs)) if not args.verbose: from AthenaConfiguration.ComponentFactory import CompFactory diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx index c751d105649810a1b0db80f7b5fadc8d8bf624d6..2842156823b2d6e33d50ac5fd1faa58fb3082b18 100644 --- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx +++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx @@ -200,6 +200,8 @@ StatusCode NtupleDumperAlg::initialize() ATH_CHECK(m_trackCollection.initialize()); ATH_CHECK(m_trackCollectionWithoutIFT.initialize()); ATH_CHECK(m_trackSegmentCollection.initialize()); + ATH_CHECK(m_trackTrueSegmentCollection.initialize()); + ATH_CHECK(m_vetoNuContainer.initialize()); ATH_CHECK(m_vetoContainer.initialize()); ATH_CHECK(m_triggerContainer.initialize()); @@ -323,6 +325,16 @@ StatusCode NtupleDumperAlg::initialize() m_tree->Branch("TrackSegment_py", &m_trackseg_py); m_tree->Branch("TrackSegment_pz", &m_trackseg_pz); + addBranch("TrueTrackSegments",&m_nTruetracksegs); + m_tree->Branch("TrueTrackSegment_Chi2", &m_Truetrackseg_Chi2); + m_tree->Branch("TrueTrackSegment_nDoF", &m_Truetrackseg_DoF); + m_tree->Branch("TrueTrackSegment_x", &m_Truetrackseg_x); + m_tree->Branch("TrueTrackSegment_y", &m_Truetrackseg_y); + m_tree->Branch("TrueTrackSegment_z", &m_Truetrackseg_z); + m_tree->Branch("TrueTrackSegment_px", &m_Truetrackseg_px); + m_tree->Branch("TrueTrackSegment_py", &m_Truetrackseg_py); + m_tree->Branch("TrueTrackSegment_pz", &m_Truetrackseg_pz); + //TrackCollection m_tree->Branch("Track_PropagationError", &m_propagationError, "Track_PropagationError/I"); m_tree->Branch("longTracks", &m_longTracks, "longTracks/I"); @@ -731,6 +743,25 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const return StatusCode::SUCCESS; } } + if (m_doRandomFilter) { + bool trig_random = m_tap & 16; + if ( !(trig_random)) { + // don't process events that fail to activate random trigger + ATH_MSG_DEBUG("event did not pass random trigger filter"); + return StatusCode::SUCCESS; + } + + } + + if (m_doRandomOnlyFilter) { + bool trig_random = m_tap == 16; + if ( !(trig_random)) { + // don't process events that fail to activate random only trigger + ATH_MSG_DEBUG("event did not pass random trigger filter"); + return StatusCode::SUCCESS; + } + + } if (m_doScintFilter) { // filter events, but use waveform peak cuts instead of triggers, as triggers could miss signals slightly out of time @@ -1505,6 +1536,26 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const m_trackseg_pz.push_back(SegMomentum.z()); } + SG::ReadHandle<TrackCollection> trackTrueSegmentCollection {m_trackTrueSegmentCollection, ctx}; + ATH_CHECK(trackTrueSegmentCollection.isValid()); + for (const Trk::Track* trackSeg : *trackTrueSegmentCollection) { + if (trackSeg == nullptr) continue; + m_nTruetracksegs += 1; + m_Truetrackseg_Chi2.push_back(trackSeg->fitQuality()->chiSquared()); + m_Truetrackseg_DoF.push_back(trackSeg->fitQuality()->numberDoF()); + auto SegParameters = trackSeg->trackParameters()->front(); + const Amg::Vector3D SegPosition = SegParameters->position(); + const Amg::Vector3D SegMomentum = SegParameters->momentum(); + m_Truetrackseg_x.push_back(SegPosition.x()); + m_Truetrackseg_y.push_back(SegPosition.y()); + m_Truetrackseg_z.push_back(SegPosition.z()); + m_Truetrackseg_px.push_back(SegMomentum.x()); + m_Truetrackseg_py.push_back(SegMomentum.y()); + m_Truetrackseg_pz.push_back(SegMomentum.z()); + } + + + // finished processing event, now fill ntuple tree m_tree->Fill(); m_eventsPassed += 1; @@ -1602,6 +1653,16 @@ NtupleDumperAlg::clearTree() const m_trackseg_py.clear(); m_trackseg_pz.clear(); + m_nTruetracksegs = 0; + m_Truetrackseg_Chi2.clear(); + m_Truetrackseg_DoF.clear(); + m_Truetrackseg_x.clear(); + m_Truetrackseg_y.clear(); + m_Truetrackseg_z.clear(); + m_Truetrackseg_px.clear(); + m_Truetrackseg_py.clear(); + m_Truetrackseg_pz.clear(); + m_xup.clear(); m_yup.clear(); m_zup.clear(); diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h index 36729773eec08aa8674dfebb43bec17953731c92..c5051ae797e6eee967abf9de69394b88f86d6650 100644 --- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h +++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h @@ -83,6 +83,8 @@ private: SG::ReadHandleKey<TrackCollection> m_trackCollection { this, "TrackCollection", "CKFTrackCollection", "Input track collection name" }; SG::ReadHandleKey<TrackCollection> m_trackCollectionWithoutIFT { this, "TrackCollectionWithoutIFT", "CKFTrackCollectionWithoutIFT", "Input track collection name (without IFT)" }; SG::ReadHandleKey<TrackCollection> m_trackSegmentCollection {this, "TrackSegmentCollection", "SegmentFit", "Input track segment collection name"}; + SG::ReadHandleKey<TrackCollection> m_trackTrueSegmentCollection {this, "TrackTrueSegmentCollection", "Segments", "Input true track segment collection name"}; + SG::ReadHandleKey<xAOD::WaveformHitContainer> m_vetoNuContainer { this, "VetoNuContainer", "VetoNuWaveformHits", "VetoNu hit container name" }; SG::ReadHandleKey<xAOD::WaveformHitContainer> m_vetoContainer { this, "VetoContainer", "VetoWaveformHits", "Veto hit container name" }; @@ -123,6 +125,9 @@ private: BooleanProperty m_doScintFilter { this, "DoScintFilter", false, "Only events that pass scintillator coincidence cuts are passed" }; BooleanProperty m_doTrackFilter { this, "DoTrackFilter", true, "Only events that have >= 1 long track are passed, also non-colliding events with a track or calo signal are passed (default)" }; BooleanProperty m_stableOnly { this, "StableOnly", true, "Only events recorded during stable beams are saved (default)" }; + + BooleanProperty m_doRandomFilter { this, "DoRandomFilter", false, "Only events also recorded with random triggers are stored." }; + BooleanProperty m_doRandomOnlyFilter { this, "DoRandomOnlyFilter", false, "Only events recorded with only random triggers are stored." }; BooleanProperty m_useFlukaWeights { this, "UseFlukaWeights", false, "Flag to weight events according to value stored in HepMC::GenEvent" }; BooleanProperty m_useGenieWeights { this, "UseGenieWeights", false, "Flag to weight events according to Genie luminosity" }; @@ -240,6 +245,16 @@ private: mutable std::vector<double> m_trackseg_py; mutable std::vector<double> m_trackseg_pz; + mutable unsigned int m_nTruetracksegs; + mutable std::vector<double> m_Truetrackseg_Chi2; + mutable std::vector<double> m_Truetrackseg_DoF; + mutable std::vector<double> m_Truetrackseg_x; + mutable std::vector<double> m_Truetrackseg_y; + mutable std::vector<double> m_Truetrackseg_z; + mutable std::vector<double> m_Truetrackseg_px; + mutable std::vector<double> m_Truetrackseg_py; + mutable std::vector<double> m_Truetrackseg_pz; + mutable int m_longTracks; mutable int m_propagationError; mutable std::vector<int> m_module_eta0; diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/GhostBusters.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/GhostBusters.cxx index 6eab8b1b84f2a745e8d02395f8063306428dbd58..f191ba2c72d04c5668590abcdd320a0846ba41be 100644 --- a/Tracking/Acts/FaserActsKalmanFilter/src/GhostBusters.cxx +++ b/Tracking/Acts/FaserActsKalmanFilter/src/GhostBusters.cxx @@ -34,6 +34,7 @@ StatusCode GhostBusters::initialize() { StatusCode GhostBusters::execute(const EventContext &ctx) const { m_event_number = ctx.eventID().event_number(); + ATH_MSG_INFO("GHOSTBUSTING Event Number = " << m_event_number); SG::WriteHandle outputTrackCollection {m_outputTrackCollection, ctx}; std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>(); @@ -62,6 +63,25 @@ StatusCode GhostBusters::execute(const EventContext &ctx) const { double meanX = 0.5 * (minX + maxX); double meanY = 0.5 * (minY + maxY); double deltaX = deltaY / (2 * std::tan(0.02)); + + ATH_MSG_INFO("maxX = " << maxX); + ATH_MSG_INFO("maxY = " << maxY); + ATH_MSG_INFO("minX = " << minX); + ATH_MSG_INFO("minY = " << minY); + ATH_MSG_INFO("deltaY = " << deltaY); + ATH_MSG_INFO("meanX = " << meanX); + ATH_MSG_INFO("meanY = " << meanY); + ATH_MSG_INFO("deltaX = " << deltaX); + + ATH_MSG_INFO("yLowerBound = " << meanY - m_yTolerance * deltaY); + ATH_MSG_INFO("yUpperBound = " << meanY + m_yTolerance * deltaY); + ATH_MSG_INFO("xLeftLowerBound = " << meanX - (1 + m_xTolerance) * deltaX); + ATH_MSG_INFO("xLeftUpperBound = " << meanX - (1 - m_xTolerance) * deltaX); + ATH_MSG_INFO("xRightLowerBound = " << meanX + (1 - m_xTolerance) * deltaX); + ATH_MSG_INFO("xRightUpperBound = " << meanX + (1 + m_xTolerance) * deltaX); + + + for (Segment &segment : stationSegments) { bool isGhost = (segment.y() > meanY - m_yTolerance * deltaY) && (segment.y() < meanY + m_yTolerance * deltaY) && ( ((segment.x() > meanX - (1 + m_xTolerance) * deltaX) && @@ -69,6 +89,10 @@ StatusCode GhostBusters::execute(const EventContext &ctx) const { ((segment.x() > meanX + (1 - m_xTolerance) * deltaX) && (segment.x() < meanX + (1 + m_xTolerance) * deltaX))); if (isGhost) segment.setGhost(); if (not isGhost && segment.size() >= 5) nGoodSegments++; + ATH_MSG_INFO("Segment Y = " << segment.y()); + ATH_MSG_INFO("Segment X = " << segment.x()); + ATH_MSG_INFO("Segment isGhost = " << isGhost); + ATH_MSG_INFO("nGoodSegments = " << nGoodSegments); } for (const Segment &segment : stationSegments) { m_x = segment.x(); @@ -77,17 +101,31 @@ StatusCode GhostBusters::execute(const EventContext &ctx) const { m_chi2 = segment.chi2(); m_station = segment.station(); m_size = segment.size(); + ATH_MSG_INFO("m_x = " << m_x); + ATH_MSG_INFO("m_y = " << m_y); + ATH_MSG_INFO("m_z = " << m_z); // m_majorityHits = segment.majorityHits(); m_isGhost = segment.isGhost(); - if (nGoodSegments >= 2 && segment.size() == 4) m_isGhost = true; + ATH_MSG_INFO("m_isGhost = " << m_isGhost); + ATH_MSG_INFO("nGoodSegments = " << nGoodSegments); + ATH_MSG_INFO("segment.size() = " << segment.size()); + // if (nGoodSegments >= 2 && segment.size() == 4) m_isGhost = true; + ATH_MSG_INFO("m_isGhost after if statement = " << m_isGhost); m_tree->Fill(); if (segment.isGhost()) continue; - if (nGoodSegments >= 2 && segment.size() == 4) continue; + // if (nGoodSegments >= 2 && segment.size() == 4) continue; outputTracks->push_back(new Trk::Track(*segment.track())); } } ATH_CHECK(outputTrackCollection.record(std::move(outputTracks))); + + + + + + + return StatusCode::SUCCESS; }