{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# The Ontology Spec and Units\n", "This example describes how BDF's canonical column ontology is loaded and queried through the `bdf.spec` module. It is aimed at those who want to inspect the quantities BDF understands, pin a specific ontology version, or perform unit conversions with the spec as the single source of truth." ] }, { "cell_type": "code", "execution_count": null, "id": "1", "metadata": {}, "outputs": [], "source": [ "from bdf import spec\n", "from bdf.spec import COLUMN_ONTOLOGY, ColumnOntology, Quantity" ] }, { "cell_type": "markdown", "id": "2", "metadata": {}, "source": [ "## The default ontology\n", "`bdf.spec.COLUMN_ONTOLOGY` is a module-level singleton built at import time from the ontology snapshot bundled with the installed package. This is the latest packaged version and is what every reader, normalizer, and validator uses by default. Iterating it yields `(mr_name, Quantity)` pairs." ] }, { "cell_type": "code", "execution_count": null, "id": "3", "metadata": {}, "outputs": [], "source": [ "print(\"ontology version:\", COLUMN_ONTOLOGY.ontology_version)\n", "print(\"quantity count:\", len(list(COLUMN_ONTOLOGY)))\n", "print(\"first 8 mr_names:\", [mr for mr, _ in COLUMN_ONTOLOGY][:8])" ] }, { "cell_type": "markdown", "id": "4", "metadata": {}, "source": [ "Each quantity is a `Quantity` model carrying the canonical unit, the human-readable label template, the machine-readable name, the ontology IRI, accepted synonyms, and documentation metadata. Look one up by `mr_name` with item access (or attribute access)." ] }, { "cell_type": "code", "execution_count": null, "id": "5", "metadata": {}, "outputs": [], "source": [ "q = COLUMN_ONTOLOGY[\"discharging_capacity_ah\"]\n", "print(\"mr_name: \", q.mr_name)\n", "print(\"unit: \", q.unit)\n", "print(\"label_template: \", q.label_template)\n", "print(\"formatted_label:\", q.formatted_label)\n", "print(\"notation: \", q.effective_notation)\n", "print(\"iri: \", q.iri)\n", "print(\"required: \", q.required)\n", "print(\"synonyms: \", q.synonyms[:5])\n", "print(\"definition: \", q.definition)" ] }, { "cell_type": "markdown", "id": "6", "metadata": {}, "source": [ "The required and optional label sets are derived directly from the loaded ontology, so they always reflect whichever version is active." ] }, { "cell_type": "code", "execution_count": null, "id": "7", "metadata": {}, "outputs": [], "source": [ "print(\"required labels:\", COLUMN_ONTOLOGY.required_labels())\n", "print(\"optional labels:\", COLUMN_ONTOLOGY.optional_labels()[:6], \"...\")" ] }, { "cell_type": "markdown", "id": "8", "metadata": {}, "source": [ "## Loading other versions or a custom ontology\n", "The default singleton is fine for everyday use, but you can build an independent `ColumnOntology` and point it at a different source. Construct a fresh instance with `ColumnOntology.build()` so you never mutate the shared `COLUMN_ONTOLOGY` singleton.\n", "\n", "`load_ttl(path)` adopts the quantities from any local Turtle file, which is the way to use a custom or in-development ontology. Here we load the snapshot bundled with the package to show the mechanism offline." ] }, { "cell_type": "code", "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ "import importlib.resources\n", "from pathlib import Path\n", "\n", "custom = ColumnOntology.build()\n", "ttl_path = Path(str(importlib.resources.files(\"bdf.data\").joinpath(\"bdf-ontology-snapshot.ttl\")))\n", "custom.load_ttl(ttl_path)\n", "print(\"custom ontology version:\", custom.ontology_version, \"with\", len(list(custom)), \"quantities\")" ] }, { "cell_type": "markdown", "id": "10", "metadata": {}, "source": [ "`load_latest(refresh=True)` fetches the live ontology from `w3id.org`, parses it, and caches it locally. Use it to pick up a release newer than the bundled snapshot. (This cell requires network access.)" ] }, { "cell_type": "code", "execution_count": null, "id": "11", "metadata": {}, "outputs": [], "source": [ "latest = ColumnOntology.build()\n", "try:\n", " latest.load_latest(refresh=True)\n", " print(\"live ontology version:\", latest.ontology_version)\n", "except Exception as exc:\n", " print(\"live fetch unavailable, keeping bundled snapshot:\", type(exc).__name__)" ] }, { "cell_type": "markdown", "id": "12", "metadata": {}, "source": [ "You can pin a specific release explicitly with `load_version`. It serves the cached copy when present, and otherwise fetches that release tag, verifies its `owl:versionInfo` matches, and caches it for next time." ] }, { "cell_type": "code", "execution_count": null, "id": "13", "metadata": {}, "outputs": [], "source": [ "pinned = ColumnOntology.build()\n", "pinned.load_version(\"1.1.0\")\n", "print(\"loaded pinned version:\", pinned.ontology_version)" ] }, { "cell_type": "markdown", "id": "14", "metadata": {}, "source": [ "## Resolving labels to machine-readable names\n", "Given a column label as it appears in a BDF dataframe, the ontology resolves it back to its canonical quantity. `mr_name_from_label` returns the machine-readable name; `quantity_from_label` returns the matching `Quantity` together with the unit parsed from the label. Both prefer non-deprecated quantities when a base label is shared." ] }, { "cell_type": "code", "execution_count": null, "id": "15", "metadata": {}, "outputs": [], "source": [ "print(\"mr_name:\", COLUMN_ONTOLOGY.mr_name_from_label(\"Discharging Capacity / Ah\"))\n", "\n", "matched, unit = COLUMN_ONTOLOGY.quantity_from_label(\"Voltage / mV\")\n", "print(\"matched quantity:\", matched.mr_name)\n", "print(\"unit from label: \", unit)" ] }, { "cell_type": "markdown", "id": "16", "metadata": {}, "source": [ "Two module-level helpers parse labels without touching the ontology: `parse_label` splits a label into its base and normalized unit, and `unit_from_label` returns just the unit." ] }, { "cell_type": "code", "execution_count": null, "id": "17", "metadata": {}, "outputs": [], "source": [ "print(\"parse_label: \", spec.parse_label(\"Voltage / mV\"))\n", "print(\"unit_from_label:\", spec.unit_from_label(\"Test Time / s\"))" ] }, { "cell_type": "markdown", "id": "18", "metadata": {}, "source": [ "## Unit conversion\n", "Unit conversion is driven by the canonical unit recorded on each quantity. `get_unit_conversion(src, dst)` returns the `(scale, offset)` pair that maps a value in `src` units to `dst` units, or `None` when the units are dimensionally incompatible. The scale/offset form covers affine conversions such as \u00b0C to K." ] }, { "cell_type": "code", "execution_count": null, "id": "19", "metadata": {}, "outputs": [], "source": [ "print(\"s -> h: \", spec.get_unit_conversion(\"s\", \"h\"))\n", "print(\"Ah -> mAh: \", spec.get_unit_conversion(\"Ah\", \"mAh\"))\n", "print(\"degC -> K: \", spec.get_unit_conversion(\"degC\", \"K\"))\n", "print(\"V -> A: \", spec.get_unit_conversion(\"V\", \"A\"))" ] }, { "cell_type": "markdown", "id": "20", "metadata": {}, "source": [ "A `Quantity` knows its own canonical unit, so it can build conversions relative to itself. `convert_to(dst)` converts from the quantity's unit to `dst`, and `convert_from(src)` converts a source unit into the quantity's unit \u2014 exactly what a reader does when ingesting vendor data." ] }, { "cell_type": "code", "execution_count": null, "id": "21", "metadata": {}, "outputs": [], "source": [ "cap = COLUMN_ONTOLOGY[\"discharging_capacity_ah\"]\n", "print(\"canonical unit: \", cap.unit)\n", "print(\"convert_to('mAh'): \", cap.convert_to(\"mAh\"))\n", "print(\"convert_from('mAh'): \", cap.convert_from(\"mAh\"))" ] }, { "cell_type": "markdown", "id": "22", "metadata": {}, "source": [ "Applying a conversion is just `value * scale + offset`. The example below converts a millivolt reading into the canonical volt unit using the pair returned for the voltage quantity." ] }, { "cell_type": "code", "execution_count": null, "id": "23", "metadata": {}, "outputs": [], "source": [ "voltage = COLUMN_ONTOLOGY[\"voltage_volt\"]\n", "scale, offset = voltage.convert_from(\"mV\")\n", "raw_mv = [3200.0, 3300.0, 3400.0]\n", "converted_v = [v * scale + offset for v in raw_mv]\n", "print(\"mV:\", raw_mv)\n", "print(\"V: \", converted_v)" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }