Operations — energy balance for a production line
An energy balance over one hour of steady production, written so that the closure error is visible rather than absorbed. Typed FlowScript for supply chains. Keywords: supply chain, logistics, network, supplier.
Make it your own.
// An energy balance over one hour of steady production, written so that
// the closure error is visible rather than absorbed.
//
// The line is a bread plant tunnel oven. Gas and electrical inputs are
// metered; the outputs are the heat that actually goes into the product,
// the flue, the extraction air, the walls, and the heat recovered to the
// proofer. What does not close is 16 kW, or 1.2% of the input, which is
// inside the 5% an accepted balance is normally allowed and is stated
// rather than quietly shared out among the measured streams.
//
// Specific energy is the number the site is actually judged on. It comes
// out at 0.363 kW.h per kilogramme of dough, against a best-practice
// benchmark of 0.310 — a gap of 17%, most of which is sitting in the flue.
energy_balance tunnel_oven {
title: "Tunnel oven energy balance — bread plant line 2"
boundary: "Oven inlet to cooler inlet. The proofer is outside the boundary; heat sent to it counts as an output."
basis: "One hour at steady state, 3600 kg of dough in, 92 C core at exit"
method: "Gas meter and sub-metered electrical panel; flue loss from oxygen and temperature; wall loss from an infrared survey at 24 points"
measured: "2026-01-29, 06:00 to 07:00, mid-run"
throughput: 3600 kg
benchmark_specific_energy: 0.310 kW*h/kg source "Carbon Trust industrial bakery benchmark, plant bread"
}
energy_input gas_burners {
of: tunnel_oven
title: "Natural gas to the burners, net calorific value"
power: 1240 kW
instrument: "Rotary gas meter, corrected for temperature and pressure"
}
energy_input electrical_drives {
of: tunnel_oven
title: "Circulation fans, conveyor drives and extraction"
power: 68 kW
instrument: "Panel sub-meter, one-minute averages"
}
energy_output product_heat {
of: tunnel_oven
title: "Into the product — sensible heat plus water evaporated from the dough"
power: 512 kW
basis: "Mass loss 11.4% at 2257 kJ/kg latent, plus sensible heat to 92 C"
}
energy_output flue_loss {
of: tunnel_oven
title: "Flue gas to atmosphere at 214 C, 6.1% oxygen"
power: 384 kW
basis: "Siegert calculation from flue temperature and oxygen"
}
energy_output extraction_loss {
of: tunnel_oven
title: "Extraction air and end-seal air ingress"
power: 178 kW
basis: "Measured extraction volume and temperature rise"
}
energy_output wall_loss {
of: tunnel_oven
title: "Wall, roof and end losses"
power: 96 kW
basis: "Infrared survey, 24 points, natural convection correlation"
}
energy_output recovered_to_proofer {
of: tunnel_oven
title: "Recovered to the proofer through the flue heat exchanger"
power: 122 kW
basis: "Water-side flow and temperature rise on the recovery circuit"
}
balance_result oven_balance {
of: tunnel_oven
total_input: = sum(kind:energy_input, "power") * 1[kW]
total_output: = sum(kind:energy_output, "power") * 1[kW]
closure_error: = total_input - total_output
closure_percent: = pct(closure_error, total_input)
// Efficiency on the product alone, and again with the recovered heat
// credited — both are quoted in the trade and they differ by nine points.
thermal_efficiency: = pct(product_heat.power, total_input)
efficiency_with_recovery: = pct(product_heat.power + recovered_to_proofer.power, total_input)
flue_share: = pct(flue_loss.power, total_input)
energy_per_hour: = total_input * 1[h]
specific_energy: = energy_per_hour / tunnel_oven.throughput
gap_to_benchmark: = pct(specific_energy - tunnel_oven.benchmark_specific_energy, tunnel_oven.benchmark_specific_energy)
annual_hours: 6200
annual_energy: = total_input * 6200[h]
}
saving_option flue_economiser {
of: oven_balance
label: "Second economiser on the flue, raising recovery to 210 kW"
additional_recovery: 88 kW
gas_displaced_per_hour: = additional_recovery * 1[h]
hours_per_year: 6200
// The proofer cannot take the full recovered duty all year, so the
// derate is a factor in the chain rather than a sentence under it.
// This block used to compute the payback on 88 kW for all 6200 h and
// then caveat that it "assumes 78% utilisation" — a business case that
// reads as though the derate is already in it, when it is not, and so
// prints a payback 22% shorter than the plant can deliver. If the
// seasonal profile is remeasured, change this one number and every
// figure below it moves with it.
heat_utilisation: 0.78 source "Proofer demand profile 2025 — share of production hours able to absorb the full recovered duty"
annual_gas_saved: = additional_recovery * 6200[h] * heat_utilisation
gas_price_per_kwh: 0.061
currency: "GBP"
annual_saving: = (annual_gas_saved / 1[kW*h]) * gas_price_per_kwh
capital_cost: 74000
payback_years: = capital_cost / annual_saving
caveat: "The proofer cannot absorb 210 kW in summer, so the duty is seasonal. heat_utilisation carries that into the arithmetic, so annual_gas_saved, annual_saving and payback_years above are all derated figures; undo the factor and the payback would read about seven months shorter than the plant can deliver."
}
saving_option seal_and_insulate {
of: oven_balance
label: "End-seal replacement and 60 mm additional roof insulation"
wall_loss_reduction: 34 kW
extraction_reduction: 22 kW
total_reduction: = wall_loss_reduction + extraction_reduction
annual_gas_saved: = total_reduction * 6200[h]
annual_saving: = (annual_gas_saved / 1[kW*h]) * 0.061
capital_cost: 21000
payback_years: = capital_cost / annual_saving
}
funnel_step energy_in {
n: = oven_balance.total_input
title: "Energy into the oven — gas and electrical"
}
funnel_step after_flue {
n: = energy_in.n - flue_loss.power
from: energy_in
title: "After the flue loss"
}
funnel_step after_extraction {
n: = after_flue.n - extraction_loss.power
from: after_flue
title: "After extraction and end-seal losses"
}
funnel_step after_walls {
n: = after_extraction.n - wall_loss.power
from: after_extraction
title: "After wall and roof losses"
}
funnel_step after_recovery {
n: = after_walls.n - recovered_to_proofer.power
from: after_walls
title: "After heat recovered to the proofer"
}
funnel_step into_product {
n: = product_heat.power
from: after_recovery
title: "Into the product — the 16 kW gap is the closure error"
}
note closure_discipline {
text: "An energy balance that closes to zero has usually been made to. The honest move is to measure what can be measured, compute the rest, and print the difference: 16 kW here, which is smaller than the uncertainty on the flue calculation alone and therefore not worth chasing further."
anchor: oven_balance
}
view cascade: funnel(energy_in)
caption cascade_caption {
of: cascade
title: "Where the oven's energy goes"
text: "One hour at steady state. Each step subtracts one measured loss, so the width of a step is the loss it removes; the final gap between the last step and the heat into the product is the closure error."
n_statement: "Basis: 3600 kg of dough per hour, 1308 kW in."
style: journal
}