Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 10:46:03

0001 #!/usr/bin/env python3
0002 """Compare lAger HepMC3 ASCII samples (e p -> e' J/psi p', J/psi -> e+ e- (gamma)).
0003 
0004 usage: compare.py OUTDIR LABEL=file.hepmc [LABEL=file.hepmc ...]
0005 The first sample is the reference.
0006 """
0007 import sys
0008 import numpy as np
0009 from scipy import stats
0010 import matplotlib
0011 matplotlib.use("Agg")
0012 import matplotlib.pyplot as plt
0013 from matplotlib.backends.backend_pdf import PdfPages
0014 
0015 
0016 def mdot(a, b):
0017     return a[..., 3] * b[..., 3] - np.sum(a[..., :3] * b[..., :3], axis=-1)
0018 
0019 
0020 def read_hepmc(path):
0021     """Return dict of per-event 4-vectors (px,py,pz,E) and the raw particle rows."""
0022     out = {k: [] for k in ("k", "kp", "P", "Pp", "jpsi", "lm", "lp", "ngam", "egam")}
0023     raw = []
0024 
0025     def flush(ev):
0026         if ev is None:
0027             return
0028         parts = ev
0029         beam_e = next(p for p in parts.values() if p["st"] == 4 and p["pid"] == 11)
0030         beam_p = next(p for p in parts.values() if p["st"] == 4 and p["pid"] == 2212)
0031         jpsi = next(p for p in parts.values() if p["pid"] == 443)
0032         scat = next(p for p in parts.values() if p["pid"] == 11 and p["st"] == 1 and p["par"] == beam_e["id"])
0033         rec = next(p for p in parts.values() if p["pid"] == 2212 and p["st"] == 1)
0034         lm = next(p for p in parts.values() if p["pid"] == 11 and p["par"] == jpsi["id"])
0035         lp = next(p for p in parts.values() if p["pid"] == -11 and p["par"] == jpsi["id"])
0036         gams = [p for p in parts.values() if p["pid"] == 22 and p["par"] == jpsi["id"]]
0037         for key, p in (("k", beam_e), ("kp", scat), ("P", beam_p), ("Pp", rec), ("jpsi", jpsi), ("lm", lm), ("lp", lp)):
0038             out[key].append(p["v"])
0039         out["ngam"].append(len(gams))
0040         out["egam"].append(sum(g["v"][3] for g in gams))
0041         raw.append(np.concatenate([p["v"] for p in sorted(parts.values(), key=lambda p: p["id"])]))
0042 
0043     ev = None
0044     vtx_parent = {}
0045     with open(path) as f:
0046         for line in f:
0047             c = line[:2]
0048             if c == "E ":
0049                 flush(ev)
0050                 ev, vtx_parent = {}, {}
0051             elif c == "V ":
0052                 tok = line.split()
0053                 ins = tok[3].strip("[]").split(",")
0054                 vtx_parent[int(tok[1])] = int(ins[0])
0055             elif c == "P ":
0056                 tok = line.split()
0057                 pid_, par = int(tok[1]), int(tok[2])
0058                 if par < 0:  # produced at a vertex: use its first incoming particle
0059                     par = vtx_parent.get(par, par)
0060                 ev[pid_] = dict(id=pid_, par=par, pid=int(tok[3]), st=int(tok[9]),
0061                                 v=np.array([float(x) for x in tok[4:8]]))
0062         flush(ev)
0063     d = {k: np.array(v) for k, v in out.items()}
0064     return d, raw
0065 
0066 
0067 def observables(d):
0068     k, kp, P, Pp, V = d["k"], d["kp"], d["P"], d["Pp"], d["jpsi"]
0069     q = k - kp
0070     o = {}
0071     o["Q2 [GeV2]"] = -mdot(q, q)
0072     o["y"] = mdot(P, q) / mdot(P, k)
0073     o["W [GeV]"] = np.sqrt(mdot(P + q, P + q))
0074     dP = P - Pp
0075     o["-t [GeV2]"] = -mdot(dP, dP)
0076     o["x_B"] = o["Q2 [GeV2]"] / (2 * mdot(P, q))
0077 
0078     def pt(v): return np.hypot(v[:, 0], v[:, 1])
0079     def eta(v):
0080         p = np.linalg.norm(v[:, :3], axis=1)
0081         return 0.5 * np.log((p + v[:, 2]) / (p - v[:, 2]))
0082     def mom(v): return np.linalg.norm(v[:, :3], axis=1)
0083 
0084     o["J/psi pT [GeV]"] = pt(V)
0085     o["J/psi eta"] = eta(V)
0086     o["J/psi p [GeV]"] = mom(V)
0087     o["scat. e- eta"] = eta(kp)
0088     o["scat. e- p [GeV]"] = mom(kp)
0089     o["recoil p pT [GeV]"] = pt(Pp)
0090     o["recoil p eta"] = eta(Pp)
0091     o["decay e- eta"] = eta(d["lm"])
0092     o["decay e+ eta"] = eta(d["lp"])
0093     o["decay e- p [GeV]"] = mom(d["lm"])
0094     ll = d["lm"] + d["lp"]
0095     o["M(e+e-) [GeV]"] = np.sqrt(mdot(ll, ll))
0096     o["FSR photon E [GeV]"] = d["egam"]
0097     o["N FSR photons"] = d["ngam"].astype(float)
0098     # four-momentum conservation check (beam in = everything final)
0099     fin = kp + Pp + d["lm"] + d["lp"]
0100     o["|sum p_in - p_out| (no FSR) [GeV]"] = np.linalg.norm((k + P - fin)[:, :3], axis=1)
0101     return o
0102 
0103 
0104 LOGX = {"Q2 [GeV2]", "y", "-t [GeV2]", "x_B", "FSR photon E [GeV]"}
0105 
0106 
0107 def main():
0108     outdir = sys.argv[1]
0109     samples = [a.split("=", 1) for a in sys.argv[2:]]
0110     data = {}
0111     for label, path in samples:
0112         d, raw = read_hepmc(path)
0113         data[label] = (observables(d), raw)
0114         print(f"read {label}: {len(raw)} events from {path}")
0115 
0116     labels = [s[0] for s in samples]
0117     ref = labels[0]
0118     ref_obs = data[ref][0]
0119 
0120     # exact-match check of every other sample against the reference
0121     print("\n== event-by-event comparison vs", ref)
0122     for lab in labels[1:]:
0123         a, b = data[ref][1], data[lab][1]
0124         n = min(len(a), len(b))
0125         same_shape = sum(1 for i in range(n) if a[i].shape == b[i].shape)
0126         ident = sum(1 for i in range(n) if a[i].shape == b[i].shape and np.allclose(a[i], b[i], rtol=1e-9, atol=1e-12))
0127         first_diff = next((i for i in range(n) if not (a[i].shape == b[i].shape and np.allclose(a[i], b[i], rtol=1e-9, atol=1e-12))), None)
0128         print(f"  {lab}: {ident}/{n} events identical (rel 1e-9); same topology {same_shape}/{n}; first differing event: {first_diff}")
0129 
0130     print("\n== distribution comparison (KS two-sample p-value; means)")
0131     hdr = f"  {'observable':38s}" + "".join(f"{l:>22s}" for l in labels) + "".join(f"{'KS p '+l:>18s}" for l in labels[1:])
0132     print(hdr)
0133     rows = []
0134     for name in ref_obs:
0135         line = f"  {name:38s}"
0136         for lab in labels:
0137             x = data[lab][0][name]
0138             line += f"{np.mean(x):>12.5g} ±{np.std(x)/np.sqrt(len(x)):<9.2g}"
0139         ps = []
0140         for lab in labels[1:]:
0141             p = stats.ks_2samp(ref_obs[name], data[lab][0][name]).pvalue
0142             ps.append(p)
0143             line += f"{p:>18.3f}"
0144         rows.append((name, ps))
0145         print(line)
0146 
0147     with PdfPages(f"{outdir}/comparison.pdf") as pdf:
0148         names = list(ref_obs)
0149         for i0 in range(0, len(names), 6):
0150             fig, axes = plt.subplots(2, 3, figsize=(15, 8))
0151             for ax, name in zip(axes.flat, names[i0:i0 + 6]):
0152                 allx = np.concatenate([data[l][0][name] for l in labels])
0153                 lo, hi = np.percentile(allx, [0.1, 99.9])
0154                 if name in LOGX and lo > 0:
0155                     bins = np.logspace(np.log10(lo), np.log10(hi), 50); ax.set_xscale("log")
0156                 elif name == "N FSR photons":
0157                     bins = np.arange(-0.5, allx.max() + 1.5)
0158                 else:
0159                     bins = np.linspace(lo, hi, 50)
0160                 for lab in labels:
0161                     x = data[lab][0][name]
0162                     h, e = np.histogram(x, bins=bins)
0163                     w = 1.0 / len(x)
0164                     c = 0.5 * (e[1:] + e[:-1])
0165                     if lab == ref:
0166                         ax.hist(x, bins=bins, weights=np.full(len(x), w), histtype="stepfilled", alpha=0.3, label=lab)
0167                     else:
0168                         ax.errorbar(c, h * w, yerr=np.sqrt(h) * w, fmt=".", ms=3, label=lab)
0169                 ax.set_xlabel(name); ax.set_ylabel("fraction / bin")
0170                 if name in ("-t [GeV2]", "FSR photon E [GeV]", "M(e+e-) [GeV]", "|sum p_in - p_out| (no FSR) [GeV]"):
0171                     ax.set_yscale("log")
0172                 ax.legend(fontsize=7)
0173             fig.tight_layout(); pdf.savefig(fig); fig.savefig(f"{outdir}/comparison_p{i0 // 6 + 1}.png", dpi=90); plt.close(fig)
0174     print(f"\nplots: {outdir}/comparison.pdf")
0175 
0176 
0177 if __name__ == "__main__":
0178     main()