Skip to content

feat: LensTool-native dPIE parameterization (from_lenstool) + parity validation #485

Description

@Jammy2211

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

  1. 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.
  2. 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.
  3. 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).
  4. 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.
  5. 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)

  1. 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.
  2. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions