BIM Consistency Checker for Construction
Overview
Validate BIM model consistency including naming conventions, parameter completeness, spatial relationships, classification compliance, and cross-reference integrity.
Business Case
BIM consistency checking ensures:
- Data Quality: Complete and accurate model data
- Interoperability: Models work across platforms
- Coordination: Consistent information for all trades
- Deliverable Compliance: Meet BIM execution plan requirements
Technical Implementation
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional, Set
from enum import Enum
import re
class CheckSeverity(Enum):
ERROR = "error"
WARNING = "warning"
INFO = "info"
class CheckCategory(Enum):
NAMING = "naming"
PARAMETERS = "parameters"
SPATIAL = "spatial"
CLASSIFICATION = "classification"
GEOMETRY = "geometry"
RELATIONSHIPS = "relationships"
@dataclass
class ConsistencyIssue:
element_id: str
element_name: str
category: CheckCategory
severity: CheckSeverity
rule: str
message: str
suggestion: str = ""
@dataclass
class ConsistencyReport:
model_name: str
total_elements: int
elements_checked: int
issues: List[ConsistencyIssue]
issues_by_category: Dict[str, int]
issues_by_severity: Dict[str, int]
pass_rate: float
@dataclass
class NamingConvention:
element_type: str
pattern: str
description: str
examples: List[str]
class BIMConsistencyChecker:
"""Check BIM model consistency and data quality."""
# Default naming conventions
DEFAULT_NAMING_RULES = [
NamingConvention(
element_type='Level',
pattern=r'^(L|Level)\s*\d{1,2}$|^(B|Basement)\s*\d?$|^(R|Roof)$',
description='Levels should follow L01, Level 1, B1, Roof pattern',
examples=['L01', 'Level 1', 'B1', 'Roof']
),
NamingConvention(
element_type='Grid',
pattern=r'^[A-Z]$|^\d{1,2}$|^[A-Z]\.\d$',
description='Grids should be single letters (A-Z) or numbers',
examples=['A', '1', 'A.1']
),
NamingConvention(
element_type='Room',
pattern=r'^\d{3,4}[A-Z]?\s*-?\s*.+',
description='Rooms should have number and name (101 - Office)',
examples=['101 - Office', '201A Conference']
),
NamingConvention(
element_type='Wall',
pattern=r'^(INT|EXT|CW|CMU|GYP)[-_].+',
description='Walls should have type prefix',
examples=['INT-GYP-1HR', 'EXT-CMU-8IN']
),
NamingConvention(
element_type='Door',
pattern=r'^[A-Z]?\d{2,3}[A-Z]?$',
description='Doors should follow type numbering',
examples=['101', 'A101', '101A']
),
]
# Required parameters by element type
REQUIRED_PARAMETERS = {
'Wall': ['Fire Rating', 'Function', 'Structural'],
'Door': ['Fire Rating', 'Width', 'Height', 'Frame Material'],
'Room': ['Name', 'Number', 'Area', 'Department'],
'Window': ['Width', 'Height', 'Glass Type'],
'Floor': ['Structural', 'Fire Rating'],
'Ceiling': ['Height', 'Type'],
'Column': ['Structural Material', 'Shape'],
'Beam': ['Structural Material', 'Size'],
}
def __init__(self):
self.naming_rules: List[NamingConvention] = list(self.DEFAULT_NAMING_RULES)
self.required_params: Dict[str, List[str]] = dict(self.REQUIRED_PARAMETERS)
self.issues: List[ConsistencyIssue] = []
def add_naming_rule(self, rule: NamingConvention):
"""Add custom naming convention rule."""
self.naming_rules.append(rule)
def set_required_parameters(self, element_type: str, parameters: List[str]):
"""Set required parameters for an element type."""
self.required_params[element_type] = parameters
def check_model(self, elements: List[Dict]) -> ConsistencyReport:
"""Run all consistency checks on model elements."""
self.issues = []
for element in elements:
self._check_naming(element)
self._check_parameters(element)
self._check_spatial(element)
self._check_classification(element)
self._check_geometry(element)
# Cross-element checks
self._check_relationships(elements)
self._check_duplicates(elements)
# Calculate statistics
issues_by_category = {}
issues_by_severity = {}
for issue in self.issues:
cat = issue.category.value
sev = issue.severity.value
issues_by_category[cat] = issues_by_category.get(cat, 0) + 1
issues_by_severity[sev] = issues_by_severity.get(sev, 0) + 1
elements_with_issues = len(set(i.element_id for i in self.issues))
pass_rate = (len(elements) - elements_with_issues) / len(elements) * 100 if elements else 100
return ConsistencyReport(
model_name='Model',
total_elements=len(elements),
elements_checked=len(elements),
issues=self.issues,
issues_by_category=issues_by_category,
issues_by_severity=issues_by_severity,
pass_rate=pass_rate
)
def _check_naming(self, element: Dict):
"""Check element naming conventions."""
element_type = element.get('type', '')
name = element.get('name', '')
element_id = element.get('id', '')
if not name:
self.issues.append(ConsistencyIssue(
element_id=element_id,
element_name='(no name)',
category=CheckCategory.NAMING,
severity=CheckSeverity.ERROR,
rule='Name Required',
message='Element has no name',
suggestion='Assign a descriptive name following conventions'
))
return
# Check against naming rules
for rule in self.naming_rules:
if rule.element_type.lower() in element_type.lower():
if not re.match(rule.pattern, name, re.IGNORECASE):
self.issues.append(ConsistencyIssue(
element_id=element_id,
element_name=name,
category=CheckCategory.NAMING,
severity=CheckSeverity.WARNING,
rule=f'{rule.element_type} Naming',
message=f'Name "{name}" does not follow convention',
suggestion=f'{rule.description}. Examples: {", ".join(rule.examples)}'
))
# Check for special characters
if re.search(r'[<>:"/\\|?*]', name):
self.issues.append(ConsistencyIssue(
element_id=element_id,
element_name=name,
category=CheckCategory.NAMING,
severity=CheckSeverity.ERROR,
rule='Invalid Characters',
message='Name contains invalid characters',
suggestion='Remove special characters: < > : " / \\ | ? *'
))
def _check_parameters(self, element: Dict):
"""Check parameter completeness."""
element_type = element.get('type', '')
element_id = element.get('id', '')
name = element.get('name', '')
params = element.get('parameters', {})
# Check required parameters
required = self.required_params.get(element_type, [])
for param in required:
if param not in params or params[param] in [None, '', 'None']:
self.issues.append(ConsistencyIssue(
element_id=element_id,
element_name=name,
category=CheckCategory.PARAMETERS,
severity=CheckSeverity.WARNING,
rule='Required