Skip to main content
ClaudeWave
Skill173 repo starsupdated 3mo ago

cwicr-waste-calculator

Calculate material waste factors and losses using CWICR norms. Apply waste percentages, cutting losses, and spillage factors to material quantities.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction /tmp/cwicr-waste-calculator && cp -r /tmp/cwicr-waste-calculator/1_DDC_Toolkit/CWICR-Database/cwicr-waste-calculator ~/.claude/skills/cwicr-waste-calculator
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# CWICR Waste Calculator

## Business Case

### Problem Statement
Material estimates need waste factors:
- Cutting/trimming losses
- Spillage and breakage
- Overordering requirements
- Different waste by material type

### Solution
Systematic waste calculation using CWICR material data with industry-standard waste factors by material category.

### Business Value
- **Accurate ordering** - Include realistic waste
- **Cost control** - Budget for actual usage
- **Sustainability** - Track and reduce waste
- **Benchmarking** - Compare waste across projects

## Technical Implementation

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


class WasteCategory(Enum):
    """Waste category types."""
    CUTTING = "cutting"          # Cutting/trimming losses
    SPILLAGE = "spillage"        # Liquid material spillage
    BREAKAGE = "breakage"        # Damaged materials
    OVERRUN = "overrun"          # Installation overrun
    THEFT = "theft"              # Site theft allowance
    WEATHER = "weather"          # Weather damage


@dataclass
class WasteFactor:
    """Waste factor for a material."""
    material_code: str
    material_name: str
    base_quantity: float
    unit: str
    cutting_waste_pct: float
    spillage_pct: float
    breakage_pct: float
    overrun_pct: float
    total_waste_pct: float
    quantity_with_waste: float
    waste_quantity: float
    waste_cost: float


# Industry standard waste factors by material type
WASTE_FACTORS = {
    'concrete': {
        'cutting': 0.02, 'spillage': 0.03, 'breakage': 0.0, 'overrun': 0.02
    },
    'rebar': {
        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02
    },
    'brick': {
        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.03, 'overrun': 0.02
    },
    'block': {
        'cutting': 0.04, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.02
    },
    'lumber': {
        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03
    },
    'plywood': {
        'cutting': 0.12, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.02
    },
    'drywall': {
        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.03, 'overrun': 0.02
    },
    'tile': {
        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.05, 'overrun': 0.03
    },
    'paint': {
        'cutting': 0.0, 'spillage': 0.05, 'breakage': 0.0, 'overrun': 0.10
    },
    'mortar': {
        'cutting': 0.0, 'spillage': 0.05, 'breakage': 0.0, 'overrun': 0.03
    },
    'insulation': {
        'cutting': 0.08, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03
    },
    'roofing': {
        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.05
    },
    'pipe': {
        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02
    },
    'wire': {
        'cutting': 0.03, 'spillage': 0.0, 'breakage': 0.0, 'overrun': 0.05
    },
    'conduit': {
        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02
    },
    'duct': {
        'cutting': 0.08, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.03
    },
    'steel': {
        'cutting': 0.03, 'spillage': 0.0, 'breakage': 0.0, 'overrun': 0.02
    },
    'glass': {
        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.05, 'overrun': 0.02
    },
    'flooring': {
        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03
    },
    'adhesive': {
        'cutting': 0.0, 'spillage': 0.08, 'breakage': 0.0, 'overrun': 0.05
    },
    'default': {
        'cutting': 0.05, 'spillage': 0.02, 'breakage': 0.02, 'overrun': 0.03
    }
}


class CWICRWasteCalculator:
    """Calculate material waste using CWICR data."""

    def __init__(self, cwicr_data: pd.DataFrame):
        self.materials = cwicr_data
        self._index_data()

    def _index_data(self):
        """Index materials data."""
        if 'material_code' in self.materials.columns:
            self._mat_index = self.materials.set_index('material_code')
        elif 'work_item_code' in self.materials.columns:
            self._mat_index = self.materials.set_index('work_item_code')
        else:
            self._mat_index = None

    def _detect_material_type(self, description: str) -> str:
        """Detect material type from description."""
        desc_lower = str(description).lower()

        for mat_type in WASTE_FACTORS.keys():
            if mat_type in desc_lower:
                return mat_type

        # Check common synonyms
        synonyms = {
            'concrete': ['beton', 'cement'],
            'rebar': ['reinforcement', 'armature', 'арматура'],
            'brick': ['кирпич', 'block'],
            'lumber': ['wood', 'timber', 'древесина'],
            'drywall': ['gypsum', 'plasterboard', 'гипсокартон'],
            'tile': ['ceramic', 'плитка', 'керамика'],
            'paint': ['краска', 'coating'],
            'insulation': ['изоляция', 'утеплитель'],
            'pipe': ['труба', 'piping'],
            'wire': ['провод', 'cable', 'кабель']
        }

        for mat_type, words in synonyms.items():
            if any(word in desc_lower for word in words):
                return mat_type

        return 'default'

    def get_waste_factors(self, material_type: str) -> Dict[str, float]:
        """Get waste factors for material type."""
        return WASTE_FACTORS.get(material_type, WASTE_FACTORS['default'])

    def calculate_waste(self,
                        material_code: str,
                        base_quantity: float,
                        unit_cost: float = 0,
                        custom_factors: Dict[str, float] = None) -> WasteFactor:
        """Calculate waste for a material."""

        # Get material info
        material_name = material_code
        unit = "unit"

        if self._mat_index is not None and material_code in self._mat_index.index:
            mat = self._mat_index.loc[material_code]
            material_name = str(mat.get('descr