from config import FLOW_RATES, USAGE_PATTERNS, USER_MULTIPLIERS, DRINKING_WATER def calculate_usage(requirements): """ Calculates daily water usage and survival days. IMPORTANT - How effective multipliers work (matching Excel model): - eff_shower/sink = SUM of (count × mult) across all user types → This already encodes total group usage, so NO extra × num_adults needed - eff_toilet = SUM of all adult counts (toilet mult = 1 for everyone) → Used for display only; toilet formula uses base per-person × 1 event - Toilet daily = flow × frequency × 1 (base, not scaled by eff_toilet) - Drinking adults = 0.7 × num_adults from CORE PARAMS (not user type total) """ # ── User type counts (from User Type Distribution) ────────────────────── num_expert = requirements.get("num_expert", 0) num_typical = requirements.get("num_typical", 0) num_glamper = requirements.get("num_glamper", 0) # ── Core params ────────────────────────────────────────────────────────── num_adults = requirements.get("num_adults", num_expert + num_typical + num_glamper) num_children = requirements.get("num_children", 0) climate_mult = requirements.get("climate_mult", 1.0) # ── Effective Multipliers (SUM — already encodes all adults) ───────────── # Excel: Eff Shower = 2×0.6 + 0×1.0 + 1×1.5 = 2.7 (NOT divided by adults) eff_shower_mult = ( num_expert * USER_MULTIPLIERS["expert"]["shower"] + num_typical * USER_MULTIPLIERS["typical"]["shower"] + num_glamper * USER_MULTIPLIERS["glamper"]["shower"] ) eff_sink_mult = ( num_expert * USER_MULTIPLIERS["expert"]["sink"] + num_typical * USER_MULTIPLIERS["typical"]["sink"] + num_glamper * USER_MULTIPLIERS["glamper"]["sink"] ) # Toilet mult = 1 for all types → eff_toilet = total adults (display only) eff_toilet_mult = num_expert + num_typical + num_glamper # ── Daily Fresh Water Per Activity ─────────────────────────────────────── # Shower: flow × duration × frequency × eff_shower_mult × climate # (eff_shower_mult already accounts for all adults — no × num_adults) shower_daily = ( FLOW_RATES["shower"] * USAGE_PATTERNS["shower"]["duration"] * USAGE_PATTERNS["shower"]["frequency"] * eff_shower_mult * climate_mult ) # Kitchen Sink: same pattern kitchen_sink_daily = ( FLOW_RATES["kitchen_sink"] * USAGE_PATTERNS["kitchen_sink"]["duration"] * USAGE_PATTERNS["kitchen_sink"]["frequency"] * eff_sink_mult * climate_mult ) # Bathroom Sink: same pattern bathroom_sink_daily = ( FLOW_RATES["bathroom_sink"] * USAGE_PATTERNS["bathroom_sink"]["duration"] * USAGE_PATTERNS["bathroom_sink"]["frequency"] * eff_sink_mult * climate_mult ) # Toilet: base per-person usage only (1.8 gal × 6 flushes × 1 person base) # Excel shows 10.8 regardless of group size in activity table toilet_daily = ( FLOW_RATES["toilet"] * USAGE_PATTERNS["toilet"]["frequency"] * 1 # base single-person; eff_toilet is display only ) # Drinking: uses core num_adults (e.g., 2) not user-type total drinking_adults_daily = DRINKING_WATER["adult"] * num_adults drinking_children_daily = DRINKING_WATER["child"] * num_children total_drinking_daily = drinking_adults_daily + drinking_children_daily # ── Totals ─────────────────────────────────────────────────────────────── total_fresh_used_per_day = ( shower_daily + kitchen_sink_daily + bathroom_sink_daily + toilet_daily + total_drinking_daily ) total_grey_added_per_day = shower_daily + kitchen_sink_daily + bathroom_sink_daily total_black_added_per_day = toilet_daily # ── Tank Levels ────────────────────────────────────────────────────────── fresh_cap = requirements.get("fresh_cap", 0) grey_cap = requirements.get("grey_cap", 0) black_cap = requirements.get("black_cap", 0) fresh_level = requirements.get("fresh_level", 0) grey_level = requirements.get("grey_level", 0) black_level = requirements.get("black_level", 0) # ── Survival Days ──────────────────────────────────────────────────────── # Fresh: runs DOWN → days until empty # Grey: fills UP → days until full # Black: fills UP → days until full days_fresh = (fresh_level / total_fresh_used_per_day if total_fresh_used_per_day > 0 and fresh_level > 0 else 0.0) days_grey = ((grey_cap - grey_level) / total_grey_added_per_day if total_grey_added_per_day > 0 else float('inf')) days_black = ((black_cap - black_level) / total_black_added_per_day if total_black_added_per_day > 0 else float('inf')) overall_survival = min(days_fresh, days_grey, days_black) # Limiting factor tank_map = {days_fresh: "Fresh", days_grey: "Grey", days_black: "Black"} limiting_tank = tank_map[min(days_fresh, days_grey, days_black)] return { "daily_usage": { "fresh": round(total_fresh_used_per_day, 2), "grey": round(total_grey_added_per_day, 2), "black": round(total_black_added_per_day, 2), "drinking": round(total_drinking_daily, 2), "shower": round(shower_daily, 2), "kitchen_sink": round(kitchen_sink_daily, 2), "bathroom_sink": round(bathroom_sink_daily, 2), "toilet": round(toilet_daily, 2), }, "survival_days": { "fresh": round(days_fresh, 4), "grey": round(days_grey, 4), "black": round(days_black, 4), "overall": round(overall_survival, 4), "limiting_tank": limiting_tank, }, "effective_multipliers": { "shower": round(eff_shower_mult, 3), "sink": round(eff_sink_mult, 3), "toilet": eff_toilet_mult, }, "tanks": { "fresh": {"cap": fresh_cap, "level": fresh_level}, "grey": {"cap": grey_cap, "level": grey_level}, "black": {"cap": black_cap, "level": black_level} } }