Input writers

A writer turns one species and one job specification into the input files of one program. Writers fail closed: before rendering anything, a writer lists everything in the request it cannot express and, if the list is not empty, refuses with the reasons. It never guesses a keyword. The stance is the one vibe-qc’s QCSchema adapter takes, applied from the writing side.

The writers

vibe-sketch writers

Writer

Writes

Tasks

xyz

Cartesian geometry in angstrom. Charge and multiplicity go into the comment line as charge=0 multiplicity=1, the extended XYZ convention.

geometry

qcschema-molecule

A QCSchema molecule, schema version 2: geometry in bohr (CODATA 2018), charge, multiplicity. Only the fields vibe-qc’s adapter accepts.

geometry

orca

An ORCA 6 input (ORCA 5 reads it too).

sp, opt, freq, opt+freq, tddft, nmr

qcschema

A QCSchema input, schema version 1, exactly as vibe-qc’s adapter reads it: the molecule, the energy or hessian driver, model with method and basis, empty keywords.

sp, freq (HF)

vibeqc

A Python script that runs the job with vibe-qc’s run_job and writes QVF and Molden files.

sp, opt, freq, opt+freq, tddft

Every writer writes the first conformer of the species and refuses a species that has none; vibe-sketch embed, POST /api/documents/{id}/embed, Generate 3D in the browser, or --embed on vibe-sketch input make one. xyz and qcschema-molecule are geometry-only, so they refuse every calculation: asked for a B3LYP optimisation, xyz answers with one reason per thing it cannot carry, the task, the method, the basis and so on. qcschema refuses what vibe-qc’s QCSchema adapter would: an optimisation (an AtomicInput has no such driver), TDDFT, NMR, a Hessian for anything but HF, and every keyword, from dispersion to resources.

vibe-qc

The vibeqc writer emits a script in the style of vibe-view’s vibe-qc templates: the geometry in angstrom converted to bohr with the CODATA 2018 factor, one Molecule with the charge and multiplicity (run_job takes neither), and one run_job call with QVF and Molden output:

vibe-sketch input claisen.vsk --species m3 --program vibeqc --task opt+freq --method B3LYP --dispersion D3BJ --basis def2-SVP --approximation RIJCOSX --solvent CPCM:ethanol --nprocs 4 --embed

The translation follows what vibe-qc’s run_job accepts, and refuses past that:

Field

vibe-qc

method

HF as rhf or uhf; a functional as rks or uks with functional= (LDA, PBE, PBEsol, BLYP, B3LYP, PBE0, BHandHLYP, TPSS, revTPSS, TPSSh, M06-L, M06-2X, SCAN, r2SCAN, r2SCAN0, MN15, ωB97, ωB97X, ωB97X-D, ωB97X-V, ωB97M-V, HSE06, CAM-B3LYP, LC-ωPBE); the 3c composites, double hybrids, MP2, CCSD, CCSD(T) and DLPNO as methods. Never method="auto", which runs FCI or selected CI on small molecules. ωB97X-D3 is not in vibe-qc and is refused, not swapped for ωB97X-D.

basis

The basis sets vibe-qc ships: Pople, def2 (and def2-m), Dunning with aug- and core-valence, Jensen pc and pcseg, x2c-TZVPall, LANL2DZ, dhf.

approximation

RIJCOSX as density_fit=True, cosx=True, RI-J as density_fit=True, for HF and DFT. vibe-qc picks the fitting basis for the orbital basis; aux_basis may only name one of its fitting sets, such as def2-tzvp-jk.

dispersion

D3(BJ) or D4 on a functional (D4 needs vibe-qc’s optional dftd4).

solvent

CPCM with vibe-qc’s presets; vibe-qc has no SMD.

task

optimize=True; frequencies as hessian=True, a finite-difference Hessian on HF and DFT only; TDDFT as tddft=True, tddft_type="casida". vibe-qc computes no NMR shieldings.

constraints

Positions only, as geom_freeze.

resources

nprocs as num_threads; memory per core is refused, as vibe-qc takes a total budget.

ORCA

The ORCA writer translates the specification through a keyword table (vibesketch.inputs.orca_keywords) and refuses whatever the table does not cover, with the reason:

Field

What the table knows

method

HF; BP86, BLYP, PBE, revPBE, TPSS, SCAN, r2SCAN; B3LYP, B3PW91, PBE0, TPSSh, TPSS0, M06, M06-2X, BHandHLYP, X3LYP, CAM-B3LYP, ωB97X, LC-BLYP, r2SCAN0, PW6B95; with their own dispersion B97-D3, ωB97X-D3, ωB97X-D4, ωB97M-D4, ωB97X-V, ωB97M-V; double hybrids B2PLYP, DSD-PBEP86, PWPB95; the composites HF-3c, PBEh-3c, B97-3c, r2SCAN-3c, ωB97X-3c; MP2, RI-MP2, CCSD, CCSD(T), DLPNO-CCSD, DLPNO-CCSD(T). Write ω as w: wB97X-D3.

basis

The def2 (and ma-def2), Pople, Dunning (cc-pVXZ, aug-, core-valence), Jensen (pc, pcseg, pcSseg, pcJ) and x2c families.

aux_basis

def2/J, SARC/J, x2c/J; def2/JK, cc-pVXZ/JK; def2-SVP/C to def2-QZVPP/C, cc-pVXZ/C; AutoAux.

approximation

RIJCOSX and RIJONX (with a /J basis), RIJK (with a /JK basis).

dispersion

D2, D3ZERO (or D3), D3BJ (or D3(BJ)), D4.

solvent

CPCM or SMD, in water, methanol, ethanol, acetonitrile, acetone, DMSO, DMF, THF, dichloromethane, chloroform, toluene, benzene, hexane, cyclohexane, diethyl ether, pyridine, ammonia, octanol, ethyl acetate, carbon tetrachloride or 1,4-dioxane.

Beyond unknown names, it refuses what ORCA would not run as asked:

  • Composites: a basis set, auxiliary basis or approximation with a composite, which brings its own.

  • Dispersion: a dispersion correction for a method that has one built in, or for MP2 and coupled cluster.

  • Auxiliary bases: an approximation without the auxiliary basis set it needs, or with one it cannot use. RI-MP2 and DLPNO need a /C basis; with an approximation they need two auxiliary bases, more than the specification holds.

  • Tasks the method cannot do:

    • an optimisation with coupled cluster

    • frequencies where ORCA has no analytic Hessian: double hybrids, VV10 functionals, MP2, coupled cluster

    • TDDFT with MP2 or coupled cluster

    • NMR with coupled cluster

  • Bad inputs: a constraint naming an atom the species does not have, and a species with no electrons (H⁺).

The input follows the house style of the qc-input-library ORCA inputs:

  • Keyword line first: reference (UKS or UHF, spelt out for open shells), method, dispersion, basis, auxiliary basis, approximation, TightSCF, the task keywords, then CPCM(solvent).

  • Comments: the formula, method, basis and purpose, then the species and the geometry’s origin.

  • Blocks: %maxcore and %pal when the job names resources, then the %cpcm SMD block, %tddft (full TDDFT, tda false) and %geom constraints.

  • Geometry: angstrom with ten aligned decimals; no blank lines.

SMD is written as CPCM(solvent) with %cpcm smd true SMDsolvent "..." end, the form ORCA 5 and 6 both read. The file is named <species>_<method>_<basis>[_<options>].inp by the library’s naming rules, for example m1_b3lyp_def2-tzvp_cpcm-water_d3bj_freq_opt_rijcosx.inp.

! B3LYP D3BJ def2-TZVP def2/J RIJCOSX TightSCF Opt Freq CPCM(water)
# H2O / B3LYP / def2-TZVP — optimisation, then harmonic frequencies
# m1: water; vibe-sketch 0.1.0, geometry: etkdg+mmff94
%maxcore 3000
%pal nprocs 4 end
* xyz 0 1
O   0.0000000000   0.0000000000   0.1173000000
H   0.0000000000   0.7572000000  -0.4692000000
H   0.0000000000  -0.7572000000  -0.4692000000
*

Writing an input

From the command line, name a job of the document, or a species and a writer:

vibe-sketch input scheme.vsk --job j1
vibe-sketch input scheme.vsk --species m1 --program qcschema-molecule --output-dir inputs

With a species and a writer, the specification comes from options, or whole from --spec as JSON; --embed makes the 3D structure first when the species has none:

vibe-sketch input scheme.vsk --species m3 --program orca --task opt+freq --method B3LYP --dispersion D3BJ --basis def2-SVP --aux-basis def2/J --approximation RIJCOSX --solvent SMD:ethanol --nprocs 4 --maxcore 2000 --embed

--species repeats, and --all takes every species of the document; both write into --output-dir. A species the writer refuses, such as the proton in a mechanism, is reported and the others are still written; the exit status is 3 if any was refused:

vibe-sketch input claisen.vsk --all --program orca --task opt+freq --method r2SCAN-3c --embed --output-dir inputs

Without --output-dir the files go to standard output. A refusal prints the reasons and exits with status 3; an unknown job, species or writer is a usage error with status 2.

From Python:

from vibesketch.document import load
from vibesketch.inputs import WriterRefusal, write_input

document = load("scheme.vsk")
try:
    bundle = write_input(document, job="j1")
except WriterRefusal as refusal:
    print(refusal.reasons)
else:
    for file in bundle.files:
        print(file.name, file.media_type)

POST /api/documents/{id}/inputs writes one program’s input for several species of a stored document (species, default all) with one spec, as JSON or, with "format": "zip", as one archive with REFUSED.txt listing what was refused. The browser’s Inputs section uses it.

Over REST, POST /api/inputs takes the document and either job, or species and program with an optional spec; "embed": true makes the 3D structure first when the species has none, without storing it. It answers with the files, or with status 422 and {"detail": {"program": ..., "reasons": [...]}} when the writer refuses, and writes nothing.

Adding a writer

Subclass vibesketch.inputs.InputWriter, declare the name, the tasks and the specification fields it can express, and implement _refusals (the writer-specific reasons to refuse) and _render (the files). The base class’s write is the only public entry point: it checks the task and every specification field against what the writer declared, adds the writer’s own reasons, and only calls _render when there is nothing to refuse. vibesketch.inputs.register makes the writer available to the SDK, the CLI and the REST API at once.