Plan V&V 0.2.2 alpha : assemblage, scaling et couverture¶
Objet et statut¶
Ce document prépare la campagne de vérification et validation du backend numérique de QF Solver. Il ne constitue ni une qualification, ni une décision de maturité, ni une autorisation de publication. Aucun changement de formulation d'élément n'est recherché dans ce lot.
Les objectifs sont les suivants :
- mesurer séparément le coût de l'assemblage, de la réduction, de la résolution et du post-traitement ;
- supprimer les copies sparse évitables sans changer les matrices produites ;
- rendre le choix de méthode et de backend explicable et surchargeable ;
- vérifier la non-régression des résultats statiques, modaux, transitoires et harmoniques ;
- faire progresser la couverture branchée globale de
86 %vers90 %par ajouts de tests utiles, et non par diminution des exigences. Ce gate est maintenant atteint à90,195 %après la campagne de contrats et la régression complète.
Cartographie des chemins¶
| Analyse | Assemblage | Réduction/opérateur | Résolution | Mesures à conserver |
|---|---|---|---|---|
| Statique linéaire | GlobalAssembler ou ChunkedScipyAssembler |
contraintes/MPC puis système réduit | direct, CG, MINRES, GMRES | temps par phase, NNZ, résidus, réactions |
| Modal | GlobalAssembler K/M |
réduction et condensation éventuelle | eigsh/lobpcg, SLEPc optionnel |
fréquences, résidus propres, orthogonalité M |
| Newmark | K/M puis matrice effective | mêmes contraintes à chaque pas | factorisation réutilisée ou itératif | pas, énergie, résidus, coût factorisation |
| Harmonique | K, M, C et opérateur complexe | réduction par fréquence | résolution sparse complexe SciPy | fréquences, résidus, coût par fréquence |
| TET4 grand modèle | assembleur chunké ou PETSc AIJ/BAIJ | Dirichlet distribué | CG/KSP ou matrix-free | NNZ, chunks, RAM, temps MPI, convergence |
Les formulations d'éléments restent en dehors de la couche de sélection. Un élément fournit ses matrices locales ; l'assemblage et la résolution ne doivent pas connaître sa démonstration particulière.
Architecture cible par couches¶
Formulation élémentaire
-> AssemblyPlan / kernel local
-> SparseAssemblyAccumulator ou matrice native PETSc
-> ConstraintReduction / opérateur sparse
-> LinearSolverBackend ou EigenSolverBackend
-> diagnostics de convergence et ressources
-> résultat, audit et preuve V&V
Couche d'assemblage¶
La première optimisation appliquée est l'accumulation CSR pairwise commune,
dans solveur.core.sparse_accumulator. Elle évite l'addition répétée d'un
nouveau chunk à une matrice globale déjà grande. Le chemin standard et le
chemin SciPy grand modèle utilisent maintenant cette même primitive.
La tranche realisee introduit AssemblyPlan, construit une fois par couple
(model, dofs). Il conserve les éléments suivants :
- référence de la spécification d'élément ;
- connectivité et indices DDL globaux ;
- nombre d'entrées locales ;
- groupe de matériau et possibilité de cache ;
- nombre d'entrées locales, avec le temps de préparation du plan.
Le plan porte aussi une empreinte SHA-256 déterministe du modèle, des DDL et
du chunk_size effectif. Elle couvre notamment les coordonnées, éléments,
matériaux, blocages, ressorts, masses, MPC/RBE, contacts et paramètres du
modèle sérialisables. La vérification de réutilisation exige l'identité de
l'objet déjà préparé, l'égalité des dimensions et DDL, puis l'égalité de cette
empreinte et du découpage demandé. Une modification du modèle ou du chunk
invalide donc explicitement le plan ; aucune matrice locale n'est conservée
par cette empreinte.
La préparation réduit les appels répétés à ElementRegistry, aux conversions
de listes de DDL et aux recherches de coordonnées pour K/M. Elle ne met pas en
cache une matrice locale lorsque l'orientation, la géométrie ou l'état matériel
la rendent variable. Les analyses modal, Newmark et harmonique reutilisent
egalement ce plan dans une paire K/M. Dans chaque chunk, le motif temporaire de
DDL (rows, cols) est partage entre les deux matrices, alors que les valeurs
de K et M restent separees. Aucun motif global n'est conserve afin de ne pas
transformer la mutualisation en nouvelle pression memoire. Les diagnostics
paired_assembly=true et shared_chunk_pattern=true rendent cette propriete
verifiable. Le benchmark K/M confirme un gain de temps local, mais aucune
economie memoire n'est revendiquee : la paire conserve deux flux de valeurs et
son estimation temporaire reste plus conservative.
Couche de résolution¶
solveur.core.solver_backend choisit le backend ;
solveur.core.linear_policy choisit la méthode ;
solveur.core.linear_methods exécute la méthode et produit les diagnostics.
La règle de base est :
- petit système : direct sparse si l'estimation mémoire l'autorise ;
- réel SPD démontré ou déclaré explicitement : CG ;
- réel symétrique sans preuve SPD : MINRES ;
- non symétrique : GMRES ;
- modal généralisé :
eigsh/lobpcg, ou SLEPc si explicitement sélectionné et disponible ; - backend
auto: SciPy par défaut, PETSc seulement avec un signal de taille ou une préférence explicite et une trace de sélection.
Chaque exécution doit conserver le backend demandé, le backend utilisé, la
méthode, le nombre d'itérations, les résidus initial/final/relatif, la
tolérance et la raison de terminaison. Une non-convergence doit être une
erreur explicite ou un état not_converged, jamais un résultat silencieux.
Diagnostic causal de l'assemblage¶
Les hypothèses à mesurer, dans cet ordre, sont :
- kernel local : temps passé dans la formulation élémentaire ;
- construction du motif : répétitions
repeat/tile, conversion des DDL ; - conversion sparse : coût
COO -> CSR, tri et fusion des doublons ; - fusion des chunks : nombre de fusions et mémoire temporaire ;
- discrètes et contraintes : ajout des ressorts/masses puis réduction ;
- réutilisation : matrice effective Newmark et factorisation constante.
Les métriques attendues sont assembly_seconds, les temps par phase, le
nombre de chunks, le pic d'entrées d'un chunk, nnz final, la mémoire sparse
estimée et, lorsque disponible, un pic RSS séparé du temps de résolution.
Le chemin standard accepte aussi assembly_memory_budget_mb et
enforce_assembly_memory_budget=true : l'estimation conservatrice du chunk
est faite avant allocation, puis l'exécution avertit ou refuse explicitement
selon la politique choisie. Ce garde-fou ne change pas la valeur par défaut.
Une optimisation ne sera acceptée que si elle conserve la matrice à une
tolérance numérique documentée et améliore au moins une mesure sans dégrader
les autres au-delà de la marge définie par la campagne.
Idees d'architecture pour la suite¶
Les options suivantes sont compatibles entre elles et peuvent etre introduites par petits lots :
- conserver
AssemblyPlancomme contrat immutable de connectivite et de DDL, puis ajouter un cache local de noyaux uniquement pour les elements dont la matrice locale est independante de l'etat et de l'orientation ; - exposer une interface
SparseOperatorcommune aux matrices CSR et auxLinearOperator, afin que modal, Newmark et les grands modeles puissent eviter une materialisation dense sans dupliquer la politique de resolution ; - separer explicitement
AssemblyBackend,LinearSolverBackendetEigenSolverBackend, avec un objet de diagnostics commun et un adaptateur PETSc/SLEPc optionnel ; - remplacer les copies repetitives de DDL par des index locaux compacts dans chaque chunk, tout en conservant des indices globaux int64 au point de fusion. Cette option doit etre mesuree sur la memoire et la vitesse avant d'etre activee ;
- ajouter un plan de reutilisation de factorisation indexe par une empreinte des operateurs, de l'amortissement et du pas de temps. Une invalidation stricte doit interdire toute reutilisation apres changement de K, M, C ou des contraintes ;
- produire un budget de ressources avant allocation, puis choisir entre
direct, iteratif ou PETSc selon
nnz, memoire estimee, symetrie et taille. La decision doit rester surchargeable par l'utilisateur.
Campagne de non-régression¶
Niveau A : identité d'assemblage¶
Pour chaque cas représentatif, comparer l'ancienne référence archivée et le nouveau chemin sur :
- dimensions, symétrie et nombre de non-zéros ;
- norme de la différence
K_new - K_refetM_new - M_ref; - équilibre des charges et réactions ;
- patch test et énergie linéaire ;
- empreinte numérique lorsque le format est stable.
Le test ne doit pas comparer des arrondis textuels ; il doit comparer des grandeurs numériques et accepter uniquement une tolérance explicitement justifiée.
Niveau B : analyses¶
La suite rapide doit couvrir au minimum un cas TET4, un TET10, un MITC3, un MITC4, un BEAM2 et une entité discrète pour :
- statique linéaire ;
- modal et résidus propres ;
- Newmark avec et sans amortissement lorsque la branche est couverte ;
- harmonique sur un petit balayage fréquentiel.
Les campagnes V&V existantes restent séparées des tests rapides. Elles sont relancées après une modification du chemin partagé, pas après un changement isolé de documentation.
Niveau C : scaling¶
Le script manuel scripts/benchmark_sparse_scaling.py mesure le backend de
résolution sur environ 1k, 10k et 100k DDL. Le script
scripts/benchmark_assembly_scaling.py mesure en plus l'assemblage TET4
chunké aux mêmes ordres de grandeur. Le cas 1M reste manuel et conditionné
par la mémoire disponible.
Le premier point de comparaison instrumenté d'assemblage est archivé dans
qualification/benchmarks/qf_solver_0_2_2_assembly_scaling_reference.json.
Le chemin SciPy grand modèle réutilise maintenant le cache matériau entre les
chunks et sépare dans assembly_phase_seconds le kernel élémentaire de la
conversion COO/CSR. Sur 107811 DDL, la médiane de trois répétitions donne
environ 0,860 s de kernel, 0,714 s de conversion COO/CSR, 0,093 s de
fusion et 0,024 s de finalisation. Le champ chunk_build reste la somme du
kernel local et de la conversion sparse, et ne doit pas être interprété comme
le seul coût de la formulation élémentaire.
Le runner manuel scripts/benchmark_assembly_chunk_sweep.py compare aussi les
tailles 1024, 2048, 4096, 8192 et 16384 sur le même cas 100000 DDL.
La médiane locale est respectivement de 1,937 s, 1,775 s, 1,706 s,
1,689 s et 1,673 s. Le meilleur temps ne gagne qu'environ 2 % par rapport
à 4096, alors que le pic de triplets passe de 143055 à 448551. La valeur
par défaut ne change donc pas : le sweep prouve un compromis temps/mémoire,
pas une justification suffisante pour augmenter globalement le chunk.
Cette preuve est archivée dans
qualification/benchmarks/qf_solver_0_2_2_assembly_chunk_sweep_reference.json.
Le candidat de construction CSR directe est comparé au baseline COO/CSR dans
qualification/benchmarks/qf_solver_0_2_2_assembly_scaling_comparison.json.
Les NNZ restent identiques aux trois tailles et la différence numérique est
nulle. Le changement de temps médian global reste toutefois dans le bruit de
la campagne (+2,15 % sur cet échantillon), donc le candidat ne constitue pas
encore une preuve de gain de performance ; il est conservé pour sa réduction
d'un objet intermédiaire, avec une nouvelle campagne nécessaire avant toute
promotion de performance.
Chaque ligne de benchmark doit contenir : DDL, éléments, NNZ, chunk size, temps d'assemblage, temps de résolution, méthode, backend, itérations, résidu final et estimations mémoire. Les benchmarks lourds ne doivent pas devenir des tests CI obligatoires.
La comparaison dédiée du motif K/M est préparée par
scripts/benchmark_standard_km_pair.py. Elle rejoue le même modèle avec
l'assemblage séparé puis groupé, compare K et M par norme relative et conserve
les phases, NNZ, ratio de temps et estimations mémoire. Son résultat attendu
est archive dans
qualification/benchmarks/qf_solver_0_2_2_standard_km_pair_reference.json.
Le runner scripts/compare_large_backends.py exécute la même observable
statique sur SciPy, matrix-free et PETSc lorsqu'il est disponible. Sur le bloc
reproductible de 24 DDL, SciPy et matrix-free passent et donnent un écart de
déplacement de 5,82e-16; l'exécution locale PETSc est enregistrée SKIP car
petsc4py n'est pas installé. Cette preuve locale ne ferme donc pas la
comparaison PETSc/BAIJ : une campagne HPC reste nécessaire.
La campagne locale apres compactage des indices a donne les ratios
groupe/separe de 0,916 a 1k DDL, 0,888 a 10k DDL et 0,890 a 100k DDL,
soit environ 8,4 % a 11,2 % de temps total economise sur cet environnement.
Les NNZ et les differences
relatives de K et M sont identiques a zero aux trois tailles. L'estimation de
memoire temporaire du chemin groupe est volontairement plus conservative ;
aucun gain memoire n'est revendique sur cette preuve.
Leviers d'assemblage à tester dans l'ordre¶
Les mesures 100 k DDL imposent l'ordre suivant :
- Réduire le coût du kernel élémentaire. Tester la vectorisation par famille d'éléments et matériau, puis une parallélisation contrôlée du kernel. La parallélisation ne doit pas modifier l'ordre des réductions sans tolérance numérique documentée.
- Réutiliser uniquement les motifs invariants. Le motif DDL peut être réutilisé entre K et M et entre plusieurs résolutions d'un même modèle, mais les valeurs locales doivent rester recalculables pour une orientation, une géométrie ou un état matériau variable. Toute empreinte de cache doit inclure le modèle, les DDL, les contraintes et la configuration de chunk.
- Choisir le chunk par budget de ressources. Un autotuning borné peut
comparer quelques tailles de chunk à partir du NNZ et de la mémoire
estimée. Le runner
scripts/benchmark_assembly_chunk_sweep.pyproduit maintenant une recommandation advisory avec--memory-budget-mb, bloque si aucune taille mesurée ne respecte le budget et ne modifie pas la valeur par défaut de l'assembleur. Sur la référence 100k et un budget de4 000 000octets, la recommandation est4096avec une estimation de3 433 320octets. Il ne doit jamais allouer un second triplet global pour essayer une taille. - Basculer vers matrix-free ou PETSc/BAIJ lorsque le modèle le justifie. Cette voie doit être comparée au chemin SciPy sur la même observable mécanique ; elle ne peut pas devenir le défaut sans preuve de précision, de mémoire et de reproductibilité.
Le passage CSR direct a été mesuré mais ne montre pas encore de gain global robuste. Le premier correctif de performance à privilégier est donc désormais le kernel élémentaire, pas une augmentation aveugle du nombre de chunks. Le critère de promotion sera un gain médian documenté, une mémoire maximale non dégradée et une non-régression stricte des matrices, déplacements, réactions et résidus.
Une seconde configuration est maintenant archivée avec la décomposition
centrée à douze TET4 par cellule et un matériau isotrope distinct. Elle sert de
contrôle de reproductibilité du kernel sur une topologie différente ; comme le
nombre d'éléments et de NNZ change, elle ne doit pas être transformée en ratio
avant/après avec le maillage historique. La comparaison directe des champs et
réactions avec le solveur standard est maintenant fermée sur 1 677 et
18 357 DDL. Les déplacements, réactions fixées, déformations, contraintes et
von Mises restent finis, avec des écarts relatifs de l'ordre de 1e-13 pour un
seuil contractuel 1e-7. L'artefact reproductible est
qualification/benchmarks/qf_solver_0_2_2_large_standard_field_comparison.json.
Cette preuve vérifie la cohérence mécanique entre chemins d'assemblage ; elle
ne mesure ni le gain de temps ni la capacité à exécuter plusieurs millions de
DDL.
La reutilisation de la matrice effective Newmark est mesuree dans
qualification/benchmarks/qf_solver_0_2_2_newmark_factorization_reference.json.
Sur 1 029 et 10 125 DDL, une factorisation est reutilisee pour huit
resolutions, avec des residus dynamiques maximum de 1,28e-11 et 2,21e-11.
Le seuil de 100k DDL reste une campagne manuelle, car le cout memoire d'une
LU directe doit etre mesure avant toute generalisation.
Politique de couverture à 80 %¶
La couverture globale est un indicateur de garde, pas une preuve physique.
La régression pertinente du 23 août 2026 a produit 1336 passed,
107 deselected, avec 88,67 % de couverture branchée. Le gate CI retenu est
80 %; 90 % n'est pas un objectif de release. La progression reste
découpée fonctionnellement pour suivre les zones qui doivent encore être
renforcées :
- porter le backend commun, la politique et l'accumulateur à au moins
95 %de couverture branchée ; - porter l'assemblage standard et le chemin grand modèle à au moins
90 %avec cas d'erreur de mémoire, chunk vide, doublons, discrètes et contraintes ; - porter l'API publique, les résultats et les diagnostics à au moins
90 %; - traiter les branches réelles actuellement faibles dans modal, IO schema, lecture Gmsh, large solver et géométrie MITC4 ;
- conserver le seuil global CI à
80 %avec--cov-branch; tout relèvement futur doit être une décision explicite de release ; - interdire les exclusions de couverture nouvelles sans justification dans le dossier V&V.
Pour les campagnes à partir de 2 000 000 DDL, le gate
MULTI-MILLION-GATE impose un backend scalable (petsc ou matrix_free) et
demande un budget mémoire explicite. Sans budget fourni, le rapport passe en
WARNING et interdit de considérer la campagne comme automatiquement prête.
Avec un budget inférieur à l'estimation PETSc indicative, le gate est en
FAIL. Ce contrôle est une readiness avant calcul : il ne lance pas le modèle
et reste hors CI rapide.
Les tests de performance ne doivent pas gonfler artificiellement le taux : ils servent à mesurer, les tests unitaires servent à prouver les contrats et les tests V&V servent à prouver les grandeurs mécaniques.
Etat de fermeture au 2026-08-23¶
| Gate | Etat | Preuve ou limite |
|---|---|---|
| Couverture globale branchee >= 80 % | PASS |
1336 passed, 88,67 %, --cov-branch |
| Non-regression statique/modal/Newmark/harmonique | PASS |
campagne unit/integration : 1327 passed |
| Matrices, reactions et champs | PASS |
comparaisons sparse/large et tests de contrats archivees |
| Diagnostics de convergence et garde memoire | PASS |
tests cibles et artefacts de readiness |
| Scaling 1k/10k/100k | PASS |
benchmarks reproductibles et mesures assembleur archivees |
| PETSc multi-rang et multi-million statique TET4 | PASS_BOUNDED |
quatre cas Docker réels 2M/4M x 2/4 rangs, CG+GAMG/BAIJ, efficacité forte minimale 0,615 |
| SLEPc/modal et dynamique grand modèle | PASS_BOUNDED |
modal SLEPc jusqu'à 107 811 DDL ; Newmark PETSc/GAMG à 2 044 416 DDL ; tentative modale 2M arrêtée par la limite de ressources |
| Partitionnement graphe | PASS_BOUNDED |
PT-Scotch à 2M DDL, efficacité forte 0,621 |
| Seconde configuration matérielle | OPEN_NON_BLOCKING |
la campagne reste limitée à une machine et à une image Docker épinglée |
| Audit documentaire/public | PASS |
42 tests documentation, 6 skips explicites, audits publics sans finding |
La tranche logicielle, la cible de couverture et le dossier backend borne sont
donc documentes. Le statut global du dossier reste draft : la preuve Docker
ne vaut pas validation generale d'un modal a plusieurs millions, d'une autre
machine ou d'une promotion de maturite.
Gates de sortie de l'alpha backend¶
Le chantier restera draft tant que tous les points suivants ne sont pas
documentés :
- baseline reproductible avant/après ;
- non-régression statique, modale, Newmark et harmonique ;
- non-régression de la matrice assemblée et des réactions ;
- diagnostics de non-convergence testés ;
- benchmark 1k/10k/100k exécuté ;
- readiness multi-million vérifiée à partir de
2 000 000DDL avec backend et budget mémoire tracés ; - assemblage grand modèle comparé avec l'ancien chemin ;
- couverture globale mesurée et gate CI à 80 % respecté ;
- limites PETSc/SLEPc, SciPy, matrix-free et conversions denses documentées ;
- aucun chemin de publication, tag ou décision Owner déduit automatiquement.
Décisions d'architecture proposées¶
La recommandation est d'avancer par petits lots :
- accumulateur sparse commun et métriques, déjà appliqués ;
AssemblyPlanet cache de motif, avec test d'identité ;- réutilisation de motif K/M/matrice effective ;
- opérateurs modaux sans inversion dense et réutilisation de factorisation ;
- backend PETSc/SLEPc optionnel après benchmark, jamais par obligation ;
- couverture ciblée et relèvement progressif du seuil ;
- revue technique locale, puis seulement une revue Owner de release.
Cette séquence limite le risque : l'assemblage peut être accéléré sans modifier la formulation, et chaque étape est réversible tant que les preuves numériques ne sont pas comparées.