mako-emob 0.20.0

NZR-EMob / Modell 2 allocation engine — the virtual Bilanzierungsgebiet for charge-session-exact supplier allocation (BK6-20-160 Anlage 6, BK6-24-267)
Documentation

mako-emob

NZR-EMob / Modell 2 — the virtual Bilanzierungsgebiet

„Bring your own supplier" at a charge point, per BNetzA BK6-20-160 Anlage 6 („NZR-EMob"), the BDEW Anwendungshilfe Zum Modell 2 V1.3 and Beschluss BK6-24-267. A Ladepunktbetreiber runs a regelzonenweites Bilanzierungsgebiet of its own and books every charging session into the Bilanzkreis of the supplier the customer chose — quarter hour by quarter hour, across DSO borders, with intraday supplier changes.

This crate holds the allocation engine, its invariants, and the three Modellwechsel legs: pure domain, no I/O, no wire rendering. The decision trees live in mako_pruefung::emob and the answer Fristen in mako_fristen::antwort::EMOB, because that is where every other market process keeps them.

Five things this crate exists to get right

1. The Deltamenge is a term in the identity, not a rounding error

NGZ(t, richtung) = Σ Zuordnungen + Deltamenge        exactly, every ¼ h

Anlage 6 §IV.1 obliges the LPB to assign the whole Bilanzierungsgebiet every quarter hour. Whatever no Marktlokation claimed is the Deltamenge, and §IV.2 books it to a Bilanzkreis the LPB names, at the LPB's own cost. It settles in money, so QuarterHourAllocation reports it as a field and returns a ConservationProof beside every row rather than asserting it away.

The arithmetic is metering::allocation::allocate with each claim capped at itself — reused rather than reimplemented, because that crate's Σ allocated + residual = total identity is this one and its residual is the Deltamenge. One call covers both directions of imbalance:

Case Result Delta
claims under the NGZ every claim met in full NGZ − Σ claims
claims over the NGZ every claim cut back in proportion zero up to the six-decimal cut, and Ueberdeckung is recorded

Over-claiming is real — local generation behind the Netzanschluss feeds the charge points — and no published rule resolves it. Proportional cut-back is this crate's stated default and is recorded on the result, because an operator whose claims routinely exceed the NGZ has a metering problem, not a rounding one.

2. The Anmeldung answer window is seven Werktage

Three Werktage is the window on the other two legs: the LF's answer to a 55240, and the VNB's answer to an Abmeldung (55242 → 55243), which has no LF leg to wait for. It cannot work for the Anmeldung — the VNB may spend 3 WT sending the 55240 and the LF has 3 WT of its own, so E_0510 Prüfschritt 1 („Ging innerhalb der Antwortfrist eine Ablehnung des Lieferanten ein?") is undecidable before the 6. WT. The windows and a const assertion that pins the relation live in mako_fristen::antwort.

3. Bezug and Einspeisung never net

Richtung is part of the allocation key and each direction carries its own non-negative pool. Netting them inside a quarter hour would let a V2G discharge cancel a neighbour's draw, and both would vanish from their suppliers' Bilanzkreise. The Zeitreihentyp the BIKO expects for an Einspeisungs-BK-SZR eMob is still open, so a deployment holds those rows — but the direction is modelled regardless, because the alternative is silent netting.

4. Two identifiers are deliberately not the market's

VirtualMaloId::new refuses an eleven-digit value. AWH Kap. 1.6.1 lets the LPB use its Stromnetzbetreibernummer for Zählpunktbildung und BG-Beantragung and for nothing else; nothing grants it a MaLo-ID range for the per-vehicle objects it needs internally. A virtual Marktlokation that looked like a BDEW MaLo-ID would collide with a real one the day a Netzbetreiber issued it, and the collision would surface as energy booked to a stranger's Bilanzkreis.

TokenRef carries an opaque keyed hash, never an RFID UID or an eMAID. Those identify a natural person's charging contract across every operator they visit; the allocation only needs to know which virtual MaLo a session belongs to, which is a lookup the token registry already performed.

5. Silence is not consent

Neither Anlage 6 nor the AWH gives an unanswered leg a default outcome. The GPKE Beendigung der Zuordnung has one — „Verstreicht die Frist …, gilt dies als Bestätigung" — and Modell 2 has nothing of the kind, so an expired window puts the process in Eskaliert and waits for an operator. Confirming would move a Marktlokation between Bilanzierungsgebieten on no one's say-so.

The three legs

One state machine, three workflow names, because all three run on the same Marktlokation and a process resolves by (business key, workflow name):

Workflow Request → Answer Tree Frist Answered by
emob-anmeldung 55238 → 55239 E_0513E_0510 7 WT NB (VNB)
emob-zuordnungsende 55240 → 55241 E_0511 3 WT LF
emob-abmeldung 55242 → 55243 E_0512 3 WT NB (VNB)

The 55240 leg runs inside the Anmeldung's own window, which is why that window is seven Werktage. An answer carries both its code and the tree that produced it into SG4 STS+E01 DE 9013/1131: A01 means opposite things in E_0510 and E_0511, so the pair is the smallest unit that means anything.

mako_emob::EmobModule registers the six Prüfidentifikatoren; makod routes, adapts and renders them.

The quarter-hour grid needs no DST special case

A Viertelstunde is an instant plus fifteen minutes of real time, and German local time is offset from UTC by whole hours — so a UTC-aligned quarter hour is aligned in Europe/Berlin too, and the 92- and 100-slot days are simply days with fewer or more instants in them. No day is assumed to hold 96 of them.

Provenance rides on every value

A charge point reporting clock-aligned meter values every 900 s (OCPP AlignedDataCtrlr) measures each quarter hour. A CDR reports one total and splitting it assumes constant power, which a tapering charge curve violates — an estimate wearing the shape of a measurement, whose error lands on whichever supplier held the slot boundary. Provenance is therefore on every allocated value, and SessionSplit reports what the six-decimal cut could not place instead of dropping it.

Prüfidentifikatoren

PID Message From → To Answered by
55238 Anmeldung in Modell 2 NB (LPB) → NB (VNB) 55239, E_0513E_0514E_0510
55240 Beendigung der Zuordnung zur MaLo NB (VNB) → LF 55241, E_0511
55242 Abmeldung aus dem Modell 2 NB (LPB) → NB (VNB) 55243, E_0512
55235 / 55236 Zuordnung / Beendigung ZP der NGZ zur NZR verantw. NB → benachb. NB, ÜNB 55237, E_0102 / E_0103 (MaBiS)
13018 MSCONS Netzgangzeitreihe NB (VNB) → NB (LPB), ÜNB

A01 is an Ablehnung in E_0510 and a Zustimmung in E_0511 and E_0512. The three run in the same process and a combined VNB+LF deployment walks all of them, so every lookup is keyed on (ebd, code).

The market role on the wire is always NB — the BDEW Rollenmodell defines no LPB („der LPB kommuniziert aus prozessualer Sicht wie die Rolle NB", AWH Kap. 1.4). mako_engine::marktrolle::Marktrolle::Lpb is a deployment role, the same pattern as Nmsb/Amsb and Lfn/Lfa.

Sources

  • BK6-20-160 (21.12.2020) Anlage 6 „NZR-EMob"; Mitteilung Nr. 4 (03.05.2022)
  • BDEW AWH „Zum Modell 2 …" V1.3 (01.04.2025)
  • BDEW AWH Ergänzung der Marktregeln … (MaBiS) V1.0 (27.04.2022)
  • BK6-24-267 (15.05.2025), bestandskräftig
  • UTILMD AHB Strom 2.2 Kap. 11; EBD 4.3 Kap. 17
  • MaBiS BK6-24-174 Anlage 3, Kap. 3.8 / 3.10 / 5

Related crates

Crate Role
mako-emobthis crate The Modell 2 allocation engine, its invariants and the three Modellwechsel legs
mako-engine Event-sourced workflow runtime — Workflow, Process, EventStore, deadlines
mako-fristen When an answer is due — Werktage, the MaKo holiday calendar, the per-PID Antwortfristen
mako-pruefung Where the E_0510E_0513 Entscheidungsbäume live (mako_pruefung::emob)
mako-mabis The Summenzeitreihen the virtual Bilanzierungsgebiet settles through
makod Production daemon — routes, adapts and renders these workflows

Part of mako, an open-source Rust platform for German energy market communication (Marktkommunikation). Full documentation: https://hupe1980.github.io/mako/

License

MIT OR Apache-2.0