Source code for caustics.lenses.epl

# mypy: disable-error-code="operator,dict-item"
from typing import Optional, Union, Annotated

from caskade import forward, Param

from ..backend_obj import backend, ArrayLike
from .base import ThinLens, CosmologyType, NameType, ZType
from . import func
from ..angle_mixin import Angle_Mixin

__all__ = ("EPL",)


[docs] class EPL(Angle_Mixin, ThinLens): """ Elliptical power law (EPL, aka singular power-law ellipsoid) profile. This class represents a thin gravitational lens model with an elliptical power law profile. The lensing equations are solved iteratively using an approach based on Tessore et al. 2015. Attributes ---------- n_iter: int Number of iterations for the iterative solver. chunk_size: int Number of iterations to do in parallel for the iterative solver. s: float Softening length for the elliptical power-law profile. *Unit: arcsec* Parameters ---------- z_l: Optional[Union[ArrayLike, float]] This is the redshift of the lens. In the context of gravitational lensing, the lens is the galaxy or other mass distribution that is bending the light from a more distant source. *Unit: unitless* x0 and y0: Optional[Union[ArrayLike, float]] These are the coordinates of the lens center in the lens plane. The lens plane is the plane perpendicular to the line of sight in which the deflection of light by the lens is considered. *Unit: arcsec* q: Optional[Union[ArrayLike, float]] This is the axis ratio of the lens, i.e., the ratio of the minor axis to the major axis of the elliptical lens. *Unit: unitless* phi: Optional[Union[ArrayLike, float]] This is the orientation of the lens on the sky, typically given as an angle measured counter-clockwise from some reference direction. *Unit: radians* Rein: Optional[Union[ArrayLike, float]] The Einstein radius of the lens, exact at q=1.0. *Unit: arcsec* t: Optional[Union[ArrayLike, float]] This is the power-law slope parameter of the lens model. In the context of the EPL model, t is equivalent to the gamma parameter minus one, where gamma is the power-law index of the radial mass distribution of the lens. *Unit: unitless* """ _null_params = { "x0": 0.0, "y0": 0.0, "q": 0.5, "phi": 0.0, "Rein": 1.0, "t": 1.0, } def __init__( self, cosmology: CosmologyType, z_l: ZType = None, z_s: ZType = None, x0: Annotated[ Optional[Union[ArrayLike, float]], "X coordinate of the lens center", True ] = None, y0: Annotated[ Optional[Union[ArrayLike, float]], "Y coordinate of the lens center", True ] = None, q: Annotated[ Optional[Union[ArrayLike, float]], "Axis ratio of the lens", True ] = None, phi: Annotated[ Optional[Union[ArrayLike, float]], "Position angle of the lens", True ] = None, Rein: Annotated[ Optional[Union[ArrayLike, float]], "Einstein radius of the lens", True ] = None, t: Annotated[ Optional[Union[ArrayLike, float]], "Power law slope (`gamma-1`) of the lens", True, ] = None, angle_system: str = "q_phi", e1: Optional[Union[ArrayLike, float]] = None, e2: Optional[Union[ArrayLike, float]] = None, c1: Optional[Union[ArrayLike, float]] = None, c2: Optional[Union[ArrayLike, float]] = None, s: Annotated[ float, "Softening length for the elliptical power-law profile" ] = 0.0, n_iter: Annotated[int, "Number of iterations for the iterative solver"] = 18, chunk_size: Annotated[ Optional[int], "Number of chunks for the iterative solver" ] = None, name: NameType = None, ): """ Initialize an EPL lens model. Parameters ----------- name: string Name of the lens model. cosmology: Cosmology Cosmology object that provides cosmological distance calculations. z_l: Optional[ArrayLike] Redshift of the lens. If not provided, it is considered as a free parameter. *Unit: unitless* x0: Optional[ArrayLike] X coordinate of the lens center. If not provided, it is considered as a free parameter. *Unit: arcsec* y0: Optional[ArrayLike] Y coordinate of the lens center. If not provided, it is considered as a free parameter. *Unit: arcsec* q: Optional[ArrayLike] Axis ratio of the lens. If not provided, it is considered as a free parameter. *Unit: unitless* phi: Optional[ArrayLike] Position angle of the lens. If not provided, it is considered as a free parameter. *Unit: radians* Rein: Optional[ArrayLike] Einstein radius of the lens. *Unit: arcsec* t: Optional[ArrayLike] Power law slope (`gamma-1`) of the lens. If not provided, it is considered as a free parameter. *Unit: unitless* s: float Softening length for the elliptical power-law profile. *Unit: arcsec* n_iter: int Number of iterations for the iterative solver. chunk_size: Optional[int] Number of iterations to do in parallel for the iterative solver. If not provided, it is set to n_iter which is fastest, but uses more memory. """ super().__init__(cosmology, 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.q = Param("q", q, shape=(), units="unitless", valid=(0, 1)) self.phi = Param( "phi", phi, shape=(), units="radians", valid=(0, backend.pi), cyclic=True ) self.Rein = Param("Rein", Rein, shape=(), units="arcsec", valid=(0, None)) self.t = Param("t", t, shape=(), units="unitless", valid=(0, 2)) self.angle_system = angle_system if self.angle_system == "e1_e2": self.e1 = e1 self.e2 = e2 elif self.angle_system == "c1_c2": self.c1 = c1 self.c2 = c2 self.s = s self.n_iter = n_iter self.chunk_size = chunk_size
[docs] @forward def reduced_deflection_angle( self, x: ArrayLike, y: ArrayLike, x0: Annotated[ArrayLike, "Param"], y0: Annotated[ArrayLike, "Param"], q: Annotated[ArrayLike, "Param"], phi: Annotated[ArrayLike, "Param"], Rein: Annotated[ArrayLike, "Param"], t: Annotated[ArrayLike, "Param"], ) -> tuple[ArrayLike, ArrayLike]: """ Compute the reduced deflection angles of the lens. 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 *Unit: arcsec* y_component: ArrayLike Deflection Angle *Unit: arcsec* """ return func.reduced_deflection_angle_epl( x0, y0, q, phi, Rein, t, x, y, self.n_iter, self.chunk_size )
[docs] @forward def potential( self, x: ArrayLike, y: ArrayLike, x0: Annotated[ArrayLike, "Param"], y0: Annotated[ArrayLike, "Param"], q: Annotated[ArrayLike, "Param"], phi: Annotated[ArrayLike, "Param"], Rein: Annotated[ArrayLike, "Param"], t: Annotated[ArrayLike, "Param"], ): """ Compute the lensing potential of the lens. 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_epl( x0, y0, q, phi, Rein, t, x, y, self.n_iter, self.chunk_size )
[docs] @forward def convergence( self, x: ArrayLike, y: ArrayLike, x0: Annotated[ArrayLike, "Param"], y0: Annotated[ArrayLike, "Param"], q: Annotated[ArrayLike, "Param"], phi: Annotated[ArrayLike, "Param"], Rein: Annotated[ArrayLike, "Param"], t: Annotated[ArrayLike, "Param"], ) -> ArrayLike: """ Compute the convergence of the lens, which describes the local density of the lens. Parameters ---------- x: ArrayLike X coordinates in the lens plane. *Unit: arcsec* y: ArrayLike Y coordinates in the lens plane. *Unit: arcsec* Returns ------- ArrayLike The convergence of the lens. *Unit: unitless* """ return func.convergence_epl(x0, y0, q, phi, Rein, t, x, y, self.s)