mcpbeat Sign in

Bim Cost Estimation Cwicr Agent Skill

Automated cost estimation from BIM models using DDC CWICR database with 55,719 work items. AI classification + vector search for accurate pricing.

6k tokens
context cost
the whole folder, loaded on every use
3
files
instructions only
0
copies elsewhere
how many repositories repackaged it
264
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill bim-cost-estimation-cwicr

The instruction itself

9 sections, as written by the author

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 work item descriptions.
Example: ["Brick masonry laying", "Cement mortar for brick joints", "Internal cement plaster 15mm"]
"""

        response = self.openai.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": prompt}],
            response_format={"type": "json_object"}
        )

        try:
            result = json.loads(response.choices[0].message.content)
            return result.get('work_items', [bim_type])
        except:
            return [bim_type]

    def estimate_element(
        self,
        bim_type: str,
        category: str,
        quantity: float,
        quantity_unit: str,
        phase: str = "Construction"
    ) -> List[CostLineItem]:
        """Estimate cost for single BIM element type"""

        # Decompose into work items
        work_descriptions = self.decompose_bim_type(bim_type, category)

        line_items = []

        for work_desc in work_descriptions:
            # Search CWICR for matching rate
            matches = self.search_cwicr(work_desc, limit=1)

            if not matches:
                continue

            best_match = matches[0]

            # Convert quantity if units don't match
            adjusted_qty = self._convert_units(
                quantity, quantity_unit, best_match.unit
            )

            # Calculate costs
            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=self._get_trade(category)
            ))

        return line_items

    def estimate_from_qto(
        self,
        qto_data: pd.DataFrame,
        type_column: str = "Type Name",
        category_column: str = "Category",
        quantity_column: str = "Volume"
    ) -> List[CostLineItem]:
        """Generate estimate from QTO DataFrame"""

        all_line_items = []

        # Group by type
        grouped = qto_data.groupby([category_column, type_column]).agg({
            quantity_column: 'sum'
        }).reset_index()

        for _, row in grouped.iterrows():
            items = self.estimate_element(
                bim_type=row[type_column],
                category=row[category_column],
                quantity=row[quantity_column],
                quantity_unit="m³"  # Assume volume, adjust based on category
            )
            all_line_items.extend(items)

        return all_line_items

    def _convert_units(
        self,
        value: float,
        from_unit: str,
        to_unit: str
    ) -> float:
        """Convert between units"""

        # Simplified conversion - expand as needed
        conversions = {
            ('m³', 'm³'): 1.0,
            ('m²', 'm²'): 1.0,
            ('m', 'm'): 1.0,
            ('ft³', 'm³'): 0.0283168,
            ('ft²', 'm²'): 0.092903,
            ('ft', 'm'): 0.3048,
        }

        key = (from_unit.lower(), to_unit.lower())
        factor = conversions.get(key, 1.0)

        return value * factor

    def _get_trade(self, category: str) -> str:
        """Map BIM category to trade"""
        trade_map = {
            'Walls': 'Masonry',
            'Floors': 'Concrete',
            'Structural Columns': 'Concrete',
            'Structural Framing': 'Steel',
            'Doors': 'Carpentry',
            'Windows': 'Glazing',
            'Plumbing Fixtures': 'Plumbing',
            'Electrical Equipment': 'Electrical',
            'Mechanical Equipment': 'HVAC'
        }
        return trade_map.get(category, 'General')

    def generate_estimate_report(
        self,
        line_items: List[CostLineItem],
        project_name: str,
        output_path: str
    ) -> dict:
        """Generate comprehensive estimate report"""

        # Convert to DataFrame
        records = []
        for item in line_items:
            records.append({
                'BIM Type': item.bim_type,
                'Work Item': item.work_item.description,
                'CWICR Code': item.work_item.cwicr_code,
                'Quantity': round(item.quantity, 2),
                'Unit': item.quantity_unit,
                'Unit Price': round(item.work_item.unit_price, 2),
                'Labor': round(item.labor_cost, 2),
                'Material': round(item.material_cost, 2),
                'Equipment': round(item.equipment_cost, 2),
                'Total': round(item.total_cost, 2),
                'Phase': item.phase,
                'Trade': item.trade,
                'Currency': item.work_item.currency,
                'Confidence': round(item.work_item.confidence, 2)
            })

        df = pd.DataFrame(records)

        # Calculate totals
        total_cost = df['Total'].sum()
        total_labor = df['Labor'].sum()
        total_material = df['Material'].sum()
        total_equipment = df['Equipment'].sum()

        # Summary by trade
        by_trade = df.groupby('Trade')['Total'].sum().sort_values(ascending=False)

        # Write Excel
        excel_path = f"{output_path}/{project_name}_Estimate.xlsx"
        with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
            # Summary sheet
            summary_data = {
                'Metric': ['Total Cost', 'Labor Cost', 'Material Cost', 'Equipment Cost'],
                'Value': [total_cost, total_labor, total_material, total_equipment]
            }
            pd.DataFrame(summary_data).to_excel(writer, sheet_name='Summary', index=False)

            # By Trade
            by_trade.to_frame().to_excel(writer, sheet_name='By Trade')

            # Detail
            df.to_excel(writer, sheet_name='Detail', index=False)

        return {
            'excel_path': excel_path,
            'total_cost': total_cost,
            'total_labor': total_labor,
            'total_material': total_material,
            'total_equipment': total_equipment,
            'by_trade': by_trade.to_dict(),
            'line_items': len(df),
            'currency': line_items[0].work_item.currency if line_items else 'USD'
        }


# Usage Example
def estimate_from_bim_model(
    model_path: str,
    qdrant_url: str,
    language: str = "EN",
    output_dir: str = "."
) -> dict:
    """Complete BIM to cost estimation workflow"""

    import subprocess
    from pathlib import Path

    # Step 1: Convert BIM to Excel
    print("Converting BIM model...")
    subprocess.run([
        r"C:\DDC\RvtExporter.exe",
        model_path,
        "complete", "bbox"
    ])

    xlsx_path = Path(model_path).with_suffix('.xlsx')

    # Step 2: Load QTO data
    print("Loading quantity data...")
    df = pd.read_excel(xlsx_path)

    # Step 3: Initialize estimator
    estimator = BIMCostEstimator(
        qdrant_url=qdrant_url,
        language=language
    )

    # Step 4: Generate estimate
    print("Generating cost estimate...")
    line_items = estimator.estimate_from_qto(df)

    # Step 5: Generate report
    project_name = Path(model_path).stem
    result = estimator.generate_estimate_report(
        line_items=line_items,
        project_name=project_name,
        output_path=output_dir
    )

    print(f"\nEstimate Complete!")
    print(f"Total Cost: {result['currency']} {result['total_cost']:,.2f}")
    print(f"Excel Report: {result['excel_path']}")

    return result


if __name__ == "__main__":
    result = estimate_from_bim_model(
        model_path=r"C:\Projects\Building.rvt",
        qdrant_url="https://your-qdrant-instance.io",
        language="DE",
        output_dir=r"C:\Projects\Estimates"
    )

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

  • CWICR Repository: https://github.com/datadrivenconstruction/OpenConstructionEstimate-DDC-CWICR
  • Live Demo: https://openconstructionestimate.com
  • Qdrant: https://qdrant.tech

*"Resource-based costing separates physical quantities from volatile prices, enabling transparent and auditable estimates."*

Other skills for the same job

different authors, same section of the catalogue
Uspto Database
by BioTender-max
×1

Access USPTO patent data via PatentsView REST API and Google Patents Public Data (BigQuery). Search by inventor, assignee, CPC, or keywords; download metadata and claims; analyze portfolios; track tech trends. For IP landscape analysis, competitor monitoring, prior art search, and tech forecasting in life sciences and biotech.

5k tokens
Signal Scanner
by gooseworks-ai

> Detect buying signals across TAM companies and watchlist personas. (headcount growth, tech stack changes, funding rounds), (2) Apify-powered signals (job postings, LinkedIn content analysis, profile changes), and (3) post-processing with dedup, scoring, and lead status updates. Writes signals to Supabase signals table for downstream activation.

11k tokens scripts
Data Export
by indranilbanerjee

Export marketing data. Use when: sending data to BigQuery, Google Sheets, or Supabase for analysis or reporting.

4k tokens
Paywall Strategy
by vibeeval

Mobil uygulama paywall strateji rehberi. 14 kategori benchmark database, 4 paywall modeli, trial optimizasyonu, placement mapping, pricing psychology, regional pricing (PPP) ve Apple/Google compliance checklist.

2k tokens
Godot 3d Materials
by thedivergentai

Expert patterns for Godot 3D PBR materials using StandardMaterial3D including albedo, metallic/roughness workflows, normal maps, ORM texture packing, transparency modes, and shader conversion. Use when creating realistic 3D surfaces, PBR workflows, or material optimization. Trigger keywords: StandardMaterial3D, BaseMaterial3D, albedo_texture, metallic, metallic_texture, roughness, roughness_texture, normal_texture, normal_enabled, orm_texture, transparency, alpha_scissor, alpha_hash, cull_mode, ShaderMaterial, shader parameters.

13k tokens scripts
Godot Resource Data Patterns
by thedivergentai

Expert blueprint for data-oriented design using Resource/RefCounted classes (item databases, character stats, reusable data structures). Covers typed arrays, serialization, nested resources, and resource caching. Use when implementing data systems OR inventory/stats/dialogue databases. Keywords Resource, RefCounted, ItemData, CharacterStats, database, serialization, @export, typed arrays.

9k tokens scripts
Mesh Memory
by lingxling

Self-hosted semantic memory for AI agents via MCP. Save worklogs, decisions, and notes, then recall them across sessions by meaning, not keyword. Postgres + pgvector with auto-tagging.

2k tokens
Reference Data
by JoelLewis

Design and manage reference data systems — security master, client master, account master, identifier mapping, pricing data sources, golden source designation, and governance. Use when building or evaluating a security master database, mapping identifiers across systems (CUSIP to ISIN, SEDOL to FIGI), designing client master models for onboarding or KYC, defining account master attributes across custodians, designating golden sources and MDM patterns across systems, establishing a pricing vendor hierarchy with fallback order, establishing reference data governance and stewardship, handling identifier changes from corporate actions, or troubleshooting issues traced to missing or changed identifiers. Trigger on: security master, CUSIP, ISIN, SEDOL, FIGI, client master, account master, pricing data, reference data, golden source, MDM, master data, identifier mapping, data governance, vendor hierarchy.

5k tokens

How to use it

Copy the folder

Take datadrivenconstruction/bim-cost-estimation-cwicr from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.