Skip to main content

Crate metering

Crate metering 

Source
Expand description

German energy-metering quantities: exact, cited, and calendar-correct.

A pure library — no I/O, no async, no clock — for the quantities German metering is settled on. Every quantity is a Decimal, every rounding names its places and mode (precision), and every rule cites the clause it implements. It computes kWh, m³ and kW, not money.

Time is Europe/Berlin. A DayBoundary — Strom at 00:00, Gas at 06:00 — names days, months and years as UTC Periods of their real length (23, 24 or 25 hours a day). Interval data enters as MeterIntervals, validated once into a Series — sorted, on one grid, one channel — which the rest of the crate consumes.

§Quick start

Aggregate a series into the quantities a billing period is invoiced on:

use metering::prelude::*;
use time::{Duration, macros::date};

let day = DayBoundary::Strom.day(date!(2026 - 06 - 01)).unwrap();
let slots = (0..96)
    .map(|i| MeterInterval::quarter_hour(day.start() + Duration::minutes(15 * i), dec!(1), QualityFlag::Measured))
    .collect::<Result<Vec<_>, _>>()?;
let series = Series::new(Resolution::QUARTER_HOUR, DayBoundary::Strom, slots)?;

let period = aggregate(&series, day)?;
assert_eq!(period.arbeitsmenge, dec!(96));            // kWh
assert_eq!(period.spitzenleistung_kw, Some(dec!(4))); // 1 kWh in 15 min
assert_eq!(period.coverage.pct(), Some(dec!(100)));

Validate, then substitute — a missing quarter-hour is an error finding, and a gap up to 2 h is interpolated between validated measured values:

use metering::prelude::*;
use metering::time::holiday::Bundesland;
use metering::vee::substitute::{Method, SubstitutionReason};
use metering::vee::validation::Grade;
use time::{Duration, macros::{date, datetime}};

let day = DayBoundary::Strom.day(date!(2026 - 06 - 01)).unwrap();
let kwh = |i: i64| Decimal::new(200 + i, 3); // a ramp, 0.200 … 0.295 kWh
let slots = (0..96)
    .filter(|i| *i != 50) // one quarter-hour never arrived
    .map(|i| MeterInterval::quarter_hour(day.start() + Duration::minutes(15 * i), kwh(i), QualityFlag::Measured))
    .collect::<Result<Vec<_>, _>>()?;
let series = Series::new(Resolution::QUARTER_HOUR, DayBoundary::Strom, slots)?;

let rules = Rules::strom(day, datetime!(2026-07-01 0:00 UTC), None);
let report = validate(&series, &rules);
assert_eq!(report.grade(), Grade::C);

let policy = Policy::strom(Bundesland::Be, SubstitutionReason::CommunicationFailure);
let filled = substitute(&series, &report, &policy)?;
assert_eq!(filled.substitutes[0].method, Method::Interpolation);
assert_eq!(filled.substitutes[0].value, dec!(0.25)); // between 0.249 and 0.251
assert_eq!(validate(&filled.series, &rules).grade(), Grade::A);

The Gastag and gas conversion — a gas day runs 06:00 to 06:00 local, and a metered volume becomes kWh_Hs through Brennwert and Zustandszahl:

use metering::gas::conversion::gas_m3_to_kwh_hs;
use metering::prelude::*;
use time::macros::{date, datetime};

let gastag = DayBoundary::Gas.day(date!(2026 - 01 - 15)).unwrap();
assert_eq!(gastag.start(), datetime!(2026-01-15 5:00 UTC));
// The Gastag across the autumn change is 25 hours long.
assert_eq!(DayBoundary::Gas.day(date!(2026 - 10 - 24)).unwrap().duration().whole_hours(), 25);

// 100 m³ × 11.2 kWh/m³ × 0.95
assert_eq!(gas_m3_to_kwh_hs(dec!(100), dec!(11.2), dec!(0.95)), Some(dec!(1064.000)));

§Features

serde adds Serialize/Deserialize to the coded enums, identifiers, inputs and most results, and enables time/serde; the covered types and wire representation are under Design.

§Modules

GroupModules
Series, time, IDsseries (intervals, Series, readings, resampling), time (calendar, holidays, resolution), ids (MaLo, MeLo, EIC, BDEW codes, OBIS)
VEEvee — validation and grading, substitute values, Messtyp
Billingbilling — aggregation, Zählzeiten, reactive energy, imbalance, losses, forecast
SLPslp — strom (BDEW SLP), gas (SigLinDe)
Gas, heatgas (DVGW G 685 conversion), heat (HeizkostenV)
Gridgrid — § 14a EnWG, Ausfallarbeit, EN 50160, rollout
Allocationallocation — UTILTS Berechnungsformel, § 42b community, § 42c sharing, sessions
EEGeeg — MiSpeL, EEG § 51/§ 51a, EnFG § 46
Cross-cuttingprecision (every rounding), error, prelude

Start with the guides; the design constraints and regulatory basis are the reference for rules and sources.

Re-exports§

pub use error::ParseError;
pub use ids::obis::ObisCode;
pub use ids::MaloId;
pub use ids::MeloId;
pub use series::Series;
pub use series::interval::Direction;
pub use series::interval::MeterInterval;
pub use series::interval::QualityFlag;
pub use series::interval::Sparte;
pub use series::interval::Unit;
pub use time::calendar::DayBoundary;
pub use time::calendar::Period;
pub use time::resolution::Resolution;

Modules§

allocation
Allocation between metering points: the UTILTS Berechnungsformel, the § 42b community allocation, § 42c energy sharing and charging sessions.
billing
Billing quantities: Arbeitsmenge and Spitzenleistung, tariff registers, reactive energy, imbalance, network losses and the annual forecast.
eeg
EEG and EnFG quantities: negative-price quarter-hours, Zeitgleichheit, and the BNetzA MiSpeL Festlegung for storage and charge points.
error
ParseError — the single error type every FromStr in this crate returns, so one ? chain decodes a row:
gas
Gas: volume-to-energy conversion per DVGW G 685.
grid
Grid operation: § 14a EnWG control, Redispatch 2.0 Ausfallarbeit, EN 50160 power quality and the MsbG smart-meter rollout.
heat
Heat and warm water: HeizkostenV.
ids
Market identifiers: MaLo/MeLo-IDs, EIC, BDEW codes, Regelzonen and OBIS codes.
precision
Every rounding in the crate: decimal places and mode, in one table.
prelude
The common pipeline in one import: the core nouns, the entry points from readings to a billing period, and Decimal with its dec! literal.
series
The metered time series: MeterInterval, the validated Series, register readings and resampling.
slp
Standard load profiles: BDEW SLP Strom and SigLinDe SLP Gas.
time
Europe/Berlin time: the calendar (DayBoundary, Period), statutory holidays and interval resolutions.
vee
Validation, estimation and editing on a validated Series.