ChargeCliff
How it works

Why your 30 minute charge took an hour.

Every number on this site comes out of about a dozen equations. This page walks through all of them, plain English first and the actual math second, so you can decide for yourself whether the tool is worth trusting. There's a section near the bottom on what it gets wrong, because a page like this that only lists the confident parts isn't much use to anyone.

The short version

If you read nothing else:

Batteries don't fill at a steady rate

They take a lot of power when they're nearly empty and much less as they fill. The advertised kW number is a brief peak, not a speed.

Cold makes it dramatically worse

Below freezing, some cars cut their intake nearly in half before they'll accept a single amp.

Highway speed and cabin heat eat range

Air resistance climbs with the square of speed, and the cabin heater draws real power that the EPA cycle mostly doesn't capture.

Paying by the minute punishes slow cars

Same plan, same plug, and a slow-charging car can pay four times as much per kWh as a fast one.

Why you can't just divide

Say you drive a Lexus RZ 450e. It holds 64 kWh, you're going from 10% to 80%, so you need about 44.8 kWh. You pull into a 150 kW stall. That's roughly 18 minutes of charging, right?

44.8 kWh150 kW × 60 = 17.9 minutes the tempting arithmetic

It's 31 minutes on a mild day. If it's 30°F out, it's 56 minutes.

The problem is that 150 kW is a ceiling, not a speed. Your car touches that number briefly while the battery is nearly empty, then backs off, and keeps backing off for the rest of the session. By 80% the RZ is pulling around 28 kW. Dividing capacity by charger rating quietly assumes you got the peak the whole time, which is why every tool that does it tells you a number you never see.

The model integrates reciprocal power within each one-percent interval, splitting at curve breakpoints and charger limits. For a linear segment, time is proportional to ln(end power / start power) divided by the power change. Reported power is the energy/time average; battery health scales the energy in each interval.

time = Σ 0.01 × pack kWhaverage power over this % × 60
physics/charging.py · simulate_charge_session()  ·  summed from your starting % to your target

Each vehicle carries its own list of measured breakpoints (at 10% it pulls this much, at 40% this much) and the model reads power straight off that list, interpolating in a straight line between the points. Real curves step like that, since a BMS derate is an on/off decision rather than a smooth slope, so a handful of honest breakpoints beats a polished curve fit that implies precision nobody measured.

Cold batteries charge slowly

Cold electrolyte is thicker and the cells fight the current harder. Push full power into a cold pack anyway and you start plating metallic lithium onto the anode, which permanently eats capacity and, in the bad cases, eventually shorts the cell. No manufacturer is willing to risk that, so the BMS limits current until the pack warms up.

The model treats that as a ceiling on power, set by the pack's own temperature: a share of the car's peak, from a floor at freezing to no limit at 68°F. With the default floor:

Pack temperature20°F32°F40°F50°F60°F68°F
Ceiling, share of peak56%56%66%78%90%100%
ceiling = floor × peakpack at or below 32°F
ceiling = ( floor + Tpack − 3236 × (1 − floor) ) × peak between 32 and 68°F
no ceilingat or above 68°F
physics/charging.py · cold_gate_multiplier()  ·  floor defaults to 0.56, set per vehicle

Two things follow that a flat multiplier missed. First, the ceiling only bites where the car would otherwise draw more: past 80% an RZ takes under 30 kW cold or warm, so the cold costs nothing there. Second, charging warms the pack. About 3.5% of the energy going in becomes heat in the cells, and a pack holds roughly 1 Wh per °F for each kWh of capacity, so a frozen RZ starting at 30°F ends a 10 to 80% session at about 53°F, and the ceiling lifts as it goes.

The RZ's floor is its own: 22% of peak, about 31 kW. That is what it takes for the model to match the published cold benchmark, 10 to 80% at 30°F in 50 to 60 minutes. It gives 55.8 minutes, 35 of them held down by the cold. The same session on a warmed pack takes 32.8.

The rate in each 1% slice is the lowest of four ceilings:

power = min ( stall kW , stall amps × pack volts , curve × age , cold ceiling )
physics/charging.py · simulate_charge_session()

The second term is why "150 kW" means less than it says. A stall has a current limit as well as a power rating; the model takes it as the rating reached at 400 V unless you enter the amps on the label. A 400 V pack sits between about 320 and 400 V, so an RZ 300e on a 150 kW stall peaks at 132 kW, not its own 143. An 800 V Ioniq 5 on the same stall gets the full 150. The session chart colours every slice by which ceiling set it.

Selecting a warm pack starts the session at 77°F. That is an assumption, not a measurement: the model does not know how long your preconditioning ran.

When to walk away

Every minute at a charger buys range, but the later minutes buy less. The useful question on a road trip is not "is this still fast?" but "would the trip be quicker if I left now and stopped again later?" That has an exact answer, the one foragers and economists know as the marginal value theorem.

A stop costs a fixed overhead (pull off, park, plug in, pay) plus charging time, and returns miles. Over a long trip, total time is least when each stop ends where its average miles per minute, overhead included, peaks. Up to that point the next percent adds miles faster than the stop has averaged; after it, slower.

unplug at the SoC that maximises  miles added since arrivingoverhead + minutes charging
physics/charging.py · _trip_optimal_unplug()

Here is the RZ 450e from 10% on a 150 kW stall on a mild day, with a 10 minute overhead, at its 70 mph efficiency of 2.35 miles per kWh:

ChargePullingNext minute addsStop has averaged
30%128.9 kW5.05 mi1.87 mi/min
45%130.4 kW5.11 mi2.57 mi/min
50%70.4 kW2.76 mi2.67 mi/min
60%62.1 kW2.43 mi2.63 mi/min
80%31.0 kW1.21 mi2.46 mi/min
90%14.7 kW0.58 mi1.95 mi/min

The average peaks at 50%, the cliff itself: 12.6 minutes of charging against 32.8 to reach 80%. Until October 2026 this page used a rule of thumb (under a mile a minute, or 60% off peak) that told RZ drivers to sit through the cliff to 75%.

The overhead matters, and not smoothly. For the same session:

Overhead0 min5 min10 min15 min20 min30 min
Unplug at45%48%50%75%76%78%

Past about 12 minutes of overhead the long plateau after the cliff becomes worth charging through, and the answer jumps to the second cliff at 75%. On a charger under 25 kW there is no answer at all: that's AC charging, where the charger sets the pace from start to finish.

The theorem assumes the next charger is within reach and no worse than this one. If it isn't, charge for the distance; the model does not plan a route.

Speed, cold, and the heater

The EPA test cycle averages under 50 mph with the climate control mostly idle, which is not what an interstate looks like. The model builds highway consumption from the forces involved, for each car.

Road load

Three things take energy at a steady speed on level ground: rolling the tyres (flat with speed), pushing air aside (rising with the square of speed), and the electrics that run whether you move or not (spread over fewer hours the faster you go).

Wh per mile = k × [ Crr m g + ½ ρ Cd A v2η + Pauxv ]
physics/highway.py · road_load()  ·  Crr 0.009, η 0.85, Paux 400 W, m = curb + 150 kg

Mass, drag coefficient and frontal area are each car's own. So is the share of its load that is drag, which is why speed costs some cars much more than others:

Energy per mile vs 55 mphDrag share at 7065 mph70 mph75 mph80 mph
Mercedes EQS (Cd 0.20)55%1.16×1.26×1.36×1.46×
Lexus RZ 450e (Cd 0.29)68%1.22×1.34×1.47×1.62×
GMC Hummer EV (Cd 0.50)74%1.25×1.39×1.54×1.70×

The one fitted number is k. A car with a published road test gets the k that reproduces it; every other car gets the fleet's typical k. How far that lands from the truth is measured below, not assumed.

Cold air, thin air

Air density ρ comes from the ideal gas law and the barometric formula: 0°F air is 15% denser than 70°F air, and Denver's is 17% thinner than the coast's. On top of that, cold cells, stiff tyres and thick lubricants cost up to 20% more, a separate penalty of 3% per 10°F below 65°F.

The heater

A resistance cabin heater pulls two to three kW, and unlike a gas car you don't get that heat for free off the engine. But it isn't a fixed load either: it runs flat out in a hard freeze, cycles at part duty on a cool day, and is off on a mild one. So the model scales the rated wattage by a duty fraction before spreading it over distance:

duty = 1 at 20°F, falling to 0 at 65°F heating
duty = 0 at 75°F, rising to 0.35 at 100°F air conditioning
Wh per mile = heater watts × dutyyour mph
physics/highway.py · hvac_duty(), hvac_consumption_wh_per_mile()

Air conditioning tops out at about a third of the heater's draw because a compressor moves heat rather than making it. Between 65°F and 75°F nothing runs. The heater costs less per mile the faster you drive, since it draws by the hour while you cover ground by the mile; drag wins that argument comfortably.

All of it at once

Same RZ 450e, same battery, four different days. The safe window is 80% down to 10%, since nobody road trips to zero:

ConditionsUsingFull packRealistic range
55 mph, mild317 Wh/mi202 mi141 mi
75 mph, mild468 Wh/mi137 mi96 mi
75 mph, 45°F533 Wh/mi120 mi84 mi
75 mph, 20°F614 Wh/mi104 mi73 mi

141 miles down to 73 between the best and worst row, on a car that never changed. That spread is what strands people who planned around the window sticker.

Older batteries

You don't need an OBD-II dongle for a decent guess at pack health. Two things wear a battery down: time, as lithium gets locked into a layer that grows on the anode, and use, which the odometer stands in for. The layer grows ever more slowly, so calendar fade goes with the square root of age: a fast first year, then a slow decade. That is the shape the large fleet surveys show (Geotab: about 1.8% a year on average; Recurrent: most packs above 90% past 100,000 miles).

health = 1.0 − a × √years − b × miles
nickel packs: a 0.022, b 0.00000045 · LFP: a 0.015, b 0.0000003 · floored at 70%
physics/aging.py · estimate_soh()

A worn pack gets hit twice. It holds less, and its internal resistance has grown, roughly twice as fast as its capacity has faded, so the BMS lets in less power to hold heat and cell voltage down:

power share = 11 + 2 × (1 − health)
physics/charging.py · aged_power_factor()
Age and milesHealth (nickel)Health (LFP)A 150 kW peak becomes
new100%100%150.0 kW
5 years, 60,00092.4%94.8%130.2 kW
8 years, 120,00088.4%92.2%121.7 kW
12 years, 200,00083.4%88.8%112.6 kW

Paying by the minute

Some networks bill by time rather than energy. If your car decides how fast it accepts power and the meter charges by the minute regardless, then a slow car is paying for the privilege of being slow. The rate-trap calculator bills the simulated session one percent at a time, so you can see where.

Here are real cars on the same plan at 40 cents a minute, with gas at $3.30 and a 30 mpg gas car to compare against:

CarTookGotPaidWorks out to
Ioniq 5, 10→80% on a 350 kW stall16.3 min51.8 kWh$6.50$0.126/kWh
RZ 450e, 10→90% on a 150 kW stall51.8 min51.2 kWh$20.72$0.405/kWh
Bolt EUV, 10→80% on its 55 kW cap60.6 min45.5 kWh$24.24$0.533/kWh
Bolt EUV, 10→90% at 30°F98.4 min52.0 kWh$39.34$0.757/kWh

The Bolt paid four times as much per kWh as the Ioniq 5 and went home with less energy. The last row works out to the cost of running a car that gets 12 miles per gallon.

The verdict compares like with like: what a mile costs on this charge against what it costs in the gas car. Under 60% of the gas car's cost per mile is fair, up to parity is elevated, and past parity driving on that charge costs more than driving on gasoline. (Until October 2026 it used fixed $/kWh bands, which called the same session fair in one state and a trap in the next whatever gas cost there.) Prices are per kWh that reached the battery: a DC stall meters about 5% more than the pack keeps.

slice cost = minutes × price per minute (by power tier) + kWh0.95 × price per kWh
physics/billing.py · bill_session()

What this gets wrong

All of the above is a model, and a model is a pile of decisions someone made. Here are the ones I'd push back on if I were reading this page instead of writing it.

Mass and drag don't see drivetrains or tyres

Scored on road tests it never saw, the highway model lands within 9% for the median car and 34% at worst (below). The misses have a pattern: the big pickups come out 20 to 25% pessimistic, the RZ and a Plaid on 21-inch wheels 30% optimistic. Rolling resistance, drivetrain efficiency and the electrical load are fleet constants here, and they plainly differ by car. A car with its own road test is calibrated to it; the rest carry this error and say so.

Frontal areas are estimated

Drag coefficients are published; frontal areas mostly aren't. They are 0.84 × width × height, the usual approximation, which is the weakest input in the drag term.

The heater duty curve is a straight line

Heating ramps from off at 65°F to full at 20°F, cooling from off at 75°F to a third of the heater's draw at 100°F. Real cars have heat pumps, seat heaters, sun load and a driver's own comfort setting, none of which the model knows about.

The pack's heating is two round numbers

3.5% of charging energy as heat and about 1 Wh per °F per kWh of capacity, with no cooling below 68°F and no preconditioning. Real BMS behaviour is stepped and specific to each manufacturer; only the RZ's cold floor is fitted to a cold benchmark.

Ageing ignores most of what matters

Climate, how often the car was DC fast charged, and how long it sat at 100% all drive real degradation. None of them are inputs. The power derate's factor of two is a stated mechanism, not a fit.

The unplug point assumes an equal next charger

It is the right answer when the next stop is in reach and as good as this one. When it isn't, the right answer is "enough to get there", and that needs a route the model doesn't have.

Most of the charge curves are sourced, not measured

Four of the 42 cars are calibrated against a named published dyno or road test. The other 38 come from public charging reports and get flagged as unverified in both the API and the UI.

How I check these numbers

These charging benchmarks are wired into the test suite. Change a curve or a formula in a way that pushes any of them outside its published range and the build fails.

CarScenarioPublishedModel
Lexus RZ 300e10→80%, 72°F, 150 kW32 to 37 min32.8 min
Lexus RZ 450e, cold10→80%, 30°F, 150 kW50 to 60 min55.8 min
Lexus RZ 450e, highway75 mph, 45°F, heat on115 to 125 mi120 mi
Chevy Bolt EUV15→80%, 70°F, 55 kW50 to 55 min56 min
Hyundai Ioniq 510→80%, 72°F, 350 kWabout 18 min16.3 min

The highway model is checked differently, because a car calibrated to its own road test will always match that test. Until October 2026 this page listed fifteen of them "within 1%", which proved nothing. Now each of the 16 published road tests is predicted from the car's mass, drag and frontal area with the fleet factor fitted to the other fifteen, never its own:

Car (source)MeasuredModel, held outError
Chevy Silverado EV RST (InsideEVs)441 mi330 mi−25%
Ford F-150 Lightning ER (InsideEVs)270 mi215 mi−20%
Cadillac Lyriq RWD (InsideEVs)330 mi288 mi−13%
Tesla Cybertruck AWD (InsideEVs)302 mi269 mi−11%
BMW iX xDrive50 (InsideEVs)345 mi322 mi−7%
Chevy Bolt EUV (InsideEVs)231 mi219 mi−5%
Ford Mustang Mach-E AWD ER (InsideEVs)270 mi263 mi−3%
Kia EV6 GT-Line AWD (InsideEVs)245 mi243 mi−1%
Tesla Model 3 Long Range (InsideEVs)310 mi312 mi+1%
Tesla Model Y Long Range, AC on (InsideEVs)276 mi278 mi+1%
Mercedes EQS 450+ (InsideEVs)391 mi407 mi+4%
Porsche Taycan 4 Cross Turismo (InsideEVs)302 mi322 mi+6%
Polestar 2 Long Range (InsideEVs)226 mi255 mi+13%
Hyundai Ioniq 6 AWD (Consumer Reports)265 mi300 mi+13%
Tesla Model S Plaid, 21-inch (InsideEVs)300 mi390 mi+30%
Lexus RZ 450e, 75 mph 45°F (Car and Driver)120 mi160 mi+34%

Median error 9%, mean 12%, 8 of 16 within 10%. That is the error to expect for the 26 cars with no road test, and their pages say so. The test suite pins these bounds and fails if the published file drifts from the engine.

The other 38 charge curves get a different safeguard. Each one records the date it was last checked against real world data, and the build fails if any of them goes 18 months without a look. Manufacturers push charging software updates that change these curves.

Every figure on this page came out of the same engine that answers /api/v1/calculate/*.