Spaces:
Running
Running
Download src/simulator.py from Liisdotcom/simulator: direct link, hf CLI and curl.
- Browser
- Download file 6.59 kB
-
https://huggingface.co/spaces/Liisdotcom/simulator/resolve/main/src/simulator.py
- Command line
-
hf download hf://spaces/Liisdotcom/simulator/src/simulator.py
-
curl -L -o simulator.py https://huggingface.co/spaces/Liisdotcom/simulator/resolve/main/src/simulator.py
6.59 kB
| """ | |
| LiiS Circular Economy Simulator | |
| Core calculations for CTI metrics and P&L projections. | |
| """ | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| import yaml | |
| class CTIMetrics: | |
| """Circular Transition Indicator metrics.""" | |
| eolrr: float # End-of-Life Recovery Rate (0-1) | |
| pri: float # Primary Resource Input (0-1, lower is better) | |
| lifetime_extension: float # Ratio of actual to design lifetime | |
| units_recovered: int # Absolute units recovered per cycle | |
| blended_cost: float # EUR per unit (new + refurb weighted) | |
| class PLProjection: | |
| """Profit & Loss projection.""" | |
| revenue_per_unit: float | |
| cost_per_unit: float | |
| margin_per_unit: float | |
| margin_percent: float | |
| break_even_price: float | |
| def load_product(name: str) -> dict: | |
| """Load product configuration from YAML.""" | |
| config_path = Path(__file__).parent.parent / "products" / name / "config.yaml" | |
| if not config_path.exists(): | |
| raise FileNotFoundError(f"Product config not found: {config_path}") | |
| return yaml.safe_load(config_path.read_text()) | |
| def calculate_cti(config: dict, distributed_units: int = 200) -> CTIMetrics: | |
| """ | |
| Calculate CTI metrics for a product configuration. | |
| Args: | |
| config: Product configuration dict (from YAML) | |
| distributed_units: Number of units distributed in pilot | |
| Returns: | |
| CTIMetrics with calculated values | |
| """ | |
| returns = config["return_assumptions"] | |
| design = config["design"] | |
| costs = config["costs"] | |
| # EoLRR = Return Rate × (Grade A + Grade B percentage) | |
| return_rate = returns["return_rate"] | |
| grade_a = returns["grade_distribution"]["a"] | |
| grade_b = returns["grade_distribution"]["b"] | |
| eolrr = return_rate * (grade_a + grade_b) | |
| # Units recovered | |
| units_recovered = int(distributed_units * eolrr) | |
| # PRI for Year 2 steady state (simplified) | |
| # Assumes monthly demand of distributed_units / 12 | |
| monthly_demand = distributed_units / 12 | |
| months_until_return = returns["months_until_return"] | |
| refurb_available = units_recovered / (months_until_return / 12) | |
| # New units needed = demand - refurb available | |
| new_units_monthly = max(0, monthly_demand - refurb_available) | |
| total_monthly = monthly_demand | |
| pri = new_units_monthly / total_monthly if total_monthly > 0 else 1.0 | |
| # Lifetime Extension | |
| cycles = design["max_cycles"] | |
| use_per_cycle = design["use_cycle_years"] | |
| design_lifetime = design["lifetime_years"] | |
| lifetime_extension = (cycles * use_per_cycle) / design_lifetime | |
| # Blended cost | |
| mfg_cost = costs["manufacturing_unit_cost"] | |
| avg_refurb_cost = ( | |
| costs["refurbishment"]["grade_a"] * grade_a | |
| + costs["refurbishment"]["grade_b"] * grade_b | |
| ) / (grade_a + grade_b) | |
| blended_cost = ( | |
| new_units_monthly * mfg_cost + refurb_available * avg_refurb_cost | |
| ) / total_monthly | |
| return CTIMetrics( | |
| eolrr=round(eolrr, 3), | |
| pri=round(pri, 3), | |
| lifetime_extension=round(lifetime_extension, 2), | |
| units_recovered=units_recovered, | |
| blended_cost=round(blended_cost, 2), | |
| ) | |
| def calculate_pl(config: dict, cti: CTIMetrics) -> PLProjection: | |
| """ | |
| Calculate P&L projection based on CTI metrics. | |
| Args: | |
| config: Product configuration dict | |
| cti: Calculated CTI metrics | |
| Returns: | |
| PLProjection with financial metrics | |
| """ | |
| pricing = config["pricing"] | |
| costs = config["costs"] | |
| # Revenue per unit over lease term | |
| monthly_price = pricing["lease_monthly"] | |
| lease_months = pricing["lease_term_months"] | |
| revenue_per_unit = monthly_price * lease_months | |
| # Cost per unit (blended manufacturing + logistics) | |
| logistics_cost = costs["logistics"]["delivery"] + costs["logistics"]["return"] | |
| cost_per_unit = cti.blended_cost + logistics_cost | |
| # Margin | |
| margin_per_unit = revenue_per_unit - cost_per_unit | |
| margin_percent = (margin_per_unit / revenue_per_unit) * 100 if revenue_per_unit > 0 else 0 | |
| # Break-even monthly price | |
| break_even_price = cost_per_unit / lease_months | |
| return PLProjection( | |
| revenue_per_unit=round(revenue_per_unit, 2), | |
| cost_per_unit=round(cost_per_unit, 2), | |
| margin_per_unit=round(margin_per_unit, 2), | |
| margin_percent=round(margin_percent, 1), | |
| break_even_price=round(break_even_price, 2), | |
| ) | |
| def format_report(config: dict, cti: CTIMetrics, pl: PLProjection) -> str: | |
| """Generate a markdown report for a product.""" | |
| name = config["product"]["name"] | |
| report = f"""# CTI Analysis: {name} | |
| ## Summary | |
| | Metric | Value | Target | | |
| |--------|-------|--------| | |
| | EoLRR | {cti.eolrr:.1%} | {config['cti_parameters']['target_eolrr']:.0%} | | |
| | PRI (Year 2) | {cti.pri:.1%} | {config['cti_parameters']['target_pri_year2']:.0%} | | |
| | Lifetime Extension | {cti.lifetime_extension:.1f}x | 1.0x | | |
| ## P&L Projection | |
| | Metric | Value | | |
| |--------|-------| | |
| | Revenue per unit | EUR {pl.revenue_per_unit:.2f} | | |
| | Cost per unit (blended) | EUR {pl.cost_per_unit:.2f} | | |
| | Margin per unit | EUR {pl.margin_per_unit:.2f} | | |
| | Margin % | {pl.margin_percent:.1f}% | | |
| | Break-even price | EUR {pl.break_even_price:.2f}/month | | |
| ## Configuration | |
| - **Lease price**: EUR {config['pricing']['lease_monthly']}/month | |
| - **Lease term**: {config['pricing']['lease_term_months']} months | |
| - **Manufacturing cost**: EUR {config['costs']['manufacturing_unit_cost']} | |
| - **Return rate**: {config['return_assumptions']['return_rate']:.0%} | |
| ## Grade Distribution | |
| | Grade | % | Refurb Cost | | |
| |-------|---|-------------| | |
| | A (reusable) | {config['return_assumptions']['grade_distribution']['a']:.0%} | EUR {config['costs']['refurbishment']['grade_a']} | | |
| | B (refurbish) | {config['return_assumptions']['grade_distribution']['b']:.0%} | EUR {config['costs']['refurbishment']['grade_b']} | | |
| | C (scrap) | {config['return_assumptions']['grade_distribution']['c']:.0%} | EUR {config['costs']['refurbishment']['grade_c']} | | |
| """ | |
| return report | |
| if __name__ == "__main__": | |
| # Example usage | |
| for product_name in ["besafe", "hamax"]: | |
| print(f"\n{'='*50}") | |
| print(f"Analyzing: {product_name}") | |
| print("=" * 50) | |
| config = load_product(product_name) | |
| cti = calculate_cti(config) | |
| pl = calculate_pl(config, cti) | |
| print(f"EoLRR: {cti.eolrr:.1%}") | |
| print(f"PRI: {cti.pri:.1%}") | |
| print(f"Lifetime Extension: {cti.lifetime_extension:.1f}x") | |
| print(f"Blended Cost: EUR {cti.blended_cost:.2f}") | |
| print(f"Margin: {pl.margin_percent:.1f}%") | |