Leer, modificar y exportar archivos IFC con IfcOpenShell. Extrae elementos, propiedades, geometría y metadatos de modelos BIM. Usa cuando el usuario diga "IFC", "IfcOpenShell", "modelo IFC", "propiedades IFC", "exportar IFC", "leer IFC", "interoperabilidad BIM", "open BIM".
46:T2c1d,
Rick usa este skill para leer, analizar, modificar y exportar archivos IFC (Industry Foundation Classes) usando IfcOpenShell y herramientas Python open source.
pip install ifcopenshell
IfcOpenShell soporta IFC2x3, IFC4 e IFC4x3. La versión de Python debe coincidir con la del paquete (3.9-3.12).
Para geometría y visualización:
pip install ifcopenshell[geometry]
pip install OCC # Open CASCADE para renderizado
Paquetes complementarios:
| Paquete | Uso |
|---|---|
ifcopenshell |
| Lectura, escritura y modificación de IFC |
ifcpatch | Transformaciones batch sobre archivos IFC |
ifcdiff | Comparación entre versiones de modelos IFC |
ifcclash | Detección de interferencias geométricas |
ifccsv | Exportar/importar propiedades IFC a CSV |
bimtester | Validación de requisitos BIM con Gherkin |
import ifcopenshell
ifc = ifcopenshell.open("modelo.ifc")
print(f"Schema: {ifc.schema}") # IFC2X3, IFC4, IFC4X3
print(f"Nombre: {ifc.header.file_name.name}")
print(f"Elementos totales: {len(ifc.by_type('IfcProduct'))}")
from collections import Counter
tipos = Counter(e.is_a() for e in ifc.by_type("IfcProduct"))
for tipo, cantidad in tipos.most_common():
print(f" {tipo}: {cantidad}")
Salida típica:
IfcWallStandardCase: 142
IfcDoor: 87
IfcWindow: 64
IfcSpace: 38
IfcColumn: 24
IfcSlab: 18
IfcBeam: 12
def get_properties(element):
"""Extrae todas las propiedades de un elemento IFC como dict."""
props = {}
for definition in element.IsDefinedBy:
if definition.is_a("IfcRelDefinesByProperties"):
pset = definition.RelatingPropertyDefinition
if pset.is_a("IfcPropertySet"):
for prop in pset.HasProperties:
if prop.is_a("IfcPropertySingleValue"):
value = prop.NominalValue.wrappedValue if prop.NominalValue else None
props[prop.Name] = value
return props
wall = ifc.by_type("IfcWall")[0]
props = get_properties(wall)
for name, value in props.items():
print(f" {name}: {value}")
def get_quantities(element):
"""Extrae IfcElementQuantity (áreas, volúmenes, longitudes)."""
quantities = {}
for definition in element.IsDefinedBy:
if definition.is_a("IfcRelDefinesByProperties"):
qset = definition.RelatingPropertyDefinition
if qset.is_a("IfcElementQuantity"):
for q in qset.Quantities:
if q.is_a("IfcQuantityArea"):
quantities[q.Name] = q.AreaValue
elif q.is_a("IfcQuantityVolume"):
quantities[q.Name] = q.VolumeValue
elif q.is_a("IfcQuantityLength"):
quantities[q.Name] = q.LengthValue
return quantities
slab = ifc.by_type("IfcSlab")[0]
print(get_quantities(slab))
# {'NetArea': 45.2, 'GrossVolume': 9.04, 'Width': 0.2}
# Muros
walls = ifc.by_type("IfcWall")
# Puertas
doors = ifc.by_type("IfcDoor")
# Espacios (habitaciones, zonas)
spaces = ifc.by_type("IfcSpace")
# Elementos estructurales
columns = ifc.by_type("IfcColumn")
beams = ifc.by_type("IfcBeam")
slabs = ifc.by_type("IfcSlab")
# Instalaciones MEP
ducts = ifc.by_type("IfcDuctSegment")
pipes = ifc.by_type("IfcPipeSegment")
# Todos los elementos con geometría
products = ifc.by_type("IfcProduct")
# Muros de un nivel específico
level_2_walls = [
w for w in ifc.by_type("IfcWall")
if any(
rel.RelatingStructure.Name == "Nivel 2"
for rel in getattr(w, "ContainedInStructure", [])
)
]
# Elementos por GlobalId
element = ifc.by_guid("2O2Fr$t4X7Zf8NOew3FLOH")
import csv
walls = ifc.by_type("IfcWall")
with open("muros.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["GlobalId", "Name", "Tag", "ObjectType"])
for wall in walls:
writer.writerow([
wall.GlobalId,
wall.Name or "",
wall.Tag or "",
wall.ObjectType or ""
])
import json
def element_to_dict(el):
return {
"id": el.GlobalId,
"type": el.is_a(),
"name": el.Name or "",
"properties": get_properties(el),
"quantities": get_quantities(el),
}
data = [element_to_dict(e) for e in ifc.by_type("IfcSpace")]
with open("espacios.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
import ifcopenshell
import ifcopenshell.api
ifc = ifcopenshell.open("modelo.ifc")
wall = ifc.by_type("IfcWall")[0]
# Cambiar el nombre del elemento
wall.Name = "Muro Exterior Principal"
# Agregar/modificar una propiedad en un PropertySet existente
for definition in wall.IsDefinedBy:
if definition.is_a("IfcRelDefinesByProperties"):
pset = definition.RelatingPropertyDefinition
if pset.is_a("IfcPropertySet") and pset.Name == "Pset_WallCommon":
for prop in pset.HasProperties:
if prop.Name == "IsExternal":
prop.NominalValue = ifc.createIfcBoolean(True)
# Crear un nuevo PropertySet con ifcopenshell.api
pset = ifcopenshell.api.run("pset.add_pset", ifc,
product=wall,
name="Custom_Props"
)
ifcopenshell.api.run("pset.edit_pset", ifc,
pset=pset,
properties={
"EspesorNominal": 0.20,
"Acabado": "Pintura blanca",
"FechaRevision": "2026-03-01"
}
)
ifc.write("modelo_modificado.ifc")
llm.generate para análisis de modelosimport json
spaces = ifc.by_type("IfcSpace")
space_data = []
for space in spaces:
props = get_properties(space)
quants = get_quantities(space)
space_data.append({
"name": space.Name or space.LongName or "Sin nombre",
"area_m2": quants.get("NetFloorArea", quants.get("GrossFloorArea", "N/D")),
"properties": props
})
prompt = f"""Analiza los espacios de este modelo IFC y genera un resumen ejecutivo:
- Total de espacios: {len(space_data)}
- Datos: {json.dumps(space_data[:20], indent=2, ensure_ascii=False)}
Incluye: superficie total, distribución por uso, observaciones sobre normativa."""
# Enviar a Rick para análisis con LLM
result = await client.execute_task("llm.generate", {
"prompt": prompt,
"model": "gemini-2.5-flash"
})
windows.fs.* para leer IFC desde VM# Leer un IFC almacenado en la VM (G:\Mi unidad\...)
result = await client.execute_task("windows.fs.read_binary", {
"path": r"G:\Mi unidad\Rick-David\Modelos BIM\edificio.ifc"
})
# Guardar localmente, procesar con IfcOpenShell
import tempfile, os
with tempfile.NamedTemporaryFile(suffix=".ifc", delete=False) as tmp:
tmp.write(result["content"])
tmp_path = tmp.name
ifc = ifcopenshell.open(tmp_path)
# ... procesar ...
os.unlink(tmp_path)
import ifcopenshell
import csv
def extract_space_areas(ifc_path: str, output_csv: str):
"""Extrae GlobalId, nombre, nivel y área de todos los IfcSpace."""
ifc = ifcopenshell.open(ifc_path)
spaces = ifc.by_type("IfcSpace")
rows = []
for space in spaces:
name = space.LongName or space.Name or "Sin nombre"
global_id = space.GlobalId
level = "N/D"
for rel in getattr(space, "ContainedInStructure", []):
if rel.RelatingStructure.is_a("IfcBuildingStorey"):
level = rel.RelatingStructure.Name
break
area = None
for definition in space.IsDefinedBy:
if definition.is_a("IfcRelDefinesByProperties"):
qset = definition.RelatingPropertyDefinition
if qset.is_a("IfcElementQuantity"):
for q in qset.Quantities:
if q.is_a("IfcQuantityArea") and "Floor" in q.Name:
area = round(q.AreaValue, 2)
break
rows.append({
"GlobalId": global_id,
"Nombre": name,
"Nivel": level,
"Area_m2": area or "N/D"
})
with open(output_csv, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["GlobalId", "Nombre", "Nivel", "Area_m2"])
writer.writeheader()
writer.writerows(rows)
total = sum(r["Area_m2"] for r in rows if isinstance(r["Area_m2"], (int, float)))
print(f"Espacios: {len(rows)} | Superficie total: {total:.2f} m²")
return rows
# Uso
extract_space_areas("edificio.ifc", "espacios_areas.csv")
ifcpatch -i modelo.ifc -o resultado.ifc -r OffsetObjectPlacement -a 1000,2000,0
ifcpatch -i modelo.ifc -o resultado.ifc -r ResetAbsoluteCoordinates
ifcdiff modelo_v1.ifc modelo_v2.ifc -o cambios.json
from ifcclash import ifcclash
settings = ifcclash.IfcClashSettings()
settings.output = "clashes.json"
clasher = ifcclash.IfcClash(settings)
clasher.add_group("Estructura", "estructura.ifc")
clasher.add_group("MEP", "mep.ifc")
clasher.clash()
clasher.export()
by_type("IfcDoor") + get_properties() + exportar a CSV.extract_space_areas() con sum de áreas.ifcdiff entre v1 y v2, generar reporte.ifcopenshell.api.run("pset.edit_pset") en loop.ifcclash con dos grupos de archivos.geometry que depende de Open CASCADE.ifcopenshell.api provee una API de alto nivel para crear y modificar entidades IFC de forma segura.Edit PDFs with natural-language instructions using the nano-pdf CLI.