# 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](https://github.com/Anyterial/altermagnets). Use Python 3.12 or newer and install the three modules (also included in the `httk2` metapackage): ```bash 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: ```text 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`: ```text 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`: ```text 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/`. ```json [ {"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. ```python 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`: ```python 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`: ```python 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: ```bash 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: ```bash 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](https://docs.httk.org/httk-serve/dev/main/) and the [store database documentation](https://docs.httk.org/httk-store/dev/main/db.html).