Retour au README
Un corpus est une liste de numéros de DPE réels, publiés par l'ADEME. Les tests de corpus rejouent ces DPE dans le moteur Open3CL et comparent les sorties calculées à celles du DPE d'origine. C'est le principal indicateur de qualité du projet : il mesure, sur des dizaines de milliers de cas réels, l'écart entre Open3CL et les logiciels certifiés.
- Principe
- Ce qui est contrôlé
- Les corpus disponibles
- Lancer un corpus
- Variables d'environnement
- Quotas de l'API ADEME
- Les rapports produits
- Le rapport interactif
- Mise à jour automatique du README
- Investiguer un écart
flowchart TD
A["📋 corpus.csv<br/><sub>10 000 numéros de DPE</sub>"] --> B{"DPE en cache<br/>local ?"}
B -- oui --> D["⚙️ calcul_3cl"]
B -- non --> C["⬇️ API ADEME"] --> D
D --> E["📐 Comparaison des 22 grandeurs<br/><sub>écart relatif vs DPE d'origine</sub>"]
E --> F{"Tous les contrôles<br/>bloquants ≤ 5 % ?"}
F -- oui --> G["✅ DPE conforme"]
F -- non --> H["❌ DPE au-dessus du seuil"]
G --> I["📊 Rapports JSON · CSV · HTML"]
H --> I
Le calcul est réparti sur plusieurs worker threads (voir MAX_WORKER_THREADS), avec une barre de progression pour
le téléchargement et une pour l'analyse.
Le seuil de tolérance est de 5 % par défaut. Un DPE n'est compté comme conforme que si tous les contrôles bloquants restent sous ce seuil.
Ce sont eux qui déterminent la conformité d'un DPE.
| Propriété | Grandeur |
|---|---|
logement.sortie.ef_conso.conso_ecs |
Consommation d'eau chaude sanitaire (EF) |
logement.sortie.ef_conso.conso_ch |
Consommation de chauffage (EF) |
logement.sortie.ep_conso.ep_conso_5_usages (ou _m2) |
Consommation 5 usages en énergie primaire |
logement.sortie.emission_ges.emission_ges_5_usages (ou _m2) |
Émissions de gaz à effet de serre |
Ils ne bloquent pas, mais permettent de localiser l'origine d'un écart : si conso_ch dérive, c'est souvent
besoin_ch, lui-même souvent une déperdition, qui est en cause.
| Famille | Propriétés contrôlées |
|---|---|
| Enveloppe | deperdition_mur, deperdition_baie_vitree, deperdition_plancher_bas, deperdition_plancher_haut, deperdition_porte, deperdition_pont_thermique, deperdition_renouvellement_air, hperm |
| Apports & besoins | surface_sud_equivalente, besoin_ecs, besoin_ch |
| Auxiliaires | conso_auxiliaire_distribution_ch, conso_auxiliaire_distribution_ecs, conso_auxiliaire_generation_ch, conso_auxiliaire_generation_ecs, conso_auxiliaire_ventilation |
Tip
Pour remonter à la cause d'un écart, lisez les contrôles de haut en bas de la chaîne de calcul : une déperdition fausse fait dériver le besoin, qui fait dériver la consommation, qui fait dériver l'étiquette.
Neuf corpus, 10 000 DPE chacun, situés dans test/corpus/files.
| Fichier | Contenu |
|---|---|
corpus_dpe.csv |
Corpus généraliste, tous types de DPE confondus |
dpe_logement_individuel_2025.csv |
DPE de logements individuels réalisés en 2025 |
dpe_maison_individuelle_2025.csv |
DPE de maisons individuelles réalisés en 2025 |
dpe_appartement_individuel_chauffage_individuel_2025.csv |
Appartements avec chauffage individuel, 2025 |
dpe_appartement_individuel_chauffage_collectif_2025.csv |
Appartements avec chauffage collectif, 2025 |
dpe_immeuble_chauffage_individuel.csv |
DPE à l'immeuble, logements à chauffage individuel |
dpe_immeuble_chauffage_collectif.csv |
DPE à l'immeuble, logements à chauffage collectif |
dpe_immeuble_chauffage_mixte.csv |
DPE à l'immeuble, logements à chauffage mixte |
dpe_individuel_a_partir_dpe_immeuble_2026.csv |
Appartements générés à partir d'un DPE immeuble, 2026 |
# Tous les corpus + mise à jour automatique des résultats dans le README
npm run test:corpus:all
# Le corpus par défaut (test/corpus/files/corpus_dpe.csv)
npm run test:corpus
# Un corpus précis
npm run test:corpus -- corpus-file-path=corpus.csv
# Un dossier de cache des DPE
npm run test:corpus -- dpes-folder-path=/home/user/dpes
# Un seul DPE, pour investiguer
npm run test:corpus -- dpes-code=2592E1233185X
# Tous les corpus + alimentation de la base de résultats
npm run test:corpus:all+database| Argument | Description |
|---|---|
corpus-file-path=<path> |
Chemin relatif du fichier de corpus. Défaut : test/corpus/files/corpus_dpe.csv |
dpes-folder-path=<path> |
Dossier de cache des DPE. Un DPE déjà présent n'est pas retéléchargé. Peut être remplacé par DPE_FOLDER_PATH |
dpes-code=<code> |
N'exécute qu'un seul DPE, identifié par son numéro |
no-dpe-pos=<n> |
Position de la colonne du numéro de DPE dans le CSV d'entrée (défaut : 0) |
| Nom | Description |
|---|---|
DPE_FOLDER_PATH |
Obligatoire — dossier de stockage des fichiers DPE (ou dpes-folder-path en ligne de commande) |
ADEME_API_CLIENT_ID |
Client id de l'API ADEME |
ADEME_API_CLIENT_SECRET |
Client secret de l'API ADEME |
MAX_WORKER_THREADS |
Nombre maximum de threads. Défaut : os.availableParallelism * 1.5 |
WORKER_THREADS_CHUNKS |
Nombre de DPE analysés par thread. Défaut : 200 |
DOWNLOAD_DPE_WAIT |
Temps d'attente entre deux téléchargements, en ms. Défaut : 1000 |
SCW_ACCESS_KEY |
Client id de l'API Scaleway |
SCW_SECRET_KEY |
Client secret de l'API Scaleway |
S3_REGION |
Région du bucket S3 de stockage des DPE |
S3_ENDPOINT |
Endpoint du bucket S3 de stockage des DPE |
| Fenêtre | Limite |
|---|---|
| Par seconde | 100 requêtes |
| Par minute | 1 000 requêtes |
| Par jour | 10 000 requêtes |
Lorsqu'un corpus doit télécharger beaucoup de DPE absents du cache local, la configuration la plus stable est :
export MAX_WORKER_THREADS=10
export API_ADEME_DOWNLOAD_WAIT=1000
export WORKER_THREADS_CHUNKS=300Tout est écrit dans dist/reports/corpus/<corpus>.csv/, suffixé par la branche git courante — ce qui permet de
comparer deux branches dans le rapport interactif.
Le nom de branche est normalisé pour tenir dans un nom de fichier : tout ce qui n'est ni lettre, ni chiffre, ni .,
_ ou - devient un tiret. fix/issue-205-rg-qp0-pcs donne donc corpus_global_report_fix-issue-205-rg-qp0-pcs.json
— sans quoi le / désignerait un dossier inexistant et l'écriture du rapport échouerait.
| Fichier | Contenu |
|---|---|
corpus_global_report_<branche>.json |
Synthèse : nombre de DPE, ratio de réussite, détail par contrôle |
corpus_detailed_report_<branche>.csv |
Une ligne par DPE, avec pour chaque grandeur _input, _output et _diff |
corpus_dpe_list_above_threshold_<branche>.json |
Liste des DPE au-dessus du seuil |
corpus_dpe_list_above_threshold_diff_main_<branche>.json |
DPE au-dessus du seuil sur cette branche mais pas sur main |
../corpus_list_main.json |
Index des corpus et des branches, consommé par le rapport HTML |
Structure du rapport global
{
"threshold": "5%",
"totalDpesInFile": 10000,
"nbValidDpe": 9980,
"nbInvalidDpeVersion": 20,
"nbExcludedDpe": 0,
"nbAllChecksBelowThreshold": 4591,
"successRatio": "45.91 %",
"dpeRunFailed": ["2262E0721086N"],
"checks": {
"conso_ch": { "nbBelowThreshold": 6486, "mandatory": true, "successRatio": "64.86 %" }
}
}npm run reports:previewOuvre dist/reports/corpus/index.html dans le navigateur. Le tableau de bord affiche :
- les indicateurs clés : DPE analysés, ratio de réussite, nombre de DPE au-dessus du seuil ;
- le ratio par contrôle, trié par sévérité, avec distinction des contrôles bloquants ;
- la liste des DPE au-dessus du seuil, avec au survol le détail des propriétés en écart (valeur attendue, valeur calculée, écart en %) ;
- la comparaison entre deux branches : DPE nouvellement en échec (
NEW), corrigés (FIX), ou communs.
Le rapport est également publié à chaque build : https://open3cl.github.io/engine/reports/corpus.
npm run test:corpus:all enchaîne automatiquement sur :
npm run reports:readmeCe script (scripts/generate_corpus_readme.js) n'écrit
que sur main : sur une branche de travail, il affiche un rappel et s'arrête sans rien modifier,
le README gardant les chiffres de main. Il s'arrête de même, sans rien écraser, quand aucun
Les rapports de la branche, eux, sont bien produits par le corpus et restent comparables à main
dans le rapport interactif. --dry-run affiche quand même le bloc, --force écrit quand même.
Sur main, il :
- détermine la version à laquelle rattacher les résultats
(
scripts/corpus_version.js,npm run reports:versionpour l'afficher) : les commits postérieurs au dernier tag sont analysés avec le même analyseur et les mêmesreleaseRulesque semantic-release (.releaserc) :- aucun commit publiable (
docs,test,ci...) : c'est la dernière release ; - sinon, c'est la prochaine :
fix/perf/refactor→ patch,feat→ minor,BREAKING CHANGEou!→ major. Elle est marquée « à publier » ;
- aucun commit publiable (
- lit les rapports globaux de
main; - regénère le bloc situé entre
<!-- CORPUS:START -->et<!-- CORPUS:END -->dans le README ; - reconstruit
docs/CORPUS-HISTORY.mdà partir de git : pour chaque tagvX.Y.Z, il relitcorpus_global_report_main.jsonau dernier commit demaindont le code est encore celui de cette version (juste avant le commit publiable suivant). Les rapports commités après le merge (docs: update reports) sont ainsi rattachés à la bonne release, alors que le tag, posé au merge, embarque encore ceux de la précédente. La version « à publier » y est ajoutée à partir de l'exécution courante. Les cinq dernières versions sont détaillées, les sections rédigées à la main sont conservées ; - met à jour
docs/corpus-history.jsonet sa copiedist/reports/corpus/corpus_history.json, lue par la section « Historique des versions » du rapport interactif : une courbe par corpus, un point par version (le point « à publier » est relié en pointillé), bascule nombre / taux, survol pour comparer les corpus d'une version, légende cliquable pour en masquer.
Au merge sur main, semantic-release exécute npm run reports:stamp <version>
(scripts/stamp_corpus_release.js) avant son commit de
release : si les résultats commités sont « à publier » sous la version effectivement publiée, le
libellé est retiré du README, de l'historique et des données de la courbe, et ces fichiers entrent
dans le commit de release, donc dans le tag et sur GitHub Pages. Les chiffres ne sont pas recalculés.
Si la version prévue diffère de la version publiée (autre PR fusionnée entre-temps, type de commit
modifié au squash...), rien n'est réécrit et un avertissement est affiché dans le job : relancez
npm run test:corpus:all sur main puis commitez en docs:.
Tip
Les deux usages sont couverts : lancer les corpus sur main avant la release (résultats
« à publier », marqués publiés automatiquement au release), ou après la release (résultats
directement rattachés au dernier tag, le commit docs: ne déclenchant pas de nouvelle release).
| Option | Description |
|---|---|
--readme=<path> |
Fichier markdown à mettre à jour. Défaut : README.md |
--branch=<name> |
Branche des rapports à lire. Défaut : branche git courante |
--version=<x.y.z> |
Version affichée dans le README. Défaut : version prévue |
--date=<aaaa-mm-jj> |
Date affichée dans le README. Défaut : aujourd'hui |
--dry-run |
Affiche le bloc généré sans rien écrire |
--force |
Écrit même hors de main |
--no-history |
N'alimente ni CORPUS-HISTORY.md, ni la courbe |
-
Isoler le DPE dans le rapport interactif, ou directement dans le CSV détaillé :
grep "^2263E1261479X," dist/reports/corpus/corpus_dpe.csv/corpus_detailed_report_main.csv -
Le rejouer seul, avec les logs :
npm run test:corpus -- dpes-code=2263E1261479X
-
Remonter la chaîne : la colonne
_diffdu CSV donne l'écart de chaque grandeur. Le premier écart dans l'ordre enveloppe → besoins → systèmes → consommations est presque toujours la cause des suivants. -
Figer le cas en ajoutant un test dans
test/à partir du DPE téléchargé danstest/fixtures/.
Retour au README · Guide de contribution : CONTRIBUTING.fr.md
