Cet article de blog propose une introduction à Python et IfcOpenshell, pour le traitement des fichiers IFC. Nous allons passer étape par étape, de l'installation des outils de développement nécessaires, jusqu'à l'écriture et l'exécution de scripts de traitement personnalisés. L'objectif est de passer en revue quelques notions élémentaires du langage, pour faciliter ensuite l'extraction et la manipulation de données IFC.
Si vous découvrez python, ou la programmation en générle, le contenu de cet article pourrait vous convenir.
Prérequis
Pour commencer, installez les outils ci-après pour votre système d'exploitation.
Python
Le programme qui interprète et exécute le code python, peut être téléchargé depuis le site officiel python.org.
Pour des raisons de compatibilité, au moment de la rédaction de cet article, la version de python doit être <3.15, >=3.10.
Visual Studio Code
VS Code est un éditeur de code, qui offre une expérience utilisateur pratique, aussi bien pour écrire que pour éditer du code, quel que soit le langage. Au fond, il s'agit simplement de fichiers texte avec une extension spéciale (.py : python, .cs : C# …).
Peut être téléchargé via code.visualstudio.com
Maintenant que nous avons les outils nécessaires, ci-après une brève introduction à quelques notions fondamentales du langage python.
Introduction à python
ℹ️ Si vous avez déjà de l'expérience avec le scripting python, vous pouvez ignorer ce chapitre.
Ceci n'est en aucun cas un aperçu approfondi de python, mais j'espère que ça éveillera votre curiosité à l'explorer davantage, croyez-moi, ça en vaut la peine.
Quand on exécute du code python, l'interpréteur lit le texte de haut en bas, traitant chaque ligne comme une instruction et évaluant son résultat.
Une règle simple : tout ce qui suit un # est considéré comme un commentaire, que ce soit au début ou au milieu d'une ligne.
Variables et types de données
Une variable est une unité de donnée à laquelle on peut donner un nom unique. Une variable peut porter n'importe quel nom, à l'exception de quelques-uns réservés par le langage (for, if, int, dict, list, print, len...)
x = 2 # la variable x reçoit la valeur 2 de type int (entier)y = 2.45 # floatt = 'hello world' # str (chaîne de caractères), assignée avec des guillemets simples 'texte' ou doubles "texte"_list= [1, 2.3, 'test', t] # listeb = True # bool, une valeur binaire qui vaut soit True soit False_dict = {"a":5, "b":x} # dict, une collection de clés et de valeurs._set = {"wall01", "wall02", 21} # set, une liste d'éléments uniques
Boucles
Une boucle, comme son nom l'indique, répète les mêmes instructions plusieurs fois. Les boucles sont utilisées pour itérer sur une séquence (liste, dict, str, …), en traitant chaque élément un par un (boucle for). Elles peuvent aussi être utilisées pour répéter les mêmes instructions encore et encore jusqu'à ce qu'une condition de sortie soit remplie (boucle while).
En prenant for comme exemple :
# Itération sur la liste l définie précédemmentfor item in items:print(item) # affiche la représentation str de l'élément à l'écran#...#autres instructions
Notez qu'une indentation est utilisée pour délimiter des blocs de code, elle n'est donc pas optionnelle.
Conditions
Il est fréquent de vouloir exécuter du code sous une certaine condition
x = 3 # assigne à x la valeur 3if x%2 == 0 : # vérifie si le reste de la division est égal à 0print('even') # ❌ ceci ne sera pas exécutéelse:print('odd') # ✅ ceci sera exécuté
Fonctions
Une fonction est un moyen de regrouper un ensemble d'instructions, pour les rendre facilement réutilisables. Dans les outils de programmation visuelle comme dynamo, une fonction est comparable à un nœud. Elle peut prendre des valeurs en entrée (arguments), et retourner d'autres valeurs en sortie
# Définition de la fonctiondef isEven (value):if value%2 == 0 : # vérifie si le reste de la division est égal à 0return 'even'else:return 'odd'# Appel de la fonctionr = isEven(4) # la variable r reçoit alors la valeur de retour de la fonction => 'even' dans ce cas
En programmation orientée objet (POO), un objet peut avoir plusieurs propriétés (valeurs) et méthodes (ensembles d'instructions).
Quand on parle d'objets, il faut mentionner les classes. Une classe est un plan à partir duquel un objet peut être instancié. On peut voir une classe comme un type d'objet, un objet étant alors une instance de ce type.
Étant donné une classe Person avec les attributs (Name, BirthDate) et une méthode getAge(). Supposons que la variable person1 soit déjà définie avec les informations d'un individu, on peut interagir avec elle comme suit :
# Ayant cette classeperson1.Name # Paulperson1.BirthDate # 04/05/1990person1.getAge() # retournera l'âge calculé à ce jour => '36'
Si cette introduction brève à python était suffisamment compréhensible, très bien 🎉 !! Vous pouvez consulter la documentation officielle pour aller plus loin 🚀. Si cela n'est pas le cas, il est fortement conseillé de consulter la documentation officielle ou d'autres ressources en ligne, voire votre IA préférée, vous en obtiendrez davantage de ressources.
IFCOpenshell
IfcOpenshell, est un projet open-source qui fournit divers outils et librairies.
Dans cet article, nous allons travailler avec la librairie python ifcopenshell, utilisée pour manipuler des fichiers IFC.
Il existe d'autres librairies liées au projet IfcOpenshell (liste complète) conçues pour d'autres formats openBIM comme BCF, IDS, et pour l'interopérabilité avec d'autres outils.
Pour reprendre l'analogie avec Dynamo, on peut voir une librairie comme un package externe, contenant divers nœuds.
Là où l'analogie s'arrête, c'est que cette librairie et le schéma IFC sont tous deux open source, on peut donc les utiliser et construire des solutions autour d'eux sans aucune dépendance à un éditeur et en toute liberté.
Pour notre démonstration pratique de traitement IFC, nous allons construire le pipeline suivant
flowchart LR
A[📂 Ouvrir les fichiers] --> B[📄 Extraire les attributs en CSV]
B --> C[✏️ Modifier les attributs]
C --> D[🔄 Mettre à jour l'IFC]
D --> E[💾 Sauvegarder l'IFC]Pour commencer, créons d'abord un dossier de travail, python-ifc-automation par exemple.
Lancez VS Code et ouvrez le dossier créé.
Ensuite, nous allons récupérer la librairie via pip, le gestionnaire de paquets officiel de python, fourni avec python. Une librairie est un ensemble de fichiers contenant du code qui remplit un objectif précis.
Installation d'IfcOpenShell
Avec VS Code, vous pouvez suivre ces étapes :
- Ouvrez le terminal (vous pouvez aussi utiliser le raccourci
Ctrl+ù).
Un terminal ou une ligne de commande est un moyen d'interagir avec un système d'exploitation en tapant des commandes et des instructions, plutôt qu'en cliquant sur des boutons dans une interface utilisateur. - Tapez cette commande pour installer
ifcopenshellpip install ifcopenshell
Vous pouvez aussi ouvrir le terminal intégré depuis l'interface Terminal > New Terminal
Commencez par créer un dossier IFC, c'est là que les fichiers .ifc seront placés.
Extraction des attributs
Créez un fichier à la racine du dossier de travail nommé extract_attributes.py et collez le code suivant.
import ifcopenshell # importe la librairie qu'on vient d'installerimport csv # importe la librairie csv intégrée# Création d'une variable contenant le chemin vers le fichier IFC, il peut être relatif ou absoluifc_file_path = "IFC/AC20-FZK-Haus.ifc" # Changez ceci pour cibler un autre fichier# Utilisation de la fonction open() d'ifcopenshell pour parser le modèle ifcifc_file = ifcopenshell.open(ifc_file_path)# sélectionne tous les IfcElements (IfcElement est la catégorie parente contenant tous les produits AEC) du modèleifcElements = ifc_file.by_type('IfcElement') # Peut être remplacé par une classe spécifique (IfcWall, IfcWindow ....)# Initialisation d'une liste vide où on va stocker les données des élémentsifcElementsData = []for ifcElement in ifcElements:# pour chaque élément, get_info() retourne un dictionnaire avec les attributs# par exemple {'Name' : 'Exterior_wall_01' , 'Description':'Description_01'.....}ifcElementData = ifcElement.get_info(scalar_only=True)# Ajout des données de l'élément à la listeifcElementsData.append(ifcElementData)# création d'un ensemble des noms d'attributsattributes = set()for ifcElementData in ifcElementsData:# ifcElementData.keys() retourne les clés du dictionnaire :# {'Name' : 'Exterior_wall_01' , 'Description': 'Description_01' .....} => ('Name', 'Description',...)attributes.update(ifcElementData.keys())# Sauvegarde des donnéeswith open("IFC/AC20-FZK-Haus.csv", "w", newline="", encoding="utf-8") as f:writer = csv.DictWriter(f, fieldnames=attributes)writer.writeheader()writer.writerows(ifcElementsData)
Pensez à sauvegarder le fichier, vous pouvez utiliser le raccourci Ctrl + s
Dans le terminal, exécutez cette commande python .\extract_attributes.py, vous pouvez utiliser la touche tabulation ↹ pour l'autocomplétion.
Vous devriez voir le fichier CSV AC20-FZK-Haus.csv généré dans le dossier IFC.
Vous pouvez installer l'extension CSV qui facilite l'interaction avec les données CSV.
Mise à jour des attributs
L'étape suivante de cette pipeline consiste à mettre à jour les attributs via le CSV. Certains attributs sont en lecture seule et ne doivent pas être modifiés.
Créez un nouveau fichier python update_attributes.py et collez le code suivant
# Met à jour les attributs des éléments IFC depuis un fichier CSV (une ligne par élément,# associée au modèle via le GlobalId).import ifcopenshellimport csvifc_file_path = "IFC/AC20-FZK-Haus.ifc"# Charge tout le modèle en mémoire. Rien n'est sauvegardé avant le write() ci-dessous.ifc_file = ifcopenshell.open(ifc_file_path)# newline="" laisse le module csv gérer lui-même les fins de ligne.with open('IFC/AC20-FZK-Haus.csv', mode='r', newline="") as f:# DictReader transforme chaque ligne en dict, avec les clés de l'en-tête.reader = csv.DictReader(f)# Conversion en liste dès maintenant ; le reader ne fonctionne que tant que le fichier est ouvert.attributesCSV = list(reader)def infer_type(value):"""Les valeurs du CSV sont toujours du texte. Transforme "42" en 42, "4.2" en 4.2,et laisse le reste tel quel en chaîne de caractères."""try:return int(value)except ValueError:try:return float(value)except ValueError:return valuefor newElementAttribute in attributesCSV:# .get() retourne None au lieu de planter si la colonne est manquante.elementGlobalId = newElementAttribute.get('GlobalId')# Sans ID, on ne sait pas quel élément mettre à jour, donc on passe la ligne.if not elementGlobalId:continueelement = ifc_file.by_guid(elementGlobalId)# Noms des attributs que possède réellement cet élément.elementAttributeNames = element.get_info().keys()for attribute, value in newElementAttribute.items():# Ignore les champs gérés par ifcopenshell, les attributs que cet élément# ne possède pas (les écrire lèverait une erreur), et les cellules vides.if (attribute not in ('id', 'GlobalId', 'type')) and (attribute in elementAttributeNames) and (value != ""):element.__setattr__(attribute, infer_type(value)) # La valeur de l'attribut est définie ici# Sauvegarde dans un nouveau fichier pour ne pas modifier l'original.ifc_file.write('IFC/AC20-FZK-Haus_updated.ifc')
Modifiez le CSV généré précédemment pour mettre à jour certains attributs. Dans l'exemple ci-dessous, l'attribut description est renseigné pour tous les éléments.
Utilisez cette commande pour exécuter le script python .\update_attributes.py
Extraction des attributs de plusieurs IFC
Le précédent extract_attributes.py ne traite qu'un seul fichier à la fois, et son chemin est écrit manuellement à chaque fois. Pour une meilleure expérience utilisateur, nous allons le modifier pour qu'il recherche tous les fichiers .ifc du dossier IFC. Le script exécute ensuite la même logique d'extraction pour chaque fichier.
Créez un nouveau fichier batch_extract_attributes.py et collez le code suivant
# Exporte les attributs de chaque élément de chaque fichier IFC vers un CSVimport ifcopenshellimport csvimport glob # trouve les fichiers correspondant à un motif générique# Mise de la logique d'extraction dans une fonctiondef extract_attributes_2_csv(ifc_file_path):ifc_file = ifcopenshell.open(ifc_file_path)# Chaque élément physique (murs, portes, dalles...). Ceci exclut les# espaces, les étages et les relations.ifcElements = ifc_file.by_type('IfcElement')ifcElementsData = []for ifcElement in ifcElements:# get_info() retourne un dict des attributs de l'élément.# scalar_only=True retire les références à d'autres objets IFC, ne# gardant que les valeurs simples qui tiennent dans une cellule CSV.ifcElementsData.append(ifcElement.get_info(scalar_only=True))# Différents types d'éléments ont différents attributs, on rassemble donc# l'union de toutes les clés pour l'en-tête CSV. Un set élimine les doublons.attributes = set()for ifcElementData in ifcElementsData:attributes.update(ifcElementData.keys())# Construit le chemin de sortie en changeant l'extension : house.ifc -> house.csvifc_extension = ifc_file_path.split(".")[-1]csv_file_path = ifc_file_path.replace(ifc_extension, 'csv')with open(csv_file_path, "w", newline="", encoding="utf-8") as f:# DictWriter fait correspondre les clés de chaque dict aux colonnes. Un# attribut manquant pour un élément est simplement laissé vide.writer = csv.DictWriter(f, fieldnames=attributes)writer.writeheader()writer.writerows(ifcElementsData)# Recherche et liste tous les fichiers .ifc du dossier IFC ; * correspond à n'importe quel nom de fichier.ifc_file_paths = glob.glob('IFC/*.ifc')for ifc_file_path in ifc_file_paths:print('Traitement : ', ifc_file_path)extract_attributes_2_csv(ifc_file_path)print('Terminé : ', ifc_file_path)
Vous pouvez ajouter d'autres fichiers IFC dans le dossier IFC
Pour lancer le traitement par lot, utilisez la commande suivante
python .\batch_extract_attributes.py
Pour plus de simplicité d'usage, on peut mettre en place un fichier exécutable (.bat sous windows), pour ne pas avoir à repasser par VS Code à chaque fois.
Créez un fichier run_batch_extraction.bat et collez-y ces commandes.
python .\batch_extract_attributes.pypause
Valable pour Windows. Pour linux et macOS, adptez cela pour un fichier .sh.
On peut maintenant lancer le script d'extraction en exécutant le fichier .bat
il est aussi possible d'exécuter le script via l'interface
Extraire les attributs :
pensez à sauvegarder les fichiers après édition (ctrl+s)
- Plutôt que de traiter un seul fichier, essayez de traiter par lot une liste de fichiers dans le dossier IFC (en utilisant une boucle par exemple).
Aller plus loin
Pour continuer cette lancée, essayez d'implémenter les fonctionnalités suivantes:
- Mise à jour des attributs de plusieurs fichiers IFC à la fois.
- Inclure les propriétés et quantités
- Utiliser Excel pour les résultats de l'extraction à la place du CSV
💡Pour y arriver, vous auriez éventuellement besoin d'installer d'autres librairies, google / votre IA préférée seront vos alliés.
Pourquoi apprendre à programmer alors que l'IA le fait très bien ? Mon avis est que l'utilisation des LLMs pour obtenir un résultat c'est bien, mais être réellement capable de le comprendre, vérifier et améliorer c'est encore mieux. De plus, avoir les fondamentaux est primordial pour bien formuler un problème, et pouvoir appréhender les notions algorithmiques sous-jacentes. Autrement dit, dans un monde avec des calculatrices savoir compter est tout sauf futile.