Skip to content

Review fixes for #166 (backend separation) - #179

Open
oliviermattelaer wants to merge 18 commits into
backend_seperation_PRfrom
claude/pr166-review-fixes
Open

oliviermattelaer wants to merge 18 commits into
backend_seperation_PRfrom
claude/pr166-review-fixes

Conversation

@oliviermattelaer

@oliviermattelaer oliviermattelaer commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Fixes for the review of #166, as a PR into backend_seperation_PR so they can be looked at (and accepted or dropped) separately from the split itself. One commit per item, in the order of the review comment; each commit message says what was wrong, how it was reproduced and how the fix was checked, so any of them can be reverted on its own.

Needs fixing (review items 1-3), plus three regressions found while fixing them

Commit What
a2c5a52 (1) nMF back in scope for initProc: every merged-flavor model (e.g. MSSM u u~ > n1 n1) failed to compile. Now builds and reproduces the merge-base ME.
4f37adb (new) Color choice goes through the ncolor_flow flows again. The split hard-coded jamp2_sv[ncolor*iParity+icol] += cxabs2(jamp_sv[icol]), dropping the per-process jampflow_lines. For DDM (all-gluon) processes ncolor != ncolor_flow: on g g > g g g (6 DDM jamps, 24 flows) the split selected 12 distinct flows, one of them 568/1024 times, against 24 on the merge base. MEs were bit-identical, so an ME-only comparison cannot see it. The flow lines now go to a generated ColorFlows.inc that SigmaKin.cc includes.
4db0133 (new, GPU) backend/gpu did not compile (DeviceAccessJamp2::kernelAccessIcolConst and #include "CPPProcess.h" in MatrixElementKernels.cc were dropped), and two device-semantics regressions: calculate_jamps wrote jamps with nhel = 1 while color_sum_kernel reads the [2][ncolor][nGoodHel][nevt] buffer with nGoodHel (nhel is the color stride), and the atomicAdd on the helicity-shared colAllJamp2s became a racy +=. All restored to the merge base's own lines.
e364c25 (2) Squared split orders restored instead of refused. The backend .cc files are compiled once per P* directory against its generated headers, so a compile-time switch suffices (same pattern as shouldUseBlas): ProcessData::nampso/njampso/nsqampso, pairing tables in ColorData.h, color_sum_cpu_splitorders (the old template's pair loop) under if constexpr( nampso > 1 ), jamp buffers sized njampso; GPU static_asserts nampso == 1 like the old #error. color_sum_splitorders.cc is folded in and removed.
ce3908a (3) Drop the call to the undefined _link_backend_dirs_in_P in trex.py.

Worth changing (review items 4-6)

Commit What
9507875 (4) Select mgOnGpuVectors.h on a MGONGPU_HAS_VECTORS_H config macro instead of __has_include, which a stray src/mgOnGpuVectors.h (TREX copies one) turned true for scalar/GPU builds. Reproduced: the stray header broke BACKEND=scalar; now it builds and matches.
0ce9731 (5) Restore MGONGPU_CPPSIMD in the two mixed-mode guards of backend/simd/color_sum.cc, so they agree with nParityCS/nParity. Latent only; output unchanged.
c501a7d (6) Copy only regular .h/.cc files from the backend and src/rambo template dirs: a .DS_Store placed there used to land in every output.

Optional cleanup (review items 8-11, 13-14; 15 partly)

Commit What
d76b6b2 Stop rendering process_sigmaKin_function.inc (result was discarded; it was being kept in sync as a 4th copy of SigmaKin.cc); remove it and color_sum_blas_loop.inc.
6603911 Remove get_matrix_single_process, jamp_ncolor, process_matrix.inc, color_sum_blas.inc: no caller left.
0cbc16f Remove the empty MemoryAccessVectors.h of backend/cpu and backend/gpu.
8701fe0 backend/common/ for the 9 files byte-identical in all three backends, searched after backend/<variant>/. Deliberately no name in both: a quoted #include resolves in the including file's directory first, so a common "host" header would shadow a GPU override. The 6 files shared only by cpu and simd therefore stay duplicated.
e4cf21b Stale comments (e.g. a CalculateJamps.cc that never existed; backend sources described as compiled once when they are compiled per P* and must be).
06b52cd fpeEnable() moves from constexpr_math.h to its own backend/common/fpe.h.
c5db8e9 Restore the nprocesses == 1 caveat on helcolDenominators; the actual sizing is #168.

Items 7 (HRDCOD=1, already broken on main) and 12 (.gitignore PROC*) are intentionally not addressed here.

CI fixes (the 34 checks that fail on #166 as well)

Commit What
1e93379 #include <cstring> in backend/{cpu,simd}/SigmaKin.cc and backend/gpu/MemoryBuffers.h, which call memcpy. clang/libc++ pulls it in transitively, GCC/libstdc++ does not, so on the Linux runners every madmatrix build stopped at SigmaKin.cc:71: error: 'memcpy' was not declared in this scope. That one error is 33 of the 34 red checks on #166 (every acceptancetest_mg7_*, all check_xsec_processes, acceptancetest_30/32, unittest_31). Reproduced locally with g++-16; with the fix, test_check_language_function_epem_aa, test_standalone_cpp, test_standalone_split_orders_interference and test_e_e_collision_mg7 pass under CXX=g++-16.
61201d4 FD gauge: define_gauge_dir / calculate_propagator_factor are non-template functions defined in HelAmps_<model>.h but marked INLINE, which is empty unless HELINL=1. Now that CPPProcess.h includes HelAmps and SigmaKin.cc / umami.cc include CPPProcess.h, the process library failed to link with duplicate symbols (acceptancetest_uncovered_merged, hidden behind the memcpy error on #166). Marked inline; the FD tests pass with clang and GCC and the golden is regenerated for these lines.
fb9bc7d Regenerate the testIO_FDgauge_madmatrix golden (HelAmps_sm.h): the only differences are the two intended ones, the madmatrix namespace and the backend-conditional mgOnGpuVectors.h include.

Validation

  • Against the merge base aa9d133fa, through UMAMI with identical momenta and random numbers (1024 events): g g > t t~, g g > g g g, e+ e- > mu+ mu-, MSSM u u~ > n1 n1, u u~ > u u~ QED^2==0 are bit-identical in ME, per-event selected color and selected helicity (simd_128 mixed; scalar and FPTYPE=d spot-checked).
  • Split orders, FPTYPE=d: u u~ > u u~ QED^2<=4 and u u~ > d d~ g QED^2==2 bit-identical, u u~ > u u~ QED^2==2 to 9e-16; u u~ > t t~ QED^2==2 is analytically zero and noise (~1e-15) on both. In mixed precision the interference differs at the float level (~1e-6 relative), as expected from the float color sum under cancellation. At the RAMBO 1000 GeV point the interference is -5.5828746494657286e-02 against the recorded -5.5828746494657258e-02. test_standalone_split_orders_interference passes (it cannot on the current branch).
  • Unit suite: 1872 tests, same two reds as on the unmodified backend_seperation_PR (scipy missing; an order-dependent MadLoop IOTest that passes alone).
  • GPU: not run on hardware. No CUDA/HIP toolchain was available. Every backend/gpu translation unit was syntax-checked on the host against minimal CUDA/cuBLAS/thrust stubs, differentially: the merge base's GPU code is clean under the same stubs, backend_seperation_PR shows the two compile errors above, this branch is clean (and a split-order process hits the new static_assert). The stride and atomicAdd fixes are device semantics that check cannot see, so please build and run the cuda backend before merging.

Also noted, not fixed here

  • TREX still cannot produce output, for reasons already present on main: edit_rwgt_header looks up PLUGIN_export_cpp on model_handling (trex.py defines it in its own namespace), and librex.so does not compile with Apple clang (Rex.h:1609). Candidates for a separate issue.
  • Correction to the review comment on Backend separation 2 #166: it said 22 of the 30 cpu/simd file pairs were byte-identical. The actual count was 15 of 30. The recommendation does not change.

🤖 Generated with Claude Code

oliviermattelaer and others added 18 commits September 18, 2026 13:46
… setup

The backend split moved `constexpr int nMF` out of
process_function_definitions.inc into madmatrix::ProcessTables, which
CPPProcess.cc did not include, while the generated initProc still sizes
tIPF_partner1/partner2/value as nMF * nIPF. Every process with merged
flavor couplings (nIPF > 0, e.g. MSSM_SLHA2 u u~ > n1 n1) failed with
"use of undeclared identifier 'nMF'".

Include ProcessTables.h and pull nMF in next to `using namespace
ProcessData`, where nIPF already comes from, so the generated text is
unchanged. u u~ > n1 n1 now builds on scalar and simd_128 and reproduces
the pre-split MeanMatrixElemValue (0.000570885 +- 6.9555e-06).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The backend split hard-coded the color choice in calculate_jamps as
`jamp2_sv[ncolor * iParity + icol] += cxabs2( jamp_sv[icol] )` for
icol < ncolor. Before, it went through the per-process jampflow_lines /
jamp_flow_col holes of process_matrix.inc, which set_color_flow_lines_cpp
still computes but which nothing emitted any more.

For a process whose color sum runs on the (n-2)! DDM basis the two
differ: g g > g g g has ncolor = 6 but picks among ncolor_flow = 24 trace
flows, rebuilt from the DDM jamps through the Kleiss-Kuijf relations.
The split filled 6 slots with the wrong amplitudes and left 18 at zero,
so the selected color (the LHE color flow) was wrong while the matrix
element stayed bit-identical -- which is why an ME-only comparison could
not see it. Same 1024 events, simd_128: 24 distinct flows selected on
the merge base, 12 on the split, one of them 568 times.

Write those lines to a generated ColorFlows.inc, #included by
backend/<variant>/SigmaKin.cc right after EvaluateDiagrams.inc in the
same scope, and have it expose jampflow_sv; the color choice loops over
ncolor_flow again (cpu, simd and gpu).

Checked through UMAMI on identical momenta and random numbers: g g > g g g
(scalar, simd_128 mixed and FPTYPE=d), g g > t t~ and e+ e- > mu+ mu-
reproduce the merge base's per-event color and helicity sequences and
matrix elements exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… path

Four regressions against the merge base in backend/gpu, all restored to
the merge base's own lines:

* DeviceAccessJamp2 lost kernelAccessIcolConst, which the color choice
  in sigmaKin still calls (compile error).
* MatrixElementKernels.cc lost #include "CPPProcess.h", while the device
  kernel still sizes its color-choice buffer from CPPProcess::ncolor_flow
  (compile error).
* calculate_jamps wrote its jamps with nhel = 1. allJamps is the
  [2][ncolor][nGoodHel][nevt] super-buffer, where nhel is the color
  stride, and color_sum_kernel reads it back with nGoodHel: for ncolor > 1
  and more than one good helicity writer and reader address different
  slots, and helicities overwrite each other. Write with dcNGoodHel again
  (it is 1 during the good-helicity scan, nGoodHel in sigmaKin).
* The color-choice accumulation into colAllJamp2s lost its atomicAdd.
  That buffer has no helicity dimension and the good helicities run
  concurrently (one stream each, or blockIdx.y in the async path), so a
  plain += races.

No CUDA/HIP toolchain was available: the device semantics (the last two
items) are untested here. What was checked is a host -fsyntax-only of
every gpu translation unit against minimal CUDA/cuBLAS/thrust stubs, run
differentially: the merge base's GPU code is clean under the same stubs,
the split had the two compile errors above, and with this commit it is
clean again for g g > t t~ and g g > g g g.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r sum

The split made any process whose '^2' constraint leaves more than one
amplitude order abort at generation, on the reasoning that the shared
backend/{cpu,simd}/color_sum.cc cannot carry a process-specific variant.
But those backend sources are compiled once per P* directory, against
that directory's generated ProcessData.h / ColorData.h, so a compile-time
switch is enough -- the same pattern as ColorMatrixData::shouldUseBlas.

* ProcessData.h always defines nampso, njampso = ncolor * nampso and
  nsqampso (1, ncolor, 1 without split orders); CPPProcess's
  split_order_constants now alias them.
* ColorData.h carries the sqSoIndex / chosenSqso pairing tables (the
  trivial 1x1 ones when unsplit).
* backend/{cpu,simd}/color_sum.cc gain color_sum_cpu_splitorders, the
  pair loop of the former color_sum_splitorders.cc template (ALL ordered
  pairs, never a triangle), selected by `if constexpr( nampso > 1 )`, so
  the unsplit path's source is untouched. That template is removed.
* The calculate_jamps / sigmaKin jamp buffers are sized njampso again,
  where the merge base used the jamp_ncolor hole; the BLAS buffers keep
  ncolor behind a static_assert (cpp_blas_wanted is already false when
  split).
* backend/gpu/SigmaKin.cc static_asserts nampso == 1, the replacement
  for the template's #error under MGONGPUCPP_GPUIMPL.

Validation against the merge base on identical momenta and random
numbers (1024 events through UMAMI): unsplit processes (g g > t t~,
g g > g g g, e+ e- > mu+ mu-, MSSM u u~ > n1 n1, u u~ > u u~ QED^2==0)
stay bit-identical; with FPTYPE=d, u u~ > u u~ QED^2<=4 and
u u~ > d d~ g QED^2==2 are bit-identical and u u~ > u u~ QED^2==2 agrees to
9e-16, same per-event color and helicity choices, on simd_128 and
scalar. At the RAMBO 1000 GeV point the interference is
-5.5828746494657286e-02 against the recorded -5.5828746494657258e-02.
test_standalone_split_orders_interference passes again. The host (clang) stub
syntax check of the GPU build refuses a split process with the new
static_assert message and stays clean for unsplit ones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TREX_ProcessExporter.generate_subprocess_directory called
self._link_backend_dirs_in_P(matrix_element), which is defined nowhere,
so `output standalone_trex` raised AttributeError on the first
subprocess. TREX deliberately stays on the old layout ("for TREX for now
no /backend"), and one TREX P* directory builds both the C++ and the
CUDA library, so there is no single backend variant to link anyway.

Note this is not the first thing that stops TREX: on main already,
edit_rwgt_header looks up PLUGIN_export_cpp on model_handling (trex.py
defines it in its own namespace), and librex.so does not compile with
Apple clang (Rex.h:1609 builds std::bad_any_cast from a string). Both
are pre-existing and untouched here. Checked by driving
`output standalone_trex` for g g > t t~ through a PYTHONPATH plugin copy
with the first of those patched out locally: before this commit it
stops with this AttributeError, after it the output is written without
a traceback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…_include

CPPProcess.h, Parameters.h and HelAmps_<model>.h decided "am I the simd
backend?" with #if __has_include("mgOnGpuVectors.h"). That asks whether
some file of that name is reachable on the include path, and -I$(SRC) is
on it: a stray src/mgOnGpuVectors.h -- TREX copies one there, and an
older output can leave one behind -- silently moves a scalar or GPU
build onto the SIMD path. Reproduced by dropping the simd header into
src/ of a g g > t t~ output: BACKEND=scalar then fails with
"redefinition of 'neppV'".

backend/simd/mgOnGpuConfig.h, and the legacy single-layout
mgOnGpuConfig.h TREX still uses (which always ships the header), now
define MGONGPU_HAS_VECTORS_H, and the three shared headers test that.
The stray-header case builds and matches again; g g > t t~ (scalar and
simd_128) and MSSM u u~ > n1 n1 are bit-identical to the merge base, and
the GPU stub syntax check stays clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ards

Two of the mixed-precision guards in backend/simd/color_sum.cc
(deltaMEs_next, and the store into the second neppV page) had dropped
`defined MGONGPU_CPPSIMD`, while nParityCS in the same file and nParity
in SigmaKin.cc keep it. The three have to agree: without the SIMD term
color_sum_cpu would write MEs at ievt0 + neppV for a page the caller
neither zeroed nor owns.

Not reachable today -- mgOnGpuVectors.h does not even compile without
MGONGPU_CPPSIMD (`const int neppV = MGONGPU_CPPSIMD`) -- so this only
restores the merge base's guards. color_sum_cpu is now textually the
same in backend/cpu and backend/simd. simd_128 output is bit-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e dirs

The backend/{cpu,simd,gpu} and src/rambo from_template lists were built
from `sorted(os.listdir(...))`, i.e. whatever happened to be in those
directories when madmatrix.output was imported. Reproduced: a
backend/cpu/.DS_Store (which macOS Finder creates routinely) was copied
into every generated output.

template_sources() keeps only regular, non-hidden .h/.cc files, which is
everything these directories are meant to hold: the copied file sets are
unchanged (30/31/32/9), while .DS_Store and an editor backup umami.cc~
placed in the template dirs no longer reach the output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sigmaKin and everything it calls moved to backend/<variant>/SigmaKin.cc,
but get_sigmaKin_lines still read process_sigmaKin_function.inc and
%-substituted it for every subprocess, and the result was thrown away:
process_function_definitions.inc no longer has a hole for it. The split
nevertheless kept editing that template in step with the three
SigmaKin.cc copies, which made it a fourth copy of the same code that no
build compiles, and the obvious place for the next fix to land in three
copies out of four.

get_sigmaKin_lines now returns nothing (it keeps the base class's
multichannel guard); process_sigmaKin_function.inc and
color_sum_blas_loop.inc, reachable only through it, are removed, with
their class attributes. The generated sources are byte-identical for
g g > t t~ and MSSM u u~ > n1 n1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
get_all_sigmaKin_lines no longer calls get_matrix_single_process, and the
per-process color_sum.cc it used to write is gone, which leaves without
any caller:

* OneProcessExporterMadMatrix.get_matrix_single_process, and
  jamp_ncolor(), which only it (and the sigmaKin rendering dropped in the
  previous commit) used -- the jamp buffers are sized by
  ProcessData::njampso now;
* process_matrix.inc (single_process_template), whose color-choice and
  jamp-copy lines live in backend/<variant>/SigmaKin.cc and
  ColorFlows.inc now;
* color_sum_blas.inc (blas_color_sum_template), whose body was copied
  into backend/{cpu,simd}/color_sum.cc and would only have drifted from
  it.

Comments that named them are updated. Generated sources are
byte-identical for g g > t t~ and MSSM u u~ > n1 n1, and
test_standalone_split_orders_interference passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ckends

The SIMD helpers of MemoryAccessVectors.h exist only in backend/simd.
backend/cpu/ and backend/gpu/ each kept a MemoryAccessVectors.h holding
nothing but #include "mgOnGpuConfig.h" / "mgOnGpuCxtypes.h" (and, for
cpu, an empty namespace), copied into every output only so that five
MemoryAccess*.h per backend could include it.

Those five now include mgOnGpuCxtypes.h directly -- exactly what the
stub provided beyond the mgOnGpuConfig.h they already include -- and the
two stubs are removed. Scalar builds are bit-identical (g g > t t~,
MSSM u u~ > n1 n1, u u~ > u u~ QED^2==2) and the GPU stub syntax check
stays clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ackend/common

Nine files were byte-identical in backend/cpu, backend/simd and
backend/gpu -- constexpr_math.h, CrossSectionKernels.h, EventStatistics.h
and the MemoryAccess{Amplitudes,Denominators,Numerators,RandomNumbers,
Wavefunctions,Weights}.h -- i.e. three copies of ~1300 lines kept in step
by hand. They move to backend/common/, which output.py copies like the
variants and madmatrix.mk / madmatrix_src.mk search after
backend/<variant>/ (and list as prerequisites, so touching one rebuilds).

No file name is in both backend/common and a variant directory, on
purpose: a quoted #include resolves in the including file's own
directory first, so a common header would pick up a common "host"
version of a file even in a build whose variant overrides it. For the
same reason the six files shared only by cpu and simd (CrossSectionKernels.cc,
MatrixElementKernels.{cc,h}, MemoryAccessHelpers.h, MemoryBuffers.h,
mgOnGpuFptypes.h) stay where they are.

Outputs are bit-identical (g g > t t~ scalar and simd_128, g g > g g g,
MSSM u u~ > n1 n1, u u~ > u u~ QED^2==2), the GPU stub syntax check is
clean, and test_standalone_split_orders_interference passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* output.py: the backend/ copy is no longer "step 1, not yet wired into
  the build"; and the backend sources, while read from the single
  top-level backend/<variant>/ and backend/common/ dirs, are compiled
  once per P* directory -- necessarily, since they include that
  directory's generated ProcessData.h / ColorData.h / ProcessTables.h.
  The old wording suggested one shared compilation, which is the
  misreading that made split orders look impossible to support.
* model_handling.py: calculate_jamps lives in backend/*/SigmaKin.cc,
  not a CalculateJamps.cc that never existed, and EvaluateDiagrams.inc
  is #included by SigmaKin.cc, not written for CPPProcess.cc; a
  duplicated comment line on edit_colordata is dropped.
* process_function_definitions.inc: SigmaKin.cc #includes
  ColorFlows.inc too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The backend split moved fpeEnable(), the runtime SIGFPE-trap switch with
its <cfenv> dependency and one-shot static latch, out of the generated
CPPProcess.cc into constexpr_math.h -- a header otherwise about
compile-time math, which every translation unit that wants constexpr
sqrt/pow includes, and where nobody looking for where FPE traps get
enabled would think to look.

It now lives in backend/common/fpe.h, included by CPPProcess.cc, its only
caller (under MGONGPUCPP_DEBUG); constexpr_math.h loses <cfenv>.
MatrixElementKernels.cc's own fetestexcept use already includes <cfenv>
itself. CPPProcess.cc compiles with -DMGONGPUCPP_DEBUG on cpu and simd,
the default build is bit-identical, and the GPU stub check stays clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Moving helcolDenominators from process_function_definitions.inc into
ProcessData.h dropped its "assume nprocesses == 1 (#272 and #343)"
comment, the only in-tree record that the size-1 array is an assumption:
den_factors is joined over every matrix element of the P* directory, and
ProcessData.h computes nproc > 1 two lines above. Put the caveat back and
point at MadGraph7 issue #168, which tracks sizing it properly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The setters the backend split added to backend/{cpu,simd}/SigmaKin.cc,
and backend/gpu/MemoryBuffers.h, call memcpy without including
<cstring>. With clang/libc++ (macOS) it arrives transitively, so every
local build passed; with GCC/libstdc++ it does not, and every madmatrix
build on the Linux CI runners stopped at

  backend/cpu/SigmaKin.cc:71:5: error: 'memcpy' was not declared in this scope

That single error is behind 33 of the 34 failing checks on #166 and
#179: every mg7 run (no info.json, events, gridpack, MadSpin, Pythia8,
Rivet, reweight or systematics output), the standalone builds (no
matrix element) and the check command's missing "MG7 SA" column.

Reproduced and checked locally with g++-16: before, the CI error and
unittest_31's failure reproduce; after, both CPU backends build, and
test_check_language_function_epem_aa, test_standalone_cpp,
test_standalone_split_orders_interference and test_e_e_collision_mg7
pass with CXX=g++-16 (the mg7 build demonstrably compiling with it,
40.23(42) pb against the 40.3 pb reference).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
testIO_FDgauge_madmatrix records the generated src/HelAmps_sm.h, which
the backend split changed in two intended ways: the single namespace
madmatrix replaces the mg5amcGpu/mg5amcCpu pair, and mgOnGpuVectors.h is
included only by the simd backend (on MGONGPU_HAS_VECTORS_H, falling back
to mgOnGpuCxtypes.h). Those are the only two differences; regenerated
with `-U testIO_FDgauge_madmatrix -F 10`, and the test passes. No other
golden under IOTestsComparison records madmatrix output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
In the FD gauge HelAmps_<model>.h carries define_gauge_dir and
calculate_propagator_factor, pasted from helas_fd.h. Unlike the helas
routines around them they are plain functions, not templates, and were
marked INLINE -- which is empty unless HELINL=1 -- so with the default
build they are ordinary external definitions in a header.

That was harmless while only CPPProcess.cc included HelAmps. With the
backend split CPPProcess.h includes it and SigmaKin.cc (which now makes
the helas calls) and umami.cc include CPPProcess.h, so the process
library failed to link:

  duplicate symbol 'madmatrix::define_gauge_dir(...)' in umami.o, CPPProcess.o, SigmaKin.o

which is acceptancetest_uncovered_merged
(test_standalone_cpp_fd_output_consistency) on #179, and was hidden
behind the memcpy compile error on #166.

A non-template function defined in a header must be inline whatever
HELINL says, so the two are now `inline` (ALWAYS_INLINE kept on the
declarations). The FD-gauge u/d-flavour q q > h q q q q~ QCD=0 process
now builds and prints all 54 matrix elements on scalar and simd_128,
with HELINL=1, and with g++-16; test_standalone_cpp_fd_output_consistency,
test_madmatrix_fd_vs_fortran and test_madmatrix_fd_simd_lanes pass with
clang and GCC; the GPU stub check is clean. The FD-gauge madmatrix golden
records helas_fd.h and is regenerated for exactly these lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant