Serve data over OPTIMADE¶
This example starts with CIF files and a JSON table of results, stores both in
SQLite, and serves structures and _httk_records over OPTIMADE. Each result
has a formation energy and a relationship to its structure, using the same
typed-record pattern as altermagnets.
Use Python 3.12 or newer and install the three modules (also included in the
httk2 metapackage):
python -m pip install 'httk-atomistic[default]' 'httk-store[db]' httk-serve
The EntryRecord, DataEntryRecord, Property, and entry_record helpers in
the scripts below are currently unreleased. Use matching development
checkouts of httk-core and httk-store when trying this example.
Prepare the input files¶
Create this directory; results.sqlite will be generated by the import:
materials/
result_record.py
build_db.py
serve_db.py
results.json
cifs/
NaCl.cif
MgO.cif
Put your own CIF files in cifs/. For a complete runnable example, save the
following two files. Each lists the eight sites of a conventional rock-salt cell
explicitly, with the identity symmetry operation.
cifs/NaCl.cif:
data_NaCl
_cell_length_a 5.64
_cell_length_b 5.64
_cell_length_c 5.64
_cell_angle_alpha 90
_cell_angle_beta 90
_cell_angle_gamma 90
_space_group_IT_number 1
loop_
_space_group_symop_operation_xyz
'x,y,z'
loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
Na1 Na 0 0 0
Na2 Na 0.5 0.5 0
Na3 Na 0.5 0 0.5
Na4 Na 0 0.5 0.5
Cl1 Cl 0.5 0.5 0.5
Cl2 Cl 0 0 0.5
Cl3 Cl 0 0.5 0
Cl4 Cl 0.5 0 0
cifs/MgO.cif:
data_MgO
_cell_length_a 4.21
_cell_length_b 4.21
_cell_length_c 4.21
_cell_angle_alpha 90
_cell_angle_beta 90
_cell_angle_gamma 90
_space_group_IT_number 1
loop_
_space_group_symop_operation_xyz
'x,y,z'
loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
Mg1 Mg 0 0 0
Mg2 Mg 0.5 0.5 0
Mg3 Mg 0.5 0 0.5
Mg4 Mg 0 0.5 0.5
O1 O 0.5 0.5 0.5
O2 O 0 0 0.5
O3 O 0 0.5 0
O4 O 0.5 0 0
Save this as results.json. These are invented demonstration energies in
eV per atom, not calculated or measured values. Each cif names a file relative
to cifs/.
[
{"cif": "NaCl.cif", "formation_energy": -1.2},
{"cif": "MgO.cif", "formation_energy": -0.8}
]
Define, import, and serve the records¶
Save this as result_record.py. The class only says what a result contains:
the Property metadata gives the energy its meaning and unit, and the structure
field becomes an OPTIMADE relationship.
from typing import Annotated
from httk.atomistic import UnitcellStructureRecord
from httk.core import DataEntryRecord, Property, entry_record
@entry_record("example.result")
class Result(DataEntryRecord):
formation_energy: Annotated[
float,
Property(
unit="eV",
description="Formation energy per atom relative to elemental reference phases.",
),
]
structure: UnitcellStructureRecord
Save this as build_db.py:
import json
from pathlib import Path
from httk.atomistic import UnitcellStructureRecord, UnitcellStructureView
from httk.core import load
from httk.store import EntryIdScheme, SqliteStore
from result_record import Result
store = SqliteStore(
"results.sqlite", records=[Result], entry_ids=EntryIdScheme("example", "1")
)
for row in json.loads(Path("results.json").read_text()):
sid = store.save(UnitcellStructureView(load(Path("cifs") / row["cif"])))
structure = store.fetch(UnitcellStructureRecord, sid)
store.save(Result(row["formation_energy"], structure))
store.close()
Save this as serve_db.py:
from httk.serve.optimade import serve
from httk.store import SqliteStore
from result_record import Result
store = SqliteStore("results.sqlite", records=[Result])
serve(store, port=8080)
store.close()
Run the import once, then start the API:
python build_db.py
python serve_db.py
The server reads the persisted database directly; it does not need the JSON or
CIF files. Repeating the build deduplicates unchanged data. The custom energy
attribute is published as _httk_custom_formation_energy.
Query the API and include the linked structures¶
In another terminal:
curl http://127.0.0.1:8080/v1/info/_httk_records
curl http://127.0.0.1:8080/v1/structures
curl --get http://127.0.0.1:8080/v1/_httk_records \
--data-urlencode 'filter=_httk_custom_formation_energy < -1' \
--data-urlencode 'include=structures'
The last request selects the NaCl result. Its
attributes._httk_custom_formation_energy is -1.2, and its
relationships.structures points to the saved structure. The included array
contains that structure’s lattice, sites, species, and formula. Visit
/v1/info/_httk_records to see the energy property’s definition.
The API needs a running Python service, even if its companion website is published on a static host such as GitHub Pages. For more on database queries, ASGI deployment, and production serving, see the serve documentation and the store database documentation.