/hats-publish
サーベイのカタログリリースを、ソースファイルに対して検証され、文書化され、ホスティングされ、登録されたHATSコレクション(カタログ+マージン+インデックステーブル)に変換します。この手順は、hats-importとlsdbのドキュメント、Rubinインポートパイプライン(https://github.com/lincc-frameworks/rubin-dash)、および公開されたIssueやアーカイブされたインポートノートブックに記録されたチームの規約をコード化したものです。すべてのサイト固有の値は`CONFIG.md`で説明されているサイトファイルから取得され、すべての参照は公開URLであり、ここではマシン、人物、データセットの名前は指定されません。
1回の実行 = 1つの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):9つのグループ、一度にいくつかの質問、それぞれに推奨されるデフォルトと結果が含まれます。回答をcatalog.tomlと実行ログ(run_log.py decision)に書き込みます。次に計画を示します。ルート、リーダー、リソース、パーティション、ライター設定、名前、ターゲット。 - 取得。rawディレクトリにダウンロードし、チェックサムとアーカイブの整合性を検証し(
gzip -t)、すべてのファイルのsha256を[source].sha256に記録し、データモデルページをデータの横に保存します。ファイル数をアーカイブのリスト([source].expected_files)と照合し、再ダウンロードされたコピーを保持する可能性のあるディレクトリに対してベアのglobではなく、正確なファイル名をリストします。数千の小さなファイルの場合は、インデックス付きリーダー用のインデックスファイルを作成します。バックグラウンドで実行中のダウンロードは、ワークディレクトリに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ファイルに分割された1つのテーブル([source.companions]、例:同じ行を持つメインファイルと追加/光赤方偏移/ライトカーブファイル):インポートはfitsio行範囲を使用してそれらを並行して読み取り、チャンクごとにキー列をチェックし、計算列([reader].derive、ワークディレクトリ内の関数)を追加し、リスト列を1つのネスト列にパックできます([reader].pack_nested)。検証ローダーは同じファイルを独立して読み取ります。 - パーティション計画。
estimate_threshold.py --writeは、実行のライター設定で入力をサンプリングし、200~800 MBのファイルサイズルールをpixel_thresholdに変換し、パーティション数とdataset/_metadataのサイズを予測します(4 GiBフッター制限に近い場合は警告)。最も密度の高い領域に対してhighest_orderを確認します。 - トライアル。
import_collection.py --trial-file <1つのファイル>をトライアルディレクトリに実行します。dtype、nullとセンチネル、列名、配列列、座標のない行([reader].drop_rows_without_coordinatesで削除してカウント)を検査します。[reader]、[columns]、[properties]を調整します。ユーザーに何が変更されたかを示します。 - インポート。
import_collection.pyをデタッチ(nohup/setsid)するか、バッチノードでjob.sbatchとして実行します(フェーズ5~6と比較も実行します)。再開可能です。カタログ、マージン、およびインデックステーブルを[properties]と[writer]の設定でビルドします。小さなテーブル(約100万行未満、メモリ内で約1 GB未満。約10万行を超えるワイドテーブルでは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 <ジョブID>を開始します。ジョブのリソースをresubmit_opts.txtに記録し、プリエンプトまたはシステムキャンセルされたジョブを同じリソースで再送信します(hats-importは再開します。インポートが完了した後にプリエンプトされたジョブは、import.doneでマークされ、インポートをスキップして再送信されます)。それ以外の場合は停止します。 5a. ネストされた時系列プロダクト([nested]、オプション):attach_nested.pyは、参照HATSからオブジェクトごとのリスト列をIDで結合し(空間内部結合)、nest_listsでそれらを1つのネスト列にパックし、フラットなコレクションの隣に2番目のコレクションを書き込みます。ノートブックのセクション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を付けて実行します(2つのフラグのいずれかがないと停止します。ビルドにないファイルを宛先が保持している場合、--replaceが指定されていない限り、書き込み前に停止します。これにより古いファイルが削除されます)。コピー、グループ化、チェックサムマニフェスト、テーブルごとのhats_statusとhats_service_url、Webホスト上のシンボリックリンク、コレクションルートでのlsdb.open_catalogによる公開オープンテスト(失敗した場合はシンボリックリンクを元に戻す)、次にautoindexの説明。ミラーコピーは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 | 1回の実行用にテンプレートを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 | ネストされた時系列プロダクト:参照HATSからIDによってリスト列を結合、nest_lists、コレクションを書き込む |
job.sbatch | バッチルート:専用ノード上の1つの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 | ホスティングターゲット |
make_site_entry.py、registry_entry.py | data.lsdb.ioページとレジストリ |
memwatch.sh | 共有ノード上の長時間のシングルプロセスステップ用メモリ監視 |
status.sh | スケジュールされた更新用の1画面の進捗サマリー(ダウンロード、ジョブ、インポートステージ、実行ログ) |
resubmit_loop.sh | プリエンプションまたはシステムキャンセル後のバッチジョブの再送信。実行ごとに1ループ(ハートビートガード) |
run_log.py、NOTES.template.md | 実行ログとメモ |
よくある注意点
各1行。詳細と詳細はreference/pitfalls.mdを参照。
- 入力ファイルごとに1つのDaskタスク:巨大なファイルは分割し、数千の小さなファイルにはインデックス付きリーダーを使用します。
- 座標のない行はマッピングステージを停止します:最初にトライアルインポートし、フィルタリングしてカウントします。
- gzip圧縮されたCSV入力には
compression="gzip"が必要です(リーダーは開かれたハンドルを取得します)。インポートは.gzファイルにそれを追加します。 expected_total_rowsは一致する必要があります。一致しないとパイプラインはビニング後に停止します。これはチェックであり、ヒントではありません。- hats Parquetデフォルト(hats、hats-import、lsbd >= 0.11.1)を使用します:ZSTD 15、ページインデックス、float列にはBYTE_STREAM_SPLIT。ユーザーが別途決定しない限り、
[writer]は空のままにします。スキル自体が書き込むファイルはparquet_write_kwargsで書き込みます。古いバージョンはfloatを辞書エンコードで書き込み、約1.4倍大きくなります。 - デフォルトの列はスキーマに存在する必要があります。ネストされた列は
frame.subcolを使用します。 - hatsは
hats.propertiesとpropertiesを書き込みます。両方を編集するか、どちらも編集しないでください。 hats_release_dateはHATSノートの日付であり、データの日付ではありません。データの日付はt_min/t_maxに配置します。- マージンは追加の行を保持する場合があり、少なくなることはありません。包含関係によって検証します。
- マージンとインデックスは
hats_primary_table_urlにビルドパスを保持します。公開スクリプトはそれを相対パスにします。 - コレクションはルートで開きます。ページはコレクションルートを指している必要があります。
- Webの
.htaccessは最初一致とプレフィックス一致です。公開スクリプトは汎用エントリの前に挿入し、リネームではなくコピーで書き込みます(スティッキーディレクトリ)。 - リージョンによって列が異なる入力は、縮小時に列を失います。1つのスキーマに準拠させ(
[reader].conform_schema、任意の形式)、各リージョンをトライアルします。 _metadataは4 GiBを超えることはできません。estimate_threshold.pyが予測します。巨大なカタログの場合はcreate_metadata = false。- ヌルの固定サイズリストはpyarrow < 26のリーダーを壊します。NaNで埋めるか、可変長リストを使用します。
- ヘッドレスノートブックのプログレスバーはデッドウィジェットになります。ノートブックビルダーはテキストバーを使用します。
- 並列シェルは1つのワーキングディレクトリを共有します。絶対パス、
pgrep -fの代わりにpidファイルを使用します。
出力
ホスティングターゲット上の検証済みHATSコレクション、ステージングされたdata.lsdb.ioページ、レジストリブランチ、NOTES.md、verify_results.csv、実行された検証ノートブック、およびRUNLOG.md。