diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f3372b57..dde45803 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -121,69 +121,75 @@ common:setup: - runner_system_failure include: - - local: 'benchmarks/backgrounds/config.yml' - - local: 'benchmarks/backwards_ecal/config.yml' - - local: 'benchmarks/beamline/config.yml' - - local: 'benchmarks/calo_pid/config.yml' - - local: 'benchmarks/ecal_gaps/config.yml' - - local: 'benchmarks/tracking_detectors/config.yml' - - local: 'benchmarks/tracking_performances/config.yml' - - local: 'benchmarks/tracking_performances_dis/config.yml' - - local: 'benchmarks/barrel_ecal/config.yml' - - local: 'benchmarks/lfhcal/config.yml' - - local: 'benchmarks/zdc/config.yml' - - local: 'benchmarks/zdc_lyso/config.yml' - - local: 'benchmarks/zdc_neutron/config.yml' - - local: 'benchmarks/zdc_photon/config.yml' - - local: 'benchmarks/zdc_pi0/config.yml' - - local: 'benchmarks/material_scan/config.yml' - - local: 'benchmarks/pid/config.yml' - - local: 'benchmarks/rich/config.yml' - - local: 'benchmarks/timing/config.yml' - - local: 'benchmarks/b0_tracker/config.yml' - - local: 'benchmarks/insert_muon/config.yml' - - local: 'benchmarks/insert_tau/config.yml' - - local: 'benchmarks/zdc_sigma/config.yml' - - local: 'benchmarks/zdc_lambda/config.yml' - - local: 'benchmarks/insert_neutron/config.yml' - - local: 'benchmarks/femc_electron/config.yml' - - local: 'benchmarks/femc_photon/config.yml' - - local: 'benchmarks/femc_pi0/config.yml' - - local: 'benchmarks/nhcal_acceptance/config.yml' + # - local: 'benchmarks/backgrounds/config.yml' + # - local: 'benchmarks/backwards_ecal/config.yml' + # - local: 'benchmarks/beamline/config.yml' + # - local: 'benchmarks/calo_pid/config.yml' + # - local: 'benchmarks/ecal_gaps/config.yml' + # - local: 'benchmarks/tracking_detectors/config.yml' + # - local: 'benchmarks/tracking_performances/config.yml' + # - local: 'benchmarks/tracking_performances_dis/config.yml' + # - local: 'benchmarks/barrel_ecal/config.yml' + # - local: 'benchmarks/lfhcal/config.yml' + # - local: 'benchmarks/zdc/config.yml' + # - local: 'benchmarks/zdc_lyso/config.yml' + # - local: 'benchmarks/zdc_neutron/config.yml' + # - local: 'benchmarks/zdc_photon/config.yml' + # - local: 'benchmarks/zdc_pi0/config.yml' + # - local: 'benchmarks/material_scan/config.yml' + # - local: 'benchmarks/pid/config.yml' + # - local: 'benchmarks/rich/config.yml' + # - local: 'benchmarks/timing/config.yml' + # - local: 'benchmarks/b0_tracker/config.yml' + # - local: 'benchmarks/insert_muon/config.yml' + # - local: 'benchmarks/insert_tau/config.yml' + # - local: 'benchmarks/zdc_sigma/config.yml' + # - local: 'benchmarks/zdc_lambda/config.yml' + # - local: 'benchmarks/insert_neutron/config.yml' + # - local: 'benchmarks/femc_electron/config.yml' + # - local: 'benchmarks/femc_photon/config.yml' + # - local: 'benchmarks/femc_pi0/config.yml' + # - local: 'benchmarks/nhcal_acceptance/config.yml' - local: 'benchmarks/nhcal_basic_distribution/config.yml' + - local: 'benchmarks/nhcal_sampling_fraction/config.yml' + - local: 'benchmarks/nhcal_dimuon_fotoproduction/config.yml' + - local: 'benchmarks/nhcal_pion_rejection/config.yml' deploy_results: allow_failure: true stage: deploy needs: - - "collect_results:backgrounds" - - "collect_results:backwards_ecal" - - "collect_results:barrel_ecal" - - "collect_results:beamline" - - "collect_results:calo_pid" - - "collect_results:ecal_gaps" - - "collect_results:lfhcal" - - "collect_results:material_scan" - - "collect_results:pid" - - "collect_results:rich" - - "collect_results:tracking_performance" - - "collect_results:tracking_performance_campaigns" - - "collect_results:zdc_sigma" - - "collect_results:zdc_lambda" - - "collect_results:insert_neutron" - - "collect_results:tracking_performances_dis" - - "collect_results:zdc" - - "collect_results:zdc_lyso" - - "collect_results:zdc_neutron" - - "collect_results:insert_muon" - - "collect_results:insert_tau" - - "collect_results:zdc_photon" - - "collect_results:zdc_pi0" - - "collect_results:femc_electron" - - "collect_results:femc_photon" - - "collect_results:femc_pi0" - - "collect_results:nhcal_acceptance" + # - "collect_results:backgrounds" + # - "collect_results:backwards_ecal" + # - "collect_results:barrel_ecal" + # - "collect_results:beamline" + # - "collect_results:calo_pid" + # - "collect_results:ecal_gaps" + # - "collect_results:lfhcal" + # - "collect_results:material_scan" + # - "collect_results:pid" + # - "collect_results:rich" + # - "collect_results:tracking_performance" + # - "collect_results:tracking_performance_campaigns" + # - "collect_results:zdc_sigma" + # - "collect_results:zdc_lambda" + # - "collect_results:insert_neutron" + # - "collect_results:tracking_performances_dis" + # - "collect_results:zdc" + # - "collect_results:zdc_lyso" + # - "collect_results:zdc_neutron" + # - "collect_results:insert_muon" + # - "collect_results:insert_tau" + # - "collect_results:zdc_photon" + # - "collect_results:zdc_pi0" + # - "collect_results:femc_electron" + # - "collect_results:femc_photon" + # - "collect_results:femc_pi0" + # - "collect_results:nhcal_acceptance" - "collect_results:nhcal_basic_distribution" + - "collect_results:nhcal_sampling_fraction" + - "collect_results:nhcal_dimuon_fotoproduction" + - "collect_results:nhcal_pion_rejection" script: - snakemake $SNAKEMAKE_FLAGS --cores 1 results/metadata.json - find results -print | sort | tee summary.txt diff --git a/Snakefile b/Snakefile index 4526fcc9..0be9337d 100644 --- a/Snakefile +++ b/Snakefile @@ -30,30 +30,33 @@ def find_epic_libraries(): return libs -include: "benchmarks/backgrounds/Snakefile" -include: "benchmarks/backwards_ecal/Snakefile" -include: "benchmarks/barrel_ecal/Snakefile" -include: "benchmarks/beamline/Snakefile" -include: "benchmarks/calo_pid/Snakefile" -include: "benchmarks/ecal_gaps/Snakefile" -include: "benchmarks/material_scan/Snakefile" -include: "benchmarks/tracking_performances/Snakefile" -include: "benchmarks/tracking_performances_dis/Snakefile" -include: "benchmarks/lfhcal/Snakefile" -include: "benchmarks/zdc_lyso/Snakefile" -include: "benchmarks/zdc_neutron/Snakefile" -include: "benchmarks/insert_muon/Snakefile" -include: "benchmarks/zdc_lambda/Snakefile" -include: "benchmarks/zdc_photon/Snakefile" -include: "benchmarks/zdc_pi0/Snakefile" -include: "benchmarks/zdc_sigma/Snakefile" -include: "benchmarks/insert_neutron/Snakefile" -include: "benchmarks/insert_tau/Snakefile" -include: "benchmarks/femc_electron/Snakefile" -include: "benchmarks/femc_photon/Snakefile" -include: "benchmarks/femc_pi0/Snakefile" -include: "benchmarks/nhcal_acceptance/Snakefile" +# include: "benchmarks/backgrounds/Snakefile" +# include: "benchmarks/backwards_ecal/Snakefile" +# include: "benchmarks/barrel_ecal/Snakefile" +# include: "benchmarks/beamline/Snakefile" +# include: "benchmarks/calo_pid/Snakefile" +# include: "benchmarks/ecal_gaps/Snakefile" +# include: "benchmarks/material_scan/Snakefile" +# include: "benchmarks/tracking_performances/Snakefile" +# include: "benchmarks/tracking_performances_dis/Snakefile" +# include: "benchmarks/lfhcal/Snakefile" +# include: "benchmarks/zdc_lyso/Snakefile" +# include: "benchmarks/zdc_neutron/Snakefile" +# include: "benchmarks/insert_muon/Snakefile" +# include: "benchmarks/zdc_lambda/Snakefile" +# include: "benchmarks/zdc_photon/Snakefile" +# include: "benchmarks/zdc_pi0/Snakefile" +# include: "benchmarks/zdc_sigma/Snakefile" +# include: "benchmarks/insert_neutron/Snakefile" +# include: "benchmarks/insert_tau/Snakefile" +# include: "benchmarks/femc_electron/Snakefile" +# include: "benchmarks/femc_photon/Snakefile" +# include: "benchmarks/femc_pi0/Snakefile" +# include: "benchmarks/nhcal_acceptance/Snakefile" include: "benchmarks/nhcal_basic_distribution/Snakefile" +include: "benchmarks/nhcal_sampling_fraction/Snakefile" +include: "benchmarks/nhcal_dimuon_fotoproduction/Snakefile" +include: "benchmarks/nhcal_pion_rejection/Snakefile" use_s3 = config["remote_provider"].lower() == "s3" use_xrootd = config["remote_provider"].lower() == "xrootd" diff --git a/benchmarks/nhcal_basic_distribution/Snakefile b/benchmarks/nhcal_basic_distribution/Snakefile index 27312667..1cd8283d 100644 --- a/benchmarks/nhcal_basic_distribution/Snakefile +++ b/benchmarks/nhcal_basic_distribution/Snakefile @@ -1,6 +1,13 @@ +import glob +from pathlib import Path + +def get_energy_resolution_files(wildcards): + pattern = f"sim_output/nhcal_basic_distribution/energy_resolution_E*GeV_{wildcards.DETECTOR_CONFIG}_*files.root" + return sorted(glob.glob(pattern)) + rule nhcal_basic_distribution_simulate: input: - warmup=ancient("warmup/{DETECTOR_CONFIG}.edm4hep.root"), + warmup=ancient("warmup/{DETECTOR_CONFIG}.edm4hep.root"), output: "sim_output/nhcal_basic_distribution/E{ENERGY}GeV/sim_{DETECTOR_CONFIG}.{INDEX}.edm4hep.root", params: @@ -11,28 +18,28 @@ rule nhcal_basic_distribution_simulate: ENERGY=lambda wildcards: wildcards.ENERGY, DD4HEP_HASH=get_spack_package_hash("dd4hep"), NPSIM_HASH=get_spack_package_hash("npsim"), - cache: True + # cache: True shell: """ -exec ddsim \ - --compactFile {params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}.xml \ - --numberOfEvents {params.N_EVENTS} \ - --random.seed {params.SEED} \ - --enableGun \ - -v WARNING \ - --gun.particle neutron \ - --gun.thetaMin 120*degree \ - --gun.thetaMax 180*degree \ - --gun.distribution uniform \ - --gun.energy "{params.ENERGY}*GeV" \ - --outputFile {output} -""" + exec npsim \ + --compactFile {params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}.xml \ + --numberOfEvents {params.N_EVENTS} \ + --random.seed {params.SEED} \ + --enableGun \ + -v WARNING \ + --gun.particle neutron \ + --gun.thetaMin 120*degree \ + --gun.thetaMax 180*degree \ + --gun.distribution uniform \ + --gun.energy "{params.ENERGY}*GeV" \ + --outputFile {output} + """ rule nhcal_basic_distribution_combine: input: lambda wildcards: expand( - "sim_output/nhcal_basic_distribution/E{ENERGY:.1f}GeV/sim_{DETECTOR_CONFIG}.{INDEX:02d}.edm4hep.root", + "sim_output/nhcal_basic_distribution/E{ENERGY:.1f}GeV/sim_{DETECTOR_CONFIG}.{INDEX}.edm4hep.root", DETECTOR_CONFIG=wildcards.DETECTOR_CONFIG, ENERGY=float(wildcards.ENERGY), INDEX=range(int(wildcards.N)), @@ -41,20 +48,43 @@ rule nhcal_basic_distribution_combine: N=r"\d+", ENERGY=r"\d+(\.\d+)?" output: - temp("sim_output/nhcal_basic_distribution/sim_{DETECTOR_CONFIG}_E{ENERGY}GeV_combined_{N}files.edm4hep.root"), + temp("sim_output/nhcal_basic_distribution/sim_E{ENERGY}GeV_{DETECTOR_CONFIG}_{N}files.edm4hep.root"), shell: """ -hadd -f {output} {input} -""" + hadd -f {output} {input} + """ + rule nhcal_basic_distribution_analysis: input: - combined="sim_output/nhcal_basic_distribution/sim_{DETECTOR_CONFIG}_E{ENERGY}GeV_combined_{N}files.edm4hep.root", + combined="sim_output/nhcal_basic_distribution/sim_E{ENERGY}GeV_{DETECTOR_CONFIG}_{N}files.edm4hep.root", script="benchmarks/nhcal_basic_distribution/scripts/basic_distribution_analysis.cxx", output: - pdf=f"results/nhcal_basic_distribution/analysis_{{DETECTOR_CONFIG}}_E{{ENERGY}}GeV_combined_{{N}}files.pdf", - png=f"results/nhcal_basic_distribution/analysis_{{DETECTOR_CONFIG}}_E{{ENERGY}}GeV_combined_{{N}}files.png", + png="results/nhcal_basic_distribution/analysis_E{ENERGY}GeV_{DETECTOR_CONFIG}_{N}files.png", + root="sim_output/nhcal_basic_distribution/energy_resolution_E{ENERGY}GeV_{DETECTOR_CONFIG}_{N}files.root", + shell: + """ + root -l -b -q '{input.script}("{input.combined}","{output.png}","{output.root}")' + """ + +rule nhcal_basic_distribution_combine_energy_resolution: + input: + get_energy_resolution_files + output: + temp("sim_output/nhcal_basic_distribution/energy_resolution_combined_{DETECTOR_CONFIG}.root"), shell: """ - root -l -b -q '{input.script}("{input.combined}","{output.pdf}","{output.png}")' -""" + hadd -f {output} {input} + """ + +rule nhcal_basic_distribution_analysis_energy_resolution: + input: + root="sim_output/nhcal_basic_distribution/energy_resolution_combined_{DETECTOR_CONFIG}.root", + script="benchmarks/nhcal_basic_distribution/scripts/basic_distribution_energy_resolution.cxx", + output: + png="results/nhcal_basic_distribution/energy_resolution_analysis_{DETECTOR_CONFIG}.png", + shell: + """ + root -l -b -q '{input.script}("{input.root}","{output.png}")' + """ + diff --git a/benchmarks/nhcal_basic_distribution/config.yml b/benchmarks/nhcal_basic_distribution/config.yml index 9c88ad33..81e65a68 100644 --- a/benchmarks/nhcal_basic_distribution/config.yml +++ b/benchmarks/nhcal_basic_distribution/config.yml @@ -3,20 +3,20 @@ sim:nhcal_basic_distribution: stage: simulate parallel: matrix: - - ENERGY: ["0.5GeV", "0.7GeV", "1.0GeV", "2.0GeV", "5.0GeV"] + - ENERGY: ["0.5", "0.7", "1.0", "2.0", "5.0"] INDEX_RANGE: ["0 4","5 9"] script: - - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB $(for INDEX in $(seq -f '%02.0f' $INDEX_RANGE); do echo sim_output/nhcal_basic_distribution/E${ENERGY}/sim_epic_backward_hcal_only.${INDEX}.edm4hep.root; done) + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB $(for INDEX in $(seq -f '%02.0f' $INDEX_RANGE); do echo sim_output/nhcal_basic_distribution/E${ENERGY}GeV/sim_epic_backward_hcal_only.${INDEX}.edm4hep.root; done) sim:nhcal_basic_distribution_full: extends: .det_benchmark stage: simulate parallel: matrix: - - ENERGY: ["0.5GeV", "0.7GeV", "1.0GeV", "2.0GeV", "5.0GeV"] + - ENERGY: ["0.5", "0.7", "1.0", "2.0", "5.0"] INDEX_RANGE: ["0 4","5 9"] script: - - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB $(for INDEX in $(seq -f '%02.0f' $INDEX_RANGE); do echo sim_output/nhcal_basic_distribution/E${ENERGY}/sim_epic_full.${INDEX}.edm4hep.root; done) + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB $(for INDEX in $(seq -f '%02.0f' $INDEX_RANGE); do echo sim_output/nhcal_basic_distribution/E${ENERGY}GeV/sim_epic_full.${INDEX}.edm4hep.root; done) bench:nhcal_basic_distribution_analysis: extends: .det_benchmark @@ -25,9 +25,16 @@ bench:nhcal_basic_distribution_analysis: - "sim:nhcal_basic_distribution" parallel: matrix: - - ENERGY: ["0.5GeV", "0.7GeV", "1.0GeV", "2.0GeV", "5.0GeV"] + - ENERGY: ["0.5", "0.7", "1.0", "2.0", "5.0"] script: - - snakemake $SNAKEMAKE_FLAGS --cores 1 results/nhcal_basic_distribution/analysis_epic_backward_hcal_only_E${ENERGY}_combined_10files.pdf + - | + for ENERGY in 0.5 0.7 1.0 2.0 5.0; do + snakemake $SNAKEMAKE_FLAGS --cores 1 \ + results/nhcal_basic_distribution/analysis_E${ENERGY}GeV_epic_backward_hcal_only_10files.png \ + sim_output/nhcal_basic_distribution/energy_resolution_E${ENERGY}GeV_epic_backward_hcal_only.root + done + - snakemake $SNAKEMAKE_FLAGS --cores 1 \ + results/nhcal_basic_distribution/energy_resolution_analysis_epic_backward_hcal_only.png bench:nhcal_basic_distribution_analysis_full: extends: .det_benchmark @@ -36,9 +43,16 @@ bench:nhcal_basic_distribution_analysis_full: - "sim:nhcal_basic_distribution_full" parallel: matrix: - - ENERGY: ["0.5GeV", "0.7GeV", "1.0GeV", "2.0GeV", "5.0GeV"] + - ENERGY: ["0.5", "0.7", "1.0", "2.0", "5.0"] script: - - snakemake $SNAKEMAKE_FLAGS --cores 1 results/nhcal_basic_distribution/analysis_epic_full_E${ENERGY}_combined_10files.pdf + - | + for ENERGY in 0.5 0.7 1.0 2.0 5.0; do + snakemake $SNAKEMAKE_FLAGS --cores 1 \ + results/nhcal_basic_distribution/analysis_E${ENERGY}GeV_epic_full_10files.png \ + sim_output/nhcal_basic_distribution/energy_resolution_E${ENERGY}GeV_epic_full.root + done + - snakemake $SNAKEMAKE_FLAGS --cores 1 \ + results/nhcal_basic_distribution/energy_resolution_analysis_epic_full.png collect_results:nhcal_basic_distribution: @@ -49,11 +63,10 @@ collect_results:nhcal_basic_distribution: - "bench:nhcal_basic_distribution_analysis_full" parallel: matrix: - - ENERGY: ["0.5GeV", "0.7GeV", "1.0GeV", "2.0GeV", "5.0GeV"] - DETECTOR_CONFIG: ["epic_backward_hcal_only", "epic_full"] + - DETECTOR_CONFIG: ["epic_backward_hcal_only", "epic_full"] script: - ls -lrht - mv results{,_save}/ - - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output results/nhcal_basic_distribution/analysis_${DETECTOR_CONFIG}_E${ENERGY}_combined_10files.pdf + - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output results/nhcal_basic_distribution/energy_resolution_analysis_{DETECTOR_CONFIG}.png - mv results{_save,}/ diff --git a/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_analysis.cxx b/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_analysis.cxx index c29d5191..d9e35ed8 100644 --- a/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_analysis.cxx +++ b/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_analysis.cxx @@ -61,37 +61,88 @@ #include #include - -// #include "FileList.h" -// #include "EICutil.h" -// #include "BasicUtil.h" - -// #pragma link C++ class vector+; -// #pragma link C++ class vector+; -// #pragma link C++ class vector+; +#include using namespace std; using namespace ROOT; using namespace TMath; using namespace edm4hep; -int basic_distribution_analysis(const string &filename, string outname_pdf, string outname_png) +inline string addPrefixAfterSlash(const string& path, const string& prefix) { + const auto slash = path.find_last_of('/'); + if (slash == string::npos) + return prefix + path; + return path.substr(0, slash + 1) + prefix + path.substr(slash + 1); +} + +string changeExtension(const string& path, const string& new_ext) +{ + size_t pos = path.find_last_of('.'); + if (pos != string::npos) + return path.substr(0, pos) + new_ext; + return path + new_ext; +} + +int basic_distribution_analysis(const string &filename, string outname_png, string outname_root) { + constexpr double NBINS = 50; + constexpr double MIN_X_MM = -3000, MAX_X_MM = 3000; + constexpr double MIN_Y_MM = -3000, MAX_Y_MM = 3000; + constexpr double MIN_Z_MM = -4500, MAX_Z_MM = -3600; + constexpr double MIN_R_MM = 0, MAX_R_MM = 3000; + constexpr double MIN_TOTAL_ENERGY_GEV = 0, MAX_TOTAL_ENERGY_GEV = 0.3; + constexpr double MIN_HITS = 0, MAX_HITS = 300; + constexpr double MIN_LAYER_HITS = 0, MAX_LAYER_HITS = 50; + constexpr double MIN_LAYER_ENERGY_GEV = 0, MAX_LAYER_ENERGY_GEV = 0.2; + constexpr double MIN_KINETIC_GEV = 0, MAX_KINETIC_GEV = 6; + + gStyle->SetTitleSize(0.045, "XYZ"); + gStyle->SetLabelSize(0.04, "XYZ"); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetPadRightMargin(0.15); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadTopMargin(0.10); + + string outname_pdf = changeExtension(outname_png, ".pdf"); + podio::ROOTReader *reader = new podio::ROOTReader(); reader->openFile(filename); unsigned nEvents = reader->getEntries("events"); cout << "Number of events: " << nEvents << endl; - vector xPosition; - vector yPosition; - vector zPosition; - vector rPosition; - vector energy; - vector totalEventEnergy; - vector totalEventHits; - vector zLayerValues; - vector nHitsPerLayer; - vector nEnergyPerLayer; + TH1D *h_energyTotal = new TH1D("h_energyTotal", "Total Energy per Event; Energy per Event [GeV]", + NBINS, MIN_TOTAL_ENERGY_GEV, MAX_TOTAL_ENERGY_GEV); + + TH2D *h_layerEnergy = new TH2D("h_layerEnergy", "Energy in Layers; Z [mm]; Energy [GeV]", + NBINS, MIN_Z_MM, MAX_Z_MM, NBINS, MIN_LAYER_ENERGY_GEV, MAX_LAYER_ENERGY_GEV); + + TProfile *p_layerEnergy = new TProfile("p_layerEnergy", "Energy in Layers; Z [mm]; Mean Energy [GeV]", + NBINS, MIN_Z_MM, MAX_Z_MM); + + TH1D *h_hitCount = new TH1D("h_hitCount", "Hits per Event; Hits per Event; N", + NBINS, MIN_HITS, MAX_HITS); + + TH2D *h_layerHits = new TH2D("h_layerHits", "Hits in Layers; Z [mm]; Hits", + NBINS, MIN_Z_MM, MAX_Z_MM, NBINS, MIN_LAYER_HITS, MAX_LAYER_HITS); + + TProfile *p_layerHits = new TProfile("p_layerHits", "Hits in Layers; Z [mm]; Mean Hits", + NBINS, MIN_Z_MM, MAX_Z_MM); + + TH2D *h_XYPos = new TH2D("h_XYPos", "Hits position;X [mm];Y [mm]", + NBINS, MIN_X_MM, MAX_X_MM, NBINS, MIN_Y_MM, MAX_Y_MM); + + TH2D *h_ZRPos = new TH2D("h_ZRPos", "Hits position;Z [mm];R [mm]", + NBINS, MIN_Z_MM, MAX_Z_MM, NBINS, MIN_R_MM, MAX_R_MM); + + TH2D *h_XYEnergy = new TH2D("h_XYEnergy", "Hits energy;X [mm];Y [mm]", + NBINS, MIN_X_MM, MAX_X_MM, NBINS, MIN_Y_MM, MAX_Y_MM); + + TH2D *h_energyRes = new TH2D("h_energyRes", "Kinetic energy vs sum hit energy;Ekin [GeV]; Ehit sum [GeV]", + NBINS, MIN_KINETIC_GEV, MAX_KINETIC_GEV, + NBINS, MIN_TOTAL_ENERGY_GEV, MAX_TOTAL_ENERGY_GEV); + + TProfile *p_energyRes = new TProfile("p_energyRes", "Kinetic energy vs sum hit energy", + NBINS, MIN_KINETIC_GEV, MAX_KINETIC_GEV, "s"); for (unsigned ev = 0; ev < nEvents; ev++) { @@ -105,11 +156,14 @@ int basic_distribution_analysis(const string &filename, string outname_pdf, stri podio::Frame frame(std::move(frameData)); auto& hits = frame.get("HcalEndcapNHits"); - if (!hits.isValid()) + auto& mcCol = frame.get("MCParticles"); + if (!hits.isValid() && !mcCol.isValid()) { - cerr << "HcalEndcapNHits collection is not valid!" << endl; + cerr << "HcalEndcapNHits or MCParticles collection is not valid!" << endl; } + double Ekin = mcCol[0].getEnergy() - mcCol[0].getMass(); + map> layerData; double totalEnergy = 0; int totalHits = 0; @@ -117,115 +171,74 @@ int basic_distribution_analysis(const string &filename, string outname_pdf, stri for (const auto& hit : hits) { totalHits++; - energy.push_back(hit.getEnergy()); totalEnergy += hit.getEnergy(); auto pos = hit.getPosition(); - xPosition.push_back(pos.x); - yPosition.push_back(pos.y); - zPosition.push_back(pos.z); - rPosition.push_back(sqrt(pos.x * pos.x + pos.y * pos.y)); + double r = sqrt(pos.x * pos.x + pos.y * pos.y); + + h_XYPos->Fill(pos.x, pos.y); + h_ZRPos->Fill(pos.z, r); + h_XYEnergy->Fill(pos.x, pos.y, hit.getEnergy()); double zBin = round(pos.z); layerData[zBin].first++; layerData[zBin].second += hit.getEnergy(); } - totalEventEnergy.push_back(totalEnergy); - totalEventHits.push_back(totalHits); + h_energyTotal->Fill(totalEnergy); + h_hitCount->Fill(totalHits); + h_energyRes->Fill(Ekin, totalEnergy); + p_energyRes->Fill(Ekin, totalEnergy); for (const auto& [zValue, stats] : layerData) { - zLayerValues.push_back(zValue); - nHitsPerLayer.push_back(stats.first); - nEnergyPerLayer.push_back(stats.second); + h_layerHits->Fill(zValue, stats.first); + p_layerHits->Fill(zValue, stats.first); + h_layerEnergy->Fill(zValue, stats.second); + p_layerEnergy->Fill(zValue, stats.second); } - - } - - auto minmax_xPosition = minmax_element(xPosition.begin(), xPosition.end()); - auto minmax_yPosition = minmax_element(yPosition.begin(), yPosition.end()); - auto minmax_zPosition = minmax_element(zPosition.begin(), zPosition.end()); - auto minmax_rPosition = minmax_element(rPosition.begin(), rPosition.end()); - auto minmax_totalEventEnergy = minmax_element(totalEventEnergy.begin(), totalEventEnergy.end()); - auto minmax_totalEventHits = minmax_element(totalEventHits.begin(), totalEventHits.end()); - auto minmax_energy = minmax_element(energy.begin(), energy.end()); - auto minmax_zLayerValues = minmax_element(zLayerValues.begin(), zLayerValues.end()); - auto minmax_nHitsPerLayer = minmax_element(nHitsPerLayer.begin(), nHitsPerLayer.end()); - auto minmax_nEnergyPerLayer = minmax_element(nEnergyPerLayer.begin(), nEnergyPerLayer.end()); - - int nbins = nEvents/1000; - - TH1D *h_energyTotal = new TH1D("h_energyTotal", "Total Energy per Event; Energy per Event", - nbins/2, *minmax_totalEventEnergy.first, *minmax_totalEventEnergy.second); - TH2D *h_layerEnergy = new TH2D("h_layerEnergy", "Energy in Layers (Z); Z; Energy", - nbins/5, *minmax_zLayerValues.first, *minmax_zLayerValues.second, - nbins/4, *minmax_nEnergyPerLayer.first, *minmax_nEnergyPerLayer.second); - TProfile *p_layerEnergy = new TProfile("p_layerEnergy", "Energy in Layers (Z); Z; Mean Energy", - nbins/5, *minmax_zLayerValues.first, *minmax_zLayerValues.second); - TH1D *h_hitCount = new TH1D("h_hitCount", "Number of Hits per Event; Hits per Event", - nbins/2, *minmax_totalEventHits.first, *minmax_totalEventHits.second); - TH2D *h_layerHits = new TH2D("h_layerHits", "Number of Hits in Layers (Z); Layer(Z); Hits", - nbins/5, *minmax_zLayerValues.first, *minmax_zLayerValues.second, - *minmax_nHitsPerLayer.second, *minmax_nHitsPerLayer.first, *minmax_nHitsPerLayer.second); - TProfile *p_layerHits = new TProfile("p_layerHits", "Number of Hits in Layers (Z); Z; Mean Hits", - nbins/5, *minmax_zLayerValues.first, *minmax_zLayerValues.second); - TH2D *h_XYPos = new TH2D("h_XYPos", "Hits position X,Y;X [mm];Y [mm]", - nbins, *minmax_xPosition.first, *minmax_xPosition.second, - nbins, *minmax_yPosition.first, *minmax_yPosition.second); - TH2D *h_ZRPos = new TH2D("h_ZRPos", "Hits position Z,R;Z [mm];R [mm]", - nbins/5, *minmax_zPosition.first, *minmax_zPosition.second, - nbins, *minmax_rPosition.first, *minmax_rPosition.second); - TH2D *h_XYEnergy = new TH2D("h_XYEnergy", "Hits energy X,Y;X [mm];Y [mm]", - nbins, *minmax_xPosition.first, *minmax_xPosition.second, - nbins, *minmax_yPosition.first, *minmax_yPosition.second); - - for(int i = 0; i < xPosition.size(); i++) - { - - h_XYPos->Fill(xPosition[i], yPosition[i]); - h_ZRPos->Fill(zPosition[i], rPosition[i]); - h_XYEnergy->Fill(xPosition[i], yPosition[i], energy[i]); - } - - for (int i = 0; i < zLayerValues.size(); i++) - { - h_layerHits->Fill(zLayerValues[i], nHitsPerLayer[i]); - p_layerHits->Fill(zLayerValues[i], nHitsPerLayer[i]); - - h_layerEnergy->Fill(zLayerValues[i], nEnergyPerLayer[i]); - p_layerEnergy->Fill(zLayerValues[i], nEnergyPerLayer[i]); - } - - for(int i = 0; i < nEvents; i++) - { - h_energyTotal->Fill(totalEventEnergy[i]); - h_hitCount->Fill(totalEventHits[i]); } - - gStyle->SetPadLeftMargin(0.13); - - TCanvas *canvas = new TCanvas("canvas", "canvas", 1600, 800); - canvas->Divide(4,2); - canvas->cd(1); + + TFile *outFile = new TFile(outname_root.c_str(), "RECREATE"); + h_energyRes->Write(); + p_energyRes->Write(); + outFile->Close(); + delete outFile; + + TCanvas *c_evLayers = new TCanvas("c_evLayers", "c_evLayers", 1600, 800); + c_evLayers->Divide(2,2); + c_evLayers->cd(1); h_energyTotal->Draw(); - canvas->cd(2); + c_evLayers->cd(2); h_layerEnergy->Draw("COLZ"); + p_layerEnergy->SetLineWidth(3); p_layerEnergy->SetLineColor(kRed); p_layerEnergy->SetMarkerColor(kRed); p_layerEnergy->Draw("SAME"); - canvas->cd(3); + c_evLayers->cd(3); h_hitCount->Draw(); - canvas->cd(4); + c_evLayers->cd(4); h_layerHits->Draw("COLZ"); + p_layerHits->SetLineWidth(3); p_layerHits->SetLineColor(kRed); p_layerHits->SetMarkerColor(kRed); p_layerHits->Draw("SAME"); - canvas->cd(5); + + c_evLayers->SaveAs(outname_png.c_str()); + c_evLayers->SaveAs(outname_pdf.c_str()); + + TCanvas *c_hit_posE = new TCanvas("c_hit_posE", "c_hit_posE", 1600, 800); + c_hit_posE->Divide(2,2); + c_hit_posE->cd(1); h_XYPos->Draw("COLZ"); - canvas->cd(6); + c_hit_posE->cd(2); h_ZRPos->Draw("COLZ"); - canvas->cd(7); + c_hit_posE->cd(3); h_XYEnergy->Draw("COLZ"); + c_hit_posE->cd(4); + + c_hit_posE->SaveAs(addPrefixAfterSlash(outname_png, "hit_posE_").c_str()); + c_hit_posE->SaveAs(addPrefixAfterSlash(outname_pdf, "hit_posE_").c_str()); - canvas->SaveAs(outname_pdf.c_str()); - canvas->SaveAs(outname_png.c_str()); + delete reader; + delete c_evLayers; + delete c_hit_posE; return 0; -} +} \ No newline at end of file diff --git a/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_energy_resolution.cxx b/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_energy_resolution.cxx new file mode 100644 index 00000000..3bc505d9 --- /dev/null +++ b/benchmarks/nhcal_basic_distribution/scripts/basic_distribution_energy_resolution.cxx @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include + +#include "ROOT/RDataFrame.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TROOT.h" +#include "TRandom.h" +#include "TH3.h" + + +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" + +#include +#include +#include "podio/ROOTReader.h" +//#include +#include "podio/CollectionIDTable.h" +#include "podio/ObjectID.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/MCParticleCollectionData.h" +#include "edm4hep/MCParticle.h" +#include "edm4hep/MCParticleData.h" + +#include "edm4hep/SimCalorimeterHitCollectionData.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/SimCalorimeterHitData.h" +#include "edm4hep/SimCalorimeterHit.h" + +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitCollectionData.h" +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/CalorimeterHitData.h" +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitObj.h" + +#include "edm4eic/ClusterCollection.h" +#include "edm4eic/Cluster.h" +#include "edm4eic/ClusterData.h" + +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitCollectionData.h" +#include "edm4eic/CalorimeterHitCollection.h" +#include "edm4eic/CalorimeterHitData.h" +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitObj.h" + + +#include +#include +#include +#include + +using namespace std; +using namespace ROOT; +using namespace TMath; +using namespace edm4hep; + +string changeExtension(const string& path, const string& new_ext) +{ + size_t pos = path.find_last_of('.'); + if (pos != string::npos) + return path.substr(0, pos) + new_ext; + return path + new_ext; +} + +int basic_distribution_energy_resolution(const string &filename, string outname_png){ + + gStyle->SetTitleSize(0.045, "XYZ"); + gStyle->SetLabelSize(0.04, "XYZ"); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetPadRightMargin(0.15); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadTopMargin(0.10); + + string outname_pdf = changeExtension(outname_png, ".pdf"); + + TFile *file = TFile::Open(filename.c_str(), "READ"); + if (!file || file->IsZombie()) + { + cerr << "Cannot open file: " << filename << endl; + return 1; + } + + TH2D *h_energyRes = (TH2D*)file->Get("h_energyRes"); + TProfile *p_energyRes = (TProfile*)file->Get("p_energyRes"); + + if (!h_energyRes || !p_energyRes) + { + cerr << "Cannot retrieve histograms from file" << endl; + file->Close(); + return 1; + } + + TGraphErrors *g_resolution = new TGraphErrors(); + + for (int i = 1; i <= p_energyRes->GetNbinsX(); i++) { + double Ekin = p_energyRes->GetBinCenter(i); + double mean = p_energyRes->GetBinContent(i); + double rms = p_energyRes->GetBinError(i); + int entries = p_energyRes->GetBinEntries(i); + + if (entries < 10 || mean <= 0) continue; + + int n = g_resolution->GetN(); + g_resolution->SetPoint(n, Ekin, rms/mean); + g_resolution->SetPointError(n, 0, rms/mean * sqrt(1.0/entries)); + } + + // Fit σ/E = a/√E ⊕ b + TF1 *fit = new TF1("fit", "sqrt([0]*[0]/x + [1]*[1])", 0.1, 6); + fit->SetParameters(0.5, 0.05); + fit->SetParNames("a (stochastic)", "b (constant)"); + g_resolution->Fit(fit, "R"); + + TCanvas *c = new TCanvas("c", "Energy Resolution", 1600, 800); + c->Divide(2, 1); + + c->cd(1); + h_energyRes->Draw("COLZ"); + p_energyRes->SetLineWidth(3); + p_energyRes->SetLineColor(kRed); + p_energyRes->SetMarkerColor(kRed); + p_energyRes->Draw("SAME"); + + c->cd(2); + g_resolution->SetTitle("Energy Resolution;E_{kin} [GeV];#sigma/E"); + g_resolution->SetMarkerStyle(20); + g_resolution->Draw("AP"); + fit->Draw("SAME"); + + TLegend *leg = new TLegend(0.45, 0.65, 0.80, 0.80); + leg->AddEntry(g_resolution, "Data", "p"); + leg->AddEntry(fit, Form("Fit: #sqrt{(%.3f/#sqrt{E})^{2} + (%.3f)^{2}}", + fit->GetParameter(0), fit->GetParameter(1)), "l"); + leg->Draw(); + + c->SaveAs(outname_png.c_str()); + c->SaveAs(outname_pdf.c_str()); + + file->Close(); + + return 0; +} \ No newline at end of file diff --git a/benchmarks/nhcal_dimuon_fotoproduction/Snakefile b/benchmarks/nhcal_dimuon_fotoproduction/Snakefile new file mode 100644 index 00000000..6e5c4272 --- /dev/null +++ b/benchmarks/nhcal_dimuon_fotoproduction/Snakefile @@ -0,0 +1,83 @@ +rule nhcal_dimuon_fotoproduction_presimulate: + output: + "sim_input/DiMuon_ep_18x275GeV.{INDEX}.hepmc3", + params: + outdir=lambda wc, output: os.path.dirname(output[0]), + shell: + """ + benchmarks/nhcal_dimuon_fotoproduction/scripts/run.sh {wildcards.INDEX} {params.outdir} + """ + +rule nhcal_dimuon_fotoproduction_simulate: + input: + "sim_input/DiMuon_ep_18x275GeV.{INDEX}.hepmc3", + output: + "sim_output/nhcal_dimuon_fotoproduction/sim_{DETECTOR_CONFIG}.{INDEX}.edm4hep.root", + params: + N_EVENTS=1000, + SEED=lambda wildcards: "1" + wildcards.INDEX, + DETECTOR_PATH=os.environ["DETECTOR_PATH"], + DETECTOR_CONFIG=lambda wildcards: wildcards.DETECTOR_CONFIG, + DD4HEP_HASH=get_spack_package_hash("dd4hep"), + NPSIM_HASH=get_spack_package_hash("npsim"), + wildcard_constraints: + DETECTOR_CONFIG = r"[A-Za-z0-9_]+", + INDEX = r"\d+", + #cache: True + shell: + """ + exec npsim \ + --compactFile {params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}.xml \ + --inputFile {input} \ + -v WARNING \ + --random.seed {params.SEED} \ + --numberOfEvents {params.N_EVENTS} \ + --outputFile {output} + """ + +rule nhcal_dimuon_fotoproduction_recon: + input: + "sim_output/nhcal_dimuon_fotoproduction/sim_{DETECTOR_CONFIG}.{INDEX}.edm4hep.root" + output: + "sim_output/nhcal_dimuon_fotoproduction/sim_{DETECTOR_CONFIG}.{INDEX}.eicrecon.edm4hep.root" + params: + DETECTOR_CONFIG = lambda wildcards: wildcards.DETECTOR_CONFIG, + #cache: True + shell: + """ + exec env DETECTOR_CONFIG={params.DETECTOR_CONFIG} \ + eicrecon {input} \ + -Ppodio:output_file={output} \ + + """ + +rule nhcal_dimuon_fotoproduction_combine: + input: + lambda wildcards: expand( + "sim_output/nhcal_dimuon_fotoproduction/sim_{DETECTOR_CONFIG}.{INDEX}.eicrecon.edm4hep.root", + DETECTOR_CONFIG=wildcards.DETECTOR_CONFIG, + INDEX=range(int(wildcards.N)), + ) + output: + "sim_output/nhcal_dimuon_fotoproduction/sim_{DETECTOR_CONFIG}_{N}files.eicrecon.edm4hep.root", + wildcard_constraints: + N = r"\d+", + shell: + """ + hadd -f {output} {input} + """ + +rule nhcal_dimuon_fotoproduction_analysis: + input: + combined="sim_output/nhcal_dimuon_fotoproduction/sim_{DETECTOR_CONFIG}_{N}files.eicrecon.edm4hep.root", + script="benchmarks/nhcal_dimuon_fotoproduction/scripts/dimuon_fotoproduction_analysis.cxx", + output: + png=f"results/nhcal_dimuon_fotoproduction/analysis_{{DETECTOR_CONFIG}}_{{N}}files.png", + pdf=f"results/nhcal_dimuon_fotoproduction/analysis_{{DETECTOR_CONFIG}}_{{N}}files.pdf", + params: + DETECTOR_PATH = os.environ["DETECTOR_PATH"], + DETECTOR_CONFIG = lambda wildcards: f"{wildcards.DETECTOR_CONFIG}.xml", + shell: + """ + root -l -b -q '{input.script}("{input.combined}","{output.pdf}","{output.png}","{params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}")' + """ diff --git a/benchmarks/nhcal_dimuon_fotoproduction/config.yml b/benchmarks/nhcal_dimuon_fotoproduction/config.yml new file mode 100644 index 00000000..b9f03ae0 --- /dev/null +++ b/benchmarks/nhcal_dimuon_fotoproduction/config.yml @@ -0,0 +1,27 @@ +sim:nhcal_dimuon_fotoproduction: + extends: .det_benchmark + stage: simulate + parallel: + matrix: + - INDEX_RANGE: ["0 4","5 9"] + script: + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB $(for INDEX in $(seq -f '%02.0f' $INDEX_RANGE); do echo sim_output/nhcal_dimuon_fotoproduction/sim_epic_full.${INDEX}.edm4hep.root; done) + +bench:nhcal_dimuon_fotoproduction_analysis: + extends: .det_benchmark + stage: benchmarks + needs: + - "sim:nhcal_dimuon_fotoproduction" + script: + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB results/nhcal_dimuon_fotoproduction/analysis_epic_full_10files.pdf + +collect_results:nhcal_dimuon_fotoproduction: + extends: .det_benchmark + stage: collect + needs: + - "bench:nhcal_dimuon_fotoproduction_analysis" + script: + - ls -lrht + - mv results{,_save}/ + - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output results/nhcal_dimuon_fotoproduction/analysis_epic_full_10files.pdf + - mv results{_save,}/ diff --git a/benchmarks/nhcal_dimuon_fotoproduction/scripts/dimuon_fotoproduction_analysis.cxx b/benchmarks/nhcal_dimuon_fotoproduction/scripts/dimuon_fotoproduction_analysis.cxx new file mode 100644 index 00000000..e8771675 --- /dev/null +++ b/benchmarks/nhcal_dimuon_fotoproduction/scripts/dimuon_fotoproduction_analysis.cxx @@ -0,0 +1,923 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "TLorentzVector.h" + +#include "ROOT/RDataFrame.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TROOT.h" +#include "TRandom.h" +#include "TH3.h" + +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" +#include "DD4hep/Volumes.h" +#include "DD4hep/Objects.h" +#include "DD4hep/Shapes.h" +#include +#include +#include +#include +#include + + + +#include "podio/Frame.h" +#include "podio/CollectionBase.h" +#include "podio/ROOTReader.h" +#include "podio/CollectionIDTable.h" +#include "podio/ObjectID.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/MCParticleCollectionData.h" +#include "edm4hep/MCParticle.h" +#include "edm4hep/MCParticleData.h" + +#include "edm4hep/SimCalorimeterHitCollectionData.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/SimCalorimeterHitData.h" +#include "edm4hep/SimCalorimeterHit.h" + +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitCollectionData.h" +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/CalorimeterHitData.h" +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitObj.h" + +#include "edm4eic/ClusterCollection.h" +#include "edm4eic/Cluster.h" +#include "edm4eic/ClusterData.h" + +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitCollectionData.h" +#include "edm4eic/CalorimeterHitCollection.h" +#include "edm4eic/CalorimeterHitData.h" +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitObj.h" + +#include "edm4eic/InclusiveKinematicsCollection.h" +#include "edm4eic/InclusiveKinematics.h" +#include "edm4hep/utils/kinematics.h" +#include "edm4hep/utils/vector_utils.h" + +#include "edm4eic/vector_utils_legacy.h" +#include "edm4hep/Vector3f.h" + +#include "edm4eic/Track.h" +#include "edm4eic/TrackSegment.h" +#include "edm4eic/TrackSegmentCollectionData.h" +#include "edm4eic/TrackPoint.h" +#include "edm4eic/TrackParameters.h" + +#include "edm4eic/TrackSegmentCollection.h" +#include "edm4eic/ReconstructedParticleCollection.h" +#include "edm4eic/ReconstructedParticle.h" + +#include "edm4eic/MCRecoCalorimeterHitAssociationCollection.h" +#include "edm4eic/MCRecoCalorimeterHitAssociation.h" +#include "edm4eic/MCRecoParticleAssociationCollection.h" +#include "edm4eic/MCRecoParticleAssociation.h" + +using namespace std; +using namespace ROOT; +using namespace TMath; +using namespace edm4hep; + +dd4hep::Detector* det = NULL; + +constexpr double ETA_MIN = -4.14, ETA_MAX = -1.16; + +inline bool inNHCal(double eta) {return (eta >= ETA_MIN && eta <= ETA_MAX);} + +inline string addPrefixAfterSlash(const string& path, + const string& prefix) { + const auto slash = path.find_last_of('/'); + if (slash == string::npos) + return prefix + path; + return path.substr(0, slash + 1) + prefix + path.substr(slash + 1); +} + +void MakeLogBins(double *array, int nbins, double binLo, double binHi) +{ + double logMin = log10(binLo); + double logMax = log10(binHi); + double binWidth = (logMax - logMin) / nbins; + + for (int i = 0; i <= nbins; ++i) { + array[i] = pow(10, logMin + i * binWidth); + } +} + +TLine* makeLine(double x1, double y1, double x2, double y2) { + TLine* l = new TLine(x1, y1, x2, y2); + l->SetLineColor(kRed); + l->SetLineStyle(2); + l->SetLineWidth(2); + l->Draw("same"); + return l; +} + +static TGraph* makeEffGraph_B(TH1D* h_sel, TH1D* h_all, + const char* name, + int minAll = 2, + bool logx = false) +{ + if (!h_sel || !h_all) return nullptr; + + std::unique_ptr h_eff((TH1D*)h_sel->Clone(Form("tmp_%s", name ? name : "eff"))); + h_eff->SetDirectory(nullptr); + h_eff->Sumw2(); + h_eff->Divide(h_sel, h_all, 1.0, 1.0, "B"); + + const int nb = h_all->GetNbinsX(); + + std::unique_ptr g_log; + std::unique_ptr g_lin; + + if (logx) g_log = std::make_unique(); + else g_lin = std::make_unique(); + + int ip = 0; + for (int b = 1; b <= nb; ++b) { + const double all = h_all->GetBinContent(b); + if (all < minAll) continue; + + const double xl = h_all->GetXaxis()->GetBinLowEdge(b); + const double xh = h_all->GetXaxis()->GetBinUpEdge(b); + + const double y = 100.0 * h_eff->GetBinContent(b); + const double ey = 100.0 * h_eff->GetBinError(b); + + if (logx) { + if (xl <= 0.0 || xh <= 0.0) continue; + const double x = sqrt(xl * xh); + const double exl = x - xl; + const double exh = xh - x; + + g_log->SetPoint(ip, x, y); + g_log->SetPointError(ip, exl, exh, ey, ey); + } else { + const double x = h_all->GetBinCenter(b); + const double ex = 0.5 * (xh - xl); + + g_lin->SetPoint(ip, x, y); + g_lin->SetPointError(ip, ex, ey); + } + ++ip; + } + + TGraph* g = logx ? (TGraph*)g_log.release() : (TGraph*)g_lin.release(); + g->SetName(Form("g_%s", name ? name : "eff")); + return g; +} + +inline void drawEffPanel(TH1D* h_all, + TH1D* const h_in[3], + const char* title, + const char* xlabel, + bool logx) +{ + gPad->SetGrid(); + if (logx) gPad->SetLogx(); + + auto* mg = new TMultiGraph(); + auto* leg = new TLegend(0.63, 0.7, 0.88, 0.88); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->SetTextSize(0.04); + + Color_t colors[3] = {kBlue, kRed, kBlack}; + Style_t markers[3] = {20, 21, 22}; + + int added = 0; + + for (int i = 0; i < 3; ++i) { + if (!h_all || !h_in[i]) continue; + auto* g = makeEffGraph_B(h_in[i], h_all, Form("eff_%d", i), 2, logx); + if (!g || g->GetN() == 0) continue; + + g->SetMarkerColor(colors[i]); + g->SetMarkerStyle(markers[i]); + g->SetMarkerSize(1.0); + g->SetLineColor(kBlack); + + mg->Add(g, "PE"); + leg->AddEntry(g, Form("%d mu-in nHCAL", i), "p"); + ++added; + } + + if (h_all && h_in[0] && h_in[1] && h_in[2]) { + std::unique_ptr h_sum((TH1D*)h_in[0]->Clone("h_sum")); + h_sum->SetDirectory(nullptr); + h_sum->Add(h_in[1]); + h_sum->Add(h_in[2]); + + auto* gsum = makeEffGraph_B(h_sum.get(), h_all, "eff_sum", 2, logx); + if (gsum && gsum->GetN() > 0) { + gsum->SetMarkerStyle(25); + gsum->SetMarkerColor(kMagenta + 1); + gsum->SetFillColor(kMagenta + 1); + gsum->SetLineColor(kBlack); + gsum->SetMarkerSize(1.0); + + mg->Add(gsum, "PE"); + leg->AddEntry(gsum, "All eligible", "p"); + ++added; + } + } + + mg->SetTitle(Form("%s;%s;geom. acc. [%%]", title ? title : "", xlabel ? xlabel : "")); + mg->Draw("A"); + + gPad->Update(); + if (mg->GetHistogram()) { + double ymax = mg->GetHistogram()->GetMaximum(); + mg->GetHistogram()->SetMaximum(ymax * 1.35); + } + gPad->Modified(); + gPad->Update(); + + if (logx) { + auto* ax = mg->GetXaxis(); + if (ax) { + double xmin = ax->GetXmin(), xmax = ax->GetXmax(); + if (xmin <= 0) xmin = 1e-12; + mg->GetXaxis()->SetLimits(xmin, xmax); + } + } + + leg->Draw(); +} + +inline TVector3 getPlasticDimensionsCM(dd4hep::Detector& det, + const dd4hep::DDSegmentation::BitFieldCoder* dec, + dd4hep::DDSegmentation::CellID cid, + int slice_idx, + int plastic_slice_value = 3) +{ + const double NaN = numeric_limits::quiet_NaN(); + TVector3 dims(NaN, NaN, NaN); + try { + if (!dec) throw runtime_error("decoder==nullptr"); + if (dec->get(cid, slice_idx) != plastic_slice_value) + throw runtime_error("cell is not plastic"); + + auto de = det.volumeManager().lookupDetElement(cid); + if (!de.isValid()) throw runtime_error("lookupDetElement failed"); + + auto pv = de.placement(); + if (!pv.isValid()) throw runtime_error("DetElement has no placement"); + + auto vol = pv.volume(); + if (!vol.isValid()) throw runtime_error("Invalid Volume"); + + auto* box = dynamic_cast(vol.solid().ptr()); + if (!box) throw runtime_error("Solid is not TGeoBBox"); + + dims.SetXYZ(2.0 * box->GetDX(), + 2.0 * box->GetDY(), + 2.0 * box->GetDZ()); + + //const double dz_cm = box->GetDZ(); + //const double thickness_cm = 2.0 * dz_cm; + //return thickness_cm; + return dims; + } catch (const exception& e) { + cerr << "[WARN] getPlasticThicknessMM: " << e.what() << " (cellID=" << cid << ")\n"; + return dims; + } +} + +inline double getPlasticCenterZ_cm( + const dd4hep::DDSegmentation::BitFieldCoder* decoder, + const dd4hep::rec::CellIDPositionConverter& cellid_converter, + dd4hep::DDSegmentation::CellID cid, + int slice_index, + int plastic_slice_value = 3) +{ + try { + if (!decoder) throw std::runtime_error("decoder==nullptr"); + if (decoder->get(cid, slice_index) != plastic_slice_value) + throw std::runtime_error("cell is not plastic (slice mismatch)"); + return cellid_converter.position(cid).z(); + } catch (const std::exception& e) { + cerr << "[WARN] getPlasticDimensionsCM: " << e.what() + << " (cellID=" << cid << ")\n"; + return std::numeric_limits::quiet_NaN();; + } +} + +struct GeomState { + double x, y, z; +}; + +inline double hypot2(double a, double b){ return sqrt(a*a + b*b); } +inline int sgnD(double v){ return (v>0) - (v<0); } + +inline pair +trackXYatZ_fromTwoStates(double x1,double y1,double z1, + double x2,double y2,double z2, + double zTarget) +{ + constexpr double EPSZ = 1e-12; + + const double dz = z2 - z1; + if (abs(dz) < EPSZ) { + const double d1 = abs(zTarget - z1); + const double d2 = abs(zTarget - z2); + if (d1 < d2) return {x1, y1}; + if (d2 < d1) return {x2, y2}; + return {0.5*(x1+x2), 0.5*(y1+y2)}; + } + + const double t = (zTarget - z1) / dz; + const double x = x1 + t * (x2 - x1); + const double y = y1 + t * (y2 - y1); + return {x, y}; +} + +inline pair +trackXYatZ(const GeomState& A, const GeomState& B, double zTarget){ + return trackXYatZ_fromTwoStates( + A.x, A.y, A.z, B.x, B.y, B.z, + zTarget + ); +} + + +int dimuon_fotoproduction_analysis(const string& filename, string outname_pdf, string outname_png, TString compact_file) { + + gStyle->SetOptStat(0); + podio::ROOTReader reader; + reader.openFile(filename); + unsigned nEvents = reader.getEntries("events"); + cout << "Number of events: " << nEvents << endl; + + det = &(dd4hep::Detector::getInstance()); + det->fromCompact(compact_file.Data()); + det->volumeManager(); + det->apply("DD4hepVolumeManager", 0, 0); + + dd4hep::rec::CellIDPositionConverter cellid_converter(*det); + auto idSpec = det->readout("HcalEndcapNHits").idSpec(); + auto decoder = idSpec.decoder(); + const int slice_index = decoder->index("slice"); + if (slice_index < 0) { + cerr << "ERROR: 'slice' field not found in cell ID spec!" << endl; + return 1; + } + + //double thickness_cm = det->constantAsDouble("HcalEndcapNPolystyreneThickness")* 0.1; + + constexpr int NBINS = 60; + + constexpr double Q2_MIN = 1e-2; + constexpr double Q2_MAX = 1.0; + constexpr double X_MIN = 1e-6; + constexpr double X_MAX = 1e-3; + constexpr double Y_MIN = 0.0; + constexpr double Y_MAX = 1.0; + constexpr double W_MIN = 0.0; + constexpr double W_MAX = 160.0; + + constexpr int Z_NBINS = 100; + constexpr double Z_MIN_MM = -4500.0; + constexpr double Z_MAX_MM = -3600.0; + + constexpr int DXY_NBINS = 120; + constexpr double DXY_MIN_MM = -1000.0; + constexpr double DXY_MAX_MM = 1000.0; + + constexpr int DR_NBINS = 120; + constexpr double DR_MIN_MM = 0.0; + constexpr double DR_MAX_MM = 400.0; + + constexpr double P_MIN_GEV = 0.0; + constexpr double P_MAX_GEV = 25.0; + constexpr int P_NBINS = 50; + + constexpr double E_MIN_GEV = 2e-2; + constexpr double E_MAX_GEV = 10.0; + + constexpr int SIZE = 3; + constexpr double MIP_ENERGY_GEV = 0.002; + constexpr double E_CUTS[SIZE] = {1.5, 1.7, 2.0}; + constexpr double E_THRESH = E_CUTS[2]; + constexpr double LAYER_PROC[SIZE] = {0.6, 0.7, 0.8}; + + double t_cm; + double DR_CUTS_CM[SIZE] = {7.0, 10.0, 13.0}; + double DR_THRESH_MM = 10*DR_CUTS_CM[2]; + int LAYER_MAX = 10; + int LAYER_CUTS[SIZE] = {5, 6, 7}; + int LAYER_THRESH = LAYER_CUTS[0]; + + vector Xbins(NBINS+1), Q2bins(NBINS+1), Ebins(NBINS+1); + MakeLogBins(Q2bins.data(), NBINS, Q2_MIN, Q2_MAX); + MakeLogBins(Xbins.data(), NBINS, X_MIN, X_MAX); + MakeLogBins(Ebins.data(), NBINS, E_MIN_GEV, E_MAX_GEV); + + gStyle->SetTitleSize(0.045, "XYZ"); + gStyle->SetLabelSize(0.04, "XYZ"); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetPadRightMargin(0.15); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadTopMargin(0.10); + gROOT->ForceStyle(); + + TH2D* hEtaPt = new TH2D("hEtaPt", "Muon #eta vs p_{T};#eta;p_{T} [GeV]", 100, -6., 6., 100, 0., 7.); + TH2D* hx_Q2 = new TH2D("hx_Q2","Muon x vs Q^{2}; x; Q^{2} [GeV^{2}]", NBINS, Xbins.data(), NBINS, Q2bins.data()); + TH2D* hEta1_Eta2 = new TH2D("hEta1_Eta2", "Muon #eta_{+} vs #eta_{-}; #eta_{+} (PDG=-13); #eta_{-} (PDG=13)", 100, -6., 6., 100, -6., 6.); + + TH1D* hQ2_all = new TH1D("hQ2_all", "Q^{2}", NBINS, Q2bins.data()); + TH1D* hX_all = new TH1D("hX_all", "x", NBINS, Xbins.data()); + TH1D* hY_all = new TH1D("hY_all", "y", NBINS, Y_MIN, Y_MAX); + TH1D* hW_all = new TH1D("hW_all", "W", NBINS, W_MIN, W_MAX); + + TH1D* hQ2_in[3], *hX_in[3], *hY_in[3], *hW_in[3]; + for (int i = 0; i < 3; ++i) { + hQ2_in[i] = new TH1D(Form("hQ2_in_%d", i), "", NBINS, Q2bins.data()); + hX_in[i] = new TH1D(Form("hX_in_%d", i), "", NBINS, Xbins.data()); + hY_in[i] = new TH1D(Form("hY_in_%d", i), "", NBINS, Y_MIN, Y_MAX); + hW_in[i] = new TH1D(Form("hW_in_%d", i), "", NBINS, W_MIN, W_MAX); + } + + TH1D* hQ2_rec_all = new TH1D("hQ2_rec_all", "Q^{2} (rec)", NBINS, Q2bins.data()); + TH1D* hX_rec_all = new TH1D("hX_rec_all", "x (rec)", NBINS, Xbins.data()); + TH1D* hY_rec_all = new TH1D("hY_rec_all", "y (rec)", NBINS, Y_MIN, Y_MAX); + TH1D* hW_rec_all = new TH1D("hW_rec_all", "W (rec)", NBINS, W_MIN, W_MAX); + + TH1D* hQ2_rec_in[3], *hX_rec_in[3], *hY_rec_in[3], *hW_rec_in[3]; + for (int i = 0; i < 3; ++i) { + hQ2_rec_in[i] = new TH1D(Form("hQ2_rec_in_%d", i), "", NBINS, Q2bins.data()); + hX_rec_in[i] = new TH1D(Form("hX_rec_in_%d", i), "", NBINS, Xbins.data()); + hY_rec_in[i] = new TH1D(Form("hY_rec_in_%d", i), "", NBINS, Y_MIN, Y_MAX); + hW_rec_in[i] = new TH1D(Form("hW_rec_in_%d", i), "", NBINS, W_MIN, W_MAX); + } + + TH1D* hZ_proj = new TH1D("hZ_proj", "Muon track projections in nHCal; z [mm]; N", NBINS, Z_MIN_MM, Z_MAX_MM); + + TH1D* hZ_hits = new TH1D("hZ_hits", "Reconstructed hit z in nHCal; z [mm]; N", NBINS, Z_MIN_MM, Z_MAX_MM); + + TH3D* hDxDyZ_layer = new TH3D("hDxDyZ_layer","3D residuals (rec - proj): dx, dy vs z; dx [mm]; dy [mm]; z [mm]", NBINS, DXY_MIN_MM, DXY_MAX_MM, NBINS, DXY_MIN_MM, DXY_MAX_MM, NBINS, Z_MIN_MM, Z_MAX_MM); + + TH2D* hDxDy_all = new TH2D("hDxDy_all", + "Residuals (rec - proj): dx vs dy (all layers); dx [mm]; dy [mm]", + DXY_NBINS, DXY_MIN_MM, DXY_MAX_MM, DXY_NBINS, DXY_MIN_MM, DXY_MAX_MM); + + TH2D* hDrZ_layer = new TH2D("hDrZ_layer","Radial residual (rec - proj) vs z; dr [mm]; z [mm]", NBINS, DR_MIN_MM, DR_MAX_MM, NBINS, Z_MIN_MM, Z_MAX_MM); + + TH1D* hDr_all = new TH1D("hDr_all", + "Radial residual dr = #sqrt{dx^{2}+dy^{2}} (all layers); dr [mm]; N", + DR_NBINS, DR_MIN_MM, DR_MAX_MM); + + TH2D* hE_z = new TH2D("hE_z", "Reconstructed hit energy vs layer z; z_{layer} [mm]; E [GeV]", NBINS, Z_MIN_MM, Z_MAX_MM, NBINS, Ebins.data()); + + TH2D* hEsum_z = new TH2D("hEsum_z", "Layer energy sum vs z (reconstructed); z_{layer} [mm]; E_{sum} [GeV]", + NBINS, Z_MIN_MM, Z_MAX_MM, NBINS, Ebins.data()); + + TH1D* hE = new TH1D("hE", "Reconstructed hit energy; E [GeV]; N", 10000, 0.0, 1.0); + + TH1D* hEsum = new TH1D("hEsum", "Layer energy sum (reconstructed); E_{sum} [GeV]; N", 10000, 0.0, 1.0); + + TH1D* hP_all_mu = new TH1D("hP_all_mu", "MC muon momentum (muons in nHCal acceptance); p_{MC} [GeV]; N", P_NBINS, P_MIN_GEV, P_MAX_GEV); + + TH1D* hP_pass_dr[3] = { + new TH1D(Form("hP_pass_dr%.1fcm",DR_CUTS_CM[0]), Form("Accepted (dr<%.1fcm); p_{MC} [GeV]; N",DR_CUTS_CM[0]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_dr%.1fcm",DR_CUTS_CM[1]), Form("Accepted (dr<%.1fcm); p_{MC} [GeV]; N",DR_CUTS_CM[1]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_dr%.1fcm",DR_CUTS_CM[2]), Form("Accepted (dr<%.1fcm); p_{MC} [GeV]; N",DR_CUTS_CM[2]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + }; + + TH1D* hP_pass_ECut[3] = { + new TH1D(Form("hP_pass_E< %.1f MIP", E_CUTS[0]), Form("Accepted (E< %.1f MIP); p_{MC} [GeV]; N",E_CUTS[0]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_E< %.1f MIP", E_CUTS[1]), Form("Accepted (E< %.1f MIP); p_{MC} [GeV]; N",E_CUTS[1]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_E< %.1f MIP", E_CUTS[2]), Form("Accepted (E< %.1f MIP); p_{MC} [GeV]; N",E_CUTS[2]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + }; + + TH1D* hP_pass_LayerCut[3] = { + new TH1D(Form("hP_pass_Layer< %d layers", LAYER_CUTS[0]), Form("Accepted (Layer < %d layers); p_{MC} [GeV]; N",LAYER_CUTS[0]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_Layer< %d layers", LAYER_CUTS[1]), Form("Accepted (Layer < %d layers); p_{MC} [GeV]; N",LAYER_CUTS[1]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_Layer< %d layers", LAYER_CUTS[2]), Form("Accepted (Layer < %d layers); p_{MC} [GeV]; N",LAYER_CUTS[2]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + }; + + for (unsigned ev = 0; ev < nEvents; ev++) { + auto frameData = reader.readNextEntry(podio::Category::Event); + if (!frameData) continue; + podio::Frame frame(std::move(frameData)); + + auto& kinCol = frame.get("InclusiveKinematicsTruth"); + auto& mcCol = frame.get("MCParticles"); + auto& recParts = frame.get("ReconstructedParticles"); + auto& projSegs = frame.get("CalorimeterTrackProjections"); + auto& hcalRec = frame.get("HcalEndcapNRecHits"); + auto& assocCol = frame.get("ReconstructedParticleAssociations"); + + if (!kinCol.isValid() || !mcCol.isValid()) continue; + if (kinCol.empty()) continue; + + const auto& kin = kinCol.at(0); + double Q2 = kin.getQ2(), x = kin.getX(), y = kin.getY(), W = kin.getW(); + + bool m1_has_rec = false; + bool m2_has_rec = false; + + edm4hep::MCParticle m1, m2; + for (const auto& p : mcCol) { + if (abs(p.getPDG()) != 13 || p.getGeneratorStatus() == 0) continue; + if (p.getPDG() == 13) m1 = p; + else if (p.getPDG() == -13) m2 = p; + } + + TLorentzVector v1(m1.getMomentum().x, m1.getMomentum().y, m1.getMomentum().z, m1.getEnergy()); + TLorentzVector v2(m2.getMomentum().x, m2.getMomentum().y, m2.getMomentum().z, m2.getEnergy()); + + hEtaPt->Fill(v1.Eta(), v1.Pt()); + hEtaPt->Fill(v2.Eta(), v2.Pt()); + + hx_Q2->Fill(x,Q2); + hEta1_Eta2->Fill(v2.Eta(), v1.Eta()); + + hQ2_all->Fill(Q2); + hX_all->Fill(x); + hY_all->Fill(y); + hW_all->Fill(W); + + int nInNH = inNHCal(v1.Eta()) + inNHCal(v2.Eta()); + if (nInNH >= 0 && nInNH <= 2) { + hQ2_in[nInNH]->Fill(Q2); + hX_in[nInNH]->Fill(x); + hY_in[nInNH]->Fill(y); + hW_in[nInNH]->Fill(W); + } + + if(inNHCal(v1.Eta())) {hP_all_mu->Fill(v1.P());} + if(inNHCal(v2.Eta())){hP_all_mu->Fill(v2.P());} + + struct TaggedReco { edm4eic::ReconstructedParticle reco; int muTag; /*0=m1, 1=m2*/ }; + vector matchedRecos; + + auto find_associated_reco = [&](const edm4hep::MCParticle& mc, int muTag)->void { + try { + if (!assocCol.isValid() || assocCol.empty()) {return;} + auto simIDs = assocCol.simID(); + auto recIDs = assocCol.recID(); + const uint32_t mc_idx = mc.getObjectID().index; + for (size_t i=0; i= recParts.size()) {continue;} + auto reco = recParts.at(ridx); + if (reco.isAvailable()) { + matchedRecos.push_back({reco, muTag}); + } + } + } + } catch (...) {} + }; + + if (!assocCol.isValid() || assocCol.empty()) continue; + if (m1.isAvailable() && abs(m1.getPDG())==13) find_associated_reco(m1, 0); + if (m2.isAvailable() && abs(m2.getPDG())==13) find_associated_reco(m2, 1); + + if (!recParts.isValid() || !projSegs.isValid() || !hcalRec.isValid() || recParts.empty() || projSegs.empty() || hcalRec.empty()) continue; + + set uniqueCentersZ10; + map layerData; + for (const auto& hit : hcalRec) { + double z = hit.getPosition().z; + double zBin = round(z); + uint64_t cid = hit.getCellID(); + double zCenter_mm = 10 * getPlasticCenterZ_cm(decoder, cellid_converter, cid, slice_index, /*plastic_slice_value=*/3); + // TVector3 dim = getPlasticDimensionsCM(*det, decoder, cid, slice_index, 3); + // printf("dim.x: %f, dim.y: %f, dim.z: %f", dim.x(), dim.y(), dim.z()); + // double dr = dim.x(); + // if(ev == 0){ + // DR_CUTS_CM[0] = dr; DR_CUTS_CM[1] = dr * 2; DR_CUTS_CM[2] = dr * 3; + // DR_THRESH_MM = DR_CUTS_CM[2]*10; + // } + if (!std::isnan(zCenter_mm)) { + int z10 = lround(zCenter_mm * 10.0); + uniqueCentersZ10.insert(z10); + } + + layerData[zBin] += hit.getEnergy(); + hZ_hits->Fill(z); + hE_z->Fill(z, hit.getEnergy()); + hE->Fill(hit.getEnergy()); + } + vector layerCentersZ; + layerCentersZ.reserve(uniqueCentersZ10.size()); + for (int z10 : uniqueCentersZ10) layerCentersZ.push_back(z10 / 10.0); + if(layerCentersZ.size() > LAYER_MAX) LAYER_MAX = layerCentersZ.size(); + + for(size_t n = 0; n < SIZE; n++) LAYER_CUTS[n] = static_cast(LAYER_MAX*LAYER_PROC[n]); + LAYER_THRESH = LAYER_CUTS[0]; + + for (const auto& [zValue, sumEnergy] : layerData) {hEsum_z->Fill(zValue, sumEnergy); hEsum->Fill(sumEnergy);} + + struct TaggedTrack { edm4eic::Track tr; int muTag; }; + vector allTracks; + for (const auto& R : matchedRecos) { + for (const auto& tr : R.reco.getTracks()) { + if (tr.isAvailable()) allTracks.push_back({tr, R.muTag}); + } + } + struct SegTag { edm4eic::TrackSegment seg; int muTag; }; + vector segsTagged; + for (const auto& seg : projSegs) { + auto linkedTr = seg.getTrack(); + if (!linkedTr.isAvailable()) continue; + for (const auto& TT : allTracks) { + if (linkedTr.getObjectID() == TT.tr.getObjectID()) { + segsTagged.push_back({seg, TT.muTag}); + break; + } + } + } + + for (const auto& ST : segsTagged) { + + vector segMinDistance(LAYER_MAX, std::numeric_limits::infinity()); + vector segHitEnergy(LAYER_MAX, std::numeric_limits::quiet_NaN()); + vector thickness_cm(LAYER_MAX, std::numeric_limits::quiet_NaN()); + vector count_DrCuts(SIZE, 0); + vector count_ECuts(SIZE, 0); + + GeomState A{}, B{}; + bool haveA = false, haveB = false; + + auto points = ST.seg.getPoints(); + + for (const auto& pt : points) { + if (pt.system != 113) continue; + hZ_proj->Fill(pt.position.z); + if (!haveA) { + A.x = pt.position.x; A.y = pt.position.y; A.z = pt.position.z; + haveA = true; + } else if (!haveB) { + B.x = pt.position.x; B.y = pt.position.y; B.z = pt.position.z; + haveB = true; + break; + } + } + + if (!haveA || !haveB) {continue;} + + for (int i = 0; i < LAYER_MAX; ++i) { + double best_dr_in_layer = 1e10; + double best_E_in_layer; + double partLayerEnergySum = 0; + double ratio_HitPartLayerEnergy = 0; + dd4hep::DDSegmentation::CellID best_cid_in_layer; + + double zc = layerCentersZ[i]; + auto [X, Y] = trackXYatZ(A, B, zc); + + for (const auto& hit : hcalRec) { + const auto& hp = hit.getPosition(); + + if (fabs(hp.z - zc) > 5.0 /*mm*/) continue; + + const double dx = X - hp.x; + const double dy = Y - hp.y; + const double dr = sqrt(dx*dx + dy*dy); + if (dr < 30*10 /*mm*/) partLayerEnergySum += hit.getEnergy(); + + hDxDyZ_layer->Fill(dx, dy, hp.z); + hDxDy_all->Fill(dx, dy); + hDrZ_layer->Fill(dr, hp.z); + hDr_all->Fill(dr); + + if (dr < best_dr_in_layer) { + best_dr_in_layer = dr; + best_E_in_layer = hit.getEnergy(); + best_cid_in_layer = hit.getCellID(); + } + } + + if (best_dr_in_layer < DR_THRESH_MM) { + segMinDistance[i] = best_dr_in_layer; + segHitEnergy[i] = best_E_in_layer; + thickness_cm[i] = getPlasticDimensionsCM(*det, decoder, best_cid_in_layer, slice_index, 3).z(); + t_cm = thickness_cm[0]; + } + ratio_HitPartLayerEnergy += segHitEnergy[i]/partLayerEnergySum; + if (std::isnan(ratio_HitPartLayerEnergy) || fabs(ratio_HitPartLayerEnergy - 1) >= 0.2) continue; + for(int j = 0; j < SIZE; ++j){ + if (segMinDistance[i] < DR_CUTS_CM[j] * 10 /*mm*/){++count_DrCuts[j];} + if (segHitEnergy[i] < (E_CUTS[j] * MIP_ENERGY_GEV * thickness_cm[i] * 100)){++count_ECuts[j];} + } + } + + if (ST.muTag == 0 && count_DrCuts[SIZE-1] > LAYER_THRESH) m1_has_rec = true; + if (ST.muTag == 1 && count_DrCuts[SIZE-1] > LAYER_THRESH) m2_has_rec = true; + + for (int j = 0; j < SIZE; ++j){ + const double p = ST.muTag ? v2.P() : v1.P(); + if (count_DrCuts[j] > LAYER_THRESH) hP_pass_dr[j]->Fill(p); + if (count_ECuts[j] > LAYER_THRESH) hP_pass_ECut[j]->Fill(p); + if (count_DrCuts[SIZE-1] > LAYER_CUTS[j]) hP_pass_LayerCut[j]->Fill(p); + } + } + + int nInNH_rec_local = 0; + if (m1_has_rec) ++nInNH_rec_local; + if (m2_has_rec) ++nInNH_rec_local; + + hQ2_rec_all->Fill(Q2); + hX_rec_all->Fill(x); + hY_rec_all->Fill(y); + hW_rec_all->Fill(W); + + if (nInNH_rec_local >= 0 && nInNH_rec_local <= 2) { + hQ2_rec_in[nInNH_rec_local]->Fill(Q2); + hX_rec_in[nInNH_rec_local]->Fill(x); + hY_rec_in[nInNH_rec_local]->Fill(y); + hW_rec_in[nInNH_rec_local]->Fill(W); + } + } + + TCanvas* c = new TCanvas("c", "Muon analysis", 1600, 1000); + c->Divide(2,2); + + c->cd(1); hEtaPt->Draw("COLZ"); + c->cd(2); gPad->SetLogx(); gPad->SetGrid(); gPad->SetLogy(); hx_Q2->Draw("COLZ"); + c->cd(3); hEta1_Eta2->Draw("COLZ"); + auto* lv1 = makeLine(ETA_MIN, -6, ETA_MIN, 6); + auto* lv2 = makeLine(ETA_MAX, -6, ETA_MAX, 6); + auto* lh1 = makeLine(hEta1_Eta2->GetXaxis()->GetXmin(), ETA_MIN, + hEta1_Eta2->GetXaxis()->GetXmax(), ETA_MIN); + auto* lh2 = makeLine(hEta1_Eta2->GetXaxis()->GetXmin(), ETA_MAX, + hEta1_Eta2->GetXaxis()->GetXmax(), ETA_MAX); + + c->SaveAs(outname_pdf.c_str()); + c->SaveAs(outname_png.c_str()); + + + TCanvas* c_sim = new TCanvas("c_sim", "Muon geometrical accuracy analysis", 1600, 1000); + c_sim->Divide(2, 2); + + c_sim->cd(1); drawEffPanel(hQ2_all, hQ2_in, "Geom. acc. vs Q^{2}", "Q^{2} [GeV^{2}]", true); + c_sim->cd(2); drawEffPanel(hX_all, hX_in, "Geom. acc. vs x", "x", true); + c_sim->cd(3); drawEffPanel(hY_all, hY_in, "Geom. acc. vs y", "y", false); + c_sim->cd(4); drawEffPanel(hW_all, hW_in, "Geom. acc. vs W", "W [GeV]", false); + + c_sim->SaveAs(addPrefixAfterSlash(outname_png, "sim_").c_str()); + c_sim->SaveAs(addPrefixAfterSlash(outname_pdf, "sim_").c_str()); + + TCanvas* c_rec = new TCanvas("c_rec", "Muon geometrical accuracy analysis rec", 1600, 1000); + c_rec->Divide(2, 2); + + c_rec->cd(1); drawEffPanel(hQ2_rec_all, hQ2_rec_in, "Geom. acc. vs Q^{2}", "Q^{2} [GeV^{2}]", true); + c_rec->cd(2); drawEffPanel(hX_rec_all, hX_rec_in, "Geom. acc. vs x", "x", true); + c_rec->cd(3); drawEffPanel(hY_rec_all, hY_rec_in, "Geom. acc. vs y", "y", false); + c_rec->cd(4); drawEffPanel(hW_rec_all, hW_rec_in, "Geom. acc. vs W", "W [GeV]", false); + + c_rec->SaveAs(addPrefixAfterSlash(outname_png, "rec_").c_str()); + c_rec->SaveAs(addPrefixAfterSlash(outname_pdf, "rec_").c_str()); + + TCanvas* c_hitTrack = new TCanvas("c_hitTrack", "Muon hit and track analysis", 1600, 1000); + c_hitTrack->Divide(2,2); + c_hitTrack->cd(1); gPad->SetGrid(); hZ_proj->Draw(); + c_hitTrack->cd(2); gPad->SetGrid(); hZ_hits->Draw(); + c_hitTrack->SaveAs(addPrefixAfterSlash(outname_png, "hitTrack_").c_str()); + c_hitTrack->SaveAs(addPrefixAfterSlash(outname_pdf, "hitTrack_").c_str()); + + TCanvas* c_hitTrackDistance = new TCanvas("c_hitTrackDistance", "Muon hit-track distance analysis", 1600, 1000); + c_hitTrackDistance->Divide(2,2); + c_hitTrackDistance->cd(1); gPad->SetGrid(); hDxDyZ_layer->Draw("COLZ"); + c_hitTrackDistance->cd(2); gPad->SetGrid(); hDxDy_all->Draw("COLZ"); + c_hitTrackDistance->cd(3); gPad->SetGrid(); hDrZ_layer->Draw("COLZ"); + c_hitTrackDistance->cd(4); gPad->SetGrid(); hDr_all->Draw("COLZ"); + c_hitTrackDistance->SaveAs(addPrefixAfterSlash(outname_png, "hitTrackDistance_").c_str()); + c_hitTrackDistance->SaveAs(addPrefixAfterSlash(outname_pdf, "hitTrackDistance_").c_str()); + + TCanvas* c_Eff = new TCanvas("c_Eff", "Muon efficiency analysis", 1600, 1000); + c_Eff->Divide(2,2); + c_Eff->cd(1); gPad->SetGrid(); hP_all_mu->SetLineColor(kBlack); hP_all_mu->Draw(); + c_Eff->cd(2); gPad->SetGrid(); + TH1D* hEff_dr[3]; + for (int idr=0; idr<3; ++idr) { + hEff_dr[idr] = (TH1D*)hP_pass_dr[idr]->Clone( + Form("hEff_dr%.1fcm", DR_CUTS_CM[idr]) + ); + hEff_dr[idr]->SetDirectory(nullptr); + hEff_dr[idr]->SetTitle(Form("Efficiency vs p_{MC} (Layers > %d); p_{MC} [GeV]; efficiency", LAYER_THRESH)); + hEff_dr[idr]->Divide(hP_pass_dr[idr], hP_all_mu, 1, 1, "B"); + } + + hEff_dr[0]->SetLineColor(kBlue); hEff_dr[0]->SetMinimum(0.0); hEff_dr[0]->SetMaximum(1.4); + hEff_dr[1]->SetLineColor(kRed); + hEff_dr[2]->SetLineColor(kGreen+2); + + hEff_dr[0]->Draw("HIST E"); + hEff_dr[1]->Draw("HIST E SAME"); + hEff_dr[2]->Draw("HIST E SAME"); + { auto leg_dr = new TLegend(0.60,0.70,0.88,0.88); + for(int idr=0; idr<3; ++idr) + {leg_dr->AddEntry(hEff_dr[idr],Form("dr < %.1f cm", DR_CUTS_CM[idr]),"l");} + leg_dr->Draw(); + } + + c_Eff->cd(3); gPad->SetGrid(); + TH1D* hEff_E[3]; + for (int ie=0; ie<3; ++ie) { + hEff_E[ie] = (TH1D*)hP_pass_ECut[ie]->Clone( + Form("hEff_E%.1fcm", E_CUTS[ie]) + ); + hEff_E[ie]->SetDirectory(nullptr); + hEff_E[ie]->SetTitle(Form("Efficiency vs p_{MC} (Layers > %d, dr < %.1f cm); p_{MC} [GeV]; efficiency",LAYER_THRESH, DR_THRESH_MM/10)); + hEff_E[ie]->Divide(hP_pass_ECut[ie], hP_all_mu, 1, 1, "B"); + } + + hEff_E[0]->SetLineColor(kBlue); hEff_E[0]->SetMinimum(0.0); hEff_E[0]->SetMaximum(1.4); + hEff_E[1]->SetLineColor(kRed); + hEff_E[2]->SetLineColor(kGreen+2); + + hEff_E[0]->Draw("HIST E "); + hEff_E[1]->Draw("HIST E SAME"); + hEff_E[2]->Draw("HIST E SAME"); + { auto leg_E = new TLegend(0.60,0.70,0.88,0.88); + for(int ie=0; ie<3; ++ie) + {leg_E->AddEntry(hEff_E[ie],Form("E < %.1f MIP", E_CUTS[ie]),"l");} + leg_E->Draw(); + } + + c_Eff->cd(4); gPad->SetGrid(); + TH1D* hEff_Layer[3]; + for (int il=0; il<3; ++il) { + hEff_Layer[il] = (TH1D*)hP_pass_LayerCut[il]->Clone( + Form("hEff_Layer%d", LAYER_CUTS[il]) + ); + hEff_Layer[il]->SetDirectory(nullptr); + hEff_Layer[il]->SetTitle(Form("Efficiency vs p_{MC} (dr < %.1f cm); p_{MC} [GeV]; efficiency", DR_THRESH_MM/10)); + hEff_Layer[il]->Divide(hP_pass_LayerCut[il], hP_all_mu, 1, 1, "B"); + } + + hEff_Layer[0]->SetLineColor(kBlue); hEff_Layer[0]->SetMinimum(0.0); hEff_Layer[0]->SetMaximum(1.4); + hEff_Layer[1]->SetLineColor(kRed); + hEff_Layer[2]->SetLineColor(kGreen+2); + + hEff_Layer[0]->Draw("HIST E "); + hEff_Layer[1]->Draw("HIST E SAME"); + hEff_Layer[2]->Draw("HIST E SAME"); + { auto leg_Layer = new TLegend(0.60,0.70,0.88,0.88); + for(int il=0; il<3; ++il) + {leg_Layer->AddEntry(hEff_Layer[il],Form("L > %d Layers", LAYER_CUTS[il]),"l");} + leg_Layer->Draw(); + } + c_Eff->SaveAs(addPrefixAfterSlash(outname_png, "matching_efficiency_").c_str()); + c_Eff->SaveAs(addPrefixAfterSlash(outname_pdf, "matching_efficiency_").c_str()); + + TCanvas* c_hEnergy = new TCanvas("c_hEnergy", "Muon hit energy analysis", 1600, 1000); + c_hEnergy->Divide(2,2); + + c_hEnergy->cd(1); gPad->SetGrid(); gPad->SetLogy(); hE_z->GetYaxis()->SetMoreLogLabels(); hE_z->Draw("COLZ"); + TProfile* pE_z = hE_z->ProfileX("pE_z"); pE_z->SetLineWidth(3); pE_z->SetLineColor(kRed); pE_z->SetMarkerColor(kRed); + pE_z->SetDirectory(nullptr); pE_z->Draw("SAME"); + + const double Ecut_GeV = MIP_ENERGY_GEV * t_cm * 100; + TLine* cut = new TLine(hE_z->GetXaxis()->GetXmin(), Ecut_GeV, hE_z->GetXaxis()->GetXmax(), Ecut_GeV); + cut->SetLineColor(kRed+1); cut->SetLineStyle(2); cut->SetLineWidth(2); + cut->Draw("SAME"); + auto* leg = new TLegend(0.58, 0.75, 0.84, 0.88); leg->SetTextSize(0.04);leg->AddEntry(cut, Form("E_{cut} = %.3f GeV", Ecut_GeV), "l"); + leg->Draw(); + + c_hEnergy->cd(2); gPad->SetGrid(); gPad->SetLogy(); hEsum_z->GetYaxis()->SetMoreLogLabels(); hEsum_z->Draw("COLZ"); + TProfile* pEsum_z = hEsum_z->ProfileX("pEsum_z"); pEsum_z->SetLineWidth(3); pEsum_z->SetLineColor(kRed); pEsum_z->SetMarkerColor(kRed); + pEsum_z->SetDirectory(nullptr); pEsum_z->Draw("SAME"); + c_hEnergy->cd(3); gPad->SetGrid(); hE->Draw(); + c_hEnergy->cd(4); gPad->SetGrid(); hEsum->Draw(); + + c_hEnergy->SaveAs(addPrefixAfterSlash(outname_png, "hit_energy_").c_str()); + c_hEnergy->SaveAs(addPrefixAfterSlash(outname_pdf, "hit_energy_").c_str()); + + delete c; + delete c_sim; + delete c_rec; + delete c_hitTrack; + delete c_hitTrackDistance; + delete c_Eff; + delete c_hEnergy; + + return 0; +} diff --git a/benchmarks/nhcal_dimuon_fotoproduction/scripts/ep_DiMuon.cmnd b/benchmarks/nhcal_dimuon_fotoproduction/scripts/ep_DiMuon.cmnd new file mode 100644 index 00000000..9fb98946 --- /dev/null +++ b/benchmarks/nhcal_dimuon_fotoproduction/scripts/ep_DiMuon.cmnd @@ -0,0 +1,180 @@ +#============================================================================== +# STAR Heavy Flavor Tune 1.1 +# +# PYTHIA Version 8.1.08 +# Date: November 7, 2008 +# Last updated by: Thomas Ullrich +# +# This file contains commands to be read in for a Pythia8 run. +# Lines not beginning with a letter or digit are comments. +# Names are case-insensitive - but spellings-sensitive! +# +# Important notes: +# ================ +# For hard 2->2 processes, such as most heavy flavor producing one, +# there's divergence in PYTHIA that needs to be suppressed. Use +# these two lines in the code before you initialize PYTHIA to avoid +# any trouble: +# UserHooks *oniumUserHook = new SuppressSmallPT(); +# pythia.setUserHooksPtr(oniumUserHook); +# +#============================================================================== + +#------------------------------------------------------------------------------ +# Parameters that need to be set by whoever runs it. +# Note that they have no meaning unless restored and used +# in the user provided code (main program). +# This is not part of the star_hf tune (just convenient) +# Documentation: /htmldoc/MainProgramSettings.html +#------------------------------------------------------------------------------ +Main:numberOfEvents = 1000 ! number of events to generate +Next:numberShowEvent = 20 ! show event record +Next:numberCount = 0 ! number of events to print +Next:numberShowInfo = 100 ! show how far along run is this many times +Main:timesAllowErrors = 100 ! abort run after this many flawed events +Init:showChangedSettings = on ! print changed flags/modes/parameters +Init:showAllSettings = on ! print all flags/modes/parameters +#Init:showAllStatistics = on ! print statistics at the end + +#------------------------------------------------------------------------------ +# Colliding beams and center-of-mass energy +# Documentation: /htmldoc/BeamParameters.html +#------------------------------------------------------------------------------ +Beams:frameType = 2 +Beams:idA = 2212 ! proton +Beams:idB = 11 ! e- +Beams:eA = 275. ! RHIC nominal (GeV) +Beams:eB = 18. ! RHIC nominal (GeV) + +#------------------------------------------------------------------------------ +# Process Selection +#------------------------------------------------------------------------------ + + +# Enable equivalent photon approximation (EPA) for both beams +Photon:Q2Max = 1.0 ! Upper Q^2 limit for EPA photons (in GeV^2) +Photon:ProcessType = 4 ! 4 = direct-direct photons +#Photon:EPA = on + +PDF:beamA2gamma = on ! EPA photon flux from beam A +PDF:beamB2gamma = on ! EPA photon flux from beam B + +# Enable gamma-gamma -> mu+ mu− +PhotonCollision:gmgm2mumu = on + +# Optional: Turn off other QED or QCD backgrounds if you want exclusivity +PartonLevel:ISR = off +PartonLevel:FSR = off +HadronLevel:all = off + + +#------------------------------------------------------------------------------ +# K factor +# Multiply almost all cross sections by this common fix factor. +# This is usually no very useful. The data can be shifted up and down +# later anyhow as we please. +# Documentation: /htmldoc/CouplingsAndScales.html +#------------------------------------------------------------------------------ +# SigmaProcess:Kfactor = 3 + +#------------------------------------------------------------------------------ +# Scales (Ramona's suggestions) +# This sets the scale to settings typically for hard probes: +# mu_F = mu_R = 2*mT +# Documentation: /htmldoc/CouplingsAndScales.html +#------------------------------------------------------------------------------ +# SigmaProcess:renormScale2 = 3 +# SigmaProcess:factorScale2 = 3 +# SigmaProcess:renormMultFac = 2 ! 2mT +# SigmaProcess:factorMultFac = 2 ! 2mT + +#------------------------------------------------------------------------------ +# To limit particle production to a certain pthat range uncomment +# these lines. Use only when you 100% know what you are doing. +# It is extremely useful to split runs up in ptHat bins to generate +# statistics evenly in pt. Book keeping is important then (cross-sections, +# number of events) to compile the final complete spectra. +# Documentation: /htmldoc/PhaseSpaceCuts.html +#------------------------------------------------------------------------------ +# PhaseSpace:pTHatMin = 4 ! Minimal pT cut +# PhaseSpace:pTHatMax = 2 + +#------------------------------------------------------------------------------ +# Random Number +# Initialize random generator according to time. Otherwise multiple jobs +# will produce the same sequence (unless you pass a different seed every +# time which is not practical). +# Documentation: /htmldoc/RandomNumberSeed.html +#------------------------------------------------------------------------------ +Random:setSeed = on +Random:seed = 0 + +#------------------------------------------------------------------------------ +# PDF Selection: +# Note: you need LHAPDF to be installed. Pythia 8 only provides a +# minimal set to get started. +# The choice of PDF here is greatly motivated by: +# A.~Sherstnev and R.~S.~Thorne, arXiv:0807.2132 and arXiv:0711.2473v3 +# and W. Vogelsang (private communication) +# These are PDFs especially made for LO Monte-Carlo generators such +# as PYTHIA. +# The state-of-the-art NLO PDF is cteq66.LHgrid which can be used +# as an alternative (also from LHAPDF) but with the known problems +# that arise when using a NLO PDF in an LO simulator. +# Documentation: /htmldoc/PDFSelection.html +#------------------------------------------------------------------------------ +#PDF:useLHAPDF = off +#PDF:LHAPDFset = MRSTMCal.LHgrid +#PDF:extrapolateLHAPDF = on +#PDF:pSet = 8 +#PDF:beamA2gamma = on + +#------------------------------------------------------------------------------ +# Settings for the event generation process in the Pythia8 library +# Effect/Relevance of MI, ISR and FSR need to be checked. For sure +# the use more CPU and hence less events/s. +# If HadronLevel:Hadronize = off we end up with the pure c, b spectra +# (which might be useful at times) +# Documentation: /htmldoc/MasterSwitches.html +# Documentation: /htmldoc/MultipleInteractions.html +#------------------------------------------------------------------------------ +#PartonLevel:MPI = on ! multiple interactions +#PartonLevel:ISR = on ! initial-state radiation +#BeamRemnants:primordialKT = on ! primordial kt +#PartonLevel:FSR = on ! final-state radiation +#HadronLevel:Hadronize = off ! no hadronization use + +#------------------------------------------------------------------------------ +# Relative production ratio vector/pseudoscalar for charm and bottom mesons +# This was originally PARJ(13) where PARJ(13) = V/(PS+V) that is the +# vector meson fraction of primary charm+bottom mesons. +# Andre David (CERN/NA60) made an exhaustive study and found that the +# world data supports 0.6 while PYTHIA default was PARJ(13) = 3/4 = 0.75 +# from simple spin counting. +# In PYTHIA8 we now use V/PS not V/(PS+V) +# Documentation: /htmldoc/FlavourSelection.html +#------------------------------------------------------------------------------ +#StringFlav:mesonCvector = 1.5 ! same as PARJ(13)=0.6 +#StringFlav:mesonBvector = 3 ! leave at 0.75 + +#------------------------------------------------------------------------------ +# Heavy quark masses. +# Note that this should match with the ones used in the PDF. +# The masses are listed in the header of the refering PDF file. +# Documentation: /htmldoc/ParticleDataScheme.html +# Documentation: /htmldoc/ParticleData.html +#------------------------------------------------------------------------------ +#4:m0 = 1.43 +#5:m0 = 4.30 + +#------------------------------------------------------------------------------ +# Particle Decay limits +# When on, only particles with a decay within a volume limited by +# rho = sqrt(x^2 + y^2) < xyMax and |z| < zMax are decayed. +# The above xyMax, expressed in mm/c. +#------------------------------------------------------------------------------ +# ParticleDecays:limitCylinder = on +# ParticleDecays:xyMax = 600 +# ParticleDecays:zMax = 1000 + +# EOF diff --git a/benchmarks/nhcal_dimuon_fotoproduction/scripts/pythiaDiMuon b/benchmarks/nhcal_dimuon_fotoproduction/scripts/pythiaDiMuon new file mode 100755 index 00000000..166b0113 Binary files /dev/null and b/benchmarks/nhcal_dimuon_fotoproduction/scripts/pythiaDiMuon differ diff --git a/benchmarks/nhcal_dimuon_fotoproduction/scripts/run.sh b/benchmarks/nhcal_dimuon_fotoproduction/scripts/run.sh new file mode 100755 index 00000000..5c130516 --- /dev/null +++ b/benchmarks/nhcal_dimuon_fotoproduction/scripts/run.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +#starpro +#setup 64b +#setup ROOT 5.34.38 +#source bin/thisroot.csh + +export ROOTLIB=/opt/software/linux-debian12-x86_64_v2/gcc-12.2.0/root-6.32.08-zlapeub2v5vfuu3m6bqz6igubplfiios/lib/root + +# PYTHIA8 +export PYTHIA8=/opt/local + +# LHAPDF +export LHAPDFSYS=/opt/software/linux-debian12-x86_64_v2/gcc-12.2.0/lhapdf-6.5.4-eu6c7akvcf5zv3otrikiyvijfcx3wzvh + +# Uzupełnienie ścieżek +export PATH=$PATH:$LHAPDFSYS/bin +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LHAPDFSYS/lib:$PYTHIA8/lib:$ROOTLIB + +# Dane Pythii +export PYTHIA8DATA=$PYTHIA8/share/Pythia8/xmldoc + +INDEX=${1:?Użycie: $0 [OUTDIR]} +OUTDIR=${2:-"$(pwd)/sim_input"} + +SCRIPT_DIR="$(dirname "$0")" + +mkdir -p "$OUTDIR" + +OUTBASE="${OUTDIR}/DiMuon_ep_18x275GeV.${INDEX}" +LOG="${OUTDIR}/DiMuon_ep_18x275GeV_${INDEX}.log" + +"${SCRIPT_DIR}/pythiaDiMuon" "${SCRIPT_DIR}/ep_DiMuon.cmnd" "$OUTBASE" | tee "$LOG" + diff --git a/benchmarks/nhcal_pion_rejection/Snakefile b/benchmarks/nhcal_pion_rejection/Snakefile new file mode 100644 index 00000000..24af06b5 --- /dev/null +++ b/benchmarks/nhcal_pion_rejection/Snakefile @@ -0,0 +1,77 @@ +rule nhcal_pion_rejection_simulate: + output: + "sim_output/nhcal_pion_rejection/sim_{DETECTOR_CONFIG}.{INDEX}.edm4hep.root", + params: + N_EVENTS=1000, + SEED=lambda wildcards: "1" + wildcards.INDEX, + DETECTOR_PATH=os.environ["DETECTOR_PATH"], + DETECTOR_CONFIG=lambda wildcards: wildcards.DETECTOR_CONFIG, + DD4HEP_HASH=get_spack_package_hash("dd4hep"), + NPSIM_HASH=get_spack_package_hash("npsim"), + wildcard_constraints: + DETECTOR_CONFIG = r"[A-Za-z0-9_]+", + INDEX = r"\d+", + #cache: True + shell: + """ +exec npsim \ + --compactFile {params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}.xml \ + --numberOfEvents {params.N_EVENTS} \ + --random.seed {params.SEED} \ + --enableGun \ + -v WARNING \ + --gun.particle pi- \ + --gun.thetaMin 120*degree \ + --gun.thetaMax 180*degree \ + --gun.distribution uniform \ + --gun.momentumMin "1*GeV" \ + --gun.momentumMax "18*GeV" \ + --outputFile {output} +""" + +rule nhcal_pion_rejection_recon: + input: + "sim_output/nhcal_pion_rejection/sim_{DETECTOR_CONFIG}.{INDEX}.edm4hep.root" + output: + "sim_output/nhcal_pion_rejection/sim_{DETECTOR_CONFIG}.{INDEX}.eicrecon.edm4hep.root" + params: + DETECTOR_CONFIG = lambda wildcards: wildcards.DETECTOR_CONFIG, + #cache: True + shell: + """ +exec env DETECTOR_CONFIG={params.DETECTOR_CONFIG} \ + eicrecon {input} \ + -Ppodio:output_file={output} \ + +""" + +rule nhcal_pion_rejection_combine: + input: + lambda wildcards: expand( + "sim_output/nhcal_pion_rejection/sim_{DETECTOR_CONFIG}.{INDEX}.eicrecon.edm4hep.root", + DETECTOR_CONFIG=wildcards.DETECTOR_CONFIG, + INDEX=range(int(wildcards.N)), + ) + output: + temp("sim_output/nhcal_pion_rejection/sim_{DETECTOR_CONFIG}_{N}files.eicrecon.edm4hep.root"), + wildcard_constraints: + N = r"\d+", + shell: + """ +hadd -f {output} {input} +""" + +rule nhcal_pion_rejection_analysis: + input: + combined="sim_output/nhcal_pion_rejection/sim_{DETECTOR_CONFIG}_{N}files.eicrecon.edm4hep.root", + script="benchmarks/nhcal_pion_rejection/scripts/pion_rejection_analysis.cxx", + output: + png=f"results/nhcal_pion_rejection/analysis_{{DETECTOR_CONFIG}}_{{N}}files.png", + pdf=f"results/nhcal_pion_rejection/analysis_{{DETECTOR_CONFIG}}_{{N}}files.pdf", + params: + DETECTOR_PATH = os.environ["DETECTOR_PATH"], + DETECTOR_CONFIG = lambda wildcards: f"{wildcards.DETECTOR_CONFIG}.xml", + shell: + """ + root -l -b -q '{input.script}("{input.combined}","{output.pdf}","{output.png}","{params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}")' +""" \ No newline at end of file diff --git a/benchmarks/nhcal_pion_rejection/config.yml b/benchmarks/nhcal_pion_rejection/config.yml new file mode 100644 index 00000000..87b56251 --- /dev/null +++ b/benchmarks/nhcal_pion_rejection/config.yml @@ -0,0 +1,28 @@ +sim:nhcal_pion_rejection: + extends: .det_benchmark + stage: simulate + parallel: + matrix: + - INDEX_RANGE: ["0 4","5 9"] + script: + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB $(for INDEX in $(seq -f '%02.0f' $INDEX_RANGE); do echo sim_output/nhcal_pion_rejection/sim_epic_full.${INDEX}.edm4hep.root; done) + + +bench:nhcal_pion_rejection_analysis: + extends: .det_benchmark + stage: benchmarks + needs: + - "sim:nhcal_pion_rejection" + script: + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB sim_output/nhcal_pion_rejection/sim_epic_full_10files.eicrecon.edm4hep.root + +collect_results:nhcal_pion_rejection: + extends: .det_benchmark + stage: collect + needs: + - "bench:nhcal_pion_rejection_analysis" + script: + - ls -lrht + - mv results{,_save}/ + - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output results/nhcal_pion_rejection/analysis_epic_full_10files.pdf + - mv results{_save,}/ diff --git a/benchmarks/nhcal_pion_rejection/scripts/pion_rejection_analysis.cxx b/benchmarks/nhcal_pion_rejection/scripts/pion_rejection_analysis.cxx new file mode 100644 index 00000000..4bcd3ec4 --- /dev/null +++ b/benchmarks/nhcal_pion_rejection/scripts/pion_rejection_analysis.cxx @@ -0,0 +1,799 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "TLorentzVector.h" + +#include "ROOT/RDataFrame.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TROOT.h" +#include "TRandom.h" +#include "TH3.h" + +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" +#include "DD4hep/Volumes.h" +#include "DD4hep/Objects.h" +#include "DD4hep/Shapes.h" +#include +#include +#include +#include +#include + + + +#include "podio/Frame.h" +#include "podio/CollectionBase.h" +#include "podio/ROOTReader.h" +#include "podio/CollectionIDTable.h" +#include "podio/ObjectID.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/MCParticleCollectionData.h" +#include "edm4hep/MCParticle.h" +#include "edm4hep/MCParticleData.h" + +#include "edm4hep/SimCalorimeterHitCollectionData.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/SimCalorimeterHitData.h" +#include "edm4hep/SimCalorimeterHit.h" + +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitCollectionData.h" +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/CalorimeterHitData.h" +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitObj.h" + +#include "edm4eic/ClusterCollection.h" +#include "edm4eic/Cluster.h" +#include "edm4eic/ClusterData.h" + +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitCollectionData.h" +#include "edm4eic/CalorimeterHitCollection.h" +#include "edm4eic/CalorimeterHitData.h" +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitObj.h" + +#include "edm4eic/InclusiveKinematicsCollection.h" +#include "edm4eic/InclusiveKinematics.h" +#include "edm4hep/utils/kinematics.h" +#include "edm4hep/utils/vector_utils.h" + +#include "edm4eic/vector_utils_legacy.h" +#include "edm4hep/Vector3f.h" + +#include "edm4eic/Track.h" +#include "edm4eic/TrackSegment.h" +#include "edm4eic/TrackSegmentCollectionData.h" +#include "edm4eic/TrackPoint.h" +#include "edm4eic/TrackParameters.h" + +#include "edm4eic/TrackSegmentCollection.h" +#include "edm4eic/ReconstructedParticleCollection.h" +#include "edm4eic/ReconstructedParticle.h" + +#include "edm4eic/MCRecoCalorimeterHitAssociationCollection.h" +#include "edm4eic/MCRecoCalorimeterHitAssociation.h" +#include "edm4eic/MCRecoParticleAssociationCollection.h" +#include "edm4eic/MCRecoParticleAssociation.h" + +using namespace std; +using namespace ROOT; +using namespace TMath; +using namespace edm4hep; + +dd4hep::Detector* det = NULL; + +constexpr double ETA_MIN = -4.14, ETA_MAX = -1.16; + +inline bool inNHCal(double eta) {return (eta >= ETA_MIN && eta <= ETA_MAX);} + +inline string addPrefixAfterSlash(const string& path, + const string& prefix) { + const auto slash = path.find_last_of('/'); + if (slash == string::npos) + return prefix + path; + return path.substr(0, slash + 1) + prefix + path.substr(slash + 1); +} + +void MakeLogBins(double *array, int nbins, double binLo, double binHi) +{ + double logMin = log10(binLo); + double logMax = log10(binHi); + double binWidth = (logMax - logMin) / nbins; + + for (int i = 0; i <= nbins; ++i) { + array[i] = pow(10, logMin + i * binWidth); + } +} + +TLine* makeLine(double x1, double y1, double x2, double y2) { + TLine* l = new TLine(x1, y1, x2, y2); + l->SetLineColor(kRed); + l->SetLineStyle(2); + l->SetLineWidth(2); + l->Draw("same"); + return l; +} + +static TGraph* makeEffGraph_B(TH1D* h_sel, TH1D* h_all, + const char* name, + int minAll = 2, + bool logx = false) +{ + if (!h_sel || !h_all) return nullptr; + + std::unique_ptr h_eff((TH1D*)h_sel->Clone(Form("tmp_%s", name ? name : "eff"))); + h_eff->SetDirectory(nullptr); + h_eff->Sumw2(); + h_eff->Divide(h_sel, h_all, 1.0, 1.0, "B"); + + const int nb = h_all->GetNbinsX(); + + std::unique_ptr g_log; + std::unique_ptr g_lin; + + if (logx) g_log = std::make_unique(); + else g_lin = std::make_unique(); + + int ip = 0; + for (int b = 1; b <= nb; ++b) { + const double all = h_all->GetBinContent(b); + if (all < minAll) continue; + + const double xl = h_all->GetXaxis()->GetBinLowEdge(b); + const double xh = h_all->GetXaxis()->GetBinUpEdge(b); + + const double y = 100.0 * h_eff->GetBinContent(b); + const double ey = 100.0 * h_eff->GetBinError(b); + + if (logx) { + if (xl <= 0.0 || xh <= 0.0) continue; + const double x = sqrt(xl * xh); + const double exl = x - xl; + const double exh = xh - x; + + g_log->SetPoint(ip, x, y); + g_log->SetPointError(ip, exl, exh, ey, ey); + } else { + const double x = h_all->GetBinCenter(b); + const double ex = 0.5 * (xh - xl); + + g_lin->SetPoint(ip, x, y); + g_lin->SetPointError(ip, ex, ey); + } + ++ip; + } + + TGraph* g = logx ? (TGraph*)g_log.release() : (TGraph*)g_lin.release(); + g->SetName(Form("g_%s", name ? name : "eff")); + return g; +} + +inline void drawEffPanel(TH1D* h_all, + TH1D* const h_in[3], + const char* title, + const char* xlabel, + bool logx) +{ + gPad->SetGrid(); + if (logx) gPad->SetLogx(); + + auto* mg = new TMultiGraph(); + auto* leg = new TLegend(0.63, 0.7, 0.88, 0.88); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->SetTextSize(0.04); + + Color_t colors[3] = {kBlue, kRed, kBlack}; + Style_t markers[3] = {20, 21, 22}; + + int added = 0; + + for (int i = 0; i < 3; ++i) { + if (!h_all || !h_in[i]) continue; + auto* g = makeEffGraph_B(h_in[i], h_all, Form("eff_%d", i), 2, logx); + if (!g || g->GetN() == 0) continue; + + g->SetMarkerColor(colors[i]); + g->SetMarkerStyle(markers[i]); + g->SetMarkerSize(1.0); + g->SetLineColor(kBlack); + + mg->Add(g, "PE"); + leg->AddEntry(g, Form("%d mu-in nHCAL", i), "p"); + ++added; + } + + if (h_all && h_in[0] && h_in[1] && h_in[2]) { + std::unique_ptr h_sum((TH1D*)h_in[0]->Clone("h_sum")); + h_sum->SetDirectory(nullptr); + h_sum->Add(h_in[1]); + h_sum->Add(h_in[2]); + + auto* gsum = makeEffGraph_B(h_sum.get(), h_all, "eff_sum", 2, logx); + if (gsum && gsum->GetN() > 0) { + gsum->SetMarkerStyle(25); + gsum->SetMarkerColor(kMagenta + 1); + gsum->SetFillColor(kMagenta + 1); + gsum->SetLineColor(kBlack); + gsum->SetMarkerSize(1.0); + + mg->Add(gsum, "PE"); + leg->AddEntry(gsum, "All eligible", "p"); + ++added; + } + } + + mg->SetTitle(Form("%s;%s;geom. acc. [%%]", title ? title : "", xlabel ? xlabel : "")); + mg->Draw("A"); + + gPad->Update(); + if (mg->GetHistogram()) { + double ymax = mg->GetHistogram()->GetMaximum(); + mg->GetHistogram()->SetMaximum(ymax * 1.35); + } + gPad->Modified(); + gPad->Update(); + + if (logx) { + auto* ax = mg->GetXaxis(); + if (ax) { + double xmin = ax->GetXmin(), xmax = ax->GetXmax(); + if (xmin <= 0) xmin = 1e-12; + mg->GetXaxis()->SetLimits(xmin, xmax); + } + } + + leg->Draw(); +} + +inline TVector3 getPlasticDimensionsCM(dd4hep::Detector& det, + const dd4hep::DDSegmentation::BitFieldCoder* dec, + dd4hep::DDSegmentation::CellID cid, + int slice_idx, + int plastic_slice_value = 3) +{ + const double NaN = numeric_limits::quiet_NaN(); + TVector3 dims(NaN, NaN, NaN); + try { + if (!dec) throw runtime_error("decoder==nullptr"); + if (dec->get(cid, slice_idx) != plastic_slice_value) + throw runtime_error("cell is not plastic"); + + auto de = det.volumeManager().lookupDetElement(cid); + if (!de.isValid()) throw runtime_error("lookupDetElement failed"); + + auto pv = de.placement(); + if (!pv.isValid()) throw runtime_error("DetElement has no placement"); + + auto vol = pv.volume(); + if (!vol.isValid()) throw runtime_error("Invalid Volume"); + + auto* box = dynamic_cast(vol.solid().ptr()); + if (!box) throw runtime_error("Solid is not TGeoBBox"); + + dims.SetXYZ(2.0 * box->GetDX(), + 2.0 * box->GetDY(), + 2.0 * box->GetDZ()); + + //const double dz_cm = box->GetDZ(); + //const double thickness_cm = 2.0 * dz_cm; + //return thickness_cm; + return dims; + } catch (const exception& e) { + cerr << "[WARN] getPlasticThicknessMM: " << e.what() << " (cellID=" << cid << ")\n"; + return dims; + } +} + +inline double getPlasticCenterZ_cm( + const dd4hep::DDSegmentation::BitFieldCoder* decoder, + const dd4hep::rec::CellIDPositionConverter& cellid_converter, + dd4hep::DDSegmentation::CellID cid, + int slice_index, + int plastic_slice_value = 3) +{ + try { + if (!decoder) throw std::runtime_error("decoder==nullptr"); + if (decoder->get(cid, slice_index) != plastic_slice_value) + throw std::runtime_error("cell is not plastic (slice mismatch)"); + return cellid_converter.position(cid).z(); + } catch (const std::exception& e) { + cerr << "[WARN] getPlasticThicknessCM: " << e.what() + << " (cellID=" << cid << ")\n"; + return std::numeric_limits::quiet_NaN();; + } +} + +struct GeomState { + double x, y, z; +}; + +inline double hypot2(double a, double b){ return sqrt(a*a + b*b); } +inline int sgnD(double v){ return (v>0) - (v<0); } + +inline pair +trackXYatZ_fromTwoStates(double x1,double y1,double z1, + double x2,double y2,double z2, + double zTarget) +{ + constexpr double EPSZ = 1e-12; + + const double dz = z2 - z1; + if (abs(dz) < EPSZ) { + const double d1 = abs(zTarget - z1); + const double d2 = abs(zTarget - z2); + if (d1 < d2) return {x1, y1}; + if (d2 < d1) return {x2, y2}; + return {0.5*(x1+x2), 0.5*(y1+y2)}; + } + + const double t = (zTarget - z1) / dz; + const double x = x1 + t * (x2 - x1); + const double y = y1 + t * (y2 - y1); + return {x, y}; +} + +inline pair +trackXYatZ(const GeomState& A, const GeomState& B, double zTarget){ + return trackXYatZ_fromTwoStates( + A.x, A.y, A.z, B.x, B.y, B.z, + zTarget + ); +} + + +int pion_rejection_analysis(const string& filename, string outname_pdf, string outname_png, TString compact_file) { + + gStyle->SetOptStat(0); + podio::ROOTReader reader; + reader.openFile(filename); + unsigned nEvents = reader.getEntries("events"); + cout << "Number of events: " << nEvents << endl; + + det = &(dd4hep::Detector::getInstance()); + det->fromCompact(compact_file.Data()); + det->volumeManager(); + det->apply("DD4hepVolumeManager", 0, 0); + + dd4hep::rec::CellIDPositionConverter cellid_converter(*det); + auto idSpec = det->readout("HcalEndcapNHits").idSpec(); + auto decoder = idSpec.decoder(); + const int slice_index = decoder->index("slice"); + if (slice_index < 0) { + cerr << "ERROR: 'slice' field not found in cell ID spec!" << endl; + return 1; + } + + //double thickness_cm = det->constantAsDouble("HcalEndcapNPolystyreneThickness")* 0.1; + + constexpr int NBINS = 60; + + constexpr double Q2_MIN = 1e-2; + constexpr double Q2_MAX = 1.0; + constexpr double X_MIN = 1e-6; + constexpr double X_MAX = 1e-3; + constexpr double Y_MIN = 0.0; + constexpr double Y_MAX = 1.0; + constexpr double W_MIN = 0.0; + constexpr double W_MAX = 160.0; + + constexpr int Z_NBINS = 100; + constexpr double Z_MIN_MM = -4500.0; + constexpr double Z_MAX_MM = -3600.0; + + constexpr int DXY_NBINS = 120; + constexpr double DXY_MIN_MM = -1000.0; + constexpr double DXY_MAX_MM = 1000.0; + + constexpr int DR_NBINS = 120; + constexpr double DR_MIN_MM = 0.0; + constexpr double DR_MAX_MM = 400.0; + + constexpr double P_MIN_GEV = 0.0; + constexpr double P_MAX_GEV = 25.0; + constexpr int P_NBINS = 50; + + constexpr double E_MIN_GEV = 2e-2; + constexpr double E_MAX_GEV = 10.0; + + constexpr int SIZE = 3; + constexpr double MIP_ENERGY_GEV = 0.002; + constexpr double E_CUTS[SIZE] = {1.5, 1.7, 2.0}; + constexpr double E_THRESH = E_CUTS[2]; + constexpr double LAYER_PROC[SIZE] = {0.6, 0.7, 0.8}; + + double t_cm; + double DR_CUTS_CM[SIZE] = {7.0, 10.0, 13.0}; + double DR_THRESH_MM = 10*DR_CUTS_CM[2]; + int LAYER_MAX = 10; + int LAYER_CUTS[SIZE] = {5, 6, 7}; + int LAYER_THRESH = LAYER_CUTS[0]; + + vector Xbins(NBINS+1), Q2bins(NBINS+1), Ebins(NBINS+1); + MakeLogBins(Q2bins.data(), NBINS, Q2_MIN, Q2_MAX); + MakeLogBins(Xbins.data(), NBINS, X_MIN, X_MAX); + MakeLogBins(Ebins.data(), NBINS, E_MIN_GEV, E_MAX_GEV); + + gStyle->SetTitleSize(0.045, "XYZ"); + gStyle->SetLabelSize(0.04, "XYZ"); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetPadRightMargin(0.15); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadTopMargin(0.10); + gROOT->ForceStyle(); + + TH2D* hEtaPt = new TH2D("hEtaPt", "Pion #eta vs p_{T};#eta;p_{T} [GeV]", 100, -6., 6., 100, 0., 7.); + + TH1D* hZ_proj = new TH1D("hZ_proj", "Pion track projections in nHCal; z [mm]; N", NBINS, Z_MIN_MM, Z_MAX_MM); + + TH1D* hZ_hits = new TH1D("hZ_hits", "Reconstructed hit z in nHCal; z [mm]; N", NBINS, Z_MIN_MM, Z_MAX_MM); + + TH3D* hDxDyZ_layer = new TH3D("hDxDyZ_layer","3D residuals (rec - proj): dx, dy vs z; dx [mm]; dy [mm]; z [mm]", NBINS, DXY_MIN_MM, DXY_MAX_MM, NBINS, DXY_MIN_MM, DXY_MAX_MM, NBINS, Z_MIN_MM, Z_MAX_MM); + + TH2D* hDxDy_all = new TH2D("hDxDy_all", + "Residuals (rec - proj): dx vs dy (all layers); dx [mm]; dy [mm]", + DXY_NBINS, DXY_MIN_MM, DXY_MAX_MM, DXY_NBINS, DXY_MIN_MM, DXY_MAX_MM); + + TH2D* hDrZ_layer = new TH2D("hDrZ_layer","Radial residual (rec - proj) vs z; dr [mm]; z [mm]", NBINS, DR_MIN_MM, DR_MAX_MM, NBINS, Z_MIN_MM, Z_MAX_MM); + + TH1D* hDr_all = new TH1D("hDr_all", + "Radial residual dr = #sqrt{dx^{2}+dy^{2}} (all layers); dr [mm]; N", + DR_NBINS, DR_MIN_MM, DR_MAX_MM); + + TH2D* hE_z = new TH2D("hE_z", "Reconstructed hit energy vs layer z; z_{layer} [mm]; E [GeV]", NBINS, Z_MIN_MM, Z_MAX_MM, NBINS, Ebins.data()); + + TH2D* hEsum_z = new TH2D("hEsum_z", "Layer energy sum vs z (reconstructed); z_{layer} [mm]; E_{sum} [GeV]", + NBINS, Z_MIN_MM, Z_MAX_MM, NBINS, Ebins.data()); + + TH1D* hE = new TH1D("hE", "Reconstructed hit energy; E [GeV]; N", 10000, 0.0, 1.0); + + TH1D* hEsum = new TH1D("hEsum", "Layer energy sum (reconstructed); E_{sum} [GeV]; N", 10000, 0.0, 1.0); + + TH1D* hP_all_pion = new TH1D("hP_all_pion", "MC pion momentum (pions in nHCal acceptance); p_{MC} [GeV]; N", P_NBINS, P_MIN_GEV, P_MAX_GEV); + + TH1D* hP_pass_dr[3] = { + new TH1D(Form("hP_pass_dr%.1fcm",DR_CUTS_CM[0]), Form("Accepted (dr<%.1fcm); p_{MC} [GeV]; N",DR_CUTS_CM[0]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_dr%.1fcm",DR_CUTS_CM[1]), Form("Accepted (dr<%.1fcm); p_{MC} [GeV]; N",DR_CUTS_CM[1]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_dr%.1fcm",DR_CUTS_CM[2]), Form("Accepted (dr<%.1fcm); p_{MC} [GeV]; N",DR_CUTS_CM[2]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + }; + + TH1D* hP_pass_ECut[3] = { + new TH1D(Form("hP_pass_E< %.1f MIP", E_CUTS[0]), Form("Accepted (E< %.1f MIP); p_{MC} [GeV]; N",E_CUTS[0]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_E< %.1f MIP", E_CUTS[1]), Form("Accepted (E< %.1f MIP); p_{MC} [GeV]; N",E_CUTS[1]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_E< %.1f MIP", E_CUTS[2]), Form("Accepted (E< %.1f MIP); p_{MC} [GeV]; N",E_CUTS[2]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + }; + + TH1D* hP_pass_LayerCut[3] = { + new TH1D(Form("hP_pass_Layer< %d layers", LAYER_CUTS[0]), Form("Accepted (Layer < %d layers); p_{MC} [GeV]; N",LAYER_CUTS[0]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_Layer< %d layers", LAYER_CUTS[1]), Form("Accepted (Layer < %d layers); p_{MC} [GeV]; N",LAYER_CUTS[1]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + new TH1D(Form("hP_pass_Layer< %d layers", LAYER_CUTS[2]), Form("Accepted (Layer < %d layers); p_{MC} [GeV]; N",LAYER_CUTS[2]), P_NBINS, P_MIN_GEV, P_MAX_GEV), + }; + + for (unsigned ev = 0; ev < nEvents; ev++) { + auto frameData = reader.readNextEntry(podio::Category::Event); + if (!frameData) continue; + podio::Frame frame(std::move(frameData)); + + auto& mcCol = frame.get("MCParticles"); + auto& recParts = frame.get("ReconstructedParticles"); + auto& projSegs = frame.get("CalorimeterTrackProjections"); + auto& hcalRec = frame.get("HcalEndcapNRecHits"); + auto& assocCol = frame.get("ReconstructedParticleAssociations"); + + if (!mcCol.isValid()) continue; + + vector vPions; + vector vLorentzPions; + for (const auto& p : mcCol) { + if (p.getPDG() != -211 || p.getGeneratorStatus() == 0) continue; + vPions.push_back(p); + TLorentzVector v(p.getMomentum().x, p.getMomentum().y, p.getMomentum().z, p.getEnergy()); + vLorentzPions.push_back(v); + hEtaPt->Fill(v.Eta(), v.Pt()); + if(inNHCal(v.Eta())) {hP_all_pion->Fill(v.P());} + } + + vector matchedRecos; + + auto find_associated_reco = [&](const edm4hep::MCParticle& mc)->void { + try { + if (!assocCol.isValid() || assocCol.empty()) return; + auto simIDs = assocCol.simID(); + auto recIDs = assocCol.recID(); + const uint32_t mc_idx = mc.getObjectID().index; + for (size_t i=0; i= recParts.size()) continue; + auto reco = recParts.at(ridx); + if (reco.isAvailable()) { + matchedRecos.push_back(reco); + } + } + } + } catch (...) {} + }; + + if (!assocCol.isValid() || assocCol.empty()) continue; + for(const auto& p: vPions) if (p.getPDG() == -211) find_associated_reco(p); + + if (!recParts.isValid() || !projSegs.isValid() || !hcalRec.isValid() || recParts.empty() || projSegs.empty() || hcalRec.empty()) continue; + + set uniqueCentersZ10; + map layerData; + for (const auto& hit : hcalRec) { + double z = hit.getPosition().z; + double zBin = round(z); + uint64_t cid = hit.getCellID(); + double zCenter_mm = 10 * getPlasticCenterZ_cm(decoder, cellid_converter, cid, slice_index, /*plastic_slice_value=*/3); + if (!std::isnan(zCenter_mm)) { + int z10 = lround(zCenter_mm * 10.0); + uniqueCentersZ10.insert(z10); + } + layerData[zBin] += hit.getEnergy(); + hZ_hits->Fill(z); + hE_z->Fill(z, hit.getEnergy()); + hE->Fill(hit.getEnergy()); + } + + vector layerCentersZ; + layerCentersZ.reserve(uniqueCentersZ10.size()); + for (int z10 : uniqueCentersZ10) layerCentersZ.push_back(z10 / 10.0); + if(layerCentersZ.size() > LAYER_MAX) LAYER_MAX = layerCentersZ.size(); + + for(size_t n = 0; n < SIZE; n++) LAYER_CUTS[n] = static_cast(LAYER_MAX*LAYER_PROC[n]); + LAYER_THRESH = LAYER_CUTS[0]; + + for (const auto& [zValue, sumEnergy] : layerData) {hEsum_z->Fill(zValue, sumEnergy); hEsum->Fill(sumEnergy);} + + vector allTracks; + for (const auto& R : matchedRecos) { + for (const auto& tr : R.getTracks()) { + if (tr.isAvailable()) allTracks.push_back(tr); + } + } + vector segsTagged; + for (const auto& seg : projSegs) { + auto linkedTr = seg.getTrack(); + if (!linkedTr.isAvailable()) continue; + for (const auto& TT : allTracks) { + if (linkedTr.getObjectID() == TT.getObjectID()) { + segsTagged.push_back(seg); + break; + } + } + } + + for (size_t s = 0; s < segsTagged.size(); ++s) { + + vector segMinDistance(LAYER_MAX, std::numeric_limits::infinity()); + vector segHitEnergy(LAYER_MAX, std::numeric_limits::quiet_NaN()); + vector thickness_cm(LAYER_MAX, std::numeric_limits::quiet_NaN()); + vector count_DrCuts(SIZE, 0); + vector count_ECuts(SIZE, 0); + + GeomState A{}, B{}; + bool haveA = false, haveB = false; + + auto points = segsTagged[s].getPoints(); + + for (const auto& pt : points) { + if (pt.system != 113) continue; + hZ_proj->Fill(pt.position.z); + if (!haveA) { + A.x = pt.position.x; A.y = pt.position.y; A.z = pt.position.z; + haveA = true; + } else if (!haveB) { + B.x = pt.position.x; B.y = pt.position.y; B.z = pt.position.z; + haveB = true; + break; + } + } + + if (!haveA || !haveB) {continue;} + + for (size_t i = 0; i < LAYER_MAX; ++i) { + double best_dr_in_layer = 1e10; + double best_E_in_layer; + double partLayerEnergySum = 0; + double ratio_HitPartLayerEnergy = 0; + dd4hep::DDSegmentation::CellID best_cid_in_layer; + + double zc = layerCentersZ[i]; + auto [X, Y] = trackXYatZ(A, B, zc); + + for (const auto& hit : hcalRec) { + const auto& hp = hit.getPosition(); + + if (fabs(hp.z - zc) > 5.0 /*mm*/) continue; + + const double dx = X - hp.x; + const double dy = Y - hp.y; + const double dr = sqrt(dx*dx + dy*dy); + if(dr < 30*10 /*mm*/) partLayerEnergySum += hit.getEnergy(); + + hDxDyZ_layer->Fill(dx, dy, hp.z); + hDxDy_all->Fill(dx, dy); + hDrZ_layer->Fill(dr, hp.z); + hDr_all->Fill(dr); + + if (dr < best_dr_in_layer) { + best_dr_in_layer = dr; + best_E_in_layer = hit.getEnergy(); + best_cid_in_layer = hit.getCellID(); + } + } + + if (best_dr_in_layer < DR_THRESH_MM) { + segMinDistance[i] = best_dr_in_layer; + segHitEnergy[i] = best_E_in_layer; + thickness_cm[i] = getPlasticDimensionsCM(*det, decoder, best_cid_in_layer, slice_index, 3).z(); + t_cm = thickness_cm[0]; + } + ratio_HitPartLayerEnergy += segHitEnergy[i]/partLayerEnergySum; + if (std::isnan(ratio_HitPartLayerEnergy) || fabs(ratio_HitPartLayerEnergy - 1) >= 0.2) continue; + for(size_t j = 0; j < SIZE; ++j){ + if (segMinDistance[i] < DR_CUTS_CM[j] * 10 /*mm*/){++count_DrCuts[j];} + if (segHitEnergy[i] < (E_CUTS[j] * MIP_ENERGY_GEV * thickness_cm[i] * 100)){++count_ECuts[j];} + } + } + for (int j = 0; j < SIZE; ++j){ + if (count_DrCuts[j] > LAYER_THRESH) hP_pass_dr[j]->Fill(vLorentzPions[s].P()); + if (count_ECuts[j] > LAYER_THRESH) hP_pass_ECut[j]->Fill(vLorentzPions[s].P()); + if (count_DrCuts[SIZE-1] > LAYER_CUTS[j]) hP_pass_LayerCut[j]->Fill(vLorentzPions[s].P()); + } + } + } + + TCanvas* c = new TCanvas("c", "Pion analysis", 1600, 1000); + c->Divide(2,2); + + c->cd(1); hEtaPt->Draw("COLZ"); + + c->SaveAs(outname_pdf.c_str()); + c->SaveAs(outname_png.c_str()); + + TCanvas* c_hitTrack = new TCanvas("c_hitTrack", "Pion hit and track analysis", 1600, 1000); + c_hitTrack->Divide(2,2); + c_hitTrack->cd(1); gPad->SetGrid(); hZ_proj->Draw(); + c_hitTrack->cd(2); gPad->SetGrid(); hZ_hits->Draw(); + c_hitTrack->SaveAs(addPrefixAfterSlash(outname_png, "hitTrack_").c_str()); + c_hitTrack->SaveAs(addPrefixAfterSlash(outname_pdf, "hitTrack_").c_str()); + + TCanvas* c_hitTrackDistance = new TCanvas("c_hitTrackDistance", "Pion hit-track distance analysis", 1600, 1000); + c_hitTrackDistance->Divide(2,2); + c_hitTrackDistance->cd(1); gPad->SetGrid(); hDxDyZ_layer->Draw("COLZ"); + c_hitTrackDistance->cd(2); gPad->SetGrid(); hDxDy_all->Draw("COLZ"); + c_hitTrackDistance->cd(3); gPad->SetGrid(); hDrZ_layer->Draw("COLZ"); + c_hitTrackDistance->cd(4); gPad->SetGrid(); hDr_all->Draw("COLZ"); + c_hitTrackDistance->SaveAs(addPrefixAfterSlash(outname_png, "hitTrackDistance_").c_str()); + c_hitTrackDistance->SaveAs(addPrefixAfterSlash(outname_pdf, "hitTrackDistance_").c_str()); + + TCanvas* c_Eff = new TCanvas("c_Eff", "Pion efficiency analysis", 1600, 1000); + c_Eff->Divide(2,2); + c_Eff->cd(1); gPad->SetGrid(); hP_all_pion->SetLineColor(kBlack); hP_all_pion->Draw(); + c_Eff->cd(2); gPad->SetGrid(); + TH1D* hEff_dr[3]; + for (int idr=0; idr<3; ++idr) { + hEff_dr[idr] = (TH1D*)hP_pass_dr[idr]->Clone( + Form("hEff_dr%.1fcm", DR_CUTS_CM[idr]) + ); + hEff_dr[idr]->SetDirectory(nullptr); + hEff_dr[idr]->SetTitle(Form("Efficiency vs p_{MC} (Layers > %d); p_{MC} [GeV]; efficiency", LAYER_THRESH)); + hEff_dr[idr]->Divide(hP_pass_dr[idr], hP_all_pion, 1, 1, "B"); + } + + hEff_dr[0]->SetLineColor(kBlue); hEff_dr[0]->SetMinimum(0.0); hEff_dr[0]->SetMaximum(1.4); + hEff_dr[1]->SetLineColor(kRed); + hEff_dr[2]->SetLineColor(kGreen+2); + + hEff_dr[0]->Draw("HIST E"); + hEff_dr[1]->Draw("HIST E SAME"); + hEff_dr[2]->Draw("HIST E SAME"); + { auto leg_dr = new TLegend(0.60,0.70,0.88,0.88); + for(int idr=0; idr<3; ++idr) + {leg_dr->AddEntry(hEff_dr[idr],Form("dr < %.1f cm", DR_CUTS_CM[idr]),"l");} + leg_dr->Draw(); + } + + c_Eff->cd(3); gPad->SetGrid(); + TH1D* hEff_E[3]; + for (int ie=0; ie<3; ++ie) { + hEff_E[ie] = (TH1D*)hP_pass_ECut[ie]->Clone( + Form("hEff_E%.1fcm", E_CUTS[ie]) + ); + hEff_E[ie]->SetDirectory(nullptr); + hEff_E[ie]->SetTitle(Form("Efficiency vs p_{MC} (Layers > %d, dr < %.1f cm); p_{MC} [GeV]; efficiency",LAYER_THRESH, DR_THRESH_MM/10)); + hEff_E[ie]->Divide(hP_pass_ECut[ie], hP_all_pion, 1, 1, "B"); + } + + hEff_E[0]->SetLineColor(kBlue); hEff_E[0]->SetMinimum(0.0); hEff_E[0]->SetMaximum(1.4); + hEff_E[1]->SetLineColor(kRed); + hEff_E[2]->SetLineColor(kGreen+2); + + hEff_E[0]->Draw("HIST E"); + hEff_E[1]->Draw("HIST E SAME"); + hEff_E[2]->Draw("HIST E SAME"); + { auto leg_E = new TLegend(0.60,0.70,0.88,0.88); + for(int ie=0; ie<3; ++ie) + {leg_E->AddEntry(hEff_E[ie],Form("E < %.1f MIP", E_CUTS[ie]),"l");} + leg_E->Draw(); + } + + c_Eff->cd(4); gPad->SetGrid(); + TH1D* hEff_Layer[3]; + for (int il=0; il<3; ++il) { + hEff_Layer[il] = (TH1D*)hP_pass_LayerCut[il]->Clone( + Form("hEff_Layer%d", LAYER_CUTS[il]) + ); + hEff_Layer[il]->SetDirectory(nullptr); + hEff_Layer[il]->SetTitle(Form("Efficiency vs p_{MC} (dr < %.1f cm); p_{MC} [GeV]; efficiency", DR_THRESH_MM/10)); + hEff_Layer[il]->Divide(hP_pass_LayerCut[il], hP_all_pion, 1, 1, "B"); + } + + hEff_Layer[0]->SetLineColor(kBlue); hEff_Layer[0]->SetMinimum(0.0); hEff_Layer[0]->SetMaximum(1.4); + hEff_Layer[1]->SetLineColor(kRed); + hEff_Layer[2]->SetLineColor(kGreen+2); + + hEff_Layer[0]->Draw("HIST E"); + hEff_Layer[1]->Draw("HIST E SAME"); + hEff_Layer[2]->Draw("HIST E SAME"); + { auto leg_Layer = new TLegend(0.60,0.70,0.88,0.88); + for(int il=0; il<3; ++il) + {leg_Layer->AddEntry(hEff_Layer[il],Form("L > %d Layers", LAYER_CUTS[il]),"l");} + leg_Layer->Draw(); + } + c_Eff->SaveAs(addPrefixAfterSlash(outname_png, "matching_efficiency_").c_str()); + c_Eff->SaveAs(addPrefixAfterSlash(outname_pdf, "matching_efficiency_").c_str()); + + TCanvas* c_hEnergy = new TCanvas("c_hEnergy", "Pion hit energy analysis", 1600, 1000); + c_hEnergy->Divide(2,2); + + c_hEnergy->cd(1); gPad->SetGrid(); gPad->SetLogy(); hE_z->GetYaxis()->SetMoreLogLabels(); hE_z->Draw("COLZ"); + TProfile* pE_z = hE_z->ProfileX("pE_z"); pE_z->SetLineWidth(3); pE_z->SetLineColor(kRed); pE_z->SetMarkerColor(kRed); + pE_z->SetDirectory(nullptr); pE_z->Draw("SAME"); + + const double Ecut_GeV = MIP_ENERGY_GEV * t_cm * 100; + TLine* cut = new TLine(hE_z->GetXaxis()->GetXmin(), Ecut_GeV, hE_z->GetXaxis()->GetXmax(), Ecut_GeV); + cut->SetLineColor(kRed+1); cut->SetLineStyle(2); cut->SetLineWidth(2); + cut->Draw("SAME"); + auto* leg = new TLegend(0.58, 0.75, 0.84, 0.88); leg->SetTextSize(0.04);leg->AddEntry(cut, Form("E_{cut} = %.3f GeV", Ecut_GeV), "l"); + leg->Draw(); + + c_hEnergy->cd(2); gPad->SetGrid(); gPad->SetLogy(); hEsum_z->GetYaxis()->SetMoreLogLabels(); hEsum_z->Draw("COLZ"); + TProfile* pEsum_z = hEsum_z->ProfileX("pEsum_z"); pEsum_z->SetLineWidth(3); pEsum_z->SetLineColor(kRed); pEsum_z->SetMarkerColor(kRed); + pEsum_z->SetDirectory(nullptr); pEsum_z->Draw("SAME"); + c_hEnergy->cd(3); gPad->SetGrid(); hE->Draw(); + c_hEnergy->cd(4); gPad->SetGrid(); hEsum->Draw(); + + c_hEnergy->SaveAs(addPrefixAfterSlash(outname_png, "hit_energy_").c_str()); + c_hEnergy->SaveAs(addPrefixAfterSlash(outname_pdf, "hit_energy_").c_str()); + + delete c; + delete c_hitTrack; + delete c_hitTrackDistance; + delete c_Eff; + delete c_hEnergy; + + return 0; +} \ No newline at end of file diff --git a/benchmarks/nhcal_sampling_fraction/Snakefile b/benchmarks/nhcal_sampling_fraction/Snakefile new file mode 100644 index 00000000..ce5bffac --- /dev/null +++ b/benchmarks/nhcal_sampling_fraction/Snakefile @@ -0,0 +1,58 @@ +rule nhcal_sampling_fraction_simulate: + output: + "sim_output/nhcal_sampling_fraction/{PARTICLE}/E{ENERGY}GeV/sim.edm4hep.root", + params: + N_EVENTS=1000, + DETECTOR_PATH=os.environ["DETECTOR_PATH"], + DETECTOR_CONFIG="epic_backward_hcal_only_sampF.xml", + DD4HEP_HASH=get_spack_package_hash("dd4hep"), + NPSIM_HASH=get_spack_package_hash("npsim"), + # cache: True + shell: + """ +set -m +exec npsim \ + --compactFile {params.DETECTOR_PATH}/{params.DETECTOR_CONFIG} \ + --numberOfEvents {params.N_EVENTS} \ + --random.seed $RANDOM \ + --enableGun \ + -v WARNING \ + --gun.particle {wildcards.PARTICLE} \ + --gun.thetaMin 120*degree \ + --gun.thetaMax 180*degree \ + --gun.distribution uniform \ + --gun.energy "{wildcards.ENERGY}*GeV" \ + --outputFile {output} +""" + + +rule nhcal_sampling_fraction_combine: + input: + lambda wildcards: expand( + "sim_output/nhcal_sampling_fraction/{PARTICLE}/E{ENERGY}GeV/sim.edm4hep.root", + ENERGY=["0.5", "0.7", "1", "2", "5", "10"], + PARTICLE=["pi-", "neutron", "e-"], + ), + wildcard_constraints: + ENERGY=r"\d+" + output: + f"sim_output/nhcal_sampling_fraction/sim_combined.edm4hep.root", + shell: + """ +hadd -f {output} {input} +""" + +rule nhcal_sampling_fraction_analysis: + input: + combined="sim_output/nhcal_sampling_fraction/sim_combined.edm4hep.root", + script="benchmarks/nhcal_sampling_fraction/scripts/sampling_fraction_analysis.cxx", + output: + pdf="results/nhcal_sampling_fraction/analysis.pdf", + png="results/nhcal_sampling_fraction/analysis.png", + params: + DETECTOR_PATH=os.environ["DETECTOR_PATH"], + DETECTOR_CONFIG="epic_backward_hcal_only_sampF.xml" + shell: + """ + root -l -b -q '{input.script}("{input.combined}","{output.pdf}","{output.png}","{params.DETECTOR_PATH}/{params.DETECTOR_CONFIG}")' +""" diff --git a/benchmarks/nhcal_sampling_fraction/config.yml b/benchmarks/nhcal_sampling_fraction/config.yml new file mode 100644 index 00000000..d1c23501 --- /dev/null +++ b/benchmarks/nhcal_sampling_fraction/config.yml @@ -0,0 +1,29 @@ +sim:nhcal_sampling_fraction: + extends: .det_benchmark + stage: simulate + parallel: + matrix: + - ENERGY: ["0.5GeV", "0.7GeV","1GeV", "2GeV", "5GeV", "10GeV"] + PARTICLE: ["pi-", "neutron", "e-"] + script: + - snakemake --cores 5 sim_output/nhcal_sampling_fraction/${PARTICLE}/E${ENERGY}/sim.edm4hep.root + +bench:nhcal_sampling_fraction: + extends: .det_benchmark + stage: benchmarks + needs: + - "sim:nhcal_sampling_fraction" + script: + - snakemake --cores 1 results/nhcal_sampling_fraction/analysis.pdf + +collect_results:nhcal_sampling_fraction: + extends: .det_benchmark + stage: collect + needs: + - "bench:nhcal_sampling_fraction" + script: + - ls -lrht + - mv results{,_save}/ + - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output results/nhcal_sampling_fraction/analysis.pdf + - mv results{_save,}/ + \ No newline at end of file diff --git a/benchmarks/nhcal_sampling_fraction/scripts/sampling_fraction_analysis.cxx b/benchmarks/nhcal_sampling_fraction/scripts/sampling_fraction_analysis.cxx new file mode 100644 index 00000000..58ee9cc4 --- /dev/null +++ b/benchmarks/nhcal_sampling_fraction/scripts/sampling_fraction_analysis.cxx @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include + +#include "ROOT/RDataFrame.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TROOT.h" +#include "TRandom.h" +#include "TH3.h" + + +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" + +#include +#include +#include "podio/ROOTReader.h" +//#include +#include "podio/CollectionIDTable.h" +#include "podio/ObjectID.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/MCParticleCollectionData.h" +#include "edm4hep/MCParticle.h" +#include "edm4hep/MCParticleData.h" + +#include "edm4hep/SimCalorimeterHitCollectionData.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/SimCalorimeterHitData.h" +#include "edm4hep/SimCalorimeterHit.h" + +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitCollectionData.h" +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/CalorimeterHitData.h" +#include "edm4hep/CalorimeterHit.h" +#include "edm4hep/CalorimeterHitObj.h" + +#include "edm4eic/ClusterCollection.h" +#include "edm4eic/Cluster.h" +#include "edm4eic/ClusterData.h" + +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitCollectionData.h" +#include "edm4eic/CalorimeterHitCollection.h" +#include "edm4eic/CalorimeterHitData.h" +#include "edm4eic/CalorimeterHit.h" +#include "edm4eic/CalorimeterHitObj.h" + +#include +#include + +using namespace std; +using namespace ROOT; +using namespace TMath; +using namespace edm4hep; + +dd4hep::Detector* det = NULL; + +inline string addPrefixAfterSlash(const string& path, + const string& prefix) { + const auto slash = path.find_last_of('/'); + if (slash == string::npos) + return prefix + path; + return path.substr(0, slash + 1) + prefix + path.substr(slash + 1); +} + +int sampling_fraction_analysis(const string &filename, string outname_pdf, string outname_png, TString compact_file) +{ + + podio::ROOTReader *reader = new podio::ROOTReader(); + reader->openFile(filename); + unsigned nEvents = reader->getEntries("events"); + cout << "Number of events: " << nEvents << endl; + + det = &(dd4hep::Detector::getInstance()); + det->fromCompact(compact_file.Data()); + det->volumeManager(); + det->apply("DD4hepVolumeManager", 0, 0); + + dd4hep::rec::CellIDPositionConverter cellid_converter(*det); + auto idSpec = det->readout("HcalEndcapNHits").idSpec(); + auto decoder = idSpec.decoder(); + const int slice_index = decoder->index("slice"); + if (slice_index < 0) { + cerr << "ERROR: 'slice' field not found in cell ID spec!" << endl; + return 1; + } + + constexpr int NBINS = 50; + + constexpr double E_MIN_GEV = 0.0; + constexpr double E_MAX_GEV = 12.0; + constexpr double SAMP_F_MIN = 0.0; + constexpr double SAMP_F_MAX = 1.0; + constexpr double SAMP_F_LOW = 0.2; + + TH2D *h_sampF_e = new TH2D("h_sampF_e", "nHCal sampling fraction vs. energy (e-); E [GeV]; sampling fraction; counts", + NBINS, E_MIN_GEV, E_MAX_GEV, NBINS, SAMP_F_MIN, SAMP_F_MAX); + + TH2D *h_sampF_n = new TH2D("h_sampF_n", "nHCal sampling fraction vs. energy (neutron); E [GeV]; sampling fraction; counts", + NBINS, E_MIN_GEV, E_MAX_GEV, NBINS, SAMP_F_MIN, SAMP_F_MAX); + + TH2D *h_sampF_pi = new TH2D("h_sampF_pi", "nHCal sampling fraction vs. energy (#pi-); E [GeV]; sampling fraction; counts", + NBINS, E_MIN_GEV, E_MAX_GEV, NBINS, SAMP_F_MIN, SAMP_F_MAX); + + TH2D *h_sampF_e_Ekin = new TH2D("h_sampF_e_Ekin", "nHCal sampling fraction vs. energy kin (e-); E [GeV]; sampling fraction; counts", + NBINS, E_MIN_GEV, E_MAX_GEV, NBINS, SAMP_F_MIN, SAMP_F_LOW); + + TH2D *h_sampF_n_Ekin = new TH2D("h_sampF_n_Ekin", "nHCal sampling fraction vs. energy kin (neutron); E [GeV]; sampling fraction; counts", + NBINS, E_MIN_GEV, E_MAX_GEV, NBINS, SAMP_F_MIN, SAMP_F_LOW); + + TH2D *h_sampF_pi_Ekin = new TH2D("h_sampF_pi_Ekin", "nHCal sampling fraction vs. energy kin (#pi-); E [GeV]; sampling fraction; counts", + NBINS, E_MIN_GEV, E_MAX_GEV, NBINS, SAMP_F_MIN, SAMP_F_LOW); + + for (unsigned ev = 0; ev < nEvents; ev++) + { + double hit_Esum = 0; + double hit_scint_Esum = 0; + double singlePart_Ekin = 0; + + auto frameData = reader->readNextEntry(podio::Category::Event); + if (!frameData) + { + cerr << "Invalid FrameData at event " << ev << endl; + continue; + } + + podio::Frame frame(std::move(frameData)); + + auto& MCParticles_coll = frame.get("MCParticles"); + auto& SimCalorimeterHit_coll = frame.get("HcalEndcapNHits"); + + if (!SimCalorimeterHit_coll.isValid()) + { + cerr << "HcalEndcapNHits collection is not valid!" << endl; + } + if (!MCParticles_coll.isValid()) + { + cerr << "MCParticles collection is not valid!" << endl; + } + + edm4hep::MCParticle mcpart = MCParticles_coll.at(0); + singlePart_Ekin = mcpart.getEnergy(); //-mcpart.getMass(); + int pdg = mcpart.getPDG(); + + for (const auto& hit : SimCalorimeterHit_coll) + { + const int hit_slice = decoder->get(hit.getCellID(), slice_index); + hit_Esum += hit.getEnergy(); + if(hit_slice == 3) hit_scint_Esum += hit.getEnergy(); + } + + if (pdg == 11) // e- + { + h_sampF_e->Fill(singlePart_Ekin, hit_scint_Esum/hit_Esum); + h_sampF_e_Ekin->Fill(singlePart_Ekin, hit_scint_Esum/singlePart_Ekin); + } + else if (pdg == -211) // pi- + { + h_sampF_pi->Fill(singlePart_Ekin, hit_scint_Esum/hit_Esum); + h_sampF_pi_Ekin->Fill(singlePart_Ekin, hit_scint_Esum/singlePart_Ekin); + } + else if (pdg == 2112) // neutron + { + h_sampF_n->Fill(singlePart_Ekin, hit_scint_Esum/hit_Esum); + h_sampF_n_Ekin->Fill(singlePart_Ekin, hit_scint_Esum/singlePart_Ekin); + } + } + + delete reader; + + h_sampF_e->Sumw2(); + h_sampF_e_Ekin->Sumw2(); + h_sampF_pi->Sumw2(); + h_sampF_pi_Ekin->Sumw2(); + h_sampF_n->Sumw2(); + h_sampF_n_Ekin->Sumw2(); + + TProfile* p_sampF_e = h_sampF_e->ProfileX(); p_sampF_e ->SetTitle("nHCal sampling fraction vs. energy (e-); E [GeV]; sampling fraction"); + TProfile* p_sampF_e_Ekin = h_sampF_e_Ekin->ProfileX(); p_sampF_e_Ekin ->SetTitle("nHCal sampling fraction vs. energy kin (e-); E [GeV]; sampling fraction"); + TProfile* p_sampF_pi = h_sampF_pi->ProfileX(); p_sampF_pi ->SetTitle("nHCal sampling fraction vs. energy (#pi-); E [GeV]; sampling fraction"); + TProfile* p_sampF_pi_Ekin = h_sampF_pi_Ekin->ProfileX(); p_sampF_pi_Ekin ->SetTitle("nHCal sampling fraction vs. energy kin (#pi-); E [GeV]; sampling fraction"); + TProfile* p_sampF_n = h_sampF_n->ProfileX(); p_sampF_n ->SetTitle("nHCal sampling fraction vs. energy (neutron); E [GeV]; sampling fraction"); + TProfile* p_sampF_n_Ekin = h_sampF_n_Ekin->ProfileX(); p_sampF_n_Ekin ->SetTitle("nHCal sampling fraction vs. energy kin (neutron); E [GeV]; sampling fraction"); + + TH1D *h_e = p_sampF_e->ProjectionX("h_e"); + TH1D *h_pi = p_sampF_pi->ProjectionX("h_pi"); + + TH1D *h_e_over_pi = (TH1D*)h_e->Clone("h_e_over_pi"); + h_e_over_pi->SetTitle("e/h ratio;E [GeV];e/h"); + h_e_over_pi->Divide(h_e, h_pi, 1, 1); + + + TCanvas *c_h = new TCanvas("canvas_h", "c_h", 1600, 800); + c_h->Divide(2,2); + c_h->cd(1); + h_sampF_e->Draw("COLZ"); + + c_h->cd(2); + h_sampF_pi->Draw("COLZ"); + + c_h->cd(3); + h_sampF_n->Draw("COLZ"); + + c_h->SaveAs(outname_pdf.c_str()); + c_h->SaveAs(outname_png.c_str()); + + TCanvas *c_p = new TCanvas("canvas_p", "c_p", 1600, 800); + c_p->Divide(2,2); + c_p->cd(1); + p_sampF_e->SetLineWidth(3); p_sampF_e->SetLineColor(kRed); p_sampF_e->SetMarkerColor(kRed); + p_sampF_e->Draw(); + c_p->cd(2); + p_sampF_pi->SetLineWidth(3); p_sampF_pi->SetLineColor(kRed); p_sampF_pi->SetMarkerColor(kRed); + p_sampF_pi->Draw(); + c_p->cd(3); + p_sampF_n->SetLineWidth(3); p_sampF_n->SetLineColor(kRed); p_sampF_n->SetMarkerColor(kRed); + p_sampF_n->Draw(); + c_p->cd(4); + h_e_over_pi->Draw(); + + c_p->SaveAs(addPrefixAfterSlash(outname_png, "profile_Ehit_").c_str()); + c_p->SaveAs(addPrefixAfterSlash(outname_pdf, "profile_Ehit_").c_str()); + + TH1D *h_e_Ekin = p_sampF_e_Ekin->ProjectionX("h_e_Ekin"); + TH1D *h_pi_Ekin = p_sampF_pi_Ekin->ProjectionX("h_pi_Ekin"); + + TH1D *h_e_over_pi_Ekin = (TH1D*)h_e_Ekin->Clone("h_e_over_pi_Ekin"); + h_e_over_pi_Ekin->SetTitle("e/h ratio;E [GeV];e/h"); + h_e_over_pi_Ekin->Divide(h_e_Ekin, h_pi_Ekin, 1, 1); + + TCanvas *c_h_Ekin = new TCanvas("canvas_h_Ekin", "c_h_Ekin", 1600, 800); + c_h_Ekin->Divide(2,2); + c_h_Ekin->cd(1); + h_sampF_e_Ekin->Draw("COLZ"); + + c_h_Ekin->cd(2); + h_sampF_pi_Ekin->Draw("COLZ"); + + c_h_Ekin->cd(3); + h_sampF_n_Ekin->Draw("COLZ"); + + c_h_Ekin->SaveAs(addPrefixAfterSlash(outname_png, "Ekin_").c_str()); + c_h_Ekin->SaveAs(addPrefixAfterSlash(outname_pdf, "Ekin_").c_str()); + + TCanvas *c_p_Ekin = new TCanvas("canvas_p_Ekin", "c_p_Ekin", 1600, 800); + c_p_Ekin->Divide(2,2); + c_p_Ekin->cd(1); + p_sampF_e_Ekin->SetLineWidth(3); p_sampF_e_Ekin->SetLineColor(kRed); p_sampF_e_Ekin->SetMarkerColor(kRed); + p_sampF_e_Ekin->Draw(); + c_p_Ekin->cd(2); + p_sampF_pi_Ekin->SetLineWidth(3); p_sampF_pi_Ekin->SetLineColor(kRed); p_sampF_pi_Ekin->SetMarkerColor(kRed); + p_sampF_pi_Ekin->Draw(); + c_p_Ekin->cd(3); + p_sampF_n_Ekin->SetLineWidth(3); p_sampF_n_Ekin->SetLineColor(kRed); p_sampF_n_Ekin->SetMarkerColor(kRed); + p_sampF_n_Ekin->Draw(); + c_p_Ekin->cd(4); + h_e_over_pi_Ekin->Draw(); + + c_p_Ekin->SaveAs(addPrefixAfterSlash(outname_png, "profile_Ekin_").c_str()); + c_p_Ekin->SaveAs(addPrefixAfterSlash(outname_pdf, "profile_Ekin_").c_str()); + + + return 0; +} \ No newline at end of file