bim-classification-ai

BIM Classification AI

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "bim-classification-ai" with this command: npx skills add datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/datadrivenconstruction-ddc-skills-for-ai-agents-in-construction-bim-classification-ai

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

import pandas as pd from typing import Dict, Any, List, Optional, Tuple from dataclasses import dataclass, field from enum import Enum import re

class ClassificationSystem(Enum): """Classification standards.""" UNIFORMAT = "uniformat" MASTERFORMAT = "masterformat" OMNICLASS = "omniclass" UNICLASS = "uniclass" CWICR = "cwicr"

@dataclass class ClassificationCode: """Classification code with metadata.""" code: str title: str system: ClassificationSystem level: int parent_code: Optional[str] = None keywords: List[str] = field(default_factory=list)

@dataclass class ClassificationResult: """Result of classification attempt.""" element_id: str element_name: str element_category: str suggested_codes: List[Tuple[ClassificationCode, float]] # (code, confidence) selected_code: Optional[ClassificationCode] = None manual_override: bool = False

class 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 codes
    uniformat_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 title
            if query_lower in code.title.lower():
                results.append(code)
                continue
            # Check keywords
            if any(query_lower in kw.lower() for kw in code.keywords):
                results.append(code)

    return results

class 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 mapping
    keywords = self.category_mappings.get(category, [])

    # Add keywords from element name
    name_words = re.findall(r'\w+', element_name.lower())
    keywords.extend(name_words)

    # Add keywords from properties
    if properties:
        for key, value in properties.items():
            if isinstance(value, str):
                keywords.extend(re.findall(r'\w+', value.lower()))

    # Search classification codes
    for 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 confidence
    seen = 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 result

def _calculate_confidence(self, code: ClassificationCode,
                         keywords: List[str], category: str) -> float:
    """Calculate classification confidence score."""
    score = 0.0

    # Direct category match
    if category in self.category_mappings:
        if code.code in self.category_mappings[category]:
            score += 0.5

    # Keyword matches
    keyword_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 match
    title_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_codes
                                   if c.system == ClassificationSystem.UNIFORMAT), None),
            'masterformat_code': next((c.code for c, _ in result.suggested_codes
                                      if 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.results
                        if 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 suggestions
        for i, (code, conf) in enumerate(result.suggested_codes[:3]):
            row[f'suggestion_{i+1}_code'] = code.code
            row[f'suggestion_{i+1}_confidence'] = round(conf, 2)

        data.append(row)

    return pd.DataFrame(data)

Quick Start

Initialize classifier

classifier = BIMClassificationAI()

Classify single element

result = 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

Load BIM elements

elements = pd.read_excel("bim_elements.xlsx")

Classify all

classified = classifier.classify_batch(elements) classified.to_excel("classified_elements.xlsx")

  1. Map to CWICR

Get UniFormat code for cost mapping

uniformat = result.selected_code.code cwicr_code = map_uniformat_to_cwicr(uniformat)

  1. Quality Check

summary = classifier.get_summary() print(f"Classification rate: {summary['classification_rate']}%")

Resources

  • DDC Book: Chapter 2.5 - Data Standards

  • Reference: UniFormat II, CSI MasterFormat

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

cad-to-data

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

drawing-analyzer

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

dwg-to-excel

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

cost-estimation-resource

No summary provided by upstream source.

Repository SourceNeeds Review