[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FJS27P0BDVC7BC9AVF5Y66":3,"skill-same:01M6FJS27P0BDVC7BC9AVF5Y66":55},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":22,"linkOnly":23,"markdownContent":24,"markdownBody":25,"frontMatter":26,"refName":28,"commitSha":29,"skillDirectory":30,"forksCount":31,"repositoryHtmlUrl":32,"repositoryHomepageUrl":6,"ownerHtmlUrl":33,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":34,"install":35,"resources":48},"01M6FJS27P0BDVC7BC9AVF5Y66","cwicr-waste-calculator",null,"Calculate material waste factors and losses using CWICR norms. Apply waste percentages, cutting losses, and spillage factors to material quantities.","kbarbel640-del\u002Fskills","kbarbel640-del","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F255315490?v=4","01M6CJPPQGCCESXMG3K7BBGQ5J","01M6CJPPX2PB2BCZERFVM2C2B3",1,"MIT","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdatadrivenconstruction\u002Fcwicr-waste-calculator\u002FSKILL.md",[],[18,19,20,21],"calculator","cwicr waste calculator","calculate","material","2026-11-10 10:12:30",false,"---\r\nslug: \"cwicr-waste-calculator\"\r\ndisplay_name: \"CWICR Waste Calculator\"\r\ndescription: \"Calculate material waste factors and losses using CWICR norms. Apply waste percentages, cutting losses, and spillage factors to material quantities.\"\r\n---\r\n\r\n# CWICR Waste Calculator\r\n\r\n## Business Case\r\n\r\n### Problem Statement\r\nMaterial estimates need waste factors:\r\n- Cutting\u002Ftrimming losses\r\n- Spillage and breakage\r\n- Overordering requirements\r\n- Different waste by material type\r\n\r\n### Solution\r\nSystematic waste calculation using CWICR material data with industry-standard waste factors by material category.\r\n\r\n### Business Value\r\n- **Accurate ordering** - Include realistic waste\r\n- **Cost control** - Budget for actual usage\r\n- **Sustainability** - Track and reduce waste\r\n- **Benchmarking** - Compare waste across projects\r\n\r\n## Technical Implementation\r\n\r\n```python\r\nimport pandas as pd\r\nimport numpy as np\r\nfrom typing import Dict, Any, List, Optional\r\nfrom dataclasses import dataclass\r\nfrom enum import Enum\r\n\r\n\r\nclass WasteCategory(Enum):\r\n    \"\"\"Waste category types.\"\"\"\r\n    CUTTING = \"cutting\"          # Cutting\u002Ftrimming losses\r\n    SPILLAGE = \"spillage\"        # Liquid material spillage\r\n    BREAKAGE = \"breakage\"        # Damaged materials\r\n    OVERRUN = \"overrun\"          # Installation overrun\r\n    THEFT = \"theft\"              # Site theft allowance\r\n    WEATHER = \"weather\"          # Weather damage\r\n\r\n\r\n@dataclass\r\nclass WasteFactor:\r\n    \"\"\"Waste factor for a material.\"\"\"\r\n    material_code: str\r\n    material_name: str\r\n    base_quantity: float\r\n    unit: str\r\n    cutting_waste_pct: float\r\n    spillage_pct: float\r\n    breakage_pct: float\r\n    overrun_pct: float\r\n    total_waste_pct: float\r\n    quantity_with_waste: float\r\n    waste_quantity: float\r\n    waste_cost: float\r\n\r\n\r\n# Industry standard waste factors by material type\r\nWASTE_FACTORS = {\r\n    'concrete': {\r\n        'cutting': 0.02, 'spillage': 0.03, 'breakage': 0.0, 'overrun': 0.02\r\n    },\r\n    'rebar': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02\r\n    },\r\n    'brick': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.03, 'overrun': 0.02\r\n    },\r\n    'block': {\r\n        'cutting': 0.04, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.02\r\n    },\r\n    'lumber': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03\r\n    },\r\n    'plywood': {\r\n        'cutting': 0.12, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.02\r\n    },\r\n    'drywall': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.03, 'overrun': 0.02\r\n    },\r\n    'tile': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.05, 'overrun': 0.03\r\n    },\r\n    'paint': {\r\n        'cutting': 0.0, 'spillage': 0.05, 'breakage': 0.0, 'overrun': 0.10\r\n    },\r\n    'mortar': {\r\n        'cutting': 0.0, 'spillage': 0.05, 'breakage': 0.0, 'overrun': 0.03\r\n    },\r\n    'insulation': {\r\n        'cutting': 0.08, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03\r\n    },\r\n    'roofing': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.05\r\n    },\r\n    'pipe': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02\r\n    },\r\n    'wire': {\r\n        'cutting': 0.03, 'spillage': 0.0, 'breakage': 0.0, 'overrun': 0.05\r\n    },\r\n    'conduit': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02\r\n    },\r\n    'duct': {\r\n        'cutting': 0.08, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.03\r\n    },\r\n    'steel': {\r\n        'cutting': 0.03, 'spillage': 0.0, 'breakage': 0.0, 'overrun': 0.02\r\n    },\r\n    'glass': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.05, 'overrun': 0.02\r\n    },\r\n    'flooring': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03\r\n    },\r\n    'adhesive': {\r\n        'cutting': 0.0, 'spillage': 0.08, 'breakage': 0.0, 'overrun': 0.05\r\n    },\r\n    'default': {\r\n        'cutting': 0.05, 'spillage': 0.02, 'breakage': 0.02, 'overrun': 0.03\r\n    }\r\n}\r\n\r\n\r\nclass CWICRWasteCalculator:\r\n    \"\"\"Calculate material waste using CWICR data.\"\"\"\r\n\r\n    def __init__(self, cwicr_data: pd.DataFrame):\r\n        self.materials = cwicr_data\r\n        self._index_data()\r\n\r\n    def _index_data(self):\r\n        \"\"\"Index materials data.\"\"\"\r\n        if 'material_code' in self.materials.columns:\r\n            self._mat_index = self.materials.set_index('material_code')\r\n        elif 'work_item_code' in self.materials.columns:\r\n            self._mat_index = self.materials.set_index('work_item_code')\r\n        else:\r\n            self._mat_index = None\r\n\r\n    def _detect_material_type(self, description: str) -> str:\r\n        \"\"\"Detect material type from description.\"\"\"\r\n        desc_lower = str(description).lower()\r\n\r\n        for mat_type in WASTE_FACTORS.keys():\r\n            if mat_type in desc_lower:\r\n                return mat_type\r\n\r\n        # Check common synonyms\r\n        synonyms = {\r\n            'concrete': ['beton', 'cement'],\r\n            'rebar': ['reinforcement', 'armature', 'арматура'],\r\n            'brick': ['кирпич', 'block'],\r\n            'lumber': ['wood', 'timber', 'древесина'],\r\n            'drywall': ['gypsum', 'plasterboard', 'гипсокартон'],\r\n            'tile': ['ceramic', 'плитка', 'керамика'],\r\n            'paint': ['краска', 'coating'],\r\n            'insulation': ['изоляция', 'утеплитель'],\r\n            'pipe': ['труба', 'piping'],\r\n            'wire': ['провод', 'cable', 'кабель']\r\n        }\r\n\r\n        for mat_type, words in synonyms.items():\r\n            if any(word in desc_lower for word in words):\r\n                return mat_type\r\n\r\n        return 'default'\r\n\r\n    def get_waste_factors(self, material_type: str) -> Dict[str, float]:\r\n        \"\"\"Get waste factors for material type.\"\"\"\r\n        return WASTE_FACTORS.get(material_type, WASTE_FACTORS['default'])\r\n\r\n    def calculate_waste(self,\r\n                        material_code: str,\r\n                        base_quantity: float,\r\n                        unit_cost: float = 0,\r\n                        custom_factors: Dict[str, float] = None) -> WasteFactor:\r\n        \"\"\"Calculate waste for a material.\"\"\"\r\n\r\n        # Get material info\r\n        material_name = material_code\r\n        unit = \"unit\"\r\n\r\n        if self._mat_index is not None and material_code in self._mat_index.index:\r\n            mat = self._mat_index.loc[material_code]\r\n            material_name = str(mat.get('description', mat.get('material_description', material_code)))\r\n            unit = str(mat.get('unit', mat.get('material_unit', 'unit')))\r\n            if unit_cost == 0:\r\n                unit_cost = float(mat.get('material_cost', mat.get('unit_cost', 0)) or 0)\r\n\r\n        # Detect material type and get factors\r\n        mat_type = self._detect_material_type(material_name)\r\n        factors = custom_factors or self.get_waste_factors(mat_type)\r\n\r\n        cutting = factors.get('cutting', 0)\r\n        spillage = factors.get('spillage', 0)\r\n        breakage = factors.get('breakage', 0)\r\n        overrun = factors.get('overrun', 0)\r\n\r\n        # Calculate total waste\r\n        total_waste_pct = cutting + spillage + breakage + overrun\r\n        waste_quantity = base_quantity * total_waste_pct\r\n        quantity_with_waste = base_quantity + waste_quantity\r\n        waste_cost = waste_quantity * unit_cost\r\n\r\n        return WasteFactor(\r\n            material_code=material_code,\r\n            material_name=material_name,\r\n            base_quantity=base_quantity,\r\n            unit=unit,\r\n            cutting_waste_pct=round(cutting * 100, 1),\r\n            spillage_pct=round(spillage * 100, 1),\r\n            breakage_pct=round(breakage * 100, 1),\r\n            overrun_pct=round(overrun * 100, 1),\r\n            total_waste_pct=round(total_waste_pct * 100, 1),\r\n            quantity_with_waste=round(quantity_with_waste, 2),\r\n            waste_quantity=round(waste_quantity, 2),\r\n            waste_cost=round(waste_cost, 2)\r\n        )\r\n\r\n    def calculate_project_waste(self,\r\n                                 materials: List[Dict[str, Any]]) -> Dict[str, Any]:\r\n        \"\"\"Calculate waste for entire project.\"\"\"\r\n\r\n        results = []\r\n        total_base_cost = 0\r\n        total_waste_cost = 0\r\n\r\n        for mat in materials:\r\n            code = mat.get('material_code', mat.get('code'))\r\n            qty = mat.get('quantity', 0)\r\n            cost = mat.get('unit_cost', 0)\r\n            custom = mat.get('waste_factors')\r\n\r\n            waste = self.calculate_waste(code, qty, cost, custom)\r\n            results.append(waste)\r\n\r\n            total_base_cost += qty * cost\r\n            total_waste_cost += waste.waste_cost\r\n\r\n        # Summary by waste category\r\n        by_category = {\r\n            'cutting': sum(r.cutting_waste_pct * r.base_quantity \u002F 100 for r in results),\r\n            'spillage': sum(r.spillage_pct * r.base_quantity \u002F 100 for r in results),\r\n            'breakage': sum(r.breakage_pct * r.base_quantity \u002F 100 for r in results),\r\n            'overrun': sum(r.overrun_pct * r.base_quantity \u002F 100 for r in results)\r\n        }\r\n\r\n        return {\r\n            'materials': results,\r\n            'total_base_cost': round(total_base_cost, 2),\r\n            'total_waste_cost': round(total_waste_cost, 2),\r\n            'waste_percentage': round(total_waste_cost \u002F total_base_cost * 100, 1) if total_base_cost > 0 else 0,\r\n            'by_category': by_category,\r\n            'order_quantity_increase': round(sum(r.waste_quantity for r in results), 2)\r\n        }\r\n\r\n    def optimize_cutting(self,\r\n                          material_code: str,\r\n                          required_lengths: List[float],\r\n                          stock_length: float) -> Dict[str, Any]:\r\n        \"\"\"Optimize cutting to minimize waste (1D cutting stock problem).\"\"\"\r\n\r\n        # Simple first-fit decreasing algorithm\r\n        sorted_lengths = sorted(required_lengths, reverse=True)\r\n        stock_pieces = []\r\n        waste_per_piece = []\r\n\r\n        for length in sorted_lengths:\r\n            placed = False\r\n            for i, remaining in enumerate(stock_pieces):\r\n                if remaining >= length:\r\n                    stock_pieces[i] -= length\r\n                    placed = True\r\n                    break\r\n\r\n            if not placed:\r\n                stock_pieces.append(stock_length - length)\r\n\r\n        total_stock_needed = len(stock_pieces)\r\n        total_material = total_stock_needed * stock_length\r\n        total_used = sum(required_lengths)\r\n        total_waste = total_material - total_used\r\n        waste_pct = total_waste \u002F total_material * 100 if total_material > 0 else 0\r\n\r\n        return {\r\n            'material_code': material_code,\r\n            'stock_pieces_needed': total_stock_needed,\r\n            'stock_length': stock_length,\r\n            'total_material': round(total_material, 2),\r\n            'total_used': round(total_used, 2),\r\n            'total_waste': round(total_waste, 2),\r\n            'waste_percentage': round(waste_pct, 1),\r\n            'cutting_efficiency': round(100 - waste_pct, 1)\r\n        }\r\n\r\n    def export_waste_report(self,\r\n                            project_waste: Dict[str, Any],\r\n                            output_path: str) -> str:\r\n        \"\"\"Export waste report to Excel.\"\"\"\r\n\r\n        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\r\n            # Summary\r\n            summary_df = pd.DataFrame([{\r\n                'Total Base Cost': project_waste['total_base_cost'],\r\n                'Total Waste Cost': project_waste['total_waste_cost'],\r\n                'Waste Percentage': project_waste['waste_percentage'],\r\n                'Order Increase': project_waste['order_quantity_increase']\r\n            }])\r\n            summary_df.to_excel(writer, sheet_name='Summary', index=False)\r\n\r\n            # Materials\r\n            mat_df = pd.DataFrame([\r\n                {\r\n                    'Material': m.material_name,\r\n                    'Base Qty': m.base_quantity,\r\n                    'Unit': m.unit,\r\n                    'Cutting %': m.cutting_waste_pct,\r\n                    'Spillage %': m.spillage_pct,\r\n                    'Breakage %': m.breakage_pct,\r\n                    'Overrun %': m.overrun_pct,\r\n                    'Total Waste %': m.total_waste_pct,\r\n                    'Order Qty': m.quantity_with_waste,\r\n                    'Waste Cost': m.waste_cost\r\n                }\r\n                for m in project_waste['materials']\r\n            ])\r\n            mat_df.to_excel(writer, sheet_name='Materials', index=False)\r\n\r\n        return output_path\r\n```\r\n\r\n## Quick Start\r\n\r\n```python\r\n# Load CWICR data\r\ncwicr = pd.read_parquet(\"ddc_cwicr_en.parquet\")\r\n\r\n# Initialize calculator\r\nwaste_calc = CWICRWasteCalculator(cwicr)\r\n\r\n# Calculate waste for single material\r\nwaste = waste_calc.calculate_waste(\r\n    material_code=\"CONC-001\",\r\n    base_quantity=100,\r\n    unit_cost=150\r\n)\r\n\r\nprint(f\"Base Qty: {waste.base_quantity}\")\r\nprint(f\"Order Qty: {waste.quantity_with_waste}\")\r\nprint(f\"Waste: {waste.total_waste_pct}%\")\r\nprint(f\"Waste Cost: ${waste.waste_cost:,.2f}\")\r\n```\r\n\r\n## Common Use Cases\r\n\r\n### 1. Project-Wide Waste\r\n```python\r\nmaterials = [\r\n    {'code': 'CONC-001', 'quantity': 200, 'unit_cost': 150},\r\n    {'code': 'REBAR-002', 'quantity': 5000, 'unit_cost': 1.2},\r\n    {'code': 'BRICK-003', 'quantity': 10000, 'unit_cost': 0.50}\r\n]\r\n\r\nproject = waste_calc.calculate_project_waste(materials)\r\nprint(f\"Total Waste Cost: ${project['total_waste_cost']:,.2f}\")\r\n```\r\n\r\n### 2. Cutting Optimization\r\n```python\r\ncutting = waste_calc.optimize_cutting(\r\n    material_code=\"REBAR-001\",\r\n    required_lengths=[2.5, 3.0, 1.8, 2.2, 4.0, 3.5],\r\n    stock_length=6.0\r\n)\r\nprint(f\"Efficiency: {cutting['cutting_efficiency']}%\")\r\n```\r\n\r\n### 3. Custom Waste Factors\r\n```python\r\ncustom_factors = {'cutting': 0.15, 'spillage': 0, 'breakage': 0.05, 'overrun': 0.05}\r\nwaste = waste_calc.calculate_waste(\"TILE-001\", 500, 25, custom_factors)\r\n```\r\n\r\n## Resources\r\n- **GitHub**: [OpenConstructionEstimate-DDC-CWICR](https:\u002F\u002Fgithub.com\u002Fdatadrivenconstruction\u002FOpenConstructionEstimate-DDC-CWICR)\r\n- **DDC Book**: Chapter 3.1 - Material Waste Management\r\n","# CWICR Waste Calculator\r\n\r\n## Business Case\r\n\r\n### Problem Statement\r\nMaterial estimates need waste factors:\r\n- Cutting\u002Ftrimming losses\r\n- Spillage and breakage\r\n- Overordering requirements\r\n- Different waste by material type\r\n\r\n### Solution\r\nSystematic waste calculation using CWICR material data with industry-standard waste factors by material category.\r\n\r\n### Business Value\r\n- **Accurate ordering** - Include realistic waste\r\n- **Cost control** - Budget for actual usage\r\n- **Sustainability** - Track and reduce waste\r\n- **Benchmarking** - Compare waste across projects\r\n\r\n## Technical Implementation\r\n\r\n```python\r\nimport pandas as pd\r\nimport numpy as np\r\nfrom typing import Dict, Any, List, Optional\r\nfrom dataclasses import dataclass\r\nfrom enum import Enum\r\n\r\n\r\nclass WasteCategory(Enum):\r\n    \"\"\"Waste category types.\"\"\"\r\n    CUTTING = \"cutting\"          # Cutting\u002Ftrimming losses\r\n    SPILLAGE = \"spillage\"        # Liquid material spillage\r\n    BREAKAGE = \"breakage\"        # Damaged materials\r\n    OVERRUN = \"overrun\"          # Installation overrun\r\n    THEFT = \"theft\"              # Site theft allowance\r\n    WEATHER = \"weather\"          # Weather damage\r\n\r\n\r\n@dataclass\r\nclass WasteFactor:\r\n    \"\"\"Waste factor for a material.\"\"\"\r\n    material_code: str\r\n    material_name: str\r\n    base_quantity: float\r\n    unit: str\r\n    cutting_waste_pct: float\r\n    spillage_pct: float\r\n    breakage_pct: float\r\n    overrun_pct: float\r\n    total_waste_pct: float\r\n    quantity_with_waste: float\r\n    waste_quantity: float\r\n    waste_cost: float\r\n\r\n\r\n# Industry standard waste factors by material type\r\nWASTE_FACTORS = {\r\n    'concrete': {\r\n        'cutting': 0.02, 'spillage': 0.03, 'breakage': 0.0, 'overrun': 0.02\r\n    },\r\n    'rebar': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02\r\n    },\r\n    'brick': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.03, 'overrun': 0.02\r\n    },\r\n    'block': {\r\n        'cutting': 0.04, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.02\r\n    },\r\n    'lumber': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03\r\n    },\r\n    'plywood': {\r\n        'cutting': 0.12, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.02\r\n    },\r\n    'drywall': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.03, 'overrun': 0.02\r\n    },\r\n    'tile': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.05, 'overrun': 0.03\r\n    },\r\n    'paint': {\r\n        'cutting': 0.0, 'spillage': 0.05, 'breakage': 0.0, 'overrun': 0.10\r\n    },\r\n    'mortar': {\r\n        'cutting': 0.0, 'spillage': 0.05, 'breakage': 0.0, 'overrun': 0.03\r\n    },\r\n    'insulation': {\r\n        'cutting': 0.08, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03\r\n    },\r\n    'roofing': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.05\r\n    },\r\n    'pipe': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02\r\n    },\r\n    'wire': {\r\n        'cutting': 0.03, 'spillage': 0.0, 'breakage': 0.0, 'overrun': 0.05\r\n    },\r\n    'conduit': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.02\r\n    },\r\n    'duct': {\r\n        'cutting': 0.08, 'spillage': 0.0, 'breakage': 0.01, 'overrun': 0.03\r\n    },\r\n    'steel': {\r\n        'cutting': 0.03, 'spillage': 0.0, 'breakage': 0.0, 'overrun': 0.02\r\n    },\r\n    'glass': {\r\n        'cutting': 0.05, 'spillage': 0.0, 'breakage': 0.05, 'overrun': 0.02\r\n    },\r\n    'flooring': {\r\n        'cutting': 0.10, 'spillage': 0.0, 'breakage': 0.02, 'overrun': 0.03\r\n    },\r\n    'adhesive': {\r\n        'cutting': 0.0, 'spillage': 0.08, 'breakage': 0.0, 'overrun': 0.05\r\n    },\r\n    'default': {\r\n        'cutting': 0.05, 'spillage': 0.02, 'breakage': 0.02, 'overrun': 0.03\r\n    }\r\n}\r\n\r\n\r\nclass CWICRWasteCalculator:\r\n    \"\"\"Calculate material waste using CWICR data.\"\"\"\r\n\r\n    def __init__(self, cwicr_data: pd.DataFrame):\r\n        self.materials = cwicr_data\r\n        self._index_data()\r\n\r\n    def _index_data(self):\r\n        \"\"\"Index materials data.\"\"\"\r\n        if 'material_code' in self.materials.columns:\r\n            self._mat_index = self.materials.set_index('material_code')\r\n        elif 'work_item_code' in self.materials.columns:\r\n            self._mat_index = self.materials.set_index('work_item_code')\r\n        else:\r\n            self._mat_index = None\r\n\r\n    def _detect_material_type(self, description: str) -> str:\r\n        \"\"\"Detect material type from description.\"\"\"\r\n        desc_lower = str(description).lower()\r\n\r\n        for mat_type in WASTE_FACTORS.keys():\r\n            if mat_type in desc_lower:\r\n                return mat_type\r\n\r\n        # Check common synonyms\r\n        synonyms = {\r\n            'concrete': ['beton', 'cement'],\r\n            'rebar': ['reinforcement', 'armature', 'арматура'],\r\n            'brick': ['кирпич', 'block'],\r\n            'lumber': ['wood', 'timber', 'древесина'],\r\n            'drywall': ['gypsum', 'plasterboard', 'гипсокартон'],\r\n            'tile': ['ceramic', 'плитка', 'керамика'],\r\n            'paint': ['краска', 'coating'],\r\n            'insulation': ['изоляция', 'утеплитель'],\r\n            'pipe': ['труба', 'piping'],\r\n            'wire': ['провод', 'cable', 'кабель']\r\n        }\r\n\r\n        for mat_type, words in synonyms.items():\r\n            if any(word in desc_lower for word in words):\r\n                return mat_type\r\n\r\n        return 'default'\r\n\r\n    def get_waste_factors(self, material_type: str) -> Dict[str, float]:\r\n        \"\"\"Get waste factors for material type.\"\"\"\r\n        return WASTE_FACTORS.get(material_type, WASTE_FACTORS['default'])\r\n\r\n    def calculate_waste(self,\r\n                        material_code: str,\r\n                        base_quantity: float,\r\n                        unit_cost: float = 0,\r\n                        custom_factors: Dict[str, float] = None) -> WasteFactor:\r\n        \"\"\"Calculate waste for a material.\"\"\"\r\n\r\n        # Get material info\r\n        material_name = material_code\r\n        unit = \"unit\"\r\n\r\n        if self._mat_index is not None and material_code in self._mat_index.index:\r\n            mat = self._mat_index.loc[material_code]\r\n            material_name = str(mat.get('description', mat.get('material_description', material_code)))\r\n            unit = str(mat.get('unit', mat.get('material_unit', 'unit')))\r\n            if unit_cost == 0:\r\n                unit_cost = float(mat.get('material_cost', mat.get('unit_cost', 0)) or 0)\r\n\r\n        # Detect material type and get factors\r\n        mat_type = self._detect_material_type(material_name)\r\n        factors = custom_factors or self.get_waste_factors(mat_type)\r\n\r\n        cutting = factors.get('cutting', 0)\r\n        spillage = factors.get('spillage', 0)\r\n        breakage = factors.get('breakage', 0)\r\n        overrun = factors.get('overrun', 0)\r\n\r\n        # Calculate total waste\r\n        total_waste_pct = cutting + spillage + breakage + overrun\r\n        waste_quantity = base_quantity * total_waste_pct\r\n        quantity_with_waste = base_quantity + waste_quantity\r\n        waste_cost = waste_quantity * unit_cost\r\n\r\n        return WasteFactor(\r\n            material_code=material_code,\r\n            material_name=material_name,\r\n            base_quantity=base_quantity,\r\n            unit=unit,\r\n            cutting_waste_pct=round(cutting * 100, 1),\r\n            spillage_pct=round(spillage * 100, 1),\r\n            breakage_pct=round(breakage * 100, 1),\r\n            overrun_pct=round(overrun * 100, 1),\r\n            total_waste_pct=round(total_waste_pct * 100, 1),\r\n            quantity_with_waste=round(quantity_with_waste, 2),\r\n            waste_quantity=round(waste_quantity, 2),\r\n            waste_cost=round(waste_cost, 2)\r\n        )\r\n\r\n    def calculate_project_waste(self,\r\n                                 materials: List[Dict[str, Any]]) -> Dict[str, Any]:\r\n        \"\"\"Calculate waste for entire project.\"\"\"\r\n\r\n        results = []\r\n        total_base_cost = 0\r\n        total_waste_cost = 0\r\n\r\n        for mat in materials:\r\n            code = mat.get('material_code', mat.get('code'))\r\n            qty = mat.get('quantity', 0)\r\n            cost = mat.get('unit_cost', 0)\r\n            custom = mat.get('waste_factors')\r\n\r\n            waste = self.calculate_waste(code, qty, cost, custom)\r\n            results.append(waste)\r\n\r\n            total_base_cost += qty * cost\r\n            total_waste_cost += waste.waste_cost\r\n\r\n        # Summary by waste category\r\n        by_category = {\r\n            'cutting': sum(r.cutting_waste_pct * r.base_quantity \u002F 100 for r in results),\r\n            'spillage': sum(r.spillage_pct * r.base_quantity \u002F 100 for r in results),\r\n            'breakage': sum(r.breakage_pct * r.base_quantity \u002F 100 for r in results),\r\n            'overrun': sum(r.overrun_pct * r.base_quantity \u002F 100 for r in results)\r\n        }\r\n\r\n        return {\r\n            'materials': results,\r\n            'total_base_cost': round(total_base_cost, 2),\r\n            'total_waste_cost': round(total_waste_cost, 2),\r\n            'waste_percentage': round(total_waste_cost \u002F total_base_cost * 100, 1) if total_base_cost > 0 else 0,\r\n            'by_category': by_category,\r\n            'order_quantity_increase': round(sum(r.waste_quantity for r in results), 2)\r\n        }\r\n\r\n    def optimize_cutting(self,\r\n                          material_code: str,\r\n                          required_lengths: List[float],\r\n                          stock_length: float) -> Dict[str, Any]:\r\n        \"\"\"Optimize cutting to minimize waste (1D cutting stock problem).\"\"\"\r\n\r\n        # Simple first-fit decreasing algorithm\r\n        sorted_lengths = sorted(required_lengths, reverse=True)\r\n        stock_pieces = []\r\n        waste_per_piece = []\r\n\r\n        for length in sorted_lengths:\r\n            placed = False\r\n            for i, remaining in enumerate(stock_pieces):\r\n                if remaining >= length:\r\n                    stock_pieces[i] -= length\r\n                    placed = True\r\n                    break\r\n\r\n            if not placed:\r\n                stock_pieces.append(stock_length - length)\r\n\r\n        total_stock_needed = len(stock_pieces)\r\n        total_material = total_stock_needed * stock_length\r\n        total_used = sum(required_lengths)\r\n        total_waste = total_material - total_used\r\n        waste_pct = total_waste \u002F total_material * 100 if total_material > 0 else 0\r\n\r\n        return {\r\n            'material_code': material_code,\r\n            'stock_pieces_needed': total_stock_needed,\r\n            'stock_length': stock_length,\r\n            'total_material': round(total_material, 2),\r\n            'total_used': round(total_used, 2),\r\n            'total_waste': round(total_waste, 2),\r\n            'waste_percentage': round(waste_pct, 1),\r\n            'cutting_efficiency': round(100 - waste_pct, 1)\r\n        }\r\n\r\n    def export_waste_report(self,\r\n                            project_waste: Dict[str, Any],\r\n                            output_path: str) -> str:\r\n        \"\"\"Export waste report to Excel.\"\"\"\r\n\r\n        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\r\n            # Summary\r\n            summary_df = pd.DataFrame([{\r\n                'Total Base Cost': project_waste['total_base_cost'],\r\n                'Total Waste Cost': project_waste['total_waste_cost'],\r\n                'Waste Percentage': project_waste['waste_percentage'],\r\n                'Order Increase': project_waste['order_quantity_increase']\r\n            }])\r\n            summary_df.to_excel(writer, sheet_name='Summary', index=False)\r\n\r\n            # Materials\r\n            mat_df = pd.DataFrame([\r\n                {\r\n                    'Material': m.material_name,\r\n                    'Base Qty': m.base_quantity,\r\n                    'Unit': m.unit,\r\n                    'Cutting %': m.cutting_waste_pct,\r\n                    'Spillage %': m.spillage_pct,\r\n                    'Breakage %': m.breakage_pct,\r\n                    'Overrun %': m.overrun_pct,\r\n                    'Total Waste %': m.total_waste_pct,\r\n                    'Order Qty': m.quantity_with_waste,\r\n                    'Waste Cost': m.waste_cost\r\n                }\r\n                for m in project_waste['materials']\r\n            ])\r\n            mat_df.to_excel(writer, sheet_name='Materials', index=False)\r\n\r\n        return output_path\r\n```\r\n\r\n## Quick Start\r\n\r\n```python\r\n# Load CWICR data\r\ncwicr = pd.read_parquet(\"ddc_cwicr_en.parquet\")\r\n\r\n# Initialize calculator\r\nwaste_calc = CWICRWasteCalculator(cwicr)\r\n\r\n# Calculate waste for single material\r\nwaste = waste_calc.calculate_waste(\r\n    material_code=\"CONC-001\",\r\n    base_quantity=100,\r\n    unit_cost=150\r\n)\r\n\r\nprint(f\"Base Qty: {waste.base_quantity}\")\r\nprint(f\"Order Qty: {waste.quantity_with_waste}\")\r\nprint(f\"Waste: {waste.total_waste_pct}%\")\r\nprint(f\"Waste Cost: ${waste.waste_cost:,.2f}\")\r\n```\r\n\r\n## Common Use Cases\r\n\r\n### 1. Project-Wide Waste\r\n```python\r\nmaterials = [\r\n    {'code': 'CONC-001', 'quantity': 200, 'unit_cost': 150},\r\n    {'code': 'REBAR-002', 'quantity': 5000, 'unit_cost': 1.2},\r\n    {'code': 'BRICK-003', 'quantity': 10000, 'unit_cost': 0.50}\r\n]\r\n\r\nproject = waste_calc.calculate_project_waste(materials)\r\nprint(f\"Total Waste Cost: ${project['total_waste_cost']:,.2f}\")\r\n```\r\n\r\n### 2. Cutting Optimization\r\n```python\r\ncutting = waste_calc.optimize_cutting(\r\n    material_code=\"REBAR-001\",\r\n    required_lengths=[2.5, 3.0, 1.8, 2.2, 4.0, 3.5],\r\n    stock_length=6.0\r\n)\r\nprint(f\"Efficiency: {cutting['cutting_efficiency']}%\")\r\n```\r\n\r\n### 3. Custom Waste Factors\r\n```python\r\ncustom_factors = {'cutting': 0.15, 'spillage': 0, 'breakage': 0.05, 'overrun': 0.05}\r\nwaste = waste_calc.calculate_waste(\"TILE-001\", 500, 25, custom_factors)\r\n```\r\n\r\n## Resources\r\n- **GitHub**: [OpenConstructionEstimate-DDC-CWICR](https:\u002F\u002Fgithub.com\u002Fdatadrivenconstruction\u002FOpenConstructionEstimate-DDC-CWICR)\r\n- **DDC Book**: Chapter 3.1 - Material Waste Management\r\n",{"slug":5,"description":7,"display_name":27},"CWICR Waste Calculator","main","e7e336ac628c3e3bd771aff1aaa469c93778f94a","skills\u002Fdatadrivenconstruction\u002Fcwicr-waste-calculator",0,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del","2026-08-23 22:39:37",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add kbarbel640-del\u002Fskills --skill cwicr-waste-calculator",{"tool":41,"command":42},"bunx","bunx skills add kbarbel640-del\u002Fskills --skill cwicr-waste-calculator",{"tool":44,"command":45},"pnpm","pnpm dlx skills add kbarbel640-del\u002Fskills --skill cwicr-waste-calculator","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdatadrivenconstruction\u002Fcwicr-waste-calculator\u002FSKILL.md",true,[49],{"path":50,"resourceType":51,"resourceTypeDesc":52,"sizeBytes":53,"sourceHtmlUrl":54},"_meta.json","REFERENCE","参考文档",312,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdatadrivenconstruction\u002Fcwicr-waste-calculator\u002F_meta.json",[56,87,116,137,143,163],{"skillNo":57,"name":58,"nameZh":6,"description":59,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":60,"categories":61,"keywords":67,"publishedAt":22},"01M6G8J55967D6281ACMKAKTEQ","crowd-prompting","A marketplace where AI agents improve prompts, system instructions, tool descriptions, and other text-based content with domain expertise from real-world operations — and earn tokens for valuable contributions.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzzadrian\u002Fcrowd-prompting\u002FSKILL.md",[62],{"categoryNo":63,"categoryName":64,"taxonomyType":65,"taxonomyTypeDesc":66},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86],"crowd prompting","prompting","api","with","tokens","domain","world","prompts","operations","instructions","text","tool","system","improve","productivity","marketplace","https","content","ai agents",{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":98,"publishedAt":22},"01M6G8J0PKPAYVET6BWE8W4C26","dilemm-ai","Compete in DilemmAI, the prisoner's dilemma AI arena at dilemm.ai. Use when an OpenClaw agent wants to create an account, design and submit strategy prompts for their bot, enter matchmaking, analyze historical matches and leaderboards, and iterate on bot strategy. Requires headless browser automation (no API available yet).","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzyori\u002Fdilemm-ai\u002FSKILL.md",[93],{"categoryNo":94,"categoryName":95,"taxonomyType":96,"taxonomyTypeDesc":97},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[99,70,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,75,115],"dilemm ai","arena","analyze","browser","openclaw agent","create","automation","browser automation","bot","openclaw","design","strategy","headless","compete","submit","matchmaking","use",{"skillNo":117,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":125,"publishedAt":22},"01M6G8HWAF3K9SW7NP3Z83CPJ6","arxiv-paper-reviews","Interact with arXiv Crawler API to fetch papers, read reviews, and submit comments. Use when working with arXiv papers, fetching paper lists by date\u002Fcategory\u002Finterest, viewing paper details with comments, or submitting paper reviews via API at http:\u002F\u002F150.158.152.82:8000.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Fweak-accept\u002FSKILL.md",[122],{"categoryNo":123,"categoryName":124,"taxonomyType":96,"taxonomyTypeDesc":97},"01JBSEEDCAT000000000000021","资料检索与研究",[126,127,128,129,130,131,132,71,133,113,115,134,70,135,136],"arxiv paper reviews","reviews","arxiv","paper","crawler","papers","comments","fetch","read","http","via",{"skillNo":138,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":142,"publishedAt":22},"01M6G8HQJ7N7WMY6YV7D7SSJ16","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Farxiv-paper-reviews\u002FSKILL.md",[141],{"categoryNo":123,"categoryName":124,"taxonomyType":96,"taxonomyTypeDesc":97},[129,128,127,126,136,131,132,71,130,113,115,133,134,70,135],{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":147,"categories":148,"keywords":149,"publishedAt":22},"01M6G8HM95Y64JNVFC2HN4E2BG","git-summary","Get a quick summary of the current Git repository including status, recent commits, branches, and contributors.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzweack\u002Fgit-summary\u002FSKILL.md",[],[150,151,152,153,154,155,156,157,158,159,160,161,162],"git","summary","git summary","commits","repository","win32","status","linux","contributors","true","quick","get","branches",{"skillNo":164,"name":165,"nameZh":6,"description":166,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":173,"publishedAt":22},"01M6G8HGEGF05CNEJCGVYFSC4R","agent-constitution","Interact with AgentConstitution governance contracts on Base Sepolia. Check compliance, read rules, log actions, query governance state.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fztsalexey\u002Fagent-constitution\u002FSKILL.md",[169,170],{"categoryNo":94,"categoryName":95,"taxonomyType":96,"taxonomyTypeDesc":97},{"categoryNo":171,"categoryName":172,"taxonomyType":65,"taxonomyTypeDesc":66},"01JBSEEDCAT000000000000037","律师与法务",[174,175,176,177,71,178,179,28,180,134,181,182,183,184,185,84,186,187,188],"agent constitution","constitution","contracts","tree","query","base","log","check","Rules","projects","compliance","actions","state","governance","github"]