Support --energy-gev in dwarf make-root for the new minicalosim energy arg
run_pbwo4/run_sampling now accept a trailing energy_GeV positional arg; thread it through plan/run/seed so datasets like pbwo4_10gev can be generated at non-default beam energies. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,15 +3,17 @@ run_pbwo4, run_sampling) and filing the output into the dataset's raw/ tree:
|
||||
|
||||
raw/<kind>/<gen>/<detector>/shard-NNN.root
|
||||
|
||||
These executables take `[configName] nEvents` and always write a fixed-name
|
||||
*.root file into the current directory — so running several in parallel
|
||||
needs separate working directories, and the output filename has to be
|
||||
discovered rather than assumed (it differs per executable: run_pbwo4 writes
|
||||
pbwo4_<n>events_hits.root, run_sampling writes sampling_<config>_<n>events_hits.root,
|
||||
others may differ again). This script gives each run its own scratch
|
||||
directory under <dataset-root>/.sim-tmp/, requires exactly one *.root to
|
||||
appear there, and moves it to the next free shard index for that detector
|
||||
(existing shards are never overwritten).
|
||||
These executables take `[configName] nEvents [energy_GeV]` (configName is
|
||||
only accepted by executables with a config selector, e.g. run_sampling;
|
||||
energy_GeV defaults to 1.0 in the executable itself if omitted here) and
|
||||
always write a fixed-name *.root file into the current directory — so
|
||||
running several in parallel needs separate working directories, and the
|
||||
output filename has to be discovered rather than assumed (it differs per
|
||||
executable: run_pbwo4 writes pbwo4_<n>events_hits.root, run_sampling writes
|
||||
sampling_<config>_<n>events_hits.root, others may differ again). This script
|
||||
gives each run its own scratch directory under <dataset-root>/.sim-tmp/,
|
||||
requires exactly one *.root to appear there, and moves it to the next free
|
||||
shard index for that detector (existing shards are never overwritten).
|
||||
|
||||
--gen must already exist under raw/<kind>/ — create one first with
|
||||
`dwarf bump-gen`.
|
||||
@@ -105,8 +107,8 @@ 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).
|
||||
def job_seed(kind: str, gen: str, job: SimJob, energy_gev: float | None) -> int:
|
||||
"""Deterministic RNG seed for one sim job, unique per (kind, gen, detector, config, shard, energy).
|
||||
|
||||
Jobs run concurrently (ThreadPoolExecutor below) and can start within the
|
||||
same wall-clock second; minicalosim's default seed falls back to
|
||||
@@ -115,14 +117,31 @@ def job_seed(kind: str, gen: str, job: SimJob) -> int:
|
||||
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}"
|
||||
key = (
|
||||
f"{kind}|{gen}|{job.detector}|{job.config or ''}|{job.shard_index}"
|
||||
f"|{energy_gev if energy_gev is not None else ''}"
|
||||
)
|
||||
return zlib.crc32(key.encode()) & 0x7FFFFFFF
|
||||
|
||||
|
||||
def build_cmd(
|
||||
executable: Path, job: SimJob, events_per_file: int, energy_gev: float | None
|
||||
) -> list[str]:
|
||||
"""minicalosim executables take positional `[configName] nEvents [energy_GeV]`."""
|
||||
cmd = [str(executable)]
|
||||
if job.config:
|
||||
cmd.append(job.config)
|
||||
cmd.append(str(events_per_file))
|
||||
if energy_gev is not None:
|
||||
cmd.append(str(energy_gev))
|
||||
return cmd
|
||||
|
||||
|
||||
def run_job(
|
||||
job: SimJob,
|
||||
executable: Path,
|
||||
events_per_file: int,
|
||||
energy_gev: float | None,
|
||||
dataset_root: Path,
|
||||
kind: str,
|
||||
gen: str,
|
||||
@@ -134,12 +153,9 @@ def run_job(
|
||||
)
|
||||
workdir.mkdir(parents=True)
|
||||
|
||||
cmd = [str(executable)]
|
||||
if job.config:
|
||||
cmd.append(job.config)
|
||||
cmd.append(str(events_per_file))
|
||||
cmd = build_cmd(executable, job, events_per_file, energy_gev)
|
||||
|
||||
env = dict(os.environ, MINICALOSIM_SEED=str(job_seed(kind, gen, job)))
|
||||
env = dict(os.environ, MINICALOSIM_SEED=str(job_seed(kind, gen, job, energy_gev)))
|
||||
result = subprocess.run(cmd, cwd=workdir, capture_output=True, text=True, env=env)
|
||||
|
||||
if result.returncode != 0:
|
||||
@@ -194,6 +210,7 @@ def run_all(
|
||||
jobs: list[SimJob],
|
||||
executable: Path,
|
||||
events_per_file: int,
|
||||
energy_gev: float | None,
|
||||
dataset_root: Path,
|
||||
kind: str,
|
||||
gen: str,
|
||||
@@ -208,6 +225,7 @@ def run_all(
|
||||
job,
|
||||
executable,
|
||||
events_per_file,
|
||||
energy_gev,
|
||||
dataset_root,
|
||||
kind,
|
||||
gen,
|
||||
@@ -238,6 +256,7 @@ def run_make_root(
|
||||
dataset_root: str,
|
||||
jobs: int,
|
||||
execute: bool,
|
||||
energy_gev: float | None = None,
|
||||
) -> None:
|
||||
if jobs < 1:
|
||||
raise SystemExit("error: --jobs must be >= 1")
|
||||
@@ -245,6 +264,8 @@ def run_make_root(
|
||||
raise SystemExit("error: --num-files must be >= 1")
|
||||
if events_per_file < 1:
|
||||
raise SystemExit("error: --events-per-file must be >= 1")
|
||||
if energy_gev is not None and energy_gev <= 0:
|
||||
raise SystemExit("error: --energy-gev must be > 0")
|
||||
if not executable.is_file() or not os.access(executable, os.X_OK):
|
||||
raise SystemExit(f"error: {executable} is not an executable file")
|
||||
|
||||
@@ -257,11 +278,7 @@ def run_make_root(
|
||||
print(f"=== {'EXECUTING' if execute else 'DRY RUN'} ===")
|
||||
print(f"executable: {executable}")
|
||||
for job in planned_jobs:
|
||||
cmd = (
|
||||
[str(executable)]
|
||||
+ ([job.config] if job.config else [])
|
||||
+ [str(events_per_file)]
|
||||
)
|
||||
cmd = build_cmd(executable, job, events_per_file, energy_gev)
|
||||
dest = (
|
||||
dataset_root_path
|
||||
/ "raw"
|
||||
@@ -270,7 +287,7 @@ def run_make_root(
|
||||
/ job.detector
|
||||
/ f"shard-{job.shard_index:03d}.root"
|
||||
)
|
||||
seed = job_seed(kind, gen, job)
|
||||
seed = job_seed(kind, gen, job, energy_gev)
|
||||
print(f" MINICALOSIM_SEED={seed} {' '.join(cmd)} -> {dest}")
|
||||
|
||||
if not execute:
|
||||
@@ -283,6 +300,7 @@ def run_make_root(
|
||||
planned_jobs,
|
||||
executable,
|
||||
events_per_file,
|
||||
energy_gev,
|
||||
dataset_root_path,
|
||||
kind,
|
||||
gen,
|
||||
|
||||
@@ -363,6 +363,16 @@ def make_root(
|
||||
gen: Annotated[
|
||||
str, typer.Option("--gen", help="Existing gen tag under raw/<kind>/, e.g. gen1")
|
||||
],
|
||||
energy_gev: Annotated[
|
||||
float | None,
|
||||
typer.Option(
|
||||
"--energy-gev",
|
||||
help="energy_GeV passed to the executable (default: executable's own "
|
||||
"default, currently 1.0). Note the dataset detector label is not "
|
||||
"derived from this — e.g. use '--detector pbwo4_10gev --energy-gev 10' "
|
||||
"to name the dataset accordingly.",
|
||||
),
|
||||
] = None,
|
||||
kind: Annotated[
|
||||
str, typer.Option("--kind", help="steps | hits | ... (default: steps)")
|
||||
] = "steps",
|
||||
@@ -390,6 +400,7 @@ def make_root(
|
||||
dataset_root=str(dataset_root),
|
||||
jobs=jobs,
|
||||
execute=execute,
|
||||
energy_gev=energy_gev,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user