/hats-publish
将巡天星表发布版本转换为 HATS 集合(星表 + 边缘表 + 索引表),并与源文件进行验证、编写文档、托管和注册。该流程整合了 hats-import 和 lsdb 文档、Rubin 导入流程(https://github.com/lincc-frameworks/rubin-dash)以及团队在公开议题和归档导入笔记本中记录的约定;
每个站点特定值均来自 CONFIG.md 中描述的站点文件,每个引用均为公开 URL,且此处不涉及任何机器、人员或数据集的名称。
一次运行 = 一个 catalog.toml(从 templates/catalog.toml 复制到工作目录)。每个模板都会读取它,其中相对路径相对于存放它的目录解析,且一次运行可完全由此文件重现。请保留工作目录:其中包含配置、日志、执行的验证笔记本和运行日志。
每次运行开始时固定模板:在工作目录中执行 T=$(bash <skill>/templates/pin_templates.sh),将其以只读方式及其 git 提交记录复制到 templates.pinned/,之后每一步(包括 job.sbatch 和 resubmit_loop.sh)都运行 $T/...。这样,git pull 或技能的编辑不会更改正在运行的导入,并且 README 会注明构建该星表的提交。若要更改运行中的代码,请使用 pin_templates.sh --repin。
何时使用
要发布新的巡天数据版本;使用更好的分区、写入器设置或元数据重新导入已有的 HATS 星表;或请求验证 HATS 副本是否与源数据匹配。
输入(仅当未设置时询问)
源文件或 URL 及其格式;巡天名称、发布版本、数据模型页面、论文、致谢和许可;是否已有 HATS 版本(查看 data.lsdb.io 的 data/ 目录树和 imports/README.md);坐标列和 ID 列。其他所有内容均在下面的访谈中确定。
流程
按顺序的阶段。加粗步骤会暂停并等待用户。每个阶段以运行日志条目(run_log.py start|end <phase>)开始和结束;模板在自身运行时自动执行此操作。
- 环境。 创建或更新环境,安装最新发布的
hats、lsdb、hats-import(pip install -U hats lsdb hats-import nested-pandas pyarrow astropy fitsio jupyter),打印版本,简要阅读发布说明以了解格式变更。将HATS_PUBLISH_SITE_ENV指向您的站点文件(CONFIG.md)。使用这些库的模板在低于最低版本时会拒绝运行,并在有新版本发布时发出警告。 - 决策访谈(
reference/decisions.md):九个组,每次几个问题,每个问题都有推荐默认值和后果。将答案写入catalog.toml和运行日志(run_log.py decision)。然后展示计划:路径、读取器、资源、分区、写入器设置、名称、目标。 - 获取数据。下载到原始目录,验证校验和和归档完整性(
gzip -t),记录每个文件的 sha256 到[source].sha256,并将数据模型页面保存在数据旁边。对照归档的文件列表([source].expected_files)统计文件数量,列出确切文件名,切勿对整个可能包含重新下载副本的目录使用简单通配符。对于数千个小文件,为索引式读取器编写索引文件。后台运行的下载会将其 PID 写入工作目录中的download.pid,以便status.sh报告其状态。 2a. 无坐标的表([source.positions_from]):attach_positions.py在参考 HATS 集合(其索引表)中按 ID 查找坐标位置,并将源数据写为带有ra/dec的 Parquet 文件;然后导入读取这些文件。带有 ECSV 头部的 CSV 文件中的数组列([reader].ecsv_header = true)在读取时解析为列表列。 2b. 被分割成行并行 FITS 文件的单个表([source.companions],例如,一个主文件加上包含相同行的额外/光红移/光变曲线文件):导入使用 fitsio 行范围并行读取它们,逐块检查键列,并可以添加计算列([reader].derive,工作目录中的一个函数),并将列表列打包为一个嵌套列([reader].pack_nested)。验证加载器独立读取相同的文件。 - 规划分区。
estimate_threshold.py --write使用运行时的写入器设置对输入进行采样,并将 200–800 MB 文件大小规则转换为pixel_threshold,并预测分区数和dataset/_metadata的大小(接近 4 GiB 页脚限制时发出警告);对照最密集区域检查highest_order。 - 试导入。
import_collection.py --trial-file <一个文件>到试导入目录;检查数据类型、空值与哨兵值、列名、数组列、无坐标行([reader].drop_rows_without_coordinates删除并计数)。调整[reader]、[columns]和[properties];向用户展示更改内容。 - 正式导入。
import_collection.py后台运行(nohup/setsid),或者使用job.sbatch在批处理节点上运行(它同时运行阶段 5–6 和比较),可恢复执行;它构建星表、边缘表和索引表,使用[properties]和[writer]设置。对于小型表(内存中约 1 M 行和 1 GB 以下;对于宽表,超过约 100 k 行时 hats-import 更快),使用import_small.py代替。然后运行write_ancillary.py:数据缩略图(每个分区的随机样本)、每个分区统计信息、PNG 预览、README 和带有来源部分 的index.html。 进度:status.sh(从工作目录运行)打印下载状态、SLURM 作业、当前 hats-import 阶段及其进度条,以及最新的运行日志条目。下载和导入可能运行数小时:一旦有任务在后台运行(阶段 2 或 5),立即开始每 5 分钟无询问更新一次。每次更新运行status.sh并仅报告更改的内容。告知用户任务正在运行,并且可以更改间隔或停止它们。当任务完成或失败时停止更新,并在任务离开队列时检查其实际状态。在可抢占队列上,在作业旁边启动resubmit_loop.sh <job id>:它记录作业的资源到resubmit_opts.txt,以相同资源重新提交被抢占或系统取消的作业(hats-import 可恢复;如果在导入完成后被抢占,由import.done标记,则重新提交时跳过导入),并在遇到其他情况时停止。 5a. 嵌套时间序列产品([nested],可选):attach_nested.py通过 ID(空间内连接)从参考 HATS 连接每个对象的列表列,使用nest_lists打包为一个嵌套列,并写入扁平集合旁边的第二个集合;笔记本的第 9b 节会协调行数、列表长度和数组样本。 - 验证,任何失败都会停止运行(
reference/verification.md):verify_structure.py --spot-check(hats-import 的检查、HATS 备注合规性、编码扫描)→run_verify.sh(针对源数据的值级笔记本;检查结果为 PASS、FAIL 或 SKIP,并附有原因)→ 与用户一起查看检查清单。 - 文档和归档。将
catalog.toml、使用的脚本和执行的笔记本添加到 data.lsdb.io 的imports/目录,并在其 README 顶部添加一行(作为站点拉取请求的一部分)。 - 托管(
reference/publishing.md)。运行publish_web.sh --check和publish_mirror.sh --check,显示预览(将复制什么,以及目标上已有哪些内容),仅在用户批准后使用--yes运行它们(如果没有这两个标志之一,它们会停止;如果目标包含构建所缺少的文件,它们会在写入前停止,除非给出--replace来删除过时文件):复制、分组、校验和清单、每个表的hats_status和hats_service_url、Web 主机上的符号链接、使用lsdb.open_catalog在集合根目录上进行公开开放测试(失败则回滚符号链接),然后是自动索引描述;镜像副本会重新验证到verify_results_mirror.csv。 - 注册。
make_site_entry.py在 data.lsdb.io 克隆的一个分支上写入页面条目和密度图(合并到现有条目中)并暂存更改;询问提交信息;拉取请求由用户创建。在合并并部署后,registry_entry.py在注册表克隆的一个分支上运行注册表生成器,以供用户创建拉取请求;它需要一个指向origin/main的干净站点克隆。 - 收尾。从模板生成
NOTES.md;开放事项(镜像、注册表、部署、旧版本的退役);与用户一起运行run_log.py feedback,了解默认值中哪些不清晰、手动或错误;run_log.py render --scrub并提供拉取请求,将日志添加到此仓库的runlogs/目录。
重新导入:使用 ImportArguments.reimport_from_hats 设置新的阈值或排序,重建边缘表和索引,写入新目录,验证,运行 compare_versions.py --new <new> --old <previous> --key <id>(共享区域的模式、行和值;类型收窄单独报告),重新指向符号链接,在页面和注册表指向新版本之前保留旧副本。
模板
| 文件 | 用途 |
|---|---|
catalog.toml | 运行配置(复制并填写) |
pin_templates.sh | 为单次运行固定模板到 templates.pinned/(提交、本地差异、校验和);使用 --repin 更改 |
_common.py、catalog_source.py、readers.py | 共享帮助程序:配置、版本、站点变量、写入器设置、属性、源加载器、坐标过滤器 |
estimate_threshold.py | 文件大小规则 → pixel_threshold |
import_collection.py | hats-import 集合构建(--dry-run、--trial-file) |
import_small.py | 小型表的内存中路径 |
attach_positions.py | 对于无坐标表,从参考 HATS 按 ID 查找位置;将 ECSV 数组解析为列表列 |
attach_nested.py | 嵌套时间序列产品:按 ID 从参考 HATS 连接列表列,nest_lists,写入集合 |
job.sbatch | 批处理路径:导入、辅助文件、验证和比较作为一个 SLURM 作业在专用节点上运行 |
write_ancillary.py | 缩略图、统计信息、预览、README、index.html |
verify_structure.py | 结构、合规性和编码检查 |
build_verify_notebook.py、run_verify.sh | 值级验证笔记本 |
compare_versions.py | 比较新构建与现有 HATS 版本(重新导入、与已发布副本的验证) |
publish_mirror.sh、publish_web.sh | 托管目标 |
memwatch.sh | 共享节点上长时间单进程步骤的内存监控 |
status.sh | 单屏进度摘要(下载、作业、导入阶段、运行日志),用于定期更新 |
resubmit_loop.sh | 在抢占或系统取消后重新提交批处理作业;每次运行一个循环(心跳防护) |
run_log.py、NOTES.template.md | 运行日志和笔记 |
注意事项
每条一行;详细信息和更多内容请参阅 reference/pitfalls.md。
- 每个输入文件一个 Dask 任务:拆分巨大文件,对数千个小文件使用索引读取器。
- 无坐标行会停止映射阶段:先试导入,然后过滤并计数。
- Gzip 压缩的 CSV 输入需要
compression="gzip"(读取器会获得一个已打开的文件句柄);导入会为.gz文件自动添加。 expected_total_rows必须匹配,否则流程在分箱后停止;它是一个检查,而非提示。- 使用 hats Parquet 默认值(hats、hats-import、lsdb >= 0.11.1):ZSTD 15、页面索引、对浮点列使用 BYTE_STREAM_SPLIT。除非用户另有决定,否则将
[writer]留空;技能自身写入的任何文件都使用parquet_write_kwargs。旧版本将浮点数使用字典编码,大约大 1.4 倍。 - 默认列必须存在于模式中;嵌套列使用
frame.subcol。 - hats 写入
hats.properties和properties;两者都编辑或都不编辑。 hats_release_date是 HATS 备注的日期,而非数据日期;将数据日期放在t_min/t_max中。- 边缘表可能包含额外行,但绝不能少:通过包含关系验证。
- 边缘表和索引表在
hats_primary_table_url中包含构建路径;发布脚本会使其变为相对路径。 - 在根目录打开集合;页面必须指向集合根目录。
- Web 的
.htaccess是首次匹配和前缀匹配;发布脚本在通用条目之前插入,并通过复制而非重命名写入(粘滞目录)。 - 输入中按区域不同的列会在归并时静默丢失:统一为一种模式(
[reader].conform_schema,任何格式),对每个区域进行试导入。 _metadata不能超过 4 GiB:estimate_threshold.py会预测其大小;对于巨大的星表,设置create_metadata = false。- 空值的固定长度列表会破坏 pyarrow < 26 的读取器:用 NaN 填充或使用可变长度列表。
- 无头笔记本中的进度条会变成死控件;笔记本构建器使用文本进度条。
- 并行的 shell 共享一个工作目录:使用绝对路径,使用 PID 文件而非
pgrep -f。
输出
托管目标上的一个经过验证的 HATS 集合、一个暂存的 data.lsdb.io 页面、一个注册表分支、NOTES.md、verify_results.csv、执行的验证笔记本和 RUNLOG.md。