Skip to content

Python API

The command line is a thin layer over these functions; use them from notebooks or your own scripts.

from meidnet.config import load_config, config_from_dict
from meidnet.pipeline import check, train, generate

cfg = load_config("meidnet.yaml")
info = check(cfg)                 # dict: report (counts, skip reasons), records, family, report_path
info = check(cfg, keep_structures=True)   # records also keep their pymatgen Structure (record.structure)
model_path = train(cfg)           # writes model.pt + training_report.html, returns the path
result = generate(cfg)            # GenerationResult: .saved (candidates), .targets (funnel logs)

Pieces

from meidnet.checkpoint import load_checkpoint, describe, property_ranges
lm = load_checkpoint("model.pt")          # LoadedModel: .model, .stats (property names/units/normalisation)
lm.model.encode_crystal(x)                # structure → latent (x from meidnet.data.featurize)
lm.model.encode_properties(p)             # normalised property vector → latent

from meidnet.family import load_family
fam = load_family("perovskite_abx3", variant="halide", exclude=["Pb"], overrides={"tolerance_factor": {"max": 1.0}})

from meidnet.constraints import build_candidate, evaluate, CONSTRAINTS
cand = evaluate(build_candidate(fam, {"A": "Cs", "B": "Sn", "X": "Br"}), fam.constraints)
cand.passed, [r.to_dict() for r in cand.results]

from meidnet.generate import Designer
from meidnet.config import GenerationSection
g = GenerationSection(family="perovskite_abx3", variant="halide",
                      objectives=[{"property": "dir_gap"}], targets=[{"dir_gap": 2.0}], rounds=3, steps=200)
res = Designer(lm, fam, g, on_saved=lambda c, t: print(c.formula)).run("out/")

from meidnet.designspace import enumerate_space, space_to_csv
space = enumerate_space(fam, lm)          # every composition: rule values + predictions

Custom rules and search terms: CONSTRAINTS.register, meidnet.terms.SEARCH_TERMS.register.

Extra rules in the configuration

generation.extra_constraints adds rules to the family's own for one run, without editing the family file — this is how the Studio saves a predicted-property window:

generation:
  family: perovskite_abx3
  variant: halide
  extra_constraints:
    - {name: property_window, property: dir_gap, min: 1.5, max: 3.0}

meidnet.pipeline.family_for(cfg, need_variant=True) returns the family a run uses, with these rules appended; generation.family / generation.variant take precedence over the top-level family.

3D datasets (chemiscope)

meidnet.studio.chemiscope turns MEIDNet objects into chemiscope datasets — plain dicts you can save with json.dump and open at chemiscope.org, or show in a notebook with chemiscope.show(...):

from meidnet.studio.chemiscope import (space_dataset, candidates_dataset, records_dataset,
                                       published_data_dataset, structure_to_chemiscope)

ds = space_dataset(fam, space, lm, max_structures=2000)   # design space: formula, site_*, pred_*, rule_*, passes_all
ds = candidates_dataset(candidate_dicts, fam, run_dir, lm=lm)   # candidates of a run (reads their CIFs when present;
                                                                # lm adds units and property names)
ds = records_dataset(info["records"], cfg.data.properties, source="my data")   # needs check(cfg, keep_structures=True)
structure_to_chemiscope(structure)                         # one pymatgen Structure → {size, names, x, y, z, cell}

candidate_dicts are saved candidates as dicts — [c.to_dict() for c in res.saved] after a Designer run, or [c for t in json.load(open("generation.json"))["targets"] for c in t["saved"]] from a finished run. run_dir is the run folder that contains generation/ (cfg.out_dir after meidnet generate); where a CIF file is not found, the structure is rebuilt on the prototype from the candidate's elements.

Scripting the Studio

The Studio server is a small class you can drive without a browser — the 04 Colab notebook does exactly this. Every method takes the same JSON-like dicts the page sends; session keeps users (or experiments) apart.

import base64
from meidnet.studio.server import Studio

st = Studio(None)                                   # the published model; or Studio(load_config("meidnet.yaml"))
sid = "my-session"
st.state(sid)                                       # model, ranges, families, limits, session info

b64 = base64.b64encode(open("materials.csv", "rb").read()).decode()
up = st.upload({"session": sid, "files": [{"name": "materials.csv", "b64": b64}]})
up["suggest"]                                       # suggested id / CIF / property columns

st.check_data({"session": sid, "id_column": "material_id", "cif_column": "cif",
               "properties": [{"column": "dir_gap", "unit": "eV"}],
               "family": "perovskite_abx3", "variant": "halide"})
st.start_train({"session": sid, "epochs": 10, "batch_size": 16})
st.status(sid, "train")                             # poll: running, progress (epoch, loss, curve), done, error
st.select_model(sid, "published")                   # or "own"

st.family({"session": sid, "family": "perovskite_abx3", "variant": "halide"})   # design space, scored
st.start_search({"session": sid, "generation": {"per_target": 2, "rounds": 2, "steps": 100}})
st.status(sid, "search")                            # candidates as they are found
st.chemiscope(sid, "space", "perovskite_abx3", "halide")   # or "candidates" / "data"
st.validate({"yaml": "generation:\n  rounds: 2\n"})        # {ok, generation, notes} or {ok: False, errors}
st.export({"session": sid, "generation": {}})              # the meidnet.yaml text