Skip to main content

chem_llm

An LLM agent that runs end-to-end DFT band-structure studies with Quantum ESPRESSO.

Give it a compound and a space group. It fetches the structure from the Materials Project, reduces it to the primitive cell, builds a perturbed structure ensemble, writes the Quantum ESPRESSO inputs, runs SCF + bands + post-processing, and plots the band structure. It writes every script itself, and checks its own work against a set of invariants: electron counts are read from pw.out rather than recalled, k-paths are derived from the actual cell, units are converted rather than assumed.

from chem_llm import DFTAgent

agent = DFTAgent()
result = agent.run("C", "Fd-3m", spacegroup_name="diamond")

print(result)        # C (Fd-3m): OK | 4.040 eV | 49 steps | 49.9 min | Qwen/Qwen3.8-27B
print(result.plot)   # chem_llm_runs/C/calculations/000/bands_000.pdf

Install

pip install chem-llm                 # the agent, local Qwen backend
pip install "chem-llm[claude]"       # + the Claude API backend
pip install "chem-llm[notebook]"     # + JupyterLab, to run main.ipynb

Python 3.12+. You also need:

  • Quantum ESPRESSO — a working pw.x and bands.x. It is not a Python dependency.
  • A GPU for the default local model (Qwen/Qwen3.8-27B, ~55 GB of bf16 weights; four 40 GB A100s is comfortable). The Claude API backend needs no GPU but bills per step.
  • A Materials Project API key in MP_API_KEY — the agent fetches every structure from there.

Keys are read from the environment, or from a .env file in the working directory:

MP_API_KEY=...
HF_TOKEN=...            # only needed for gated Hugging Face models
ANTHROPIC_API_KEY=...   # only for backend="claude"

Configuration

An agent's configuration is a Settings object. Each DFTAgent owns a private copy, so agents with different settings can coexist in one process — there is no global configuration step.

Defaults

DFTAgent() with no arguments runs with the setup this project was developed with on NERSC Perlmutter. DFTAgent.default_settings() returns them as an editable object.

default
model Qwen/Qwen3.8-27B, local (backend="qwen")
generation max_new_tokens=5000, greedy (do_sample=False)
agent loop max_steps=80, max_history=32, read_max_chars=10000
Quantum ESPRESSO pw.x / bands.x, serial, OMP_NUM_THREADS=1, 2 h timeout per step
QE module espresso/7.5-libxc-7.0.0-cpu on Perlmutter, none elsewhere
model cache $HF_HOME, else $PSCRATCH/huggingface on NERSC, else ~/.cache/huggingface
docs index ./retrieval_index/ase
runs ./chem_llm_runs/<compound>

The two machine-specific defaults turn on only when the environment identifies Perlmutter (NERSC_HOST) or NERSC scratch ($PSCRATCH). On NERSC the model cache must not live on the home filesystem: it rejects the file locks Hugging Face takes (OSError: [Errno 524]).

Changing them

Three equivalent ways:

from chem_llm import DFTAgent, Settings

# keyword overrides, for one-off changes
agent = DFTAgent(pw_x="/opt/qe/bin/pw.x", mpi_prefix="srun -n 8", max_steps=60)

# edit the defaults
settings = DFTAgent.default_settings()
settings.qe.module = None
settings.model.max_new_tokens = 4000
agent = DFTAgent(settings)

# a config file (https://github.com/brtboi/chem_llm/blob/master/config.example.yaml documents every field)
agent = DFTAgent(Settings.from_yaml("config.yaml"))

Precedence is keyword argument > Settings field > environment / .env > default.

Settings has three nested sections:

  • qe — QESettings: pw_x, bands_x, module, mpi_prefix, omp_num_threads, timeout_seconds
  • model — ModelSettings: backend, qwen_model, max_new_tokens, temperature, do_sample, and claude_*
  • agent — AgentSettings: max_steps, max_history, read_max_chars

At the top level it holds the credentials (mp_api_key, hf_token, anthropic_api_key) and the paths hf_home, doc_index_dir and work_root.

The agent copies the Settings it is given, so edit agent.settings once an agent exists. Generation and loop parameters are read when each run starts, so they can change between runs without reloading the model. The backend and model id are fixed once the model loads.

While a run executes, its agent's settings are what the tools see — how QE is invoked, the Materials Project key, the docs index — and what the agent's own scripts inherit. That is scoped to the run, not exported process-wide.

Running

agent = DFTAgent()
agent.load()                        # optional: pay the model-load cost up front

result = agent.run(
    "CsPbBr3", "Pm-3m",             # required: formula, space group (Materials Project notation)
    spacegroup_number=221,
    spacegroup_name="cubic perovskite",
    work_dir="runs/cspbbr3",        # default: settings.work_root / compound
    clear_dir=True,
    log_file="runs/cspbbr3.jsonl",  # default: work_dir / log.jsonl
    n_structures=51,                # size of the perturbed ensemble
    relativistic="scalar",          # or "fully" for spin-orbit
    functional="pbesol",
)

The model loads once and stays loaded, so a sweep pays the (multi-minute) load cost once:

for compound, spacegroup in [("C", "Fd-3m"), ("MgO", "Fm-3m"), ("GaAs", "F-43m")]:
    print(agent.run(compound, spacegroup))

run() returns a RunResult read back off disk rather than from the agent's own claims: completed, scf_converged, dft_finished, n_atoms, n_electrons, band_gap (eV, computed from the bands output), plot, summary (the agent's own write-up), num_steps, runtime_seconds, and the full agent state. result.ok is true only if the agent finished and all three QE stages reported JOB DONE and a gap could be computed.

For a single run without keeping an agent around, run_dft_agent("Si", "Fd-3m") builds one, runs it and discards it.

Documentation index

The agent's search_docs tool searches a local index of the ASE and Quantum ESPRESSO reference docs. Without it the agent works from memory of API signatures and namelist variables, which is where most of its wasted steps come from. Every agent has one (agent.docs, at settings.doc_index_dir); build it once per machine:

docs = agent.docs
if not docs.available:
    docs.build()                              # scrapes + embeds; needs network, a few minutes

docs.search("ecutwfc units", top_k=3)         # the same retrieval the agent's tool runs

A different index: DFTAgent(docs="path/to/index") or DocumentationIndex("path").build().

Backends

backend="qwen" (the default) runs a local transformers model on your own GPU and costs nothing per step. backend="claude" drives the same loop through the Anthropic API: same prompt, same parsing, same tools, only the text generation differs. It needs the claude extra (pip install "chem-llm[claude]"), ANTHROPIC_API_KEY, and bills for every step — a full run is 40–60 API calls.

Command line

chem-llm TiO2 P4_2/mnm --name rutile --work-dir runs/tio2
chem-llm C Fd-3m --pw-x /opt/qe/bin/pw.x --max-steps 60
chem-llm --build-docs                        # build the documentation index
chem-llm --help

chem-llm-replay sandbox/test27               # re-execute a logged run's tool calls, no LLM
chem-llm-replay-copy 17 25                   # replay sandbox/test17 into sandbox/test25

chem-llm reads config.yaml from the current directory or its parents when one exists (or --config PATH), otherwise the defaults.

Repository layout

The source repository holds more than the package:

chem_llm/            the package
  agent.py           DFTAgent, DocumentationIndex         (public API)
  settings.py        Settings and its sections             (public API)
  pipeline.py        RunResult, run_dft_agent, output inspection
  tasks.py           the task prompt (build_task)
  agent_core.py      system prompt, agent loop, tool-call parsing
  claude_backend.py  Claude API backend
  tools/             the agent's tools: files, Python, QE, Materials Project, pseudopotentials,
                     docs search, QE input validation
  retrieval/         documentation scraping, chunking, BM25 + embedding search, reranking
  main.py, replay.py command-line entry points
  config.py          legacy module constants, used by the sandbox run scripts
main.ipynb           walkthrough: settings, docs index, one run, a sweep
config.example.yaml  every setting, documented
scripts/             build_doc_index.py, build_qe_namelists.py (regenerates tools/qe_namelists.py)
sandbox/             past experiment runs and their run_test*.py drivers
logs/                historical run logs
example/, deepseudopot_example/, deepseudopot_agent.ipynb, DEEPSEUDOPOT_HANDOFF.md
                     reference workflow + the paused DeePseudopot (neural pseudopotential) work

Development

From a checkout of the repository:

uv sync                               # editable install + the dev group (Jupyter, anthropic)
uv build                              # -> dist/: wheel and sdist
python scripts/build_qe_namelists.py  # after a QE version bump: refresh the namelist table

main.ipynb walks through settings, the documentation index, one run and a sweep.

The (currently hidden) train_deepseudopot tool runs DeePseudopot, which has its own license and is not shipped in the package. The repository vendors a checkout at chem_llm/DeePseudopot/; with an installed package, set DEEPSEUDOPOT_DIR to a checkout.

License

MIT — see LICENSE.

Metadata

Release files for chem-llm 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for chem-llm 0.1.0
File Size Uploaded Provenance Actions
chem_llm-0.1.0.tar.gz 73.8 kB Without provenance Details for chem_llm-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for chem-llm 0.1.0
File Interpreter ABI Platform Actions
chem_llm-0.1.0-py3-none-any.whl Python 3 none any Details for chem_llm-0.1.0-py3-none-any.whl

Total release size: 163.1 kB

Release files / chem_llm-0.1.0.tar.gz

Download URL chem_llm-0.1.0.tar.gz
Size 73.8 kB
Tags Source
SHA-256 checksum
How to use checksums
fe727c48b5f4f1f8e8aa77d7c6c0a5b28edcb8c14375f62726d91a172fdd21ec
BLAKE2b-256 checksum
How to use checksums
1a575e6fcd8e31b61785d4dfe06a43340651a2e2ceef29e1f855244e9d0cbff0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"SLES","version":"15.6","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / chem_llm-0.1.0-py3-none-any.whl

Download URL chem_llm-0.1.0-py3-none-any.whl
Size 89.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ec581c9870fa9508cca013beeba4af0a3e0d5325214d0551ce3335886722e388
BLAKE2b-256 checksum
How to use checksums
e384c9f4639f9614cae303d0da5670a210e7898f79a7cdacf0c8b185726e4bce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"SLES","version":"15.6","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for chem-llm 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page