ufoamp 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. ufoamp-1.0.0/MANIFEST.in +6 -0
  2. ufoamp-1.0.0/PKG-INFO +268 -0
  3. ufoamp-1.0.0/README.md +252 -0
  4. ufoamp-1.0.0/doc/figs/study1_fig_1a.pdf +0 -0
  5. ufoamp-1.0.0/doc/figs/study1_fig_1b.pdf +0 -0
  6. ufoamp-1.0.0/doc/figs/study2_fig_2a.pdf +0 -0
  7. ufoamp-1.0.0/doc/figs/study2_fig_2b.pdf +0 -0
  8. ufoamp-1.0.0/doc/figs/study3_fig_3a.pdf +0 -0
  9. ufoamp-1.0.0/doc/figs/study3_fig_3b.pdf +0 -0
  10. ufoamp-1.0.0/doc/figs/study4_fig_4a.pdf +0 -0
  11. ufoamp-1.0.0/doc/figs/study4_fig_4c.pdf +0 -0
  12. ufoamp-1.0.0/doc/figs/study5_fig_5a.pdf +0 -0
  13. ufoamp-1.0.0/doc/figs/study5_fig_5b.pdf +0 -0
  14. ufoamp-1.0.0/doc/figs/study5_fig_5c.pdf +0 -0
  15. ufoamp-1.0.0/doc/figs/study6_fig_6b.pdf +0 -0
  16. ufoamp-1.0.0/doc/figs/study6_fig_6c.pdf +0 -0
  17. ufoamp-1.0.0/doc/ufoamp.pdf +0 -0
  18. ufoamp-1.0.0/doc/ufoamp.tex +330 -0
  19. ufoamp-1.0.0/examples/analysis_example.py +57 -0
  20. ufoamp-1.0.0/examples/helicity_amplitudes.py +23 -0
  21. ufoamp-1.0.0/examples/vbf_hh_scan.py +30 -0
  22. ufoamp-1.0.0/pyproject.toml +29 -0
  23. ufoamp-1.0.0/run_tests.sh +11 -0
  24. ufoamp-1.0.0/setup.cfg +4 -0
  25. ufoamp-1.0.0/tests/_paths.py +12 -0
  26. ufoamp-1.0.0/tests/test_ee_mumu.py +84 -0
  27. ufoamp-1.0.0/tests/test_gauge_invariance.py +38 -0
  28. ufoamp-1.0.0/tests/test_jax.py +26 -0
  29. ufoamp-1.0.0/tests/test_mg5ref.py +28 -0
  30. ufoamp-1.0.0/tests/test_orders_frames.py +32 -0
  31. ufoamp-1.0.0/tests/test_permutation.py +42 -0
  32. ufoamp-1.0.0/tests/test_qcd.py +49 -0
  33. ufoamp-1.0.0/tests/test_sign_color.py +75 -0
  34. ufoamp-1.0.0/tests/test_ufo_eeWW.py +80 -0
  35. ufoamp-1.0.0/tests/test_ufo_eemumu.py +60 -0
  36. ufoamp-1.0.0/tests/test_ufo_eemumu_Z.py +106 -0
  37. ufoamp-1.0.0/tests/test_ww_xsec.py +33 -0
  38. ufoamp-1.0.0/ufoamp/__init__.py +1 -0
  39. ufoamp-1.0.0/ufoamp/analysis.py +610 -0
  40. ufoamp-1.0.0/ufoamp/backend.py +23 -0
  41. ufoamp-1.0.0/ufoamp/color.py +107 -0
  42. ufoamp-1.0.0/ufoamp/couplings.py +111 -0
  43. ufoamp-1.0.0/ufoamp/dirac.py +100 -0
  44. ufoamp-1.0.0/ufoamp/jaxme.py +125 -0
  45. ufoamp-1.0.0/ufoamp/lorentz_eval.py +224 -0
  46. ufoamp-1.0.0/ufoamp/mg5ref.py +145 -0
  47. ufoamp-1.0.0/ufoamp/phasespace.py +40 -0
  48. ufoamp-1.0.0/ufoamp/process.py +181 -0
  49. ufoamp-1.0.0/ufoamp/propagators.py +41 -0
  50. ufoamp-1.0.0/ufoamp/py.typed +0 -0
  51. ufoamp-1.0.0/ufoamp/recursion.py +770 -0
  52. ufoamp-1.0.0/ufoamp/ufo_model.py +298 -0
  53. ufoamp-1.0.0/ufoamp/validate.py +150 -0
  54. ufoamp-1.0.0/ufoamp/vertex_eval.py +539 -0
  55. ufoamp-1.0.0/ufoamp/wavefunctions.py +166 -0
  56. ufoamp-1.0.0/ufoamp/wf_batched.py +46 -0
  57. ufoamp-1.0.0/ufoamp.egg-info/PKG-INFO +268 -0
  58. ufoamp-1.0.0/ufoamp.egg-info/SOURCES.txt +60 -0
  59. ufoamp-1.0.0/ufoamp.egg-info/dependency_links.txt +1 -0
  60. ufoamp-1.0.0/ufoamp.egg-info/entry_points.txt +2 -0
  61. ufoamp-1.0.0/ufoamp.egg-info/requires.txt +9 -0
  62. ufoamp-1.0.0/ufoamp.egg-info/top_level.txt +1 -0
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include run_tests.sh
3
+ recursive-include tests *.py
4
+ recursive-include examples *.py
5
+ recursive-include doc *.tex *.pdf
6
+ recursive-include doc/figs *.pdf
ufoamp-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,268 @@
1
+ Metadata-Version: 2.4
2
+ Name: ufoamp
3
+ Version: 1.0.0
4
+ Summary: Tree-level helicity amplitudes and squared matrix elements directly from a UFO model
5
+ Author: Spencer Ellis
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: numpy>=1.24
10
+ Provides-Extra: jax
11
+ Requires-Dist: jax>=0.4; extra == "jax"
12
+ Requires-Dist: jaxlib>=0.4; extra == "jax"
13
+ Provides-Extra: test
14
+ Requires-Dist: jax>=0.4; extra == "test"
15
+ Requires-Dist: jaxlib>=0.4; extra == "test"
16
+
17
+ # ufoamp v1
18
+
19
+
20
+ A standalone Python matrix-element generator that reads a UFO model directly
21
+ and evaluates tree-level helicity amplitudes and |M|² numerically, with
22
+ **coupling values chosen at run time** (no code generation, no recompilation).
23
+ Built for the VBF di-Higgs / Goldstone-equivalence study: switching
24
+ CV, C2V, C3 — or switching from the SM UFO to HHVBF — is a function call.
25
+
26
+ Validated for the Standard Model, the HHVBF UFO and SMEFTsim 3 (all nine
27
+ flavour/scheme variants load; U35 MW-scheme validated in detail; see *Validation*).
28
+
29
+ ## Quick start
30
+
31
+ ```bash
32
+ pip install ufoamp-1.0.0-py3-none-any.whl # or: pip install -e /path/to/ufoamp (source tree)
33
+ python3 examples/vbf_hh_scan.py /path/to/HHVBF_UFO
34
+ python3 examples/helicity_amplitudes.py /path/to/HHVBF_UFO
35
+
36
+ # validation ladder: tell it where the UFOs are, then every line must say PASS
37
+ export UFOAMP_MODELS=/path/to/models # containing SM_UFO/, HHVBF_UFO/, SMEFTsim/UFO_models/
38
+ # or individually: UFOAMP_SM_UFO, UFOAMP_HHVBF_UFO, UFOAMP_SMEFTSIM_UFO
39
+ ./run_tests.sh
40
+ ```
41
+ SMEFTsim: `git clone https://github.com/SMEFTsim/SMEFTsim` and point
42
+ `UFOAMP_SMEFTSIM_UFO` at `UFO_models/SMEFTsim_U35_MwScheme_UFO`.
43
+
44
+ ```python
45
+ from ufoamp.ufo_model import load
46
+ from ufoamp.process import Process
47
+
48
+ M = load("HHVBF_UFO")
49
+ proc = Process(M, initial=[2, 1], final=[2, 1, 25, 25], # u d -> u d H H
50
+ overrides={"CV": 1, "C2V": 2, "C3": 1, "MU": 0, "MD": 0})
51
+ m2 = proc.m2(momenta) # spin+colour summed, averaged
52
+ amp = proc.helicity_amplitude(momenta, (1,-1,1,-1,0,0)) # one helicity config
53
+ me.set_param({"C2V": 0, "C3": 5}) # runtime knob (MatrixElement API)
54
+ ```
55
+
56
+ `momenta` is a list of NumPy 4-vectors `[E, px, py, pz]` (metric +−−−), in the
57
+ order `initial + final`. `ufoamp.phasespace.rambo` generates flat phase space.
58
+
59
+ ### Coupling control
60
+ * `overrides={name: value}` — set any **external** UFO parameter (C3, CV, C2V,
61
+ masses, `cabi`, …). Internal parameters and all couplings are re-derived
62
+ consistently.
63
+ * `restrict_couplings={GC_name: value}` — force individual UFO couplings
64
+ (e.g. switch off a vertex class by setting its couplings to 0).
65
+ * `inject={name: value}` — pin derived parameters (ee, sw, vev, …) to compare
66
+ two models at one physical point.
67
+
68
+ ## Using it in an analysis (`ufoamp.analysis`)
69
+
70
+ ```python
71
+ from ufoamp.analysis import MatrixElement, read_lhe, lhe_to_process_momenta
72
+
73
+ me = MatrixElement("HHVBF_UFO", "u d > u d h h", # process string (UFO names or PDG codes)
74
+ couplings={"CV": 1, "C2V": 2, "C3": 1}) # any external UFO parameter
75
+ me.m2(momenta) # one event -> |M|^2 (spin/colour summed, initial averaged)
76
+ me.m2_events(events, n_jobs=8) # (N, n_particles, 4) -> (N,)
77
+ me.set_param({"C2V": 0}) # switch hypothesis, no rebuild -> reweighting
78
+ me.helicity_amplitudes(momenta) # {(h1,...,hn): M}
79
+
80
+ # polarisations of EXTERNAL particles (massive vectors: L = h0, T = h+-1)
81
+ w = MatrixElement("HHVBF_UFO", "w+ w- > h h", couplings={...})
82
+ w.m2(momenta, pol={"w+": "L"}) # W+ longitudinal, W- summed
83
+ w.m2_polarized(momenta, ["w+", "w-"]) # {'LL','LT','TL','TT'} (sums to the total exactly)
84
+
85
+ # MadGraph events
86
+ for pdgs, mom, status, hel in read_lhe("events.lhe.gz"):
87
+ ev = lhe_to_process_momenta(pdgs, mom, status, me.initial, me.final)
88
+ print(me.m2(ev))
89
+ ```
90
+ * **Restrict cards / param cards**: `MatrixElement(..., param_card="restrict_4FSZeroYukawa.dat")`
91
+ or `SampleEvaluator(..., param_card="param_card.dat")` reads any SLHA card
92
+ (the UFO's `restrict_*.dat` or the `param_card.dat` of a MadGraph run) and
93
+ applies its values — zeros switch vertices off exactly as MadGraph's
94
+ restriction does. Precedence: light-quark defaults < card < `couplings`.
95
+ `read_param_card(path, model)` returns the {parameter: value} dict.
96
+ * **`max_orders={"QCD": 0}`** reproduces MadGraph's `QCD=0`: vertex couplings whose
97
+ UFO coupling order exceeds the limit are dropped (model-agnostic; prefer it
98
+ to dropping "vertices with a gluon"). Without it, gluon exchange is included
99
+ and dominates identical-quark channels.
100
+ * **`pol_frame`** sets the frame in which *external* helicities are defined:
101
+ `"lab"` (default), `"partonic_cm"` (initial-state rest frame — MadGraph's
102
+ `me_frame` default for polarised samples), or a list of legs. The
103
+ helicity-summed |M|² is frame independent; polarised pieces are not, so match
104
+ the frame of whatever you compare to.
105
+ * `SampleEvaluator` handles mixed-flavour samples (`p p > ...`): one
106
+ `MatrixElement` per partonic channel, built and cached on first use.
107
+ * `massless_light_quarks=True` (default) zeroes the u,d,s,c masses **and** their
108
+ Yukawa parameters (`ymup`, `ymdo`, …), as MadGraph does; otherwise Higgs and
109
+ Goldstone emission off the quark lines survives at O(y_q).
110
+ * Events and helicity configurations are batched through one pass of the
111
+ recursion (a flat batch axis on every wavefunction, current, momentum and
112
+ propagator), every vertex contraction runs a pre-compiled pairwise einsum
113
+ plan, and exactly-vanishing (chirality) helicity configurations are pruned.
114
+ Cost per core: ~1.6 ms per VBF-HH event, ~20 ms per `u d > u d z z h`
115
+ event; 2→2 processes are ~0.1 ms. `m2_events(..., chunk=32, n_jobs=N)`;
116
+ for LHE samples use `SampleEvaluator.m2_sample(read_lhe(path), vpol="LL")`.
117
+ * **Internal (t-channel) boson polarisation — propagator decomposition.**
118
+ For the W/Z radiated off the quark lines the unitary-gauge numerator is split
119
+ exactly, −g^{μν} + k^μk^ν/M² = T + L + S, with ε_± and ε_L built from the
120
+ off-shell k in a chosen frame (default: rest frame of the non-fermion final
121
+ state, i.e. the HH system); for spacelike k the completeness relation is
122
+ −g + kk/k² = T − ε_Lε_L*. This is the "propagator decomposition" definition:
123
+ frame-dependent, and the components interfere in |M|².
124
+ ```python
125
+ me.set_vpol("LL") # both fusing bosons longitudinal (one letter per initial quark)
126
+ me.m2(momenta) # coherent |M_LL|^2
127
+ me.m2_vpol(momenta, codes=("LL","LT","TL","TT","A")) # A = full propagator
128
+ me.set_vpol("LT", frame="lab"); me.set_vpol(None) # frame choice / switch off
129
+ ```
130
+ Letters: L, T, +, −, S (k k remainder), P (=T+L), A (=everything). For
131
+ massless quark lines k·J = 0, so S ≡ 0 and the split is gauge-independent.
132
+ Only t-channel lines (initial↔final fermion) are decomposed; s-channel
133
+ u d → W* diagrams keep the full propagator.
134
+
135
+ ## What is inside
136
+
137
+ | module | role |
138
+ |---|---|
139
+ | `ufo_model.py` | self-contained UFO loader (modern and legacy UFOs) |
140
+ | `couplings.py` | evaluates parameters → couplings with runtime overrides |
141
+ | `dirac.py`, `wavefunctions.py`, `propagators.py` | frozen conventions: metric (+−−−), Dirac basis, helicity spinors, polarization vectors incl. longitudinal, Feynman-gauge propagators |
142
+ | `vertex_eval.py` | **general `einsum` evaluator**: any UFO Lorentz string (Metric, P, Gamma, ProjM/P, Identity, Gamma5, Epsilon) → off-shell current |
143
+ | `recursion.py` | **Berends–Giele recursion** driven by the UFO vertex table: no diagram enumeration; currents tagged by fermion-line connectivity → exchange signs and colour flow |
144
+ | `process.py` | user interface: helicity amplitudes, |M|² |
145
+ | `phasespace.py` | RAMBO (massive) |
146
+ | `analysis.py` | `MatrixElement`: process strings, runtime couplings, polarisation selection, event arrays, LHE reader |
147
+
148
+ ### Conventions established (and validated) from the UFO files
149
+ * UFO vertex leg labels are **outgoing** particle types; a physical leg plugs
150
+ into label X if outgoing, anti(X) if incoming.
151
+ * `P(mu,k)` is the outgoing momentum of leg k (`P_SIGN = −1` relative to the
152
+ all-incoming bookkeeping). Fixed by the e⁺e⁻→W⁺W⁻ gauge cancellation: the
153
+ other sign makes |M_LL|² grow like s².
154
+ * `Gamma(mu,a,b)` = (γ^μ)_{ab}; a is the ψ̄ (row) slot, b the ψ (column) slot.
155
+ * UFO couplings already contain the `i`; Feynman rule = coupling × colour × Lorentz.
156
+ * **Gauge is auto-detected** (`gauge="auto"`): Feynman gauge when the UFO
157
+ ships Goldstone bosons (SM_UFO, HHVBF), unitary gauge (propagator with the
158
+ k^μk^ν/M² term) when it does not (SMEFTsim and most MadGraph models). Can be
159
+ forced with `gauge="feynman"|"unitary"`. Feynman ≡ unitary is verified
160
+ numerically on W_LW_L→HH and VBF-HH.
161
+ * `drop_vertex=lambda vertex, pdgs: ...` removes whole vertices. Prefer it to
162
+ zeroing couplings by name: UFO couplings are shared across vertices (the SM
163
+ WWHH contact shares its coupling with WWGG).
164
+
165
+ ## Validation (all exact unless stated)
166
+ | test | checks | result |
167
+ |---|---|---|
168
+ | e⁺e⁻→μ⁺μ⁻, photon | engine + UFO plumbing vs e⁴(1+cos²θ) | 1e-9 |
169
+ | e⁺e⁻→μ⁺μ⁻, γ+Z | chiral slots, Z width, interference; A_FB sign change across the pole | 1e-7 |
170
+ | σ(e⁺e⁻→μ⁺μ⁻) | absolute normalisation vs 4πα²/3s | 1.00000000 |
171
+ | e⁺e⁻→W⁺W⁻ | longitudinal gauge cancellation (VVV, t-channel ν, γ/Z) | |M_LL|² → const |
172
+ | Bhabha | fermion-exchange sign via s/t interference | 1e-8 |
173
+ | e⁺e⁻→uū | colour sum N_c Q_u² | exact |
174
+ | **u d → u d H H** | **HHVBF(1,1,1) ≡ SM UFO, 6-point, colour flows, CKM** | **1.000000000** |
175
+ | u d → u d H H | amplitude is C2V·A + CV²·B + CV·C3·C + E (E = Goldstone t-channel) | 1e-9 |
176
+ | W_L W_L → HH vs G⁺G⁻ → HH | Goldstone equivalence theorem | ratio → 1.0004 at 8 TeV |
177
+ | SM Feynman vs SM unitary | gauge invariance: W_LW_L→HH (all helicities), VBF-HH | exact / 1e-8 |
178
+ | SMEFTsim e⁺e⁻→μ⁺μ⁻ | SM limit; 4-fermion contact vs photon normalisation (LL only); **Fierz identity cll↔cll1** (crossed chains + fermion sign); linearity | exact |
179
+ | **SMEFTsim(c=0) vs SM_UFO, VBF-HH** | two UFOs, two gauges, 6-point | **1e-9** |
180
+ | HH→HHHH via \|H\|⁶ | 6-leg vertex plumbing | exact |
181
+ | SMEFTsim cH, cHbox, cHDD, cHW on VBF-HH | Higgs-sector coefficients act; cH exactly linear | — |
182
+
183
+ σ(e⁺e⁻→W⁺W⁻) = 20.8 pb at 200 GeV is the tree-level α(M_Z)-scheme Born value;
184
+ the LEP2 number (~17 pb) includes ISR (~−11 %) and a scheme shift (~−7 %).
185
+
186
+ ## Physics finding you should know (HHVBF UFO)
187
+ HHVBF scales only the physical VVH (CV), VVHH (C2V), HHH (C3) vertices; every
188
+ Goldstone–Higgs vertex is left at its SM value. Consequences, all reproduced
189
+ numerically here:
190
+ * In Feynman gauge the VBF-HH amplitude is `C2V·A + CV²·B + CV·C3·C + E`
191
+ where `E` is the unscaled Goldstone t-channel exchange.
192
+ * The O(s) growth of W_L W_L → HH is ∝ **(C2V − 1)**, independent of CV, in
193
+ Feynman gauge (ufoamp, RECOLA). In unitary gauge (MadGraph default) it is
194
+ ∝ (C2V − CV²). The two agree only at CV = 1. Away from CV = 1 the model is
195
+ gauge-dependent, so MadGraph and RECOLA will disagree there; C2V and C3 scans
196
+ at CV = 1 are safe.
197
+
198
+ ## QCD colour
199
+ Every UFO colour structure (`T`, `f`, `d`, `Identity`, `Epsilon`, products with
200
+ contracted indices) is a numeric tensor contracted in the same compiled einsum
201
+ as the Lorentz structure; currents carry their open external colour indices as
202
+ tensor axes and the amplitude is a tensor over external colours
203
+ (`helicity_amplitude` returns it; |M|² is its norm). Validated to ~1e-15
204
+ against the analytic massless 2→2 results (qq'→qq', qq→qq, qq̄→q'q̄', qq̄→gg,
205
+ gg→qq̄, qg→qg, gg→gg) and against MadGraph. Cost scales as 8^(#gluons):
206
+ ≤4 gluons is fast (VBF+jet ~60 ms/event), gg→ggg is ~17 s/event. Colour
207
+ sextets are not supported.
208
+
209
+ ## Majorana fermions
210
+ Majorana particles (spin-1/2, self-conjugate) and Dirac fermions in
211
+ fermion-flow-violating ("clashing arrow") vertices, as FeynRules writes e.g.
212
+ chargino–lepton–sneutrino couplings, are handled by charge-conjugating the
213
+ spinor in place whenever it enters a slot of the other type
214
+ (ψ → −ψᵀC⁻¹, ψ̄ → Cψ̄ᵀ, C = iγ²γ⁰); fermion signs come from the chain slots.
215
+ Validated against MG5 on MSSM_SLHA2 at 1e-14–1e-16: e⁺e⁻→χ̃⁰₁χ̃⁰₁ (both flow
216
+ orientations of the selectron exchange), χ̃⁰₁χ̃⁰₂, χ̃⁰₃χ̃⁰₄ (negative SLHA
217
+ masses), χ̃⁺₁χ̃⁻₁, u d̄→χ̃⁰₁χ̃⁺₁, gluino pairs from qq̄ and gg, and 6-point
218
+ e⁺e⁻→χ̃⁰₁χ̃⁰₁ℓ⁺ℓ⁻. Propagator poles use |m| in the width term so negative mass
219
+ eigenvalues are handled correctly.
220
+
221
+ Not yet supported: spin-2, colour sextets, custom propagators/form factors.
222
+
223
+ ## Independent reference: MadGraph5 standalone (`ufoamp.mg5ref`)
224
+ ```python
225
+ from ufoamp.mg5ref import MG5Reference
226
+ ref = MG5Reference("/path/to/mg5amcnlo", "sm", "u d > u d z z h QCD=0") # generates + compiles C++ once
227
+ me = MatrixElement("/path/to/mg5amcnlo/models/sm", "u d > u d z z h",
228
+ param_card=ref.param_card, max_orders={"QCD": 0}, gauge="unitary")
229
+ ref.compare(me, events) # max |ratio-1| on identical points; symmetry factors handled
230
+ ```
231
+ Needs MG5 (Python) and g++ only. `tests/test_mg5ref.py` runs eight SM
232
+ processes (EW, QCD, top, Higgs, VBF, ZZHjj) at machine precision.
233
+
234
+ ### Conventions needed to match MadGraph exactly (all now defaults or options)
235
+ * `restrict="default"`: `<ufo>/restrict_default.dat` is applied implicitly, as
236
+ MG5 does on `import model` (e.g. MG's `sm` has a diagonal CKM; restricted
237
+ parameters are absent from the run's param_card).
238
+ * `gauge="unitary"` (drops Goldstone vertices automatically). MG5 standalone
239
+ keeps widths in t-channel propagators (`zerowidth_tchannel=False`, default);
240
+ MG5 *event generation* defaults to `zerowidth_tchannel=True` in the run card.
241
+ * `scheme="fixed_width"` (default, MG5) or `"cms"` (complex-mass scheme:
242
+ complex masses in couplings and the unitary numerator; RECOLA-like).
243
+ * **Processes with external unstable bosons (e.g. `z z h j j`) are gauge
244
+ dependent at O(Γ/M) (~0.3–0.7 %) with any width scheme**, because on-shell
245
+ external Z's sit on the real mass shell: this is why Feynman-gauge ufoamp and
246
+ unitary-gauge MG5 agree to "4 digits" only; use `gauge="unitary"` to
247
+ reproduce MG5, or compare with widths set to zero.
248
+
249
+ ## SMEFT notes (SMEFTsim 3)
250
+ * Loader is library-free (never imports the UFO's Python-2 `object_library`), so
251
+ legacy and SMEFTsim UFOs load directly. Wilson coefficients are ordinary
252
+ `overrides` (`cH`, `cHbox`, `cll`, …, `LambdaSMEFT`).
253
+ * n-point vertices (5- and 6-leg), 4-fermion operators (spinor chains read off
254
+ each Lorentz structure; Fierz-crossed structures tagged separately), the
255
+ `Sigma` atom, and atom powers (`P(-1,1)**2`) are supported; every Lorentz
256
+ structure in all nine SMEFTsim UFOs parses.
257
+ * Input-scheme subtleties reproduced: `cll1` shifts G_F (vev, dgw, …) and adds
258
+ the photon-vertex coupling `GC_344`; `lam` is `Gf·MH²/√2`; couplings use
259
+ `vevhat`. SMEFTsim ships the SM loop-induced Hγγ/HZγ/Hgg effective vertices
260
+ as tree vertices and a Wolfenstein CKM (`CKMlambda`).
261
+
262
+ ## Scope and next steps
263
+ * Done: SM, HHVBF, SMEFTsim; any tree process from n-point vertices incl.
264
+ 4-fermion; colour for quark lines (colour-flow matrix N_c^cycles); Feynman
265
+ and unitary gauges.
266
+ * Not yet: external gluons / T^a, f^abc colour algebra (QCD-induced processes),
267
+ Majorana fermions, spin-2; vectorisation over phase-space points for fast MC
268
+ integration.
ufoamp-1.0.0/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # ufoamp v1
2
+
3
+
4
+ A standalone Python matrix-element generator that reads a UFO model directly
5
+ and evaluates tree-level helicity amplitudes and |M|² numerically, with
6
+ **coupling values chosen at run time** (no code generation, no recompilation).
7
+ Built for the VBF di-Higgs / Goldstone-equivalence study: switching
8
+ CV, C2V, C3 — or switching from the SM UFO to HHVBF — is a function call.
9
+
10
+ Validated for the Standard Model, the HHVBF UFO and SMEFTsim 3 (all nine
11
+ flavour/scheme variants load; U35 MW-scheme validated in detail; see *Validation*).
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ pip install ufoamp-1.0.0-py3-none-any.whl # or: pip install -e /path/to/ufoamp (source tree)
17
+ python3 examples/vbf_hh_scan.py /path/to/HHVBF_UFO
18
+ python3 examples/helicity_amplitudes.py /path/to/HHVBF_UFO
19
+
20
+ # validation ladder: tell it where the UFOs are, then every line must say PASS
21
+ export UFOAMP_MODELS=/path/to/models # containing SM_UFO/, HHVBF_UFO/, SMEFTsim/UFO_models/
22
+ # or individually: UFOAMP_SM_UFO, UFOAMP_HHVBF_UFO, UFOAMP_SMEFTSIM_UFO
23
+ ./run_tests.sh
24
+ ```
25
+ SMEFTsim: `git clone https://github.com/SMEFTsim/SMEFTsim` and point
26
+ `UFOAMP_SMEFTSIM_UFO` at `UFO_models/SMEFTsim_U35_MwScheme_UFO`.
27
+
28
+ ```python
29
+ from ufoamp.ufo_model import load
30
+ from ufoamp.process import Process
31
+
32
+ M = load("HHVBF_UFO")
33
+ proc = Process(M, initial=[2, 1], final=[2, 1, 25, 25], # u d -> u d H H
34
+ overrides={"CV": 1, "C2V": 2, "C3": 1, "MU": 0, "MD": 0})
35
+ m2 = proc.m2(momenta) # spin+colour summed, averaged
36
+ amp = proc.helicity_amplitude(momenta, (1,-1,1,-1,0,0)) # one helicity config
37
+ me.set_param({"C2V": 0, "C3": 5}) # runtime knob (MatrixElement API)
38
+ ```
39
+
40
+ `momenta` is a list of NumPy 4-vectors `[E, px, py, pz]` (metric +−−−), in the
41
+ order `initial + final`. `ufoamp.phasespace.rambo` generates flat phase space.
42
+
43
+ ### Coupling control
44
+ * `overrides={name: value}` — set any **external** UFO parameter (C3, CV, C2V,
45
+ masses, `cabi`, …). Internal parameters and all couplings are re-derived
46
+ consistently.
47
+ * `restrict_couplings={GC_name: value}` — force individual UFO couplings
48
+ (e.g. switch off a vertex class by setting its couplings to 0).
49
+ * `inject={name: value}` — pin derived parameters (ee, sw, vev, …) to compare
50
+ two models at one physical point.
51
+
52
+ ## Using it in an analysis (`ufoamp.analysis`)
53
+
54
+ ```python
55
+ from ufoamp.analysis import MatrixElement, read_lhe, lhe_to_process_momenta
56
+
57
+ me = MatrixElement("HHVBF_UFO", "u d > u d h h", # process string (UFO names or PDG codes)
58
+ couplings={"CV": 1, "C2V": 2, "C3": 1}) # any external UFO parameter
59
+ me.m2(momenta) # one event -> |M|^2 (spin/colour summed, initial averaged)
60
+ me.m2_events(events, n_jobs=8) # (N, n_particles, 4) -> (N,)
61
+ me.set_param({"C2V": 0}) # switch hypothesis, no rebuild -> reweighting
62
+ me.helicity_amplitudes(momenta) # {(h1,...,hn): M}
63
+
64
+ # polarisations of EXTERNAL particles (massive vectors: L = h0, T = h+-1)
65
+ w = MatrixElement("HHVBF_UFO", "w+ w- > h h", couplings={...})
66
+ w.m2(momenta, pol={"w+": "L"}) # W+ longitudinal, W- summed
67
+ w.m2_polarized(momenta, ["w+", "w-"]) # {'LL','LT','TL','TT'} (sums to the total exactly)
68
+
69
+ # MadGraph events
70
+ for pdgs, mom, status, hel in read_lhe("events.lhe.gz"):
71
+ ev = lhe_to_process_momenta(pdgs, mom, status, me.initial, me.final)
72
+ print(me.m2(ev))
73
+ ```
74
+ * **Restrict cards / param cards**: `MatrixElement(..., param_card="restrict_4FSZeroYukawa.dat")`
75
+ or `SampleEvaluator(..., param_card="param_card.dat")` reads any SLHA card
76
+ (the UFO's `restrict_*.dat` or the `param_card.dat` of a MadGraph run) and
77
+ applies its values — zeros switch vertices off exactly as MadGraph's
78
+ restriction does. Precedence: light-quark defaults < card < `couplings`.
79
+ `read_param_card(path, model)` returns the {parameter: value} dict.
80
+ * **`max_orders={"QCD": 0}`** reproduces MadGraph's `QCD=0`: vertex couplings whose
81
+ UFO coupling order exceeds the limit are dropped (model-agnostic; prefer it
82
+ to dropping "vertices with a gluon"). Without it, gluon exchange is included
83
+ and dominates identical-quark channels.
84
+ * **`pol_frame`** sets the frame in which *external* helicities are defined:
85
+ `"lab"` (default), `"partonic_cm"` (initial-state rest frame — MadGraph's
86
+ `me_frame` default for polarised samples), or a list of legs. The
87
+ helicity-summed |M|² is frame independent; polarised pieces are not, so match
88
+ the frame of whatever you compare to.
89
+ * `SampleEvaluator` handles mixed-flavour samples (`p p > ...`): one
90
+ `MatrixElement` per partonic channel, built and cached on first use.
91
+ * `massless_light_quarks=True` (default) zeroes the u,d,s,c masses **and** their
92
+ Yukawa parameters (`ymup`, `ymdo`, …), as MadGraph does; otherwise Higgs and
93
+ Goldstone emission off the quark lines survives at O(y_q).
94
+ * Events and helicity configurations are batched through one pass of the
95
+ recursion (a flat batch axis on every wavefunction, current, momentum and
96
+ propagator), every vertex contraction runs a pre-compiled pairwise einsum
97
+ plan, and exactly-vanishing (chirality) helicity configurations are pruned.
98
+ Cost per core: ~1.6 ms per VBF-HH event, ~20 ms per `u d > u d z z h`
99
+ event; 2→2 processes are ~0.1 ms. `m2_events(..., chunk=32, n_jobs=N)`;
100
+ for LHE samples use `SampleEvaluator.m2_sample(read_lhe(path), vpol="LL")`.
101
+ * **Internal (t-channel) boson polarisation — propagator decomposition.**
102
+ For the W/Z radiated off the quark lines the unitary-gauge numerator is split
103
+ exactly, −g^{μν} + k^μk^ν/M² = T + L + S, with ε_± and ε_L built from the
104
+ off-shell k in a chosen frame (default: rest frame of the non-fermion final
105
+ state, i.e. the HH system); for spacelike k the completeness relation is
106
+ −g + kk/k² = T − ε_Lε_L*. This is the "propagator decomposition" definition:
107
+ frame-dependent, and the components interfere in |M|².
108
+ ```python
109
+ me.set_vpol("LL") # both fusing bosons longitudinal (one letter per initial quark)
110
+ me.m2(momenta) # coherent |M_LL|^2
111
+ me.m2_vpol(momenta, codes=("LL","LT","TL","TT","A")) # A = full propagator
112
+ me.set_vpol("LT", frame="lab"); me.set_vpol(None) # frame choice / switch off
113
+ ```
114
+ Letters: L, T, +, −, S (k k remainder), P (=T+L), A (=everything). For
115
+ massless quark lines k·J = 0, so S ≡ 0 and the split is gauge-independent.
116
+ Only t-channel lines (initial↔final fermion) are decomposed; s-channel
117
+ u d → W* diagrams keep the full propagator.
118
+
119
+ ## What is inside
120
+
121
+ | module | role |
122
+ |---|---|
123
+ | `ufo_model.py` | self-contained UFO loader (modern and legacy UFOs) |
124
+ | `couplings.py` | evaluates parameters → couplings with runtime overrides |
125
+ | `dirac.py`, `wavefunctions.py`, `propagators.py` | frozen conventions: metric (+−−−), Dirac basis, helicity spinors, polarization vectors incl. longitudinal, Feynman-gauge propagators |
126
+ | `vertex_eval.py` | **general `einsum` evaluator**: any UFO Lorentz string (Metric, P, Gamma, ProjM/P, Identity, Gamma5, Epsilon) → off-shell current |
127
+ | `recursion.py` | **Berends–Giele recursion** driven by the UFO vertex table: no diagram enumeration; currents tagged by fermion-line connectivity → exchange signs and colour flow |
128
+ | `process.py` | user interface: helicity amplitudes, |M|² |
129
+ | `phasespace.py` | RAMBO (massive) |
130
+ | `analysis.py` | `MatrixElement`: process strings, runtime couplings, polarisation selection, event arrays, LHE reader |
131
+
132
+ ### Conventions established (and validated) from the UFO files
133
+ * UFO vertex leg labels are **outgoing** particle types; a physical leg plugs
134
+ into label X if outgoing, anti(X) if incoming.
135
+ * `P(mu,k)` is the outgoing momentum of leg k (`P_SIGN = −1` relative to the
136
+ all-incoming bookkeeping). Fixed by the e⁺e⁻→W⁺W⁻ gauge cancellation: the
137
+ other sign makes |M_LL|² grow like s².
138
+ * `Gamma(mu,a,b)` = (γ^μ)_{ab}; a is the ψ̄ (row) slot, b the ψ (column) slot.
139
+ * UFO couplings already contain the `i`; Feynman rule = coupling × colour × Lorentz.
140
+ * **Gauge is auto-detected** (`gauge="auto"`): Feynman gauge when the UFO
141
+ ships Goldstone bosons (SM_UFO, HHVBF), unitary gauge (propagator with the
142
+ k^μk^ν/M² term) when it does not (SMEFTsim and most MadGraph models). Can be
143
+ forced with `gauge="feynman"|"unitary"`. Feynman ≡ unitary is verified
144
+ numerically on W_LW_L→HH and VBF-HH.
145
+ * `drop_vertex=lambda vertex, pdgs: ...` removes whole vertices. Prefer it to
146
+ zeroing couplings by name: UFO couplings are shared across vertices (the SM
147
+ WWHH contact shares its coupling with WWGG).
148
+
149
+ ## Validation (all exact unless stated)
150
+ | test | checks | result |
151
+ |---|---|---|
152
+ | e⁺e⁻→μ⁺μ⁻, photon | engine + UFO plumbing vs e⁴(1+cos²θ) | 1e-9 |
153
+ | e⁺e⁻→μ⁺μ⁻, γ+Z | chiral slots, Z width, interference; A_FB sign change across the pole | 1e-7 |
154
+ | σ(e⁺e⁻→μ⁺μ⁻) | absolute normalisation vs 4πα²/3s | 1.00000000 |
155
+ | e⁺e⁻→W⁺W⁻ | longitudinal gauge cancellation (VVV, t-channel ν, γ/Z) | |M_LL|² → const |
156
+ | Bhabha | fermion-exchange sign via s/t interference | 1e-8 |
157
+ | e⁺e⁻→uū | colour sum N_c Q_u² | exact |
158
+ | **u d → u d H H** | **HHVBF(1,1,1) ≡ SM UFO, 6-point, colour flows, CKM** | **1.000000000** |
159
+ | u d → u d H H | amplitude is C2V·A + CV²·B + CV·C3·C + E (E = Goldstone t-channel) | 1e-9 |
160
+ | W_L W_L → HH vs G⁺G⁻ → HH | Goldstone equivalence theorem | ratio → 1.0004 at 8 TeV |
161
+ | SM Feynman vs SM unitary | gauge invariance: W_LW_L→HH (all helicities), VBF-HH | exact / 1e-8 |
162
+ | SMEFTsim e⁺e⁻→μ⁺μ⁻ | SM limit; 4-fermion contact vs photon normalisation (LL only); **Fierz identity cll↔cll1** (crossed chains + fermion sign); linearity | exact |
163
+ | **SMEFTsim(c=0) vs SM_UFO, VBF-HH** | two UFOs, two gauges, 6-point | **1e-9** |
164
+ | HH→HHHH via \|H\|⁶ | 6-leg vertex plumbing | exact |
165
+ | SMEFTsim cH, cHbox, cHDD, cHW on VBF-HH | Higgs-sector coefficients act; cH exactly linear | — |
166
+
167
+ σ(e⁺e⁻→W⁺W⁻) = 20.8 pb at 200 GeV is the tree-level α(M_Z)-scheme Born value;
168
+ the LEP2 number (~17 pb) includes ISR (~−11 %) and a scheme shift (~−7 %).
169
+
170
+ ## Physics finding you should know (HHVBF UFO)
171
+ HHVBF scales only the physical VVH (CV), VVHH (C2V), HHH (C3) vertices; every
172
+ Goldstone–Higgs vertex is left at its SM value. Consequences, all reproduced
173
+ numerically here:
174
+ * In Feynman gauge the VBF-HH amplitude is `C2V·A + CV²·B + CV·C3·C + E`
175
+ where `E` is the unscaled Goldstone t-channel exchange.
176
+ * The O(s) growth of W_L W_L → HH is ∝ **(C2V − 1)**, independent of CV, in
177
+ Feynman gauge (ufoamp, RECOLA). In unitary gauge (MadGraph default) it is
178
+ ∝ (C2V − CV²). The two agree only at CV = 1. Away from CV = 1 the model is
179
+ gauge-dependent, so MadGraph and RECOLA will disagree there; C2V and C3 scans
180
+ at CV = 1 are safe.
181
+
182
+ ## QCD colour
183
+ Every UFO colour structure (`T`, `f`, `d`, `Identity`, `Epsilon`, products with
184
+ contracted indices) is a numeric tensor contracted in the same compiled einsum
185
+ as the Lorentz structure; currents carry their open external colour indices as
186
+ tensor axes and the amplitude is a tensor over external colours
187
+ (`helicity_amplitude` returns it; |M|² is its norm). Validated to ~1e-15
188
+ against the analytic massless 2→2 results (qq'→qq', qq→qq, qq̄→q'q̄', qq̄→gg,
189
+ gg→qq̄, qg→qg, gg→gg) and against MadGraph. Cost scales as 8^(#gluons):
190
+ ≤4 gluons is fast (VBF+jet ~60 ms/event), gg→ggg is ~17 s/event. Colour
191
+ sextets are not supported.
192
+
193
+ ## Majorana fermions
194
+ Majorana particles (spin-1/2, self-conjugate) and Dirac fermions in
195
+ fermion-flow-violating ("clashing arrow") vertices, as FeynRules writes e.g.
196
+ chargino–lepton–sneutrino couplings, are handled by charge-conjugating the
197
+ spinor in place whenever it enters a slot of the other type
198
+ (ψ → −ψᵀC⁻¹, ψ̄ → Cψ̄ᵀ, C = iγ²γ⁰); fermion signs come from the chain slots.
199
+ Validated against MG5 on MSSM_SLHA2 at 1e-14–1e-16: e⁺e⁻→χ̃⁰₁χ̃⁰₁ (both flow
200
+ orientations of the selectron exchange), χ̃⁰₁χ̃⁰₂, χ̃⁰₃χ̃⁰₄ (negative SLHA
201
+ masses), χ̃⁺₁χ̃⁻₁, u d̄→χ̃⁰₁χ̃⁺₁, gluino pairs from qq̄ and gg, and 6-point
202
+ e⁺e⁻→χ̃⁰₁χ̃⁰₁ℓ⁺ℓ⁻. Propagator poles use |m| in the width term so negative mass
203
+ eigenvalues are handled correctly.
204
+
205
+ Not yet supported: spin-2, colour sextets, custom propagators/form factors.
206
+
207
+ ## Independent reference: MadGraph5 standalone (`ufoamp.mg5ref`)
208
+ ```python
209
+ from ufoamp.mg5ref import MG5Reference
210
+ ref = MG5Reference("/path/to/mg5amcnlo", "sm", "u d > u d z z h QCD=0") # generates + compiles C++ once
211
+ me = MatrixElement("/path/to/mg5amcnlo/models/sm", "u d > u d z z h",
212
+ param_card=ref.param_card, max_orders={"QCD": 0}, gauge="unitary")
213
+ ref.compare(me, events) # max |ratio-1| on identical points; symmetry factors handled
214
+ ```
215
+ Needs MG5 (Python) and g++ only. `tests/test_mg5ref.py` runs eight SM
216
+ processes (EW, QCD, top, Higgs, VBF, ZZHjj) at machine precision.
217
+
218
+ ### Conventions needed to match MadGraph exactly (all now defaults or options)
219
+ * `restrict="default"`: `<ufo>/restrict_default.dat` is applied implicitly, as
220
+ MG5 does on `import model` (e.g. MG's `sm` has a diagonal CKM; restricted
221
+ parameters are absent from the run's param_card).
222
+ * `gauge="unitary"` (drops Goldstone vertices automatically). MG5 standalone
223
+ keeps widths in t-channel propagators (`zerowidth_tchannel=False`, default);
224
+ MG5 *event generation* defaults to `zerowidth_tchannel=True` in the run card.
225
+ * `scheme="fixed_width"` (default, MG5) or `"cms"` (complex-mass scheme:
226
+ complex masses in couplings and the unitary numerator; RECOLA-like).
227
+ * **Processes with external unstable bosons (e.g. `z z h j j`) are gauge
228
+ dependent at O(Γ/M) (~0.3–0.7 %) with any width scheme**, because on-shell
229
+ external Z's sit on the real mass shell: this is why Feynman-gauge ufoamp and
230
+ unitary-gauge MG5 agree to "4 digits" only; use `gauge="unitary"` to
231
+ reproduce MG5, or compare with widths set to zero.
232
+
233
+ ## SMEFT notes (SMEFTsim 3)
234
+ * Loader is library-free (never imports the UFO's Python-2 `object_library`), so
235
+ legacy and SMEFTsim UFOs load directly. Wilson coefficients are ordinary
236
+ `overrides` (`cH`, `cHbox`, `cll`, …, `LambdaSMEFT`).
237
+ * n-point vertices (5- and 6-leg), 4-fermion operators (spinor chains read off
238
+ each Lorentz structure; Fierz-crossed structures tagged separately), the
239
+ `Sigma` atom, and atom powers (`P(-1,1)**2`) are supported; every Lorentz
240
+ structure in all nine SMEFTsim UFOs parses.
241
+ * Input-scheme subtleties reproduced: `cll1` shifts G_F (vev, dgw, …) and adds
242
+ the photon-vertex coupling `GC_344`; `lam` is `Gf·MH²/√2`; couplings use
243
+ `vevhat`. SMEFTsim ships the SM loop-induced Hγγ/HZγ/Hgg effective vertices
244
+ as tree vertices and a Wolfenstein CKM (`CKMlambda`).
245
+
246
+ ## Scope and next steps
247
+ * Done: SM, HHVBF, SMEFTsim; any tree process from n-point vertices incl.
248
+ 4-fermion; colour for quark lines (colour-flow matrix N_c^cycles); Feynman
249
+ and unitary gauges.
250
+ * Not yet: external gluons / T^a, f^abc colour algebra (QCD-induced processes),
251
+ Majorana fermions, spin-2; vectorisation over phase-space points for fast MC
252
+ integration.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file