@@ -78,28 +78,40 @@ def wrapper(
7878 return wrapper
7979
8080
81- class OperateDeflections :
81+ class LensCalc :
8282 """
83- Packages methods which manipulate the 2D deflection angle map returned from the `deflections_yx_2d_from` function
84- of a mass object (e.g. a `MassProfile`, `Galaxy`).
83+ Computes lensing quantities from a deflection-angle callable and an optional potential callable.
8584
86- The majority of methods are those which from the 2D deflection angle map compute lensing quantities like a 2D
87- shear field, magnification map or the Einstein Radius.
85+ The deflection callable is used to compute the Hessian, Jacobian, convergence, shear,
86+ magnification, critical curves, caustics, and Einstein radius/mass. If a potential
87+ callable is also supplied, ``fermat_potential_from`` is available as well.
8888
8989 Parameters
9090 ----------
9191 deflections_yx_2d_from
92- A callable with signature ``(grid, xp=np, **kwargs)`` that returns the 2D deflection angles on the given
93- grid. Typically a bound method of a ``MassProfile``, ``Galaxy``, or ``Galaxies`` instance.
92+ A callable with signature ``(grid, xp=np, **kwargs)`` that returns the 2D deflection
93+ angles on the given grid. Typically a bound method of a ``MassProfile``, ``Galaxy``,
94+ or ``Galaxies`` instance.
95+ potential_2d_from
96+ Optional callable with signature ``(grid, xp=np, **kwargs)`` that returns the 2D
97+ lensing potential on the given grid. Required only for ``fermat_potential_from``.
9498 """
9599
96- def __init__ (self , deflections_yx_2d_from ):
100+ def __init__ (self , deflections_yx_2d_from , potential_2d_from = None ):
97101 self .deflections_yx_2d_from = deflections_yx_2d_from
102+ self .potential_2d_from = potential_2d_from
98103
99104 @classmethod
100105 def from_mass_obj (cls , mass_obj ):
101- """Construct from any object that has a ``deflections_yx_2d_from`` method."""
102- return cls (deflections_yx_2d_from = mass_obj .deflections_yx_2d_from )
106+ """Construct from any object that has a ``deflections_yx_2d_from`` method.
107+
108+ If the object also exposes ``potential_2d_from``, it is captured so that
109+ ``fermat_potential_from`` is available on the returned instance.
110+ """
111+ return cls (
112+ deflections_yx_2d_from = mass_obj .deflections_yx_2d_from ,
113+ potential_2d_from = getattr (mass_obj , "potential_2d_from" , None ),
114+ )
103115
104116 @classmethod
105117 def from_tracer (
@@ -130,6 +142,8 @@ def from_tracer(
130142 Index of the second plane used by ``deflections_between_planes_from``.
131143 Ignored when ``use_multi_plane=False``. Defaults to ``-1`` (source plane).
132144 """
145+ potential_2d_from = getattr (tracer , "potential_2d_from" , None )
146+
133147 if use_multi_plane :
134148 from functools import partial
135149
@@ -138,9 +152,13 @@ def from_tracer(
138152 tracer .deflections_between_planes_from ,
139153 plane_i = plane_i ,
140154 plane_j = plane_j ,
141- )
155+ ),
156+ potential_2d_from = potential_2d_from ,
142157 )
143- return cls (deflections_yx_2d_from = tracer .deflections_yx_2d_from )
158+ return cls (
159+ deflections_yx_2d_from = tracer .deflections_yx_2d_from ,
160+ potential_2d_from = potential_2d_from ,
161+ )
144162
145163 def time_delay_geometry_term_from (self , grid , xp = np ) -> aa .Array2D :
146164 """
@@ -177,6 +195,39 @@ def time_delay_geometry_term_from(self, grid, xp=np) -> aa.Array2D:
177195 return aa .ArrayIrregular (values = delay )
178196 return aa .Array2D (values = delay , mask = grid .mask )
179197
198+ def fermat_potential_from (self , grid , xp = np ) -> aa .Array2D :
199+ """
200+ Returns the Fermat potential for a given grid of image-plane positions.
201+
202+ This is the sum of the geometric time delay term and the gravitational (Shapiro) delay
203+ term (i.e. the lensing potential), and is given by:
204+
205+ .. math::
206+ \\ phi(\\ boldsymbol{\\ theta}) = \\ frac{1}{2} |\\ boldsymbol{\\ theta} - \\ boldsymbol{\\ beta}|^2
207+ - \\ psi(\\ boldsymbol{\\ theta})
208+
209+ Requires that ``potential_2d_from`` was supplied at construction (e.g. via
210+ ``LensCalc.from_mass_obj`` or ``LensCalc.from_tracer``).
211+
212+ Parameters
213+ ----------
214+ grid
215+ The 2D grid of (y,x) arc-second coordinates the Fermat potential is computed on.
216+ xp
217+ The array module (``numpy`` or ``jax.numpy``).
218+ """
219+ if self .potential_2d_from is None :
220+ raise ValueError (
221+ "fermat_potential_from requires a potential_2d_from callable. "
222+ "Construct LensCalc with potential_2d_from, or use from_mass_obj / from_tracer."
223+ )
224+ time_delay = self .time_delay_geometry_term_from (grid = grid , xp = xp )
225+ potential = self .potential_2d_from (grid = grid , xp = xp )
226+ fermat_potential = time_delay - potential
227+ if isinstance (grid , aa .Grid2DIrregular ):
228+ return aa .ArrayIrregular (values = fermat_potential )
229+ return aa .Array2D (values = fermat_potential , mask = grid .mask )
230+
180231 def tangential_eigen_value_from (self , grid , xp = np ) -> aa .Array2D :
181232 """
182233 Returns the tangential eigen values of lensing jacobian, which are given by the expression:
0 commit comments