Methodology
This page is the engine, written out. Every number on the site — the pre-rendered state pages and the interactive calculator alike — comes from one module, src/lib/engine/, that implements exactly what follows. If a formula changes, this page changes in the same commit.
Principles
- Federal policy is data, not a constant. The credit percentage lives in a versioned file keyed by year. Nothing in the calculation code knows the number 30.
- Every input carries a source and a verification date.
- A value we have not verified is
null. The engine falls back to a national figure and raises a flag that the page must show. We never type in a plausible number. - One engine feeds everything. The state pages are rendered at build time by the same function the calculator runs in your browser. They cannot disagree.
The federal credit in 2026
Two different statutes, two different recipients, and in 2026 they diverge completely.
| You buy (cash or loan) | Someone else owns it (lease / PPA) | |
|---|---|---|
| Statute | IRC 25D (expired) | IRC 48E (Clean Electricity Investment Credit) |
| Credit in 2025 | 30% | 30% |
| Credit in 2026 | 0% | 30% |
| Who claims it | You, on your return | system owner (lessor / PPA provider), passed through in pricing - NOT claimable by the homeowner |
| Verified | 2026-09-05 | 2026-09-04 |
25D unavailable for property placed in service after 2025-12-31 (One Big Beautiful Bill Act).
Why the owner gets 30% rather than the 6% base rate. One-megawatt exception: a facility with maximum net output under 1 MW AC is entitled to the increased credit rate without meeting prevailing-wage and apprenticeship requirements (26 CFR 1.45Y-3). Residential arrays are 5-15 kW, far under the threshold.26 CFR 1.45Y-3.
Risks a lease customer should ask about:
- Systems whose construction begins after 4 July 2026 fall under different termination rules.
- Systems placed in service after 31 December 2027 face restrictions.
- Equipment sourcing must satisfy the foreign-entity-of-concern (FEOC) rules.
1 · Your consumption
We start from the one number you know: your monthly bill. Part of it is a fixed charge solar cannot remove.
energyBill = max(0, monthlyBill − fixedMonthlyFee) × 12
annualKwh = energyBill ÷ raterate is your state's average residential price in $/kWh from the EIA. Overridable in the calculator.
2 · Sizing the system
The system is sized to produce a target share of your annual use, given how much a kilowatt of panels yields where you live.
sizeKw = annualKwh × offsetTarget ÷ specificYield
panels = ⌈ sizeKw × 1000 ÷ panelWatts ⌉
roofSqft = panels × sqftPerPanel
year1Production = sizeKw × specificYieldspecificYield is kWh per kW DC per year from NREL PVWatts v8 for your city, or a statewide default, at 20° tilt, due south, 14% system losses, fixed open rack.
3 · What it costs
grossSolar = sizeKw × 1000 × usdPerWatt
grossTotal = grossSolar + (battery ? batteryCost : 0)
federalCredit = grossTotal × policy[year].purchase.creditPct ÷ 100
stateIncentives = Σ over each verified incentive paid to the buyer:
tax_credit_pct → min(grossTotal × pct ÷ 100, cap)
tax_credit_flat → value
rebate_flat → value
rebate_per_watt → min(sizeKw × 1000 × value, cap)
netCost = max(0, grossTotal − federalCredit − stateIncentives)
A performance incentive is not a discount on the price: it is paid for electricity
produced, on a contract term, and enters the cash flow in the years it arrives.
as_generated → productionᵧ × value, for term years
part_upfront → the term's expected output × value, of which
upfrontShare arrives at switch-on and the rest
is spread over the following years
An incentive on file that the engine cannot pay out raises an error and the page
does not publish: understating a return is the same failure as overstating one.usdPerWatt is the installed cost before incentives on the modelled basis. The engine runs the identical formulas a second time on the reference basis where one exists, and every page shows both (see Two price bases). A state without its own cost record is not published.
4 · Twenty-five years of bills
For each year y from 0 to 24, we compute what you would have paid without solar and what you pay with it. The difference is your gross saving.
rateY = rate × (1 + escalation)^y
productionY = year1Production × (1 − degradation)^y
selfUsed = min(productionY × selfConsumption, annualKwh)
exported = max(0, productionY − selfUsed)
imported = max(0, annualKwh − selfUsed)
solarCharges = productionY × productionChargePerKwh + sizeKw × capacityChargePerKwMonth × 12
billNoSolar = annualKwh × rateY + fixedFee × 12
billWithSolar = imported × rateY + fixedFee × 12 − exported × rateY × exportRatio + solarCharges
grossSavings = billNoSolar − billWithSolar
netSavings = grossSavings − om − (y + 1 = inverterYear ? inverterCost : 0)exportRatio is the single number that makes California different from Massachusetts. Under full-retail net metering it is 1.0 and self-consumption does not matter. Under California's net billing tariff it is roughly 0.20, and a battery — which raises self-consumption from 40% to 75% — becomes decisive.
5 · Cash, loan, lease
Cash
cashFlows = [ −netCost, netSavings₁, netSavings₂, … netSavings₂₅ ]
payback = breakEvenYear(cashFlows)
netSavings25 = Σ netSavings − netCost
roi = netSavings25 ÷ netCost
irr = irr(cashFlows)Loan
payment = monthlyPayment(netCost − downPayment, apr, termYears)
loanFlows = [ −downPayment, netSavingsᵧ − 12 × payment (while y ≤ term), then netSavingsᵧ ]
totalInterest = 12 × payment × termYears − (netCost − downPayment)The federal credit, when there is one, is applied to the amount financed.
The curves: cumulative position
position[y] = Σ flows[0..y] for each way of paying, y = 0 … 25
cash: flows = [ −netCost, netSavings₁ … netSavings₂₅ ]
loan: flows = [ −downPayment, netSavingsᵧ − 12 × payment … ]
lease: flows = [ 0, grossSavingsᵧ − leasePaymentᵧ + stateCreditᵧ … ]Every chart on the site is drawn from these series, at build time or in your browser, so a picture can never disagree with the table beside it. Year 0 is the day the system is switched on; the cash curve crosses zero at the payback year printed in the table.
Zero-upfront scenarios have no payback
Nothing was paid up front, so "payback" is meaningless for a zero-down loan or a lease. The engine reports a cash-flow verdict instead, and the pages print it in words:
threshold = annualBill × immediateMinShareOfBill
immediate if netSavings₁ ≥ threshold
neutral if 0 ≤ netSavings₁ < threshold → "cash-flow neutral from year 1 (+$x/yr)"
later if netSavings₁ < 0 and Σ netSavings₁..ᵧ ≥ 0 for some y → "positive from year y"
never otherwiseA one-dollar margin must never read as "immediate". The 25-year net is the column to compare; it is printed first and in bold.
Lease / PPA
ppaRateY = rate × ppaRateVsRetail × (1 + escalator)^y
leasePayment = productionY × ppaRateY
netSavingsY = grossSavingsY − leasePayment
upfront = 0
federalCreditToYou = 0The lease is priced below your retail rate because the owner keeps the 30% credit. You see it only as a discount. The engine never credits it to you.
Finance functions
monthlyPayment(P, apr, years)
r = apr ÷ 12, n = years × 12
= P × r ÷ (1 − (1 + r)^−n) (P ÷ n when apr = 0)
breakEvenYear(flows)
flows[0] is the outlay at t = 0; flows[i] arrives over year i (t = i−1 → i)
find the first i where Σ flows[0..i] ≥ 0, then interpolate inside that year:
payback = (i − 1) + (−Σ flows[0..i−1]) ÷ flows[i]; null if never
(checked by a test: the cash curve crosses zero at the printed payback)
irr(flows)
the r where Σ flows[i] ÷ (1 + r)^i = 0,
found by bisection on [−90%, +150%], 200 iterations; null if no sign changeTwo price bases
Installed cost is the one input with two defensible answers, and they differ by roughly 60%. A single number would be false either way — and a reader who is later quoted the other price would rightly stop trusting the site — so the engine computes the economics on both bases, through the same code path, and every state page and the calculator show the range.
| competitive quoted prices | typical reported prices | |
|---|---|---|
| What it is | The average of real, competitive quotes on a marketplace — what you get if you shop around. | The median price recorded in permit and incentive data across all installers and equipment tiers — what the average buyer actually paid. |
| Source | EnergySage Marketplace real-quote average, as of Sep 2026; page updated 2026-08-28 (EnergySage 2026) | LBNL Tracking the Sun 2024 Edition (OSTI 2438480), slide 37, median installed price, residential systems installed in 2023, 2023$/W DC, read from the chart labels (LBNL 2023) |
| Used for | Every modelled figure: the tables, the state ranking, the calculator. | The second line beside payback, net cost and 25-year net. |
| Coverage | 11 states | 9 of 11 states. LBNL publishes no median where it has fewer than 20 observations (IL, PA); those pages say so rather than borrow a neighbour's figure. |
The gap is methodology, not staleness. Which basis matches the quote in your hand decides your payback — which is exactly why both are shown.
Assumptions
Read straight from src/data/finance/defaults.json, last verified 2026-09-04. Each one is exposed as an adjustable control wherever it matters.
| Assumption | Default | Basis |
|---|---|---|
| Panel rating | 400 W | Typical residential module, 2026 |
| Roof area per panel | 17.5 sq ft | Module footprint incl. spacing |
| Offset target | 100% of annual use | System sized to cover the whole bill |
| Panel degradation | 0.5% / yr | Manufacturer warranty curves |
| Operations & maintenance | $150 / yr | Cleaning, monitoring, minor repair |
| Inverter replacement | $1,800 in year 12 | Single string inverter lifetime |
| Analysis horizon | 25 years | Standard panel warranty |
| Smallest system routinely installed | 5 kW | The smallest system a household is realistically sold. LBNL Tracking the Sun (2024 edition, systems installed in 2023) reports a median residential size of 7.4 kW with the 20th-to-80th percentile band at 5-11 kW, so four in five installed systems are at least 5 kW. It is not a hard floor - one in five is smaller - but below it a buyer is outside what most installers routinely quote, and the per-watt price rises because permitting, design and a day of labour do not scale down. The engine applies this only when a caller asks for it (overrides.minSystemKw); it is not imposed on the default sizing. |
| Median installed system | 7.4 kW | Median US residential system size, LBNL Tracking the Sun 2024 edition, systems installed in 2023. Recorded so the page can quote it rather than restate it. |
| Fixed monthly utility charge | $12 | Non-bypassable customer charge; solar does not remove it. |
| Electricity price escalation | 2.5% / yr | The single most contested assumption in any solar calculator. Always show it as an adjustable slider. |
| Self-consumption, no battery | 40% | Share of generation consumed on site rather than exported. Irrelevant under 1:1 net metering, decisive under NEM 3.0. |
| Self-consumption, with battery | 75% | Battery shifts evening load onto daytime production |
| Loan | 6.99% APR, 20 yr, $0 down | Typical unsecured solar loan |
| Lease / PPA price | 70% of retail, +2.9000000000000004% / yr | Third-party owner keeps the 48E credit and prices below the retail rate. Zero down, lower savings. |
| Battery | 13.5 kWh usable, $12,000 installed | One residential unit |
| "Immediate" threshold | 10% of the annual bill, in year-1 net cash flow | A zero-upfront scenario is called immediate only if its year-1 net cash flow is at least this share of the annual bill. Below it: cash-flow neutral. A one-dollar margin must never read as a win. |
Data and sources
| Dataset | Source | Status |
|---|---|---|
| Electricity rates | U.S. EIA API v2, electricity/retail-sales, residential sector | 2026-07 |
| Installed cost | EnergySage Marketplace real-quote average, as of Sep 2026; page updated 2026-08-28 — one source for every state. National average: EnergySage Marketplace national average before incentives (12 kW typical system, $31,135); page updated 2026-06-30. LBNL Tracking the Sun 2023 medians are recorded for reference only. | 2026-09-05 |
| Production | NLR (formerly NREL) PVWatts v8 API, 1 kW reference system | verified |
| Net metering | Per state: the commission regulation, statute, utility tariff sheet or state agency publication that governs it — each linked on its state page with the date read. Two states also charge solar customers a per-unit fee (Connecticut on production, New York on system size); both are on the bill in every figure. | 2026-09-05 |
| State incentives | Primary state sources (statute, tax department, program administrator, regulator), curated by hand, one record at a time; DSIRE as the index | 2026-09-05 |
| Federal policy | IRS; 26 CFR 1.45Y-3 | 2026-09-05 |
What is not modelled
Being clear about this is part of the method. This is a screening estimate, not a site survey.
- Two literature defaults, not measurements. Self-consumption (40% without a battery, 75% with) and the lease price (70% of retail) are typical published values, not figures we measured. Under California's NEM 3.0 they decide whether a lease saves or costs money, so treat any lease result there as directional until it has been checked against real quotes.
- A ceiling on export credits. The engine credits every exported kilowatt-hour at the state's export ratio, with no annual limit. That is right for a system sized to the household's own use — the default — where annual production is about equal to annual consumption and the year-end surplus is near zero. It is wrong for a system generating materially more than the home uses: Pennsylvania settles a year-end surplus at the price to compare, New Jersey at the wholesale value and Maryland at the commodity portion, all below the in-year credit, and New Jersey does not permit a system larger than the customer's annual needs at all. Each of those provisions is recorded in
netmetering/state.jsonwith its source; none is modelled, so the site does not publish figures for a deliberately oversized system. - Price per watt by system size. One installed price is applied at every size. Real small systems cost more per watt, because permitting, design and a day of labour do not shrink with the array, so figures for unusually small systems are optimistic.
- Time-of-use rates. A single average $/kWh is used. Under TOU tariffs the value of exported and self-used energy differs by hour.
- Merchant SREC markets. Where certificates trade at a market price rather than a fixed contract rate — Pennsylvania, for instance — there is no single value to record, so nothing is paid out. Fixed-rate contracts, such as those in Illinois and New Jersey, are modelled, on their own term and payment schedule.
- Roof specifics. Orientation, shading and pitch beyond the PVWatts reference assumptions.
- Home value, insurance, and property tax effects.
- Loan fees and dealer fees, which in practice are often folded into the quoted price.
- Discounting. The 25-year net is a plain sum, not a present value. The IRR figure is there for readers who want the time value of money.
- Utility rate structure changes, including the grid-access and demand charges some utilities have introduced for solar customers.