"""07 — Supportable household-load envelope (energy + power + autonomy).
NOT a load study — the household's actual load profile is still an open item (HANDOFF).
This BOUNDS what daily kWh and instantaneous kW THIS configuration can support, from the
computed winter-binding production (02) and the battery/inverter limits (04). Off-grid the
binding constraint is winter production; the battery sets short-term autonomy; the Phase-1
generator backfills multi-day deficits.
Run (after 02, 04): ~/.venvs/prsm-electrical/bin/python scripts/07_load_capacity.py
"""
from common import load_site, require, banner, result, dump_result, load_result
# Fraction of delivered AC production usable as household load. PVWatts already applied a
# 14% system loss to AC, so this band is mainly LiFePO4 round-trip (~0.95) on the
# time-shifted energy plus a practical reserve. A band, not a point estimate.
USE_LO, USE_HI = 0.85, 0.95
DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def main():
cfg = load_site()
banner("07 SUPPORTABLE HOUSEHOLD LOAD (energy + power + autonomy)")
p = load_result("02_production")
m = p["ac_monthly_kwh"]
dec = p["dec_kwh_day"]
jun = p["jun_kwh_day"]
winter = round((m[0] + m[11]) / (DAYS[0] + DAYS[11]), 1) # Dec+Jan per day
shoulder = round(sum(m[i] / DAYS[i] for i in (2, 3, 8, 9)) / 4, 1) # Mar/Apr/Sep/Oct
annual = round(p["ac_annual_kwh"] / 365.0, 1)
inv_w = require(cfg, "inverter.ac_continuous_w")
surge_w = require(cfg, "inverter.ac_surge_w")
leg_a = require(cfg, "inverter.ac_cont_a_per_leg")
usable = require(cfg, "battery.usable_kwh")
dod = require(cfg, "battery.dod_design")
usable_dod = round(usable * dod, 1)
seasons = [("Deep winter (Dec)", dec), ("Winter avg (Dec-Jan)", winter),
("Shoulder (Mar/Apr/Sep/Oct)", shoulder), ("Summer (Jun)", jun),
("Annual avg", annual)]
print("\n ENERGY — supportable daily load, solar-only (production x 0.85-0.95):")
print(f" {'season':<28}{'AC produced':>13}{'usable load':>18}")
rows = []
for name, prod in seasons:
lo, hi = USE_LO * prod, USE_HI * prod
print(f" {name:<28}{prod:>9.1f} kWh{f'{lo:.0f}-{hi:.0f} kWh/day':>18}")
rows.append({"season": name, "ac_kwh_day": prod,
"supportable_kwh_day": [round(lo), round(hi)]})
print("\n POWER — instantaneous envelope (inverter limits, from 02/04):")
result(" continuous", inv_w, "W", "max sum of simultaneous loads")
result(" surge (3.5 s)", surge_w, "W", "motor / compressor starts")
result(" per 120 V leg", leg_a, "A", "~3 kW per leg of split-phase")
print(f"\n AUTONOMY — one battery, usable {usable_dod} kWh @ {dod*100:.0f}% DoD:")
print(f" {'daily load':>12}{'days w/o sun':>16}")
auton = {}
for daily in (5, 8, 10, 13, 20):
d = round(usable_dod / daily, 2)
print(f" {daily:>8} kWh{d:>13} days")
auton[str(daily)] = d
dump_result("07_load_capacity", {
"production_kwh_day": {n: v for n, v in seasons},
"usage_band": [USE_LO, USE_HI],
"supportable_daily_load_kwh": rows,
"inverter_continuous_W": inv_w, "inverter_surge_W": surge_w, "per_leg_A": leg_a,
"battery_usable_kwh_80dod": usable_dod, "days_autonomy": auton,
"note": "capacity envelope, not a load study; generator backfills winter deficit",
})
if __name__ == "__main__":
main()