This is a screening tool. It answers "is solar worth looking into here, roughly, and does a battery help?" in a few seconds, in your browser, with nothing hidden. It is not an engineering study and it is not a quote. Everything below is either a measurement someone else made and we look up, or a piece of arithmetic described here in full.

Where the data comes from

Two outside sources, both fetched once, offline, and committed as static files. The deployed page never calls anything at runtime.

Sunlight and temperature — PVGIS v5.2 (European Commission JRC)
For each of 1 210 towns and cities across the EU-27, the UK, Switzerland and Norway, three things were fetched at that town's own coordinates: its annual yield for a 35°-tilt south-facing system including 14% system losses, the twelve monthly fractions that yield is spread over, and twelve monthly mean air temperatures (2016–2020 average). Yield and seasonal shape come from the PVcalc endpoint, temperatures from MRcalc.
Place names — GeoNames cities15000 (CC BY 4.0)
Up to the fifty most-populous places per country, under their local names.

PVGIS sends no cross-origin headers, so a static site genuinely cannot call it from a browser — the lookup table is not a shortcut, it is the only way this can work without a server. Refreshing the data means re-running the offline fetch and committing the result.

Why every town carries its own measurements

Yield, seasonal shape and temperature are all read at the town's own coordinates, never interpolated from surrounding points. In mountainous country that distinction is decisive: a valley city and a point a few kilometres away up the slope can differ by 8 K in January. An error that size does not merely dent a heat pump estimate, it inverts it, and it can silently zero out the cooling demand of a city with real summers. Anything that depends on temperature therefore has to be measured where people actually live rather than nearby.

One quantity is interpolated rather than measured per town: the roof orientation correction. How much a tilted, rotated roof gains or loses against a 35° south reference depends on the sun's angle, which varies with latitude rather than with local terrain, so it is safe to interpolate. Correction factors are held for reference latitude bands 40°, 50° and 60° N; the factor for your roof is interpolated bilinearly across tilt and azimuth within each band, then linearly between the two bands your town falls between.

The band factors themselves are measured rather than modelled, and each band averages four land points spread across its longitudes — Spain, Portugal, Italy and Greece at 40° N; Germany, Poland, Czechia and Belgium at 50° N; Norway, Sweden, Finland and Estonia at 60° N — with the local horizon switched off. The averaging is what makes the table honest. Because these are ratios, a sample point's overall sunniness cancels out but its asymmetry does not: a single point outside Oslo has hills to the east, and a table built there would hand one hillside's skyline to every roof in Scandinavia.

From a roof to an hourly production series

System size

If you give a roof area rather than a system size, it is converted at 0.2 kWp per m² of module (roughly a modern panel), with 80% of the area you stated treated as actually usable — chimneys, setbacks and awkward corners. Give the kWp directly and neither assumption applies.

Annual total

annual kWh = kWp × (the town's measured kWh/kWp at 35° south) × (orientation factor) × (your performance ratio ÷ 0.86)

0.86 is the performance ratio behind the PVGIS figure — its default 14% system loss. Raising or lowering the performance ratio in the advanced settings scales the result against that reference rather than replacing it.

Hourly shape

The annual total is then spread over 8 760 hours in three steps:

  1. Within a day, closed-form solar geometry. Solar declination from the day of the year, hour angle from the hour (solar noon assumed at 12:00 local — the equation of time and your longitude within the time zone are below the resolution this tool claims), then the cosine of the incidence angle on your tilted, rotated plane. A fixed 18% of the light is treated as diffuse and follows the sun's elevation rather than the angle onto the panel, so an east-facing roof still produces something at noon.
  2. Across the year, the measured envelope. Each month's geometric total is rescaled so the twelve monthly totals match the fractions PVGIS measured for your town. The shape within each month is left as geometry made it.
  3. Finally, anchored on the measured annual total from the step above.

Step 2 is not a refinement, it is load-bearing. Pure geometry gives a June-to-January production ratio of about 1.6 more or less everywhere. The measured ratios run from about 1.5 in Madrid to 3.6 in central Europe to 8.8 in Oslo — cloud, haze and horizon, none of which geometry knows about. Without the measured envelope the tool would overstate winter self-sufficiency everywhere north of the Alps, and flatter the battery while doing it.

The household load profile

Your annual consumption — the figure from your bill — is spread over the year with a standard-load-profile-style residential shape: a morning peak, a daytime trough, a larger evening peak, a flatter and later weekend pattern, and a mild seasonal swing (about 1.18× in December against 0.85× in July) for lighting and indoor time. Weekdays and weekends alternate in the usual 5:2 mix; which calendar day is a Monday does not matter at this resolution.

The shape is relative and the whole series is then scaled so that it sums to exactly the annual figure you gave. That is deliberate: the annual number is something you actually know, and the shape is something nobody knows without a meter.

This is the model's weakest input and it is worth being blunt about it. It has no household in it — no shift work, no woodstove, no swimming pool, no one at home on Tuesdays. Two houses with identical annual consumption and very different daily rhythms get identical answers here, and in reality they would not.

The temperature series

Heat pumps and air conditioners respond to temperature, and a heat pump's efficiency depends on how cold it is at the moment it runs, so twelve monthly means are not enough. An hourly series is reconstructed from them: the monthly means are anchored at the middle of each month and interpolated between with a cosine easing (so turning points round off instead of kinking, and December wraps into January without a step), then a fixed ±5 K day/night swing peaking at 15:00 is laid on top.

What comes out is a smooth climatology, not weather. There are no cold snaps, no heatwaves, no still grey fortnights. It is adequate for annual energy — which is all this tool reports — and it must never be used to size anything for a peak day.

Heat pump, electric car, air conditioning

Each of the three is an independent 8 760-hour series added on top of your base load, so switching two on is just an element-wise sum. Crucially, they are all evaluated against the same panels and the same battery you chose — nothing is resized when you flip a toggle. That is what makes "adding an electric car does this to your self-sufficiency" a statement about the car rather than about a different system.

Heat pump

Annual heat demand is floor area × an intensity for the building's vintage:

Before 1980, little insulation
160 kWh/m²/year
1980–2000
105 kWh/m²/year
2000–2010
70 kWh/m²/year
After 2010, or fully renovated
45 kWh/m²/year
Passive house / Minergie-P
25 kWh/m²/year

That demand is distributed over the year by heating degree days below a 15 °C base — the gap up to a ~20 °C indoor target is assumed to be covered by sunlight through the windows and by the people and appliances inside.

Whether a day needs heat at all is decided from that day's mean temperature, the standard degree-day convention. Testing each hour on its own instead would put the heat pump to work on a cool July night, which no building with any thermal mass does. Within a heating day the heat is weighted towards the colder hours, with a small floor so that a mild day spreads its demand instead of collapsing it onto one hour.

Electricity is then that hour's heat divided by that hour's coefficient of performance:

COP = 0.42 × (Tsupply + 273.15) ÷ max(5, Tsupply − Toutdoor), clamped to 1.6–5.5

— that is, 42% of the theoretical Carnot limit, which is about what a real machine achieves. Evaluating the COP hour by hour rather than using one seasonal figure is the whole point: a fixed COP would flatter exactly those midwinter hours when the machine works hardest and the sun is weakest. Your choice of heat distribution sets Tsupply — underfloor heating 35 °C, mixed 45 °C, old radiators 55 °C — and it matters a great deal.

Space heating only. Domestic hot water is not modelled, so a household running its hot water off the same heat pump will use more than this shows.

Electric car

Annual energy is kilometres × consumption per 100 km, plus 10% charging losses, because the meter sees more than the battery receives. That energy is spread over every day of the year with one of two shapes:

Both move exactly the same annual energy; only the timing differs, and the timing is the entire question for solar. The default is deliberately the unflattering one. Defaulting to solar-following charging would quietly inflate self-consumption for every user, including the majority whose car is not at home on a weekday afternoon.

Air conditioning

Cooling demand is floor area × cooling degree hours above a 22 °C base × 0.00125 kWh per m² per degree-hour, divided by the SEER you set. Unlike heating this is evaluated hourly rather than on the daily mean, because cooling genuinely does respond to a hot afternoon in a way heating does not respond to a cold hour.

Of the three extras this is the only one that improves the solar match: it wants power exactly when the panels have it.

How the battery is operated

Every hour of the year, in order:

  1. Production meets whatever load is happening at that moment, directly.
  2. Any surplus charges the battery — limited by the room left in it and by a power limit of 0.5 C (an 8 kWh battery charges at up to 4 kW).
  3. Any remaining deficit is drawn from the battery, under the same power limit and whatever charge it holds.
  4. What is left over is exported; what is still missing is imported.

Round-trip efficiency is split evenly over the two conversions (its square root applied on the way in and again on the way out), and the capacity you enter is treated as usable capacity, not nameplate. The battery starts the year empty. Full cycles are reported as total energy stored ÷ usable capacity.

This rule is greedy and myopic: it knows nothing about the next hour. It never holds space back for an afternoon it can see coming, never buys cheap night electricity to sell or use later, never trades against a time-of-use tariff, and never manages its own degradation. A real optimiser beats it, and the gap between this rule and a solved dispatch is the entire reason the companion solver-based tool exists. What this rule gives you instead is a number you can follow line by line, in a page that loads in a second and costs nothing to run.

Money: bills, payback and NPV

What is being bought

panels = kWp × your price per kWp   ·   battery = kWh × your price per kWh

What a year is worth

year's value = (solar you used yourself × retail price) + (solar you exported × feed-in tariff) − maintenance

Maintenance is 1% of the system cost per year for the panels, and zero for the battery on its own. Panel output falls 0.5% per year. If you set a tariff escalation, both prices compound at that rate. Later years reuse the self-consumption and self-sufficiency rates from the simulated year rather than re-running the hourly dispatch annually — across a 0.5%/year decline that drift is far smaller than the uncertainty already sitting in the synthetic load profile.

Your bill

bill = (imported × retail price) − (exported × feed-in tariff)

Standing charges and any fixed monthly fee are not in it, so compare the difference between the "before" and "after" bills rather than either figure against your actual bill.

Payback and NPV

Payback is the simple, undiscounted one: the cumulative cash position starts at minus the purchase price and the year it crosses zero is reported. The payback chart plots exactly that line, deliberately undiscounted — a discounted curve crossing somewhere other than the payback figure printed beside it would read as a contradiction.

NPV is the discounted verdict: each year's value divided by (1 + discount rate) raised to that year, summed over the system's life, minus the purchase price. Cash flows are treated as arriving at the end of each year. It is the headline figure because it is the one that answers "am I better off than if I had done something else with the money".

The battery is judged incrementally

This is the part most worth understanding. The battery is not compared against having no solar at all — it is compared against the same panels without a battery. Its entire earnings are the energy it moves out of the export column and into the self-consumption column:

battery's year = (extra energy used on site × retail price) − (export revenue given up × feed-in tariff)

So a battery's case is driven by the spread between what you pay for electricity and what you are paid for exporting it, not by the retail price. Where export is paid well, batteries struggle here; where export is paid badly, they do well. The battery is evaluated over its own lifetime, which is usually shorter than the panels'. The headline figure on the results page is the panels' NPV plus the battery's.

What is not costed

Only the panels and the battery. Buying and installing a heat pump, an electric car or an air conditioner appears in no figure anywhere in this tool. Their effect on your electricity bill is fully modelled; their price is not. Nor are grants, subsidies, tax relief, or the running cost of whatever the heat pump replaces — if you are switching from gas or oil, the saving on that fuel is real and it is not here.

Every amount stays in the currency you entered the tariffs in. No exchange rates are applied anywhere.

What this model cannot do

All of these are consequences of the design, not bugs waiting to be tuned away. They are the price of a tool that runs entirely in your browser in a second.

If a figure here is going to inform a purchase, treat it as the thing that tells you whether the conversation is worth having — then have the conversation with someone who will come and look at the roof.

The calculation is a handful of plain, dependency-free JavaScript files, and every constant quoted above is in them: js/calc/pv.js (yield and hourly production), js/calc/climate.js (temperature), js/data/load-profiles.js (household load and the three extras), js/calc/dispatch.js (the battery rule) and js/calc/economics.js (NPV, payback, the incremental battery case).