Learned mass-conserving deprojection of galaxy images -> 3D stellar density, rotation curve, potential.
Project description
Disk Galaxy Deprojection
This repository builds a baseline-plus-residual probabilistic benchmark for deprojecting barred-galaxy stellar mass structure from S4G-like images.
For project background, current Milestone 2b status, and continuation guidance,
start with docs/reports/2026-06-10-project-handoff.md.
Deprojecting a galaxy image — the dgdp package
dgdp turns a galaxy image into its deprojected 3-D stellar-mass distribution, rotation curve,
and (optionally) gravitational potential, using a bundled pretrained model — no training
data or GPU required.
Install
pip install git+https://github.com/lucyundead/disk-galaxy-deprojection
Core install is light (numpy, astropy, matplotlib) and ships the ~1 MB model. Retraining
the model additionally needs the TNG table and the [train] extra (pip install '.[train]').
Quickstart
Python:
from dgdp import deproject
r = deproject("galaxy.fits", distance_mpc=15.2, inclination_deg=30, pa_onsky_deg=153,
ml=1.0, stellar_mass=6e10, n_samples=48)
r.density_3d # (nR, nphi, nz) cylindrical stellar-mass cube [Msun]
r.v_circ([1, 2, 5, 10]) # rotation curve at those radii [km/s]
r.rms_z([1, 5, 10]) # vertical thickness RMS|z|(R) [kpc]
r.rms_z_samples([1, 5]) # posterior draws (n_samples, nR) -> uncertainty bands
r.v_circ_samples([1, 5]) # same for the rotation curve
r.reproj["history"] # reprojection-consistency residual (see below)
r.edge_on, r.face_on # 2-D renderings
r.save("out/") # density.npz + rotation_curve.csv + deprojection.png
CLI:
dgdp-deproject galaxy.fits --distance-mpc 15.2 --inclination-deg 30 --pa-onsky-deg 153 \
--ml 1.0 --stellar-mass 6e10 -o out/
Geometry & M/L
- Geometry: give
pix_arcsec+pa_pix_deg(+center) to bypass WCS (e.g. an S4G cutout), or let a FITS WCS supply the pixel scale and convert an on-skypa_onsky_deg. - The deprojected shape is M/L-independent; M/L only sets the v_c amplitude. Fix the absolute
scale with either (a)
stellar_mass(total M*, M/L already folded in), (b)luminosity×ml, or (c)zeropoint+band_solar_mag+ distance to calibrate the image to a luminosity. With none of these the outputs use a relative scale.
Potential (optional)
r.potential(R, z) and dgdp-deproject --potential return the AGAMA CylSpline potential Φ(R,z).
AGAMA is not pip-installable — build it from source
(https://github.com/GalacticDynamics-Oxford/Agama) and put it on PYTHONPATH; everything else
works without it.
Method
The in-plane surface density is anchored geometrically from the image; a learned fixed-dictionary
sech² vertical profile q_m(z;R) — trained on 185 TNG50 barred galaxies × 198 projections — supplies
the thickness, mass-conserving by construction. See
docs/reports/2026-06-30-mixture-qm-fixed-dictionary.md.
Two consistency layers on top:
- Reprojection loop (
reproject_iters, default 2): the thick reconstruction is projected back to the sky and the Σ anchors are corrected until the model actually reproduces the observed image (the plain geometric stretch assumes zero thickness). Early-stops with revert, so it never returns a worse-reprojecting model;r.reproj["history"]holds the obs-weighted mean |log(obs/model)| per pass andr.reproj["ratio"]the final residual map. - Posterior sampling (
n_samples): the bundled model is a mixture density network; sampling its head propagates the vertical-profile posterior to RMS|z|(R) and v_c(R) bands (rms_z_samples/v_circ_samples). Band coverage is calibrated on the held-out TNG50 val split (src/dgdp/models/dgdp_fixed_dict.calibration.json).
Milestone 1 predicts posterior residuals for physical summaries:
- radial stellar mass profile
- radial surface-density profile
- vertical scale-height summary
- bar thickness summary
- bar amplitude summary
- central concentration summary
Start with:
python -m pytest -q
python scripts/build_synthetic_benchmark.py --config configs/milestone1.synthetic.toml
python scripts/train_summary_residual_mdn.py --data outputs/milestone1_synthetic/residual_table.npz
python scripts/evaluate_summary_residual.py --run-dir outputs/milestone1_synthetic
Synthetic Milestone 1 Benchmark
Run the synthetic benchmark:
python scripts/build_synthetic_benchmark.py --config configs/milestone1.synthetic.toml
python scripts/train_summary_residual_mdn.py --data outputs/milestone1_synthetic/residual_table.npz --output-dir outputs/milestone1_synthetic
python scripts/evaluate_summary_residual.py --run-dir outputs/milestone1_synthetic
The benchmark writes:
outputs/milestone1_synthetic/manifest.csvoutputs/milestone1_synthetic/residual_table.npzoutputs/milestone1_synthetic/summary_residual_mdn.ptoutputs/milestone1_synthetic/normalization.npzoutputs/milestone1_synthetic/metrics.json
Milestone 2 Cluster TNG50 Ingestion
Milestone 2 runs TNG-facing work on the remote cluster through the existing HPC wrapper at /home/lucyundead/codex/hpc-agent/hpc.
python scripts/cluster_dgdp.py sync
python scripts/cluster_dgdp.py check-env
python scripts/cluster_dgdp.py reproduce-milestone1
python scripts/cluster_dgdp.py run-tng50
python scripts/cluster_dgdp.py run-tng50-density
python scripts/cluster_dgdp.py fetch-tng50
The cluster workflow keeps full TNG50 snapshots remote and fetches only compact
artifacts under outputs/tng50_milestone2/.
If local rsync is unavailable, the project CLI falls back to archive transfer
through the HPC wrapper and does not delete remote files.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file disk_galaxy_deprojection-0.3.0.tar.gz.
File metadata
- Download URL: disk_galaxy_deprojection-0.3.0.tar.gz
- Upload date:
- Size: 623.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c16af2924fc43964f048617d0489f61110aec2f7cf4b9807c64edc7b7c136c4
|
|
| MD5 |
1d1a769e5afa35519bee7a75783a2eb3
|
|
| BLAKE2b-256 |
cb7b12480bef9b054abb26fb86666b089803d03a39272d0654c195051835709b
|
Provenance
The following attestation bundles were made for disk_galaxy_deprojection-0.3.0.tar.gz:
Publisher:
release.yml on lucyundead/disk-galaxy-deprojection
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
disk_galaxy_deprojection-0.3.0.tar.gz -
Subject digest:
6c16af2924fc43964f048617d0489f61110aec2f7cf4b9807c64edc7b7c136c4 - Sigstore transparency entry: 2047779886
- Sigstore integration time:
-
Permalink:
lucyundead/disk-galaxy-deprojection@1722205defc993d591bebeb0567586a642f923e2 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/lucyundead
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1722205defc993d591bebeb0567586a642f923e2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file disk_galaxy_deprojection-0.3.0-py3-none-any.whl.
File metadata
- Download URL: disk_galaxy_deprojection-0.3.0-py3-none-any.whl
- Upload date:
- Size: 591.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ebff2198587f144908568d0e6c31e73624e8cb31a619ebab26a806b8903e277b
|
|
| MD5 |
1e596ec8e0ce5d109835a5dea8151d65
|
|
| BLAKE2b-256 |
8d9a1207f19faecb2d16b6e5f9b6bbcb04c65f8e335864041e0e5f02f7e9ad03
|
Provenance
The following attestation bundles were made for disk_galaxy_deprojection-0.3.0-py3-none-any.whl:
Publisher:
release.yml on lucyundead/disk-galaxy-deprojection
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
disk_galaxy_deprojection-0.3.0-py3-none-any.whl -
Subject digest:
ebff2198587f144908568d0e6c31e73624e8cb31a619ebab26a806b8903e277b - Sigstore transparency entry: 2047779896
- Sigstore integration time:
-
Permalink:
lucyundead/disk-galaxy-deprojection@1722205defc993d591bebeb0567586a642f923e2 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/lucyundead
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1722205defc993d591bebeb0567586a642f923e2 -
Trigger Event:
push
-
Statement type: