Design Patterns
This page explains the key structural decisions behind CoreMeta4Cat โ how the four pillars are modelled, how they connect to a dataset, and what design patterns make the schema extensible and machine-actionable.
Reading guide
This page is written in three tiers. Most users only need the first two.
| Tier | Who it's for | Sections |
|---|---|---|
| Overview | Everyone โ data providers, repository managers | The entry point, The four pillars |
| Pattern explanations | Users who want to understand how to navigate or extend the schema | Classification pattern, Activity pattern, Mixin pattern |
| Technical depth | Schema developers and DCAT-AP-PLUS integrators | Sections marked with ๐ฌ |
The entry point: CatalysisDataset
Every CoreMeta4Cat record starts with a CatalysisDataset. This is a dcat:Dataset โ fully compatible with plain DCAT and DCAT-AP โ extended with four additional link slots that connect to the four CoreMeta4Cat pillars.
id: ex:dataset-001
type: CatalysisDataset # โ dcat:Dataset
# Layer 1: global classification
rdf_type:
id: voc4cat:0007001
title: "heterogeneous catalysis"
# Layer 2: links to the four pillars
was_generated_by:
- id: ex:synthesis-001
type: Synthesis
- id: ex:characterization-001
type: Characterization
is_about_activity:
- id: ex:reaction-001
type: Reaction
is_about_entity:
- id: ex:catalyst-001
type: CatalystSample
The key points here are:
rdf_typecarries a controlled vocabulary term from Voc4Cat to classify which field of catalysis the dataset belongs to.was_generated_bylinks to activities that produced the data (Synthesis, Characterization, Simulation).is_about_activitylinks to the Reaction being studied โ which is the catalytic process itself, not a data-generating step.is_about_entitylinks to the catalyst sample or material the dataset concerns.
The four pillars
The four CoreMeta4Cat pillars โ Synthesis, Characterization, Reaction, and Simulation โ are the core of the metadata model. Each is a separate class, defined in its own subprofile module, and linked from the CatalysisDataset via the slots above.
coremeta4cat.yaml (aggregator + CatalysisDataset)
โโโ coremeta4cat_common.yaml (shared slots and enumerations)
โโโ coremeta4cat_synthesis_ap.yaml (Synthesis + 12 preparation methods)
โโโ coremeta4cat_characterization_ap.yaml (Characterization + 28 techniques)
โโโ coremeta4cat_reaction_ap.yaml (Reaction + 8 reactor types)
โโโ coremeta4cat_simulation_ap.yaml (Simulation + 4 methods + 12 properties)
Synthesis
What it is: The process of preparing a catalyst. Synthesis is a DataGeneratingActivity โ it produces data about the preparation, and it produces a CatalystSample as its physical output.
Key links:
realized_planโ aPreparationMethodsubclass (the protocol used)had_input_entityโPrecursorinstances (the starting materials)had_output_entityโCatalystSample(the resulting catalyst)
Twelve preparation methods are currently defined, each as a concrete PreparationMethod subclass:
-
Wet chemistry
Impregnation, Co-Precipitation, Deposition-Precipitation, Sol-Gel, Molecular Synthesis
-
Thermal / gas-phase
Solvothermal, Combustion Synthesis, Flame Spray Pyrolysis, Sublimation, Plasma-Assisted
-
Mechanical / energy-assisted
Mechanochemical Synthesis, Microwave-Assisted, Sonochemical Synthesis
-
Surface / thin-film
Atomic Layer Deposition, Exsolution Synthesis
Characterization
What it is: The measurement of a catalyst or catalytic system using an analytical technique. Characterization is also a DataGeneratingActivity โ it produces measurement data.
Key links:
evaluated_entityโ theCatalystSampleor material being measuredrealized_planโ aCharacterizationTechniquesubclass (the measurement protocol)carried_out_byโ the instrument (Device) performing the measurement
Twenty-eight techniques are currently defined, organised into groups:
| Group | Techniques |
|---|---|
| Diffraction | Powder XRD, Single Crystal XRD |
| X-ray spectroscopy | XAS/XANES/EXAFS, XPS, EDX |
| Vibrational spectroscopy | FTIR, DRIFTS, Raman, NMR |
| Electron microscopy | TEM, SEM |
| Thermal analysis | TGA, TPR, TPO |
| Surface & pore analysis | BET |
| Elemental analysis | ICP-AES, Elemental Analysis (CHNS) |
| Optical & electronic | UV-Vis, Photoluminescence, Photoluminescence Lifetime |
| Electrochemistry | Cyclic Voltammetry, Conductivity Measurement |
| Particle sizing | Dynamic Light Scattering |
| Mass spectrometry | ESI-MS, GC-MS, HPLC-MS |
| Chromatography | GC, HPLC |
Reaction
What it is: The catalytic reaction being studied. Unlike Synthesis and Characterization, Reaction is not a DataGeneratingActivity. It is the process being observed, not the process generating the dataset. In the schema it's named CatalyticReaction, and it specializes chemdcat-ap's generic ChemicalReaction -- see Pattern 5: Specializing chemdcat-ap below for what that means in practice.
Key links:
used_reactor(is_a: carried_out_by) โ aChemicalReactor(the physical reactor)used_reactant(is_a: had_input_entity) โ the reactant feedsproduct_identification_methodโ aCharacterizationTechniqueused for product analysis
Eight reactor design types are defined, all specializing chemdcat-ap's generic Reactor class via the abstract ChemicalReactor:
FixedBedReactor ยท CSTR ยท PlugFlowReactor ยท Autoclave ยท SlurryReactor ยท Microreactor ยท ElectrochemicalReactor ยท FluidizedBedReactor
Operando experiments
For in-situ or operando experiments (e.g. XRD measured while a reaction runs), the dataset carries both links simultaneously:
was_generated_by:
- type: Characterization # PowderXRD โ the process that made the data
is_about_activity:
- type: Reaction # the catalytic process being monitored
Simulation
What it is: A computational study of a catalyst or catalytic mechanism. Simulation is a DataGeneratingActivity โ it generates data computationally.
Key links:
realized_planโ aSimulationMethodsubclass (DFT, MD, Microkinetics, MonteCarlo)carried_out_byโ aSoftwareagent (the simulation package)evaluated_entityโ the catalyst model or structure being simulated
Twelve calculated property classes capture the type of computed output: ElectronicStructure, BandGap, ThermodynamicStability, PhononDispersion, Surfaces, GrainBoundaries, ElasticConstants, DielectricTensors, EquationsOfState, AqueousStability, Piezoelectricity, Ferroelectrics.
Pattern 1: Classification via rdf_type
Pattern summary
Flexible, machine-actionable classification of datasets, activities, and entities using ontology terms โ without creating a separate class for every possible value.
Rather than defining a fixed class hierarchy for every type of catalysis or every synthesis method, CoreMeta4Cat uses a single rdf_type slot on each class to carry a controlled vocabulary term from Voc4Cat, CHMO, or another ontology. This keeps the schema compact while staying fully machine-actionable.
On CatalysisDataset โ classify the catalysis research field:
rdf_type:
id: VOC4CAT:0007001
title: "heterogeneous catalysis"
On Synthesis โ classify the preparation method type:
type: Synthesis
rdf_type:
id: voc4cat:0007016
title: "impregnation"
realized_plan:
type: Impregnation # the concrete method class with all parameter slots
On Characterization โ classify the measurement technique:
type: Characterization
rdf_type:
id: CHMO:0000158
title: "powder X-ray diffraction"
realized_plan:
type: PowderXRD # the concrete technique class with all measurement slots
The rdf_type slot gives the machine-readable ontology term; the concrete subclass (Impregnation, PowderXRD, โฆ) provides the structured parameter slots. Both are used together.
The allowed values for rdf_type on CatalysisDataset are defined in CatalysisResearchFieldEnum -- see the CatalysisDataset page for the full value table and a worked example combining multiple pillars into one dataset.
Pattern 2: Activities and Plans
Pattern summary
A two-part structure separates what was done (the Activity) from the protocol describing how to do it (the Plan). This mirrors the PROV-O model and keeps the schema clean.
Each pillar that generates data follows this two-part structure:
Activity (what was done) Plan (the protocol)
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ
Synthesis โโโ PreparationMethod
Characterization โโโ CharacterizationTechnique
Simulation โโโ SimulationMethod
The Activity carries the instance-level data (who did it, when, on what sample, with what output). The Plan carries the method-level data (parameter settings, instrument configuration, protocol steps).
# The activity โ what happened
id: ex:synthesis-001
type: Synthesis
nominal_composition: "5wt% Ni/Al2O3"
had_input_entity:
- id: ex:precursor-001
type: Precursor
name: "Ni(NO3)2ยท6H2O"
precursor_quantity: 1.24 # g
had_output_entity:
- id: ex:catalyst-001
type: CatalystSample
# The plan โ the protocol
realized_plan:
id: ex:method-001
type: Impregnation
impregnation_type: incipient_wetness
impregnation_duration: 12.0 # h
drying_temperature: 120.0 # ยฐC
drying_time: 12.0 # h
calcination_final_temperature: 500.0 # ยฐC
calcination_dwelling_time: 4.0 # h
calcination_gaseous_environment: "air"
This separation means a single PreparationMethod record could in principle be shared across multiple Synthesis activities โ a direct gain for reproducibility. That sharing depends on the Plan being independently identifiable, which is why realized_plan.id is populated above.
Plan (external, DCAT-AP-PLUS) only carries title/description โ no id. Rather than repeat an id slot on each of PreparationMethod, CharacterizationTechnique, SimulationMethod, and ProductIdentificationMethod individually, CoreMeta4Cat inserts one shared intermediate class, CatalysisPlan (is_a: Plan, abstract), that adds id once; all four then specialize CatalysisPlan instead of Plan directly. realized_plan itself needed inlined: true added explicitly on each of its class-level overrides (on Synthesis, Characterization, Simulation) once its range became identifier-bearing โ LinkML's default for a single-valued, class-typed slot switches from "inline the whole object" to "reference by id" the moment the range class gains an identifier slot, unless told otherwise.
A LinkML JSON Schema quirk worth knowing
Slots marked recommended: true (not required: true) still show up in the generated JSON Schema's required array โ gen-json-schema has no native "recommended" tier to fall back to, so it folds recommended into required rather than dropping the distinction. In practice this means every Recommended field on a class needs a value for an instance to validate via linkml-run-examples (or any other JSON-Schema-based validator), not just Mandatory ones.
Pattern 3: Mixin classes
Pattern summary
Slot groups that are shared across multiple methods are factored into reusable mixin classes, so each slot is defined exactly once and inherited wherever needed.
Many preparation methods share common process steps โ drying, calcination, precipitation. Rather than repeating the same slots in every method class, CoreMeta4Cat uses mixin classes that bundle related slots:
| Mixin | Slots it provides | Used by |
|---|---|---|
DryingMixin |
drying_device, drying_temperature, drying_time, drying_atmosphere |
Impregnation, CoPrecipitation, DepositionPrecipitation, SonochemicalSynthesis, MolecularSynthesis |
CalcinationMixin |
calcination_initial_temperature, calcination_final_temperature, calcination_dwelling_time, calcination_heating_rate, calcination_gaseous_environment, calcination_gas_flow_rate, number_of_cycles |
Impregnation, CoPrecipitation, DepositionPrecipitation, SonochemicalSynthesis, ExsolutionSynthesis |
PrecipitationMixin |
precipitating_agent, synthesis_ph, mixing_rate, mixing_time, mixing_temperature, order_of_addition, aging_temperature, aging_time |
CoPrecipitation, DepositionPrecipitation |
ThermalSynthesisMixin |
synthesis_temperature, synthesis_duration, equipment, vessel_type, atmosphere |
Solvothermal, PlasmaAssisted, CombustionSynthesis, MicrowaveAssisted, MechanochemicalSynthesis, Sublimation |
The same pattern is used in the Characterization subprofile for analytical techniques:
| Mixin | Used by |
|---|---|
XRaySourceMixin |
PowderXRD, SingleCrystalXRD, XPS, EDX |
ElectronMicroscopyMixin |
TEM, SEM |
TemperatureProgramMixin |
TPR, TPO, Thermogravimetry |
ChromatographyMixin |
GC, HPLC, GC-MS, HPLC-MS |
MassRangeMixin |
GC-MS, HPLC-MS, ESI-MS |
ElectrochemistryMixin |
CyclicVoltammetry, ConductivityMeasurement |
In LinkML, a mixin class has no class_uri of its own and generates no independent node shape. It is a pure slot container, mixed in via the mixins: key on a concrete class.
Pattern 4: Shared slots in coremeta4cat_common
Pattern summary
Slots referenced by two or more subprofiles are declared once in coremeta4cat_common.yaml and imported by all subprofiles. This keeps the schema DRY (Don't Repeat Yourself) -- but it also means editing one changes it everywhere it's used, not just in the context you had in mind. See the warning below.
Some slots appear in multiple pillars โ for example, has_atmosphere is relevant to Synthesis (e.g. MolecularSynthesis), Characterization (e.g. InfraredSpectroscopy, NMRSpectroscopy), and Reaction (CatalyticReaction). These shared slots live in coremeta4cat_common.yaml:
coremeta4cat_common.yaml โ shared slots include:
has_atmosphere, solvent, has_experiment_duration,
has_heating_rate, has_operation_mode, has_energy,
number_of_scans, resolution, step_size, carrier_gas,
has_sample_mass, has_calcination_*, has_drying_*, ...
Slots that are exclusive to a single subprofile are declared in that subprofile file only. Physical quantity slots that belong to chemdcat-ap's own material/chemistry model (e.g. has_temperature, has_pressure) live further down the import chain, in material_entities_ap.yaml โ see Pattern 5.
Editing a shared slot changes it everywhere
LinkML gives each slot exactly one definition, reused via slots:/slot_usage, not one copy per class. If you edit a shared slot's field through the Excel inbox workflow and no slot_usage override already exists for the class you're editing, the change applies to the slot's single global definition -- affecting every other class that uses it too. The inbox tooling detects this and warns you, listing every other class the edit will also affect. If you only meant to change it for one class, that needs a slot_usage override added directly in the YAML (open a schema issue), not an Excel edit.
๐ฌ Deep dive: The EvaluatedActivity distinction
Technical section
This section is intended for schema developers and DCAT-AP-PLUS integrators. It is not required reading for data providers.
One of the most important architectural decisions in CoreMeta4Cat is that Reaction is not a DataGeneratingActivity. Instead it is an EvaluatedActivity.
In DCAT-AP-PLUS, the distinction is:
| Class | Meaning | Links to dataset via |
|---|---|---|
DataGeneratingActivity |
A process that produces the data in the dataset | prov:wasGeneratedBy |
EvaluatedActivity |
A process that the dataset is about, but which did not produce it | is_about_activity |
For catalysis, a Reaction is the catalytic process being studied. The data is generated by measuring that reaction โ via a Characterization activity. The Reaction itself does not produce the data file.
This distinction enables operando experiments to be described correctly:
# Operando XRD during CO oxidation
was_generated_by:
- type: Characterization # PowderXRD run โ this produced the data
rdf_type:
id: CHMO:0000158
title: "powder X-ray diffraction"
is_about_activity:
- type: CatalyticReaction # the CO oxidation reaction being monitored
catalyst_quantity:
- value: 50.0
unit: https://qudt.org/vocab/unit/MilliGM
used_reactant:
- title: "CO (1 vol%)"
- title: "O2 (2 vol%)"
used_reactor:
- type: FixedBedReactor
If Reaction were modelled as a DataGeneratingActivity, this relationship would collapse: it would be impossible to distinguish the measurement from the catalytic process it monitors.
The same distinction appears in DCAT-AP-PLUS itself โ the NMR example in the base documentation uses was_generated_by: NMRSpectroscopy (the measurement) and evaluated_entity: MaterialSample (the thing measured). CoreMeta4Cat extends this by adding is_about_activity: Reaction for cases where a process โ not just a material โ is being monitored.
Import hierarchy
The full import chain, from the CoreMeta4Cat top level down to the DCAT-AP-PLUS base, is:
coremeta4cat.yaml
โโโ coremeta4cat_common.yaml
โโโ chem_dcat_ap.yaml (chemdcat-ap, vendored locally)
โโโ chemical_reaction_ap.yaml (ChemicalReaction, Reactor)
โโโ chemical_entities_ap.yaml (ChemicalEntity, MaterialisticMixin)
โโโ material_entities_ap.yaml (Temperature, Mass, Pressure, ...)
โโโ dcat_ap_plus โ DCAT-AP-PLUS base (external, not vendored)
โโโ coremeta4cat_synthesis_ap.yaml
โโโ coremeta4cat_characterization_ap.yaml
โโโ coremeta4cat_reaction_ap.yaml
โโโ coremeta4cat_simulation_ap.yaml
Each layer adds domain-specific classes and slots on top of the layer below, without modifying it. This means CoreMeta4Cat datasets remain valid DCAT-AP-PLUS instances, which in turn remain valid DCAT-AP datasets.
The chem_dcat_ap.yaml / chemical_reaction_ap.yaml / chemical_entities_ap.yaml / material_entities_ap.yaml files are chemdcat-ap โ vendored (copied) into this repo rather than imported as a package. dcat_ap_plus itself is not vendored; it's resolved via LinkML's normal import mechanism against its published schema URI.
Pattern 5: Specializing chemdcat-ap (inheritance)
Pattern summary
CatalyticReaction and ChemicalReactor are is_a specializations of chemdcat-ap's generic ChemicalReaction and Reactor classes, not independent classes that merely resemble them. This means they inherit chemdcat-ap's slots on top of their own catalysis-specific ones.
Earlier versions of this schema modelled CatalyticReaction and ChemicalReaction as unrelated siblings โ both is_a: EvaluatedActivity, duplicating the generic reaction concept instead of specializing it. That's been fixed:
ChemicalReaction (chemdcat-ap) --is_a--> EvaluatedActivity
^
| is_a
CatalyticReaction (coremeta4cat)
Reactor (chemdcat-ap) --is_a--> Device
^
| is_a
ChemicalReactor (coremeta4cat, abstract)
^
| is_a
FixedBedReactor, CSTR, PlugFlowReactor, Autoclave,
SlurryReactor, Microreactor, ElectrochemicalReactor, FluidizedBedReactor
What this means in practice:
CatalyticReactionautomatically inheritsChemicalReaction's slots โused_starting_material,used_reactant,generated_product,used_catalyst,used_solvent,used_reactor,has_duration,has_temperature,has_pressure,has_yield,has_reaction_step,related_resourceโ on top of its own catalysis-specific slots (catalyst_quantity,catalyst_type,reactor_temperature_range, ...).- Where a coremeta4cat-specific slot's purpose fully overlapped with an inherited one, the coremeta4cat slot was removed in favour of the inherited slot:
reactantwas dropped in favour ofused_reactant(rangeReagent). Other cases (e.g.experiment_pressurevs. the inheritedhas_pressure) are not identical in shape and remain an open reconciliation item, not yet a bug to "fix" by deleting one side. - Where
CatalyticReactionneeded to narrow an inherited slot (e.g. requiring a specificChemicalReactorrather than any generic reactor), the narrowing is applied viaslot_usageon the most specific already-existing inherited slot (used_reactor,is_a: carried_out_by) rather than on the generic parent relation (carried_out_by) directly. Narrowing the parent relation instead would duplicateused_reactorfor no reason -- both would resolve to the same effective field, just under two different names. - The same specialization applies one level down:
ChemicalReactor(and its 8 concrete reactor types) inheritsReactor's slots, rather than duplicating them.
Where you'll see this:
- Generated docs (
reaction.md) show the full inherited slot set underCatalyticReaction, not just the catalysis-specific ones. - The vocabulary workbook shows inherited fields as grey, read-only rows (see the workbook's Legend sheet) โ they're visible for reference, but they belong to chemdcat-ap, not to this schema, so they can't be edited through the Excel inbox workflow.
- Naming: the schema class is
CatalyticReaction, notReaction, specifically to avoid a name clash with chemdcat-ap'sChemicalReactionnow that one specializes the other. Generated docs display it as "Reaction" for readability, but always useCatalyticReactionin data files and code.