Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions .github/workflows/run_madtrex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/usr/bin/env python3
import os
import sys
import subprocess
from pathlib import Path
import csv

ALLOWED_PROCESSES = [ "ee_mumu", "gg_tt", "gg_tt01g", "gg_ttg", "gg_ttgg", "gg_ttggg", "gq_ttq", "heft_gg_bb", "nobm_pp_ttW", "pp_tt012j", "smeft_gg_tttt", "susy_gg_t1t1", "susy_gg_tt" ]
MADGRAPH_CLI = Path.cwd() / ".." / ".." / "MG5aMC" / "mg5amcnlo" / "bin" / "mg5_aMC"

PROCESSES_NON_TRIVIAL_MODELS = {
"heft_gg_bb": "heft",
"smeft_gg_tttt": "SMEFTsim_topU3l_MwScheme_UFO-massless",
"susy_gg_t1t1": "MSSM_SLHA2",
"susy_gg_tt": "MSSM_SLHA2",
}

def generate_dat_content(process_dir: Path, rwgt_card_path: Path, process_name: str) -> str:
run_card = f"launch {process_dir}\n"
run_card += "reweight=madtrex\n"
run_card += "set nevents 10000\n"
run_card += "set iseed 489\n"
run_card += f"{rwgt_card_path}\n"
# if the model of this process is not trivial, import it preventively
# so that if it is still Python 2, it will be converted before the
# reweighting procedure takes place
if process_name in PROCESSES_NON_TRIVIAL_MODELS:
model = PROCESSES_NON_TRIVIAL_MODELS[process_name]
run_card = f"set auto_convert_model True\nimport model {model}\n" + run_card
return run_card

def is_executable(path: Path) -> bool:
return path.is_file() and os.access(path, os.X_OK)

def write_rwgt_card(path: Path) -> None:
if path.exists():
return
content = """launch\nset sminputs 1 scan:[j for j in range(100,200,10)]\n"""
path.write_text(content, encoding="utf-8")


def load_csv(path):
with open(path, newline="") as f:
reader = csv.DictReader(f, fieldnames=["RWGT", "VALUE", "ERROR"])
for row in reader:
yield float(row["VALUE"]), float(row["ERROR"])

def dump_logs(stdout_log, stderr_log):
print("Dumping run logs...")
print("==== STDOUT ====")
with open(stdout_log, "r") as file:
print(file.read())
print("\n\n==== STDERR ====")
with open(stderr_log, "r") as file:
print(file.read())
print("================")

def main() -> int:
# Name of the directory of the process to test
process_dir = Path(sys.argv[1])
# Label for the process (must be in the allowed list)
process = process_dir.name.replace(".mad", "")

# Treat current working directory as HOME
HOME = Path.cwd()

process_path = (HOME / process_dir).resolve()
if not process_path.exists():
print(f"ERROR: Process {process} not found at: {process_path}", file=sys.stderr)
return 1

if process not in ALLOWED_PROCESSES:
print(
f"ERROR: PROCESS '{process}' is not in the allowed list.\n"
f"Allowed: {sorted(ALLOWED_PROCESSES)}",
file=sys.stderr,
)
return 1

# Check that baseline rwgt.csv exists
baseline_csv = HOME / "CODEGEN" / "PLUGIN" / "CUDACPP_SA_OUTPUT" / "test" / "MadtRex_baseline" / f"{process}_rwgt.csv"
if not baseline_csv.exists():
print(
f"ERROR: Baseline rwgt.csv not found at:\n {baseline_csv}\n"
f"Ensure the baseline file exists before running.",
file=sys.stderr,
)
return 1

# Write rwgt_card.dat if not exists
rwgt_card_path = HOME / "rwgt_card.dat"
try:
write_rwgt_card(rwgt_card_path)
except Exception as e:
print(f"ERROR: Failed to write rwgt_card.dat: {e}", file=sys.stderr)
return 1

# Write PROCESS.dat to HOME
dat_path = HOME / f"{process}.dat"
try:
dat_content = generate_dat_content(process_dir, rwgt_card_path, process)
dat_path.write_text(dat_content, encoding="utf-8")
except Exception as e:
print(f"ERROR: Failed to write {dat_path}: {e}", file=sys.stderr)
return 1

# Run mg5_aMC with PROCESS.dat as argument, wait for completion
LOGS = HOME / "logs"
LOGS.mkdir(exist_ok=True)
stdout_log = LOGS / f"mg5_{process}.stdout.log"
stderr_log = LOGS / f"mg5_{process}.stderr.log"
print(f"Launching: {MADGRAPH_CLI} {dat_path}")
try:
with stdout_log.open("wb") as out, stderr_log.open("wb") as err:
result = subprocess.run(
[str(MADGRAPH_CLI), str(dat_path)],
cwd=str(HOME),
stdout=out,
stderr=err,
check=False,
)
if result.returncode != 0:
print(
f"ERROR: mg5_aMC exited with code {result.returncode}. "
f"See logs:\n stdout: {stdout_log}\n stderr: {stderr_log}",
file=sys.stderr,
)
dump_logs(stdout_log, stderr_log)
return result.returncode
else:
print(f"mg5_aMC finished. Logs:\n stdout: {stdout_log}\n stderr: {stderr_log}")
except FileNotFoundError:
print(f"ERROR: Failed to launch {MADGRAPH_CLI}", file=sys.stderr)
return 1
except Exception as e:
print(f"ERROR: mg5_aMC run failed: {e}", file=sys.stderr)
return 1

# Remove process.dat
dat_path.unlink(missing_ok=True)

# Move rwgt_results.csv → HOME/baseline/PROCESS_rwgt.csv
madtrex_csv = process_path / "rw_me" / "SubProcesses" / "rwgt_results.csv"
if not madtrex_csv.exists():
print(
f"ERROR: Expected results not found at:\n {madtrex_csv}\n"
f"Ensure the run produced rwgt_results.csv.",
file=sys.stderr,
)
dump_logs(stdout_log, stderr_log)
return 1

all_ok = True
for i, ((v_base, _), (v_mad, _)) in enumerate(zip(load_csv(baseline_csv), load_csv(madtrex_csv)), start=1):
diff = abs(v_base - v_mad)
tol = 0.05 * v_mad

if diff >= tol:
print(f"Error: Row {i}: |{v_base} - {v_mad}| = {diff} >= {tol}")
all_ok = False

if not all_ok:
print(f"Some checks failed for process {process}.", file=sys.stderr)
sys.exit(1)
print(f"All checks passed for process {process}.")

return 0

if __name__ == "__main__":
sys.exit(main())
99 changes: 97 additions & 2 deletions .github/workflows/testsuite_oneprocess.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (C) 2020-2024 CERN and UCLouvain.
# Copyright (C) 2020-2025 CERN and UCLouvain.
# Licensed under the GNU Lesser General Public License (version 3 or later).
# Created by: A. Valassi (Oct 2023) for the MG5aMC CUDACPP plugin.
# Further modified by: A. Valassi (2023-2024) for the MG5aMC CUDACPP plugin.
# Further modified by: A. Valassi, D. Massaro (2023-2025) for the MG5aMC CUDACPP plugin.

#----------------------------------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -279,4 +279,99 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}

madtrex:
if: ${{ endsWith(inputs.process, '.mad') }} # Reweighting test to do only in case of madevent
runs-on: ubuntu-22.04 # Python 3.12 not yet supported for reweighting
needs: codegen

steps:
- uses: actions/checkout@v4
with:
submodules: 'true'

- name: split_prnum
# See https://stackoverflow.com/a/73467112
id: split
run: echo "prnum=PR${GITHUB_REF_NAME%%/*}" >> $GITHUB_OUTPUT

- name: HELLO_MADTREX
run: |
echo "HELLO_MADTREX ${{ inputs.process }} $(date)"
echo "Workflow run_id is ${{ github.run_id }}"
echo "Workflow ref_name is ${{ github.ref_name }}"
echo "Workflow PR number is ${{ steps.split.outputs.prnum }}"
echo "Current directory is $(pwd)"
echo "Current git commit is $(git log --oneline -n1 | cut -d' ' -f1)"
gh extension install actions/gh-actions-cache
REPO=${{ github.repository }}
echo "List codegencache keys for this run_id (start)"
gh actions-cache list -R $REPO --sort created-at --order asc --key codegencache-${{ runner.os }}-${{ inputs.process }}-${{ github.run_id }}
echo "List codegencache keys for this run_id (end)"
env:
GH_TOKEN: ${{ github.token }}

- name: restore_codegen_cache
id: codegen-cache-restore
uses: actions/cache/restore@v4
with:
path: |
epochX/cudacpp/${{ inputs.process }}
key: codegencache-${{ runner.os }}-${{ inputs.process }}-${{ github.run_id }}
restore-keys: |
codegencache-${{ runner.os }}-${{ inputs.process }}-${{ github.run_id }}

- name: symlink_plugin
run: |
cd MG5aMC/mg5amcnlo/PLUGIN
ln -s ../../MG5aMC_PLUGIN/CUDACPP_OUTPUT ./

- name: check_f2py
run: |
# check if f2py is installed, if not, install it
if type -P "f2py" &>/dev/null; then
echo "f2py is installed"
else
echo "f2py not found"
echo "check if numpy.f2py is present"
if python3 -m numpy.f2py -v &>/dev/null; then
echo "numpy.f2py is installed correctly, creating wrapper"
else
echo "numpy.f2py not found, it will be installed"
sudo apt update && sudo apt install -y python3-numpy
echo "Installation done, now create wrapper"
fi
mkdir -p "$HOME/.local/bin"
echo '#!/usr/bin/env bash' > "$HOME/.local/bin/f2py"
echo 'exec python3 -m numpy.f2py "$@"' >> "$HOME/.local/bin/f2py"
chmod +x "$HOME/.local/bin/f2py"
echo "$HOME/.local/bin" >> "$GITHUB_PATH" # this updates path for the subsequent steps
export PATH="$HOME/.local/bin:$PATH" # this for the current step
fi

# Validate that "f2py" actually executes (not just resolves)
if ! f2py -v &>/dev/null; then
echo "f2py resolves but does not execute correctly."
echo "Cannot run reweighting!"
exit 1
fi

- name: madtrex_test
run: |
cd epochX/cudacpp
../../.github/workflows/run_madtrex.py ${{ inputs.process }}

- name: GOODBYE_MADTREX
run: |
echo "GOODBYE_MADTREX ${{ inputs.process }} $(date)"
echo "Workflow run_id is ${{ github.run_id }}"
echo "Workflow ref_name is ${{ github.ref_name }}"
echo "Workflow PR number is ${{ steps.split.outputs.prnum }}"
echo "Current directory is $(pwd)"
echo "Current git commit is $(git log --oneline -n1 | cut -d' ' -f1)"
REPO=${{ github.repository }}
echo "List codegencache keys for this run_id (start)"
gh actions-cache list -R $REPO --sort created-at --order asc --key codegencache-${{ runner.os }}-${{ inputs.process }}-${{ github.run_id }}
echo "List codegencache keys for this run_id (end)"
env:
GH_TOKEN: ${{ github.token }}
#----------------------------------------------------------------------------------------------------------------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 0.192061, 0.000424581
rwgt_2, 0.147728, 0.000254344
rwgt_3, 0.123323, 0.000160641
rwgt_4, 0.107587, 0.000100209
rwgt_5, 0.0966111, 0.000105217
rwgt_6, 0.0885644, 0.000122552
rwgt_7, 0.0824521, 0.000135735
rwgt_8, 0.0776822, 0.000146035
rwgt_9, 0.0738789, 0.000154259
rwgt_10, 0.0707923, 0.000160943
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 844.049, 1.99731
rwgt_2, 844.049, 1.99731
rwgt_3, 844.049, 1.99731
rwgt_4, 844.049, 1.99731
rwgt_5, 844.049, 1.99731
rwgt_6, 844.049, 1.99731
rwgt_7, 844.049, 1.99731
rwgt_8, 844.049, 1.99731
rwgt_9, 844.049, 1.99731
rwgt_10, 844.049, 1.99731
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 440.654, 0.474357
rwgt_2, 440.654, 0.474357
rwgt_3, 440.654, 0.474357
rwgt_4, 440.654, 0.474357
rwgt_5, 440.654, 0.474357
rwgt_6, 440.654, 0.474357
rwgt_7, 440.654, 0.474357
rwgt_8, 440.654, 0.474357
rwgt_9, 440.654, 0.474357
rwgt_10, 440.654, 0.474357
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 412.337, 1.27291
rwgt_2, 412.337, 1.27291
rwgt_3, 412.337, 1.27291
rwgt_4, 412.337, 1.27291
rwgt_5, 412.337, 1.27291
rwgt_6, 412.337, 1.27291
rwgt_7, 412.337, 1.27291
rwgt_8, 412.337, 1.27291
rwgt_9, 412.337, 1.27291
rwgt_10, 412.337, 1.27291
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 250.25, 0.762839
rwgt_2, 250.25, 0.762839
rwgt_3, 250.25, 0.762839
rwgt_4, 250.25, 0.762839
rwgt_5, 250.25, 0.762839
rwgt_6, 250.25, 0.762839
rwgt_7, 250.25, 0.762839
rwgt_8, 250.25, 0.762839
rwgt_9, 250.25, 0.762839
rwgt_10, 250.25, 0.762839
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 123.712, 0.386311
rwgt_2, 123.712, 0.386311
rwgt_3, 123.712, 0.386311
rwgt_4, 123.712, 0.386311
rwgt_5, 123.712, 0.386311
rwgt_6, 123.712, 0.386311
rwgt_7, 123.712, 0.386311
rwgt_8, 123.712, 0.386311
rwgt_9, 123.712, 0.386311
rwgt_10, 123.712, 0.386311
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 70.1922, 0.241705
rwgt_2, 70.1922, 0.241705
rwgt_3, 70.1922, 0.241705
rwgt_4, 70.1922, 0.241705
rwgt_5, 70.1922, 0.241705
rwgt_6, 70.1922, 0.241705
rwgt_7, 70.1922, 0.241705
rwgt_8, 70.1922, 0.241705
rwgt_9, 70.1922, 0.241705
rwgt_10, 70.1922, 0.241705
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 4.11321e+08, 551469
rwgt_2, 4.11321e+08, 551469
rwgt_3, 4.11321e+08, 551469
rwgt_4, 4.11321e+08, 551469
rwgt_5, 4.11321e+08, 551469
rwgt_6, 4.11321e+08, 551469
rwgt_7, 4.11321e+08, 551469
rwgt_8, 4.11321e+08, 551469
rwgt_9, 4.11321e+08, 551469
rwgt_10, 4.11321e+08, 551469
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 0.639355, 0.000526606
rwgt_2, 0.67942, 0.000502079
rwgt_3, 0.705468, 0.000486133
rwgt_4, 0.724627, 0.000474403
rwgt_5, 0.739576, 0.000492311
rwgt_6, 0.751671, 0.000515007
rwgt_7, 0.761708, 0.000533843
rwgt_8, 0.770198, 0.000549775
rwgt_9, 0.777488, 0.000563454
rwgt_10, 0.783824, 0.000575344
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rwgt_1, 1456.15, 3.06487
rwgt_2, 1456.15, 3.06487
rwgt_3, 1456.15, 3.06487
rwgt_4, 1456.15, 3.06487
rwgt_5, 1456.15, 3.06487
rwgt_6, 1456.15, 3.06487
rwgt_7, 1456.15, 3.06487
rwgt_8, 1456.15, 3.06487
rwgt_9, 1456.15, 3.06487
rwgt_10, 1456.15, 3.06487
Loading