CC2, CC3, CCSD, CCSDT, QCISD, and perturbative triples

Canonical coupled cluster is the small-molecule accuracy reference in vibe-qc. method="ccsd" runs DF-CCSD; method="ccsd(t)" adds the standard perturbative triples correction. The same choice can be made explicitly with triples="none" or triples="(t)". Density fitting is the default integral route; pass CCSDOptions(density_fit=False) for the conventional route with exact four-index integrals (see Conventional (non-DF) integrals). Closed-shell QCISD and QCISD(T) are available through method="qcisd" and method="qcisd(t)". Ground-state closed-shell CC2 is available through method="cc2" or method="ci", citype="cc2". Full iterative CCSDT is available through method="ccsdt" or method="ci", citype="ccsdt" for benchmark-scale molecular calculations. Ground-state CC3 is available through method="cc3" or method="ci", citype="cc3" for closed-shell benchmark calculations.

Both closed- and open-shell references are handled automatically: a closed-shell (singlet) molecule runs on an RHF reference through the spin-adapted kernel, and an open-shell molecule (multiplicity > 1) runs on a UHF reference through the spin-orbital UCCSD(T) kernel by default. For a spin-pure restricted-open-shell reference, pass ccsd_reference="rohf": the same spin-orbital kernel is used, with ROHF supplying identical alpha/beta spatial orbitals and per-spin Fock matrices.

See the canonical CCSD(T) tutorial for a worked, paste-and-run walkthrough of every reference type below.

At a glance

High-level entry point

run_job(method="cc2"), run_job(method="ccsd", triples=..., ccsd_reference=...), run_job(method="cc3"), run_job(method="ccsdt"), run_job(method="qcisd")

Low-level entry point

run_ccsd / run_uccsd / run_rohf_ccsd / run_uccsd_from_mos; cc3; ccsdt

Reference

CCSD: RHF, UHF, or ROHF; CC3: RHF; CCSDT: RHF or common-orbital ROHF

Integral form

CCSD: density-fitted or exact four-index; CC3/CCSDT: exact four-index MO Hamiltonian

Scope

CCSD: dense O(N^6) small-molecule pilot; CC3/CCSDT: dense determinant-space benchmark routes

Validation

in-repo spin-orbital/FCI anchors and out-of-process PySCF/ORCA comparisons

Quick start

import vibeqc as vq

mol = vq.Molecule(
    [
        vq.Atom(8, [0.000, 0.000, 0.000]),
        vq.Atom(1, [0.000, 1.499, -1.160]),
        vq.Atom(1, [0.000, -1.499, -1.160]),
    ],
)

result = vq.run_job(mol, basis="cc-pvdz", method="ccsd(t)", output="h2o_ccsd_t")
print(result.ccsd.e_ccsd_correlation)
print(result.ccsd.e_t)
print(result.energy_total)

run_job writes the usual .out / citation sidecars and attaches the post-SCF result as result.ccsd.

CC2

method="cc2" implements the ground-state second-order approximate coupled cluster model of Christiansen, Koch, and Jorgensen. It solves the complete singles projection together with the first-order doubles equation built from the T1-transformed Hamiltonian and the diagonal [F,T2] commutator:

result = vq.run_job(mol, basis="cc-pvdz", method="cc2", output="h2o_cc2")
print(result.ccsd.e_ccsd_correlation)  # CC2 correlation energy
print(result.energy_total)

CC2 currently supports closed-shell RHF references, frozen occupied cores, density fitting, and the conventional exact-integral route selected by CCSDOptions(density_fit=False). It is an energy-only route and does not accept triples=. The dense implementation shares CCSD’s integral tensors and small-molecule memory envelope.

CC3

method="cc3" implements the iterative CC3 model of Koch and coworkers. Singles and doubles are optimized to convergence while connected triples are regenerated from the CC3 triples equation on every iteration and coupled back into the singles and doubles residuals:

result = vq.run_job(
    mol,
    basis="sto-3g",
    method="cc3",
    cc3_options=vq.CC3Options(
        conv_tol_energy=1.0e-10,
        conv_tol_residual=1.0e-8,
    ),
    output="h2o_cc3",
)
print(result.e_correlation)
print(result.e_total)
print(result.t3_norm)

The high-level route uses canonical RHF orbitals and requires a closed-shell singlet. It freezes chemical-core orbitals by default; use CC3Options(n_frozen_core=0) for an all-electron calculation. The low-level vq.cc3(hamiltonian, options) entry point accepts an orthonormal spatial-orbital Hamiltonian and leaves frozen-core selection explicit.

CC3 is an energy-only, exact-integral benchmark route. The current determinant-space implementation evaluates the defining commutators directly, so its memory and runtime grow combinatorially and it is intended for compact orbital spaces. Iteration details are written at the verbose output level; the standard .out contains the converged summary and amplitude norms.

Full CCSDT

method="ccsdt" solves the full coupled-cluster singles, doubles, and triples equations. The implementation evaluates the defining projected similarity transform directly in determinant space, including all connected and disconnected terms generated by T1 + T2 + T3:

result = vq.run_job(
    mol,
    basis="sto-3g",
    method="ccsdt",
    ccsdt_options=vq.CCSDTOptions(
        conv_tol_energy=1.0e-10,
        conv_tol_residual=1.0e-8,
    ),
    output="h2o_ccsdt",
)
print(result.e_correlation)
print(result.e_total)
print(result.t3_norm)

The high-level route uses RHF orbitals for closed shells and common-orbital ROHF orbitals for open shells. It freezes chemical-core orbitals by default; set CCSDTOptions(n_frozen_core=0) for an all-electron calculation. The low-level vq.ccsdt(hamiltonian, options) function accepts an orthonormal spatial-orbital Hamiltonian and leaves frozen-core selection explicit.

CCSDT is an energy-only, exact-integral benchmark route. Its determinant-space algorithm grows combinatorially, so it is intended for compact orbital spaces and method validation rather than large production molecules. It does not accept the perturbative triples= selector or CCSDOptions. Iteration details are written at the verbose output level; the normal .out contains the converged summary and amplitude norms.

Triples selector

The high-level driver accepts a triples= selector for the perturbative triples mode:

vq.run_job(mol, basis="cc-pvdz", method="ccsd", triples="none")
vq.run_job(mol, basis="cc-pvdz", method="ccsd", triples="(t)")
vq.run_job(mol, basis="cc-pvdz", method="ccsd", triples="[t]")
vq.run_job(mol, basis="cc-pvdz", method="ccsd", triples="A-CCSD(T)")

method="ccsd(t)" is equivalent to method="ccsd", triples="(t)". Passing triples="none" disables the perturbative correction even when the method keyword is ccsd(t), and the output / citation method label follows the effective choice.

triples="[t]" selects the fourth-order bracket correction CCSD[T], which is identically the original CCSD+T(CCSD) of Urban and coworkers (1985); the spelling triples="+T(CCSD)" is accepted as a synonym. The standard (T) is [T] plus the fifth-order singles-triples coupling; the result reports both pieces separately as result.ccsd.e_t4 and result.ccsd.e_t5_st, and the .out block prints the decomposition. CCSD[T] is closed-shell only; the open-shell kernel implements the standard (T).

triples="A-CCSD(T)" selects the closed-shell asymmetric/Lambda triples correction. The output and citation route record the effective method as a-ccsd(t) and the result exposes the Lambda residual as result.ccsd.lambda_residual_norm.

AutoCI-style selector

run_job also accepts the shared citype= spelling planned for the single-reference CI/CC ladder:

vq.run_job(mol, basis="sto-3g", method="ci", citype="cisd")
vq.run_job(mol, basis="cc-pvdz", method="ci", citype="ccsd(t)")
vq.run_job(mol, basis="sto-3g", method="ci", citype="cc3")
vq.run_job(mol, basis="sto-3g", method="ci", citype="ccsdt")

Supported values are cisd, cc2, ccsd, ccsd(t), cc3, ccsdt, ccd, lccd, lccsd, cepa(0)..cepa(3), qcisd, and qcisd(t).

For CISD, pass cisd_options=vq.CISDOptions(nroots=..., max_det=...) to request multiple roots or raise the determinant-space guard.

Coupled-pair variants: CCD, LCCD, LCCSD, CEPA(n)

The classic coupled-pair ladder between MP2 and CCSD is available as variants of the same closed-shell DF kernel, either directly as method= strings or through citype=:

vq.run_job(mol, basis="cc-pvdz", method="ccd")       # CCSD without singles
vq.run_job(mol, basis="cc-pvdz", method="lccd")      # linearized CCD
vq.run_job(mol, basis="cc-pvdz", method="lccsd")     # linearized CCSD == cepa(0)
vq.run_job(mol, basis="cc-pvdz", method="cepa(1)")   # Meyer's CEPA

What each variant does:

Variant

Definition

ccd

CCSD with the singles amplitudes frozen at zero (Čížek 1966).

lccd

Linearized CCD: the doubles residual truncated to terms linear in T2.

lccsd / cepa(0)

Linearized CCSD (singles + doubles, all nonlinear terms dropped).

cepa(1), cepa(2), cepa(3)

The linearized residual plus Meyer’s pair-specific EPV shifts (coupled-electron-pair approximation).

All variants require a closed-shell (RHF) reference; an open-shell molecule raises NotImplementedError (use ccsd / ccsd(t) with the UHF or ROHF reference instead). None of them defines a (T) correction, so triples= is rejected. On the low-level API the same selection is vq.CCSDOptions(cc_variant="cepa(1)", compute_triples=False) with vq.run_ccsd.

Two implementation properties are worth knowing. First, there is no second equation set: the linearized variants extract the linear part of the canonical CCSD residual algebraically exactly (a polynomial-stencil construction, machine-precision-validated against the in-repo spin-orbital anchor). Second, the CEPA shift convention is the closed-shell table of Wennmohs and Neese (Chem. Phys. 343, 217 (2008)) as implemented by ORCA’s MDCI module, validated to sub-microhartree agreement against out-of-process ORCA 6.1 RI-CEPA/n with the same auxiliary basis. Note that CEPA(1..3) energies depend on the choice of occupied orbitals: vibe-qc uses canonical MOs, which corresponds to ORCA’s %mdci Localize false (ORCA localizes internal orbitals by default and will differ by a few tenths of a millihartree on that account).

QCISD and QCISD(T)

QCISD is available as method="qcisd" or run_job(method="ci", citype="qcisd"). QCISD(T) is available as method="qcisd(t)", method="qcisd", triples="(t)", or citype="qcisd(t)".

Like the coupled-pair variants, QCISD currently uses the closed-shell RHF kernel only. Open-shell molecules raise NotImplementedError; use ccsd / ccsd(t) with the UHF or ROHF reference for radicals.

Implementation detail: QCISD is built by exact two-parameter monomial selection from the validated CCSD residual. It keeps the published QCISD operator set, including the T2-quadratic disconnected quadruple term, and uses the CI-like energy expression without the quadratic T1*T1 contribution. QCISD(T) follows the original Pople-Head-Gordon-Raghavachari convention: on QCISD amplitudes the triples increment is E[T] + 2*E_ST, not the CCSD(T) E[T] + E_ST combination. This is pinned against out-of-process ORCA 6.1 RI-QCISD(T) with canonical orbitals and the same RI auxiliary basis.

Low-level API

Use the low-level API when you already have a converged RHF reference or need explicit iteration controls:

import vibeqc as vq

basis = vq.BasisSet(mol, "cc-pvdz")
hf = vq.run_rhf(mol, basis, vq.RHFOptions())

opts = vq.CCSDOptions(
    triples="(t)",
    n_frozen_core=vq.chemical_core_orbital_count(mol),
    conv_tol_energy=1e-10,
    conv_tol_residual=1e-9,
)
cc = vq.run_ccsd(mol, basis, hf, opts)
print(cc.e_ccsd_t)

If opts.aux_basis is empty and density_fit=True, run_ccsd auto-resolves the matching RI auxiliary basis for the orbital basis. The legacy boolean compute_triples remains supported on CCSDOptions; the triples property is the human-readable front end to the same setting.

Conventional (non-DF) integrals

For strict parity against conventional CCSD(T) in other programs, run the coupled-cluster step on exact four-index MO integrals instead of the RI factorisation. Pass density_fit=False; no auxiliary basis is needed (or used), so this also works for basis sets without a registered RI auxiliary:

cc = vq.run_job(
    mol,
    basis="cc-pvdz",
    method="ccsd(t)",
    ccsd_options=vq.CCSDOptions(density_fit=False, n_frozen_core=0),
)

Everything downstream of the integral assembly (amplitude equations, DIIS, the (T) correction) is identical to the DF path; only the source of the (pq|rs) blocks changes. The .out block reports the route honestly: Algorithm = CCSD(T) (no DF- prefix) and Density fitting = off, with no RI auxiliary basis line. Both the closed-shell (RHF) and the open-shell (UHF / ROHF) kernels support the conventional route; all-electron conventional CCSD(T) and UCCSD(T) are validated against PySCF’s conventional kernels to a few nanohartree on H2O and the OH radical in cc-pVDZ (tests/test_ccsd_canonical_noDF.py).

The conventional route holds the AO ERI tensor (nbf^4 doubles) and the correlated-window MO tensor in memory during the integral build, so it is a small-molecule route by design. The RI error of the default DF route is orders of magnitude below chemical accuracy (about 1e-5 to 1e-4 Ha on the correlation energy with the matched -ri auxiliary), so DF remains the right default for everything except integral-exact cross-code parity work. FNO-CCSD(T) and the DLPNO pilots remain DF-only.

Open-shell (UHF) references

For an open-shell molecule (multiplicity > 1), run_job(method="ccsd(t)") runs UHF and then the spin-orbital UCCSD(T) kernel automatically. No extra flags are needed; the result is attached as result.ccsd with the same CCSDResult fields as the closed-shell path.

import vibeqc as vq

ch3 = vq.Molecule([...], charge=0, multiplicity=2)   # methyl radical
result = vq.run_job(ch3, basis="cc-pvdz", method="ccsd(t)")
print(result.ccsd.e_ccsd_t)

The open-shell kernel evaluates the spin-orbital coupled-cluster equations directly on the UHF reference, so it reproduces canonical UCCSD/UCCSD(T) (validated against PySCF cc.UCCSD/UCCSD(T) to well under a microhartree). Use the low-level run_uccsd(mol, basis, uhf_result, CCSDOptions(...)) when you already have a converged UHF reference:

basis = vq.BasisSet(ch3, "cc-pvdz")
uhf = vq.run_uhf(ch3, basis, vq.UHFOptions())
cc = vq.run_uccsd(ch3, basis, uhf, vq.CCSDOptions(triples="(t)"))

Open-shell SCF is sometimes harder to converge than closed-shell: if the UHF reference does not converge, CCSD is skipped (no coupled-cluster numbers are produced from an unconverged reference). For difficult radicals, pass a uhf_options=vq.UHFOptions(...) with a level shift / higher max_iter to run_job.

Frozen natural orbitals (FNO)

Canonical CCSD(T) cost is dominated by the size of the virtual space. Frozen natural orbitals shrink it: the dominant virtual natural orbitals of the MP2 density are kept and the rest are discarded before CCSD(T), recovering almost all of the correlation energy at a fraction of the cost (DePrince and Sherrill, J. Chem. Theory Comput. 9, 2687 (2013)).

Turn it on with fno=True on CCSDOptions:

import vibeqc as vq

# occupation-threshold selection (default 1e-5): keep every natural orbital
# whose MP2 occupation is at or above the threshold
vq.run_job(mol, basis="cc-pvtz", method="ccsd(t)",
           ccsd_options=vq.CCSDOptions(fno=True))

# or keep a fixed fraction of the virtual space
vq.run_job(mol, basis="cc-pvtz", method="ccsd(t)",
           ccsd_options=vq.CCSDOptions(fno=True, fno_keep_fraction=0.6))

The retained virtuals are semicanonicalized (the virtual Fock block is re-diagonalized) so the perturbative (T) stays exact in the truncated space. By default a delta-MP2 correction E_MP2[full] - E_MP2[trunc] is added back (fno_delta_mp2=True), which recovers most of the small correlation lost to truncation. The .out file reports how many natural orbitals were kept and the size of the delta-MP2 correction.

The result is an FNOCCSDResult with the same fields as CCSDResult plus n_virtual_kept, n_virtual_total, and delta_mp2. The low-level entry point is vq.cc.run_fno_ccsd(mol, basis, rhf_result, options).

FNO options:

Option

Default

Meaning

fno

False

enable the FNO virtual-space truncation

fno_occ_threshold

1e-5

keep natural orbitals with occupation >= this

fno_keep_fraction

None

if set, keep this fraction of virtuals (overrides the threshold)

fno_delta_mp2

True

add the E_MP2[full] - E_MP2[trunc] correction

With no truncation (fno_keep_fraction=1.0 or fno_occ_threshold=0) FNO-CCSD(T) reproduces canonical CCSD(T) to machine precision, since CCSD is invariant to virtual rotation and the semicanonicalization restores the canonical (T).

Result Fields

CCSDResult exposes:

  • e_hf

  • e_ccsd_correlation

  • e_ccsd

  • e_t

  • e_ccsd_t

  • e_total

  • n_iter, converged

  • t1_norm, t2_norm

  • cc_trace

The per-iteration cc_trace entries are CCSDIteration records with energy, energy change, residual norms, and the active DIIS subspace.

Limits

The current canonical engine is deliberately conservative:

  • RHF (closed-shell), UHF (open-shell default), and ROHF (ccsd_reference="rohf") references are all supported. All three drive the same coupled-cluster equations (the closed-shell path spin-adapts them; the open-shell paths evaluate them in spin-orbital form).

  • Dense O(N^6) pilot implementation for small molecules. Closed-shell uses the spin-adapted kernel; open-shell uses the spin-orbital kernel (same equations, validated to machine precision against the in-repo spin-orbital reference), which carries the usual spin-orbital factor in cost and memory.

  • Density fitting is the default production path; the conventional exact-integral route (density_fit=False, see above) is supported for cross-code parity work on small molecules. FNO and the DLPNO pilots require density fitting.

  • Frozen occupied cores are supported. The virtual space can be truncated with frozen natural orbitals (fno=True, see above); explicit frozen-virtual lists are not supported.

  • FNO is closed-shell (RHF reference) only so far; open-shell FNO is a roadmap item.

  • A-CCSD(T) and CCSD[T] / CCSD+T(CCSD) are implemented for closed-shell CCSD only, not for QCISD.

  • Full CCSDT is available for RHF and common-orbital ROHF references. Its determinant-space implementation uses exact MO integrals and is intended for compact benchmark spaces; it is separate from the O(N^6) CCSD kernels.

  • Ground-state CC3 is available for closed-shell RHF references. Its determinant-space implementation uses exact MO integrals and is intended for compact benchmark spaces.

See also