Skip to content

Commit 7ebab73

Browse files
Jammy2211Jammy2211
authored andcommitted
merge
2 parents df98a0c + 42b4c71 commit 7ebab73

9 files changed

Lines changed: 149 additions & 180 deletions

File tree

autogalaxy/ellipse/ellipse/ellipse.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def total_points_from(self, pixel_scale: float) -> int:
106106

107107
return np.min([500, int(np.round(circular_radius_pixels, 1))])
108108

109-
def angles_from_x0_from(self, pixel_scale: float, n_i : int = 0) -> np.ndarray:
109+
def angles_from_x0_from(self, pixel_scale: float, n_i: int = 0) -> np.ndarray:
110110
"""
111111
Returns the angles from the x-axis to a discrete number of points ranging from 0.0 to 2.0 * np.pi radians.
112112
@@ -138,7 +138,9 @@ def angles_from_x0_from(self, pixel_scale: float, n_i : int = 0) -> np.ndarray:
138138

139139
return np.linspace(0.0, 2.0 * np.pi, total_points + n_i)[:-1]
140140

141-
def ellipse_radii_from_major_axis_from(self, pixel_scale: float, n_i : int = 0) -> np.ndarray:
141+
def ellipse_radii_from_major_axis_from(
142+
self, pixel_scale: float, n_i: int = 0
143+
) -> np.ndarray:
142144
"""
143145
Returns the distance from the centre of the ellipse to every point on the ellipse, which are called
144146
the ellipse radii.
@@ -173,7 +175,7 @@ def ellipse_radii_from_major_axis_from(self, pixel_scale: float, n_i : int = 0)
173175
),
174176
)
175177

176-
def x_from_major_axis_from(self, pixel_scale: float, n_i : int = 0) -> np.ndarray:
178+
def x_from_major_axis_from(self, pixel_scale: float, n_i: int = 0) -> np.ndarray:
177179
"""
178180
Returns the x-coordinates of the points on the ellipse, starting from the x-coordinate of the major-axis
179181
of the ellipse after rotation by its `angle` and moving counter-clockwise.
@@ -198,9 +200,7 @@ def x_from_major_axis_from(self, pixel_scale: float, n_i : int = 0) -> np.ndarra
198200

199201
return ellipse_radii_from_major_axis * np.cos(angles_from_x0) + self.centre[1]
200202

201-
def y_from_major_axis_from(
202-
self, pixel_scale: float, n_i : int = 0
203-
) -> np.ndarray:
203+
def y_from_major_axis_from(self, pixel_scale: float, n_i: int = 0) -> np.ndarray:
204204
"""
205205
Returns the y-coordinates of the points on the ellipse, starting from the y-coordinate of the major-axis
206206
of the ellipse after rotation by its `angle` and moving counter-clockwise.
@@ -231,7 +231,10 @@ def y_from_major_axis_from(
231231
- self.centre[0]
232232
)
233233

234-
def points_from_major_axis_from(self, pixel_scale: float, n_i : int = 0,
234+
def points_from_major_axis_from(
235+
self,
236+
pixel_scale: float,
237+
n_i: int = 0,
235238
) -> np.ndarray:
236239
"""
237240
Returns the (y,x) coordinates of the points on the ellipse, starting from the major-axis of the ellipse

autogalaxy/ellipse/fit_ellipse.py

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ def points_from_major_axis_from(self) -> np.ndarray:
7070
if total_points_required == total_points - total_points_masked:
7171
continue
7272

73-
points = self.ellipse.points_from_major_axis_from(pixel_scale=self.dataset.pixel_scales[0],
74-
n_i=i)
73+
points = self.ellipse.points_from_major_axis_from(
74+
pixel_scale=self.dataset.pixel_scales[0], n_i=i
75+
)
7576

7677
if i == i_total:
7778

@@ -108,52 +109,6 @@ def _points_from_major_axis(self) -> np.ndarray:
108109
"""
109110
return self.points_from_major_axis_from()
110111

111-
# @property
112-
# def mask_interp(self) -> np.ndarray:
113-
# """
114-
# Returns the mask values of the dataset that the ellipse fits, which are computed by overlaying the ellipse over
115-
# the 2D data and performing a 2D interpolation at discrete (y,x) coordinates on the ellipse on the dataset's
116-
# mask.
117-
#
118-
# When an input (y,x) coordinate intepolates only unmasked values (`data.mask=False`) the intepolatred value
119-
# is 0.0, where if it interpolates one or a masked value (`data.mask=True`), the interpolated value is positive.
120-
# To mask all values which interpolate a masked value, all interpolated values above 1 and converted to `True`.
121-
#
122-
# This mask is used to remove these pixels from a fit and evaluate how many ellipse points are used for each
123-
# ellipse fit.
124-
#
125-
# The (y,x) coordinates on the ellipse where the interpolation occurs are computed in the
126-
# `points_from_major_axis` property of the `Ellipse` class, with the documentation describing how these points
127-
# are computed.
128-
#
129-
# Returns
130-
# -------
131-
# The data values of the ellipse fits, computed via a 2D interpolation of where the ellipse
132-
# overlaps the data.
133-
# """
134-
# return self.interp.mask_interp(self._points_from_major_axis) > 0.0
135-
136-
# @property
137-
# def total_points_interp(self) -> int:
138-
# """
139-
# Returns the total number of points used to interpolate the data and noise-map values of the ellipse.
140-
#
141-
# For example, if the ellipse spans 10 pixels, the total number of points will be 10.
142-
#
143-
# The calculation removes points if one or more interpolated value uses a masked value, meaning the interpolation
144-
# is not reliable.
145-
#
146-
# The (y,x) coordinates on the ellipse where the interpolation occurs are computed in the
147-
# `points_from_major_axis` property of the `Ellipse` class, with the documentation describing how these points
148-
# are computed.
149-
#
150-
# Returns
151-
# -------
152-
# The noise-map values of the ellipse fits, computed via a 2D interpolation of where the ellipse
153-
# overlaps the noise-map.
154-
# """
155-
# return self.data_interp[np.invert(self.mask_interp)].shape[0]
156-
157112
@property
158113
def data_interp(self) -> aa.ArrayIrregular:
159114
"""
@@ -174,8 +129,6 @@ def data_interp(self) -> aa.ArrayIrregular:
174129
"""
175130
data = self.interp.data_interp(self._points_from_major_axis)
176131

177-
# data[self.mask_interp] = np.nan
178-
179132
return aa.ArrayIrregular(values=data)
180133

181134
@property
@@ -198,8 +151,6 @@ def noise_map_interp(self) -> aa.ArrayIrregular:
198151
"""
199152
noise_map = self.interp.noise_map_interp(self._points_from_major_axis)
200153

201-
# noise_map[self.mask_interp] = np.nan
202-
203154
return aa.ArrayIrregular(values=noise_map)
204155

205156
@property

autogalaxy/ellipse/plot/fit_ellipse_plotters.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,14 @@ def figures_2d(
8888
points = fit.points_from_major_axis_from()
8989

9090
x = points[:, 1]
91-
y = points[:, 0] * -1.0 # flip for plot
91+
y = points[:, 0] * -1.0 # flip for plot
9292

9393
ellipse_list.append(aa.Grid2DIrregular.from_yx_1d(y=y, x=x))
9494

9595
visuals_2d = self.get_visuals_2d() + Visuals2D(
96-
positions=ellipse_list,
97-
lines=ellipse_list
96+
positions=ellipse_list, lines=ellipse_list
9897
)
9998

100-
10199
self.mat_plot_2d.plot_array(
102100
array=self.fit_list[0].data,
103101
visuals_2d=visuals_2d,

autogalaxy/gui/scribbler.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import matplotlib.pyplot as plt
55
from typing import Tuple
66

7+
78
class Scribbler:
89
def __init__(
910
self,
@@ -31,25 +32,25 @@ def __init__(
3132
origin = image.geometry.origin
3233
pixel_scales = image.geometry.pixel_scales
3334

34-
x0_pix = int(
35+
x0_pix = int(
3536
(extent[0] - origin[1]) / pixel_scales[1]
3637
+ central_pixel_coordinates[1]
3738
+ 0.5
3839
)
3940

40-
x1_pix = int(
41+
x1_pix = int(
4142
(extent[1] - origin[1]) / pixel_scales[1]
4243
+ central_pixel_coordinates[1]
4344
+ 0.5
4445
)
4546

46-
y0_pix = int(
47+
y0_pix = int(
4748
(extent[2] - origin[0]) / pixel_scales[0]
4849
+ central_pixel_coordinates[0]
4950
+ 0.5
5051
)
5152

52-
y1_pix = int(
53+
y1_pix = int(
5354
(extent[3] - origin[0]) / pixel_scales[0]
5455
+ central_pixel_coordinates[0]
5556
+ 0.5

autogalaxy/operate/deflections.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,93 @@ def deflections_yx_scalar(self, y, x, pixel_scales):
100100
def __eq__(self, other):
101101
return self.__dict__ == other.__dict__ and self.__class__ is other.__class__
102102

103+
def time_delay_geometry_term_from(self, grid) -> aa.Array2D:
104+
"""
105+
Returns the geometric time delay term of the Fermat potential for a given grid of image-plane positions.
106+
107+
This term is given by:
108+
109+
.. math::
110+
\[\tau_{\text{geom}}(\boldsymbol{\theta}) = \frac{1}{2} |\boldsymbol{\theta} - \boldsymbol{\beta}|^2\]
111+
112+
where:
113+
- \( \boldsymbol{\theta} \) is the image-plane coordinate,
114+
- \( \boldsymbol{\beta} = \boldsymbol{\theta} - \boldsymbol{\alpha}(\boldsymbol{\theta}) \) is the source-plane coordinate,
115+
- \( \boldsymbol{\alpha} \) is the deflection angle at each image-plane coordinate.
116+
117+
Parameters
118+
----------
119+
grid
120+
The 2D grid of (y,x) arc-second coordinates the deflection angles and time delay geometric term are computed
121+
on.
122+
123+
Returns
124+
-------
125+
The geometric time delay term at each grid position.
126+
"""
127+
deflections = self.deflections_yx_2d_from(grid=grid)
128+
129+
src_y = grid[:, 0] - deflections[:, 0]
130+
src_x = grid[:, 1] - deflections[:, 1]
131+
132+
delay = 0.5 * ((grid[:, 0] - src_y) ** 2 + (grid[:, 1] - src_x) ** 2)
133+
134+
if isinstance(grid, aa.Grid2DIrregular):
135+
return aa.ArrayIrregular(values=delay)
136+
return aa.Array2D(values=delay, mask=grid.mask)
137+
138+
def fermat_potential_from(self, grid) -> aa.Array2D:
139+
"""
140+
Returns the Fermat potential for a given grid of image-plane positions.
141+
142+
This is the sum of the geometric time delay term and the gravitational (Shapiro) delay term (i.e. the lensing
143+
potential), and is given by:
144+
145+
.. math::
146+
\[\phi(\boldsymbol{\theta}) = \frac{1}{2} |\boldsymbol{\theta} - \boldsymbol{\beta}|^2 - \psi(\boldsymbol{\theta})\]
147+
148+
where:
149+
- \( \boldsymbol{\theta} \) is the image-plane coordinate,
150+
- \( \boldsymbol{\beta} = \boldsymbol{\theta} - \boldsymbol{\alpha}(\boldsymbol{\theta}) \) is the source-plane coordinate,
151+
- \( \psi(\boldsymbol{\theta}) \) is the lensing potential,
152+
- \( \phi(\boldsymbol{\theta}) \) is the Fermat potential.
153+
154+
Parameters
155+
----------
156+
grid
157+
The 2D grid of (y,x) arc-second coordinates the Fermat potential is computed on.
158+
159+
Returns
160+
-------
161+
The Fermat potential at each grid position.
162+
"""
163+
time_delay_geometry_term = self.time_delay_geometry_term_from(grid=grid)
164+
potential = self.potential_2d_from(grid=grid)
165+
166+
fermat_potential = time_delay_geometry_term - potential
167+
168+
if isinstance(grid, aa.Grid2DIrregular):
169+
return aa.ArrayIrregular(values=fermat_potential)
170+
return aa.Array2D(values=fermat_potential, mask=grid.mask)
171+
172+
def time_delays_from(self, grid) -> aa.Array2D:
173+
"""
174+
Returns the 2D time delay map of lensing object, which is computed as the deflection angles in the y and x
175+
directions multiplied by the y and x coordinates of the grid.
176+
177+
Parameters
178+
----------
179+
grid
180+
The 2D grid of (y,x) arc-second coordinates the deflection angles and time delay are computed on.
181+
"""
182+
deflections_yx = self.deflections_yx_2d_from(grid=grid)
183+
184+
return aa.Array2D(
185+
values=deflections_yx[:, 0] * grid[:, 0]
186+
+ deflections_yx[:, 1] * grid[:, 1],
187+
mask=grid.mask,
188+
)
189+
103190
def __hash__(self):
104191
return hash(repr(self))
105192

test_autogalaxy/ellipse/ellipse/test_ellipse.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,3 @@ def test__points_from_major_axis():
152152
assert ellipse.points_from_major_axis_from(pixel_scale=1.0)[1][0] == pytest.approx(
153153
-0.2123224755, 1.0e-4
154154
)
155-

test_autogalaxy/ellipse/model/test_analysis_ellipse.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
def test__make_result__result_imaging_is_returned(masked_imaging_7x7):
1212
ellipse_list = af.Collection(af.Model(ag.Ellipse) for _ in range(2))
1313

14-
ellipse_list[0].major_axis = 1.0
15-
ellipse_list[1].major_axis = 2.0
14+
ellipse_list[0].major_axis = 0.2
15+
ellipse_list[1].major_axis = 0.4
1616

1717
model = af.Collection(ellipses=ellipse_list)
1818

@@ -30,8 +30,8 @@ def test__figure_of_merit(
3030
):
3131
ellipse_list = af.Collection(af.Model(ag.Ellipse) for _ in range(2))
3232

33-
ellipse_list[0].major_axis = 1.0
34-
ellipse_list[1].major_axis = 2.0
33+
ellipse_list[0].major_axis = 0.2
34+
ellipse_list[1].major_axis = 0.4
3535

3636
multipole_0_prior_0 = af.UniformPrior(lower_limit=0.0, upper_limit=0.1)
3737
multipole_0_prior_1 = af.UniformPrior(lower_limit=0.0, upper_limit=0.1)

0 commit comments

Comments
 (0)