# Standalone MW/M31 profile explorer

This directory is a reproducible, read-only consumer of the frozen v19 M31
CGMsum products in `../paper_apj_v19_cgmsum_draft/`. It does not modify the
paper tree or the cognition OS.

## Generate all products

Run with the same Python 3/CIAO-Sherpa environment used by the authoritative
conditional-prior audit:

```bash
cd /u/Lang3/program/M31CGM/mw_m31_profile_explorer
unset PYTHONHOME HEADAS
python3 generate_explorer_products.py
```

An alternate build root can be supplied with `--output-dir PATH`; the script
creates `products/` and `figures/` beneath it.
Generation is fail-closed: the script recomputes all 14 actual-HI4PI-NH
predictions through
`audit_apj_v19_cgmsum_conditional_priors.py` and stops if they differ from the
frozen footprint CSV.

## Verify

```bash
cd /u/Lang3/program/M31CGM/mw_m31_profile_explorer
unset PYTHONHOME HEADAS
python3 -m pytest -q
ruff check generate_explorer_products.py test_generate_explorer_products.py
```

## Build and inspect the standalone site

```bash
cd /u/Lang3/program/M31CGM/mw_m31_profile_explorer
python build_explorer_site.py
python -m http.server 8876 --bind 127.0.0.1 \
  --directory cloudflare_mw_m31_profile_explorer/site
```

The builder renders into `.site-staging`, validates local links, PNG
dimensions, scientific wording gates, and SHA-256 entries, then atomically
promotes the validated tree to `cloudflare_mw_m31_profile_explorer/site/`.
Failed builds preserve the previous site and unrelated parent files.

After deployment, verify every manifest-listed production object:

```bash
python verify_production.py
```

## Scientific conventions

- Locatelli et al. (2024) reference Combined beta=0.5 model:
  `beta=0.5`, `c=0.046`, `n0=0.032 cm^-3`, `Rd=6.2 kpc`, `zd=1.1 kpc`,
  `kT=0.15 keV`, `Z=0.1 Z_sun`.
- The line-of-sight emission measure is
  `integral(n_h^2 + n_d^2) ds`; halo and disk are independent additive
  emission components. No `2 n_h n_d` cross term is included.
- Continuous profiles use fixed `NH=0.056 x 10^22 cm^-2`. Sherpa evaluates
  the absorbed flux-per-EM factor once, then geometry reuses that factor.
- The response-matched branch applies the frozen O VIII closure
  `0.9498760469603238`; the direct-EM branch is retained separately.
- Positive signed M31-axis radius follows the exact great circle from M31
  toward the Galactic center. Sky points use `D=780 kpc` and
  `R=D tan(theta)`.
- Field positions use the exact TAN/gnomonic projection about M31, rotated to
  that same axis; both signed and perpendicular coordinates are retained.
- Observed radial-profile points use circular `rproj_kpc`; horizontal bars show
  the nominal radial reach of a 15 arcmin aperture at 780 kpc
  (`3.403413640147222 kpc`). They are geometric extents, not statistical errors
  or exact camera-mask-weighted radii.
- Zhang et al. (2024) is a sampled 10--30 kpc beta profile normalized to
  `S(20 kpc)=0.9418332370377434`, with `rc=7.39 kpc`, `beta=0.37`.
- Grayson et al. (2025) values remain five discrete 10--30 kpc bin bands;
  they are never interpolated into radial curves.
- Plots use Matplotlib Agg and SciencePlots `['science', 'no-latex']`. Every
  figure is emitted at 1-column (3.35 in) and 2-column (7 in) size as PDF and
  400-dpi PNG. No interactive `plt.show()` is used.

## Products

Data (`products/`):

- `mw_latitude_profile.csv` -- fixed M31 longitude, `b=-|b|`, 5--90 deg.
- `mw_m31_gc_signed_axis_profile.csv` -- exact great-circle -50--50 kpc profile.
- `m31_field_profile_decomposition.csv` -- 14 public CGMsum fields, untouched
  observed columns/aliases, exact projected coordinates, frozen actual-NH
  reference predictions, and fixed-NH geometry-only predictions.
- `conditional_m31_templates.csv` -- Zhang curve plus bin-only Grayson bands.
- `explorer_summary.json` -- formulas, constants, hashes, closure, profile
  diagnostics, and all field estimators.

Figures in `figures/` (each has `_1col`/`_2col` and `.png`/`.pdf` variants):

- `mw_latitude_profile_*`
- `mw_signed_axis_profile_*`
- `cgmsum_m31_radius_profile_*`
- `figure3_field_conversion_*`
