From c850540bad24c49bb1ce5b3b1034e374abbcc0e6 Mon Sep 17 00:00:00 2001 From: Maximilian Linhoff Date: Tue, 13 Jun 2023 18:26:58 +0200 Subject: [PATCH 1/5] Add support for using toymodel in telescope frame --- ctapipe/image/tests/test_toy.py | 120 +++++++++++++++++++++---------- ctapipe/image/toymodel.py | 122 +++++++++++++++++--------------- ctapipe/io/toymodel.py | 2 +- 3 files changed, 147 insertions(+), 97 deletions(-) diff --git a/ctapipe/image/tests/test_toy.py b/ctapipe/image/tests/test_toy.py index 85f762421af..8229163fc64 100644 --- a/ctapipe/image/tests/test_toy.py +++ b/ctapipe/image/tests/test_toy.py @@ -6,11 +6,13 @@ from pytest import approx from scipy.stats import norm, poisson, skewnorm +from ctapipe.coordinates.telescope_frame import TelescopeFrame from ctapipe.image.toymodel import WaveformModel, obtain_time_image +@pytest.mark.parametrize("frame", ["telescope", "camera"]) @pytest.mark.parametrize("seed", [None, 0]) -def test_intensity(seed, monkeypatch, prod5_lst): +def test_intensity(seed, frame, monkeypatch, prod5_lst): """ Test generation of the toymodel roughly follows the given intensity. @@ -19,13 +21,19 @@ def test_intensity(seed, monkeypatch, prod5_lst): """ from ctapipe.image import toymodel - geom = prod5_lst.camera.geometry + if frame == "camera": + geom = prod5_lst.camera.geometry + unit = u.m + else: + geom = prod5_lst.camera.geometry.transform_to(TelescopeFrame()) + unit = u.deg + + x, y = u.Quantity([0.2, 0.3], unit) + width = 0.05 * unit + length = 0.15 * unit - x, y = u.Quantity([0.2, 0.3], u.m) - width = 0.05 * u.m - length = 0.15 * u.m intensity = 200 - psi = "30d" + psi = 30 * u.deg # make sure we set a fixed seed for this test even when testing the # API without giving the rng @@ -43,33 +51,43 @@ def test_intensity(seed, monkeypatch, prod5_lst): ) # test if signal reproduces given cog values - assert np.average(geom.pix_x.to_value(u.m), weights=signal) == approx(0.2, rel=0.15) - assert np.average(geom.pix_y.to_value(u.m), weights=signal) == approx(0.3, rel=0.15) + assert np.average(geom.pix_x.to_value(unit), weights=signal) == approx( + 0.2, rel=0.15 + ) + assert np.average(geom.pix_y.to_value(unit), weights=signal) == approx( + 0.3, rel=0.15 + ) # test if signal reproduces given width/length values cov = np.cov(geom.pix_x.value, geom.pix_y.value, aweights=signal) eigvals, _ = np.linalg.eigh(cov) - assert np.sqrt(eigvals[0]) == approx(width.to_value(u.m), rel=0.15) - assert np.sqrt(eigvals[1]) == approx(length.to_value(u.m), rel=0.15) + assert np.sqrt(eigvals[0]) == approx(width.to_value(unit), rel=0.15) + assert np.sqrt(eigvals[1]) == approx(length.to_value(unit), rel=0.15) # test if total intensity is inside in 99 percent confidence interval assert poisson(intensity).ppf(0.05) <= signal.sum() <= poisson(intensity).ppf(0.95) -def test_skewed(prod5_lst): +@pytest.mark.parametrize("frame", ["telescope", "camera"]) +def test_skewed(frame, prod5_lst): from ctapipe.image.toymodel import SkewedGaussian # test if the parameters we calculated for the skew normal # distribution produce the correct moments rng = np.random.default_rng(0) - geom = prod5_lst.camera.geometry + if frame == "camera": + geom = prod5_lst.camera.geometry + unit = u.m + else: + geom = prod5_lst.camera.geometry.transform_to(TelescopeFrame()) + unit = u.deg - x, y = u.Quantity([0.2, 0.3], u.m) - width = 0.05 * u.m - length = 0.15 * u.m + x, y = u.Quantity([0.2, 0.3], unit) + width = 0.05 * unit + length = 0.15 * unit intensity = 50 - psi = "30d" + psi = 30 * u.deg skewness = 0.3 model = SkewedGaussian( @@ -81,20 +99,26 @@ def test_skewed(prod5_lst): mean, var, skew = skewnorm(a=a, loc=loc, scale=scale).stats(moments="mvs") assert np.isclose(mean, 0) - assert np.isclose(var, length.to_value(u.m) ** 2) + assert np.isclose(var, length.to_value(unit) ** 2) assert np.isclose(skew, skewness) -def test_compare(prod5_lst): +@pytest.mark.parametrize("frame", ["telescope", "camera"]) +def test_compare(frame, prod5_lst): from ctapipe.image.toymodel import Gaussian, SkewedGaussian - geom = prod5_lst.camera.geometry + if frame == "camera": + geom = prod5_lst.camera.geometry + unit = u.m + else: + geom = prod5_lst.camera.geometry.transform_to(TelescopeFrame()) + unit = u.deg - x, y = u.Quantity([0.2, 0.3], u.m) - width = 0.05 * u.m - length = 0.15 * u.m + x, y = u.Quantity([0.2, 0.3], unit) + width = 0.05 * unit + length = 0.15 * unit intensity = 50 - psi = "30d" + psi = 30 * u.deg skewed = SkewedGaussian(x=x, y=y, width=width, length=length, psi=psi, skewness=0) normal = Gaussian(x=x, y=y, width=width, length=length, psi=psi) @@ -105,14 +129,29 @@ def test_compare(prod5_lst): assert np.isclose(signal_skewed, signal_normal).all() -def test_obtain_time_image(prod5_sst): +@pytest.mark.parametrize("frame", ["telescope", "camera"]) +def test_obtain_time_image(frame, prod5_sst): geom = prod5_sst.camera.geometry - centroid_x = u.Quantity(0.05, u.m) - centroid_y = u.Quantity(0.05, u.m) + if frame == "camera": + geom = prod5_sst.camera.geometry + unit = u.m + scale = 1.0 + else: + geom_cam_frame = prod5_sst.camera.geometry + geom = geom_cam_frame.transform_to(TelescopeFrame()) + unit = u.deg + # further down we test the std deviation, but that scales + # with the size of our shower in the camera + r_tel_frame = geom.guess_radius().to_value(u.deg) + r_cam_frame = geom_cam_frame.guess_radius().to_value(u.m) + scale = r_tel_frame / r_cam_frame + + centroid_x = u.Quantity(0.05, unit) + centroid_y = u.Quantity(0.05, unit) psi = u.Quantity(70, u.deg) - time_gradient = u.Quantity(0, u.ns / u.m) + time_gradient = u.Quantity(0, u.ns / unit) time_intercept = u.Quantity(0, u.ns) time = obtain_time_image( geom.pix_x, @@ -125,7 +164,7 @@ def test_obtain_time_image(prod5_sst): ) np.testing.assert_allclose(time, 0) - time_gradient = u.Quantity(0, u.ns / u.m) + time_gradient = u.Quantity(0, u.ns / unit) time_intercept = u.Quantity(40, u.ns) time = obtain_time_image( geom.pix_x, @@ -138,7 +177,7 @@ def test_obtain_time_image(prod5_sst): ) np.testing.assert_allclose(time, 40) - time_gradient = u.Quantity(20, u.ns / u.m) + time_gradient = u.Quantity(20 / scale, u.ns / unit) time_intercept = u.Quantity(40, u.ns) time = obtain_time_image( geom.pix_x, @@ -151,7 +190,7 @@ def test_obtain_time_image(prod5_sst): ) np.testing.assert_allclose(time.std(), 1.710435, rtol=0.1) - time_gradient = u.Quantity(20, u.ns / u.m) + time_gradient = u.Quantity(20, u.ns / unit) time_intercept = u.Quantity(40, u.ns) time = obtain_time_image( centroid_x, @@ -165,13 +204,20 @@ def test_obtain_time_image(prod5_sst): np.testing.assert_allclose(time, 40) -def test_waveform_model(prod5_sst): +@pytest.mark.parametrize("frame", ["telescope", "camera"]) +def test_waveform_model(frame, prod5_sst): from ctapipe.image.toymodel import Gaussian prod5_sst = deepcopy(prod5_sst) - geom = prod5_sst.camera.geometry readout = prod5_sst.camera.readout + if frame == "camera": + geom = prod5_sst.camera.geometry + unit = u.m + else: + geom = prod5_sst.camera.geometry.transform_to(TelescopeFrame()) + unit = u.deg + ref_duration = 67 n_ref_samples = 100 pulse_sigma = 3 @@ -184,12 +230,12 @@ def test_waveform_model(prod5_sst): ) readout.sampling_rate = u.Quantity(2, u.GHz) - centroid_x = u.Quantity(0.05, u.m) - centroid_y = u.Quantity(0.05, u.m) - length = u.Quantity(0.03, u.m) - width = u.Quantity(0.008, u.m) + centroid_x = u.Quantity(0.05, unit) + centroid_y = u.Quantity(0.05, unit) + length = u.Quantity(0.03, unit) + width = u.Quantity(0.008, unit) psi = u.Quantity(70, u.deg) - time_gradient = u.Quantity(50, u.ns / u.m) + time_gradient = u.Quantity(50, u.ns / unit) time_intercept = u.Quantity(20, u.ns) _, charge, _ = Gaussian( diff --git a/ctapipe/image/toymodel.py b/ctapipe/image/toymodel.py index bb9a9f5abf6..5fb17ca48d9 100644 --- a/ctapipe/image/toymodel.py +++ b/ctapipe/image/toymodel.py @@ -18,15 +18,16 @@ >>> print(image.shape) (400,) """ -import numpy as np -from ctapipe.utils import linalg -from ctapipe.image.hillas import camera_to_shower_coordinates -import astropy.units as u -from astropy.coordinates import Angle -from scipy.stats import multivariate_normal, skewnorm, norm -from scipy.ndimage import convolve1d from abc import ABCMeta, abstractmethod + +import astropy.units as u +import numpy as np from numpy.random import default_rng +from scipy.ndimage import convolve1d +from scipy.stats import multivariate_normal, norm, skewnorm + +from ctapipe.image.hillas import camera_to_shower_coordinates +from ctapipe.utils import linalg __all__ = [ "WaveformModel", @@ -40,15 +41,13 @@ TOYMODEL_RNG = default_rng(0) -@u.quantity_input( - x=u.m, - y=u.m, - centroid_x=u.m, - centroid_y=u.m, - psi=u.deg, - time_gradient=u.ns / u.m, - time_intercept=u.ns, -) +def _obtain_image_no_units( + x, y, centroid_x, centroid_y, psi, time_gradient, time_intercept +): + longitudinal, _ = camera_to_shower_coordinates(x, y, centroid_x, centroid_y, psi) + return longitudinal * time_gradient + time_intercept + + def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_intercept): """Create a pulse time image for a toymodel shower. Assumes the time development occurs only along the longitudinal (major) axis of the shower, and scales @@ -56,19 +55,19 @@ def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_int Parameters ---------- - x : u.Quantity[length] + x : u.Quantity[angle] X camera coordinate to evaluate the time at. Usually the array of pixel X positions - y : u.Quantity[length] + y : u.Quantity[angle] Y camera coordinate to evaluate the time at. Usually the array of pixel Y positions - centroid_x : u.Quantity[length] + centroid_x : u.Quantity[angle] X camera coordinate for the centroid of the shower - centroid_y : u.Quantity[length] + centroid_y : u.Quantity[angle] Y camera coordinate for the centroid of the shower psi : convertible to `astropy.coordinates.Angle` rotation angle about the centroid (0=x-axis) - time_gradient : u.Quantity[time/length] + time_gradient : u.Quantity[time/angle] Rate at which the time changes with distance along the shower axis time_intercept : u.Quantity[time] Pulse time at the shower centroid @@ -79,11 +78,17 @@ def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_int Pulse time in nanoseconds at (x, y) """ - longitudinal, _ = camera_to_shower_coordinates(x, y, centroid_x, centroid_y, psi) - longitudinal_m = longitudinal.to_value(u.m) - time_gradient_ns_m = time_gradient.to_value(u.ns / u.m) - time_intercept_ns = time_intercept.to_value(u.ns) - return longitudinal_m * time_gradient_ns_m + time_intercept_ns + unit = x.unit + # camera frame + return _obtain_image_no_units( + x=x.to_value(unit), + y=y.to_value(unit), + centroid_x=centroid_x.to_value(unit), + centroid_y=centroid_y.to_value(unit), + psi=psi.to_value(u.rad), + time_gradient=time_gradient.to_value(u.ns / unit), + time_intercept=time_intercept.to_value(u.ns), + ) class WaveformModel: @@ -183,11 +188,9 @@ def from_camera_readout(cls, readout, gain_channel=0): class ImageModel(metaclass=ABCMeta): - @u.quantity_input(x=u.m, y=u.m) @abstractmethod def pdf(self, x, y): - """Probability density function. - """ + """Probability density function.""" def generate_image(self, camera, intensity=50, nsb_level_pe=20, rng=None): """Generate a randomized DL1 shower image. @@ -244,7 +247,6 @@ def expected_signal(self, camera, intensity): class Gaussian(ImageModel): - @u.quantity_input(x=u.m, y=u.m, length=u.m, width=u.m) def __init__(self, x, y, length, width, psi): """Create 2D Gaussian model for a shower image in a camera. @@ -264,32 +266,36 @@ def __init__(self, x, y, length, width, psi): a `scipy.stats` object """ + self.unit = x.unit self.x = x self.y = y self.width = width self.length = length self.psi = psi - @u.quantity_input(x=u.m, y=u.m) - def pdf(self, x, y): - """2d probability for photon electrons in the camera plane""" aligned_covariance = np.array( - [[self.length.to_value(u.m) ** 2, 0], [0, self.width.to_value(u.m) ** 2]] + [ + [self.length.to_value(self.unit) ** 2, 0], + [0, self.width.to_value(self.unit) ** 2], + ] ) # rotate by psi angle: C' = R C R+ rotation = linalg.rotation_matrix_2d(self.psi) rotated_covariance = rotation @ aligned_covariance @ rotation.T + self.dist = multivariate_normal( + mean=u.Quantity([self.x, self.y]).to_value(self.unit), + cov=rotated_covariance, + ) - return multivariate_normal( - mean=[self.x.to_value(u.m), self.y.to_value(u.m)], cov=rotated_covariance - ).pdf(np.column_stack([x.to_value(u.m), y.to_value(u.m)])) + def pdf(self, x, y): + """2d probability for photon electrons in the camera plane""" + X = np.column_stack([x.to_value(self.unit), y.to_value(self.unit)]) + return self.dist.pdf(X) class SkewedGaussian(ImageModel): - """A shower image that has a skewness along the major axis. - """ + """A shower image that has a skewness along the major axis.""" - @u.quantity_input(x=u.m, y=u.m, length=u.m, width=u.m) def __init__(self, x, y, length, width, psi, skewness): """Create 2D skewed Gaussian model for a shower image in a camera. Skewness is only applied along the main shower axis. @@ -313,6 +319,7 @@ def __init__(self, x, y, length, width, psi, skewness): a `scipy.stats` object """ + self.unit = x.unit self.x = x self.y = y self.width = width @@ -320,6 +327,12 @@ def __init__(self, x, y, length, width, psi, skewness): self.psi = psi self.skewness = skewness + a, loc, scale = self._moments_to_parameters() + self.long_dist = skewnorm(a=a, loc=loc, scale=scale) + self.trans_dist = norm(loc=0, scale=self.width.to_value(self.unit)) + self.rotation = linalg.rotation_matrix_2d(-self.psi) + self.mu = u.Quantity([self.x, self.y]).to_value(self.unit) + def _moments_to_parameters(self): """Returns loc and scale from mean, std and skewnewss.""" # see https://en.wikipedia.org/wiki/Skew_normal_distribution#Estimation @@ -327,26 +340,17 @@ def _moments_to_parameters(self): delta = np.sign(self.skewness) * np.sqrt( (np.pi / 2 * skew23) / (skew23 + (0.5 * (4 - np.pi)) ** (2 / 3)) ) - a = delta / np.sqrt(1 - delta ** 2) - scale = self.length.to_value(u.m) / np.sqrt(1 - 2 * delta ** 2 / np.pi) + a = delta / np.sqrt(1 - delta**2) + scale = self.length.to_value(self.unit) / np.sqrt(1 - 2 * delta**2 / np.pi) loc = -scale * delta * np.sqrt(2 / np.pi) return a, loc, scale - @u.quantity_input(x=u.m, y=u.m) def pdf(self, x, y): """2d probability for photon electrons in the camera plane.""" - mu = u.Quantity([self.x, self.y]).to_value(u.m) - - rotation = linalg.rotation_matrix_2d(-Angle(self.psi)) - pos = np.column_stack([x.to_value(u.m), y.to_value(u.m)]) - long, trans = rotation @ (pos - mu).T - - trans_pdf = norm(loc=0, scale=self.width.to_value(u.m)).pdf(trans) - - a, loc, scale = self._moments_to_parameters() - - return trans_pdf * skewnorm(a=a, loc=loc, scale=scale).pdf(long) + pos = np.column_stack([x.to_value(self.unit), y.to_value(self.unit)]) + long, trans = self.rotation @ (pos - self.mu).T + return self.trans_dist.pdf(trans) * self.long_dist.pdf(long) class RingGaussian(ImageModel): @@ -355,17 +359,17 @@ class RingGaussian(ImageModel): """ - @u.quantity_input(x=u.m, y=u.m, radius=u.m, sigma=u.m) def __init__(self, x, y, radius, sigma): + self.unit = x.unit self.x = x self.y = y self.sigma = sigma self.radius = radius + self.dist = norm( + self.radius.to_value(self.unit), self.sigma.to_value(self.unit) + ) - @u.quantity_input(x=u.m, y=u.m) def pdf(self, x, y): """2d probability for photon electrons in the camera plane.""" - r = np.sqrt((x - self.x) ** 2 + (y - self.y) ** 2) - - return norm(self.radius.to_value(u.m), self.sigma.to_value(u.m)).pdf(r) + return self.dist.pdf(r) diff --git a/ctapipe/io/toymodel.py b/ctapipe/io/toymodel.py index 6e24d04d04d..05477f4e41b 100644 --- a/ctapipe/io/toymodel.py +++ b/ctapipe/io/toymodel.py @@ -145,7 +145,7 @@ def generate_event(self): y=y, length=length * u.m, width=width * u.m, - psi=f"{psi}d", + psi=psi * u.deg, skewness=skewness, ) image, _, _ = model.generate_image(cam, intensity) From e18d5f4b83f0c2077e88534a5cf7c650f0079d9f Mon Sep 17 00:00:00 2001 From: Maximilian Linhoff Date: Mon, 19 Jun 2023 11:26:36 +0200 Subject: [PATCH 2/5] Update docstrings --- ctapipe/image/toymodel.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ctapipe/image/toymodel.py b/ctapipe/image/toymodel.py index 5fb17ca48d9..ba88fb1479b 100644 --- a/ctapipe/image/toymodel.py +++ b/ctapipe/image/toymodel.py @@ -55,15 +55,15 @@ def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_int Parameters ---------- - x : u.Quantity[angle] + x : u.Quantity[angle|length] X camera coordinate to evaluate the time at. Usually the array of pixel X positions - y : u.Quantity[angle] + y : u.Quantity[angle|length] Y camera coordinate to evaluate the time at. Usually the array of pixel Y positions - centroid_x : u.Quantity[angle] + centroid_x : u.Quantity[angle|length] X camera coordinate for the centroid of the shower - centroid_y : u.Quantity[angle] + centroid_y : u.Quantity[angle|length] Y camera coordinate for the centroid of the shower psi : convertible to `astropy.coordinates.Angle` rotation angle about the centroid (0=x-axis) @@ -79,7 +79,6 @@ def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_int """ unit = x.unit - # camera frame return _obtain_image_no_units( x=x.to_value(unit), y=y.to_value(unit), @@ -252,13 +251,13 @@ def __init__(self, x, y, length, width, psi): Parameters ---------- - centroid : u.Quantity[length, shape=(2, )] + centroid : u.Quantity[angle|length, shape=(2, )] position of the centroid of the shower in camera coordinates - width: u.Quantity[length] + width: u.Quantity[angle|length] width of shower (minor axis) - length: u.Quantity[length] + length: u.Quantity[angle|length] length of shower (major axis) - psi : convertable to `astropy.coordinates.Angle` + psi : u.Quantity[angle] rotation angle about the centroid (0=x-axis) Returns @@ -305,13 +304,13 @@ def __init__(self, x, y, length, width, psi, skewness): Parameters ---------- - centroid : u.Quantity[length, shape=(2, )] + centroid : u.Quantity[angle|length, shape=(2, )] position of the centroid of the shower in camera coordinates - width: u.Quantity[length] + width: u.Quantity[angle|length] width of shower (minor axis) - length: u.Quantity[length] + length: u.Quantity[angle|length] length of shower (major axis) - psi : convertable to `astropy.coordinates.Angle` + psi : u.Quantity[angle] rotation angle about the centroid (0=x-axis) Returns @@ -355,8 +354,8 @@ def pdf(self, x, y): class RingGaussian(ImageModel): """A shower image consisting of a ring with gaussian radial profile. - Simplified model for a muon ring. + Simplified model for a muon ring. """ def __init__(self, x, y, radius, sigma): From 195fdb8970034057069913b18afa8d8c9464284d Mon Sep 17 00:00:00 2001 From: Maximilian Linhoff Date: Mon, 19 Jun 2023 12:45:05 +0200 Subject: [PATCH 3/5] Remove unneeded funciton --- ctapipe/image/toymodel.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/ctapipe/image/toymodel.py b/ctapipe/image/toymodel.py index ba88fb1479b..016757198c0 100644 --- a/ctapipe/image/toymodel.py +++ b/ctapipe/image/toymodel.py @@ -41,13 +41,6 @@ TOYMODEL_RNG = default_rng(0) -def _obtain_image_no_units( - x, y, centroid_x, centroid_y, psi, time_gradient, time_intercept -): - longitudinal, _ = camera_to_shower_coordinates(x, y, centroid_x, centroid_y, psi) - return longitudinal * time_gradient + time_intercept - - def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_intercept): """Create a pulse time image for a toymodel shower. Assumes the time development occurs only along the longitudinal (major) axis of the shower, and scales @@ -79,15 +72,16 @@ def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_int """ unit = x.unit - return _obtain_image_no_units( - x=x.to_value(unit), - y=y.to_value(unit), - centroid_x=centroid_x.to_value(unit), - centroid_y=centroid_y.to_value(unit), - psi=psi.to_value(u.rad), - time_gradient=time_gradient.to_value(u.ns / unit), - time_intercept=time_intercept.to_value(u.ns), - ) + x = x.to_value(unit) + y = y.to_value(unit) + centroid_x = centroid_x.to_value(unit) + centroid_y = centroid_y.to_value(unit) + psi = psi.to_value(u.rad) + time_gradient = time_gradient.to_value(u.ns / unit) + time_intercept = time_intercept.to_value(u.ns) + + longitudinal, _ = camera_to_shower_coordinates(x, y, centroid_x, centroid_y, psi) + return longitudinal * time_gradient + time_intercept class WaveformModel: From 98bf240d672fc550234b841015fe3c148523cf09 Mon Sep 17 00:00:00 2001 From: Maximilian Linhoff Date: Mon, 19 Jun 2023 12:46:19 +0200 Subject: [PATCH 4/5] Only use length in docstring --- ctapipe/image/toymodel.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ctapipe/image/toymodel.py b/ctapipe/image/toymodel.py index 016757198c0..7c1575e75c4 100644 --- a/ctapipe/image/toymodel.py +++ b/ctapipe/image/toymodel.py @@ -48,15 +48,15 @@ def obtain_time_image(x, y, centroid_x, centroid_y, psi, time_gradient, time_int Parameters ---------- - x : u.Quantity[angle|length] + x : u.Quantity[length] X camera coordinate to evaluate the time at. Usually the array of pixel X positions - y : u.Quantity[angle|length] + y : u.Quantity[length] Y camera coordinate to evaluate the time at. Usually the array of pixel Y positions - centroid_x : u.Quantity[angle|length] + centroid_x : u.Quantity[length] X camera coordinate for the centroid of the shower - centroid_y : u.Quantity[angle|length] + centroid_y : u.Quantity[length] Y camera coordinate for the centroid of the shower psi : convertible to `astropy.coordinates.Angle` rotation angle about the centroid (0=x-axis) @@ -245,11 +245,11 @@ def __init__(self, x, y, length, width, psi): Parameters ---------- - centroid : u.Quantity[angle|length, shape=(2, )] + centroid : u.Quantity[length, shape=(2, )] position of the centroid of the shower in camera coordinates - width: u.Quantity[angle|length] + width: u.Quantity[length] width of shower (minor axis) - length: u.Quantity[angle|length] + length: u.Quantity[length] length of shower (major axis) psi : u.Quantity[angle] rotation angle about the centroid (0=x-axis) @@ -298,11 +298,11 @@ def __init__(self, x, y, length, width, psi, skewness): Parameters ---------- - centroid : u.Quantity[angle|length, shape=(2, )] + centroid : u.Quantity[length, shape=(2, )] position of the centroid of the shower in camera coordinates - width: u.Quantity[angle|length] + width: u.Quantity[length] width of shower (minor axis) - length: u.Quantity[angle|length] + length: u.Quantity[length] length of shower (major axis) psi : u.Quantity[angle] rotation angle about the centroid (0=x-axis) From 974f7bc1bca19d08812444acacb799ab06638036 Mon Sep 17 00:00:00 2001 From: Maximilian Linhoff Date: Mon, 19 Jun 2023 12:47:24 +0200 Subject: [PATCH 5/5] Add docstring --- docs/changes/2349.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/changes/2349.feature.rst diff --git a/docs/changes/2349.feature.rst b/docs/changes/2349.feature.rst new file mode 100644 index 00000000000..8eb063a77fb --- /dev/null +++ b/docs/changes/2349.feature.rst @@ -0,0 +1 @@ +Add support for using ``ctapipe.image.toymodel`` features in ``TelescopeFrame``.