# mypy: disable-error-code="dict-item"
from typing import Optional, Union, Annotated, Literal
from warnings import warn
from caskade import forward, Param
from ..backend_obj import ArrayLike, backend
from .base import ThinLens, CosmologyType, NameType, ZType
from . import func
__all__ = ("ExternalShear",)
[docs]
class ExternalShear(ThinLens):
"""
Represents an external shear effect in a gravitational lensing system.
Attributes
----------
name: str
Identifier for the lens instance.
cosmology: Cosmology
The cosmological model used for lensing calculations.
z_l: Optional[Union[ArrayLike, float]]
The redshift of the lens.
*Unit: unitless*
z_s: Optional[Union[ArrayLike, float]]
The redshift of the source.
*Unit: unitless*
x0, y0: Optional[Union[ArrayLike, float]]
Coordinates of the shear center in the lens plane.
*Unit: arcsec*
gamma_1, gamma_2: Optional[Union[ArrayLike, float]]
Shear components.
*Unit: unitless*
Notes
------
The shear components gamma_1 and gamma_2 represent an external shear, a gravitational
distortion that can be caused by nearby structures outside of the main lens galaxy.
"""
_null_params = {
"x0": 0.0,
"y0": 0.0,
"gamma_1": 0.1,
"gamma_2": 0.1,
}
def __init__(
self,
cosmology: CosmologyType,
z_l: ZType = None,
z_s: ZType = None,
x0: Annotated[
Optional[Union[ArrayLike, float]],
"x-coordinate of the shear center in the lens plane",
True,
] = None,
y0: Annotated[
Optional[Union[ArrayLike, float]],
"y-coordinate of the shear center in the lens plane",
True,
] = None,
gamma_1: Annotated[
Optional[Union[ArrayLike, float]],
"Shear component in the x-direction",
True,
] = None,
gamma_2: Annotated[
Optional[Union[ArrayLike, float]],
"Shear component in the y-direction",
True,
] = None,
parametrization: Literal["cartesian", "angular"] = "cartesian",
s: Annotated[
float, "Softening length for the elliptical power-law profile"
] = 0.0,
name: NameType = None,
**kwargs,
):
super().__init__(cosmology=cosmology, z_l=z_l, name=name, z_s=z_s)
self.x0 = Param("x0", x0, shape=(), units="arcsec")
self.y0 = Param("y0", y0, shape=(), units="arcsec")
self.gamma_1 = Param("gamma_1", gamma_1, shape=(), units="unitless")
self.gamma_2 = Param("gamma_2", gamma_2, shape=(), units="unitless")
self._parametrization = "cartesian"
self.parametrization = parametrization
if self.parametrization == "angular":
self.gamma.value = kwargs.get("gamma", None)
self.phi.value = kwargs.get("phi", None)
self.s = s
@property
def parametrization(self) -> str:
return self._parametrization
@parametrization.setter
def parametrization(self, value: str):
if value not in ["cartesian", "angular"]:
raise ValueError(
f"Invalid parametrization: {value}. Must be 'cartesian' or 'angular'."
)
if value == "angular" and self._parametrization != "angular":
self.gamma = Param(
"gamma",
shape=self.gamma_1.shape if self.gamma_1.static else (),
units="unitless",
)
self.phi = Param(
"phi",
shape=self.gamma_1.shape if self.gamma_1.static else (),
units="radians",
)
if self.gamma_1.static:
warn(
f"Parameter {self.gamma_1.name} was static, value now overridden by new {value} parametrization. To remove this warning, have {self.gamma_1.name} be dynamic when changing parametrizations.",
)
self.gamma_1.value = lambda p: func.gamma_phi_to_gamma1(
p["gamma"].value, p["phi"].value
)
if self.gamma_2.static:
warn(
f"Parameter {self.gamma_2.name} was static, value now overridden by new {value} parametrization. To remove this warning, have {self.gamma_2.name} be dynamic when changing parametrizations.",
)
self.gamma_2.value = lambda p: func.gamma_phi_to_gamma2(
p["gamma"].value, p["phi"].value
)
self.gamma_1.link(self.gamma)
self.gamma_1.link(self.phi)
self.gamma_2.link(self.gamma)
self.gamma_2.link(self.phi)
if value == "cartesian" and self._parametrization != "cartesian":
self.gamma_1.value = None
self.gamma_2.value = None
try:
if self.gamma.static:
warn(
f"Parameter {self.gamma.name} was static, value now overridden by new {value} parametrization. To remove this warning, have {self.gamma.name} be dynamic when changing parametrizations.",
)
self.gamma_1.unlink(self.gamma)
self.gamma_2.unlink(self.gamma)
del self.gamma
except AttributeError:
pass
try:
if self.phi.static:
warn(
f"Parameter {self.phi.name} was static, value now overridden by new {value} parametrization. To remove this warning, have {self.phi.name} be dynamic when changing parametrizations.",
)
self.gamma_1.unlink(self.phi)
self.gamma_2.unlink(self.phi)
del self.phi
except AttributeError:
pass
self._parametrization = value
[docs]
@forward
def reduced_deflection_angle(
self,
x: ArrayLike,
y: ArrayLike,
x0: Annotated[ArrayLike, "Param"],
y0: Annotated[ArrayLike, "Param"],
gamma_1: Annotated[ArrayLike, "Param"],
gamma_2: Annotated[ArrayLike, "Param"],
) -> tuple[ArrayLike, ArrayLike]:
"""
Calculates the reduced deflection angle.
Parameters
----------
x: ArrayLike
x-coordinates in the lens plane.
*Unit: arcsec*
y: ArrayLike
y-coordinates in the lens plane.
*Unit: arcsec*
Returns
-------
x_component: ArrayLike
Deflection Angle in x-direction.
*Unit: arcsec*
y_component: ArrayLike
Deflection Angle in y-direction.
*Unit: arcsec*
"""
return func.reduced_deflection_angle_external_shear(
x0, y0, gamma_1, gamma_2, x, y
)
[docs]
@forward
def potential(
self,
x: ArrayLike,
y: ArrayLike,
x0: Annotated[ArrayLike, "Param"],
y0: Annotated[ArrayLike, "Param"],
gamma_1: Annotated[ArrayLike, "Param"],
gamma_2: Annotated[ArrayLike, "Param"],
) -> ArrayLike:
"""
Calculates the lensing potential.
Parameters
----------
x: ArrayLike
x-coordinates in the lens plane.
*Unit: arcsec*
y: ArrayLike
y-coordinates in the lens plane.
*Unit: arcsec*
Returns
-------
ArrayLike
The lensing potential.
*Unit: arcsec^2*
"""
return func.potential_external_shear(x0, y0, gamma_1, gamma_2, x, y)
[docs]
@forward
def convergence(
self,
x: ArrayLike,
y: ArrayLike,
) -> ArrayLike:
"""
The convergence is undefined for an external shear.
Parameters
----------
x: ArrayLike
x-coordinates in the lens plane.
*Unit: arcsec*
y: ArrayLike
y-coordinates in the lens plane.
*Unit: arcsec*
Returns
-------
ArrayLike
Convergence for an external shear.
*Unit: unitless*
Raises
------
NotImplementedError
This method is not implemented as the convergence is not defined
for an external shear.
"""
return backend.zeros_like(x)