How it works¶
httk-optimade implements an OPTIMADE server as a pipeline of small, independently testable layers. The package was ported from the OPTIMADE implementation in httk v1 and reorganized around two explicit seams: a web-framework seam and a storage-backend seam.
Request flow¶
ASGI request
└─ runtime/asgi.py — adapts the HTTP request to a RawRequest
└─ engine/processing.py — process(): dispatches to an endpoint
├─ engine/validate.py — validates URL, version, and query parameters
├─ filter/ — parses the OPTIMADE filter language to an AST
├─ query_function — the backend seam (executes entry queries)
└─ endpoints/ — builds the JSON:API reply documents
Runtime (
httk.optimade.runtime): a Starlette application with a single catch-all GET route. It builds aRawRequest(base URL, representation, query string) and renders the resultingEndpointResponse. All errors are converted to OPTIMADE JSON:API error documents.Engine (
httk.optimade.engine):validate_optimade_request()resolves the endpoint (including versioned base URLs like/v1.0.0/structures), validates query parameters, and computes the response fields.process()routes the validated request to the matching endpoint reply generator, parsing thefilter=parameter when present.Filter parsing (
httk.optimade.filter): the OPTIMADE filter grammar (shipped asoptimade_filter_grammar.ebnf) is parsed with a vendored LR(1) parser into a nested-tuple abstract syntax tree.Backend (
httk.optimade.backend): the filter AST is translated into backend search expressions and executed. This is the storage seam.
The backend seam¶
process() never touches storage directly; it calls a QueryFunction
callback. The standard implementation is provided by BackendAdapter:
Store/Searcherprotocols (backend/protocols.py) describe the query interface a storage backend must implement (mirroring the httk v1httk.dbsearcher API:variable(),add(),count(), set operations likehas_any, literal string matching (contains/startswith/endswith, which carry no pattern syntax), and the comparison operators).EntrySourcepairs a queryable target (e.g. a table or type) with a mapping from OPTIMADE response fields to row extractors. An entry endpoint can be backed by several sources; results are concatenated and offset/limit are redistributed across them.Field handlers (
backend/handlers.py) translate filter operations on OPTIMADE properties into search expressions over backend fields. A backend supplies a handler table per entry type;simple_property_handlers()derives one generically from a property-key map and the entry’s property types. When aBackendAdapteris given no handlers, it derives them from its schema.
httk-optimade is a generic implementation of the OPTIMADE protocol: it
carries no materials-science knowledge of its own. The served entry types,
their properties, and their records are supplied from outside — see
Entry providers. A backend can also plug in at the lower
level by implementing the store protocols directly; the repository’s
examples/demo_server/ shows a complete in-memory implementation serving
several entry types.
Entry providers¶
For a worked usage guide with runnable examples, see Serving entry providers.
The materials-science mapping lives outside httk-optimade, behind the neutral
httk.core.EntryProvider contract (defined in httk-core, so neither package
depends on the other). A provider describes its entry types (as first-class
httk.core.EntryTypeDefinition objects — vendored standards or definitions built
with from_optimade/from_simple), states how each served property maps to a
record key, and yields the records:
entry_types()→ entry-type name to anEntryTypeDefinition;property_keys(entry_type)→ served-property to record-key map (at leastid/type; every served name must be described by the definition);records(entry_type)→ an iterable of plain JSON-able record dicts.
adapter_from_providers([...]) (backend/providers.py) turns one or more
providers into a fully wired BackendAdapter over an in-memory store: it builds
the ServedSchema from the definitions (validating each served property
against the definition), the filter handlers from the property keys, and the
response-field extractors from the property keys, then loads the records.
The materials provider itself lives in httk-atomistic
(httk.atomistic.structure_entries.StructureEntryProvider), which serves
OPTIMADE structures — species, species_at_sites, lattice_vectors,
cartesian_site_positions, … — from httk.atomistic.Structure objects.
Providers self-register a factory via httk.core.register_entry_provider, so
providers_from_registry() can enumerate the installed ones (applications
instantiate them with their data).
OPTIMADE version support¶
The served API version is 1.3.0 (versioned base URLs /v1, /v1.3, /v1.3.0).
Relative to OPTIMADE v1.0.0, the implementation includes:
boolean values (
TRUE/FALSE) in the filter language (v1.2),the v1.2 entry listing info format: top-level
id/typeand properties presented as OPTIMADE Property Definitions (the property-definition model and generator live in httk-core,httk.core.property_definitions),extended
metainformation:implementationwithsource_urlandissue_tracker, and the optionalschema,database, andrequest_delayfields viaOptimadeConfig,the structures properties added in v1.2 (space-group symmetry fields) and v1.3 (
fractional_site_positions,site_coordinate_span,optimization_type,wyckoff_positions, …) — recognized in filters and as response fields; they are served asnulluntil a backend implements them,sorting of entry listings (the
sortquery parameter),relationships between entries and the
includequery parameter (compound documents with a top-levelincludedfield), with per-identifier relationshipmeta(descriptionand the v1.3role),filtering on relationships:
<type>.id HAS ...and depth-1 relationship-property filters such asreferences.doi CONTAINS "10.1"(resolved by a two-phase semi-join over the related entry type; each dotted filter node is resolved independently, matching the reference implementation’s semantics),the
references,files, andtrajectoriesentry types,per-property metadata (
meta.property_metadataand thex-optimade-metadata-definitionin property definitions),the partial data protocol (JSON Lines format and the
dimension_slicesquery parameter) with the compact list representation for trajectories,the
license,available_licenses, andavailable_licenses_for_entriesbase-info attributes, and thewarnings,last_id, andlinks.describedbyresponse fields viaOptimadeConfig.
Optional parts of the specification that are not implemented: cross-source sort
merging, filtering on relationship paths nested deeper than one level
(references.structures.x), on relationship meta
(.description/.role), and dotted LENGTH filters, the sparse JSON Lines
layout, index meta-databases, transaction mechanisms, and rejection of
unrecognized query parameters.
Serving additional entry types¶
The served entry types and their properties are described by a ServedSchema
(schema/served.py), built with build_served_schema(). A backend registers
extra entry types by passing them in and wiring an EntrySource for each:
build_served_schema(definitions, served)derives the endpoint/field tables from a mapping of entry type toEntryTypeDefinitionand the per-entry list of served property names (defaulting to every described property). Thetrajectoriesentry type is generated by frame-wrapping thestructuresproperties (schema/trajectories.py, which takes the structuresEntryTypeDefinition) and turning the result into a definition viaentry_type_definition_from_simple.BackendAdapter(schema=..., sources={entry: (EntrySource(...),)})binds each served entry type to a queryable target and its field extractors.
The examples/demo_server/ backend registers references, files, and
trajectories this way alongside structures and calculations.
Large properties and slicing¶
Field extractors may return a PartialValue (backend/partial.py) instead of
a concrete value for large, dimensioned properties (e.g. a trajectory’s
cartesian_site_positions). In a normal reply such a value is served as null
with a meta.partial_data_links entry pointing at
partial_data/<type>/<id>/<property>, which streams the data in the JSON Lines
format. A client may instead request an inline slice with the
dimension_slices=name[start:stop:step] query parameter (single-entry
endpoints only); note that stop is inclusive per the specification. Values
that are constant across a dimension (e.g. nelements across a trajectory’s
frames) are served as single-item constant compact lists, which is legal
because those axes are declared compactable.
Differences from the httk v1 implementation¶
The stdlib
httk.httkweb.webserverbinding was replaced by a Starlette ASGI app + uvicorn development server (serve()), andcreate_asgi_app()supports production ASGI deployment.serve()takes aBackendAdapterinstead of a rawhttk.dbstore.The client-side
validation/subpackage (stale at OPTIMADE 0.9.5) was not ported; use the officialoptimade-validatorinstead.Several latent bugs were fixed (query-string derivation, caller-supplied endpoints, offsets beyond the result set) — see the regression tests in
tests/.Timestamps in
metaare now UTC.