File indexing completed on 2026-11-09 09:29:53
0001 """dRICH example workflow payloads and objective calculations."""
0002
0003 from pathlib import Path
0004
0005 import numpy as np
0006
0007
0008
0009
0010
0011 def sim_reco_files(sim_dir, npart, trial_tag, point, particle):
0012 """Return simulation and reconstruction file paths for one scan point."""
0013 tag = (
0014 f"{npart}_{trial_tag}_{particle}_p_{point['p']}_"
0015 f"eta_{point['eta_min']}_{point['eta_max']}"
0016 )
0017 return Path(sim_dir) / f"scan_{tag}.root", Path(sim_dir) / f"recon_scan_{tag}.root"
0018
0019
0020 def analysis_result_file(results_dir, npart, trial_tag, point):
0021 """Return the analysis text output for one scan point."""
0022 return (
0023 Path(results_dir)
0024 / f"recon_scan_{npart}_{trial_tag}_p_{point['p']}_"
0025 f"eta_{point['eta_min']}_{point['eta_max']}.txt"
0026 )
0027
0028
0029 def _trial_context(problem_config, workflow_context):
0030 """Return inputs from the shared workflow context."""
0031 if problem_config is None:
0032 raise ValueError("dRICH payloads require problem_config")
0033 if problem_config.evaluation_config is None:
0034 raise ValueError("dRICH payloads require problem.evaluation_config")
0035
0036 eval_config = problem_config.evaluation_config
0037 trial_index = workflow_context.get("trial_index")
0038 if trial_index is None:
0039 raise ValueError("dRICH payloads require trial_index")
0040 output_dir = Path(workflow_context["output_dir"])
0041 paths = {
0042 "results_dir": output_dir / "ana",
0043 "sim_dir": output_dir / "sim_rec",
0044 }
0045 return eval_config, trial_index, paths
0046
0047
0048
0049
0050
0051 def aggregate_objectives(*, problem_config, workflow_context, **_):
0052 """Return objectives for a framework objective-plan step."""
0053 eval_config, trial_index, paths = _trial_context(
0054 problem_config,
0055 workflow_context,
0056 )
0057 particle_count = eval_config["npart"]
0058 scan_points = eval_config["scan_points"]
0059 results = np.array(
0060 [
0061 np.loadtxt(
0062 analysis_result_file(
0063 paths["results_dir"],
0064 particle_count,
0065 str(trial_index),
0066 point,
0067 )
0068 )
0069 for point in scan_points
0070 ]
0071 )
0072 momenta = np.array([point["p"] for point in scan_points])
0073 metric_values = (
0074 ("piKsep_etalow", results[momenta == 15, 2], results[momenta == 15, 3]),
0075 ("piKsep_etahigh", results[momenta == 45, 2], results[momenta == 45, 3]),
0076 ("acceptance", results[1:, 0], results[1:, 1]),
0077 )
0078 metrics = {}
0079 for name, values, errors in metric_values:
0080 metrics[name] = float(np.mean(values))
0081 metrics[f"{name}_err"] = float(np.linalg.norm(errors) / len(errors))
0082
0083 if any(np.isnan(value) for value in metrics.values()):
0084 raise RuntimeError(f"NaN objective for trial {trial_index}")
0085
0086 return metrics
0087
0088
0089
0090
0091
0092 def sim_rec_payloads(*, problem_config, workflow_context, **_):
0093 """Return payload records for simulation/reconstruction jobs."""
0094 eval_config, trial_index, paths = _trial_context(
0095 problem_config,
0096 workflow_context,
0097 )
0098 trial_tag = str(trial_index)
0099 particle_count = eval_config["npart"]
0100 records = []
0101 for point in eval_config["scan_points"]:
0102 for particle in eval_config["particles"]:
0103 sim_file, reco_file = sim_reco_files(
0104 paths["sim_dir"],
0105 particle_count,
0106 trial_tag,
0107 point,
0108 particle,
0109 )
0110 records.append(
0111 {
0112 "npart": particle_count,
0113 "particle": particle,
0114 "p": point["p"],
0115 "eta_min": point["eta_min"],
0116 "eta_max": point["eta_max"],
0117 "sim_file": str(sim_file),
0118 "reco_file": str(reco_file),
0119 }
0120 )
0121 return records
0122
0123
0124 def ana_payloads(*, problem_config, workflow_context, **_):
0125 """Return payload records for analysis jobs."""
0126 eval_config, trial_index, paths = _trial_context(
0127 problem_config,
0128 workflow_context,
0129 )
0130 trial_tag = str(trial_index)
0131 particle_count = eval_config["npart"]
0132
0133 records = []
0134 for point in eval_config["scan_points"]:
0135 records.append(
0136 {
0137 "radiator": point["radiator"],
0138 "bootstrap_samples": eval_config["bootstrap_samples"],
0139 "nbootstraps": eval_config["nbootstraps"],
0140 "ana_inputs": " ".join(
0141 str(
0142 sim_reco_files(
0143 paths["sim_dir"],
0144 particle_count,
0145 trial_tag,
0146 point,
0147 particle,
0148 )[1]
0149 )
0150 for particle in eval_config["particles"]
0151 ),
0152 "analysis_output": str(
0153 analysis_result_file(
0154 paths["results_dir"],
0155 particle_count,
0156 trial_tag,
0157 point,
0158 )
0159 ),
0160 }
0161 )
0162 return records