Skip to content

Commit 765cf9f

Browse files
sawenzelclaude
andcommitted
Add a test that multithreaded o2-sim gives the same output for 1 and N threads
This adds the script o2-sim-mt-equality.sh and a ctest that runs it. - The script runs the same seeded workload with --isMT -j 1 and -j N and compares the kinematics and hits per event and branch. - It reports the first difference and exits with 1 when the outputs differ. - A sequential run with the same seed is the reference for the number of hits, so that a detector clone without hits fails. - The ctest replaces the disabled o2sim_G4_mt test and is registered only for Geant4 built with multithreading. Usage: o2-sim-mt-equality.sh -n 5 -j 4 --field 0 HALL MAG DIPO COMP PIPE ABSO SHIL TPC https://its.cern.ch/jira/browse/O2-7305 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent abe2f2a commit 765cf9f

2 files changed

Lines changed: 157 additions & 20 deletions

File tree

‎run/CMakeLists.txt‎

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -230,26 +230,21 @@ set_tests_properties(o2sim_G4
230230
G4)
231231
set_property(TEST o2sim_G4 APPEND PROPERTY ENVIRONMENT ${G4ENV})
232232

233-
# # note that the MT is currently only supported in the non FairMQ version
234-
# # switched off
235-
# o2_add_test_command(NAME o2sim_G4_mt
236-
# WORKING_DIRECTORY ${SIMTESTDIR}
237-
# TIMEOUT 400
238-
# COMMAND $<TARGET_FILE:${o2simSerialExecutable}>
239-
# COMMAND_LINE_ARGS -n
240-
# 1
241-
# -e
242-
# TGeant4
243-
# --isMT
244-
# on
245-
# -o
246-
# o2simG4MT
247-
# ENVIRONMENT "${SIMENV}"
248-
# LABELS "g4;sim;long")
249-
#set_tests_properties(o2sim_G4_mt
250-
# PROPERTIES PASS_REGULAR_EXPRESSION
251-
# "Macro finished succesfully")
252-
#set_property(TEST o2sim_G4_mt APPEND PROPERTY ENVIRONMENT ${G4ENV})
233+
# multithreaded Geant4 must give the same output for 1 and 2 transport threads
234+
if(Geant4_multithreaded_FOUND)
235+
o2_add_test_command(NAME o2sim_G4_mt_equality
236+
WORKING_DIRECTORY ${SIMTESTDIR}
237+
TIMEOUT 600
238+
COMMAND ${CMAKE_SOURCE_DIR}/run/o2-sim-mt-equality.sh
239+
COMMAND_LINE_ARGS --workdir
240+
${SIMTESTDIR}/mt_equality
241+
ENVIRONMENT "${SIMENV}"
242+
LABELS "g4;sim;long")
243+
set_tests_properties(o2sim_G4_mt_equality
244+
PROPERTIES PASS_REGULAR_EXPRESSION
245+
"EQUALITY TEST PASSED")
246+
set_property(TEST o2sim_G4_mt_equality APPEND PROPERTY ENVIRONMENT ${G4ENV})
247+
endif()
253248

254249

255250
o2_add_test(CheckStackG4
@@ -360,6 +355,7 @@ set_tests_properties(o2sim_G4_checklogs
360355
PROPERTIES FIXTURES_REQUIRED G4)
361356
endif()
362357

358+
install(PROGRAMS o2-sim-mt-equality.sh DESTINATION ${CMAKE_INSTALL_BINDIR})
363359
install(FILES o2-sim-client.py PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ DESTINATION ${CMAKE_INSTALL_BINDIR})
364360
install(DIRECTORY SimExamples/
365361
DESTINATION examples

‎run/o2-sim-mt-equality.sh‎

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env bash
2+
# Checks that multithreaded Geant4 in o2-sim gives the same output for 1 and N transport threads.
3+
#
4+
# Usage: o2-sim-mt-equality.sh [-n events] [-j threads] [-g generator] [--field kG] [--seed seed]
5+
# [--seeding track|subevent] [--tolerance fraction] [--workdir dir] [module ...]
6+
#
7+
# Runs the same seeded workload with --isMT -j 1 and --isMT -j N and compares the kinematics and hit files
8+
# event by event and branch by branch. A sequential run with the same seed is the reference for the number of
9+
# hits of each detector, which must not be zero and must agree within the tolerance (default 0.2). Exits with
10+
# 1 at the first difference. Without modules, the passive geometry with ITS is used.
11+
12+
nev=2
13+
nthreads=2
14+
gen=pythia8pp
15+
field=-5
16+
seed=4711
17+
seeding=track
18+
tolerance=0.2
19+
workdir=
20+
modules=()
21+
while [ $# -gt 0 ]; do
22+
case $1 in
23+
-n) nev=$2; shift 2 ;;
24+
-j) nthreads=$2; shift 2 ;;
25+
-g) gen=$2; shift 2 ;;
26+
--field) field=$2; shift 2 ;;
27+
--seed) seed=$2; shift 2 ;;
28+
--seeding) seeding=$2; shift 2 ;;
29+
--tolerance) tolerance=$2; shift 2 ;;
30+
--workdir) workdir=$2; shift 2 ;;
31+
-h|--help) sed -n '2,10p' $0; exit 0 ;;
32+
*) modules+=("$1"); shift ;;
33+
esac
34+
done
35+
[ ${#modules[@]} -eq 0 ] && modules=(HALL MAG DIPO COMP PIPE ABSO SHIL ITS)
36+
case $seeding in
37+
track) seedconf="SimCutParams.trackSeed=true" ;;
38+
subevent) seedconf="SimCutParams.trackSeed=false" ;;
39+
*) echo "unknown seeding '$seeding', use track or subevent"; exit 1 ;;
40+
esac
41+
o2sim=o2-sim
42+
[ -x "$O2_ROOT/bin/o2-sim" ] && o2sim=$O2_ROOT/bin/o2-sim
43+
[ -z "$workdir" ] && workdir=$(mktemp -d ${TMPDIR:-/tmp}/o2sim-mt-equality-XXXXXX)
44+
mkdir -p $workdir && cd $workdir || exit 1
45+
46+
# content hash and number of entries per event and branch of the o2sim tree, independent of the basket layout
47+
cat > hashtree.C <<'EOF'
48+
void hashtree(const char* fname)
49+
{
50+
TFile f(fname);
51+
auto t = (TTree*)f.Get("o2sim");
52+
if (!t) {
53+
return;
54+
}
55+
std::vector<std::tuple<TClass*, void*, std::string>> objs;
56+
objs.reserve(t->GetListOfBranches()->GetEntries()); // the branch addresses point into objs
57+
for (auto b : *t->GetListOfBranches()) {
58+
auto br = (TBranch*)b;
59+
TClass* cl = nullptr;
60+
EDataType dt;
61+
br->GetExpectedType(cl, dt);
62+
if (!cl) {
63+
continue;
64+
}
65+
objs.emplace_back(cl, cl->New(), br->GetName());
66+
br->SetAddress(&std::get<1>(objs.back()));
67+
}
68+
for (Long64_t i = 0; i < t->GetEntries(); ++i) {
69+
t->GetEntry(i);
70+
for (auto& [cl, obj, name] : objs) {
71+
TBufferFile buf(TBuffer::kWrite);
72+
buf.WriteObjectAny(obj, cl);
73+
TMD5 md5;
74+
md5.Update((UChar_t*)buf.Buffer(), buf.Length());
75+
md5.Final();
76+
printf("HASH event %lld branch %s %s\n", i, name.c_str(), md5.AsString());
77+
if (auto proxy = cl->GetCollectionProxy()) {
78+
TVirtualCollectionProxy::TPushPop helper(proxy, obj);
79+
printf("COUNT event %lld branch %s %u\n", i, name.c_str(), proxy->Size());
80+
}
81+
}
82+
}
83+
}
84+
EOF
85+
86+
run() { # <directory> <o2-sim mode options>
87+
local dir=$1
88+
shift
89+
mkdir -p $dir && cd $dir
90+
$o2sim "$@" -n $nev -g $gen -e TGeant4 --seed $seed --field $field -m ${modules[@]} --configKeyValues "$seedconf" > sim.log 2>&1
91+
local rc=$?
92+
if [ $rc -ne 0 ] || ! grep -q "SIMULATION RETURNED SUCCESFULLY" sim.log; then
93+
echo "o2-sim in $dir failed (exit code $rc), see $workdir/$dir"
94+
exit 1
95+
fi
96+
for f in o2sim_Kine.root o2sim_Hits*.root; do
97+
[ -f $f ] || continue
98+
root -l -b -q "../hashtree.C(\"$f\")" 2>/dev/null > $f.out
99+
grep "^HASH" $f.out | sed "s/^HASH/$f/" > $f.hashes
100+
grep "^COUNT" $f.out | awk '{n += $NF} END {print n + 0}' > $f.count
101+
done
102+
cd ..
103+
}
104+
105+
echo "o2-sim MT equality: ${modules[*]}, $nev events of $gen, field $field kG, $seeding seeding, 1 against $nthreads threads, in $workdir"
106+
run seq -j 1
107+
run mt1 --isMT 1 -j 1
108+
run mt$nthreads --isMT 1 -j $nthreads
109+
status=0
110+
for h in mt1/*.hashes; do
111+
f=$(basename $h)
112+
file=${f%.hashes}
113+
if [ ! -s $h ]; then
114+
echo "$file: no entries"
115+
status=1
116+
elif cmp -s $h mt$nthreads/$f; then
117+
echo "$file: identical ($(wc -l < $h) event-branch hashes)"
118+
else
119+
echo "$file: DIFFERS, first at $(diff $h mt$nthreads/$f | grep -m1 '^<' | cut -d' ' -f3-6)"
120+
status=1
121+
fi
122+
done
123+
[ $(ls mt$nthreads/*.hashes | wc -l) -eq $(ls mt1/*.hashes | wc -l) ] || { echo "the runs wrote different output files"; status=1; }
124+
# the sequential run as reference for the number of hits, which catches a detector clone that makes no hits
125+
for c in seq/o2sim_Hits*.root.count; do
126+
[ -f "$c" ] || continue
127+
file=$(basename ${c%.count})
128+
nseq=$(cat $c)
129+
nmt=$(cat mt1/$file.count 2>/dev/null || echo 0)
130+
if [ "$nmt" -eq 0 ]; then
131+
echo "$file: no hits in MT (sequential: $nseq)"
132+
status=1
133+
elif ! awk -v a=$nseq -v b=$nmt -v t=$tolerance 'BEGIN {exit !(a > 0 && (a - b < 0 ? b - a : a - b) <= t * a)}'; then
134+
echo "$file: $nmt hits in MT against $nseq in sequential mode, beyond the tolerance $tolerance"
135+
status=1
136+
else
137+
echo "$file: $nmt hits in MT, $nseq in sequential mode"
138+
fi
139+
done
140+
[ $status -eq 0 ] && echo "EQUALITY TEST PASSED" || echo "EQUALITY TEST FAILED"
141+
exit $status

0 commit comments

Comments
 (0)