---
title: "从具名SPARC文件重新算质量模型"
description: "独立运行合成数据与NGC2403的保存、六起点拟合、质量光度比轮廓和共同距离倾角场景，保留真实数据的明显残差。"
canonical_url: "https://sandboxphysics.com/zh/stories/galaxy-rotation-curves/recomputing-a-named-sparc-galaxy/"
language: "zh-CN"
page_type: "story"
alternate_languages:
  en: "https://sandboxphysics.com/stories/galaxy-rotation-curves/recomputing-a-named-sparc-galaxy/"
last_modified: "2026-11-05"
---

[星系旋转曲线与质量分解](https://sandboxphysics.com/zh/stories/galaxy-rotation-curves/)

星系旋转曲线 · L3

# 从具名SPARC文件重新算质量模型

Sandbox Physics · 2026-11-05

先用自有合成数据跑通保存、拟合与作图，再在自己的电脑取得固定NGC2403文件，重算同样的分析。每个最优值都从保存的观测重新求出；公开材料不包含原始SPARC观测。

## 开始前需要什么

读完前三章，能在终端运行Python。复算使用NumPy与SciPy；不需要Lab代码、网页连接、原论文PDF或作者私有软件。

## 先解压独立科学包

下载本页的科学ZIP，核对SHA-256后解压到新目录，在该目录打开终端。README列出实际测试环境：Python3.9.6、NumPy2.0.2、SciPy1.13.1。使用其他兼容版本时，也应记录自己的版本；优化停止点的末几位可能变化。

包里有独立Python、自己的合成输入、单位与源文件身份说明。默认计算不访问网络。浏览器Lab的交互引擎、原论文、原始SPARC表与实测散点图都不在包内。下面先走一条完全离线的计算链。

对应阅读：Independent saved-observation rotation-curve calculation

## 先保存观测，再让拟合程序读取它

第一条命令从薄指数恒星盘、气体盘和规定的NFW引力生成48个半径的合成速度，再加入预先固定的随机噪声，写成JSON。生成程序在终端说明注入条件；保存给拟合程序的观测文件不含注入参数。

第二条命令重新打开这个文件，用其中的速度、误差与分量列拟合。它不会读取生成程序的真值。NFW条件最优质量光度比约为 $0.607$，与注入的 $0.55$ 不完全相同：加过噪声后，最优值也发生了移动。

程序对NFW和平核晕各用六个起点，并保存全部停止状态、函数调用次数与代价。合成平核例子的若干起点得到 $\chi^2\simeq167.54$，另一个得到更低的 $126.43$。这两个结果都满足停止条件，仍要逐一起点比较代价。

```
python science/generate_observation.py --output results/own-observation.json
python science/analyze.py --input results/own-observation.json --output results/own-analysis
```

对应阅读：Independent saved-observation rotation-curve calculation

## 从同一份结果重算检查与图

验证程序先重算球晕的正密度积分和薄盘的圆环势参照，再检查距离与倾角的数值导数，以及两个似然坐标的Jacobian关系。它还逐项重算已保存的预测，检查拟合结果指向同一份输入。

输出保留了错误算法对照：把指数盘当球、把接近的浮点数直接相减，以及把共同误差当独立误差。正确算法的原门槛与错误对照分别报告，不把一个失败项混进“全部都对”的标题。

作图程序读取保存的观测和分析结果，给出总速度、分项、误差棒及条件轮廓。示例图采用本页自己的合成数据。平核族在这份特意由NFW生成的数据上拟合较差，这是检查方法的一种对照，不能拿来判断真实星系。

![自有合成速度与误差，以及重新拟合的NFW分量分解](https://sandboxphysics.com/_astro/curve.DOUhBg3w.svg)

自有48点合成观测。独立Python从保存的速度、误差与分量基底重新拟合；这不是实测星系图像。等效分量速度代表引力，有符号速度平方相加得到总量。 [查看完整图像](https://sandboxphysics.com/_astro/curve.DOUhBg3w.svg)

```
python science/validate.py --input results/own-observation.json --analysis results/own-analysis --output results/own-validation.json
python science/plot.py --input results/own-observation.json --analysis results/own-analysis --output results/own-figures
```

对应阅读：Independent saved-observation rotation-curve calculation

## 只取得两个固定上游文件

若要进入真实对象分支，运行下面的显式联网命令。程序访问SPARC官方Table1和小型Rotmod压缩包，合计只接收138,996字节；固定大小、SHA-256与成员CRC全部通过，才取出NGC2403那一个成员。其余174个对象不解压、不用于分析。文件身份改变时停止，不自动重试或换镜像。

本轮作者在2026年11月5日实际从空目录取得这两份200响应；这次新取源与后面的离线冷复算分别留有收条。将来服务器是否仍提供相同版本，需要读者自己的请求结果说明。

Table1的固定文件身份是28,259字节；压缩包是110,737字节。完整哈希见科学包的source元数据与下载程序。公开材料保留可执行的取源步骤，原件留在读者电脑。

```
python science/fetch_named_source.py --online --http-budget-bytes 138996 --output local-sparc
```

对应阅读：Lelli, McGaugh & Schombert (2016), SPARC, AJ152,157 · Independent saved-observation rotation-curve calculation

## 把原生列转成可追溯的保存观测

所选对象是质量标记为1、无核球的NGC2403。Table1给出 $D_0=3.16\pm0.16\,\mathrm{Mpc}$、$i_0=63^{\circ}\pm3^{\circ}$；共 $73$ 个径向样点。保存命令先核对原文件哈希、具名行的19个字段和无核球条件，再写出这一组观测。

八列依次为半径、总观测圆轨速度、速度误差、气体等效速度、单位质量光度比恒星速度、核球速度、恒星盘面亮度与核球面亮度。半径单位为千秒差距，速度列为千米每秒。原生速度误差不包含倾角系统项，也不是一张完整的协方差矩阵。

气体列的负号标记径向力方向；恒星列保留原生有限厚度盘。保存记录不是原始二维速度场，因而这套程序没有重新测翘曲、非圆运动或光度去投影。

```
python science/save_native.py --source local-sparc --output results/native-observation.json
```

对应阅读：Lelli, McGaugh & Schombert (2016), SPARC, AJ152,157 · Independent saved-observation rotation-curve calculation

## 让真实数据经过同一套优化与敏感性分析

对保存的真实观测运行同一个analyze程序。优化范围为质量光度比0.05至1.5、晕密度每立方千秒差距1000至一百亿太阳质量、尺度半径0.05至200千秒差距。它比交互演示的参数窗更宽；范围本身仍是分析条件。

程序另外固定十个质量光度比，分别优化晕参数；还把全局距离和倾角各取中心及正负一倍元数据误差，形成九种共同几何场景。每种场景都重新拟合，不把九种情况当成后验样本，也不把元数据误差自动变成已知联合先验。

本次重新计算的NFW质量光度比约 $0.288$、尺度 $7.147\,\mathrm{kpc}$；平核结果约 $0.758$ 与 $3.029\,\mathrm{kpc}$。工作约化卡方分别为 $9.138$ 与 $10.696$。两者都明显偏差，原始误差与全部选中样点保持不变。

```
python science/analyze.py --input results/native-observation.json --output results/native-analysis
python science/validate.py --input results/native-observation.json --analysis results/native-analysis --output results/native-validation.json
```

对应阅读：Independent saved-observation rotation-curve calculation · Lelli, McGaugh & Schombert (2016), SPARC, AJ152,157

## 实测派生图由读者在本地生成

下面的显式选项确认你正在生成本地实测派生图。图与输出留在自己的电脑，用于查看哪些半径偏离、哪些参数抵到边界，以及残差是否连续同向。公开正文的示例图仍全部使用自有合成输入。

早期开发曾遇到三类失败：Table1固定字节切列与实际空白分隔不符；NumPy布尔量无法直接写成JSON；小半径平核相减及似然坐标审查没有通过原数值门。改过的解析或等价算法和旧失败各自留存；本包重新算出数学负例，不把早期退出成功当成全部物理范围已经验证。

```
python science/plot.py --allow-local-observations --input results/native-observation.json --analysis results/native-analysis --output results/local-measured-figures
```

对应阅读：Independent saved-observation rotation-curve calculation

## 读下一份数据时也能沿用的检查

先记文件身份与选择条件，再确认每列的单位和正负号。保存观测后才拟合，检查六起点与边界，再把共同几何变化和点误差分别处理。最后同时读残差和模型条件：一个较低的分数仍需解释它怎样处理了物理与误差。

本教程已经实际重算固定对象的条件模型；它没有给出暗物质粒子鉴别、校准后的概率区间或完整原始速度场分析。下一篇把这些习惯带到一份更新的多模型论文，比较作者换了哪些观测与假设。

对应阅读：Independent saved-observation rotation-curve calculation · Lelli, McGaugh & Schombert (2016), SPARC, AJ152,157

## 练习与解释性答案

1. 为什么默认流程要先写出观测JSON，再另开程序读它拟合？

   展开解释

   这样拟合的输入是实际保存的速度与误差，生成程序的注入参数不在输入里。也便于核对文件哈希、重复分析和比较不同优化起点。验证程序还放入一个无效的额外真值字段，确认拟合不会读取它。

2. 如果本地已有精确上游文件，怎样在零网络条件下验证取源和转换步骤？

   展开解释

   用下载程序的--from-cache参数指向已经核对过的本地原件目录，并选择一个新的输出目录。它仍核哈希和CRC，再运行保存与分析命令。这证明的是缓存复算；新下载要由真实网络收条另行说明。

3. 真实对象的两个工作约化卡方都很大，能否直接把误差放大到结果接近一？

   展开解释

   放大误差会改变问题，需要独立的误差模型和证据。当前结果应先保留，用残差、几何敏感性和物理模型缺项寻找原因。仅为了把分数变得好看而缩小偏离，会隐藏原来的失配。

## 动手检验预测

### [比较恒星盘与条件暗晕](https://sandboxphysics.com/zh/labs/galaxy-rotation-curves/)

1. 先用自有合成默认值，导出保存的观测与正演记录。
2. 比较导出里的单位、源身份与完整似然；它们应对应当前已接受的同一模型。
3. 若在本地导入固定两文件，区分手动正演与本教程离线六起点拟合，随后在自己的输出目录查看残差。

## 独立科学复算材料

[下载科学复算ZIP](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/galaxy-rotation-science-v1.zip) · [阅读安装与计算范围](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/README.md)

离线计算需要Python、NumPy与SciPy，默认合成数据会完整生成并重新拟合。可选的具名SPARC文件只在自己的电脑取得；ZIP不含原始观测或浏览器交互引擎。

单件脚本、字节与SHA-256

- [README.md](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/README.md) (6785 B)
  `5fbf0d29e006fc06f35d2290fd8fee7d5abc2301b601814165495331f9c37a84`
- [LICENSE.txt](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/LICENSE.txt) (1325 B)
  `4f15facc82fd0218a74907ae83f1716569277d9c40f55b249451dd8d89191b2f`
- [requirements.txt](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/requirements.txt) (185 B)
  `359f4c650ca6dd5a2705535e32377aa86db27d8d6ebd9b47333070850b9c5980`
- [source-identities.json](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/source-identities.json) (724 B)
  `409cf4931c19dc97049f943eb005165a59f88d1842a3dd2596eaa1dabcfbd458`
- [own-observation.json](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/own-observation.json) (9009 B)
  `48cd32150a8eff02b4c3e870cb82f8df008d90c2b5aed3f9bca743506852a5e1`
- [analyze.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/analyze.py) (2750 B)
  `5baed281117d3980af0f0e19e428c4163f79a97a84d1db71955ecbee7993ac3a`
- [conditional\_fit.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/conditional_fit.py) (3618 B)
  `37700d28edc852a10d09f949a7270a652b61f1c9d30cc2d8d0e99d4347e92d70`
- [fetch\_named\_source.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/fetch_named_source.py) (5014 B)
  `240b29a1c6e929895b5ead31269b495b1223be0615da62c703e2962028e6de5a`
- [generate\_observation.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/generate_observation.py) (849 B)
  `c5442424f9cecdcd81022ec31cae0b81bdc21400f06eea6dde4108c7062de743`
- [observation\_record.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/observation_record.py) (2693 B)
  `2d791013b5c8b289e6bfdc5ba9ed408e18b9fc4334d44ba32091546ed297e752`
- [physical\_units.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/physical_units.py) (255 B)
  `2db6448e5f4e657ef48ffd9d2a58c7487c6f7655a8033dc001327fe43192507c`
- [plot.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/plot.py) (5734 B)
  `c30e8e160f1f73f6451ebb903402a316ecba858814e6b1d5059ad31926abb4e1`
- [rotation\_model.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/rotation_model.py) (2003 B)
  `97c2c5ca56373f20996b26ef179267549ad3a341817b29666ce368029cfa96a1`
- [save\_native.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/save_native.py) (655 B)
  `bbf0881e28cf1dd4d13105af656cc4072e3a42a62a5f7013b9f48075b764c082`
- [spherical\_halos.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/spherical_halos.py) (1459 B)
  `4678e06bf172167f3a8a3a5d199447658eb51860507405b80110807165c5ef83`
- [synthetic\_record.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/synthetic_record.py) (1538 B)
  `6285cda07f2b9324806d25ecc89712eb6b94479e3caaa1f24a88c004d449a063`
- [thin\_disk\_reference.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/thin_disk_reference.py) (1298 B)
  `67ba0512e47c10d10c5b8d17f435d808812eec25f0edce296b6c80eaf72e0bc0`
- [validate.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/validate.py) (5709 B)
  `7cfbf116c5e51e9a3e1aa0ecd113a24f7a069706336ae01221de67dbf331cb6d`
- [verify\_analytic\_limits.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/verify_analytic_limits.py) (3073 B)
  `68bca45f2d3d8a1f8cda804bb45c3e165025effa0221c2f45d7c1416009bae6a`
- [verify\_nuisance\_mapping.py](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/verify_nuisance_mapping.py) (4184 B)
  `04b8587eb21a257b809fe930c33d43ddb2e008e4ebf2026134ee0a1d35b4015e`
- [SHA256SUMS.json](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/SHA256SUMS.json) (3306 B)
  `2fb52c937f2026d872f053e17343e55595fb3568862c20573d59123f3d897b49`
- [galaxy-rotation-science-v1.zip](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/galaxy-rotation-science-v1.zip) (29081 B)
  `f389268ed651be3a66ad7d87ee6760ebe39d87f6bd2498b2fb8904c9f10893a3`

[上一篇：距离与倾角为什么会改变整条曲线](https://sandboxphysics.com/zh/stories/galaxy-rotation-curves/moving-a-whole-rotation-curve/) [下一篇：怎样读反馈、暗晕与MOND的条件比较](https://sandboxphysics.com/zh/stories/galaxy-rotation-curves/reading-feedback-and-gravity-comparisons/)

## 来源与计算范围

1. [Lelli, McGaugh & Schombert (2016), SPARC, AJ152,157](https://astroweb.cwru.edu/SPARC/)

   官方固定Table1与Rotmod\_LTG.zip，只使用具名NGC2403成员。主论文第3.1至3.3及5.1至5.3节、原Table2说明用于核读气体符号、单位恒星质量光度比、有限厚度与误差语义。新取源与缓存复算分别记录；原观测与实测派生图留在本地。

2. [Independent saved-observation rotation-curve calculation](https://sandboxphysics.com/stories/galaxy-rotation-curves/materials/galaxy-rotation-science-v1.zip)

   自有MIT Python实际生成合成观测、六起点拟合、轮廓、共同几何场景、正密度/圆环势/Jacobian参照与SVG。可选具名源获取、73点保存转换和拟合均实际执行。默认离线，ZIP不含Lab引擎、原SPARC行、论文或实测派生图。
