DDC_Skills_for_AI_Agents_in_Construction bim-validation-report
Generate comprehensive BIM model validation reports. Check data quality, completeness, and compliance with standards.
install
source · Clone the upstream repo
git clone https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction "$T" && mkdir -p ~/.claude/skills && cp -r "$T/1_DDC_Toolkit/BIM-Analysis/bim-validation-report" ~/.claude/skills/datadrivenconstruction-ddc-skills-for-ai-agents-in-construction-bim-validation-r && rm -rf "$T"
manifest:
1_DDC_Toolkit/BIM-Analysis/bim-validation-report/SKILL.mdsource content
BIM Validation Report Generator
Business Case
Problem Statement
BIM models often have quality issues:
- Missing required properties
- Invalid or inconsistent data
- Non-compliant with project standards
- Incomplete model information
Solution
Automated BIM validation system that checks models against configurable rules and generates detailed compliance reports.
Business Value
- Quality assurance - Catch issues early
- Standards compliance - Meet project requirements
- Automation - Reduce manual QC effort
- Transparency - Clear validation results
Technical Implementation
import pandas as pd from datetime import datetime from typing import Dict, Any, List, Optional, Callable from dataclasses import dataclass, field from enum import Enum class ValidationSeverity(Enum): """Validation issue severity.""" ERROR = "error" WARNING = "warning" INFO = "info" class ValidationStatus(Enum): """Overall validation status.""" PASSED = "passed" PASSED_WITH_WARNINGS = "passed_with_warnings" FAILED = "failed" class RuleCategory(Enum): """Validation rule categories.""" REQUIRED_PROPERTIES = "required_properties" DATA_FORMAT = "data_format" NAMING_CONVENTION = "naming_convention" GEOMETRIC = "geometric" CLASSIFICATION = "classification" RELATIONSHIPS = "relationships" @dataclass class ValidationRule: """Single validation rule.""" rule_id: str name: str category: RuleCategory description: str severity: ValidationSeverity check_function: Callable applicable_categories: List[str] = field(default_factory=list) enabled: bool = True @dataclass class ValidationIssue: """Single validation issue.""" issue_id: str rule_id: str rule_name: str element_id: str element_name: str element_category: str severity: ValidationSeverity message: str details: Dict[str, Any] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: return { 'issue_id': self.issue_id, 'rule_id': self.rule_id, 'rule_name': self.rule_name, 'element_id': self.element_id, 'element_name': self.element_name, 'element_category': self.element_category, 'severity': self.severity.value, 'message': self.message } @dataclass class ValidationReport: """Complete validation report.""" project_name: str model_name: str validated_at: datetime status: ValidationStatus total_elements: int elements_with_issues: int issues: List[ValidationIssue] rules_checked: int summary_by_severity: Dict[str, int] summary_by_category: Dict[str, int] class BIMValidationEngine: """BIM model validation engine.""" def __init__(self, project_name: str, model_name: str): self.project_name = project_name self.model_name = model_name self.rules: List[ValidationRule] = [] self.issues: List[ValidationIssue] = [] self._issue_counter = 0 # Load default rules self._load_default_rules() def _load_default_rules(self): """Load standard validation rules.""" # Required properties rules self.add_rule(ValidationRule( rule_id="REQ-001", name="Element Name Required", category=RuleCategory.REQUIRED_PROPERTIES, description="All elements must have a name", severity=ValidationSeverity.ERROR, check_function=lambda e: bool(e.get('name')) )) self.add_rule(ValidationRule( rule_id="REQ-002", name="Level Assignment Required", category=RuleCategory.REQUIRED_PROPERTIES, description="Elements must be assigned to a level", severity=ValidationSeverity.WARNING, check_function=lambda e: bool(e.get('level')), applicable_categories=["Walls", "Floors", "Doors", "Windows"] )) self.add_rule(ValidationRule( rule_id="REQ-003", name="Material Required", category=RuleCategory.REQUIRED_PROPERTIES, description="Structural elements must have material defined", severity=ValidationSeverity.ERROR, check_function=lambda e: bool(e.get('material')), applicable_categories=["Structural Columns", "Structural Framing", "Floors"] )) # Naming convention rules self.add_rule(ValidationRule( rule_id="NAM-001", name="No Special Characters", category=RuleCategory.NAMING_CONVENTION, description="Names should not contain special characters", severity=ValidationSeverity.WARNING, check_function=self._check_no_special_chars )) self.add_rule(ValidationRule( rule_id="NAM-002", name="Name Length Check", category=RuleCategory.NAMING_CONVENTION, description="Names should be between 3 and 100 characters", severity=ValidationSeverity.INFO, check_function=lambda e: 3 <= len(e.get('name', '')) <= 100 )) # Classification rules self.add_rule(ValidationRule( rule_id="CLS-001", name="Classification Code Present", category=RuleCategory.CLASSIFICATION, description="Elements should have classification code", severity=ValidationSeverity.WARNING, check_function=lambda e: bool(e.get('classification_code') or e.get('uniformat')) )) # Geometric rules self.add_rule(ValidationRule( rule_id="GEO-001", name="Non-Zero Volume", category=RuleCategory.GEOMETRIC, description="3D elements must have non-zero volume", severity=ValidationSeverity.ERROR, check_function=lambda e: float(e.get('volume', 0)) > 0, applicable_categories=["Walls", "Floors", "Structural Columns", "Structural Framing"] )) self.add_rule(ValidationRule( rule_id="GEO-002", name="Valid Bounding Box", category=RuleCategory.GEOMETRIC, description="Elements must have valid bounding box", severity=ValidationSeverity.ERROR, check_function=self._check_valid_bbox )) def _check_no_special_chars(self, element: Dict[str, Any]) -> bool: """Check name for special characters.""" import re name = element.get('name', '') return bool(re.match(r'^[\w\s\-\.]+$', name)) def _check_valid_bbox(self, element: Dict[str, Any]) -> bool: """Check for valid bounding box.""" try: min_x = float(element.get('min_x', 0)) max_x = float(element.get('max_x', 0)) min_y = float(element.get('min_y', 0)) max_y = float(element.get('max_y', 0)) min_z = float(element.get('min_z', 0)) max_z = float(element.get('max_z', 0)) return max_x > min_x and max_y > min_y and max_z > min_z except (ValueError, TypeError): return False def add_rule(self, rule: ValidationRule): """Add validation rule.""" self.rules.append(rule) def add_custom_rule(self, rule_id: str, name: str, category: RuleCategory, check_function: Callable, severity: ValidationSeverity = ValidationSeverity.WARNING, description: str = "", categories: List[str] = None): """Add custom validation rule.""" rule = ValidationRule( rule_id=rule_id, name=name, category=category, description=description, severity=severity, check_function=check_function, applicable_categories=categories or [] ) self.add_rule(rule) def validate_element(self, element: Dict[str, Any]) -> List[ValidationIssue]: """Validate single element against all rules.""" issues = [] element_category = element.get('category', '') for rule in self.rules: if not rule.enabled: continue # Check if rule applies to this category if rule.applicable_categories and element_category not in rule.applicable_categories: continue try: passed = rule.check_function(element) if not passed: self._issue_counter += 1 issue = ValidationIssue( issue_id=f"ISS-{self._issue_counter:05d}", rule_id=rule.rule_id, rule_name=rule.name, element_id=str(element.get('element_id', '')), element_name=str(element.get('name', '')), element_category=element_category, severity=rule.severity, message=rule.description ) issues.append(issue) except Exception as e: # Rule check failed self._issue_counter += 1 issue = ValidationIssue( issue_id=f"ISS-{self._issue_counter:05d}", rule_id=rule.rule_id, rule_name=rule.name, element_id=str(element.get('element_id', '')), element_name=str(element.get('name', '')), element_category=element_category, severity=ValidationSeverity.ERROR, message=f"Rule check error: {str(e)}" ) issues.append(issue) return issues def validate_model(self, elements_df: pd.DataFrame) -> ValidationReport: """Validate entire BIM model.""" self.issues = [] elements_with_issues = set() for _, row in elements_df.iterrows(): element = row.to_dict() element_issues = self.validate_element(element) if element_issues: elements_with_issues.add(element.get('element_id')) self.issues.extend(element_issues) # Calculate summaries summary_by_severity = { 'error': sum(1 for i in self.issues if i.severity == ValidationSeverity.ERROR), 'warning': sum(1 for i in self.issues if i.severity == ValidationSeverity.WARNING), 'info': sum(1 for i in self.issues if i.severity == ValidationSeverity.INFO) } summary_by_category = {} for issue in self.issues: cat = issue.element_category summary_by_category[cat] = summary_by_category.get(cat, 0) + 1 # Determine overall status if summary_by_severity['error'] > 0: status = ValidationStatus.FAILED elif summary_by_severity['warning'] > 0: status = ValidationStatus.PASSED_WITH_WARNINGS else: status = ValidationStatus.PASSED return ValidationReport( project_name=self.project_name, model_name=self.model_name, validated_at=datetime.now(), status=status, total_elements=len(elements_df), elements_with_issues=len(elements_with_issues), issues=self.issues, rules_checked=len([r for r in self.rules if r.enabled]), summary_by_severity=summary_by_severity, summary_by_category=summary_by_category ) def export_report(self, report: ValidationReport, output_path: str): """Export validation report to Excel.""" with pd.ExcelWriter(output_path, engine='openpyxl') as writer: # Summary sheet summary_data = { 'Metric': ['Project', 'Model', 'Validated At', 'Status', 'Total Elements', 'Elements with Issues', 'Rules Checked', 'Errors', 'Warnings', 'Info'], 'Value': [report.project_name, report.model_name, report.validated_at.isoformat(), report.status.value, report.total_elements, report.elements_with_issues, report.rules_checked, report.summary_by_severity['error'], report.summary_by_severity['warning'], report.summary_by_severity['info']] } pd.DataFrame(summary_data).to_excel(writer, sheet_name='Summary', index=False) # Issues sheet issues_df = pd.DataFrame([i.to_dict() for i in report.issues]) if not issues_df.empty: issues_df.to_excel(writer, sheet_name='Issues', index=False) # By Category sheet cat_df = pd.DataFrame([ {'Category': k, 'Issue Count': v} for k, v in report.summary_by_category.items() ]) if not cat_df.empty: cat_df.to_excel(writer, sheet_name='By Category', index=False) return output_path def generate_validation_report(elements_df: pd.DataFrame, project_name: str, model_name: str, output_path: str = None) -> ValidationReport: """Quick function to generate validation report.""" engine = BIMValidationEngine(project_name, model_name) report = engine.validate_model(elements_df) if output_path: engine.export_report(report, output_path) return report
Quick Start
# Load BIM elements elements = pd.read_excel("bim_elements.xlsx") # Run validation report = generate_validation_report( elements, project_name="Office Tower", model_name="Architectural Model v3.2", output_path="validation_report.xlsx" ) print(f"Status: {report.status.value}") print(f"Errors: {report.summary_by_severity['error']}") print(f"Warnings: {report.summary_by_severity['warning']}")
Common Use Cases
1. Custom Validation Rules
engine = BIMValidationEngine("Project", "Model") # Add custom rule engine.add_custom_rule( rule_id="CUSTOM-001", name="Fire Rating Required", category=RuleCategory.REQUIRED_PROPERTIES, check_function=lambda e: bool(e.get('fire_rating')), severity=ValidationSeverity.ERROR, categories=["Walls", "Doors"] )
2. Filter Issues
# Get only errors errors = [i for i in report.issues if i.severity == ValidationSeverity.ERROR] # Get issues for specific category wall_issues = [i for i in report.issues if i.element_category == "Walls"]
3. Automated QC Pipeline
report = engine.validate_model(elements) if report.status == ValidationStatus.FAILED: send_notification("BIM validation failed", report.summary_by_severity)
Resources
- DDC Book: Chapter 4.3 - BIM Validation
- Reference: ISO 19650, buildingSMART IDS