Skip to content

QuantityValue as a base building block: one syntax, consensus @context, x-oold-context synonyms (worked example) #107

Open

Description

@SimonStier

From the OO-LD Scientific Profile work (science, in particular materials science and engineering; MaterialsCommons). Cross-links: emmo-repo/EMMO#376, emmo-repo/EMMO#377, mapping-commons/rda-fair-mappings#85; MSE specialization lives in OpenSemanticWorld-Packages. Companion requirements issue: #108. Companion dataset model issue: #109.

Motivation

QuantityValue (a numeric value plus a unit) is the fundamental building block reused by every
typed field in the reference schemas (Time, Voltage, ElectricCurrent, ...). It must be
clean, reasoner-friendly, and aligned with QUDT and the VIM/EMMO quantity model, so that typed
rows (e.g. CyclingDataRow) and the Dataset storage mechanism build on a solid base.

Current state

world.opensemantic.characteristics.quantitative:

  • QuantityValue (OSW4082937906634af992cf9a1b18d772cf): value (number, required, HasValue) +
    unit (string, HasUnit), described as a numeric value times a QUDT unit.
  • QuantityValueType (OSWac07a46c2cf14f3daec503136861f5ab): metaclass generating per-kind
    value+unit schemas, default_unit resolved by querying HasQuantity along SubClassOf.
  • world.opensemantic.quantities: the fundamental quantities, units and prefixes as OSW items.
    OpenSemanticWorld Packages uses uuid-based identifiers for ALL entities, units included
    (e.g. Item:OSWfc7ffe7e6e00592a8b3f3f472fbef2a2 "Henry"), each carrying an
    exact_ontology_match to the QUDT unit IRI.

Proposal

  • value: number + unit as a unit IRI (QUDT/EMMO), not a free string. Per-quantity subclasses
    restrict the allowed units (enum of unit IRIs or x-oold-range to a unit kind), which maps to
    the *Unit enums in the Python bindings.
  • @context aligns value/unit to QUDT and the VIM/EMMO quantity model, so instances are the
    compact type+value+unit form that a reasoner can classify as a VIM QuantityValue.
  • Model unit as an IRI reference to a unit individual in the data, sidestepping the
    class-vs-individual "punning" question in instances and leaving the ontology-side choice to
    EMMO (resolved on the unreleased EMMO 1.0.4 branch, see status below).

Worked example: one syntax, consensus @context, synonyms in x-oold-context

Pattern, per the current spec (Standard Extensions, "Multi-Mapping" and "Term mappings and
synonyms"): a single syntactical schema; the primary/consensus RDF mapping goes into the
default @context (general vocabularies, here QUDT, are the likely agreement); all further
mappings go into the structured x-oold-context keyword, an object keyed by TERM, each term
holding a dict keyed by the synonym IRI, whose values are promotable JSON-LD term-definition
fragments plus a strippable x-sssom block (round-trips to an SSSOM mapping set; export
prioritizes a synonym by IRI namespace, the "ontology family"). The inline key* shorthand
also exists in the spec but documents at most a couple of alternatives without metadata and
without SSSOM round-trip, so this example keeps every non-primary mapping in x-oold-context.

Class-level correspondences use the same mechanism through the inline type term: instances
carry type (aliased to @type), the class name QuantityValue is itself a context term
mapped to the primary class IRI, and class synonyms attach to that term (the pattern proposed
in mapping-commons/rda-fair-mappings#85; explicit spec coverage is collected as a requirement
in the companion issue #108). Since instances carry type inline,
x-oold-instance-rdf-type is not needed; that keyword only covers instances without an
inline type.

Every mapping below was validated against the source files (pinned local copies: QUDT 3.4.0,
EMMO 1.0.0, PMDco 3.0.0; EMMO 1.0.4 branch checked on top) or the schema.org term pages.

{
  "$id": ".../QuantityValue.schema.json",
  "title": "QuantityValue",
  "type": "object",
  "properties": {
    "type":        { "type": "array", "items": { "type": "string" }, "default": ["QuantityValue"] },
    "value":       { "type": "number" },
    "unit":        { "type": "string", "format": "iri-reference" },
    "standard_uncertainty": { "type": "number",
      "description": "Standard uncertainty, expressed in the unit of value (GUM convention)" },
    "relative_standard_uncertainty": { "type": "number",
      "description": "Dimensionless ratio, NOT percent; ratio-scale quantities only, undefined at value 0; redundant with standard_uncertainty (u = r * |v|), give only one" }
  },
  "required": ["value"],

  // primary (consensus) mapping: QUDT
  "@context": {
    "@version": 1.1,
    "qudt":   "http://qudt.org/schema/qudt/",
    "qunit":  "http://qudt.org/vocab/unit/",
    "schema": "http://schema.org/",
    "obo":    "http://purl.obolibrary.org/obo/",
    "emmo":   "https://w3id.org/emmo#",
    // strict-array property, so the alias declares @container @set (round-trip rule:
    // a single-element array would otherwise return as a scalar and fail re-validation)
    "type":   { "@id": "@type", "@container": "@set" },
    "QuantityValue": "qudt:QuantityValue",
    // numbers stay uncoerced: JSON numbers project to xsd:double, the only fractional
    // datatype that round-trips natively. QUDT hints xsd:decimal on Quantifiable, but our
    // nodes are not typed Quantifiable, so no constraint applies; a QUDT-strict consumer
    // can canonicalize doubles to decimals on ingest.
    "value":  "qudt:value",
    // @vocab (not @id): unit values resolve as context terms first, so subclasses can
    // declare unit individuals as value terms (id-aliases); full IRIs remain valid values
    "unit":   { "@id": "qudt:hasUnit", "@type": "@vocab" },
    "standard_uncertainty":          "qudt:standardUncertainty",
    "relative_standard_uncertainty": "qudt:relativeStandardUncertainty"
  },

  // structured synonyms: term -> { synonym IRI -> term-definition fragment + x-sssom }.
  // Promotable into @context (export picks one synonym per term, prioritized by namespace);
  // strippable x-sssom rows form the SSSOM mapping set (subject_id = primary IRI implicit,
  // object_id = key, predicate_id defaults to skos:exactMatch). mapping_set_id is the SSSOM
  // grouping slot (set-level in SSSOM, so entries sharing it form one set on round-trip);
  // its range is EntityReference, so absolute IRIs or CURIEs, no relative references.
  "x-oold-context": {
    "value": {
      "schema:value":    { "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/schemaorg" } },
      "obo:OBI_0001937": { "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/pmdco" } }
      // still no EMMO synonym: the canonical EMMO shape keeps the nested Numerical node
      // even on the 1.0.4 branch (EMMO#376 open); the branch's new flat quantityValue
      // data property (EMMO_ea67144c) is the candidate once its attachment point settles
    },
    "unit": {
      "schema:unitCode": { "@type": "@vocab",
        "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/schemaorg",
                     "comment": "URL branch of schema:unitCode, carrying the QUDT unit IRI" } },
      "obo:IAO_0000039": { "@type": "@vocab",
        "x-sssom": { "predicate_id": "skos:closeMatch",
                     "mapping_set_id": "https://example.org/mappings/pmdco",
                     "comment": "range is iao:measurement unit label; unit-label vs QUDT-IRI mapping needed" } },
      "emmo:EMMO_eeb06032_dd4f_476e_9da6_aa24302b7588": { "@type": "@vocab",
        "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/emmo",
                     "object_source": "https://w3id.org/emmo",
                     "object_source_version": "https://w3id.org/emmo/1.0.4",
                     "comment": "hasReferencePart; target is the pinned 1.0.4 branch snapshot, where units are individuals (EMMO#377 resolved); unreleased, see status" } }
    },
    "standard_uncertainty": {
      "emmo:EMMO_bba7f0f0_3707_42b9_b3fa_fd04610ef147": {
        "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/emmo",
                     "object_source_version": "https://w3id.org/emmo/1.0.4",
                     "comment": "standardUncertainty (VIM4 3.6); 1.0.4 branch only; attachment point (domain Data) to verify" } }
    },
    // class synonyms on the type term (community alignments; instances carry type inline)
    "QuantityValue": {
      "schema:QuantitativeValue": { "x-sssom": { "predicate_id": "skos:closeMatch",
                     "mapping_set_id": "https://example.org/mappings/schemaorg",
                     "comment": "broader: also covers intervals/ranges" } },
      "obo:OBI_0001931": { "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/pmdco" } },
      "emmo:EMMO_f658c301_ce93_46cf_9639_4eace2c5d1d5": {
        "x-sssom": { "predicate_id": "skos:closeMatch",
                     "mapping_set_id": "https://example.org/mappings/emmo",
                     "comment": "EMMO QuantityValue requires a nested numerical part (EMMO#376)" } }
    }
  }
}

Unit aliases in the per-quantity subclass (id-alias): Time

Units are individuals appearing as instance VALUES, so the term-keyed machinery does not reach
them under "@type": "@id"; the base schema therefore coerces unit with "@type": "@vocab"
(values resolve as context terms first, full IRIs stay valid). The per-quantity subclass then
only declares its unit individuals as value terms, pins them with an enum (which also guards
the @vocab fallback against minting IRIs from typos), sets the SI unit as the default, and
overrides the inherited type default with the more specific class (requirement R3 in #108). Naming: the class is Time, matching the reference library
and QUDT's quantity kind; EMMO names the ISQ quantity Duration (ISO 80000 item 3-9),
attached as a class synonym. All IRIs verified: qunit SEC/MilliSEC/HR,
emmo:Second/MilliSecond/Hour and emmo Duration EMMO_0adabf6f (1.0.4 snapshot),
UO_0000010/0000028/0000032 (via OLS); PMDco has no time/duration quantity class, so only the
units map on the PMDco side.

{
  "$id": ".../Time.schema.json",
  "title": "Time",
  "allOf": [{ "$ref": ".../QuantityValue.schema.json" }],
  "@context": [
    ".../QuantityValue.schema.json",
    {
      "qkind": "http://qudt.org/vocab/quantitykind/",
      // consensus class term: the QUDT quantity kind; QUDT models kinds as INDIVIDUALS,
      // so typing with it puns (the mirror of the unit punning, EMMO#377)
      "Time": "qkind:Time",
      "SEC":      "qunit:SEC",             // unit individuals as value terms
      "MilliSEC": "qunit:MilliSEC",
      "HR":       "qunit:HR"
    }
  ],
  "properties": {
    "type": { "default": ["Time"] },       // overrides the inherited default
    "unit": { "enum": ["SEC", "MilliSEC", "HR"], "default": "SEC",   // SI unit as default
              "x-enum-varnames": ["second", "milli_second", "hour"] }
  },
  "x-oold-context": {
    "Time": {
      "emmo:EMMO_d4f7d378_5e3b_468a_baa1_a7e98358cda7": {
        "x-sssom": { "predicate_id": "skos:exactMatch",
                     "mapping_set_id": "https://example.org/mappings/emmo",
                     "object_source_version": "https://w3id.org/emmo/1.0.4",
                     "comment": "Time (ISO 80000 item 3-7); Duration is its subclass" } }
    },
    "SEC": {
      "emmo:Second":    { "x-sssom": { "predicate_id": "skos:exactMatch",
        "mapping_set_id": "https://example.org/mappings/emmo" } },
      "obo:UO_0000010": { "x-sssom": { "predicate_id": "skos:exactMatch",
        "mapping_set_id": "https://example.org/mappings/pmdco",
        "comment": "UO second; UO units are classes, punning like EMMO#377" } }
    },
    "MilliSEC": {
      "emmo:MilliSecond": { "x-sssom": { "predicate_id": "skos:exactMatch",
        "mapping_set_id": "https://example.org/mappings/emmo" } },
      "obo:UO_0000028":   { "x-sssom": { "predicate_id": "skos:exactMatch",
        "mapping_set_id": "https://example.org/mappings/pmdco", "comment": "UO millisecond" } }
    },
    "HR": {
      "emmo:Hour":      { "x-sssom": { "predicate_id": "skos:exactMatch",
        "mapping_set_id": "https://example.org/mappings/emmo" } },
      "obo:UO_0000032": { "x-sssom": { "predicate_id": "skos:exactMatch",
        "mapping_set_id": "https://example.org/mappings/pmdco", "comment": "UO hour" } }
    }
  }
}

The value terms map onto the per-quantity *Unit enums / x-enum-varnames in the Python
bindings. Naming convention there (verified in
opensemantic.characteristics.quantitative-python, _collection.py): enum member names and
x-enum-varnames are the snake-case serialization of the LaTeX siunitx notation (second,
milli_second, kilo_electron_volt, milli_mole_per_hour_per_meter_squared, ...); the enum
VALUES in those packages are the uuid-based OSW unit items (prefixed units as #OSW...
fragments of the base unit item). Alias tables are generatable from the EMMO 1.0.4 unit
individuals' qudtReference annotations. OpenSemanticWorld Packages is a further alias community for the same mechanism:
it identifies ALL entities by uuid-based IRIs, units included (world.opensemantic.quantities,
e.g. Item:OSWfc7ffe7e6e00592a8b3f3f472fbef2a2 "Henry"), and each unit item already stores its
exact_ontology_match to the QUDT unit IRI, ready-made SSSOM rows for an
https://example.org/mappings/osw set. Reconciling such uuid-based IRIs with the
human-readable QUDT/EMMO ones is precisely what the id-alias mechanism is for. Mapping sets are named after the COMMUNITY, not the namespace: the pmdco set
holds obo: terms (OBI, IAO, UO) and would equally hold pmd: terms, which is exactly why
selection needs the set tag rather than namespace prioritization (R1). The unit enum is
generatable from QUDT's qudt:applicableUnit inventory on quantitykind:Time (verified: it
lists SEC, HR, DAY, ...). A strict QUDT shape can additionally assert the kind as data
(qudt:hasQuantityKind -> quantitykind:Time) instead of relying on the punned type.

Data instance, effective schemas, flavoured RDF exports

Instance of the Time subschema (only one uncertainty field, per the redundancy rule):

{
  "@context": "https://example.org/schemas/Time.schema.json",
  "$schema":  "https://example.org/schemas/Time.schema.json",
  "@id": "https://example.org/m1-duration",
  "type": ["Time"],
  "value": 12.7,
  "unit": "SEC",
  "standard_uncertainty": 0.05
}

Selecting a mapping set (profile) promotes the tagged synonyms into the context; the EFFECTIVE
schema differs from the declared one only in its @context (all JSON Schema keywords are
unchanged, the instance stays valid and untouched). Terms without a synonym in the selected
set fall back to the declared consensus mapping. Promotion strips x-sssom; a fragment that
is then empty counts as bare and inherits the primary term's coercion (here @vocab on
unit). Effective @context per selection (relevant terms, abbreviated notation):

// declared / consensus (no set selected)
"Time": qkind:Time,
"value": qudt:value,  "unit": qudt:hasUnit (@vocab),  "SEC": qunit:SEC,
"standard_uncertainty": qudt:standardUncertainty

// mapping set https://example.org/mappings/emmo selected
"Time": emmo:EMMO_d4f7d378 (Time),
"value": qudt:value (fallback, EMMO#376),  "unit": emmo:EMMO_eeb06032 hasReferencePart (@vocab),
"SEC": emmo:Second,  "standard_uncertainty": emmo:EMMO_bba7f0f0 standardUncertainty

// mapping set https://example.org/mappings/pmdco selected
"Time": qkind:Time (fallback, no PMDco quantity class),
"value": obo:OBI_0001937,  "unit": obo:IAO_0000039 (@vocab),
"SEC": obo:UO_0000010,  "standard_uncertainty": qudt:standardUncertainty (fallback)

The resulting RDF, same instance document in all three views:

# consensus
@prefix qudt:  <http://qudt.org/schema/qudt/> .
@prefix qkind: <http://qudt.org/vocab/quantitykind/> .
@prefix qunit: <http://qudt.org/vocab/unit/> .

<https://example.org/m1-duration>
    a qkind:Time ;                        # punned quantity kind (mirror of EMMO#377)
    qudt:value 1.27E1 ;                   # xsd:double, the natively round-tripping datatype
    qudt:hasUnit qunit:SEC ;              # value term SEC -> QUDT unit individual
    qudt:standardUncertainty 5.0E-2 .
# mapping set emmo (pinned 1.0.4 branch)
@prefix emmo: <https://w3id.org/emmo#> .
@prefix qudt: <http://qudt.org/schema/qudt/> .

<https://example.org/m1-duration>
    a emmo:EMMO_d4f7d378_5e3b_468a_baa1_a7e98358cda7 ;            # Time (class synonym)
    emmo:EMMO_eeb06032_dd4f_476e_9da6_aa24302b7588 emmo:Second ;  # hasReferencePart -> unit individual
    emmo:EMMO_bba7f0f0_3707_42b9_b3fa_fd04610ef147 5.0E-2 ;       # standardUncertainty
    qudt:value 1.27E1 .                   # no EMMO value term (EMMO#376), consensus fallback
# mapping set pmdco
@prefix obo:   <http://purl.obolibrary.org/obo/> .
@prefix qudt:  <http://qudt.org/schema/qudt/> .
@prefix qkind: <http://qudt.org/vocab/quantitykind/> .

<https://example.org/m1-duration>
    a qkind:Time ;                          # no PMDco quantity class, consensus fallback
    obo:OBI_0001937 1.27E1 ;                # has specified numeric value
    obo:IAO_0000039 obo:UO_0000010 ;        # has measurement unit label -> UO second (punned class)
    qudt:standardUncertainty 5.0E-2 .       # no PMDco target, consensus fallback

The three graphs come from the SAME instance document; only the selected mapping set differs.

Round-trip verification (oold-schema validator)

The example is executable: the concrete schema files plus the instance
(QuantityValue.schema.json, Time.schema.json, Time.instance.json, exactly as listed above)
pass this repository's validation pipeline (scripts/validate.mjs <dir>, offline):
meta-schema conformance, $ref composition, round-trip pattern lint, instance validation with
formats enforced, generator satisfiability, remote-context processing, and the lossless
instance -> RDF -> instance round-trip:

Committed instances (schema + formats):
OK         Time.instance.json (instance of Time.schema.json)
JSON-LD - generated instance roundtrip (no loss, reconstruction re-validates):
OK         QuantityValue.schema.json
OK         Time.schema.json
JSON-LD - instance roundtrip (instance -> RDF -> instance):
OK         Time.instance.json -> 4 triples, lossless (compacted)

12/12 checks passed

The flavoured views are verified by a small jsonld.js companion script implementing the
selection rule (promote the synonyms tagged with the chosen mapping_set_id, fall back to the
declared mapping otherwise; a bare fragment inherits the primary coercion) and round-trips the
SAME instance through each effective context. All three views (consensus, emmo, pmdco) are
lossless and produce exactly the N-Quads shown in the Turtle blocks above, fallbacks included.

Two schema properties are load-bearing for this result:

  • the type alias declares @container: @set; a strict-array property without it comes back
    from RDF as a scalar and fails re-validation (enforced statically by the validator's
    pattern lint);
  • numbers are uncoerced (xsd:double), the only fractional datatype the round-trip contract
    (fromRDF with native types) restores as JSON numbers; an xsd:decimal coercion returns
    as a string and fails both the compare and re-validation (see the datatype note in the
    schema).

Notation notes (verified against the current spec):

Validation record (per mapping):

mapping validated against verdict
value -> qudt:value SCHEMA_QUDT.ttl 3.4.0: rdf:Property, "value expressed as a decimal" clean
value -> qudt:numericValue 3.4.0: only owl:AnnotationProperty, no domain/range rejected
unit -> qudt:hasUnit ObjectProperty, range qudt:Unit, no domain; Quantifiable: allValuesFrom qudt:Unit, maxCard 1; qudt:QuantityValue subClassOf Quantifiable clean
standard_uncertainty -> qudt:standardUncertainty on Quantifiable: allValuesFrom xsd:decimal, maxCard 1; same-unit convention (GUM) clean
relative_standard_uncertainty -> qudt:relativeStandardUncertainty SCHEMA_QUDT.ttl 3.4.0: "standard uncertainty divided by the magnitude of the exact value"; dimensionless ratio, not percent clean
standard_uncertainty -> emmo standardUncertainty 1.0.4 branch: EMMO_bba7f0f0, DatatypeProperty, subPropertyOf uncertainty, subPropertyOf dataValue (domain emmo Data) clean syntactically; unreleased; whether QuantityValue nodes fall under the Data domain needs verification
type -> schema:QuantitativeValue schema.org: point value or interval for product characteristics class synonym, closeMatch (broader, also covers intervals)
unit value terms SEC -> obo:UO_0000010, MilliSEC -> obo:UO_0000028 OLS: UO second / millisecond; UO units are OWL classes clean conceptually; class punning caveat like EMMO#377
expanded / coverage factor / probability / interval -> (any vocabulary) QUDT 3.4.0, EMMO 1.0.0 and 1.0.4, PMDco 3.0.0, schema.org out of scope for the base schema; deferred to a node-shaped uncertainty characteristic
value -> schema:value schema.org: expected type Number (recommended for QuantitativeValue) clean
unit -> schema:unitCode schema.org: Text or URL, "UN/CEFACT Common Code (3 characters) or a URL"; we use the URL branch with the QUDT unit IRI clean
type -> obo:OBI_0001931 pmdco.ttl: scalar value specification = "a numeral and a unit label", subClassOf OBI_0001933, min 1 OBI_0001937 clean, flat shape matches ours
value -> obo:OBI_0001937 DatatypeProperty, domain OBI_0001933 (value specification); our node is typed OBI_0001931 which is a subclass, domain-consistent clean
unit -> obo:IAO_0000039 ObjectProperty, Functional, range IAO_0000003 (measurement unit label) caveat: entails the QUDT unit IRI is a "measurement unit label" individual; needs the unit-label <-> QUDT-IRI mapping
type -> emmo QuantityValue emmo.ttl 1.0.0: EMMO_f658c301, "numerical + reference", example "6.8 m" clean as class correspondence; shape differs (nested value)
unit -> emmo hasReferencePart EMMO_eeb06032: domain QuantityValue, range EMMO_18ce5200 (MeasurementUnit is a subclass); unchanged on the 1.0.4 branch clean structurally; the EMMO#377 class-vs-individual caveat is resolved on the 1.0.4 branch (units are individuals there)
unit -> emmo hasMeasurementUnit EMMO_bed1d005: domain is PhysicalQuantity, NOT QuantityValue; unchanged on the 1.0.4 branch rejected (domain violation)
value -> emmo (any term) QuantityValue requires exactly-1 hasNumericalPart -> a Numerical NODE (EMMO_4ce76d7f); restriction unchanged on the 1.0.4 branch (plus exactly-1 hasReferencePart). hasSIQuantityValue was rejected earlier (domain PhysicalQuantity, "3 cm" string) and is REMOVED on the 1.0.4 branch structural mismatch, term mapping impossible; needs a rule/SSSOM-level mapping or the EMMO#376 simplification

Key insight: the flat consensus shape maps cleanly to QUDT, schema.org, and PMDco/OBI, but not
to EMMO, whose nested parthood shape (numerical part as a separate node) cannot be produced by
JSON-LD term mapping alone. That is precisely the EMMO#376 discussion; until resolved, the EMMO
synonyms cover the unit only and the value mapping is delegated to SSSOM/rules.

EMMO status (checked 2026-07-27): still no (pre)release implementing an EMMO#376 decision;
latest release/tag is 1.0.3 (2026-01-20). However, the unreleased 1.0.4 BRANCH (74 commits
ahead, activity through 2026-06-30) contains directly relevant changes:

  • Units became individuals with human-readable IRIs, e.g. emmo:Second (PR #392, module
    disciplines/units/unit-individuals.ttl aggregating ~1600 individuals; unit classes are kept
    as their types). This resolves the EMMO#377 punning caveat on our unit synonym, and each
    individual carries qudtReference (e.g. "http://qudt.org/vocab/unit/SEC"), ucumCode and
    schema:unitCode annotations, so EMMO itself ships the QUDT <-> EMMO unit mapping needed for
    individual-level aliases (see Requirements collection: mapping-set selection, class aliases, individual (id) aliases, co-emission, x-sssom slots, version pinning #108, id-alias requirement).
  • A VIM4-aligned data-property family arrived (PR #397): dataValue (renamed from
    hasDataValue, domain emmo Data) with subproperties quantityValue (altLabel measuredValue),
    meanQuantityValue, uncertainty, standardUncertainty, coverageInterval,
    coverageProbability. quantityValue is the likely flat value-mapping candidate and
    standardUncertainty an uncertainty synonym candidate once released; whether asserting them
    directly on a QuantityValue node is domain-consistent (QuantityValue subClassOf Metrological
    vs dataValue's domain Data) still needs verification.
  • The EMMO#376 class shape is unchanged: QuantityValue still requires exactly-1
    hasNumericalPart and exactly-1 hasReferencePart, and the issue is still OPEN.

Decision: the 1.0.4 branch is pinned as the EMMO alignment target. A snapshot of the validated
modules (metrology, data, symbolic, unit individuals) at commit 1249265 (2026-06-30) is
pinned locally. Warning: the published build at
https://w3id.org/emmo/1.0.4 is STALE (predates the April 2026 merges; lacks the dataValue
family and the unit individuals), so validate against the snapshot, not the resolved URL.
Watch EMMO#376/#377 and re-verify the synonyms when 1.0.4 (or later) is actually released.

Uncertainty coverage

Reference frame: GUM (JCGM 100, Guide to the Expression of Uncertainty in Measurement) and
VIM (JCGM 200, International Vocabulary of Metrology; VIM4 = the fourth edition, which the
EMMO 1.0.4 annotations cite) are the BIPM/JCGM metrology references all target vocabularies
align to. How far the uncertainty fields reach across them (all cells verified against the
local ontology files / term pages, 2026-07-27; "-" = no term):

GUM/VIM4 concept QUDT 3.4.0 EMMO 1.0.0 (released) EMMO 1.0.4 branch PMDco 3.0.0 schema.org
standard uncertainty (VIM4 3.6) qudt:standardUncertainty (on Quantifiable, maxCard 1; plus standardUncertaintySN) - (structural only, see below) standardUncertainty (data property) - -
relative standard uncertainty qudt:relativeStandardUncertainty - - - -
generic dispersion parameter (VIM4 3.1) - MeasurementUncertainty class, attached to the QUANTITY via hasMetrologicalUncertainty (domain ObjectiveProperty) uncertainty (super data property, "does not specify how defined") pmd:PMD_0001036 "uncertainty" (an ICE class, no dedicated property) schema:marginOfError (domain Observation, not QuantitativeValue)
expanded uncertainty / coverage factor k - - - - -
coverage interval (VIM4 3.12) - - coverageInterval - -
coverage probability (VIM4 3.13) - - coverageProbability - -
mean / expected value - - meanQuantityValue - -
distribution / evaluation type (GUM Type A/B) - - - - -

Assessment:

  • The dominant single-number case means the standard uncertainty; qudt:standardUncertainty
    and qudt:relativeStandardUncertainty are the only RELEASED flat mappings, so they are the
    consensus @context entries (the flat standard_uncertainty /
    relative_standard_uncertainty terms).

  • Released EMMO (1.0.0-1.0.3) models uncertainty structurally: a MeasurementUncertainty node
    attached to the quantity (not the QuantityValue) via hasMetrologicalUncertainty. Like the
    value (EMMO#376), this is term-unmappable from a flat scalar; SSSOM/rule level only. The
    1.0.4 branch's standardUncertainty data property becomes a direct synonym candidate on
    release (same domain caveat as quantityValue).

  • PMDco offers only the class; an uncertainty would be another value specification about the
    measurement datum (structural, term-unmappable). schema:marginOfError sits on Observation,
    so it is not a synonym for a QuantityValue slot; usable only at dataset/observation level.

  • Gaps everywhere: expanded uncertainty and coverage factor appear in NO target vocabulary;
    coverage interval/probability only on the EMMO branch; relative standard uncertainty only in
    QUDT; distribution/evaluation type nowhere.
    Is uncertainty actually a scalar? No; only its consensus serialization is:

  • Physically, a standard uncertainty is itself a quantity carrying the SAME unit as the value
    (u of a voltage is in volts). The scalar form works only under an explicit unit-inheritance
    rule: the bare number is the standard uncertainty expressed in the unit of the enclosing
    QuantityValue. QUDT hard-codes exactly this convention (allValuesFrom xsd:decimal on
    Quantifiable), and the EMMO 1.0.4 branch properties are likewise datatype properties.
    Released EMMO instead types it as a full node (MeasurementUncertainty, itself an
    ObjectiveProperty, i.e. a quantity in its own right).

  • The scalar cannot express: asymmetric bounds (+0.3/-0.2), expanded uncertainty with its
    coverage factor, relative form, coverage interval endpoints, distribution/evaluation type.

  • The base schema keeps only the two well-defined flat fields directly on QuantityValue:
    standard_uncertainty (in the unit of value, GUM convention) and
    relative_standard_uncertainty (dimensionless ratio, NOT percent; ratio-scale quantities
    only, undefined at value 0; redundant with the absolute form, give only one). No nesting: a
    transparent nested record emits zero triples of its own and all target models are flat, so
    it would add @nest machinery without semantic content. Reflected in model.py.

  • Everything else (expanded uncertainty, coverage factor, coverage probability and interval,
    distribution, budget) is out of scope for the base schema and deferred to a future separate,
    node-shaped uncertainty characteristic; that shape matches released EMMO's structural
    pattern (a MeasurementUncertainty node via hasMetrologicalUncertainty on the quantity) and
    can pick up EMMO 1.0.4's coverageInterval / coverageProbability / meanQuantityValue when
    released.

Alignment targets beyond EMMO

  • PMDco (BFO/OBO): obi:scalar value specification (OBI_0001931) = numeral (obi:has specified
    numeric value, OBI_0001937) + unit label (iao:measurement unit label, IAO_0000003) is nearly
    isomorphic to our {value, unit}; units there are UO/label-based, so a unit-label <-> QUDT-IRI
    mapping is part of the alignment.

  • BattINFO stack (battery domain): battinfo 0.7.0 is an import manifest that defines no
    domain terms itself and pins the EMMO domain ontologies domain-battery 0.19.0 and
    domain-electrochemistry 0.34.0 (pinned local copies). Validated anchors
    for the battery reference schemas: BatteryCell (battery_68ed592a) for the sample,
    BatteryCycling (battery_1d33b96d) and CyclingTest (electrochemistry_435b22d4) for the test
    process, and TestTime (electrochemistry_27b3799c) / CellVoltage (electrochemistry_4ebe2ef1)
    / CellCurrent (electrochemistry_637ee9c4) as class synonyms for the CyclingDataRow field
    types. BattINFO also ships a generated JSON-LD context with 4844 readable label terms
    resolving to the hashed EMMO IRIs, a large-scale precedent for the label/id-alias
    machinery (Requirements collection: mapping-set selection, class aliases, individual (id) aliases, co-emission, x-sssom slots, version pinning #108, R2/R3).

Relation to EMMO discussions

  • Document the preferred way to represent of quantities emmo-repo/EMMO#376 (document the preferred representation of quantities): proposes a compact
    type + numeric value + unit form and a SWRL rule to infer a VIM QuantityValue. Our compact
    OO-LD QuantityValue is exactly that shape; aligning the @context lets the rule fire. The
    1.0.4 branch's quantityValue data property points the same way.
  • Relating quantity individuals to units emmo-repo/EMMO#377 (relating quantity individuals to units): the unit class-vs-individual
    punning problem. Modeling unit as an IRI reference in the data avoids punning at the data
    layer; the 1.0.4 branch resolves it ontology-side with unit individuals.

References

  • QuantityValue slot schema: world.opensemantic.characteristics.quantitative/base/Category/OSW4082937906634af992cf9a1b18d772cf.slot_jsonschema.json
  • QuantityValueType slot schema: world.opensemantic.characteristics.quantitative/base/Category/OSWac07a46c2cf14f3daec503136861f5ab.slot_jsonschema.json
  • Class/type aliases via the inline type term: mapping-commons/rda-fair-mappings discussion 85

Activity

  1. added
    scienceDomain-driven: science / materials science and engineering profile requests
    profileApplication-profile layer: raised by or affecting OO-LD profiles
    on Aug 1, 2026
  2. LukasGold commented on Aug 3, 2026

    @LukasGold
    Contributor

    Converting the EU Battery Passport model (batterypass/BatteryPassDataModel 1.2.0, the model behind
    EU 2023/1542) from SAMM to OO-LD brought us here: 42 of its 141 properties carry a samm-c:unit, and
    SAMM's own JSON Schema generator drops every one of them, so the shipped JSON has
    "batteryMass": 699 with no unit anywhere. Useful data point for this issue, maybe: QuantityValue
    earns its place as a base building block outside the scientific profile too.

    One thing I think is worth settling before the shape is fixed.

    A default unit does not reach RDF, so the round-trip is silently lossy

    The Time subclass sets "unit": { "enum": [...], "default": "SEC" }, which is the natural way to
    say "this quantity is in seconds unless stated otherwise". But default is a JSON Schema annotation
    and is not materialized, so an instance that relies on it emits no unit triple:

    # {"value": 12.7, "unit": "SEC"}
    <x> qudt:hasUnit qunit:SEC .
    <x> qudt:value   "1.27E1"^^xsd:double .
    
    # {"value": 12.7}          <- unit omitted, default SEC
    <x> qudt:value   "1.27E1"^^xsd:double .

    The quantity becomes dimensionless on export, and nothing flags it: the round-trip check compares
    what was in the instance, and the unit was never there, so no property is reported as lost. The
    instance survives the trip while the quantity does not.

    The practical consequence is that unit has to be repeated in every instance to be meaningful, which
    is exactly the redundancy the schema-level default was meant to remove.

    To be clear, this is not a defect in #107: its worked instance carries "unit": "SEC" explicitly and
    its Turtle is correct. The point is only that the default cannot be leaned on, and it is the obvious
    thing for an implementer to lean on.

    Worth noting the spec already solves the equivalent problem for the class. OOLD-INS-008 requires
    tooling to materialize the schema-declared rdf:type on export, precisely so a schema-level fact
    reaches RDF without living in the data. There is no counterpart for a schema-declared value.

    Two ways to close it

    A @context cannot fix this: it maps keys to IRIs, it does not inject values, so absent data yields
    no triple however the context is authored. The fact has to arrive either by tooling materializing it
    at export, or by inference from something already materialized. Hence two options.

    Option 1: carry the unit on the type. Works today, no new keyword.

    x-oold-instance-rdf-type is already materialized under OOLD-INS-008, so a subclass naming a
    unit-bearing class gets it into RDF for free, with no unit property in the instance:

    { "allOf": [{ "$ref": "Time.schema.json" }],
      "x-oold-instance-rdf-type": ["ex:TimeInSeconds"],
      "properties": { "unit": { "const": "SEC" } } }

    {"value": 12.7} then produces (verified with jsonld.toRDF):

    <x> a ex:TimeInSeconds ; qudt:value "1.27E1"^^xsd:double .

    and one axiom in the vocabulary yields the unit:

    ex:TimeInSeconds rdfs:subClassOf
        [ a owl:Restriction ; owl:onProperty qudt:hasUnit ; owl:hasValue qunit:SEC ] .

    Costs a class per quantity-and-unit pair, and the triple only exists after reasoning, so plain SPARQL
    over the raw graph still sees no unit. (I verified the JSON-LD half; the hasValue entailment is
    standard but I did not run a reasoner.)

    Option 2: opt-in materialization of a schema-fixed value.

    "unit": { "enum": ["SEC", "MilliSEC", "HR"], "default": "SEC", "x-oold-materialize-default": true }

    with an export rule parallel to OOLD-INS-008: tooling exporting an instance MUST materialize the
    schema-declared value of an absent property so marked. Two details make or break it:

    • Opt-in, not every default. A blanket rule would make "default": 0 or "default": "" start
      emitting triples and would turn genuine fallbacks into assertions.
    • The round-trip compare needs one normalization rule, or this breaks it: storage holds
      {value}, export adds the unit, reimport yields {value, unit} and the compare sees an added key.
      The rule that resolves it is a property whose value equals its schema default is equivalent to its
      omission
      , which is sound because that is what a default means.

    There is a symmetry argument here: the spec already derives a frame @default from the schema for
    the RDF-to-JSON direction, so a schema-declared value can be restored on the way back but never
    asserted on the way out. Option 2 is the missing half.

    approach instance size triple without reasoning new spec surface modelling cost
    repeat unit per instance largest yes none none
    Option 1, unit on the type smallest no none a class per quantity-and-unit pair
    Option 2, materialize the default smallest yes one keyword, one export rule, one compare rule none

    We would lean to Option 2, since it keeps the graph self-describing for consumers that do not reason,
    but Option 1 needs nothing new and may well be enough where a reasoner is in the pipeline anyway.
    Either way it seems worth stating explicitly which one a QuantityValue subclass is expected to use,
    because the default currently looks load-bearing and is not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

profileApplication-profile layer: raised by or affecting OO-LD profilesscienceDomain-driven: science / materials science and engineering profile requests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions