Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6a28859
Improved grid job submission
GernotMaier May 18, 2026
1b242d9
Merge branch 'skills-unit-tests' into power-law-events
GernotMaier May 18, 2026
10f73e0
unit tests
GernotMaier May 18, 2026
27131e4
Merge branch 'skills-unit-tests' into power-law-events
GernotMaier May 18, 2026
84bea4a
changelog
GernotMaier May 18, 2026
5302844
reference energy
GernotMaier May 18, 2026
9c6ce59
Merge branch 'skills-unit-tests' into power-law-events
GernotMaier May 18, 2026
4f5bc15
Potential fix for pull request finding
GernotMaier May 18, 2026
c070387
copilot
GernotMaier May 18, 2026
d8d625b
Merge branch 'main' into power-law-events
GernotMaier May 18, 2026
d011ff1
ensure list instead of ensure iterable
GernotMaier May 18, 2026
5d5c0bd
simplify
GernotMaier May 18, 2026
3eab711
tmp improvements
GernotMaier May 18, 2026
2db96ff
obsolete
GernotMaier May 18, 2026
d7cb00c
simplification
GernotMaier May 18, 2026
536b53a
unit tests
GernotMaier May 18, 2026
1104342
simplify
GernotMaier May 18, 2026
15f8140
fix dirs
GernotMaier May 19, 2026
5d63522
simplifications
GernotMaier May 19, 2026
cab3d5b
Merge branch 'main' into power-law-events
GernotMaier May 19, 2026
5173395
cleanup
GernotMaier May 19, 2026
e71dd90
Merge branch 'power-law-events' into generalize-simulation-grid
GernotMaier May 19, 2026
e48ca5f
simplification
GernotMaier May 19, 2026
adb3a0b
improved naming
GernotMaier May 19, 2026
a415860
cleanup [skip ci]
GernotMaier May 19, 2026
343f6f1
production grid
GernotMaier May 19, 2026
cbf4bbe
generalize grid specifications
GernotMaier May 19, 2026
eb5a5a0
namiing
GernotMaier May 19, 2026
956c92b
remove duplicated test
GernotMaier May 19, 2026
c08f4c5
observation time
GernotMaier May 19, 2026
6d2b2f6
remove telescope ids
GernotMaier May 19, 2026
83232fa
unit test
GernotMaier May 19, 2026
026356d
improved readability
GernotMaier May 19, 2026
0550406
unit tests
GernotMaier May 19, 2026
9ad933a
documentation
GernotMaier May 19, 2026
2f3ec32
sonar
GernotMaier May 19, 2026
bf5eb79
tmptest
GernotMaier May 19, 2026
f7da7f4
duplication
GernotMaier May 19, 2026
2de6d32
cleanup
GernotMaier May 19, 2026
1297756
changelog
GernotMaier May 19, 2026
e3bfdc5
simplify
GernotMaier May 19, 2026
dbf14a5
Potential fix for pull request finding
GernotMaier May 19, 2026
ae385b0
Potential fix for pull request finding
GernotMaier May 19, 2026
2f0a754
consistent command line parameter
GernotMaier May 19, 2026
a617462
binning definition
GernotMaier May 21, 2026
51819c3
Merge branch 'main' into generalize-simulation-grid
GernotMaier May 21, 2026
afe6ba6
changelogs
GernotMaier May 21, 2026
79016f1
time of observation
GernotMaier May 21, 2026
ae67242
tests
GernotMaier May 21, 2026
502b6b5
simplification
GernotMaier May 21, 2026
7d200f1
Merge branch 'main' into generalize-simulation-grid
GernotMaier May 22, 2026
b2ca26d
Merge branch 'main' into generalize-simulation-grid
GernotMaier May 23, 2026
ff29c13
doc fixes
GernotMaier May 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/changes/2190.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Major improvements to simulation production configuration preparation with clear separation introduced between observation grid generation and backend submission code.
Allow NSHOW to follow a pre-defined power law for simulation production grid definition.
36 changes: 33 additions & 3 deletions docs/source/api-reference/production_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,16 @@ the calculation of the number of events to be simulated given a pre-determined m
:members:
```

(generate-production-grid)=
(corsika-limits-lookup)=

## generate_production_grid
## corsika_limits_lookup

```{eval-rst}
.. automodule:: production_configuration.generate_production_grid
.. automodule:: production_configuration.corsika_limits_lookup
:members:
```


(interpolation-handler)=

## interpolation_handler
Expand All @@ -61,6 +62,35 @@ the calculation of the number of events to be simulated given a pre-determined m
:members:
```

(job-grid-io)=

## job_grid_io

```{eval-rst}
.. automodule:: production_configuration.job_grid_io
:members:
```

(observation-grid)=

## observation_grid

```{eval-rst}
.. automodule:: production_configuration.observation_grid
:members:
```

(simulation-jobs)=

## simulation_jobs

```{eval-rst}
.. automodule:: production_configuration.simulation_jobs
:members:
```

(merge-corsika-limits)=

## merge_corsika_limits

```{eval-rst}
Expand Down
2 changes: 0 additions & 2 deletions docs/source/api-reference/visualization.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ the visualization module.
:members:
```

(merge-corsika-limits)=


(plot-corsika-limits)=

Expand Down
2 changes: 1 addition & 1 deletion src/simtools/applications/plot_production_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Model version used to read the site reference coordinates.
observation_time (str, optional)
Observation time in UTC ISO format used for Alt/Az <-> RA/Dec transformations.
If omitted, the application uses ``metadata.observing_time_utc`` from the
If omitted, the application uses ``metadata.time_of_observation_utc`` from the
grid file when available.
plot_ra_dec_tracks (flag, optional)
If provided, plot RA/Dec guide tracks on top of the sky projection. When native
Expand Down
218 changes: 78 additions & 140 deletions src/simtools/applications/production_generate_grid.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,30 @@
#!/usr/bin/python3

r"""
Generate a grid of simulation points using flexible axes definitions.

This application generates a grid of simulation points based on the provided axes
definitions. The axes definitions (range, binning) are specified in a file.
The viewcone, radius and energy thresholds are provided as a lookup table and
are interpolated based on the generated grid points. The generated grid points are
filtered based on the specified telescope IDs and the limits from the lookup table.
The generated grid points are saved to a file.
It can also convert the generated points to RA/Dec coordinates if the selected
coordinate system is 'ra_dec'.

For ``coordinate_system='ra_dec'``, the underlying grid generation supports
declination-line sampling with hour-angle spacing and applies zenith-angle
filtering based on the configured zenith range in that mode.
When explicit ``ra`` / ``dec`` axes are provided, all YAML-defined grid points are
preserved in the serialized output.
Generate simulation job grid for production configurations.

This application expands simulation job rows and writes them to disk as ECSV files.
It supports both:

- explicit cartesian job-grid configuration (primary, zenith, energy range, etc.), and
- axes-based production-grid configuration with optional ``ra_dec`` coordinate handling
and lookup-table interpolation.

Command line arguments
----------------------
axes (str, required)
Path to a YAML or JSON file defining the axes of the grid.
coordinate_system (str, optional, default='zenith_azimuth')
The coordinate system for the grid generation ('zenith_azimuth' or 'ra_dec').
In ``ra_dec`` mode, observing location/time are used to build sky directions and
derive corresponding zenith/azimuth values for interpolation (ICRS/J2000 frame).
observing_time (str, optional)
axis (repeatable)
Compact axis definition in the form
``--axis <name> <min> <unit> <max> <unit> <binning> [scaling]``.
Example: ``--axis azimuth 310 deg 20 deg 3 linear``.
time_of_observation (str, optional)
Time of the observation in UTC (format: 'YYYY-MM-DD HH:MM:SS').
Used only in ``ra_dec`` mode (for coordinate transforms and sidereal-time
sampling). Ignored in ``zenith_azimuth`` mode.
lookup_table (str, required)
Used only if RA/Dec axes are provided (for coordinate transforms and sidereal-time
sampling). Ignored otherwise.
corsika_limits (str, optional)
Path to the lookup table for simulation limits. The table should contain
varying azimuth and/or zenith angles.
telescope_ids (list of str, optional)
List of telescope names used to filter the lookup table rows
(e.g. ``MSTN-15``).
simtel_file (str, optional)
Path to a sim_telarray file used only when lookup-table telescope selections
are stored as numeric telescope IDs.
output_file (str, optional, default='grid_output.ecsv')
Output file for the generated grid points (default: 'grid_output.ecsv').
varying azimuth and/or zenith angles for the selected array layout.
output_file (str, optional, default='job_grid.ecsv')
Output file for the generated executable job grid.


Example
Expand All @@ -50,56 +34,50 @@
.. code-block:: console

simtools-production-generate-grid --site North --model_version 6.0.2 \
--axes tests/resources/production_grid_generation_axes_definition.yml \
--coordinate_system zenith_azimuth \
--lookup_table tests/resources/corsika_simulation_limits/
merged_corsika_limits_for_test.ecsv \
--telescope_ids MSTN-15
--array_layout_name alpha \
--axis azimuth 310 deg 20 deg 3 linear \
--axis zenith 30 deg 40 deg 2 linear \
--axis nsb 4 MHz 5 MHz 2 linear \
--axis offset 0 deg 10 deg 2 linear \
--corsika_limits tests/resources/corsika_simulation_limits/merged_corsika_limits.ecsv

To generate an all-sky RA/Dec direction grid and serialize output in RA/Dec,
execute:

.. code-block:: console

simtools-production-generate-grid --site North --model_version 6.0.2 \
--axes tests/resources/production_grid_generation_axes_definition_radec.yml \
--coordinate_system ra_dec --observing_time "2017-09-16 00:00:00" \
--lookup_table tests/resources/corsika_simulation_limits/
merged_corsika_limits_for_test.ecsv \
--telescope_ids MSTN-15
--array_layout_name alpha \
--axis ra 0 deg 360 deg 36 linear \
--axis dec -90 deg 90 deg 18 linear \
--axis nsb 4 MHz 4 MHz 1 linear \
--axis offset 0 deg 10 deg 2 linear \
--time_of_observation "2017-09-16 00:00:00" \
--corsika_limits tests/resources/corsika_simulation_limits/merged_corsika_limits.ecsv
"""

from pathlib import Path

from astropy.coordinates import EarthLocation
from astropy.time import Time

from simtools.application_control import build_application
from simtools.io.ascii_handler import collect_data_from_file
from simtools.model.site_model import SiteModel
from simtools.production_configuration.generate_production_grid import GridGeneration
from simtools.production_configuration.job_grid_io import serialize_job_grid
from simtools.production_configuration.simulation_jobs import (
build_job_grid_metadata,
build_simulation_jobs,
)


def _add_arguments(parser):
"""Register application-specific command line arguments."""
parser.add_argument(
"--axes",
type=str,
required=True,
help="Path to a file defining the grid axes.",
)
parser.add_argument(
"--coordinate_system",
type=str,
default="zenith_azimuth",
"--axis",
action="append",
nargs="+",
required=False,
help=(
"Coordinate system ('zenith_azimuth' or 'ra_dec'). "
"In 'ra_dec' mode, sky directions are generated using observing"
" location/time and converted to zenith/azimuth for interpolation."
"Compact axis definition: --axis <name> <min> <unit> <max> <unit> <binning> "
"[scaling]. May be repeated."
),
)
parser.add_argument(
"--observing_time",
"--time_of_observation",
type=str,
required=False,
help=(
Expand All @@ -109,103 +87,63 @@ def _add_arguments(parser):
parser.add_argument(
"--output_file",
type=str,
default="grid_output.ecsv",
help="Output file for the generated grid points (default: 'grid_output.ecsv').",
default="job_grid.ecsv",
help="Output file for the generated executable job grid.",
)
parser.add_argument(
"--telescope_ids",
"--corsika_limits",
type=str,
nargs="*",
default=None,
help=(
"List of telescope names used to get specific limits from the lookup table "
"(e.g. MSTN-15)."
),
)
parser.add_argument(
"--lookup_table",
type=str,
required=True,
required=False,
help="Path to the lookup table for simulation limits. "
"Table required with varying azimuth and or zenith angle. ",
)
parser.add_argument(
"--simtel_file",
type=str,
"--number_of_runs",
help="Number of runs to be simulated.",
type=int,
required=False,
default=1,
)
parser.add_argument(
"--nshow_power_index",
help=(
"Power-law index used to scale the baseline nshow with the geometric-mean energy "
"of each energy_range entry."
),
type=float,
required=False,
default=None,
)
parser.add_argument(
"--nshow_reference_energy",
help=(
"Optional path to a sim_telarray file used to map sim_telarray telescope IDs "
"to telescope names when lookup-table selections are numeric IDs."
"Reference energy for nshow power-law scaling (for example: '100 GeV'). "
"Required together with --nshow_power_index."
),
type=str,
required=False,
default=None,
)


def load_axes(file_path: str):
"""
Load axes definitions from a YAML or JSON file.

Parameters
----------
file_path : str
Path to the axes YAML or JSON file.

Returns
-------
list[dict]
List of axes definitions with Quantity values.
"""
if not Path(file_path).exists():
raise FileNotFoundError(f"Axes file {file_path} not found.")

return collect_data_from_file(file_path)


def main():
"""See CLI description."""
app_context = build_application(
initialization_kwargs={
"db_config": True,
"simulation_model": ["version", "site", "model_version"],
"preserve_by_version_keys": ["array_layout_name"],
"simulation_model": ["site", "layout", "telescope", "model_version"],
"simulation_configuration": {"software": None, "corsika_configuration": ["all"]},
},
)

output_filepath = app_context.io_handler.get_output_file(app_context.args["output_file"])

axes = load_axes(app_context.args["axes"])
site_model = SiteModel(
model_version=app_context.args["model_version"],
site=app_context.args["site"],
job_rows = build_simulation_jobs(app_context.args)
serialize_job_grid(
job_rows=job_rows,
output_file=app_context.io_handler.get_output_file(app_context.args["output_file"]),
metadata=build_job_grid_metadata(app_context.args),
)

ref_lat = site_model.get_parameter_value_with_unit("reference_point_latitude")
ref_long = site_model.get_parameter_value_with_unit("reference_point_longitude")
altitude = site_model.get_parameter_value_with_unit("reference_point_altitude")

observing_location = EarthLocation(lat=ref_lat, lon=ref_long, height=altitude)

coordinate_system = app_context.args["coordinate_system"]
observing_time = None
if app_context.args.get("observing_time"):
observing_time = Time(app_context.args["observing_time"], scale="utc")
elif coordinate_system == "ra_dec":
observing_time = Time.now()

grid_gen = GridGeneration(
axes=axes,
coordinate_system=coordinate_system,
observing_location=observing_location,
observing_time=observing_time,
lookup_table=app_context.args["lookup_table"],
telescope_ids=app_context.args["telescope_ids"],
simtel_file=app_context.args.get("simtel_file"),
)

grid_points = grid_gen.generate_grid()

if coordinate_system == "ra_dec":
grid_points = grid_gen.convert_coordinates(grid_points)
grid_gen.serialize_grid_points(grid_points, output_file=output_filepath)


if __name__ == "__main__":
main()
Loading
Loading