From af3744ab2553a4a82ee08397dbba3b11ca375b72 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 14:32:26 +0100 Subject: [PATCH 01/11] unit trest passes plus docstrings --- autogalaxy/convert.py | 65 +++++++++++++++++++++++++++------ test_autogalaxy/test_convert.py | 35 +++++++++++++++++- 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index 4b8e8a0bd..060dfbd50 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -2,7 +2,7 @@ from typing import Tuple -def ell_comps_from(axis_ratio, angle): +def ell_comps_from(axis_ratio : float, angle : float) -> Tuple[float, float]: """ Convert an input axis ratio (0.0 > q > 1.0) and rotation position angle defined counter clockwise from the positive x-axis(0.0 > angle > 180) to the (y,x) ellipitical components e1 and e2. @@ -21,18 +21,42 @@ def ell_comps_from(axis_ratio, angle): return (ellip_y, ellip_x) -def axis_ratio_and_angle_from(ell_comps): +def axis_ratio_and_angle_from(ell_comps : Tuple[float, float]) -> Tuple[float, float]: """ - Convert the ellipitical components e1 and e2 to an axis ratio (0.0 > q > 1.0) and rotation position angle - defined counter clockwise from the positive x-axis(0.0 > angle > 180) to . + Returns the axis-ratio and position angle in degrees (-45 < angle < 135.0) from input elliptical components e1 + and e2 of a light or mass profile. + + The elliptical components of a light or mass profile are given by: + + elliptical_component_y = ell_comps[0] = (1-q)/(1+q) * sin(2 * angle) + elliptical_component_x = ell_comps[1] = (1-q)/(1+q) * cos(2 * angle) + + The axis-ratio and angle are therefore given by: + + axis_ratio = (1 - fac) / (1 + fac) + angle = 0.5 * arctan(ell_comps[0] / ell_comps[1]) + + where `fac = sqrt(ell_comps[1] ** 2 + ell_comps[0] ** 2). + + Which this function returns in degrees. + + An additional check is performed which requires the angle is between -45 and 135 degrees. This ensures that + for certain values of `ell_comps` the angle does not jump from one boundary to another (e.g. without this check + certain values of `ell_comps` return -1.0 degrees and others 179.0 degrees). This ensures that when error + estimates are computed from samples of a lens model via marginalization, the calculation is not biased by the + angle jumping between these two values. Parameters ---------- - ell_comps : (float, float) - The first and second ellipticity components of the elliptical coordinate system. + ell_comps + The elliptical components of the light or mass profile which are converted to an angle. """ angle = np.arctan2(ell_comps[0], ell_comps[1]) / 2 angle *= 180.0 / np.pi + + if abs(angle) > 45 and angle < 0: + angle += 180 + fac = np.sqrt(ell_comps[1] ** 2 + ell_comps[0] ** 2) if fac > 0.999: fac = 0.999 # avoid unphysical solution @@ -41,7 +65,7 @@ def axis_ratio_and_angle_from(ell_comps): return axis_ratio, angle -def axis_ratio_from(ell_comps): +def axis_ratio_from(ell_comps : Tuple[float, float]): """ Convert the ellipitical components e1 and e2 to an axis ratio (0.0 > q > 1.0) and rotation position angle defined counter clockwise from the positive x-axis(0.0 > angle > 180) to . @@ -55,16 +79,35 @@ def axis_ratio_from(ell_comps): return axis_ratio -def angle_from(ell_comps): +def angle_from(ell_comps : (float, float)) -> float: """ - Convert the ellipitical components e1 and e2 to an rotation position angle in degrees (0.0 > angle > 18-.0). + Returns the position angle in degrees (-45 < angle < 135.0) from input elliptical components e1 and e2 + of a light or mass profile. + + The elliptical components of a light or mass profile are given by: + + elliptical_component_y = ell_comps[0] = (1-q)/(1+q) * sin(2 * angle) + elliptical_component_x = ell_comps[1] = (1-q)/(1+q) * cos(2 * angle) + + The angle is therefore given by: + + angle = 0.5 * arctan(ell_comps[0] / ell_comps[1]) + + Which this function returns in degrees. + + An additional check is performed which requires the angle is between -45 and 135 degrees. This ensures that + for certain values of `ell_comps` the angle does not jump from one boundary to another (e.g. without this check + certain values of `ell_comps` return -1.0 degrees and others 179.0 degrees). This ensures that when error + estimates are computed from samples of a lens model via marginalization, the calculation is not biased by the + angle jumping between these two values. Parameters ---------- - ell_comps : (float, float) - The first and second ellipticity components of the elliptical coordinate system. + ell_comps + The elliptical components of the light or mass profile which are converted to an angle. """ axis_ratio, angle = axis_ratio_and_angle_from(ell_comps=ell_comps) + return angle diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index 46117e2da..c0cdb1802 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -1 +1,34 @@ -pass +import pytest + +import autogalaxy as ag + +def test__angle_from(): + angle = ag.convert.angle_from( + ell_comps=(0.0, 1.0) + ) + + assert angle == pytest.approx(0.0, 1.0e-4) + + angle = ag.convert.angle_from( + ell_comps=(1.0, 0.0) + ) + + assert angle == pytest.approx(45.0, 1.0e-4) + + angle = ag.convert.angle_from( + ell_comps=(0.0, -1.0) + ) + + assert angle == pytest.approx(90.0, 1.0e-4) + + angle = ag.convert.angle_from( + ell_comps=(-1.0, 0.0) + ) + + assert angle == pytest.approx(-45.0, 1.0e-4) + + angle = ag.convert.angle_from( + ell_comps=(-1.0, -1.0) + ) + + assert angle == pytest.approx(112.5, 1.0e-4) \ No newline at end of file From 26ac2280eb6338058bfa29d1996ee008e59d6709 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 14:35:04 +0100 Subject: [PATCH 02/11] more docstrings --- autogalaxy/convert.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index 060dfbd50..9ee22db32 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -4,8 +4,15 @@ def ell_comps_from(axis_ratio : float, angle : float) -> Tuple[float, float]: """ - Convert an input axis ratio (0.0 > q > 1.0) and rotation position angle defined counter clockwise from the - positive x-axis(0.0 > angle > 180) to the (y,x) ellipitical components e1 and e2. + Returns the elliptical components e1 and e2 of a light or mass profile from an input angle in degrees and axis + ratio. + + The elliptical components of a light or mass profile are given by: + + elliptical_component_y = ell_comps[0] = (1-axis_ratio)/(1+axis_ratio) * sin(2 * angle) + elliptical_component_x = ell_comps[1] = (1-axis_ratio)/(1+axis_ratio) * cos(2 * angle) + + Which are the values this function returns. Parameters ---------- @@ -28,8 +35,8 @@ def axis_ratio_and_angle_from(ell_comps : Tuple[float, float]) -> Tuple[float, f The elliptical components of a light or mass profile are given by: - elliptical_component_y = ell_comps[0] = (1-q)/(1+q) * sin(2 * angle) - elliptical_component_x = ell_comps[1] = (1-q)/(1+q) * cos(2 * angle) + elliptical_component_y = ell_comps[0] = (1-axis_ratio)/(1+axis_ratio) * sin(2 * angle) + elliptical_component_x = ell_comps[1] = (1-axis_ratio)/(1+axis_ratio) * cos(2 * angle) The axis-ratio and angle are therefore given by: @@ -67,13 +74,25 @@ def axis_ratio_and_angle_from(ell_comps : Tuple[float, float]) -> Tuple[float, f def axis_ratio_from(ell_comps : Tuple[float, float]): """ - Convert the ellipitical components e1 and e2 to an axis ratio (0.0 > q > 1.0) and rotation position angle - defined counter clockwise from the positive x-axis(0.0 > angle > 180) to . + Returns the axis-ratio from input elliptical components e1 and e2 of a light or mass profile. + + The elliptical components of a light or mass profile are given by: + + elliptical_component_y = ell_comps[0] = (1-axis_ratio)/(1+axis_ratio) * sin(2 * angle) + elliptical_component_x = ell_comps[1] = (1-axis_ratio)/(1+axis_ratio) * cos(2 * angle) + + The axis-ratio is therefore given by: + + axis_ratio = (1 - fac) / (1 + fac) + + where `fac = sqrt(ell_comps[1] ** 2 + ell_comps[0] ** 2). + + Which this function returns. Parameters ---------- - ell_comps : (float, float) - The first and second ellipticity components of the elliptical coordinate system. + ell_comps + The elliptical components of the light or mass profile which are converted to an angle. """ axis_ratio, angle = axis_ratio_and_angle_from(ell_comps=ell_comps) return axis_ratio From dad5aa7f62b991f1c4d82e6c923b507f90f39105 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 14:38:43 +0100 Subject: [PATCH 03/11] add axis_ratio to tests --- autogalaxy/convert.py | 2 +- test_autogalaxy/test_convert.py | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index 9ee22db32..ddb0e65be 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -98,7 +98,7 @@ def axis_ratio_from(ell_comps : Tuple[float, float]): return axis_ratio -def angle_from(ell_comps : (float, float)) -> float: +def angle_from(ell_comps : Tuple[float, float]) -> float: """ Returns the position angle in degrees (-45 < angle < 135.0) from input elliptical components e1 and e2 of a light or mass profile. diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index c0cdb1802..e1f9aeb6b 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -2,33 +2,38 @@ import autogalaxy as ag -def test__angle_from(): - angle = ag.convert.angle_from( +def test__axis_ratio_and_angle_from(): + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( ell_comps=(0.0, 1.0) ) + assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(0.0, 1.0e-4) - angle = ag.convert.angle_from( + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( ell_comps=(1.0, 0.0) ) + assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(45.0, 1.0e-4) - angle = ag.convert.angle_from( + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( ell_comps=(0.0, -1.0) ) + assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(90.0, 1.0e-4) - angle = ag.convert.angle_from( + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( ell_comps=(-1.0, 0.0) ) - + + assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(-45.0, 1.0e-4) - angle = ag.convert.angle_from( + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( ell_comps=(-1.0, -1.0) ) + assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(112.5, 1.0e-4) \ No newline at end of file From bd7d2ada436602160b799f784ccfd59ff8992cdd Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 14:39:52 +0100 Subject: [PATCH 04/11] added test__el_comps --- test_autogalaxy/test_convert.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index e1f9aeb6b..77eef3eed 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -2,6 +2,14 @@ import autogalaxy as ag +def test__ell_comps_from(): + + ell_comps = ag.convert.ell_comps_from( + axis_ratio=0.00050025012, angle=0.0 + ) + + assert ell_comps == pytest.approx((0.0, 0.999), 1.0e-4) + def test__axis_ratio_and_angle_from(): axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( ell_comps=(0.0, 1.0) From b876981b6ab4839e94c05d00aca461eeff8d08d9 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 14:50:16 +0100 Subject: [PATCH 05/11] test__shear_gamma_1_2_from --- autogalaxy/convert.py | 11 ++++++- test_autogalaxy/test_convert.py | 55 ++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index ddb0e65be..95f754a61 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -151,8 +151,17 @@ def shear_magnitude_and_angle_from( """ angle = np.arctan2(gamma_2, gamma_1) / 2 * 180.0 / np.pi magnitude = np.sqrt(gamma_1**2 + gamma_2**2) + if angle < 0: - return magnitude, angle + 180.0 + angle += 180.0 + + print(angle) + + if abs(angle - 90) > 45: + if angle > 90: + angle -= 180 + vvv + return magnitude, angle diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index 77eef3eed..e4c08c78f 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -44,4 +44,57 @@ def test__axis_ratio_and_angle_from(): ) assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) - assert angle == pytest.approx(112.5, 1.0e-4) \ No newline at end of file + assert angle == pytest.approx(112.5, 1.0e-4) + + +def test__shear_gamma_1_2_from(): + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=0.0 + ) + + assert gamma_1 == pytest.approx(0.05, 1.0e-4) + assert gamma_2 == pytest.approx(0.0, 1.0e-4) + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=45.0 + ) + + assert gamma_1 == pytest.approx(0.0, 1.0e-4) + assert gamma_2 == pytest.approx(0.05, 1.0e-4) + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=90.0 + ) + + assert gamma_1 == pytest.approx(-0.05, 1.0e-4) + assert gamma_2 == pytest.approx(0.0, 1.0e-4) + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=135.0 + ) + + assert gamma_1 == pytest.approx(0.0, 1.0e-4) + assert gamma_2 == pytest.approx(-0.05, 1.0e-4) + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=180.0 + ) + + assert gamma_1 == pytest.approx(0.05, 1.0e-4) + assert gamma_2 == pytest.approx(0.0, 1.0e-4) + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=225.0 + ) + + assert gamma_1 == pytest.approx(0.0, 1.0e-4) + assert gamma_2 == pytest.approx(0.05, 1.0e-4) + + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( + magnitude=0.05, angle=-45.0 + ) + + assert gamma_1 == pytest.approx(0.0, 1.0e-4) + assert gamma_2 == pytest.approx(-0.05, 1.0e-4) + From bd153afb6f6cb000a3d8b2893465fc6f6ff5a359 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 14:53:19 +0100 Subject: [PATCH 06/11] test__shear_magnitude_and_angle_from --- autogalaxy/convert.py | 3 -- test_autogalaxy/test_convert.py | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index 95f754a61..4e3082e0c 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -155,12 +155,9 @@ def shear_magnitude_and_angle_from( if angle < 0: angle += 180.0 - print(angle) - if abs(angle - 90) > 45: if angle > 90: angle -= 180 - vvv return magnitude, angle diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index e4c08c78f..2b60e416d 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -98,3 +98,59 @@ def test__shear_gamma_1_2_from(): assert gamma_1 == pytest.approx(0.0, 1.0e-4) assert gamma_2 == pytest.approx(-0.05, 1.0e-4) +def test__shear_magnitude_and_angle_from(): + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.05, gamma_2=0.0 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + assert angle == pytest.approx(0.0, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.0, gamma_2=0.05 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + assert angle == pytest.approx(45.0, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=-0.05, gamma_2=0.0 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + assert angle == pytest.approx(90.0, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.0, gamma_2=-0.05 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + assert angle == pytest.approx(135.0, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.05, gamma_2=0.0 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + assert angle == pytest.approx(0.0, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.0, gamma_2=0.05 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + assert angle == pytest.approx(45.0, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.05, gamma_2=0.0 + ) + + assert magnitude == pytest.approx(0.05, 1.0e-4) + + magnitude, angle = ag.convert.shear_magnitude_and_angle_from( + gamma_1=0.05, gamma_2=-0.05 + ) + + assert magnitude == pytest.approx(0.07071067811865, 1.0e-4) + assert angle == pytest.approx(-22.5, 1.0e-4) \ No newline at end of file From a8fc04f9a248ddc983783923c84b5e0e3f3c150f Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 15:41:21 +0100 Subject: [PATCH 07/11] implement more conversions docstrings --- autogalaxy/convert.py | 123 +++++++++++++++++++++++++------- test_autogalaxy/test_convert.py | 62 +++++----------- 2 files changed, 117 insertions(+), 68 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index 4e3082e0c..7385422c8 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -2,7 +2,7 @@ from typing import Tuple -def ell_comps_from(axis_ratio : float, angle : float) -> Tuple[float, float]: +def ell_comps_from(axis_ratio: float, angle: float) -> Tuple[float, float]: """ Returns the elliptical components e1 and e2 of a light or mass profile from an input angle in degrees and axis ratio. @@ -28,7 +28,7 @@ def ell_comps_from(axis_ratio : float, angle : float) -> Tuple[float, float]: return (ellip_y, ellip_x) -def axis_ratio_and_angle_from(ell_comps : Tuple[float, float]) -> Tuple[float, float]: +def axis_ratio_and_angle_from(ell_comps: Tuple[float, float]) -> Tuple[float, float]: """ Returns the axis-ratio and position angle in degrees (-45 < angle < 135.0) from input elliptical components e1 and e2 of a light or mass profile. @@ -45,7 +45,7 @@ def axis_ratio_and_angle_from(ell_comps : Tuple[float, float]) -> Tuple[float, f where `fac = sqrt(ell_comps[1] ** 2 + ell_comps[0] ** 2). - Which this function returns in degrees. + This function returns the axis-ratio and angle in degrees. An additional check is performed which requires the angle is between -45 and 135 degrees. This ensures that for certain values of `ell_comps` the angle does not jump from one boundary to another (e.g. without this check @@ -72,7 +72,7 @@ def axis_ratio_and_angle_from(ell_comps : Tuple[float, float]) -> Tuple[float, f return axis_ratio, angle -def axis_ratio_from(ell_comps : Tuple[float, float]): +def axis_ratio_from(ell_comps: Tuple[float, float]): """ Returns the axis-ratio from input elliptical components e1 and e2 of a light or mass profile. @@ -84,10 +84,11 @@ def axis_ratio_from(ell_comps : Tuple[float, float]): The axis-ratio is therefore given by: axis_ratio = (1 - fac) / (1 + fac) + angle = 0.5 * arctan(ell_comps[0] / ell_comps[1]) where `fac = sqrt(ell_comps[1] ** 2 + ell_comps[0] ** 2). - Which this function returns. + This function returns the axis-ratio. Parameters ---------- @@ -98,7 +99,7 @@ def axis_ratio_from(ell_comps : Tuple[float, float]): return axis_ratio -def angle_from(ell_comps : Tuple[float, float]) -> float: +def angle_from(ell_comps: Tuple[float, float]) -> float: """ Returns the position angle in degrees (-45 < angle < 135.0) from input elliptical components e1 and e2 of a light or mass profile. @@ -108,11 +109,12 @@ def angle_from(ell_comps : Tuple[float, float]) -> float: elliptical_component_y = ell_comps[0] = (1-q)/(1+q) * sin(2 * angle) elliptical_component_x = ell_comps[1] = (1-q)/(1+q) * cos(2 * angle) - The angle is therefore given by: - + axis_ratio = (1 - fac) / (1 + fac) angle = 0.5 * arctan(ell_comps[0] / ell_comps[1]) - Which this function returns in degrees. + where `fac = sqrt(ell_comps[1] ** 2 + ell_comps[0] ** 2). + + This function returns the angle in degrees. An additional check is performed which requires the angle is between -45 and 135 degrees. This ensures that for certain values of `ell_comps` the angle does not jump from one boundary to another (e.g. without this check @@ -132,9 +134,26 @@ def angle_from(ell_comps : Tuple[float, float]) -> float: def shear_gamma_1_2_from(magnitude: float, angle: float) -> Tuple[float, float]: """ - :param angle: angel - :param magnitude: ellipticity - :return: + Returns the shear gamma 1 and gamma 2 values an input shear magnitude and angle in degrees. + + The gamma 1 and gamma 2 components of a shear are given by: + + gamma_1 = magnitude * np.cos(2 * angle * np.pi / 180.0) + gamma_2 = magnitude * np.sin(2 * angle * np.pi / 180.0) + + Which are the values this function returns. + + Converting from gamma 1 and gamma 2 to magnitude and angle is given by: + + magnitude = np.sqrt(gamma_1**2 + gamma_2**2) + angle = np.arctan2(gamma_2, gamma_1) / 2 * 180.0 / np.pi + + Parameters + ---------- + axis_ratio + Ratio of light profiles ellipse's minor and major axes (b/a). + angle + Rotation angle of light profile counter-clockwise from positive x-axis. """ gamma_1 = magnitude * np.cos(2 * angle * np.pi / 180.0) gamma_2 = magnitude * np.sin(2 * angle * np.pi / 180.0) @@ -145,9 +164,32 @@ def shear_magnitude_and_angle_from( gamma_1: float, gamma_2: float ) -> Tuple[float, float]: """ - :param e1: ellipticity component - :param e2: ellipticity component - :return: angle and abs value of ellipticity + Returns the shear magnitude and angle in degrees from input shear gamma 1 and gamma 2 values. + + The gamma 1 and gamma 2 components of a shear are given by: + + gamma_1 = magnitude * np.cos(2 * angle * np.pi / 180.0) + gamma_2 = magnitude * np.sin(2 * angle * np.pi / 180.0) + + Converting from gamma 1 and gamma 2 to magnitude and angle is given by: + + magnitude = np.sqrt(gamma_1**2 + gamma_2**2) + angle = np.arctan2(gamma_2, gamma_1) / 2 * 180.0 / np.pi + + Which are the values this function returns. + + Additional checks are performed which requires the angle is between -45 and 135 degrees. This ensures that + for certain values of `gamma_1` and `gamma_2` the angle does not jump from one boundary to another (e.g. without + this check certain values of `gamma_1` and `gamma_2` return -1.0 degrees and others 179.0 degrees). This ensures + that when error estimates are computed from samples of a lens model via marginalization, the calculation is not + biased by the angle jumping between these two values. + + Parameters + ---------- + gamma_1 + The gamma 1 component of the shear. + gamma_2 + The gamma 2 component of the shear. """ angle = np.arctan2(gamma_2, gamma_1) / 2 * 180.0 / np.pi magnitude = np.sqrt(gamma_1**2 + gamma_2**2) @@ -155,18 +197,34 @@ def shear_magnitude_and_angle_from( if angle < 0: angle += 180.0 - if abs(angle - 90) > 45: - if angle > 90: - angle -= 180 + if abs(angle - 90) > 45 and angle > 90: + angle -= 180 return magnitude, angle def shear_magnitude_from(gamma_1: float, gamma_2: float) -> float: """ - :param e1: ellipticity component - :param e2: ellipticity component - :return: angle and abs value of ellipticity + Returns the shear magnitude and angle in degrees from input shear gamma 1 and gamma 2 values. + + The gamma 1 and gamma 2 components of a shear are given by: + + gamma_1 = magnitude * np.cos(2 * angle * np.pi / 180.0) + gamma_2 = magnitude * np.sin(2 * angle * np.pi / 180.0) + + Converting from gamma 1 and gamma 2 to magnitude and angle is given by: + + magnitude = np.sqrt(gamma_1**2 + gamma_2**2) + angle = np.arctan2(gamma_2, gamma_1) / 2 * 180.0 / np.pi + + The magnitude value is what this function returns. + + Parameters + ---------- + gamma_1 + The gamma 1 component of the shear. + gamma_2 + The gamma 2 component of the shear. """ magnitude, angle = shear_magnitude_and_angle_from(gamma_1=gamma_1, gamma_2=gamma_2) return magnitude @@ -174,9 +232,26 @@ def shear_magnitude_from(gamma_1: float, gamma_2: float) -> float: def shear_angle_from(gamma_1: float, gamma_2: float) -> float: """ - :param e1: ellipticity component - :param e2: ellipticity component - :return: angle and abs value of ellipticity + Returns the shear magnitude and angle in degrees from input shear gamma 1 and gamma 2 values. + + The gamma 1 and gamma 2 components of a shear are given by: + + gamma_1 = magnitude * np.cos(2 * angle * np.pi / 180.0) + gamma_2 = magnitude * np.sin(2 * angle * np.pi / 180.0) + + Converting from gamma 1 and gamma 2 to magnitude and angle is given by: + + magnitude = np.sqrt(gamma_1**2 + gamma_2**2) + angle = np.arctan2(gamma_2, gamma_1) / 2 * 180.0 / np.pi + + The angle value is what this function returns. + + Parameters + ---------- + gamma_1 + The gamma 1 component of the shear. + gamma_2 + The gamma 2 component of the shear. """ magnitude, angle = shear_magnitude_and_angle_from(gamma_1=gamma_1, gamma_2=gamma_2) return angle diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index 2b60e416d..bf2e7959e 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -2,104 +2,78 @@ import autogalaxy as ag -def test__ell_comps_from(): - ell_comps = ag.convert.ell_comps_from( - axis_ratio=0.00050025012, angle=0.0 - ) +def test__ell_comps_from(): + ell_comps = ag.convert.ell_comps_from(axis_ratio=0.00050025012, angle=0.0) assert ell_comps == pytest.approx((0.0, 0.999), 1.0e-4) + def test__axis_ratio_and_angle_from(): - axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( - ell_comps=(0.0, 1.0) - ) + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from(ell_comps=(0.0, 1.0)) assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(0.0, 1.0e-4) - axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( - ell_comps=(1.0, 0.0) - ) + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from(ell_comps=(1.0, 0.0)) assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(45.0, 1.0e-4) - axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( - ell_comps=(0.0, -1.0) - ) + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from(ell_comps=(0.0, -1.0)) assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(90.0, 1.0e-4) - axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( - ell_comps=(-1.0, 0.0) - ) - + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from(ell_comps=(-1.0, 0.0)) + assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(-45.0, 1.0e-4) - axis_ratio, angle = ag.convert.axis_ratio_and_angle_from( - ell_comps=(-1.0, -1.0) - ) + axis_ratio, angle = ag.convert.axis_ratio_and_angle_from(ell_comps=(-1.0, -1.0)) assert axis_ratio == pytest.approx(0.00050025012, 1.0e-4) assert angle == pytest.approx(112.5, 1.0e-4) def test__shear_gamma_1_2_from(): - - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=0.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=0.0) assert gamma_1 == pytest.approx(0.05, 1.0e-4) assert gamma_2 == pytest.approx(0.0, 1.0e-4) - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=45.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=45.0) assert gamma_1 == pytest.approx(0.0, 1.0e-4) assert gamma_2 == pytest.approx(0.05, 1.0e-4) - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=90.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=90.0) assert gamma_1 == pytest.approx(-0.05, 1.0e-4) assert gamma_2 == pytest.approx(0.0, 1.0e-4) - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=135.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=135.0) assert gamma_1 == pytest.approx(0.0, 1.0e-4) assert gamma_2 == pytest.approx(-0.05, 1.0e-4) - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=180.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=180.0) assert gamma_1 == pytest.approx(0.05, 1.0e-4) assert gamma_2 == pytest.approx(0.0, 1.0e-4) - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=225.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=225.0) assert gamma_1 == pytest.approx(0.0, 1.0e-4) assert gamma_2 == pytest.approx(0.05, 1.0e-4) - gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from( - magnitude=0.05, angle=-45.0 - ) + gamma_1, gamma_2 = ag.convert.shear_gamma_1_2_from(magnitude=0.05, angle=-45.0) assert gamma_1 == pytest.approx(0.0, 1.0e-4) assert gamma_2 == pytest.approx(-0.05, 1.0e-4) -def test__shear_magnitude_and_angle_from(): +def test__shear_magnitude_and_angle_from(): magnitude, angle = ag.convert.shear_magnitude_and_angle_from( gamma_1=0.05, gamma_2=0.0 ) @@ -153,4 +127,4 @@ def test__shear_magnitude_and_angle_from(): ) assert magnitude == pytest.approx(0.07071067811865, 1.0e-4) - assert angle == pytest.approx(-22.5, 1.0e-4) \ No newline at end of file + assert angle == pytest.approx(-22.5, 1.0e-4) From 10d00e57c4b0b3787051278a4eea5db4b101ed19 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 17:48:41 +0100 Subject: [PATCH 08/11] update and test multipole_k_m_and_phi_m_from --- autogalaxy/convert.py | 63 ++++++++++++++++++ .../mass/total/power_law_multipole.py | 64 +------------------ .../mass/total/test_power_law_multipole.py | 3 +- test_autogalaxy/test_convert.py | 22 +++++++ 4 files changed, 90 insertions(+), 62 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index 7385422c8..bc8ed3f81 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -1,3 +1,4 @@ +from astropy import units import numpy as np from typing import Tuple @@ -255,3 +256,65 @@ def shear_angle_from(gamma_1: float, gamma_2: float) -> float: """ magnitude, angle = shear_magnitude_and_angle_from(gamma_1=gamma_1, gamma_2=gamma_2) return angle + +def multipole_k_m_and_phi_m_from( + multipole_comps: Tuple[float, float], m: int +) -> Tuple[float, float]: + """ + Converts the multipole component parameters to their normalizartion value `k_m` and angle `phi`, + which are given by: + + .. math:: + \phi^{\rm mass}_m = \arctan{\frac{\epsilon_{\rm 2}^{\rm mp}}{\epsilon_{\rm 2}^{\rm mp}}}, \, \, + k^{\rm mass}_m = \sqrt{{\epsilon_{\rm 1}^{\rm mp}}^2 + {\epsilon_{\rm 2}^{\rm mp}}^2} \, . + + The conversion depends on the multipole order `m`, to ensure that all possible rotationally symmetric + multiple mass profiles are available in the conversion for multiple components spanning -inf to inf. + + Parameters + ---------- + multipole_comps + The first and second components of the multipole. + + Returns + ------- + The normalization and angle parameters of the multipole. + """ + phi_m = ( + np.arctan2(multipole_comps[0], multipole_comps[1]) * 180.0 / np.pi / float(m) + ) + k_m = np.sqrt(multipole_comps[1] ** 2 + multipole_comps[0] ** 2) + + if phi_m < -90.0 / m: + phi_m += 360.0 / m + + return k_m, phi_m + + +def multipole_comps_from(k_m: float, phi_m: float, m: int) -> Tuple[float, float]: + """ + Converts the multipole normalizartion value `k_m` and angle `phi` to their multipole component parameters, + which are given by: + + .. math:: + \phi^{\rm mass}_m = \arctan{\frac{\epsilon_{\rm 2}^{\rm mp}}{\epsilon_{\rm 2}^{\rm mp}}}, \, \, + k^{\rm mass}_m = \sqrt{{\epsilon_{\rm 1}^{\rm mp}}^2 + {\epsilon_{\rm 2}^{\rm mp}}^2} \, . + + The conversion depends on the multipole order `m`, to ensure that all possible rotationally symmetric + multiple mass profiles are available in the conversion for multiple components spanning -inf to inf. + + Parameters + ---------- + k_m + The magnitude of the multipole. + phi_m + The angle of the multipole. + + Returns + ------- + The multipole component parameters. + """ + multipole_comp_0 = k_m * np.sin(phi_m * float(m) * units.deg.to(units.rad)) + multipole_comp_1 = k_m * np.cos(phi_m * float(m) * units.deg.to(units.rad)) + + return (multipole_comp_0, multipole_comp_1) \ No newline at end of file diff --git a/autogalaxy/profiles/mass/total/power_law_multipole.py b/autogalaxy/profiles/mass/total/power_law_multipole.py index e99288919..354bdabb0 100644 --- a/autogalaxy/profiles/mass/total/power_law_multipole.py +++ b/autogalaxy/profiles/mass/total/power_law_multipole.py @@ -4,6 +4,8 @@ import autoarray as aa +from autogalaxy import convert + from autogalaxy.profiles.mass.abstract.abstract import MassProfile @@ -36,66 +38,6 @@ def radial_and_angle_grid_from( return radial_grid, angle_grid -def multipole_parameters_from( - multipole_comps: Tuple[float, float], m: int -) -> Tuple[float, float]: - """ - Converts the multipole component parameters to their normalizartion value `k_m` and angle `phi`, - which are given by: - - .. math:: - \phi^{\rm mass}_m = \arctan{\frac{\epsilon_{\rm 2}^{\rm mp}}{\epsilon_{\rm 2}^{\rm mp}}}, \, \, - k^{\rm mass}_m = \sqrt{{\epsilon_{\rm 1}^{\rm mp}}^2 + {\epsilon_{\rm 2}^{\rm mp}}^2} \, . - - The conversion depends on the multipole order `m`, to ensure that all possible rotationally symmetric - multiple mass profiles are available in the conversion for multiple components spanning -inf to inf. - - Parameters - ---------- - multipole_comps - The first and second components of the multipole. - - Returns - ------- - The normalization and angle parameters of the multipole. - """ - phi_m = ( - np.arctan2(multipole_comps[0], multipole_comps[1]) * 180.0 / np.pi / float(m) - ) - k_m = np.sqrt(multipole_comps[1] ** 2 + multipole_comps[0] ** 2) - - return k_m, phi_m - - -def multipole_comps_from(k_m: float, phi_m: float, m: int) -> Tuple[float, float]: - """ - Converts the multipole normalizartion value `k_m` and angle `phi` to their multipole component parameters, - which are given by: - - .. math:: - \phi^{\rm mass}_m = \arctan{\frac{\epsilon_{\rm 2}^{\rm mp}}{\epsilon_{\rm 2}^{\rm mp}}}, \, \, - k^{\rm mass}_m = \sqrt{{\epsilon_{\rm 1}^{\rm mp}}^2 + {\epsilon_{\rm 2}^{\rm mp}}^2} \, . - - The conversion depends on the multipole order `m`, to ensure that all possible rotationally symmetric - multiple mass profiles are available in the conversion for multiple components spanning -inf to inf. - - Parameters - ---------- - k_m - The magnitude of the multipole. - phi_m - The angle of the multipole. - - Returns - ------- - The multipole component parameters. - """ - multipole_comp_0 = k_m * np.sin(phi_m * float(m) * units.deg.to(units.rad)) - multipole_comp_1 = k_m * np.cos(phi_m * float(m) * units.deg.to(units.rad)) - - return (multipole_comp_0, multipole_comp_1) - - class PowerLawMultipole(MassProfile): def __init__( self, @@ -179,7 +121,7 @@ def __init__( self.slope = slope self.multipole_comps = multipole_comps - self.k_m, self.angle_m = multipole_parameters_from( + self.k_m, self.angle_m = convert.multipole_k_m_and_phi_m_from( multipole_comps=multipole_comps, m=m ) self.angle_m *= units.deg.to(units.rad) diff --git a/test_autogalaxy/profiles/mass/total/test_power_law_multipole.py b/test_autogalaxy/profiles/mass/total/test_power_law_multipole.py index ff3dcd3eb..5ab8a7f89 100644 --- a/test_autogalaxy/profiles/mass/total/test_power_law_multipole.py +++ b/test_autogalaxy/profiles/mass/total/test_power_law_multipole.py @@ -1,6 +1,7 @@ -import autogalaxy as ag import pytest +import autogalaxy as ag + grid = ag.Grid2DIrregular([[1.0, 1.0], [2.0, 2.0], [3.0, 3.0], [2.0, 4.0]]) diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index bf2e7959e..da172a4be 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -128,3 +128,25 @@ def test__shear_magnitude_and_angle_from(): assert magnitude == pytest.approx(0.07071067811865, 1.0e-4) assert angle == pytest.approx(-22.5, 1.0e-4) + +def test__multipole_k_m_and_phi_m_from(): + + k_m, phi = ag.convert.multipole_k_m_and_phi_m_from(multipole_comps=(0.1, 0.0), m=1) + + assert k_m == pytest.approx(0.1, 1e-3) + assert phi == pytest.approx(90.0, 1e-3) + + k_m, phi = ag.convert.multipole_k_m_and_phi_m_from(multipole_comps=(0.0, 0.1), m=1) + + assert k_m == pytest.approx(0.1, 1e-3) + assert phi == pytest.approx(0.0, 1e-3) + + k_m, phi = ag.convert.multipole_k_m_and_phi_m_from(multipole_comps=(0.1, 0.0), m=2) + + assert k_m == pytest.approx(0.1, 1e-3) + assert phi == pytest.approx(45.0, 1e-3) + + k_m, phi = ag.convert.multipole_k_m_and_phi_m_from(multipole_comps=(-0.1, -0.1), m=2) + + assert k_m == pytest.approx(0.1, 1e-3) + assert phi == pytest.approx(0.141421356, 1e-3) \ No newline at end of file From fa19ef63558750ade51274a89a7f1fff6343ca48 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 17:50:17 +0100 Subject: [PATCH 09/11] black + fix test --- autogalaxy/convert.py | 3 ++- test_autogalaxy/test_convert.py | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index bc8ed3f81..bad8c6666 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -257,6 +257,7 @@ def shear_angle_from(gamma_1: float, gamma_2: float) -> float: magnitude, angle = shear_magnitude_and_angle_from(gamma_1=gamma_1, gamma_2=gamma_2) return angle + def multipole_k_m_and_phi_m_from( multipole_comps: Tuple[float, float], m: int ) -> Tuple[float, float]: @@ -317,4 +318,4 @@ def multipole_comps_from(k_m: float, phi_m: float, m: int) -> Tuple[float, float multipole_comp_0 = k_m * np.sin(phi_m * float(m) * units.deg.to(units.rad)) multipole_comp_1 = k_m * np.cos(phi_m * float(m) * units.deg.to(units.rad)) - return (multipole_comp_0, multipole_comp_1) \ No newline at end of file + return (multipole_comp_0, multipole_comp_1) diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index da172a4be..18a1cc3d5 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -129,8 +129,8 @@ def test__shear_magnitude_and_angle_from(): assert magnitude == pytest.approx(0.07071067811865, 1.0e-4) assert angle == pytest.approx(-22.5, 1.0e-4) -def test__multipole_k_m_and_phi_m_from(): +def test__multipole_k_m_and_phi_m_from(): k_m, phi = ag.convert.multipole_k_m_and_phi_m_from(multipole_comps=(0.1, 0.0), m=1) assert k_m == pytest.approx(0.1, 1e-3) @@ -146,7 +146,9 @@ def test__multipole_k_m_and_phi_m_from(): assert k_m == pytest.approx(0.1, 1e-3) assert phi == pytest.approx(45.0, 1e-3) - k_m, phi = ag.convert.multipole_k_m_and_phi_m_from(multipole_comps=(-0.1, -0.1), m=2) + k_m, phi = ag.convert.multipole_k_m_and_phi_m_from( + multipole_comps=(-0.1, -0.1), m=2 + ) - assert k_m == pytest.approx(0.1, 1e-3) - assert phi == pytest.approx(0.141421356, 1e-3) \ No newline at end of file + assert k_m == pytest.approx(0.14142135, 1e-3) + assert phi == pytest.approx(112.5, 1e-3) From d75f4b3430da90bda6a8b64ad2864df5460180c7 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 17:51:09 +0100 Subject: [PATCH 10/11] added test__multipole_comps_from --- test_autogalaxy/test_convert.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test_autogalaxy/test_convert.py b/test_autogalaxy/test_convert.py index 18a1cc3d5..a74c09e4a 100644 --- a/test_autogalaxy/test_convert.py +++ b/test_autogalaxy/test_convert.py @@ -152,3 +152,22 @@ def test__multipole_k_m_and_phi_m_from(): assert k_m == pytest.approx(0.14142135, 1e-3) assert phi == pytest.approx(112.5, 1e-3) + + +def test__multipole_comps_from(): + + multipole_comps = ag.convert.multipole_comps_from(k_m=0.1, phi_m=90.0, m=1) + + assert multipole_comps == pytest.approx((0.1, 0.0), 1e-3) + + multipole_comps = ag.convert.multipole_comps_from(k_m=0.1, phi_m=0.0, m=1) + + assert multipole_comps == pytest.approx((0.0, 0.1), 1e-3) + + multipole_comps = ag.convert.multipole_comps_from(k_m=0.1, phi_m=45.0, m=2) + + assert multipole_comps == pytest.approx((0.1, 0.0), 1e-3) + + multipole_comps = ag.convert.multipole_comps_from(k_m=0.14142135, phi_m=112.5, m=2) + + assert multipole_comps == pytest.approx((-0.1, -0.1), 1e-3) \ No newline at end of file From 1894d2c20b7b2d93a1ef0b07e87c8d1f409456eb Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 24 Apr 2024 17:53:34 +0100 Subject: [PATCH 11/11] docstrings --- autogalaxy/convert.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/autogalaxy/convert.py b/autogalaxy/convert.py index bad8c6666..3364b1812 100644 --- a/autogalaxy/convert.py +++ b/autogalaxy/convert.py @@ -119,9 +119,10 @@ def angle_from(ell_comps: Tuple[float, float]) -> float: An additional check is performed which requires the angle is between -45 and 135 degrees. This ensures that for certain values of `ell_comps` the angle does not jump from one boundary to another (e.g. without this check - certain values of `ell_comps` return -1.0 degrees and others 179.0 degrees). This ensures that when error - estimates are computed from samples of a lens model via marginalization, the calculation is not biased by the - angle jumping between these two values. + certain values of `ell_comps` return -1.0 degrees and others 179.0 degrees). + + This ensures that when error estimates are computed from samples of a lens model via marginalization, the + calculation is not biased by the angle jumping between these two values. Parameters ---------- @@ -181,9 +182,10 @@ def shear_magnitude_and_angle_from( Additional checks are performed which requires the angle is between -45 and 135 degrees. This ensures that for certain values of `gamma_1` and `gamma_2` the angle does not jump from one boundary to another (e.g. without - this check certain values of `gamma_1` and `gamma_2` return -1.0 degrees and others 179.0 degrees). This ensures - that when error estimates are computed from samples of a lens model via marginalization, the calculation is not - biased by the angle jumping between these two values. + this check certain values of `gamma_1` and `gamma_2` return -1.0 degrees and others 179.0 degrees). + + This ensures that when error estimates are computed from samples of a lens model via marginalization, the + calculation is not biased by the angle jumping between these two values. Parameters ---------- @@ -262,8 +264,9 @@ def multipole_k_m_and_phi_m_from( multipole_comps: Tuple[float, float], m: int ) -> Tuple[float, float]: """ - Converts the multipole component parameters to their normalizartion value `k_m` and angle `phi`, - which are given by: + Returns the multipole normalization value `k_m` and angle `phi` from the multipole component parameters. + + The normalization and angle are given by: .. math:: \phi^{\rm mass}_m = \arctan{\frac{\epsilon_{\rm 2}^{\rm mp}}{\epsilon_{\rm 2}^{\rm mp}}}, \, \, @@ -272,6 +275,13 @@ def multipole_k_m_and_phi_m_from( The conversion depends on the multipole order `m`, to ensure that all possible rotationally symmetric multiple mass profiles are available in the conversion for multiple components spanning -inf to inf. + Additional checks are performed which requires the angle `phi_m` is between -45 and 135 degrees. This ensures that + for certain multipole component values the angle does not jump from one boundary to another (e.g. without + this check certain values of `gamma_1` and `gamma_2` return -1.0 degrees and others 179.0 degrees). + + This ensures that when error estimates are computed from samples of a lens model via marginalization, the + calculation is not biased by the angle jumping between these two values. + Parameters ---------- multipole_comps @@ -294,8 +304,7 @@ def multipole_k_m_and_phi_m_from( def multipole_comps_from(k_m: float, phi_m: float, m: int) -> Tuple[float, float]: """ - Converts the multipole normalizartion value `k_m` and angle `phi` to their multipole component parameters, - which are given by: + Returns the multipole component parameters from their normalization value `k_m` and angle `phi`. .. math:: \phi^{\rm mass}_m = \arctan{\frac{\epsilon_{\rm 2}^{\rm mp}}{\epsilon_{\rm 2}^{\rm mp}}}, \, \,