httk quickstart: databases
httk-store provides relational storage and querying over SQLite and DuckDB, built on plain frozen dataclasses — no base classes, no ORM sessions. Exact values (rationals, surd cell bases) are stored exactly. The examples below need httk-store and httk-atomistic installed.
Create a database file, store a structure in it, and retrieve it
The first opening of a store declares which durable record layouts it may
contain and how public entry IDs are minted; here every stored structure uses the normalized
UnitcellStructureRecord layout:
from httk.atomistic import (
StructureEntry,
UnitcellStructure,
UnitcellStructureRecord,
UnitcellStructureView,
)
from httk.store import EntryIdScheme, SqliteStore
structure = UnitcellStructure(
cell=[["5.64", 0, 0], [0, "5.64", 0], [0, 0, "5.64"]],
sites=[
[0, 0, 0], ["1/2", "1/2", 0], ["1/2", 0, "1/2"], [0, "1/2", "1/2"],
["1/2", "1/2", "1/2"], [0, 0, "1/2"], [0, "1/2", 0], ["1/2", 0, 0],
],
species_at_sites=["Na", "Na", "Na", "Na", "Cl", "Cl", "Cl", "Cl"],
)
store = SqliteStore(
"example.sqlite",
entry_records={StructureEntry: UnitcellStructureRecord},
entry_ids=EntryIdScheme("example", "structures"),
)
sid = store.save(structure)
fetched = store.fetch(UnitcellStructureRecord, sid)
restored = UnitcellStructureView(fetched)
print("Saved row", sid, "with stable structure id", restored.id)
save() accepts the natural structure object and projects its cell, sites,
species, and composition recursively — there is no manual record-conversion
step. The hexadecimal .id is a content hash: structural, and stable across
equivalent objects and stores. The integer sid is only a local relational
row identifier. EntryIdScheme mints public lineage IDs such as
example-structures-1; revision IDs append ~1. Saving an equal structure
again deduplicates to the same row.
SqliteStore() without a filename creates an in-memory database;
DuckdbStore(...) works the same way.
Search the database
Bind a variable to a record class, add conditions, and freeze the query with
results():
search = store.searcher()
s = search.variable(UnitcellStructureRecord)
search.add(s.species_at_sites.has_any("Cl"))
for row in search.results(structure=s):
print("Found:", UnitcellStructureView(row.structure).formula)
Running this generates the output:
Found: ClNa
List fields have set operations (has_any, has_only, is_in),
references chain into automatic joins, and two variables of the same class form
a self-join.
Store your own data
Any frozen dataclass whose field types resolve is storable. Storage behavior is
tuned with typing.Annotated markers from httk-core (Indexed,
Unique, Skip, Shape, ...), and rational values round-trip exactly:
from dataclasses import dataclass
from fractions import Fraction
from typing import Annotated
from httk.core import Indexed
from httk.store import SqliteStore
@dataclass(frozen=True)
class Measurement:
formula: Annotated[str, Indexed()]
spacegroup: int
energy: Fraction
store = SqliteStore(entry_records={})
sid = store.save(Measurement("NaCl", 225, Fraction(-13, 3)))
store.save(Measurement("MgO", 225, Fraction(-29, 7)))
print("Energy round-trips exactly:", store.fetch(Measurement, sid).energy)
search = store.searcher()
m = search.variable(Measurement)
search.add(m.spacegroup == 225)
for row in search.results(measurement=m):
print(row.measurement.formula, row.measurement.energy)
Running this generates the output:
Energy round-trips exactly: -13/3
NaCl -13/3
MgO -29/7
The explicit entry_records={} declares a store containing only private
custom dataclasses.
More
The top-site data guide gives the ecosystem overview. The full database details cover child tables and references, content-based deduplication, cursors, store federation, and serving a store over the OPTIMADE protocol.