Le processus rmfit
rmfit (/groups/rdeboer1/user/rdeboer1/R-matrix/rmfit/, README là-bas) fait ce qu'un évaluateur fait à la main — polir, inverser les signes d'interférence, ajouter ou retirer des niveaux, désactiver des jeux de données suspects — sous forme d'étapes reprises qu'un script exécute sur un nœud de cluster et qu'une nouvelle session peut reprendre à partir du STATUS.md. Il n'est pas installé comme un paquet :
export PYTHONPATH=/groups/rdeboer1/user/rdeboer1/R-matrix
python3 -m rmfit.cli <campaign_dir> <command> [options]
Lisez ce fichier du début à la fin avant la première commande sur une nouvelle réaction ; pour reprendre une campagne, « Lire l'état » et « La boucle » suffisent. Les améliorations vont dans le journal à la fin.
1. Lire l'état, ne jamais le recalculer
Un répertoire de campagne contient tout ce dont une nouvelle session a besoin :
STATUS.md— meilleur objectif vérifié par structure, cycles récents et lignes de journal. Régénérer avecstatus;status --jsonpour un dump compact.readme— le cahier de laboratoire (rmfit ajoute un paragraphe par cycle ; ajoutez le vôtre aveclog "..."). Convention d'archive : le lire avant de toucher à quoi que ce soit.campaign.log— chaque ligne imprimée par le pilote ;rmfit_sge.log/rmfit_*.log— journaux des tâches cluster.ledger.sqlite+candidates/*.npz— chaque vecteur candidat et son score. Le candidat en place est le meilleur candidat avec un scoreverified(une évaluation fraîche et mono-session du modèle complet). Les nombres shardés sont classés ; les nombres vérifiés sont rapportés.campaign.json— modèle de base, drapeaux du moteur, nombre de shards, politique de bornes, configuration de recherche.<reaction>.azr+.sav— l'export vérifié prêt pour l'interface graphique.variants/— fichiers de shard, modèles de fenêtre, fichiers avec/sans niveau (jetables).
qstat -u $USER en premier : une commande de campagne à la fois par répertoire (elles partagent le
livre de comptes et variants/). Le nœud de connexion est pour status, report, log, export,
tests courts (<= 6 workers, <= 30 min). Tout le reste s'exécute comme une tâche.
2. Lancer une campagne sur une nouvelle réaction
Prérequis (faites-les dans le répertoire d'ajustement de la réaction, pas dans la campagne) :
- Un fichier
.azrqui s'ouvre dans l'interface graphique, avecdata/, un répertoireoutput/, et unreadme. Connaître les drapeaux de mode CLI que cette réaction utilise (--use-brune,--ignore-externals,--no-long-wavelength, ...) : ils ne sont pas dans le.azr, ils varient par réaction (~/R-matrix/CLAUDE.md), et rmfit en a besoin sous forme de JSON--flags. - Reproduire le dernier ajustement terminé avant de construire dessus : charger son
param.savpar nom (pyazr), confirmer que l'objectif correspond àchiSquared.out(chi2 + pénalité de normalisation), puis l'intégrer dans un fichier propre (rmfit.engine.bake) et le vérifier avec le CLI en mode 1 avec un fichier externe vierge. Ne jamais faire confiance à un bake qui a été « vérifié » avec le.savcomme fichier externe (cela écrase<levels>), et ne jamais utiliser de paramètres provenant d'un ajustement en cours ou planté — seulement le dernier terminé. Purger les lignes de niveau inactives. - Fusionner les nouveaux jeux de données sur le fichier baked (
AzrModel.add_data_segment), plafonné à la plage d'énergie couverte par le schéma de niveaux (set_segment_energy_range), avec normalisations libres et une erreur systématique qui reflète l'article. Examiner chaque nouveau fichier : une seule section efficace négative ou aberrante peut dominer l'objectif. - Enregistrer tout cela dans le
readmedu répertoire de la réaction.
Premiers ajustements : construire des paramètres de départ avant tout ajustement global (DeBoer, 2025-10-25 — RÉÉVALUER PLUS TARD)
Provisoire. Ceci est la suggestion générale de DeBoer, écrite avant d'avoir été testée de bout en bout. La réévaluer une fois
que 10B+α (la première réaction à l'utiliser, 10B+a/10-25-26_Rmatrix_Claude) aura un ajustement réel : enregistrer ce qui a fonctionné et ce qui
n'a pas fonctionné ici, et modifier ou abandonner la procédure en conséquence.
DeBoer : « Fixer les normes d'un seul jeu de données représentatif mais laisser les autres varier. Cela fournit alors une échelle plus fixe pour les sections efficaces initialement, mais permet toujours aux jeux de données avec des normes incohérentes mais des formes cohérentes de ne pas gêner l'ajustement. Dans notre cas, puisque nous avons des données dans chaque canal, je suggérerais de fixer nos données pour commencer et de laisser les autres varier. De plus, nous devons nous assurer de permettre aux canaux de plus bas L pour chaque paire de particules de varier, en donnant une petite valeur non nulle pour chaque niveau, même si la littérature ne lui donne pas de branche vers ce canal. Je suggérerais également de commencer à basse énergie et d'effectuer quelques polissages sur un niveau à la fois pour voir à quel point il peut correspondre aux données. Ensuite, revenir et essayer d'ajouter des niveaux là où des résonances sont clairement présentes et qui ne correspondent pas à l'un des niveaux connus en énergie et/ou largeur. Je ne tenterais pas un véritable ajustement avant d'avoir des paramètres de départ raisonnablement bons. »
En tant que procédure :
- Fixer l'échelle avec un jeu de données représentatif. Fixer ses normes à 1 (nos propres données quand elles couvrent chaque canal) ; laisser la norme de chaque autre jeu varier. Les jeux avec des formes cohérentes mais une échelle différente ne bloquent alors pas l'ajustement.
- Amorcer le canal de plus bas L de chaque paire, pour chaque niveau. Lui donner une petite amplitude non nulle (0,01 γ_W, définie dans le moteur ; des espaces réservés physiques minuscules, voir l'entrée d'amorçage du 2025-10-25), même là où la littérature ne liste aucune branche vers ce canal.
- Chaque canal qui s'ouvre dans la plage d'ajustement est dans le modèle — par défaut. Chaque fois que la plage d'ajustement est
étendue (une nouvelle fenêtre d'énergie, ou un jeu de données qui atteint plus haut), lister chaque partition ouverte en dessous du HAUT de
la plage : chaque état excité de chaque noyau résiduel (ENSDF) et les autres partitions de particules légères (d, t,
³He, ...), avec des seuils Ex = sepE + e2. Ajouter une paire pour chacun avant d'ajuster, sans qu'on vous le demande : le plus bas
L autorisé de chaque spin de canal sur chaque niveau ; amorcé selon l'item 2 sur les niveaux au seuil ou dans les 0,5 MeV en dessous du
seuil et sur les pôles de fond, inerte (0, fixe) ailleurs. Ajouter les nouvelles sorties à chaque segment sommé
(sections efficaces totales, total (n,p), (n,α), …). NE PAS plafonner une fenêtre de données à un seuil à la place. Un plafond est une
solution temporaire : le journaliser dans le readme avec le canal qui l'a imposé, et le lister pour l'évaluateur.
(DeBoer 2026-10-30, extension HE de 11B+α : la section efficace totale d'ELBE ¹⁴N+n avait été plafonnée à E_n 5,1 MeV parce que
¹⁴N(n,n₄) n'avait pas de paire. Cela a laissé le sommet de 1,2 MeV de l'ajustement sans sa contrainte la plus forte, un total à norme fixe.
Exemple de constructeur :
11B+a/10-27-26_HE_claude/model/add_pairs_he.py, 7 paires n4 a3 d0 p1 n5 n6 p2.) - Niveau par niveau, à partir de la basse énergie. Polir un niveau à la fois (son énergie et ses largeurs, plus les normes libres) contre les données autour de lui, et regarder à quel point il peut correspondre.
- Ensuite ajouter des niveaux là où une résonance est clairement présente et qu'aucun niveau connu ne lui correspond en énergie et/ou largeur.
- Alors seulement un véritable ajustement (global) :
initd'une campagne sur le résultat.
Pourquoi (10B+α étape 1, 2025-10-25) : les polissages globaux à partir d'un mauvais départ (χ²/N ≈ 200, niveaux du 2-27-25 avec de nouveaux
canaux proches de zéro) ont échangé la force manquante contre des normalisations. Avec chaque norme libre, presque toutes sont tombées à
0,38-0,68 en 150 évaluations. Bornées à 0,7-1,5, huit étaient sur une borne et l'ajustement a stagné à χ²/N 12,7. Avec
χ² ~ 10⁵, les a priori de normalisation de quelques pour cent ne coûtent rien (10B+a/10-25-26_rmfit_stage1, _stage1b).
Ensuite, dans un nouveau répertoire de campagne daté (<reaction>/<M>-<D>-<YY>_rmfit_campaign/) :
mkdir <dir> && cp <base>.azr <dir>/ && cp -r data <dir>/data && mkdir <dir>/output
python3 -m rmfit.cli <dir> init --base <dir>/<base>.azr [--sav <base>.sav] --nshards 24 \
--flags '{"use_long_wavelength": false, "ignore_externals": true, "use_brune": true}' \
--policy '{"theta2_cap": 1.0, "bg_theta2_cap": 3.0, "bg_energy": <Ex above the data>, "e_window": 0.3, "norm_range": [0.2, 5.0]}' \
--search '{"top_k": 6, "pairs_per_group": 4, "stage1_nfev": 30, "stage2_nfev": 60, "union_nfev": 100}'
--flags est passé directement à pyazr.azure2(), donc la paramétrisation est choisie là :
"use_brune": true (Brune, le défaut de l'archive), "use_park": true (Park : le vecteur d'ajustement
contient les amplitudes de largeur réduite observées, Gamma = 2 P gamma^2 ; voir la compétence AZURE2 sur
« Paramétrisation de Park »), "use_brune": false (Lane-Thomas standard). Une base par
campagne — les vecteurs du livre de comptes, les instantanés .npz et les compagnons .sav sont tous dedans.
--objective JSON optionnel (campaign.json objective, deBoer et al. 2017 Sec. sur le fitting) :
{"dataset_weight": "reduced", "dataset_key": "file", "loss": "sivia"}.
dataset_weight: reduced est la méthode du chi-carré réduit (le chi-carré de chaque jeu de données
est divisé par son nombre de points, remis à l'échelle par N/K pour que l'objectif garde une magnitude de
chi-carré ; les lignes de pénalité sont non pondérées) — cela empêche un grand jeu avec de petites erreurs de
dominer ; loss: sivia remplace le chi-carré par la PDF plus large de Sivia,
-2 log[(1 - e^{-R^2/2})/R^2] - 2 log 2 (~R^2/2 pour les petits résidus, ~2 log R^2 pour les valeurs
aberrantes : les valeurs aberrantes sont sous-pondérées comme si leur barre d'erreur était gonflée). La perte de Sivia
est appliquée aux résidus standardisés par le chi2/N de référence de chaque jeu de données (loss_scale: dataset, les échelles sont calculées par init à partir de la ligne de base simple et stockées dans campaign.json) :
sans cela, sur un modèle avec chi2/N ~ 20-300, chaque point se trouve dans la queue logarithmique,
les données perdent ~10x leur poids face aux pénalités quadratiques de normalisation et l'ajustement dérive
(13C+a, 2026-09-08 : chi2 simple 701k -> 1 190k en un polissage, MANA abandonné, les normes
se sont figées au nominal). Les deux méthodes sont « statistiquement incorrectes mais utiles lorsque les barres d'erreur sont
sous-estimées ». L'objectif
est une propriété d'une campagne : les candidats vérifiés sous différents objectifs ne sont pas
comparables, donc un changement d'objectif signifie un nouveau répertoire de campagne (l'amorcer avec
l'export précédent). REPORT.md et segment_scores gardent le chi-carré simple par jeu de données quel que soit
l'objectif. Vérifié dans rmfit/tests/test_objective_13n.py.
init vérifie la ligne de base (une évaluation du modèle complet), enregistre la structure,
écrit STATUS.md et le script de tâche run_crc_rmfit (nœud long 24 cœurs, envoie un mail à
[email protected], -m abe). Réexécuter init conserve une configuration existante à moins que de nouvelles valeurs
ne soient données. Signification de la politique : theta2_cap est la limite de Wigner pour les niveaux physiques,
bg_theta2_cap pour les pôles de fond (niveaux avec Ex >= bg_energy, dont les énergies restent
fixes) — 3.0 est une estimation de départ, pas une valeur physique par défaut : vérifier où se situent
les amplitudes de fond de la ligne de base (l'audit de cap de init) avant de lui faire confiance ;
12C+alpha élastique avait besoin de ~100 (voir l'entrée de journal du 2026-09-30) ; e_window est la liberté d'énergie d'un niveau par polissage (MeV) ; norm_range
multiplie la normalisation nominale. narrow_window (optionnel, par exemple
{"gamma_kev": 5, "factor": 3, "min_kev": 1}) maintient un niveau dont les largeurs partielles libres totalisent
moins de gamma_kev dans ±max(min_kev, factor·Γ) de son énergie actuelle au lieu de
±e_window, afin qu'un polissage ne puisse pas éloigner un niveau étroit de son pic (le 13C+a 9/2+
a dérivé de 180 keV en un polissage de resserrement). Nombre de shards = cœurs ; le sharding est exact
(le chi-carré est additif sur les segments), donc plus de shards n'achètent que de la vitesse.
Vérification du coût sur un nouveau modèle (campaign.log après init) : un objectif shardé devrait
être de quelques secondes et un Jacobien de dizaines de secondes. Si un segment avec convolution
cible-effet domine (comme Cierjacks l'a fait sur 13C+a), rmfit le replie lui-même (model.ConvSpec) ;
les segments « somme » composites sont autorisés comme pièces de convolution.
2b. Spec, audit, décisions (2026-11-07 ; Phase 1 de R-matrix/rmfit/DESIGN.md)
Avant init, écrire la spec de la campagne (spec.json, modèle rmfit/templates/SPEC_TEMPLATE.json, schéma dans
rmfit/spec.py) : chaque jeu de données avec son rôle (anchor = norme fixée au nominal ; free avec prior_sigma ; excluded
avec la raison ; screen), la fenêtre d'ajustement, le bloc gelé, les paires qui doivent exister, les bornes / l'objectif / la recherche, ce que le
pilote peut faire de lui-même (pre_authorized : adopt_level true / false / règles, flip_adopt, dataset_off_adopt, ...),
les règles d'arrêt et les décisions permanentes avec leur propriétaire et leur date. Ensuite :
python3 -m rmfit.audit <base>.azr --out AUDIT.md # pré-vol, sans session moteur : zéro largeurs libres, fusions à 1 keV,
# normes nominales != 1, normError en TANT QUE FRACTION, colonnes FLAG comme
# erreurs, erreurs minuscules, doublons, pics, seuils, <targetInt>
# sur les segments de test, noms de fichiers .sav hérités, chemin de sortie, broche du moteur
python3 -m rmfit.cli <dir> init --base ... --spec spec.json # flags/policy/search/objective/nshards de la spec sauf si
# donnés ; les désaccords HAUTS avec le .azr refusent l'init
python3 -m rmfit.cli <dir> spec check # à tout moment : SPEC_CHECK.md ; spec render -> SPEC.md ; audit -> AUDIT.md
Un résultat d'audit HAUT est corrigé (ou enregistré comme accepté dans les décisions permanentes de la spec) avant la ligne de base. Pendant
la campagne, tout ce que la spec ne répond pas va à DECISIONS.md (rmfit decisions liste, rmfit decide D-003 "adopt" --by DeBoer répond) ; un ajout de niveau dont accept n'est pas pré-autorisé par la spec est enregistré comme accept-pending
avec une décision en file d'attente et n'est PAS adopté (l'ancienne adoption automatique ne s'applique qu'aux campagnes sans spec).
Projets de spec à copier : 10B+a/11-6-26_s5_highE/spec_draft.json, 13C+a/11-06-26_merged_regionD/spec_draft.json.
Les trois examens en temps inactif de la section 3b sont générés, pas écrits à la main, depuis le 2026-11-07 :
python3 -m rmfit.cli <dir> audit --kind all # audits/{input,data,literature}/PROMPT.md + FINDINGS.md vide
# donner chaque PROMPT.md à un sous-agent d'arrière-plan (outil Agent, un modèle moins cher, au plus trois à la fois) ; il écrit FINDINGS.md
python3 -m rmfit.cli <dir> audit --findings # lignes non vérifiées (candidates) de chaque FINDINGS.md
python3 -m rmfit.cli <dir> audit --status data 1 confirmed --note "vérification ligne 4007
…
[suite tronquée pour la traduction]