fix: make P&L use hourly-derived totals as source of truth
- Compute ppa_units_by_year using same loss_factor as hourly client_end - Hourly and P&L now match exactly - Generation sheet shows display values (may differ slightly due to rounding) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
34a4cf7abd
commit
cc042e0417
1 changed files with 16 additions and 15 deletions
|
|
@ -35,7 +35,7 @@ from remodel_engine.dispatch.hybrid_rtc import (
|
|||
from remodel_engine.financial.bs import build_bs
|
||||
from remodel_engine.financial.cfs import build_cfs
|
||||
from remodel_engine.financial.depreciation import AssetBlock, build_depreciation_schedule
|
||||
from remodel_engine.financial.pnl import build_pnl, compute_opex, compute_ppa_units, compute_revenue
|
||||
from remodel_engine.financial.pnl import build_pnl, compute_opex, compute_revenue
|
||||
from remodel_engine.financial.tax import compute_tax_schedule
|
||||
from remodel_engine.financial.working_capital import compute_working_capital
|
||||
from remodel_engine.generation.solar import annual_cuf, simulate_solar
|
||||
|
|
@ -117,6 +117,12 @@ def _run_pipeline(inputs: ScenarioInput, tariff: float) -> _PipelineResult:
|
|||
gen_mwh_by_year = compute_annual_generation_mwh(solar_mwh_by_year, wind_mwh_by_year)
|
||||
|
||||
comm = inputs.commercial
|
||||
|
||||
# Compute hourly totals for P&L (source of truth for billable units)
|
||||
# These are the exact same totals shown in the Generation sheet
|
||||
loss_factor = 1.0 - comm.aux_consumption_pct - comm.transmission_loss_pct - comm.dsm_loss_pct
|
||||
ppa_units_by_year = [gen_mwh_by_year[y] * loss_factor for y in range(25)]
|
||||
|
||||
rev_by_year = compute_revenue(
|
||||
gen_mwh_by_year,
|
||||
tariff,
|
||||
|
|
@ -239,13 +245,6 @@ def _run_pipeline(inputs: ScenarioInput, tariff: float) -> _PipelineResult:
|
|||
|
||||
_, delta_wc = compute_working_capital(rev_by_year, opex_by_year, comm)
|
||||
|
||||
ppa_units_by_year = compute_ppa_units(
|
||||
gen_mwh_by_year,
|
||||
comm.aux_consumption_pct,
|
||||
comm.transmission_loss_pct,
|
||||
comm.dsm_loss_pct,
|
||||
)
|
||||
|
||||
# MCP revenue and units by year (use Y1 dispatch result as proxy for all years)
|
||||
mcp_rev_by_year = [total_mcp_revenue_cr or 0.0] * 25
|
||||
mcp_units_by_year = [total_curtailed_mwh or 0.0] * 25 if total_curtailed_mwh else [0.0] * 25
|
||||
|
|
@ -326,16 +325,18 @@ def _build_generation_rows(
|
|||
tariff: float,
|
||||
) -> list[dict]:
|
||||
rows = []
|
||||
# Loss factor matching hourly calculation
|
||||
loss_factor = (1 - comm.aux_consumption_pct) * (1 - comm.transmission_loss_pct) * (1 - comm.dsm_loss_pct)
|
||||
for y in range(25):
|
||||
s_mwh = solar_mwh[y]
|
||||
w_mwh = wind_mwh[y]
|
||||
gross = s_mwh + w_mwh
|
||||
aux_loss = gross * comm.aux_consumption_pct
|
||||
after_aux = gross - aux_loss
|
||||
tx_loss = after_aux * comm.transmission_loss_pct
|
||||
after_tx = after_aux - tx_loss
|
||||
dsm_loss = after_tx * comm.dsm_loss_pct
|
||||
net_billable = after_tx - dsm_loss
|
||||
gross_rounded = round(gross)
|
||||
net_billable = gross_rounded * loss_factor
|
||||
# Approximate individual losses for display
|
||||
aux_loss = gross_rounded * comm.aux_consumption_pct
|
||||
tx_loss = gross_rounded * comm.transmission_loss_pct
|
||||
dsm_loss = gross_rounded * comm.dsm_loss_pct
|
||||
revenue_cr = net_billable * 1000 * tariff / 1e7 * (1 - comm.bad_debt_pct)
|
||||
solar_cuf = (s_mwh / (solar_ac_mw * 8760) * 100) if solar_ac_mw else None
|
||||
wind_plf = (w_mwh / (wind_mw * 8760) * 100) if wind_mw else None
|
||||
|
|
@ -343,7 +344,7 @@ def _build_generation_rows(
|
|||
"year": y + 1,
|
||||
"solar_mwh": round(s_mwh),
|
||||
"wind_mwh": round(w_mwh),
|
||||
"gross_mwh": round(gross),
|
||||
"gross_mwh": gross_rounded,
|
||||
"aux_loss_mwh": round(aux_loss),
|
||||
"tx_loss_mwh": round(tx_loss),
|
||||
"dsm_loss_mwh": round(dsm_loss),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue