mg7: default histograms, plots with bands, and user defaults for the launch switches - #190
Closed
oliviermattelaer wants to merge 7 commits into
Closed
oliviermattelaer wants to merge 7 commits into
oliviermattelaer wants to merge 7 commits into
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>
This was referenced Sep 20, 2026
… 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>
oliviermattelaer
added a commit
that referenced
this pull request
Sep 23, 2026
…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>
Contributor
Author
|
Superseded by #195, which merges this branch in (default histograms, plots/HwU output, input/default_switch.txt, restored LHE auto-switch). On top of it, #195 adds [run] weighted_histograms (default off) and postprocessing_histograms (default on), and fixes the scan-summary means, which counted the under/overflow bins as in-range bins. |
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.
Stacked on #188 (base
fix-output-formats), because the first commit edits what that PR does. Please merge #188 first; the diff here is only the six commits on top of it.1. Launch switches: user defaults, and the LHE auto-revert restored
#188 turned the silent
output_format = 'lhe'override into an error. Put back: asking for a shower in the launch question is enough, a user should not have to know that it constrainsoutput_format.The switch that made that override visible (
analysisdefaulting to ExRoot wheneverexrootanalysis_pathis set — its default value even when nothing is installed) stays off, and gets a proper home instead:input/default_switch.txt, the switch counterpart ofinput/default_run_card_lo.dat.Read by
ControlSwitch, so one 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 — an unavailable program is reported and skipped, and an NLOfixed_order = ONstill drags the shower default off. Verified on a real LO and a real NLO output.Along the way, three things that were quietly broken:
MG5DIR/internal/, a directory that does not exist —input/default_run_card_{lo,nlo}.datandinput/default_run_card_mg7.tomlwere therefore never applied (the only test of the feature monkeypatches the path);default_run_card_lo.datwas checked before materialising all three, so an installation predating the mg7 one never received it;AskRun.get_allowed_reweightreturnedNoneon its first call.2.
[histograms]: a default set of plots per processAn empty section nobody filled in. The observable names are built from the final state, so
RunCardMG7.create_default_for_processnow writes them at output time: the pt and eta of every final-state particle, the invariant mass of every pair, the partonic sqrt(s), and the distribution of the event weight.A particle goes under a predefined group (
jet,lepton, ...) when the run time cannot tell its members apart: MadGraph merges a group's flavours into one subprocess and only the merged representative reaches the observables, sop p > l+ l-islepton, noteandmu(which would silently select nothing). Anything else gets a[multiparticles]group of its own (t = [6, -6]). Multiplicity is the smallest count over subprocesses —jet_2in a subprocess with one jet is a hard error in madspace'sbuild_indices, not an empty histogram.Ranges are order-of-magnitude guesses from the collider energy, or from the decaying particle's mass for a 1 → N width. On
p p > t t~ jat 13.6 TeV every histogram holds the cross section with under 1.2% in the overflow bin.weightis the one key that is not an observable of the momenta. madspace gainedEventHistogramSpec::from_weightfor it: the event weight over the reference weight (the cross section), so the axis does not depend on the process — an unweighted sample is a spike at 1, a partially unweighted one shows its spread.In the launch question,
set histograms OFFremoves them all andset histograms defaultputs the generated ones back.3. Plots, with the bands, in
Events/<run>/plotsEvery run now draws one PDF per observable and says where:
Read straight from the event-sample histograms rather than through an HwU file, which is what buys the bands: the scale envelope and the PDF uncertainty are already computed per bin by madspace, while
histograms.pyhas 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 dσ/dx 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.[run] make_plots, true by default, turns it off. matplotlib is imported lazily and only insideplots.py: a run without it says so once and carries on.4.
MADatLO.HwU(opt-in)For comparison with NLO: the same distributions in the format aMC@NLO writes as
MADatNLO.HwU, somadgraph/various/histograms.pyplots and compares them with no conversion step.[run] write_hwu, false by default — it is a second copy of numbersinfo.jsonalready carries.The variation columns are labelled
muR=1.00 muF=2.00/PDF= 331901(anddyn=3 muR=...), which is what makeshistograms.pyread them as an envelope and a band; 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 — a PDF set's central member carriesmur = muf = 1and would otherwise be written as a scale variation, hiding it from the band.Two conventions applied on the way out, both of them where a converter goes wrong: HwU bins hold dσ/dx, so every column is divided by the bin width; 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.
5. Which python runs a run, and a quiet stdout
bin/generate_eventstrusted#! /usr/bin/env python3. A run needs the packages of the environment MadGraph7 was started from — matplotlib, the LHAPDF bindings, madspace — and thepython3the PATH resolves to often has none of them; on one setup here the plots were silently skipped for exactly that reason. The launcher now pinssys.executableand, because a shebang is only a default, re-executes itself through that interpreter when it finds it is running somewhere else.The path is deliberately not resolved through its symlinks: in a virtual environment
sys.executablepoints 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 — 28549 lines for one run here, once anything lets DEBUG through the root logger. Its loggers are pinned to WARNING before the import, since matplotlib logs its own start-up while being imported.
Testing
test_DensityMatrixObservables22(No module named 'scipy').weighthistogram (python madspace/install.py --source).madgraph.various.histogramsrather than checking the text against itself), 12 for the plots, 7 for the launcher re-exec, 7 intest_mg7_launch.pyfor the switches.e+ e- > mu+ mu-, NLOp p > e+ e- [QCD], mg7p p > t t~andp p > t t~ j.🤖 Generated with Claude Code