SAMM / Catena-X Pipeline Pattern¶
The DataStack pipeline enriches data by filling prototype graphs. For Catena-X data, the SAMM aspect model schema is the prototype graph: it defines the semantic structure of the payload — the entities, their types, relationships, units, and datatypes — and the pipeline instantiates that structure with real data values to produce a self-contained, queryable knowledge graph.
This pattern is used for all 26 samm-mapping-* datasets on futurecarproduction.materialsdata.space and covers domains from material properties and composition through sustainability, recycling, and circular economy strategies.
Two enrichment stages¶
A Catena-X JSON payload already conforms to a published SAMM aspect model (io.catenax.material_data:1.0.0 and siblings). Enrichment proceeds in two stages that keep concerns separate:
| Stage | What it does | Tool |
|---|---|---|
| 1 | Lift flat JSON to SAMM-aligned RDF — the payload enriched by its SAMM prototype graph | RDFConverter /api/createrdf + YARRRML |
| 2 | Translate SAMM RDF to target ontology (PMDco / AutoMatCE) for cross-domain interoperability | SPARQL CONSTRUCT/INSERT on Fuseki |
Stage 1 produces a self-contained graph: the JSON values are now linked to the SAMM schema context that defines their meaning. Stage 2 bridges that graph into broader cross-community vocabularies.
For the single-stage variant — when AAS JSON maps directly to PMDco without an intermediate schema step — see IDTA / AAS Submodel Pipeline.
Stage 1 — JSON payload → SAMM-aligned intermediate RDF¶
Tool: RDFConverter /api/createrdf
Inputs:
- Flat JSON payload conforming to a Catena-X SAMM aspect model (e.g.
io.catenax.material_data:1.0.0) - YARRRML
.yamlmapping file stored as a CKAN resource in themappingsgroup
Output: SAMM-patterned intermediate RDF graph (e.g. material_data_test_pa6gf30-joined.ttl, 96 KB)
The mapping uses JSONPath iterators to traverse the flat JSON and emit RDF triples in the SAMM/CX namespace with deterministic subject URIs. The key characteristic is that the output stays in the CX namespace — it is not yet PMDco.
YARRRML mapping structure¶
The snippet below shows the characteristic prefix block and iterator structure. The samm: prefix anchors the mapping to the Eclipse Tractus-X SAMM meta-model; mat: is the aspect-model-specific namespace for the MaterialData entity.
# illustrative example — structure matches real PA6GF30 mappings on futurecarproduction.materialsdata.space
prefixes:
rr: 'http://www.w3.org/ns/r2rml#'
rml: 'http://semweb.mmlab.be/ns/rml#'
ql: 'http://semweb.mmlab.be/ns/ql#'
samm: 'urn:bamm:io.openmanufacturing:meta-model:1.0.0#'
mat: 'urn:bamm:io.catenax.material_data:1.0.0#'
cx: 'https://w3id.org/catenax/ontology/core#'
qudt: 'http://qudt.org/schema/qudt/'
unit: 'http://qudt.org/vocab/unit/'
xsd: 'http://www.w3.org/2001/XMLSchema#'
sources:
materialData:
access: '$(data_url)'
referenceFormulation: jsonpath
iterator: '$.materialData[*]'
mappings:
MaterialData:
sources: [materialData]
s: mat:MaterialData_$(materialId)
po:
- [a, mat:MaterialData]
- [mat:materialId, $(materialId)]
- [mat:density, $(density), xsd:decimal]
- [mat:youngsModulus, $(youngsModulus), xsd:decimal]
Every property name in the po block matches the SAMM aspect model schema exactly. The SAMM schema is encoded directly in the YARRRML prefixes and property names — not loaded as a separate file at runtime. The real material.json-to-samm.yaml (published on futurecarproduction.materialsdata.space) shows this clearly:
prefixes:
cx: urn:samm:io.catenax.material_accounting:1.0.0#
ext1: urn:samm:io.catenax.shared.material_classification:1.0.0#
ext2: urn:samm:io.catenax.shared.industry_core.common:1.0.0#
mappings:
Material:
s: MaterialMaterial:$(globalAssetId)
po:
- [a, cx:Material]
- [cx:thermalCharacterization, $(thermalCharacterization)]
- [cx:chemicalCharacterization, $(chemicalCharacterization)]
The cx:, ext1:, ext2: namespace URIs are the SAMM aspect model version — pinned in the YARRRML. This is what lets Stage 2 join the data graph with the schema graph without any runtime schema resolution.
Stage 2 — SAMM RDF → target ontology (SPARQL CONSTRUCT/INSERT)¶
Tool: SPARQL engine (Fuseki)
Inputs (two named graphs in Fuseki):
| Graph | Contents |
|---|---|
dataGraph |
mat:MaterialData instances with literal property values — the Stage 1 output |
schemaGraph |
samm:Property definitions with unit and datatype metadata — the SAMM aspect model schema TTL |
Output: Named graph <pmdco> inside the Fuseki dataset (exportable as pmdco-full.ttl)
SPARQL INSERT structure¶
For each property, the CONSTRUCT clause emits four interconnected nodes:
PREFIX pmd: <https://w3id.org/pmd/co/>
PREFIX obo: <http://purl.obolibrary.org/obo/>
PREFIX qudt: <http://qudt.org/schema/qudt/>
PREFIX unit: <http://qudt.org/vocab/unit/>
PREFIX mat: <urn:bamm:io.catenax.material_data:1.0.0#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
INSERT {
GRAPH <pmdco> {
# Node 1 — physical material entity (BFO material)
?material a obo:BFO_0000040 .
# Node 2 — quality inhering in the material
?quality a pmd:PMD_0000952 ; # e.g. Density quality class
obo:RO_0000080 ?material . # inheres in
# Node 3 — scalar measurement datum
?datum a obo:OBI_0001931 ;
obo:RO_0000052 ?quality ; # characteristic of
obo:IAO_0000417 ?quality . # is quality measured as
# Node 4 — numeric quantity value + unit
?qv a qudt:QuantityValue ;
qudt:numericValue ?numVal ;
qudt:unit unit:KiloGM-PER-M3 ;
obo:OBI_0001938 ?datum . # has value specification
}
}
WHERE {
GRAPH ?dataGraph {
?materialInst a mat:MaterialData ;
mat:density ?rawVal ;
mat:materialId ?id .
}
# Bind deterministic SHA256-based IRIs
BIND(IRI(CONCAT("https://example.org/material/", SHA256(STR(?id)))) AS ?material)
BIND(IRI(CONCAT("https://example.org/quality/density/", SHA256(STR(?id)))) AS ?quality)
BIND(IRI(CONCAT("https://example.org/datum/density/", SHA256(STR(?id)))) AS ?datum)
BIND(IRI(CONCAT("https://example.org/qv/density/", SHA256(STR(?id)))) AS ?qv)
# Unit conversion: raw value already in kg/m³ → factor 1
BIND(xsd:decimal(?rawVal) AS ?numVal)
}
The real material.samm-to-pmdco.construct.sparql (published on futurecarproduction.materialsdata.space) uses a VALUES table to handle all properties in one generic CONSTRUCT block. Each row assigns a property a role and conversion factor — the CONSTRUCT clause generates the appropriate PMDco node pattern from those roles:
VALUES (?prop ?role ?targetClass ?unit ?conversionFactor ?status ?note) {
( cx:chemicalCharacterization "information" obo:IAO_0000027 UNDEF 1.0 "generated" "Domain review required." )
( ext2:globalAssetId "identifier" obo:IAO_0000027 UNDEF 1.0 "generated" "Domain review required." )
( ext1:materialClassification "information" obo:IAO_0000027 UNDEF 1.0 "generated" "Domain review required." )
( cx:materialFormat "quality" obo:IAO_0000300 UNDEF 1.0 "generated" "Domain review required." )
( cx:materialStatus "quality" obo:IAO_0000027 UNDEF 1.0 "generated" "Domain review required." )
( cx:thermalCharacterization "information" obo:IAO_0000027 UNDEF 1.0 "generated" "Domain review required." )
( cx:wasteCode "identifier" pmd:PMD_0060009 UNDEF 1.0 "generated" "Domain review required." )
}
The role value ("quality", "identifier", "information") drives which branches of the CONSTRUCT clause fire — one VALUES table replaces nine separate INSERT blocks.
The real pmdco-mapping-insert.sparql repeats this four-node block for all 13 properties:
stressAtBreak, flexuralStrength, youngsModulus, flexuralModulus, strainAtBreak, impactStrength, density, meltingTemperature, glassTransitionTemperature, humidity, waterAbsorption, linearThermalExpansionCoefficientParallel, linearThermalExpansionCoefficientTransverse.
One unit conversion is non-trivial: mat:kiloJoulePerSquareMeter × 1000 → unit:J-PER-M2 (impact strength). All other properties use factor 1.
IRI strategy throughout is SHA256 hash-based for full determinism across repeated inserts.
CKAN dataset structure¶
Each samm-mapping-* dataset on the portal carries four resource types:
| Resource | Format | Role |
|---|---|---|
*.json-to-samm.yaml |
YAML | Stage 1 YARRRML mapping |
| SAMM aspect model | TTL | Upstream ontology (external GitHub link, becomes schemaGraph) |
*.samm-to-pmdco.construct.sparql |
SPARQL | Stage 2 CONSTRUCT → PMDco |
*.samm-to-automatce.construct.sparql |
SPARQL | Stage 2 CONSTRUCT → AutoMatCE |
Live example — PA6GF30 cross-project use case¶
The Cross-Project Use Case dataset at dataportal.material-digital.de demonstrates the full pipeline for PA6GF30 (30% glass-filled polyamide 6), sourced from a Catena-X industrial dataspace participant via Eclipse Dataspace Connector (EDC).
All files are published on dataportal.material-digital.de — Cross Project Use Case.
Input JSON (material_data_test_pa6gf30.json) — flat Catena-X payload:
{
"materialInformation": {
"materialName": "PA6GF30",
"materialIdentifier": "Z1234"
},
"mechanicalProperty": {
"flexuralStrength": 210,
"youngsModulus": 9800,
"strainAtBreak": 3
},
"thermophysicalProperty": {
"humidity": 2.1,
"glassTransitionTemperature": 20,
"meltingTemperature": 223
},
"physicalProperty": { "density": 1530 }
}
Stage 1 output (material_data_test_pa6gf30-joined.ttl) — SAMM-aligned RDF, the payload enriched by its prototype graph:
@prefix mat: <urn:samm:io.catenax.material_data:1.0.0#> .
<#MaterialData_Z1234> a mat:MaterialData ;
mat:materialInformation <#MaterialInformation_Z1234> ;
mat:mechanicalProperty <#MechanicalProperty_210_9800> .
<#MaterialInformation_Z1234> a mat:MaterialInformationEntity ;
mat:materialIdentifier "Z1234" ;
mat:materialName "PA6GF30" .
<#MechanicalProperty_210_9800> a mat:MechanicalPropertyEntity ;
mat:youngsModulus "9800" ;
mat:flexuralStrength "210" ;
mat:impactStrength "74" .
The flat JSON values are now typed instances in the mat: SAMM namespace — self-contained and queryable before Stage 2 runs.
End-to-end workflow orchestrated by cross_project_usecase.ipynb:
- Retrieve — pull PA6GF30 JSON payload from Catena-X dataspace via EDC
- JSON → RDF — RDFConverter
/api/createrdf+ Stage 1 YARRRML →material_data_test_pa6gf30-joined.ttl(96 KB, SAMM-aligned RDF) - RDF → PMDco — SPARQL INSERT reads from
dataGraph(Step 2 TTL) +schemaGraph(SAMM aspect model) → named graph<pmdco>in Fuseki - Export —
pmdco-full.ttl— standalone PMDco knowledge graph
Trigger mechanism — NOT automated¶
Manual step required
Stage 2 is not triggered automatically by the pipeline. It requires a human to initiate it explicitly.
Tool: pmdco-mapping-insert.html — a browser helper page stored as a CKAN resource.
How it works:
- Open the HTML file in a browser
- Verify the Fuseki endpoint URL and the SPARQL file URL shown in the form
- Click Run
Internally, the JavaScript fetches the .sparql file from CKAN and POSTs it to Fuseki /$/updatewithContent-Type: application/sparql-update`.
Prerequisites before clicking Run:
dataGraphmust already be loaded in Fuseki (viafuseki_updateaction, triggered by the Stage 1 YARRRML conversion)schemaGraph(SAMM aspect model TTL) must also be loaded in Fuseki viafuseki_update
Stage 1 (YARRRML → intermediate RDF → Fuseki load) is fully automated by the pipeline. Stage 2 is the deliberate manual gate.
When to use the two-stage variant¶
| Criterion | Single-stage (CSV / AAS → target ontology) | Two-stage (SAMM/CX → SAMM RDF → target ontology) |
|---|---|---|
| Source | Raw data with no established ontology | Data conforming to a published schema (SAMM, OPC UA, etc.) |
| Prototype graph | Custom prototype graph authored for your data | The SAMM aspect model schema is the prototype graph |
| Stage 1 output | — (goes directly to target ontology) | Self-contained SAMM-enriched RDF graph |
| Stage 2 | — | SPARQL CONSTRUCT bridges to cross-community vocabulary |
| IRI strategy | Template-based | SHA256 hash-based for full determinism |
| When to choose | Your data has no existing schema vocabulary | Source data already conforms to a published aspect model |
Key insight: when source data already comes with a schema that acts as its prototype graph, honour that structure in Stage 1 — produce a self-contained enriched graph first. Stage 2 then bridges vocabularies without touching the original data or mapping. The YARRRML mapping stays clean and portable; the ontology bridge lives in a separate, inspectable .sparql file.
Replicating this pattern for your domain¶
To apply the two-stage pattern to a new SAMM aspect model:
- Identify your SAMM aspect model TTL and its namespace IRI — this becomes
schemaGraph - Author a Stage 1 mapping — YARRRML with the SAMM namespace in
prefixes, JSONPath iterators matching the JSON payload structure; follow the mapping authoring guide - Upload Stage 1 resources to CKAN — JSON payload, YARRRML
.yaml, SAMM TTL (as external link resource) - Load both graphs into Fuseki — use the
fuseki_updateCKAN action for the Stage 1 TTL output, and separately for the SAMM TTL - Author Stage 2 SPARQL — copy the four-node pattern above, replace the PMDco class IRIs and QUDT units with those appropriate for your properties, add a VALUES table if mapping many properties in one query
- Store the
.sparqlfile and*-insert.htmlhelper as CKAN resources on the same dataset
For the single-stage approach (no intermediate ontology), see IDTA / AAS Submodel Pipeline.