بنقرة واحدة
spheno-build
Compile a model-specific SPheno binary from SARAH output and run spectrum / RGE calculations.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Compile a model-specific SPheno binary from SARAH output and run spectrum / RGE calculations.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Numerically evaluate a FormCalc-reduced one-loop amplitude with LoopTools 2.16 (Passarino-Veltman integrals via the Wolfram/LoopTools MathLink) and emit a scattering/v1 JSON (σ_SI, σ_SD) for direct detection. Unblocks the 2HDM+a loop-only DD path. Self-heals via _shared/installs/looptools preflight.
Reduce a FeynArts amplitude list with FormCalc 9.10. Produces amp_reduced.m and amp_reduced.meta.json sidecar conforming to amp_reduced.meta/v1. Self-heals via _shared/installs/formcalc preflight.
Generate Feynman diagrams and amplitudes using FeynArts 3.11. Supports built-in models (SM, SMQCD, THDM, MSSM) and SARAH-generated models. Outputs FeynAmpList.m, diagrams.pdf, topologies.json, and metadata sidecar.
Compute direct-detection likelihoods and 90%-CL exclusion verdicts using DDCalc 2.2.0. Leaf consumer of scattering/v1 JSON from /micromegas or /formcalc.
MadDM — dark matter relic density, direct detection cross-sections, indirect detection rates, parameter scans with experimental limit comparison
Run HiggsBounds-5 + HiggsSignals-2 constraint checks on a model SLHA file. Computes hb_allowed (per-channel AND), hs_consistent (Δχ² < 6.18), p-values, and per-channel CSV. Supports single-point and scan-directory modes.
| name | spheno-build |
| description | Compile a model-specific SPheno binary from SARAH output and run spectrum / RGE calculations. |
Compile a model-specific SPheno binary from SARAH output, run it on a LesHouches input card, and produce an SLHA mass spectrum. Scan-friendly with deterministic recoverable-failure handling.
Invoke after /sarah-build has produced SARAH output for the named model.
Prerequisites, checked before any work:
## Preflight: SPheno).$STATE_ROOT/models/<name>/sarah_output/SPheno/<SarahName>/ exists (produced by /sarah-build).gfortran on PATH.If any prereq is missing the skill emits a fatal blocker and stops.
Before any other action, run:
bash plugins/hep-ph-toolkit/_shared/installs/spheno/detect.sh
plugins/hep-ph-toolkit/_shared/installs/spheno/INSTALL.md into context and
follow it. When the install completes, re-run detect.sh before proceeding.
If it still fails, halt with the blocker code from the install reference.scripts/compile_model.py <model_name> [--force]
sarah_name from model_name via _shared/sarah_name.py.sha256(spec.yaml) + "=" + sarah_version + "+" + spheno_version,
stored at $STATE_ROOT/models/<name>/spheno_bin/.build_key.--force: skip compile.$STATE_ROOT/models/<name>/sarah_output/SPheno/<SarahName>/ into
<spheno_src_path>/<SarahName>/.make; see below).make -C <spheno_src_path> Model=<SarahName> -j<cpu_count> (os.cpu_count(),
NOT nproc — macOS lacks it).$STATE_ROOT/models/<name>/spheno_bin/make.log.SPHENO_COMPILE_FAILED fatal blocker with
context.make_log_tail (last 40 lines).<spheno_src_path>/bin/SPheno<SarahName> →
$STATE_ROOT/models/<name>/spheno_bin/SPheno<SarahName>.--force bypasses the key check and forces a full recompile. A cache hit is
confirmed by the absence of a make invocation in captured stderr.
SARAH's emitted SPheno model tree has three known rough edges that break the
downstream make. compile_model.py applies three small, idempotent
post-copytree fixes (safe to re-run after a cache hit):
| Helper | What it does | Why it's needed |
|---|---|---|
_patch_darwin_ar(spheno_model_src) | Rewrites ar -ruc -U → ar -ruc on lines 90 and 94 of the per-model Makefile. | Xcode's ar on Darwin does not accept -U. SPheno-4.0.5/src/Makefile branches on uname -s, but the per-model Makefile SARAH emits does not and always emits -U. No-op on Linux — -U is legitimate there. |
_dedupe_model_data_decls(spheno_model_src, sarah_name) | Strips standalone Real(dp) :: <name> lines in Model_Data_<Model>.f90 when <name> already appears in an earlier grouped declaration in the module-spec part (before Contains). | SARAH can emit a BSM parameter both in the grouped "RGE-running" declaration and as a later standalone line in the same module — gfortran rejects the redeclaration. Scoped to the module spec part only, so per-subroutine scopes (RGEAlphaS's own Real(dp) :: nQuark) are left untouched. |
_patch_notparallel(spheno_model_src) | Inserts .NOTPARALLEL: (with a sentinel marker) at the top of the per-model Makefile. | The per-model Makefile lists archive members as prerequisites without declaring inter-file Fortran .mod dependencies. Under make -jN, RGEs_<Model>.f90 can start compiling before Model_Data_<Model>.mod is written to ../include and fail with "Cannot open module file". .NOTPARALLEL: serializes only this Makefile's targets; the parent src/libSPheno.a build keeps its parallel speedup. |
Two of these were added while debugging the singlet-doublet demo on macOS
(demo_output/singlet-doublet/report.md, 2026-04-21, has the A/B diagnosis and
the compile-time race that surfaced once A and B were cleared). If a future
model trips a different SARAH emission bug, add another _patch_* helper next
to these rather than expanding one — each helper does one thing so it stays
idempotent and independently testable.
The rank-1 Dirac mass patches (_patch_rank1_dirac_mass, _patch_phasefs_init)
that let the singlet-doublet SPheno tree compile and give a physical spectrum
are documented in
sarah-build/references/sarah-workarounds.md §16–17.
scripts/run_spheno.py <model_name> [--params k=v,...] [--input-card <path>]
Invocation signature (spec §5): two positional arguments only, no shell redirection:
$STATE_ROOT/models/<name>/spheno_bin/SPheno<SarahName> <out_dir>/LesHouches.in <out_dir>/SPheno.spc
The compiled SPheno binary silently truncates its input-card path argument at 120 characters. The Fortran read into a fixed-length
CHARACTER(len=120)buffer drops anything past column 120, so aLesHouches.inpath longer than that becomes a different, non-existent path — SPheno then reports the input card as not found (or reads garbage) with no indication that truncation happened. Keep the absolute path toLesHouches.inunder 120 chars: prefer a short$STATE_ROOTand short run-dir names, and if a deep worktree pushes you over the limit, run the binary from acwdinside<out_dir>and pass the bare filenames (LesHouches.in/SPheno.spc) rather than absolute paths.
With --input-card, the card is copied verbatim into the run directory (no
templating). Otherwise leshouches_template.py generates it from
spec.parameters defaults, optionally patched by --params.
Output → $STATE_ROOT/models/<name>/runs/<TS>-<HASH>/, where <TS> is
YYYY-MM-DDTHHMMZ (minute-resolution UTC) and <HASH> is an 8-char blake2b
hash of the override set (salted on exact timestamp+overrides collision):
LesHouches.in — input card used.SPheno.spc — raw SLHA output.summary.json — parsed masses, widths, problems, mixing, spinfo_warnings.The hash suffix guarantees multiple runs inside the same UTC minute never reuse
a run_dir and overwrite one another's SPheno.spc.
scripts/run_spheno.py <model_name> --scan MpsiD=200:1000:step=100 --scan gD=0.5:2.5:step=0.5
Each --scan argument is one axis: name=start:stop:step=s. The Cartesian
product of all axes is computed; scan.py runs each point sequentially. Output
→ $STATE_ROOT/models/<name>/runs/scan_<TS>/; scan_index.csv columns:
index, <param1>, <param2>, …, status, blocker_code, slha_path,
timing_s.
Scans continue past recoverable failures. Only a SPheno crash on a point with no
.spc is status=error. Determinism: grid values via numpy-free arithmetic
(range + step), sorted lexicographically by parameter name, so scan_index.csv
is byte-identical across runs given identical inputs.
| Condition | Blocker code | Mode |
|---|---|---|
Exit ≠ 0 or SPheno.spc absent | SPHENO_NO_OUTPUT | fatal |
Block PROBLEM code 1/2/3 present | SPHENO_SPECTRUM_PROBLEM | recoverable |
Block SPINFO item 4 present | SPHENO_RGE_NONCONVERGENT | recoverable |
Clean Block MASS present | success | — |
spheno_src_path absent | SPHENO_PATH_INVALID | fatal |
| SARAH output tree missing | SPHENO_COMPILE_FAILED (compile stage) | fatal |
| Invocation path exceeds ~120-char Fortran argv limit and can't be shortened | SPHENO_PATH_TOO_LONG | fatal |
Recoverable blockers let scans continue (status=recoverable + the
blocker_code in scan_index.csv). Blockers are single-line JSON on stderr
conforming to _shared/blocker.schema.json.
scripts/leshouches_template.py build(spec, overrides) produces a LesHouches
input string with:
Block MODSEL: 1 0 (non-SUSY; only supported case in v1).Block SMINPUTS: PDG 2020 defaults (hardcoded: alpha_em^-1 = 127.934, G_F, alpha_s(MZ), MZ, m_b, m_t, m_tau).Block MINPAR: one row per spec.parameters, in declaration order (index 1..N), using default or overrides values.Block SPHENOINPUT: copied verbatim from sarah_output/SPheno/<Model>/Input_Files/LesHouches.in.<Model> (SPHENOINPUT block only).patch_minpar(text, params) replaces MINPAR entries by name-indexed lookup,
leaving other entries unchanged.
After a successful single-point run:
| Key | Value |
|---|---|
config.models[<name>].spheno_bin | Path to compiled binary |
config.models[<name>].latest_slha | Path to most recent SPheno.spc |
config.models[<name>].latest_run | Run directory name (YYYY-MM-DDTHHMMZ-<8 hex>) |
config.models[<name>].spheno_built_at | Compile completion timestamp |
Unit tests use HEPPH_STATE_ROOT and XDG_CONFIG_HOME pointed at temp
directories. Integration tests needing a real SPheno binary and gfortran are
gated on HEPPH_RUN_NETWORK_TESTS=1 or the presence of both gfortran and the
SPheno binary. The SARAH-output fixture at
tests/fixtures/sarah_output_darksu3.tar.gz is a committed placeholder (pre-W3);
after W3 merges, the manager re-dispatches scripts/regenerate_fixture.py to
replace it with real SARAH output. Fixture size hard cap: 10 MB gzipped.
| Script | Purpose |
|---|---|
scripts/run_spheno.py | CLI entry point; dispatches compile + run/scan |
scripts/dispatcher.py | Backend-agnostic spectrum dispatch (spheno vs. analytic) |
scripts/backends/spheno.py | SPheno backend wrapper (wraps run_point.run) |
scripts/backends/analytic.py | Analytic backend (calls registered Python module) |
scripts/analytic_models/ | Registered analytic modules (singlet_doublet, …) |
scripts/slha_writer.py | Render analytic result dict → SLHA text |
scripts/compile_model.py | Stage 1: copy SARAH output, run make, cache binary |
scripts/run_point.py | Stage 2: single-point SPheno invocation + classification |
scripts/parse_slha.py | Stage 3: SLHA → summary.json |
scripts/leshouches_template.py | LesHouches input card generation |
scripts/scan.py | Sequential Cartesian-product scan |
scripts/regenerate_fixture.py | Post-W3: rebuild sarah_output_darksu3.tar.gz |
/spheno-build dispatches spectrum computation to one of two backends via the
single routing point scripts/dispatcher.py (public entry
dispatch(model_name, spec, params, out_dir, config, backend_factory=None) -> dict).
Classification is backend-scoped: SPHENO_* codes for the spheno path,
ANALYTIC_* for the analytic path.
| Backend | Entry class | Module | First consumer | Prefix |
|---|---|---|---|---|
spheno | SphenoBackend | scripts/backends/spheno.py | every SARAH-clean model | SPHENO_* |
analytic | AnalyticBackend | scripts/backends/analytic.py | singlet_doublet | ANALYTIC_* |
Both conform to the SpectrumBackend Protocol at backends/__init__.py:25–35;
BackendResult (same file, 15–22) keys: status, blocker_code, slha_path,
summary, cache_hit, backend, timing_s. Backends load lazily via
_load_backend; tests inject fakes through backend_factory. Both
run_spheno.py and scan.py call dispatch(); neither imports a backend class
directly.
Selection rule (_resolve_backend_name, dispatcher lines 31–36):
spec['backends']['spectrum'] is set, use it verbatim."spheno" in spec['outputs'], use spheno.analytic.Spec-level coherence between outputs and backends.spectrum is enforced by
/sarah-build's validate_spec._validate_backends.
The effective default is
analytic, notspheno. Rules 1–2 both require keys the spec must actually carry: a top-levelbackends.spectrum, or a top-leveloutputslist containing"spheno". When a spec has neither (assinglet_doublet.yamlcurrently does — its onlyoutputs:keys are nested mixing-matrix names, not a top-level output list), the dispatcher falls through to rule 3 and runs the analytic backend. On that path the compiled SPheno binary is never invoked — the SLHA is produced byanalytic_models/singlet_doublet.py→slha_writer.py, not by SPheno. Any claim below thatsinglet_doubletis "spheno-backed by default" describes the intended end state, not what the dispatcher does today: to actually exercise the SPheno binary you must addbackends: {spectrum: spheno}(or a top-leveloutputs: [ufo, spheno]) to the spec, or pass the backend explicitly withrun_spheno.py <model> --backend spheno— that flag injectsbackends.spectruminto the in-memory spec for that run only, so a model whose spec defaults to analytic (e.g.singlet_doublet) can opt into a real SPheno spectrum without editing the spec on disk.
| Symptom | Backend |
|---|---|
| SARAH-clean BSM extension; need RGE running, loop-level Higgs, decay widths | spheno (default when outputs includes spheno) |
/sarah-build emits CheckModelFiles::MissingParameter or Param.$Failed on a rank-1 Dirac sub-block | analytic — see sarah-build/references/authoring-gotchas.md §6 |
When to enable RGE running (turn on the spheno backend). SPheno's value over
an analytic diagonalisation is almost entirely in what happens between the input
scale and the observable scale — RGE running, loop corrections to masses,
threshold matching, decay-width generation. If none of those apply, the spheno
backend is cost without value: a Fortran compile and a multi-step failure surface
for arithmetic a NumPy eigensolve does in microseconds. Most demo benchmarks
don't need it — Profumo's three models take EWSB-scale Lagrangian inputs, have
closed-form spectra, and feed a tree-level MadDM relic computation.
singlet_doublet is intended to be spheno-backed (its SARAH → SPheno path
is verified end-to-end; commit 1fb8ad8), with the analytic 3×3 diagonalisation
as a fast path — but note the correction under Selection rule above: because
the current spec carries no top-level backends.spectrum/outputs key, the
dispatcher default actually resolves to analytic and the compiled binary is
not run unless you add the key; two_hdm_a and dark_su3 declare
backends.spectrum: analytic with outputs: [ufo] and route to
analytic_models.stub_unimplemented (their SPheno paths are blocked on unrelated
bugs — SARAH RecursionLimit for 2hdm_a; the missing /dark-matter-constraints
skill for dark_su3).
Flip to spheno when at least one is true: high-scale inputs (couplings
defined above EWSB, observable at/below it — running down is the point);
loop-level spectrum sensitivity (a mass eigenvalue shifts meaningfully under
1–2-loop corrections — canonically the MSSM Higgs, or any tree-level mass in a
fragile window with an O(10 GeV) correction); intermediate thresholds (a heavy
state integrated out needs matching a single diagonalisation cannot express);
decay widths or flavour / EW-precision observables (SPheno emits partial widths
and computes b→sγ, Bs→μμ, g-2, Δρ, … from the running spectrum — an analytic
module returns mass + mixing only); unitarity / vacuum-stability checks at
arbitrary scale (need running quartics, not tree-level). If none apply, leave
analytic and either write the module or use analytic_models.stub_unimplemented
to signal BLOCKED explicitly rather than silently falling into a spheno compile.
Writing a new analytic module — the compute(spec, params) -> dict interface
contract, module discovery/registration, SLHA output, blocker codes, and the
singlet_doublet physics ship-gate pattern — is in
references/analytic-backend.md. For invoking
the analytic backend as a route around a failing SPheno compile (from a spec or
an out-of-tree driver), see
references/analytic-bypass-recipe.md.
sarah-build/references/authoring-gotchas.md §6 (Rank-1 Dirac sub-blocks and
silent parameter degradation) — canonical motivating case for choosing
analytic over spheno.sarah-build/scripts/validate_spec.py _validate_backends — enforces
outputs ↔ backends.spectrum coherence at spec-validation time.singlet_doublet ship-gate
(Eq. 3 mass matrix, Eq. 8 blind-spot invariant).