Repository navigation
QuantityValue as a base building block: one syntax, consensus @context, x-oold-context synonyms (worked example) #107
Description
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_unitresolved by queryingHasQuantityalongSubClassOf. - 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_matchto the QUDT unit IRI.
Proposal
value: number+unitas a unit IRI (QUDT/EMMO), not a free string. Per-quantity subclasses
restrict the allowed units (enum of unit IRIs orx-oold-rangeto a unit kind), which maps to
the*Unitenums in the Python bindings.@contextalignsvalue/unitto 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
unitas 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 fallbackThe 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
typealias 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
(fromRDFwith native types) restores as JSON numbers; anxsd:decimalcoercion 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):
format: iri-referenceis the spec-recommended default for IRI-valued strings (accepts
absolute IRIs, compact IRIs likequdt:V, and context-relative references).- Synonym fragments repeat
"@type": "@id"explicitly: only bare ({}) entries are specified
to inherit the primary term's coercion on promotion. x-sssom.commentis a standard SSSOM slot; the spec text enumeratespredicate_id,
confidence,mapping_justification. Whether the full SSSOM slot set rides along is a
clarification collected in Requirements collection: mapping-set selection, class aliases, individual (id) aliases, co-emission, x-sssom slots, version pinning #108.- The inline
key*shorthand remains available to co-emit consensus synonyms as redundant
triples; a co-emission policy overx-oold-contextentries is not specified and is likewise
collected as a requirement in Requirements collection: mapping-set selection, class aliases, individual (id) aliases, co-emission, x-sssom slots, version pinning #108. - Uncertainty is flat and minimal: only the two released mappings (
standard_uncertainty,
relative_standard_uncertainty), conventions stated in the field descriptions (absolute
value in the unit ofvalueper GUM; relative value a dimensionless ratio, not percent).
JSON-LD@nestcan present a grouped JSON record with flat RDF but adds machinery for zero
triples and is not used here; where it is used, synonym fragments for nested terms must
repeat the@nestentry, since promotion replaces the whole term definition. - Version pinning: TERM IRIs stay unversioned; all four target ontologies version only the
ontology document via owl:versionIRI (https://w3id.org/emmo/1.0.4,
https://w3id.org/pmd/co/3.0.0, http://qudt.org/3.4.0/schema/qudt, schema.org snapshots).
Putting the version into the prefix ("emmo": "https://w3id.org/emmo/1.0.4#") would mint
non-canonical term IRIs: the versioned document URL resolves, but the terms it declares live
under the unversioned namespace, so the emitted triples would miss the canonical terms. The
validated source version is instead recorded per mapping with the standard SSSOM slots
object_source/object_source_version(demonstrated on the EMMO synonyms above); a
document-level pin is collected as requirement R6 in Requirements collection: mapping-set selection, class aliases, individual (id) aliases, co-emission, x-sssom slots, version pinning #108.
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 ourunitsynonym, and each
individual carriesqudtReference(e.g. "http://qudt.org/vocab/unit/SEC"),ucumCodeand
schema:unitCodeannotations, 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 subpropertiesquantityValue(altLabel measuredValue),
meanQuantityValue,uncertainty,standardUncertainty,coverageInterval,
coverageProbability.quantityValueis the likely flat value-mapping candidate and
standardUncertaintyan 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
andqudt:relativeStandardUncertaintyare the only RELEASED flat mappings, so they are the
consensus@contextentries (the flatstandard_uncertainty/
relative_standard_uncertaintyterms). -
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'sstandardUncertaintydata property becomes a direct synonym candidate on
release (same domain caveat asquantityValue). -
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 ofvalue, 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@nestmachinery 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@contextlets the rule fire. The
1.0.4 branch'squantityValuedata 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. Modelingunitas 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
LukasGold commentedon Aug 3, 2026
Converting the EU Battery Passport model (
batterypass/BatteryPassDataModel1.2.0, the model behindEU 2023/1542) from SAMM to OO-LD brought us here: 42 of its 141 properties carry a
samm-c:unit, andSAMM's own JSON Schema generator drops every one of them, so the shipped JSON has
"batteryMass": 699with no unit anywhere. Useful data point for this issue, maybe:QuantityValueearns 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
Timesubclass sets"unit": { "enum": [...], "default": "SEC" }, which is the natural way tosay "this quantity is in seconds unless stated otherwise". But
defaultis a JSON Schema annotationand is not materialized, so an instance that relies on it emits no unit triple:
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
unithas to be repeated in every instance to be meaningful, whichis 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 andits 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-008requirestooling to materialize the schema-declared
rdf:typeon export, precisely so a schema-level factreaches RDF without living in the data. There is no counterpart for a schema-declared value.
Two ways to close it
A
@contextcannot fix this: it maps keys to IRIs, it does not inject values, so absent data yieldsno 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-typeis already materialized underOOLD-INS-008, so a subclass naming aunit-bearing class gets it into RDF for free, with no
unitproperty 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 withjsonld.toRDF):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
hasValueentailment isstandard but I did not run a reasoner.)
Option 2: opt-in materialization of a schema-fixed value.
with an export rule parallel to
OOLD-INS-008: tooling exporting an instance MUST materialize theschema-declared value of an absent property so marked. Two details make or break it:
default. A blanket rule would make"default": 0or"default": ""startemitting triples and would turn genuine fallbacks into assertions.
{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
@defaultfrom the schema forthe 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.
unitper instanceWe 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
QuantityValuesubclass is expected to use,because the default currently looks load-bearing and is not.