Derive a unique per-job seed for minicalosim shard generation
Concurrent job launches in create_root_files.py can start within the same wall-clock second, and minicalosim's default seed falls back to time(NULL) in that case — so two "independent" shards could silently get identical RNG state and produce byte-identical physics. Requires the companion MINICALOSIM_SEED env-var support in the minicalosim repo. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
import zlib
|
||||
from dataclasses import dataclass
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from pathlib import Path
|
||||
@@ -104,6 +105,20 @@ def plan_jobs(
|
||||
return jobs
|
||||
|
||||
|
||||
def job_seed(kind: str, gen: str, job: SimJob) -> int:
|
||||
"""Deterministic RNG seed for one sim job, unique per (kind, gen, detector, config, shard).
|
||||
|
||||
Jobs run concurrently (ThreadPoolExecutor below) and can start within the
|
||||
same wall-clock second; minicalosim's default seed falls back to
|
||||
time(NULL) in that case, so two concurrently-launched jobs can silently
|
||||
get identical RNG state and produce byte-identical physics despite
|
||||
landing in separate shard files. Deriving the seed from the full job
|
||||
identity instead keeps it both unique and reproducible.
|
||||
"""
|
||||
key = f"{kind}|{gen}|{job.detector}|{job.config or ''}|{job.shard_index}"
|
||||
return zlib.crc32(key.encode()) & 0x7FFFFFFF
|
||||
|
||||
|
||||
def run_job(
|
||||
job: SimJob,
|
||||
executable: Path,
|
||||
@@ -124,7 +139,8 @@ def run_job(
|
||||
cmd.append(job.config)
|
||||
cmd.append(str(events_per_file))
|
||||
|
||||
result = subprocess.run(cmd, cwd=workdir, capture_output=True, text=True)
|
||||
env = dict(os.environ, MINICALOSIM_SEED=str(job_seed(kind, gen, job)))
|
||||
result = subprocess.run(cmd, cwd=workdir, capture_output=True, text=True, env=env)
|
||||
|
||||
if result.returncode != 0:
|
||||
return JobResult(
|
||||
@@ -254,7 +270,8 @@ def run_make_root(
|
||||
/ job.detector
|
||||
/ f"shard-{job.shard_index:03d}.root"
|
||||
)
|
||||
print(f" {' '.join(cmd)} -> {dest}")
|
||||
seed = job_seed(kind, gen, job)
|
||||
print(f" MINICALOSIM_SEED={seed} {' '.join(cmd)} -> {dest}")
|
||||
|
||||
if not execute:
|
||||
print("\nDry run only — pass --execute to apply.")
|
||||
|
||||
Reference in New Issue
Block a user