<< All versions
Skill v1.0.0
currentAutomated scan100/100datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/bim-classification-ai
──Details
PublishedSeptember 29, 2026 at 06:57 AM
Content Hashsha256:1a95c7ef39c6240e...
Git SHAce45bbfbdd63
──Files
Files (1 file, 15.0 KB)
SKILL.md15.0 KBactive
SKILL.md · 373 lines · 15.0 KB
version: "1.0.0" name: "bim-classification-ai" description: "Classify BIM elements using AI and standard classification systems. Map elements to UniFormat, MasterFormat, OmniClass, and CWICR codes." homepage: "https://datadrivenconstruction.io" metadata: {"openclaw": {"emoji": "🔍", "os": ["win32"], "homepage": "https://datadrivenconstruction.io", "requires": {"bins": ["python3"]}}}
BIM Classification AI
Business Case
Problem Statement
BIM models often lack proper classification:
- Elements without classification codes
- Inconsistent naming conventions
- Manual classification is tedious
- Difficult to map to cost databases
Solution
AI-powered classification system that analyzes BIM element properties and suggests appropriate classification codes from multiple standards.
Business Value
- Automation - Reduce manual classification effort
- Consistency - Standardized classification across projects
- Integration - Enable cost estimation and QTO
- Quality - Improved data quality in BIM models
Technical Implementation
python
import pandas as pdfrom typing import Dict, Any, List, Optional, Tuplefrom dataclasses import dataclass, fieldfrom enum import Enumimport reclass ClassificationSystem(Enum):"""Classification standards."""UNIFORMAT = "uniformat"MASTERFORMAT = "masterformat"OMNICLASS = "omniclass"UNICLASS = "uniclass"CWICR = "cwicr"@dataclassclass ClassificationCode:"""Classification code with metadata."""code: strtitle: strsystem: ClassificationSystemlevel: intparent_code: Optional[str] = Nonekeywords: List[str] = field(default_factory=list)@dataclassclass ClassificationResult:"""Result of classification attempt."""element_id: strelement_name: strelement_category: strsuggested_codes: List[Tuple[ClassificationCode, float]] # (code, confidence)selected_code: Optional[ClassificationCode] = Nonemanual_override: bool = Falseclass ClassificationDatabase:"""Classification codes database."""def __init__(self):self.codes: Dict[ClassificationSystem, List[ClassificationCode]] = {system: [] for system in ClassificationSystem}self._load_standard_codes()def _load_standard_codes(self):"""Load standard classification codes."""# UniFormat II codesuniformat_codes = [("A", "Substructure", 1, None, ["foundation", "basement", "excavation"]),("A10", "Foundations", 2, "A", ["footing", "pile", "foundation"]),("A1010", "Standard Foundations", 3, "A10", ["spread footing", "strip footing"]),("A1020", "Special Foundations", 3, "A10", ["pile", "caisson", "mat foundation"]),("B", "Shell", 1, None, ["superstructure", "exterior", "roof"]),("B10", "Superstructure", 2, "B", ["floor", "roof", "structure"]),("B1010", "Floor Construction", 3, "B10", ["slab", "deck", "floor"]),("B1020", "Roof Construction", 3, "B10", ["roof", "deck", "truss"]),("B20", "Exterior Enclosure", 2, "B", ["wall", "window", "door"]),("B2010", "Exterior Walls", 3, "B20", ["curtain wall", "masonry", "cladding"]),("B2020", "Exterior Windows", 3, "B20", ["window", "glazing", "storefront"]),("B30", "Roofing", 2, "B", ["roof", "membrane", "insulation"]),("C", "Interiors", 1, None, ["partition", "ceiling", "floor finish"]),("C10", "Interior Construction", 2, "C", ["partition", "door", "glazing"]),("C20", "Stairs", 2, "C", ["stair", "railing", "ladder"]),("C30", "Interior Finishes", 2, "C", ["finish", "paint", "flooring"]),("D", "Services", 1, None, ["mechanical", "electrical", "plumbing"]),("D10", "Conveying", 2, "D", ["elevator", "escalator", "lift"]),("D20", "Plumbing", 2, "D", ["pipe", "fixture", "drain"]),("D30", "HVAC", 2, "D", ["duct", "hvac", "air handling"]),("D40", "Fire Protection", 2, "D", ["sprinkler", "fire", "suppression"]),("D50", "Electrical", 2, "D", ["electrical", "power", "lighting"]),]for code, title, level, parent, keywords in uniformat_codes:self.codes[ClassificationSystem.UNIFORMAT].append(ClassificationCode(code, title, ClassificationSystem.UNIFORMAT, level, parent, keywords))# MasterFormat codes (simplified)masterformat_codes = [("03", "Concrete", 1, None, ["concrete", "formwork", "reinforcing"]),("03 30 00", "Cast-in-Place Concrete", 2, "03", ["concrete", "pour", "slab"]),("03 41 00", "Precast Structural Concrete", 2, "03", ["precast", "concrete", "panel"]),("04", "Masonry", 1, None, ["brick", "block", "stone"]),("05", "Metals", 1, None, ["steel", "metal", "aluminum"]),("05 12 00", "Structural Steel Framing", 2, "05", ["beam", "column", "steel"]),("06", "Wood, Plastics, Composites", 1, None, ["wood", "timber", "lumber"]),("07", "Thermal and Moisture Protection", 1, None, ["insulation", "roofing", "waterproofing"]),("08", "Openings", 1, None, ["door", "window", "glazing"]),("09", "Finishes", 1, None, ["drywall", "paint", "flooring"]),("21", "Fire Suppression", 1, None, ["sprinkler", "fire", "suppression"]),("22", "Plumbing", 1, None, ["pipe", "fixture", "plumbing"]),("23", "HVAC", 1, None, ["hvac", "duct", "mechanical"]),("26", "Electrical", 1, None, ["electrical", "power", "lighting"]),]for code, title, level, parent, keywords in masterformat_codes:self.codes[ClassificationSystem.MASTERFORMAT].append(ClassificationCode(code, title, ClassificationSystem.MASTERFORMAT, level, parent, keywords))def search(self, query: str, system: ClassificationSystem = None) -> List[ClassificationCode]:"""Search classification codes by keyword."""results = []query_lower = query.lower()systems = [system] if system else list(ClassificationSystem)for sys in systems:for code in self.codes.get(sys, []):# Check titleif query_lower in code.title.lower():results.append(code)continue# Check keywordsif any(query_lower in kw.lower() for kw in code.keywords):results.append(code)return resultsclass BIMClassificationAI:"""AI-powered BIM element classification."""def __init__(self, classification_db: ClassificationDatabase = None):self.db = classification_db or ClassificationDatabase()self.category_mappings = self._load_category_mappings()self.results: List[ClassificationResult] = []def _load_category_mappings(self) -> Dict[str, List[str]]:"""Load Revit/IFC category to classification mappings."""return {# Structural"Structural Columns": ["B10", "05 12 00", "column", "structural"],"Structural Framing": ["B10", "05 12 00", "beam", "framing"],"Structural Foundations": ["A10", "03 30 00", "foundation", "footing"],"Floors": ["B1010", "03 30 00", "floor", "slab"],# Architectural"Walls": ["B20", "04", "wall", "partition"],"Curtain Walls": ["B2010", "08 44 00", "curtain wall", "glazing"],"Windows": ["B2020", "08 50 00", "window", "glazing"],"Doors": ["C10", "08 10 00", "door", "opening"],"Roofs": ["B30", "07 50 00", "roof", "roofing"],"Ceilings": ["C30", "09 51 00", "ceiling", "finish"],"Stairs": ["C20", "05 51 00", "stair", "railing"],# MEP"Ducts": ["D30", "23 31 00", "duct", "hvac"],"Pipes": ["D20", "22 11 00", "pipe", "plumbing"],"Electrical Equipment": ["D50", "26 20 00", "electrical", "panel"],"Lighting Fixtures": ["D50", "26 51 00", "light", "fixture"],"Sprinklers": ["D40", "21 13 00", "sprinkler", "fire protection"],"Mechanical Equipment": ["D30", "23 70 00", "ahu", "hvac equipment"],}def classify_element(self,element_id: str,element_name: str,category: str,properties: Dict[str, Any] = None,target_systems: List[ClassificationSystem] = None) -> ClassificationResult:"""Classify a single BIM element."""target_systems = target_systems or [ClassificationSystem.UNIFORMAT, ClassificationSystem.MASTERFORMAT]suggestions = []# Get keywords from category mappingkeywords = self.category_mappings.get(category, [])# Add keywords from element namename_words = re.findall(r'\w+', element_name.lower())keywords.extend(name_words)# Add keywords from propertiesif properties:for key, value in properties.items():if isinstance(value, str):keywords.extend(re.findall(r'\w+', value.lower()))# Search classification codesfor system in target_systems:for keyword in keywords:matches = self.db.search(keyword, system)for match in matches:confidence = self._calculate_confidence(match, keywords, category)suggestions.append((match, confidence))# Remove duplicates and sort by confidenceseen = set()unique_suggestions = []for code, conf in sorted(suggestions, key=lambda x: x[1], reverse=True):if code.code not in seen:seen.add(code.code)unique_suggestions.append((code, conf))result = ClassificationResult(element_id=element_id,element_name=element_name,element_category=category,suggested_codes=unique_suggestions[:5],selected_code=unique_suggestions[0][0] if unique_suggestions else None)self.results.append(result)return resultdef _calculate_confidence(self, code: ClassificationCode,keywords: List[str], category: str) -> float:"""Calculate classification confidence score."""score = 0.0# Direct category matchif category in self.category_mappings:if code.code in self.category_mappings[category]:score += 0.5# Keyword matcheskeyword_matches = sum(1 for kw in keywords if kw.lower() in[k.lower() for k in code.keywords])score += min(keyword_matches * 0.1, 0.3)# Title matchtitle_words = code.title.lower().split()title_matches = sum(1 for kw in keywords if kw.lower() in title_words)score += min(title_matches * 0.1, 0.2)return min(score, 1.0)def classify_batch(self, elements_df: pd.DataFrame,id_column: str = 'element_id',name_column: str = 'name',category_column: str = 'category') -> pd.DataFrame:"""Classify multiple elements from DataFrame."""results = []for _, row in elements_df.iterrows():result = self.classify_element(element_id=str(row[id_column]),element_name=str(row[name_column]),category=str(row[category_column]),properties=row.to_dict())results.append({'element_id': result.element_id,'element_name': result.element_name,'category': result.element_category,'uniformat_code': next((c.code for c, _ in result.suggested_codesif c.system == ClassificationSystem.UNIFORMAT), None),'masterformat_code': next((c.code for c, _ in result.suggested_codesif c.system == ClassificationSystem.MASTERFORMAT), None),'confidence': result.suggested_codes[0][1] if result.suggested_codes else 0})return pd.DataFrame(results)def get_summary(self) -> Dict[str, Any]:"""Get classification summary."""total = len(self.results)classified = sum(1 for r in self.results if r.selected_code)high_confidence = sum(1 for r in self.resultsif r.suggested_codes and r.suggested_codes[0][1] > 0.7)return {'total_elements': total,'classified': classified,'classification_rate': round(classified / total * 100, 1) if total > 0 else 0,'high_confidence': high_confidence,'high_confidence_rate': round(high_confidence / total * 100, 1) if total > 0 else 0}def export_results(self) -> pd.DataFrame:"""Export classification results to DataFrame."""data = []for result in self.results:row = {'element_id': result.element_id,'element_name': result.element_name,'category': result.element_category,'selected_code': result.selected_code.code if result.selected_code else None,'selected_title': result.selected_code.title if result.selected_code else None,'selected_system': result.selected_code.system.value if result.selected_code else None,'manual_override': result.manual_override}# Add top suggestionsfor i, (code, conf) in enumerate(result.suggested_codes[:3]):row[f'suggestion_{i+1}_code'] = code.coderow[f'suggestion_{i+1}_confidence'] = round(conf, 2)data.append(row)return pd.DataFrame(data)
Quick Start
python
# Initialize classifierclassifier = BIMClassificationAI()# Classify single elementresult = classifier.classify_element(element_id="12345",element_name="Concrete Floor Slab Level 2",category="Floors",properties={'material': 'Concrete', 'thickness': '200mm'})print(f"Suggested: {result.selected_code.code} - {result.selected_code.title}")print(f"Confidence: {result.suggested_codes[0][1]:.1%}")
Common Use Cases
1. Batch Classification
python
# Load BIM elementselements = pd.read_excel("bim_elements.xlsx")# Classify allclassified = classifier.classify_batch(elements)classified.to_excel("classified_elements.xlsx")
2. Map to CWICR
python
# Get UniFormat code for cost mappinguniformat = result.selected_code.codecwicr_code = map_uniformat_to_cwicr(uniformat)
3. Quality Check
python
summary = classifier.get_summary()print(f"Classification rate: {summary['classification_rate']}%")
Resources
- DDC Book: Chapter 2.5 - Data Standards
- Reference: UniFormat II, CSI MasterFormat