cwicr-unit-converter

Construction data comes in various unit systems:

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cwicr-unit-converter" with this command: npx skills add datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/datadrivenconstruction-ddc-skills-for-ai-agents-in-construction-cwicr-unit-converter

CWICR Unit Converter

Business Case

Problem Statement

Construction data comes in various unit systems:

  • Metric vs Imperial measurements

  • Different unit conventions by trade

  • BIM quantities need normalization

  • Regional standards differ

Solution

Comprehensive unit conversion for construction quantities, normalizing data for CWICR integration and analysis.

Business Value

  • Accuracy - Eliminate unit conversion errors

  • Consistency - Standardize across projects

  • Integration - BIM to cost data alignment

  • Global - Support international projects

Technical Implementation

import pandas as pd import numpy as np from typing import Dict, Any, List, Optional, Tuple, Union from dataclasses import dataclass from enum import Enum

class UnitCategory(Enum): """Categories of measurement units.""" LENGTH = "length" AREA = "area" VOLUME = "volume" WEIGHT = "weight" TIME = "time" QUANTITY = "quantity"

class UnitSystem(Enum): """Unit systems.""" METRIC = "metric" IMPERIAL = "imperial" MIXED = "mixed"

@dataclass class UnitConversion: """Unit conversion result.""" original_value: float original_unit: str converted_value: float target_unit: str conversion_factor: float category: UnitCategory

Conversion factors to base units

Base units: meter (length), m² (area), m³ (volume), kg (weight), hour (time)

CONVERSIONS = { # Length to meters 'm': {'factor': 1.0, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'meter': {'factor': 1.0, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'meters': {'factor': 1.0, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'cm': {'factor': 0.01, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'mm': {'factor': 0.001, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'km': {'factor': 1000.0, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'ft': {'factor': 0.3048, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'feet': {'factor': 0.3048, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'foot': {'factor': 0.3048, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'in': {'factor': 0.0254, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'inch': {'factor': 0.0254, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'inches': {'factor': 0.0254, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'yd': {'factor': 0.9144, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'yard': {'factor': 0.9144, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'yards': {'factor': 0.9144, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'mi': {'factor': 1609.344, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'mile': {'factor': 1609.344, 'category': UnitCategory.LENGTH, 'base': 'm'}, 'lf': {'factor': 0.3048, 'category': UnitCategory.LENGTH, 'base': 'm'}, # Linear foot

# Area to m²
'm2': {'factor': 1.0, 'category': UnitCategory.AREA, 'base': 'm2'},
'm²': {'factor': 1.0, 'category': UnitCategory.AREA, 'base': 'm2'},
'sqm': {'factor': 1.0, 'category': UnitCategory.AREA, 'base': 'm2'},
'cm2': {'factor': 0.0001, 'category': UnitCategory.AREA, 'base': 'm2'},
'mm2': {'factor': 0.000001, 'category': UnitCategory.AREA, 'base': 'm2'},
'ha': {'factor': 10000.0, 'category': UnitCategory.AREA, 'base': 'm2'},
'hectare': {'factor': 10000.0, 'category': UnitCategory.AREA, 'base': 'm2'},
'ft2': {'factor': 0.092903, 'category': UnitCategory.AREA, 'base': 'm2'},
'sf': {'factor': 0.092903, 'category': UnitCategory.AREA, 'base': 'm2'},
'sqft': {'factor': 0.092903, 'category': UnitCategory.AREA, 'base': 'm2'},
'yd2': {'factor': 0.836127, 'category': UnitCategory.AREA, 'base': 'm2'},
'sy': {'factor': 0.836127, 'category': UnitCategory.AREA, 'base': 'm2'},  # Square yard
'acre': {'factor': 4046.86, 'category': UnitCategory.AREA, 'base': 'm2'},

# Volume to m³
'm3': {'factor': 1.0, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'm³': {'factor': 1.0, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'cbm': {'factor': 1.0, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'l': {'factor': 0.001, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'liter': {'factor': 0.001, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'litre': {'factor': 0.001, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'ml': {'factor': 0.000001, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'ft3': {'factor': 0.0283168, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'cf': {'factor': 0.0283168, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'cuft': {'factor': 0.0283168, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'yd3': {'factor': 0.764555, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'cy': {'factor': 0.764555, 'category': UnitCategory.VOLUME, 'base': 'm3'},  # Cubic yard
'cuyd': {'factor': 0.764555, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'gal': {'factor': 0.00378541, 'category': UnitCategory.VOLUME, 'base': 'm3'},
'gallon': {'factor': 0.00378541, 'category': UnitCategory.VOLUME, 'base': 'm3'},

# Weight to kg
'kg': {'factor': 1.0, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'kilogram': {'factor': 1.0, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'g': {'factor': 0.001, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'gram': {'factor': 0.001, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'mg': {'factor': 0.000001, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
't': {'factor': 1000.0, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'ton': {'factor': 1000.0, 'category': UnitCategory.WEIGHT, 'base': 'kg'},  # Metric ton
'tonne': {'factor': 1000.0, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'mt': {'factor': 1000.0, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'lb': {'factor': 0.453592, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'lbs': {'factor': 0.453592, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'pound': {'factor': 0.453592, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'oz': {'factor': 0.0283495, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'ounce': {'factor': 0.0283495, 'category': UnitCategory.WEIGHT, 'base': 'kg'},
'st': {'factor': 907.185, 'category': UnitCategory.WEIGHT, 'base': 'kg'},  # Short ton (US)

# Time to hours
'hr': {'factor': 1.0, 'category': UnitCategory.TIME, 'base': 'hr'},
'hour': {'factor': 1.0, 'category': UnitCategory.TIME, 'base': 'hr'},
'hours': {'factor': 1.0, 'category': UnitCategory.TIME, 'base': 'hr'},
'h': {'factor': 1.0, 'category': UnitCategory.TIME, 'base': 'hr'},
'min': {'factor': 1/60, 'category': UnitCategory.TIME, 'base': 'hr'},
'minute': {'factor': 1/60, 'category': UnitCategory.TIME, 'base': 'hr'},
'day': {'factor': 8.0, 'category': UnitCategory.TIME, 'base': 'hr'},  # 8-hour workday
'days': {'factor': 8.0, 'category': UnitCategory.TIME, 'base': 'hr'},
'week': {'factor': 40.0, 'category': UnitCategory.TIME, 'base': 'hr'},  # 40-hour week

# Quantity (no conversion, just counting)
'ea': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'each': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'pc': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'pcs': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'piece': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'pieces': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'no': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'nr': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'set': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'lot': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},
'ls': {'factor': 1.0, 'category': UnitCategory.QUANTITY, 'base': 'ea'},  # Lump sum

}

class CWICRUnitConverter: """Convert between construction units."""

def __init__(self):
    self.conversions = CONVERSIONS

def normalize_unit(self, unit: str) -> str:
    """Normalize unit string for lookup."""
    return str(unit).lower().strip().replace(' ', '').replace('.', '')

def get_unit_info(self, unit: str) -> Optional[Dict[str, Any]]:
    """Get conversion info for unit."""
    normalized = self.normalize_unit(unit)
    return self.conversions.get(normalized)

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

    from_info = self.get_unit_info(from_unit)
    to_info = self.get_unit_info(to_unit)

    if not from_info:
        raise ValueError(f"Unknown source unit: {from_unit}")
    if not to_info:
        raise ValueError(f"Unknown target unit: {to_unit}")

    if from_info['category'] != to_info['category']:
        raise ValueError(
            f"Cannot convert between {from_info['category'].value} and {to_info['category'].value}"
        )

    # Convert: source -> base -> target
    base_value = value * from_info['factor']
    converted_value = base_value / to_info['factor']
    conversion_factor = from_info['factor'] / to_info['factor']

    return UnitConversion(
        original_value=value,
        original_unit=from_unit,
        converted_value=round(converted_value, 6),
        target_unit=to_unit,
        conversion_factor=conversion_factor,
        category=from_info['category']
    )

def to_metric(self, value: float, from_unit: str) -> UnitConversion:
    """Convert to standard metric unit."""

    info = self.get_unit_info(from_unit)
    if not info:
        raise ValueError(f"Unknown unit: {from_unit}")

    base_unit = info['base']
    return self.convert(value, from_unit, base_unit)

def to_imperial(self, value: float, from_unit: str) -> UnitConversion:
    """Convert to common imperial unit."""

    info = self.get_unit_info(from_unit)
    if not info:
        raise ValueError(f"Unknown unit: {from_unit}")

    imperial_map = {
        'm': 'ft',
        'm2': 'sf',
        'm3': 'cy',
        'kg': 'lb',
        'hr': 'hr'
    }

    base = info['base']
    imperial_unit = imperial_map.get(base, base)

    return self.convert(value, from_unit, imperial_unit)

def convert_dataframe(self,
                      df: pd.DataFrame,
                      value_column: str,
                      unit_column: str,
                      target_unit: str,
                      output_column: str = None) -> pd.DataFrame:
    """Convert units in DataFrame column."""

    result = df.copy()
    if output_column is None:
        output_column = f"{value_column}_converted"

    converted_values = []
    for _, row in df.iterrows():
        try:
            conversion = self.convert(
                row[value_column],
                row[unit_column],
                target_unit
            )
            converted_values.append(conversion.converted_value)
        except ValueError:
            converted_values.append(None)

    result[output_column] = converted_values
    result[f'{output_column}_unit'] = target_unit

    return result

def normalize_units(self,
                    df: pd.DataFrame,
                    value_column: str,
                    unit_column: str) -> pd.DataFrame:
    """Normalize all units to base metric units."""

    result = df.copy()
    normalized_values = []
    normalized_units = []

    for _, row in df.iterrows():
        try:
            conversion = self.to_metric(row[value_column], row[unit_column])
            normalized_values.append(conversion.converted_value)
            normalized_units.append(conversion.target_unit)
        except ValueError:
            normalized_values.append(row[value_column])
            normalized_units.append(row[unit_column])

    result[f'{value_column}_normalized'] = normalized_values
    result[f'{unit_column}_normalized'] = normalized_units

    return result

class ConstructionUnitHelper: """Helper for construction-specific unit operations."""

def __init__(self):
    self.converter = CWICRUnitConverter()

def calculate_area(self,
                   length: float, length_unit: str,
                   width: float, width_unit: str,
                   result_unit: str = 'm2') -> float:
    """Calculate area from length and width."""

    # Convert both to meters
    length_m = self.converter.convert(length, length_unit, 'm').converted_value
    width_m = self.converter.convert(width, width_unit, 'm').converted_value

    # Calculate area in m²
    area_m2 = length_m * width_m

    # Convert to requested unit
    return self.converter.convert(area_m2, 'm2', result_unit).converted_value

def calculate_volume(self,
                     length: float, length_unit: str,
                     width: float, width_unit: str,
                     height: float, height_unit: str,
                     result_unit: str = 'm3') -> float:
    """Calculate volume from dimensions."""

    # Convert all to meters
    length_m = self.converter.convert(length, length_unit, 'm').converted_value
    width_m = self.converter.convert(width, width_unit, 'm').converted_value
    height_m = self.converter.convert(height, height_unit, 'm').converted_value

    # Calculate volume in m³
    volume_m3 = length_m * width_m * height_m

    # Convert to requested unit
    return self.converter.convert(volume_m3, 'm3', result_unit).converted_value

def concrete_volume(self,
                    length_ft: float,
                    width_ft: float,
                    thickness_in: float) -> Dict[str, float]:
    """Calculate concrete volume (common US method)."""

    # Convert to meters
    length_m = self.converter.convert(length_ft, 'ft', 'm').converted_value
    width_m = self.converter.convert(width_ft, 'ft', 'm').converted_value
    thickness_m = self.converter.convert(thickness_in, 'in', 'm').converted_value

    volume_m3 = length_m * width_m * thickness_m
    volume_cy = self.converter.convert(volume_m3, 'm3', 'cy').converted_value

    return {
        'm3': round(volume_m3, 3),
        'cy': round(volume_cy, 2)
    }

def rebar_weight(self,
                 length: float, length_unit: str,
                 bar_size: str) -> Dict[str, float]:
    """Calculate rebar weight from length and bar size."""

    # Rebar weight per meter (kg/m) - US bar sizes
    rebar_weights = {
        '#3': 0.561, '#4': 0.996, '#5': 1.556,
        '#6': 2.24, '#7': 3.049, '#8': 3.982,
        '#9': 5.06, '#10': 6.41, '#11': 7.91
    }

    weight_per_m = rebar_weights.get(bar_size, 1.0)
    length_m = self.converter.convert(length, length_unit, 'm').converted_value

    weight_kg = length_m * weight_per_m
    weight_lb = self.converter.convert(weight_kg, 'kg', 'lb').converted_value

    return {
        'kg': round(weight_kg, 2),
        'lb': round(weight_lb, 2),
        'ton': round(weight_kg / 1000, 4)
    }

Quick Start

Initialize converter

converter = CWICRUnitConverter()

Simple conversion

result = converter.convert(100, 'ft', 'm') print(f"{result.original_value} {result.original_unit} = {result.converted_value} {result.target_unit}")

Convert to metric

metric = converter.to_metric(1000, 'sf') print(f"1000 sf = {metric.converted_value} m²")

Convert DataFrame

df = pd.DataFrame({ 'quantity': [100, 50, 25], 'unit': ['cy', 'm3', 'cf'] }) normalized = converter.normalize_units(df, 'quantity', 'unit')

Common Use Cases

  1. Area Calculation

helper = ConstructionUnitHelper() area = helper.calculate_area( length=50, length_unit='ft', width=30, width_unit='ft', result_unit='m2' ) print(f"Area: {area} m²")

  1. Concrete Volume

volume = helper.concrete_volume( length_ft=20, width_ft=10, thickness_in=6 ) print(f"Concrete: {volume['cy']} CY = {volume['m3']} m³")

  1. Rebar Weight

weight = helper.rebar_weight(length=100, length_unit='m', bar_size='#5') print(f"Rebar weight: {weight['kg']} kg")

  1. Normalize BIM Quantities

bim_data = pd.read_excel("bim_quantities.xlsx") normalized = converter.normalize_units(bim_data, 'Quantity', 'Unit')

Resources

  • GitHub: OpenConstructionEstimate-DDC-CWICR

  • DDC Book: Chapter 2.3 - Data Standardization

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

cad-to-data

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

drawing-analyzer

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

cost-estimation-resource

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

daily-progress-report

No summary provided by upstream source.

Repository SourceNeeds Review