mg7: npy events by default, npy->LHE converter - #195
Merged
Merged
Conversation
Restore the automatic switch to output_format = 'lhe' when a post-processing
tool is selected on an mg7 run: the npy formats carry no event record those
tools can read, but asking for a shower in the launch question should be
enough -- the user does not have to know that it constrains output_format.
Add input/default_switch.txt (materialised from the shipped, fully commented
input/.default_switch.txt), the switch counterpart of
input/default_run_card_lo.dat: "analysis = MadAnalysis5" there is the file
version of typing "analysis=MadAnalysis5" at every launch. It is read by
ControlSwitch, so the same file serves the LO madevent, NLO aMC@NLO and mg7
questions; each ignores the keys it does not have. Entries go through the
usual validation and consistency resolution, so an unavailable program is
reported and skipped rather than silently accepted, and an NLO
"fixed_order = ON" still drags the shower default off.
Along the way:
- the run_card default files were read from MG5DIR/internal/, a directory
that does not exist: input/default_run_card_{lo,nlo}.dat and
input/default_run_card_mg7.toml were therefore never applied.
- only default_run_card_lo.dat was checked before materialising all three,
so an installation predating the mg7 one never got it.
- AskRun.get_allowed_reweight returned None on its first call.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
[histograms] was an empty section nobody filled in. The observables it takes are named after the final state, so they can only be written once the process is known: RunCardMG7.create_default_for_process now fills it with the pt and eta of every final-state particle, the invariant mass of every pair of them, the partonic sqrt(s) and the distribution of the event weight. The particles are named after the [multiparticles] groups, with the "_1", "_2", ... suffix picking the hardest, second hardest, ... of a group: `p p > t t~ j` gets t_1-pt, t_2-pt, jet-pt, the three etas, the three pair masses and sqrt_s. A particle is histogrammed under a predefined group (jet, lepton, ...) when the run time cannot tell its members apart -- MadGraph merges the flavours of a group into a single subprocess and only the merged representative reaches the observables, so `p p > l+ l-` is "lepton", not "e" and "mu" (the latter would silently select nothing). A particle that matches none of that, like the top, gets a [multiparticles] group of its own. The multiplicity is the smallest over the subprocesses, since asking for the n-th hardest particle of a group is an error in a subprocess that has fewer. The ranges are order-of-magnitude guesses from the collider energy (or the mass of the decaying particle for a 1 -> N width), rounded to 1/2/5 x 10^k. On p p > t t~ j at 13.6 TeV every histogram holds the cross section with under 1.2% in the overflow bin. "weight" is the one [histograms] key that is not an observable of the momenta. madspace gained EventHistogramSpec::from_weight for it: the event weight divided by the reference weight (the cross section, i.e. the mean weight), so the axis does not depend on the process -- a fully unweighted sample is a spike at 1 and a partially unweighted one shows its spread. In the launch question, "set histograms OFF" removes them all and "set histograms default" puts the generated ones back from Cards/run_card_default.toml. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The histograms only existed as JSON inside Events/<run>/info.json, which nothing else in MadGraph reads. HwU is the format the rest of the code speaks -- an aMC@NLO run writes MADatNLO.HwU and madgraph/various/histograms.py plots and compares those -- so an mg7 run now writes the same thing next to its events, and an LO distribution can be overlaid on an NLO one with no conversion step in between. Everything info.json carries goes into the file: the nominal distribution, one column per systematics variation, and the scale/PDF bands. The variation columns are labelled "muR=1.00 muF=2.00" / "PDF= 331901" (and "dyn=3 muR=..." for a dynamical scale), which is what makes histograms.py read them as an envelope and a band rather than as anonymous curves -- the envelope it rebuilds from them is bin-by-bin the one madspace computed. The group a weight belongs to decides its label, not the variation's own fields: the central member of a PDF set carries mur = muf = 1 and would otherwise be written as a scale variation, hiding it from the band. Two conventions of the format are applied on the way out, both of them where a converter goes wrong: HwU bins hold dsigma/dx, so every column is divided by the bin width (sum(central * width) is then the cross section), and HwU has no under/overflow bins, so the first and last entries of the madspace arrays are dropped, as an aMC@NLO analysis does. The conversion is a pure function of the two dicts madspace writes, in hwu_output.py, which imports nothing. The tests read the result back with madgraph.various.histograms rather than checking the text against itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MADatLO.HwU is a second copy, in another format, of numbers Events/<run>/ info.json already carries, so it is now opt-in rather than written by every run: [run] write_hwu in the run_card, false by default, or "set write_hwu True" in the launch question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…plots
A run had no picture of anything: info.json held the distributions and
MADatLO.HwU could be fed to histograms.py by hand, but nothing was ever drawn.
Every run now writes one PDF per observable into Events/<run>/plots and says
where they went ("11 plot(s) stored in Events/run_01/plots").
The drawing reads the event-sample histograms straight from madspace rather
than going through the HwU file, which buys the bands: the scale envelope and
the PDF uncertainty are already computed per bin there, while histograms.py
has to rebuild the PDF band from the member weights and needs the LHAPDF
python module to do it -- not installed everywhere, and broken on python 3.14.
Each plot is the differential cross section with its statistical errors, the
bands behind it, and a second panel with the bands relative to the nominal.
The y axis goes logarithmic when the spectrum falls by more than a factor 50,
which is what tells a pt spectrum from an eta one.
The conventions are the ones the other two consumers of these numbers already
use, so the three agree: dsigma/dx on the y axis, and no under/overflow bins
(HwU has none, and MadBoard's browser plots slice them off too).
[run] make_plots, true by default, turns it off. matplotlib is imported
lazily and only inside plots.py: a run without it says so once and carries on,
every number being in info.json either way.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… quiet Two things a run got wrong on its own stdout. bin/generate_events trusted "#! /usr/bin/env python3". A run needs the packages of the environment MadGraph7 was started from -- matplotlib for the plots, the LHAPDF bindings, madspace -- and the python3 the PATH resolves to often has none of them: on this setup the plots were silently skipped for exactly that reason. The launcher now pins sys.executable as its shebang and, because a shebang is only a default (bypassed by "python3 bin/generate_events", ignored by the kernel when the path is too long), re-executes itself through that interpreter whenever it finds it is running somewhere else. Both paths draw the plots now. The path is deliberately not resolved through its symlinks: in a virtual environment sys.executable points at the environment, and the base interpreter it links to does not see its site-packages -- taking the realpath handed the run a python without matplotlib. A vanished interpreter is ignored rather than fatal, and the gridpack, which is the portable artefact, keeps its "/usr/bin/env python3". matplotlib narrates every font it embeds in a PDF at DEBUG level, which was a dozen lines per plot -- 28549 of them for one run here -- as soon as anything let DEBUG records through the root logger. Its loggers are now pinned to WARNING, before the import rather than after: matplotlib logs its own start-up while it is being imported, and a logger silenced afterwards is silenced four lines too late. An explicit level above WARNING is left alone. The launcher text moved into mg7_launcher_source() so that the re-exec can be tested by running it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… to it
The test pasted its one sqrt_s histogram right after the [histograms] header,
which was fine while that section was always empty. The output now writes
process-specific default histograms there, sqrt_s among them, so the edited
card defined sqrt_s.min twice: a TOML error ("Cannot overwrite a value") on
which generate_events died at once. It died with exit code 0, so the helper's
return-code check never printed the log and the test reported "no mg7 run
directory" instead.
The test also relies on its histogram being the only one (it reads
event_histograms[0] and expects 10 bins), so the section's content is now
replaced as a whole, which keeps that true whatever the defaults generate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
output_format now defaults to compact_npy. The run_card is switched to lhe before the generation only when something reads the LHE file: a post-processing driver mg7 actually runs (reweight, MadSpin, MA5, Pythia8, Delphes, Rivet -- not ExRoot, which has no mg7 driver), a time_of_flight threshold, or legacy systematics.py without madspace's native weights. The card is read as raw TOML and only its output_format line is edited, so a run_card scan survives. npy runs now keep lhe_meta.json (LHE header/<init>) and, for compact_npy, lhe_completer.json. The new npy_to_lhe module rebuilds events.lhe.gz from them (colour, helicity, resonances, rwgt/initrwgt), via bin/npy_to_lhe, the npy_to_lhe run command, or automatically when a post-processing tool runs on a run that only has npy events. Validated event by event against a direct lhe run with the same seed (p p > e+ e- j, 5000 events). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
I'd propose lhe_npy instead of compact_npy because it has the full LHE info |
lhe_npy keeps the complete LHE record (pdg ids, status, mothers, colour, helicity) in the npy file, so the events are usable without the LHE completer and convert to LHE by plain reformatting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Also #188 introduces a |
The gridpack's grid_run_card.toml copied the run's output_format, so with the new npy default a gridpack wrote npy events. A gridpack feeds a shower/detector chain, so its card now always says lhe (--output_format still picks an npy output). test_gridpack_mg7 checks the card and that events.lhe.gz is produced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take #188's header.lhe (LHE <header>/<init> written next to npy events), ExRoot-off-by-default and prompt cleanup of the per-channel npy files. Conflicts: - keep the automatic switch to LHE (force_lhe_output_if_needed) instead of #188's check_lhe_output_required error: with npy the default, raising would stop every run that turns a shower on; - drop lhe_meta.json: npy_to_lhe now copies header.lhe verbatim (it carries the cards, beams, cross section, MG7Seed and <initrwgt>) and takes the rwgt columns from the npy dtype; lhe_completer.json is kept for compact_npy (save_lhe_completer); - output_format default stays lhe_npy, with #188's comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…branch Takes #190's default [histograms], plots/HwU output and input/default_switch.txt. Both branches restore the automatic switch to LHE; keep this branch's force_lhe_output_if_needed (raw-TOML edit that survives scan cards, driven by selected_tools() and the [postprocessing] steps), with #190's rationale in its docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gridpack header: save_gridpack stores the run-independent LHE header (proc/param/run cards, beams, PDF, weight mode) in data/lhe_meta.json; the gridpack builds its LHEMeta from it with the cross section and seed of its own run, for the lhe output and for header.lhe (which had only <initrwgt> and an all-zero <init>). A compact_npy gridpack run also gets lhe_completer.json, so npy_to_lhe can convert it. Gridpacks made before this fall back to the old minimal header. Histograms: [run] weighted_histograms (default false) fills the [histograms] with the weighted events during integration; postprocessing_histograms (default true) fills them with the final events and their variation weights, which is what the plots and the HwU file use. The scan-summary means now come from the post-processing histograms when present, and no longer count the under/overflow bins as in-range bins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Includes #188 and #190 (merged in; merge those first or close them in favour of this one). Merge decisions:
the automatic switch to LHE is kept (this branch's version), not Bugfixes of final and intermediate madspace output #188's error;
Bugfixes of final and intermediate madspace output #188's
header.lhereplaces thelhe_meta.jsonthis PR first added:npy_to_lhecopies it verbatim as the LHE header;mg7: default histograms, plots with bands, and user defaults for the launch switches #190's plots, HwU output, default histograms and
input/default_switch.txtare taken as they are.Default
output_formatis nowlhe_npy(waslhe): the complete LHE record (pdg, status, mothers, colour, helicity) in npy form;compact_npystays available.Automatic switch to
lhebefore generation, only when something will read the LHE file:[postprocessing] time_of_flight >= 0;[postprocessing] systematicswith native[systematics]disabled.analysis = ExRoot(the default wherever ExRootAnalysis is installed) no longer forces LHE, since mg7 has no ExRoot driver. The run_card is read as raw TOML and only itsoutput_formatline is edited, soscan:[...]cards survive (a fullRunCardMG7parse crashed on them).npy→LHE converter (
madgraph/iolibs/template_files/mg7/npy_to_lhe.py) for when LHE is needed after the run:compact_npyruns also savelhe_completer.json; the header comes from Bugfixes of final and intermediate madspace output #188'sheader.lhe;events.lhe.gzfrom these: colour, helicity, resonances/mothers,<rwgt>and<initrwgt>;bin/npy_to_lhe [RUN|dir|events.npy] [-o OUT] [--seed N] [--no-gzip](written at output time), thenpy_to_lherun command, and automatically when a post-processing tool runs on a run that only has npy.Gridpacks default to LHE:
grid_run_card.tomlalways getsoutput_format = "lhe"whatever the run used (bin/generate_events --output_formatstill selects npy).test_gridpack_mg7checks the card and thatevents.lhe.gzis produced (passes locally).Complete gridpack LHE headers:
save_gridpackstores the cards, beams and PDF indata/lhe_meta.json. The gridpack adds its own cross section and seed, for both thelheoutput andheader.lhe(before: only<initrwgt>,<init>all zeros). Acompact_npygridpack run also getslhe_completer.json, so it can be converted.Histogram switches in
[run]:weighted_histograms(default off): fills the histograms with the weighted events during integration;postprocessing_histograms(default on): fills them with the final events and their variation weights, which the plots and the HwU file use.The scan-summary means now come from the post-processing histograms when present, and no longer count the under/overflow bins as in-range bins.
selected_tools()is now shared by the post-processing runner and the LHE check. The tool list is checked before looking for or converting an event file.Validation
p p > e+ e- j, 5000 events, same seed: acompact_npyrun (converted) and anlhe_npyrun (converted) were each compared with a directlherun. They match event by event on momenta, pdg, status, mothers, colour, helicity, weight, scale and systematics weights. The header differs only in the recordedoutput_format. Conversion takes about 1 s for 5k events.TestForceLHEOutput(default, no-op, tool, time_of_flight, legacy systematics, scan card, missing line) andTestNpyToLHE(lhe_npy round trip, default run pick, pre-existing run without header).test_mg7_launch,test_bannerand the MadSpin unit tests pass.test_mg7_reproducibilitynow setsoutput_format = "lhe"because it hashes the LHE file. The acceptance suite was not run.Limits
<mgrwt>block ([systematics] write_inputs) is not rebuilt by the converter.compact_npyconversion re-draws the completer's random choices, so it is statistically equivalent to a direct LHE run, not guaranteed byte-identical (it was identical in the test above).header.lheand cannot be converted; the converter says so.data/lhe_meta.jsonand keep the old minimal header.🤖 Generated with Claude Code