feat: Lenstool-native dPIE parameterization (from_lenstool + dPIEMassLenstool) and analytic potential#487
Merged
Merged
Conversation
- dPIEMass.from_lenstool / dPIEMassSph.from_lenstool convert Lenstool .par parameters (v_disp sigma_LT, ellipticite, angle_pos, core_radius, cut_radius) to (b0, ell_comps, ra, rs); conventions verified against the Lenstool C source (set_potfile.c, set_lens.c, e_grad.c). - dPIEMassLenstool / dPIEMassLenstoolSph wrapper profiles support model-fitting with priors directly on the Lenstool parameters; prior configs added. - dPIEMass.potential_2d_from: replace the MGE approximation (up to 15% off vs deflections for elliptical profiles) with the analytic KK93 I0.5 potential ported from Lenstool pi05 (e_pcpx.c); grad(psi) matches deflections to 1e-8. - _ellip() min-clamps at 1e-5 (Lenstool set_lens.c identical), fixing NaN deflections at ell_comps=(0,0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUuWXiS23FvmfQPLvMNjeM
This was referenced Jul 8, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Lenstool-native parameterization of the dPIE mass profile, so Lenstool users can construct and fit PyAutoGalaxy models directly in the parameters they read out of
.parfiles and Lenstool-based papers: fiducial velocity dispersionsigma(v_disp),ellipticity(ellipticite),angle_pos,r_core,r_cut. Every conversion convention was verified against the Lenstool C source (set_potfile.c,set_lens.c,e_grad.c,e_pcpx.c) — see #485 for the research trail.Two correctness fixes surfaced during parity validation:
dPIEMass.potential_2d_fromwas an MGE approximation whose gradient disagreed with the ci05f deflections by up to 15% for elliptical profiles (the MGE reconstructs a different elliptical geometry than the Kassiola & Kovner pseudo-elliptical mass model). It is replaced by the analytic KK93 I0.5 potential ported from Lenstool'spi05; finite-difference grad(psi) now matches deflections to 1.7e-8._ellip()now min-clamps at 1e-5 (identical to Lenstool'sset_lens.cclamp): the ci05 integral is degenerate at exactly zero ellipticity, soell_comps=(0.0, 0.0)elliptical dPIE profiles previously returned NaN deflections.API Changes
Added Lenstool-parameterized construction and fitting for the dPIE family:
from_lenstool(...)classmethods ondPIEMass/dPIEMassSph, and new model-fittable wrapper profilesdPIEMassLenstool/dPIEMassLenstoolSph(with prior configs).dPIEMass.potential_2d_fromvalues change (MGE approximation → exact analytic potential); dPIE deflections at |ell_comps| < 1e-5 change from NaN to the tiny-ellipticity limit. No removals or renames.See full details below.
Test Plan
test_dual_pseudo_isothermal_mass.py— 15 pass (10 new: conversion math, sqrt(3/2) sigma cross-check, emass→epot vs Lenstool's own formula, angle/radii mapping, sph↔elliptical parity, isothermal-limit b0=theta_E, potential-gradient consistency ×2, wrapper parity, af.Model composition)autolens_workspace_test/scripts/cluster/lenstool_parity.py— 6 legs, agreement 1e-7 to 1e-10 (companion PR)Validation checklist (--auto run — in-session directives, no separate plan pre-approval)
Full API Changes (for automation & release notes)
Added
ag.mp.dPIEMass.from_lenstool(centre, ellipticity, angle_pos, sigma, r_core, r_cut, redshift_object, redshift_source, cosmology=)— construct adPIEMassfrom Lenstool.parparameters; converts sigma_LT → b0 via 6·648000·(sigma/c)²·(D_LS/D_S), emass → ell_comps, radii one-to-one.ag.mp.dPIEMassSph.from_lenstool(centre, sigma, r_core, r_cut, redshift_object, redshift_source, cosmology=)— spherical variant.ag.mp.dPIEMassLenstool(centre, ellipticity, angle_pos, sigma, r_core, r_cut, redshift_object, redshift_source)— model-fittable wrapper (subclass ofdPIEMass) whose free parameters are the Lenstool ones; Planck15 cosmology fixed internally (NFWMCRLudlow convention). Prior config added.ag.mp.dPIEMassLenstoolSph(centre, sigma, r_core, r_cut, redshift_object, redshift_source)— spherical wrapper (subclass ofdPIEMassSph). Prior config added.Changed Behaviour
dPIEMass.potential_2d_from— now the analytic Kassiola & Kovner I0.5 potential (Lenstoolpi05port,psi = b0·rs/(rs−ra)·(pi05(ra) − pi05(rs))) instead of a 30-Gaussian MGE approximation. Elliptical-profile potentials change by up to ~15%; the new values are consistent with the deflection field to 1e-8. Any consumer of dPIE potentials (e.g. time delays) gets corrected numbers.PIEMass._ellip/dPIEMass._ellip— ellipticity clamped to [1e-5, 0.99999] (was max-clamp only). Deflections/convergence/potential of profiles with |ell_comps| < 1e-5 change from NaN (deflections) / unclamped to the Lenstool-identical tiny-ellipticity limit.Migration
ag.mp.dPIEMass.from_lenstool(sigma=v_disp, ellipticity=ellipticite, angle_pos=angle_pos, r_core=core_radius, r_cut=cut_radius, redshift_object=z_l, redshift_source=z_s). Notesigmais Lenstool's fiducial sigma_LT, not the central dispersion (sigma_0 = sqrt(3/2)·sigma_LT).🤖 Generated with Claude Code