12. Draw the Ca–Ti–O phase diagram

The collected store contains the relaxed structure entries, total-energy records, and product links from page 11. The Run.outputs edges provide the same honest loose-reference join without assuming a SQL table layout; fetch the two entry types and pass the resulting structures and energies to the materials-science constructor:

from math import gcd

from httk.analyse.matsci import PhaseDiagram
from httk.atomistic import StructureEntry, UnitcellStructureView
from httk.core import DataRecord, Run
from httk.store import Backend, SqlStore

store = SqlStore(Backend.sqlite("presentation.sqlite"))
structures, energies, ids = [], [], []
search = store.searcher()
run = search.variable(Run)
for item in search.results(run=run).scalars():
    structure_edge = next(edge for edge in item.outputs if edge.entry_type == "structures")
    energy_edge = next(edge for edge in item.outputs if edge.entry_type == "_httk_records")
    structure = store.fetch_entry(StructureEntry, structure_edge.entry_id)
    energy = store.fetch_by_content_id(DataRecord, energy_edge.entry_id)
    assert structure is not None and energy is not None
    structures.append(structure)
    energies.append(energy.value)
    ids.append(structure_edge.entry_id)

pd = PhaseDiagram.from_structures(structures, energies, ids=ids)
views = [UnitcellStructureView(structure) for structure in structures]

def label(structure):
    amounts = structure.composition.amount_mapping
    divisor = 0
    for amount in amounts.values():
        divisor = gcd(divisor, int(amount))
    order = sorted(amounts, key=lambda element: (element == "O", element))
    return "".join(
        element + (str(int(amounts[element]) // divisor) if int(amounts[element]) // divisor != 1 else "")
        for element in order
    )

labels = [label(structure) for structure in views]
stable = {labels[index] for index in pd.hull_indices}
print("stable", sorted(stable))
print("energy above hull", dict(zip(
    labels,
    pd.energy_above_hull,
)))
print("TiO stable", "TiO" in stable)
assert len(structures) == 6
assert stable == {"Ca", "Ti", "O", "CaO", "CaTiO3"}
assert "TiO" not in stable

The identifiers are content IDs, so the code does not assume filenames or hard-code compositions or energies. To make the result readable, map the stable IDs back to the six page-09 tags in the surrounding application; the expected stable set is Ca, Ti, O, CaO, and CaTiO3, with TiO above the hull.

Plotting is an explicit presentation step:

ax = pd.plot()
ax.figure.savefig("catio3-phase-diagram.png", dpi=160, bbox_inches="tight")
print("saved catio3-phase-diagram.png")

The mock values are demonstration numbers, not physics. The solver uses numpy.float64 linear programming for the hull; from_structures derives the compositions from the collected structures and normalizes the total energies per atom before solving.

See the materials phase-diagram API in the versioned httk-analyse documentation for more analysis options.