Overview
dPIEMass / dPIEMassSph are direct ports of LensTool's C code but are parameterized as (ra, rs, b0) in arcsec, while LensTool users think in (σ_LT [km/s], r_core, r_cut) as read out of .par files and papers. This task adds a LensTool-facing constructor with a verified parameter conversion, unit-tested conversion math, and a cross-implementation parity script — the foundation for the flagship "PyAutoLens for LensTool users" cluster example (PyAutoMind docs/cluster/8_lenstool_users_example.md).
Autonomy: supervised --auto (launched 2026-07-08, no heart-ack). Judgment gates are batched as issue questions; run checkpoints here.
Plan
- Verify LensTool's exact input conventions against the public LensTool source (
piemd/ci05 routines): the σ prefactor (6π fiducial vs 4π central), the .par ellipticite definition and its internal conversion to the Kassiola & Kovner ε, the position-angle convention, and radius units (core_radius vs core_radius_kpc).
- Add
from_lenstool(...) classmethod constructors to dPIEMass and dPIEMassSph converting (σ_LT, r_core, r_cut, ellipticity, angle, z_lens, z_source, cosmology) → (b0, ra, rs, ell_comps).
- Unit-test the conversion math (numpy-only): sqrt(3/2) σ relation, limiting-case b0 = θ_E (ra→0, rs→∞, q=1), analytic M(R) anchor from Bergamini et al. 2019 App. C.
- Add a numerical parity script in
autolens_workspace_test/scripts/cluster/ comparing convergence/deflection/potential against an independent dPIE implementation and published values.
- Document any irreconcilable convention differences prominently (they bound how close the flagship example can get).
Detailed implementation plan
Affected Repositories
- PyAutoGalaxy (primary — profile + unit tests)
- PyAutoLens (docs autosummary mirror if API surface grows)
- autolens_workspace_test (parity script; cross-package checks live here, never in library test dirs)
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoGalaxy |
main |
clean (parallel claim psf-oversample-refactor touches only autogalaxy/operate/image.py — disjoint) |
| ./PyAutoLens |
main |
clean |
| ./autolens_workspace_test |
main |
dirty (pre-existing, other tasks; worktree branches from origin/main) |
Suggested branch: feature/dpie-lenstool-param
Worktree: ~/Code/PyAutoLabs-wt/dpie-lenstool-param/
Implementation Steps
- Phase 1 — convention verification (research-first). Fetch the public LensTool source (git-cral.univ-lyon1.fr or GitHub mirror) and read the
piemd / ci05 / scaling routines to pin down: (a) exact b0 normalization from v_disp (expected: E0 = 6π (σ_LT/c)² D_LS/D_S with σ_LT = Elíasdóttir's σ_dPIE; b0 = E0·(rs²−ra²)/rs² per the existing docstring chain, equivalently b0 = 4π (σ_0/c)²·D_LS/D_S·(rs²−ra²)/rs² with σ_0 = √(3/2)·σ_LT); (b) whether .par ellipticite is (a²−b²)/(a²+b²) and where LensTool converts it to the KK93 ε = (a−b)/(a+b) that ci05 consumes (the PyAutoGalaxy port's _ellip() uses |ell_comps| = (1−q)/(1+q) directly as eps — dual_pseudo_isothermal_mass.py:458); (c) position-angle zero-point and rotation direction vs PyAuto's phi (counter-clockwise from positive x? vs from north); (d) core_radius/cut_radius arcsec vs _kpc variants. Record findings in the docstring and on this issue.
- Phase 2 — converter.
dPIEMass.from_lenstool(centre, ellipticity, angle, sigma, r_core, r_cut, redshift_object, redshift_source, cosmology=ag.cosmo.Planck15()) classmethod: ellipticity → axis-ratio q = √((1−e)/(1+e)) → ell_comps via the existing convert helpers; σ_LT → b0 via the verified prefactor and D_LS/D_S from the cosmology object; kpc-variant support via kwargs or a documented pre-conversion. dPIEMassSph.from_lenstool(...) without ellipticity/angle. Docstring must loudly document the √(3/2) fiducial-vs-central σ trap and cite Elíasdóttir et al. 2007 App. A / Bergamini et al. 2019.
- Phase 3 — unit tests in
test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_mass.py: conversion round-trips, limiting cases (ra→0, rs→∞, e=0 recovers isothermal θ_E = 4π σ_0²/c² D_LS/D_S), M(R) analytic anchor, ellipticity/angle mapping vs a directly-constructed profile. Numpy-only (no JAX in unit tests).
- Phase 4 — parity script
autolens_workspace_test/scripts/cluster/lenstool_parity.py: convergence/potential/deflection grids from a from_lenstool-constructed profile vs an independent implementation (e.g. lenstronomy's dPIE/PJaffe) and/or published LensTool model values; report max fractional deviations; assert tolerances.
- Phase 5 — docs: autosummary entries (PyAutoGalaxy + PyAutoLens mirror if needed).
Key Files
PyAutoGalaxy/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_mass.py — dPIEMass/dPIEMassSph, _ellip(), existing docstring conversion chain
PyAutoGalaxy/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_potential.py — potential-parameterized sibling (out of scope unless conventions force it in)
PyAutoGalaxy/test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_mass.py — unit tests
autolens_workspace_test/scripts/cluster/lenstool_parity.py — new parity script
Open questions (batched — answer at leisure; run proceeds on the recommended default)
- Constructor vs wrapper class:
from_lenstool classmethod (recommended, no new model-space class) suffices for constructing instances, but users wanting to fit directly in LensTool parameters (priors on σ_LT/r_cut) would need a dPIEMassLensTool profile class whose init takes LensTool params. Default: classmethod now; wrapper class deferred until the flagship example (task 8) demonstrates the need.
- Cosmology default: Planck15 (PyAuto default) vs LensTool's typical default (ΩM=0.3, ΩΛ=0.7, h=0.7). Default: PyAuto's Planck15, with the cosmology argument documented as the knob for exact LensTool comparison.
Original Prompt
Click to expand starting prompt
$(cat PyAutoMind/feature/cluster/6_dpie_lenstool_parameterization.md)
Overview
dPIEMass/dPIEMassSphare direct ports of LensTool's C code but are parameterized as (ra,rs,b0) in arcsec, while LensTool users think in (σ_LT [km/s], r_core, r_cut) as read out of.parfiles and papers. This task adds a LensTool-facing constructor with a verified parameter conversion, unit-tested conversion math, and a cross-implementation parity script — the foundation for the flagship "PyAutoLens for LensTool users" cluster example (PyAutoMinddocs/cluster/8_lenstool_users_example.md).Autonomy: supervised
--auto(launched 2026-07-08, no heart-ack). Judgment gates are batched as issue questions; run checkpoints here.Plan
piemd/ci05routines): the σ prefactor (6π fiducial vs 4π central), the.parellipticitedefinition and its internal conversion to the Kassiola & Kovner ε, the position-angle convention, and radius units (core_radiusvscore_radius_kpc).from_lenstool(...)classmethod constructors todPIEMassanddPIEMassSphconverting (σ_LT, r_core, r_cut, ellipticity, angle, z_lens, z_source, cosmology) → (b0,ra,rs,ell_comps).autolens_workspace_test/scripts/cluster/comparing convergence/deflection/potential against an independent dPIE implementation and published values.Detailed implementation plan
Affected Repositories
Branch Survey
psf-oversample-refactortouches onlyautogalaxy/operate/image.py— disjoint)Suggested branch:
feature/dpie-lenstool-paramWorktree:
~/Code/PyAutoLabs-wt/dpie-lenstool-param/Implementation Steps
piemd/ci05/ scaling routines to pin down: (a) exact b0 normalization fromv_disp(expected: E0 = 6π (σ_LT/c)² D_LS/D_S with σ_LT = Elíasdóttir's σ_dPIE; b0 = E0·(rs²−ra²)/rs² per the existing docstring chain, equivalently b0 = 4π (σ_0/c)²·D_LS/D_S·(rs²−ra²)/rs² with σ_0 = √(3/2)·σ_LT); (b) whether.parellipticiteis (a²−b²)/(a²+b²) and where LensTool converts it to the KK93 ε = (a−b)/(a+b) thatci05consumes (the PyAutoGalaxy port's_ellip()uses |ell_comps| = (1−q)/(1+q) directly aseps— dual_pseudo_isothermal_mass.py:458); (c) position-angle zero-point and rotation direction vs PyAuto's phi (counter-clockwise from positive x? vs from north); (d)core_radius/cut_radiusarcsec vs_kpcvariants. Record findings in the docstring and on this issue.dPIEMass.from_lenstool(centre, ellipticity, angle, sigma, r_core, r_cut, redshift_object, redshift_source, cosmology=ag.cosmo.Planck15())classmethod: ellipticity → axis-ratio q = √((1−e)/(1+e)) → ell_comps via the existingconverthelpers; σ_LT → b0 via the verified prefactor and D_LS/D_S from the cosmology object; kpc-variant support viakwargsor a documented pre-conversion.dPIEMassSph.from_lenstool(...)without ellipticity/angle. Docstring must loudly document the √(3/2) fiducial-vs-central σ trap and cite Elíasdóttir et al. 2007 App. A / Bergamini et al. 2019.test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_mass.py: conversion round-trips, limiting cases (ra→0, rs→∞, e=0 recovers isothermal θ_E = 4π σ_0²/c² D_LS/D_S), M(R) analytic anchor, ellipticity/angle mapping vs a directly-constructed profile. Numpy-only (no JAX in unit tests).autolens_workspace_test/scripts/cluster/lenstool_parity.py: convergence/potential/deflection grids from afrom_lenstool-constructed profile vs an independent implementation (e.g. lenstronomy's dPIE/PJaffe) and/or published LensTool model values; report max fractional deviations; assert tolerances.Key Files
PyAutoGalaxy/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_mass.py— dPIEMass/dPIEMassSph,_ellip(), existing docstring conversion chainPyAutoGalaxy/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_potential.py— potential-parameterized sibling (out of scope unless conventions force it in)PyAutoGalaxy/test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_mass.py— unit testsautolens_workspace_test/scripts/cluster/lenstool_parity.py— new parity scriptOpen questions (batched — answer at leisure; run proceeds on the recommended default)
from_lenstoolclassmethod (recommended, no new model-space class) suffices for constructing instances, but users wanting to fit directly in LensTool parameters (priors on σ_LT/r_cut) would need adPIEMassLensToolprofile class whose init takes LensTool params. Default: classmethod now; wrapper class deferred until the flagship example (task 8) demonstrates the need.Original Prompt
Click to expand starting prompt
$(cat PyAutoMind/feature/cluster/6_dpie_lenstool_parameterization.md)