Skip to main content
Docs

Search guides and API endpoints, for example “Idempotency-Key” or “submit job”.

    Tools · Quantum Chemistry

    Vibrational Spectrum Library

    GFN2-xTB IR spectra for every conformer of a 3D ensemble on a shared wavenumber grid, with Boltzmann-weighted ensemble spectra per molecule.

    Updated October 13, 2026

    On this page

    Prices, workflows, and method papersOpen in the app

    Vibrational Spectrum Library turns a conformer ensemble into a library of IR spectra on one wavenumber grid. Each conformer gets a GFN2-xTB Hessian: harmonic frequencies from the second derivatives of the energy and IR intensities from the dipole derivatives. The sticks are broadened onto the grid. Conformers of the same molecule are then combined into a Boltzmann-weighted ensemble spectrum. Because every column shares one grid, the library can be compared directly with a measured spectrum or used to fit conformer populations to one.

    How a run works

    1. Each SDF record (or mol block) is one conformer, read by RDKit with its hydrogens and 3D coordinates. Records without 3D coordinates are rejected; this tool does not generate geometries.
    2. By default xtb optimizes each conformer at the tight or very tight level and then computes the Hessian at the minimum. With the input geometry option the Hessian runs on the coordinates as given.
    3. The modes are broadened onto the grid. Conformers are grouped into molecules by canonical SMILES and weighted by the Boltzmann populations of their xTB free energies at the chosen temperature.
    4. A conformer that fails gets a row with its error, and the rest of the ensemble continues.

    Choosing the geometry

    • Optimize with xTB first gives a true GFN2-xTB minimum, so all frequencies are real. Conformers that sit close together on the force-field surface can relax to the same xTB minimum and give the same spectrum.
    • Use input geometry keeps each conformer exactly as supplied, for example DFT-optimized structures. A structure that is not an xTB minimum can show imaginary modes; they are counted for each conformer and left out of the spectra. Include explicit hydrogens for this option.

    Solvation uses the ALPB implicit solvent model. Leave the charge at 0 to use each molecule's formal charge.

    Reusing xTB / CREST frequencies

    If you have already run the frequencies mode of xTB / CREST, upload its vibrations.csv and use its structures.sdf as the conformers. The Hessian is skipped, and records are matched to the table by their molecule_index and structure_index tags. In this case conformers are weighted by their electronic energies, because the structures file carries no free energies.

    Reading the results

    • library.csv: one IR column per conformer (conformer_<index>_ir) on the wavenumber grid.
    • ensemble.csv: one Boltzmann-weighted IR column per molecule (molecule_<index>_ir).
    • conformers.csv: one row per conformer, with its molecule, SMILES, status, energies, free energy, relative energy in kcal/mol, Boltzmann population, number of modes, imaginary-mode count, and lowest frequency.
    • sticks.csv: every mode, with its wavenumber, reduced mass, and IR intensity in km/mol.
    • structures.sdf: the geometries the Hessian used, tagged with the same energies and populations.
    • logs/: the xtb output for each conformer.

    Every line shape is area-normalized, so the integral of a peak equals the mode's intensity in km/mol whatever its shape or width. Broadened spectra are therefore in km/mol per cm⁻¹.

    Limits and not covered

    Frequencies are harmonic and unscaled. GFN2-xTB fundamentals can be several percent off experiment, so apply a scaling factor or compare peak patterns rather than absolute positions. Raman activities, anharmonic corrections, VCD, and overtones and combination bands are not part of this tool. Each xtb run stops after an hour. If a large ensemble runs out of the job's time budget, the conformers that were not started are reported as skipped, and the finished ones are still returned.

    Generate the ensemble with Conformer Ensemble Generator or with the CREST conformer search in xTB / CREST. In a workflow, the structures output carries relative energy and population metrics, so a filter step can keep the low-energy conformers.

    Run it from the API

    Submit with Submit a job and the job_type below. Price it first with Estimate job reservation cost: submitting reserves that amount from your wallet, and the charge settles at the actual runtime.

    Vibrational Spectrum Library vib-spectrum

    Job type
    vib-spectrum
    Hardware
    cpu (default)
    Typical runtime
    9 min on CPU

    Payload

    Payload fields
    FieldTypeDescription
    input_data[]required(string | object)[]

    Limits: min items 1, max items 200

    input_formatrequiredstring

    One of: "molblock", "sdf"

    optimizeboolean

    Default: true

    opt_levelstring

    Default: "tight"One of: "tight", "vtight"

    chargeinteger

    Default: 0Limits: ≥ -10, ≤ 10

    uhfinteger

    Default: 0Limits: ≥ 0, ≤ 10

    solventstring | null

    One of: "water", "methanol", "acetonitrile", "acetone", "dmso", "dmf", "thf", "ch2cl2", "chcl3", "toluene", "benzene", "hexane", "ether", "ethylacetate", "dioxane", "octanol", null

    temperature_knumber

    Default: 298.15Limits: ≥ 10, ≤ 2000

    vibrationsfile_object
    broadeningstring

    Default: "gaussian"One of: "gaussian", "lorentzian"

    fwhm_cm1number

    Default: 15Limits: > 0, ≤ 500

    wn_min_cm1number

    Default: 400Limits: ≥ 0, ≤ 5000

    wn_max_cm1number

    Default: 4000Limits: > 0, ≤ 5000

    wn_step_cm1number

    Default: 1Limits: > 0, ≤ 5000

    File fields take {"name": "x.pdb", "content_b64": "…"} or a stored artifact, {"$artifact": {"id": "art-…", "port": "…"}}. Files are up to 25 MiB each.

    Example

    from cognichem_client import CogniChem
    
    client = CogniChem.from_env()  # reads COGNICHEM_API_KEY
    payload = {
        "input_data": [
            "ethanol\n     RDKit          3D\n\n  9  8  0  0  0  0  0  0  0 … (1,636 characters)",
        ],
        "input_format": "sdf",
        "optimize": True,
        "opt_level": "tight",
        "temperature_k": 298.15,
        "broadening": "gaussian",
        "fwhm_cm1": 15,
        "wn_min_cm1": 400,
        "wn_max_cm1": 4000,
        "wn_step_cm1": 20,
    }
    
    estimate = client.jobs.estimate(job_type="vib-spectrum", payload=payload, resource="cpu")
    print(f"Reserves ${estimate.cost:.2f}")
    
    job = client.jobs.submit(
        job_name="my-vib-spectrum-run",
        job_type="vib-spectrum",
        payload=payload,
        resource="cpu",
    )
    status = client.jobs.wait(job.process_id)
    if status.status == "completed":
        client.jobs.result(job.process_id, save_path=".")

    Sample data from the job catalog; long values are shortened here. Each job_name must be unique among your jobs.

    Workflow inputs

    • Molecules (list)MOLBLOCK, SDF
    • Table · optionalCSV

    Workflow outputs

    • ArchiveZIP
    • TableCSV
    • TableCSV
    • TableCSV
    • MoleculesSDF