1. Executive Summary
This document analyzes the interoperability between LinkML and ASM (Allotrope Simple Model) - two data modeling frameworks with overlapping goals but fundamentally different architectures. It defines a common canonical subset that enables bidirectional mapping and enumerates hard gaps where information is irreversibly lost in translation.
LinkML is a technology-neutral, YAML-authored data modeling language that generates downstream artifacts (JSON-Schema, SHACL, OWL, Python dataclasses, SQL DDL, etc.). It targets general-purpose data modeling with semantic hooks.
ASM is a data modeling framework for scientific and operational data, designed to support GxP-regulated environments but not limited to them. It combines JSON Schema (Draft 2020-12) with custom $asm.* extension keywords, RDF/OWL vocabularies for semantic typing, and QUDT for units of measurement. ASM instance documents are plain JSON - not JSON-LD, not RDF.
|
Dimension |
LinkML |
ASM |
|
Primary audience |
Data modelers, ontologists, developers |
Life sciences, manufacturing, GxP-regulated industries |
|
Schema language |
YAML (custom metamodel) |
JSON Schema 2020-12 + $asm.* extensions |
|
Semantic layer |
Optional (class_uri, slot_uri, mappings) |
Required (RDF/OWL vocabulary) |
|
Instance format |
JSON, YAML, CSV, RDF (multi-target) |
JSON (normative); any format representing the JSON metamodel (YAML, TOON, etc.) is usable in principle |
|
Composition model |
Import chain (named dependency DAG) |
Manifest root declaration + implicit $ref DAG |
|
Validation strategy |
Single schema (generated from model) |
Two-layer: JSON Schema + vocabulary resolution |
|
Unit system |
Annotation metadata (unit: slot) |
Validated type system (QUDT symbol → IRI mapping) |
2. Framework Profiles
2.1 LinkML
2.1.1 Core Metamodel Elements
|
Element |
Purpose |
Key Properties |
|
SchemaDefinition |
Top-level container |
id (IRI), name, prefixes, imports, default_range, classes, slots, types, enums, subsets |
|
ClassDefinition |
Entity template (≈ table, JSON object type) |
is_a, mixins, slots, attributes, slot_usage, abstract, mixin, class_uri, unique_keys, rules, tree_root |
|
SlotDefinition |
Field/property (≈ column, JSON key) |
range, multivalued, required, recommended, identifier, minimum_value, maximum_value, pattern, slot_uri, inverse, ifabsent, any_of |
|
TypeDefinition |
Scalar type extending XSD |
typeof, uri (XSD datatype), base (Python type) |
|
EnumDefinition |
Controlled vocabulary |
permissible_values (with meaning, description), reachable_from (dynamic enums) |
|
SubsetDefinition |
Organizational grouping |
Tag-based; no runtime semantics |
2.1.2 Inheritance Model
- Single is_a - forms a strict tree; slots propagate down
- Multiple mixins - cross-cutting concerns; homeomorphic rule (mixin → mixin, non-mixin → non-mixin)
- slot_usage - per-class monotonic refinement of inherited slots (can narrow range, add constraints; cannot widen)
- abstract - prevents direct instantiation
- Materialization - linkml generator can flatten all inherited slots into leaf classes
2.1.3 Slot System
|
Feature |
Description |
|
Range |
Class, Type, Enum, linkml:Any, or boolean combination (any_of, all_of, none_of, exactly_one_of) |
|
Cardinality |
required, multivalued, minimum_cardinality, maximum_cardinality, exact_cardinality |
|
Identity |
identifier: true (primary key), key: true (alternate key) |
|
Default values |
ifabsent with typed syntax: string(...), int(...), float(...), True/False |
|
Inlining |
inlined, inlined_as_list, inlined_as_dict - controls JSON serialization of object references |
|
Logical properties |
transitive, symmetric, antisymmetric, reflexive, locally_reflexive |
|
Inverse |
inverse names the reverse relationship |
|
Refinement |
slot_usage narrows constraints monotonically per class |
2.1.4 Type System
Built-in types wrapping XSD:
|
LinkML Type |
XSD |
Python |
JSON |
|
string |
xsd:string |
str |
string |
|
integer |
xsd:integer |
int |
integer |
|
float |
xsd:float |
float |
number |
|
double |
xsd:double |
float |
number |
|
decimal |
xsd:decimal |
Decimal |
number |
|
boolean |
xsd:boolean |
bool |
boolean |
|
date |
xsd:date |
date |
string |
|
datetime |
xsd:dateTime |
datetime |
string |
|
time |
xsd:time |
time |
string |
|
uri |
xsd:anyURI |
URI |
string |
|
curie |
- |
Curie |
string |
|
uriorcurie |
- |
URIorCURIE |
string |
|
nodeidentifier |
- |
NodeIdentifier |
string |
Custom types extend built-ins via typeof.
2.1.5 Enumerations
enums:
SampleRoleType:
permissible_values:
CONTROL:
description: A control sample
meaning: OBI:0000070
STANDARD:
description: A reference standard
meaning: OBI:0000938
- Static permissible values with optional meaning (ontology CURIE)
- Dynamic enums via reachable_from (query ontology branch at schema-build time)
- Enum values can have descriptions, aliases, annotations
2.1.6 Arrays (NDArray)
classes:
SpectralData:
attributes:
intensities:
range: float
array:
dimensions:
- alias: wavelength
minimum_cardinality: 1
- alias: time_point
minimum_number_dimensions: 1
maximum_number_dimensions: 3
- Shape constraints: min/max/exact dimensions, per-dimension cardinality
- Dtype constraint via slot range
- Integration: numpy, dask, zarr, HDF5, video files (via numpydantic)
- No per-dimension semantic metadata (units, scale, concept)
2.1.7 Constraints and Rules
|
Mechanism |
Scope |
Example |
|
pattern |
Slot value regex |
pattern: "^[A-Z]{2}\\d{4}$" |
|
structured_pattern |
Reusable sub-patterns |
syntax: "{float} {unit.length}" |
|
minimum_value / maximum_value |
Numeric bounds |
minimum_value: 0, maximum_value: 999 |
|
unique_keys |
Compound uniqueness |
Multiple slots forming a unique tuple |
|
rules |
If-then logic |
preconditions → postconditions on slot values |
|
Boolean combinators |
Slot-level |
any_of, all_of, none_of, exactly_one_of |
|
equals_expression |
Computed/derived values |
"{age_in_months} / 12" |
|
string_serialization |
Derived string from other slots |
"{first} {last}" |
2.1.8 Semantic Layer
- class_uri / slot_uri - map elements to ontology IRIs
- prefixes - CURIE prefix declarations
- mappings - exact_mappings, close_mappings, related_mappings, narrow_mappings, broad_mappings
- meaning on enum values - ontology term CURIE
- JSON-LD context generation - automatic from schema
- Generators - SHACL, ShEx, OWL export
2.1.9 Modularity
- imports - dependency chain (local files or remote URIs)
- linkml:types - standard type library
- Subsets - tag elements for organizational purposes (no runtime effect)
- Programmatic manipulation - SchemaView API for traversal and derivation
2.1.10 Metadata and Annotations
|
Slot |
Purpose |
|
description |
Human-readable description (markdown OK) |
|
aliases |
Alternative names |
|
structured_aliases |
With predicate, source, language |
|
deprecated |
Deprecation notice |
|
comments |
Author notes |
|
notes |
Implementation notes |
|
see_also |
Reference URIs |
|
annotations |
Arbitrary key-value pairs (extensible) |
|
unit |
UCUM/QUDT/IEC61360 annotation |
|
instantiates |
Metamodel extension reference |
2.1.11 Generators (Output Targets)
JSON-Schema, SHACL, ShEx, OWL, Python dataclasses, Pydantic, SQL DDL, SQL Alchemy, TypeScript, Java, GraphQL, Protobuf, Markdown documentation, CSV/TSV data dictionaries.
2.2 ASM (Allotrope Simple Model)
2.2.1 Architecture Overview
ASM is a two-layer validation system:
Key architectural principle: ASM is NOT JSON-LD. Property names like @type and @id resemble JSON-LD but have no JSON-LD processing semantics. There is no @context, no expansion/compaction, no graph normalization.
2.2.2 Schema Portfolio
|
Schema |
Content |
|
core.schema |
Primitive value types, XSD mappings, polymorphic value wrappers |
|
manifest.schema |
Document-level metadata: @id, @type, vocabulary[], json-schemas[], shapes[] |
|
hierarchy.schema |
Reusable document structure patterns (aggregate, indexed, value, quantity, class, binary datum) |
|
cube.schema |
Multidimensional scientific data (W3C Data Cube adapted) |
|
units.schema |
Auto-generated unit symbol catalog (QUDT + Allotrope extensions); located under qudt/REC/ |
|
<domain>.schema |
Domain-specific models (e.g., uv-vis.schema, qpcr.schema, hplc.schema, electronic-lab-notebook.schema) |
2.2.3 Value Type System
ASM uses polymorphic value types - each value can appear in simple literal form OR as a typed object:
|
Type |
Simple Form |
Typed Object Form |
|
tStringValue |
"text" |
{"@type": "sample identifier", "value": "text"} |
|
tDoubleValue |
3.14 |
{"@type": "peak area", "value": 3.14} |
|
tIntegerValue |
42 |
{"@type": "cycle count", "value": 42} |
|
tBooleanValue |
true |
{"@type": "detection flag", "value": true} |
|
tDateTimeValue |
"2026-06-17T10:30:00Z" |
{"@type": "measurement time", "value": "..."} |
Additional structured types:
|
Type |
Structure |
Purpose |
|
tQuantityValue |
{value: number, unit: string} |
Physical measurement with SI unit |
|
tRangeValue |
{minInclusive?, maxInclusive?, minExclusive?, maxExclusive?, unit?} |
Interval specification with mutual-exclusion constraints |
|
tClass |
"control sample role" |
Ontology class label (resolved via skos:prefLabel) |
|
tUnit |
"µg/mL" |
Unit symbol mapped to QUDT IRI |
|
tNamed |
Named individual with namespace |
Identity reference |
|
tReference |
JSON Pointer or IRI |
Cross-document reference |
The polymorphic dual form is a fundamental ASM design decision - it allows the same field to carry semantic type information when needed while remaining lightweight when context is sufficient.
2.2.4 Document Pattern System ($asm.pattern)
Every structural element in an ASM document conforms to one of ten named patterns:
|
Pattern |
Purpose |
Key Characteristics |
|
aggregate datum |
Container grouping related documents |
Root-level; contains arrays of child documents |
|
indexed datum |
Array of homogeneous items |
Ordered (@index) or unordered |
|
value datum |
Leaf scalar property |
Single typed value |
|
quantity datum |
Physical measurement |
{value, unit} pair with optional unit constraint |
|
class datum |
Enumerated ontology class |
String constrained to subclass labels via $asm.value-sub-class-of — open taxonomy (extending the vocabulary adds valid values) |
|
category datum |
Enumerated named individual |
String constrained to instance labels via $asm.value-instance-of — closed enumeration (fixed set of named individuals) |
|
boolean datum |
Boolean scalar property |
Leaf tBooleanValue (e.g., auto focus enabled setting) |
|
any datum |
Unconstrained value |
Property accepting any value type (e.g., description in electronicProjectRecord) |
|
binary datum |
File reference |
POSIX path to co-located companion file |
|
datacube |
Multidimensional array |
Dimension/measure structure with metadata |
These patterns are declared via $asm.pattern in the schema and form the structural grammar of ASM documents. Every node in the document tree is classifiable by pattern.
class datum vs category datum: Both constrain string values to vocabulary-backed labels, but differ in resolution semantics:
- class datum uses $asm.value-sub-class-of - permissible values are skos:prefLabels of OWL classes that are rdfs:subClassOf a specified parent class. The enumeration is open: adding a new subclass to the vocabulary automatically extends the set of valid values. Used for open taxonomies (device types, material role types, technique types).
- category datum uses $asm.value-instance-of - permissible values are skos:prefLabels of OWL named individuals that are instances of a specified class. The enumeration is closed: the set of named individuals is fixed by the ontology. Used for closed enumerations (lifecycle status, analysis type, verdict types).
The Digital Analytical Method schema (CR/2026/06) is the first ASM schema to use category datum — for analysis type (11 fixed analysis categories) and life cycle status (5 lifecycle states).
2.2.5 Datacube Model
Adapted from W3C Data Cube for multidimensional scientific data:
{
"cube-structure": {
"dimensions": [
{
"@componentDatatype": "double",
"concept": "wavelength",
"unit": "nm",
"scale": "interval"
}
],
"measures": [
{
"@componentDatatype": "double",
"concept": "absorbance",
"unit": "AU",
"$asm.fill-value": -999.0
}
]
},
"data": {
"dimensions": [[200.0, 201.0, 202.0, ...]],
"measures": [[0.142, 0.156, 0.173, ...]],
"points": [[200.0, 0.142], [201.0, 0.156], [202.0, 0.173]]
}
}
Key datacube features:
- Per-dimension semantic metadata: concept name, unit, measurement scale (nominal/ordinal/cardinal/interval/range)
- Per-measure fill values ($asm.fill-value) for sparse data representation
- Supported datatypes: double, float, decimal, integer, byte, int, short, long, string, boolean, dateTime
- Dimensions and measures as parallel flat arrays (columnar layout)
- Points: row-oriented representation (array of tuples) as an alternative to the columnar dimensions/measures layout
2.2.6 Enumeration and Vocabulary Resolution
ASM enumerations use a two-level resolution mechanism:
- Schema level - enum array with human-readable labels:
json "sample role type": { "$asm.pattern": "class datum", "$asm.value-sub-class-of": "http://purl.allotrope.org/ontologies/role#AFRL_0000035", "enum": ["control sample role", "standard sample role", "blank role"], "type": "string" }
- Vocabulary level - each label resolves to an ontology class IRI via skos:prefLabel lookup against the vocabulary closure declared in the manifest.
The constraint $asm.value-sub-class-of ensures all permissible values are labels of classes that are subclasses of the specified parent class in the ontology.
Category datum resolution — the Digital Analytical Method schema (CR/2026/06) introduces $asm.value-instance-of, which constrains values to labels of named individuals (OWL instances) rather than classes:
json "life cycle status": { "$asm.pattern": "category datum", "$asm.value-instance-of": "http://purl.allotrope.org/ontologies/result#AFR_0003289", "enum": ["draft life cycle status", "approved life cycle status", "rejected life cycle status", "effective life cycle status", "obsolete life cycle status"], "type": "string" }
The distinction: class datum enumerations are open (extending the vocabulary adds valid values); category datum enumerations are closed (the named individuals are a fixed set).
2.2.7 Array Semantics
|
Annotation |
Purpose |
Instance Effect |
|
$asm.array-ordered: true |
Semantic ordering significant |
Items include "@index": 1 (1-based) via orderedItem wrapper |
|
$asm.array-mixed: true |
Polymorphic item types |
Items include "@type": "item-class" discriminator via mixedItem wrapper |
|
Neither |
Unordered homogeneous array |
Plain JSON array |
Ordered arrays inject the @index property into each item via an allOf composition with the orderedItem definition. This is structural - the index is part of the validated JSON instance.
2.2.8 Unit System (QUDT)
- Symbols follow strict formatting: / (division), . (multiplication), ^ (exponents), _ (subscripts)
- No Unicode superscripts/subscripts - m^2 not m²
- Symbol → IRI mapping validated at schema level via $asm.unit-iri
- Two namespaces: base QUDT (http://qudt.org/vocab/unit#), Allotrope extensions (http://purl.allotrope.org/ontology/qudt-ext/unit#)
- units.schema (qudt/REC/units.schema.json) is auto-generated from the QUDT ontology and Allotrope extensions
2.2.9 Manifest and Closure Model
Every ASM document begins with a manifest declaring the semantic resources required for interpretation:
{
"manifest": {
"@id": "http://example.org/documents/experiment-2024-001",
"@type": "http://purl.allotrope.org/ontologies/manifest#Manifest",
"vocabulary": [
"http://purl.allotrope.org/voc/afo/merged/REC/2026/03/merged-without-qudt-and-inferred",
"http://purl.allotrope.org/voc/qudt/REC/2026/03/qudt-ext"
],
"json-schemas": [
"http://purl.allotrope.org/json-schemas/adm/uv-vis/REC/2026/06/uv-vis.schema"
]
}
}
The manifest schema (core/REC/manifest.schema.json) defines three optional arrays — vocabulary, json-schemas, and shapes (for SHACL constraints) — of which vocabulary and json-schemas are required. Each array lists entry-point URIs, not the full transitive closure: schema $ref chains are resolved transitively by the validator; vocabularies are either pre-merged offline (a single merged .ttl file containing the OWL import closure) or resolved via owl:imports at load time. The manifest is the root declaration that scopes what validates a document.
2.2.10 $asm.* Extension Keywords
|
Keyword |
Scope |
Purpose |
|
$asm.type |
Property |
Maps JSON type to XSD datatype IRI |
|
$asm.pattern |
Object |
Declares structural document pattern |
|
$asm.property-class |
Property |
RDF property IRI for this field |
|
$asm.lookup-property |
Property |
Property used for label→IRI resolution (typically skos:prefLabel) |
|
$asm.value-sub-class-of |
Enum property (class datum) |
Constrains enum values to subclasses of a specified class (open taxonomy) |
|
$asm.value-instance-of |
Enum property (category datum) |
Constrains enum values to instances of a specified class (closed enumeration) |
|
$asm.unit-iri |
Unit property |
Maps symbol string to QUDT IRI |
|
$asm.fill-value |
Datacube measure |
Sentinel for missing/sparse values |
|
$asm.array-ordered |
Array |
Semantic ordering with index injection |
|
$asm.array-mixed |
Array |
Polymorphic items with type discriminator |
|
$asm.binary-datum |
Object |
Boolean flag marking aggregate datums as binary content containers |
|
$asm.manifest |
Document |
Manifest schema reference |
These keywords are not JSON Schema vocabulary extensions in the formal sense - they are convention-based annotations processed by ASM-aware tooling.
2.2.11 Vocabulary Ontology (RDF/OWL)
ASM vocabularies are full OWL ontologies in Turtle format:
af-r:AFR_0002889 a owl:Class ;
rdfs:subClassOf af-r:AFR_0001234 ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty af-r:has_quantity ;
owl:someValuesFrom qudt:QuantityValue
] ;
skos:prefLabel "peak area" ;
skos:definition "The integrated area under a chromatographic peak." ;
rdfs:isDefinedBy <http://purl.allotrope.org/ontologies/result> .
Key ontology modules:
- Process (techniques, assays, separations)
- Material (samples, reagents, preparations)
- Equipment (instruments, detectors, columns)
- Quality (properties, quantities, measurements)
- Result (calculated values, statistics)
- Role (sample roles, analyst roles)
2.2.12 Extensibility Model
ASM is designed for extension without modification to the core schemas. Four mechanisms operate at different levels:
Custom information aggregate document - the customInformationAggregateDocument pattern (defined in core/REC/hierarchy.schema.json) provides a generic extension point at any document level. Items use polymorphic values (double, string, timestamp, boolean) with a datum label key. This is the designated escape hatch for data that does not fit standard ASM properties.
Schema composition - technique schemas compose core building blocks via allOf + $ref:
{
"allOf": [
{ "$ref": "hierarchy.schema#/$defs/techniqueDocument" },
{
"properties": {
"peak area": { "$asm.pattern": "quantity datum", "...": "..." }
}
}
]
}
This injects technique-specific properties into the standard hierarchy without copying or modifying the base definitions.
Vocabulary extension - custom .voc.ttl files define new OWL classes and properties that extend the AFO namespace:
ex:EXT_0001 a owl:Class ;
rdfs:subClassOf af-r:AFR_0002889 ;
skos:prefLabel "custom peak metric" ;
skos:definition "A vendor-specific peak quality metric." ;
rdfs:isDefinedBy <http://example.org/ontologies/custom> .
New vocabulary classes immediately become valid enum values for any class datum whose $asm.value-sub-class-of ancestor matches (§2.2.6).
Manifest-level integration — the manifest (§2.2.9) is the mechanism that activates schema and vocabulary extensions. Its arrays form the document's validation closure:
|
Array |
Content |
Effect |
|
json-schemas[] |
URI of the entry-point JSON Schema |
Defines the root schema; transitive $ref targets are resolved automatically |
|
vocabulary[] |
URIs of pre-merged .ttl ontology files |
Defines which class/property labels are valid for resolution |
|
shapes[] |
URIs of SHACL shapes graphs |
Defines additional constraint shapes for validation |
To integrate an extension, the document author creates a custom entry-point schema that $refs the standard technique schema and adds extension-specific properties, then declares it in the manifest:
{
"manifest": {
"vocabulary": [
"http://purl.allotrope.org/voc/afo/merged/REC/2026/03/merged-without-qudt-and-inferred",
"http://purl.allotrope.org/voc/qudt/REC/2026/03/qudt-ext",
"http://example.org/voc/custom/2026/06/custom-extension"
],
"json-schemas": [
"http://example.org/json-schemas/adm/custom/2026/06/custom-hplc-extension.schema"
]
}
}
The custom schema internally $refs hplc.schema, which in turn $refs hierarchy.schema, core.schema, cube.schema, and units.schema — all resolved transitively by the validator without explicit manifest listing.
Key integration rules:
- Schemas: The manifest json-schemas[] array lists only the entry-point technique schema. All dependencies (core.schema, hierarchy.schema, cube.schema, units.schema, detector sub-schemas) are resolved transitively via JSON Schema $ref — they do not appear in the manifest. A custom extension schema that $refs the standard hierarchy simply becomes the new entry-point listed in the manifest; the validator follows $ref chains to resolve all dependencies.
- Vocabularies: A custom .voc.ttl that introduces new subclasses (e.g., new sample roles, new result types) must be listed in vocabulary[]. Once listed, any class datum field whose $asm.value-sub-class-of constraint matches an ancestor of the new class will accept the new skos:prefLabel as a valid enum value - without modifying the technique schema's enum array.
- Units: Custom units are introduced by extending the QUDT namespace via the Allotrope extensions namespace (http://purl.allotrope.org/ontology/qudt-ext/unit#). A corresponding entry in units.schema maps the symbol string to the new unit IRI via $asm.unit-iri. Both unit namespaces (§2.2.8) — base QUDT and Allotrope extensions — are resolved through the same units.schema catalog.
The manifest is additive-only - adding a vocabulary or schema can make previously invalid documents valid (new enum values, new properties) but cannot invalidate a document that was valid under a smaller closure. This monotonicity is an architectural invariant.
2.2.13 Domain-Specific Document Hierarchies
ASM's ten structural patterns (aggregate, indexed, value, quantity, class, category, boolean, any, binary, datacube datum) are domain-agnostic building blocks. They compose into document hierarchies for specific domains - from atomic data primitives (materials, samples, analytical runs, results, methods, specifications, instruments, equipment) through compositional workflows (tests, experiments, procedures, recipes), systemic aggregations (studies, programs, regulatory submissions), to enterprise integration systems (ELN, LIMS, MES, ERP).
The Allotrope Connected Domains initiative (proposed CR/2026/06) targets seven of these as interconnected schemas: Materials, Methods, Instruments, Results, Equipment, Processes, and Specifications - enabling cross-domain ASM documents for use cases such as reaction analysis and stability study data.
For detailed domain model proposals - including BFO-aligned ontology mappings, proposed ASM document hierarchies, and current coverage assessments for each tier - see Colsman (2026).
3. Common Canonical Subset
The following concepts exist in both frameworks and can be mapped bidirectionally without information loss. This subset defines the viable interoperability surface.
3.1 Structural Modeling
|
Canonical Concept |
LinkML Realization |
ASM Realization |
Round-trip Fidelity |
|
Entity / Record type |
ClassDefinition |
JSON Schema object with properties |
Lossless |
|
Field / Property |
SlotDefinition (or attributes entry) |
JSON Schema property declaration |
Lossless |
|
Scalar string |
range: string |
tStringValue (simple form), "type": "string" |
Lossless |
|
Scalar integer |
range: integer |
tIntegerValue, "type": "integer" |
Lossless |
|
Scalar float/double |
range: float / range: double |
tDoubleValue, "type": "number" |
Lossless |
|
Scalar boolean |
range: boolean |
tBooleanValue, "type": "boolean" |
Lossless |
|
Date/DateTime |
range: date / range: datetime |
tDateTimeValue, tDateValue, "format": "date-time" |
Lossless |
|
Duration |
range: string + pattern |
tDurationValue, ISO 8601 format |
Lossless |
|
URI/IRI |
range: uri |
tIRIValue, "format": "iri" |
Lossless |
|
Required field |
required: true |
Property name in "required": [...] array |
Lossless |
|
Optional field |
required: false (default) |
Property name absent from required array |
Lossless |
|
Multivalued (list) |
multivalued: true |
"type": "array" |
Lossless |
|
Min items |
minimum_cardinality: n |
"minItems": n |
Lossless |
|
Max items |
maximum_cardinality: n |
"maxItems": n |
Lossless |
|
Composition (nested object) |
Slot with class range, inlined: true |
"$ref": "#/$defs/ChildType" or inline properties |
Lossless |
|
Pattern constraint |
pattern: "^[A-Z]\\d+$" |
"pattern": "^[A-Z]\\d+$" |
Lossless |
|
Numeric bounds |
minimum_value: 0, maximum_value: 100 |
"minimum": 0, "maximum": 100 |
Lossless |
|
Const/fixed value |
equals_string: "fixed" |
"const": "fixed" |
Lossless |
|
Description |
description: "..." |
"description": "..." (JSON Schema) or skos:definition (vocab) |
Lossless (schema-level) |
3.2 Semantic Modeling
|
Canonical Concept |
LinkML Realization |
ASM Realization |
Round-trip Fidelity |
|
Semantic type IRI for class |
class_uri: schema:Person |
$asm.property-class or @type value |
Lossless |
|
Semantic type IRI for property |
slot_uri: schema:name |
$asm.property-class on property |
Lossless |
|
Closed enumeration (category datum) |
Static permissible_values with meaning: <named-individual IRI> |
$asm.pattern: category datum, $asm.value-instance-of, static enum |
Lossless - both sides declare a fixed set; IRIs preserved as meaning: values |
|
Open taxonomy enumeration (class datum) |
Dynamic reachable_from enum (build-time materialization) |
$asm.pattern: class datum, $asm.value-sub-class-of, static enum |
Near-lossless - open-taxonomy intent preserved; ASM resolves at runtime, LinkML materializes at build time (see §5.2.4) |
|
Identifier property |
identifier: true |
@id property declaration |
Lossless |
|
Prefix declarations |
prefixes: block |
Vocabulary ontology prefixes |
Lossless (at vocabulary level) |
3.3 Quantity Modeling
|
Canonical Concept |
LinkML Realization |
ASM Realization |
Round-trip Fidelity |
|
Quantity (value + unit) |
Class with value: float + unit: string slots; unit: annotation with UCUM/QUDT |
tQuantityValue: {"value": 3.14, "unit": "mg/mL"} |
Near-lossless - structure matches; unit validation depth differs |
|
Unit reference |
unit: { ucum_code: mg/mL } or annotation |
$asm.unit-iri mapping to QUDT IRI |
Lossless if both use QUDT |
3.4 Instance Data Mapping (JSON)
For the common subset, instance data can be translated mechanically:
LinkML instance (JSON):
{
"id": "SAMPLE-001",
"name": "Reference Standard A",
"concentration": 5.0,
"concentration_unit": "mg/mL",
"role": "STANDARD"
}
ASM instance (JSON):
{
"sample identifier": "SAMPLE-001",
"sample document": {
"sample name": "Reference Standard A",
"mass concentration": { "value": 5.0, "unit": "mg/mL" },
"sample role type": "standard sample role"
}
}
The structural mapping is deterministic for the canonical subset, though field names follow different conventions (camelCase/snake_case in LinkML vs. space-separated natural language in ASM).
4. Gaps: LinkML to ASM
Features present in LinkML that cannot be faithfully represented in ASM.
4.1 HIGH Severity (Fundamental Incompatibility)
4.1.1 Slot Usage Refinement
LinkML:
classes:
Vehicle:
slots:
- parts
Car:
is_a: Vehicle
slot_usage:
parts:
range: CarPart # narrows from VehiclePart
minimum_cardinality: 4
Why unmappable: ASM has no mechanism for per-class monotonic slot refinement. Each technique schema is a standalone JSON Schema file - there is no way to express "this property, when used in this context, has a narrower range." You must duplicate the full property definition in each schema that needs different constraints.
Impact: Schema authors must manually maintain constraint consistency across technique schemas that share properties with different constraints.
4.1.2 NDArray Shape/Dtype Constraints
LinkML:
classes:
Spectrum:
attributes:
intensities:
range: float
array:
dimensions:
- alias: wavelength
minimum_cardinality: 100
maximum_cardinality: 4096
exact_number_dimensions: 1
Why unmappable: ASM's datacube model is structurally incompatible with LinkML's NDArray concept. ASM datacubes carry per-dimension semantic metadata (concept name, unit, scale type), use a columnar parallel-arrays layout, and support fill values for sparse data. LinkML NDArrays are shape-constrained typed tensors with named dimensions but no semantic metadata per dimension.
These are different abstractions solving overlapping problems:
- LinkML NDArray: "a 1D array of floats with 100–4096 elements"
- ASM Datacube: "a wavelength dimension (nm, interval scale) paired with an absorbance measure (AU, with fill value -999.0)"
Neither subsumes the other.
4.1.3 Mixin Inheritance
LinkML:
classes:
HasAliases:
mixin: true
slots:
- aliases
HasTimestamps:
mixin: true
slots:
- created_at
- modified_at
Person:
is_a: NamedThing
mixins:
- HasAliases
- HasTimestamps
Why unmappable: ASM has no mixin concept. JSON Schema allOf can achieve structural composition, but:
- There is no propagation through an inheritance tree
- There is no diamond resolution
- There is no mixin: true marker distinguishing interface-like composition from structural inheritance
- The vocabulary (rdfs:subClassOf) is single-inheritance only
Approximation: Flatten mixins into each consuming class (losing reusability metadata).
4.2 MEDIUM Severity (Lossy but Approximable)
4.2.1 is_a Hierarchy in Validation Schema
LinkML: Class hierarchies directly affect validation - abstract classes cannot be instantiated, inherited slots are automatically valid.
ASM gap: The class hierarchy lives in the vocabulary (RDF/OWL rdfs:subClassOf), not in the JSON Schema. The validation schema does not enforce inheritance - each technique schema is self-contained.
Approximation: Generate separate JSON Schema per leaf class with all inherited properties flattened.
4.2.2 Rules (Preconditions/Postconditions)
LinkML:
classes:
Address:
rules:
- preconditions:
slot_conditions:
country:
equals_string: USA
postconditions:
slot_conditions:
postal_code:
pattern: "[0-9]{5}(-[0-9]{4})?"
ASM gap: JSON Schema has if/then/else which is structurally similar but operates on JSON Schema applicators rather than slot-level conditions. The semantic framing is different (structural schema branching vs. logical rules).
Approximation: Translate to JSON Schema if/then/else with some loss of semantic clarity.
4.2.3 Computed/Derived Slots (equals_expression)
LinkML:
slots:
age_in_years:
equals_expression: "{age_in_months} / 12"
ASM gap: No derived field mechanism. All ASM fields are stored values. The converter that produces the ASM instance is responsible for computing derived values - the schema cannot express derivation rules.
Approximation: Document the derivation in the vocabulary definition (skos:definition) - no runtime enforcement.
4.2.4 Dynamic Enumerations (open taxonomies only)
LinkML:
enums:
CellType:
reachable_from:
source_ontology: obo:cl
source_nodes:
- CL:0000000
include_self: false
relationship_types:
- rdfs:subClassOf
ASM gap: Enumerations in ASM JSON Schema are static enum arrays. The vocabulary contains the full class hierarchy, but the schema enum list is materialized at build time.
Note: This gap applies only to open taxonomy (class datum, $asm.value-sub-class-of) fields. Closed enumerations (category datum, $asm.value-instance-of) have a fixed named-individual set - static permissible_values with meaning: maps to them losslessly and there is nothing to materialize.
Approximation: Materialize the dynamic enum at schema-generation time (losing dynamic update capability).
4.2.5 Compound Unique Keys
LinkML:
classes:
Isotope:
unique_keys:
main:
unique_key_slots:
- atomic_number
- neutron_number
ASM gap: No native compound uniqueness constraint. JSON Schema does not support tuple uniqueness.
Approximation: Enforce at application level; document in vocabulary.
4.2.6 Import Chains (Named Module Composition)
LinkML: imports: [base, extensions/lab] — forms a named dependency graph. Each import is a logical module name; the import chain is explicit in the schema source and traversable by tooling.
ASM gap: ASM schemas do have transitive composition via JSON Schema $ref (absolute URI resolution), but there is no named-module concept. The $ref targets are opaque URIs — there is no imports: declaration enumerating dependencies, no logical module names, and no tooling for traversing the import graph as a first-class concept. Reusability is implicit in the $ref structure rather than declared.
Approximation: The $ref graph already provides transitive composition. The gap is in tooling ergonomics (no named imports, no schema-level dependency declaration, no import-graph visualization) rather than capability.
4.2.7 Type Designators
LinkML: designates_type: true - a slot whose value determines the runtime class of the instance.
ASM gap: @type serves a similar purpose but is always constrained to a fixed vocabulary class label, not an arbitrary discriminator dispatching across a class hierarchy.
4.3 LOW Severity (Metadata/Documentation Loss)
|
Feature |
Why lost |
Impact |
|
Subsets |
No equivalent grouping in ASM |
Organizational info lost |
|
Inverse slots |
No inverse declaration (documentation-only in LinkML too) |
Minimal |
|
Logical characteristics (transitive, symmetric) |
Would need OWL axioms in vocabulary |
Documentation only |
|
default_range |
ASM requires explicit typing per property |
Syntactic convenience only |
|
recommended |
No "advisory but non-validating" marker in JSON Schema |
Soft validation signal lost |
|
Structured aliases |
Vocabulary has skos:altLabel but no source/language metadata in schema |
Metadata loss |
|
ifabsent (defaults) |
JSON Schema default keyword exists but semantics differ |
Near-mappable |
5. Gaps: ASM to LinkML
Features present in ASM that cannot be faithfully represented in LinkML.
5.1 HIGH Severity (Fundamental Incompatibility)
5.1.1 Polymorphic Value Types (Dual-Form)
ASM:
{
"sample identifier": "SAMPLE-001",
"peak area": { "@type": "peak area", "value": 42.5 }
}
Both forms are valid for the same field: a bare literal ("SAMPLE-001") or a typed object ({"@type": "...", "value": ...}). This is ASM's fundamental design pattern - it allows any scalar value to optionally carry semantic type metadata.
Why unmappable: LinkML slots have a single range. A slot cannot simultaneously accept string AND an object with {@type, value} structure. The any_of combinator can approximate this:
slots:
sample_identifier:
any_of:
- range: string
- range: TypedStringValue
But this loses:
- The guarantee that both forms represent the same semantic concept
- The @type value being drawn from the vocabulary
- The structural guarantee that typed form always has exactly {@type, value}
Impact: Any LinkML-authored schema that needs ASM instance compatibility must model every scalar field as a union type, defeating LinkML's type safety.
5.1.2 Document Pattern System ($asm.pattern)
ASM: Every structural element is tagged with one of ten patterns (aggregate datum, indexed datum, value datum, quantity datum, class datum, category datum, boolean datum, any datum, binary datum, datacube). These patterns:
- Drive tooling behavior (editors, validators, generators)
- Define the structural grammar of documents
- Are machine-readable structural classifications
Why unmappable: LinkML has no metamodel concept for "structural pattern classification." You could use annotations:
classes:
MeasurementAggregateDocument:
annotations:
asm_pattern: aggregate datum
But annotations are:
- Unvalidated by default (any string accepted)
- Not part of the generated JSON Schema
- Not usable for tool dispatch
- Not inherited through is_a
Impact: ASM's structural grammar - which determines how documents are traversed, displayed, and validated - has no LinkML representation that preserves its role as a first-class modeling concept.
5.1.3 Datacube with Per-Dimension Semantic Metadata
ASM:
{
"cube-structure": {
"dimensions": [{
"@componentDatatype": "double",
"concept": "wavelength",
"unit": "nm",
"scale": "interval"
}],
"measures": [{
"@componentDatatype": "double",
"concept": "absorbance",
"unit": "AU",
"$asm.fill-value": -999.0
}]
}
}
Why unmappable: LinkML NDArrays provide shape and dtype constraints but have no per-dimension metadata:
- No concept (what the dimension means)
- No unit (what units the values are in)
- No scale (nominal/ordinal/interval/ratio)
- No fill-value (sentinel for sparse data)
- No separation of dimensions vs. measures
The ASM datacube is closer to a self-describing scientific dataset format (like NetCDF or HDF5 with attributes) than to a shaped array type.
Approximation: Model as a class with explicit slots for structure metadata + array slots for data. Loses the integrated datacube semantics and cannot round-trip through LinkML's array machinery.
5.2 MEDIUM Severity (Lossy but Approximable)
5.2.1 Ordered Arrays with Index Injection (@index)
ASM:
{
"measurement document": [
{ "@index": 1, "sample identifier": "S1", ... },
{ "@index": 2, "sample identifier": "S2", ... }
]
}
The @index property is injected into instances by the schema (via allOf + orderedItem). Array position has semantic meaning and is explicitly encoded in the data.
LinkML gap: Multivalued slots are ordered by default in JSON, but:
- There is no mechanism to inject an explicit index property into items
- The @index property would need to be declared as a slot on the item class, conflating structural metadata with domain content
Approximation: Add an explicit index: integer slot. Loses the automatic injection semantics and the separation of structural ordering from domain modeling.
5.2.2 Mixed/Polymorphic Arrays with Type Discriminator
ASM:
{
"custom information document": [
{ "@type": "string datum", "value": "note text" },
{ "@type": "quantity datum", "value": 3.14, "unit": "mg" }
]
}
$asm.array-mixed: true declares that array items are heterogeneous, with @type serving as the discriminator.
LinkML gap: any_of on a multivalued slot's range can express heterogeneity:
slots:
custom_info:
multivalued: true
any_of:
- range: StringDatum
- range: QuantityDatum
But LinkML does not inject a @type discriminator key - the consumer must infer type from structure. JSON Schema oneOf with const on a type field is the standard approach, which LinkML can generate, but the @type key name and vocabulary-backed value are ASM-specific.
5.2.3 Manifest Closure Model
ASM: Document validity requires a manifest declaring the entry-point schemas and vocabularies. However, the manifest is not a flat, self-contained closure — both layers have transitive dependencies:
- JSON Schema layer: The manifest's json-schemas[] array lists only the top-level technique schema (e.g., liquid-chromatography.tabular.schema). That schema transitively $refs core.schema, hierarchy.schema, cube.schema, units.schema, and potentially multiple detector/sub-technique schemas. For example, the LC tabular schema directly references 12 distinct external schema files, none of which appear in the manifest. JSON Schema $ref resolution follows absolute URIs transitively — the validator must resolve the full $ref graph to validate an instance.
- Vocabulary layer: The manifest's vocabulary[] array typically lists a pre-merged ontology file (e.g., merged-without-qudt-and-inferred) plus a QUDT extension. The "merged" vocabulary is itself the output of an OWL import closure computation performed offline — it inlines all transitive owl:imports. The manifest TTL format also carries explicit owl:imports triples for tooling that performs import resolution at runtime.
The manifest's role is therefore to declare roots — not to enumerate every resource in the transitive closure. The closure is computed by following $ref chains (schemas) and owl:imports chains or pre-merged bundles (vocabularies). Adding a new vocabulary to the manifest can change which enum values are valid (because new subclasses become available), but the schema dependency graph is fixed by the $ref structure.
LinkML gap: Uses an explicit imports: chain (directed acyclic graph). Adding an import adds new schema elements transitively. Both systems have transitive dependency resolution, but they differ in how the "root set" is declared:
|
Aspect |
ASM |
LinkML |
|
Schema dependencies |
Explicit via json-schemas[] + implicit via JSON Schema $ref (absolute URIs) |
Explicit via imports: (named modules) |
|
Vocabulary dependencies |
Pre-merged offline or owl:imports chains |
Dynamic reachable_from or static enum materialization |
|
Root declaration |
Manifest json-schemas[] + vocabulary[] |
Top-level schema file with imports: |
|
Closure computation |
$ref resolution (schemas), pre-merge or runtime import (vocabs) |
Import chain traversal at schema-load time |
Approximation: Model the manifest as a top-level class with vocabulary and schema arrays. The transitive $ref graph maps naturally to LinkML imports:. The vocabulary-scope gating (runtime enum resolution dependent on manifest vocabulary list) has no LinkML equivalent.
5.2.4 $asm.lookup-property (Runtime Label Resolution - class datum only)
ASM: For class datum fields ($asm.value-sub-class-of), enum values such as "control sample role" are validated by looking up the label in the vocabulary using the property specified by $asm.lookup-property (typically skos:prefLabel). The resolution is runtime - the valid enum values are determined by what OWL subclasses exist in the vocabulary closure at validation time.
Scope: This gap applies exclusively to open taxonomy (class datum) fields. Category datum fields ($asm.value-instance-of) constrain values to a fixed set of named individuals - those are enumerated statically in both the ASM schema and the equivalent LinkML permissible_values block, so no runtime resolution gap exists.
LinkML gap: Enums are resolved at schema-build time. meaning maps a permissible value to an ontology term, but the permissible values themselves are explicitly listed in the schema. There is no "query the vocabulary at runtime" mechanism (dynamic enums exist but are materialized at build time).
Impact: An ASM class datum field can gain or lose valid values by changing the manifest's vocabulary list without touching the schema. The equivalent LinkML schema must be regenerated to reflect the change. Category datum fields are unaffected.
5.2.5 Validated Unit Symbol → IRI Mapping
ASM: $asm.unit-iri creates a validated bidirectional mapping between a unit symbol string and its QUDT IRI:
"µg/mL": {
"$asm.unit-iri": "http://qudt.org/vocab/unit#MicrogramPerMilliliter",
"const": "µg/mL"
}
LinkML gap: The unit: annotation is metadata - it documents what unit a slot holds but does not validate that the instance value matches an allowed unit symbol from a controlled registry.
Approximation: Model units as an enum with each permissible value annotated with its QUDT IRI. Loses the auto-generated nature and the bidirectional symbol↔IRI contract.
5.2.6 Technique Hierarchy as Reusable Template
ASM: The 3-tier hierarchy (AggregateDocument → Document → MeasurementAggregateDocument) is a structural template reused across 50+ technique schemas (Colsman, 2026, §3 Analytical). Each new technique instantiates this template and adds domain-specific content at defined extension points. The same aggregate/indexed patterns extend beyond analytical data into 19 proposed domain-specific document hierarchies across four tiers (Colsman, 2026), from atomic data primitives (materials, samples) through enterprise integration systems (ELN, LIMS, MES, ERP).
LinkML gap: No native "structural template" or "document pattern template" mechanism. You can model this as abstract base classes:
classes:
TechniqueAggregateDocument:
abstract: true
slots:
- data_system_document
- technique_documents
UVVisAggregateDocument:
is_a: TechniqueAggregateDocument
slot_usage:
technique_documents:
range: UVVisDocument
This works but is verbose and loses the "this is a reusable structural pattern that all techniques share" concept. Each technique must re-express the full hierarchy rather than saying "I'm an instance of the technique pattern with these specifics."
5.2.7 $asm.fill-value (Sparse Data Representation)
ASM: Datacube measures can declare a fill value (e.g., -999.0) that represents "no data" in a dense array representation.
LinkML gap: No sentinel/fill-value concept for arrays. The standard LinkML approach would be to use nullable types or optional slots, but this doesn't map to the "dense array with sentinel" pattern used in scientific data.
5.2.8 Schema-Level Semantic Annotations ($asm.*)
ASM's $asm.* keywords are processed by tooling to drive validation, resolution, and serialization behavior. They are part of the schema's operational semantics.
LinkML gap: annotations are extensible key-value metadata, but:
- They are not operationally processed by LinkML tooling
- They are not part of the generated downstream artifacts (annotations don't appear in generated JSON-Schema by default)
- They cannot drive tool behavior in the same way
Approximation: Use annotations with instantiates for typed validation (experimental feature).
5.2.9 Companion File Model (Binary Datum)
ASM: The binary datum pattern ($asm.pattern: "binary datum", property class AFR_0001928) models references to external binary content - images, raw detector frames, binary blobs - that accompany the structured JSON document. The value is always a POSIX-style relative path (tStringValue) to a companion file co-located with the .asm output.
This is an architectural pattern, not just a scalar type:
- Co-location contract - companion files must reside alongside the .asm file. The converter is responsible for extracting/copying them to the output directory.
- Hierarchy-aware placement - the binary datum sits at the document level that owns the data:
|
Companion type |
Placement level |
Example |
|
Per-measurement image |
measurementDocument |
Fluorescence stage image per cycle |
|
Per-run raw trace |
measurementAggregateDocument |
Complete detector dump |
|
Instrument calibration |
deviceControlAggregateDocument |
Calibration snapshot |
|
Document-level attachment |
<domain> document |
Protocol PDF, audit export |
- Array pattern - multiple companion files are modeled as an aggregate document containing an array of binary datum objects, each with its own POSIX path.
- Semantic identity - the property name is always POSIX path, typed to AFO property "has POSIX path". This is not a generic string field.
LinkML gap: No native binary, file, or path type. Approximations:
|
LinkML approach |
What it captures |
What it loses |
|
range: uri |
External reference |
Co-location contract, POSIX path convention, hierarchy placement rules |
|
range: string + pattern |
Path syntax |
Semantic identity (AFR_0001928), companion file lifecycle |
|
Custom TypeDefinition |
Named type |
No tooling awareness - generators ignore it |
|
annotations carrying $asm.pattern |
Metadata roundtrip |
Not processed by any LinkML generator |
The fundamental mismatch: LinkML treats file references as opaque string values. ASM treats them as a managed artifact relationship with co-location, hierarchy placement, and semantic typing. A LinkML-authored schema can represent the path string but cannot express the converter's obligation to extract companion files or the placement rules that determine where in the document hierarchy the reference belongs.
5.3 LOW Severity
|
Feature |
Why lost |
Impact |
|
Electronic signatures |
Model as a regular class |
Domain convention, not structural gap |
|
Unit formatting rules (µ vs μ, ^ for exponents) |
Use pattern constraints |
Validation possible, intent unclear |
|
Vocabulary-level OWL restrictions |
LinkML generates OWL but doesn't consume OWL restrictions for validation |
Complementary, not conflicting |
|
orderedItem / mixedItem wrapper injection |
Must be modeled as explicit class composition |
Structural overhead |
6. Interoperability Strategy
6.1 Canonical Intermediate Representation (CIR)
Define a minimal intermediate representation capturing the common subset from §3:
The CIR contains:
- Entity definitions with fields, types, cardinality, descriptions
- Enumerations with permissible values and optional ontology IRIs
- Quantity types with unit references (QUDT IRIs)
- Composition relationships (parent → child)
- Identifier declarations
- Pattern and numeric constraints
The CIR does not contain:
- Inheritance hierarchies (flattened at boundary)
- Mixins (flattened at boundary)
- Datacubes (domain-specific, handled separately)
- Document patterns (ASM-specific)
- NDArrays (LinkML-specific)
- Computed slots (LinkML-specific)
- Polymorphic value dual-form (ASM-specific)
6.2 Transformation Pipelines
L2C: LinkML → CIR
- Materialize all inherited slots via SchemaView.all_slots(class_name)
- Flatten mixins into consuming classes
- Resolve dynamic enums to static permissible-value lists
- Evaluate equals_expression to documentation annotations
- Convert NDArrays to annotated multivalued slots (losing shape constraints)
- Convert rules to documentation annotations
C2A: CIR → ASM
- Map each CIR entity to a JSON Schema object with properties
- Apply $asm.pattern based on structural heuristics:
- Entity with only scalars → value datum pattern fields
- Entity with child arrays → aggregate datum
- Enum fields → class datum with $asm.value-sub-class-of
- Quantity fields → quantity datum
- Generate $asm.type from CIR scalar types
- Generate $asm.property-class from CIR semantic IRIs
- Generate vocabulary stubs (.voc.ttl) with:
- Class declarations for entities
- skos:prefLabel for enum labels
- rdfs:subClassOf for enum parent classes
- Generate manifest template listing produced schemas and vocabularies
A2C: ASM → CIR
- Parse JSON Schema properties → CIR fields
- Resolve $asm.pattern to determine field category
- Extract type information from $asm.type or JSON Schema type
- Extract enum values from enum arrays
- Extract quantity structure from tQuantityValue pattern
- Map $asm.property-class → CIR semantic IRI
- Discard: $asm.fill-value, $asm.array-ordered, $asm.array-mixed, datacube structure, polymorphic dual-form
C2L: CIR → LinkML
- Map each CIR entity to a ClassDefinition
- Map fields to SlotDefinition with appropriate ranges
- Map enums to EnumDefinition with meaning for ontology-backed values
- Map quantities to a class with value + unit slots, annotated with unit:
- Map compositions to inlined class ranges
- Preserve semantic IRIs as class_uri / slot_uri
6.3 Lossless Extensions (Round-Trip Annotations)
For concepts that exist in one framework but not the other, define passthrough annotations that preserve information during round-tripping:
In LinkML (for ASM-specific concepts):
classes:
MeasurementDocument:
annotations:
asm_pattern: "indexed datum"
asm_array_ordered: true
attributes:
wavelength:
annotations:
asm_fill_value: -999.0
asm_property_class: "http://purl.allotrope.org/ontologies/result#AFR_0002889"
In ASM (for LinkML-specific concepts):
{
"$comment": "linkml:is_a = NamedThing",
"$comment": "linkml:mixins = [HasAliases, HasTimestamps]",
"properties": { ... }
}
These annotations enable round-tripping but carry no operational semantics in the target framework.
6.4 Recommended Approach
|
Use Case |
Recommendation |
|
New technique schema (simple) |
Author in LinkML → generate ASM via C2A pipeline → manually add datacube/polymorphic patterns |
|
New technique schema (complex with datacubes) |
Author datacube portion in ASM natively; author entity/enum portions in LinkML |
|
Existing ASM schema → documentation |
Use A2C → C2L pipeline to generate LinkML for documentation/browsing |
|
Validation |
Always validate against ASM JSON Schema (authoritative); use LinkML for authoring ergonomics only |
|
Vocabulary authoring |
Author enums in LinkML with meaning → generate vocabulary .voc.ttl stubs |
|
Schema evolution |
Track canonical subset changes in LinkML (better diff/merge tooling); regenerate ASM artifacts |
6.5 What Cannot Interoperate (Hard Boundaries)
The following scenarios require framework-native authoring - no automated translation is viable:
- Datacubes with full dimension semantics - must be authored in ASM JSON Schema
- Polymorphic value dual-form fields - must be authored in ASM; LinkML union types are a poor approximation
- Document pattern hierarchy - the 3-tier technique template must be ASM-native
- Runtime vocabulary resolution - ASM's manifest-gated enum validation has no LinkML equivalent
- Computed/derived slots with enforcement - must remain LinkML-only (no ASM representation)
- NDArray with shape constraints - must remain LinkML-only (ASM datacube is different)
- Mixin-based reuse - must remain LinkML-only (flatten for ASM)
7. Worked Examples
7.1 Mass Measurement (Successful Round-Trip)
A simple mass measurement field - fully within the canonical subset.
LinkML:
id: https://example.org/mass-measurement
name: mass_measurement
prefixes:
qudt: http://qudt.org/vocab/unit/
linkml: https://w3id.org/linkml/
imports:
- linkml:types
classes:
MassMeasurement:
class_uri: http://purl.allotrope.org/ontologies/result#AFR_0001234
description: A single mass measurement reading
attributes:
sample_identifier:
range: string
required: true
description: Unique identifier for the sample
mass:
range: float
required: true
minimum_value: 0
description: Measured mass value
unit:
ucum_code: mg
mass_unit:
range: MassUnitEnum
required: true
enums:
MassUnitEnum:
permissible_values:
mg:
meaning: qudt:Milligram
g:
meaning: qudt:Gram
kg:
meaning: qudt:Kilogram
ASM JSON Schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"massMeasurementDocument": {
"$asm.pattern": "indexed datum",
"type": "object",
"properties": {
"sample identifier": {
"$asm.pattern": "value datum",
"$asm.type": "http://www.w3.org/2001/XMLSchema#string",
"$asm.property-class": "http://purl.allotrope.org/ontologies/result#AFR_0005678",
"type": "string"
},
"mass": {
"$asm.pattern": "quantity datum",
"$asm.property-class": "http://purl.allotrope.org/ontologies/result#AFR_0001234",
"type": "object",
"properties": {
"value": { "type": "number", "minimum": 0 },
"unit": { "$ref": "#/$defs/massUnits" }
},
"required": ["value", "unit"]
}
},
"required": ["sample identifier", "mass"]
},
"massUnits": {
"type": "string",
"enum": ["mg", "g", "kg"]
}
}
}
ASM Vocabulary stub (.voc.ttl):
@prefix af-r: <http://purl.allotrope.org/ontologies/result#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
af-r:AFR_0001234 a owl:Class ;
skos:prefLabel "mass" ;
skos:definition "A measured mass value." .
Round-trip fidelity: Complete - all information preserved in both directions.
7.2 UV-Vis Spectrum (Partial - Mapping Boundary)
A UV-Vis absorbance measurement showing where the canonical subset ends.
LinkML (expressible portion):
classes:
UVVisDocument:
description: A single UV-Vis measurement result
attributes:
sample_identifier:
range: string
required: true
wavelength_range_start:
range: float
unit:
ucum_code: nm
wavelength_range_end:
range: float
unit:
ucum_code: nm
detector_type:
range: DetectorTypeEnum
# CANNOT EXPRESS: the absorbance spectrum datacube
# CANNOT EXPRESS: ordered measurement documents with @index
# CANNOT EXPRESS: polymorphic custom information items
enums:
DetectorTypeEnum:
permissible_values:
photodiode_array:
meaning: http://purl.allotrope.org/ontologies/equipment#AFE_0001234
single_wavelength:
meaning: http://purl.allotrope.org/ontologies/equipment#AFE_0005678
ASM (full representation):
{
"$asm.pattern": "aggregate datum",
"UV-vis absorbance spectrum aggregate document": {
"data system document": { ... },
"UV-vis absorbance spectrum document": [{
"@index": 1,
"$asm.pattern": "indexed datum",
"measurement aggregate document": {
"measurement document": [{
"@index": 1,
"sample document": {
"sample identifier": "SAMPLE-001"
},
"device control aggregate document": {
"device control document": [{
"@index": 1,
"detector type": "photodiode array detector"
}]
},
"absorbance spectrum": {
"$asm.pattern": "datacube",
"cube-structure": {
"dimensions": [{
"@componentDatatype": "double",
"concept": "wavelength",
"unit": "nm",
"scale": "interval"
}],
"measures": [{
"@componentDatatype": "double",
"concept": "absorbance",
"unit": "AU",
"$asm.fill-value": -999.0
}]
},
"data": {
"dimensions": [[200.0, 201.0, 202.0, 203.0]],
"measures": [[0.142, 0.156, 0.173, -999.0]]
}
}
}]
}
}]
}
}
What maps (canonical subset): sample identifier, detector type enum, wavelength range scalars.
What does NOT map:
|
Feature |
Direction |
Reason |
|
Datacube structure |
ASM → LinkML |
No per-dimension metadata in NDArray |
|
@index injection |
ASM → LinkML |
No automatic index property |
|
3-tier document hierarchy |
ASM → LinkML |
No structural template mechanism |
|
$asm.fill-value: -999.0 |
ASM → LinkML |
No sentinel concept |
|
Polymorphic value form |
ASM → LinkML |
Single range per slot |
|
Mixin-based composition |
LinkML → ASM |
No mixin in JSON Schema |
|
slot_usage refinement |
LinkML → ASM |
No per-context narrowing |
7.3 Datacube (Mapping Failure)
Demonstrating a complete mapping failure - the datacube concept is structurally incompatible.
ASM Datacube (source of truth):
{
"$asm.pattern": "datacube",
"cube-structure": {
"dimensions": [
{ "@componentDatatype": "double", "concept": "retention time", "unit": "s", "scale": "interval" },
{ "@componentDatatype": "double", "concept": "wavelength", "unit": "nm", "scale": "interval" }
],
"measures": [
{ "@componentDatatype": "double", "concept": "absorbance", "unit": "AU", "$asm.fill-value": 0.0 }
]
},
"data": {
"dimensions": [
[0.0, 0.5, 1.0, 1.5, 2.0],
[200.0, 250.0, 300.0, 350.0]
],
"measures": [
[[0.1, 0.2, 0.3, 0.4], [0.2, 0.3, 0.4, 0.5], [0.3, 0.4, 0.5, 0.6], [0.15, 0.25, 0.35, 0.45], [0.05, 0.1, 0.15, 0.2]]
]
}
}
Closest LinkML approximation:
classes:
AbsorbanceCube:
description: 2D absorbance data (retention time × wavelength)
attributes:
retention_times:
range: float
multivalued: true
# LOST: concept="retention time", unit="s", scale="interval"
# LOST: @componentDatatype
annotations:
asm_concept: retention time
asm_unit: s
asm_scale: interval
wavelengths:
range: float
multivalued: true
annotations:
asm_concept: wavelength
asm_unit: nm
asm_scale: interval
absorbance_matrix:
range: float
multivalued: true
# LOST: 2D structure (this is a flat list)
# LOST: fill-value semantics
# LOST: relationship between dimensions and measures
annotations:
asm_fill_value: "0.0"
What is lost:
- The structural relationship between dimensions and measures (the "cube" concept)
- The 2D nature of the measure data (flattened to 1D list)
- Per-dimension semantic metadata as first-class validated properties
- Fill-value semantics (sentinel vs. actual value distinction)
- The columnar parallel-array layout (dimensions[0] aligns with data[0])
- Component datatype declarations separate from range
Conclusion: The datacube is not approximable in LinkML. It must be authored and validated in ASM natively. A LinkML schema for a technique that includes datacubes should either:
- Omit the datacube and reference it as an opaque sub-document
- Model it as an annotated class with best-effort slot structure (losing validation power)
- Accept that the datacube portion cannot round-trip
8. Summary Matrix
|
Category |
Canonical (bidirectional) |
LinkML-only (no ASM) |
ASM-only (no LinkML) |
|
Entity/Class |
✅ |
- |
- |
|
Field/Slot |
✅ |
- |
- |
|
Scalar types |
✅ |
- |
Polymorphic dual-form |
|
Required/Optional |
✅ |
recommended |
- |
|
Cardinality |
✅ |
- |
- |
|
Composition |
✅ |
- |
- |
|
Enum (static) |
✅ |
- |
- |
|
Enum (dynamic/runtime) |
- |
reachable_from |
Manifest-gated vocab resolution |
|
Quantity |
✅ |
- |
- |
|
Unit reference |
✅ (QUDT) |
- |
Validated symbol→IRI mapping |
|
Semantic IRI |
✅ |
- |
- |
|
Pattern constraint |
✅ |
Structured patterns |
- |
|
Numeric bounds |
✅ |
- |
- |
|
Inheritance |
- |
is_a + mixins + slot_usage |
rdfs:subClassOf (vocab only) |
|
Arrays |
✅ (basic) |
NDArray shape/dtype |
Ordered+indexed, mixed+typed |
|
Datacube |
- |
- |
Full cube model |
|
Document patterns |
— |
— |
$asm.pattern (10 patterns) |
|
Computed fields |
- |
equals_expression |
- |
|
Rules (if-then) |
- |
preconditions/postconditions |
JSON Schema if/then (different semantics) |
|
Unique keys |
- |
unique_keys (compound) |
- |
|
Annotations |
✅ (passthrough) |
Extensible, validated via instantiates |
$asm.* (operational) |
|
Modularity |
— |
Import chain (named DAG) |
$ref graph (implicit DAG) + manifest root declaration |
|
Vocabulary |
✅ (basic) |
- |
Full OWL with restrictions |
9. LinkML Tooling Applicability to ASM
This section analyzes whether existing LinkML tools can be applied directly to ASM schemas or ASM instance data, and where custom tooling is required.
9.1 Tool Inventory
|
Tool |
Purpose |
Applicable to ASM? |
|
gen-json-schema |
Generate JSON Schema from LinkML YAML |
⚠️ Partial - generates standard JSON Schema but cannot emit $asm.* keywords |
|
linkml-validate |
Validate instance data against schema |
❌ No - cannot validate ASM instances (polymorphic values, $asm.* semantics) |
|
gen-owl |
Generate OWL ontology |
✅ Yes - can generate vocabulary stubs for ASM .voc.ttl |
|
gen-python / gen-pydantic |
Generate Python dataclasses/models |
⚠️ Partial - useful for tooling code, not for ASM validation |
|
gen-doc |
Generate Markdown/HTML documentation |
✅ Yes - works for documenting the LinkML-authored subset |
|
linkml-convert |
Convert data between JSON/YAML/CSV/RDF |
❌ No - ASM JSON structure (polymorphic values, @type/@index) is incompatible |
|
schema-automator |
Bootstrap schema from data samples |
⚠️ Partial - can infer basic structure from ASM JSON but misses all $asm.* semantics |
|
SchemaView (Python API) |
Programmatic schema traversal |
✅ Yes - useful for building custom ASM generators |
|
gen-erdiagram |
Generate ER diagrams |
✅ Yes - works for the LinkML-authored portion |
|
schemasheets |
Author schemas from spreadsheets |
✅ Yes - viable authoring surface for technique field inventories |
9.2 Schema Generation: gen-json-schema → ASM
What it produces:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$defs": {
"Person": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["id"],
"additionalProperties": false
}
}
}
What ASM needs:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"sampleDocument": {
"$asm.pattern": "indexed datum",
"type": "object",
"properties": {
"sample identifier": {
"$asm.pattern": "value datum",
"$asm.type": "http://www.w3.org/2001/XMLSchema#string",
"$asm.property-class": "http://purl.allotrope.org/ontologies/result#AFR_0005678",
"type": "string"
}
},
"required": ["sample identifier"]
}
}
}
Gaps:
|
Aspect |
gen-json-schema output |
ASM requirement |
|
JSON Schema draft |
Draft-07 |
Draft 2020-12 |
|
$asm.* keywords |
Not emitted |
Required on every property |
|
Property naming |
camelCase/snake_case |
Space-separated natural language |
|
additionalProperties |
false (closed world) |
Typically absent (open for extensions) |
|
Polymorphic values |
Not supported |
Required for most fields |
|
Document patterns |
Not emitted |
Required structural classification |
|
Quantity values |
Separate slots for value and unit |
Single {value, unit} object |
Verdict: gen-json-schema cannot produce valid ASM schemas directly. A custom ASM generator is required - but it can reuse LinkML's SchemaView API and LifecycleMixin hooks to build one.
9.3 Vocabulary Generation: gen-owl → ASM .voc.ttl
What it produces:
@prefix linkml: <https://w3id.org/linkml/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a owl:Class ;
rdfs:label "Person" ;
rdfs:subClassOf ex:NamedThing .
ex:name a owl:DatatypeProperty ;
rdfs:label "name" ;
rdfs:domain ex:Person ;
rdfs:range xsd:string .
What ASM vocabularies need:
af-r:AFR_0005678 a owl:Class ;
rdfs:subClassOf af-r:AFR_0001000 ;
skos:prefLabel "sample identifier" ;
skos:definition "A unique string identifying a sample." ;
rdfs:isDefinedBy <http://purl.allotrope.org/ontologies/result> .
Gaps:
|
Aspect |
gen-owl output |
ASM vocabulary requirement |
|
Identifier scheme |
Human-readable URIs |
Opaque AFO-style IRIs (AFR_XXXXXXX) |
|
Label property |
rdfs:label |
skos:prefLabel (required for $asm.lookup-property resolution) |
|
Documentation |
rdfs:comment |
skos:definition |
|
Ontology header |
Minimal |
Full OWL ontology with owl:imports, owl:versionIRI, dct:title, dct:created |
|
Class hierarchy source |
LinkML is_a |
May differ from AFO taxonomy |
|
Namespace |
Schema-derived |
Must match AFO/ZONTAL namespace conventions |
Verdict: gen-owl produces a useful starting point for vocabulary stubs. A post-processing step can:
- Replace rdfs:label with skos:prefLabel
- Add skos:definition from LinkML description
- Add ontology header boilerplate
- Reassign IRIs to AFO namespace pattern
This is viable as a semi-automated pipeline with manual IRI assignment.
9.4 Data Validation: linkml-validate on ASM Instances
Can linkml-validate validate ASM .asm files?
No. The fundamental blockers are:
- Polymorphic value types - ASM fields accept both "text" and {"@type": "label", "value": "text"}. LinkML validation expects a single consistent shape per slot.
- Property naming - ASM uses space-separated names ("sample identifier"). LinkML expects identifiers conforming to programming language conventions. The preserve-names flag in gen-json-schema helps but doesn't solve runtime validation.
- @type and @index injection - These structural properties are not part of any LinkML class definition but are present in ASM instances.
- Datacube structure - Not expressible in LinkML; would fail validation.
- Manifest-gated vocabulary resolution - LinkML validates against a fixed schema; it cannot resolve enum validity based on a runtime manifest.
Partial use case: If an ASM instance is pre-processed to strip polymorphic wrappers, remove @index/@type injections, and flatten quantities into separate value/unit slots, then linkml-validate could validate the resulting normalized form. This is useful for pipeline testing (validate converter output structure before ASM-specific decoration) but not for final ASM compliance.
9.5 Schema Authoring: schemasheets for Technique Field Inventories
schemasheets allows authoring LinkML schemas from spreadsheets - a natural fit for the tabular field inventories that precede technique schema authoring.
Example spreadsheet:
|
class |
field |
range |
required |
description |
unit |
meaning |
|
UVVisMeasurement |
wavelength |
float |
true |
Excitation wavelength |
nm |
AFR_0002001 |
|
UVVisMeasurement |
absorbance |
float |
true |
Measured absorbance |
AU |
AFR_0002002 |
|
UVVisMeasurement |
sample_id |
string |
true |
Sample identifier |
|
AFR_0005678 |
|
UVVisMeasurement |
detector_type |
DetectorTypeEnum |
true |
Detector technology |
|
|
Workflow:
- Domain expert fills spreadsheet with field inventory
- schemasheets converts to LinkML YAML
- Custom generator transforms to ASM JSON Schema + vocabulary stubs
- Schema author manually adds datacubes, document hierarchy, polymorphic patterns
Verdict: Viable and ergonomic for the ~60% of technique schema content that falls within the canonical subset.
9.6 Documentation: gen-doc for ASM Schema Browsing
gen-doc generates Markdown/HTML documentation from LinkML schemas - useful for producing browsable technique schema documentation.
What works:
- Class descriptions with slot tables
- Enum value listings with ontology links
- Inheritance diagrams (ER diagrams via gen-erdiagram)
- Cross-references between classes
What doesn't work:
- Datacube visualization
- Document pattern hierarchy rendering
- Polymorphic value form documentation
- Manifest closure information
Verdict: Useful for the canonical subset. ASM-specific content (datacubes, patterns) requires supplementary documentation.
9.7 Programmatic API: SchemaView for Custom ASM Generators
The most productive use of LinkML tooling for ASM is building a custom generator using the SchemaView programmatic API:
from linkml_runtime.utils.schemaview import SchemaView
sv = SchemaView("technique.yaml")
for cls in sv.all_classes().values():
if cls.abstract:
continue
# Generate ASM JSON Schema object
asm_obj = {
"$asm.pattern": infer_pattern(cls),
"type": "object",
"properties": {}
}
for slot in sv.class_induced_slots(cls.name):
asm_obj["properties"][to_asm_name(slot.name)] = {
"$asm.pattern": "value datum",
"$asm.type": range_to_xsd(slot.range),
"$asm.property-class": slot.slot_uri,
"type": range_to_json_type(slot.range)
}
This approach:
- Leverages LinkML's inheritance resolution (class_induced_slots flattens hierarchy)
- Allows custom $asm.* annotation injection
- Can read annotations from LinkML slots to carry ASM-specific metadata
- Produces ASM-compliant output with full control over naming, patterns, and structure
Verdict: The recommended integration path. SchemaView + custom generator = LinkML authoring with ASM output.
9.8 Summary: Tooling Applicability Matrix
|
Use Case |
Tool |
Applicability |
Custom Work Required |
|
Author technique field inventory |
schemasheets |
✅ Direct |
Spreadsheet template |
|
Author technique schema |
LinkML YAML |
✅ Direct |
None (for canonical subset) |
|
Generate ASM JSON Schema |
gen-json-schema |
❌ Insufficient |
Custom ASM generator via SchemaView |
|
Generate vocabulary stubs |
gen-owl |
⚠️ Partial |
Post-processing (labels, headers, IRIs) |
|
Validate ASM instances |
linkml-validate |
❌ Incompatible |
Dedicated ASM validator |
|
Validate pre-ASM pipeline output |
linkml-validate |
⚠️ After normalization |
Normalization pre-processor |
|
Generate documentation |
gen-doc + gen-erdiagram |
✅ Direct |
Supplement for ASM-specific content |
|
Bootstrap schema from samples |
schema-automator |
⚠️ Basic structure only |
Manual $asm.* annotation |
|
Build custom generators |
SchemaView API |
✅ Direct |
Generator implementation |
|
Convert instance formats |
linkml-convert |
❌ Incompatible |
Custom ASM serializer |
9.9 Recommended Toolchain
Key insight: The existing LinkML toolchain provides ~40% of the pipeline out of the box. The critical missing piece is a custom ASM generator (analogous to gen-json-schema but emitting ASM-compliant output with $asm.* keywords, polymorphic value patterns, and document hierarchy structure). This generator would use SchemaView for schema traversal and LinkML annotations to carry ASM-specific metadata through the authoring layer.
10. Conclusions
10.1 Interoperability Viability
The common canonical subset covers approximately 60–70% of typical technique schema content (entity definitions, scalar fields, enumerations, quantities, compositions, basic constraints). This is sufficient to use LinkML as an authoring surface for the majority of a technique schema, with targeted ASM-native authoring for:
- Datacubes (100% ASM-native)
- Polymorphic value patterns (100% ASM-native)
- Document hierarchy template structure (ASM-native, though LinkML class hierarchy can approximate)
- Ordered/mixed array semantics (partial ASM-native augmentation)
10.2 Recommended Architecture
- Quantities with QUDT units
- Compositions
- Constraints, descriptions
Custom ASM Generator
+ Manual datacube additions
+ Manual polymorphic patterns
10.3 Key Takeaway
LinkML and ASM are complementary, not competing frameworks. LinkML excels at schema authoring ergonomics (YAML, inheritance, modularity, multi-target generation). ASM excels at scientific data validation (two-layer architecture, domain-specific patterns, GxP compliance). ASM's ten structural patterns are domain-agnostic — the technique schema hierarchy is the first and most mature instantiation, but the same patterns extend to 19 document hierarchies spanning materials, samples, analytical, results, methods, specifications, instruments, equipment, tests, experiments, procedures, recipes, studies, programs, regulatory submissions, ELN, LIMS, MES, and ERP (Colsman, 2026). A viable interoperability strategy uses LinkML for authoring the common subset and ASM for the final validation artifact, with clear boundaries at the three high-severity gaps (datacubes, polymorphic values, document patterns).
References
Allotrope Foundation. (2024). Allotrope Framework and Simple Model (ASM). Available at: https://www.allotrope.org/product-asm
Colsman, W. (2026). The Connected Domains Model for the Pharmaceutical and Life Sciences Industry: 19 Domain-Specific Document Models for the ICAD Context Graph. ZONTAL Inc.