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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions