Categorization
There are thousands of computational models and methods, related to each other in more than one way. A strict tree cannot hold them: an exchange-correlation functional is simultaneously "a DFT thing", "a GGA thing" and "a Perdew-Burke-Ernzerhof thing", and different users arrive from different directions. A flat list cannot hold them either.
The first thing to know is that *_category names two different schemes, not one, and that
each is a published design rather than an accident of history. Models and methods follow
CateCom: a small ordered vocabulary of tiers, carried as data on the entity. Materials follow
M-CODE: a structure class defined as a recipe over building blocks, positioned on three axes.
Reading a materials_category/… path expecting tiers is the single most likely way to misread the
corpus.
| Category domain | Schemas | Scheme |
|---|---|---|
models-category |
25 | 16 tiered + 9 enum holders |
methods-category |
79 | 57 tiered + 22 enum holders |
materials-category |
17 | M-CODE — compositional |
materials-category-components |
31 | 25 entities + 6 operations |
Entities carrying a tiered categories field: method/unit-method, model/model-without-method.
Scheme 1 — CateCom, for models and methods
The tier approach comes from the CateCom paper: categorize along a small number of ordered tiers, and keep the vocabulary separate from the instances.
core/reusable/categories is the shape:
| Field | Meaning |
|---|---|
tier1 |
top-level category, e.g. pb (physics-based) |
tier2 |
second level, e.g. qm (quantum mechanical) |
tier3 |
third level, e.g. dft |
type |
general type within that path |
subtype |
specific variant |
Each field is a slugified_entry_or_slug — either a bare slug string, or an object carrying the
slug plus a human-readable name. That duality is deliberate: compact records stay compact, while
records that need to render a label can carry one without a second lookup.
The vocabulary is a chain of narrowings
A tier vocabulary is not one schema listing everything. It is a chain down the directory tree in
which each schema allOfs its parent and narrows exactly one more field to an enumeration held
in a sibling enum_options.json:
| Schema | Extends | Narrows |
|---|---|---|
models-category/pb |
core/reusable/categories |
tier1 → #/physicsBased |
models-category/pb/qm |
…/pb |
tier2 → #/quantumMechanical |
models-category/pb/qm/dft |
…/qm |
tier3 |
models-category/pb/qm/dft/ksdft |
…/dft |
type |
models-category/pb/qm/dft/ksdft/gga |
…/ksdft |
subtype |
Depth in the tree therefore tracks depth in the tier scheme, and a leaf schema like
…/ksdft/gga transitively constrains all five fields. Sixty-six of the 73 vocabulary schemas
narrow exactly one field; the other seven pin type and subtype together as a leaf shortcut.
The $ref-with-a-fragment into enum_options.json is how enumerations are shared without
duplication — 144 references in the corpus carry a JSON pointer, and this is the dominant reason.
Where the tiers actually land
The vocabulary constrains; it does not enumerate instances. The tiers reach real data through just
two entity components, listed in the table above: model/model-without-method and
method/unit-method each carry a categories object. Everything else in the tier tree exists to
say which combinations are legal.
Scheme 2 — M-CODE, for materials
Nothing under materials_category/ extends core/reusable/categories. Materials are not placed on
a tier ladder; a material category is a recipe. The scheme is
M-CODE — Materials Categorization via Ontology, Dimensionality
and Evolution (Biryukov, Choudhary, Bazhirov) — and its three axes are not merely related to the
materials_category tree: they are its directory structure.
| M-CODE axis | Where it lives | Values |
|---|---|---|
| Structural complexity (ontology) | first path segment | pristine_structures, compound_pristine_structures, defective_structures, processed_structures |
| Dimensionality | second path segment | zero-dimensional through three-dimensional |
| Evolution | the operation reached through allOf |
combinations stack, merge; modifications repeat, strain, perturb |
The third axis draws on materials_category_components/, which holds both the entities the
recipes are built from (crystal, vacuum, atom, vacancy, void_region, atomic layers…) and
the operations that combine them. The component entities carry the dimensionality axis too.
A category schema names an operation via allOf and lists its operands under stack_components or
merge_components. materials-category/pristine-structures/two-dimensional/slab
reads, in full, as a stack of [repeated unique atomic layers, vacuum]:
{
"allOf": [{ "$ref": ".../operations/core/combinations/stack.json" }],
"properties": {
"stack_components": {
"items": [
{ "$ref": ".../entities/reusable/two-dimensional/atomic_layers_unique_repeated.json" },
{ "$ref": ".../entities/core/two-dimensional/vacuum.json" }
]
}
}
}
Categories then compose on top of categories. An island defect is a stack of [slab, merge(slab, void_region), vacuum] — a recipe whose ingredients include another recipe. This is why the components layer is larger than the category layer it serves: a small set of entities and six operations generates the structure space, and adding a structure type usually means writing a new recipe rather than a new vocabulary.
The trade-off is deliberate. Tiers are good for retrieval — filter on tier2 = qm and you get
every quantum-mechanical model. Recipes are good for construction — the schema for a slab is
also, quite literally, the instructions for building one.
Category versus directory
Underneath both schemes sits the distinction that names roughly half the directories in schema/:
*_categoryschemas define the vocabulary or the recipe — what is allowed, or what a structure is made of. They constrain; they do not enumerate instances.*_directoryschemas are the catalogue — the concrete, individually described entries.models_directory/gw,properties_directory/scalar/total_energy,software_directory/modeling/espresso.materials_category_componentsis the parts bin that scheme 2 draws from.
Two roughly equal-sized layers result:
| Layer | Schemas |
|---|---|
directory |
156 |
category |
152 |
entity-component |
107 |
system |
38 |
reusable |
31 |
primitive |
23 |
application-parsing |
17 |
entity |
11 |
reference |
10 |
abstract |
9 |
in-memory-entity |
7 |
definition |
4 |
Both are browsable. The schema explorer carries a
Categories view, which walks the CateCom ladders and the three
M-CODE axes and shows each catalogue entry at the coordinate it is filed under, and a
Directories view, which lists every catalogue entry grouped by
its own facet — properties by value shape, software by kind, models and methods by their ladder
position. The file tree cannot show either: a ladder is a chain of allOf, not a path.
Worked example: finding an exchange-correlation functional
Suppose you want the PBE functional and you do not know where it lives.
- Start at the tier vocabulary. A DFT functional is physics-based, so
tier1ispb. The allowed values are inmodels_category/enum_options.json, referenced bymodels-category/pb. - Narrow through the tiers.
tier2isqm,tier3isdft,typeisksdft, and GGA is thesubtype— which is exactly the pathmodels_category/pb/qm/dft/ksdft/gga.json. - Cross to the catalogue. The concrete entries live under
models_directory/. The functional itself is amodel/mixins/functionalconcern composed into the model schema. - Check the manifest.
manifest/functional_lookup_table.yamlandmanifest/dft_unit_functionals.yamlare the registries for functionals, asmanifest/properties.yamlis for properties.
The general shape of the answer: categories tell you the coordinates, directories hold the
thing. If you are looking for "what values are allowed", read a *_category schema. If you are
looking for "the actual entry for X", read a *_directory one.
The manifests
manifest/*.yaml sits outside schema/ because it is a registry rather than a format:
properties.yaml— every property, itsschemaId, default units, andisResult/isMonitorflags. This is what the platform reads to know which properties are computed results and which are progress monitors.models.yaml,functional_lookup_table.yaml,dft_unit_functionals.yaml— the equivalent registries for models and functionals.
The manifests are joined to the schemas by schemaId, and that join is checked: a manifest entry
naming a schema that does not exist fails the build. It used to be possible for the two to drift
apart silently.
Why keep the tiers in the data?
The tier vocabulary is mirrored in the directory tree, so it is fair to ask why the tiers are also
carried as fields. Because the tree can express only one hierarchy, and it can express it only for
schemas. The categories object travels with an instance: a stored model record can be filtered
on any tier, surfaced under more than one route, and re-categorized without moving a file and
breaking every $id that points at it.
Materials pay a different price for a different benefit. Their recipes are precise enough to build
from, but there is no categories field on a material configuration to filter on, so retrieval
across the materials space works by structure class and dimensionality — the path — rather than by
query.
That asymmetry is deliberate. It is not the older scheme waiting to be extended to materials: the two schemes were designed separately, by different papers, for domains whose questions differ — the retrieval-versus-construction trade-off above. Encoding a recipe as five tier slugs would flatten out exactly the part that makes it useful.