Battery Twin Envelope (BTE) Specification#
Canonical source
This page is rendered from SPEC.md in the repository, which is the normative document.
Version: 0.1.1 (draft) Status: Draft for community review Editor: Simon Clark License: Apache-2.0 (specification text and reference implementation)
1. Motivation#
Battery digital twins are being built by labs, OEMs, service platforms, and regulators — but there is no shared answer to the question “what is a battery digital twin, as a data artifact?” Parameter sets have BPX, time-series data has BDF, semantic records have BattINFO, and regulatory data has the EU Battery Passport, yet the composition — one exchangeable object that says this battery, this specification, these models, this state, this data — is reinvented privately by every platform.
The Battery Twin Envelope (BTE) is that composition layer: a small, versioned document format for expressing and encapsulating a battery digital twin so it can be exchanged between tools, registries, and platforms.
BTE deliberately specifies documents, not engines. How a twin is hosted, simulated, or synchronized is an implementation concern (commercial or otherwise); how it is expressed is a community concern.
2. Relationship to existing standards#
A BTE envelope composes by reference, not duplication:
Concern |
Referenced standard |
Envelope section |
|---|---|---|
Semantic identity and cell records |
BattINFO IRIs / records |
|
Physics/empirical parameter sets |
BPX, BattMo parameter sets |
|
Time-series measurement data |
BDF datasets, live feeds |
|
Regulatory identity |
EU Digital Product Passport identifiers |
|
Ontology grounding |
EMMO domain-battery, via the JSON-LD context |
all sections |
Implementations MAY resolve these references with the corresponding toolchains (battinfo, batterydf, bpx, …); the envelope itself requires none of them.
3. Document model#
An envelope is a JSON object (media type suggestion:
application/battery-twin+json; conventional filename suffix .twin.json).
JSON-LD rendering is defined in §6.
3.1 Top level#
Field |
Type |
Req. |
Meaning |
|---|---|---|---|
|
string |
MUST |
Spec version this document conforms to ( |
|
string |
MUST |
Stable identifier of the twin (URN or IRI). Identical across versions of the same twin. |
|
object |
MUST |
What the twin mirrors (§3.2). |
|
object |
MAY |
Design-level description (§3.3). |
|
array |
MAY |
Model/parameter-set bindings (§3.4). |
|
object |
MAY |
Latest estimated state (§3.5). |
|
array |
MAY |
Prior state snapshots, oldest first. |
|
array |
MAY |
Links to datasets and feeds (§3.6). |
|
object |
MUST |
Creation metadata (§3.7). |
|
object |
MAY |
Namespaced vendor/tool-specific facts (§3.8). |
|
object |
MUST |
Version-chain record (§4). |
Unknown top-level fields are invalid in v0.1 (forward compatibility is
handled by bte_version); vendor- or tool-specific facts belong in
extensions (§3.8).
A document MUST declare a bte_version greater than or equal to the
earliest specification version that defines every field it uses (e.g. a
document using extensions or the §3.5 throughput fields MUST declare at
least 0.1.1).
3.2 identity#
label (MUST); manufacturer, model, serial_number, battinfo_iri
(IRI of a BattINFO cell/cell-instance record), passport_id (all MAY).
3.3 specification#
Prefer referencing a BattINFO record via battinfo_record (IRI or relative
path) over duplicating fields. Convenience fields for standalone use:
chemistry, form_factor, nominal_capacity_ah, nominal_voltage_volt.
Numeric field names carry unit suffixes following the BDF naming convention
({quantity}_{unit}, snake_case).
3.4 models[]#
Each binding: kind (bpx | battmo | pybamm | custom, MUST), name
(MUST), and exactly one of source (path/IRI) or inline (embedded
document, e.g. a BPX JSON object). Optional solver_hint (non-binding) and
validity (operating window: temperature_celsius: [low, high],
state_of_charge: [low, high]).
Envelopes describe which models apply and when they are valid — never how to execute them.
3.5 state / state_history[]#
A snapshot: as_of (ISO 8601, MUST); state_of_charge (0–1),
state_of_health (0–1.5), cycle_count, internal_resistance_ohm,
energy_throughput_kwh (lifetime cumulative energy throughput, kWh, ≥ 0),
equivalent_full_cycles (lifetime equivalent full cycles, ≥ 0, may be
fractional), method (estimation method), source_data (URI of the dataset
the estimate derives from) — all MAY. When a new snapshot replaces state,
the old one SHOULD be appended to state_history.
3.6 data[]#
Each link: kind (bdf | feed | other, MUST), uri (MUST), role
(e.g. cycling, field, characterization), description. bdf links
SHOULD point at conforming BDF datasets (e.g. *.bdf.csv).
3.7 provenance#
created (ISO 8601, MUST); created_by, tool, funding (MAY).
3.8 extensions#
A JSON object carrying vendor- or tool-specific facts that are not (yet) canonical, without forking the schema. Every key MUST match, in its entirety, the pattern
^(?!(?:bte|schema|battinfo):)[a-z][a-z0-9_-]*:\S+(?![\s\S])
i.e. <prefix>:<name> where the prefix matches [a-z][a-z0-9_-]* and the
name is one or more non-whitespace characters (no spaces, tabs, carriage
returns, or newlines anywhere in the key). Keys are case-sensitive. The
prefixes bte:, schema:, and battinfo: — and any other prefix bound in
the published JSON-LD context (§6) — are RESERVED and MUST NOT be used for
extensions. Non-ASCII characters are permitted in the name but discouraged.
Example: "lab:fixture_id": "bench-07".
Values are arbitrary JSON, but extension values MUST NOT be JSON null
(absence is expressed by omitting the key). An empty extensions object
MUST be omitted from the canonical form.
Extensions participate in canonical serialization and content hashing (§4) exactly like every other field — there is no special-casing. Consumers MUST ignore extension entries they do not understand.
4. Versioning and immutability#
Envelope documents are immutable. Updating a twin means issuing a new document with:
the same
id;version.numberincremented by 1;version.previousset to the content hash of the prior document;version.changedlisting the updated top-level sections;a new
version.timestamp.
The content hash is "sha256:" + hex(sha256(canonical_json)), where
canonical_json is the document serialized with sorted keys, separators
(",", ":"), UTF-8, and all null-valued fields omitted.
In the canonical form, all datetime values (state.as_of,
provenance.created, version.timestamp, and their state_history
counterparts) MUST be RFC 3339 UTC with a Z suffix: timestamps carrying a
non-UTC offset are converted to UTC, timestamps without an offset are
interpreted as UTC, and fractional seconds are omitted when zero and
otherwise carry no trailing zeros — 12:00:00Z, 12:00:00.5Z,
12:00:00.123456Z.
When issuing a new version, each updated top-level section REPLACES the previous section wholesale — there is no merging.
This yields a verifiable hash chain: any consumer can check that a claimed successor really derives from its predecessor. (Implementations proved this pattern in production twin platforms; BTE standardizes the shape, not the platform.)
5. Validation and conformance#
A document conforms to BTE v0.1 if it validates against the JSON Schema
published with this spec (battwin/schemas/twin-envelope.schema.json) and
satisfies the semantic rules above (one-of source/inline; ordered
windows; version-chain rules when a predecessor is available).
The reference SDK (pip install battwin) implements both layers:
battwin validate <file>.
The JSON Schema’s format: date-time asserts only that a value is a
well-formed RFC 3339 date-time; the canonical UTC-Z datetime form of §4
is authoritative in the reference model layer (which normalizes datetimes when
serializing) and is additionally asserted, for RDF renderings, by the optional
SHACL layer.
6. JSON-LD rendering#
Adding "@context" (the context published with this spec), "@id" (= id)
and "@type": "TwinEnvelope" to a conforming document yields JSON-LD, mapping
identity fields to schema.org and domain terms to the bte: prefix, with
BattINFO IRIs as first-class references.
BTE deliberately introduces no new namespace: BattINFO
(https://w3id.org/battinfo, a registered w3id namespace) is the authority
for battery-related IRIs, and BTE terms live under it:
vocabulary terms:
https://w3id.org/battinfo/twin#(thebte:prefix), e.g.bte:stateOfHealth=https://w3id.org/battinfo/twin#stateOfHealth;twin instances, when registered, follow the BattINFO registry resource pattern:
https://w3id.org/battinfo/twin/{id};a hosted context is planned at
https://w3id.org/battinfo/twin/context(mirroring the existinghttps://w3id.org/battinfo/context/records/v1.jsonconvention), so envelopes can reference the context by URL instead of inlining it.
Until the /twin redirect rules are added to the battinfo w3id registration,
treat term IRIs as provisional. Deeper EMMO alignment (quantities, units) is
planned for v0.2 in coordination with BattINFO.
7. Non-goals#
BTE v0.1 intentionally does not specify:
executing simulations (that is the job of PyBaMM, BattMo, and platforms);
hosting, APIs, or synchronization protocols for live twins;
fleet/tenant management;
acquisition of measurement data (see the
battfeedcollector toolkit and the BDF ecosystem);replacing BattINFO records, BPX files, or BDF datasets — it links them.
8. Example#
See examples/cr2032.twin.json for a complete
envelope: a CR2032 cell with a BattINFO spec reference, an inline custom
model stub, one state snapshot, a BDF data link, and a small extensions
block.
9. Acknowledgements#
Developed in continuity with the DigiBatt project, funded by the European Union under grant agreement 101103997.
Revision history#
0.1.0 — initial draft.
0.1.1 — additive, backward compatible:
state.energy_throughput_kwhandstate.equivalent_full_cycles(§3.5); top-levelextensionsobject with namespaced keys, reserved prefixes, and no null values (§3.8); the version-declaration rule (§3.1); the canonical datetime form and wholesale-replacement update semantics (§4). Documents declaringbte_version: "0.1.0"remain valid so long as they use no 0.1.1 fields.