EYThe LogEmre Yakut
← all entries
Metering · Systems · deep

How a heating bill is actually calculated

Everyone in the building has their own meter read. Then they see the bill and say “I did not burn that much”. Both are true — because half the bill comes from your meter and half from the building’s total.

binanın toplam gideri — 40.000 ₺ (faturası var, kesin) sabit pay %30daire alanına göretüketim payı %70sayaçların BİRBİRİNE oranına göredaire A1.240 birimdaire B890 birimdaire C2.010 birimısı payı ölçer ENERJİ ölçmez — birimsiz bir oran üretiro oran yalnız AYNI BİNADAKİ diğer ölçerlerle kıyaslanınca anlam kazanırısı duvardan geçer · borular ısıtır · ortak alanlar ısınır → 60 sayacın toplamı ≠ binanın tüketimi

This is the most misunderstood part of the job, so let me start at the beginning.

A building burns 40,000 lira of gas over a winter. That figure is certain: there is an invoice. Now it has to be split across 60 flats.

“Everyone pays for their own meter,” you say. You try it. It does not add up. Understanding why is understanding the whole system.

Why it does not add up

Three reasons, all physical:

  • Heat crosses walls. Even with your radiators shut you take heat from the flats above, below and beside you. Your meter reads zero and your flat is 19 °C.
  • Pipes heat things. The runs from the boiler to the flats lose heat through basements and shafts. That heat appears on nobody’s meter but has been paid for.
  • Common areas warm up. Stairwells, the entrance hall, the shelter. Nobody’s flat, everybody’s cost.

So the sum of 60 meters does not equal the building’s real consumption. Meters do not measure total cost; they measure the flats’ proportion relative to each other.

the basic split
bill = fixed_share + consumption_share

fixed share is the portion from the building’s common cost, distributed by floor area. consumption share follows the ratio between meters. The split is set by regulation; in buildings using heat cost allocators a typical distribution is 30% fixed / 70% consumption.

A heat cost allocator has no unit

The most counter-intuitive thing here: the small box on the radiator does not measure energy.

It measures the temperature difference between the radiator surface and the room, integrated over time. The result has no unit. A flat reading 1,240 means nothing on its own.

It only acquires meaning inside its own building:

flat A : 1,240 units
flat B :   890 units
flat C : 2,010 units
…
total  : 74,500 units

flat A's consumption share = 1,240 / 74,500 = 1.66%

Comparing one building’s number with another’s is therefore meaningless. This is the thing I most often have to explain to residents — and the reason the portal has a “building average” box at all.

The engine’s two phases

It cannot be done in one pass, because knowing a flat’s share requires knowing the building’s total first. So the engine is two-phase:

PHASE 1 — collect
  for each unit:
    reading delta = last_reading − first_reading
    apply corrections   (meter swap, fault, estimation)
  derive the building total

PHASE 2 — distribute
  for each unit:
    consumption share = (unit units / building total) × distributable amount
    fixed share       = (unit area  / building area)  × fixed amount
    apply exemptions, minimum shares and common-area rules
    write the bill lines

Then real life

That is the clean version. On site, this lands on top of it:

situationwhat happens
Meter replaced mid-periodBoth meters’ readings are joined; the chart must show no break
Meter faultyEstimated from the unit’s own history or the building average
Flat emptyNot zero: a minimum share applies (it takes heat from neighbours)
ExemptionA unit exempt from a line is removed from that line’s distribution base
Several boilersEach boiler feeds its own group of blocks; the calculation is built per boiler
Common areasModelled as their own “unit”, then redistributed across all units

None of these is an if in the code — they all live in the database as formula definitions. A site’s bill type describes which lines are distributed over which base. When a new rule arrives, the definition changes, not the code.

The hardest part: back-adjustment

A period closes, bills go out, and then an error surfaces — a meter was misread, a flat was linked to the wrong block.

You cannot recalculate a closed period and reissue its bills; those were paid and booked. What you do instead is a back-adjustment: carry the difference into the next period’s bill as its own line.

the trap here

The adjustment record is anchored to the source period, not the target. The difference is based on that period’s figures. Confuse the two and, when a third correction arrives, you can never again work out which difference was layered on what. This field is marked “do not touch” in the system.

What I learned

Understanding this engine taught me something unexpected about software architecture: separating rule from data is the only way a regulated system survives.

The allocation rules changed several times over five years. The code was never rewritten once — because what changed was never how it is calculated, only at what ratio it is distributed. One is code; the other is a row.

MeteringSystems