| name | bim-cost-estimation-cwicr |
| description | Automated cost estimation from BIM models using DDC CWICR database with 55,719 work items. AI classification + vector search for accurate pricing. |
| homepage | https://datadrivenconstruction.io |
| metadata | {"openclaw":{"emoji":"🏗️","os":["darwin","linux","win32"],"homepage":"https://datadrivenconstruction.io","requires":{"bins":["python3"],"env":["OPENAI_API_KEY","QDRANT_URL"]},"primaryEnv":"OPENAI_API_KEY"}} |
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
- ddc_cwicr_de
- ddc_cwicr_en
- ddc_cwicr_es
- ddc_cwicr_fr
- ddc_cwicr_hi
- ddc_cwicr_pt
- ddc_cwicr_ru
- ddc_cwicr_zh
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
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 :
():
.qdrant = QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
.openai = OpenAI(api_key=openai_api_key)
.language = language
.collection =
() -> []:
response = .openai.embeddings.create(
model=,
=text,
dimensions=
)
response.data[].embedding
() -> [WorkItem]:
query_vector = .get_embedding(query)
query_filter =
category_filter:
query_filter = Filter(
must=[
FieldCondition(
key=,
=MatchValue(value=category_filter)
)
]
)
results = .qdrant.search(
collection_name=.collection,
query_vector=query_vector,
query_filter=query_filter,
limit=limit
)
work_items = []
r results:
payload = r.payload
work_items.append(WorkItem(
cwicr_code=payload.get(, ),
description=payload.get(, ),
unit=payload.get(, ),
unit_price=(payload.get(, )),
labor_cost=(payload.get(, )),
material_cost=(payload.get(, )),
equipment_cost=(payload.get(, )),
productivity=(payload.get(, )),
currency=payload.get(, ),
confidence=r.score
))
work_items
() -> []:
prompt =
response = .openai.chat.completions.create(
model=,
messages=[{: , : prompt}],
response_format={: }
)
:
result = json.loads(response.choices[].message.content)
result.get(, [bim_type])
:
[bim_type]
() -> [CostLineItem]:
work_descriptions = .decompose_bim_type(bim_type, category)
line_items = []
work_desc work_descriptions:
matches = .search_cwicr(work_desc, limit=)
matches:
best_match = matches[]
adjusted_qty = ._convert_units(
quantity, quantity_unit, best_match.unit
)
total = adjusted_qty * best_match.unit_price
labor = adjusted_qty * best_match.labor_cost
material = adjusted_qty * best_match.material_cost
equipment = adjusted_qty * best_match.equipment_cost
line_items.append(CostLineItem(
bim_type=bim_type,
work_item=best_match,
quantity=adjusted_qty,
quantity_unit=best_match.unit,
total_cost=total,
labor_cost=labor,
material_cost=material,
equipment_cost=equipment,
phase=phase,
trade=._get_trade(category)
))
line_items
() -> [CostLineItem]:
all_line_items = []
grouped = qto_data.groupby([category_column, type_column]).agg({
quantity_column:
}).reset_index()
_, row grouped.iterrows():
items = .estimate_element(
bim_type=row[type_column],
category=row[category_column],
quantity=row[quantity_column],
quantity_unit=
)
all_line_items.extend(items)
all_line_items
() -> :
conversions = {
(, ): ,
(, ): ,
(, ): ,
(, ): ,
(, ): ,
(, ): ,
}
key = (from_unit.lower(), to_unit.lower())
factor = conversions.get(key, )
value * factor
() -> :
trade_map = {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
:
}
trade_map.get(category, )
() -> :
records = []
item line_items:
records.append({
: item.bim_type,
: item.work_item.description,
: item.work_item.cwicr_code,
: (item.quantity, ),
: item.quantity_unit,
: (item.work_item.unit_price, ),
: (item.labor_cost, ),
: (item.material_cost, ),
: (item.equipment_cost, ),
: (item.total_cost, ),
: item.phase,
: item.trade,
: item.work_item.currency,
: (item.work_item.confidence, )
})
df = pd.DataFrame(records)
total_cost = df[].()
total_labor = df[].()
total_material = df[].()
total_equipment = df[].()
by_trade = df.groupby()[].().sort_values(ascending=)
excel_path =
pd.ExcelWriter(excel_path, engine=) writer:
summary_data = {
: [, , , ],
: [total_cost, total_labor, total_material, total_equipment]
}
pd.DataFrame(summary_data).to_excel(writer, sheet_name=, index=)
by_trade.to_frame().to_excel(writer, sheet_name=)
df.to_excel(writer, sheet_name=, index=)
{
: excel_path,
: total_cost,
: total_labor,
: total_material,
: total_equipment,
: by_trade.to_dict(),
: (df),
: line_items[].work_item.currency line_items
}
() -> :
subprocess
pathlib Path
()
subprocess.run([
,
model_path,
,
])
xlsx_path = Path(model_path).with_suffix()
()
df = pd.read_excel(xlsx_path)
estimator = BIMCostEstimator(
qdrant_url=qdrant_url,
language=language
)
()
line_items = estimator.estimate_from_qto(df)
project_name = Path(model_path).stem
result = estimator.generate_estimate_report(
line_items=line_items,
project_name=project_name,
output_path=output_dir
)
()
()
()
result
__name__ == :
result = estimate_from_bim_model(
model_path=,
qdrant_url=,
language=,
output_dir=
)
n8n Workflow
See: n8n_4_CAD_(BIM)_Cost_Estimation_Pipeline_4D_5D_with_DDC_CWICR.json
stages:
- convert: RvtExporter → XLSX
- detect_project: LLM identifies project type
- generate_phases: LLM creates construction phases
- decompose: LLM breaks types into work items
- vector_search: Qdrant finds CWICR matches
- calculate: Qty × Unit Price
- validate: CTO review
- report: HTML + Excel output
Output Example
╔══════════════════════════════════════════════════════════════╗
║ COST ESTIMATE SUMMARY ║
║ Project: Office Building Berlin ║
║ Date: 2026-01-24 ║
╠══════════════════════════════════════════════════════════════╣
TOTAL PROJECT COST: EUR 4,523,678.00
───────────────────────────────────────────────────────────────
Labor: EUR 1,847,234.00 (41%)
Materials: EUR 2,312,456.00 (51%)
Equipment: EUR 363,988.00 ( 8%)
BY TRADE
───────────────────────────────────────────────────────────────
Concrete: EUR 1,234,567.00 (27%)
Masonry: EUR 876,543.00 (19%)
Steel Structure: EUR 654,321.00 (14%)
MEP: EUR 543,210.00 (12%)
Finishes: EUR 432,109.00 (10%)
Other: EUR 782,928.00 (18%)
CONFIDENCE ANALYSIS
───────────────────────────────────────────────────────────────
High (>0.85): 78%
Medium (0.70-0.85): 18%
Low (<0.70): 4%
╚══════════════════════════════════════════════════════════════╝
Resources
"Resource-based costing separates physical quantities from volatile prices, enabling transparent and auditable estimates."