/hats-publish
Transforme une version d'un catalogue d'observation en une collection HATS (catalogue + marge + tables d'index) qui est vérifiée par rapport aux fichiers source, documentée, hébergée et enregistrée. La procédure suit la documentation de hats-import et lsdb, le pipeline d'import Rubin (https://github.com/lincc-frameworks/rubin-dash) et les conventions de l'équipe enregistrées dans des tickets publics et des notebooks d'import archivés ; chaque valeur spécifique au site provient du fichier de site décrit dans CONFIG.md, chaque référence est une URL publique, et rien ici ne nomme une machine, une personne ou un ensemble de données.
Une exécution = un catalog.toml (copié depuis templates/catalog.toml dans un répertoire de travail). Chaque modèle le lit, les chemins relatifs qui s'y trouvent sont résolus par rapport au répertoire qui le contient, et une exécution est reproductible à partir de ce seul fichier. Gardez le répertoire de travail : il contient la configuration, les journaux, le notebook de vérification exécuté et le journal d'exécution.
Gelez les modèles au début de chaque exécution : T=$(bash <skill>/templates/pin_templates.sh) dans le répertoire de travail les copie en lecture seule dans templates.pinned/ avec le commit git, et chaque étape ultérieure (job.sbatch et resubmit_loop.sh inclus) exécute $T/.... Un git pull ou une modification de la compétence ne peut alors pas changer une importation en cours, et le README nomme le commit qui a construit le catalogue. Modifiez le code d'une exécution uniquement avec pin_templates.sh --repin.
Quand l'utiliser
Une nouvelle version d'un catalogue à publier ; une réimportation d'un catalogue HATS existant avec un meilleur partitionnement, des paramètres d'écriture ou des métadonnées ; une demande de vérification qu'une copie HATS correspond à sa source.
Entrées (demander uniquement si non définies)
Fichiers source ou URL et leur format ; relevé, version, page de modèle de données, article, remerciements et licence ; si une version HATS existe déjà (chercher dans l'arborescence data/ de data.lsdb.io et imports/README.md) ; les colonnes de coordonnées et d'identifiant. Tout le reste est décidé dans l'entretien ci-dessous.
Procédure
Phases dans l'ordre. Les étapes en gras s'arrêtent et attendent l'utilisateur. Chaque phase commence et se termine par une entrée dans le journal d'exécution (run_log.py start|end <phase>) ; les modèles le font eux-mêmes là où ils s'exécutent.
- Environnement. Créer ou mettre à jour un environnement avec les dernières versions publiées de
hats,lsdb,hats-import(pip install -U hats lsdb hats-import nested-pandas pyarrow astropy fitsio jupyter), afficher les versions, parcourir les notes de version pour les changements de format. PointerHATS_PUBLISH_SITE_ENVvers votre fichier de site (CONFIG.md). Les modèles qui utilisent les bibliothèques refusent de s'exécuter en dessous des versions minimales et avertissent lorsqu'une version plus récente existe. - Entretien de décision (
reference/decisions.md) : les neuf groupes, quelques questions à la fois, chacune avec sa valeur par défaut recommandée et ses conséquences. Écrire les réponses danscatalog.tomlet le journal d'exécution (run_log.py decision). Ensuite, montrer le plan : chemin, lecteur, ressources, partitionnement, paramètres d'écriture, noms, cibles. - Acquisition. Télécharger dans le répertoire brut, vérifier les sommes de contrôle et l'intégrité des archives (
gzip -t), enregistrer le sha256 de chaque fichier dans[source].sha256, et sauvegarder la page du modèle de données à côté des données. Compter les fichiers par rapport à la liste de l'archive ([source].expected_files) et lister les noms de fichiers exacts, jamais un glob nu sur un répertoire qui peut contenir des copies de téléchargement. Pour des milliers de petits fichiers, écrire des fichiers d'index pour les lecteurs indexés. Un téléchargement laissé en arrière-plan écrit son pid dansdownload.piddans le répertoire de travail afin questatus.shpuisse le signaler. 2a. Tables sans coordonnées ([source.positions_from]) :attach_positions.pyrecherche les positions par identifiant dans une collection HATS de référence (sa table d'index) et écrit la source en Parquet avecra/dec; l'import lit ensuite ces fichiers. Les CSV avec en-tête ECSV et colonnes de tableaux ([reader].ecsv_header = true) sont analysés en colonnes de listes au passage. 2b. Une table divisée en fichiers FITS parallèles par ligne ([source.companions], par exemple un fichier principal plus des fichiers supplémentaires/photo-z/courbes de lumière avec les mêmes lignes) : l'import les lit côte à côte avec des plages de lignes fitsio, vérifie les colonnes clés bloc par bloc, et peut ajouter des colonnes calculées ([reader].derive, une fonction dans le répertoire de travail) et regrouper des colonnes de listes en une colonne imbriquée ([reader].pack_nested). Le chargeur de vérification lit les mêmes fichiers indépendamment. - Planifier le partitionnement.
estimate_threshold.py --writeéchantillonne l'entrée avec les paramètres d'écriture de l'exécution et convertit la règle de taille de fichier de 200 à 800 Mo enpixel_threshold, et prédit le nombre de partitions et la taille dedataset/_metadata(avertit près de la limite de 4 Gio du pied de page) ; vérifierhighest_orderpar rapport à la région la plus dense. - Essai.
import_collection.py --trial-file <un fichier>dans un répertoire d'essai ; inspecter les dtypes, les nulls par rapport aux sentinelles, les noms de colonnes, les colonnes de tableaux, les lignes sans coordonnées ([reader].drop_rows_without_coordinatessupprime et les compte). Ajuster[reader],[columns]et[properties]; montrer à l'utilisateur ce qui a changé. - Importer.
import_collection.pydétaché (nohup/setsid), oujob.sbatchsur un nœud batch (il exécute également les phases 5-6 et la comparaison), reprenable ; il construit le catalogue, la ou les marges et les tables d'index avec les paramètres[properties]et[writer]. Les petites tables (moins d'environ 1 M de lignes et 1 Go en mémoire ; au-dessus d'environ 100 k lignes sur les tables larges, hats-import est plus rapide) utilisentimport_small.pyà la place. Ensuitewrite_ancillary.py: vignette de données (échantillon aléatoire par partition), statistiques par partition, prévisualisations PNG, README etindex.htmlavec une section de provenance. Progrès :status.sh(depuis le répertoire de travail) affiche le téléchargement, le job SLURM, l'étape actuelle de hats-import avec sa barre de progression et la dernière entrée du journal d'exécution. Les téléchargements et les importations durent des heures : dès que l'un d'eux s'exécute en arrière-plan (phase 2 ou 5), lancer des mises à jour toutes les 5 minutes sans demander. Chaque mise à jour exécutestatus.shet signale uniquement ce qui a changé. Dire qu'elles sont en cours d'exécution et que l'utilisateur peut modifier l'intervalle ou les arrêter. Les arrêter lorsque le job se termine ou échoue, et vérifier l'état réel du job lorsqu'il quitte la file d'attente. Sur une file d'attente préemptible, lancerresubmit_loop.sh <job id>à côté du job : il enregistre les ressources du job dansresubmit_opts.txt, relance les jobs préemptés ou annulés par le système avec les mêmes ressources (hats-import reprend ; un job préempté après la fin de l'importation, marqué parimport.done, est relancé avec l'importation ignorée) et s'arrête sur toute autre chose. 5a. Produit temporel imbriqué ([nested], facultatif) :attach_nested.pyjoint des colonnes de listes par objet provenant d'un HATS de référence par identifiant (jointure spatiale interne), les regroupe en une colonne imbriquée avecnest_listset écrit une deuxième collection à côté de la collection plate ; la section 9b du notebook concilie les lignes, les longueurs de listes et un échantillon de tableaux. - Vérifier, tout échec arrête l'exécution (
reference/verification.md) :verify_structure.py --spot-check(vérifications de hats-import, conformité HATS-note, balayage d'encodage) →run_verify.sh(le notebook au niveau des valeurs par rapport à la source ; les vérifications se terminent par PASS, FAIL ou SKIP avec une raison) → la liste de contrôle de révision avec l'utilisateur. - Documenter et archiver. Ajouter
catalog.toml, les scripts utilisés et le notebook exécuté au répertoireimports/de data.lsdb.io avec une ligne en haut de son README (dans le cadre de la pull request du site). - Héberger (
reference/publishing.md). Exécuterpublish_web.sh --checketpublish_mirror.sh --check, montrer les prévisualisations (ce qui serait copié, et ce que la destination contient déjà), et seulement après l'approbation de l'utilisateur, les exécuter avec--yes(sans l'un des deux indicateurs, ils s'arrêtent ; une destination qui contient des fichiers manquants à la construction les arrête avant d'écrire sauf si--replaceest donné, ce qui supprime les fichiers obsolètes) : copier, grouper, manifeste de sommes de contrôle,hats_statusethats_service_urlpar table, lien symbolique sur l'hôte web, test ouvert public aveclsdb.open_catalogsur la racine de la collection (un échec annule le lien symbolique), puis la description de l'auto-index ; la copie miroir est revérifiée dansverify_results_mirror.csv. - Enregistrer.
make_site_entry.pyécrit l'entrée de page et la carte de densité sur une branche dans un clone de data.lsdb.io (fusion dans une entrée existante) et la prépare ; demander le message de commit ; la pull request appartient à l'utilisateur. Après sa fusion et son déploiement,registry_entry.pyexécute le générateur de registre sur une branche dans un clone de registre pour la pull request de l'utilisateur ; il nécessite un clone de site propre àorigin/main. - Clôturer.
NOTES.mdà partir du modèle ; éléments ouverts (miroirs, registre, déploiement, retrait d'une version précédente) ;run_log.py feedbackavec l'utilisateur pour ce qui n'était pas clair, manuel ou erroné dans les valeurs par défaut ;run_log.py render --scrubet proposer une pull request ajoutant le journal àrunlogs/dans ce dépôt.
Réimportations : ImportArguments.reimport_from_hats pour de nouveaux seuils ou un nouveau tri, reconstruire les marges et les index, écrire dans un nouveau répertoire, vérifier, exécuter compare_versions.py --new <new> --old <previous> --key <id> (schéma, lignes et valeurs de la région partagée ; le rétrécissement de type est signalé séparément), rediriger les liens symboliques, conserver l'ancienne copie jusqu'à ce que la page et le registre pointent vers la nouvelle.
Modèles
| Fichier | Objectif |
|---|---|
catalog.toml | la configuration de l'exécution (copier et remplir) |
pin_templates.sh | geler les modèles pour une exécution dans templates.pinned/ (commit, diff local, sommes de contrôle) ; --repin pour les modifier |
_common.py, catalog_source.py, readers.py | assistants partagés : configuration, versions, variables de site, paramètres d'écriture, propriétés, chargeur de source, filtre de coordonnées |
estimate_threshold.py | règle de taille de fichier → pixel_threshold |
import_collection.py | construction de la collection hats-import (--dry-run, --trial-file) |
import_small.py | chemin en mémoire pour les petites tables |
attach_positions.py | positions par identifiant à partir d'un HATS de référence pour les tables sans coordonnées ; tableaux ECSV analysés en colonnes de listes |
attach_nested.py | produit temporel imbriqué : joindre des colonnes de listes par identifiant à partir d'un HATS de référence, nest_lists, écrire une collection |
job.sbatch | chemin batch : importation, fichiers auxiliaires, vérification et comparaison en un seul job SLURM sur un nœud dédié |
write_ancillary.py | vignette, statistiques, prévisualisations, README, index.html |
verify_structure.py | vérifications structurelles, de conformité et d'encodage |
build_verify_notebook.py, run_verify.sh | notebook de vérification au niveau des valeurs |
compare_versions.py | comparer une nouvelle construction avec une version HATS existante (réimportations, validation par rapport à une copie publiée) |
publish_mirror.sh, publish_web.sh | cibles d'hébergement |
make_site_entry.py, registry_entry.py | page et registre data.lsdb.io |
memwatch.sh | chien de garde mémoire pour les longues étapes monoprocessus sur les nœuds partagés |
status.sh | résumé de progression sur un écran (téléchargement, job, étape d'importation, journal d'exécution) pour les mises à jour planifiées |
resubmit_loop.sh | relancer un job batch après préemption ou annulation système ; une boucle par exécution (garde à cœur) |
run_log.py, NOTES.template.md | journal d'exécution et notes |
Pièges
Une ligne chacun ; détails et plus dans reference/pitfalls.md.
- Une tâche Dask par fichier d'entrée : diviser les fichiers énormes, utiliser des lecteurs indexés pour des milliers de petits fichiers.
- Les lignes sans coordonnées arrêtent l'étape de mappage : faire d'abord un import d'essai, les filtrer et les compter.
- Les entrées CSV compressées en gzip nécessitent
compression="gzip"(le lecteur obtient un descripteur ouvert) ; l'import l'ajoute pour les fichiers.gz. expected_total_rowsdoit correspondre sinon le pipeline s'arrête après le binning ; c'est une vérification, pas une indication.- Utiliser les valeurs par défaut Parquet de hats (hats, hats-import, lsdb >= 0.11.1) : ZSTD 15, index de page, BYTE_STREAM_SPLIT sur les colonnes flottantes. Laisser
[writer]vide sauf si l'utilisateur décide autrement ; écrire tout fichier que la compétence écrit elle-même avecparquet_write_kwargs. Les versions plus anciennes écrivent les flottants codés par dictionnaire, environ 1,4 × plus gros. - Les colonnes par défaut doivent exister dans le schéma ; les colonnes imbriquées utilisent
frame.subcol. - hats écrit
hats.propertiesetproperties; modifier les deux ou aucun des deux. hats_release_dateest la date de la note HATS, pas celle des données ; mettre les dates des données danst_min/t_max.- Une marge peut contenir des lignes supplémentaires, jamais moins : vérifier par containment.
- Les marges et les index portent le chemin de construction dans
hats_primary_table_url; les scripts de publication le rendent relatif. - Ouvrir les collections à la racine ; la page doit pointer vers la racine de la collection.
- Le
.htaccessweb est en première correspondance et correspondance de préfixe ; le script de publication insère avant les entrées génériques et écrit par copie, pas par renommage (répertoire collant). - Les entrées dont les colonnes diffèrent par région perdent silencieusement des colonnes lors de la réduction : se conformer à un seul schéma (
[reader].conform_schema, tout format), essayer chaque région. _metadatane peut pas dépasser 4 Gio :estimate_threshold.pyle prédit ;create_metadata = falsepour les catalogues énormes.- Les listes à taille fixe nulles cassent les lecteurs pyarrow < 26 : remplir avec NaN ou utiliser des listes à longueur variable.
- Les barres de progression dans les notebooks sans tête deviennent des widgets morts ; le constructeur de notebook utilise des barres textuelles.
- Les shells parallèles partagent un seul répertoire de travail : chemins absolus, fichiers pid au lieu de
pgrep -f.
Sortie
Une collection HATS vérifiée sur les cibles d'hébergement, une page data.lsdb.io préparée, une branche de registre, NOTES.md, verify_results.csv, le notebook de vérification exécuté et RUNLOG.md.