Cost Models and AI Quote Analysis for UK Solar and Battery
Most advice on doing a solar panel quote check in the UK stops at “get three quotes and compare.” That is useless advice, because the three quotes will describe three different systems, using three different generation estimates, three different savings methodologies, and three different definitions of what is included. Comparing the bottom-line prices tells you almost nothing. What you actually need is a normalisation step, a small number of ratio checks that expose an inflated saving in about ninety seconds, and then a model you build yourself from your own half-hourly data.
Large language models are extremely good at the first part and dangerous at the second. They will parse a 14-page PDF quote into a clean schema, spot that page 9 contradicts page 3, and write you a working pvlib script. They will also invent an MCS zone factor, misremember a panel’s Pmax by 15W, and confidently assert a Smart Export Guarantee rate that hasn’t existed for two years. The whole discipline here is knowing which job you are handing over.
The Three Numbers That Kill Most Quotes
Before any tooling, run the ratios. Every quote should be reducible to these, and if it can’t be, that itself is the finding.
Cost per kWp, PV only. You have to force the battery cost out of the bundle to get this. As of late 2026, a straightforward two-string install on an easy pitched roof with tile hooks lands around £950 to £1,300 per kWp including scaffolding and VAT. Above £1,500/kWp you are paying for something specific (SolarEdge optimisers on every module, a difficult slate roof, in-roof mounting, a long cable run) and the quote should say what.
Cost per kWh of usable battery capacity. Hybrid-inverter AC/DC systems sit around £400 to £550 per kWh installed for the mainstream units (a 9.5 kWh GivEnergy Giv-Bat or a 15.5 kWh Fogstar Energy stack). A Powerwall 3 at 13.5 kWh usually works out nearer £650 to £700 per kWh, which can still be defensible given the 11.5 kW integrated inverter, but the quote needs to earn it.
Generation in kWh per kWp per year. This is the single fastest sniff test in the whole exercise. Divide the quote’s stated annual generation by the array size. A south-facing 35° roof in the South East with no shading gets somewhere near 950 to 1,030 kWh/kWp. The same roof in Greater Manchester is closer to 900. An east-west split array drops to 780 to 850. Anything above 1,050 in the UK is either an error or a deliberately optimistic irradiance source, and anything above 1,100 is not physically available on a domestic roof here.
Here is what three real quotes look like once flattened into comparable form:
Quote A Quote B Quote C
Array 4.40 kWp 5.28 kWp 4.62 kWp
(10 x 440W) (12 x 440W) (11 x 420W)
Inverter GivEnergy Gen3 Solis S6 3.6kW SolarEdge SE4000H
5.0kW hybrid + 11 optimisers
Battery 9.5 kWh none 13.5 kWh
Roof aspect S, 35 deg S 30 / E 30 S, 40 deg, 2 dormers
Total inc VAT £9,850 £5,400 £14,200
PV subtotal £5,650 £5,400 £4,800 (derived)
Battery subtotal £4,200 - £9,400 (derived)
£/kWp (PV only) £1,284 £1,023 £1,039
£/kWh (battery) £442 - £696
Stated annual gen 4,180 kWh 4,950 kWh 3,900 kWh
kWh/kWp 950 938 844
Stated yr-1 saving £1,020 £890 £1,240
Implied value per
kWh generated 24.4p 18.0p 31.8p
That last row is the one to stare at. Divide claimed annual saving by claimed annual generation and you get the blended value the installer is implicitly assigning to every kilowatt-hour your roof makes. Quote C is claiming 31.8p per kWh generated while the household’s import rate is 27p. That is arithmetically impossible from solar alone, even at 100% self-consumption. Either they have silently baked in overnight battery arbitrage (which is legitimate but must be stated, because it depends on a tariff you may not be able to get), or the savings figure is decorative.
Quote B has the opposite tell. Its 938 kWh/kWp is being claimed for a roof that is half east-facing, where the honest blended figure is around 850. Twelve panels on that roof produce roughly 4,490 kWh, not 4,950, and the 3.6 kW inverter on a 5.28 kWp array gives a DC:AC ratio of 1.47, which will clip a further 3% to 5% of summer peaks.
Extraction Is the Job You Should Delegate
Solar quotes are deliberately hard to compare. Some itemise scaffolding, some fold it in. Some quote ex-VAT for the battery. Some show a “was £12,400, now £9,850” discount that expires Friday. Reading four of these carefully takes an hour and you will still miss things.
This is exactly the shape of task where an LLM earns its keep, provided you constrain the output. Upload the PDFs to Claude or ChatGPT and give it a fixed schema with an explicit “not stated” value, so absence becomes visible data rather than a silent gap:
Extract each attached quote into this exact JSON schema. Use the string
"NOT_STATED" for anything absent. Do not infer, estimate, or fill gaps
from typical values. Quote page numbers for every field.
{
"installer": {"name":..., "mcs_number":..., "recc_or_hies":...,
"trustmark":..., "companies_house_no":...},
"price": {"total_inc_vat":..., "vat_treatment":...,
"deposit_required":..., "deposit_pct":...,
"payment_schedule":..., "discount_expiry_stated":...},
"included": {"scaffolding":..., "dno_application":...,
"bird_protection":..., "generation_meter":...,
"epc_update":..., "roof_survey_type":...,
"making_good":...},
"array": {"panel_make_model":..., "panel_watts":..., "count":...,
"total_kwp":..., "roof_planes":[{"azimuth":...,"pitch":...,
"panels":...}], "shading_factor_used":...},
"inverter": {"make_model":..., "ac_kw":..., "dc_ac_ratio":...,
"warranty_years":...},
"battery": {"make_model":..., "nameplate_kwh":..., "usable_kwh":...,
"warranty_years":..., "warranty_cycles":...,
"warranty_throughput_mwh":..., "end_of_warranty_capacity_pct":...},
"performance": {"annual_generation_kwh":..., "mcs_methodology_stated":...,
"yr1_saving_claimed":..., "self_consumption_pct_assumed":...,
"import_rate_assumed":..., "export_rate_assumed":...,
"price_inflation_assumed":..., "degradation_assumed":...},
"warranty": {"workmanship_years":..., "backed_by":...,
"insurance_backed_guarantee":...}
}
Then output a single markdown table of all quotes side by side, one row
per leaf field, with a final column flagging fields where quotes differ
in a way that makes prices non-comparable.
The fields that come back as NOT_STATED are the finding. When self_consumption_pct_assumed is absent from all four quotes but each one claims a saving, you have four black boxes, and the next email you write asks each installer for that one number. In my experience roughly half will tell you 70%, which is the giveaway: 70% self-consumption without a battery does not happen in a UK house unless somebody is home all day running a heat pump.
Never Let the Model Supply a Number
The failure mode that ruins amateur AI-assisted modelling is asking the chatbot to do the modelling. Ask “what’s the payback on a 4.4 kWp system with a 9.5 kWh battery in Bristol” and you will get a beautifully formatted answer built on invented inputs: a plausible-looking generation figure, a SEG rate from 2023, a self-consumption percentage lifted from a US study, and arithmetic that drifts by a few percent because it’s being done token by token.
Use a source-tagging rule instead. Every input in your model carries a provenance tag, and the model is not allowed to produce a value for any of them:
| Input | Allowed source |
|---|---|
| Annual generation, per roof plane | PVGIS-SARAH3 hourly run, or pvlib with a named TMY |
| Half-hourly consumption | Your own DCC data via n3rgy or Hildebrand Glowmarkt, or your supplier’s CSV export |
| Import and export unit rates | Your actual bill and the actual tariff T&Cs |
| Panel Pmax, temp coefficient, degradation | The manufacturer datasheet PDF you downloaded |
| Battery usable capacity, round-trip efficiency, warranty throughput | The battery datasheet and warranty document |
| Prices, inclusions, DNO status | The quote itself |
What the LLM does is write the code that consumes those inputs. That is a completely different reliability profile: you can read a 40-line pandas script and check it, and you can unit-test it against a month you already know the answer to.
For the generation side, PVGIS is free, is the reference most UK analysts fall back on, and will give you hourly output. Run each roof plane separately. A 4.4 kWp crystalline-silicon array in London at azimuth 0 (PVGIS treats 0 as south), slope 35°, 14% system loss, returns about 4,300 kWh per year. Rerun the same array at azimuth −90 for a true east face and it falls to roughly 3,450 kWh. That 20% haircut is the difference between a seven-year payback and a ten-year one, and it is the number installers quietly average away when a roof has two faces.
The Self-Consumption Calculation You Have to Do Yourself
Annual totals cannot tell you what a solar system is worth, because the value depends entirely on whether generation and consumption happen in the same half hour. The only honest way to get this is overlap arithmetic on time series:
import pandas as pd
# load: index = half-hourly timestamps, Europe/London, one year
load = pd.read_csv("n3rgy_consumption.csv", index_col=0, parse_dates=True)["kwh"]
gen = pd.read_csv("pvgis_hourly_4p4kwp.csv", index_col=0, parse_dates=True)["kwh"]
gen = gen.resample("30min").ffill() / 2 # hourly kWh -> half-hourly
df = pd.concat([load.rename("load"), gen.rename("gen")], axis=1).dropna()
df["self"] = df[["load","gen"]].min(axis=1)
df["export"] = df["gen"] - df["self"]
df["import"] = df["load"] - df["self"]
IMPORT_RATE, EXPORT_RATE = 0.27, 0.15
print(f"generation {df.gen.sum():>8.0f} kWh")
print(f"self-consumed {df.self.sum():>8.0f} kWh ({df.self.sum()/df.gen.sum():.1%})")
print(f"exported {df.export.sum():>8.0f} kWh")
print(f"value £{df.self.sum()*IMPORT_RATE + df.export.sum()*EXPORT_RATE:>7.0f}")
Running that on a real household (3,900 kWh annual consumption, two adults out weekdays, 4.4 kWp south-facing) gives:
generation 4148 kWh
self-consumed 1412 kWh (34.0%)
exported 2736 kWh
value £ 792
Against Quote A’s claimed £1,020, that is a £228 shortfall in year one. On a £5,650 PV subtotal the simple payback moves from 5.5 years to 7.1 years. Not a catastrophe, but not what was sold.
Now add the battery to the same time series. Simulating a 9.5 kWh unit with 8.9 kWh usable and 90% round-trip efficiency lifts self-consumption to about 2,900 kWh (70%) and cuts export to 1,250 kWh. Total value becomes £783 + £188 = £971. The battery therefore adds £179 a year of solar-shifting value, for £4,200. That is a 23-year payback on a unit warranted for ten, and it is the single most common place where household solar economics get misrepresented.
Batteries in the UK pay for themselves through tariff arbitrage, not through storing sunshine. On Intelligent Octopus Go at roughly 7p overnight, charging 9.5 kWh costs £0.67 and displaces about 8.5 kWh of 27p import worth £2.31, netting £1.64 per cycle. You only get those cycles in the months when solar hasn’t already filled the battery, call it 200 nights, so £328 a year, and only if your daily consumption can actually absorb 8.5 kWh. Combine that with the £179 of solar shifting and you get £507 a year against £4,200, which is an 8.3-year payback and a genuinely sound purchase. Note what changed: the justification is now a tariff, and tariffs get withdrawn.
Check the warranty against your own usage plan before you commit. Two hundred grid cycles plus roughly 150 solar cycles is 350 full-equivalent cycles a year. A 4,000-cycle warranty is then an 11-year warranty in name and a 10-year one in practice, so build replacement into the model rather than treating year 11 onwards as free money. Details on how to structure that properly, including degradation curves, inverter replacement at year 12 or 13 (£900 to £1,400 fitted), and why discounting matters more than most DIY spreadsheets admit, are in Building a Payback Model That Survives Scrutiny.
Prompts That Find Contradictions
Once you have the extraction table and your own model, the LLM has a second useful job: adversarial reading. It is good at noticing that two numbers in the same document can’t both be true.
Attached: (1) the four quote PDFs, (2) my extraction table,
(3) my model output CSV (generation, self-consumption, export,
value, by month).
For each quote, list every internal contradiction and every
disagreement with my model. For each item give:
- the two figures that conflict, with page numbers
- which one you believe and why
- the £/year impact on year-one saving
- the exact question I should email the installer
Rules: quote no figure that is not in the attachments. If you need
a value I have not supplied, say "need: <value>" instead of
estimating it. Do not summarise. Do not tell me whether to buy.
The output from a run like that, on real quotes, tends to surface things such as: a claimed generation figure on the summary page that is 9% higher than the MCS performance estimate on the annex page; a saving calculated at an import rate of 32p when the household’s actual rate is 27p; a 5.28 kWp array behind a 3.68 kW inverter with no mention of clipping; a “25-year warranty” that turns out to be the panel manufacturer’s product warranty, with installer workmanship covered for two years and no insurance-backed guarantee named.
Also worth checking, because it is cheap: a deposit above 25% of contract value is outside the RECC consumer code, and an in-home sale carries a 14-day cancellation right. A quote with a same-day discount deadline is, by construction, trying to run down a clock you legally control.
Where the Model Will Lie to You
Keep a list of the specific hallucinations this domain produces, because they recur.
Export tariff rates are the worst offender. Ask an LLM what Octopus pays for export and you may get a figure that was correct eighteen months ago, presented with no hedge. Always read it off the current tariff page yourself.
Datasheet specifications drift by small, dangerous amounts. A model will give you 445W for a panel that is 440W, or a temperature coefficient of −0.30%/°C for a module that is −0.34%/°C. Five watts across twelve panels is 60W of array, which is inside the noise; a wrong temperature coefficient changes your summer yield estimate by a percent or two. Download the PDF and have the model read it rather than recall it.
MCS methodology gets fabricated wholesale. There are postcode zone tables and shading factor tables behind the MCS 037 performance estimate, and an LLM asked for a specific zone’s kWh/kWp figure will produce a number that looks exactly like a real one. Use PVGIS as your independent check and treat the installer’s MCS figure as a claim to be verified, not a source.
Regulatory thresholds are near misses. Expect confusion between the G98 notification limit for small connections and the G99 application needed above it, and expect uncertainty about which VAT rate applies to a standalone battery retrofit versus a solar-plus-battery install. These are all published; check them rather than asking.
Closing the Loop After Installation
The model you built during the quote check is the same model you use to catch a fault three years later. Export your inverter’s monthly generation (GivEnergy, SolarEdge and Solis all expose an API; Enphase’s Envoy has a local endpoint; Home Assistant will happily log any of them to InfluxDB), and compare against the PVGIS monthly profile you already have.
Set a tolerance before you start looking, or you will chase weather. Weather-normalise using actual irradiance for the month from Open-Meteo or Solcast, then flag anything more than about 12% below expectation. A single string dropping out shows up as roughly a 50% shortfall and is obvious. What the tolerance is really for is the slow stuff: one optimiser offline, a module with a cracked cell, bird fouling on the bottom row, a hedge that has grown 400mm since the survey. Those take a percent or two a year and nobody notices them from the app’s cheerful monthly total.
One household I worked through this with found their October generation running 18% light for two consecutive years while August was fine. The cause was a neighbour’s leylandii clipping the lower string for ninety minutes either side of solar noon once the sun angle dropped below about 20°, which no survey in June would ever have caught, and which the original shading factor had not modelled. The fix was a conversation over a fence. Finding it required having written down, in advance, what October was supposed to look like.
In this section
The supporting pages under this subject.