From 9e070e3db449fb712272d882e52a628980c9c57c Mon Sep 17 00:00:00 2001 From: Gernot Maier Date: Mon, 11 May 2026 16:33:12 +0200 Subject: [PATCH 01/27] Add event-level production comparison CLI --- PLAN-LongIntegrationTests.md | 106 ++++++++ PR.md | 24 ++ docs/source/user-guide/applications.md | 1 + ...ols-compare-productions-on-event-level.rst | 6 + pyproject.toml | 1 + .../compare_productions_on_event_level.py | 79 ++++++ .../sim_events/production_comparison.py | 204 +++++++++++++++ .../plot_event_level_production_comparison.py | 246 ++++++++++++++++++ ...compare_productions_on_event_level_run.yml | 17 ++ ...test_compare_productions_on_event_level.py | 45 ++++ .../sim_events/test_production_comparison.py | 84 ++++++ ..._plot_event_level_production_comparison.py | 45 ++++ 12 files changed, 858 insertions(+) create mode 100644 PLAN-LongIntegrationTests.md create mode 100644 PR.md create mode 100644 docs/source/user-guide/applications/simtools-compare-productions-on-event-level.rst create mode 100644 src/simtools/applications/compare_productions_on_event_level.py create mode 100644 src/simtools/sim_events/production_comparison.py create mode 100644 src/simtools/visualization/plot_event_level_production_comparison.py create mode 100644 tests/integration_tests/config/compare_productions_on_event_level_run.yml create mode 100644 tests/unit_tests/applications/test_compare_productions_on_event_level.py create mode 100644 tests/unit_tests/sim_events/test_production_comparison.py create mode 100644 tests/unit_tests/visualization/test_plot_event_level_production_comparison.py diff --git a/PLAN-LongIntegrationTests.md b/PLAN-LongIntegrationTests.md new file mode 100644 index 0000000000..dd99858e13 --- /dev/null +++ b/PLAN-LongIntegrationTests.md @@ -0,0 +1,106 @@ +# Simtools Long Integration Tests + +Long integration tests (LITs) allow to compare simulations results obtained with different simtools version (includes CORSIKA, sim-telarray, simtools), production models, or hadronic interaction models. Goal is to + +- detect unintended physics regressions +- quantify differences between model versions (expected and unexpected differences) +- provide release qualification metrics +- provide long-term monitoring of execution statistics + +Two types of tools are required - this work is about the implementation of the first type. + +1. configure and run simulation productions for long integration tests +2. tools to compare two/several simulation production results + +## Simulation productions for LITs + +Assumption (for now) is that these productions are medium sized and run on the local HTCondor cluster. The future extension to a Grid-style setup should be taken into account (separation of production definition from execution backend). + +Start with fixed energy simulations for easier comparison (and quicker testing). This can easily be extended later to energy ranges. + +Comparison grid: + +- simtools versions +- production model versions +- interaction models + +Production grid: + +- zenith angles (e.g., 20, 40, 60 deg) +- azimuth angles (e.g., 0, 180 deg) +- energy (e.g., [0.03, 0.1, 0.3, 1., 3., 10., 30., 100. TeV]) +- primary (e.g. [‘gamma’, ‘proton’]) + +The grid might be adjustable (testing with 1 grid point, release with many). + +Additional input: + +- event statistics per energy bin (number of runs x events per run) *fixed during prototyping* +- scatter area (E) *fixed during prototyping* +- adjust energy grid as function of zenith *fixed during prototyping* + +Implementation: + +- tool similar to `simtools-simulate-prod-htcondor-generator` (or adaption of this tool) +- write one condor file per simtools versions (as these are defined by Apptainer times) +- modify the configuration paramaeter `apptainer_image` such that it can digest a dict with (label: apptainer image path) entries, e.g.: +``` +apptainer_image: + 7.0.0: + 6.3.0: +``` +- write a config file with one line per production grid point (similar to VTSSimpipe) to be ingested to HTCondor (x number of runs) + +This means that there will be at most len(simtools versions) job submission steps. + +## Technical details + +Suggestion is to modify `simtools-simulate-prod-htcondor-generator`. + +Copy and adapt the example in tests/integration_tests/config/simulate_prod_htcondor_generator_gamma_20_deg_north.yml: + +- value listed in the comparison and production grid should be allowed to be lists in this configuration +- one condor output file per apptainer_image (this is the `container_image` field in the condor file) + +simtools-simulate-prod-htcondor-generator should be modified that it doesn't write a file with the values from the grid included (like the example in htcondor_submit/simulate_prod.submit.sh) but : + +- a parameter file with one line per grid point (job) with the value listed: + + Example for params.txt: + + ```text + 0.1 20 0 gamma epos 7.0.0 + 0.1 20 0 gamma epos 6.3.0 + ``` + + - note that this is the combination of the comparison and production grid values (energy, zenith, azimuth, primary, interaction model), so this file might be quite long (depending on the grid size); the simtools version is handled by the container image fields. + +Condor should submit then with `queue name,value,tag from params.txt` (instead of `queue `). + + is the parameter given with `--pack_for_grid_register` and should contain the apptainer label (see modification of `apptainer_image` field in the config file) to be used for the job execution. This label should also be used in condor files, parameter file, and the job execution script. + +Remember to keep the following for job execution: + +```text +# Process ID used to generate run number +process_id="$1" +# Load environment variables (for DB access) +set -a; source "$2" +``` + +For the implementation: + +- try to minimize code changes to the existing tool +- even when running with the current functionality (no grid), the should should use a params.txt file +- Scalar config values are internally normalized to lists of length 1. +- all runs should start with the same random seed and run number (this might change later; let's start simple) + + +Do not implement: + +- Grid backend execution +- adaptive statistics +- energy-dependent scatter area +- comparison tooling + +Make a plan with detailed suggestions. Goal is to have a work plan in markdown format that can be used for implementation. This should include technical details and code snippets where appropriate. diff --git a/PR.md b/PR.md new file mode 100644 index 0000000000..cdfdc11409 --- /dev/null +++ b/PR.md @@ -0,0 +1,24 @@ +## Summary + +Add grid-style HTCondor submission support to `simtools-simulate-prod-htcondor-generator`. + +This change expands selected production and comparison parameters into a `params.txt` file, writes HTCondor submit files that queue from those params, and updates the submit script to consume per-job values from HTCondor arguments instead of hardcoded values. + +## What Changed + +- allow grid axes such as `primary`, `azimuth_angle`, `zenith_angle`, `model_version`, and interaction models to be provided as lists +- support `apptainer_image` as either a single image path or a label-to-image mapping +- generate one HTCondor params file row per expanded job and one condor file per apptainer label +- pass job-specific values, including resolved `array_layout_name`, through the params file into the submit script +- preserve energy units in the params file and forward them correctly to `simtools-simulate-prod` +- add unit and integration coverage for parser behavior and HTCondor grid generation + +## Example Config + +See the grid example in: + +`tests/integration_tests/config/simulate_prod_htcondor_generator_grid_example.yml` + +## Validation + +- `pytest -q tests/unit_tests/configuration/test_configurator.py tests/unit_tests/applications/test_simulate_prod_htcondor_generator.py tests/unit_tests/job_execution/test_htcondor_script_generator.py` diff --git a/docs/source/user-guide/applications.md b/docs/source/user-guide/applications.md index 76c277c7de..d96cc885f0 100644 --- a/docs/source/user-guide/applications.md +++ b/docs/source/user-guide/applications.md @@ -52,6 +52,7 @@ Parameters with the same functionality are named consistently the same among all simtools-convert-all-model-parameters-from-simtel simtools-convert-geo-coordinates-of-array-elements simtools-convert-model-parameter-from-simtel +simtools-compare-productions-on-event-level simtools-db-add-file-to-db simtools-db-add-simulation-model-from-repository-to-db simtools-db-add-value-from-json-to-db diff --git a/docs/source/user-guide/applications/simtools-compare-productions-on-event-level.rst b/docs/source/user-guide/applications/simtools-compare-productions-on-event-level.rst new file mode 100644 index 0000000000..d23ca2d9b3 --- /dev/null +++ b/docs/source/user-guide/applications/simtools-compare-productions-on-event-level.rst @@ -0,0 +1,6 @@ + +simtools-compare-productions-on-event-level +=========================================== + +.. automodule:: compare_productions_on_event_level + :members: diff --git a/pyproject.toml b/pyproject.toml index 6bd86626a6..fae01d68b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ optional-dependencies.tests = [ urls."bug tracker" = "https://github.com/gammasim/simtools/issues" urls.documentation = "https://gammasim.github.io/simtools/" urls.repository = "https://github.com/gammasim/simtools" +scripts.simtools-compare-productions-on-event-level = "simtools.applications.compare_productions_on_event_level:main" scripts.simtools-convert-all-model-parameters-from-simtel = "simtools.applications.convert_all_model_parameters_from_simtel:main" scripts.simtools-convert-geo-coordinates-of-array-elements = "simtools.applications.convert_geo_coordinates_of_array_elements:main" scripts.simtools-convert-model-parameter-from-simtel = "simtools.applications.convert_model_parameter_from_simtel:main" diff --git a/src/simtools/applications/compare_productions_on_event_level.py b/src/simtools/applications/compare_productions_on_event_level.py new file mode 100644 index 0000000000..84dc05161c --- /dev/null +++ b/src/simtools/applications/compare_productions_on_event_level.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 + +r"""Compare multiple simulation productions on event level. + +The application accepts repeated production descriptors and creates event-level +comparison plots using reduced event data files. + +Command line arguments +---------------------- +production (repeated, required) + Production descriptor in two fields: + 1) label + 2) comma-separated event data file patterns +output_path (str, required) + Output directory for generated comparison plots. + +Examples +-------- +Compare two productions: + +.. code-block:: console + + simtools-compare-productions-on-event-level \ + --production baseline "data/baseline/*.h5" \ + --production candidate "data/candidate/*.h5,data/candidate_extra/*.h5" \ + --output_path simtools-output/ +""" + +from simtools.application_control import build_application +from simtools.sim_events.production_comparison import ( + collect_production_metrics, + parse_production_arguments, +) +from simtools.visualization import plot_event_level_production_comparison + + +def _add_arguments(parser): + """Register application-specific command line arguments.""" + parser.initialize_application_arguments(["output_path"]) + parser.add_argument( + "--production", + action="append", + nargs="+", + metavar=("LABEL", "EVENT_DATA_FILES"), + required=True, + help=( + "Production descriptor. " + "Use as: --production