diff --git a/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx b/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx index 9f62fd57b12fa450f6e2218b841501ca2351cf2a..c3f5313c0e52ddc68009ac2d6d3e41103b103b8a 100644 --- a/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx +++ b/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx @@ -6,6 +6,7 @@ #include <map> #include <utility> +#include <cmath> CaloWaveformDigiAlg::CaloWaveformDigiAlg(const std::string& name, ISvcLocator* pSvcLocator) @@ -99,19 +100,31 @@ CaloWaveformDigiAlg::execute(const EventContext& ctx) const { // Create structure to store pulse for each channel std::map<Identifier, std::vector<uint16_t>> waveforms = m_digiTool->create_waveform_map(m_ecalID); + // Sum energy for each channel (i.e. identifier) + std::map<unsigned int, float> esum; + for (const auto& hit : *caloHitHandle) { + esum[hit.identify()] += hit.energyLoss(); + } + + // Loop over time samples for (const auto& tk : m_timekernel) { std::map<unsigned int, float> counts; // Convolve hit energy with evaluated kernel and sum for each hit id (i.e. channel) - for (const auto& hit : *caloHitHandle) { - counts[hit.identify()] += tk * hit.energyLoss(); + //for (const auto& hit : *caloHitHandle) { + // counts[hit.identify()] += tk * hit.energyLoss(); + //} + + // Convolve summed energy with evaluated kernel for each hit id (i.e. channel) + for (const auto& e : esum) { + counts[e.first] = tk * e.second; } // Subtract count from basleine and add result to correct waveform vector for (const auto& c : counts) { - unsigned int baseline = m_digiTool->generate_baseline(m_base_mean, m_base_rms); - int value = baseline - c.second; + float baseline = m_digiTool->generate_baseline(m_base_mean, m_base_rms); + int value = std::round(baseline - c.second); if (value < 0) { ATH_MSG_WARNING("Found pulse " << c.second << " larger than baseline " << c.first); diff --git a/Control/CalypsoExample/Digitization/scripts/faser_digi.py b/Control/CalypsoExample/Digitization/scripts/faser_digi.py index 0e48e1e27dee3b1e17ecddd3fcdef4c17fc0936e..0da1361cec9191f96014543a68aaca777e0acff8 100755 --- a/Control/CalypsoExample/Digitization/scripts/faser_digi.py +++ b/Control/CalypsoExample/Digitization/scripts/faser_digi.py @@ -25,6 +25,8 @@ parser.add_argument("-g", "--geom", default="TI12MC", help="Specify geometry (default: TI12MC, alt: TestBeamMC)") parser.add_argument("-t", "--tag", default="", help="Specify digi tag (to append to output filename)") +parser.add_argument("--subtractTime", type=float, default=-999., + help="Subtract time parameter for SCT RDOs") parser.add_argument("--digiTag", default="", help="Specify tag for waveform digi folder") parser.add_argument("--short", default="", @@ -161,6 +163,10 @@ acc.merge(FaserGeometryCfg(ConfigFlags)) from FaserSCT_Digitization.FaserSCT_DigitizationConfigNew import FaserSCT_DigitizationCfg acc.merge(FaserSCT_DigitizationCfg(ConfigFlags)) +# Set the time offset for SCT RDOs +pualg = acc.getEventAlgo("StandardPileUpToolsAlg") +pualg.PileUpTools['FaserSCT_DigitizationTool'].SurfaceChargesGenerator.SubtractTime = args.subtractTime + # Pass something to set folder tag from CaloDigiAlgs.CaloDigiAlgsConfig import CaloWaveformDigitizationCfg acc.merge(CaloWaveformDigitizationCfg(ConfigFlags, digiTag=args.digiTag)) diff --git a/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py b/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py index ec595160f8766d3737aef15c53082c970c7e9062..b945ffbbd19c024d549d0d0f84fdfca768759f68 100755 --- a/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py +++ b/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py @@ -29,8 +29,12 @@ parser.add_argument("-s", "--slice", type=int, default=0, help="Specify file slice to produce") parser.add_argument("-f", "--files", type=int, default=5, help="Specify number of input files to run in one batch") +parser.add_argument("--complete", action="store_true", + help="This is the complete run, remove segment numbers") parser.add_argument("-t", "--tag", default="", help="Specify digi tag (to append to output filename)") +parser.add_argument("--subtractTime", type=float, default=-999., + help="Subtract time parameter for SCT RDOs") parser.add_argument("--digiTag", default="", help="Specify tag for waveform digi folder") parser.add_argument("--short", default="", @@ -67,24 +71,35 @@ if len(dirlist) == 0: print("HITS files available:") [print(file) for file in dirlist] +# Get run number for pattern matching +stem = dirlist[0].stem +spl = stem.split('-') +short = spl[1] +run = spl[2] + for seg in seglist: - # Assume these are in numerical order from 0 - if seg >= len(dirlist): - print(f"Requested file segment {seg} but only {len(dirlist)} files found") - if args.partial: - break - else: - sys.exit(1) # Abort this job + # No longer assume all HITS files are complete + #if seg >= len(dirlist): + # print(f"Requested file segment {seg} but only {len(dirlist)} files found") + # if args.partial: + # break + # else: + # sys.exit(1) # Abort this job # Check if segment number exists in hits file (this is not perfect) - segstr = f"{seg:05d}" - if segstr not in dirlist[seg].name: - print(f"Segment {segstr} not in file {dirlist[seg].name}!") + segstr = f"FaserMC-{short}-{run}-{seg:05d}*HITS.root" + matchlist = list(dirpath.glob(segstr)) + if len(matchlist) == 0: + print(f"Segment {segstr} not found!") if not args.partial: sys.exit(1) # abort + continue + elif len(matchlist) > 1: + print(f"Segment {segstr} matched {len(matchlist)} files!") + sys.exit(1) # Should never happen! else: - print(f"Segment {segstr} found in file {dirlist[seg]}") + print(f"Segment {segstr} found!") - filelist.append(dirlist[seg]) + filelist.append(matchlist[0]) if len(filelist) == 0: # Asked for range that doesn't exist @@ -114,8 +129,11 @@ seghi = int(spl[3]) if len(args.short) > 0: short += args.short +# Shouldn't normally remove segments with MC as we may always add +# more files later. --complete overrides this if wanted + # Build output filename -if seglo == 0 and (seghi+1) == len(dirlist): # Full run +if args.complete and (seglo == 0) and ((seghi+1) == len(dirlist)): # Full run outfile = f"FaserMC-{short}-{run}" elif seglo == seghi: # Single segment outfile = f"FaserMC-{short}-{run}-{seglo:05}" @@ -215,6 +233,10 @@ acc.merge(FaserGeometryCfg(ConfigFlags)) from FaserSCT_Digitization.FaserSCT_DigitizationConfigNew import FaserSCT_DigitizationCfg acc.merge(FaserSCT_DigitizationCfg(ConfigFlags)) +# Set the time offset for SCT RDOs +pualg = acc.getEventAlgo("StandardPileUpToolsAlg") +pualg.PileUpTools['FaserSCT_DigitizationTool'].SurfaceChargesGenerator.SubtractTime = args.subtractTime + # Pass something to set folder tag from CaloDigiAlgs.CaloDigiAlgsConfig import CaloWaveformDigitizationCfg acc.merge(CaloWaveformDigitizationCfg(ConfigFlags, digiTag=args.digiTag)) diff --git a/Control/CalypsoExample/Digitization/scripts/submit_faser_digi.sh b/Control/CalypsoExample/Digitization/scripts/submit_faser_digi.sh index 7ab8eb3734b2cba6676ecd8651967233565ae6dc..fcf5e2045b192ef913a9fc1ea0b75c8fae4074bd 100644 --- a/Control/CalypsoExample/Digitization/scripts/submit_faser_digi.sh +++ b/Control/CalypsoExample/Digitization/scripts/submit_faser_digi.sh @@ -2,10 +2,10 @@ # Used with a condor file to submit to vanilla universe # # Usage: -# submit_faser_digi.sh [--highGain] filepath [release_directory] [working_directory] +# submit_faser_digi.sh filepath [release_directory] [working_directory] # # Options: -# --highGain - apply high gain settings to the Calorimeter PMTs (for muons) +# --digiTag <tag> - override digitization tag for calo gain # --geom - geometry setting # --out - specify output location (in EOS) to copy output HITS file # --log - specify output location (in EOS) for log file @@ -34,6 +34,12 @@ do shift; shift;; + --subtractTime) + echo "Subtract $2 ns from SCT RDOs" + timestr="--subtractTime $2" + shift; + shift;; + -g | --geom) geomstr="--geom $2"; shift; @@ -115,6 +121,7 @@ echo `date` - $HOSTNAME echo "File: $file_name" echo "Geom: $geomstr" echo "Gain: $gainstr" +echo "Time: $timestr" echo "Release: $release_directory" echo "Output: $output_directory" echo "Starting: $starting_directory" @@ -178,7 +185,10 @@ cd "$file_stem" # # Run job # -faser_digi.py $geomstr $gainstr $tagstr "$file_path" +faser_digi.py $geomstr $gainstr $timestr $tagstr "$file_path" +digi_code=$? +echo "Return code: $digi_code" + # # Print out ending time date @@ -189,25 +199,52 @@ export EOS_MGM_URL=root://eospublic.cern.ch # if ! [ -z "$outdest" ] then + echo "Output directory:" ls -l - echo "copy *-RDO.root to $outdest" - mkdir -p $outdest - eos cp *-RDO.root ${outdest}/ || true + thefile=`ls *-RDO.root` + if [ $? -eq 0 ]; then + echo "copy $thefile to $outdest" + eos mkdir -p $outdest + eos cp $thefile ${outdest}/${thefile} || true + + # Check that this worked + eos ls ${outdest}/${thefile} > /dev/null + if [ $? -eq 0 ]; then + echo "file $thefile copied to $outdest" + copy_code=0 + else + echo "didnt find $thefile in $outdest !" + copy_code=1 + fi + else + echo "ls *-RDO.root returned nothing!" + copy_code=1 + fi fi # # Also copy log file if ! [ -z "$logdest" ] then cd .. + echo "Working directory:" ls -l echo "copy $logfile to $logdest" - mkdir -p $logdest + eos mkdir -p $logdest eos cp $logfile $logdest/$logfile elif ! [ -z "$outdest" ] then cd .. + echo "Working directory:" ls -l echo "copy $logfile to $outdest" - mkdir -p $outdest + eos mkdir -p $outdest eos cp $logfile $outdest/$logfile fi + +# Make sure to return an error is calypso failed +if [ $digi_code -ne 0 ] || [ $copy_code -ne 0 ]; then + exit 1 +else + exit 0 +fi + diff --git a/Control/CalypsoExample/Digitization/scripts/submit_faser_digi_merge.sh b/Control/CalypsoExample/Digitization/scripts/submit_faser_digi_merge.sh index 98c34d0eb1f3f5de958e6ab4f8438d4e9405ee54..50278c33e2435236fff3bc2b6a94c13aa5794817 100755 --- a/Control/CalypsoExample/Digitization/scripts/submit_faser_digi_merge.sh +++ b/Control/CalypsoExample/Digitization/scripts/submit_faser_digi_merge.sh @@ -2,10 +2,11 @@ # Used with a condor file to submit to vanilla universe # # Usage: -# submit_faser_digi_merge.sh [--highGain] dirpath slice nfiles [release_directory] [working_directory] +# submit_faser_digi_merge.sh dirpath slice nfiles [release_directory] [working_directory] # # Options: -# --highGain - apply high gain settings to the Calorimeter PMTs (for muons) +# --digiTag <tag> - override digitization tag for calo gain +# --partial - allow missing files in merge # --geom - geometry setting # --out - specify output location (in EOS) to copy output HITS file # --log - specify output location (in EOS) for log file @@ -28,6 +29,7 @@ SECONDS=0 # # Job options strings gainstr="" +timestr="" partialstr="" geomstr="" # @@ -41,6 +43,12 @@ do shift; shift;; + --subtractTime) + echo "Subtract $2 ns from SCT RDOs" + timestr="--subtractTime $2" + shift; + shift;; + --partial) echo "Allowing partial merge" partialstr="--partial" @@ -146,6 +154,7 @@ echo `date` - $HOSTNAME echo "Directory: $dir_path" echo "Geom: $geomstr" echo "Gain: $gainstr" +echo "Time: $timestr" echo "Slice: $slice" echo "NFiles: $nfiles" echo "Release: $release_directory" @@ -212,7 +221,9 @@ cd "$file_stem" # # Run job # -faser_digi_merge.py $partialstr $geomstr $gainstr $tagstr --slice $slice --files $nfiles $dir_path +faser_digi_merge.py $partialstr $geomstr $gainstr $timestr $tagstr --slice $slice --files $nfiles $dir_path +digi_code=$? +echo "Return code: $digi_code" # # Print out ending time date @@ -223,25 +234,52 @@ export EOS_MGM_URL=root://eospublic.cern.ch # if ! [ -z "$outdest" ] then + echo "Output directory:" ls -l - echo "copy *-RDO.root to $outdest" - mkdir -p $outdest - eos cp *-RDO.root ${outdest}/ || true + thefile=`ls *-RDO.root` + if [ $? -eq 0 ]; then + echo "copy $thefile to $outdest" + eos mkdir -p $outdest + eos cp $thefile ${outdest}/${thefile} || true + + # Check that this worked + eos ls ${outdest}/${thefile} > /dev/null + if [ $? -eq 0 ]; then + echo "file $thefile copied to $outdest" + copy_code=0 + else + echo "didnt find $thefile in $outdest !" + copy_code=1 + fi + else + echo "ls *-RDO.root returned nothing!" + copy_code=1 + fi fi # # Also copy log file if ! [ -z "$logdest" ] then cd .. + echo "Working directory:" ls -l echo "copy $logfile to $logdest" - mkdir -p $logdest + eos mkdir -p $logdest eos cp $logfile $logdest/$logfile elif ! [ -z "$outdest" ] then cd .. + echo "Working directory:" ls -l echo "copy $logfile to $outdest" - mkdir -p $outdest + eos mkdir -p $outdest eos cp $logfile $outdest/$logfile fi + +# Make sure to return an error is calypso failed +if [ $digi_code -ne 0 ] || [ $copy_code -ne 0 ]; then + exit 1 +else + exit 0 +fi + diff --git a/Control/CalypsoExample/Generation/python/faser_parser.py b/Control/CalypsoExample/Generation/python/faser_parser.py index ff8f1d3792a666bf92dca51b16bf7adf58c7500d..055dd7200201ce69823d798609cb991d85f4e794 100644 --- a/Control/CalypsoExample/Generation/python/faser_parser.py +++ b/Control/CalypsoExample/Generation/python/faser_parser.py @@ -40,16 +40,22 @@ def faser_pgparser(): help="Specify PDG ID of particle (note plus/minus different) or list (e.g.: --pid -13 13)") parser.add_argument("--mass", default=105.66, type=float, help="Specify particle mass (in MeV)") - parser.add_argument("--radius", default=100., type=float, + parser.add_argument("--radius", default=100., type=float, nargs="*", help="Specify radius (in mm)") parser.add_argument("--angle", default=0.005, type=float_or_none, help="Specify angular width (in Rad)") parser.add_argument("--zpos", default=None, type=float, help="Specify z position of particles (in mm) (helpful to avoid FASERnu)") + parser.add_argument("--pidd1", default=None, type=int, + help="Specify PDG ID of daugther 1 for DIF generator") + parser.add_argument("--pidd2", default=None, type=int, + help="Specify PDG ID of daugther 2 for DIF generator") + + parser.add_argument("--sampler", default="log", help="Specify energy sampling (log, lin, const, hist, hist2D)") - parser.add_argument("--hist_name", default="log", + parser.add_argument("--hist_name", default="", help="Specify energy sampling name for hist sampler file.root:hist") parser.add_argument("--minE", default=10., type=float, diff --git a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py index cc1628efdb9245e32b7abbbc9ca60a60b2151fad..169badecf7cf2a392a95b098a858634c5a2523c6 100755 --- a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py +++ b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py @@ -101,24 +101,50 @@ if __name__ == '__main__': from AthenaCommon.PhysicalConstants import pi if isinstance(args.pid, list): - # Note args.pid is a list, must make this a set for ParticleGun - pidarg = set(args.pid) + # Note args.pid is a list, must make this a set for ParticleGun if have > 1 + if len(args.pid) > 1: + pidarg = set(args.pid) + else: + pidarg = args.pid[0] else: # Just pass a single value pidarg = args.pid print(f"Using pid: {args.pid} => {pidarg}") + if isinstance(args.radius, list) and len(args.radius) == 1: + args.radius = args.radius[0] + + # Create the simgun dictionary # Negative radius gives uniform sampling # Positive radius gives Gaussian sampling - sg_dict = { - "Generator" : "SingleParticle", - "pid" : pidarg, "mass" : args.mass, - "theta" : PG.GaussianSampler(0, args.angle, oneside = True), - "phi" : [0, 2*pi], "radius" : args.radius, - "randomSeed" : args.outfile - } + + if args.pidd1: + if args.pidd2 is None: + args.pidd2 = -args.pidd1 + + sg_dict = { + "Generator" : "DecayInFlight", + "mother_pid" : pidarg, + "daughter1_pid" : args.pidd1, + "daughter2_pid" : args.pidd2, + "mass" : args.mass, + "theta" : PG.GaussianSampler(0, args.angle, oneside = True) if args.angle is not None and args.angle != "None" else None, + "phi" : [0, 2*pi], "radius" : args.radius, + "randomSeed" : args.outfile + } + + else: + + sg_dict = { + "Generator" : "SingleParticle", + "pid" : pidarg, "mass" : args.mass, + "theta" : PG.GaussianSampler(0, args.angle, oneside = True) if args.angle is not None and args.angle != "None" else None, + "phi" : [0, 2*pi], "radius" : args.radius, + "randomSeed" : args.outfile + } + # -1000 is safely upstream of detector (to be checked) # Note zpos is in mm! @@ -132,6 +158,12 @@ if __name__ == '__main__': sg_dict["energy"] = PG.LogSampler(args.minE*GeV, args.maxE*GeV) elif args.sampler == "const": sg_dict["energy"] = PG.ConstSampler(args.maxE*GeV) + elif args.sampler == "hist": + fname, hname = args.hist_name.split(":") + sg_dict["energy"] = PG.TH1Sampler(fname, hname) + elif args.sampler == "hist2D": + fname, hname = args.hist_name.split(":") + sg_dict["energy"] = PG.TH2Sampler(fname, hname) else: print(f"Sampler {args.sampler} not known!") sys.exit(1) diff --git a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py index f295da6bdc7b914a47b817a3e3d770dc9731e20a..21e1d51a6af7d92ebac09d051e80c919d1c39687 100755 --- a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py +++ b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py @@ -315,5 +315,11 @@ b = time.time() from AthenaCommon.Logging import log log.info(f"Finish execution in {b-a} seconds") -sys.exit(int(sc.isFailure())) +# Signal errors +if sc.isSuccess(): + log.info("Execution succeeded") + sys.exit(0) +else: + log.info("Execution failed, return 1") + sys.exit(1) diff --git a/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh b/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh index 20b39c9178b01a3e0252b2cb9351c39359939106..f75b144a1bf2d0a725692dbaa82be34ad788cd5c 100755 --- a/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh +++ b/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh @@ -8,7 +8,10 @@ # --out - specify output location (in EOS) to copy output HITS file # --log - specify output location (in EOS) for log file # --geom - specify geometry +# +# Monte Carlo options: # --isMC - needed for MC reco +# --caloTag <tag> - override MC reco tag for calo gain (to match digi tag) # # file_path - full file name (with path) # release_directory - optional path to release install directory (default pwd) @@ -25,6 +28,9 @@ # Keep track of time SECONDS=0 # +# Job option strings +gainstr="" +# # Parse command-line options while [ -n "$1" ] do @@ -45,9 +51,16 @@ do shift;; --isMC) + echo "Set isMC true" ismc=1 shift;; + --caloTag) + echo "Override calo digi tag with $2" + gainstr="--MC_calibTag $2" + shift; + shift;; + --) # End of options shift; # Eat this break;; # And stop parsing @@ -119,6 +132,7 @@ echo `date` - $HOSTNAME echo "File: $file_name" echo "Filepath: $file_path" echo "Geom: $geom" +echo "Gain: $gainstr" echo "Release: $release_directory" echo "Output: $output_directory" echo "Starting: $starting_directory" @@ -216,7 +230,9 @@ else mcstr="--isMC" fi # -faser_reco.py "--nevents=$nevents" $geomstr $tagstr $mcstr "$file_path" +faser_reco.py "--nevents=$nevents" $geomstr $tagstr $mcstr $gainstr "$file_path" +reco_code=$? +echo "Return code: $reco_code" # # Print out ending time date @@ -230,11 +246,27 @@ if ! [ -z "$outdest" ] then echo "Output directory:" ls -l - echo "copy *-xAOD.root to $outdest" - eos mkdir -p $outdest - # Keep this line from stopping script, so we might get a log file - # || true ensures script continues even if copy fails - eos cp *-xAOD.root ${outdest}/ || true + thefile=`ls *-xAOD.root` + if [ $? -eq 0 ]; then + echo "copy $thefile to $outdest" + eos mkdir -p $outdest + # Keep this line from stopping script, so we might get a log file + # || true ensures script continues even if copy fails + eos cp $thefile ${outdest}/${thefile} || true + + # Check that it worked + eos ls ${outdest}/${thefile} > /dev/null + if [ $? -eq 0 ]; then + echo "file $thefile copied to $outdest" + copy_code=0 + else + echo "didnt find $thefile in $outdest !" + copy_code=1 + fi + else + echo "ls *-xAOD.root returned nothing!" + copy_code=1 + fi fi # # Copy log file second @@ -255,3 +287,11 @@ then eos mkdir -p $outdest eos cp $logfile $outdest/$logfile fi + +# Make sure to return an error is calypso failed +if [ $reco_code -ne 0 ] || [ $copy_code -ne 0 ]; then + exit 1 +else + exit 0 +fi + diff --git a/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh b/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh index 83a2470f1e93bd9703e0e44c5abca3a9aeebe7c1..6e4d4df2eac92d4836c2b77104093ab2dd81c01c 100755 --- a/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh +++ b/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh @@ -35,6 +35,11 @@ do xangle=1 shift;; # This 'eats' the argument + --fshift) + echo "Applying FLUKA crossing-angle shift" + xangle=2 + shift;; + -l | --log) logdest="$2"; shift; @@ -183,9 +188,16 @@ cd "${file_stem}" #fi if [[ -z "$xangle" ]]; then faser_simulate.py --skip "$skip_events" -n "$nevts" "$infile" "$outfile" + sim_code=$? +elif [[ $xangle == 1 ]]; then + faser_simulate.py --yangle -0.000160 --yshift 12.0 --skip "$skip_events" -n "$nevts" "$infile" "$outfile" + sim_code=$? else - faser_simulate.py --yangle -0.000150 --yshift 12.0 --skip "$skip_events" -n "$nevts" "$infile" "$outfile" + echo "Applying double crossing-angle shift for FLUKA!" + faser_simulate.py --yangle -0.000310 --yshift 12.0 --skip "$skip_events" -n "$nevts" "$infile" "$outfile" + sim_code=$? fi +echo "Return code: $sim_code" # # Print out ending time date @@ -196,16 +208,34 @@ export EOS_MGM_URL=root://eospublic.cern.ch # if ! [ -z "$outdest" ] then + echo "Output directory:" ls -l - echo "copy *-HITS.root to $outdest" - eos mkdir -p $outdest - eos cp *-HITS.root ${outdest}/ || true + thefile=`ls *-HITS.root` + if [ $? -eq 0 ]; then + echo "copy $thefile to $outdest" + eos mkdir -p $outdest + eos cp $thefile ${outdest}/${thefile} || true + + # Check that this worked + eos ls ${outdest}/${thefile} > /dev/null + if [ $? -eq 0 ]; then + echo "file $thefile copied to $outdest" + copy_code=0 + else + echo "didnt find $thefile in $outdest !" + copy_code=1 + fi + else + echo "ls *-HITS.root returned nothing!" + copy_code=1 + fi fi # # Also copy log file if ! [ -z "$logdest" ] then cd .. + echo "Working directory:" ls -l echo "copy $logfile to $logdest" eos mkdir -p $logdest @@ -213,9 +243,17 @@ then elif ! [ -z "$outdest" ] then cd .. + echo "Working directory:" ls -l echo "copy $logfile to $outdest" eos mkdir -p $outdest eos cp $logfile $outdest/$logfile fi +# Make sure to return an error is calypso failed +if [ $sim_code -ne 0 ] || [ $copy_code -ne 0 ]; then + exit 1 +else + exit 0 +fi + diff --git a/Generators/DIFGenerator/python/DIFSampler.py b/Generators/DIFGenerator/python/DIFSampler.py index 8ec22459aba93a430093c4bd7b2a3f13e2d0f206..007830b63c82ec06f19af6fbd3169cfc10ce8e2e 100644 --- a/Generators/DIFGenerator/python/DIFSampler.py +++ b/Generators/DIFGenerator/python/DIFSampler.py @@ -93,9 +93,8 @@ class DIFSampler(PG.ParticleSampler): # def __init__(self, daughter1_pid = 11, daughter2_pid = -11, mother_pid = None, mother_mom = PG.EThetaMPhiSampler(sqrt((1*TeV)**2 + (10*MeV)**2),0,10*MeV,0),mother_pos = CylinderSampler([0, 100**2],[0, 2*pi],[-1500, 0],0)): def __init__(self, daughter1_pid=13, daughter2_pid=-13, mother_pid=None, mother_mom=PG.EThetaMPhiSampler(sqrt((1*TeV)**2 + (500*MeV)**2), [0, 0.0002], 500*MeV, [0, 2*pi]), - my_z_position=-1500): - # mother_pos=CylinderSampler([0, 100**2], [0, 2*pi], [-1500, 0], 0)): - self._mother_sampler = PG.ParticleSampler(pid = mother_pid, mom = mother_mom, pos=CylinderSampler([0, 100**2], [0, 2*pi], my_z_position, 0)) + mother_pos=CylinderSampler([0, 100**2], [0, 2*pi], [-1500, 0], 0)): + self._mother_sampler = PG.ParticleSampler(pid = mother_pid, mom = mother_mom, pos=mother_pos) self.daughter1 = self.particle(daughter1_pid) self.daughter2 = self.particle(daughter2_pid) diff --git a/Generators/FaserParticleGun/python/FaserParticleGunConfig.py b/Generators/FaserParticleGun/python/FaserParticleGunConfig.py index ac30494a4815f5bb06a5d67408b4c0551297a63b..d2e82a88d3fd6229b2e3ad33932ead7d5d650720 100644 --- a/Generators/FaserParticleGun/python/FaserParticleGunConfig.py +++ b/Generators/FaserParticleGun/python/FaserParticleGunConfig.py @@ -9,7 +9,7 @@ import ParticleGun as PG from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator from AthenaConfiguration.ComponentFactory import CompFactory # from AthenaCommon.Constants import VERBOSE, INFO -from AthenaCommon.SystemOfUnits import TeV +from AthenaCommon.SystemOfUnits import TeV, GeV, MeV from AthenaCommon.PhysicalConstants import pi ### add radial pos sampler ### with gaussian beam implemented @@ -144,7 +144,31 @@ def FaserParticleGunDecayInFlightCfg(ConfigFlags, **kwargs) : pg = cfg.getPrimary() from DIFGenerator import DIFSampler - pg.sampler = DIFSampler(**kwargs) + + if "radius" in kwargs: + kwargs["mother_pos"] = RadialPosSampler(x = kwargs.setdefault("x", 0.0), + y = kwargs.setdefault("y", 0.0), + z = kwargs.setdefault("z", -3750.0), + r = kwargs.setdefault("radius", 1.0), + t = kwargs.setdefault("t", 0.0) ) + else: + from DIFGenerator.DIFSampler import CylinderSampler + kwargs["mother_pos"] = CylinderSampler([0, 100**2], [0, 2*pi], [-1500, 0], 0) + + if not "mother_mom" in kwargs: + kwargs["mother_mom"] = PG.EThetaMPhiSampler(kwargs.setdefault("energy", ((1*TeV)**2 + (500*MeV)**2)**0.5), + kwargs.setdefault("theta", [0, 0.0002]), + kwargs.setdefault("mass", 500*MeV), + kwargs.setdefault("phi", [0, 2*pi]) ) + + + + pg.sampler = DIFSampler(kwargs.setdefault("daughter1_pid", 13), + kwargs.setdefault("daughter2_pid", -13), + kwargs.setdefault("mother_pid", None), + kwargs["mother_mom"], + kwargs["mother_pos"] ) + return cfg @@ -190,9 +214,12 @@ def FaserParticleGunForeseeCfg(ConfigFlags, **kwargs) : return cfg def FaserParticleGunCfg(ConfigFlags) : - # generator = ConfigFlags.Sim.Gun.setdefault("Generator", "SingleParticle") - generator = ConfigFlags.Sim.Gun.setdefault("Generator", "DecayInFlight") + generator = ConfigFlags.Sim.Gun.setdefault("Generator", "SingleParticle") + # generator = ConfigFlags.Sim.Gun.setdefault("Generator", "DecayInFlight") + kwargs = ConfigFlags.Sim.Gun + del kwargs["Generator"] + if generator == "SingleEcalParticle" : return FaserParticleGunSingleEcalParticleCfg(ConfigFlags, **kwargs) elif generator == "Cosmics" : diff --git a/Generators/FaserParticleGun/python/RadialPosSampler.py b/Generators/FaserParticleGun/python/RadialPosSampler.py index 6fcd6829b29ea474d5c7380526d3c46bab9c6871..19b1db116991c082d0771699813df2fe94c0a1d9 100644 --- a/Generators/FaserParticleGun/python/RadialPosSampler.py +++ b/Generators/FaserParticleGun/python/RadialPosSampler.py @@ -35,12 +35,17 @@ class RadialPosSampler(Sampler): def r(self): "r position sampler" - fwhm = 2*self.radius - sig = fwhm/(2 * sqrt(2 * log(2))) - - if self.radius < 0: - return sqrt(random.uniform(0, abs(self.radius**2))) + #fwhm = 2*self.radius + #sig = fwhm/(2 * sqrt(2 * log(2))) + + if isinstance(self.radius, list) and len(self.radius) == 2: + # If a list of length 2, generate uniformally over an annulus from r[0] to r[1] + return sqrt(random.uniform(abs(self.radius[0]**2), abs(self.radius[1]**2))) + elif self.radius < 0: + # Generate uniformally up to |r| if r is < 0 + return sqrt(random.uniform(0, abs(self.radius**2))) else: + # Else generate as a Gaussian with widht r x = random.gauss(0, self.radius) y = random.gauss(0, self.radius) return sqrt(x**2 + y**2) diff --git a/Generators/ForeseeGenerator/share/generate_forsee_events.py b/Generators/ForeseeGenerator/share/generate_forsee_events.py index 0c3096f8abe6a640391999613442ce1a267f4aca..6ef5d1d0377a147246b97b9e203fcc224355bae2 100644 --- a/Generators/ForeseeGenerator/share/generate_forsee_events.py +++ b/Generators/ForeseeGenerator/share/generate_forsee_events.py @@ -47,13 +47,13 @@ class ForeseeGenerator(object): else: self.foresee = Foresee(path = self.path) - # Generate 6 cm high to account for translation from ATLAS to FASER coord. system + # Generate 6.5 cm high to account for translation from ATLAS to FASER coord. system # TODO: relax this a bit as daughters may enter even if mother doesn't # self.foresee.set_detector(selection="np.sqrt(x.x**2 + x.y**2)< 0.1", # channels=[self.mode], distance=480, length=1.5 , # luminosity=1/1000.) # 1 pb-1 - self.foresee.set_detector(selection="np.sqrt(x.x**2 + (x.y - 0.06)**2)< 0.1", + self.foresee.set_detector(selection="np.sqrt(x.x**2 + (x.y - 0.065)**2)< 0.1", channels=[self.mode], distance=480, length=1.5 , luminosity=1/1000.) # 1 pb-1 diff --git a/Generators/ForeseeGenerator/share/validate_grid.py b/Generators/ForeseeGenerator/share/validate_grid.py index cf01468483c71f68b3687ec2a7a17a031fe84d7a..88edb1194ee6ecebe37464906b26a03afde7b1da 100644 --- a/Generators/ForeseeGenerator/share/validate_grid.py +++ b/Generators/ForeseeGenerator/share/validate_grid.py @@ -1,13 +1,14 @@ import json import subprocess as proc -import os +from glob import glob +import os, sys grid = "../calypso/Generators/ForeseeGenerator/share/points.json" with open(grid) as f: data = json.load(f) -name = "DarkPhotonGrid13p6" +name = "DarkPhotonGrid13p6_65mm" path = f"/bundle/data/FASER/Carl_Output/Foresee/{name}/" ecom = 13.6 pid1 = 11 @@ -15,7 +16,12 @@ pid2 = -11 for coup, masses in data["samples"].items(): for m in masses: - infile = f"{path}events_{ecom}TeV_m{m}GeV_c{float(coup):.1e}to_{pid1}_{pid2}_{name}.hepmc" + files = glob(f"{path}events_{ecom}TeV_m{m}GeV_c{float(coup):.1e}to_{pid1}_{pid2}_{name}*.hepmc") + + if len(files) != 1: + continue + + infile = files[0] outfile = infile.replace(".hepmc", ".EVNT.pool.root") valfile = infile.replace(".hepmc", ".VAL.pool.root") valdir = infile.replace(".hepmc", "") diff --git a/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py b/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py index 0f725fe9a5a4bf2dc63b915ccf9768b1ffd60c0f..27b30e25df16dd8baf1c4ce4b82b6a9e6a0f7b6f 100755 --- a/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py +++ b/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py @@ -22,7 +22,9 @@ parser.add_argument("path", parser.add_argument("--slice", type=int, default=0, help="Specify ordinal output file to produce") parser.add_argument("--files", type=int, default=1, - help="Specify files per slice") + help="Specify reco files per slice") +parser.add_argument("--merge", type=int, default=1, + help="Specify merged files per reco file (MC only)") parser.add_argument("--last", type=int, default=0, help="Specify last file in slice (normally --files)") @@ -41,7 +43,15 @@ parser.add_argument("--isMC", action='store_true', help="Running on digitised MC rather than data") parser.add_argument("--partial", action='store_true', help="Allow partial input files") + +parser.add_argument("--unblind", action='store_true', + help="Don't apply signal blinding (default: do)") +parser.add_argument("--fluka", action='store_true', + help="Add FLUKA weights to ntuple") +parser.add_argument("--genie", action='store_true', + help="Add Genie weights to ntuple") + args = parser.parse_args() from pathlib import Path @@ -53,21 +63,42 @@ filelist = [] # If this is a directory, need to create file list if filepath.is_dir(): - # Parsing MC is tricky - if args.isMC: - print("Monte Carlo not supported yet!") - sys.exit(0) - - # Use expected data pattern to find files + # Use expected data pattern to find files (data only) runstr = filepath.stem - start = args.slice * args.files - if args.last > 0: - end = start + args.last + + # Make list of segments to search for + seglist = [] + if args.merge > 1: + start = args.slice * args.files * args.merge + + # Number of files to combine + if args.last > 0: + num = args.last + else: + num = args.files + + # Make segment list + for i in range(start, start+num*args.merge, args.merge): + seg = f"{i:05d}-{(i+args.merge-1):05d}" + seglist.append(seg) + else: - end = start + args.files + start = args.slice * args.files + if args.last > 0: + end = start + args.last + else: + end = start + args.files + + seglist = [f'{seg:05d}' for seg in range(start, end)] + + + for seg in seglist: + + if args.isMC: + searchstr = f"FaserMC-*-{seg}-*xAOD.root" + else: + searchstr = f"Faser-Physics-{runstr}-{seg}-*xAOD.root" - for seg in range(start, end): - searchstr = f"Faser-Physics-{runstr}-{seg:05d}-*xAOD.root" flist = list(filepath.glob(searchstr)) if len(flist) == 0: print(f"Didn't find file {searchstr}!") @@ -86,21 +117,41 @@ if filepath.is_dir(): filelist.append(filestr) # End of loop over segments + # Parse name to create outfile firstfile = Path(filelist[0]) firststem = str(firstfile.stem) + firstfaser = firststem.split('-')[0] + firstshort = firststem.split('-')[1] + runstr = firststem.split('-')[2] firstseg = firststem.split('-')[3] + if args.merge > 1: + firstseg2 = firststem.split('-')[4] lastfile = Path(filelist[-1]) laststem = str(lastfile.stem) lastseg = laststem.split('-')[3] + if args.merge > 1: + lastseg = laststem.split('-')[4] + + print(f"Faser = {firstfaser}") + print(f"Short = {firstshort}") + print(f"Run = {runstr}") + print(f"First = {firstseg}") + print(f"Last = {lastseg}") + print(f"Args = {args.tag}") + print(f"Blind = {not args.unblind}") # Find any tags - tagstr = firststem.replace(f"Faser-Physics-{runstr}-{firstseg}", "") + tagstr = firststem.replace(f"{firstfaser}-{firstshort}-{runstr}-{firstseg}", "") + if args.merge > 1: + tagstr = tagstr.replace(f"-{firstseg2}", "") + tagstr = tagstr.replace("-xAOD", "") - print(f"Tag = {tagstr}") + + print(f"Tag = {tagstr}") # Build output name - outfile = f"Faser-Physics-{runstr}-{firstseg}-{lastseg}" + outfile = f"{firstfaser}-{firstshort}-{runstr}-{firstseg}-{lastseg}" # This will include the leading - if len(tagstr) > 0: @@ -111,6 +162,7 @@ if filepath.is_dir(): outfile += "-PHYS.root" + # If this is a single file, just process that # Could be a url, so don't check if this is a file else: @@ -161,12 +213,11 @@ Configurable.configurableRun3Behavior = True # Configure ConfigFlags.Input.Files = filelist ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03" # Always needed; must match FaserVersionS +ConfigFlags.Input.isMC = args.isMC if args.isMC: ConfigFlags.IOVDb.DatabaseInstance = "OFLP200" # Use MC conditions - ConfigFlags.Input.isMC = True # Needed to bypass autoconfig else: ConfigFlags.IOVDb.DatabaseInstance = "CONDBR3" # Use data conditions - ConfigFlags.Input.isMC = False # Needed to bypass autoconfig ConfigFlags.Input.ProjectName = "data21" # Needed to bypass autoconfig @@ -184,14 +235,21 @@ acc.merge(PoolReadCfg(ConfigFlags)) # algorithm from NtupleDumper.NtupleDumperConfig import NtupleDumperAlgCfg if args.isMC: - acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, UseFlukaWeights=True)) + if args.genie: + acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, UseGenieWeights=True)) + elif args.fluka: + acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, UseFlukaWeights=True)) + else: + acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile)) + else: - acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile)) + acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, DoBlinding=(not args.unblind))) -from AthenaConfiguration.ComponentFactory import CompFactory -AthenaEventLoopMgr = CompFactory.AthenaEventLoopMgr() -AthenaEventLoopMgr.EventPrintoutInterval=1000 -acc.addService(AthenaEventLoopMgr) +if not args.verbose: + from AthenaConfiguration.ComponentFactory import CompFactory + AthenaEventLoopMgr = CompFactory.AthenaEventLoopMgr() + AthenaEventLoopMgr.EventPrintoutInterval=1000 + acc.addService(AthenaEventLoopMgr) # Hack to avoid problem with our use of MC databases when isMC = False if not args.isMC: @@ -217,4 +275,10 @@ b = time.time() log.info(f"Finish execution in {b-a} seconds") # Success should be 0 -sys.exit(int(sc.isFailure())) +if sc.isSuccess(): + log.info("Execution succeeded") + sys.exit(0) +else: + log.info("Execution failed, return 1") + sys.exit(1) + diff --git a/PhysicsAnalysis/NtupleDumper/scripts/submit_faser_ntuple_maker.sh b/PhysicsAnalysis/NtupleDumper/scripts/submit_faser_ntuple_maker.sh index d32998724cb479aeff240dee489257b9f1a0f3fd..9e3273dbb970f5b5d80958f67656490c045a7612 100755 --- a/PhysicsAnalysis/NtupleDumper/scripts/submit_faser_ntuple_maker.sh +++ b/PhysicsAnalysis/NtupleDumper/scripts/submit_faser_ntuple_maker.sh @@ -10,6 +10,9 @@ # --log - specify output location (in EOS) for log file # --isMC - needed for MC reco # --partial - allow missing files +# --merge - merge factor of reco files (for MC) +# --fluka - create fluka weights +# --genie - create genie weights # # dirpath - full directory path to HITS files # slice - ordinal output file number @@ -28,6 +31,10 @@ SECONDS=0 # Defaults ismc="" partialstr="" +mergestr="" +flukastr="" +geniestr="" +unblindstr="" # # Parse command-line options while [ -n "$1" ] @@ -52,6 +59,23 @@ do partialstr="--partial" shift;; + --merge) + mergestr="--merge $2"; + shift; + shift;; + + --fluka) + flukastr="--fluka"; + shift;; + + --genie) + geniestr="--genie"; + shift;; + + --unblind) + unblindstr="--unblind"; + shift;; + --) # End of options shift; # Eat this break;; # And stop parsing @@ -140,6 +164,7 @@ echo `date` - $HOSTNAME echo "Directory: $dir_path" echo "Slice: $slice" echo "NFiles: $nfiles" +echo "Merge: $mergestr" echo "Release: $release_directory" echo "Output: $output_directory" echo "Starting: $starting_directory" @@ -219,7 +244,9 @@ export EOS_MGM_URL=root://eospublic.cern.ch # # Run job # -faser_ntuple_maker.py $last_file_str $partialstr $tagstr $ismc --slice $slice --files $nfiles $dir_path +faser_ntuple_maker.py $last_file_str $partialstr $tagstr $ismc --slice $slice --files $nfiles $mergestr $flukastr $geniestr $unblindstr $dir_path +ntup_code=$? +echo "Return code: $ntup_code" # # Print out ending time date @@ -250,3 +277,6 @@ then mkdir -p $outdest eos cp $logfile $outdest/$logfile fi + +# Make sure to return an error if ntuple failed +exit $ntup_code diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx index b61be213ac21f4c7cb568b9bb97177260c266090..3f476edb40480e5871a2df39a3cd2695ea894e4c 100644 --- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx +++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx @@ -106,6 +106,7 @@ StatusCode NtupleDumperAlg::initialize() ATH_CHECK(m_preshowerCalibratedContainer.initialize()); ATH_CHECK(m_ecalCalibratedContainer.initialize()); + ATH_CHECK(m_eventInfoKey.initialize()); ATH_CHECK(detStore()->retrieve(m_sctHelper, "FaserSCT_ID")); ATH_CHECK(detStore()->retrieve(m_vetoNuHelper, "VetoNuID")); @@ -160,8 +161,8 @@ StatusCode NtupleDumperAlg::initialize() //WAVEFORMS addWaveBranches("VetoNu",2,4); - addWaveBranches("VetoSt1",2,6); - addWaveBranches("VetoSt2",1,14); + addWaveBranches("VetoSt1",1,14); + addWaveBranches("VetoSt2",2,6); addWaveBranches("Timing",4,8); addWaveBranches("Preshower",2,12); addWaveBranches("Calo",4,0); @@ -197,6 +198,7 @@ StatusCode NtupleDumperAlg::initialize() m_tree->Branch("TrackSegment_pz", &m_trackseg_pz); //TrackCollection + m_tree->Branch("Track_PropagationError", &m_propagationError, "Track_PropagationError/I"); m_tree->Branch("longTracks", &m_longTracks, "longTracks/I"); m_tree->Branch("Track_Chi2", &m_Chi2); m_tree->Branch("Track_nDoF", &m_DoF); @@ -304,6 +306,24 @@ StatusCode NtupleDumperAlg::initialize() m_tree->Branch("truthPdg", &m_truthPdg, "truthPdg/I"); m_tree->Branch("CrossSection", &m_crossSection, "crossSection/D"); + + // first 10 truth particles + + m_tree->Branch("truth_P", &m_truth_P); + m_tree->Branch("truth_px", &m_truth_px); + m_tree->Branch("truth_py", &m_truth_py); + m_tree->Branch("truth_pz", &m_truth_pz); + m_tree->Branch("truth_m", &m_truth_m); + m_tree->Branch("truth_pdg", &m_truth_pdg); + + m_tree->Branch("truth_prod_x", &m_truth_prod_x); + m_tree->Branch("truth_prod_y", &m_truth_prod_y); + m_tree->Branch("truth_prod_z", &m_truth_prod_z); + + m_tree->Branch("truth_dec_x", &m_truth_dec_x); + m_tree->Branch("truth_dec_y", &m_truth_dec_y); + m_tree->Branch("truth_dec_z", &m_truth_dec_z); + // for mother + daughter particle truth infomation m_tree->Branch("truthM_P", &m_truthM_P); @@ -410,6 +430,8 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const bool trig_coincidence_vetoesORpreshower_and_calo = false; if ( ((m_tap&2)!=0) && ((m_tap&1)!=0) ) trig_coincidence_vetoesORpreshower_and_calo = true; + bool trig_calo = (m_tap & 1); + // for random trigger, store charge of scintillators in histograms if (trig_random) { // Read in Waveform containers @@ -462,7 +484,8 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const return StatusCode::SUCCESS; // finished with this randomly triiggered event - } else if ( !(trig_coincidence_preshower_and_vetoes || trig_coincidence_timing_and_vetoesORpreshower || trig_coincidence_timing_and_calo || trig_coincidence_vetoesORpreshower_and_calo) ) { + // Try adding calo-only trigger + } else if ( !(trig_coincidence_preshower_and_vetoes || trig_coincidence_timing_and_vetoesORpreshower || trig_coincidence_timing_and_calo || trig_coincidence_vetoesORpreshower_and_calo || trig_calo) ) { // don't process events that fail to activate coincidence triggers return StatusCode::SUCCESS; } @@ -546,30 +569,64 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const SG::ReadHandle<xAOD::TruthParticleContainer> truthParticleContainer { m_truthParticleContainer, ctx }; if (truthParticleContainer.isValid() && truthParticleContainer->size() > 0) { - for (auto particle : *truthParticleContainer) - { - if ( particle->barcode() == 1 || particle->barcode() == 2 || particle->barcode() == 3 ) - { - - if ( particle->pdgId() == 32) // mother particle (A') - { - m_truthM_P.push_back(particle->p4().P()); - m_truthM_px.push_back(particle->p4().X()); - m_truthM_py.push_back(particle->p4().Y()); - m_truthM_pz.push_back(particle->p4().Z()); - - if ( particle->hasProdVtx()) { - m_truthM_x.push_back(particle->prodVtx()->x()); - m_truthM_y.push_back(particle->prodVtx()->y()); - m_truthM_z.push_back(particle->prodVtx()->z()); - } else { + int ipart(0); + for (auto particle : *truthParticleContainer) + { + + // loop over first 10 truth particles (for non A' samples) + + if (ipart++ < 10) { + + m_truth_P.push_back(particle->p4().P()); + m_truth_px.push_back(particle->p4().X()); + m_truth_py.push_back(particle->p4().Y()); + m_truth_pz.push_back(particle->p4().Z()); + m_truth_m.push_back(particle->m()); + m_truth_pdg.push_back(particle->pdgId()); + + if ( particle->hasProdVtx()) { + m_truth_prod_x.push_back(particle->prodVtx()->x()); + m_truth_prod_y.push_back(particle->prodVtx()->y()); + m_truth_prod_z.push_back(particle->prodVtx()->z()); + } else { + m_truth_prod_x.push_back(NaN); + m_truth_prod_y.push_back(NaN); + m_truth_prod_z.push_back(NaN); + } + + if ( particle->hasDecayVtx()) { + m_truth_dec_x.push_back(particle->decayVtx()->x()); + m_truth_dec_y.push_back(particle->decayVtx()->y()); + m_truth_dec_z.push_back(particle->decayVtx()->z()); + } else { + m_truth_dec_x.push_back(NaN); + m_truth_dec_y.push_back(NaN); + m_truth_dec_z.push_back(NaN); + } + } + + + if ( particle->barcode() == 1 || particle->barcode() == 2 || particle->barcode() == 3 ) { + + if ( particle->pdgId() == 32) { // mother particle (A') + + m_truthM_P.push_back(particle->p4().P()); + m_truthM_px.push_back(particle->p4().X()); + m_truthM_py.push_back(particle->p4().Y()); + m_truthM_pz.push_back(particle->p4().Z()); + + if ( particle->hasDecayVtx()) { // decay vertex for A' particle + m_truthM_x.push_back(particle->decayVtx()->x()); + m_truthM_y.push_back(particle->decayVtx()->y()); + m_truthM_z.push_back(particle->decayVtx()->z()); + } else { m_truthM_x.push_back(NaN); m_truthM_y.push_back(NaN); m_truthM_z.push_back(NaN); } + } - } if ( particle->pdgId() == 11) // daughter particle (positron) { m_truthd0_P.push_back(particle->p4().P()); @@ -604,12 +661,13 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const m_truthd1_z.push_back(NaN); } } - } + } } - } + } } // load in calibrated calo container + if (m_doCalib) { SG::ReadHandle<xAOD::CalorimeterHitContainer> ecalCalibratedContainer { m_ecalCalibratedContainer, ctx }; ATH_CHECK(ecalCalibratedContainer.isValid()); for (auto hit : *ecalCalibratedContainer) { @@ -649,6 +707,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const ATH_MSG_DEBUG("Calibrated preshower: ch is " << ch << ", edep is " << hit->E_dep() << ", E_EM is " << hit->E_EM() << ", Nmip is " << hit->Nmip() << ", fit_to_raw_ratio is " << hit->fit_to_raw_ratio()); } + } // process all waveeform data fro all scintillator and calorimeter channels SG::ReadHandle<xAOD::WaveformHitContainer> vetoNuContainer { m_vetoNuContainer, ctx }; @@ -673,14 +732,23 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const FillWaveBranches(*ecalContainer); // enforce blinding such that events that miss the veto layers and have a large calo signal are skipped and not in the output root file - if ( (!isMC) && m_doBlinding) { - if ( m_calo_total_E_EM/1000.0 > 10.0 ) { // energy is in MeV so divide by 1000 to compare to 10 GeV + // Only blind colliding BCIDs (+/- 1) + if ( (!isMC) && m_doBlinding && abs(m_distanceToCollidingBCID) <= 1) { + if (m_calo_total_E_EM > m_blindingCaloThreshold ) { // energy is in MeV if (m_wave_status[4] == 1 and m_wave_status[5] == 1 and m_wave_status[6] == 1 and m_wave_status[7] == 1 and m_wave_status[14] == 1) { // hit status == 1 means it is below threshold. channles 4 and 5 are vetoNu, channels 6,7, and 14 are veto return StatusCode::SUCCESS; } } } + SG::ReadDecorHandle<xAOD::EventInfo,uint32_t> eventInfo (m_eventInfoKey, ctx); + if (eventInfo->errorState(xAOD::EventInfo_v1::SCT) == xAOD::EventInfo::Error) { + m_propagationError = 1; + ATH_MSG_DEBUG("NtupleDumper: xAOD::EventInfo::SCT::Error"); + } else { + m_propagationError = 0; + } + // get geometry context FaserActsGeometryContext faserGeometryContext = m_trackingGeometryTool->getNominalGeometryContext(); auto gctx = faserGeometryContext.context(); @@ -766,8 +834,16 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const // loop over all reconstructed tracks and use only the tracks that have hits in all three tracking stations (excludes IFT) // store track parameters at most upstream measurement and at most downstream measurement // extrapolate track to all scintillator positions and store extrapolated position and angle - SG::ReadHandle<TrackCollection> trackCollection {m_trackCollectionWithoutIFT, ctx}; // use track collection that excludes IFT + SG::ReadHandle<TrackCollection> trackCollection; + if (m_useIFT) { + SG::ReadHandle<TrackCollection> tc {m_trackCollection, ctx}; // use track collection that excludes IFT + trackCollection = tc; + } else { + SG::ReadHandle<TrackCollection> tc {m_trackCollectionWithoutIFT, ctx}; // use track collection that excludes IFT + trackCollection = tc; + } ATH_CHECK(trackCollection.isValid()); + for (const Trk::Track* track : *trackCollection) { if (track == nullptr) continue; @@ -1219,7 +1295,20 @@ NtupleDumperAlg::clearTree() const m_truthBarcode = 0; m_truthPdg = 0; - m_truthM_P.clear(); + m_truth_P.clear(); + m_truth_px.clear(); + m_truth_py.clear(); + m_truth_pz.clear(); + m_truth_m.clear(); + m_truth_pdg.clear(); + m_truth_prod_x.clear(); + m_truth_prod_y.clear(); + m_truth_prod_z.clear(); + m_truth_dec_x.clear(); + m_truth_dec_y.clear(); + m_truth_dec_z.clear(); + + m_truthM_P.clear(); m_truthM_px.clear(); m_truthM_py.clear(); m_truthM_pz.clear(); diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h index 5cc342021993ca07354e6a4924f7e5cfb07ae552..6d4ccc381a4b65831678a657c035ecc4e79c2b65 100644 --- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h +++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h @@ -21,6 +21,8 @@ #include "FaserActsKalmanFilter/IFiducialParticleTool.h" #include "FaserActsKalmanFilter/ITrackTruthMatchingTool.h" #include "TrackerSimEvent/FaserSiHitCollection.h" +#include "xAODEventInfo/EventInfo.h" +#include "StoreGate/ReadDecorHandle.h" #include <vector> @@ -84,7 +86,8 @@ private: SG::ReadHandleKey<xAOD::FaserTriggerData> m_FaserTriggerData { this, "FaserTriggerDataKey", "FaserTriggerData", "ReadHandleKey for xAOD::FaserTriggerData"}; SG::ReadHandleKey<xAOD::WaveformClock> m_ClockWaveformContainer { this, "WaveformClockKey", "WaveformClock", "ReadHandleKey for ClockWaveforms Container"}; - ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool { this, "ExtrapolationTool", "FaserActsExtrapolationTool" }; + SG::ReadDecorHandleKey<xAOD::EventInfo> m_eventInfoKey{this, "EventInfoKey", "EventInfo"}; + ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool { this, "ExtrapolationTool", "FaserActsExtrapolationTool" }; ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"}; ToolHandle<ITrackTruthMatchingTool> m_trackTruthMatchingTool {this, "TrackTruthMatchingTool", "TrackTruthMatchingTool"}; ToolHandle<IFiducialParticleTool> m_fiducialParticleTool {this, "FiducialParticleTool", "FiducialParticleTool"}; @@ -98,6 +101,8 @@ private: const PreshowerID* m_preshowerHelper; const EcalID* m_ecalHelper; + BooleanProperty m_useIFT { this, "UseIFT", false, "Use IFT tracks" }; + BooleanProperty m_doCalib { this, "DoCalib", true, "Fill calibrated calorimeter quantities" }; BooleanProperty m_doBlinding { this, "DoBlinding", true, "Blinding will not output events with Calo signal > 10 GeV e-" }; 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" }; @@ -106,6 +111,8 @@ private: DoubleProperty m_genieLuminosity { this, "GenieLuminosity", 150.0, "Genie luminosity in inverse fb." }; DoubleProperty m_minMomentum { this, "MinMomentum", 50000.0, "Write out all truth particles with a momentum larger MinMomentum"}; + DoubleProperty m_blindingCaloThreshold {this, "BlindingCaloThreshold", 25000.0, "Blind events with Ecal energy above threshold (in MeV)"}; + double m_baseEventCrossSection {1.0}; const double kfemtoBarnsPerMilliBarn {1.0e12}; @@ -189,6 +196,7 @@ private: mutable std::vector<double> m_trackseg_pz; mutable int m_longTracks; + mutable int m_propagationError; mutable std::vector<double> m_Chi2; mutable std::vector<double> m_DoF; mutable std::vector<double> m_xup; @@ -274,7 +282,7 @@ private: mutable std::vector<double> m_truthM_py; mutable std::vector<double> m_truthM_pz; - mutable std::vector<double> m_truthM_x; + mutable std::vector<double> m_truthM_x; // decay vertex of A' mutable std::vector<double> m_truthM_y; mutable std::vector<double> m_truthM_z; @@ -284,7 +292,7 @@ private: mutable std::vector<double> m_truthd0_py; mutable std::vector<double> m_truthd0_pz; - mutable std::vector<double> m_truthd0_x; + mutable std::vector<double> m_truthd0_x; // production vertex for daughter particles mutable std::vector<double> m_truthd0_y; mutable std::vector<double> m_truthd0_z; @@ -298,6 +306,27 @@ private: mutable std::vector<double> m_truthd1_y; mutable std::vector<double> m_truthd1_z; + // first 10 truth particles + + mutable std::vector<double> m_truth_P; + mutable std::vector<double> m_truth_px; + mutable std::vector<double> m_truth_py; + mutable std::vector<double> m_truth_pz; + mutable std::vector<double> m_truth_m; + + mutable std::vector<double> m_truth_dec_x; // components of decay vertex (mm) + mutable std::vector<double> m_truth_dec_y; + mutable std::vector<double> m_truth_dec_z; + + mutable std::vector<double> m_truth_prod_x; // components of production vertex (mm) + mutable std::vector<double> m_truth_prod_y; + mutable std::vector<double> m_truth_prod_z; + + mutable std::vector<int> m_truth_pdg; // pdg of first 10 truth particles + + + + mutable double m_truthLeptonMomentum; mutable int m_truthBarcode; mutable int m_truthPdg; diff --git a/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx b/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx index ea03a77f16d12cd4cb45c99c96a20c1fbc8323e7..1869860e84a401fffbfe6a588d6b8e78ba702e9a 100644 --- a/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx +++ b/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx @@ -3,6 +3,7 @@ #include "ScintSimEvent/ScintHitIdHelper.h" #include <map> +#include <cmath> ScintWaveformDigiAlg::ScintWaveformDigiAlg(const std::string& name, @@ -110,42 +111,68 @@ ScintWaveformDigiAlg::execute(const EventContext& ctx) const { waveforms = m_digiTool->create_waveform_map(m_preshowerID); } + // Sum energy for each channel (i.e. identifier) + std::map<Identifier, float> esum; + + Identifier id; + for (const auto& hit : *scintHitHandle) { + if (first.isTrigger()) { + Identifier plate_id = m_triggerID->plate_id(hit.getIdentifier()); + // Need to do something better here, but for now just fill both + id = m_triggerID->pmt_id(plate_id, 0); // ID for PMT 0 + esum[id] += hit.energyLoss(); + id = m_triggerID->pmt_id(plate_id, 1); // ID for PMT 1 + esum[id] += hit.energyLoss(); + } else { + // All others have only 1 PMT + // Use detector id (not hit id) throughout + id = hit.getIdentifier(); + esum[id] += hit.energyLoss(); + } + } + // Loop over time samples - // Should really do sums first, as repeating these in the loop is a waste for (const auto& tk : m_timekernel) { std::map<Identifier, float> counts; - // Convolve hit energy with evaluated kernel and sum for each hit id (i.e. channel) - Identifier id; - for (const auto& hit : *scintHitHandle) { - - // Special handling for trigger scintillator - if (first.isTrigger()) { - // Hits are created per plate - // Need to split between 2 PMTs - - // Identifier from hit is plate ID - Identifier plate_id = m_triggerID->plate_id(hit.getIdentifier()); - - // Need to do something better here, but for now just fill both - id = m_triggerID->pmt_id(plate_id, 0); // ID for PMT 0 - counts[id] += tk * hit.energyLoss(); - id = m_triggerID->pmt_id(plate_id, 1); // ID for PMT 1 - counts[id] += tk * hit.energyLoss(); - - } else { - // All others have only 1 PMT - // Use detector id (not hit id) throughout - id = hit.getIdentifier(); - counts[id] += tk * hit.energyLoss(); - } +// // Convolve hit energy with evaluated kernel and sum for each hit id (i.e. channel) +// Identifier id; +// for (const auto& hit : *scintHitHandle) { +// +// // Special handling for trigger scintillator +// if (first.isTrigger()) { +// // Hits are created per plate +// // Need to split between 2 PMTs +// +// // Identifier from hit is plate ID +// Identifier plate_id = m_triggerID->plate_id(hit.getIdentifier()); +// +// // Need to do something better here, but for now just fill both +// id = m_triggerID->pmt_id(plate_id, 0); // ID for PMT 0 +// counts[id] += tk * hit.energyLoss(); +// id = m_triggerID->pmt_id(plate_id, 1); // ID for PMT 1 +// counts[id] += tk * hit.energyLoss(); +// +// } else { +// // All others have only 1 PMT +// // Use detector id (not hit id) throughout +// id = hit.getIdentifier(); +// counts[id] += tk * hit.energyLoss(); +// } +// } + + // Convolve summed energy with evaluated kernel for each hit id (i.e. channel) + for (const auto& e : esum) { + // Convert hit id to Identifier and store + id = e.first; + counts[id] = tk * e.second; } // Subtract count from basleine and add result to correct waveform vector for (const auto& c : counts) { - unsigned int baseline = m_digiTool->generate_baseline(m_base_mean, m_base_rms); - int value = baseline - c.second; + float baseline = m_digiTool->generate_baseline(m_base_mean, m_base_rms); + int value = std::round(baseline - c.second); if (value < 0) { ATH_MSG_WARNING("Found pulse " << c.second << " larger than baseline " << c.first); diff --git a/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.cxx b/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.cxx index ea353da6c61a80535c20a3f92f3598a227b65b0f..bada70e4db0f3e105788e0042f3986ffef3e4bef 100644 --- a/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.cxx +++ b/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.cxx @@ -170,6 +170,20 @@ StatusCode FaserSCT_SurfaceChargesGenerator::initialize() { // ---------------------------------------------------------------------- StatusCode FaserSCT_SurfaceChargesGenerator::finalize() { ATH_MSG_DEBUG("FaserSCT_SurfaceChargesGenerator::finalize()"); + + if (m_tofNum > 0) { + float mean_TOF = m_tofSum / m_tofNum; + float rms_TOF = sqrt(m_tofSum2 / m_tofNum - mean_TOF*mean_TOF); + ATH_MSG_INFO("TOF hits: " << m_tofNum); + ATH_MSG_INFO("Mean TOF: " << mean_TOF); + ATH_MSG_INFO("RMS TOF: " << rms_TOF); + + } + + if (m_tfix > -998.) { + ATH_MSG_INFO("Used fixed value: " << m_tfix.value()); + } + return StatusCode::SUCCESS; } @@ -335,13 +349,22 @@ void FaserSCT_SurfaceChargesGenerator::processSiHit(const SiDetectorElement* ele float timeOfFlight{p_eventTime + hitTime(phit)}; // Kondo 19/09/2007: Use the coordinate of the center of the module to calculate the time of flight - timeOfFlight -= (element->center().mag()) / CLHEP::c_light; + // Torrence 19/02/2023: Use global z coordinate rather than distance to origin for FASER + timeOfFlight -= (element->center().z()) / CLHEP::c_light; // !< extract the distance to the origin of the module to Time of flight // !< timing set from jo to adjust (subtract) the timing if (m_tsubtract > -998.) { timeOfFlight -= m_tsubtract; } + + ATH_MSG_DEBUG("Time of flight: " << timeOfFlight); + + // Keep some stats + m_tofNum += 1; + m_tofSum += timeOfFlight; + m_tofSum2 += (timeOfFlight * timeOfFlight); + // ---************************************** const CLHEP::Hep3Vector pos{phit.localStartPosition()}; @@ -512,6 +535,7 @@ void FaserSCT_SurfaceChargesGenerator::processSiHit(const SiDetectorElement* ele const float sdist{static_cast<float>(design->scaledDistanceToNearestDiode(position))}; // !< dist on the surface from the hit point to the nearest strip (diode) const float t_surf{surfaceDriftTime(2.0 * sdist)}; // !< Surface drift time const float totaltime{(m_tfix > -998.) ? m_tfix.value() : t_drift + timeOfFlight + t_surf}; // !< Total drift time + ATH_MSG_VERBOSE(std::fixed << std::setprecision(4) << "Surface time: " << t_surf << " Drift time: " << t_drift << " TOF: " << timeOfFlight << " Total: " << totaltime); inserter(SiSurfaceCharge(position, SiCharge(q1, totaltime, hitproc, trklink))); } else { ATH_MSG_VERBOSE(std::fixed << std::setprecision(8) << "Local position (phi, eta, depth): (" diff --git a/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.h b/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.h index b84f584c52ad1ac182f237d62a7639aeb0fe2f13..4e9ec5775edc2c3b90fd095c411e66435bc7d754 100644 --- a/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.h +++ b/Tracker/TrackerDigitization/FaserSCT_Digitization/src/FaserSCT_SurfaceChargesGenerator.h @@ -137,6 +137,12 @@ class FaserSCT_SurfaceChargesGenerator : public extends<AthAlgTool, ISCT_Surface bool m_SurfaceDriftFlag{false}; //!< surface drift ON/OFF + // Keep track of TOF + // These are updated in processSiHit which is const, so make mutable + mutable int m_tofNum{0}; + mutable float m_tofSum{0.}; + mutable float m_tofSum2{0.}; + // -- Histograms // TProfile* m_h_efieldz{nullptr}; // TH1F* m_h_efield{nullptr}; diff --git a/Tracking/Acts/FaserActsKalmanFilter/python/CKF2Config.py b/Tracking/Acts/FaserActsKalmanFilter/python/CKF2Config.py index 0c890e1eb0542037393884a0d181abe9799c6664..5d6ed5c4f3170cfc57b058d6bc9432329c1da561 100644 --- a/Tracking/Acts/FaserActsKalmanFilter/python/CKF2Config.py +++ b/Tracking/Acts/FaserActsKalmanFilter/python/CKF2Config.py @@ -107,9 +107,7 @@ def CKF2Cfg(flags, **kwargs): ckf.PerformanceWriter = False ckf.nMax = 10 - # Use larger chi2 cut until alignment improves - # ckf.chi2Max = 25 - ckf.chi2Max = 100000 + ckf.chi2Max = 25 # Protect against running out of memory on busy events ckf.maxSteps = 5000 acc.addEventAlgo(ckf) diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx index 3647ac98abdd9e5176fb0902fb368e89dc741dcb..f990be9458e0ee2ac0a5bdef8b670703f1424c26 100644 --- a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx +++ b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx @@ -2,6 +2,7 @@ #include "StoreGate/ReadHandle.h" #include "StoreGate/ReadCondHandleKey.h" +#include "StoreGate/WriteDecorHandle.h" #include "TrackerSpacePoint/FaserSCT_SpacePointCollection.h" #include "TrackerSpacePoint/FaserSCT_SpacePoint.h" #include "TrackerIdentifier/FaserSCT_ID.h" @@ -28,9 +29,9 @@ #include "Acts/Propagator/Propagator.hpp" #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" - - #include "Acts/EventData/Measurement.hpp" +#include "Acts/Propagator/PropagatorError.hpp" +#include "Acts/TrackFinding/CombinatorialKalmanFilterError.hpp" size_t CKF2::TrajectoryInfo::nClusters {0}; @@ -49,6 +50,7 @@ StatusCode CKF2::initialize() { ATH_CHECK(m_kalmanFitterTool1.retrieve()); ATH_CHECK(m_createTrkTrackTool.retrieve()); ATH_CHECK(m_trackCollection.initialize()); + ATH_CHECK(m_eventInfoKey.initialize()); if (m_performanceWriter && !m_noDiagnostics) { ATH_CHECK(m_performanceWriterTool.retrieve()); } @@ -78,6 +80,8 @@ StatusCode CKF2::execute() { const EventContext& ctx = Gaudi::Hive::currentContext(); m_numberOfEvents++; + SG::WriteDecorHandle<xAOD::EventInfo,uint32_t> eventInfo (m_eventInfoKey, ctx); + SG::WriteHandle trackContainer{m_trackCollection, ctx}; std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>(); @@ -150,6 +154,14 @@ StatusCode CKF2::execute() { std::list<TrajectoryInfo> allTrajectories; for (auto &result : results) { if (not result.ok()) { + // TODO use status bits for different errors + // result.error() == Acts::CombinatorialKalmanFilterError::NoTrackFound + if (result.error() == Acts::PropagatorError::StepCountLimitReached || + result.error() == Acts::CombinatorialKalmanFilterError::PropagationReachesMaxSteps) { + if (!eventInfo->updateErrorState(xAOD::EventInfo::SCT, xAOD::EventInfo::Error)) { + ATH_MSG_WARNING (" cannot set error state for SCT"); + } + } continue; } CKFResult ckfResult = result.value(); diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h index 2111afcf6b52d46b45e1e2539d105290b937689e..170f0353904bef82095146c50bf98d5721ef2138 100644 --- a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h +++ b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h @@ -21,6 +21,7 @@ #include "KalmanFitterTool.h" #include <boost/dynamic_bitset.hpp> #include "CreateTrkTrackTool.h" +#include "xAODEventInfo/EventInfo.h" using ConstTrackStateProxy = Acts::detail_lt::TrackStateProxy<IndexSourceLink, 6, true>; using ClusterSet = boost::dynamic_bitset<>; @@ -148,6 +149,7 @@ private: ToolHandle<CreateTrkTrackTool> m_createTrkTrackTool {this, "CreateTrkTrackTool", "CreateTrkTrackTool"}; Gaudi::Property<bool> m_isMC {this, "isMC", false}; SG::WriteHandleKey<TrackCollection> m_trackCollection { this, "OutputCollection", "CKFTrackCollection", "Output track collection name" }; + SG::WriteDecorHandleKey<xAOD::EventInfo> m_eventInfoKey{this, "EventInfoKey", "EventInfo"}; }; #endif // FASERACTSKALMANFILTER_CKF2_H diff --git a/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h b/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h index c30351902a1b79e9a58ad33d5f58a5d04ed13faa..37cb7af183c2d89b9ada3579f76820bfe6487c5c 100644 --- a/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h +++ b/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h @@ -51,7 +51,7 @@ public: virtual std::vector<float> evaluate_timekernel(TF1* kernel) const = 0; /// Generate random baseline - virtual unsigned int generate_baseline(int mean, int rms) const = 0; + virtual float generate_baseline(float mean, float rms) const = 0; /// Create structure to store pulse for each channel template <class T> diff --git a/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx index c62d7f8a753490907bd1e8a513cad5341a2a46be..3a48027d57e3710a2ec592aed6a9ed749b9f6848 100644 --- a/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx +++ b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx @@ -22,7 +22,7 @@ WaveformDigitisationTool::initialize() { ATH_MSG_INFO( name() << "::initalize()" ); m_nsamples = 600; - m_random = new TRandom3(); + m_random = new TRandom3(0); return StatusCode::SUCCESS; } @@ -41,7 +41,7 @@ WaveformDigitisationTool::evaluate_timekernel(TF1* kernel) const { return timekernel; } -unsigned int -WaveformDigitisationTool::generate_baseline(int mean, int rms) const { +float +WaveformDigitisationTool::generate_baseline(float mean, float rms) const { return m_random->Gaus(mean, rms); } diff --git a/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h index e2dd5169152845824927baeeae7ce8fc36ab46f8..b6554c337830fbd2183752cc1b05123ea827b4f1 100644 --- a/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h +++ b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h @@ -32,7 +32,7 @@ class WaveformDigitisationTool: public extends<AthAlgTool, IWaveformDigitisation std::vector<float> evaluate_timekernel(TF1* kernel) const; /// Generate random baseline - unsigned int generate_baseline(int mean, int rms) const; + float generate_baseline(float mean, float rms) const; private: