Ray Tracing#

The ray-tracing modules handle ray tracing simulations, incident angle calculations, PSF analysis and I/O.

incident_angles#

Calculate photon incident angles on focal plane and primary/secondary mirrors.

The ray-tracing backend (simtools.ray_tracing.backends.interface) provides, per photon, the angle of incidence at the focal surface with respect to the optical axis [deg] and, for dual-mirror telescopes, the angles of incidence on the primary and secondary mirror [deg] and the hit points on them.

class ray_tracing.incident_angles.IncidentAnglesCalculator(config_data, output_dir, label=None, context=None)[source]#

Trace a star and compute incident angles at mirrors or focal surfaces.

Parameters:
config_datadict

Simulation configuration (e.g. site, telescope, model_version, zenith_angle, off_axis_angle, source_distance, number_of_photons). Zenith defaults to 0 degrees. Off-axis angles use the PSF camera-x convention.

output_dirstr or pathlib.Path

Output directory where logs, scripts, photons files and results are written.

labelstr, optional

Label used to name outputs; defaults to incident_angles_<telescope> when omitted.

Notes

Additional options are read from config_data when present: - perfect_mirror (bool, default False) Focal-plane incidence angles are calculated for all telescope models. Primary and secondary mirror incidence angles are additionally calculated only for dual-mirror telescopes.

run()[source]#

Trace the star with the ray-tracing backend and return an angle table.

Returns:
astropy.table.QTable

Table containing at least the angle_incidence_focal column and, when configured, primary/secondary angles and hit geometry.

run_for_offsets(offsets)[source]#

Run the simulation for multiple off-axis angles.

For each off-axis angle provided, run a full simulation, labeling output files accordingly.

Parameters:
offsetsIterable[float]

Off-axis angles in degrees.

Returns:
dict[float, astropy.table.QTable]

Mapping from off-axis angle (deg) to the resulting table.

save_model_parameters(results_by_offset)[source]#

Write incident-angle model-parameter tables and their JSON metadata.

Export the filter distribution and enabled mirror distributions. Both mirror parameters are exported only for dual-mirror optics. Densities describe unweighted rays completing the optical path, without requiring a camera pixel hit.

Files are placed under a telescope-named subdirectory to match repository convention.

Parameters:
results_by_offsetdict

Dictionary with results for each offset.

ray_tracing#

Ray tracing simulations and analysis.

Traces star light through the telescope optics with the configured ray-tracing backend (simtools.ray_tracing.backends.interface) and computes the PSF (D80 containment diameter), effective mirror area and effective focal length versus off-axis angle.

class ray_tracing.ray_tracing.RayTracing(telescope_model, site_model, label=None, zenith_angle=<Quantity 20. deg>, off_axis_angle=<Quantity [0.] deg>, source_distance=<Quantity 10. km>, single_mirror_mode=False, use_random_focal_length=False, random_focal_length_seed=None, mirror_numbers='all', offset_file=None, offset_directions=None, context=None)[source]#

Ray tracing simulations and analysis.

Parameters:
telescope_model, site_modelTelescopeModel, SiteModel

Telescope and site models.

labelstr

Label used for output file naming.

zenith_angleastropy.units.Quantity

Zenith angle.

off_axis_anglelist of tuples or astropy.units.Quantity, optional

Off-axis angles as (x, y) tuples in degrees, or scalar angles. Scalar angles generate offsets in the cardinal directions offset_directions.

source_distanceastropy.units.Quantity

Source distance.

single_mirror_modebool

Trace each mirror panel instead of the telescope optics.

use_random_focal_lengthbool

Use random focal length variation of the mirror panels.

random_focal_length_seedint

Seed for the random focal length variation.

mirror_numberslist, str

List of mirror numbers (or ‘all’).

offset_filePath or str, optional

ECSV file with x, y offset columns (in degrees).

offset_directionslist, optional

Cardinal directions [‘N’, ‘S’, ‘E’, ‘W’] for scalar offsets.

contextRunContext, optional

Run context.

analyze(export=True, force=False, use_rx=False, no_tel_transmission=False, containment_fraction=0.8, save_photons=False, keep_images=True)[source]#

Analyze the ray tracing results: PSF containment, effective area and focal length.

The effective mirror area is detected_photons * total_area / total_photons and the effective focal length is centroid_radius / tan(off_axis_angle).

Parameters:
exportbool

If True, export the results to a file.

forcebool

If True, existing results files are removed and the analysis is repeated.

use_rxbool

If True, use the rx binary of sim_telarray; otherwise the internal psf_analysis.

no_tel_transmissionbool

If True, the telescope transmission is not applied.

containment_fractionfloat

Containment fraction for the PSF calculation, in [0, 1].

save_photonsbool

If True, keep the photon files of the backend.

keep_imagesbool

If True, keep the PSF image of each off-axis angle (see images).

export_results()[source]#

Export the results to an ECSV file.

get_psf_mm(row_index=0)[source]#

Return the PSF diameter from the analysis results in mm.

Parameters:
row_indexint

Row index into the results table (default: 0).

Returns:
float

PSF diameter in millimeters.

images()[source]#

Return the analyzed PSF images, one per off-axis angle.

Returns:
list of PSFImage or None

PSF images with photon positions, centroids and PSF containment diameters.

plot(key, save=False, psf_diameter_cm=None, **kwargs)[source]#

Plot the analysis results versus the radial off-axis angle.

Parameters:
keystr

psf_cm, psf_deg, eff_area or eff_flen.

savebool

If True, the figures are saved.

psf_diameter_cmfloat

PSF diameter marked in the cumulative PSF plot (in cm).

**kwargs

Passed to the plot function.

Returns:
matplotlib.figure.Figure

Figure containing the requested plot.

Raises:
KeyError

If key is not among the valid options.

plot_histogram(key, **kwargs)[source]#

Plot a histogram of a results column.

Parameters:
keystr

psf_cm, psf_deg, eff_area or eff_flen.

**kwargs

Passed to the histogram plot.

Raises:
KeyError

If key is not among the valid options.

simulate(test=False, force=False, compress_photons=True, max_workers=8)[source]#

Run the ray tracing simulations with the configured backend.

Parameters:
testbool

Simulate much fewer photons.

forcebool

Simulate again even if the backend keeps earlier results.

compress_photonsbool

Compress the photon files written by the backend.

max_workersint

Maximum number of concurrent simulations of the backend.

Raises:
ValueError

If max_workers is not positive.

backends.interface#

Ray-tracing request types, backend contracts, and helpers.

See RayTracingBackend for the common backend contract.

class ray_tracing.backends.interface.LightSourceRayTracingBackend(*args, **kwargs)[source]#

Ray-tracing backend that also traces calibration light sources.

Such backends set traces_light_sources; for other backends sim_telarray’s LightEmission programs make the light and sim_telarray traces it.

camera_photons(request)[source]#

Return, for each light pulse of a LightSourceRequest, an iterator over blocks.

Each block holds the photons crossing the camera plane (through the focal-surface vertex, normal to the optical axis; telescope frame, metres): position_m, direction, time_ns, weight (photons) and wavelength_nm.

class ray_tracing.backends.interface.LightSourceRequest(position_m, axis, angular_profile, pulse, wavelength_nm, photons_per_pulse, telescope_zenith, seeds, inside_telescope=False, camera_radius_m=None)[source]#

The light of a calibration light source (a point emitter), pulse by pulse.

Parameters:
position_m: tuple of float

Emitter position in the telescope frame [m] (+z along the optical axis).

axis: tuple of float

Unit vector of the axis of the angular profile (telescope frame).

angular_profile: tuple of numpy.ndarray

Angles to the axis [deg] and relative intensities per solid angle.

pulse: tuple of numpy.ndarray or None

Emission times [ns] and relative rates; None: instantaneous.

wavelength_nm: float

Wavelength [nm].

photons_per_pulse: tuple of float

Photons emitted into 4 pi in each pulse.

telescope_zenith: float

Telescope zenith angle [deg].

seeds: tuple of int

(Instrument seed, simulation seed).

inside_telescope: bool

The light starts inside the telescope on its way to the camera (flasher): it reaches the camera directly, without the mirrors.

camera_radius_m: float or None

Radius of the camera [m]; the light of a source inside the telescope is made only on its way to the camera.

class ray_tracing.backends.interface.RayTracingBackend(*args, **kwargs)[source]#

Contract implemented by ray-tracing backends.

A backend traces star light through the optics of one telescope model. The ray-tracing applications describe what to trace (StarImageRequest) and analyse what comes back; how a backend traces, which files it writes and what it keeps in memory stays inside the backend.

A backend class is constructed with the telescope model, the site model, a label, the telescope zenith angle [deg] and the single-mirror flag. Backends are selected by name through get_backend.

incidence_angles(request, work_directory, name, with_mirrors, keep_files=False)[source]#

Return per-photon incidence angles on the focal surface (and mirrors).

Also returns the intermediate files written.

psf_image(request, focal_length, containment_fraction, use_rx=False, save_photons=False)[source]#

Return the PSFImage of one traced image and whether the structure was traced.

If the telescope structure was traced, telescope_transmission does not apply.

simulate(requests, test=False, force=False, compress_photons=True, max_workers=8)[source]#

Trace the requested star images.

class ray_tracing.backends.interface.StarImageRequest(off_axis_x, off_axis_y, source_distance_km, seeds, photons_per_instance, mirror_number=None, parameter_changes=<factory>)[source]#

One star image to trace.

Parameters:
off_axis_x, off_axis_y: float

Star position in the camera [deg].

source_distance_km: float

Source distance [km].

seeds: tuple of tuple of int

(Instrument seed, simulation seed) of each random instrument instance.

photons_per_instance: int

Photons traced per instance.

mirror_number: int or None

Mirror in single-mirror mode.

parameter_changes: dict

Model-parameter values replacing those of the telescope model.

property key#

Return the identifier of the image (offsets and mirror).

ray_tracing.backends.interface.get_backend(name=None, *, context=None)[source]#

Return the ray-tracing backend class.

Parameters:
name: str or None

Backend name; defaults to the context’s configured backend.

contextRunContext, optional

Run configuration. The environment context is used when omitted.

Returns:
type

Backend class.

ray_tracing.backends.interface.image_file_name(backend, request, file_type, suffix)[source]#

Return the name of a file of one traced image.

Parameters:
backend: object

Backend (telescope model, zenith angle, label and single-mirror flag).

request: StarImageRequest

Star image.

file_type: str

File type (e.g. ray_tracing_photons).

suffix: str

File suffix.

Returns:
str

File name.

backends.sim_telarray#

Ray tracing with sim_telarray.

sim_telarray simulates the star light of each random instrument instance in one run and writes photon lists; images are read from these lists (or analysed with sim_telarray’s rx). Incidence angles come from the imaging list of the sim_telarray debug-trace build.

class ray_tracing.backends.sim_telarray.SimtelBackend(telescope_model, site_model, label, zenith_angle, single_mirror_mode, *, context=None)[source]#

Ray tracing with sim_telarray (see simtools.ray_tracing.backends.interface).

Parameters:
telescope_model: TelescopeModel

Telescope model.

site_model: SiteModel

Site model.

label: str

Label of the output files.

zenith_angle: float

Telescope zenith angle [deg].

single_mirror_mode: bool

Trace single mirrors.

incidence_angles(request, work_directory, name, with_mirrors, keep_files=False)[source]#

Per-photon incidence angles from a sim_telarray debug-trace run.

Parameters:
request: StarImageRequest

Star image (one instance; off_axis_x is the field angle).

work_directory: Path

Directory for logs, scripts and photon files.

name: str

Name of the run’s files.

with_mirrors: bool

Also angles and hit points on the primary and secondary mirror.

keep_files: bool

Keep the imaging list (not reported as intermediate file).

Returns:
tuple of dict and list

Angles [deg] and hit points [m] (angle_incidence_focal, and with mirrors angle_incidence_primary, angle_incidence_secondary, primary_hit_x, primary_hit_y, secondary_hit_x, secondary_hit_y), and the intermediate files written.

psf_image(request, focal_length, containment_fraction, use_rx=False, save_photons=False)[source]#

Return the PSF image of one simulated image.

Parameters:
request: StarImageRequest

Image.

focal_length: float

Focal length [cm].

containment_fraction: float

PSF containment fraction.

use_rx: bool

Analyse the photon list with sim_telarray’s rx.

save_photons: bool

Keep the photon and star lists; they are removed otherwise.

Returns:
tuple of PSFImage and bool

The image, and False (sim_telarray does not trace the telescope structure).

simulate(requests, test=False, force=False, compress_photons=True, max_workers=8)[source]#

Run one sim_telarray simulation per image; photon lists are written to disk.

Independent full-telescope simulations run concurrently. Single-mirror simulations run serially because their temporary camera files are shared.

Parameters:
requests: list of StarImageRequest

Images to trace.

test: bool

Test mode (single short run).

force: bool

Simulate again even if photon lists exist.

compress_photons: bool

Compress the photon lists (gzip).

max_workers: int

Maximum number of concurrent full-telescope simulations.

backends.obdeect#

Ray tracing with obdeect, in process.

obdeect compiles the model parameters of a telescope into an optical model and traces star light through the optics to the camera entrance. The light source, the telescope pointing and the parameter changes of the ray-tracing modes are those of the sim_telarray backend, so that both backends answer the same question. Images stay in memory.

obdeect also makes the light of calibration light sources (point emitters) and traces it to the camera entrance.

class ray_tracing.backends.obdeect.ObdeectBackend(telescope_model, site_model, label, zenith_angle, single_mirror_mode, *, context=None)[source]#

Ray tracing with obdeect, in process (see simtools.ray_tracing.backends.interface).

Parameters:
telescope_model: TelescopeModel

Telescope model.

site_model: SiteModel

Site model (not used: obdeect traces from the telescope entrance).

label: str

Label of the output files.

zenith_angle: float

Telescope zenith angle [deg].

single_mirror_mode: bool

Trace single mirrors.

camera_photons(request)[source]#

Photons of a calibration light source at the camera entrance, per pulse in blocks.

incidence_angles(request, work_directory, name, with_mirrors, keep_files=False)[source]#

Per-photon incidence angles (see the sim_telarray backend); no files are written.

Returns:
tuple of dict and list

Angles [deg] and hit points [m], and no intermediate files.

psf_image(request, focal_length, containment_fraction, use_rx=False, save_photons=False)[source]#

Return the PSF image of one traced image.

Parameters as for the sim_telarray backend; save_photons writes the photons as an ECSV table.

Returns:
tuple of PSFImage and bool

The image, and whether the telescope structure was traced.

simulate(requests, test=False, force=False, compress_photons=True, max_workers=8)[source]#

Trace the images; they are kept in memory.

test, force, compress_photons and max_workers concern photon files and processes and have no effect here.

class ray_tracing.backends.obdeect.ObdeectRayTracer(telescope_model)[source]#

Trace star light through one telescope model with obdeect.

Parameters:
telescope_model: TelescopeModel

Telescope model.

optical_model(telescope_zenith, instrument_seed, changes=None)[source]#

Optical model at a telescope zenith angle [deg] (cached).

instrument_seed fixes the random mirror errors of this instrument instance; changes are model-parameter values replacing those of the telescope model (see simtools.ray_tracing.parameter_changes).

trace_light_source(request)[source]#

Make and trace the light of a calibration light source, pulse by pulse.

Parameters:
request: LightSourceRequest

Light source.

Yields:
iterator of dict

The photons of one pulse crossing the camera plane, block by block (see simtools.ray_tracing.backends).

trace_star(zenith_angle, off_axis_x, off_axis_y, source_distance_m, photons_per_instance, seeds, changes=None)[source]#

Trace star light, as in the sim_telarray ray-tracing runs.

photons_per_instance photons for each random instrument instance, given by its (instrument seed, simulation seed) in seeds, as sim_telarray runs one simulation per instance; changes as in optical_model.

Returns:
dict

Arrival arrays (see obdeect.OpticalModel.trace) of the photons reaching the camera entrance, the number of photons traced and the area they were sampled on [m^2], and whether the optical model shadows with its own structure.

ray_tracing.backends.obdeect.star_direction(zenith_angle, off_axis_x, off_axis_y)[source]#

Return the unit vector to the star in the telescope frame (+z: optical axis).

parameter_changes#

Model-parameter changes of ray-tracing modes, the same for every backend.

Each mode is a set of model-parameter values replacing those of the telescope model: sim_telarray receives them as configuration options, other backends as changed parameter records.

ray_tracing.parameter_changes.sim_telarray_options(changes)[source]#

Return the changes as sim_telarray configuration options (name=value).

Options carry sim_telarray’s names of the model parameters; model parameters that sim_telarray does not read (e.g. the telescope structure) are left out. Names that are not model parameters are taken as sim_telarray keywords.

Parameters:
changes: dict

Model-parameter values.

Returns:
dict

Option values as sim_telarray reads them.

ray_tracing.parameter_changes.single_mirror(mirror_list, mirror_focal_length, camera_distance)[source]#

Return the changes of the single-mirror mode.

The mirror is placed alone at the dish centre and imaged from twice its focal length; the camera is at that distance.

Parameters:
mirror_list: str or Path

Mirror list with the single mirror.

mirror_focal_length: float

Mirror focal length [cm].

camera_distance: float

Distance of camera and light source from the mirror [cm].

Returns:
dict

Model-parameter values.

psf_analysis#

PSF image analysis for ray tracing results.

Processes photon position lists to compute image centroids, PSF containment diameters (D80 and other fractions), effective area, and cumulative radial distributions.

class ray_tracing.psf_analysis.PSFImage(focal_length=None, total_scattered_area=None, containment_fraction=None, context=None)[source]#

Image composed of list of photon positions (2D).

Load photon list from sim_telarray file and compute centroids, psf containers, effective area, as well as plot the image as a 2D histogram. Internal units: photon positions in cm internally.

Parameters:
focal_length: float

Focal length of the system in cm. If not given, PSF can only be computed in cm.

total_scattered_area: float

Scatter area of all photons in m^2. If not given, effective area cannot be computed.

containment_fraction: float

Containment fraction for PSF calculation.

get_cumulative_data(radius=None)[source]#

Compute cumulative intensity as function of radius from centroid.

For each radius, counts the fraction of total photons within that distance using binary search on pre-sorted radial distances.

Parameters:
radius: array

Array with radius calculate the cumulative PSF in distance units.

Returns:
(radius, intensity)
get_effective_area(tel_transmission=1.0)[source]#

Return effective area pre calculated.

Parameters:
telescope_transmissionfloat

Telescope transmission parameter.

Returns:
float

Pre-calculated effective area. None if it could not be calculated (e.g because the total scattering area was not set).

get_image_data(centralized=True)[source]#

Provide image data (2D photon positions in cm) as lists.

Parameters:
centralized: bool

Centroid of the image is set to (0, 0) if True.

Returns:
(x, y), the photons positions in cm.
get_psf(fraction=0.8, unit='cm')[source]#

Return PSF containment diameter.

Computes (if not cached) the diameter containing the specified fraction of photons using an iterative algorithm or brute-force scanning.

Parameters:
fraction: float

Fraction of photons within the containing radius.

unit: str

‘cm’ or ‘deg’. ‘deg’ will not work if focal length was not set.

Returns:
float:

Containing diameter for a certain intensity fraction (PSF).

plot_cumulative(file_name=None, psf_diameter_cm=None, **kwargs)[source]#

Plot cumulative data (intensity vs radius).

Parameters:
file_name: str

Name of the file to save the plot to.

psf_diameter_cm: float

PSF diameter value to be marked in the plot (in cm).

**kwargs:

Customization of line plot (e.g., color, linestyle, linewidth).

plot_image(centralized=True, file_name=None, **kwargs)[source]#

Plot 2D image as histogram (in cm).

Parameters:
centralized: bool

Centroid of the image is set to (0, 0) if True.

**kwargs:

image_* for the histogram plot and psf_* for the psf circle.

process_photon_list(photon_file, use_rx)[source]#

Read and process a photon list file generated by sim_telarray.

Extracts photon positions, computes centroids and effective area. Can use either sim_telarray’s rx binary or internal computation.

Parameters:
photons_file: str

Name of sim_telarray file with photon list.

use_rx: bool

Use the RX method for analysis.

read_photon_list_from_simtel_file(photons_file)[source]#

Read photon list file generated by sim_telarray and store the photon positions (2D).

Computes image centroid as mean of photon positions, effective area as (detected_photons * total_scattered_area) / total_photons, and sorts radial distances from centroid for efficient PSF calculation.

Parameters:
photons_file: str

Name of sim_telarray file with photon list.

Raises:
RuntimeError

If photon positions X and Y are not compatible or are empty.

set_photon_positions(x, y, total_photons, total_area)[source]#

Set the photon image and compute centroid, effective area and radii.

Parameters:
x, y: array_like

Photon positions in the camera plane [cm].

total_photons: int

Number of photons simulated (detected or not).

total_area: float

Area the photons were sampled on [m^2].

set_psf(value, fraction=0.8, unit='cm')[source]#

Set PSF calculated from other methods.

Parameters:
value: float

PSF value to be set

fraction: float

Fraction of photons within the containing radius.

unit: str

‘cm’ or ‘deg’. ‘deg’ will not work if focal length was not set.

psf_parameter_optimisation#

PSF parameter optimisation for the mirror alignment parameters.

Gradient descent minimising the RMSD (or KS statistic) of cumulative PSF curves. Logs and plots are written by simtools.ray_tracing.psf_parameter_reports.

class ray_tracing.psf_parameter_optimisation.GradientStepResult(params, psf_diameter, metric, p_value, simulated_data, step_accepted, learning_rate)[source]#

Outcome of one gradient descent step; params is None when the step failed.

class ray_tracing.psf_parameter_optimisation.PSFOptimizationJob(args, output_dir, context=None)[source]#

Serializable arguments and output directory for one complete PSF workflow.

class ray_tracing.psf_parameter_optimisation.PSFParameterOptimizer(tel_model, site_model, args_dict, data_to_plot, radius, output_dir, context=None)[source]#

Gradient descent optimizer for the PSF mirror alignment parameters.

Parameters:
tel_model, site_modelTelescopeModel, SiteModel

Telescope and site models.

args_dictdict

Simulation configuration arguments.

data_to_plotdict

Measured PSF data under the “measured” key.

radiusnumpy.ndarray

Radius values in cm for PSF evaluation.

output_dirPath

Directory for optimisation results and plots.

contextRunContext, optional

Run context.

analyze_monte_carlo_error(n_simulations=500)[source]#

Run Monte Carlo simulations of the initial parameters and summarise the spread.

apply_gradient_step(current_params, gradients, learning_rate)[source]#

Apply a gradient step; the zenith angle (index 1) of mirror alignment is kept.

calculate_gradient(current_params, current_metric, epsilon=0.0005)[source]#

Return the finite-difference gradient per parameter; None if an evaluation failed.

get_initial_parameters()[source]#

Return the current PSF parameter values of the telescope model.

perform_gradient_step_with_retries(current_params, current_metric, learning_rate, max_retries=3)[source]#

Attempt a gradient descent step with adaptive learning rate reduction.

Returns:
GradientStepResult

Updated parameters and step status.

run_gradient_descent(rmsd_threshold, learning_rate, max_iterations=200)[source]#

Run gradient descent; returns (best_params, best_psf_diameter, results).

run_simulation(pars, pdf_pages=None, is_best=False, use_cache=True, use_ks_statistic=None)[source]#

Run a PSF simulation (cached unless plotting); returns (diameter, metric, p, data).

ray_tracing.psf_parameter_optimisation.calculate_ks_statistic(data, sim)[source]#

Return the two-sample KS test between measured and simulated cumulative PSF curves.

ray_tracing.psf_parameter_optimisation.calculate_rmsd(data, sim)[source]#

Return the RMSD between measured and simulated cumulative PSF curves.

ray_tracing.psf_parameter_optimisation.export_psf_parameters(best_pars, telescope, parameter_version, output_dir, context=None)[source]#

Export the optimised PSF parameters as simulation model parameter files.

One JSON file per parameter: {output_dir}/{telescope}/{name}-{version}.json. Errors are logged, not raised.

ray_tracing.psf_parameter_optimisation.get_previous_values(tel_model)[source]#

Return the current horizontal and vertical mirror alignment parameter values.

ray_tracing.psf_parameter_optimisation.load_and_process_data(args_dict)[source]#

Load the measured PSF data from an ECSV file.

Returns:
tuple of (OrderedDict, array)

Normalised cumulative PSF under “measured”, and the radius values in cm.

ray_tracing.psf_parameter_optimisation.run_psf_optimization_job(job)[source]#

Initialize the controller models and run the PSF optimisation workflow.

ray_tracing.psf_parameter_optimisation.run_psf_optimization_workflow(tel_model, site_model, args_dict, output_dir, context=None)[source]#

Run the PSF parameter optimisation workflow using gradient descent.

ray_tracing.psf_parameter_optimisation.run_psf_simulation(tel_model, site, args_dict, pars, data_to_plot, radius, pdf_pages=None, is_best=False, use_ks_statistic=False, context=None)[source]#

Run one ray tracing PSF simulation and calculate the optimisation metric.

Parameters:
tel_model, siteTelescopeModel, Site

Telescope and site models.

args_dictdict

Simulation configuration arguments.

parsdict

Parameter values to test.

data_to_plotdict

Measured PSF data under the “measured” key.

radiusnumpy.ndarray

Radius values in cm for PSF evaluation.

pdf_pagesPDF report, optional

Open report for adding plots.

is_bestbool, optional

True if this is the best parameter set.

use_ks_statisticbool, optional

Use the KS statistic as metric instead of RMSD.

contextRunContext, optional

Run context for ray tracing.

Returns:
tuple of (float, float, float or None, array)

PSF diameter [cm], metric, p-value (None for RMSD), simulated PSF data.

psf_parameter_reports#

Output of the PSF parameter optimisation.

Writes the gradient descent and Monte Carlo log files, the optimisation plots, the PSF diameter versus off-axis angle plot, and the PDF reports.

ray_tracing.psf_parameter_reports.create_optimization_plots(args_dict, gd_results, tel_model, data_to_plot, output_dir)[source]#

Create PSF plots for every fifth optimisation iteration and the final one.

Parameters:
args_dictdict

Command-line arguments; plots are created only if save_plots is set.

gd_resultslist

(params, rmsd, _, psf_diameter, _) tuples from gradient descent.

tel_modelTelescopeModel

Telescope model object for naming files.

data_to_plotdict

Measured PSF data.

output_dirPath

Directory where the PDF file is saved.

ray_tracing.psf_parameter_reports.create_psf_vs_offaxis_plot(tel_model, site_model, args_dict, best_pars, output_dir, context=None)[source]#

Plot the PSF diameter versus off-axis angle using the best parameters.

Parameters:
tel_modelTelescopeModel

Telescope model object.

site_modelSiteModel

Site model object.

args_dictdict

Parsed command-line arguments.

best_parsdict

Best parameter set.

output_dirPath

Output directory for plots.

contextRunContext, optional

Run context for ray tracing.

ray_tracing.psf_parameter_reports.setup_pdf_plotting(args_dict, output_dir, tel_model_name)[source]#

Set up a PDF report for the gradient descent plots if plot_all is requested.

Returns:
context manager

PDF report context manager, or a null context if plotting is not requested.

ray_tracing.psf_parameter_reports.write_gradient_descent_log(gd_results, best_pars, best_psf_diameter, output_dir, tel_model, use_ks_statistic=False, fraction=0.8)[source]#

Write the gradient descent progression to a log file.

Parameters:
gd_resultslist

(params, metric, p_value, psf_diameter, simulated_data) per iteration.

best_parsdict

Best parameter values found.

best_psf_diameterfloat

PSF containment diameter in cm for the best parameter set.

output_dirPath

Directory where the log file is written.

tel_modelTelescopeModel

Telescope model used to name the output file.

use_ks_statisticbool, optional

Log the KS statistic instead of the RMSD.

fractionfloat, optional

PSF containment fraction for labeling.

Returns:
Path

Path to the created log file.

ray_tracing.psf_parameter_reports.write_monte_carlo_analysis(mc_results, output_dir, tel_model, use_ks_statistic=False, fraction=0.8)[source]#

Write the Monte Carlo uncertainty analysis to a log file.

Parameters:
mc_resultstuple

Results of PSFParameterOptimizer.analyze_monte_carlo_error.

output_dirPath

Directory where the log file is written.

tel_modelTelescopeModel

Telescope model used to name the output file.

use_ks_statisticbool, optional

Analyze the KS statistic instead of the RMSD.

fractionfloat, optional

PSF containment fraction for labeling.

Returns:
Path

Path to the created log file.

mirror_panel_psf#

Mirror panel PSF calculation with per-mirror PSF diameter optimization.

class ray_tracing.mirror_panel_psf.MirrorOptimizationJob(label, args, mirror_index, measured_psf)[source]#

Compact input required to reconstruct one mirror optimization.

class ray_tracing.mirror_panel_psf.MirrorOptimizationResult(mirror, measured_psf_mm, optimized_rnda, simulated_psf_mm, percentage_diff, containment_fraction)[source]#

Dataclass to store the result of a single mirror RNDA optimization.

class ray_tracing.mirror_panel_psf.MirrorPanelPSF(label, args_dict, model_directory=None, *, context=None)[source]#

Mirror panel PSF and random reflection angle (RNDA) calculation.

This class derives the RNDA for mirror panels by optimizing per-mirror PSF diameters (at a given containment fraction) using a percentage difference metric. Optimization uses a gradient descent with finite-difference gradients.

Parameters:
labelstr

Application label.

args_dictdict

Dictionary with input arguments, e.g. site, telescope, data path, model version.

model_directoryPath, optional

Directory of the model configuration files.

optimize_single_mirror(mirror_idx, measured_psf)[source]#

Optimize RNDA for a single mirror using gradient descent.

Parameters:
mirror_idxint

Zero-based mirror index.

measured_psffloat

Measured PSF diameter in mm.

Returns:
MirrorOptimizationResult

Optimization results for the mirror.

optimize_with_gradient_descent()[source]#

Optimize all mirrors in parallel using process pool.

Notes

This method updates these instance attributes: - per_mirror_results (list of MirrorOptimizationResult) - rnda_opt (list of float) - final_percentage_diff (float)

write_optimization_data()[source]#

Write optimization results and optionally export as a model parameter.

write_psf_histogram()[source]#

Plot histogram of measured vs simulated psf values.

optics_validation#

Validating telescope optical model parameters via ray tracing.

ray_tracing.optics_validation.load_data(datafile)[source]#

Load the data file with the measured PSF vs radius [cm].

Parameters:
datafilestr or Path

Path to the file containing the measured cumulative PSF. Expected columns: radial distance in mm, differential intensity, integral intensity.

Returns:
numpy.ndarray

Structured array with fields ‘Radius [cm]’ and ‘Relative intensity’.

ray_tracing.optics_validation.validate_cumulative_psf(app_context)[source]#

Simulate PSF measurements and compare cumulative PSF with measured data.

Parameters:
app_contextobject

Application context with args and paths attributes.

Raises:
ValueError

If measured PSF data is not provided.

ray_tracing.optics_validation.validate_optics(app_context)[source]#

Build telescope model, run ray tracing, plot PSF/area/focal-length results.

Parameters:
app_contextobject

Application context with args and paths attributes.

Prepare isolated PSF evaluation jobs for local and remote execution.

simtools.ray_tracing.psf_evaluation.dispatch_evaluations(parameters, args, measured_data, radius, output_dir, max_workers, worker, context=None)[source]#

Execute a batch with independent output paths and portable runtime settings.

telescope_shadowing#

Ray tracing calculations for telescope-structure shadowing.

ray_tracing.telescope_shadowing.shadowing_shares(tel_model, zenith_deg, photons, seed)[source]#

Light each structure element stops on its own, from obdeect.

Parameters:
tel_modelTelescopeModel

Telescope model whose optical structure is traced.

zenith_degfloat

Zenith angle of the star in degrees.

photonsint

Number of photons to trace.

seedint

Random seed for photon generation.

Returns:
dict

Element name -> (share of the unobscured light stopped by the element alone in per cent, number of photons whose path crosses the element).