"""14 — Transition ALTERNATIVE: shore power straight into the 6000XP AC-IN (no Chargeverter).
If the AHJ permits grid into the inverter's AC input, Phase 1 can charge the single Docan
pack through the 6000XP's OWN onboard AC charger instead of the DC-coupled Chargeverter.
This sizes the shore -> AC-IN branch and checks the limits. Single 6000XP, single pack.
Inputs (site.yaml, CONFIRMED unless noted):
- inverter.batt_max_charge_a 125 A DC from PV; note says 115 A from AC -> AC-charge cap
- inverter.ac_input_passthrough_a 50 A (grid/gen bypass = branch design basis)
- battery.charge_cutoff_v 58.4 V (worst-case AC-draw voltage, conservative)
- battery.bms_continuous_charge_a 150 A (pack BMS charge limit)
- AC 240 V split-phase
ASSUMPTION (flagged, not from datasheet): AC->DC onboard-charger efficiency = 0.92
(Chargeverter GC is 94%; 6000XP onboard charger taken conservatively at 92% -> higher AC draw).
Run: ~/.venvs/prsm-electrical/bin/python scripts/14_shore_acin.py
"""
import importlib
from common import load_site, require, get, banner, result, dump_result
_c = importlib.import_module("03_conductors")
size_run, report = _c.size_run, _c.report
AC_CHARGE_EFF = 0.92 # ASSUMPTION (see header) — conservative for AC-draw sizing
V_AC = 240.0
def main():
cfg = load_site()
banner("14 TRANSITION ALT — shore -> 6000XP AC-IN (no Chargeverter)")
ac_charge_dc_a = 115 # site.yaml note: 115 A from AC
pv_charge_dc_a = require(cfg, "inverter.batt_max_charge_a") # 125 (PV)
ac_in_cap_a = require(cfg, "inverter.ac_input_passthrough_a") # 50
v_charge = require(cfg, "battery.charge_cutoff_v") # 58.4 (worst case)
vnom = require(cfg, "battery.nominal_v")
cap_ah = require(cfg, "battery.capacity_ah")
bms_charge_lim = require(cfg, "battery.bms_continuous_charge_a") # 150
runs = get(cfg, "phase2.runs_ft") or cfg["runs_ft"]
tgt = cfg["targets"]
L = runs.get("inverter_to_loads", 10)
result("inverter AC-charge cap", f"{ac_charge_dc_a} A DC", "", "site.yaml: 125 A from PV / 115 A from AC")
result("6000XP AC input cap", f"{ac_in_cap_a} A @ 240V", "", "grid/gen passthrough (branch design basis)")
# --- AC draw at the full 115 A DC AC-charge limit (conservative worst-case voltage) ---
p_dc = ac_charge_dc_a * v_charge
i_ac_charge = p_dc / (AC_CHARGE_EFF * V_AC)
result("charge power (DC, worst-case)", f"{p_dc:.0f} W", "", f"{ac_charge_dc_a} A x {v_charge} V (absorption ceiling)")
result("AC draw for full charge", f"{i_ac_charge:.1f} A @ 240V", "",
f"= {p_dc:.0f} W / ({AC_CHARGE_EFF} eff x 240 V); <= {ac_in_cap_a} A input cap "
f"({'PASS' if i_ac_charge <= ac_in_cap_a else 'OVER'}, {ac_in_cap_a - i_ac_charge:.0f} A spare for loads)")
# --- pack-side checks (single Docan pack) ---
c_rate = ac_charge_dc_a / cap_ah
result("pack charge vs BMS limit", f"{ac_charge_dc_a} / {bms_charge_lim} A", "",
f"{'PASS' if ac_charge_dc_a <= bms_charge_lim else 'FAIL'} ({100*ac_charge_dc_a/bms_charge_lim:.0f}%); "
f"C-rate {c_rate:.2f}C (<= 0.5C)")
# --- branch sized to the CHARGE DRAW (continuous), the realistic transition feed.
# OCPD stays <= the inverter's 50 A input rating; the inverter's internal breaker also protects the input.
print("\n SHORE -> AC-IN BRANCH (NEC 210/215, 240.4(D); sized to the ~30 A charge draw):")
r = size_run("Shore -> 6000XP AC-IN (240V)", mode="continuous", current=i_ac_charge,
vref=V_AC, length_ft=L, target_pct=tgt["vdrop_ac_pct_max"], ambient_c=30, ccc=2)
report(r)
result("branch result", f"{r['final_gauge_AWG']} AWG Cu + {r['ocpd_A']} A 2-pole + {r['egc_AWG']} AWG EGC", "",
f"Vdrop {r['vdrop_pct']}%; carries the {i_ac_charge:.0f} A charge draw; OCPD {r['ocpd_A']} A <= {ac_in_cap_a} A input rating. "
f"240 V branch like an EV charger (NEC 210/240, not 705). Upsize to 6 AWG/50 A to also pass full loads from shore.")
# --- shore-source scenarios: what a given feed allows ---
print("\n SHORE-SOURCE SCENARIOS (program AC-charge current to the feed):")
rows = []
for feed_a in (30, 50):
feed_w = feed_a * V_AC
dc_a_at_feed = min(ac_charge_dc_a, (feed_w * AC_CHARGE_EFF) / v_charge)
loads_spare_a = max(0.0, feed_a - i_ac_charge) if feed_a >= i_ac_charge else 0.0
note = (f"full {ac_charge_dc_a} A DC charge (~{i_ac_charge:.0f} A AC) + ~{loads_spare_a:.0f} A spare for loads"
if feed_a >= i_ac_charge else
f"charge limited to ~{dc_a_at_feed:.0f} A DC; program inverter AC-charge <= {feed_a} A")
result(f" {feed_a} A / 240 V shore", f"~{dc_a_at_feed:.0f} A DC achievable", "", note)
rows.append({"shore_feed_a": feed_a, "dc_charge_a_achievable": round(dc_a_at_feed, 1),
"loads_spare_a": round(loads_spare_a, 1)})
dump_result("14_shore_acin", {
"scope": "Phase-1 transition alternative: shore -> single 6000XP AC-IN (permitted scenario)",
"ac_charge_dc_a": ac_charge_dc_a, "pv_charge_dc_a": pv_charge_dc_a,
"ac_input_cap_a": ac_in_cap_a, "charge_voltage_worstcase_V": v_charge,
"ac_charge_eff_assumed": AC_CHARGE_EFF,
"ac_draw_full_charge_a": round(i_ac_charge, 1),
"ac_draw_le_input_cap": bool(i_ac_charge <= ac_in_cap_a),
"spare_for_loads_a": round(ac_in_cap_a - i_ac_charge, 1),
"pack_c_rate": round(c_rate, 3), "pack_charge_le_bms": bool(ac_charge_dc_a <= bms_charge_lim),
"branch": {"gauge_AWG": r["final_gauge_AWG"], "ocpd_A": r["ocpd_A"],
"egc_AWG": r["egc_AWG"], "vdrop_pct": r["vdrop_pct"], "length_ft": L},
"shore_scenarios": rows,
"vs_chargeverter": "Skips the EG4 Chargeverter GC + its DC tap; reuses the AC-IN branch (also needed for the generator).",
"nec_basis": "Grid -> inverter AC input = NEC 210/240 branch + the inverter's transfer switch; off-grid inverter does NOT export (no 705). AHJ must permit grid-on-AC-IN (the scenario premise).",
})
if __name__ == "__main__":
main()