qto-reportGenerate Quantity Take-Off (QTO) reports from BIM/CAD data. Extract volumes, areas, counts by category. Group elements, apply calculation rules, and create c...
Install via ClawdBot CLI:
clawdbot install datadrivenconstruction/qto-reportBased on DDC methodology (Chapter 3.2), this skill automates the extraction and grouping of quantities from BIM/CAD data. QTO is the foundation for cost estimation, scheduling, and project planning in construction.
Book Reference: "Quantity Take-Off и автоматическое создание смет" / "QTO and Automated Estimates"
"QTO Quantity Take-Off: группировка данных по атрибутам позволяет автоматически извлекать объемы и количества из BIM-моделей для расчета стоимости."
— DDC Book, Chapter 3.2
The QTO process is central to 5D BIM:
```python
import pandas as pd
df = pd.read_csv("revit_export.csv")
qto = df.groupby('Category').agg({
'Volume': 'sum',
'Area': 'sum',
'ElementId': 'count'
}).rename(columns={'ElementId': 'Count'})
qto['Unit_Price'] = [150, 80, 450, 200] # $/m³
qto['Total_Cost'] = qto['Volume'] * qto['Unit_Price']
qto.to_excel("qto_report.xlsx")
```
```python
import pandas as pd
def generate_qto(df, group_by='Category'):
"""
Generate Quantity Take-Off grouped by specified column
Args:
df: DataFrame with BIM elements
group_by: Column(s) to group by
Returns:
QTO summary DataFrame
"""
# Define aggregations based on available columns
agg_dict = {}
if 'Volume' in df.columns:
agg_dict['Volume'] = 'sum'
if 'Area' in df.columns:
agg_dict['Area'] = 'sum'
if 'Length' in df.columns:
agg_dict['Length'] = 'sum'
if 'Count' in df.columns:
agg_dict['Count'] = 'sum'
else:
agg_dict['ElementId'] = 'count'
qto = df.groupby(group_by).agg(agg_dict)
if 'ElementId' in agg_dict:
qto = qto.rename(columns={'ElementId': 'Count'})
return qto.round(2)
qto = generate_qto(df, group_by='Category')
print(qto)
```
```python
def generate_multi_level_qto(df):
"""Generate QTO grouped by multiple levels"""
qto = df.groupby(['Level', 'Category', 'Material']).agg({
'Volume': ['sum', 'count'],
'Area': 'sum'
}).round(2)
# Flatten column names
qto.columns = ['Volume_m3', 'Element_Count', 'Area_m2']
# Add percentages
qto['Volume_Pct'] = (qto['Volume_m3'] /
qto['Volume_m3'].sum() * 100).round(1)
return qto.sort_values('Volume_m3', ascending=False)
qto = generate_multi_level_qto(df)
qto.to_excel("qto_multi_level.xlsx")
```
```python
def generate_qto_pivot(df, values='Volume', index='Level', columns='Category'):
"""Generate QTO as pivot table"""
pivot = pd.pivot_table(
df,
values=values,
index=index,
columns=columns,
aggfunc='sum',
fill_value=0,
margins=True,
margins_name='TOTAL'
).round(2)
return pivot
qto_pivot = generate_qto_pivot(df, values='Volume')
qto_pivot.to_excel("qto_pivot.xlsx")
```
```python
def calculate_cost_from_qto(qto_df, prices_df, quantity_col='Volume'):
"""
Calculate costs by applying unit prices to quantities
Args:
qto_df: QTO DataFrame with quantities
prices_df: DataFrame with Category and Unit_Price
quantity_col: Column containing quantities
"""
# Merge with prices
result = qto_df.reset_index().merge(
prices_df, on='Category', how='left'
)
# Calculate costs
result['Total_Cost'] = result[quantity_col] * result['Unit_Price']
result['Cost_Pct'] = (result['Total_Cost'] /
result['Total_Cost'].sum() * 100).round(1)
# Summary
grand_total = result['Total_Cost'].sum()
print(f"Grand Total: ${grand_total:,.2f}")
return result
prices = pd.DataFrame({
'Category': ['Wall', 'Floor', 'Column', 'Beam', 'Foundation'],
'Unit_Price': [150, 80, 450, 200, 120], # $/m³
'Unit': ['m³', 'm³', 'm³', 'm³', 'm³']
})
cost_estimate = calculate_cost_from_qto(qto, prices)
cost_estimate.to_excel("cost_estimate.xlsx", index=False)
```
```python
def apply_excel_rules(df, rules_path):
"""
Apply calculation rules defined in Excel file
Excel format:
| Category | Formula_Type | Factor | Unit |
| Wall | volume | 1.05 | m³ |
| Floor | area | 1.10 | m² |
"""
rules = pd.read_excel(rules_path)
results = []
for _, rule in rules.iterrows():
category = rule['Category']
formula_type = rule['Formula_Type']
factor = rule['Factor']
category_data = df[df['Category'] == category].copy()
if formula_type == 'volume':
category_data['Quantity'] = category_data['Volume'] * factor
elif formula_type == 'area':
category_data['Quantity'] = category_data['Area'] * factor
elif formula_type == 'length':
category_data['Quantity'] = category_data['Length'] * factor
elif formula_type == 'count':
category_data['Quantity'] = category_data.groupby('Category').ngroup() + 1
category_data['Unit'] = rule['Unit']
results.append(category_data)
return pd.concat(results, ignore_index=True)
df_with_quantities = apply_excel_rules(df, "calculation_rules.xlsx")
```
```python
def process_revit_export(csv_path):
"""Process standard Revit schedule export"""
df = pd.read_csv(csv_path)
# Standardize column names
column_mapping = {
'Family and Type': 'Type',
'Volume': 'Volume',
'Area': 'Area',
'Count': 'Count',
'Level': 'Level',
'Category': 'Category'
}
df = df.rename(columns={
k: v for k, v in column_mapping.items()
if k in df.columns
})
# Convert volume from cubic feet to cubic meters (if needed)
if 'Volume' in df.columns:
# Revit exports in cubic feet by default
df['Volume_m3'] = df['Volume'] * 0.0283168
return df
df = process_revit_export("revit_schedule.csv")
qto = generate_qto(df)
```
```python
import ifcopenshell
import pandas as pd
def extract_qto_from_ifc(ifc_path):
"""Extract quantities from IFC file"""
ifc = ifcopenshell.open(ifc_path)
elements = []
for element in ifc.by_type("IfcBuildingElement"):
# Get properties
props = {
'GlobalId': element.GlobalId,
'Name': element.Name,
'Type': element.is_a(),
'Material': None,
'Volume': None,
'Area': None
}
# Extract quantities from property sets
for definition in element.IsDefinedBy:
if definition.is_a('IfcRelDefinesByProperties'):
pset = definition.RelatingPropertyDefinition
if pset.is_a('IfcElementQuantity'):
for qty in pset.Quantities:
if qty.is_a('IfcQuantityVolume'):
props['Volume'] = qty.VolumeValue
elif qty.is_a('IfcQuantityArea'):
props['Area'] = qty.AreaValue
elements.append(props)
return pd.DataFrame(elements)
df = extract_qto_from_ifc("model.ifc")
qto = generate_qto(df, group_by='Type')
```
```python
def material_breakdown_qto(df):
"""Detailed breakdown by material type"""
breakdown = df.groupby(['Category', 'Material', 'Type']).agg({
'Volume': 'sum',
'Area': 'sum',
'ElementId': 'nunique'
}).rename(columns={'ElementId': 'Unique_Elements'})
# Add subtotals for each category
category_totals = df.groupby('Category').agg({
'Volume': 'sum',
'Area': 'sum'
})
breakdown['Category_Volume_Pct'] = breakdown.apply(
lambda row: (row['Volume'] /
category_totals.loc[row.name[0], 'Volume'] * 100),
axis=1
).round(1)
return breakdown
material_qto = material_breakdown_qto(df)
material_qto.to_excel("material_breakdown.xlsx")
```
```python
def qto_with_waste(df, waste_factors):
"""
Apply waste factors to quantities
Args:
waste_factors: dict like {'Concrete': 1.05, 'Steel': 1.03}
"""
qto = df.groupby(['Category', 'Material']).agg({
'Volume': 'sum'
}).reset_index()
# Apply waste factors
qto['Waste_Factor'] = qto['Material'].map(waste_factors).fillna(1.0)
qto['Net_Volume'] = qto['Volume']
qto['Gross_Volume'] = qto['Volume'] * qto['Waste_Factor']
qto['Waste_Volume'] = qto['Gross_Volume'] - qto['Net_Volume']
return qto
waste = {'Concrete': 1.05, 'Brick': 1.08, 'Steel': 1.03}
qto = qto_with_waste(df, waste)
```
```python
def compare_qto(design_df, asbuilt_df, group_by='Category'):
"""Compare designed vs as-built quantities"""
design_qto = design_df.groupby(group_by)['Volume'].sum()
asbuilt_qto = asbuilt_df.groupby(group_by)['Volume'].sum()
comparison = pd.DataFrame({
'Design': design_qto,
'AsBuilt': asbuilt_qto
})
comparison['Difference'] = comparison['AsBuilt'] - comparison['Design']
comparison['Variance_%'] = (
(comparison['AsBuilt'] - comparison['Design']) /
comparison['Design'] * 100
).round(1)
return comparison
comparison = compare_qto(design_df, asbuilt_df)
print(comparison)
```
```python
def export_qto_report(qto_df, base_name, include_charts=True):
"""Export QTO to Excel with formatting and charts"""
from openpyxl import Workbook
from openpyxl.chart import BarChart, Reference
# Excel with multiple sheets
with pd.ExcelWriter(f"{base_name}.xlsx", engine='openpyxl') as writer:
# Summary sheet
qto_df.to_excel(writer, sheet_name='Summary')
# Detailed data
if hasattr(qto_df, 'reset_index'):
qto_df.reset_index().to_excel(
writer, sheet_name='Details', index=False
)
# CSV for integration
qto_df.to_csv(f"{base_name}.csv")
# JSON for API
qto_df.reset_index().to_json(
f"{base_name}.json", orient='records', indent=2
)
print(f"Exported: {base_name}.xlsx, .csv, .json")
export_qto_report(qto, "project_qto")
```
| Task | Code |
|------|------|
| Basic QTO | df.groupby('Category')['Volume'].sum() |
| Multi-column QTO | df.groupby(['Level', 'Category']).agg({...}) |
| Pivot QTO | pd.pivot_table(df, values='Volume', ...) |
| Apply prices | qto.merge(prices, on='Category') |
| Calculate cost | df['Cost'] = df['Volume'] * df['Unit_Price'] |
| Add waste factor | df['Gross'] = df['Net'] * waste_factor |
cost-estimation-resource for detailed cost calculationsauto-estimate-generator for automated estimate creationgantt-chart for 4D scheduling integrationco2-estimation for carbon footprint calculationsGenerated Mar 1, 2026
Automate quantity take-offs from BIM models to calculate material volumes and areas for accurate cost estimates. Integrates with unit price databases to generate detailed cost reports, reducing manual errors in construction budgeting.
Extract quantities by level and category to support 4D BIM scheduling, enabling resource allocation and timeline forecasting. Helps project managers optimize workflows and track progress against planned quantities.
Generate QTO reports grouped by material type to streamline procurement processes. Automates calculations for ordering materials like concrete or steel, ensuring accurate quantities and reducing waste on construction sites.
Use QTO functions to verify BIM data completeness by checking volumes, areas, and counts against design specifications. Identifies discrepancies early in the design phase to improve model accuracy.
Calculate material quantities for environmental assessments, such as carbon footprint or energy use. Supports green building certifications by providing data on resource consumption from BIM models.
Offer the QTO skill as a cloud-based subscription service with pay-per-use or tiered pricing. Includes features like automated report generation and integration with BIM tools, targeting construction firms for recurring revenue.
Provide tailored QTO solutions and implementation services for large construction projects. Revenue comes from one-time setup fees, training, and ongoing support for integrating the skill with client-specific workflows.
Monetize aggregated QTO data by offering benchmarking insights and industry reports to construction companies. Leverage the skill's output to create value-added services like cost trend analysis and predictive modeling.
💬 Integration Tip
Ensure Python and ifcopenshell are installed, and use pandas DataFrames for seamless data handling from BIM exports like CSV or Excel files.
Use when designing new system architecture, reviewing existing designs, or making architectural decisions. Invoke for system design, architecture review, design patterns, ADRs, scalability planning.
Guide any property decision for buyers, sellers, landlords, investors, or agents in any jurisdiction.
Document significant technical decisions with context, rationale, and consequences to maintain clear, lightweight architectural records for future reference.
Dual-stream event publishing combining Kafka for durability with Redis Pub/Sub for real-time delivery. Use when building event-driven systems needing both guaranteed delivery and low-latency updates. Triggers on dual stream, event publishing, Kafka Redis, real-time events, pub/sub, streaming architecture.
Predict construction project costs using Machine Learning. Use Linear Regression, K-Nearest Neighbors, and Random Forest models on historical project data. Train, evaluate, and deploy cost prediction models.
Generate photorealistic architectural renders and visualizations using each::sense AI. Create exterior views, interior renders, sketch-to-render conversions,...