Memory budget

Every run_job calculation runs through a pre-flight memory check that (a) reports the estimated peak memory in the text output and (b) aborts with an explanation if that estimate exceeds the machine’s available RAM. This is a guardrail against the most common catastrophic failure mode of a QC code — calculations that silently thrash to disk and freeze the host.

What you’ll see in the .out file

Job: RKS / PBE  basis=cc-pvdz
Atoms (bohr)
------------------------------------------------------
   1  Z=  8       0.00000000      0.00000000      0.00000000
   2  Z=  1       0.00000000      1.43000000     -0.98000000
   3  Z=  1       0.00000000     -1.43000000     -0.98000000
charge=0  multiplicity=1  n_electrons=10

vibe-qc estimates this calculation will require ~0.13 GB of memory:
    ERI tensor            0.00 GB
    Fock + density + 1e   0.00 GB
    DIIS history          0.00 GB
    MO workspace          0.00 GB
    DFT grid + chi        0.10 GB
Available on this machine: 24.0 GB. Proceeding.

  iter     energy (Ha)            dE          ||[F,DS]||   DIIS
  ...

The headline figure already carries a 20 % safety headroom over the sum of the per-category numbers (configurable via MemoryEstimate.headroom_factor).

When the estimate exceeds available RAM

vibe-qc estimates this calculation will require ~218.4 GB of memory:
    ERI tensor      186.0 GB
    ...
Available on this machine: 7.2 GB. ABORTING.

InsufficientMemoryError: Set `options.memory_override = True` (or pass
`memory_override=True` to `run_job`) to proceed anyway. Consider a
smaller basis or, once shipped in v0.6+, density fitting / on-disk
scratch.

Overriding the check

Pass memory_override=True to run_job:

from vibeqc import Molecule, run_job

mol = Molecule.from_xyz("large.xyz")
run_job(
    mol, basis="def2-tzvp", method="rhf",
    output="huge",
    memory_override=True,    # accept the risk of swap / freeze
)

The output then reads Proceeding (override) instead of Proceeding so anyone reading the log later knows what happened.

Estimators covered

Method

Dominant cost

Notes

RHF / UHF

Dense 4-index ERI tensor: \(n^4 \cdot 8\) bytes

64 GB at n=300

RKS / UKS

RHF baseline + DFT grid + χ matrices

\(\sim\) \(n_\text{pts} \cdot n \cdot 8\) for χ

MP2

OVOV MO-basis tensor

\(n_\text{occ}^2 \cdot n_\text{vir}^2 \cdot 8\)

UMP2

Three spin-channel OVOV tensors

\(\alpha\alpha + \beta\beta + \alpha\beta\)

Periodic and post-HF methods beyond MP2 are currently not covered by the estimator; they ship estimators with the corresponding drivers (v0.8+).

Reading the probe yourself

The cross-platform “how much memory is available right now” probe is exposed for scripting:

>>> import vibeqc
>>> vibeqc.available_memory_bytes() / 1024**3
24.3...

It tries psutil.virtual_memory().available first (install with pip install psutil to get the highest-quality number), then falls back to /proc/meminfo on Linux and os.sysconf on macOS. Returns 0 if all probes fail — check_memory treats that as “unknown” and silently proceeds rather than false-aborting on an unsupported platform.

Writing your own estimator

If you call the low-level SCF drivers (run_rhf, run_rks, …) directly instead of through run_job, you can invoke the estimator yourself:

from vibeqc import estimate_memory, check_memory

est = estimate_memory(mol, basis, method="rhf", options=rhf_options)
print(est)                       # human-readable block
check_memory(est)                # raises InsufficientMemoryError if over budget
# ... your own driver call ...

The estimator returns a MemoryEstimate dataclass with a by_category dict, so you can inspect exactly where the memory goes.