BIM Cost Estimation with DDC CWICR
Generate accurate cost estimates from BIM models using AI classification and the DDC CWICR construction cost database.
Business Case
Problem: Traditional cost estimation:
- Manual and time-consuming (weeks for detailed estimate)
- Subjective and inconsistent between estimators
- Requires specialized knowledge
- Difficult to update with design changes
Solution: Automated BIM-to-cost pipeline:
- Extract quantities directly from model
- AI classifies elements to work items
- Vector search finds matching prices in CWICR
- Complete estimate in hours, not weeks
ROI: 80% reduction in estimation time, consistent methodology
System Architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ BIM TO COST ESTIMATION PIPELINE │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │
│ │ BIM │ │ DDC │ │ AI │ │ DDC CWICR │ │
│ │ Model │────►│Converter│────►│ LLM │────►│ Vector Search │ │
│ │.rvt/.ifc│ │ │ │ │ │ (Qdrant) │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
│ │ .xlsx │ │ Work │ │ Matched │ │
│ │ QTO │ │ Items │ │ Rates │ │
│ └─────────┘ └─────────┘ └──────────┘ │
│ │ │ │ │
│ └──────────────┼────────────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ COST ESTIMATE │ │
│ │ │ │
│ │ • By element │ │
│ │ • By trade │ │
│ │ • By phase │ │
│ │ • Resources │ │
│ └─────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
DDC CWICR Database
Database Overview:
work_items: 55,719
resources: 27,672
languages: 9 (AR, DE, EN, ES, FR, HI, PT, RU, ZH)
fields_per_item: 85
embedding_model: text-embedding-3-large (3072d)
vector_db: Qdrant
Collections:
- ddc_cwicr_ar # Arabic (Dubai prices)
- ddc_cwicr_de # German (Berlin prices)
- ddc_cwicr_en # English (Toronto prices)
- ddc_cwicr_es # Spanish (Barcelona prices)
- ddc_cwicr_fr # French (Paris prices)
- ddc_cwicr_hi # Hindi (Mumbai prices)
- ddc_cwicr_pt # Portuguese (São Paulo prices)
- ddc_cwicr_ru # Russian (St. Petersburg prices)
- ddc_cwicr_zh # Chinese (Shanghai prices)
Pipeline Stages
| Stage | Name | Description |
|---|---|---|
| 0 | Collect BIM Data | Extract elements from Revit/IFC |
| 1 | Project Detection | AI identifies project type |
| 2 | Phase Generation | AI creates construction phases |
| 3 | Element Assignment | AI maps types to phases |
| 4 | Work Decomposition | AI breaks types into work items |
| 5 | Vector Search | Find matching rates in CWICR |
| 6 | Unit Mapping | Convert BIM units to rate units |
| 7 | Cost Calculation | Qty × Unit Price |
| 7.5 | Validation | CTO review for completeness |
| 8 | Aggregation | Sum by phases and categories |
| 9 | Report Generation | HTML and Excel outputs |
Python Implementation
import pandas as pd
import numpy as np
from qdrant_client import QdrantClient
from qdrant_client.models import Filter, FieldCondition, MatchValue
from openai import OpenAI
from typing import List, Dict, Optional
from dataclasses import dataclass
import json
@dataclass
class WorkItem:
"""Matched work item from CWICR"""
cwicr_code: str
description: str
unit: str
unit_price: float
labor_cost: float
material_cost: float
equipment_cost: float
productivity: float # units per hour
currency: str
confidence: float
@dataclass
class CostLineItem:
"""Single line item in estimate"""
bim_type: str
work_item: WorkItem
quantity: float
quantity_unit: str
total_cost: float
labor_cost: float
material_cost: float
equipment_cost: float
phase: str
trade: str
class BIMCostEstimator:
"""BIM to cost estimation using DDC CWICR"""
def __init__(
self,
qdrant_url: str,
qdrant_api_key: str = None,
openai_api_key: str = None,
language: str = "EN"
):
self.qdrant = QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
self.openai = OpenAI(api_key=openai_api_key)
self.language = language
self.collection = f"ddc_cwicr_{language.lower()}"
def get_embedding(self, text: str) -> List[float]:
"""Generate embedding for text"""
response = self.openai.embeddings.create(
model="text-embedding-3-large",
input=text,
dimensions=3072
)
return response.data[0].embedding
def search_cwicr(
self,
query: str,
limit: int = 5,
category_filter: str = None
) -> List[WorkItem]:
"""Search CWICR database for matching work items"""
# Get embedding
query_vector = self.get_embedding(query)
# Build filter if category specified
query_filter = None
if category_filter:
query_filter = Filter(
must=[
FieldCondition(
key="category",
match=MatchValue(value=category_filter)
)
]
)
# Search
results = self.qdrant.search(
collection_name=self.collection,
query_vector=query_vector,
query_filter=query_filter,
limit=limit
)
# Parse results
work_items = []
for r in results:
payload = r.payload
work_items.append(WorkItem(
cwicr_code=payload.get('code', ''),
description=payload.get('description', ''),
unit=payload.get('unit', ''),
unit_price=float(payload.get('unit_price', 0)),
labor_cost=float(payload.get('labor_cost', 0)),
material_cost=float(payload.get('material_cost', 0)),
equipment_cost=float(payload.get('equipment_cost', 0)),
productivity=float(payload.get('productivity', 1)),
currency=payload.get('currency', 'USD'),
confidence=r.score
))
return work_items
def decompose_bim_type(
self,
bim_type: str,
category: str
) -> List[str]:
"""Use LLM to decompose BIM type into work items"""
prompt = f"""
Decompose this BIM element type into construction work items:
BIM Type: {bim_type}
Category: {category}
List the individual work activities needed to construct this element.
For example, "Brick Wall 240mm" decomposes into:
- Masonry: Brick laying
- Mortar: Cement mortar for joints
- Plaster: Internal plaster finish
- Paint: Wall painting
Return a JSON array of