File size: 7,229 Bytes
0a87e25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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}
        }
    }