Skip to content

Many calculations at once

compute takes a single input or a list of them. A list returns a list of outputs in input order, and every other argument applies to each input.

from qccompute import compute

outputs = compute([inp1, inp2, inp3])

What runs in parallel

  • Programs (ORCA, xtb, Psi4, TeraChem, DFTB+, CREST, ...) run side by side. Each runs in its own directory with its own captured output, exactly as a single calculation does.
  • Python calculations (machine-learned potentials, tblite, geomeTRIC) take over the process's output streams, working directory, and environment while they run, so they run one at a time, after the programs.
  • A list computed from inside a Python calculation (for example in a geomeTRIC child) runs one input at a time.

Cores and GPUs

max_workers is a budget of CPU threads: by default, the cores this process may use. Each calculation draws the threads its codec declares from what the input asks for (ORCA's pal, Psi4's -n, CREST's threads, xtb's parallel), and one otherwise. So pal 8 ORCA runs on a 32-core machine run four at a time, with no extra setting. Calculations start in input order; one that asks for more threads than the budget runs alone.

GPU programs (TeraChem) get their own devices: each calculation runs with CUDA_VISIBLE_DEVICES set to its GPUs, as many at once as there are GPUs. The GPUs are those in CUDA_VISIBLE_DEVICES if it is set (in environment or the process), or else those nvidia-smi lists. If none are found, GPU calculations run one at a time without pinning.

Failures

Every calculation runs first. Then, with raise_exc=True (the default), one ComputeErrors is raised if any failed. It is an ExceptionGroup: each sub-exception is the error that calculation would have raised on its own, with its prog_output and its position in the list as input_index, and outputs holds every output.

from qccompute.exceptions import ComputeErrors, ProgramTimeoutError

try:
    outputs = compute(inputs, timeout=600)
except* ProgramTimeoutError as timeouts:
    print(f"{len(timeouts.exceptions)} calculations timed out")

With raise_exc=False the list is returned as is; check each output.success.

Following progress

print_logs=True prints every line of output prefixed with its input's index, such as [3]. An update_func receives (index, all_logs, new_logs) and is called from the threads running the calculations, so it must be thread-safe.

Interrupting

Ctrl-C stops the list: queued calculations are dropped and running programs are killed together with every process they started.