Skip to content

Modules

parc

the parc

parc.configs

parc.configs

Provide different grid configurations.

The module contains the following configs:

  • edf - All sites under EDF control.
  • endesa
  • enel
  • rwe
  • tepco

parc.constants

UnitTechnology

Bases: StrEnum

NUCLEAR class-attribute instance-attribute

NUCLEAR = 'nuclear'

THERMAL class-attribute instance-attribute

THERMAL = 'thermal'

HYDRO class-attribute instance-attribute

HYDRO = 'hydro'

PHOTO class-attribute instance-attribute

PHOTO = 'photo'

WIND class-attribute instance-attribute

WIND = 'wind'

OTHER class-attribute instance-attribute

OTHER = 'other'

parc.databases

parc.grids

parc.naming

parc.naming.ecs

BUILDING_CODE_LABELS module-attribute

BUILDING_CODE_LABELS = MappingProxyType({
    "A": "Bâtiments et installations de site de l'aménagement",
    "B": "Bâtiment de site de l'exploitation",
    "C": "Rejets, réfrigérants",
    "D": "Bâtiments diesel",
    "E": "Poste d'interconnexion",
    "F": "Bâtiments à fioul / bâtiment électrique non classé",
    "G": "Galeries",
    "H": "Bâtiments d'entreposage et stockage provisoire G.V. usés",
    "I": "Chauffage central (production eau chaude)",
    "J": "Plate-forme transformateurs auxiliaires",
    "K": "Bâtiment combustible",
    "L": "Bâtiments électriques et des auxiliaires de sauvegarde",
    "M": "Salle des machines",
    "N": "Bâtiments des auxiliaires nucléaires",
    "O": "Bâtiment de stockage d'eau",
    "P": "Station de pompage et de filtration",
    "Q": "Bâtiment de traitement des effluents",
    "R": "Bâtiment réacteur",
    "S": "Environnement, site",
    "T": "Plate-forme transformateurs principaux",
    "U": "Protection site, poste d'accès principal",
    "V": "Bâtiments des auxiliaires généraux",
    "W": "Bâtiments périphériques des bâtiments réacteurs et bâtiments d'exploitation",
    "X": "Aire de stockage des effluents",
    "Y": "Bâtiment déminéralisation",
    "Z": "Bâtiment de stockage de gaz",
})

FIRE_CODE_ORDER module-attribute

FIRE_CODE_ORDER = ('ZFI', 'SFI', 'ZFS', 'SFS', 'SFC')

FIRE_CRITERION_LABELS module-attribute

FIRE_CRITERION_LABELS = MappingProxyType({
    "C": "Confinement de matière radioactive",
    "I": "Limitation et indisponibilité",
    "S": "Sûreté",
})

FIRE_KIND_LABELS module-attribute

FIRE_KIND_LABELS = MappingProxyType({
    "S": "Secteur",
    "Z": "Zone",
})

JOINT_AND_CANIVEAU_COVERS module-attribute

JOINT_AND_CANIVEAU_COVERS = MappingProxyType({
    "CB": "Couvre joint ou couvre caniveau béton",
    "CM": "Couvre joint ou couvre caniveau métallique",
})

LOCAL_CODE_LABELS module-attribute

LOCAL_CODE_LABELS = MappingProxyType({
    "A": "Zone de montage",
    "C": "Zone de circulation",
    "L": "Local (pièce)",
    "M": "Zone de manutention",
})

SITE_BUILDING_SUBFUNCTIONS module-attribute

SITE_BUILDING_SUBFUNCTIONS = MappingProxyType({
    "0": "Chauffage, climatisation",
    "1": "Eclairage normal et de secours",
    "2": "Distribution électrique normale",
    "3": "Distribution électrique secourue",
    "4": "Détection incendie",
    "5": "Distribution eau incendie",
    "6": "Distribution eau potable",
})

STRUCTURE_CHARACTERISTIC_CODES module-attribute

STRUCTURE_CHARACTERISTIC_CODES = MappingProxyType({
    "B": "Béton",
    "M": "Métal",
    "V": "Vide",
})

STRUCTURE_COMPONENT_CODES module-attribute

STRUCTURE_COMPONENT_CODES = MappingProxyType({
    "A": "Ancrage à sceller (Halfen)",
    "B": "Caniveaux",
    "C": "Cadre à sceller",
    "D": "Porte",
    "E": "Fer plat",
    "F": "Fourreau / chatière",
    "G": "Garde corps",
    "K": "Carottage",
    "L": "Palier / seuil",
    "N": "Portillon",
    "P": "Platine à sceller",
    "Q": "Console (cornière, ...)",
    "R": "Réservation",
    "S": "Divers à sceller",
    "T": "Trémie",
    "U": "Puisard",
    "V": "Volée",
    "W": "Supportage",
    "Y": "Pylône",
    "Z": "Ecran thermique pour les chemins de câble",
})

STRUCTURE_ELEMENT_CODES module-attribute

STRUCTURE_ELEMENT_CODES = MappingProxyType({
    "B": "Echelle",
    "C": "Chemin de câbles",
    "D": "Dalle",
    "E": "Cage d'escalier et d'ascenseur",
    "F": "Fondation, semelle",
    "J": "Joint",
    "L": "Levée",
    "M": "Massif",
    "N": "Nervure, poutre, longrine",
    "P": "Poteau",
    "V": "Voile",
    "X": "Faux plafond",
})

TREMIE_EXTENSION_TYPES module-attribute

TREMIE_EXTENSION_TYPES = MappingProxyType({
    "D": "Trémie destinée à une porte",
    "E": "Trémie électrique",
    "F": "Trémie équipée (chatière, hublot, fenêtre...)",
    "K": "Trémie destinée à commande de vanne déportée",
    "L": "Trémie destinée à un passage libre / escalier / circulation",
    "M": "Trémie de manutention",
    "R": "Trémie de réserve",
    "T": "Trémie de tuyauterie",
    "V": "Trémie de ventilation",
    "W": "Trémie de transfert d'air",
    "X": "Trémie à destination multiple",
    "Z": "Trémie SAS du BR",
})

ELECTRICAL_SUPPLY_FINDER_PATTERN module-attribute

ELECTRICAL_SUPPLY_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{ELECTRICAL_SUPPLY_REGEX})(?![A-Z0-9])"
)

ELECTRICAL_SUPPLY_PATTERN module-attribute

ELECTRICAL_SUPPLY_PATTERN = compile(
    f"^{ELECTRICAL_SUPPLY_REGEX}$"
)

FIRE_FINDER_PATTERN module-attribute

FIRE_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{FIRE_REGEX})(?![A-Z0-9])"
)

FIRE_PATTERN module-attribute

FIRE_PATTERN = compile(f'^{FIRE_REGEX}$')

LOCATION_FINDER_PATTERN module-attribute

LOCATION_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{LOCATION_REGEX})(?![A-Z0-9])"
)

LOCATION_PATTERN module-attribute

LOCATION_PATTERN = compile(f'^{LOCATION_REGEX}$')

ELEMENTARY_SYSTEM_CODES module-attribute

ELEMENTARY_SYSTEM_CODES = MappingProxyType({
    "ABP": "Poste d'eau basse pression et réchauffeurs",
    "ACA": "Alimentation en eau depuis l'entrée économiseur jusqu'au ballon y compris les pompes de circulation assistée",
    "ACO": "Reprise des condensats du poste d'eau",
    "ADA": "Ensemble motopompe alimentaire de démarrage et d'arrêt",
    "ADG": "Alimentation et dégazage (bâche et dégazeur)",
    "AFR": "Fluide de régulation turbopompe alimentaire",
    "AGR": "Graissage soulèvement virage turbopompe alimentaire",
    "AHP": "Poste d'eau haute pression, moyenne pression et réchauffeurs",
    "APA": "Ensemble motopompe alimentaire (y compris graissage)",
    "APG": "Purge générateur de vapeur (chaudière)",
    "APP": "Ensemble turbopompe alimentaire",
    "ARE": "Alimentation normale des générateurs de vapeur",
    "ASG": "Alimentation auxiliaire de secours des GV",
    "ATH": "Traitement d'huile turbopompe alimentaire",
    "BAC": "Alimentation charbon vers la chaudière",
    "BDC": "Décrasseur",
    "BED": "Stockage et évacuation des refus broyeurs",
    "BEH": "Evacuation hydraulique des suies et machefers",
    "BEM": "Evacuation machefers stockage destockage",
    "BES": "Evacuation et stockage des suies sèches",
    "BKE": "Dépotage stockage fioul usine (engins manutention appareil chauffage)",
    "BKI": "Dépotage stockage fioul domestique",
    "BKO": "Dépotage stockage fioul lourd",
    "BKP": "Stockage gaz (propane)",
    "BMC": "Déchargement stockage et reprise sur parc",
    "BSC": "Stockage calcaire",
    "BSG": "Stockage gypse humide",
    "BSH": "Reprise et évacuation gypse humide",
    "BSP": "Injection et résidu des produits de désulfuration primaire (injection au foyer)",
    "BSS": "Injection et résidu des produits de désulfuration secondaire (traitement des fumées)",
    "BVF": "Voies ferrées",
    "CDI": "Eau de dilution - Refroidissement des rejets",
    "CET": "Etanchéité labyrinthes turbine et turbopompe alimentaire",
    "CEX": "Circuit d'extraction (pompe de reprise)",
    "CPA": "Protection cathodique",
    "CRF": "Circulation eau (graissage, filtration et isolement)",
    "CTA": "Nettoyage du faisceau condenseur (taprogge, technos)",
    "CTE": "Traitement eau de circulation",
    "CTF": "Vaccination acide des réfrigérants",
    "CVF": "Réfrigérants atmosphériques - Ventilation forcée",
    "CVG": "Circuit de vide galeries station de pompage",
    "CVI": "Vide condenseur",
    "CVP": "Réfrigération des purges des réfrigérants atmosphériques",
    "DTD": "Transport pneumatique de documents",
    "DTE": "Transport pneumatique des échantillons",
    "DTF": "Réseau informatique ((bureautique)",
    "DTL": "Distribution de télévision en circuit fermé",
    "DTV": "Transmission - téléphone - recherche de personnes - distribution de l'heure",
    "EAS": "Aspersion - recirculation de l'aspersion",
    "EAU": "Instrumentation de l'enceinte (auscultations )",
    "EBA": "Balayage du Bâtiment Réacteur",
    "EDE": "Mise en dépression de l'espace entre enceinte",
    "ENC": "Noyage du corium",
    "EPP": "Etanchéité et contrôle des fuites de l'enceinte (sas, traversées, tampons...)",
    "ETY": "Contrôle H2 en cas d'APRP et d'accident grave",
    "EVF": "Filtration interne",
    "EVR": "Ventilation continue bâtiment réacteur",
    "EVU": "Evacuation de chaleur du bâtiment réacteur",
    "FAR": "Air de refroidissement du contrôle de flamme",
    "FBU": "Contrôle flamme (caméra + cellule)",
    "FCA": "Air de combustion (soufflage)",
    "FCR": "Cannes rétractiles",
    "FDA": "Désox recirculation absorbeur",
    "FDB": "Désox alimentations absorbeur",
    "FDG": "Désox centrifugation",
    "FDP": "Désox purge de l'absorbeur",
    "FDR": "Désox réchauffage des fumées désul furées",
    "FDT": "Désox tirage additionnel",
    "FEU": "Enveloppe physique de la chaudière (trappe, trou d'homme, poste, regards)",
    "FFU": "Traitement des fumées (dépoussiéreurs désenfumeurs)",
    "FGA": "Brûleurs d'allumage (démarrage)",
    "FGC": "Brûleurs de charge ou de soutien",
    "FGI": "Torches pilotes",
    "FKP": "Alimentation gaz",
    "FMA": "Air d'étanchéité broyeurs",
    "FOA": "Broyeurs",
    "FPA": "Préchauffeurs d'air",
    "FPI": "Poste préparation fioul domestique",
    "FPO": "Poste de préparation fioul lourd",
    "FRA": "Réchauffeur d'air régénératif",
    "FRM": "Ramonage",
    "FRT": "Reprise égoutture",
    "FSA": "Air primaire commun",
    "FTA": "Tirage (circuit d'air de combustion)",
    "FTC": "Essais de cannes de brûleurs (banc de nettoyage)",
    "FYA": "Recyclage (ventilation recyclage des fumées ou air chaud)",
    "GBP": "Contournement turbine BP (MP)",
    "GCA": "Conservation de la turbine à l'arrêt",
    "GCT": "Contournement turbine condenseur",
    "GDA": "Génératrice asynchrone",
    "GDG": "Circuit d'huile",
    "GDK": "Circuit combustible",
    "GDM": "Groupes électrogènes principaux",
    "GEA": "Transformateur auxiliaire",
    "GEV": "Evacuation d'énergie (transfo soutirage inclus)",
    "GEX": "Excitation et régulation alternateur",
    "GFR": "Fluide de régulation turbine",
    "GGR": "Graissage - soulèvement - virage",
    "GHE": "Huile d'étanchéité alternateur",
    "GHP": "Contournement turbine HP",
    "GME": "Eléments mesure turbine",
    "GPA": "Protection alternateur et évacuation d'énergie",
    "GPV": "Circuits principaux de vapeur turbine et purges",
    "GRE": "Réglage et contrôle turbine",
    "GRH": "Réfrigération de l'alternateur (air ou hydrogène)",
    "GRV": "Remplissage - vidange - appoint H2",
    "GSE": "Sécurités turbine (protections)",
    "GSS": "Sécheurs surchauffeurs",
    "GST": "Eau stator",
    "GTH": "Traitement d'huile",
    "GTR": "Téléréglage - télémesures - comptage",
    "GZA": "Admission d'air de la turbine - système anti-givrage",
    "GZD": "Système de démarrage turbine",
    "GZE": "Echappement turbine",
    "GZI": "Alimentation des injecteurs en fioul domestique",
    "GZL": "Lavage compresseur",
    "GZO": "Injection d'eau déminéralisée - réduction Nox",
    "GZP": "Système d'alimentation en gaz",
    "GZV": "Turbo alternateur (réfrigération, ventilation, sécurité, chambre à combustion)",
    "JAC": "Production eau incendie classée",
    "JAN": "Production eau incendie non classée",
    "JDT": "Détection incendie",
    "JPD": "Protection et distribution eau incendie îlot conventionnel",
    "JPH": "Protection et distribution eau incendie cuves à huile Salle des Machines",
    "JPI": "Protection et distribution eau incendie îlot nucléaire",
    "JPL": "Protection et distribution eau incendie du bâtiment électrique",
    "JPN": "Protection et distribution eau incendie du bloc usine",
    "JPQ": "Protection et distribution eau incendie du bâtiment de traitement des effluents",
    "JPS": "Protection et distribution eau incendie de site",
    "JPT": "Protection et distribution eau incendie des transformateurs",
    "JPV": "Protection et distribution eau incendie Diesels",
    "KAC": "Aide à la consignation",
    "KBS": "Boîte de soudure froide (mesure analogique)",
    "KCC": "Télétransmission avec CNC (Centres Nationaux de Crise)",
    "KCM": "Surveillance des matériels de contrôle-commande de la salle des machine",
    "KCO": "Matériels de contrôle - commande centralisés (automate et traitement)",
    "KCU": "Surveillance des matériels de contrôle-commande de la station de pompage",
    "KDO": "Système d'acquisition de données",
    "KIC": "Système informatique de conduite",
    "KIF": "Contrôle informatique de fonctionnement",
    "KIR": "Instrumentation de surveillance du circuit primaire",
    "KIT": "Traitement des informations (TCI) (traitement centralisé de l'information)",
    "KKK": "Contrôle général des accès",
    "KKV": "Surveillance vidéo de site",
    "KLE": "Réseau local d'entreprise",
    "KLI": "Réseau local industriel",
    "KME": "Acquisition de mesure pour essai périodique",
    "KOS": "Oscillo perturbographie",
    "KPE": "Tachy perturbographie",
    "KRA": "Mesures séisme",
    "KRC": "Contrôle de contamination corporelle et dosimétrique",
    "KRG": "Régulation générale",
    "KRH": "Détection hydrogène îlot nucléaire",
    "KRS": "Contrôle de pollution (radioprotection - météorologie - pollution)",
    "KRT": "Mesure de santé (radioprotection tranche)",
    "KSA": "Traitement des alarmes",
    "KSC": "Instrumentation salle de Commande",
    "KSD": "Système de surveillance et de diagnostic(PSAD)",
    "KSR": "Panneau de repli",
    "KSU": "Platine de sauvegarde et d'alerte",
    "KTC": "Contrôle du combustible",
    "KTF": "Contrôle de la composition des fumées",
    "KTG": "Auscultation table de groupe et réfrigérants atmosphériques",
    "KTR": "Contrôle des résidus et coproduits de combustion",
    "KZC": "Centre national de crise",
    "KZR": "Calculateur d'interface de tranche CASOAR",
    "LHP": "Diesel division 1 (> ou = à 5,5 kV)",
    "LHQ": "Diesel division 2 (> ou = à 5,5 kV)",
    "LHR": "Diesel division 3 (> ou = à 5,5 kV)",
    "LHS": "Diesel division 4 (> ou = à 5,5 kV)",
    "LJP": "Diesel de secours 690V division 1",
    "LJS": "Diesel de secours 690V division 4",
    "LLP": "Diesel 400 V alternatif voie A",
    "LLQ": "Diesel 400 V alternatif voie B",
    "LTR": "Circuit de terre",
    "LYS": "Circuit d'essai de batteries",
    "PMB": "Manutention emballage du combustible",
    "PMC": "Manutention du combustible - machine de chargement - ponts passerelle et auxiliaire",
    "PME": "Postes d'examen du combustible neuf et usagé",
    "PMG": "Machine à serrer et desserrer les goujons",
    "PML": "Eclairage immergé",
    "PMO": "Outillage de manutention",
    "PMT": "Manutention du combustible : transfert et ascenseur",
    "PTR": "Traitement et refroidissement d'eau des piscines",
    "RAM": "Alimentation des mécanismes des grappes",
    "RBS": "Borication de sécurité",
    "RCP": "Circuit primaire",
    "RCV": "Contrôle chimique et volumétrique",
    "REA": "Appoint eau et bore",
    "REN": "Echantillonnage nucléaire",
    "RGL": "Commandes des grappes longues",
    "RIC": "Instrumentation interne du cœur",
    "RIS": "Injection de sécurité",
    "RPE": "Purges, évents et exhaures nucléaires",
    "RPN": "Mesure de la puissance nucléaire",
    "RPR": "Protection réacteur",
    "RRA": "Réfrigération à l'arrêt",
    "RRI": "Réfrigération intermédiaire",
    "RRM": "Ventilation de refroidissement des mécanismes de grappes",
    "SAA": "Production air respirable",
    "SAO": "Production d'air d'oxydation DESOX",
    "SAP": "Production air comprimé de travail et de régulation",
    "SAR": "Distribution air comprimé de régulation",
    "SAT": "Distribution air comprimé de travail",
    "SAX": "Moyen de conservation à l'arrêt",
    "SBE": "Equipement du bloc entretien chaud du site y compris la laverie de site",
    "SDA": "Production d'eau déminéralisée",
    "SDB": "Traitement des boues de la déminéralisation",
    "SDD": "Distribution eau déminéralisée réacteur (stockage inclus)",
    "SDR": "Distribution eau déminéralisée Ph9 installation conventionnelle (stockage inclus)",
    "SDX": "Stockage des produits chimiques et de neutralisation des effluents de la déminéralisation",
    "SEA": "Eau à déminéraliser (pré-traitement)",
    "SEB": "Eau brute",
    "SEC": "Eau brute secourue (réfrigération intermédiaire RRI)",
    "SEF": "Prise d'eau- filtration- degrilleurs",
    "SEG": "Recueil et stockage de solution de gypse DESOX",
    "SEH": "Recueil des huiles et des effluents hydrocarbonés (stockage inclus)",
    "SEI": "Eaux industrielles",
    "SEJ": "Traitement des effluents hydrocarbonnés",
    "SEK": "Recueil, contrôle et rejet des effluents du circuit secondaire",
    "SEN": "Eau brute réfrigération SRI",
    "SEO": "Eaux perdues à l'égout",
    "SEP": "Eau potable",
    "SEQ": "Stockage et distribution eau propre",
    "SES": "Eau surchauffée",
    "SET": "Fourniture d'eau tiède à usage extérieur",
    "SEU": "Eau pluviale (collecte)",
    "SEV": "Traitement des rejets de désulfuration (Desox)",
    "SEW": "Traitement des rejets chaufferies (eaux cendreuses)",
    "SEZ": "Nappe phréatique (rabattement)",
    "SFI": "Filtration eau brute",
    "SGA": "Distribution d'argon",
    "SGC": "Distribution gaz carbonique",
    "SGH": "Distribution d'hydrogène",
    "SGL": "Distribution d'huile",
    "SGN": "Distribution d'azote",
    "SGO": "Distribution d'oxygène",
    "SIC": "Préparation lait calcaire (DESOX)",
    "SIR": "Conditionnement chimique (injection réactif)",
    "SIT": "Contrôle chimique (échantillonnage)",
    "SKA": "Stockage argon",
    "SKC": "Stockage gaz carbonique",
    "SKH": "Stockage hydrogéne",
    "SKL": "Stockage huile",
    "SKN": "Stockage d'azote",
    "SKO": "Stockage oxygène",
    "SKR": "Remplissage-vidange huile moteur des pompes primaires",
    "SKZ": "Stockage des gaz (H2, O2, N2, CO2 et gaz rares)",
    "SMW": "Dégelage des wagons et engins de manutention",
    "SNE": "Neutralisation des effluents de site",
    "SNL": "Nettoyage - lançage GV",
    "SNV": "Nettoyage par le vide de la salle des machines",
    "SRI": "Réfrigération intermédiaire - circuits conventionnels (NORIA)",
    "STE": "Traçage électrique",
    "STF": "Traçage électrique (circuits nucléaires)",
    "STV": "Traçage vapeur",
    "SVA": "Distribution de vapeur auxiliaire",
    "SVT": "Transformateur de vapeur",
    "TEG": "Effluents gazeux",
    "TEK": "Contrôle et rejet des effluents de l'îlot nucléaire",
    "TEN": "Circuits d'échantillonnage des effluents du BTE",
    "TEP": "Effluents liquides primaires",
    "TER": "Réservoir complémentaire de santé",
    "TES": "Effluents solides - tranche",
    "TEU": "Effluents liquides usés",
    "TRI": "Réfrigération intermédiaire traitement des effluents",
    "VDA": "Décharge atmosphère",
    "VPU": "Purges de conditionnement des circuits vapeur",
    "VRD": "Vapeur resurchauffée et désurchauffée",
    "VSC": "Soutirage vapeur S.B.T",
    "VSD": "Vapeur surchauffée et désurchauffe",
    "VVP": "Circuit vapeur principal, soupapes de mise à l'atmosphère et évents, vannes GV",
    "XCA": "Chaudière à fioul - chaudière électrique",
    "YBM": "Bras mort (instrumentation provisoire sur les tronçons inter-isolements)",
    "YGV": "Dispositif d'instrumentation GV",
})

ELEMENTARY_SYSTEM_FAMILIES module-attribute

ELEMENTARY_SYSTEM_FAMILIES = MappingProxyType({
    "DA*": "Ascenseurs et monte-charges",
    "DC*": "Ventilation - conditionnement",
    "DE*": "Eau glacée (production et distribution)",
    "DF*": "Désenfumage (si indépendant de la ventilation)",
    "DM*": "Appareils et engins de manutention",
    "DN*": "Eclairage normal du site, bâtiments et surfaces ouvertes",
    "DS*": "Eclairage de secours du site, bâtiments et surfaces ouvertes",
    "DT.": "Téléphone, transmission et télétransmission",
    "DV*": "Ventilation - conditionnement (non contaminable)",
    "DW*": "Ventilation - désenfumage - conditionnement",
    "GD.": "Groupe à moteurs thermiques (diesels..)",
    "GZ": "Groupes turbines à combustion",
    "JA.": "Production eau d'incendie",
    "JD.": "Détection incendie",
    "JG*": "Protection incendie par gaz inerte",
    "JP.": "Protection incendie et distribution eau incendie",
    "KA.": "Aide à l'exploitation (conduite et maintenance)",
    "KC.": "Matériel de contrôle commande (logique et analogique)",
    "KG.": "Système informatique de gestion",
    "KI.": "Systèmes informatiques de conduite (de commande et surveillance)",
    "KK.": "Contrôle accès",
    "KL.": "Réseau informatique",
    "KR.": "Mesure d'ambiance",
    "KS.": "Salle de commande - panneaux de repli - et pupitres",
    "KT.": "Instrumentation spéciale indépendante",
    "KZ.": "Système informatique de surveillance ou commande à distance",
    "LA.": "Production et distribution puissance 230V continu",
    "LB.": "Production et distribution équipement 125 V continu",
    "LC.": "Production et distribution relayage 48 V continu",
    "LD.": "Production et distribution régulation 30V continu",
    "LE.": "Production et distribution régulation 24 V continu",
    "LG.": "Distribution > ou = à 5,5 kV alternatif (non secouru)",
    "LH#": "Courant alternatif > ou = à 5,5 kV secouru",
    "LI.": "Distribution 690 V alternatif normal",
    "LJ.": "Distribution 690 V alternatif secouru",
    "LK.": "Distribution 400 V alternatif normal (sous tableau LG)",
    "LL#": "Distribution 400 V alternatif secouru (sous tableau LH)",
    "LM.": "Distribution 220 V alternatif non régulée",
    "LN.": "Production et distribution 220 V alternatif sans coupure",
    "LO.": "Distribution 400V alternative régulée",
    "LP.": "Distribution 110 V alternatif non secouru",
    "LQ.": "Distribution 110 V alternatif secouru",
    "LR.": "Distribution 20 kV alternatif (10 kV selon les sites)",
    "LS.": "Boucles d'essai",
    "LV.": "Production et distribution 400V sans coupure",
    "LY.": "Circuit d'essai",
    "SA.": "Production et distribution d'air",
    "SB.": "Bloc entretien chaud",
    "SD.": "Eau déminéralisée (production - traitement - distribution - stockage)",
    "SE.": "Distribution et traitement de l'eau (sauf eau déminéralisée)",
    "SF.": "Filtration",
    "SG.": "Distribution fluide autre que eau et air",
    "SI.": "Chimie, réactifs et traitements",
    "SK.": "Stockage fluides autres que eau et air",
    "SM.": "Traitement des engins de manutention",
    "SN.": "Traitement par le vide nettoyage",
    "SP.": "Protection",
    "SR.": "Réfrigération",
    "ST.": "Traçage",
    "SV.": "Vapeur auxiliaire",
})

FUNCTIONAL_SET_CODES module-attribute

FUNCTIONAL_SET_CODES = MappingProxyType({
    (name): (value) for system in ElementarySystems
})

KNOWN_ECS_CODES module-attribute

KNOWN_ECS_CODES = MappingProxyType({
    None: FUNCTIONAL_SET_CODES,
    None: ELEMENTARY_SYSTEM_FAMILIES,
    None: ELEMENTARY_SYSTEM_CODES,
})

RG_FINDER_PATTERN module-attribute

RG_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{RG_REGEX})(?![A-Z0-9])"
)

RG_PATTERN module-attribute

RG_PATTERN = compile(f'^{RG_REGEX}$')

ACTIONNEUR_STATE_QUALIFIERS module-attribute

ACTIONNEUR_STATE_QUALIFIERS = MappingProxyType({
    "1": "Enclenché",
    "2": "Disponible",
    "3": "Vanne ouverte (sur fin de course moteur) ou actionneur enclenché",
    "4": "Vanne ouverte (sur fin de course de tige) ou matériel THT ouvert ou déclenché",
    "5": "Vanne fermée (sur fin de course moteur) ou actionneur déclenché",
    "6": "Vanne fermée (sur fin de course de tige) ou matériel THT fermé ou enclenché",
    "7": "Défaut électrique",
    "8": "Première position intermédiaire depuis l'ouverture vers la fermeture",
    "9": "Deuxième position intermédiaire depuis l'ouverture vers la fermeture",
})

CONTROL_LOCATION_QUALIFIERS module-attribute

CONTROL_LOCATION_QUALIFIERS = MappingProxyType({
    "P": "Moyen de conduite principal (MCP)",
    "S": "Moyen de conduite de secours (MCS)",
    "R": "Moyen de conduite de repli",
    "L": "Moyen de conduite local",
    "T": "Moyen de conduite décentralisé",
})

MATERIAL_CODE_LABELS module-attribute

MATERIAL_CODE_LABELS = MappingProxyType({
    "AA": ("Alarme conventionnelle",),
    "AC": ("Ascenseur", "Monte-charge"),
    "AD": ("Absorbeur",),
    "AE": ("Aérotherme",),
    "AG": ("Agitateur", "Vibreur"),
    "AI": ("Armoire incendie",),
    "AK": ("Point d'ancrage",),
    "AM": ("Amplificateur",),
    "AN": ("Alimentation stabilisée",),
    "AO": ("Anode",),
    "AP": ("Alternateur",),
    "AQ": ("Accumulateur fluide autre qu'électrique",),
    "AR": ("Armoire", "Armoire de distribution"),
    "AS": ("Assemblages combustibles",),
    "AU": ("Dispositif d'arrêt d'urgence",),
    "AV": ("Avaloir (eaux pluviales)",),
    "BA": (
        "Bâche",
        "Bouteille de gaz",
        "Bouteille tampon sur prise de pression",
        "Cuve",
        "Fosse septique",
        "Puisard",
        "Réservoir",
    ),
    "BC": ("Boîte de connexion (bloc essai)",),
    "BF": (
        "Borne fontaine",
        "Bouche d'arrosage",
        "Rampe d'aspersion",
        "Sprinkler",
    ),
    "BH": (
        "Bouche d'aération",
        "Bouche d'air et d'extraction",
    ),
    "BJ": (
        "Borne incendie",
        "Bouche incendie",
        "Poteau incendie",
    ),
    "BK": (
        "Mécanisme des grappes",
        "Unité de commande de barres",
    ),
    "BM": ("Injecteur",),
    "BN": ("Bornier", "Répartiteur"),
    "BO": ("Bouchon",),
    "BQ": ("Bloc sécurité (éclairage secours)",),
    "BR": ("Barres de contrôle et de sécurité",),
    "BS": ("Boîte de soudure froide",),
    "BT": ("Accumulateur électrique", "Batterie"),
    "BU": ("Batardeau",),
    "BV": ("Boîtier de voyants",),
    "BW": ("Refoulement d'air et de soufflage",),
    "BY": ("Broyeur",),
    "BZ": ("Caisson",),
    "CA": ("Câble",),
    "CB": ("Capacité", "Condensateur"),
    "CC": ("Commande de choix",),
    "CD": ("Commande diverse",),
    "CE": ("Composant électrique",),
    "CF": ("Centrifugeuse",),
    "CG": (
        "Commande calculateur logique",
        "Commande groupé logique",
        "Commande logique de fonction",
    ),
    "CH": ("Chaudière", "Générateur de vapeur"),
    "CI": ("Commande individuelle logique",),
    "CK": ("Commande de grappe",),
    "CL": ("Armoire de climatisation", "Climatiseur"),
    "CM": ("Serrure",),
    "CN": ("Colonne (appareil)",),
    "CO": ("Compresseur", "Surpresseur"),
    "CP": ("Coupleur (hydraulique ou mécanique)",),
    "CQ": ("Chassis",),
    "CR": ("boîtier d'essai PTT (en coffret)", "Coffret"),
    "CS": ("Condenseur",),
    "CU": ("Cuvelage",),
    "CV": ("Caniveau",),
    "CW": ("Commande d'appoint primaire",),
    "CY": ("Cheminée",),
    "DA": ("Dispositif auto bloquant",),
    "DB": ("Amortisseur",),
    "DD": (
        "Cheminée de désurchauffe",
        "Chemise de désurchauffe",
        "Tuyère de désurchauffe",
    ),
    "DE": ("Déminéraliseur", "Désioniseur"),
    "DG": ("Dégrilleur",),
    "DH": ("Déshuileur",),
    "DI": (
        "Diaphragme (autre que ceux de mesure)",
        "Limiteur de débit",
        "Obturateur",
        "Tuyère",
        "Venturi",
    ),
    "DJ": ("Emetteur/Récepteur infra-rouge",),
    "DK": ("Disque de rupture (membrane déchirable)",),
    "DL": ("Onduleur",),
    "DM": ("Connexion des dispositifs mobiles",),
    "DR": ("Distributeur (tiroir)",),
    "DS": ("Déshydratant", "Dessicateur", "Sécheur"),
    "DT": ("Cellule photo électrique", "Détecteur"),
    "DV": ("Distributeur vibrant",),
    "DX": ("Dépoussiéreur",),
    "DZ": ("Dégazeur",),
    "EA": ("Electroaimant",),
    "EB": ("Electrolyseur",),
    "EE": ("Electro d'embrayage",),
    "EG": ("Mélangeur",),
    "EJ": ("Ejecteur",),
    "EL": (
        "Electrovanne pilote (uniquement pour plusieurs vannes)",
    ),
    "EN": ("Courbe sur écran", "Enregistreur"),
    "EP": (
        "Convertisseur électropneumatique (uniquement pour plusieurs vannes)",
    ),
    "ER": ("Electrofrein",),
    "ES": ("Appareils d'éclairage",),
    "ET": ("Extracteur",),
    "EU": ("Humidificateur d'air",),
    "EV": ("Evaporateur",),
    "EW": ("Electrode de référence (mesure PH)",),
    "EX": ("Echangeur",),
    "EZ": ("Extincteur",),
    "FA": ("Fiche d'alarme",),
    "FI": ("Filtre", "Pré filtre"),
    "FL": ("Flexible",),
    "FO": ("Fibre optique",),
    "FP": ("Fond plein",),
    "FU": ("Fusible",),
    "FX": (
        "Dispositif de fixation",
        "Point fixe d'accrochage",
    ),
    "GA": ("Générateur de courant alternatif",),
    "GC": ("Générateur de courant continu",),
    "GD": ("Générateur de fonction",),
    "GE": ("Groupe électrogène",),
    "GF": ("Groupe frigorifique",),
    "GL": ("Gaine de ventilation",),
    "GM": ("Générateur de mousse",),
    "GR": ("Graisseur", "Lubrificateur"),
    "GS": ("Siphon", "Siphon de sol"),
    "GT": ("Entonnoir", "Gate"),
    "GU": ("Générateur ultrasons",),
    "HA": ("Lampe", "LED", "Voyant"),
    "HB": ("Boule roulante", "Souris"),
    "HC": (
        "Calculateur",
        "Micro ordinateur",
        "Microprocesseur",
        "Unité centrale",
    ),
    "HD": ("afficheur", "Bargraphe", "Indicateur"),
    "HE": ("Instrumentation",),
    "HI": ("Imprimante", "Télescriptrice", "Télex"),
    "HK": ("Clavier fonctionnel", "Console système"),
    "HL": (
        "Lecteur de badges",
        "Lecteur de Bande",
        "Streamer",
    ),
    "HN": ("Liaison fil à fil",),
    "HP": ("Pot de visualisation",),
    "HQ": ("Demultiplexeur", "Multiplexeur"),
    "HR": ("Horloge",),
    "HS": ("Indicateur de circulation",),
    "HT": ("Interphonie", "Moyens de communication"),
    "HV": ("Ecran",),
    "HW": ("Antenne",),
    "HX": ("Alarme sonore", "Klaxon"),
    "HY": ("Modem", "Transceiver"),
    "JA": (
        "Appareil de coupure électrique",
        "Contacteur",
        "Disjoncteur",
    ),
    "JB": ("Jeu de barres",),
    "JC": ("Cellule électrique",),
    "JO": ("Joint", "Joint de dilatation"),
    "JP": ("Pont de barre",),
    "JQ": ("Contacteur statique",),
    "JR": ("Réserve 380 V et 6,6 kV",),
    "JS": ("Sectionneur",),
    "JT": ("Sectionneur de mise à la terre",),
    "JW": ("Parafoudre",),
    "KA": ("Alarme sur écran",),
    "KD": ("Orifice déprimogène de mesure de débit",),
    "KI": ("Crépine",),
    "KM": ("Information analogique élaborée",),
    "KR": ("Cryogénérateur",),
    "KS": ("Information logique élaborée",),
    "KT": ("Elément primaire de température",),
    "LA": ("Chariot de manutention",),
    "LB": ("Câble de levage",),
    "LC": ("Potence",),
    "LD": ("Dispositif de chargement et de manutention",),
    "LF": (
        "Fer de roulement",
        "Poutre de manutention",
        "Rail",
    ),
    "LG": ("Grappin",),
    "LI": ("Trappe de manutention",),
    "LM": ("Moufle",),
    "LP": ("Palan", "Treuil"),
    "LR": ("Pont", "Pont roulant", "Portique"),
    "LT": (
        "Tapis de transfert",
        "Transfert",
        "Transporteur",
    ),
    "MA": (
        "Mesure d'activité",
        "Mesure de flux",
        "Mesure de rayonnement",
    ),
    "MC": ("Mesure de vitesse",),
    "MD": ("Mesure de débit",),
    "ME": ("Mesure acoustique",),
    "MF": ("Mesure de fréquence", "Mesure de phase"),
    "MG": ("Mesure d'analyse physico-chimique",),
    "MH": ("Mesure de temps",),
    "MI": ("Mesure d'intensité",),
    "MJ": ("Détecteur incendie",),
    "ML": ("Mesure d'opacité", "Mesure de luminosité"),
    "MM": ("Mesure de déplacement", "Mesure de position"),
    "MN": ("Mesure de niveau",),
    "MO": (
        "Moteur (uniquement pour les actionneurs à moteur multiple)",
    ),
    "MP": ("Mesure de pression",),
    "MQ": ("Mesure de puissance réactive",),
    "MR": (
        "Mesure d'impédance",
        "Mesure de conductivité",
        "Mesure de résistance",
        "Mesure de résistivité",
    ),
    "MS": ("Mesure santé",),
    "MT": ("Mesure de température",),
    "MU": ("Mesure de tension",),
    "MV": (
        "Mesure de dilatation",
        "Mesure de poussée",
        "Mesure de séisme",
        "Mesure de vibration",
    ),
    "MW": ("Mesure de puissance active",),
    "MX": ("Mesure divers mécanique",),
    "MY": ("Mesure divers électrique",),
    "MZ": ("Mesure divers physique",),
    "NA": ("Nacelle",),
    "ND": ("Nœud",),
    "NE": ("Fonction de base",),
    "NF": ("Sous-fonction",),
    "NL": ("Liaison fonctionnelle",),
    "PB": ("Piège à son", "Silencieux"),
    "PE": ("Postiche élément combustible",),
    "PG": ("Pompe électromagnétique",),
    "PI": ("Poste incendie",),
    "PJ": ("Connecteur", "Prise", "Prise informatique"),
    "PL": ("Palier",),
    "PN": ("Piston", "Vérin"),
    "PO": ("Pompe",),
    "PP": ("Pupitre",),
    "PQ": ("Presse à compacter",),
    "PT": ("Strap",),
    "PU": ("Purgeur",),
    "PX": ("Poste examen du combustible",),
    "QA": ("Compteur d'activité",),
    "QC": ("Compte tour",),
    "QD": ("Compteur volumétrique",),
    "QH": ("Compteur de temps",),
    "QM": ("Compteur de manœuvres",),
    "QN": ("Compteur numérique",),
    "QQ": ("Compteur d'énergie réactive",),
    "QW": ("Compteur d'énergie active",),
    "QX": ("Compteur d'événements",),
    "RA": ("Registre d'air (isolement ou réglage)",),
    "RB": ("Rampe de bouteilles",),
    "RD": ("Redresseur",),
    "RE": ("Réchauffeur non électrique",),
    "RF": (
        "Batterie froide",
        "Réfrigérant",
        "Refroidisseur d'air",
    ),
    "RG": ("Commande réglante groupée",),
    "RI": ("Commande réglante individuelle",),
    "RJ": ("Raccord incendie",),
    "RK": ("Rack",),
    "RP": ("Refroidisseur de purges ou de condensats",),
    "RR": (
        "Multiplicateur de vitesse",
        "Réducteur de vitesse",
        "Variateur de vitesse",
    ),
    "RS": (
        "Convecteur",
        "Elément de préchauffage",
        "Réchauffeur électrique",
        "Résistance chauffante",
    ),
    "RV": ("Recombineurs d'hydrogène",),
    "RW": ("Répéteur multiport (HUB)",),
    "RX": ("Régime (de consignation, d'essai...)",),
    "RY": ("Manchette démontable", "Raccord par bride"),
    "SA": ("TOR neutronique d'activité - flux",),
    "SC": ("TOR de vitesse",),
    "SD": ("Contrôleur de circulation", "TOR de débit"),
    "SE": ("TOR acoustique",),
    "SF": ("TOR de fréquence - phase",),
    "SG": ("TOR d'analyse physico-chimique",),
    "SH": ("TOR détecteur à seuil d'humidité",),
    "SI": ("TOR d'intensité",),
    "SJ": ("TOR détecteur d'incendie",),
    "SK": ("TOR de contrainte",),
    "SL": ("TOR de luminosité",),
    "SM": (
        "Fin de course (PMC)",
        "TOR de déplacement",
        "TOR de position",
    ),
    "SN": ("TOR de niveau",),
    "SP": ("TOR de pression",),
    "SR": (
        "TOR d'impédance",
        "TOR de conductivité",
        "TOR de résistance",
    ),
    "SS": ("TOR de santé",),
    "ST": ("Thermostat", "TOR de température"),
    "SU": ("TOR de présence tension",),
    "SV": (
        "TOR de dilatation",
        "TOR de poussée",
        "TOR de vibration",
    ),
    "SX": ("TOR divers mécanique",),
    "SY": (
        "Information TOR venant de la régulation",
        "TOR divers électrique",
    ),
    "SZ": ("TOR divers physique",),
    "TA": ("Transformateur auxiliaire réseau",),
    "TB": ("Tableau",),
    "TC": ("Turbine",),
    "TF": ("Grilles filtrantes", "Tambours filtrants"),
    "TH": ("Thermocouple",),
    "TI": ("Transformateur d'intensité",),
    "TO": (
        "Bouton poussoir",
        "Commutateur aveugle",
        "Mécanisme de verrouillage à clé",
        "Touche",
    ),
    "TP": ("Transformateur principal",),
    "TR": ("Transformateur de puissance",),
    "TS": ("Transformateur de soutirage",),
    "TT": ("Puits de terre", "Regard de terre"),
    "TU": ("Transformateur de tension",),
    "TV": ("Auto-transformateur de puissance",),
    "TW": ("Traversée",),
    "TX": ("Transformateur de vapeur",),
    "TY": ("Tuyauterie",),
    "UP": ("Unité de polarité",),
    "UR": ("Platine relais", "Unité de relayage"),
    "US": ("Unité d'isolement",),
    "UU": ("Variateur de tension",),
    "VA": ("Vanne d'air",),
    "VB": ("Vanne eau borée et non primaire",),
    "VC": ("Vanne eau de circulation",),
    "VD": ("Vanne eau déminéralisée",),
    "VE": ("Vanne eau brute",),
    "VF": ("Vanne combustible principal",),
    "VG": ("Vanne CO2- gaz divers",),
    "VH": ("Vanne d'huile",),
    "VI": ("Vanne d'air de ventilation",),
    "VJ": ("Vanne effluents gazeux",),
    "VK": ("Vanne effluents liquides",),
    "VL": ("Vanne eau de condensation",),
    "VM": (
        "Vanne combustible d'allumage (propane - mazout)",
    ),
    "VN": ("Vanne eau de circuit Noria",),
    "VP": ("Vanne eau primaire",),
    "VQ": ("Vanne liquide organique",),
    "VR": ("Vanne réactif",),
    "VS": ("Vanne effluents solides (boues, suies ...)",),
    "VT": ("Vanne eau potable - eau de nappe",),
    "VV": ("Vanne vapeur",),
    "VX": ("Vanne argon",),
    "VY": ("Vanne hydrogène",),
    "VZ": ("Vanne azote",),
    "WB": ("Volet bas (cellule)",),
    "WH": ("Volet haut (cellule)",),
    "WM": ("Electroménager",),
    "WN": ("Télémanipulateur",),
    "WO": ("Machine-outil", "Outillage"),
    "WV": ("Raccord rapide",),
    "XB": ("Relais bistable",),
    "XC": ("Relais à contact de passage",),
    "XH": ("Relais de fréquence",),
    "XI": ("Relais d'intensité",),
    "XK": ("Relais de défaut",),
    "XP": ("Relais d'antipompage",),
    "XR": (
        "Relais duplex radio (émetteur-récepteur)",
        "Relais instantanés autres que définis dans ce tableau (répétiteurs..)",
    ),
    "XS": ("Relais de surcharge",),
    "XT": ("Relais auxiliaire temporisé (cas général)",),
    "XU": ("Relais de tension", "Relais voltmétrique"),
    "XW": ("Relais de puissance",),
    "XZ": ("Relais de détection de terre",),
    "YC": ("Image de conduite",),
    "YE": ("Image de suivi d'équipement",),
    "YM": ("Image menu",),
    "YP": ("Image de procédure",),
    "YR": ("Renvoi fléché",),
    "YS": ("Image de suivi de situation",),
    "ZD": ("Soufflet de dilatation",),
    "ZE": ("Séparateur",),
    "ZF": ("Surchauffeur (quand séparé du sécheur)",),
    "ZI": ("Silencieux",),
    "ZK": ("Synchrocoupleur",),
    "ZM": ("Servomoteur",),
    "ZN": ("Sonde à résistance",),
    "ZO": ("Soudeuse",),
    "ZS": ("Sas",),
    "ZV": ("Soufflante", "Ventilateur"),
    "ZZ": ("Sécheur-surchauffeur",),
})

RF_FINDER_PATTERN module-attribute

RF_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<rf>{RF_REGEX})(?![A-Z0-9])"
)

RF_PATTERN module-attribute

RF_PATTERN = compile(f'^{RF_REGEX}$')

DetectedReference dataclass

DetectedReference(kind, raw, start, end, parsed)

A parsed ECS reference detected inside a text.

kind instance-attribute

kind

raw instance-attribute

raw

start instance-attribute

start

end instance-attribute

end

parsed instance-attribute

parsed

canonical property

canonical

ExtractedReference dataclass

ExtractedReference(raw, canonical, start, end, kind=None)

Reference-like token extracted from free text.

raw instance-attribute

raw

canonical instance-attribute

canonical

start instance-attribute

start

end instance-attribute

end

kind class-attribute instance-attribute

kind = None

ReferenceValidationResult dataclass

ReferenceValidationResult(
    query,
    normalized_query,
    is_exact_match,
    candidates,
    truncated=False,
)

Result of a generic ECS reference validation or completion attempt.

query instance-attribute

query

normalized_query instance-attribute

normalized_query

is_exact_match instance-attribute

is_exact_match

candidates instance-attribute

candidates

truncated class-attribute instance-attribute

truncated = False

has_candidates property

has_candidates

BuildingTrigram dataclass

BuildingTrigram(code)

Structured view of a building trigram.

code instance-attribute

code

is_building property

is_building

family property

family

zone property

zone

family_label property

family_label

BuildingIdentification dataclass

BuildingIdentification(raw)

Shared interpretation of the 4-digit building identification section.

raw instance-attribute

raw

level property

level

sequence property

sequence

altitude_range property

altitude_range

ElectricalSupplyReference dataclass

ElectricalSupplyReference(tranche, system, slot)

ECS electrical supply characteristic.

tranche instance-attribute

tranche

system instance-attribute

system

slot instance-attribute

slot

code property

code

system_label property

system_label

support_kind property

support_kind

enclosure_number property

enclosure_number

column property

column

row property

row

subcolumn property

subcolumn

FireSectorReference dataclass

FireSectorReference(
    tranche, building, identification, kind, criterion
)

ECS fire sectorisation characteristic.

tranche instance-attribute

tranche

building instance-attribute

building

identification instance-attribute

identification

kind instance-attribute

kind

criterion instance-attribute

criterion

code property

code

building_label property

building_label

kind_label property

kind_label

criterion_label property

criterion_label

sector_number property

sector_number

rank property

rank

LocationReference dataclass

LocationReference(
    tranche, building, identification, local_kind
)

ECS localisation characteristic.

tranche instance-attribute

tranche

building instance-attribute

building

identification instance-attribute

identification

local_kind instance-attribute

local_kind

code property

code

building_label property

building_label

local_kind_label property

local_kind_label

local_number property

local_number

ElementarySystems

Bases: StrEnum

Top-level ECS functional sets.

A class-attribute instance-attribute

A = "Alimentation en eau - poste d'eau"

B class-attribute instance-attribute

B = "Manutention, stockage des combustibles et de leurs résidus"

C class-attribute instance-attribute

C = 'Condensation, refroidissement (condenseur)'

D class-attribute instance-attribute

D = "Systèmes annexes non importants pour la production (divers)"

E class-attribute instance-attribute

E = 'Enceinte de confinement'

F class-attribute instance-attribute

F = 'Chaudière (combustion, générateur de vapeur)'

G class-attribute instance-attribute

G = "Groupe turboalternateur et évacuation d'énergie"

H class-attribute instance-attribute

H = 'Bâtiments'

J class-attribute instance-attribute

J = 'Incendie'

K class-attribute instance-attribute

K = 'Contrôle'

L class-attribute instance-attribute

L = 'Distribution électrique'

P class-attribute instance-attribute

P = 'Piscine de stockage du combustible'

R class-attribute instance-attribute

R = 'Réacteur'

S class-attribute instance-attribute

S = 'Services généraux liés à la production'

T class-attribute instance-attribute

T = 'Traitement des effluents'

V class-attribute instance-attribute

V = 'Circuit vapeur principal'

X class-attribute instance-attribute

X = 'Production vapeur auxiliaire'

Y class-attribute instance-attribute

Y = "Installations d'essai"

GeographicIdentification dataclass

GeographicIdentification(raw)

Section 2 of an RG.

raw instance-attribute

raw

level property

level

order property

order

altitude_range property

altitude_range

GeographicReference dataclass

GeographicReference(
    tranche,
    building,
    identification,
    structure,
    extension="",
)

Parsed ECS repère géographique.

tranche instance-attribute

tranche

building instance-attribute

building

identification instance-attribute

identification

structure instance-attribute

structure

extension class-attribute instance-attribute

extension = ''

code property

code

building_label property

building_label

structure_meanings property

structure_meanings

extension_meanings property

extension_meanings

MaterialCodeDescription dataclass

MaterialCodeDescription(
    code, bigram, qualifier, labels, qualifier_meanings
)

Structured description for an ECS material code.

code instance-attribute

code

bigram instance-attribute

bigram

qualifier instance-attribute

qualifier

labels instance-attribute

labels

qualifier_meanings instance-attribute

qualifier_meanings

FunctionalReference dataclass

FunctionalReference(
    tranche, system, identification, material, extension=""
)

Parsed ECS repère fonctionnel.

tranche instance-attribute

tranche

system instance-attribute

system

identification instance-attribute

identification

material instance-attribute

material

extension class-attribute instance-attribute

extension = ''

system_label property

system_label

canonical property

canonical

extension_meanings property

extension_meanings

with_extension

with_extension(extension)
Source code in parc/naming/ecs/rf.py
def with_extension(self, extension: str) -> FunctionalReference:
    return FunctionalReference(
        tranche=self.tranche,
        system=self.system,
        identification=self.identification,
        material=self.material,
        extension=normalize_rf_token(extension),
    )

IdentificationSection dataclass

IdentificationSection(raw)

Section 2 of an RF.

raw instance-attribute

raw

is_compact property

is_compact

sub_function property

sub_function

base_function property

base_function

order property

order

MaterialSection dataclass

MaterialSection(bigram, qualifier)

Section 3 of an RF.

bigram instance-attribute

bigram

qualifier instance-attribute

qualifier

code property

code

description property

description

labels property

labels

qualifier_meanings property

qualifier_meanings

RFValidationResult dataclass

RFValidationResult(
    query,
    normalized_query,
    is_exact_match,
    candidates,
    truncated=False,
)

Result of an RF validation or completion attempt.

query instance-attribute

query

normalized_query instance-attribute

normalized_query

is_exact_match instance-attribute

is_exact_match

candidates instance-attribute

candidates

truncated class-attribute instance-attribute

truncated = False

uses_pattern property

uses_pattern

has_candidates property

has_candidates

analyze_text

analyze_text(text)

Detect ECS references inside arbitrary text.

Source code in parc/naming/ecs/analyzer.py
def analyze_text(text: str) -> list[DetectedReference]:
    """Detect ECS references inside arbitrary text."""

    detections: list[DetectedReference] = []
    seen: set[tuple[int, int]] = set()

    for kind, pattern, group_name, parser in DETECTOR_SPECS:
        for match in pattern.finditer(text.upper()):
            start, end = match.span(group_name)
            key = (start, end)
            if key in seen:
                continue
            try:
                parsed = parser(match.group(group_name))
            except ValueError:
                continue
            seen.add(key)
            raw = text[start:end]
            detections.append(
                DetectedReference(
                    kind=kind,
                    raw=raw,
                    start=start,
                    end=end,
                    parsed=parsed,
                )
            )

    return sorted(detections, key=lambda item: (item.start, item.end, item.kind))

extract_references

extract_references(text)

Extract canonical ECS references from free text.

This extractor is intentionally conservative: it accepts exact references, plus obvious normalizations such as a missing trailing - when the completed form becomes an exact ECS reference.

Source code in parc/naming/ecs/analyzer.py
def extract_references(text: str) -> list[ExtractedReference]:
    """Extract canonical ECS references from free text.

    This extractor is intentionally conservative: it accepts exact references,
    plus obvious normalizations such as a missing trailing ``-`` when the
    completed form becomes an exact ECS reference.
    """

    detections: list[ExtractedReference] = []

    for match in EXTRACT_TOKEN_PATTERN.finditer(text.upper()):
        raw = text[match.start("token") : match.end("token")]
        token = raw.strip().upper()

        detection: DetectedReference | None = None
        try:
            detection = parse_reference(token)
        except ValueError:
            if not token.endswith("-"):
                try:
                    detection = parse_reference(f"{token}-")
                except ValueError:
                    detection = None

        if detection is None and "*" in token and _has_ecs_pattern_anchor(token):
            detections.append(
                ExtractedReference(
                    raw=raw,
                    canonical=token,
                    start=match.start("token"),
                    end=match.end("token"),
                    kind="pattern",
                )
            )
            continue

        if detection is None and "?" in token:
            result = validate_reference(token, limit=2)
            if len(result.candidates) == 1:
                detection = result.candidates[0]
            elif result.candidates:
                detections.append(
                    ExtractedReference(
                        raw=raw,
                        canonical=token,
                        start=match.start("token"),
                        end=match.end("token"),
                        kind="pattern",
                    )
                )
                continue

        if detection is None:
            continue

        detections.append(
            ExtractedReference(
                raw=raw,
                canonical=detection.canonical,
                start=match.start("token"),
                end=match.end("token"),
                kind=detection.kind,
            )
        )

    return detections

format_detection

format_detection(detection)

Render one detection as a human-readable block.

Source code in parc/naming/ecs/analyzer.py
def format_detection(detection: DetectedReference) -> str:
    """Render one detection as a human-readable block."""

    parsed = detection.parsed
    lines = [
        f"- type: {detection.kind}",
        f"  raw: {detection.raw}",
        f"  canonical: {detection.canonical}",
        f"  span: {detection.start}-{detection.end}",
    ]

    if isinstance(parsed, FunctionalReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  system", parsed.system)
        _append(lines, "  system_label", parsed.system_label)
        _append(lines, "  identification", parsed.identification.raw)
        _append(lines, "  sub_function", parsed.identification.sub_function)
        _append(lines, "  base_function", parsed.identification.base_function)
        _append(lines, "  order", parsed.identification.order)
        _append(lines, "  material", parsed.material.code)
        _append(lines, "  material_labels", parsed.material.labels)
        _append(lines, "  material_qualifier_meanings", parsed.material.qualifier_meanings)
        _append(lines, "  extension", parsed.extension)
        _append(lines, "  extension_meanings", parsed.extension_meanings)
        _append_rf_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, LocationReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  building", parsed.building)
        _append(lines, "  building_label", parsed.building_label)
        _append(lines, "  level", parsed.identification.level)
        _append(lines, "  altitude_range", _format_altitude_range(parsed.identification.altitude_range))
        _append(lines, "  local_number", parsed.local_number)
        _append(lines, "  local_kind", parsed.local_kind)
        _append(lines, "  local_kind_label", parsed.local_kind_label)
        _append_location_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, FireSectorReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  building", parsed.building)
        _append(lines, "  building_label", parsed.building_label)
        _append(lines, "  level", parsed.identification.level)
        _append(lines, "  altitude_range", _format_altitude_range(parsed.identification.altitude_range))
        _append(lines, "  sector_number", parsed.sector_number)
        _append(lines, "  fire_kind", parsed.kind)
        _append(lines, "  fire_kind_label", parsed.kind_label)
        _append(lines, "  criterion", parsed.criterion)
        _append(lines, "  criterion_label", parsed.criterion_label)
        _append(lines, "  fire_order_rank", parsed.rank)
        _append_fire_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, ElectricalSupplyReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  system", parsed.system)
        _append(lines, "  system_label", parsed.system_label)
        _append(lines, "  slot", parsed.slot)
        _append(lines, "  support_kind", parsed.support_kind)
        _append(lines, "  enclosure_number", parsed.enclosure_number)
        _append(lines, "  column", parsed.column)
        _append(lines, "  row", parsed.row)
        _append(lines, "  subcolumn", parsed.subcolumn)
        _append_electrical_supply_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, GeographicReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  building", parsed.building)
        _append(lines, "  building_label", parsed.building_label)
        _append(lines, "  level", parsed.identification.level)
        _append(lines, "  altitude_range", _format_altitude_range(parsed.identification.altitude_range))
        _append(lines, "  order", parsed.identification.order)
        _append(lines, "  structure", parsed.structure)
        _append(lines, "  structure_meanings", parsed.structure_meanings)
        _append(lines, "  extension", parsed.extension)
        _append(lines, "  extension_meanings", parsed.extension_meanings)
        _append_rg_breakdown(lines, parsed)
        return "\n".join(lines)

    return "\n".join(lines)

format_report

format_report(detections)

Render a full report for multiple detections.

Source code in parc/naming/ecs/analyzer.py
def format_report(detections: list[DetectedReference]) -> str:
    """Render a full report for multiple detections."""

    if not detections:
        return "No ECS reference detected."
    blocks = [format_detection(detection) for detection in detections]
    return "\n\n".join(blocks)

parse_reference

parse_reference(value)

Parse a single ECS reference by trying every known exact type.

Source code in parc/naming/ecs/analyzer.py
def parse_reference(value: str) -> DetectedReference:
    """Parse a single ECS reference by trying every known exact type."""

    for kind, parser in REFERENCE_PARSERS:
        try:
            parsed = parser(value)
        except ValueError:
            continue
        return DetectedReference(
            kind=kind,
            raw=value,
            start=0,
            end=len(str(value)),
            parsed=parsed,
        )
    msg = f"Unknown ECS reference: {value!r}"
    raise ValueError(msg)

validate_reference

validate_reference(query, *, limit=20)

Validate or complete a reference across all supported ECS types.

Source code in parc/naming/ecs/analyzer.py
def validate_reference(query: str, *, limit: int = 20) -> ReferenceValidationResult:
    """Validate or complete a reference across all supported ECS types."""

    normalized_query = normalize_reference_query(query)
    try:
        exact = parse_reference(normalized_query)
    except ValueError:
        exact = None

    if exact is not None:
        return ReferenceValidationResult(
            query=query,
            normalized_query=normalized_query,
            is_exact_match=True,
            candidates=(exact,),
            truncated=False,
        )

    candidates: list[DetectedReference] = []
    seen: set[tuple[str, str]] = set()

    def add(detection: DetectedReference) -> bool:
        key = (detection.kind, detection.canonical)
        if key in seen:
            return False
        seen.add(key)
        candidates.append(detection)
        return len(candidates) > limit

    for kind, parser, prefixes, charsets, optional_charsets in PATTERN_SPECS:
        for detection in _pattern_candidates_for_kind(
            normalized_query,
            kind=kind,
            parser=parser,
            prefixes=prefixes,
            charsets=charsets,
            optional_charsets=optional_charsets,
        ):
            if add(detection):
                return ReferenceValidationResult(
                    query=query,
                    normalized_query=normalized_query,
                    is_exact_match=False,
                    candidates=tuple(candidates[:limit]),
                    truncated=True,
                )

    rf_result = validate_rf(normalized_query, limit=limit + 1)
    for candidate in rf_result.candidates:
        if add(_build_detected_reference("rf", str(candidate), candidate)):
            return ReferenceValidationResult(
                query=query,
                normalized_query=normalized_query,
                is_exact_match=False,
                candidates=tuple(candidates[:limit]),
                truncated=True,
            )

    return ReferenceValidationResult(
        query=query,
        normalized_query=normalized_query,
        is_exact_match=False,
        candidates=tuple(candidates[:limit]),
        truncated=len(candidates) > limit or rf_result.truncated,
    )

describe_structure_code

describe_structure_code(code)

Return the meaning of a structure code.

Source code in parc/naming/ecs/buildings.py
def describe_structure_code(code: str) -> tuple[str, ...]:
    """Return the meaning of a structure code."""

    normalized = code.strip().upper().replace(" ", "")
    if len(normalized) != 3:
        msg = f"Invalid ECS structure code: {code!r}"
        raise ValueError(msg)

    if normalized[0] == "C":
        return ("Chemin de câbles", f"Voie électrique {normalized[1]}", f"Tablette {normalized[2]}")

    meanings = [STRUCTURE_ELEMENT_CODES.get(normalized[0], f"Elément {normalized[0]}")]
    meanings.append(STRUCTURE_CHARACTERISTIC_CODES.get(normalized[1], f"Caractéristique {normalized[1]}"))
    if normalized[2] != "-":
        meanings.append(STRUCTURE_COMPONENT_CODES.get(normalized[2], f"Composant {normalized[2]}"))
    return tuple(meanings)

describe_structure_extension

describe_structure_extension(structure_code, extension)

Return the known meaning of a structure extension.

Source code in parc/naming/ecs/buildings.py
def describe_structure_extension(structure_code: str, extension: str) -> tuple[str, ...]:
    """Return the known meaning of a structure extension."""

    structure_code = structure_code.strip().upper().replace(" ", "")
    extension = extension.strip().upper().replace(" ", "")
    if not extension:
        return ()

    meanings: list[str] = []
    if structure_code.endswith("T") and extension[0] in TREMIE_EXTENSION_TYPES:
        meanings.append(TREMIE_EXTENSION_TYPES[extension[0]])
        if extension[1:]:
            meanings.append(f"Identifiant composant {extension[1:]}")
    elif structure_code[0] in {"J"} or structure_code.endswith("B"):
        if len(extension) >= 2 and extension[:2] in JOINT_AND_CANIVEAU_COVERS:
            meanings.append(JOINT_AND_CANIVEAU_COVERS[extension[:2]])
            if extension[2:]:
                meanings.append(f"Identifiant composant {extension[2:]}")
    else:
        meanings.append(f"Identifiant composant {extension}")
    return tuple(meanings)

level_altitude_range

level_altitude_range(level)

Return the nominal altitude range associated with a level number.

Source code in parc/naming/ecs/buildings.py
def level_altitude_range(level: str | int) -> tuple[float, float] | None:
    """Return the nominal altitude range associated with a level number."""

    if isinstance(level, str):
        level = int(level)

    if level == 1:
        return (-10.0, -8.01)
    if 2 <= level <= 49:
        start = float(level - 10)
        return (start, start + 0.99)
    if 50 <= level <= 89:
        start = float((level - 50) * 2 + 40)
        return (start, start + 1.99)
    return None

normalize_building_code

normalize_building_code(code)

Normalize a building trigram.

Source code in parc/naming/ecs/buildings.py
def normalize_building_code(code: str) -> str:
    """Normalize a building trigram."""

    return code.strip().upper().replace(" ", "")

parse_building_trigram

parse_building_trigram(code)

Parse a building trigram.

Source code in parc/naming/ecs/buildings.py
def parse_building_trigram(code: str) -> BuildingTrigram:
    """Parse a building trigram."""

    normalized = normalize_building_code(code)
    if len(normalized) != 3 or not normalized.startswith("H"):
        msg = f"Invalid ECS building trigram: {code!r}"
        raise ValueError(msg)
    return BuildingTrigram(normalized)

build_electrical_supply_regex

build_electrical_supply_regex(*, tranche=None, system=None)

Build a regex for electrical supply characteristics.

Source code in parc/naming/ecs/characteristics.py
def build_electrical_supply_regex(*, tranche: str | None = None, system: str | None = None) -> re.Pattern[str]:
    """Build a regex for electrical supply characteristics."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_ecs_code(system)) if system is not None else r"L[A-Z]{2}")
        + r"[0-9][A-Z0-9][A-Z0-9][0-9]JC-"
    )
    return re.compile(f"^{pattern}$")

build_fire_sector_regex

build_fire_sector_regex(
    *,
    tranche=None,
    building=None,
    kind=None,
    criterion=None,
)

Build a regex for fire sectorisation characteristics.

Source code in parc/naming/ecs/characteristics.py
def build_fire_sector_regex(
    *, tranche: str | None = None, building: str | None = None, kind: str | None = None, criterion: str | None = None
) -> re.Pattern[str]:
    """Build a regex for fire sectorisation characteristics."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_characteristic(building)) if building is not None else r"H[A-Z]{2}")
        + r"[0-9]{4}"
        + (re.escape(normalize_characteristic(kind)) if kind is not None else r"[SZ]")
        + "F"
        + (re.escape(normalize_characteristic(criterion)) if criterion is not None else r"[CIS]")
    )
    return re.compile(f"^{pattern}$")

build_location_regex

build_location_regex(
    *, tranche=None, building=None, local_kind=None
)

Build a regex for localisation characteristics.

Source code in parc/naming/ecs/characteristics.py
def build_location_regex(
    *, tranche: str | None = None, building: str | None = None, local_kind: str | None = None
) -> re.Pattern[str]:
    """Build a regex for localisation characteristics."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_characteristic(building)) if building is not None else r"H[A-Z]{2}")
        + r"[0-9]{4}"
        + "Z"
        + (re.escape(normalize_characteristic(local_kind)) if local_kind is not None else r"[ACLM]")
        + "-"
    )
    return re.compile(f"^{pattern}$")

find_electrical_supply_references

find_electrical_supply_references(text)

Find every electrical supply characteristic in a text.

Source code in parc/naming/ecs/characteristics.py
def find_electrical_supply_references(text: str) -> list[ElectricalSupplyReference]:
    """Find every electrical supply characteristic in a text."""

    return [
        parse_electrical_supply_reference(match.group("value"))
        for match in ELECTRICAL_SUPPLY_FINDER_PATTERN.finditer(text.upper())
    ]

find_fire_sector_references

find_fire_sector_references(text)

Find every fire sectorisation characteristic in a text.

Source code in parc/naming/ecs/characteristics.py
def find_fire_sector_references(text: str) -> list[FireSectorReference]:
    """Find every fire sectorisation characteristic in a text."""

    return [parse_fire_sector_reference(match.group("value")) for match in FIRE_FINDER_PATTERN.finditer(text.upper())]

find_location_references

find_location_references(text)

Find every localisation characteristic in a text.

Source code in parc/naming/ecs/characteristics.py
def find_location_references(text: str) -> list[LocationReference]:
    """Find every localisation characteristic in a text."""

    return [parse_location_reference(match.group("value")) for match in LOCATION_FINDER_PATTERN.finditer(text.upper())]

normalize_characteristic

normalize_characteristic(value)

Normalize a characteristic code.

Source code in parc/naming/ecs/characteristics.py
def normalize_characteristic(value: str) -> str:
    """Normalize a characteristic code."""

    return value.strip().upper().replace(" ", "")

parse_electrical_supply_reference

parse_electrical_supply_reference(value)

Parse an ECS electrical supply characteristic.

Source code in parc/naming/ecs/characteristics.py
def parse_electrical_supply_reference(value: str) -> ElectricalSupplyReference:
    """Parse an ECS electrical supply characteristic."""

    normalized = normalize_characteristic(value)
    match = ELECTRICAL_SUPPLY_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS electrical supply reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    return ElectricalSupplyReference(
        tranche=groups["tranche"],
        system=normalize_ecs_code(groups["system"]),
        slot=groups["slot"],
    )

parse_fire_sector_reference

parse_fire_sector_reference(value)

Parse an ECS fire sectorisation characteristic.

Source code in parc/naming/ecs/characteristics.py
def parse_fire_sector_reference(value: str) -> FireSectorReference:
    """Parse an ECS fire sectorisation characteristic."""

    normalized = normalize_characteristic(value)
    match = FIRE_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS fire sector reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    if groups["kind"] == "Z" and groups["criterion"] == "C":
        msg = f"Invalid ECS fire sector reference: {value!r}"
        raise ValueError(msg)
    return FireSectorReference(
        tranche=groups["tranche"],
        building=parse_building_trigram(groups["building"]).code,
        identification=BuildingIdentification(groups["identification"]),
        kind=groups["kind"],
        criterion=groups["criterion"],
    )

parse_location_reference

parse_location_reference(value)

Parse an ECS localisation characteristic.

Source code in parc/naming/ecs/characteristics.py
def parse_location_reference(value: str) -> LocationReference:
    """Parse an ECS localisation characteristic."""

    normalized = normalize_characteristic(value)
    match = LOCATION_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS location reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    return LocationReference(
        tranche=groups["tranche"],
        building=parse_building_trigram(groups["building"]).code,
        identification=BuildingIdentification(groups["identification"]),
        local_kind=groups["kind"],
    )

describe_ecs_code

describe_ecs_code(code)

Return the ECS label associated with code if it exists.

Source code in parc/naming/ecs/ecs.py
def describe_ecs_code(code: str) -> str | None:
    """Return the ECS label associated with ``code`` if it exists."""

    return KNOWN_ECS_CODES.get(normalize_ecs_code(code))

normalize_ecs_code

normalize_ecs_code(code)

Normalize a user-provided ECS code before lookup.

Source code in parc/naming/ecs/ecs.py
def normalize_ecs_code(code: str) -> str:
    """Normalize a user-provided ECS code before lookup."""

    return code.strip().upper().replace(" ", "")

build_rg_regex

build_rg_regex(
    *, tranche=None, building=None, structure=None
)

Build a regex for geographic references.

Source code in parc/naming/ecs/geographic.py
def build_rg_regex(
    *, tranche: str | None = None, building: str | None = None, structure: str | None = None
) -> re.Pattern[str]:
    """Build a regex for geographic references."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_rg(building)) if building is not None else r"H[A-Z]{2}")
        + r"[0-9]{4}"
        + (re.escape(normalize_rg(structure)) if structure is not None else r"[A-Z]{2}[A-Z0-9-]")
        + r"[A-Z0-9]{0,4}"
    )
    return re.compile(f"^{pattern}$")

find_rgs

find_rgs(text)

Extract geographic references from text.

Source code in parc/naming/ecs/geographic.py
def find_rgs(text: str) -> list[GeographicReference]:
    """Extract geographic references from text."""

    return [parse_rg(match.group("value")) for match in RG_FINDER_PATTERN.finditer(text.upper())]

is_rg

is_rg(value)

Return True if value is a valid ECS RG.

Source code in parc/naming/ecs/geographic.py
def is_rg(value: str) -> bool:
    """Return ``True`` if ``value`` is a valid ECS RG."""

    try:
        parse_rg(value)
    except ValueError:
        return False
    return True

normalize_rg

normalize_rg(value)

Normalize a geographic reference.

Source code in parc/naming/ecs/geographic.py
def normalize_rg(value: str) -> str:
    """Normalize a geographic reference."""

    return value.strip().upper().replace(" ", "")

parse_rg

parse_rg(value)

Parse an ECS repère géographique.

Source code in parc/naming/ecs/geographic.py
def parse_rg(value: str) -> GeographicReference:
    """Parse an ECS repère géographique."""

    normalized = normalize_rg(value)
    match = RG_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS geographic reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    if groups["structure"][0] not in STRUCTURE_ELEMENT_CODES:
        msg = f"Invalid ECS geographic reference: {value!r}"
        raise ValueError(msg)
    return GeographicReference(
        tranche=groups["tranche"],
        building=parse_building_trigram(groups["building"]).code,
        identification=GeographicIdentification(groups["identification"]),
        structure=groups["structure"],
        extension=groups["extension"],
    )

describe_extension

describe_extension(material_code, extension)

Return known meanings for a section 4 extension.

Source code in parc/naming/ecs/materials.py
def describe_extension(material_code: str, extension: str) -> tuple[str, ...]:
    """Return known meanings for a section 4 extension."""

    code = normalize_material_code(material_code)
    extension = normalize_material_code(extension)
    if not extension:
        return ()

    bigram, qualifier = split_material_code(code)
    meanings: list[str] = []

    if code == "ARC" and len(extension) == 4:
        meanings.append(f"Carte en rack {extension[:2]}, position {extension[2:]}")

    if bigram in {"TY", "GL"} and len(extension) == 4:
        component_names = {
            "C": "Cintre",
            "E": "Coude",
            "L": "Piquage",
            "S": "Point de supportage ou de fixation",
            "T": "Té",
        }
        if extension[0] in component_names:
            meanings.append(f"{component_names[extension[0]]}{extension[1:]}")

    if bigram == "DT" and qualifier == "J" and len(extension) == 4:
        detector_types = {"I": "Détecteur individuel", "M": "Détecteur maître", "S": "Détecteur esclave"}
        if extension[0] in detector_types:
            meanings.append(f"{detector_types[extension[0]]} {extension[1:]}")

    if bigram in SENSOR_ANALOG_BIGRAMS | SENSOR_TOR_BIGRAMS:
        sensor_extensions = {"7": "Défaut capteur", "A": "Alimentation", "I": "Première mesure", "J": "Deuxième mesure"}
        if extension[0] in sensor_extensions:
            meanings.append(sensor_extensions[extension[0]])
        elif extension[0].isdigit():
            meanings.append(f"Seuil {extension[0]}")

    return tuple(meanings)

describe_material_bigram

describe_material_bigram(code)

Return labels associated with an ECS material bigram.

Source code in parc/naming/ecs/materials.py
def describe_material_bigram(code: str) -> tuple[str, ...] | None:
    """Return labels associated with an ECS material bigram."""

    return MATERIAL_CODE_LABELS.get(normalize_material_code(code)[:2])

describe_material_code

describe_material_code(code)

Return a structured description for an ECS material code.

Source code in parc/naming/ecs/materials.py
def describe_material_code(code: str) -> MaterialCodeDescription:
    """Return a structured description for an ECS material code."""

    bigram, qualifier = split_material_code(code)
    labels = MATERIAL_CODE_LABELS.get(bigram, ())
    return MaterialCodeDescription(
        code=f"{bigram}{qualifier}",
        bigram=bigram,
        qualifier=qualifier,
        labels=labels,
        qualifier_meanings=describe_material_qualifier(bigram, qualifier),
    )

describe_material_qualifier

describe_material_qualifier(bigram, qualifier)

Return known meanings for the third character of a material code.

Source code in parc/naming/ecs/materials.py
def describe_material_qualifier(bigram: str, qualifier: str) -> tuple[str, ...]:
    """Return known meanings for the third character of a material code."""

    bigram = normalize_material_code(bigram)[:2]
    qualifier = normalize_material_code(qualifier) or "-"
    meanings: list[str] = []

    exact = MATERIAL_QUALIFIER_HINTS.get(bigram)
    if exact and qualifier in exact:
        meanings.append(exact[qualifier])

    for helper in (
        _sensor_qualifier_meaning,
        _location_qualifier_meaning,
        _transformer_qualifier_meaning,
        _image_qualifier_meaning,
        _valve_qualifier_meaning,
        _generic_actionneur_meanings,
    ):
        meanings.extend(helper(bigram, qualifier))

    deduped: list[str] = []
    for meaning in meanings:
        if meaning not in deduped:
            deduped.append(meaning)
    return tuple(deduped)

normalize_material_code

normalize_material_code(code)

Normalize an ECS material code.

Source code in parc/naming/ecs/materials.py
def normalize_material_code(code: str) -> str:
    """Normalize an ECS material code."""

    return code.strip().upper().replace(" ", "")

split_material_code

split_material_code(code)

Split a material code into bigram and qualifier.

Source code in parc/naming/ecs/materials.py
def split_material_code(code: str) -> tuple[str, str]:
    """Split a material code into bigram and qualifier."""

    normalized = normalize_material_code(code)
    if len(normalized) == 2:
        return normalized, "-"
    if len(normalized) == 3:
        return normalized[:2], normalized[2]
    msg = f"Invalid ECS material code: {code!r}"
    raise ValueError(msg)

build_rf

build_rf(
    *,
    tranche,
    system,
    identification,
    material,
    extension="",
)

Build an RF from explicit sections.

Source code in parc/naming/ecs/rf.py
def build_rf(
    *,
    tranche: str | int | None,
    system: str,
    identification: str | int,
    material: str,
    extension: str = "",
) -> FunctionalReference:
    """Build an RF from explicit sections."""

    tranche_str = "" if tranche is None else str(tranche)
    identification_str = f"{int(identification):04d}" if isinstance(identification, int) else str(identification)
    material_code = normalize_rf_token(material)
    if len(material_code) == 2:
        material_code = f"{material_code}-"
    return parse_rf(f"{tranche_str}{normalize_ecs_code(system)}{identification_str}{material_code}{extension}")

build_rf_regex

build_rf_regex(
    *,
    tranche=None,
    system=None,
    identification=None,
    material=None,
    extension=None,
    anchored=True,
)

Build a regex for RF matching from exact section values.

Source code in parc/naming/ecs/rf.py
def build_rf_regex(
    *,
    tranche: str | None = None,
    system: str | None = None,
    identification: str | None = None,
    material: str | None = None,
    extension: str | None = None,
    anchored: bool = True,
) -> re.Pattern[str]:
    """Build a regex for RF matching from exact section values."""

    material = normalize_rf_token(material) if material else None
    if material and len(material) == 2:
        material = f"{material}-"

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_ecs_code(system)) if system is not None else r"[A-Z]{3}")
        + (re.escape(str(identification)) if identification is not None else r"[0-9]{3,4}")
        + (re.escape(material[:2]) if material is not None else r"[A-Z]{2}")
        + (re.escape(material[2]) if material is not None else r"[A-Z0-9-]")
        + (re.escape(normalize_rf_token(extension)) if extension is not None else r"[A-Z0-9]{0,4}")
    )
    if anchored:
        pattern = f"^{pattern}$"
    return re.compile(pattern)

find_rfs

find_rfs(text)

Extract every canonical RF found in a block of text.

Source code in parc/naming/ecs/rf.py
def find_rfs(text: str) -> list[FunctionalReference]:
    """Extract every canonical RF found in a block of text."""

    found: list[FunctionalReference] = []
    for match in RF_FINDER_PATTERN.finditer(text.upper()):
        found.append(parse_rf(match.group("rf")))
    return found

is_rf

is_rf(value)

Return True if value is a valid ECS RF.

Source code in parc/naming/ecs/rf.py
def is_rf(value: str) -> bool:
    """Return ``True`` if ``value`` is a valid ECS RF."""

    try:
        parse_rf(value)
    except ValueError:
        return False
    return True

normalize_rf

normalize_rf(value)

Normalize a full RF while preserving ECS semantics.

Source code in parc/naming/ecs/rf.py
def normalize_rf(value: str) -> str:
    """Normalize a full RF while preserving ECS semantics."""

    return normalize_rf_token(value)

normalize_rf_query

normalize_rf_query(value)

Normalize an RF validation query while preserving * and ?.

Source code in parc/naming/ecs/rf.py
def normalize_rf_query(value: str) -> str:
    """Normalize an RF validation query while preserving ``*`` and ``?``."""

    return normalize_rf_token(value)

parse_rf

parse_rf(value)

Parse an ECS repère fonctionnel.

Source code in parc/naming/ecs/rf.py
def parse_rf(value: str) -> FunctionalReference:
    """Parse an ECS repère fonctionnel."""

    normalized = normalize_rf(value)
    match = RF_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS RF: {value!r}"
        raise ValueError(msg)

    groups = match.groupdict()
    system = normalize_ecs_code(groups["system"])
    if describe_ecs_code(system) is None:
        msg = f"Invalid ECS RF: {value!r}"
        raise ValueError(msg)
    return FunctionalReference(
        tranche=groups["tranche"],
        system=system,
        identification=IdentificationSection(groups["identification"]),
        material=MaterialSection(groups["material_bigram"], groups["material_qualifier"]),
        extension=groups["extension"],
    )

suggest_rf_candidates

suggest_rf_candidates(query, *, limit=20)

Return candidate RF completions for a query or minimatch-like proposal.

Source code in parc/naming/ecs/rf.py
def suggest_rf_candidates(query: str, *, limit: int = 20) -> tuple[FunctionalReference, ...]:
    """Return candidate RF completions for a query or minimatch-like proposal."""

    normalized_query = normalize_rf_query(query)
    if not normalized_query:
        return ()

    candidates: list[FunctionalReference] = []
    seen: set[str] = set()

    def add(candidate: str) -> bool:
        if candidate in seen or not proposal_matches_candidate(normalized_query, candidate):
            return False
        try:
            parsed = parse_rf(candidate)
        except ValueError:
            return False
        seen.add(candidate)
        candidates.append(parsed)
        return len(candidates) >= limit

    base_candidates: list[str] = []
    for base_candidate in _iter_base_candidates(normalized_query):
        base_candidates.append(base_candidate)
        if add(base_candidate):
            return tuple(candidates)

    for base_candidate in base_candidates:
        for candidate in _iter_extension_candidates(base_candidate, normalized_query):
            if add(candidate):
                return tuple(candidates)

    return tuple(candidates)

validate_rf

validate_rf(query, *, limit=20)

Validate or complete an RF proposal.

The query may be: - an exact RF - a partial RF prefix - a minimatch-like pattern using ? and *

Source code in parc/naming/ecs/rf.py
def validate_rf(query: str, *, limit: int = 20) -> RFValidationResult:
    """Validate or complete an RF proposal.

    The query may be:
    - an exact RF
    - a partial RF prefix
    - a minimatch-like pattern using ``?`` and ``*``
    """

    normalized_query = normalize_rf_query(query)
    try:
        exact = parse_rf(normalized_query)
    except ValueError:
        exact = None

    if exact is not None:
        return RFValidationResult(
            query=query,
            normalized_query=normalized_query,
            is_exact_match=True,
            candidates=(exact,),
            truncated=False,
        )

    candidates = suggest_rf_candidates(normalized_query, limit=limit + 1)
    truncated = len(candidates) > limit
    if truncated:
        candidates = candidates[:limit]

    return RFValidationResult(
        query=query,
        normalized_query=normalized_query,
        is_exact_match=False,
        candidates=candidates,
        truncated=truncated,
    )

parc.naming.ecs.analyzer

Detection and reporting helpers for ECS references embedded in text.

ParsedReference module-attribute

DETECTOR_SPECS module-attribute

DETECTOR_SPECS = (
    (
        "location",
        LOCATION_FINDER_PATTERN,
        "value",
        parse_location_reference,
    ),
    (
        "fire-sector",
        FIRE_FINDER_PATTERN,
        "value",
        parse_fire_sector_reference,
    ),
    (
        "electrical-supply",
        ELECTRICAL_SUPPLY_FINDER_PATTERN,
        "value",
        parse_electrical_supply_reference,
    ),
    ("rg", RG_FINDER_PATTERN, "value", parse_rg),
    ("rf", RF_FINDER_PATTERN, "rf", parse_rf),
)

REFERENCE_PARSERS module-attribute

REFERENCE_PARSERS = (
    ("location", parse_location_reference),
    ("fire-sector", parse_fire_sector_reference),
    (
        "electrical-supply",
        parse_electrical_supply_reference,
    ),
    ("rg", parse_rg),
    ("rf", parse_rf),
)

DIGITS module-attribute

DIGITS = tuple('0123456789')

UPPER module-attribute

UPPER = tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ')

ALNUM module-attribute

ALNUM = tuple('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')

RG_STRUCTURE_START module-attribute

RG_STRUCTURE_START = tuple('BCDEFJLMNPVX')

RG_STRUCTURE_END module-attribute

RG_STRUCTURE_END = tuple(
    "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

LOCATION_CHARSETS module-attribute

LOCATION_CHARSETS = (
    DIGITS,
    ("H",),
    UPPER,
    UPPER,
    DIGITS,
    DIGITS,
    DIGITS,
    DIGITS,
    ("Z",),
    tuple("ACLM"),
    ("-",),
)

FIRE_CHARSETS module-attribute

FIRE_CHARSETS = (
    DIGITS,
    ("H",),
    UPPER,
    UPPER,
    DIGITS,
    DIGITS,
    DIGITS,
    DIGITS,
    tuple("SZ"),
    ("F",),
    tuple("CIS"),
)

ELECTRICAL_SUPPLY_CHARSETS module-attribute

ELECTRICAL_SUPPLY_CHARSETS = (
    DIGITS,
    ("L",),
    UPPER,
    UPPER,
    DIGITS,
    ALNUM,
    ALNUM,
    DIGITS,
    ("J",),
    ("C",),
    ("-",),
)

RG_MANDATORY_CHARSETS module-attribute

RG_MANDATORY_CHARSETS = (
    DIGITS,
    ("H",),
    UPPER,
    UPPER,
    DIGITS,
    DIGITS,
    DIGITS,
    DIGITS,
    RG_STRUCTURE_START,
    UPPER,
    RG_STRUCTURE_END,
)

RG_OPTIONAL_CHARSETS module-attribute

RG_OPTIONAL_CHARSETS = (ALNUM, ALNUM, ALNUM, ALNUM)

PATTERN_SPECS module-attribute

PATTERN_SPECS = (
    (
        "location",
        parse_location_reference,
        ("", *DIGITS),
        LOCATION_CHARSETS[1:],
        (),
    ),
    (
        "fire-sector",
        parse_fire_sector_reference,
        ("", *DIGITS),
        FIRE_CHARSETS[1:],
        (),
    ),
    (
        "electrical-supply",
        parse_electrical_supply_reference,
        ("", *DIGITS),
        ELECTRICAL_SUPPLY_CHARSETS[1:],
        (),
    ),
    (
        "rg",
        parse_rg,
        ("", *DIGITS),
        RG_MANDATORY_CHARSETS[1:],
        RG_OPTIONAL_CHARSETS,
    ),
)

EXTRACT_TOKEN_PATTERN module-attribute

EXTRACT_TOKEN_PATTERN = compile(
    "(?P<token>[A-Z0-9?*-]{4,})"
)

ExtractedReference dataclass

ExtractedReference(raw, canonical, start, end, kind=None)

Reference-like token extracted from free text.

raw instance-attribute

raw

canonical instance-attribute

canonical

start instance-attribute

start

end instance-attribute

end

kind class-attribute instance-attribute

kind = None

DetectedReference dataclass

DetectedReference(kind, raw, start, end, parsed)

A parsed ECS reference detected inside a text.

kind instance-attribute

kind

raw instance-attribute

raw

start instance-attribute

start

end instance-attribute

end

parsed instance-attribute

parsed

canonical property

canonical

ReferenceValidationResult dataclass

ReferenceValidationResult(
    query,
    normalized_query,
    is_exact_match,
    candidates,
    truncated=False,
)

Result of a generic ECS reference validation or completion attempt.

query instance-attribute

query

normalized_query instance-attribute

normalized_query

is_exact_match instance-attribute

is_exact_match

candidates instance-attribute

candidates

truncated class-attribute instance-attribute

truncated = False

has_candidates property

has_candidates

normalize_reference_query

normalize_reference_query(value)

Normalize a reference validation query while preserving wildcards.

Source code in parc/naming/ecs/analyzer.py
def normalize_reference_query(value: str) -> str:
    """Normalize a reference validation query while preserving wildcards."""

    return value.strip().upper().replace(" ", "")

parse_reference

parse_reference(value)

Parse a single ECS reference by trying every known exact type.

Source code in parc/naming/ecs/analyzer.py
def parse_reference(value: str) -> DetectedReference:
    """Parse a single ECS reference by trying every known exact type."""

    for kind, parser in REFERENCE_PARSERS:
        try:
            parsed = parser(value)
        except ValueError:
            continue
        return DetectedReference(
            kind=kind,
            raw=value,
            start=0,
            end=len(str(value)),
            parsed=parsed,
        )
    msg = f"Unknown ECS reference: {value!r}"
    raise ValueError(msg)

validate_reference

validate_reference(query, *, limit=20)

Validate or complete a reference across all supported ECS types.

Source code in parc/naming/ecs/analyzer.py
def validate_reference(query: str, *, limit: int = 20) -> ReferenceValidationResult:
    """Validate or complete a reference across all supported ECS types."""

    normalized_query = normalize_reference_query(query)
    try:
        exact = parse_reference(normalized_query)
    except ValueError:
        exact = None

    if exact is not None:
        return ReferenceValidationResult(
            query=query,
            normalized_query=normalized_query,
            is_exact_match=True,
            candidates=(exact,),
            truncated=False,
        )

    candidates: list[DetectedReference] = []
    seen: set[tuple[str, str]] = set()

    def add(detection: DetectedReference) -> bool:
        key = (detection.kind, detection.canonical)
        if key in seen:
            return False
        seen.add(key)
        candidates.append(detection)
        return len(candidates) > limit

    for kind, parser, prefixes, charsets, optional_charsets in PATTERN_SPECS:
        for detection in _pattern_candidates_for_kind(
            normalized_query,
            kind=kind,
            parser=parser,
            prefixes=prefixes,
            charsets=charsets,
            optional_charsets=optional_charsets,
        ):
            if add(detection):
                return ReferenceValidationResult(
                    query=query,
                    normalized_query=normalized_query,
                    is_exact_match=False,
                    candidates=tuple(candidates[:limit]),
                    truncated=True,
                )

    rf_result = validate_rf(normalized_query, limit=limit + 1)
    for candidate in rf_result.candidates:
        if add(_build_detected_reference("rf", str(candidate), candidate)):
            return ReferenceValidationResult(
                query=query,
                normalized_query=normalized_query,
                is_exact_match=False,
                candidates=tuple(candidates[:limit]),
                truncated=True,
            )

    return ReferenceValidationResult(
        query=query,
        normalized_query=normalized_query,
        is_exact_match=False,
        candidates=tuple(candidates[:limit]),
        truncated=len(candidates) > limit or rf_result.truncated,
    )

analyze_text

analyze_text(text)

Detect ECS references inside arbitrary text.

Source code in parc/naming/ecs/analyzer.py
def analyze_text(text: str) -> list[DetectedReference]:
    """Detect ECS references inside arbitrary text."""

    detections: list[DetectedReference] = []
    seen: set[tuple[int, int]] = set()

    for kind, pattern, group_name, parser in DETECTOR_SPECS:
        for match in pattern.finditer(text.upper()):
            start, end = match.span(group_name)
            key = (start, end)
            if key in seen:
                continue
            try:
                parsed = parser(match.group(group_name))
            except ValueError:
                continue
            seen.add(key)
            raw = text[start:end]
            detections.append(
                DetectedReference(
                    kind=kind,
                    raw=raw,
                    start=start,
                    end=end,
                    parsed=parsed,
                )
            )

    return sorted(detections, key=lambda item: (item.start, item.end, item.kind))

extract_references

extract_references(text)

Extract canonical ECS references from free text.

This extractor is intentionally conservative: it accepts exact references, plus obvious normalizations such as a missing trailing - when the completed form becomes an exact ECS reference.

Source code in parc/naming/ecs/analyzer.py
def extract_references(text: str) -> list[ExtractedReference]:
    """Extract canonical ECS references from free text.

    This extractor is intentionally conservative: it accepts exact references,
    plus obvious normalizations such as a missing trailing ``-`` when the
    completed form becomes an exact ECS reference.
    """

    detections: list[ExtractedReference] = []

    for match in EXTRACT_TOKEN_PATTERN.finditer(text.upper()):
        raw = text[match.start("token") : match.end("token")]
        token = raw.strip().upper()

        detection: DetectedReference | None = None
        try:
            detection = parse_reference(token)
        except ValueError:
            if not token.endswith("-"):
                try:
                    detection = parse_reference(f"{token}-")
                except ValueError:
                    detection = None

        if detection is None and "*" in token and _has_ecs_pattern_anchor(token):
            detections.append(
                ExtractedReference(
                    raw=raw,
                    canonical=token,
                    start=match.start("token"),
                    end=match.end("token"),
                    kind="pattern",
                )
            )
            continue

        if detection is None and "?" in token:
            result = validate_reference(token, limit=2)
            if len(result.candidates) == 1:
                detection = result.candidates[0]
            elif result.candidates:
                detections.append(
                    ExtractedReference(
                        raw=raw,
                        canonical=token,
                        start=match.start("token"),
                        end=match.end("token"),
                        kind="pattern",
                    )
                )
                continue

        if detection is None:
            continue

        detections.append(
            ExtractedReference(
                raw=raw,
                canonical=detection.canonical,
                start=match.start("token"),
                end=match.end("token"),
                kind=detection.kind,
            )
        )

    return detections

format_detection

format_detection(detection)

Render one detection as a human-readable block.

Source code in parc/naming/ecs/analyzer.py
def format_detection(detection: DetectedReference) -> str:
    """Render one detection as a human-readable block."""

    parsed = detection.parsed
    lines = [
        f"- type: {detection.kind}",
        f"  raw: {detection.raw}",
        f"  canonical: {detection.canonical}",
        f"  span: {detection.start}-{detection.end}",
    ]

    if isinstance(parsed, FunctionalReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  system", parsed.system)
        _append(lines, "  system_label", parsed.system_label)
        _append(lines, "  identification", parsed.identification.raw)
        _append(lines, "  sub_function", parsed.identification.sub_function)
        _append(lines, "  base_function", parsed.identification.base_function)
        _append(lines, "  order", parsed.identification.order)
        _append(lines, "  material", parsed.material.code)
        _append(lines, "  material_labels", parsed.material.labels)
        _append(lines, "  material_qualifier_meanings", parsed.material.qualifier_meanings)
        _append(lines, "  extension", parsed.extension)
        _append(lines, "  extension_meanings", parsed.extension_meanings)
        _append_rf_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, LocationReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  building", parsed.building)
        _append(lines, "  building_label", parsed.building_label)
        _append(lines, "  level", parsed.identification.level)
        _append(lines, "  altitude_range", _format_altitude_range(parsed.identification.altitude_range))
        _append(lines, "  local_number", parsed.local_number)
        _append(lines, "  local_kind", parsed.local_kind)
        _append(lines, "  local_kind_label", parsed.local_kind_label)
        _append_location_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, FireSectorReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  building", parsed.building)
        _append(lines, "  building_label", parsed.building_label)
        _append(lines, "  level", parsed.identification.level)
        _append(lines, "  altitude_range", _format_altitude_range(parsed.identification.altitude_range))
        _append(lines, "  sector_number", parsed.sector_number)
        _append(lines, "  fire_kind", parsed.kind)
        _append(lines, "  fire_kind_label", parsed.kind_label)
        _append(lines, "  criterion", parsed.criterion)
        _append(lines, "  criterion_label", parsed.criterion_label)
        _append(lines, "  fire_order_rank", parsed.rank)
        _append_fire_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, ElectricalSupplyReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  system", parsed.system)
        _append(lines, "  system_label", parsed.system_label)
        _append(lines, "  slot", parsed.slot)
        _append(lines, "  support_kind", parsed.support_kind)
        _append(lines, "  enclosure_number", parsed.enclosure_number)
        _append(lines, "  column", parsed.column)
        _append(lines, "  row", parsed.row)
        _append(lines, "  subcolumn", parsed.subcolumn)
        _append_electrical_supply_breakdown(lines, parsed)
        return "\n".join(lines)

    if isinstance(parsed, GeographicReference):
        _append(lines, "  tranche", parsed.tranche)
        _append(lines, "  building", parsed.building)
        _append(lines, "  building_label", parsed.building_label)
        _append(lines, "  level", parsed.identification.level)
        _append(lines, "  altitude_range", _format_altitude_range(parsed.identification.altitude_range))
        _append(lines, "  order", parsed.identification.order)
        _append(lines, "  structure", parsed.structure)
        _append(lines, "  structure_meanings", parsed.structure_meanings)
        _append(lines, "  extension", parsed.extension)
        _append(lines, "  extension_meanings", parsed.extension_meanings)
        _append_rg_breakdown(lines, parsed)
        return "\n".join(lines)

    return "\n".join(lines)

format_report

format_report(detections)

Render a full report for multiple detections.

Source code in parc/naming/ecs/analyzer.py
def format_report(detections: list[DetectedReference]) -> str:
    """Render a full report for multiple detections."""

    if not detections:
        return "No ECS reference detected."
    blocks = [format_detection(detection) for detection in detections]
    return "\n\n".join(blocks)

parc.naming.ecs.buildings

EDF ECS building, level and structure helpers.

BUILDING_CODE_LABELS module-attribute

BUILDING_CODE_LABELS = MappingProxyType({
    "A": "Bâtiments et installations de site de l'aménagement",
    "B": "Bâtiment de site de l'exploitation",
    "C": "Rejets, réfrigérants",
    "D": "Bâtiments diesel",
    "E": "Poste d'interconnexion",
    "F": "Bâtiments à fioul / bâtiment électrique non classé",
    "G": "Galeries",
    "H": "Bâtiments d'entreposage et stockage provisoire G.V. usés",
    "I": "Chauffage central (production eau chaude)",
    "J": "Plate-forme transformateurs auxiliaires",
    "K": "Bâtiment combustible",
    "L": "Bâtiments électriques et des auxiliaires de sauvegarde",
    "M": "Salle des machines",
    "N": "Bâtiments des auxiliaires nucléaires",
    "O": "Bâtiment de stockage d'eau",
    "P": "Station de pompage et de filtration",
    "Q": "Bâtiment de traitement des effluents",
    "R": "Bâtiment réacteur",
    "S": "Environnement, site",
    "T": "Plate-forme transformateurs principaux",
    "U": "Protection site, poste d'accès principal",
    "V": "Bâtiments des auxiliaires généraux",
    "W": "Bâtiments périphériques des bâtiments réacteurs et bâtiments d'exploitation",
    "X": "Aire de stockage des effluents",
    "Y": "Bâtiment déminéralisation",
    "Z": "Bâtiment de stockage de gaz",
})

SITE_BUILDING_SUBFUNCTIONS module-attribute

SITE_BUILDING_SUBFUNCTIONS = MappingProxyType({
    "0": "Chauffage, climatisation",
    "1": "Eclairage normal et de secours",
    "2": "Distribution électrique normale",
    "3": "Distribution électrique secourue",
    "4": "Détection incendie",
    "5": "Distribution eau incendie",
    "6": "Distribution eau potable",
})

LOCAL_CODE_LABELS module-attribute

LOCAL_CODE_LABELS = MappingProxyType({
    "A": "Zone de montage",
    "C": "Zone de circulation",
    "L": "Local (pièce)",
    "M": "Zone de manutention",
})

FIRE_KIND_LABELS module-attribute

FIRE_KIND_LABELS = MappingProxyType({
    "S": "Secteur",
    "Z": "Zone",
})

FIRE_CRITERION_LABELS module-attribute

FIRE_CRITERION_LABELS = MappingProxyType({
    "C": "Confinement de matière radioactive",
    "I": "Limitation et indisponibilité",
    "S": "Sûreté",
})

FIRE_CODE_ORDER module-attribute

FIRE_CODE_ORDER = ('ZFI', 'SFI', 'ZFS', 'SFS', 'SFC')

STRUCTURE_ELEMENT_CODES module-attribute

STRUCTURE_ELEMENT_CODES = MappingProxyType({
    "B": "Echelle",
    "C": "Chemin de câbles",
    "D": "Dalle",
    "E": "Cage d'escalier et d'ascenseur",
    "F": "Fondation, semelle",
    "J": "Joint",
    "L": "Levée",
    "M": "Massif",
    "N": "Nervure, poutre, longrine",
    "P": "Poteau",
    "V": "Voile",
    "X": "Faux plafond",
})

STRUCTURE_CHARACTERISTIC_CODES module-attribute

STRUCTURE_CHARACTERISTIC_CODES = MappingProxyType({
    "B": "Béton",
    "M": "Métal",
    "V": "Vide",
})

STRUCTURE_COMPONENT_CODES module-attribute

STRUCTURE_COMPONENT_CODES = MappingProxyType({
    "A": "Ancrage à sceller (Halfen)",
    "B": "Caniveaux",
    "C": "Cadre à sceller",
    "D": "Porte",
    "E": "Fer plat",
    "F": "Fourreau / chatière",
    "G": "Garde corps",
    "K": "Carottage",
    "L": "Palier / seuil",
    "N": "Portillon",
    "P": "Platine à sceller",
    "Q": "Console (cornière, ...)",
    "R": "Réservation",
    "S": "Divers à sceller",
    "T": "Trémie",
    "U": "Puisard",
    "V": "Volée",
    "W": "Supportage",
    "Y": "Pylône",
    "Z": "Ecran thermique pour les chemins de câble",
})

TREMIE_EXTENSION_TYPES module-attribute

TREMIE_EXTENSION_TYPES = MappingProxyType({
    "D": "Trémie destinée à une porte",
    "E": "Trémie électrique",
    "F": "Trémie équipée (chatière, hublot, fenêtre...)",
    "K": "Trémie destinée à commande de vanne déportée",
    "L": "Trémie destinée à un passage libre / escalier / circulation",
    "M": "Trémie de manutention",
    "R": "Trémie de réserve",
    "T": "Trémie de tuyauterie",
    "V": "Trémie de ventilation",
    "W": "Trémie de transfert d'air",
    "X": "Trémie à destination multiple",
    "Z": "Trémie SAS du BR",
})

JOINT_AND_CANIVEAU_COVERS module-attribute

JOINT_AND_CANIVEAU_COVERS = MappingProxyType({
    "CB": "Couvre joint ou couvre caniveau béton",
    "CM": "Couvre joint ou couvre caniveau métallique",
})

BuildingTrigram dataclass

BuildingTrigram(code)

Structured view of a building trigram.

code instance-attribute

code

is_building property

is_building

family property

family

zone property

zone

family_label property

family_label

normalize_building_code

normalize_building_code(code)

Normalize a building trigram.

Source code in parc/naming/ecs/buildings.py
def normalize_building_code(code: str) -> str:
    """Normalize a building trigram."""

    return code.strip().upper().replace(" ", "")

parse_building_trigram

parse_building_trigram(code)

Parse a building trigram.

Source code in parc/naming/ecs/buildings.py
def parse_building_trigram(code: str) -> BuildingTrigram:
    """Parse a building trigram."""

    normalized = normalize_building_code(code)
    if len(normalized) != 3 or not normalized.startswith("H"):
        msg = f"Invalid ECS building trigram: {code!r}"
        raise ValueError(msg)
    return BuildingTrigram(normalized)

level_altitude_range

level_altitude_range(level)

Return the nominal altitude range associated with a level number.

Source code in parc/naming/ecs/buildings.py
def level_altitude_range(level: str | int) -> tuple[float, float] | None:
    """Return the nominal altitude range associated with a level number."""

    if isinstance(level, str):
        level = int(level)

    if level == 1:
        return (-10.0, -8.01)
    if 2 <= level <= 49:
        start = float(level - 10)
        return (start, start + 0.99)
    if 50 <= level <= 89:
        start = float((level - 50) * 2 + 40)
        return (start, start + 1.99)
    return None

describe_structure_code

describe_structure_code(code)

Return the meaning of a structure code.

Source code in parc/naming/ecs/buildings.py
def describe_structure_code(code: str) -> tuple[str, ...]:
    """Return the meaning of a structure code."""

    normalized = code.strip().upper().replace(" ", "")
    if len(normalized) != 3:
        msg = f"Invalid ECS structure code: {code!r}"
        raise ValueError(msg)

    if normalized[0] == "C":
        return ("Chemin de câbles", f"Voie électrique {normalized[1]}", f"Tablette {normalized[2]}")

    meanings = [STRUCTURE_ELEMENT_CODES.get(normalized[0], f"Elément {normalized[0]}")]
    meanings.append(STRUCTURE_CHARACTERISTIC_CODES.get(normalized[1], f"Caractéristique {normalized[1]}"))
    if normalized[2] != "-":
        meanings.append(STRUCTURE_COMPONENT_CODES.get(normalized[2], f"Composant {normalized[2]}"))
    return tuple(meanings)

describe_structure_extension

describe_structure_extension(structure_code, extension)

Return the known meaning of a structure extension.

Source code in parc/naming/ecs/buildings.py
def describe_structure_extension(structure_code: str, extension: str) -> tuple[str, ...]:
    """Return the known meaning of a structure extension."""

    structure_code = structure_code.strip().upper().replace(" ", "")
    extension = extension.strip().upper().replace(" ", "")
    if not extension:
        return ()

    meanings: list[str] = []
    if structure_code.endswith("T") and extension[0] in TREMIE_EXTENSION_TYPES:
        meanings.append(TREMIE_EXTENSION_TYPES[extension[0]])
        if extension[1:]:
            meanings.append(f"Identifiant composant {extension[1:]}")
    elif structure_code[0] in {"J"} or structure_code.endswith("B"):
        if len(extension) >= 2 and extension[:2] in JOINT_AND_CANIVEAU_COVERS:
            meanings.append(JOINT_AND_CANIVEAU_COVERS[extension[:2]])
            if extension[2:]:
                meanings.append(f"Identifiant composant {extension[2:]}")
    else:
        meanings.append(f"Identifiant composant {extension}")
    return tuple(meanings)

parc.naming.ecs.characteristics

Parsers for ECS characteristics associated with equipment and buildings.

LOCATION_REGEX module-attribute

LOCATION_REGEX = "(?P<tranche>[0-9])?(?P<building>H[A-Z]{2})(?P<identification>[0-9]{4})Z(?P<kind>[ACLM])-"

LOCATION_PATTERN module-attribute

LOCATION_PATTERN = compile(f'^{LOCATION_REGEX}$')

LOCATION_FINDER_PATTERN module-attribute

LOCATION_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{LOCATION_REGEX})(?![A-Z0-9])"
)

FIRE_REGEX module-attribute

FIRE_REGEX = "(?P<tranche>[0-9])?(?P<building>H[A-Z]{2})(?P<identification>[0-9]{4})(?P<kind>[SZ])F(?P<criterion>[CIS])"

FIRE_PATTERN module-attribute

FIRE_PATTERN = compile(f'^{FIRE_REGEX}$')

FIRE_FINDER_PATTERN module-attribute

FIRE_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{FIRE_REGEX})(?![A-Z0-9])"
)

ELECTRICAL_SUPPLY_REGEX module-attribute

ELECTRICAL_SUPPLY_REGEX = "(?P<tranche>[0-9])?(?P<system>L[A-Z]{2})(?P<slot>[0-9][A-Z0-9][A-Z0-9][0-9])JC-"

ELECTRICAL_SUPPLY_PATTERN module-attribute

ELECTRICAL_SUPPLY_PATTERN = compile(
    f"^{ELECTRICAL_SUPPLY_REGEX}$"
)

ELECTRICAL_SUPPLY_FINDER_PATTERN module-attribute

ELECTRICAL_SUPPLY_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{ELECTRICAL_SUPPLY_REGEX})(?![A-Z0-9])"
)

BuildingIdentification dataclass

BuildingIdentification(raw)

Shared interpretation of the 4-digit building identification section.

raw instance-attribute

raw

level property

level

sequence property

sequence

altitude_range property

altitude_range

LocationReference dataclass

LocationReference(
    tranche, building, identification, local_kind
)

ECS localisation characteristic.

tranche instance-attribute

tranche

building instance-attribute

building

identification instance-attribute

identification

local_kind instance-attribute

local_kind

code property

code

building_label property

building_label

local_kind_label property

local_kind_label

local_number property

local_number

FireSectorReference dataclass

FireSectorReference(
    tranche, building, identification, kind, criterion
)

ECS fire sectorisation characteristic.

tranche instance-attribute

tranche

building instance-attribute

building

identification instance-attribute

identification

kind instance-attribute

kind

criterion instance-attribute

criterion

code property

code

building_label property

building_label

kind_label property

kind_label

criterion_label property

criterion_label

sector_number property

sector_number

rank property

rank

ElectricalSupplyReference dataclass

ElectricalSupplyReference(tranche, system, slot)

ECS electrical supply characteristic.

tranche instance-attribute

tranche

system instance-attribute

system

slot instance-attribute

slot

code property

code

system_label property

system_label

support_kind property

support_kind

enclosure_number property

enclosure_number

column property

column

row property

row

subcolumn property

subcolumn

normalize_characteristic

normalize_characteristic(value)

Normalize a characteristic code.

Source code in parc/naming/ecs/characteristics.py
def normalize_characteristic(value: str) -> str:
    """Normalize a characteristic code."""

    return value.strip().upper().replace(" ", "")

parse_location_reference

parse_location_reference(value)

Parse an ECS localisation characteristic.

Source code in parc/naming/ecs/characteristics.py
def parse_location_reference(value: str) -> LocationReference:
    """Parse an ECS localisation characteristic."""

    normalized = normalize_characteristic(value)
    match = LOCATION_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS location reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    return LocationReference(
        tranche=groups["tranche"],
        building=parse_building_trigram(groups["building"]).code,
        identification=BuildingIdentification(groups["identification"]),
        local_kind=groups["kind"],
    )

parse_fire_sector_reference

parse_fire_sector_reference(value)

Parse an ECS fire sectorisation characteristic.

Source code in parc/naming/ecs/characteristics.py
def parse_fire_sector_reference(value: str) -> FireSectorReference:
    """Parse an ECS fire sectorisation characteristic."""

    normalized = normalize_characteristic(value)
    match = FIRE_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS fire sector reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    if groups["kind"] == "Z" and groups["criterion"] == "C":
        msg = f"Invalid ECS fire sector reference: {value!r}"
        raise ValueError(msg)
    return FireSectorReference(
        tranche=groups["tranche"],
        building=parse_building_trigram(groups["building"]).code,
        identification=BuildingIdentification(groups["identification"]),
        kind=groups["kind"],
        criterion=groups["criterion"],
    )

parse_electrical_supply_reference

parse_electrical_supply_reference(value)

Parse an ECS electrical supply characteristic.

Source code in parc/naming/ecs/characteristics.py
def parse_electrical_supply_reference(value: str) -> ElectricalSupplyReference:
    """Parse an ECS electrical supply characteristic."""

    normalized = normalize_characteristic(value)
    match = ELECTRICAL_SUPPLY_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS electrical supply reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    return ElectricalSupplyReference(
        tranche=groups["tranche"],
        system=normalize_ecs_code(groups["system"]),
        slot=groups["slot"],
    )

build_location_regex

build_location_regex(
    *, tranche=None, building=None, local_kind=None
)

Build a regex for localisation characteristics.

Source code in parc/naming/ecs/characteristics.py
def build_location_regex(
    *, tranche: str | None = None, building: str | None = None, local_kind: str | None = None
) -> re.Pattern[str]:
    """Build a regex for localisation characteristics."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_characteristic(building)) if building is not None else r"H[A-Z]{2}")
        + r"[0-9]{4}"
        + "Z"
        + (re.escape(normalize_characteristic(local_kind)) if local_kind is not None else r"[ACLM]")
        + "-"
    )
    return re.compile(f"^{pattern}$")

build_fire_sector_regex

build_fire_sector_regex(
    *,
    tranche=None,
    building=None,
    kind=None,
    criterion=None,
)

Build a regex for fire sectorisation characteristics.

Source code in parc/naming/ecs/characteristics.py
def build_fire_sector_regex(
    *, tranche: str | None = None, building: str | None = None, kind: str | None = None, criterion: str | None = None
) -> re.Pattern[str]:
    """Build a regex for fire sectorisation characteristics."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_characteristic(building)) if building is not None else r"H[A-Z]{2}")
        + r"[0-9]{4}"
        + (re.escape(normalize_characteristic(kind)) if kind is not None else r"[SZ]")
        + "F"
        + (re.escape(normalize_characteristic(criterion)) if criterion is not None else r"[CIS]")
    )
    return re.compile(f"^{pattern}$")

build_electrical_supply_regex

build_electrical_supply_regex(*, tranche=None, system=None)

Build a regex for electrical supply characteristics.

Source code in parc/naming/ecs/characteristics.py
def build_electrical_supply_regex(*, tranche: str | None = None, system: str | None = None) -> re.Pattern[str]:
    """Build a regex for electrical supply characteristics."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_ecs_code(system)) if system is not None else r"L[A-Z]{2}")
        + r"[0-9][A-Z0-9][A-Z0-9][0-9]JC-"
    )
    return re.compile(f"^{pattern}$")

find_location_references

find_location_references(text)

Find every localisation characteristic in a text.

Source code in parc/naming/ecs/characteristics.py
def find_location_references(text: str) -> list[LocationReference]:
    """Find every localisation characteristic in a text."""

    return [parse_location_reference(match.group("value")) for match in LOCATION_FINDER_PATTERN.finditer(text.upper())]

find_fire_sector_references

find_fire_sector_references(text)

Find every fire sectorisation characteristic in a text.

Source code in parc/naming/ecs/characteristics.py
def find_fire_sector_references(text: str) -> list[FireSectorReference]:
    """Find every fire sectorisation characteristic in a text."""

    return [parse_fire_sector_reference(match.group("value")) for match in FIRE_FINDER_PATTERN.finditer(text.upper())]

find_electrical_supply_references

find_electrical_supply_references(text)

Find every electrical supply characteristic in a text.

Source code in parc/naming/ecs/characteristics.py
def find_electrical_supply_references(text: str) -> list[ElectricalSupplyReference]:
    """Find every electrical supply characteristic in a text."""

    return [
        parse_electrical_supply_reference(match.group("value"))
        for match in ELECTRICAL_SUPPLY_FINDER_PATTERN.finditer(text.upper())
    ]

parc.naming.ecs.ecs

EDF ECS naming reference.

Source: [28] ENSIRM0100021 A - Codification ECS.pdf.

FUNCTIONAL_SET_CODES module-attribute

FUNCTIONAL_SET_CODES = MappingProxyType({
    (name): (value) for system in ElementarySystems
})

ELEMENTARY_SYSTEM_FAMILIES module-attribute

ELEMENTARY_SYSTEM_FAMILIES = MappingProxyType({
    "DA*": "Ascenseurs et monte-charges",
    "DC*": "Ventilation - conditionnement",
    "DE*": "Eau glacée (production et distribution)",
    "DF*": "Désenfumage (si indépendant de la ventilation)",
    "DM*": "Appareils et engins de manutention",
    "DN*": "Eclairage normal du site, bâtiments et surfaces ouvertes",
    "DS*": "Eclairage de secours du site, bâtiments et surfaces ouvertes",
    "DT.": "Téléphone, transmission et télétransmission",
    "DV*": "Ventilation - conditionnement (non contaminable)",
    "DW*": "Ventilation - désenfumage - conditionnement",
    "GD.": "Groupe à moteurs thermiques (diesels..)",
    "GZ": "Groupes turbines à combustion",
    "JA.": "Production eau d'incendie",
    "JD.": "Détection incendie",
    "JG*": "Protection incendie par gaz inerte",
    "JP.": "Protection incendie et distribution eau incendie",
    "KA.": "Aide à l'exploitation (conduite et maintenance)",
    "KC.": "Matériel de contrôle commande (logique et analogique)",
    "KG.": "Système informatique de gestion",
    "KI.": "Systèmes informatiques de conduite (de commande et surveillance)",
    "KK.": "Contrôle accès",
    "KL.": "Réseau informatique",
    "KR.": "Mesure d'ambiance",
    "KS.": "Salle de commande - panneaux de repli - et pupitres",
    "KT.": "Instrumentation spéciale indépendante",
    "KZ.": "Système informatique de surveillance ou commande à distance",
    "LA.": "Production et distribution puissance 230V continu",
    "LB.": "Production et distribution équipement 125 V continu",
    "LC.": "Production et distribution relayage 48 V continu",
    "LD.": "Production et distribution régulation 30V continu",
    "LE.": "Production et distribution régulation 24 V continu",
    "LG.": "Distribution > ou = à 5,5 kV alternatif (non secouru)",
    "LH#": "Courant alternatif > ou = à 5,5 kV secouru",
    "LI.": "Distribution 690 V alternatif normal",
    "LJ.": "Distribution 690 V alternatif secouru",
    "LK.": "Distribution 400 V alternatif normal (sous tableau LG)",
    "LL#": "Distribution 400 V alternatif secouru (sous tableau LH)",
    "LM.": "Distribution 220 V alternatif non régulée",
    "LN.": "Production et distribution 220 V alternatif sans coupure",
    "LO.": "Distribution 400V alternative régulée",
    "LP.": "Distribution 110 V alternatif non secouru",
    "LQ.": "Distribution 110 V alternatif secouru",
    "LR.": "Distribution 20 kV alternatif (10 kV selon les sites)",
    "LS.": "Boucles d'essai",
    "LV.": "Production et distribution 400V sans coupure",
    "LY.": "Circuit d'essai",
    "SA.": "Production et distribution d'air",
    "SB.": "Bloc entretien chaud",
    "SD.": "Eau déminéralisée (production - traitement - distribution - stockage)",
    "SE.": "Distribution et traitement de l'eau (sauf eau déminéralisée)",
    "SF.": "Filtration",
    "SG.": "Distribution fluide autre que eau et air",
    "SI.": "Chimie, réactifs et traitements",
    "SK.": "Stockage fluides autres que eau et air",
    "SM.": "Traitement des engins de manutention",
    "SN.": "Traitement par le vide nettoyage",
    "SP.": "Protection",
    "SR.": "Réfrigération",
    "ST.": "Traçage",
    "SV.": "Vapeur auxiliaire",
})

ELEMENTARY_SYSTEM_CODES module-attribute

ELEMENTARY_SYSTEM_CODES = MappingProxyType({
    "ABP": "Poste d'eau basse pression et réchauffeurs",
    "ACA": "Alimentation en eau depuis l'entrée économiseur jusqu'au ballon y compris les pompes de circulation assistée",
    "ACO": "Reprise des condensats du poste d'eau",
    "ADA": "Ensemble motopompe alimentaire de démarrage et d'arrêt",
    "ADG": "Alimentation et dégazage (bâche et dégazeur)",
    "AFR": "Fluide de régulation turbopompe alimentaire",
    "AGR": "Graissage soulèvement virage turbopompe alimentaire",
    "AHP": "Poste d'eau haute pression, moyenne pression et réchauffeurs",
    "APA": "Ensemble motopompe alimentaire (y compris graissage)",
    "APG": "Purge générateur de vapeur (chaudière)",
    "APP": "Ensemble turbopompe alimentaire",
    "ARE": "Alimentation normale des générateurs de vapeur",
    "ASG": "Alimentation auxiliaire de secours des GV",
    "ATH": "Traitement d'huile turbopompe alimentaire",
    "BAC": "Alimentation charbon vers la chaudière",
    "BDC": "Décrasseur",
    "BED": "Stockage et évacuation des refus broyeurs",
    "BEH": "Evacuation hydraulique des suies et machefers",
    "BEM": "Evacuation machefers stockage destockage",
    "BES": "Evacuation et stockage des suies sèches",
    "BKE": "Dépotage stockage fioul usine (engins manutention appareil chauffage)",
    "BKI": "Dépotage stockage fioul domestique",
    "BKO": "Dépotage stockage fioul lourd",
    "BKP": "Stockage gaz (propane)",
    "BMC": "Déchargement stockage et reprise sur parc",
    "BSC": "Stockage calcaire",
    "BSG": "Stockage gypse humide",
    "BSH": "Reprise et évacuation gypse humide",
    "BSP": "Injection et résidu des produits de désulfuration primaire (injection au foyer)",
    "BSS": "Injection et résidu des produits de désulfuration secondaire (traitement des fumées)",
    "BVF": "Voies ferrées",
    "CDI": "Eau de dilution - Refroidissement des rejets",
    "CET": "Etanchéité labyrinthes turbine et turbopompe alimentaire",
    "CEX": "Circuit d'extraction (pompe de reprise)",
    "CPA": "Protection cathodique",
    "CRF": "Circulation eau (graissage, filtration et isolement)",
    "CTA": "Nettoyage du faisceau condenseur (taprogge, technos)",
    "CTE": "Traitement eau de circulation",
    "CTF": "Vaccination acide des réfrigérants",
    "CVF": "Réfrigérants atmosphériques - Ventilation forcée",
    "CVG": "Circuit de vide galeries station de pompage",
    "CVI": "Vide condenseur",
    "CVP": "Réfrigération des purges des réfrigérants atmosphériques",
    "DTD": "Transport pneumatique de documents",
    "DTE": "Transport pneumatique des échantillons",
    "DTF": "Réseau informatique ((bureautique)",
    "DTL": "Distribution de télévision en circuit fermé",
    "DTV": "Transmission - téléphone - recherche de personnes - distribution de l'heure",
    "EAS": "Aspersion - recirculation de l'aspersion",
    "EAU": "Instrumentation de l'enceinte (auscultations )",
    "EBA": "Balayage du Bâtiment Réacteur",
    "EDE": "Mise en dépression de l'espace entre enceinte",
    "ENC": "Noyage du corium",
    "EPP": "Etanchéité et contrôle des fuites de l'enceinte (sas, traversées, tampons...)",
    "ETY": "Contrôle H2 en cas d'APRP et d'accident grave",
    "EVF": "Filtration interne",
    "EVR": "Ventilation continue bâtiment réacteur",
    "EVU": "Evacuation de chaleur du bâtiment réacteur",
    "FAR": "Air de refroidissement du contrôle de flamme",
    "FBU": "Contrôle flamme (caméra + cellule)",
    "FCA": "Air de combustion (soufflage)",
    "FCR": "Cannes rétractiles",
    "FDA": "Désox recirculation absorbeur",
    "FDB": "Désox alimentations absorbeur",
    "FDG": "Désox centrifugation",
    "FDP": "Désox purge de l'absorbeur",
    "FDR": "Désox réchauffage des fumées désul furées",
    "FDT": "Désox tirage additionnel",
    "FEU": "Enveloppe physique de la chaudière (trappe, trou d'homme, poste, regards)",
    "FFU": "Traitement des fumées (dépoussiéreurs désenfumeurs)",
    "FGA": "Brûleurs d'allumage (démarrage)",
    "FGC": "Brûleurs de charge ou de soutien",
    "FGI": "Torches pilotes",
    "FKP": "Alimentation gaz",
    "FMA": "Air d'étanchéité broyeurs",
    "FOA": "Broyeurs",
    "FPA": "Préchauffeurs d'air",
    "FPI": "Poste préparation fioul domestique",
    "FPO": "Poste de préparation fioul lourd",
    "FRA": "Réchauffeur d'air régénératif",
    "FRM": "Ramonage",
    "FRT": "Reprise égoutture",
    "FSA": "Air primaire commun",
    "FTA": "Tirage (circuit d'air de combustion)",
    "FTC": "Essais de cannes de brûleurs (banc de nettoyage)",
    "FYA": "Recyclage (ventilation recyclage des fumées ou air chaud)",
    "GBP": "Contournement turbine BP (MP)",
    "GCA": "Conservation de la turbine à l'arrêt",
    "GCT": "Contournement turbine condenseur",
    "GDA": "Génératrice asynchrone",
    "GDG": "Circuit d'huile",
    "GDK": "Circuit combustible",
    "GDM": "Groupes électrogènes principaux",
    "GEA": "Transformateur auxiliaire",
    "GEV": "Evacuation d'énergie (transfo soutirage inclus)",
    "GEX": "Excitation et régulation alternateur",
    "GFR": "Fluide de régulation turbine",
    "GGR": "Graissage - soulèvement - virage",
    "GHE": "Huile d'étanchéité alternateur",
    "GHP": "Contournement turbine HP",
    "GME": "Eléments mesure turbine",
    "GPA": "Protection alternateur et évacuation d'énergie",
    "GPV": "Circuits principaux de vapeur turbine et purges",
    "GRE": "Réglage et contrôle turbine",
    "GRH": "Réfrigération de l'alternateur (air ou hydrogène)",
    "GRV": "Remplissage - vidange - appoint H2",
    "GSE": "Sécurités turbine (protections)",
    "GSS": "Sécheurs surchauffeurs",
    "GST": "Eau stator",
    "GTH": "Traitement d'huile",
    "GTR": "Téléréglage - télémesures - comptage",
    "GZA": "Admission d'air de la turbine - système anti-givrage",
    "GZD": "Système de démarrage turbine",
    "GZE": "Echappement turbine",
    "GZI": "Alimentation des injecteurs en fioul domestique",
    "GZL": "Lavage compresseur",
    "GZO": "Injection d'eau déminéralisée - réduction Nox",
    "GZP": "Système d'alimentation en gaz",
    "GZV": "Turbo alternateur (réfrigération, ventilation, sécurité, chambre à combustion)",
    "JAC": "Production eau incendie classée",
    "JAN": "Production eau incendie non classée",
    "JDT": "Détection incendie",
    "JPD": "Protection et distribution eau incendie îlot conventionnel",
    "JPH": "Protection et distribution eau incendie cuves à huile Salle des Machines",
    "JPI": "Protection et distribution eau incendie îlot nucléaire",
    "JPL": "Protection et distribution eau incendie du bâtiment électrique",
    "JPN": "Protection et distribution eau incendie du bloc usine",
    "JPQ": "Protection et distribution eau incendie du bâtiment de traitement des effluents",
    "JPS": "Protection et distribution eau incendie de site",
    "JPT": "Protection et distribution eau incendie des transformateurs",
    "JPV": "Protection et distribution eau incendie Diesels",
    "KAC": "Aide à la consignation",
    "KBS": "Boîte de soudure froide (mesure analogique)",
    "KCC": "Télétransmission avec CNC (Centres Nationaux de Crise)",
    "KCM": "Surveillance des matériels de contrôle-commande de la salle des machine",
    "KCO": "Matériels de contrôle - commande centralisés (automate et traitement)",
    "KCU": "Surveillance des matériels de contrôle-commande de la station de pompage",
    "KDO": "Système d'acquisition de données",
    "KIC": "Système informatique de conduite",
    "KIF": "Contrôle informatique de fonctionnement",
    "KIR": "Instrumentation de surveillance du circuit primaire",
    "KIT": "Traitement des informations (TCI) (traitement centralisé de l'information)",
    "KKK": "Contrôle général des accès",
    "KKV": "Surveillance vidéo de site",
    "KLE": "Réseau local d'entreprise",
    "KLI": "Réseau local industriel",
    "KME": "Acquisition de mesure pour essai périodique",
    "KOS": "Oscillo perturbographie",
    "KPE": "Tachy perturbographie",
    "KRA": "Mesures séisme",
    "KRC": "Contrôle de contamination corporelle et dosimétrique",
    "KRG": "Régulation générale",
    "KRH": "Détection hydrogène îlot nucléaire",
    "KRS": "Contrôle de pollution (radioprotection - météorologie - pollution)",
    "KRT": "Mesure de santé (radioprotection tranche)",
    "KSA": "Traitement des alarmes",
    "KSC": "Instrumentation salle de Commande",
    "KSD": "Système de surveillance et de diagnostic(PSAD)",
    "KSR": "Panneau de repli",
    "KSU": "Platine de sauvegarde et d'alerte",
    "KTC": "Contrôle du combustible",
    "KTF": "Contrôle de la composition des fumées",
    "KTG": "Auscultation table de groupe et réfrigérants atmosphériques",
    "KTR": "Contrôle des résidus et coproduits de combustion",
    "KZC": "Centre national de crise",
    "KZR": "Calculateur d'interface de tranche CASOAR",
    "LHP": "Diesel division 1 (> ou = à 5,5 kV)",
    "LHQ": "Diesel division 2 (> ou = à 5,5 kV)",
    "LHR": "Diesel division 3 (> ou = à 5,5 kV)",
    "LHS": "Diesel division 4 (> ou = à 5,5 kV)",
    "LJP": "Diesel de secours 690V division 1",
    "LJS": "Diesel de secours 690V division 4",
    "LLP": "Diesel 400 V alternatif voie A",
    "LLQ": "Diesel 400 V alternatif voie B",
    "LTR": "Circuit de terre",
    "LYS": "Circuit d'essai de batteries",
    "PMB": "Manutention emballage du combustible",
    "PMC": "Manutention du combustible - machine de chargement - ponts passerelle et auxiliaire",
    "PME": "Postes d'examen du combustible neuf et usagé",
    "PMG": "Machine à serrer et desserrer les goujons",
    "PML": "Eclairage immergé",
    "PMO": "Outillage de manutention",
    "PMT": "Manutention du combustible : transfert et ascenseur",
    "PTR": "Traitement et refroidissement d'eau des piscines",
    "RAM": "Alimentation des mécanismes des grappes",
    "RBS": "Borication de sécurité",
    "RCP": "Circuit primaire",
    "RCV": "Contrôle chimique et volumétrique",
    "REA": "Appoint eau et bore",
    "REN": "Echantillonnage nucléaire",
    "RGL": "Commandes des grappes longues",
    "RIC": "Instrumentation interne du cœur",
    "RIS": "Injection de sécurité",
    "RPE": "Purges, évents et exhaures nucléaires",
    "RPN": "Mesure de la puissance nucléaire",
    "RPR": "Protection réacteur",
    "RRA": "Réfrigération à l'arrêt",
    "RRI": "Réfrigération intermédiaire",
    "RRM": "Ventilation de refroidissement des mécanismes de grappes",
    "SAA": "Production air respirable",
    "SAO": "Production d'air d'oxydation DESOX",
    "SAP": "Production air comprimé de travail et de régulation",
    "SAR": "Distribution air comprimé de régulation",
    "SAT": "Distribution air comprimé de travail",
    "SAX": "Moyen de conservation à l'arrêt",
    "SBE": "Equipement du bloc entretien chaud du site y compris la laverie de site",
    "SDA": "Production d'eau déminéralisée",
    "SDB": "Traitement des boues de la déminéralisation",
    "SDD": "Distribution eau déminéralisée réacteur (stockage inclus)",
    "SDR": "Distribution eau déminéralisée Ph9 installation conventionnelle (stockage inclus)",
    "SDX": "Stockage des produits chimiques et de neutralisation des effluents de la déminéralisation",
    "SEA": "Eau à déminéraliser (pré-traitement)",
    "SEB": "Eau brute",
    "SEC": "Eau brute secourue (réfrigération intermédiaire RRI)",
    "SEF": "Prise d'eau- filtration- degrilleurs",
    "SEG": "Recueil et stockage de solution de gypse DESOX",
    "SEH": "Recueil des huiles et des effluents hydrocarbonés (stockage inclus)",
    "SEI": "Eaux industrielles",
    "SEJ": "Traitement des effluents hydrocarbonnés",
    "SEK": "Recueil, contrôle et rejet des effluents du circuit secondaire",
    "SEN": "Eau brute réfrigération SRI",
    "SEO": "Eaux perdues à l'égout",
    "SEP": "Eau potable",
    "SEQ": "Stockage et distribution eau propre",
    "SES": "Eau surchauffée",
    "SET": "Fourniture d'eau tiède à usage extérieur",
    "SEU": "Eau pluviale (collecte)",
    "SEV": "Traitement des rejets de désulfuration (Desox)",
    "SEW": "Traitement des rejets chaufferies (eaux cendreuses)",
    "SEZ": "Nappe phréatique (rabattement)",
    "SFI": "Filtration eau brute",
    "SGA": "Distribution d'argon",
    "SGC": "Distribution gaz carbonique",
    "SGH": "Distribution d'hydrogène",
    "SGL": "Distribution d'huile",
    "SGN": "Distribution d'azote",
    "SGO": "Distribution d'oxygène",
    "SIC": "Préparation lait calcaire (DESOX)",
    "SIR": "Conditionnement chimique (injection réactif)",
    "SIT": "Contrôle chimique (échantillonnage)",
    "SKA": "Stockage argon",
    "SKC": "Stockage gaz carbonique",
    "SKH": "Stockage hydrogéne",
    "SKL": "Stockage huile",
    "SKN": "Stockage d'azote",
    "SKO": "Stockage oxygène",
    "SKR": "Remplissage-vidange huile moteur des pompes primaires",
    "SKZ": "Stockage des gaz (H2, O2, N2, CO2 et gaz rares)",
    "SMW": "Dégelage des wagons et engins de manutention",
    "SNE": "Neutralisation des effluents de site",
    "SNL": "Nettoyage - lançage GV",
    "SNV": "Nettoyage par le vide de la salle des machines",
    "SRI": "Réfrigération intermédiaire - circuits conventionnels (NORIA)",
    "STE": "Traçage électrique",
    "STF": "Traçage électrique (circuits nucléaires)",
    "STV": "Traçage vapeur",
    "SVA": "Distribution de vapeur auxiliaire",
    "SVT": "Transformateur de vapeur",
    "TEG": "Effluents gazeux",
    "TEK": "Contrôle et rejet des effluents de l'îlot nucléaire",
    "TEN": "Circuits d'échantillonnage des effluents du BTE",
    "TEP": "Effluents liquides primaires",
    "TER": "Réservoir complémentaire de santé",
    "TES": "Effluents solides - tranche",
    "TEU": "Effluents liquides usés",
    "TRI": "Réfrigération intermédiaire traitement des effluents",
    "VDA": "Décharge atmosphère",
    "VPU": "Purges de conditionnement des circuits vapeur",
    "VRD": "Vapeur resurchauffée et désurchauffée",
    "VSC": "Soutirage vapeur S.B.T",
    "VSD": "Vapeur surchauffée et désurchauffe",
    "VVP": "Circuit vapeur principal, soupapes de mise à l'atmosphère et évents, vannes GV",
    "XCA": "Chaudière à fioul - chaudière électrique",
    "YBM": "Bras mort (instrumentation provisoire sur les tronçons inter-isolements)",
    "YGV": "Dispositif d'instrumentation GV",
})

KNOWN_ECS_CODES module-attribute

KNOWN_ECS_CODES = MappingProxyType({
    None: FUNCTIONAL_SET_CODES,
    None: ELEMENTARY_SYSTEM_FAMILIES,
    None: ELEMENTARY_SYSTEM_CODES,
})

ElementarySystems

Bases: StrEnum

Top-level ECS functional sets.

A class-attribute instance-attribute

A = "Alimentation en eau - poste d'eau"

B class-attribute instance-attribute

B = "Manutention, stockage des combustibles et de leurs résidus"

C class-attribute instance-attribute

C = 'Condensation, refroidissement (condenseur)'

D class-attribute instance-attribute

D = "Systèmes annexes non importants pour la production (divers)"

E class-attribute instance-attribute

E = 'Enceinte de confinement'

F class-attribute instance-attribute

F = 'Chaudière (combustion, générateur de vapeur)'

G class-attribute instance-attribute

G = "Groupe turboalternateur et évacuation d'énergie"

H class-attribute instance-attribute

H = 'Bâtiments'

J class-attribute instance-attribute

J = 'Incendie'

K class-attribute instance-attribute

K = 'Contrôle'

L class-attribute instance-attribute

L = 'Distribution électrique'

P class-attribute instance-attribute

P = 'Piscine de stockage du combustible'

R class-attribute instance-attribute

R = 'Réacteur'

S class-attribute instance-attribute

S = 'Services généraux liés à la production'

T class-attribute instance-attribute

T = 'Traitement des effluents'

V class-attribute instance-attribute

V = 'Circuit vapeur principal'

X class-attribute instance-attribute

X = 'Production vapeur auxiliaire'

Y class-attribute instance-attribute

Y = "Installations d'essai"

normalize_ecs_code

normalize_ecs_code(code)

Normalize a user-provided ECS code before lookup.

Source code in parc/naming/ecs/ecs.py
def normalize_ecs_code(code: str) -> str:
    """Normalize a user-provided ECS code before lookup."""

    return code.strip().upper().replace(" ", "")

describe_ecs_code

describe_ecs_code(code)

Return the ECS label associated with code if it exists.

Source code in parc/naming/ecs/ecs.py
def describe_ecs_code(code: str) -> str | None:
    """Return the ECS label associated with ``code`` if it exists."""

    return KNOWN_ECS_CODES.get(normalize_ecs_code(code))

parc.naming.ecs.geographic

Parser for ECS repères géographiques (RG).

RG_REGEX module-attribute

RG_REGEX = "(?P<tranche>[0-9])?(?P<building>H[A-Z]{2})(?P<identification>[0-9]{4})(?P<structure>[BCDEFJLMNPVX][A-Z][A-Z0-9-])(?P<extension>[A-Z0-9]{0,4})"

RG_PATTERN module-attribute

RG_PATTERN = compile(f'^{RG_REGEX}$')

RG_FINDER_PATTERN module-attribute

RG_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<value>{RG_REGEX})(?![A-Z0-9])"
)

GeographicIdentification dataclass

GeographicIdentification(raw)

Section 2 of an RG.

raw instance-attribute

raw

level property

level

order property

order

altitude_range property

altitude_range

GeographicReference dataclass

GeographicReference(
    tranche,
    building,
    identification,
    structure,
    extension="",
)

Parsed ECS repère géographique.

tranche instance-attribute

tranche

building instance-attribute

building

identification instance-attribute

identification

structure instance-attribute

structure

extension class-attribute instance-attribute

extension = ''

code property

code

building_label property

building_label

structure_meanings property

structure_meanings

extension_meanings property

extension_meanings

normalize_rg

normalize_rg(value)

Normalize a geographic reference.

Source code in parc/naming/ecs/geographic.py
def normalize_rg(value: str) -> str:
    """Normalize a geographic reference."""

    return value.strip().upper().replace(" ", "")

parse_rg

parse_rg(value)

Parse an ECS repère géographique.

Source code in parc/naming/ecs/geographic.py
def parse_rg(value: str) -> GeographicReference:
    """Parse an ECS repère géographique."""

    normalized = normalize_rg(value)
    match = RG_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS geographic reference: {value!r}"
        raise ValueError(msg)
    groups = match.groupdict()
    if groups["structure"][0] not in STRUCTURE_ELEMENT_CODES:
        msg = f"Invalid ECS geographic reference: {value!r}"
        raise ValueError(msg)
    return GeographicReference(
        tranche=groups["tranche"],
        building=parse_building_trigram(groups["building"]).code,
        identification=GeographicIdentification(groups["identification"]),
        structure=groups["structure"],
        extension=groups["extension"],
    )

is_rg

is_rg(value)

Return True if value is a valid ECS RG.

Source code in parc/naming/ecs/geographic.py
def is_rg(value: str) -> bool:
    """Return ``True`` if ``value`` is a valid ECS RG."""

    try:
        parse_rg(value)
    except ValueError:
        return False
    return True

build_rg_regex

build_rg_regex(
    *, tranche=None, building=None, structure=None
)

Build a regex for geographic references.

Source code in parc/naming/ecs/geographic.py
def build_rg_regex(
    *, tranche: str | None = None, building: str | None = None, structure: str | None = None
) -> re.Pattern[str]:
    """Build a regex for geographic references."""

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_rg(building)) if building is not None else r"H[A-Z]{2}")
        + r"[0-9]{4}"
        + (re.escape(normalize_rg(structure)) if structure is not None else r"[A-Z]{2}[A-Z0-9-]")
        + r"[A-Z0-9]{0,4}"
    )
    return re.compile(f"^{pattern}$")

find_rgs

find_rgs(text)

Extract geographic references from text.

Source code in parc/naming/ecs/geographic.py
def find_rgs(text: str) -> list[GeographicReference]:
    """Extract geographic references from text."""

    return [parse_rg(match.group("value")) for match in RG_FINDER_PATTERN.finditer(text.upper())]

parc.naming.ecs.matching

Wildcard helpers shared by ECS validators.

GLOB_STAR module-attribute

GLOB_STAR = '*'

GLOB_SINGLE module-attribute

GLOB_SINGLE = '?'

proposal_matches_candidate

proposal_matches_candidate(query, candidate)

Return True when a wildcard query matches a candidate value.

Source code in parc/naming/ecs/matching.py
def proposal_matches_candidate(query: str, candidate: str) -> bool:
    """Return ``True`` when a wildcard query matches a candidate value."""

    @cache
    def matches(query_index: int, candidate_index: int) -> bool:
        if query_index == len(query):
            return True
        if candidate_index == len(candidate):
            return all(char == GLOB_STAR for char in query[query_index:])

        query_token = query[query_index]
        if query_token == GLOB_STAR:
            return matches(query_index + 1, candidate_index) or matches(query_index, candidate_index + 1)
        if query_token == GLOB_SINGLE:
            return matches(query_index + 1, candidate_index + 1)
        if query_token == candidate[candidate_index]:
            return matches(query_index + 1, candidate_index + 1)
        return False

    return matches(0, 0)

proposal_can_match_completion

proposal_can_match_completion(query, prefix)

Return True if a prefix can still be completed to satisfy query.

Source code in parc/naming/ecs/matching.py
def proposal_can_match_completion(query: str, prefix: str) -> bool:
    """Return ``True`` if a prefix can still be completed to satisfy ``query``."""

    @cache
    def matches(query_index: int, prefix_index: int) -> bool:
        if query_index == len(query) or prefix_index == len(prefix):
            return True

        query_token = query[query_index]
        if query_token == GLOB_STAR:
            return matches(query_index + 1, prefix_index) or matches(query_index, prefix_index + 1)
        if query_token == GLOB_SINGLE:
            return matches(query_index + 1, prefix_index + 1)
        if query_token == prefix[prefix_index]:
            return matches(query_index + 1, prefix_index + 1)
        return False

    return matches(0, 0)

parc.naming.ecs.materials

EDF ECS material codes and helpers.

Source: [28] ENSIRM0100021 A - Codification ECS.pdf.

MATERIAL_CODE_LABELS module-attribute

MATERIAL_CODE_LABELS = MappingProxyType({
    "AA": ("Alarme conventionnelle",),
    "AC": ("Ascenseur", "Monte-charge"),
    "AD": ("Absorbeur",),
    "AE": ("Aérotherme",),
    "AG": ("Agitateur", "Vibreur"),
    "AI": ("Armoire incendie",),
    "AK": ("Point d'ancrage",),
    "AM": ("Amplificateur",),
    "AN": ("Alimentation stabilisée",),
    "AO": ("Anode",),
    "AP": ("Alternateur",),
    "AQ": ("Accumulateur fluide autre qu'électrique",),
    "AR": ("Armoire", "Armoire de distribution"),
    "AS": ("Assemblages combustibles",),
    "AU": ("Dispositif d'arrêt d'urgence",),
    "AV": ("Avaloir (eaux pluviales)",),
    "BA": (
        "Bâche",
        "Bouteille de gaz",
        "Bouteille tampon sur prise de pression",
        "Cuve",
        "Fosse septique",
        "Puisard",
        "Réservoir",
    ),
    "BC": ("Boîte de connexion (bloc essai)",),
    "BF": (
        "Borne fontaine",
        "Bouche d'arrosage",
        "Rampe d'aspersion",
        "Sprinkler",
    ),
    "BH": (
        "Bouche d'aération",
        "Bouche d'air et d'extraction",
    ),
    "BJ": (
        "Borne incendie",
        "Bouche incendie",
        "Poteau incendie",
    ),
    "BK": (
        "Mécanisme des grappes",
        "Unité de commande de barres",
    ),
    "BM": ("Injecteur",),
    "BN": ("Bornier", "Répartiteur"),
    "BO": ("Bouchon",),
    "BQ": ("Bloc sécurité (éclairage secours)",),
    "BR": ("Barres de contrôle et de sécurité",),
    "BS": ("Boîte de soudure froide",),
    "BT": ("Accumulateur électrique", "Batterie"),
    "BU": ("Batardeau",),
    "BV": ("Boîtier de voyants",),
    "BW": ("Refoulement d'air et de soufflage",),
    "BY": ("Broyeur",),
    "BZ": ("Caisson",),
    "CA": ("Câble",),
    "CB": ("Capacité", "Condensateur"),
    "CC": ("Commande de choix",),
    "CD": ("Commande diverse",),
    "CE": ("Composant électrique",),
    "CF": ("Centrifugeuse",),
    "CG": (
        "Commande calculateur logique",
        "Commande groupé logique",
        "Commande logique de fonction",
    ),
    "CH": ("Chaudière", "Générateur de vapeur"),
    "CI": ("Commande individuelle logique",),
    "CK": ("Commande de grappe",),
    "CL": ("Armoire de climatisation", "Climatiseur"),
    "CM": ("Serrure",),
    "CN": ("Colonne (appareil)",),
    "CO": ("Compresseur", "Surpresseur"),
    "CP": ("Coupleur (hydraulique ou mécanique)",),
    "CQ": ("Chassis",),
    "CR": ("boîtier d'essai PTT (en coffret)", "Coffret"),
    "CS": ("Condenseur",),
    "CU": ("Cuvelage",),
    "CV": ("Caniveau",),
    "CW": ("Commande d'appoint primaire",),
    "CY": ("Cheminée",),
    "DA": ("Dispositif auto bloquant",),
    "DB": ("Amortisseur",),
    "DD": (
        "Cheminée de désurchauffe",
        "Chemise de désurchauffe",
        "Tuyère de désurchauffe",
    ),
    "DE": ("Déminéraliseur", "Désioniseur"),
    "DG": ("Dégrilleur",),
    "DH": ("Déshuileur",),
    "DI": (
        "Diaphragme (autre que ceux de mesure)",
        "Limiteur de débit",
        "Obturateur",
        "Tuyère",
        "Venturi",
    ),
    "DJ": ("Emetteur/Récepteur infra-rouge",),
    "DK": ("Disque de rupture (membrane déchirable)",),
    "DL": ("Onduleur",),
    "DM": ("Connexion des dispositifs mobiles",),
    "DR": ("Distributeur (tiroir)",),
    "DS": ("Déshydratant", "Dessicateur", "Sécheur"),
    "DT": ("Cellule photo électrique", "Détecteur"),
    "DV": ("Distributeur vibrant",),
    "DX": ("Dépoussiéreur",),
    "DZ": ("Dégazeur",),
    "EA": ("Electroaimant",),
    "EB": ("Electrolyseur",),
    "EE": ("Electro d'embrayage",),
    "EG": ("Mélangeur",),
    "EJ": ("Ejecteur",),
    "EL": (
        "Electrovanne pilote (uniquement pour plusieurs vannes)",
    ),
    "EN": ("Courbe sur écran", "Enregistreur"),
    "EP": (
        "Convertisseur électropneumatique (uniquement pour plusieurs vannes)",
    ),
    "ER": ("Electrofrein",),
    "ES": ("Appareils d'éclairage",),
    "ET": ("Extracteur",),
    "EU": ("Humidificateur d'air",),
    "EV": ("Evaporateur",),
    "EW": ("Electrode de référence (mesure PH)",),
    "EX": ("Echangeur",),
    "EZ": ("Extincteur",),
    "FA": ("Fiche d'alarme",),
    "FI": ("Filtre", "Pré filtre"),
    "FL": ("Flexible",),
    "FO": ("Fibre optique",),
    "FP": ("Fond plein",),
    "FU": ("Fusible",),
    "FX": (
        "Dispositif de fixation",
        "Point fixe d'accrochage",
    ),
    "GA": ("Générateur de courant alternatif",),
    "GC": ("Générateur de courant continu",),
    "GD": ("Générateur de fonction",),
    "GE": ("Groupe électrogène",),
    "GF": ("Groupe frigorifique",),
    "GL": ("Gaine de ventilation",),
    "GM": ("Générateur de mousse",),
    "GR": ("Graisseur", "Lubrificateur"),
    "GS": ("Siphon", "Siphon de sol"),
    "GT": ("Entonnoir", "Gate"),
    "GU": ("Générateur ultrasons",),
    "HA": ("Lampe", "LED", "Voyant"),
    "HB": ("Boule roulante", "Souris"),
    "HC": (
        "Calculateur",
        "Micro ordinateur",
        "Microprocesseur",
        "Unité centrale",
    ),
    "HD": ("afficheur", "Bargraphe", "Indicateur"),
    "HE": ("Instrumentation",),
    "HI": ("Imprimante", "Télescriptrice", "Télex"),
    "HK": ("Clavier fonctionnel", "Console système"),
    "HL": (
        "Lecteur de badges",
        "Lecteur de Bande",
        "Streamer",
    ),
    "HN": ("Liaison fil à fil",),
    "HP": ("Pot de visualisation",),
    "HQ": ("Demultiplexeur", "Multiplexeur"),
    "HR": ("Horloge",),
    "HS": ("Indicateur de circulation",),
    "HT": ("Interphonie", "Moyens de communication"),
    "HV": ("Ecran",),
    "HW": ("Antenne",),
    "HX": ("Alarme sonore", "Klaxon"),
    "HY": ("Modem", "Transceiver"),
    "JA": (
        "Appareil de coupure électrique",
        "Contacteur",
        "Disjoncteur",
    ),
    "JB": ("Jeu de barres",),
    "JC": ("Cellule électrique",),
    "JO": ("Joint", "Joint de dilatation"),
    "JP": ("Pont de barre",),
    "JQ": ("Contacteur statique",),
    "JR": ("Réserve 380 V et 6,6 kV",),
    "JS": ("Sectionneur",),
    "JT": ("Sectionneur de mise à la terre",),
    "JW": ("Parafoudre",),
    "KA": ("Alarme sur écran",),
    "KD": ("Orifice déprimogène de mesure de débit",),
    "KI": ("Crépine",),
    "KM": ("Information analogique élaborée",),
    "KR": ("Cryogénérateur",),
    "KS": ("Information logique élaborée",),
    "KT": ("Elément primaire de température",),
    "LA": ("Chariot de manutention",),
    "LB": ("Câble de levage",),
    "LC": ("Potence",),
    "LD": ("Dispositif de chargement et de manutention",),
    "LF": (
        "Fer de roulement",
        "Poutre de manutention",
        "Rail",
    ),
    "LG": ("Grappin",),
    "LI": ("Trappe de manutention",),
    "LM": ("Moufle",),
    "LP": ("Palan", "Treuil"),
    "LR": ("Pont", "Pont roulant", "Portique"),
    "LT": (
        "Tapis de transfert",
        "Transfert",
        "Transporteur",
    ),
    "MA": (
        "Mesure d'activité",
        "Mesure de flux",
        "Mesure de rayonnement",
    ),
    "MC": ("Mesure de vitesse",),
    "MD": ("Mesure de débit",),
    "ME": ("Mesure acoustique",),
    "MF": ("Mesure de fréquence", "Mesure de phase"),
    "MG": ("Mesure d'analyse physico-chimique",),
    "MH": ("Mesure de temps",),
    "MI": ("Mesure d'intensité",),
    "MJ": ("Détecteur incendie",),
    "ML": ("Mesure d'opacité", "Mesure de luminosité"),
    "MM": ("Mesure de déplacement", "Mesure de position"),
    "MN": ("Mesure de niveau",),
    "MO": (
        "Moteur (uniquement pour les actionneurs à moteur multiple)",
    ),
    "MP": ("Mesure de pression",),
    "MQ": ("Mesure de puissance réactive",),
    "MR": (
        "Mesure d'impédance",
        "Mesure de conductivité",
        "Mesure de résistance",
        "Mesure de résistivité",
    ),
    "MS": ("Mesure santé",),
    "MT": ("Mesure de température",),
    "MU": ("Mesure de tension",),
    "MV": (
        "Mesure de dilatation",
        "Mesure de poussée",
        "Mesure de séisme",
        "Mesure de vibration",
    ),
    "MW": ("Mesure de puissance active",),
    "MX": ("Mesure divers mécanique",),
    "MY": ("Mesure divers électrique",),
    "MZ": ("Mesure divers physique",),
    "NA": ("Nacelle",),
    "ND": ("Nœud",),
    "NE": ("Fonction de base",),
    "NF": ("Sous-fonction",),
    "NL": ("Liaison fonctionnelle",),
    "PB": ("Piège à son", "Silencieux"),
    "PE": ("Postiche élément combustible",),
    "PG": ("Pompe électromagnétique",),
    "PI": ("Poste incendie",),
    "PJ": ("Connecteur", "Prise", "Prise informatique"),
    "PL": ("Palier",),
    "PN": ("Piston", "Vérin"),
    "PO": ("Pompe",),
    "PP": ("Pupitre",),
    "PQ": ("Presse à compacter",),
    "PT": ("Strap",),
    "PU": ("Purgeur",),
    "PX": ("Poste examen du combustible",),
    "QA": ("Compteur d'activité",),
    "QC": ("Compte tour",),
    "QD": ("Compteur volumétrique",),
    "QH": ("Compteur de temps",),
    "QM": ("Compteur de manœuvres",),
    "QN": ("Compteur numérique",),
    "QQ": ("Compteur d'énergie réactive",),
    "QW": ("Compteur d'énergie active",),
    "QX": ("Compteur d'événements",),
    "RA": ("Registre d'air (isolement ou réglage)",),
    "RB": ("Rampe de bouteilles",),
    "RD": ("Redresseur",),
    "RE": ("Réchauffeur non électrique",),
    "RF": (
        "Batterie froide",
        "Réfrigérant",
        "Refroidisseur d'air",
    ),
    "RG": ("Commande réglante groupée",),
    "RI": ("Commande réglante individuelle",),
    "RJ": ("Raccord incendie",),
    "RK": ("Rack",),
    "RP": ("Refroidisseur de purges ou de condensats",),
    "RR": (
        "Multiplicateur de vitesse",
        "Réducteur de vitesse",
        "Variateur de vitesse",
    ),
    "RS": (
        "Convecteur",
        "Elément de préchauffage",
        "Réchauffeur électrique",
        "Résistance chauffante",
    ),
    "RV": ("Recombineurs d'hydrogène",),
    "RW": ("Répéteur multiport (HUB)",),
    "RX": ("Régime (de consignation, d'essai...)",),
    "RY": ("Manchette démontable", "Raccord par bride"),
    "SA": ("TOR neutronique d'activité - flux",),
    "SC": ("TOR de vitesse",),
    "SD": ("Contrôleur de circulation", "TOR de débit"),
    "SE": ("TOR acoustique",),
    "SF": ("TOR de fréquence - phase",),
    "SG": ("TOR d'analyse physico-chimique",),
    "SH": ("TOR détecteur à seuil d'humidité",),
    "SI": ("TOR d'intensité",),
    "SJ": ("TOR détecteur d'incendie",),
    "SK": ("TOR de contrainte",),
    "SL": ("TOR de luminosité",),
    "SM": (
        "Fin de course (PMC)",
        "TOR de déplacement",
        "TOR de position",
    ),
    "SN": ("TOR de niveau",),
    "SP": ("TOR de pression",),
    "SR": (
        "TOR d'impédance",
        "TOR de conductivité",
        "TOR de résistance",
    ),
    "SS": ("TOR de santé",),
    "ST": ("Thermostat", "TOR de température"),
    "SU": ("TOR de présence tension",),
    "SV": (
        "TOR de dilatation",
        "TOR de poussée",
        "TOR de vibration",
    ),
    "SX": ("TOR divers mécanique",),
    "SY": (
        "Information TOR venant de la régulation",
        "TOR divers électrique",
    ),
    "SZ": ("TOR divers physique",),
    "TA": ("Transformateur auxiliaire réseau",),
    "TB": ("Tableau",),
    "TC": ("Turbine",),
    "TF": ("Grilles filtrantes", "Tambours filtrants"),
    "TH": ("Thermocouple",),
    "TI": ("Transformateur d'intensité",),
    "TO": (
        "Bouton poussoir",
        "Commutateur aveugle",
        "Mécanisme de verrouillage à clé",
        "Touche",
    ),
    "TP": ("Transformateur principal",),
    "TR": ("Transformateur de puissance",),
    "TS": ("Transformateur de soutirage",),
    "TT": ("Puits de terre", "Regard de terre"),
    "TU": ("Transformateur de tension",),
    "TV": ("Auto-transformateur de puissance",),
    "TW": ("Traversée",),
    "TX": ("Transformateur de vapeur",),
    "TY": ("Tuyauterie",),
    "UP": ("Unité de polarité",),
    "UR": ("Platine relais", "Unité de relayage"),
    "US": ("Unité d'isolement",),
    "UU": ("Variateur de tension",),
    "VA": ("Vanne d'air",),
    "VB": ("Vanne eau borée et non primaire",),
    "VC": ("Vanne eau de circulation",),
    "VD": ("Vanne eau déminéralisée",),
    "VE": ("Vanne eau brute",),
    "VF": ("Vanne combustible principal",),
    "VG": ("Vanne CO2- gaz divers",),
    "VH": ("Vanne d'huile",),
    "VI": ("Vanne d'air de ventilation",),
    "VJ": ("Vanne effluents gazeux",),
    "VK": ("Vanne effluents liquides",),
    "VL": ("Vanne eau de condensation",),
    "VM": (
        "Vanne combustible d'allumage (propane - mazout)",
    ),
    "VN": ("Vanne eau de circuit Noria",),
    "VP": ("Vanne eau primaire",),
    "VQ": ("Vanne liquide organique",),
    "VR": ("Vanne réactif",),
    "VS": ("Vanne effluents solides (boues, suies ...)",),
    "VT": ("Vanne eau potable - eau de nappe",),
    "VV": ("Vanne vapeur",),
    "VX": ("Vanne argon",),
    "VY": ("Vanne hydrogène",),
    "VZ": ("Vanne azote",),
    "WB": ("Volet bas (cellule)",),
    "WH": ("Volet haut (cellule)",),
    "WM": ("Electroménager",),
    "WN": ("Télémanipulateur",),
    "WO": ("Machine-outil", "Outillage"),
    "WV": ("Raccord rapide",),
    "XB": ("Relais bistable",),
    "XC": ("Relais à contact de passage",),
    "XH": ("Relais de fréquence",),
    "XI": ("Relais d'intensité",),
    "XK": ("Relais de défaut",),
    "XP": ("Relais d'antipompage",),
    "XR": (
        "Relais duplex radio (émetteur-récepteur)",
        "Relais instantanés autres que définis dans ce tableau (répétiteurs..)",
    ),
    "XS": ("Relais de surcharge",),
    "XT": ("Relais auxiliaire temporisé (cas général)",),
    "XU": ("Relais de tension", "Relais voltmétrique"),
    "XW": ("Relais de puissance",),
    "XZ": ("Relais de détection de terre",),
    "YC": ("Image de conduite",),
    "YE": ("Image de suivi d'équipement",),
    "YM": ("Image menu",),
    "YP": ("Image de procédure",),
    "YR": ("Renvoi fléché",),
    "YS": ("Image de suivi de situation",),
    "ZD": ("Soufflet de dilatation",),
    "ZE": ("Séparateur",),
    "ZF": ("Surchauffeur (quand séparé du sécheur)",),
    "ZI": ("Silencieux",),
    "ZK": ("Synchrocoupleur",),
    "ZM": ("Servomoteur",),
    "ZN": ("Sonde à résistance",),
    "ZO": ("Soudeuse",),
    "ZS": ("Sas",),
    "ZV": ("Soufflante", "Ventilateur"),
    "ZZ": ("Sécheur-surchauffeur",),
})

SENSOR_ANALOG_BIGRAMS module-attribute

SENSOR_ANALOG_BIGRAMS = frozenset({
    "MA",
    "MC",
    "MD",
    "ME",
    "MF",
    "MG",
    "MH",
    "MI",
    "MJ",
    "ML",
    "MM",
    "MN",
    "MP",
    "MQ",
    "MR",
    "MS",
    "MT",
    "MU",
    "MV",
    "MW",
    "MX",
    "MY",
    "MZ",
})

SENSOR_TOR_BIGRAMS module-attribute

SENSOR_TOR_BIGRAMS = frozenset({
    "SA",
    "SC",
    "SD",
    "SE",
    "SF",
    "SG",
    "SH",
    "SI",
    "SJ",
    "SK",
    "SL",
    "SM",
    "SN",
    "SP",
    "SR",
    "SS",
    "ST",
    "SU",
    "SV",
    "SX",
    "SY",
    "SZ",
})

CONTROL_LOCATION_QUALIFIERS module-attribute

CONTROL_LOCATION_QUALIFIERS = MappingProxyType({
    "P": "Moyen de conduite principal (MCP)",
    "S": "Moyen de conduite de secours (MCS)",
    "R": "Moyen de conduite de repli",
    "L": "Moyen de conduite local",
    "T": "Moyen de conduite décentralisé",
})

ACTIONNEUR_STATE_QUALIFIERS module-attribute

ACTIONNEUR_STATE_QUALIFIERS = MappingProxyType({
    "1": "Enclenché",
    "2": "Disponible",
    "3": "Vanne ouverte (sur fin de course moteur) ou actionneur enclenché",
    "4": "Vanne ouverte (sur fin de course de tige) ou matériel THT ouvert ou déclenché",
    "5": "Vanne fermée (sur fin de course moteur) ou actionneur déclenché",
    "6": "Vanne fermée (sur fin de course de tige) ou matériel THT fermé ou enclenché",
    "7": "Défaut électrique",
    "8": "Première position intermédiaire depuis l'ouverture vers la fermeture",
    "9": "Deuxième position intermédiaire depuis l'ouverture vers la fermeture",
})

MATERIAL_QUALIFIER_HINTS module-attribute

MATERIAL_QUALIFIER_HINTS = MappingProxyType({
    "HA": MappingProxyType({
        "-": "Etat nominal du capteur ou de la séquence",
        "2": "Organe indisponible",
        "3": "Vanne ouverte (sur fin de course moteur) ou actionneur enclenché",
        "4": "Vanne ouverte (sur fin de course tige)",
        "5": "Vanne fermée (sur fin de course moteur) ou actionneur déclenché",
        "6": "Vanne fermée (sur fin de course tige)",
        "7": "Défaut électrique ou défaut sur processus/séquence",
    }),
    "CA": MappingProxyType({
        "A": "Câble moyenne tension",
        "B": "Câble basse tension",
        "C": "Câble de contrôle",
        "F": "Câble informatique",
        "H": "Câble haute tension",
        "I": "Câble interphone",
        "M": "Liaison mesure",
        "S": "Câble de sonorisation",
        "T": "Câble téléphone",
    }),
    "BN": MappingProxyType({
        "D": "Raccordements divers",
        "E": "Electrique",
        "I": "Informatique",
        "T": "Raccordement PTT",
    }),
    "BC": MappingProxyType({
        "D": "Raccordements divers",
        "E": "Electrique",
        "I": "Informatique",
        "T": "Raccordement PTT",
    }),
    "PJ": MappingProxyType({
        "D": "Raccordements divers",
        "E": "Electrique",
        "I": "Informatique",
        "T": "Raccordement PTT",
    }),
    "DT": MappingProxyType({
        "H": "Hyper fréquence",
        "I": "Infrarouge",
        "J": "Incendie",
    }),
    "CE": MappingProxyType({
        "C": "Condensateur",
        "D": "Diode ou thyristor",
        "L": "Self ou inductance",
        "R": "Résistance, potentiomètre ou shunt",
        "T": "Télérupteur",
    }),
    "HE": MappingProxyType({
        "C": "Chromatographe",
        "K": "Convertisseur électrique ou changeur de fréquence",
        "O": "Oscilloperturbographe",
        "T": "Tachyperturbographe",
    }),
    "HT": MappingProxyType({
        "B": "Recherche de personnes / beeper",
        "G": "Généphone",
        "I": "Interphone",
        "L": "Haut parleur",
        "M": "Microphone",
        "T": "Téléphone",
        "Y": "Radio / talkie walkie",
        "Z": "Téléphone de sûreté",
    }),
    "TW": MappingProxyType({
        "E": "Traversée extérieure gauche",
        "I": "Traversée intérieure gauche",
    }),
    "FL": MappingProxyType({"P": "Flexible provisoire"}),
    "FP": MappingProxyType({"P": "Fond plein provisoire"}),
    "PT": MappingProxyType({"P": "Strap provisoire"}),
    "TY": MappingProxyType({"B": "Branche de tuyauterie"}),
    "GL": MappingProxyType({
        "B": "Branche de gaine de ventilation"
    }),
    "FI": MappingProxyType({
        "A": "Filtre de très haute efficacité (absolu)",
        "D": "Filtre provisoire / filtre de démarrage",
        "F": "Filtre de très haute efficacité (fin)",
        "I": "Piège à iode",
        "P": "Filtre de moyenne efficacité (préfiltre)",
    }),
    "JO": MappingProxyType({
        "E": "Joint d'étanchéité à presse étoupe",
        "G": "Joint d'étanchéité à garniture mécanique",
        "I": "Joint isolant",
        "J": "Joint plein",
    }),
    "BA": MappingProxyType({
        "B": "Ballon",
        "N": "Bouteille de niveau",
        "P": "Trou d'homme",
        "T": "Bouteille tampon sur prise de pression",
    }),
    "RB": MappingProxyType({"G": "Bouteille de gaz"}),
    "GT": MappingProxyType({"E": "Entonnoir", "G": "Gate"}),
    "PO": MappingProxyType({
        "C": "Chassis",
        "K": "Corps",
        "M": "Moteur de l'organe",
    }),
    "ZV": MappingProxyType({"C": "Chassis", "K": "Corps"}),
    "CV": MappingProxyType({
        "C": "Caniveau de câble",
        "H": "Chambre de tirage de câble",
    }),
    "CM": MappingProxyType({
        "L": "Serrure électrique",
        "M": "Serrure mécanique",
    }),
    "BZ": MappingProxyType({
        "C": "Caisson de batterie froide",
        "F": "Caisson de filtre",
        "H": "Caisson de réchauffeur",
    }),
    "AS": MappingProxyType({
        "A": "Absorbant",
        "C": "Couverture",
        "E": "Protection / écran",
        "F": "Combustible",
        "K": "Assemblage avec absorbant consommable",
        "L": "Restricteur de flux",
        "M": "Modérateur",
        "N": "Plénum",
        "R": "Réflecteur",
        "S": "Source de neutron",
        "X": "Assemblage spécial",
    }),
    "RX": MappingProxyType({
        "C": "Consignation",
        "E": "Essai",
        "I": "Intervention immédiate",
        "R": "Réquisition",
        "T": "Exceptionnel de travaux",
        "X": "Exploitation et lignage",
    }),
    "NL": MappingProxyType({
        "E": "Liaison électrique",
        "T": "Liaison fluide",
        "V": "Liaison ventilation",
    }),
})

TRANSFORMER_BIGRAMS module-attribute

TRANSFORMER_BIGRAMS = frozenset({
    "TA",
    "TI",
    "TP",
    "TR",
    "TS",
    "TU",
    "TV",
})

IMAGE_BIGRAMS module-attribute

IMAGE_BIGRAMS = frozenset({
    "YC",
    "YE",
    "YF",
    "YM",
    "YP",
    "YR",
    "YS",
})

NON_ASSOCIATED_COMMAND_BIGRAMS module-attribute

NON_ASSOCIATED_COMMAND_BIGRAMS = frozenset({
    "CC",
    "CD",
    "CG",
    "CI",
    "RG",
    "RI",
})

ALARM_BIGRAMS module-attribute

ALARM_BIGRAMS = frozenset({'AA', 'KA'})

MaterialCodeDescription dataclass

MaterialCodeDescription(
    code, bigram, qualifier, labels, qualifier_meanings
)

Structured description for an ECS material code.

code instance-attribute

code

bigram instance-attribute

bigram

qualifier instance-attribute

qualifier

labels instance-attribute

labels

qualifier_meanings instance-attribute

qualifier_meanings

normalize_material_code

normalize_material_code(code)

Normalize an ECS material code.

Source code in parc/naming/ecs/materials.py
def normalize_material_code(code: str) -> str:
    """Normalize an ECS material code."""

    return code.strip().upper().replace(" ", "")

split_material_code

split_material_code(code)

Split a material code into bigram and qualifier.

Source code in parc/naming/ecs/materials.py
def split_material_code(code: str) -> tuple[str, str]:
    """Split a material code into bigram and qualifier."""

    normalized = normalize_material_code(code)
    if len(normalized) == 2:
        return normalized, "-"
    if len(normalized) == 3:
        return normalized[:2], normalized[2]
    msg = f"Invalid ECS material code: {code!r}"
    raise ValueError(msg)

describe_material_bigram

describe_material_bigram(code)

Return labels associated with an ECS material bigram.

Source code in parc/naming/ecs/materials.py
def describe_material_bigram(code: str) -> tuple[str, ...] | None:
    """Return labels associated with an ECS material bigram."""

    return MATERIAL_CODE_LABELS.get(normalize_material_code(code)[:2])

describe_material_qualifier

describe_material_qualifier(bigram, qualifier)

Return known meanings for the third character of a material code.

Source code in parc/naming/ecs/materials.py
def describe_material_qualifier(bigram: str, qualifier: str) -> tuple[str, ...]:
    """Return known meanings for the third character of a material code."""

    bigram = normalize_material_code(bigram)[:2]
    qualifier = normalize_material_code(qualifier) or "-"
    meanings: list[str] = []

    exact = MATERIAL_QUALIFIER_HINTS.get(bigram)
    if exact and qualifier in exact:
        meanings.append(exact[qualifier])

    for helper in (
        _sensor_qualifier_meaning,
        _location_qualifier_meaning,
        _transformer_qualifier_meaning,
        _image_qualifier_meaning,
        _valve_qualifier_meaning,
        _generic_actionneur_meanings,
    ):
        meanings.extend(helper(bigram, qualifier))

    deduped: list[str] = []
    for meaning in meanings:
        if meaning not in deduped:
            deduped.append(meaning)
    return tuple(deduped)

describe_material_code

describe_material_code(code)

Return a structured description for an ECS material code.

Source code in parc/naming/ecs/materials.py
def describe_material_code(code: str) -> MaterialCodeDescription:
    """Return a structured description for an ECS material code."""

    bigram, qualifier = split_material_code(code)
    labels = MATERIAL_CODE_LABELS.get(bigram, ())
    return MaterialCodeDescription(
        code=f"{bigram}{qualifier}",
        bigram=bigram,
        qualifier=qualifier,
        labels=labels,
        qualifier_meanings=describe_material_qualifier(bigram, qualifier),
    )

describe_extension

describe_extension(material_code, extension)

Return known meanings for a section 4 extension.

Source code in parc/naming/ecs/materials.py
def describe_extension(material_code: str, extension: str) -> tuple[str, ...]:
    """Return known meanings for a section 4 extension."""

    code = normalize_material_code(material_code)
    extension = normalize_material_code(extension)
    if not extension:
        return ()

    bigram, qualifier = split_material_code(code)
    meanings: list[str] = []

    if code == "ARC" and len(extension) == 4:
        meanings.append(f"Carte en rack {extension[:2]}, position {extension[2:]}")

    if bigram in {"TY", "GL"} and len(extension) == 4:
        component_names = {
            "C": "Cintre",
            "E": "Coude",
            "L": "Piquage",
            "S": "Point de supportage ou de fixation",
            "T": "Té",
        }
        if extension[0] in component_names:
            meanings.append(f"{component_names[extension[0]]}{extension[1:]}")

    if bigram == "DT" and qualifier == "J" and len(extension) == 4:
        detector_types = {"I": "Détecteur individuel", "M": "Détecteur maître", "S": "Détecteur esclave"}
        if extension[0] in detector_types:
            meanings.append(f"{detector_types[extension[0]]} {extension[1:]}")

    if bigram in SENSOR_ANALOG_BIGRAMS | SENSOR_TOR_BIGRAMS:
        sensor_extensions = {"7": "Défaut capteur", "A": "Alimentation", "I": "Première mesure", "J": "Deuxième mesure"}
        if extension[0] in sensor_extensions:
            meanings.append(sensor_extensions[extension[0]])
        elif extension[0].isdigit():
            meanings.append(f"Seuil {extension[0]}")

    return tuple(meanings)

parc.naming.ecs.rf

Helpers to parse and manipulate ECS repères fonctionnels (RF).

RF_REGEX module-attribute

RF_REGEX = "(?P<tranche>[0-9])?(?P<system>[A-Z]{3})(?P<identification>[0-9]{3,4})(?P<material_bigram>[A-Z]{2})(?P<material_qualifier>[A-Z0-9-])(?P<extension>[A-Z0-9]{0,4})"

RF_PATTERN module-attribute

RF_PATTERN = compile(f'^{RF_REGEX}$')

RF_FINDER_PATTERN module-attribute

RF_FINDER_PATTERN = compile(
    f"(?<![A-Z0-9])(?P<rf>{RF_REGEX})(?![A-Z0-9])"
)

RF_QUALIFIER_CHARS module-attribute

RF_QUALIFIER_CHARS = (
    "-",
    *(tuple("0123456789")),
    *(tuple("ABCDEFGHIJKLMNOPQRSTUVWXYZ")),
)

RF_EXTENSION_CHARS module-attribute

RF_EXTENSION_CHARS = tuple(
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

RF_QUALIFIER_PASSES module-attribute

RF_QUALIFIER_PASSES = (
    ("-",),
    tuple(
        char for char in RF_QUALIFIER_CHARS if char != "-"
    ),
)

KNOWN_RF_SYSTEM_CODES module-attribute

KNOWN_RF_SYSTEM_CODES = tuple(
    sorted(
        code
        for code in KNOWN_ECS_CODES
        if len(code) == 3 and isalpha()
    )
)

KNOWN_RF_MATERIAL_BIGRAMS module-attribute

KNOWN_RF_MATERIAL_BIGRAMS = tuple(
    sorted(MATERIAL_CODE_LABELS)
)

IDENTIFICATION_CANDIDATES module-attribute

IDENTIFICATION_CANDIDATES = _identification_candidates()

IdentificationSection dataclass

IdentificationSection(raw)

Section 2 of an RF.

raw instance-attribute

raw

is_compact property

is_compact

sub_function property

sub_function

base_function property

base_function

order property

order

MaterialSection dataclass

MaterialSection(bigram, qualifier)

Section 3 of an RF.

bigram instance-attribute

bigram

qualifier instance-attribute

qualifier

code property

code

description property

description

labels property

labels

qualifier_meanings property

qualifier_meanings

FunctionalReference dataclass

FunctionalReference(
    tranche, system, identification, material, extension=""
)

Parsed ECS repère fonctionnel.

tranche instance-attribute

tranche

system instance-attribute

system

identification instance-attribute

identification

material instance-attribute

material

extension class-attribute instance-attribute

extension = ''

system_label property

system_label

canonical property

canonical

extension_meanings property

extension_meanings

with_extension

with_extension(extension)
Source code in parc/naming/ecs/rf.py
def with_extension(self, extension: str) -> FunctionalReference:
    return FunctionalReference(
        tranche=self.tranche,
        system=self.system,
        identification=self.identification,
        material=self.material,
        extension=normalize_rf_token(extension),
    )

RFValidationResult dataclass

RFValidationResult(
    query,
    normalized_query,
    is_exact_match,
    candidates,
    truncated=False,
)

Result of an RF validation or completion attempt.

query instance-attribute

query

normalized_query instance-attribute

normalized_query

is_exact_match instance-attribute

is_exact_match

candidates instance-attribute

candidates

truncated class-attribute instance-attribute

truncated = False

uses_pattern property

uses_pattern

has_candidates property

has_candidates

normalize_rf_token

normalize_rf_token(value)

Normalize a single RF token.

Source code in parc/naming/ecs/rf.py
def normalize_rf_token(value: str) -> str:
    """Normalize a single RF token."""

    return value.strip().upper().replace(" ", "")

normalize_rf

normalize_rf(value)

Normalize a full RF while preserving ECS semantics.

Source code in parc/naming/ecs/rf.py
def normalize_rf(value: str) -> str:
    """Normalize a full RF while preserving ECS semantics."""

    return normalize_rf_token(value)

normalize_rf_query

normalize_rf_query(value)

Normalize an RF validation query while preserving * and ?.

Source code in parc/naming/ecs/rf.py
def normalize_rf_query(value: str) -> str:
    """Normalize an RF validation query while preserving ``*`` and ``?``."""

    return normalize_rf_token(value)

parse_rf

parse_rf(value)

Parse an ECS repère fonctionnel.

Source code in parc/naming/ecs/rf.py
def parse_rf(value: str) -> FunctionalReference:
    """Parse an ECS repère fonctionnel."""

    normalized = normalize_rf(value)
    match = RF_PATTERN.fullmatch(normalized)
    if not match:
        msg = f"Invalid ECS RF: {value!r}"
        raise ValueError(msg)

    groups = match.groupdict()
    system = normalize_ecs_code(groups["system"])
    if describe_ecs_code(system) is None:
        msg = f"Invalid ECS RF: {value!r}"
        raise ValueError(msg)
    return FunctionalReference(
        tranche=groups["tranche"],
        system=system,
        identification=IdentificationSection(groups["identification"]),
        material=MaterialSection(groups["material_bigram"], groups["material_qualifier"]),
        extension=groups["extension"],
    )

is_rf

is_rf(value)

Return True if value is a valid ECS RF.

Source code in parc/naming/ecs/rf.py
def is_rf(value: str) -> bool:
    """Return ``True`` if ``value`` is a valid ECS RF."""

    try:
        parse_rf(value)
    except ValueError:
        return False
    return True

suggest_rf_candidates

suggest_rf_candidates(query, *, limit=20)

Return candidate RF completions for a query or minimatch-like proposal.

Source code in parc/naming/ecs/rf.py
def suggest_rf_candidates(query: str, *, limit: int = 20) -> tuple[FunctionalReference, ...]:
    """Return candidate RF completions for a query or minimatch-like proposal."""

    normalized_query = normalize_rf_query(query)
    if not normalized_query:
        return ()

    candidates: list[FunctionalReference] = []
    seen: set[str] = set()

    def add(candidate: str) -> bool:
        if candidate in seen or not proposal_matches_candidate(normalized_query, candidate):
            return False
        try:
            parsed = parse_rf(candidate)
        except ValueError:
            return False
        seen.add(candidate)
        candidates.append(parsed)
        return len(candidates) >= limit

    base_candidates: list[str] = []
    for base_candidate in _iter_base_candidates(normalized_query):
        base_candidates.append(base_candidate)
        if add(base_candidate):
            return tuple(candidates)

    for base_candidate in base_candidates:
        for candidate in _iter_extension_candidates(base_candidate, normalized_query):
            if add(candidate):
                return tuple(candidates)

    return tuple(candidates)

validate_rf

validate_rf(query, *, limit=20)

Validate or complete an RF proposal.

The query may be: - an exact RF - a partial RF prefix - a minimatch-like pattern using ? and *

Source code in parc/naming/ecs/rf.py
def validate_rf(query: str, *, limit: int = 20) -> RFValidationResult:
    """Validate or complete an RF proposal.

    The query may be:
    - an exact RF
    - a partial RF prefix
    - a minimatch-like pattern using ``?`` and ``*``
    """

    normalized_query = normalize_rf_query(query)
    try:
        exact = parse_rf(normalized_query)
    except ValueError:
        exact = None

    if exact is not None:
        return RFValidationResult(
            query=query,
            normalized_query=normalized_query,
            is_exact_match=True,
            candidates=(exact,),
            truncated=False,
        )

    candidates = suggest_rf_candidates(normalized_query, limit=limit + 1)
    truncated = len(candidates) > limit
    if truncated:
        candidates = candidates[:limit]

    return RFValidationResult(
        query=query,
        normalized_query=normalized_query,
        is_exact_match=False,
        candidates=candidates,
        truncated=truncated,
    )

build_rf

build_rf(
    *,
    tranche,
    system,
    identification,
    material,
    extension="",
)

Build an RF from explicit sections.

Source code in parc/naming/ecs/rf.py
def build_rf(
    *,
    tranche: str | int | None,
    system: str,
    identification: str | int,
    material: str,
    extension: str = "",
) -> FunctionalReference:
    """Build an RF from explicit sections."""

    tranche_str = "" if tranche is None else str(tranche)
    identification_str = f"{int(identification):04d}" if isinstance(identification, int) else str(identification)
    material_code = normalize_rf_token(material)
    if len(material_code) == 2:
        material_code = f"{material_code}-"
    return parse_rf(f"{tranche_str}{normalize_ecs_code(system)}{identification_str}{material_code}{extension}")

build_rf_regex

build_rf_regex(
    *,
    tranche=None,
    system=None,
    identification=None,
    material=None,
    extension=None,
    anchored=True,
)

Build a regex for RF matching from exact section values.

Source code in parc/naming/ecs/rf.py
def build_rf_regex(
    *,
    tranche: str | None = None,
    system: str | None = None,
    identification: str | None = None,
    material: str | None = None,
    extension: str | None = None,
    anchored: bool = True,
) -> re.Pattern[str]:
    """Build a regex for RF matching from exact section values."""

    material = normalize_rf_token(material) if material else None
    if material and len(material) == 2:
        material = f"{material}-"

    pattern = (
        (re.escape(str(tranche)) if tranche is not None else r"[0-9]?")
        + (re.escape(normalize_ecs_code(system)) if system is not None else r"[A-Z]{3}")
        + (re.escape(str(identification)) if identification is not None else r"[0-9]{3,4}")
        + (re.escape(material[:2]) if material is not None else r"[A-Z]{2}")
        + (re.escape(material[2]) if material is not None else r"[A-Z0-9-]")
        + (re.escape(normalize_rf_token(extension)) if extension is not None else r"[A-Z0-9]{0,4}")
    )
    if anchored:
        pattern = f"^{pattern}$"
    return re.compile(pattern)

find_rfs

find_rfs(text)

Extract every canonical RF found in a block of text.

Source code in parc/naming/ecs/rf.py
def find_rfs(text: str) -> list[FunctionalReference]:
    """Extract every canonical RF found in a block of text."""

    found: list[FunctionalReference] = []
    for match in RF_FINDER_PATTERN.finditer(text.upper()):
        found.append(parse_rf(match.group("rf")))
    return found

parc.sites

parc.technologies

parc.types

Grid

Grid(*, name='grid', sites=None)

A grid is just a list of units

Source code in parc/types/grid.py
def __init__(self, *, name: str = "grid", sites: list[Site] | None = None):
    if sites is None:
        sites = []
    self.name = name
    self.sites = sites

sites instance-attribute

sites = sites

name instance-attribute

name = name

Site

Site(*, name, name_full=None, units)

units: only use

Source code in parc/types/site.py
def __init__(self, *, name: str, name_full: str | None = None, units: list[Unit]):
    self.name = name
    self.name_full = name_full
    self.units = units

name instance-attribute

name = name

name_full instance-attribute

name_full = name_full

units instance-attribute

units = units

Unit

Unit(*, technology, name, electricity_producer=True)

electricity_producer: can be false for common buildings (storage, control rooms...)

Source code in parc/types/unit.py
def __init__(
    self,
    *,
    technology: UnitTechnology,
    name: str,
    electricity_producer: bool = True,
):
    self.name = name
    self.technology = technology
    self.electricity_producer = electricity_producer
    self._unique_key = name + technology + str(electricity_producer)
    if self._unique_key in _ALL_UNITS:
        # a unit must be unique
        raise UniqueUnitError(_ALL_UNITS[self._unique_key])
    _ALL_UNITS[self._unique_key] = self

name instance-attribute

name = name

technology instance-attribute

technology = technology

electricity_producer instance-attribute

electricity_producer = electricity_producer

parc.utils