API reference#

The public API is importable from the top-level battwin package; the modules below are where the objects live. Anything not documented here (names starting with _) is internal.

battwin.envelope: the document model#

Battery Twin Envelope (BTE) document model.

A twin envelope is an immutable, serializable JSON document that composes existing open artifacts into one exchangeable representation of a battery digital twin:

  • identity -> who/what the twin mirrors (serials, passport IDs, BattINFO IRIs)

  • specification -> the cell/pack spec, by reference to a BattINFO record

  • models -> parameter sets (BPX/BattMo/…) with validity windows

  • state -> estimated states (SoC/SoH/…) with provenance

  • data -> links to time-series (BDF datasets, live feeds)

  • extensions -> namespaced vendor/tool-specific facts (non-canonical)

  • version -> immutable version chain (content-hash linked)

The envelope deliberately references other resources by IRI/path instead of importing their toolchains: BattINFO records, BPX files, and BDF datasets are linked, never embedded code dependencies. Executing models, hosting twins, and synchronizing live state are out of scope (see SPEC.md, “Non-goals”).

battwin.envelope.BTE_VERSION = '0.1.1'#

str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.

pydantic model battwin.envelope.TwinEnvelope[source]#

The top-level Battery Twin Envelope document.

extensions carries namespaced, non-canonical facts (SPEC.md §3.8); it participates in canonical_json() and content_hash() like every other field.

Fields:
field bte_version: str = '0.1.1'#
field id: str [Required]#

Stable identifier of the twin (URN or IRI).

Constraints:
  • min_length = 1

field identity: Identity [Required]#
field specification: Specification | None = None#
field models: list[ModelBinding] [Optional]#
field state: StateSnapshot | None = None#
field state_history: list[StateSnapshot] [Optional]#
field data: list[DataLink] [Optional]#
field provenance: Provenance [Required]#
field extensions: dict[str, Any] | None = None#

Vendor/tool-specific facts that are not (yet) canonical. Keys MUST be namespaced as ‘<prefix>:<name>’ (SPEC.md §3.8); values are arbitrary non-null JSON.

field version: VersionInfo [Required]#
pydantic model battwin.envelope.Identity[source]#

What physical (or virtual) battery this twin mirrors.

Fields:
field label: str [Required]#

Human-readable name of the twinned battery.

Constraints:
  • min_length = 1

field manufacturer: str | None = None#
field model: str | None = None#
field serial_number: str | None = None#
field battinfo_iri: str | None = None#

IRI of the cell/cell-instance record in a BattINFO registry, e.g. https://w3id.org/battinfo/cell/<id>.

field passport_id: str | None = None#

EU Digital Product Passport identifier, if any.

pydantic model battwin.envelope.Specification[source]#

The design-level description of the battery, by reference where possible.

Fields:
field battinfo_record: str | None = None#

IRI or relative path of a BattINFO cell-spec record. Prefer referencing over duplicating spec fields below.

field chemistry: str | None = None#
field form_factor: str | None = None#
field nominal_capacity_ah: float | None = None#
Constraints:
  • ge = 0

field nominal_voltage_volt: float | None = None#
Constraints:
  • ge = 0

pydantic model battwin.envelope.ModelBinding[source]#

A parameter set / model attached to the twin.

Exactly one of source (path or IRI) or inline (embedded document, e.g. a BPX JSON object) must be provided.

Fields:
field kind: Literal['bpx', 'battmo', 'pybamm', 'custom'] [Required]#
field name: str [Required]#
Constraints:
  • min_length = 1

field source: str | None = None#
field inline: dict[str, Any] | None = None#
field solver_hint: str | None = None#

Non-binding hint for implementations, e.g. ‘pybamm’ or ‘battmo’.

field validity: ValidityWindow | None = None#
pydantic model battwin.envelope.ValidityWindow[source]#

Operating window within which a model binding is considered valid.

Fields:
field temperature_celsius: tuple[float, float] | None = None#
field state_of_charge: tuple[float, float] | None = None#
pydantic model battwin.envelope.StateSnapshot[source]#

An estimated state of the battery at a point in time.

Fields:
field as_of: UTCDateTime [Required]#
Constraints:
  • func = <function _rfc3339_utc at 0x7f47046640e0>

  • return_type = <class ‘str’>

  • when_used = json

field state_of_charge: float | None = None#
Constraints:
  • ge = 0.0

  • le = 1.0

field state_of_health: float | None = None#
Constraints:
  • ge = 0.0

  • le = 1.5

field cycle_count: int | None = None#
Constraints:
  • ge = 0

field internal_resistance_ohm: float | None = None#
Constraints:
  • ge = 0

field energy_throughput_kwh: float | None = None#

Lifetime cumulative energy throughput, in kWh.

Constraints:
  • ge = 0

field equivalent_full_cycles: float | None = None#

Lifetime equivalent full cycles (may be fractional).

Constraints:
  • ge = 0

field method: str | None = None#

How the state was estimated, e.g. ‘coulomb_counting’.

field source_data: str | None = None#

URI of the dataset the estimate was derived from.

A link to time-series or other data belonging to the twin.

Fields:
field kind: Literal['bdf', 'feed', 'other'] [Required]#
field uri: str [Required]#

Path, URL, or IRI of the dataset (e.g. a .bdf.csv file).

Constraints:
  • min_length = 1

field role: str | None = None#

e.g. ‘cycling’, ‘field’, ‘reference’, ‘characterization’.

field description: str | None = None#
pydantic model battwin.envelope.Provenance[source]#
Fields:
field created: UTCDateTime [Required]#
Constraints:
  • func = <function _rfc3339_utc at 0x7f47046640e0>

  • return_type = <class ‘str’>

  • when_used = json

field created_by: str | None = None#
field tool: str | None = None#
field funding: str | None = None#
pydantic model battwin.envelope.VersionInfo[source]#

Immutable version chain: previous is the content hash of the prior document.

Fields:
field number: int = 1#
Constraints:
  • ge = 1

field previous: str | None = None#

sha256 content hash of the previous envelope version.

field changed: list[str] [Optional]#
field timestamp: UTCDateTime [Required]#
Constraints:
  • func = <function _rfc3339_utc at 0x7f47046640e0>

  • return_type = <class ‘str’>

  • when_used = json

battwin.envelope.new_envelope(*, label, twin_id=None, chemistry=None, created_by=None, timestamp=None)[source]#

Scaffold a minimal valid envelope (used by battwin init).

Parameters:
  • label (str)

  • twin_id (str | None)

  • chemistry (str | None)

  • created_by (str | None)

  • timestamp (datetime | None)

Return type:

TwinEnvelope

battwin.io: reading and writing#

Load and save Battery Twin Envelope documents.

battwin.io.from_dict(doc)[source]#

Build an envelope from a parsed JSON document.

Accepts both plain-JSON and JSON-LD renderings: @context and @type are dropped, and @id is used as id when the plain key is absent.

Parameters:

doc (dict[str, Any])

Return type:

TwinEnvelope

battwin.io.load(path)[source]#

Load an envelope from a .twin.json / .json / .jsonld file.

Parameters:

path (str | Path)

Return type:

TwinEnvelope

battwin.io.save(envelope, path, *, jsonld=False, indent=2)[source]#

Write an envelope to disk; returns the path written.

With jsonld=True the document carries @context/@id/@type (self-contained JSON-LD); otherwise plain JSON is written.

Parameters:
Return type:

Path

battwin.validate: validation#

Validation of Battery Twin Envelope documents.

Three layers, matching the spec:

  1. JSON Schema (battwin/schemas/twin-envelope.schema.json) — the public, language-neutral contract. Anyone can validate an envelope without Python.

  2. Model rules (pydantic, battwin.envelope) — the reference implementation’s stricter semantic checks (e.g. a model binding must have exactly one of source/inline).

  3. SHACL shapes (battwin/shapes/twin-envelope.shapes.ttl, optional) — graph-level constraints on the JSON-LD rendering, so envelopes exchanged as RDF are checkable too. Requires the battwin[shacl] extra.

validate_dict/validate_file run the first two — plus the version-declaration rule of SPEC.md §3.1 (a document must declare a bte_version that defines every field it uses) — and the SHACL layer when called with shacl=True. They return a flat list of human-readable problem strings (empty list = valid).

battwin.validate.load_schema()[source]#

Return the packaged BTE JSON Schema.

Return type:

dict[str, Any]

battwin.validate.load_context()[source]#

Return the packaged BTE JSON-LD context (the value of @context).

Return type:

dict[str, Any]

battwin.validate.load_shapes()[source]#

Return the packaged BTE SHACL shapes as Turtle text.

Return type:

str

battwin.validate.shacl_problems(doc)[source]#

Validate the JSON-LD rendering of doc against the packaged shapes.

Plain-JSON documents are wrapped with the packaged @context (and @id/@type) first; documents that already carry @context are parsed as-is. Returns problems formatted like the other layers, prefixed shacl:. Requires the optional dependency pyshacl.

Parameters:

doc (dict[str, Any])

Return type:

list[str]

battwin.validate.validate_dict(doc, *, shacl=False)[source]#

Validate a parsed envelope document; returns problems (empty = valid).

With shacl=True the packaged SHACL shapes are also run against the JSON-LD rendering (requires the battwin[shacl] extra).

Parameters:
Return type:

list[str]

battwin.validate.validate_file(path, *, shacl=False)[source]#

Validate an envelope file; returns problems (empty = valid).

With shacl=True the packaged SHACL shapes are also run (requires the battwin[shacl] extra).

Parameters:
Return type:

list[str]

battwin.battinfo: BattINFO helpers#

Seed a Battery Twin Envelope from a BattINFO registry record.

envelope_from_battinfo dereferences a cell-spec IRI (e.g. https://w3id.org/battinfo/spec/<id>), reads the identity facts out of the returned record, and scaffolds a valid envelope whose specification references the record by IRI – the envelope points at the registry, it does not copy the record in.

Standard library only, by design: fetching a record is one HTTPS GET returning JSON, so twinning a cell from its IRI works on a bare pip install battwin. (The heavier [data] extra is for tabular data, not for this.)

The record shapes handled here follow the battery-genome registry exports: identity facts are looked up in metadata first, then in the battinfo_records.*.product block; the literal string "unknown" is treated as absent, so a sparse record seeds a sparse (but valid) envelope rather than one full of placeholder values.

battwin.battinfo.envelope_from_battinfo(iri, *, label=None, twin_id=None, chemistry=None, created_by=None, timestamp=None, fetch=None)[source]#

Scaffold an envelope for the cell described by a BattINFO record.

The record is dereferenced and its identity facts (name, manufacturer, model, chemistry, capacity/voltage when present) seed identity and specification; specification.battinfo_record carries the given IRI so consumers can re-resolve the source. label and chemistry override what the record says (useful while a record is sparse). fetch injects a record loader for tests (defaults to fetch_battinfo_record()).

Parameters:
Return type:

TwinEnvelope

battwin.battinfo.fetch_battinfo_record(iri, *, timeout_s=30.0)[source]#

Dereference iri (following redirects) and return the JSON record.

Sends Accept: application/json; w3id.org IRIs 303-redirect to the registry, which urllib follows. Raises ValueError for a non-HTTP IRI or a non-object payload, and lets network errors (urllib.error.URLError, an OSError subclass) propagate for the caller to surface.

Parameters:
Return type:

dict[str, Any]

battwin.ecm: ECM parameter sets#

ECM Parameter Set (ECM-PS) validation.

An ECM-PS document expresses equivalent-circuit-model parameters in a form deliberately styled after BPX (Header/Parameterisation/State/Validation sections, natural-language parameter names with bracketed SI units, values as constants or interpolated tables), so that ECM-PS reads as the ECM counterpart of a BPX file even though BPX itself defines no ECM model type. Semantic grounding lives at spec level: each defined parameter name maps to a class in the EMMO domain-equivalent-circuit-model ontology, and the Header may carry a BattINFO record IRI. A twin attaches an ECM-PS through a model binding whose payload (inline or the document behind source) is the document itself.

battwin packages the ECM-PS JSON Schema validation-only: this module can check that a payload is a well-formed ECM-PS document, and never evaluates the model – execution belongs to PyBaMM/PyBOP and friends (SPEC.md §7). The schema $id under the EMMO domain namespace is provisional until the corresponding w3id redirects are registered.

battwin.ecm.ecm_ps_problems(doc)[source]#

Validate a parsed ECM-PS document; returns problems (empty = valid).

Problem strings follow the envelope validators’ format, prefixed ecm:. Only schema conformance is checked – cross-value consistency (e.g. that every R{i}/C{i} up to Number of RC elements is present, or that table rows are equally long) is enforced by consumers such as battwin.sim.build_thevenin().

Parameters:

doc (dict[str, Any])

Return type:

list[str]

battwin.ecm.load_ecm_schema()[source]#

Return the packaged ECM-PS JSON Schema.

Return type:

dict[str, Any]

battwin.ecm.validate_ecm_ps_file(path)[source]#

Validate an ECM-PS file; returns problems (empty = valid).

Parameters:

path (str | Path)

Return type:

list[str]

battwin.sim: simulation (battwin[sim])#

Run a twin’s ECM model binding in PyBaMM (the battwin[sim] extra).

This is the execution half of the ECM story: battwin.ecm checks that a binding’s payload is a well-formed ECM Parameter Set; this module turns that payload into a pybamm.equivalent_circuit.Thevenin model and runs an experiment against it. ECM-PS 0.2 is styled after BPX (Header/Parameterisation sections, bracketed-unit parameter names, scalar-or-table values, Kelvin), and the mapping here follows the parameter names’ own -> PyBaMM annotations in the packaged schema.

The spec fence is unchanged: the format never specifies execution. This module is an optional convenience of the reference SDK – pip install "battwin[sim]" – and everything it computes flows back into the envelope as ordinary spec objects (data links, state snapshots) via TwinEnvelope.next_version().

Deliberate simplifications, surfaced in TheveninBuild.warnings or as errors:

  • Open-circuit voltage [V] is the mean of the charge/discharge branches when both are present – PyBaMM’s basic Thevenin has a single OCV, so the hysteresis branches and decay rate are dropped (kept in the ECM-PS for round-trip).

  • Expression-string values are not part of ECM-PS and are rejected with a clear error; only constants and 1-D/2-D interpolated tables are evaluated.

class battwin.sim.TheveninBuild(model, parameter_values, options, warnings=<factory>)[source]#

A ready-to-run PyBaMM Thevenin model built from an ECM-PS document.

Parameters:
battwin.sim.build_thevenin(ecm_ps, *, initial_soc=1.0, ambient_celsius=25.0)[source]#

Build a PyBaMM Thevenin model + parameter values from an ECM-PS 0.2 document.

Constants pass through; 1-D tables become SoC interpolants; 2-D tables become (temperature, SoC) interpolants (the document’s Kelvin axis is converted to the Celsius axis PyBaMM’s ECM callbacks use).

Parameters:
Return type:

TheveninBuild

battwin.sim.run_experiment(build, instructions, *, period_s=10.0)[source]#

Run a PyBaMM experiment against a built Thevenin model.

instructions are PyBaMM experiment strings (e.g. "Discharge at 1C until 2.5 V"). Returns plain column lists in BDF naming (positive current = charging, per the BDF sign convention – PyBaMM’s load-positive sign is flipped here) so callers can write a .bdf.csv and attach it to the envelope as a data link.

Parameters:
Return type:

dict[str, list[float]]

battwin.fit: parameter identification (battwin[fit])#

Fit a twin’s ECM parameters against measured data (the battwin[fit] extra).

This closes the loop the tutorials walk one way: a twin links measured data (DataLink), and this module uses PyBOP to identify circuit parameters from that data, returning a new ECM-PS document with the fitted constants and the fit’s provenance recorded in the document’s User-defined section. The caller attaches the result to the twin as a new model binding via TwinEnvelope.next_version() – outputs land back in the envelope as ordinary spec objects, like everything else in the reference SDK.

Scope, stated plainly: parameters are fitted as constants (a fitted value replaces whatever the base document carried for that name, including a 2-D table – surfaced in FitResult.warnings). Identifying full SoC/temperature lookup surfaces from one dataset is ill-posed and out of scope. OCV curves are not fittable here either; they come from characterization, not regression against a drive trace.

class battwin.fit.FitResult(ecm_ps, fitted, rmse_volt, initial_rmse_volt, n_iterations, warnings=<factory>)[source]#

The outcome of fit_thevenin().

Parameters:
ecm_ps: dict[str, Any]#

fitted constants in place, provenance in User-defined.

Type:

A new ECM-PS document

fitted: dict[str, float]#

Fitted parameter values by Circuit name.

rmse_volt: float#

Root-mean-square voltage error of the fitted model over the dataset.

initial_rmse_volt: float#

The same cost at the initial values, for before/after comparison.

battwin.fit.fit_thevenin(ecm_ps, data, *, fit=('R0 [Ohm]',), initial=None, bounds=None, initial_soc=1.0, ambient_celsius=25.0, max_iterations=1000, source_data=None)[source]#

Fit Circuit parameters of an ECM-PS document to measured data.

data is a mapping of BDF-named columns (test_time_second, current_ampere with the BDF positive-=-charging sign, and voltage_volt), e.g. the columns of a dataset the twin links – read_bdf() reads one from disk. fit names the Circuit parameters to identify ("R0 [Ohm]", "R1 [Ohm]", "C1 [F]", …); each is fitted as a constant, starting from initial (default: the base document’s value at ambient temperature and mid-SoC) within bounds (default: a factor of 10 either side of the start).

Returns a FitResult whose ecm_ps is a new, schema-valid document; source_data, when given, is recorded in the fit provenance so the fitted model names the dataset it was calibrated against.

Parameters:
Return type:

FitResult

battwin.fit.read_bdf(path)[source]#

Read a BDF-convention CSV into columns of floats keyed by column name.

Parameters:

path (str | Path)

Return type:

dict[str, list[float]]