---
title: "From O VIII Line Map to M31 Broadband Transfer — Locatelli+2024 Reference Combined β=0.5 Geometry"
subtitle: "面向物理系本科一年级 · 可执行教程"
author: "M31 CGM Team"
date: "2026-07-18"
format:
html:
toc: true
toc-depth: 3
code-fold: true
code-tools: true
fig-cap-location: bottom
fig-width: 7
fig-height: 4
fig-align: center
embed-resources: true
execute:
echo: true
eval: true
warning: false
message: false
jupyter: python3
---
# From O VIII Line Map to M31 Broadband Transfer — Locatelli+2024 Reference Combined β=0.5 Geometry
> **教程目标**:解释 Locatelli+2024 的 O VIII line map → reference Combined β=0.5 geometry → M31 十四场 broadband transfer。这是项目中最复杂的转换链。
> **Tutorial goal**: Explain the O VIII line map → reference Combined β=0.5 geometry → 14-field M31 broadband transfer chain from Locatelli+2024 — the most complex conversion chain in the project.
> **目标读者**:物理系本科一年级,已学完普通物理(电磁学/光学),了解基本的原子物理概念(能级、跃迁),但不需要天文观测经验。
> **Target audience**: First-year physics undergraduates who have completed general physics (electromagnetism/optics) and basic atomic physics, without requiring astronomical observing experience.
> **核心问题**:Locatelli+2024 用 eRASS1 西半球 O VIII 线图拟合了银河系晕的几何模型。我们如何把这个几何模型外推到 M31 方向,并从 O VIII 谱线强度转换到 Figure 3 所需的 0.5–2.0 keV 宽波段吸收后表面亮度?
---
## 0. Setup: Environment and Data
本教程只需要 Python 标准科学栈。所有数据文件已随教程提供。
```{python}
#| label: setup
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path
plt.style.use('seaborn-v0_8-whitegrid')
plt.rcParams['font.size'] = 12
plt.rcParams['figure.dpi'] = 150
plt.rcParams['savefig.dpi'] = 300
# NOTE: do NOT add a CJK font fallback here — see SKILL.md
# All matplotlib text labels stay English-only.
DATA = Path("assets/data")
print("Environment ready!")
```
---
## 2. Background: What Did Locatelli+2024 Measure?
### 2.1 The eRASS1 O VIII Line Map
Locatelli et al. (2024, A&A 681, A78) used the **eROSITA All-Sky Survey** first data release (eRASS1) to construct an **O VIII line intensity map** in the 0.614–0.694 keV band (80 eV window). Their analysis covered the **western Galactic half** ($180^\circ < l < 360^\circ$).
> **Critical geographic fact**: M31 is at $l \approx 121^\circ$ — firmly in the **eastern** half, **outside** the Locatelli fitting domain. Any M31-direction prediction is a **geometry extrapolation**.
### 2.2 The Reference Combined β=0.5 Model
They fit the O VIII map with a two-component density model:
**Spherical halo**: $n_h(r) = C \cdot r^{-3\beta}$, with $\beta = 0.5$, $C = 0.046$
**Exponential disk**: $n_d(R, z) = n_0 \exp(-R/R_h) \exp(-|z|/z_h)$, with $n_0 = 0.032$ cm$^{-3}$, $R_h = 6.2$ kpc, $z_h = 1.1$ kpc
**Plasma parameters**: $kT = 0.15$ keV, $Z = 0.1\ Z_\odot$
**Emission implementation**: $\epsilon \propto n_h^2 + n_d^2$ — **no cross term** (as specified by the authors for this reference model).
### 2.3 The Five-Step Conversion Chain
```{mermaid}
graph TD
A["eRASS1 O VIII line map<br/>Western half 180° < l < 360°"] --> B["Fit geometry:<br/>Spherical halo + Exponential disk<br/>n_h² + n_d², β=0.5"]
B --> C["Step 1: LOS integration<br/>Sun → 14 M31 fields<br/>EM + intrinsic O VIII"]
C --> D["Step 2: 80 eV Gaussian<br/>match eROSITA response<br/>emissivity closure"]
D --> E["Step 3: Line→Broadband<br/>Fix O VIII norm<br/>Z=0.1 → Z=0.3 APEC"]
E --> F["Step 4: HI4PI absorption<br/>Full-screen phabs<br/>per-field N_H"]
F --> G["Step 5: CGMsum weights<br/>Inverse-variance<br/>weighted average"]
G --> H["All-field = 1.290382<br/>Footprint 1.158–1.341<br/>Tension = −0.326"]
```
---
## 3. Load the Data: 14 M31 XMM Fields
```{python}
#| label: load-data
df = pd.read_csv(DATA / "m31_cgmsum_locatelli2024_reference_beta0p5_m31_footprint_predictions.csv")
print(f"Loaded {len(df)} fields (rows)")
print(f"Columns ({len(df.columns)}):")
for i, c in enumerate(df.columns):
print(f" [{i:2d}] {c}")
df.head(5)
```
### Column Reference
| Column | Meaning |
|--------|---------|
| `obsid` | XMM-Newton observation ID |
| `side` | M31 side: North/NW or South/SE |
| `ra_deg`, `dec_deg` | Pointing coordinates (J2000) |
| `nh_hi4pi_1e22_cm-2` | HI4PI full-sky $N_H$ ($10^{22}$ cm$^{-2}$) |
| `galactic_l_deg`, `galactic_b_deg` | Galactic coordinates |
| `reference_emission_measure_n2_kpc_cm-6` | Emission measure $\int n^2 ds$ (kpc cm$^{-6}$) |
| `reference_intrinsic_o8_0p614_0p694_intensity_lu` | Intrinsic O VIII intensity (Line Units) |
| `direct_em_target_apec_absorbed_0p4_1p25_primary_fluxunit` | Direct EM → APEC (Z=0.3), absorbed, 0.4–1.25 keV |
| `o8_response_matched_emissivity_closure_scale` | Response-matched emissivity closure scale factor |
| `reference_response_matched_absorbed_0p4_1p25_primary_fluxunit` | Response-matched, absorbed, 0.4–1.25 keV |
| `line_normalized_z0p3_absorbed_0p4_1p25_primary_fluxunit` | Line-normalized Z=0.3, absorbed, 0.4–1.25 keV |
| `direct_em_target_apec_absorbed_0p5_2p0_figure_fluxunit` | Direct EM, Z=0.3, absorbed, 0.5–2.0 keV |
| `reference_response_matched_absorbed_0p5_2p0_figure_fluxunit` | Response-matched, Z=0.1, absorbed, 0.5–2.0 keV |
| `line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit` | **Final**: line-normalized Z=0.3, absorbed, 0.5–2.0 keV |
---
## 4. Step 1: From 3D Density to Emission Measure & O VIII Intensity
### 4.1 Physics: Line-of-Sight Integration
From the solar position ($R_\odot = 8.2$ kpc), we integrate along each of the 14 M31 field lines of sight:
$$EM_i = \int_0^{s_\text{max}} [n_h^2(s) + n_d^2(s)] \, ds$$
The intrinsic O VIII line intensity follows from the APEC emissivity:
$$I_{\text{O VIII},i} = EM_i \times \Lambda_{\text{O VIII}}(kT=0.15\ \text{keV}, Z=0.1)$$
where $\Lambda_{\text{O VIII}}$ is the APEC emission coefficient for the O VIII line complex, and L.U. = Line Units = photons s$^{-1}$ cm$^{-2}$ sr$^{-1}$.
### 4.2 Visualize: EM and O VIII Across 14 Fields
```{python}
#| label: step1-plot
#| fig-cap: "Emission measure and intrinsic O VIII intensity across 14 M31 fields. Color by North/South side."
#| fig-width: 10
#| fig-height: 4.5
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5), constrained_layout=True)
colors = {'North/NW': '#2196F3', 'South/SE': '#F44336'}
for side in df['side'].unique():
s = df[df['side'] == side]
ax1.scatter(s['galactic_b_deg'], s['reference_emission_measure_n2_kpc_cm-6'],
label=side, color=colors[side], s=80, alpha=0.8, edgecolor='k')
ax2.scatter(s['galactic_b_deg'], s['reference_intrinsic_o8_0p614_0p694_intensity_lu'],
label=side, color=colors[side], s=80, alpha=0.8, edgecolor='k')
ax1.set_xlabel('Galactic latitude b (deg)')
ax1.set_ylabel('EM (kpc cm⁻⁶)')
ax1.set_title('Emission Measure vs Galactic Latitude')
ax1.legend()
ax2.set_xlabel('Galactic latitude b (deg)')
ax2.set_ylabel('Intrinsic O VIII (L.U.)')
ax2.set_title('Intrinsic O VIII Intensity vs Galactic Latitude')
ax2.legend()
plt.suptitle('Step 1: LOS Integration — Solar Position → M31 Fields', fontsize=13, fontweight='bold')
plt.show()
em = df['reference_emission_measure_n2_kpc_cm-6']
o8 = df['reference_intrinsic_o8_0p614_0p694_intensity_lu']
print(f"EM range: {em.min():.4e} – {em.max():.4e} kpc cm⁻⁶")
print(f"O VIII range: {o8.min():.2f} – {o8.max():.2f} L.U.")
print(f"O VIII / EM ratio: {o8.iloc[0]/em.iloc[0]:.2f} L.U. / (kpc cm⁻⁶) (constant — same kT, Z)")
```
**Observation**: The EM and O VIII both decrease with more negative Galactic latitude (further from the MW plane). The ratio O VIII/EM is constant across all fields because the plasma parameters ($k$T, $Z$) are identical for all sightlines.
---
## 5. Step 2: 80 eV FWHM Gaussian — Matching the eROSITA Response
### 5.1 Why Match the Response?
Locatelli+2024 measured O VIII in an **80 eV window** centered at 0.654 keV to match eROSITA's instrumental energy resolution. Our APEC calculation produces a detailed emission spectrum; we must **convolve it with an 80 eV FWHM Gaussian** to reproduce the same line-band treatment as the original map.
This yields a **single emissivity closure scale factor**:
```{python}
#| label: step2-closure
scale = df['o8_response_matched_emissivity_closure_scale'].unique()
print(f"Emissivity closure scale factor: {scale[0]:.6f}")
print(f"Same for all fields: {len(scale) == 1}")
# Apply the scale: intrinsic → response-matched O VIII
df['o8_response_matched'] = df['reference_intrinsic_o8_0p614_0p694_intensity_lu'] * scale[0]
print(f"Response-matched O VIII: {df['o8_response_matched'].min():.2f} – {df['o8_response_matched'].max():.2f} L.U.")
```
The factor ≈ 0.9499 means the 80 eV window captures ~95% of the O VIII line flux — the remaining ~5% falls outside the window due to the Gaussian wings.
---
## 6. Step 3: Line-to-Broadband Bridge — O VIII (Z=0.1) → 0.5-2.0 keV (Z=0.3)
### 6.1 The Core Challenge
- **Input**: O VIII line intensity (0.614–0.694 keV, $Z=0.1$)
- **Output**: 0.5–2.0 keV broadband surface brightness ($Z=0.3$)
This step involves **two simultaneous transformations**:
1. **Spectrum expansion**: O VIII is one line in a rich plasma spectrum. The same plasma also emits continuum (bremsstrahlung) and other lines. The ratio of total broadband flux to O VIII line flux depends on $k_T$ and $Z$.
2. **Abundance change**: The original model uses $Z=0.1$ (matching Locatelli's fit), but our Figure 3 standard is $Z=0.3$ (more typical for MW CGM). Higher metallicity → more line emission → larger broadband flux.
### 6.2 How We Do It
We **fix the O VIII normalization** (the quantity actually measured by Locatelli) and compute:
$$F_{0.5-2.0}(Z=0.3) = I_{\text{O VIII}}(Z=0.1) \times \frac{\int_{0.5}^{2.0} \Lambda(E; kT, Z=0.3)\, dE}{\int_{\text{O VIII}} \Lambda(E; kT, Z=0.1)\, dE}$$
The denominator uses $Z=0.1$ (original model), the numerator uses $Z=0.3$ (target). Both are computed with APEC CIE.
### 6.3 Visualize the Transformation
```{python}
#| label: step3-plot
#| fig-cap: "Line-to-broadband transformation: O VIII (Z=0.1) → 0.5–2.0 keV (Z=0.3). The abundance change amplifies the broadband flux."
#| fig-width: 10
#| fig-height: 4.5
col_z01 = 'reference_response_matched_absorbed_0p5_2p0_figure_fluxunit'
col_z03 = 'line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit'
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5), constrained_layout=True)
# Left: Z=0.1 vs Z=0.3
for side in df['side'].unique():
s = df[df['side'] == side]
ax1.scatter(s[col_z01], s[col_z03], label=side, color=colors[side],
s=80, alpha=0.8, edgecolor='k')
mx = max(df[col_z01].max(), df[col_z03].max())
mn = min(df[col_z01].min(), df[col_z03].min())
ax1.plot([mn, mx], [mn, mx], 'k--', alpha=0.5, label='1:1 (no change)')
ax1.set_xlabel('Z=0.1, response-matched, absorbed')
ax1.set_ylabel('Z=0.3, line-normalized, absorbed')
ax1.set_title('Effect of Abundance Change (Z=0.1 → Z=0.3)')
ax1.legend()
# Right: Ratio distribution
ratio_z = df[col_z03] / df[col_z01]
ax2.hist(ratio_z, bins=10, edgecolor='k', alpha=0.7, color='steelblue')
ax2.axvline(ratio_z.mean(), color='red', linestyle='--',
label=f'Mean = {ratio_z.mean():.4f}')
ax2.set_xlabel('Broadcast flux ratio (Z=0.3 / Z=0.1)')
ax2.set_ylabel('Number of fields')
ax2.set_title('Distribution of Line-to-Broadband Conversion Factors')
ax2.legend()
plt.suptitle('Step 3: Line-to-Broadband Bridge', fontsize=13, fontweight='bold')
plt.show()
print(f"Z=0.1 absorbed range: {df[col_z01].min():.4f} – {df[col_z01].max():.4f}")
print(f"Z=0.3 absorbed range: {df[col_z03].min():.4f} – {df[col_z03].max():.4f}")
print(f"Mean conversion factor: {ratio_z.mean():.4f}")
```
**Observation**: The $Z=0.1 \to Z=0.3$ change amplifies the broadband flux by a nearly constant factor (~1.27×). This is because the same geometry model applies to all fields, and the line-to-broadband ratio depends only on $k_T$ and $Z$, which are the same for all sightlines.
---
## 7. Step 4: HI4PI Full-Screen Absorption
### 7.1 Photoelectric Absorption by Galactic HI
X-ray photons traveling through the Milky Way's neutral hydrogen disk are absorbed via photoelectric ionization. The transmission fraction is:
$$T(E) = \exp[-\sigma(E) \cdot N_H]$$
where $\sigma(E)$ is the energy-dependent photoelectric cross-section and $N_H$ is the neutral hydrogen column density.
We use **HI4PI** full-sky $N_H$ measurements for each field. The absorption is applied as **full-screen phabs** — all model emission passes through the full HI column.
### 7.2 Absorption vs Column Density
```{python}
#| label: step4-plot
#| fig-cap: "HI4PI absorption effect: transmission decreases with increasing column density."
#| fig-width: 10
#| fig-height: 4.5
# Compare: direct EM target (no absorption) vs line-normalized Z=0.3 (with absorption)
col_direct = 'direct_em_target_apec_absorbed_0p5_2p0_figure_fluxunit'
col_final = 'line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit'
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5), constrained_layout=True)
# Left: direct vs final
for side in df['side'].unique():
s = df[df['side'] == side]
ax1.scatter(s[col_direct], s[col_final], label=side, color=colors[side],
s=80, alpha=0.8, edgecolor='k')
mx2 = max(df[col_direct].max(), df[col_final].max())
mn2 = min(df[col_direct].min(), df[col_final].min())
ax1.plot([mn2, mx2], [mn2, mx2], 'k--', alpha=0.5)
ax1.set_xlabel('Direct EM target (Z=0.3, absorbed)')
ax1.set_ylabel('Line-normalized (Z=0.3, absorbed)')
ax1.set_title('Direct EM vs Line-Normalized')
ax1.legend()
# Right: Transmission vs N_H
transmission = df[col_final] / df[col_direct]
sc = ax2.scatter(df['nh_hi4pi_1e22_cm-2'], transmission,
c=df['galactic_b_deg'], cmap='RdBu_r', s=80, edgecolor='k')
ax2.set_xlabel('N_H (10²² cm⁻²)')
ax2.set_ylabel('Transmission')
ax2.set_title('Absorption Transmission vs N_H')
cbar = fig.colorbar(sc, ax=ax2)
cbar.set_label('Galactic b (deg)')
plt.suptitle('Step 4: HI4PI Full-Screen Absorption', fontsize=13, fontweight='bold')
plt.show()
print(f"Transmission range: {transmission.min():.4f} – {transmission.max():.4f}")
print(f"Mean transmission: {transmission.mean():.4f}")
print(f"N_H range: {df['nh_hi4pi_1e22_cm-2'].min():.4f} – {df['nh_hi4pi_1e22_cm-2'].max():.4f} (×10²² cm⁻²)")
```
---
## 8. Step 5: CGMsum Inverse-Variance Weighted Average
### 8.1 Why Weighted?
The 14 fields have different exposures, backgrounds, and signal-to-noise ratios. To compare model predictions with the observed CGMsum total, we use the **same inverse-variance weights** as the observed measurement. This ensures the model and observation are in the same statistical frame.
### 8.2 Final Numbers
```{python}
#| label: step5-final
final_col = 'line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit'
finals = df[final_col].to_numpy()
# All-field weighted mean (registered value)
ALL_FIELD = 1.290382
print(f"All-field weighted mean (registered): {ALL_FIELD:.6f}")
print(f"Simple mean (for reference): {finals.mean():.6f}")
print(f"Field range: [{finals.min():.6f}, {finals.max():.6f}]")
print(f"Observed CGMsum total: 0.964727")
print(f"Tension (model − observed): {ALL_FIELD - 0.964727:.6f}")
print(f" → negative tension: model OVER-predicts MW foreground")
```
---
## 9. Final Assembly: The Complete Chain in One Picture
```{python}
#| label: final-plot
#| fig-cap: "Complete conversion chain: from intrinsic O VIII to Figure 3 broadband. The model over-predicts the observed total."
#| fig-width: 10
#| fig-height: 6
fig, axes = plt.subplots(2, 2, figsize=(12, 10), constrained_layout=True)
# Panel 1: O VIII intrinsic distribution
axes[0, 0].bar(range(len(df)), df['reference_intrinsic_o8_0p614_0p694_intensity_lu'],
color='steelblue', alpha=0.7, edgecolor='white')
axes[0, 0].set_xlabel('Field index')
axes[0, 0].set_ylabel('Intrinsic O VIII (L.U.)')
axes[0, 0].set_title('Step 1: Intrinsic O VIII (4.14–4.56 L.U.)')
# Panel 2: The three broadband columns side by side
x = np.arange(len(df))
w = 0.25
axes[0, 1].bar(x - w, df['reference_response_matched_absorbed_0p5_2p0_figure_fluxunit'],
w, label='Z=0.1 (matched)', color='#90CAF9', edgecolor='white')
axes[0, 1].bar(x, df['line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit'],
w, label='Z=0.3 (line-norm)', color='#42A5F5', edgecolor='white')
axes[0, 1].bar(x + w, df['direct_em_target_apec_absorbed_0p5_2p0_figure_fluxunit'],
w, label='Z=0.3 (direct EM)', color='#1565C0', edgecolor='white')
axes[0, 1].set_xlabel('Field index')
axes[0, 1].set_ylabel('Broadband flux (Figure 3 units)')
axes[0, 1].set_title('Steps 2–4: Broadband Conversion Variations')
axes[0, 1].legend(fontsize=8)
# Row 2: Final values vs N_H and galactic coordinates
axes[1, 0].scatter(df['nh_hi4pi_1e22_cm-2'], df[final_col],
c=df['galactic_b_deg'], cmap='RdBu_r', s=80, edgecolor='k')
axes[1, 0].set_xlabel('$N_H$ (10²² cm⁻²)')
axes[1, 0].set_ylabel('Final broadband flux')
axes[1, 0].set_title('Step 4: Final Flux vs N_H')
# Row 2: Final vs galactic coordinates
sc2 = axes[1, 1].scatter(df['galactic_l_deg'], df['galactic_b_deg'],
c=df[final_col], cmap='viridis', s=150, edgecolor='k')
axes[1, 1].set_xlabel('Galactic l (deg)')
axes[1, 1].set_ylabel('Galactic b (deg)')
axes[1, 1].set_title('14 M31 Fields: Final Flux on Sky')
cbar2 = fig.colorbar(sc2, ax=axes[1, 1])
cbar2.set_label('Final flux')
plt.suptitle('Locatelli+2024: Complete Conversion Chain Summary', fontsize=14, fontweight='bold')
plt.show()
print("="*60)
print(f"ALL-FIELD WEIGHTED AVERAGE: {ALL_FIELD:.6f}")
print(f"FIELD RANGE: [{finals.min():.6f}, {finals.max():.6f}]")
print(f"OBSERVED CGMsum: 0.964727")
print(f"TENSION: {ALL_FIELD - 0.964727:+.6f}")
print("="*60)
```
---
## 10. Key Assumption Checklist
| Step | Assumption | Origin |
|------|-----------|--------|
| Geometry | Combined β=0.5 model extrapolated from western half ($l>180^\circ$) to M31 ($l\approx121^\circ$) | **Project augmentation** — original paper has no M31 prediction |
| Emission | $n_h^2 + n_d^2$, no cross term | Original paper (reference model) |
| Plasma | $k_T=0.15$ keV, $Z=0.1$ (original) → $Z=0.3$ (target) | Abundance change is a **project augmentation** |
| Response | 80 eV FWHM Gaussian matching eROSITA | **Project augmentation** — original paper uses different treatment |
| Line→Broadband | APEC CIE model, O VIII normalization fixed | **Project augmentation** — original paper only reports O VIII |
| Absorption | HI4PI full-screen phabs | **Project augmentation** — more conservative than original |
| Weighting | CGMsum inverse-variance weights | **Project augmentation** — ensures statistical comparability |
| Geometry | Solar position at $R_\odot=8.2$ kpc | Standard assumption |
> **Safety**: The all-field value 1.290382 is a **named reference prediction**, not a measurement. The footprint spread 1.158–1.341 represents **deterministic spatial variation** (diagonal sensitivity), not a posterior credible interval. Changing any assumption (geometry, abundance, absorption position, line normalization) changes the tension magnitude.
---
## 11. Exercises
### Exercise 1: Verify the Conversion Chain
Pick one field and trace the conversion from intrinsic O VIII to final broadband:
```{python}
#| label: ex1
#| echo: false
row = df.iloc[0]
print(f"Field: {row['obsid']} ({row['side']})")
print(f" (1) EM = {row['reference_emission_measure_n2_kpc_cm-6']:.4e} kpc cm⁻⁶")
print(f" (2) Intrinsic O VIII = {row['reference_intrinsic_o8_0p614_0p694_intensity_lu']:.2f} L.U.")
print(f" (3) × closure scale ({row['o8_response_matched_emissivity_closure_scale']:.4f})")
print(f" → response-matched = {row['o8_response_matched_emissivity_closure_scale'] * row['reference_intrinsic_o8_0p614_0p694_intensity_lu']:.2f} L.U.")
print(f" (4) Line→Broadband Z=0.3: {row['line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit']:.6f}")
print(f" (5) Final broadband (absorbed) = {row['line_normalized_z0p3_absorbed_0p5_2p0_figure_fluxunit']:.6f}")
```
### Exercise 2: Qualitative Sensitivity
How would the all-field prediction change if:
1. The disk scale height $z_h$ were increased to 2.0 kpc?
2. The metallicity were switched back to $Z=0.1$?
3. The absorption were changed from full-screen to partial-screen (50% of gas in front of absorber)?
```python
#| label: ex2
#| echo: false
print("Qualitative predictions:")
print("1. Larger z_h → more disk gas in M31 LOS → EM ↑ → prediction ↑ → tension larger")
print("2. Z=0.1 → less line emission in broadband → prediction ↓ → tension smaller")
print("3. Partial screen → less absorption → prediction ↑ → tension larger")
```
---
## 12. Summary: The Full Chain
```{mermaid}
graph TD
A["eRASS1 O VIII map<br/>Western half 180° < l < 360°"] --> B["Fit: Spherical + Disk<br/>β=0.5, kT=0.15 keV, Z=0.1"]
B --> C["Step 1: LOS integration<br/>Sun → 14 M31 fields<br/>EM + intrinsic O VIII<br/>4.14–4.56 L.U."]
C --> D["Step 2: 80 eV Gaussian<br/>eROSITA response match<br/>Closure scale = 0.9499"]
D --> E["Step 3: Line→Broadband<br/>Fix O VIII norm<br/>Z=0.1 → Z=0.3 APEC"]
E --> F["Step 4: HI4PI phabs<br/>Full-screen absorption<br/>Per-field N_H"]
F --> G["Step 5: CGMsum weights<br/>Inverse-variance<br/>weighted average"]
G --> H["All-field = 1.290382<br/>Footprint 1.158–1.341<br/>Tension = −−0.326"]
```
**One-sentence summary**: Locatelli+2024's O VIII line map, fitted with a Combined β=0.5 spherical halo + exponential disk model, yields an all-field broadband prediction of 1.290382 when extrapolated to M31 and converted through all project-standard augmentations (line-to-broadband, Z=0.3, full-screen absorption, CGMsum weights) — exceeding the observed total of 0.965 and producing a +0.326 tension that serves as a foreground upper bound.
---
## References
1. **Locatelli et al. 2024**, *The warm-hot circumgalactic medium of the Milky Way as seen by eROSITA*, A&A 681, A78
2. **Miller & Bregman 2013**, *The Milky Way's Hot Gas Halo*, ApJ 770, 118 — upstream source of the path-length cross-term formula
3. **HI4PI Collaboration 2016**, *HI4PI: A full-sky H I survey*, A&A 594, A116
4. **M31 CGM Project** (this work): `paper_apj_v19_cgmsum_draft/`
---
> **教程结束** 🎓
> Next: Read the Ponti+2023 eFEDS tutorial for fixed SWCX scenario band reconstruction.