Source code for caustics.lenses.sie

# mypy: disable-error-code="operator,union-attr,dict-item"
from typing import Optional, Union, Annotated, Literal
from warnings import warn

from caskade import forward, Param

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

__all__ = ("SIE",)


[docs] class SIE(Angle_Mixin, ThinLens): """ A class representing a Singular Isothermal Ellipsoid (SIE) strong gravitational lens model. This model is based on Keeton 2001, which can be found at https://arxiv.org/abs/astro-ph/0102341. Attributes ---------- name: str The name of the lens. cosmology: Cosmology An instance of the Cosmology class. 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: Optional[Union[ArrayLike, float]] The x-coordinate of the lens center. *Unit: arcsec* y0: Optional[Union[ArrayLike, float]] The y-coordinate of the lens center. *Unit: arcsec* q: Optional[Union[ArrayLike, float]] The axis ratio of the lens. *Unit: unitless* phi: Optional[Union[ArrayLike, float]] The orientation angle of the lens (position angle). *Unit: radians* Rein: Optional[Union[ArrayLike, float]] The Einstein radius of the lens. *Unit: arcsec* s: float The core radius of the lens (defaults to 0.0). *Unit: arcsec* """ _null_params = { "x0": 0.0, "y0": 0.0, "q": 0.5, "phi": 0.0, "Rein": 1.0, } def __init__( self, cosmology: CosmologyType, z_l: ZType = None, z_s: ZType = None, x0: Annotated[ Optional[Union[ArrayLike, float]], "The x-coordinate of the lens center", True, ] = None, y0: Annotated[ Optional[Union[ArrayLike, float]], "The y-coordinate of the lens center", True, ] = None, q: Annotated[ Optional[Union[ArrayLike, float]], "The axis ratio of the lens convergence", True, ] = None, phi: Annotated[ Optional[Union[ArrayLike, float]], "The orientation angle of the lens (position angle)", True, ] = None, Rein: Annotated[ Optional[Union[ArrayLike, float]], "The Einstein radius of the lens", True ] = None, parametrization: Literal["Rein", "velocity_dispersion"] = "Rein", sigma_v: Optional[Union[ArrayLike, float]] = 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, "The core radius of the lens"] = 0.0, name: NameType = None, **kwargs, ): """ Initialize the SIE lens model. """ 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._parametrization = "Rein" self.parametrization = parametrization if self.parametrization == "velocity_dispersion": self.sigma_v = sigma_v 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 @property def parametrization(self) -> str: return self._parametrization @parametrization.setter def parametrization(self, value: str): if value not in ["Rein", "velocity_dispersion"]: raise ValueError( f"Invalid parametrization: {value}. Must be 'Rein' or 'velocity_dispersion'." ) if ( value == "velocity_dispersion" and self._parametrization != "velocity_dispersion" ): self.sigma_v = Param( "sigma_v", shape=self.Rein.shape if self.Rein.static else (), units="km/s", valid=(0, None), ) if self.Rein.static: warn( f"Parameter {self.Rein.name} is static, value now overridden by new {value} parametrization. To remove this warning, have {self.Rein.name} be dynamic when changing parametrizations.", ) def sigma_v_to_rein(p): Dls = p["cosmology"].angular_diameter_distance_z1z2( p["z_l"].value, p["z_s"].value ) Ds = p["cosmology"].angular_diameter_distance(p["z_s"].value) return func.sigma_v_to_rein_sie(p["sigma_v"].value, Dls, Ds) self.Rein.value = lambda p: sigma_v_to_rein(p) self.Rein.link(self.sigma_v) self.Rein.link(self.z_s) self.Rein.link(self.z_l) self.Rein.link("cosmology", self.cosmology) if value == "Rein" and self.parametrization != "Rein": try: self.Rein = None if self.sigma_v.static: warn( f"Parameter {self.sigma_v.name} was static, value now overridden by new {value} parametrization. To remove this warning, have {self.sigma_v.name} be dynamic when changing parametrizations.", ) del self.sigma_v 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"], q: Annotated[ArrayLike, "Param"], phi: Annotated[ArrayLike, "Param"], Rein: Annotated[ArrayLike, "Param"], ) -> tuple[ArrayLike, ArrayLike]: """ Calculate the physical deflection angle. Parameters ---------- x: ArrayLike The x-coordinate of the lens. *Unit: arcsec* y: ArrayLike The y-coordinate of the lens. *Unit: arcsec* Returns -------- x_component: ArrayLike The x-component of the deflection angle. *Unit: arcsec* y_component: ArrayLike The y-component of the deflection angle. *Unit: arcsec* """ return func.reduced_deflection_angle_sie(x0, y0, q, phi, Rein, x, y, self.s)
[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"], ) -> ArrayLike: """ Compute the lensing potential. Parameters ---------- x: ArrayLike The x-coordinate of the lens. *Unit: arcsec* y: ArrayLike The y-coordinate of the lens. *Unit: arcsec* Returns ------- ArrayLike The lensing potential. *Unit: arcsec^2* """ return func.potential_sie(x0, y0, q, phi, Rein, x, y, self.s)
[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"], ) -> ArrayLike: """ Calculate the projected mass density. Parameters ---------- x: ArrayLike The x-coordinate of the lens. *Unit: arcsec* y: ArrayLike The y-coordinate of the lens. *Unit: arcsec* Returns ------- ArrayLike The projected mass density. *Unit: unitless* """ return func.convergence_sie(x0, y0, q, phi, Rein, x, y, self.s)