diff --git a/config/infer/icarus/modifier/charge_scale/mod_charge_scale_260813.yaml b/config/infer/icarus/modifier/charge_scale/mod_charge_scale_260813.yaml new file mode 100644 index 0000000..bd79adb --- /dev/null +++ b/config/infer/icarus/modifier/charge_scale/mod_charge_scale_260813.yaml @@ -0,0 +1,25 @@ +# ICARUS charge-scale detector variation +# Revision date: August 13, 2026 + +__meta__: + kind: mod + name: "charge_scale" + version: "260813" + date: "2026-08-13" + description: "Charge-scale detector variation" + applies_to: [mc] + compatible_with: + model: ">=250303" + +override: + model.modules.calibration.response: + # Apply the response variation before charge calibrations. + priority: 10 + response_func: 1.0279*x - 0.000287*x*x + 2.4915e-7*x*x*x - 4.522989e-11*x*x*x*x + model.modules.calibration.smearing: + # Apply the smearing after the response function + priority: 9 + distribution: normal + mode: multiplicative + mean: 1.0 + scale: 0.14953 diff --git a/config/infer/sbnd/modifier/charge_scale/mod_charge_scale_260813.yaml b/config/infer/sbnd/modifier/charge_scale/mod_charge_scale_260813.yaml index f05c8a0..5531419 100644 --- a/config/infer/sbnd/modifier/charge_scale/mod_charge_scale_260813.yaml +++ b/config/infer/sbnd/modifier/charge_scale/mod_charge_scale_260813.yaml @@ -20,6 +20,13 @@ override: response: # Apply the response variation before charge calibrations. priority: 10 - response_func: 1.00582*x - 0.0001188*x*x - 4.4164e-8*x*x*x + 1.04071e-11*x*x*x*x + response_func: 0.9894425*x + 7.6032e-7*x*x - 2.552e-7*x*x*x + 6.74704e-11*x*x*x*x + smearing: + # Apply the smearing after the response function + priority: 9 + distribution: normal + mode: multiplicative + mean: 1.0 + scale: 0.0511 post.apply_calibrations.depositions_source: depositions diff --git a/tests/test_config_validation.py b/tests/test_config_validation.py index 0c4b513..39b1aff 100644 --- a/tests/test_config_validation.py +++ b/tests/test_config_validation.py @@ -393,3 +393,64 @@ def test_sbnd_charge_scale_continues_from_varied_depositions( assert ( config["post"]["apply_calibrations"]["depositions_source"] == "depositions" ) + + def test_icarus_charge_scale_only_adds_response_and_smearing( + self, config_infer_root, tmp_path + ): + """The ICARUS variation preserves calibration supplied by the base model.""" + icarus_root = config_infer_root / "icarus" + modifier = ( + icarus_root / "modifier" / "charge_scale" / "mod_charge_scale_260813.yaml" + ) + modifier_config = yaml.safe_load(modifier.read_text(encoding="utf-8")) + assert modifier_config["__meta__"]["compatible_with"] == {"model": ">=250303"} + assert set(modifier_config["override"]) == { + "model.modules.calibration.response", + "model.modules.calibration.smearing", + } + + composite = tmp_path / "composite.yaml" + composite.write_text( + yaml.safe_dump( + { + "include": [ + str(icarus_root / "full_chain_co_260501.yaml"), + str(modifier), + ] + } + ), + encoding="utf-8", + ) + config = load_config_with_includes(composite) + calibration = config["model"]["modules"]["calibration"] + assert calibration["stage"] == "segmentation" + assert calibration["gain"]["gain"] == 79.9169 + assert "recombination" in calibration + assert "lifetime" in calibration + assert "transparency" in calibration + assert calibration["response"]["priority"] == 10 + assert calibration["smearing"]["priority"] == 9 + + def test_icarus_charge_scale_accepts_first_in_chain_calibration( + self, config_infer_root, tmp_path + ): + """The first ICARUS model with in-chain calibration supports the modifier.""" + icarus_root = config_infer_root / "icarus" + modifier = ( + icarus_root / "modifier" / "charge_scale" / "mod_charge_scale_260813.yaml" + ) + composite = tmp_path / "composite.yaml" + composite.write_text( + yaml.safe_dump( + { + "include": [ + str(icarus_root / "full_chain_co_250303.yaml"), + str(modifier), + ] + } + ), + encoding="utf-8", + ) + + config = load_config_with_includes(composite) + assert "response" in config["model"]["modules"]["calibration"]