Logging & output#
Every run in this guide has quietly produced a transcript — the system summary, the SCF cycle table, the convergence check. This chapter is about seeing, replaying, and saving that output: how to stream it live, render it after the fact, get it as machine-readable data, and persist a whole calculation to disk.
One event stream behind everything#
qc-rs’s logging and display share a single structured event stream. As a run proceeds it emits typed
events — a System summary, a plan, per-cycle SCF records, a result — and every way of viewing output is
just a different rendering of that one stream. That is why the live log, the replayed transcript, and the raw
JSON all show the same information: they are the same events, formatted differently. Because it is
structured (not free-text prints), you can render it as a human transcript or consume it as data.
Live logging: run(log=...)#
.run() is silent by default — it computes and returns. Pass log= to stream the transcript as the
run happens (introduced in the quickstart):
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run(log="stdout")
The log= target can be:
|
effect |
|---|---|
|
stream to the terminal / notebook cell as the run proceeds |
a file path |
write the rendered transcript to that file |
a file-like object |
write to any stream you pass |
Two more controls shape the live output:
log_style="modern"(default) or"orca"— the visual style of the transcript (a compact modern layout, or one reminiscent of ORCA output).plot=True— draw the SCF convergence plot inline as it runs (needs matplotlib; see visualization).
Tip
MPI runs
On a parallel run, log_rank="root" (default) logs only rank 0; "all", "gather", or a list of ranks
control which ranks report. This keeps a many-rank run from flooding the output. Parallelism itself is
Part IV.
Replaying a finished run: log() and show()#
The transcript is stored on the checkpoint, so you can render it after the run without recomputing:
m.log() # replay the transcript as text
m.log(format="markdown") # ... as Markdown (nice in a notebook)
m.log(format="jsonl") # ... as one JSON object per line
m.show("result") # a rendered snapshot of the current state + results
log(format=...)re-renders the stored event stream —"text"(default),"markdown", or"jsonl".show(...)renders a snapshot (state and results) rather than the running transcript — useful for a quick “what does this checkpoint hold right now” view.
Because these read the stored events, they cost nothing to call and give the identical transcript every
time — no need to re-run with log="stdout" just to see the output again.
Output as data: run_events()#
For programmatic use — testing, dashboards, harvesting numbers — run_events() returns the raw event
stream as a list of JSON strings, one per event:
import json
events = m.run_events() # list[str], each a JSON object
len(events) # 18
first = json.loads(events[0]) # the System summary event
first["event"] # e.g. "system"
first["nao"], first["nuclear_repulsion"] # structured fields, ready to use
[json.loads(e).get("kind") for e in events] # e.g. the auto-inserted 'sad' guess shows up here
Each event is a dict with an event field naming its type and the structured payload for that type
(atom list, basis size, per-cycle energies, …). This is the stream the human transcript is rendered from,
so nothing shown in the log is hidden from the data.
Saving and loading a calculation#
A checkpoint — molecule, current electronic state, results, and transcript — persists to an HDF5 .qch5
file, and reloads into a checkpoint you can query or extend:
m.save("water.qch5") # persist the whole checkpoint
r = qc.chk.load("water.qch5") # restore it
r.scf.energy # -76.026794 results survive the round-trip
r.scf(xc="b3lyp").run() # ... and you can add more steps to the loaded checkpoint
save()/load() are how you stop and resume work: run an expensive SCF once, save it, and later load it
to compute properties, optimize, or restart — no recomputation. (This is also the file behind
guess("read", source=...) from the initial-guess chapter.)
Worked example: run quietly, inspect afterwards#
import qc, json
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run() # silent
m.log() # ... now render the transcript
n_events = len(m.run_events()) # 18 structured events available as data
m.save("water.qch5") # keep it for next time
print("events:", n_events, "| E:", round(m.scf.energy, 6))
Exercise 9
You ran
m = chk.scf(ref="r").run()(nolog=) and now want to see the SCF cycle table without paying for another SCF. What do you call?You need the per-cycle energies in a Python script to make your own plot. Which method gives you the data, and in what form?
An expensive optimization finished. How do you make sure you never have to repeat it, and how would you later reuse its converged orbitals as the starting guess for a bigger-basis run?
Solution to Exercise 9
m.log()— it replays the transcript stored on the checkpoint, at no compute cost (log(format=...)for Markdown/JSONL).m.run_events()— a list of JSON strings;json.loadseach and pull the per-cycle fields from the SCF-cycle events.m.save("opt.qch5")persists it; laterqc.chk.load("opt.qch5")restores it with results intact. To reuse its orbitals,qc.chk.new(..., ao="<bigger>").guess("read", source="opt.qch5").scf(...).run().
That completes the day-to-day workflow. The rest of Part III is the large molecular-properties suite — turning a converged wavefunction into charges, bond orders, topological analysis, and the real-space fields you met in visualization.