Module reference

The GoodVibes Python package is organised into the modules listed below. Most users will only need goodvibes.api (the v4.2 façade); the lower-level modules are documented for advanced/embedded use.

Public API (v4.2)

Programmatic API façade for GoodVibes (v4.2 item 5).

A small import-friendly entry point for notebooks and scripts. Replaces the 15-positional-arg calc_bbe constructor with kwargs that have the same defaults as the CLI, and returns a structured ThermoResult rather than the raw calc_bbe instance.

from goodvibes import compute_thermo, compute_batch r = compute_thermo(“file.log”, QH=True, spc=”TZ”) print(r.qh_gibbs_free_energy)

rs = compute_batch(glob.glob(”*.log”))

Internally just calls calc_bbe; no behavior change relative to the CLI. The underlying calc_bbe and QCData instances stay accessible via result.bbe / result.qcdata for advanced use (e.g. PES analysis, direct attribute reads not yet promoted to the result dataclass).

class goodvibes.api.ThermoResult(file: str, name: str, scf_energy: float | None, sp_energy: float | None, zpe: float | None, enthalpy: float | None, qh_enthalpy: float | None, entropy: float | None, qh_entropy: float | None, gibbs_free_energy: float | None, qh_gibbs_free_energy: float | None, frequency_wn: List[float] | None, im_frequency_wn: List[float] | None, inverted_freqs: List[float] | None, point_group: str | None, symmno: int | None, linear_mol: bool, multiplicity: int | None, job_type: str | None, level_of_theory: str | None, program: str | None, bbe: Any, qcdata: Any)[source]

Bases: object

Bundle of thermochemistry for one structure.

All numeric fields are in atomic units (Hartree for energies, Hartree/K for entropies); frequencies are cm⁻¹. Fields that aren’t available for the given file (e.g. qh_enthalpy when QH=False, sp_energy when spc=None) are reported as None rather than a sentinel.

bbe: Any
enthalpy: float | None
entropy: float | None
file: str
frequency_wn: List[float] | None
gibbs_free_energy: float | None
property has_thermo: bool

True when calc_bbe found enough information to compute G(T). False for SP-only outputs (no frequency block).

im_frequency_wn: List[float] | None
inverted_freqs: List[float] | None
job_type: str | None
level_of_theory: str | None
linear_mol: bool
multiplicity: int | None
name: str
point_group: str | None
program: str | None
qcdata: Any
qh_enthalpy: float | None
qh_entropy: float | None
qh_gibbs_free_energy: float | None
scf_energy: float | None
sp_energy: float | None
symmno: int | None
zpe: float | None
goodvibes.api.bbe_to_result(bbe: Any, path: str | None = None, *, level_of_theory: str | None = None) ThermoResult[source]

Project a calc_bbe instance into a ThermoResult.

Useful for adapting CLI internals (which keep thermo_data as a {path: calc_bbe} dict) to the structured API without re-parsing. level_of_theory is read from the file via read_initial() if not supplied; pass it explicitly to avoid the extra file scan.

goodvibes.api.compute_batch(paths: Sequence[str], *, jobs: int = 1, **kwargs: Any) List[ThermoResult][source]

Compute thermochemistry for a list of files.

Parameters:
  • paths – list of QC output file paths.

  • jobs – parallelism level. 1 (default) is sequential — no process-pool overhead. > 1 spawns that many worker processes via concurrent.futures.ProcessPoolExecutor. 0 or negative uses os.cpu_count() (or 1 if unknown).

  • **kwargs – forwarded unchanged to every compute_thermo call.

Returns:

Results in input order (matches executor.map’s contract). Workers can’t write to the orchestrator’s logger, so per-file log.info from inside calc_bbe is silenced under jobs > 1; warnings that surface through ThermoResult (e.g. missing-frequency files) still come through unchanged.

goodvibes.api.compute_thermo(path: str | None = None, *, qcdata: Any = None, QS: str = 'grimme', QH: bool = False, s_freq_cutoff: float = 100.0, h_freq_cutoff: float = 100.0, temperature: float = 298.15, concentration: float | None = None, freq_scale_factor: float | None = None, zpe_scale_factor: float | None = None, solv: str | None = None, spc: str | None = None, invert: float | None = None, symm: bool = False, mm_freq_scale_factor: float | None = None, inertia: str = 'global') ThermoResult[source]

Compute thermochemistry for one QC output file.

Pass either path (a filesystem location) or a pre-parsed qcdata (skips re-parsing). When concentration is None the gas-phase reference at 1 atm is used (P / RT).

Parameters mirror the CLI:

QS ‘grimme’ (default) or ‘truhlar’ quasi-harmonic entropy QH apply Head-Gordon quasi-harmonic enthalpy correction s_freq_cutoff entropy cutoff (cm⁻¹) h_freq_cutoff enthalpy cutoff (cm⁻¹) — only used when QH=True temperature K concentration mol/L. None → gas phase 1 atm. freq_scale_factor None → auto-lookup harm_fac from level of theory.

Applied to the partition-function frequencies (used in H_vib and S_vib).

zpe_scale_factor None → auto-lookup zpe_fac from level of theory.

Applied to ZPE only. If freq_scale_factor is explicitly set but zpe_scale_factor is None, ZPE inherits freq_scale_factor (back-compat).

solv ‘none’, or solvent name for free-space correction spc None, ‘link’, or filename suffix for SPC files invert None, or threshold for converting small imag → real symm apply pymsym symmetry-number correction

goodvibes.api.to_dataframe(results: Sequence[ThermoResult])[source]

Convert a list of ThermoResults into a pandas DataFrame.

Pandas is an optional dependency; raises ImportError with an install hint if it isn’t available. The DataFrame has one row per result and columns for every public scalar field on ThermoResult (omits the bbe and qcdata references).

goodvibes.api.to_parquet(results: Sequence[ThermoResult], path: str) None[source]

Write a list of ThermoResults to a Parquet file at path.

Same column set as to_dataframe. Requires pandas + a Parquet engine (pyarrow or fastparquet); install with pip install goodvibes[full] or pip install pyarrow.

CLI orchestrator

Quasi-harmonic thermochemical corrections for electronic structure calculations.

goodvibes.GoodVibes.compute_thermochem(files, options, qcdata_cache=None)[source]

Run calc_bbe for each file and collect results.

For files matching the –media solvent name, the neat solvent concentration is used instead of options.conc. Parallelized across options.jobs worker processes when >1; sequential otherwise.

Parameters:
  • files (list) – output file paths.

  • options (Namespace) – parsed CLI options. Uses: QS, QH, S_freq_cutoff, H_freq_cutoff, temperature, conc, freq_scale_factor, freespace, spc, invert, symm, mm_freq_scale_factor, inertia, media, jobs.

  • qcdata_cache (dict, optional) – pre-parsed QCData keyed by basename.

Returns:

file path → calc_bbe mapping (thermo_data).

Return type:

dict

goodvibes.GoodVibes.main()[source]

CLI entry point: parse arguments, compute thermochemistry, and print results.

goodvibes.GoodVibes.parse_arguments()[source]

Parse command-line arguments and return (options, args).

goodvibes.GoodVibes.resolve_scaling_factor(files, options, level_of_theory)[source]

Look up or validate the vibrational frequency scaling factor.

If the user provided –freq_scale_factor, log it. Otherwise, attempt automatic lookup from the Truhlar database based on level of theory. Warns if multiple levels of theory are found.

Parameters:
  • files (list) – output file paths.

  • options (Namespace) – parsed CLI options. Uses: freq_scale_factor, mm_freq_scale_factor, boltz, ee.

  • level_of_theory (list) – level of theory strings, one per file.

goodvibes.GoodVibes.validate_and_configure(options, solvation_model)[source]

Validate solvent, print QH/QS configuration, and return (symm_option, vmm_option).

goodvibes.GoodVibes.warn_orca_prescaled(files)[source]

Warn about ORCA files where pre-scaled frequencies were un-scaled.

Thermochemistry engine

class goodvibes.thermo.ThermoOptions(QS: str = 'grimme', QH: bool = False, s_freq_cutoff: float = 100.0, h_freq_cutoff: float = 100.0, temperature: float = 298.15, concentration: float | None = None, freq_scale_factor: float | None = None, zpe_scale_factor: float | None = None, solv: str | None = None, spc: str | None = None, invert: float | None = None, symm: bool = False, mm_freq_scale_factor: float | None = None, inertia: str = 'global')[source]

Bases: object

Bundle of thermochemistry options for calc_bbe.from_options.

Frozen so it’s safe to share across worker processes (parallel parsing) and across multiple calc_bbe calls without accidental mutation. Field names match the v4.2 façade compute_thermo kwargs; the older calc_bbe.__init__ parameter names are mapped internally.

QH: bool = False
QS: str = 'grimme'
concentration: float | None = None
freq_scale_factor: float | None = None
h_freq_cutoff: float = 100.0
inertia: str = 'global'
invert: float | None = None
mm_freq_scale_factor: float | None = None
s_freq_cutoff: float = 100.0
solv: str | None = None
spc: str | None = None
symm: bool = False
temperature: float = 298.15
zpe_scale_factor: float | None = None
goodvibes.thermo.calc_avg_moment_of_inertia(roconst)[source]

Compute the average moment of inertia from a sequence of rotational constants.

Parameters:

roconst (list[float]) – Rotational constants in gigahertz (GHz).

Returns:

Average moment of inertia in kilogram square meters (kg·m^2).

Return type:

float

Raises:

ValueError – If roconst is empty or if the mean rotational constant is not greater than zero.

class goodvibes.thermo.calc_bbe(file, QS='grimme', QH=False, cutoff=100.0, H_FREQ_CUTOFF=100.0, temp=298.15, conc=None, scale_fac=None, solv=None, spc=None, invert=None, symm=False, mm_freq_scale_factor=None, inertia='global', qcdata=None, zpe_scale_fac=None, _from_options=False)[source]

Bases: object

Compute “black box” entropy and enthalpy values along with all other thermochemical quantities.

Computes H, S from partition functions, applying quasi-harmonic corrections

xyz

contains Cartesian coordinates and atom data.

Type:

QCData

job_type

contains information on the type of Gaussian job such as ground or transition state optimization, frequency.

Type:

str

roconst

list of parsed rotational constants from Gaussian calculations.

Type:

list

program

program used in chemical computation.

Type:

str

version_program

program version used in chemical computation.

Type:

str

solvation_model

solvation model used in chemical computation.

Type:

str

file

input chemical computation output file.

Type:

str

charge

overall charge of molecule.

Type:

int

empirical_dispersion

empirical dispersion model used in computation.

Type:

str

multiplicity

multiplicity of molecule or chemical system.

Type:

int

mult

multiplicity of molecule or chemical system.

Type:

int

point_group

point group of molecule or chemical system used for symmetry corrections.

Type:

str

sp_energy

single-point energy parsed from output file.

Type:

float

sp_program

program used for single-point energy calculation.

Type:

str

sp_version_program

version of program used for single-point energy calculation.

Type:

str

sp_solvation_model

solvation model used for single-point energy calculation.

Type:

str

sp_file

single-point energy calculation output file.

Type:

str

sp_charge

overall charge of molecule in single-point energy calculation.

Type:

int

sp_empirical_dispersion

empirical dispersion model used in single-point energy computation.

Type:

str

sp_multiplicity

multiplicity of molecule or chemical system in single-point energy computation.

Type:

int

cpu

days, hours, mins, secs, msecs of computation time.

Type:

list

scf_energy

self-consistent field energy.

Type:

float

frequency_wn

frequencies parsed from chemical computation output file.

Type:

list

im_freq

imaginary frequencies parsed from chemical computation output file.

Type:

list

inverted_freqs

frequencies inverted from imaginary to real numbers.

Type:

list

zero_point_corr

thermal corrections for zero-point energy parsed from file.

Type:

float

zpe

vibrational zero point energy computed from frequencies.

Type:

float

enthalpy

enthalpy computed from partition functions.

Type:

float

qh_enthalpy

enthalpy computed from partition functions, quasi-harmonic corrections applied.

Type:

float

entropy

entropy of chemical system computed from partition functions.

Type:

float

qh_entropy

entropy of chemical system computed from partition functions, quasi-harmonic corrections applied.

Type:

float

gibbs_free_energy

Gibbs free energy of chemical system computed from enthalpy and entropy.

Type:

float

qh_gibbs_free_energy

Gibbs free energy of chemical system computed from quasi-harmonic enthalpy and/or entropy.

Type:

float

linear_warning

flag for linear molecules, may be missing a rotational constant.

Type:

bool

ex_sym(file)[source]

Determine the molecular point group and symmetry number using pymsym.

Parameters:

file (str) – Ignored by this method; present for API compatibility.

Returns:

(symmetry_number, point_group) where symmetry_number is an int and point_group is a string.

Return type:

tuple

Raises:

RuntimeError – If the pymsym package is not installed.

classmethod from_options(qcdata_or_path, options)[source]

Construct a calc_bbe from a ThermoOptions bundle.

Recommended v5.0+ entry point — replaces the legacy 15-argument positional constructor (which still works but emits a DeprecationWarning). Accepts either a parsed QCData instance (skips re-parsing) or a filesystem path (parses internally, like the old constructor).

When options.freq_scale_factor and/or options.zpe_scale_factor are None, this method auto-looks them up from the file’s level of theory via the Truhlar database (mirroring the CLI’s behavior). Pass explicit floats to skip the lookup.

sym_correction(file)[source]

Compute and return the symmetry entropy correction and detected point group for the structure identified by file.

Parameters:

file (str) – Filename or identifier for the structure passed to ex_sym to determine symmetry number and point group.

Returns:

sym_correction (float): Symmetry entropy correction converted to internal energy units (J/mol divided by J_TO_AU, i.e., same units used elsewhere in the module). pgroup (str): Detected molecular point-group symbol.

Return type:

tuple

goodvibes.thermo.calc_damp(frequency_wn, freq_cutoff)[source]

Compute per-mode damping factors for quasi-harmonic interpolation between RRHO and free-rotor entropy regimes.

Parameters:
  • frequency_wn (list[float]) – Vibrational frequencies in cm^-1.

  • freq_cutoff (float) – Cutoff frequency in cm^-1 at which the damping factor is approximately 0.5.

Returns:

Damping factors (0 to 1) for each input frequency; values near 1 indicate RRHO behavior, values near 0 indicate free-rotor behavior.

Return type:

list[float]

goodvibes.thermo.calc_electronic_entropy(multiplicity)[source]

Compute the electronic entropy contribution from spin multiplicity.

Parameters:

multiplicity (int) – Spin multiplicity (number of degenerate electronic states).

Returns:

Electronic entropy in J/(mol*K), equal to R * ln(multiplicity).

Return type:

float

goodvibes.thermo.calc_freerot_entropy(temperature, frequency_wn, bav=1e-44, freq_scale_factor=1.0, fract_modelsys=None)[source]

Compute per-mode free-rotor vibrational entropies for a list of vibrational modes.

Parameters:
  • temperature (float) – Temperature in kelvin.

  • frequency_wn (Sequence[float]) – Vibrational frequencies in cm^-1.

  • bav (float) – Reference average moment of inertia in kg·m^2 (defaults to GRIMME_BAV).

  • freq_scale_factor (float or Sequence[float]) – Frequency scale factor applied to modes. If ONIOM blending is used (fract_modelsys provided), this should be a two-item sequence [qm_scale, mm_scale].

  • fract_modelsys (Sequence[float] or None) – Per-mode ONIOM fractions (values in [0,1]) to blend QM/MM scale factors; pass None to disable ONIOM blending.

Returns:

Per-mode free-rotor entropies in J/(mol·K).

Return type:

list[float]

goodvibes.thermo.calc_qRRHO_energy(temperature, frequency_wn, freq_scale_factor=1.0)[source]

Compute per-mode quasi-rigid-rotor harmonic-oscillator (qRRHO) vibrational energy terms.

Parameters:
  • temperature (float) – Temperature in kelvin used for thermal factors.

  • frequency_wn (list[float]) – Vibrational frequencies in cm⁻¹.

  • freq_scale_factor (float | list[float], optional) – Global frequency scaling factor (or list of per-mode factors) applied to each frequency.

Returns:

Per-mode qRRHO vibrational energy terms in J/mol.

Return type:

list[float]

goodvibes.thermo.calc_rotational_energy(temperature, monatomic=False, linear=False)[source]

Compute the rotational energy for a species at a given temperature.

Returns 0 for monatomic species, R*T for linear molecules, and 3/2*R*T for non-linear molecules.

Parameters:
  • temperature (float) – Temperature in kelvin.

  • monatomic (bool) – If True, treat as a single atom (zero rotational energy).

  • linear (bool) – If True, treat as a linear molecule (use R*T).

Returns:

Rotational energy in joules per mole.

Return type:

float

goodvibes.thermo.calc_rotational_entropy(temperature, rotemp, symmno=1, monatomic=False, linear=False)[source]

Calculate the rotational entropy of a species at a given temperature.

Parameters:
  • temperature (float) – Temperature in kelvin.

  • rotemp (Sequence[float]) – Rotational temperatures (K). For linear molecules provide at least one value; for non-linear provide three principal rotational temperatures.

  • symmno (int) – Molecular symmetry number (default 1).

  • monatomic (bool) – If True, treat as a single atom (entropy = 0).

  • linear (bool) – If True, treat as a linear molecule.

Returns:

Rotational entropy in J/(mol·K).

Return type:

float

goodvibes.thermo.calc_rrho_entropy(temperature, frequency_wn, freq_scale_factor=1.0, fract_modelsys=None)[source]

Compute per-mode vibrational entropies using the rigid-rotor harmonic-oscillator (RRHO) model.

Supports ONIOM-style blending of QM/MM scale factors when fract_modelsys is provided: in that case freq_scale_factor is expected to be a two-element iterable [qm_scale, mm_scale] and per-mode scale factors are blended by the fractions in fract_modelsys.

Parameters:
  • temperature (float) – Temperature in kelvin.

  • frequency_wn (iterable) – Vibrational wavenumbers in cm⁻¹.

  • freq_scale_factor (float or iterable) – Frequency scaling factor applied to each mode, or a two-element sequence [qm_scale, mm_scale] when using ONIOM blending.

  • fract_modelsys (iterable, optional) – Per-mode fractions for ONIOM blending; when provided, per-mode scale = qm_scale*frac + mm_scale*(1-frac).

Returns:

List of per-mode vibrational entropies in J/(mol*K).

goodvibes.thermo.calc_translational_energy(temperature)[source]

Compute the ideal-gas translational molar energy at a specified temperature.

Parameters:

temperature (float) – Temperature in kelvin (K).

Returns:

Translational energy in joules per mole (J/mol), equal to 3/2 · R · T.

Return type:

float

goodvibes.thermo.calc_translational_entropy(molecular_mass, conc, temperature, solvent=None)[source]

Calculate the translational entropy for a species at a given temperature and concentration.

Parameters:
  • molecular_mass (float) – Molecular mass in atomic mass units (amu).

  • conc (float) – Concentration in moles per liter (mol/L).

  • temperature (float) – Temperature in kelvin (K).

  • solvent (str, optional) – If provided, adjusts the effective free volume using the solvent name via get_free_space; if None, assumes ideal-gas volume.

Returns:

Translational entropy in joules per mole per kelvin (J/(mol·K)).

Return type:

float

goodvibes.thermo.calc_vibrational_energy(temperature, frequency_wn, freq_scale_factor=1.0, fract_modelsys=None)[source]

Compute the vibrational energy (zero-point + thermal contributions) in joules per mole at a given temperature.

Parameters:
  • temperature (float) – Temperature in kelvin; must be greater than 0.

  • frequency_wn (list[float]) – Vibrational mode wavenumbers in cm^-1.

  • freq_scale_factor (float or list[float], optional) – Frequency scaling factor. If ONIOM blending is used (fract_modelsys provided), supply [qm_scale, mm_scale]; otherwise a single scalar scale factor is applied to all modes.

  • fract_modelsys (list[float] or None, optional) – Per-mode ONIOM fractions (values between 0 and 1). When provided, per-mode scale factors are computed by blending the two entries of freq_scale_factor according to these fractions. If None, no ONIOM blending is applied.

Returns:

Total vibrational energy (J/mol), including zero-point energy and thermal excitations.

Return type:

float

Raises:

ValueError – If temperature is not greater than 0, or if mode energies produce numerical overflow (indicative of too-low temperature).

goodvibes.thermo.calc_zeropoint_energy(frequency_wn, freq_scale_factor=1.0, fract_modelsys=None)[source]

Compute the vibrational zero-point energy for a set of vibrational modes.

When fract_modelsys is provided, freq_scale_factor is expected to be a two-element sequence [qm_scale, mm_scale]; per-mode scale factors are blended using the fractions in fract_modelsys.

Parameters:
  • frequency_wn (list) – Vibrational wavenumbers (cm^-1).

  • freq_scale_factor (float or sequence) – Global scale factor or [qm_scale, mm_scale] for ONIOM blending.

  • fract_modelsys (list, optional) – Per-mode fractions for ONIOM blending; if given, a per-mode scale factor is computed by blending the two entries of freq_scale_factor.

Returns:

Vibrational zero-point energy (J/mol), computed as the sum over modes of 0.5 * h * nu.

Return type:

float

goodvibes.thermo.get_free_space(solv)[source]

Estimate the accessible free volume in a bulk solvent (mL per L).

Computes the volume fraction of a litre of solvent that is not occupied by solvent molecules using literature molarities and molecular volumes (Shakhnovich & Whitesides). This function is deprecated and experimental.

Parameters:

solv (str) – Solvent name (case-sensitive) expected in the supported set.

Returns:

Estimated accessible free volume in milliliters per liter.

Return type:

float

Notes

  • If solv is not recognized a UserWarning is emitted and the function returns 1000.0 (gas-phase fallback).

  • The function emits a DeprecationWarning on use.

Native parsers and QCData

class goodvibes.io.QCData(file: str = '', program: str = '', version_program: str = '', job_type: str = '', scf_energy: float | None = None, charge: int | None = None, multiplicity: int = 1, solvation_model: str = '', empirical_dispersion: str = '', molecular_mass: float = 0.0, symmno: int = 1, linear_mol: bool = False, point_group: str = '', roconst: List[float] = <factory>, rotemp: List[float] = <factory>, linear_warning: bool = False, frequency_wn: List[float] = <factory>, im_frequency_wn: List[float] = <factory>, zero_point_corr: float | None = None, cpu: List[int] = <factory>, nprocs: int = 1, fract_modelsys: List[float] = <factory>, has_oniom: bool = False, atom_nums: List[int] = <factory>, atom_types: List[str] = <factory>, cartesians: List[List[float]] = <factory>, applied_freq_scale_factor: float = 1.0, sp_energy: float | None = None, sp_version_program: str = '', sp_solvation_model: str = '', sp_charge: int | None = None, sp_empirical_dispersion: str = '', sp_multiplicity: int | None = None, sp_suffix: str = '', sp_file: str = '')[source]

Bases: object

Program-agnostic container for parsed quantum chemistry data.

Populated by parse_qcdata() in io.py. Consumed by calc_bbe in thermo.py.

applied_freq_scale_factor: float = 1.0
atom_nums: List[int]
atom_types: List[str]
cartesians: List[List[float]]
charge: int | None = None
cpu: List[int]
empirical_dispersion: str = ''
file: str = ''
fract_modelsys: List[float]
frequency_wn: List[float]
has_oniom: bool = False
im_frequency_wn: List[float]
job_type: str = ''
linear_mol: bool = False
linear_warning: bool = False
molecular_mass: float = 0.0
multiplicity: int = 1
nprocs: int = 1
point_group: str = ''
program: str = ''
roconst: List[float]
rotemp: List[float]
scf_energy: float | None = None
solvation_model: str = ''
sp_charge: int | None = None
sp_empirical_dispersion: str = ''
sp_energy: float | None = None
sp_file: str = ''
sp_multiplicity: int | None = None
sp_solvation_model: str = ''
sp_suffix: str = ''
sp_version_program: str = ''
symmno: int = 1
version_program: str = ''
zero_point_corr: float | None = None
goodvibes.io.compute_connectivity(atom_types, cartesians, tolerance=0.2)[source]

Compute molecular connectivity based on covalent radii.

goodvibes.io.dict_to_qcdata(d)[source]

Reconstruct a QCData instance from a dictionary.

goodvibes.io.element_id(massno, num=False)[source]

Get element symbol from mass number.

Used in parsing output files to determine elements present in file.

Parameter: massno (int): mass of element.

Returns: str: element symbol, or ‘XX’ if not found in periodic table.

goodvibes.io.find_spc_file(name, spc)[source]

Locate the single-point-correction output paired with name.

Pattern matched: {name}_{spc}.{log,out} — exact match. --spc TZVP finds filename_TZVP.log only; it will NOT match filename_def2_TZVP.log. To pair with a file like filename_def2_TZVP.log, pass the full suffix (--spc def2_TZVP).

Parameters:
  • name (str) – Path stem of the parent output file (no extension).

  • spc (str) – Suffix passed via --spc.

Returns:

Path to the matching file, or None if nothing matches.

Return type:

str or None

goodvibes.io.gaussian_jobtype(filename)[source]

Read the jobtype from a Gaussian archive string.

goodvibes.io.level_of_theory(file)[source]

Read output for the level of theory and basis set used.

goodvibes.io.load_cache(path)[source]

Load a QCData cache from a JSON file.

Auto-detects the format. v1.0+ payloads (the unified schema written by –export / –json) are read by extracting each result’s qcdata block; the legacy cache envelope ({_cache_version, entries}, written by pre-v5.0 –cache-save) is also accepted for back-compat.

Parameters:

path (str) – Path to JSON file (either v1.0 unified schema or legacy cache).

Returns:

Mapping of {basename_key: QCData}.

Return type:

dict

goodvibes.io.parse_ase_thermo(file)[source]

Parse a GoodVibes ASE thermo extxyz file.

The format is standard extended XYZ with thermochemistry metadata in the second line (key=value, ASE Atoms.info convention). Required keys: program=ase, scf_energy. Frequencies are space-separated in cm-1 (negatives are imaginary). See tests/ase/README.md for the full spec.

goodvibes.io.parse_data(file)[source]

Read computational chemistry output file.

Attempt to obtain single point energy, program type, program version, solvation_model, charge, empirical_dispersion, and multiplicity from file.

Parameter: file (str): name of file to be parsed.

Returns: float: single point energy. str: program used to run calculation. str: version of program used to run calculation. str: solvation model used in chemical calculation (if any). str: original filename parsed. int: overall charge of molecule or chemical system. str: empirical dispersion used in chemical calculation (if any). int: multiplicity of molecule or chemical system.

goodvibes.io.parse_gaussian_thermo(file)[source]

Parse Gaussian output for all thermochemistry-relevant data.

Returns QCData with raw frequencies (negative = imaginary, no inversion applied). Frequency inversion is a user policy decision handled in thermo.py.

Parameters:

file (str) – Path to Gaussian output file.

goodvibes.io.parse_nwchem_thermo(file)[source]

Parse NWChem output for all thermochemistry-relevant data.

Returns QCData with raw frequencies (negative = imaginary, no inversion applied).

Parameters:

file (str) – Path to NWChem output file.

goodvibes.io.parse_orca_thermo(file)[source]

Parse ORCA output for all thermochemistry-relevant data.

Uses native line-by-line parsing.

Parameters:

file (str) – Path to ORCA output file.

goodvibes.io.parse_qcdata(file)[source]

Parse any supported output file into a QCData object.

Detects program from file content and delegates to the correct parser.

Parameters:

file (str) – Path to quantum chemistry output file.

goodvibes.io.parse_qchem_thermo(file)[source]

Parse a Q-Chem 6 output file for all thermochemistry-relevant data.

Handles single-job and multi-job (@@@-separated opt + freq + sp) inputs: the LAST occurrence wins for SCF energy, geometry, and frequencies, so the converged opt geometry and post-correlation energies are picked up correctly. Recognises native HF/DFT total energy, MP2 total energy, CCSD total energy, and CCSD(T) total energy via the precedence CCSD(T) > CCSD > MP2 > Total energy.

Parameters:

file (str) – Path to a Q-Chem 6 output file.

goodvibes.io.parse_xtb_thermo(file)[source]

Parse xtb output for all thermochemistry-relevant data.

xtb takes a .xyz coordinate file plus CLI flags rather than an input deck. The parser handles single-point (xtb file.xyz), Hessian-only (--hess), and optimization+Hessian (--ohess) jobs, plus implicit solvation (GBSA via -g, ALPB via --alpb, CPCM-X via --cpcmx).

For runs without --opt/--ohess (no optimized-geometry block in the output), Cartesians fall back to the paired .xyz input file.

Parameters:

file (str) – Path to xtb output file.

goodvibes.io.qcdata_to_dict(qcdata)[source]

Convert a QCData instance to a JSON-serializable dictionary.

goodvibes.io.read_initial(file)[source]

At beginning of procedure, read level of theory, solvation model, and check for normal termination

goodvibes.io.save_cache(cache_dict, path)[source]

Write QCData cache to a JSON file.

Parameters:
  • cache_dict (dict) – Mapping of {basename_key: qcdata_dict} where each value is from qcdata_to_dict().

  • path (str) – Output JSON file path.

goodvibes.io.sp_cpu(file)[source]

Read single-point output for CPU time.

Delegates to parse_qcdata(file).cpu so the SPC CPU is identical to what a standalone parse of the same file would report. This guarantees that goodvibes <parents> --spc <suffix> totals correctly reflect the SPC files’ CPU — including program-specific accumulation (Gaussian sums multi-step Job cpu time lines) and nprocs scaling (ORCA reports wall time only; the parser scales by parallel-MPI process count to estimate effective CPU).

goodvibes.io.write_xyz(filepath, files, thermo_data)[source]

Write optimized Cartesian coordinates to a multi-structure .xyz file.

Each structure block contains the atom count, a comment line with the filename and SCF energy, and the Cartesian coordinates.

Parameters:
  • filepath (str) – output .xyz file path.

  • files (list) – output file paths whose coordinates to write.

  • thermo_data (dict) – file path → calc_bbe mapping.

ASE (extended XYZ) bridge

Optional helper for emitting GoodVibes ASE thermo extxyz files from an ASE-driven calculation.

ASE is not a runtime dependency of GoodVibes. This module imports it lazily so the rest of the package works without ASE installed; only direct callers of write_thermo_extxyz need it. See tests/ase/README.md for the format spec.

goodvibes.ase_helper.write_thermo_extxyz(path, atoms, energy, frequencies=None, charge=0, multiplicity=1, level_of_theory=None, solvation_model='gas phase', empirical_dispersion='', point_group=None, symmno=None, linear_mol=None, zpe=None, job_type=None, energy_units='Hartree')[source]

Write a GoodVibes-compatible ASE thermo .extxyz file for the supplied geometry and metadata.

Parameters:
  • path (str) – Output file path (typically ending with .extxyz).

  • atoms (ase.Atoms) – Geometry; element symbols and Cartesian positions are written.

  • energy (float) – Electronic (SCF) energy.

  • frequencies (iterable[float], optional) – Vibrational frequencies in cm^-1; negative values indicate imaginary modes.

  • charge (int, optional) – Total molecular charge. Default: 0.

  • multiplicity (int, optional) – Spin multiplicity. Default: 1.

  • level_of_theory (str, optional) – Free-form method/basis description (e.g., “B3LYP/6-31G*”) used for GoodVibes scaling lookup.

  • solvation_model (str or None, optional) – Description of solvation model (defaults to ‘gas phase’); set to None to omit.

  • empirical_dispersion (str, optional) – Empirical dispersion tag (included when non-empty).

  • point_group (str, optional) – Molecular point group symbol.

  • symmno (int, optional) – Symmetry number.

  • linear_mol (bool, optional) – Whether the molecule is linear.

  • zpe (float, optional) – Zero-point energy in the same units as energy.

  • job_type (str, optional) – Job type hint (e.g., ‘Freq’, ‘GSFreq’, ‘TS’, ‘SP’); parser may infer if omitted.

  • energy_units (str, optional) – Units for energy (default: ‘Hartree’).

Raises:

ImportError – If ASE is not installed (suggests installing via pip install ase).

Output rendering (Rich tables, JSON)

Output formatting and printing functions for GoodVibes.

goodvibes.output.print_cpu_time(thermo_data, exclude=None)[source]

Aggregate and log total CPU time across the provided thermochemistry results.

Parameters:
  • thermo_data (dict) – Mapping of file path to calc_bbe-like objects whose cpu and optional sp_cpu attributes contribute to the total.

  • exclude (str, optional) – Glob pattern; files matching this pattern are omitted from the summed total.

goodvibes.output.print_pes_results(thermo_data, options, dup_list, boltz_facs=None, interval_bbe_data=None, interval=None, file_list=None)[source]

Print relative PES energies from a YAML-defined reaction pathway.

Reads the PES definition, computes relative energies with optional Boltzmann weighting and conformational corrections, and prints formatted tables. Optionally computes enantioselectivity and generates reaction profile graphs.

Parameters:
  • thermo_data (dict) – file path → calc_bbe mapping.

  • options (Namespace) – parsed CLI options. Uses: dp, QH, spc, gconf, temperature_interval, pes, temperature, ee, graph.

  • dup_list (list) – pairs of duplicate/enantiomer file paths.

  • boltz_facs (dict, optional) – Boltzmann factors keyed by file path. Computed by get_boltz() in the orchestrator. None when –ee is off.

  • interval_bbe_data (list, optional) – per-file, per-temperature calc_bbe data.

  • interval (range, optional) – temperature steps for variable-T PES.

  • file_list (list, optional) – file list from temperature interval analysis.

goodvibes.output.print_pes_tables(result, options, temperature=None)[source]

Render PES results as Rich tables (one per pathway).

Parameters:
  • result – PESResult instance (from goodvibes.pes_loader.load_pes).

  • options – argparse Namespace; reads .QH, .spc, .gconf.

  • temperature

    1. Defaults to result.temperatures[0].

goodvibes.output.print_results(thermo_data, options, media_conc=None, dup_list=None, boltz_facs=None)[source]

Print the single-temperature thermochemistry results table.

Outputs energies, enthalpies, entropies and free energies for each file. Optionally includes Boltzmann weighting, imaginary frequencies, symmetry point groups, CPU times, and media concentration annotations.

Parameters:
  • thermo_data (dict) – file path → calc_bbe mapping.

  • options (Namespace) – parsed CLI options. Uses: dp, QH, invert, spc, imag_freq, boltz, symm, duplicate, temperature, cputime, media.

  • media_conc (float, optional) – neat solvent concentration for display.

  • dup_list (list, optional) – pairs of duplicate/enantiomer file paths. Computed by deduplicate() in the orchestrator. Defaults to [].

  • boltz_facs (dict, optional) – Boltzmann factors keyed by file path. Computed by get_boltz() in the orchestrator. None when –boltz is off.

goodvibes.output.print_selectivity_results(results_by_method)[source]

Print one or more selectivity tables, one block per method.

Parameters:

results_by_method (dict[str, list[SelectivityResult]]) – ordered mapping of method name (e.g. “Boltzmann-averaged”, “Lowest conformer only”) to its list of SelectivityResults. Single-temperature mode passes a one-element list per method; scan mode passes one per temperature.

goodvibes.output.print_temperature_interval(thermo_data, options, media_conc=None, qcdata_cache=None)[source]

Recompute thermochemical properties over a temperature interval, log formatted results for each structure and return the computed per-temperature data.

For each file in thermo_data this function evaluates thermochemistry at every temperature in the interval (derived from options.temperature_interval), logs a human-readable table of H, T·S, G(T) and their quasi-harmonic variants when requested, and collects the resulting computed objects.

Parameters:
  • thermo_data (dict) – Mapping of file path → previously computed thermochemistry object (used to determine file order).

  • options (Namespace) – Parsed CLI/options object. Uses these attributes: temperature_interval, dp, QH, QS, S_freq_cutoff, H_freq_cutoff, spc, conc, freq_scale_factor, freespace, invert, inertia, media.

  • media_conc (float, optional) – Solvent concentration (M) to annotate solvent-matching structures when media is enabled.

  • qcdata_cache (dict, optional) – Optional mapping from file basename → pre-parsed QCData to pass through to the recomputation routine.

Returns:

(interval_bbe_data, interval, file_list)
  • interval_bbe_data (list[list]): Outer list indexed by file; each inner list contains the recomputed thermochemistry objects (one per temperature).

  • interval (range): Python range object describing the temperatures iterated.

  • file_list (list): List of file paths in the order processed.

Return type:

tuple

goodvibes.output.write_json_results(thermo_data, options, path, media_conc_per_file=None, boltz_facs=None, selectivity_results=None, selectivity_lowest_results=None, pes_result=None)[source]

Write structured run results to path as JSON.

Captures every parsed QCData field plus computed thermo per file, the schema version, the goodvibes version, and the run options. Schema is preview/v0.x — fields may shift before v5.0 stabilizes it.

Parameters:
  • thermo_data (dict) – file path -> calc_bbe.

  • options (Namespace) – the parsed CLI options.

  • path (str) – output JSON file path.

  • media_conc_per_file (dict, optional) – file path -> neat solvent concentration applied to that specific file (None for files where –media didn’t match).

  • boltz_facs (dict, optional) – file path -> Boltzmann factor.

  • selectivity_results (list[SelectivityResult], optional) – one entry per temperature; included as a top-level “selectivity” block.

Selectivity (v4.1 redesign)

Boltzmann weighting and selectivity calculations for GoodVibes.

class goodvibes.selectivity.SelectivityResult(temperature: float, key: str, labels: List[str], files_per_label: Dict[str, List[str]], populations: Dict[str, float], raw_boltzmann: Dict[str, float], preferred: str, ee: float | None = None, ddG: float | None = None)[source]

Bases: object

N-species selectivity outcome at one temperature.

Numeric data only — formatting strings (e.g. “60:40”, “1.5:1”) are derived in the print layer from populations. Pairwise data are not stored here: for N=2, ee + ddG suffice; for N>2, downstream consumers derive any ratios they need from populations.

ddG: float | None = None
ee: float | None = None
files_per_label: Dict[str, List[str]]
key: str
labels: List[str]
populations: Dict[str, float]
preferred: str
raw_boltzmann: Dict[str, float]
temperature: float
goodvibes.selectivity.assign_files_to_labels(files, label_patterns)[source]

Group file paths by fnmatch against each label’s pattern.

Each pattern is tested against TWO candidates per file, in order:

  1. the file’s basename (e.g. DA_exo_12_i.out) — matches when species are encoded in the filename, the v4.x default.

  2. the basename of the file’s immediate parent directory (e.g. exo) — matches when species are organized into per-species subdirectories.

A file is assigned to the first label whose pattern matches either candidate. So a layout like exo/DA_*.out endo/DA_*.out works with --label exo=exo --label endo=endo (parent-dir match), and the original --label exo='*_exo_*' (basename match) keeps working unchanged.

Parameters:
  • files (Iterable[str]) – file paths to species.

  • label_patterns (dict) – ordered label -> fnmatch glob pattern.

Returns:

label -> list of matching file paths.

Return type:

dict[str, list[str]]

goodvibes.selectivity.compute_selectivity(thermo_data, files_per_label, temperature, dup_list=None, key='gibbs')[source]

Compute populations and (for N=2) ee + ΔΔG‡ for a labeled species set.

Parameters:
  • thermo_data (dict) – file path -> calc_bbe.

  • files_per_label (dict) – ordered label -> list of file paths.

  • temperature (float) – Kelvin.

  • dup_list (list, optional) – pairs of duplicate files to exclude from sums.

  • key (str) – ‘gibbs’ (qh_gibbs_free_energy) or ‘energy’ (scf_energy).

Returns:

SelectivityResult.

Raises:

ValueError – if any label has no files, or if no files have a usable energy attribute.

goodvibes.selectivity.compute_selectivity_lowest_only(thermo_data, files_per_label, temperature, dup_list=None, key='gibbs')[source]

Selectivity using only the most stable conformer per species.

For each label, picks the file with the lowest key (default qh_gibbs_free_energy), drops the rest, and runs the standard Boltzmann calc on the resulting 1-conformer-per-species set. The result complements compute_selectivity by showing how much of the selectivity comes from conformer mixing versus the gap between the lowest TSs.

goodvibes.selectivity.compute_selectivity_lowest_only_scan(thermo_data, files_per_label, temperatures, dup_list=None, key='gibbs')[source]

Lowest-only selectivity at each temperature in temperatures.

goodvibes.selectivity.compute_selectivity_scan(thermo_data, files_per_label, temperatures, dup_list=None, key='gibbs')[source]

Compute a SelectivityResult at each temperature in temperatures.

Convenience wrapper that pairs naturally with –ti temperature intervals; the species grouping is fixed across temperatures.

goodvibes.selectivity.get_boltz(thermo_data, temperature, dup_list, key='gibbs')[source]

Produce normalized Boltzmann populations across all files.

Used by –boltz for the per-file population display and by the legacy –ee selectivity flow (which still calls get_selectivity).

Parameters:
  • thermo_data (dict) – file path -> calc_bbe.

  • temperature (float) – Kelvin.

  • dup_list (list) – pairs [file_i, file_j]; entries whose path is the FIRST member of any pair are excluded (legacy behavior).

  • key (str) – ‘gibbs’ or ‘energy’.

Returns:

file path -> normalized population (Σ = 1.0).

Return type:

dict[str, float]

goodvibes.selectivity.get_selectivity(pattern, files, boltz_facs, temperature, dup_list)[source]

DEPRECATED — legacy ‘a:b’ colon-pattern selectivity.

Forwards to compute_selectivity with a 2-label spec built from the glob pattern. Emits a DeprecationWarning. Use –label / –selectivity going forward.

Returns the legacy 6-tuple: (ee, er, ratio, dd_free_energy, failed, pref) so the existing CLI print path keeps working unchanged.

goodvibes.selectivity.load_label_yaml(path)[source]

Load a selectivity YAML file.

Supports two shapes (mutually exclusive):

labels: { R: ‘P_R_’, S: ‘P_S_’ } # fnmatch patterns files: { R: [a.log, b.log], S: [c.log, …] } # explicit file lists

Returns:

mode is ‘patterns’ or ‘files’; dict maps label to its spec (string pattern, or list of file paths).

Return type:

(mode, dict)

goodvibes.selectivity.parse_label_args(label_args)[source]

Parse repeatable –label NAME=PATTERN args into an ordered dict.

Parameters:

label_args (list[str] or None) – values from argparse for –label.

Returns:

ordered mapping label -> fnmatch glob pattern.

Return type:

dict[str, str]

PES — data model (v4.2)

PES data model.

Pure data + arithmetic; no I/O, no parsing, no global state. The model is what the legacy parser and the new YAML parser produce, and what the output layer (Rich tables, JSON) consumes.

Three layers:

ConformerSet one species, ≥1 conformers (calc_bbe instances) Point stoichiometric sum of species at one node of a path Pathway ordered list of points + a designated zero PESResult container of pathways + options + temperatures

Arithmetic on ThermoVector collapses the legacy nine-parallel-list pattern into one expression: rel = point.thermo(T) - zero.thermo(T).

class goodvibes.pes_model.ConformerSet(name: str, files: List[str], bbes: List[Any])[source]

Bases: object

A named species + ≥1 calc_bbe entries.

Encapsulates the rollup math: pure Boltzmann average, lowest-only, or gconf-corrected (lowest + Boltzmann adjustment + mixing entropy).

bbes: List[Any]
boltzmann_weighted(T: float) ThermoVector[source]

Pure Boltzmann-weighted average over conformers, weights from qh-G.

files: List[str]
gconf_corrected(T: float, QH: bool = True) ThermoVector[source]

Lowest conformer + Boltzmann adjustment + mixing entropy −R Σ pᵢ ln pᵢ.

Mirrors the legacy pes.get_pes algorithm:

H_tot = H_min + (Σ pᵢ Hᵢ − H_min) S_tot = S_min + (Σ pᵢ Sᵢ + Σ −R pᵢ ln pᵢ − S_min) G(T) = H_tot − T·S_tot (or qh-H/qh-S if QH)

ZPE/SCF/SPC follow the plain Boltzmann sum (no mixing-entropy correction applies to those).

property is_single: bool
lowest_conformer() ThermoVector[source]

Vector for the conformer with the lowest qh_gibbs_free_energy.

name: str
class goodvibes.pes_model.PESOptions(units: 'str' = 'kcal/mol', decimals: 'int' = 2, gconf: 'bool' = True, QH: 'bool' = True, spc_used: 'bool' = False, lowest_only: 'bool' = False)[source]

Bases: object

QH: bool = True
decimals: int = 2
gconf: bool = True
lowest_only: bool = False
spc_used: bool = False
to_user_units(hartree: float | None) float | None[source]
units: str = 'kcal/mol'
class goodvibes.pes_model.PESResult(pathways: ~typing.List[~goodvibes.pes_model.Pathway], options: ~goodvibes.pes_model.PESOptions, temperatures: ~typing.List[float] = <factory>)[source]

Bases: object

Top-level container: pathways + options + temperatures.

options: PESOptions
pathways: List[Pathway]
temperatures: List[float]
class goodvibes.pes_model.Pathway(name: str, points: List[Point], zero: Point = None)[source]

Bases: object

Ordered points + a designated zero (defaults to points[0]).

name: str
points: List[Point]
relative(T: float, gconf: bool = True, QH: bool = True, lowest_only: bool = False) List[ThermoVector][source]

Per-point ΔThermo relative to self.zero, in Hartree.

zero: Point = None
class goodvibes.pes_model.Point(label: str, species: List[Tuple[int, ConformerSet]])[source]

Bases: object

One node of a pathway: a stoichiometric sum of ConformerSets.

classmethod from_label(label: str, species_map: dict) Point[source]

Build a Point from a label string and {name: ConformerSet} map.

label: str
species: List[Tuple[int, ConformerSet]]
thermo(T: float, gconf: bool = True, QH: bool = True, lowest_only: bool = False) ThermoVector[source]

Total thermo at this point: Σ coeff_i × ConformerSet_i.thermo(T).

Per-species rollup precedence:

lowest_only=True → the species’ lowest qh-G conformer only else gconf=True → lowest + Boltzmann adjustment + mixing entropy else → pure Boltzmann-weighted average

class goodvibes.pes_model.ThermoVector(scf_energy: float, zpe: float, enthalpy: float, qh_enthalpy: float, entropy: float, qh_entropy: float, gibbs: float, qh_gibbs: float, sp_energy: float | None = None)[source]

Bases: object

Bundle of thermo quantities for one species at one temperature.

Energy fields are in Hartree; entropy fields are in Hartree/K (matching calc_bbe). sp_energy is None when –spc was not used; arithmetic propagates None (if either operand has sp_energy=None, the result does).

enthalpy: float
entropy: float
gibbs: float
qh_enthalpy: float
qh_entropy: float
qh_gibbs: float
scf_energy: float
sp_energy: float | None = None
classmethod zero(with_sp: bool = False) ThermoVector[source]

Identity element for addition.

zpe: float
goodvibes.pes_model.parse_point_label(label: str) List[Tuple[int, str]][source]

Parse a point label like “2*A + B + C” into [(2,’A’), (1,’B’), (1,’C’)].

Whitespace-tolerant. Coefficients must be positive integers. Returns the terms in source order; duplicate species are allowed (they’ll be summed).

PES — loader and dispatcher (v4.2)

Format-agnostic PES loading pipeline.

Three stages, each independently testable:

text ──parse_legacy()/parse_yaml()──▶ PESSpec PESSpec + thermo_data ──build_pes_result()──▶ PESResult

PESSpec is the intermediate that both parsers produce: a description of pathways, species (as file patterns), zero overrides, and options — but no references to actual calc_bbe instances. The builder resolves patterns against thermo_data, constructs ConformerSet`s, and emits a fully-realized `PESResult.

The format dispatcher load_pes() sniffs the file content (presence of — # PES/# SPECIES/# FORMAT markers) to choose the parser; the legacy path emits a DeprecationWarning.

class goodvibes.pes_loader.PESSpec(pathways: ~typing.Dict[str, ~typing.List[str]], species: ~typing.Dict[str, str | ~typing.List[str]], zero: ~typing.Dict[str, str] = <factory>, options: ~goodvibes.pes_model.PESOptions = <factory>, format_extras: ~typing.Dict[str, str] = <factory>)[source]

Bases: object

Format-agnostic intermediate. Both parsers emit this.

pathways

pathway name -> ordered list of point labels.

Type:

Dict[str, List[str]]

species

species name -> file pattern (glob or literal) or list of patterns.

Type:

Dict[str, str | List[str]]

zero

pathway name -> point label to use as zero. Optional; pathways missing from this dict default to their first point.

Type:

Dict[str, str]

options

PESOptions parsed from the FORMAT block.

Type:

goodvibes.pes_model.PESOptions

format_extras

any FORMAT keys not consumed by PESOptions (kept verbatim so graph_reaction_profile can read them).

Type:

Dict[str, str]

format_extras: Dict[str, str]
options: PESOptions
pathways: Dict[str, List[str]]
species: Dict[str, str | List[str]]
zero: Dict[str, str]
goodvibes.pes_loader.build_pes_result(spec: PESSpec, thermo_data: dict, temperatures: List[float] | None = None) PESResult[source]

Resolve patterns, build ConformerSets, parse point labels, return PESResult.

All species referenced (transitively) by the pathways’ points must resolve to at least one file in thermo_data; unresolved names raise KeyError.

goodvibes.pes_loader.is_legacy_format(text: str) bool[source]

Sniff for the — # PES / # SPECIES / # FORMAT markers.

Presence of any one of these markers anywhere in the file means legacy. Otherwise the file is treated as true YAML.

goodvibes.pes_loader.load_pes(path: str, thermo_data: dict, temperatures: List[float] | None = None) PESResult[source]

Read a PES definition file, parse it, and return a PESResult.

Sniffs the file format (legacy — # PES markers vs proper YAML); legacy emits a DeprecationWarning.

goodvibes.pes_loader.resolve_pattern(pattern: str, thermo_data: dict) List[str][source]

Resolve a species pattern against the keys of thermo_data.

Two pattern flavors:

  • File patterns (default) — matched against the basename of each key with the file extension stripped. Supports fnmatch globs; a pattern with no glob characters must equal the stem exactly.

  • Directory patterns — strings prefixed with @dir: (set automatically by the YAML loader when the user writes {dir: “X”} / {dirs: […]}). Matched against the basename of each key’s parent directory. Supports fnmatch globs.

Returns the matched thermo_data keys in their original (insertion) order.

PES — legacy line-based parser (deprecated)

Parser for the legacy line-based PES format.

The format predates v4.2; despite the .yaml extension it isn’t valid YAML (parens in species names like Cu(III)-S and the [a, b, c] PES point lists with bare identifiers would either fail or parse strangely).

Sections are introduced by — # NAME comment markers:

— # PES

Reaction: [Int-I + TolS, Int-II, Int-III]

— # SPECIES

Int-I: Int-I_*

— # FORMAT

units: kcal/mol dec: 1 zero: Int-I + TolS

The parser produces a PESSpec; format keys it doesn’t consume (graph styling — dpi, color, title, etc.) are preserved in format_extras so graph_reaction_profile can keep reading them.

goodvibes.pes_legacy.parse_legacy(text: str) PESSpec[source]

Parse the legacy line-based PES format into a PESSpec.

PES — true-YAML parser (v4.2)

Parser for the new (true YAML) PES format.

Schema:

pathways:

Reaction: [“Int-I + TolS + TolSH”, “Int-II + TolSH”, “Int-III”]

species:

Int-I: {files: “Int-I_*.log”} TolS: {files: [“TolS.log”]} Int-II: {files: “Int-II_*.log”}

zero:

Reaction: “Int-I + TolS + TolSH” # optional; defaults to points[0]

format:

units: kcal/mol decimals: 1

pathways is a dict of pathway-name → ordered list of point label strings. species is a dict of species-name → {files: <glob string OR list>} (the dict shape leaves room for per-species options later — scaling factors, symmetry numbers — without breaking the schema). zero is optional and applies per pathway. format parses into PESOptions.

goodvibes.pes_yaml.parse_yaml(text: str) PESSpec[source]

Parse the YAML text into a PESSpec. PyYAML is required.

PES — back-compat surface and reaction-profile plot

PES driver: legacy get_pes class re-implemented on top of the v4.2 model.

The class signature is unchanged; everything that printed/plotted off get_pes instances in v4.0/v4.1 still does. Internally we delegate parsing and rollup to pes_loader + pes_model, then project the resulting PESResult into the legacy parallel-list attribute layout that output.print_pes_results and pes.graph_reaction_profile already consume.

This shim ships through the v4.x line. v5.1 removes get_pes entirely in favor of the structured API (item 12 / Sub-plan B in ROADMAP.md).

class goodvibes.pes.get_pes(file, thermo_data, temperature, gconf, QH)[source]

Bases: object

Back-compat surface around pes_loader.load_pes.

Reads a PES definition file (legacy line-based or modern YAML), computes Boltzmann-weighted thermo at each pathway point with optional gconf correction, and exposes the same parallel-list attribute layout as the pre-v4.2 implementation: path, species, *_abs, *_zero, plus g_qhgvals / g_species_qhgzero / g_rel_val for the reaction-profile plot.

goodvibes.pes.graph_reaction_profile(graph_data, options, plt)[source]

Render a reaction energy profile plot using quasi-harmonic Gibbs free energies.

Parameters:
  • graph_data (get_pes) – Populated PES object providing pathway labels and thermodynamic arrays used for plotting (e.g., path, qhg_abs, qhg_zero, e_abs, species, g_qhgvals, g_species_qhgzero, g_rel_val, and units).

  • options (object) – Options container with a graph attribute pointing to a formatting file that controls plot appearance (ylim, colors, title, dpi, etc.). If dpi is set in that file, an image file named “Rxn_profile_<options.graph stem>.png” will be written.

  • plt (module) – The matplotlib.pyplot module (used to create and display the figure).

goodvibes.pes.jitter(datasets, color, ax, nx, marker, edgecol='black')[source]

Randomly offsets and plots vertically overlapping points to reduce marker overlap on a matplotlib Axes.

Parameters:
  • datasets (iterable) – Iterable of numeric y-values (each element plotted as a point).

  • color (str or tuple) – Marker color.

  • ax (matplotlib.axes.Axes) – Axes on which to draw the markers.

  • nx (float) – Center x-coordinate around which points are jittered.

  • marker (str) – Marker style string.

  • edgecol (str, optional) – Marker edge color. Defaults to ‘black’.

Sorting and duplicate detection

Structure sorting and duplicate detection for GoodVibes.

goodvibes.sort.deduplicate(thermo_data, *, e_cutoff=0.05, ro_cutoff=0.01, rmsd_cutoff=None)[source]

Identify duplicate or enantiomeric structures by comparing energies, rotational constants, and optionally Cartesian RMSD.

All active criteria must pass for a pair to be flagged as duplicate.

Parameters:
  • thermo_data (dict) – file path → calc_bbe mapping.

  • e_cutoff (float) – max absolute SCF energy difference in kcal/mol (default 0.05).

  • ro_cutoff (float) – max relative difference in rotational constants as a fraction, e.g. 0.01 = 1% (default 0.01).

  • rmsd_cutoff (float or None) – max Cartesian RMSD in Angstrom. None (default) disables RMSD comparison; 0.125 matches CREST.

Returns:

pairs [file_i, file_j] flagged as duplicates.

Return type:

list

goodvibes.sort.kabsch_rmsd(coords_a, coords_b)[source]

Compute the RMSD between two Nx3 Cartesian coordinate sets after optimal rigid alignment using the Kabsch algorithm.

Parameters:
  • coords_a (array-like) – Reference coordinates with shape (N, 3).

  • coords_b (array-like) – Mobile coordinates with shape (N, 3).

Returns:

Root-mean-square deviation between the aligned coordinates, in the same units as the inputs.

Return type:

float

goodvibes.sort.sort_thermo(thermo_data, key)[source]

Return thermo_data reordered by the given energy attribute (lowest first).

Entries with linear_warning, missing the attribute, or with a None value are placed at the end.

Parameters:
  • thermo_data (dict) – file path → calc_bbe mapping.

  • key (str) – sort mode — ‘energy’ (scf_energy) or ‘gibbs’ (qh_gibbs_free_energy).

Returns:

new dict with the same items in sorted order.

Return type:

dict

File validation

File validation and consistency checks for GoodVibes.

goodvibes.validation.check_files(thermo_data, options, level_of_theory)[source]

Run consistency checks across all calculation output files.

Checks: program version, solvation model, level of theory, charge/multiplicity, standard concentration, linear molecule frequencies, TS imaginary frequencies, empirical dispersion, and (if –spc) single-point correction consistency.

Parameters:
  • thermo_data (dict) – file path → calc_bbe mapping.

  • options (Namespace) – parsed CLI options. Uses: conc, spc, duplicate.

  • level_of_theory (list) – level of theory strings, one per file.

goodvibes.validation.collect_and_validate_files(files, options)[source]

Read initial metadata for each output file, remove files that terminated with ‘Error’ or ‘Incomplete’, and verify SPC file termination when SPC mode is enabled.

When a file’s initial read reports progress ‘Error’ or ‘Incomplete’, that file is removed from the returned lists and a warning is logged. If SPC mode (options.spc) is set and not ‘link’, the function attempts to locate corresponding SPC files; if any discovered SPC file reports ‘Error’ or ‘Incomplete’ the program exits. Returned lists remain aligned: the returned level_of_theory and solvation_model correspond to the returned files order.

Parameters:
  • files (list) – Paths to output files to validate.

  • options (Namespace) – Parsed CLI options. Uses the spc attribute to control SPC lookup behavior.

Returns:

(files, level_of_theory, solvation_model) — the filtered file paths, and parallel lists of each file’s level of theory and solvation model.

Return type:

tuple

goodvibes.validation.print_check_fails(check_attribute, file, attribute, option2=None)[source]

Report groups of files that share differing attribute values.

Groups files by the values in check_attribute (or by (value, option2_value) when option2 is provided) and logs each distinct group. For each group the doc prints the attribute value(s) and up to the first three filenames; if more files are present the remaining count is reported.

Parameters:
  • check_attribute (list) – Attribute values aligned with the files list (one value per file).

  • file (list) – File identifiers aligned with check_attribute.

  • attribute (str) – Human-readable name of the attribute being checked (e.g., “levels of theory”).

  • option2 (list, optional) – Secondary attribute values aligned with the files list; when provided groups are keyed by (check_attribute[i], option2[i]).

Solvent database

Solvent database for media concentration corrections.

Solvent molecular weights [g/mol] and densities [g/mL] at 20 °C, loaded from solvents.json alongside this module. Each solvent entry defines a canonical name and one or more lookup aliases (abbreviations, full names).

Public API:

solvents – dict mapping alias (lowercase str) -> (mw, density) tuple

goodvibes.media.compute_media_conc(media, file)[source]

Compute the neat-solvent molar concentration when the output file corresponds to the specified solvent.

Parameters:
  • media (str) – Solvent name as provided (e.g., from –media).

  • file (str) – Path to the output file used to infer the solvent name.

Returns:

Neat-solvent concentration in mol/L if the file’s solvent matches media, None otherwise.

Return type:

float or None

goodvibes.media.lookup_solvent(name)[source]

Look up a solvent by alias and return its (mw, density).

Raises ValueError with a “did you mean …” hint and a list of common aliases if name is unknown. Use this to validate user input (e.g. –media / –freespace) up-front; compute_media_conc and other call sites can then assume the alias is valid.

Frequency scaling factors (Truhlar v5)

Vibrational frequency scaling factors from the Truhlar group database.

Frequency scaling factors taken from version 5 of the Truhlar group database (https://comp.chem.umn.edu/freqscale/).

Alecu, I. M.; Zheng, J.; Zhao, Y.; Truhlar, D. G. J. Chem. Theory Comput. 2010, 6, 2872-2887.

The data is stored in scaling_factors.json alongside this module. Each entry contains scaling factors for ZPEs, harmonic frequencies, and fundamentals.

goodvibes.vib_scale_factors.D()

Directly obtained from the ZPVE15/10 or F38/10 databases (Ref. 1).

goodvibes.vib_scale_factors.C()

Obtained by applying a systematic correction of -0.0025 to preexisting scale factor.

goodvibes.vib_scale_factors.R()

Obtained via the Reduced Scale Factor Optimization Model (Ref. 1).

Public API:

scaling_data_dict – dict mapping canonicalized level/basis -> ScalingData scaling_refs – list of reference citation strings ScalingData – namedtuple for scaling factor data canonicalize_level – normalize level/basis strings for lookup FUNCTIONAL_ALIASES – dict of known functional name aliases

class goodvibes.vib_scale_factors.ScalingData(level_basis, zpe_fac, zpe_ref, zpe_meth, harm_fac, harm_ref, harm_meth, fund_fac, fund_ref, fund_meth)

Bases: tuple

fund_fac

Alias for field number 7

fund_meth

Alias for field number 9

fund_ref

Alias for field number 8

harm_fac

Alias for field number 4

harm_meth

Alias for field number 6

harm_ref

Alias for field number 5

level_basis

Alias for field number 0

zpe_fac

Alias for field number 1

zpe_meth

Alias for field number 3

zpe_ref

Alias for field number 2

goodvibes.vib_scale_factors.canonicalize_level(level_basis)[source]

Normalize a level/basis string to a canonical form suitable for dictionary lookup.

Parameters:

level_basis (str) – Level and optional basis separated by ‘/’, e.g. “b3lyp/6-31g*”.

Returns:

Canonicalized “FUNCTIONAL” or “FUNCTIONAL/BASIS” string where the functional name has been normalized (aliases resolved and case-normalized) and formatted for consistent lookup.

Return type:

str

Constants

Constants and literature references for GoodVibes.

Utilities

Utility classes and functions for GoodVibes.

goodvibes.utils.add_time(tm, cpu)[source]

Create a datetime representing tm’s day/time advanced by an elapsed CPU-style interval.

Parameters:
  • tm (datetime) – Source datetime whose day, hour, minute, second, and microsecond are used.

  • cpu (Sequence[int]) – Elapsed time as [days, hrs, mins, secs, msecs].

Returns:

A new datetime with year set to 100 and month set to 1, using tm’s day/time plus the interval from cpu (milliseconds interpreted as 1/1000 second).

Return type:

datetime

goodvibes.utils.all_same(items)[source]

Determine whether every element of items equals the first element.

Parameters:

items (Sequence) – A non-empty sequence of comparable elements.

Returns:

True if every element equals the first element, False otherwise.

Raises:

IndexError – If items is empty.

goodvibes.utils.display_name(file)[source]

Get the basename of a file path without its extension for display.

Parameters:

file (str) – Path or filename from which to extract the display name.

Returns:

The filename portion of file with the final extension removed.

Return type:

display_name (str)

goodvibes.utils.fatal(message)[source]

Log a critical error message and terminate the process.

Shuts down the logging subsystem and exits the process with status code 1.

Parameters:

message (str) – The message to emit at the critical level.

goodvibes.utils.get_console_dat() Console[source]

Return the Rich Console for the .dat file (no color, box-drawing chars).

goodvibes.utils.get_console_stdout() Console[source]

Get the Rich Console configured for colored stdout output.

Returns:

The Rich Console instance used for stdout.

Return type:

Console

Raises:

RuntimeError – If setup_logging() has not been called and the console is not initialized.

goodvibes.utils.natural_key(path)[source]

Sort key that orders conf_2 before conf_10 (and before conf_a).

Splits on digit runs and treats them as integers so ordinary string comparison won’t put conf_10 between conf_1 and conf_2. Comparison uses the basename so files from different directories with the same name don’t separate solely by directory path.

goodvibes.utils.setup_logging(filein, append)[source]

Configure the ‘goodvibes’ logger to write to both stdout and a .dat file and initialize module-level Rich consoles.

Initializes the logger named ‘goodvibes’ to emit messages to standard output and to a file at “{filein}_{append}.dat”. Opens the .dat file for writing and, if Rich is available, assigns module-level Console instances for stdout and the .dat file for later use.

Parameters:
  • filein (str) – Prefix for the output file (e.g., “GoodVibes”).

  • append (str) – Suffix for the output file (e.g., “output”).

Top-level package

GoodVibes: quasi-harmonic thermochemistry from QC outputs.

Programmatic API (v4.2):

from goodvibes import compute_thermo, compute_batch, ThermoResult r = compute_thermo(“file.log”, QH=True, spc=”TZ”) print(r.qh_gibbs_free_energy)

The calc_bbe class remains the canonical engine; compute_thermo is just a kwargs façade that returns a structured ThermoResult.

class goodvibes.ThermoOptions(QS: str = 'grimme', QH: bool = False, s_freq_cutoff: float = 100.0, h_freq_cutoff: float = 100.0, temperature: float = 298.15, concentration: float | None = None, freq_scale_factor: float | None = None, zpe_scale_factor: float | None = None, solv: str | None = None, spc: str | None = None, invert: float | None = None, symm: bool = False, mm_freq_scale_factor: float | None = None, inertia: str = 'global')[source]

Bases: object

Bundle of thermochemistry options for calc_bbe.from_options.

Frozen so it’s safe to share across worker processes (parallel parsing) and across multiple calc_bbe calls without accidental mutation. Field names match the v4.2 façade compute_thermo kwargs; the older calc_bbe.__init__ parameter names are mapped internally.

QH: bool = False
QS: str = 'grimme'
concentration: float | None = None
freq_scale_factor: float | None = None
h_freq_cutoff: float = 100.0
inertia: str = 'global'
invert: float | None = None
mm_freq_scale_factor: float | None = None
s_freq_cutoff: float = 100.0
solv: str | None = None
spc: str | None = None
symm: bool = False
temperature: float = 298.15
zpe_scale_factor: float | None = None
class goodvibes.ThermoResult(file: str, name: str, scf_energy: float | None, sp_energy: float | None, zpe: float | None, enthalpy: float | None, qh_enthalpy: float | None, entropy: float | None, qh_entropy: float | None, gibbs_free_energy: float | None, qh_gibbs_free_energy: float | None, frequency_wn: List[float] | None, im_frequency_wn: List[float] | None, inverted_freqs: List[float] | None, point_group: str | None, symmno: int | None, linear_mol: bool, multiplicity: int | None, job_type: str | None, level_of_theory: str | None, program: str | None, bbe: Any, qcdata: Any)[source]

Bases: object

Bundle of thermochemistry for one structure.

All numeric fields are in atomic units (Hartree for energies, Hartree/K for entropies); frequencies are cm⁻¹. Fields that aren’t available for the given file (e.g. qh_enthalpy when QH=False, sp_energy when spc=None) are reported as None rather than a sentinel.

bbe: Any
enthalpy: float | None
entropy: float | None
file: str
frequency_wn: List[float] | None
gibbs_free_energy: float | None
property has_thermo: bool

True when calc_bbe found enough information to compute G(T). False for SP-only outputs (no frequency block).

im_frequency_wn: List[float] | None
inverted_freqs: List[float] | None
job_type: str | None
level_of_theory: str | None
linear_mol: bool
multiplicity: int | None
name: str
point_group: str | None
program: str | None
qcdata: Any
qh_enthalpy: float | None
qh_entropy: float | None
qh_gibbs_free_energy: float | None
scf_energy: float | None
sp_energy: float | None
symmno: int | None
zpe: float | None
goodvibes.bbe_to_result(bbe: Any, path: str | None = None, *, level_of_theory: str | None = None) ThermoResult[source]

Project a calc_bbe instance into a ThermoResult.

Useful for adapting CLI internals (which keep thermo_data as a {path: calc_bbe} dict) to the structured API without re-parsing. level_of_theory is read from the file via read_initial() if not supplied; pass it explicitly to avoid the extra file scan.

goodvibes.compute_batch(paths: Sequence[str], *, jobs: int = 1, **kwargs: Any) List[ThermoResult][source]

Compute thermochemistry for a list of files.

Parameters:
  • paths – list of QC output file paths.

  • jobs – parallelism level. 1 (default) is sequential — no process-pool overhead. > 1 spawns that many worker processes via concurrent.futures.ProcessPoolExecutor. 0 or negative uses os.cpu_count() (or 1 if unknown).

  • **kwargs – forwarded unchanged to every compute_thermo call.

Returns:

Results in input order (matches executor.map’s contract). Workers can’t write to the orchestrator’s logger, so per-file log.info from inside calc_bbe is silenced under jobs > 1; warnings that surface through ThermoResult (e.g. missing-frequency files) still come through unchanged.

goodvibes.compute_thermo(path: str | None = None, *, qcdata: Any = None, QS: str = 'grimme', QH: bool = False, s_freq_cutoff: float = 100.0, h_freq_cutoff: float = 100.0, temperature: float = 298.15, concentration: float | None = None, freq_scale_factor: float | None = None, zpe_scale_factor: float | None = None, solv: str | None = None, spc: str | None = None, invert: float | None = None, symm: bool = False, mm_freq_scale_factor: float | None = None, inertia: str = 'global') ThermoResult[source]

Compute thermochemistry for one QC output file.

Pass either path (a filesystem location) or a pre-parsed qcdata (skips re-parsing). When concentration is None the gas-phase reference at 1 atm is used (P / RT).

Parameters mirror the CLI:

QS ‘grimme’ (default) or ‘truhlar’ quasi-harmonic entropy QH apply Head-Gordon quasi-harmonic enthalpy correction s_freq_cutoff entropy cutoff (cm⁻¹) h_freq_cutoff enthalpy cutoff (cm⁻¹) — only used when QH=True temperature K concentration mol/L. None → gas phase 1 atm. freq_scale_factor None → auto-lookup harm_fac from level of theory.

Applied to the partition-function frequencies (used in H_vib and S_vib).

zpe_scale_factor None → auto-lookup zpe_fac from level of theory.

Applied to ZPE only. If freq_scale_factor is explicitly set but zpe_scale_factor is None, ZPE inherits freq_scale_factor (back-compat).

solv ‘none’, or solvent name for free-space correction spc None, ‘link’, or filename suffix for SPC files invert None, or threshold for converting small imag → real symm apply pymsym symmetry-number correction

goodvibes.to_dataframe(results: Sequence[ThermoResult])[source]

Convert a list of ThermoResults into a pandas DataFrame.

Pandas is an optional dependency; raises ImportError with an install hint if it isn’t available. The DataFrame has one row per result and columns for every public scalar field on ThermoResult (omits the bbe and qcdata references).

goodvibes.to_parquet(results: Sequence[ThermoResult], path: str) None[source]

Write a list of ThermoResults to a Parquet file at path.

Same column set as to_dataframe. Requires pandas + a Parquet engine (pyarrow or fastparquet); install with pip install goodvibes[full] or pip install pyarrow.