ifc-data-extractionExtract structured data from IFC (Industry Foundation Classes) files using IfcOpenShell. Parse BIM models, extract quantities, properties, spatial relationships, and export to various formats.
Install via ClawdBot CLI:
clawdbot install datadrivenconstruction/ifc-data-extractionThis skill provides comprehensive IFC file parsing and data extraction using IfcOpenShell. Extract element data, quantities, properties, and relationships from BIM models for analysis and reporting.
Based on Open BIM Standards - Working with vendor-neutral IFC format for maximum interoperability.
"IFC является открытым стандартом для обмена BIM-данными, позволяющим извлекать информацию независимо от программного обеспечения."
— DDC Methodology
```python
import ifcopenshell
import ifcopenshell.util.element as element_util
import pandas as pd
ifc = ifcopenshell.open("model.ifc")
project = ifc.by_type("IfcProject")[0]
print(f"Project: {project.Name}")
walls = ifc.by_type("IfcWall")
print(f"Total walls: {len(walls)}")
wall_data = []
for wall in walls:
psets = element_util.get_psets(wall)
wall_data.append({
'GlobalId': wall.GlobalId,
'Name': wall.Name,
'Type': wall.is_a(),
'Level': get_level(wall),
'Properties': psets
})
df = pd.DataFrame(wall_data)
print(df.head())
```
```python
import ifcopenshell
import ifcopenshell.util.element as element_util
import ifcopenshell.util.placement as placement_util
import ifcopenshell.geom
import pandas as pd
from typing import List, Dict, Optional, Any
class IFCExtractor:
"""Extract data from IFC files"""
def init(self, ifc_path: str):
self.model = ifcopenshell.open(ifc_path)
self.settings = ifcopenshell.geom.settings()
def get_project_info(self) -> Dict:
"""Extract project metadata"""
project = self.model.by_type("IfcProject")[0]
site = self.model.by_type("IfcSite")
building = self.model.by_type("IfcBuilding")
return {
'project_id': project.GlobalId,
'project_name': project.Name,
'description': project.Description,
'site_count': len(site),
'building_count': len(building),
'schema': self.model.schema
}
def get_all_elements(self, element_types: List[str] = None) -> pd.DataFrame:
"""Extract all elements of specified types"""
if element_types is None:
element_types = [
'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',
'IfcDoor', 'IfcWindow', 'IfcStair', 'IfcRoof'
]
all_elements = []
for ifc_type in element_types:
elements = self.model.by_type(ifc_type)
for elem in elements:
data = self._extract_element_data(elem)
data['IFC_Type'] = ifc_type
all_elements.append(data)
return pd.DataFrame(all_elements)
def _extract_element_data(self, element) -> Dict:
"""Extract data from single element"""
# Basic info
data = {
'GlobalId': element.GlobalId,
'Name': element.Name,
'Description': element.Description,
'ObjectType': element.ObjectType if hasattr(element, 'ObjectType') else None
}
# Get level/storey
data['Level'] = self._get_element_level(element)
# Get material
data['Material'] = self._get_element_material(element)
# Get type
data['TypeName'] = self._get_element_type(element)
# Get all property sets
psets = element_util.get_psets(element)
data['PropertySets'] = psets
# Extract common quantities
base_quantities = psets.get('BaseQuantities', {})
data.update({
'Length': base_quantities.get('Length'),
'Width': base_quantities.get('Width'),
'Height': base_quantities.get('Height'),
'Area': base_quantities.get('NetSideArea') or base_quantities.get('GrossArea'),
'Volume': base_quantities.get('NetVolume') or base_quantities.get('GrossVolume')
})
return data
def _get_element_level(self, element) -> Optional[str]:
"""Get the building storey for an element"""
if hasattr(element, 'ContainedInStructure'):
for rel in element.ContainedInStructure or []:
if rel.RelatingStructure.is_a('IfcBuildingStorey'):
return rel.RelatingStructure.Name
return None
def _get_element_material(self, element) -> Optional[str]:
"""Get material name for element"""
if hasattr(element, 'HasAssociations'):
for rel in element.HasAssociations or []:
if rel.is_a('IfcRelAssociatesMaterial'):
material = rel.RelatingMaterial
if hasattr(material, 'Name'):
return material.Name
elif hasattr(material, 'ForLayerSet'):
layers = material.ForLayerSet.MaterialLayers
if layers:
return layers[0].Material.Name
return None
def _get_element_type(self, element) -> Optional[str]:
"""Get element type name"""
if hasattr(element, 'IsTypedBy'):
for rel in element.IsTypedBy or []:
return rel.RelatingType.Name
return None
def extract_quantities(self) -> pd.DataFrame:
"""Extract quantities for all elements"""
elements = self.get_all_elements()
# Group by category and level
quantities = elements.groupby(['IFC_Type', 'Level']).agg({
'GlobalId': 'count',
'Volume': 'sum',
'Area': 'sum',
'Length': 'sum'
}).rename(columns={'GlobalId': 'Count'}).reset_index()
return quantities
def extract_levels(self) -> pd.DataFrame:
"""Extract building levels/storeys"""
storeys = self.model.by_type("IfcBuildingStorey")
level_data = []
for storey in storeys:
level_data.append({
'GlobalId': storey.GlobalId,
'Name': storey.Name,
'Elevation': storey.Elevation,
'Description': storey.Description
})
return pd.DataFrame(level_data).sort_values('Elevation')
def extract_spaces(self) -> pd.DataFrame:
"""Extract spaces/rooms"""
spaces = self.model.by_type("IfcSpace")
space_data = []
for space in spaces:
psets = element_util.get_psets(space)
base_qty = psets.get('BaseQuantities', {})
space_data.append({
'GlobalId': space.GlobalId,
'Name': space.Name,
'LongName': space.LongName,
'Level': self._get_element_level(space),
'Area': base_qty.get('NetFloorArea'),
'Volume': base_qty.get('NetVolume'),
'Height': base_qty.get('Height')
})
return pd.DataFrame(space_data)
def extract_materials(self) -> pd.DataFrame:
"""Extract material summary"""
materials = {}
for elem in self.model.by_type("IfcProduct"):
material = self._get_element_material(elem)
if material:
if material not in materials:
materials[material] = {'count': 0, 'volume': 0}
materials[material]['count'] += 1
psets = element_util.get_psets(elem)
volume = psets.get('BaseQuantities', {}).get('NetVolume', 0)
if volume:
materials[material]['volume'] += volume
return pd.DataFrame.from_dict(materials, orient='index').reset_index()
def extract_relationships(self) -> pd.DataFrame:
"""Extract element relationships"""
relationships = []
# Spatial containment
for rel in self.model.by_type("IfcRelContainedInSpatialStructure"):
for elem in rel.RelatedElements:
relationships.append({
'Element': elem.GlobalId,
'Element_Type': elem.is_a(),
'Relationship': 'ContainedIn',
'Related_To': rel.RelatingStructure.GlobalId,
'Related_Type': rel.RelatingStructure.is_a()
})
# Aggregation
for rel in self.model.by_type("IfcRelAggregates"):
for part in rel.RelatedObjects:
relationships.append({
'Element': part.GlobalId,
'Element_Type': part.is_a(),
'Relationship': 'PartOf',
'Related_To': rel.RelatingObject.GlobalId,
'Related_Type': rel.RelatingObject.is_a()
})
return pd.DataFrame(relationships)
```
```python
import numpy as np
class IFCGeometryExtractor:
"""Extract geometry data from IFC elements"""
def init(self, ifc_path: str):
self.model = ifcopenshell.open(ifc_path)
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.USE_WORLD_COORDS, True)
def get_element_geometry(self, element) -> Dict:
"""Extract geometry for single element"""
try:
shape = ifcopenshell.geom.create_shape(self.settings, element)
verts = shape.geometry.verts
faces = shape.geometry.faces
# Calculate bounding box
vertices = np.array(verts).reshape(-1, 3)
min_coords = vertices.min(axis=0)
max_coords = vertices.max(axis=0)
dimensions = max_coords - min_coords
return {
'GlobalId': element.GlobalId,
'vertices_count': len(vertices),
'faces_count': len(faces) // 3,
'min_x': min_coords[0],
'min_y': min_coords[1],
'min_z': min_coords[2],
'max_x': max_coords[0],
'max_y': max_coords[1],
'max_z': max_coords[2],
'length': dimensions[0],
'width': dimensions[1],
'height': dimensions[2],
'center_x': (min_coords[0] + max_coords[0]) / 2,
'center_y': (min_coords[1] + max_coords[1]) / 2,
'center_z': (min_coords[2] + max_coords[2]) / 2
}
except:
return {'GlobalId': element.GlobalId, 'error': 'Geometry extraction failed'}
def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:
"""Get bounding boxes for all elements of type"""
elements = self.model.by_type(element_type)
boxes = [self.get_element_geometry(e) for e in elements]
return pd.DataFrame(boxes)
def calculate_volumes(self, element_type: str) -> pd.DataFrame:
"""Calculate volumes using geometry"""
elements = self.model.by_type(element_type)
volumes = []
for elem in elements:
try:
shape = ifcopenshell.geom.create_shape(self.settings, elem)
# Calculate volume from mesh (simplified)
verts = np.array(shape.geometry.verts).reshape(-1, 3)
bbox_volume = np.prod(verts.max(axis=0) - verts.min(axis=0))
volumes.append({
'GlobalId': elem.GlobalId,
'Name': elem.Name,
'BBox_Volume': bbox_volume
})
except:
pass
return pd.DataFrame(volumes)
```
```python
class IFCExporter:
"""Export IFC data to various formats"""
def init(self, extractor: IFCExtractor):
self.extractor = extractor
def to_excel(self, output_path: str, include_all: bool = True):
"""Export to Excel with multiple sheets"""
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# Project info
project_info = pd.DataFrame([self.extractor.get_project_info()])
project_info.to_excel(writer, sheet_name='Project', index=False)
# All elements
if include_all:
elements = self.extractor.get_all_elements()
elements.to_excel(writer, sheet_name='Elements', index=False)
# Quantities
quantities = self.extractor.extract_quantities()
quantities.to_excel(writer, sheet_name='Quantities', index=False)
# Levels
levels = self.extractor.extract_levels()
levels.to_excel(writer, sheet_name='Levels', index=False)
# Spaces
spaces = self.extractor.extract_spaces()
spaces.to_excel(writer, sheet_name='Spaces', index=False)
# Materials
materials = self.extractor.extract_materials()
materials.to_excel(writer, sheet_name='Materials', index=False)
return output_path
def to_csv(self, output_dir: str):
"""Export to multiple CSV files"""
import os
os.makedirs(output_dir, exist_ok=True)
exports = {
'elements.csv': self.extractor.get_all_elements(),
'quantities.csv': self.extractor.extract_quantities(),
'levels.csv': self.extractor.extract_levels(),
'spaces.csv': self.extractor.extract_spaces(),
'materials.csv': self.extractor.extract_materials()
}
for filename, df in exports.items():
df.to_csv(os.path.join(output_dir, filename), index=False)
return output_dir
def to_json(self, output_path: str):
"""Export to JSON"""
import json
data = {
'project': self.extractor.get_project_info(),
'elements': self.extractor.get_all_elements().to_dict('records'),
'quantities': self.extractor.extract_quantities().to_dict('records'),
'levels': self.extractor.extract_levels().to_dict('records'),
'materials': self.extractor.extract_materials().to_dict('records')
}
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, default=str)
return output_path
def to_database(self, connection_string: str, table_prefix: str = 'ifc_'):
"""Export to SQL database"""
from sqlalchemy import create_engine
engine = create_engine(connection_string)
tables = {
f'{table_prefix}elements': self.extractor.get_all_elements(),
f'{table_prefix}quantities': self.extractor.extract_quantities(),
f'{table_prefix}levels': self.extractor.extract_levels(),
f'{table_prefix}spaces': self.extractor.extract_spaces(),
f'{table_prefix}materials': self.extractor.extract_materials()
}
for table_name, df in tables.items():
# Remove complex columns for database storage
simple_df = df.select_dtypes(exclude=['object']).copy()
for col in df.columns:
if df[col].dtype == 'object':
simple_df[col] = df[col].astype(str)
simple_df.to_sql(table_name, engine, if_exists='replace', index=False)
return list(tables.keys())
```
| Element Type | Common Properties | Quantities |
|-------------|-------------------|------------|
| IfcWall | IsExternal, FireRating | Length, Height, Area, Volume |
| IfcSlab | IsExternal, LoadBearing | Area, Volume, Perimeter |
| IfcColumn | LoadBearing | Height, CrossSectionArea |
| IfcBeam | LoadBearing | Length, CrossSectionArea |
| IfcDoor | FireRating, AcousticRating | Width, Height |
| IfcWindow | ThermalTransmittance | Width, Height, Area |
```python
PSETS = {
'Pset_WallCommon': ['IsExternal', 'LoadBearing', 'FireRating'],
'Pset_SlabCommon': ['IsExternal', 'LoadBearing', 'AcousticRating'],
'Pset_ColumnCommon': ['IsExternal', 'LoadBearing'],
'Pset_BeamCommon': ['LoadBearing', 'FireRating'],
'Pset_DoorCommon': ['FireRating', 'AcousticRating', 'SecurityRating'],
'Pset_WindowCommon': ['ThermalTransmittance', 'GlazingType'],
'BaseQuantities': ['Length', 'Width', 'Height', 'Area', 'Volume']
}
```
bim-validation-pipeline for validating extracted dataqto-report for quantity take-off reports4d-simulation for linking to schedulesGenerated Mar 1, 2026
Extract quantities like area, volume, and material properties from IFC files to automate cost estimation and bill of materials generation. This reduces manual measurement errors and speeds up bidding processes in construction projects.
Parse IFC files to extract equipment data, spatial relationships, and maintenance properties for populating facility management systems. Enables seamless transition from design to operations, improving asset tracking and lifecycle management.
Extract material quantities and properties from IFC models to calculate embodied carbon, energy performance, and compliance with green building standards. Supports environmental certifications and regulatory reporting in sustainable design.
Use extracted spatial data and element properties from IFC files to identify conflicts between building systems (e.g., MEP vs. structural). Facilitates coordination among trades during design and construction phases.
Extract detailed element data from IFC models of existing structures to support renovation projects. Helps in assessing conditions, planning upgrades, and preserving architectural heritage with accurate digital records.
Offer a cloud-based service where users upload IFC files to extract and export structured data via APIs or web interfaces. Monetize through subscription tiers based on usage volume and advanced features like custom reporting.
Provide tailored solutions for companies needing specific data extraction workflows, such as integrating IFC data with ERP or CMMS systems. Revenue comes from project-based fees and ongoing support contracts.
License the extraction skill as an embedded component for BIM software, CAD tools, or construction management platforms. Partners pay licensing fees to enhance their products with IFC parsing capabilities.
💬 Integration Tip
Ensure IfcOpenShell is properly installed with dependencies; use virtual environments to manage Python packages and handle large IFC files with memory-efficient streaming if needed.
Advanced filesystem operations - listing, searching, batch processing, and directory analysis for Clawdbot
Perform advanced filesystem tasks including listing, recursive searching by name or content, batch copying/moving/deleting files, and analyzing directory siz...
Essential SSH commands for secure remote access, key management, tunneling, and file transfers.
Extract text from PDF files for LLM processing
The directory for AI agent services. Discover tools, platforms, and infrastructure built for agents.
Advanced filesystem operations - listing, searching, batch processing, and directory analysis for Clawdbot