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, spc_applied: bool = False, temperature: float | None = None, options: Any = None, freq_scale_factor: float | None = None, zpe_scale_factor: float | None = None, scale_factor_source: str | None = None, symmetry_source: str | None = None, n_imag: int | None = None)[source]
Bases:
objectBundle 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
- freq_scale_factor: float | None = None
- 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
- n_imag: int | None = None
- name: str
- options: Any = None
- point_group: str | None
- program: str | None
- qcdata: Any
- qh_enthalpy: float | None
- qh_entropy: float | None
- qh_gibbs_free_energy: float | None
- scale_factor_source: str | None = None
- scf_energy: float | None
- sp_energy: float | None
- spc_applied: bool = False
- symmetry_source: str | None = None
- symmno: int | None
- temperature: float | None = None
- zpe: float | None
- zpe_scale_factor: float | None = 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[Any], *, jobs: int = 1, **kwargs: Any) List[ThermoResult][source]
Compute thermochemistry for a list of files or QCData objects.
- Parameters:
paths – QC output file paths and/or parsed
QCDatainstances (e.g. fromQCData.from_atomsin an MLIP workflow).jobs – parallelism level.
1(default) is sequential — no process-pool overhead.> 1spawns that many worker processes viaconcurrent.futures.ProcessPoolExecutor.0or negative usesos.cpu_count()(or 1 if unknown).**kwargs – forwarded unchanged to every
compute_thermocall.
- Returns:
Results in input order (matches
executor.map’s contract). Workers can’t write to the orchestrator’s logger, so per-filelog.infofrom insidecalc_bbeis silenced underjobs > 1; warnings that surface throughThermoResult(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, inertia: str = 'global', strict_spc: bool = False) 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 strict_spc raise MissingSinglePointError when spc is set but
the single-point energy is missing or unparseable (default: RuntimeWarning and the frequency-level energy is used)
- 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 and the frequency lists). The imaginary frequencies are kept as
im_frequency_wn: a space-separated string in cm⁻¹, as--imagprints them, or None when the structure has none.
- 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, 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, zpe_scale_factor, boltz, ee.
level_of_theory (list) – level of theory strings, one per file.
Thermochemistry engine
- exception goodvibes.thermo.MissingSinglePointError[source]
Bases:
ValueErrorRaised under strict_spc when a requested single-point energy is missing or unparseable instead of silently using the frequency-level energy.
- goodvibes.thermo.SCALE_FACTOR_SOURCES = ('user', 'truhlar', 'mlip-unscaled', 'none-found')
Where
calc_bbe.scale_factor_sourcesays the harmonic frequency scale factor came from.
- exception goodvibes.thermo.ScaleFactorWarning[source]
Bases:
UserWarningNo vibrational scaling factor was found for a level of theory, so its frequencies were used unscaled (factor 1.0).
- 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, inertia: str = 'global', strict_spc: bool = False, scale_factor_source: str | None = None)[source]
Bases:
objectBundle 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
- s_freq_cutoff: float = 100.0
- scale_factor_source: str | None = None
- solv: str | None = None
- spc: str | None = None
- strict_spc: bool = False
- symm: bool = False
- temperature: float = 298.15
- zpe_scale_factor: float | None = None
- goodvibes.thermo.calc_avg_moment_of_inertia(roconst)[source]
Average moment of inertia (Grimme’s Bav for the free-rotor interpolation, –bav conf) from the rotational constants: the mean over the principal axes of I_i = h / (8 pi^2 B_i).
Averaging the moments (not the constants) is Grimme’s definition and is what ORCA and xtb use; for prolate tops (A >> B ~ C) the two averages differ by an order of magnitude. Earlier versions returned h / <B>, i.e. without the 8 pi^2 and averaged over constants (issue #113). The effect on qh-G is small (Bav only damps the lowest modes) but was measurable against ORCA / xtb reference values (up to ~30 uEh).
- Parameters:
roconst (list[float]) – Rotational constants in gigahertz (GHz). Zero entries (the missing axis of a linear molecule) are ignored.
- Returns:
Average moment of inertia in kilogram square meters (kg·m^2).
- Return type:
float
- Raises:
ValueError – If roconst is empty, contains a negative constant, or has no positive entry.
- 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, inertia='global', qcdata=None, zpe_scale_fac=None, strict_spc=False, _from_options=False)[source]
Bases:
objectCompute “black box” entropy and enthalpy values along with all other thermochemical quantities.
Computes H, S from partition functions, applying quasi-harmonic corrections
- 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/importable.
- classmethod from_options(qcdata_or_path, options, *, scale_factor_source=None)[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.
The result’s
scale_factor_sourcerecords where the harmonic factor came from: ‘user’ (passed in), ‘truhlar’ (database lookup), ‘mlip-unscaled’ (an ASE input whose level names no basis set, i.e. an MLIP, with no database entry: 1.0 by design) or ‘none-found’ (1.0 because the level of theory is not in the database; a ScaleFactorWarning says so). A caller that resolved the factors itself (the CLI) passesscale_factor_source.
- 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)[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) – Frequency scale factor applied to all modes.
- 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)[source]
Compute per-mode vibrational entropies using the rigid-rotor harmonic-oscillator (RRHO) model.
- Parameters:
temperature (float) – Temperature in kelvin.
frequency_wn (iterable) – Vibrational wavenumbers in cm⁻¹.
freq_scale_factor (float) – Frequency scaling factor applied to each mode.
- 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)[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, optional) – Frequency scaling factor applied to all modes.
- 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)[source]
Compute the vibrational zero-point energy for a set of vibrational modes.
- Parameters:
frequency_wn (list) – Vibrational wavenumbers (cm^-1).
freq_scale_factor (float) – Global scale factor applied to all modes.
- 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.HessianData(hessian: ndarray, masses: List[float], program: str, source: str)[source]
Bases:
objectCartesian Hessian and the masses needed to mass-weight it.
Produced by parse_hessian(). Units follow the QC programs’ native conventions: the Hessian is in Hartree/Bohr^2 and masses are in amu. Row/column order is 3N Cartesian components (x1, y1, z1, x2, …) matching the atom order of the parsed geometry.
- hessian: ndarray
- masses: List[float]
- program: str
- source: str
- 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 = '', level_of_theory: 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, has_oniom: bool = False, atom_nums: List[int] = <factory>, atom_types: List[str] = <factory>, cartesians: List[List[float]] = <factory>, per_atom_masses: 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 = '', sp_level_of_theory: str = '')[source]
Bases:
objectProgram-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 = ''
- frequency_wn: List[float]
- classmethod from_atoms(atoms, energy, *, frequencies=None, energy_units='eV', frequency_units='cm-1', name=None, method=None, charge=None, multiplicity=None, symm='auto', masses='isotopic', job_type=None, solvation_model='gas phase', empirical_dispersion='', linear_mol=None, zpe=None)[source]
Build a QCData from an ASE
Atoms(or anything withget_chemical_symbols()andget_positions()), an electronic energy and, optionally, vibrational frequencies; no file involved.- Parameters:
atoms – geometry;
atoms.infomay supplycharge,multiplicity,nameandlevel_of_theorydefaults.energy – electronic energy in
energy_units(‘eV’ default, also ‘hartree’, ‘kcal/mol’, ‘kJ/mol’).frequencies – vibrational wavenumbers in
frequency_units(‘cm-1’ default; ‘eV’ / ‘meV’ are converted). A negative or complex value is an imaginary mode. Translational and rotational modes must already be removed (seefrom_vibrations(), which does that for an ASEVibrationsData).name – display name (
ThermoResult.name); defaultatoms.info['name']or ‘atoms’.method – model chemistry label, e.g. ‘MACE-OFF23’ or ‘B3LYP/6-31G(d)’. Stored as
level_of_theory; when it matches an entry of the Truhlar scaling database the same scale factors as for a file are applied, otherwise 1.0 (by design for an MLIP, a method naming no basis set; with a ScaleFactorWarning for a QM level).charge – default
atoms.infovalues, else 0 / 1.multiplicity – default
atoms.infovalues, else 0 / 1.symm – ‘auto’ (default) detects the point group and symmetry number with pymsym when it is installed, an int sets the symmetry number directly, None leaves it at 1 (C1).
masses – ‘isotopic’ (most-abundant-isotope masses, the QC-program convention), ‘atoms’ (
atoms.get_masses(), standard atomic weights) or a sequence of per-atom masses in amu.job_type – ‘Freq’, ‘TSFreq’ (‘TS’), ‘SP’; inferred from the frequencies when None (an imaginary mode → ‘TSFreq’).
linear_mol – force linearity; detected from the geometry when None.
zpe – zero-point energy in
energy_unitsto record as parsed metadata (GoodVibes recomputes the ZPE it reports from the frequencies, as for every program).
- classmethod from_vibrations(atoms, vibdata, energy, *, energy_units='eV', drop_tr_modes=True, imag_threshold_cm1=-15.0, **kw)[source]
Build a QCData from an ASE vibrational analysis.
vibdatais anase.vibrations.VibrationsData(or aVibrationsobject, or any sequence of wavenumbers in cm⁻¹; complex or negative values are imaginary). The 3N modes of a finite-difference Hessian include the 6 (5 for a linear molecule) translations and rotations, whichdrop_tr_modesremoves as the modes of smallest magnitude. An imaginary mode smaller in magnitude than|imag_threshold_cm1|is numerical noise on a slightly rough (MLIP) surface and is taken as real at|ν|with aRuntimeWarning; larger ones stay imaginary. ARuntimeWarningalso reports a transition state (job_type='TS') without exactly one imaginary mode, a minimum with any, or more than one imaginary mode when nojob_typewas given. Other keywords go tofrom_atoms().
- has_oniom: bool = False
- im_frequency_wn: List[float]
- job_type: str = ''
- level_of_theory: str = ''
- linear_mol: bool = False
- linear_warning: bool = False
- molecular_mass: float = 0.0
- multiplicity: int = 1
- nprocs: int = 1
- per_atom_masses: List[float]
- 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_level_of_theory: str = ''
- sp_multiplicity: int | None = None
- sp_solvation_model: str = ''
- sp_suffix: str = ''
- sp_version_program: str = ''
- symmno: int = 1
- version_program: str = ''
- with_single_point(energy, units, method=None, *, solvation_model=None, charge=None, multiplicity=None)[source]
A copy of this QCData whose enthalpies and free energies use
energy, a single point at another level, in place of the electronic energy of the frequency calculation: a composite such as DFT//MLIP (DFT energy, MLIP geometry and frequencies) with no single-point output file.The single point is applied the way
spcapplies one read from a file (ThermoResult.spc_appliedis True andsp_energyisenergy) without passingspc; an explicitspcstill wins. The copy keeps the attached energy throughcompute_thermo, re-evaluation at other temperatures,--exportcaches and embedded conformers. The vibrational scale factor is still looked up for the level of the frequencies.- Parameters:
energy – the single-point electronic energy.
units – its units, ‘hartree’, ‘eV’, ‘kcal/mol’ or ‘kJ/mol’ (required, so an energy is never read in the wrong units).
method – level of theory of the single point, e.g. ‘DLPNO-CCSD(T)/def2-TZVP’; recorded as
sp_level_of_theory.solvation_model – of the single point; default those of this QCData.
charge – of the single point; default those of this QCData.
multiplicity – of the single point; default those of this QCData.
- 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.
Keys that are not QCData fields are ignored so that payloads written by other GoodVibes versions (e.g. the retired
fract_modelsysfield) still load.
- 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 TZVPfindsfilename_TZVP.logonly; it will NOT matchfilename_def2_TZVP.log. To pair with a file likefilename_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.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_hessian(file)[source]
Parse the Cartesian Hessian for a frequency job into HessianData.
Unlike parse_qcdata(), which extracts the program’s printed frequencies, this returns the raw second-derivative matrix – needed by consumers that re-mass-weight it, e.g. isotopologue analysis in Kinisot.
- Supported sources:
Gaussian: the archive force-constant block of a freq .log/.out.
ORCA: the companion .hess file (same stub as the .out), or a .hess path given directly.
- Parameters:
file (str) – QC output file (.log/.out) or an ORCA .hess file.
- Returns:
hessian (Hartree/Bohr^2), masses (amu), program, source.
- Return type:
- Raises:
FileNotFoundError, ValueError – If no file, no Hessian data, or an unsupported program.
- 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
.xyzcoordinate 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.xyzinput 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.read_xyz_frames(path, *, energy_units=None, energy_key=None, method=None, charge=None, multiplicity=None)[source]
Read every frame of a multi-frame
.xyzor.extxyzfile as an energy-only QCData: a geometry and an electronic energy, no frequencies.For conformer ensembles (CREST
crest_conformers.xyz, xtb trajectories) and MLIP sweeps (ase.io.writeof many frames).compute_batchevaluates the list, and aConformerSetof the results weighted by'electronic'gives Boltzmann populations and the ensemble energy; free energies need frequencies, so the thermochemical quantities are None.The energy of a frame comes from its comment line: in an extxyz one (
key=valuepairs) fromenergy_keyor, by default, the first ofenergy,free_energy,total_energy(eV, the ASE convention),scf_energy(hartree) andE(eV), a named key other than these being in eV; in a plain one fromenergy: <value>(xtb) or a bare number (CREST), in hartree. Anenergy_units(orscf_energy_units) key, or theenergy_unitsargument, overrides the units. A frame without an energy is an error.- Parameters:
path – the
.xyz/.extxyzfile.energy_units – units of every frame’s energy (‘hartree’, ‘eV’, ‘kcal/mol’, ‘kJ/mol’); default as above.
energy_key – the extxyz key holding the energy.
method – level of theory of the energies (
level_of_theory), e.g. ‘GFN2-xTB’ or ‘MACE-OFF23’; default the frame’slevel_of_theorykey.charge – default the frame’s keys, else 0 / 1.
multiplicity – default the frame’s keys, else 0 / 1.
- Returns:
A list of QCData, one per frame, named
<stem>_<n>(nfrom 1, zero-padded) unless a frame has anamekey;job_type‘SP’.
- goodvibes.io.resolve_output_file(file, extensions=('.log', '.out', '.extxyz'))[source]
Return the on-disk path to parse for
file.The path the caller actually gave is always preferred when it exists. Only when it does not (e.g. an extension-less stem, or
--spctwins named by stem) are sibling files with the same stem and one ofextensionstried, in order. ReturnsNoneif nothing exists.Earlier versions tried
stem.logbefore the given path, so asking forx.outsilently parsedx.logwhen both were present.
- 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).cpuso the SPC CPU is identical to what a standalone parse of the same file would report. This guarantees thatgoodvibes <parents> --spc <suffix>totals correctly reflect the SPC files’ CPU — including program-specific accumulation (Gaussian sums multi-stepJob cpu timelines) 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.apply_cli_pes_options(result, options)[source]
Sync run-time CLI flags into
result.options.The PES file only carries presentation options (units, decimals);
--nogconf,--lowest-only,-qand--spccome from the command line and must be applied to the model before any consumer (Rich tables, JSONpesblock,--pes-plot) reads it. Call this once right afterload_pes; it is idempotent.- Parameters:
result – PESResult instance (from goodvibes.pes_loader.load_pes).
options – argparse Namespace; reads .gconf, .QH, .spc, .lowest_only.
- Returns:
The same
result, for chaining.
- goodvibes.output.pes_tables(result, temperature=None, conc=None)[source]
Rich tables for a PESResult: one
rich.table.Tableper pathway.Usable from a notebook or script without the CLI’s logging set-up:
from rich import print for table in pes_tables(result): print(table)
- Parameters:
result – PESResult (
goodvibes.load_pesor built by hand). Itsoptionsdecide units, decimals, rollup and columns; the CLI syncs them withapply_cli_pes_options.temperature –
Defaults to the result’s first temperature.
conc – user concentration in mol/L for the title; None → 1 atm.
- 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 –
Defaults to result.temperatures[0].
- goodvibes.output.print_profile_selectivity(results, units='kcal/mol')[source]
The selectivities of a reaction-profile document’s
selectivityblocks (Profile.evaluate_selectivity): a summary line, a table of branch barriers and populations, and any Curtin–Hammett warning.
- 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 (list[float]): the temperatures iterated (see utils.parse_temperature_interval).
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, profile=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.
pes_result (PESResult, optional) – written as the “pes” block.
profile (goodvibes.profile.Profile, optional) – the evaluated reaction-profile document, written as the “profile” block (payload 1.1; without embedded conformers).
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, major: str | None = None, ee_signed: float | None = None, ratio: float | None = None, ensemble_energies: Dict[str, float] | None = None, quantity: str | None = None, name: str | None = None, series: str | None = None, reference: str | None = None, barriers: Dict[str, float] | None = None, curtin_hammett: str | None = None, warnings: Tuple[str, ...] = ())[source]
Bases:
objectN-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.
Conventions (v2, GoodVibes 5.1):
majoris the label with the largest population; on an exact tie, the first listed.preferredis the same label (kept from v1).eeis the excess of the major over the minor for two labels,|p1 - p2| * 100, always ≥ 0;ee_signedis(p1 - p2) * 100with the labels in the order given, so it is positive when the first label is the major one. Both are None for more than two labels.ddG(two labels) andratio(any number) compare the major with the runner-up:ddG = RT ln(p_major / p_runner_up)in Hartree, ≥ 0, andratio = p_major / p_runner_up.ensemble_energiesis each label’s ensemble energy-RT ln Σ exp(-E_i / RT)over its conformers, in Hartree: absolute when computed from structures, relative to the reference point when computed from a reaction-profile document (then equal tobarriers).
- barriers: Dict[str, float] | None = None
- curtin_hammett: str | None = None
- ddG: float | None = None
- ee: float | None = None
- ee_signed: float | None = None
- ensemble_energies: Dict[str, float] | None = None
- files_per_label: Dict[str, List[str]]
- key: str
- labels: List[str]
- major: str | None = None
- name: str | None = None
- populations: Dict[str, float]
- preferred: str
- quantity: str | None = None
- ratio: float | None = None
- raw_boltzmann: Dict[str, float]
- reference: str | None = None
- series: str | None = None
- temperature: float
- warnings: Tuple[str, ...] = ()
- exception goodvibes.selectivity.SelectivityWarning[source]
Bases:
UserWarningA selectivity rests on an assumption the data do not support (a Curtin–Hammett precondition that fails, a branch that is not a transition state).
- 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:
the file’s basename (e.g.
DA_exo_12_i.out) — matches when species are encoded in the filename, the v4.x default.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_*.outendo/DA_*.outworks 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', thermo_by_temperature=None)[source]
Lowest-only selectivity at each temperature in temperatures.
The lowest conformer per species is chosen at each temperature from that temperature’s free energies when
thermo_by_temperatureis given (see compute_selectivity_scan).
- goodvibes.selectivity.compute_selectivity_scan(thermo_data, files_per_label, temperatures, dup_list=None, key='gibbs', thermo_by_temperature=None)[source]
Compute a SelectivityResult at each temperature in temperatures.
Pairs with –ti temperature intervals; the species grouping is fixed across temperatures. Free energies are temperature dependent, so pass
thermo_by_temperature({T: thermo_data recomputed at T}) to use the correct G(T) at each point of the scan; the CLI does this. With onlythermo_datathe same free energies are reused at every T and only the Boltzmann factor’s RT changes, which is what this function did before 4.5 and is still the right thing whenkey='energy'.
- goodvibes.selectivity.describe_selectivity(result, units='kcal/mol', decimals=1)[source]
A one-line summary: the major branch, the signed ee (two branches) or the ratio over the runner-up, ΔΔG‡ and the Curtin–Hammett status, e.g.
er (G, 298.15 K): major TS_R, ee +76.6 % (TS_R vs TS_S), ΔΔG‡ 1.20 kcal/mol, Curtin–Hammett satisfied.
- 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]
- goodvibes.selectivity.selectivity_from_energies(energies, temperature, *, key=None, quantity=None, files_per_label=None, **extra)[source]
A SelectivityResult from each label’s conformer energies.
- Parameters:
energies (Mapping[str, Sequence[float]]) – ordered label -> the energies (Hartree) of its conformers; one value per label for a single structure or an already rolled-up level.
temperature (float) – Kelvin.
key (str) – recorded on the result (
quantityis the registry id;keydefaults to it).quantity (str) – recorded on the result (
quantityis the registry id;keydefaults to it).files_per_label (dict, optional) – label -> file paths, recorded.
extra – further SelectivityResult fields (
name,series,reference,barriers,curtin_hammett,warnings).
A label with no energy gets population 0.
- Raises:
ValueError – fewer than two labels, or no energy at all.
- goodvibes.selectivity.selectivity_rows(results, units='kcal/mol')[source]
One row per label of each SelectivityResult, for tables and CSV:
selectivity(the block id),series,temperature,branch,barrier(units; document selectivities only),population(%),major(bool),ee(the signed ee of the result),ddG(units) andcurtin_hammett.
PES — data model (v4.2)
PES / reaction-profile data model.
Pure data + arithmetic; no I/O, no parsing, no global state. The model
is what the legacy parser and the YAML parser produce, and what the
output layer (Rich tables, JSON), plot_profile and the selectivity
code consume.
- Layers:
ThermoVector the thermo quantities of one structure or sum at one T ComputedEntry one parsed structure (QCData + ThermoOptions), evaluable
at any temperature (memoised)
ConformerSet one species, ≥1 conformers, with the ensemble rollups Point stoichiometric sum of species at one node of a pathway,
with a role (reactant | minimum | ts | product) and a display label
Edge a connection between two points (step | barrierless | none) Pathway ordered points + edges + a designated zero Series one line set on the axes / one column set in a table:
a quantity at a temperature, computed from the model or declared (typed-in levels that are never re-evaluated)
PESResult pathways + options + temperatures + series
Pathway.relative / Pathway.levels are the only places where
relative values are formed: every table, JSON block and figure reads
them, so they cannot disagree.
- class goodvibes.pes_model.ComputedEntry(qcdata: Any, options: Any, file: str = '', _cache: Any, ~typing.Any]=<factory>)[source]
Bases:
objectOne parsed structure plus the options it is evaluated with.
thermo(T)returns the ThermoVector at temperatureTand is memoised on the (temperature, options) pair, so a multi-temperature profile never re-parses a file and never evaluates the same temperature twice. When the options carry no explicit concentration the gas-phase standard state follows the temperature (P/RT), exactly as the CLI’s--tiscan does.- property base_temperature: float
- bbe(T: float | None = None, options: Any = None) Any[source]
The
calc_bbeat temperatureT(default: the base T).
- file: str = ''
- classmethod from_bbe(bbe: Any, file: str | None = None) ComputedEntry | None[source]
Wrap a
calc_bbebuilt bycalc_bbe.from_options(orcompute_thermo). Returns None when the object does not carry the parsed input and its options (stubs, or the legacy 15-argument constructor), in which case it can only be used at the temperature it was computed at.
- classmethod from_result(result: Any) ComputedEntry[source]
Wrap a
ThermoResultfromcompute_thermo.
- options: Any
- qcdata: Any
- thermo(T: float | None = None, options: Any = None) ThermoVector[source]
ThermoVector at temperature
T(default: the base T).
- class goodvibes.pes_model.ConformerSet(name: str, files: List[str], bbes: List[Any], entries: List[ComputedEntry] | None = None, weight_by: str = 'qh_gibbs')[source]
Bases:
objectA named species + ≥1 conformers.
bbesare thecalc_bbe(or calc_bbe-shaped) objects at the base temperature, parallel tofiles. When every one of them carries its parsed input and options (anything built throughcalc_bbe.from_options/compute_thermo),entriesis filled automatically and the set can be evaluated at any temperature; otherwise the rollups use the base-temperature values whateverTis passed (the pre-4.6 behaviour).Encapsulates the rollup math: pure Boltzmann average, lowest-only, or gconf-corrected (lowest + Boltzmann adjustment + mixing entropy), weighting by
weight_by(a registry quantity id; qh_gibbs by default).- property base_temperature: float | None
- bbes: List[Any]
- boltzmann_weighted(T: float) ThermoVector[source]
Pure Boltzmann-weighted average over conformers, weights from
weight_by(qh-G by default).
- dedup(e_cutoff: float = 0.05, ro_cutoff: float = 0.01, rmsd_cutoff: float | None = None) ConformerSet[source]
A new set without duplicate / enantiomeric conformers, using the same gates as
goodvibes.sort.deduplicate(energy in kcal/mol, rotational constants as a fraction, optional Cartesian RMSD) and the same convention as the CLI’s--dedup: of each flagged pair the later file is the redundant copy and is dropped. A single-conformer set is returned unchanged.
- ensemble_free_energy(T: float, quantity: str | None = None) float[source]
−RT ln Σᵢ exp(−Gᵢ/RT) in Hartree over the conformers, i.e. the conformationally averaged value of
quantity(default:weight_by) including the mixing entropy.For
quantity='qh_gibbs'this equalsgconf_corrected(T).qh_gibbswhenweight_byis qh_gibbs.
- entries: List[ComputedEntry] | None = None
- files: List[str]
- classmethod from_results(name: str, results: Sequence[Any], weight_by: str = 'qh_gibbs') ConformerSet[source]
Build a species from
ThermoResultobjects (compute_thermo).
- 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(T: float | None = None) ThermoVector[source]
Vector for the conformer with the lowest
weight_byvalue (qh-G by default) atT.
- lowest_index(T: float | None = None, quantity: str | None = None) int[source]
Index of the conformer with the lowest
quantityatT.
- name: str
- populations(T: float, quantity: str | None = None) List[float][source]
Boltzmann populations of the conformers at
T(sum to 1), weighted byquantity(default:weight_by).
- property recomputable: bool
True when the set can be evaluated at temperatures other than the one its conformers were computed at.
- rollup(T: float, gconf: bool = True, QH: bool = True, lowest_only: bool = False) ThermoVector[source]
The species-level vector under the given rollup mode: lowest_only → lowest conformer; else gconf → gconf_corrected; else the pure Boltzmann average.
- s_conf(T: float, quantity: str | None = None) float[source]
Conformational (mixing) entropy −R Σ pᵢ ln pᵢ in Hartree/K.
- vectors(T: float | None = None) List[ThermoVector][source]
Per-conformer ThermoVectors at
T(base temperature when None or when the set is not recomputable).
- weight_by: str = 'qh_gibbs'
- goodvibes.pes_model.EDGE_KINDS = ('step', 'barrierless', 'none')
Edge kinds.
stepis an ordinary connector,barrierlessa dotted one (association / dissociation without a located TS),noneno line.
- class goodvibes.pes_model.Edge(src: str, dst: str, kind: str = 'step')[source]
Bases:
objectA connection between two points of a pathway (by point label).
- dst: str
- kind: str = 'step'
- src: 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
- property rollup_kw: Dict[str, bool]
Keyword arguments for
Point.thermo/Pathway.relative.
- spc_used: bool = False
- 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>, series: ~typing.List[~goodvibes.pes_model.Series] = <factory>, order: ~typing.List[str] | None = None, source: ~typing.Any = None)[source]
Bases:
objectTop-level container: pathways + options + temperatures + series.
- default_series(quantity: str = 'qh_gibbs', temperatures: Sequence[float] | None = None) List[Series][source]
One computed series per temperature (the implicit series of a result that declares none).
- levels(series: Sequence[Series] | None = None) Dict[str, Dict[str, Dict[str, float | None]]][source]
{series id: {pathway name: {point label: value}}} in user units, for the given series (default:
self.seriesor the implicit qh-G series per temperature).
- merged_order() List[str][source]
Point labels in x order:
orderwhen set, else the union of the pathways’ point sequences, each pathway’s order preserved.
- options: PESOptions
- order: List[str] | None = None
- property recomputable: bool
True when every species can be evaluated at other temperatures.
- source: Any = None
- property temperature: float
The first (base) temperature.
- temperatures: List[float]
- goodvibes.pes_model.POINT_ROLES = ('reactant', 'minimum', 'ts', 'product')
Point roles. A transition state is drawn with its label above the bar and takes part in barrier annotations; minima/reactants/products are labelled below.
- class goodvibes.pes_model.Pathway(name: str, points: List[Point], zero: Point = None, edges: List[Edge] = None)[source]
Bases:
objectOrdered points + edges + a designated zero (defaults to points[0]).
edgesdefault to onestepedge between each pair of consecutive points; give an explicit list to mark barrierless steps or to omit a connector. Edges name points by label.- property computable: bool
True when every point (and the zero) has species, so the full thermochemistry table can be formed for this pathway.
- edge_kind(src: str, dst: str) str | None[source]
Kind of the edge src -> dst, or None when there is none.
- property labels: List[str]
- levels(T: float, quantity: str = 'qh_gibbs', gconf: bool = True, QH: bool = True, lowest_only: bool = False) Dict[str, float | None][source]
{point label: Δquantity relative to the zero, in Hartree} at
T.quantityis a registry id or alias (goodvibes.quantities); entropies are returned as T·ΔS. A value is None when the quantity is unavailable (single-point energy without –spc). A point without species (one that only carries declared values in a reaction-profile document) is absent from the result, and a pathway whose zero has no species gives an empty mapping.
- name: str
- 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.
This and
levels()are the only places relative values are formed; tables, JSON and figures all read them.
- class goodvibes.pes_model.Point(label: str, species: List[Tuple[int, ConformerSet]], role: str = 'minimum', display: str | None = None)[source]
Bases:
objectOne node of a pathway: a stoichiometric sum of ConformerSets.
labelis the point’s identity (the string written in the PES file, e.g. “2*A + B”);displayis what a figure prints for it (defaults to the label);roleis one ofPOINT_ROLES.- display: str | None = None
- property display_label: str
- classmethod from_label(label: str, species_map: dict, role: str = 'minimum', display: str | None = None) Point[source]
Build a Point from a label string and {name: ConformerSet} map.
- property id: str
- property is_ts: bool
- label: str
- role: str = 'minimum'
- 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.Series(id: str, label: str, quantity: str = 'qh_gibbs', temperature: float | None = None, method: str | None = None, style: Dict[str, ~typing.Any]=<factory>, levels: Dict[str, ~typing.Dict[str, float | None]] | None=None, declared: bool = False, units: str | None = None, uncertainty: Dict[str, ~typing.Dict[str, float]] | None=None, standard_state: Dict[str, ~typing.Any] | None=None, extensions: Dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectOne quantity at one temperature: a line set on the axes, a column set in a table.
A computed series (
declared=False) is evaluated from the model throughPathway.levels(temperature, quantity);temperatureNone means the result’s first temperature. Itslevels, when set, are a stored evaluation (a reaction-profile document written byProfile.evaluate) and are used as they are. A declared series (declared=True) carries typed-inlevels— literature values, a hand-entered table — as {pathway name: {point label: value}} inunits(default: the result’s units) and is never re-evaluated; a point missing from its levels is simply absent from the figure.styleholds matplotlib overrides (linestyle,color, …).- declared: bool = False
- classmethod declared_from(id: str, label: str, levels: Mapping[str, Mapping[str, float | None]], *, quantity: str = 'gibbs', temperature: float | None = None, method: str | None = None, units: str | None = None, style: Dict[str, Any] | None = None) Series[source]
A declared series from {pathway: {point label: value}}.
- evaluate(result: PESResult, pathway: Pathway) Dict[str, float | None][source]
{point label: value in the result’s units} for one pathway.
Stored levels (every declared series, and a computed series read from an evaluated document) are returned converted to the result’s units; otherwise a computed series goes through
Pathway.levels.
- evaluate_uncertainty(result: PESResult, pathway: Pathway) Dict[str, float][source]
{point label: uncertainty in the result’s units} for one pathway (empty when the series carries none).
- extensions: Dict[str, Any]
- id: str
- label: str
- levels: Dict[str, Dict[str, float | None]] | None = None
- method: str | None = None
- quantity: str = 'qh_gibbs'
- standard_state: Dict[str, Any] | None = None
- style: Dict[str, Any]
- temperature: float | None = None
- uncertainty: Dict[str, Dict[str, float]] | None = None
- units: str | None = None
- 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:
objectBundle 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, and every field but scf_energy is None for an energy-only structure (no frequencies); arithmetic propagates None (if either operand has a None field, the result does).
- enthalpy: float
- entropy: float
- get(quantity, T: float | None = None) float | None[source]
Value of a registry quantity in Hartree (see goodvibes.quantities).
Entropy quantities are returned as T·S and therefore need
T.e_zpeis (sp_energy if present else scf_energy) + zpe, matching the table convention that H and G are SPC-substituted when –spc is used. Returns None where the underlying value is None (no SPC).
- 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.merge_point_order(sequences: Sequence[Sequence[str]]) List[str][source]
Merge several ordered label sequences into one order that keeps every sequence’s own order. A label new to the merged list is inserted just before the earliest already-placed label that follows it in its own sequence, or appended when none does; so
[R, Int1, TS1, P]and[R, TS1, P]merge to the first, and two branches[R, TS_R, P_R]/[R, TS_S, P_S]keep each branch contiguous.
- 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:
objectFormat-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.
- 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.
Three formats are accepted: a reaction-profile document (
schema: reaction-profile/1.x, YAML or JSON; seegoodvibes.profile), the v2 PES YAML, and the legacy--- # PEStext (deprecated; emits a DeprecationWarning). The returned result carries the document it was read from asresult.source(agoodvibes.profile.Profile; the two older formats are upgraded).
- 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.
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.
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 (goodvibes.profile, plot_profile).
- class goodvibes.pes.get_pes(file, thermo_data, temperature, gconf, QH)[source]
Bases:
objectBack-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’.
Plots (matplotlib, goodvibes[plot])
Visualization for GoodVibes.
Pure plotting layer that renders the v4.2+ structured types (PESResult, SelectivityResult, ThermoResult) to matplotlib axes. matplotlib is an optional dependency; install with pip install goodvibes[plot] (or pip install matplotlib directly).
All functions accept an optional ax=None; when None, they create a fresh plt.subplots() figure and return the axes. The plt import is deferred so just import goodvibes.plot doesn’t fail when matplotlib is missing — only the call-site fails, with a clear message.
- Public API:
plot_profile(pes_result, series=…, …) — reaction profile (ProfileAxes) STYLE_PRESETS / resolve_preset(name) — figure presets for plot_profile plot_pes(pes_result, ax=None, **kw) — 4.2-4.5 shim over plot_profile plot_selectivity_strip(selectivity,
thermo_lookup, ax=None) — per-species scatter
plot_boltzmann_histogram(conformers, …) — population bars plot_temperature_scan(conformers or profile, …) — thermo or levels vs T
- class goodvibes.plot.ProfileAxes(figure: ~typing.Any, axes: ~typing.List[~typing.Any], levels: ~typing.Dict[str, ~typing.Dict[str, ~typing.Dict[str, float | None]]], units: str, order: ~typing.List[str], x: ~typing.Dict[str, float], series: ~typing.List[~typing.Any], pathways: ~typing.List[~typing.Any], colors: ~typing.Dict[str, ~typing.Any], linestyles: ~typing.Dict[str, str], layout: str = 'overlay', uncertainty: ~typing.Dict[str, ~typing.Dict[str, ~typing.Dict[str, float]]] = <factory>, preset: str | None = None, rc: ~typing.Dict[str, ~typing.Any] = <factory>, label_size: ~typing.Any = 'x-small', pes_result: ~typing.Any = None, _ids: ~typing.Any = <factory>)[source]
Bases:
objectWhat
plot_profiledrew, with the numbers behind it.levelsis {series id: {pathway name: {point label: value}}} inunits: the same evaluation the bars were drawn from, so a table written from it cannot disagree with the figure;uncertaintyhas the same shape for the series that carry one.orderis the merged x order (point labels) andxmaps a label to its position.element_idsmaps each drawn element’s SVG id to what it shows.- annotate_barrier(pathway, src: str, dst: str, series=None, *, fmt: str = '{:+.1f}', offset: float = 0.3, color=None, ax=None)[source]
Mark the difference dst − src on one pathway with a vertical double-headed arrow beside
dstand the value inunits. Returns the matplotlib annotation.
- property ax
The (first) matplotlib Axes.
- axes: List[Any]
- colors: Dict[str, Any]
- property element_ids: Dict[str, Dict[str, str]]
- figure: Any
- label_size: Any = 'x-small'
- layout: str = 'overlay'
- level(pathway, point: str, series=None) float | None[source]
A drawn level in
units.seriesdefaults to the first.
- levels: Dict[str, Dict[str, Dict[str, float | None]]]
- linestyles: Dict[str, str]
- order: List[str]
- pathways: List[Any]
- pes_result: Any = None
- preset: str | None = None
- rc: Dict[str, Any]
- save(*paths: str, dpi: int = 200, bbox_inches: str = 'tight', embed: bool = True, **kw) None[source]
Write the figure to each path (format by extension).
An SVG keeps its text as text (
svg.fonttype = 'none'), gives every bar, connector, label and error bar anid(seeelement_ids) and, unlessembed=False, carries the reaction-profile document of what was drawn in a<metadata>element, sogoodvibes.load_profile("figure.svg")reads the numbers back. Output is deterministic (no date, fixed id salt).
- series: List[Any]
- to_document() dict[source]
The reaction-profile document of what was drawn: the drawn series with the drawn levels (and uncertainties), in
units. Needs thePESResultthe figure came from (plot_profilekeeps it).
- uncertainty: Dict[str, Dict[str, Dict[str, float]]]
- units: str
- x: Dict[str, float]
- goodvibes.plot.STYLE_PRESETS: Dict[str, StylePreset | None] = {'double-column': StylePreset(name='double-column', figsize=(7.0, 3.2), panel_height=2.4, font_size=8, label_size=7, bar_width=1.5, connector_width=0.9, axes_width=0.8, marker_size=4), 'none': None, 'single-column': StylePreset(name='single-column', figsize=(3.35, 2.6), panel_height=1.9, font_size=7, label_size=6, bar_width=1.2, connector_width=0.7, axes_width=0.6, marker_size=3), 'slide': StylePreset(name='slide', figsize=(10.0, 5.6), panel_height=3.2, font_size=16, label_size=14, bar_width=3.0, connector_width=1.8, axes_width=1.2, marker_size=7)}
Named styles for
style.preset(reaction-profile documents),plot_profile(preset=...)andgoodvibes-profile plot --preset. ‘none’ keeps matplotlib’s defaults and a figure sized to the profile.
- goodvibes.plot.SVG_METADATA_ID = 'goodvibes-reaction-profile'
id of the
<metadata>element that carries a figure’s document.
- class goodvibes.plot.StylePreset(name: str, figsize: Tuple[float, float], panel_height: float, font_size: float, label_size: float, bar_width: float, connector_width: float, axes_width: float, marker_size: float)[source]
Bases:
objectFigure size, font sizes (pt) and line widths (pt) for one target.
Applied inside a
matplotlib.rc_contextfor the figure only, never set globally.figsizeis the overlay figure in inches; withlayout='panels'each panel ispanel_heighttall.- axes_width: float
- bar_width: float
- connector_width: float
- figsize: Tuple[float, float]
- font_size: float
- label_size: float
- marker_size: float
- name: str
- panel_height: float
- property rc: Dict[str, Any]
- goodvibes.plot.embed_svg_metadata(svg: str, payload: Mapping[str, Any]) str[source]
Insert
payload(JSON) as a<metadata id="goodvibes-reaction-profile">element right after the opening<svg>tag.
- goodvibes.plot.plot_boltzmann_histogram(source: Any, *, temperature: float = 298.15, quantity: str | None = None, top: int | None = None, sort: bool = True, ax=None, title: str | None = None)[source]
Bar chart of conformer Boltzmann populations.
- Parameters:
source – a
ConformerSet, a sequence ofThermoResult(one species’ conformers), or a mapping{group: ConformerSet or results}. With a mapping the populations are taken over all conformers together (for example the transition states of competing pathways, so the bars show where the selectivity comes from); bars are coloured by group and the legend gives each group’s total population.temperature – K; each conformer is re-evaluated at it when the set carries its parsed data.
quantity – the registry quantity weighted (default the set’s
weight_by,qh_gibbs;electronicfor energy-only ensembles).top – draw only the
topmost populated conformers, plus one “other” bar for the rest.sort – most populated first (default); False keeps input order.
ax – optional matplotlib Axes.
title – figure title; generated when None.
- Returns:
The matplotlib Axes. Each bar’s
gidispop-<group>-<name>.
- goodvibes.plot.plot_pes(pes_result: Any, *, ax=None, pathway_index: int | Sequence[int] | None = None, connector_style: str = 'bezier', colors: Sequence[str] | None = None, show_conformers: bool = False, thermo_lookup: Mapping[str, float] | Callable[[str], float] | None = None, title: str | None = None, label_points: bool = False, quantity: str = 'qh_gibbs')[source]
Plot one or more pathways from a PESResult as a reaction profile.
A thin wrapper over
plot_profile()kept for the 4.2–4.5 API; it returns the matplotlib Axes. New code should callplot_profile, which also returns the drawn levels and supports several series (temperatures, quantities, declared values) on one axes.- Parameters:
pes_result – a PESResult from goodvibes.pes_loader.load_pes.
ax – optional matplotlib Axes; new figure if None.
pathway_index – which pathway(s) to draw (None → all, an int, or a list of ints).
connector_style – ‘bezier’ (default) or ‘linear’.
colors – per-pathway colors in pathway order.
show_conformers – scatter individual conformer values around each level.
thermo_lookup – deprecated and ignored (conformer values are read from the PESResult); accepted for backwards compatibility.
title – figure title; defaults to the pathway names + temperature.
label_points – annotate each point’s value next to its bar.
quantity – which relative quantity to draw, by registry id or alias (see goodvibes.quantities); the y-label follows the choice.
- Returns:
The matplotlib Axes the profile(s) were drawn on.
- goodvibes.plot.plot_profile(pes_result: Any, *, series=None, quantity: str | None = None, temperatures: Sequence[float] | None = None, pathways=None, layout: str = 'overlay', ax=None, style: Mapping[str, Any] | None = None, colors=None, show_conformers: bool = False, label_points: bool = False, title: str | None = None, order: Sequence[str] | None = None, preset: str | None = None, uncertainty: bool = True) ProfileAxes[source]
Draw a reaction profile from a
PESResult.Every pathway is drawn on a shared x axis whose order is the merge of the pathways’ point sequences (
PESResult.merged_order), so pathways of different lengths, or branches sharing a reactant, line up by point label. Colour encodes the pathway; linestyle encodes the series (a quantity at a temperature, computed from the model or declared). Transition-state points (role='ts') carry their value label above the bar, other points below; abarrierlessedge is a dotted connector; an edge of kindnonedraws no connector.- Parameters:
pes_result – a PESResult (
goodvibes.load_pesor built by hand).series – what to draw. A
Series/ list ofSeries(computed or declared), or series ids frompes_result.series. Default:pes_result.serieswhen it declares any, else one computed series ofquantityper temperature.quantity – registry id or alias for the implicit series (default ‘qh_gibbs’); the y-label follows it.
temperatures – temperatures of the implicit series (default: the result’s); several give a temperature overlay.
pathways – which pathways to draw (names, indices or objects); default all.
layout – ‘overlay’ (all pathways on one axes) or ‘panels’ (one axes per pathway, shared y).
ax – an Axes to draw on (overlay only); a new figure otherwise.
style – {‘connector’: ‘bezier’ | ‘linear’ | ‘step’, ‘bar_half’: float, ‘decimals’: int, ‘figsize’: (w, h), ‘linestyles’: […], ‘preset’: name}.
colors – per-pathway colours, a sequence in pathway order or a {name: colour} mapping. Default: black for one pathway, the matplotlib cycle otherwise.
show_conformers – scatter each conformer at level + (conformer − species rollup) in the plotted quantity (computed series only).
label_points – print each level’s value next to its bar.
title – figure title (default: pathway names, and the temperature when there is one).
order – explicit x order of point labels (overrides the result’s).
preset – a
STYLE_PRESETSname (‘single-column’, ‘double-column’, ‘slide’; defaultstyle['preset']or ‘none’): figure size, fonts and line widths for that target, applied to this figure only. An explicitstyle['figsize']still wins.uncertainty – draw each series’
uncertaintyas an error bar (± the value) on its levels.
- Returns:
A
ProfileAxeswith the figure, axes and the drawn levels.
- goodvibes.plot.plot_selectivity_strip(selectivity: Any, thermo_lookup: Mapping[str, float] | Callable[[str], float], *, ax=None, units: str = 'kcal/mol', title: str | None = None, jitter: float = 0.04, seed: int = 0)[source]
Per-species strip plot of conformer ΔG values.
Each species is one column; conformer ΔG values (relative to the lowest across all species) are scattered as dots.
- Parameters:
selectivity – a SelectivityResult (from goodvibes.selectivity.compute_selectivity) — provides the label order, populations, and files_per_label.
thermo_lookup – either a {file_path: qh_gibbs_free_energy} mapping (Hartree) or a callable that takes a path and returns the same. Used to read the per-conformer ΔG.
ax – optional matplotlib Axes. New figure created if None.
units – ‘kcal/mol’ (default), ‘kJ/mol’, ‘eV’ or ‘hartree’.
title – figure title; auto-generated from the result’s temperature and key when None.
jitter – horizontal spread of conformer dots within each species column (fraction of column width).
seed – RNG seed for the jitter (deterministic plots).
- Returns:
The matplotlib Axes the strip plot was drawn on.
- goodvibes.plot.plot_temperature_scan(source: Any, temperatures: Sequence[float] | None = None, *, quantities: Sequence[str] | None = None, points: Sequence[str] | None = None, pathway: str | None = None, units: str = 'kcal/mol', ax=None, title: str | None = None)[source]
Thermochemistry against temperature.
Two kinds of
source:a
ConformerSet(or a sequence ofThermoResult, one species’ conformers) andtemperatures: one line per quantity (default Δqh-G, Δqh-H and T·Δqh-S) of the conformer ensemble (the gconf rollup), relative to its value at the first temperature;a reaction-profile
Profile: one line per point (points, default every point ofpathwaybut its zero;pathwaydefaults to the first) giving its level against temperature over the document’s series of one quantity (quantities[0], default that of the first series with a temperature). A point is read onpathwaywhen it is there, else on the first pathway that holds it (each relative to that pathway’s zero). Withtemperaturesthe document is evaluated at them first (it needs embedded conformers).
- Returns:
The matplotlib Axes; each line’s
gidisscan-<quantity>orscan-<point>.
- goodvibes.plot.read_svg_metadata(svg: str) dict | None[source]
The payload
ProfileAxes.saveembedded in an SVG, or None.
- goodvibes.plot.resolve_preset(name: str | None) StylePreset | None[source]
The StylePreset called
name(None or ‘none’ for the defaults).
Structured-output schema
Stable schema definitions for GoodVibes structured output.
The shape of the JSON written by –json / goodvibes.api.write_json_results is now documented and stable. This module is the single source of truth for that schema:
SCHEMA_VERSION — the current shipping version.
SCHEMA_MIN_COMPATIBLE — readers tolerate any version ≥ this.
- validate(payload) — runtime sanity check; raises ValueError
with a clear message on shape/version mismatches.
- TypedDicts (Payload, ResultEntry, ThermoBlock, …) — give
static checkers and editors completion when working with parsed payloads.
The JSON file is also accepted by –import as a cache (skips re-parsing the underlying QC outputs); the same schema therefore plays two roles.
## Version policy (v1.0+)
- Major bump (X.0) breaking change — fields removed or renamed,
or value types changed.
- Minor bump (1.X) backwards-compatible addition — new fields,
new optional blocks, new enum values.
Patch bump (1.0.X) no-op (we don’t bump for typo fixes etc.).
Readers SHOULD accept any minor version ≥ their min and ignore unknown fields. Writers SHOULD emit the version they were built against.
- class goodvibes.schema.PESBlock[source]
Bases:
TypedDict- pathways: List[PESPathway]
- class goodvibes.schema.PESPathway[source]
Bases:
TypedDict- name: str
- temperature: float
- units: str
- zero_label: str
- class goodvibes.schema.PESPoint[source]
Bases:
TypedDict- label: str
- relative: dict
- species: List[dict]
- class goodvibes.schema.Payload[source]
Bases:
TypedDictThe top-level JSON document GoodVibes writes.
- generated_at: str
- goodvibes_version: str
- options: dict
- profile: dict
- results: List[ResultEntry]
- schema_version: str
- selectivity: SelectivityBlock
- selectivity_lowest: SelectivityBlock
- class goodvibes.schema.ResultEntry[source]
Bases:
TypedDictOne per input QC file.
- boltzmann_factor: float | None
- file: str
- media_conc: float | None
- name: str
- qcdata: dict | None
- thermo: ThermoBlock
- goodvibes.schema.SCHEMA_MIN_COMPATIBLE = '1.0'
The oldest schema a reader in this build will accept. Bump only on a true breaking change (e.g. v2.0).
- goodvibes.schema.SCHEMA_VERSION = '1.1'
The schema version this build of GoodVibes writes. 1.1 adds the optional top-level
profileblock (a reaction-profile document, see goodvibes.profile); a 1.0 reader ignores it.
- class goodvibes.schema.SelectivityBlock[source]
Bases:
TypedDict- key: str
- labels: List[str]
- results: List[SelectivityResult]
- class goodvibes.schema.SelectivityResult[source]
Bases:
TypedDictOne per temperature inside selectivity/selectivity_lowest.
- ddG: float | None
- ee: float | None
- ee_signed: float | None
- ensemble_energies: dict
- files_per_label: dict
- major: str
- populations: dict
- preferred: str
- ratio: float | None
- raw_boltzmann: dict
- temperature: float
- class goodvibes.schema.ThermoBlock[source]
Bases:
TypedDictPer-file thermochemical numbers in atomic units (Hartree, Hartree/K).
Entries are Optional[float] — None when the QC output didn’t expose that quantity (e.g. sp_energy is None unless –spc was used, frequencies are absent for SP-only outputs).
- applied_freq_scale_factor: float | None
- enthalpy: float | None
- entropy: float | None
- frequency_wn: List[float] | None
- gibbs_free_energy: float | None
- im_frequency_wn: List[float] | None
- inverted_freqs: List[float] | None
- job_type: str | None
- linear_mol: bool | None
- multiplicity: int | None
- point_group: str | None
- 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
- zero_point_corr: float | None
- zpe: float | None
- goodvibes.schema.validate(payload: Any) None[source]
Light runtime sanity check on a loaded payload.
Verifies the document has a schema_version that this build can read and that the required top-level fields are present. Does NOT deeply type-check every field — that’s the consumer’s responsibility via the TypedDicts above.
- Raises:
ValueError – with a one-line, actionable message on any failure.
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, groups=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.
The energy and rotational-constant gates cannot tell enantiomers apart, so when structures belong to labelled species (
--label R=... S=...) passgroupsand only pairs inside the same group are compared: the R and S transition states of a selectivity calculation are then never collapsed into one. Files in no group are compared only with each other.- 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.
groups (Mapping[str, Iterable[str]] or None) – label → files. None (default) compares every pair.
- 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 canonical_solvent(name) – the canonical name of a solvent alias, or None
- goodvibes.media.canonical_solvent(name)[source]
The canonical name of the solvent that
nameis an alias of (case-insensitive), or None when it is not a known alias.
- goodvibes.media.compute_media_conc(media, file)[source]
Compute the neat-solvent molar concentration when the output file corresponds to the specified solvent.
The file’s name (without extension) and media match when they are aliases of the same solvent, so
--media h2oapplies towater.logas well as toH2O.log.- 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.
The functional name has aliases resolved and case normalized. The basis set is normalized for the two spelling variations seen across programs: Pople star shorthand is expanded (“6-31+G**” == “6-31+G(d,p)”, “6-31G*” == “6-31G(d)”) and hyphens are dropped (“def2-TZVP” == “def2TZVP”, “ma-TZVP” == “maTZVP”). Database keys pass through the same normalization, so lookups are insensitive to these variations.
- Parameters:
level_basis (str) – Level and optional basis separated by ‘/’, e.g. “b3lyp/6-31g*”.
- Returns:
Canonicalized “FUNCTIONAL” or “FUNCTIONAL/BASIS” string.
- 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_2beforeconf_10(and beforeconf_a).Splits on digit runs and treats them as integers so ordinary string comparison won’t put
conf_10betweenconf_1andconf_2. Comparison uses the basename so files from different directories with the same name don’t separate solely by directory path.
- goodvibes.utils.parse_temperature_interval(spec)[source]
Turn a
--tispecification into the list of temperatures to scan.specis"start,end"or"start,end,step"(kelvin). With two values the range is divided into ten steps. Values are floats: a step of 0.5 K or a start of 298.15 K is honoured rather than truncated to integers (which used to turn--ti 200,201,0.5into a range() error and--ti 298.15,398.15,50into 298, 348, 398). The end temperature is included when it lies on the grid (within 1e-9 K).- Returns:
temperatures in ascending order.
- Return type:
list[float]
- Raises:
ValueError – on a malformed spec, a non-positive step or end < start.
- 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 and reaction profiles from QC and MLIP outputs.
Programmatic API:
from goodvibes import compute_thermo, compute_batch, ThermoResult r = compute_thermo(“file.log”, QH=True, spc=”TZ”) print(r.qh_gibbs_free_energy)
# file-free (ASE / MLIP) from goodvibes import QCData qc = QCData.from_vibrations(atoms, vib.get_vibrations(), atoms.get_potential_energy()) r = compute_thermo(qcdata=qc)
# reaction profiles from goodvibes import load_pes, plot_profile pes = load_pes(“profile.yaml”, {r.file: r.bbe for r in results}) plot_profile(pes, temperatures=[298.15, 373.15]).save(“profile.svg”)
# reaction-profile documents (reaction-profile/1.x) from goodvibes import load_profile doc = load_profile(“profile.yaml”).evaluate(results, with_conformers=True) doc.dump(“profile.json”); doc.plot().save(“profile.svg”)
The calc_bbe class remains the canonical engine; compute_thermo is just a kwargs façade that returns a structured ThermoResult.
- class goodvibes.ComputedEntry(qcdata: Any, options: Any, file: str = '', _cache: Any, ~typing.Any]=<factory>)[source]
Bases:
objectOne parsed structure plus the options it is evaluated with.
thermo(T)returns the ThermoVector at temperatureTand is memoised on the (temperature, options) pair, so a multi-temperature profile never re-parses a file and never evaluates the same temperature twice. When the options carry no explicit concentration the gas-phase standard state follows the temperature (P/RT), exactly as the CLI’s--tiscan does.- property base_temperature: float
- bbe(T: float | None = None, options: Any = None) Any[source]
The
calc_bbeat temperatureT(default: the base T).
- file: str = ''
- classmethod from_bbe(bbe: Any, file: str | None = None) ComputedEntry | None[source]
Wrap a
calc_bbebuilt bycalc_bbe.from_options(orcompute_thermo). Returns None when the object does not carry the parsed input and its options (stubs, or the legacy 15-argument constructor), in which case it can only be used at the temperature it was computed at.
- classmethod from_result(result: Any) ComputedEntry[source]
Wrap a
ThermoResultfromcompute_thermo.
- options: Any
- qcdata: Any
- thermo(T: float | None = None, options: Any = None) ThermoVector[source]
ThermoVector at temperature
T(default: the base T).
- class goodvibes.ConformerSet(name: str, files: List[str], bbes: List[Any], entries: List[ComputedEntry] | None = None, weight_by: str = 'qh_gibbs')[source]
Bases:
objectA named species + ≥1 conformers.
bbesare thecalc_bbe(or calc_bbe-shaped) objects at the base temperature, parallel tofiles. When every one of them carries its parsed input and options (anything built throughcalc_bbe.from_options/compute_thermo),entriesis filled automatically and the set can be evaluated at any temperature; otherwise the rollups use the base-temperature values whateverTis passed (the pre-4.6 behaviour).Encapsulates the rollup math: pure Boltzmann average, lowest-only, or gconf-corrected (lowest + Boltzmann adjustment + mixing entropy), weighting by
weight_by(a registry quantity id; qh_gibbs by default).- property base_temperature: float | None
- bbes: List[Any]
- boltzmann_weighted(T: float) ThermoVector[source]
Pure Boltzmann-weighted average over conformers, weights from
weight_by(qh-G by default).
- dedup(e_cutoff: float = 0.05, ro_cutoff: float = 0.01, rmsd_cutoff: float | None = None) ConformerSet[source]
A new set without duplicate / enantiomeric conformers, using the same gates as
goodvibes.sort.deduplicate(energy in kcal/mol, rotational constants as a fraction, optional Cartesian RMSD) and the same convention as the CLI’s--dedup: of each flagged pair the later file is the redundant copy and is dropped. A single-conformer set is returned unchanged.
- ensemble_free_energy(T: float, quantity: str | None = None) float[source]
−RT ln Σᵢ exp(−Gᵢ/RT) in Hartree over the conformers, i.e. the conformationally averaged value of
quantity(default:weight_by) including the mixing entropy.For
quantity='qh_gibbs'this equalsgconf_corrected(T).qh_gibbswhenweight_byis qh_gibbs.
- entries: List[ComputedEntry] | None = None
- files: List[str]
- classmethod from_results(name: str, results: Sequence[Any], weight_by: str = 'qh_gibbs') ConformerSet[source]
Build a species from
ThermoResultobjects (compute_thermo).
- 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(T: float | None = None) ThermoVector[source]
Vector for the conformer with the lowest
weight_byvalue (qh-G by default) atT.
- lowest_index(T: float | None = None, quantity: str | None = None) int[source]
Index of the conformer with the lowest
quantityatT.
- name: str
- populations(T: float, quantity: str | None = None) List[float][source]
Boltzmann populations of the conformers at
T(sum to 1), weighted byquantity(default:weight_by).
- property recomputable: bool
True when the set can be evaluated at temperatures other than the one its conformers were computed at.
- rollup(T: float, gconf: bool = True, QH: bool = True, lowest_only: bool = False) ThermoVector[source]
The species-level vector under the given rollup mode: lowest_only → lowest conformer; else gconf → gconf_corrected; else the pure Boltzmann average.
- s_conf(T: float, quantity: str | None = None) float[source]
Conformational (mixing) entropy −R Σ pᵢ ln pᵢ in Hartree/K.
- vectors(T: float | None = None) List[ThermoVector][source]
Per-conformer ThermoVectors at
T(base temperature when None or when the set is not recomputable).
- weight_by: str = 'qh_gibbs'
- class goodvibes.Edge(src: str, dst: str, kind: str = 'step')[source]
Bases:
objectA connection between two points of a pathway (by point label).
- dst: str
- kind: str = 'step'
- src: str
- class goodvibes.EnergySpan(tdts: str, tdi: str, span: float, reaction_energy: float, tof: float, tof_span: float, temperature: float, units: str, tdts_after_tdi: bool, control: Dict[str, float]=<factory>)[source]
Bases:
objectThe energy-span analysis of one catalytic cycle.
span(δE) andreaction_energy(ΔG_r) are inunits;tofis the exact energy-span TOF andtof_spanits approximationkB T / h exp(-δE / RT)(both s⁻¹; zero or negative when the cycle is not exergonic).controlmaps every TS and intermediate to its degree of TOF control (the TSs sum to 1, and so do the intermediates).- control: Dict[str, float]
- reaction_energy: float
- span: float
- tdi: str
- tdts: str
- tdts_after_tdi: bool
- temperature: float
- tof: float
- tof_span: float
- units: str
- exception goodvibes.MissingSinglePointError[source]
Bases:
ValueErrorRaised under strict_spc when a requested single-point energy is missing or unparseable instead of silently using the frequency-level energy.
- class goodvibes.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
- property rollup_kw: Dict[str, bool]
Keyword arguments for
Point.thermo/Pathway.relative.
- spc_used: bool = False
- units: str = 'kcal/mol'
- class goodvibes.PESResult(pathways: ~typing.List[~goodvibes.pes_model.Pathway], options: ~goodvibes.pes_model.PESOptions, temperatures: ~typing.List[float] = <factory>, series: ~typing.List[~goodvibes.pes_model.Series] = <factory>, order: ~typing.List[str] | None = None, source: ~typing.Any = None)[source]
Bases:
objectTop-level container: pathways + options + temperatures + series.
- default_series(quantity: str = 'qh_gibbs', temperatures: Sequence[float] | None = None) List[Series][source]
One computed series per temperature (the implicit series of a result that declares none).
- levels(series: Sequence[Series] | None = None) Dict[str, Dict[str, Dict[str, float | None]]][source]
{series id: {pathway name: {point label: value}}} in user units, for the given series (default:
self.seriesor the implicit qh-G series per temperature).
- merged_order() List[str][source]
Point labels in x order:
orderwhen set, else the union of the pathways’ point sequences, each pathway’s order preserved.
- options: PESOptions
- order: List[str] | None = None
- property recomputable: bool
True when every species can be evaluated at other temperatures.
- source: Any = None
- property temperature: float
The first (base) temperature.
- temperatures: List[float]
- class goodvibes.Pathway(name: str, points: List[Point], zero: Point = None, edges: List[Edge] = None)[source]
Bases:
objectOrdered points + edges + a designated zero (defaults to points[0]).
edgesdefault to onestepedge between each pair of consecutive points; give an explicit list to mark barrierless steps or to omit a connector. Edges name points by label.- property computable: bool
True when every point (and the zero) has species, so the full thermochemistry table can be formed for this pathway.
- edge_kind(src: str, dst: str) str | None[source]
Kind of the edge src -> dst, or None when there is none.
- property labels: List[str]
- levels(T: float, quantity: str = 'qh_gibbs', gconf: bool = True, QH: bool = True, lowest_only: bool = False) Dict[str, float | None][source]
{point label: Δquantity relative to the zero, in Hartree} at
T.quantityis a registry id or alias (goodvibes.quantities); entropies are returned as T·ΔS. A value is None when the quantity is unavailable (single-point energy without –spc). A point without species (one that only carries declared values in a reaction-profile document) is absent from the result, and a pathway whose zero has no species gives an empty mapping.
- name: str
- 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.
This and
levels()are the only places relative values are formed; tables, JSON and figures all read them.
- class goodvibes.Point(label: str, species: List[Tuple[int, ConformerSet]], role: str = 'minimum', display: str | None = None)[source]
Bases:
objectOne node of a pathway: a stoichiometric sum of ConformerSets.
labelis the point’s identity (the string written in the PES file, e.g. “2*A + B”);displayis what a figure prints for it (defaults to the label);roleis one ofPOINT_ROLES.- display: str | None = None
- property display_label: str
- classmethod from_label(label: str, species_map: dict, role: str = 'minimum', display: str | None = None) Point[source]
Build a Point from a label string and {name: ConformerSet} map.
- property id: str
- property is_ts: bool
- label: str
- role: str = 'minimum'
- 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.Profile(*, title=None, description=None, units='kcal/mol', ensemble='ideal-gas', default_temperature=298.15, species=None, points=None, pathways=None, order=None, methods=None, series=None, annotations=None, style=None, provenance=None, namespace=None, extensions=None, selectivity=None)[source]
Bases:
objectA reaction-profile document (see the module docstring).
Build one with
load_profile(),from_dict(),from_table()(CSV of relative energies) orfrom_pes_result(); evaluate its computed series withevaluate(); write it withdump(); draw it withplot(); tabulate it withto_rows()/to_dataframe().- diff(other: Profile, *, tolerance: float = 0.01, units: str | None = None, series: Sequence[str] | None = None) ProfileDiff[source]
How
otherdiffers from this document.Compares the document fields (title, units, ensemble, default_temperature), points, pathways, series (matched by id: added, removed, changed metadata, and every level and uncertainty, which differ when they are more than
toleranceapart, inunits, default this document’s; the other document’s values are converted), selectivity blocks and annotations. Provenance, style and the GoodVibes namespace (sources, embedded conformers) are not compared.seriesrestricts the series compared.
- dump(path, *, include_conformers: bool = True, indent: int | None = 2) None[source]
Write the explicit form to
path(.json, .yaml or .yml).indent=Nonewrites compact JSON (a document with embedded conformers is several times smaller).
- energy_span(pathway: str | None = None, series: str | None = None, *, reaction_energy: float | None = None)[source]
goodvibes.kinetics.energy_spanof a pathway read as one catalytic turnover: its points in order, the last being the regenerated catalyst with the products (unlessreaction_energyis given), TSs by their role.
- evaluate(thermo_data=None, *, temperatures: Sequence[float] | None = None, options: PESOptions | None = None, default_series: Sequence[Series] | None = None, with_conformers: bool = False, invocation: str | None = None, base_temperature: float | None = None) Profile[source]
A new document with every computed series evaluated.
The data is
thermo_data({file: calc_bbe} orThermoResultlist) resolved throughgoodvibes.sources, or, when it is None, the embeddedgoodvibes.conformers.temperaturesturns each computed series into one series per temperature (ids suffixed@<T>K). A document without computed series getsdefault_series(default: Δqh-G(T) atdefault_temperature).optionsoverrides the rollup (the CLI passes its flags this way).base_temperaturereplacesdefault_temperaturefor computed series that give no temperature (the CLI passes its run temperature, so the document matches the tables and thepesblock of the same run).with_conformersembeds every structure’s parsed data and thermochemistry options so the document can be re-evaluated, at any temperature, with no output files. Declared series are copied unchanged. Adds aprovenanceblock.
- evaluate_selectivity(block: str | None = None, *, series: str | None = None, warn: bool = True) List[SelectivityResult][source]
The selectivity of each
selectivityblock, from series levels.For every block (or only
block, an id) and every series that has levels for its reference and branches (or only the block’sseries, orserieshere), each branch’s barrier islevel(branch) - level(reference)on a pathway holding both, and its populationexp(-barrier / RT)normalised over the branches, at the series’ temperature (default_temperaturewhen it has none). Declared series work as well as computed ones; computed ones need levels (seeevaluate()).The result follows the SelectivityResult conventions (labels are the branch point ids in the order listed, so
ee_signedis positive when the first branch is the major one) and recordsbarriersandensemble_energiesrelative to the reference, in Hartree.curtin_hammettis ‘violated’ when a branch lies at or below the reference, when a point between the reference and a branch lies below the reference (a deeper resting state), or when theinterconversionpoint’s barrier is not below the lowest branch barrier; ‘satisfied’ when an interconversion barrier is given and lower; ‘assumed’ otherwise. Each violation, and a branch that is not a transition state, is listed inwarningsand raised as aSelectivityWarningwhenwarn.Raises ProfileError for an unknown block or series, or when no series has levels for a block.
- classmethod from_dict(data: Mapping, *, strict: bool = False) Profile[source]
Parse and validate a document mapping (explicit form or the GoodVibes v2 PES YAML shorthand). Raises ProfileError listing every problem; warnings are kept on
.warningsand emitted asProfileWarning.
- classmethod from_figure(figure) Profile[source]
The document of what a
ProfileAxesdrew: its points and pathways, and the drawn series with the drawn levels (and uncertainties) in the figure’s units. Embedded conformers are dropped; a computed series keeps its temperature and method.
- classmethod from_pes_result(pes_result: PESResult, *, quantity: str = 'qh_gibbs', title: str | None = None, with_conformers: bool = False, invocation: str | None = None) Profile[source]
An evaluated document for a PESResult built in Python (or loaded with
load_pes): points, pathways, the files of every species (asgoodvibes.sources) and one computed series ofquantityper temperature of the result (or the result’s own series).
- classmethod from_spec(spec, kind: str = 'legacy') Profile[source]
Upgrade a PESSpec (
kind‘legacy’ for--- # PEStext, ‘v2’ for the GoodVibes v2 PES YAML).
- classmethod from_table(source, *, layout: str = 'auto', units: str = 'kcal/mol', quantity: str = 'gibbs', temperature: float | None = 298.15, method: str | None = None, series_id: str = 'table', label: str | None = None, title: str | None = None) Profile[source]
A declared-only profile from a CSV of relative energies.
sourceis a path, a file object, CSV text or a list of row mappings.layout='wide': apointcolumn, optionalroleanddisplaycolumns, then one column per pathway (a column namedpathway:seriesgives several series); a point belongs to a pathway when its cell is not empty, in row order, and the first such point is the pathway’s zero.layout='long': columnspathway,point,valueand optionallyseries,label,quantity,temperature,units,method,role,display,uncertainty.'auto'picks long when the header haspathwayandvalue. Values are inunits(or a long row’s own units) and are relative to the pathway zero; an empty cell means the point is not on that pathway,null/none/—an unknown value.
- levels() Dict[str, Dict[str, Dict[str, float | None]]][source]
{series id: {pathway: {point: value in the document units}}} for every series with levels (declared, or evaluated).
- method_thermo(method: str | None) Dict[str, Any][source]
The thermochemistry options
methodsets for itself undergoodvibes.thermo.by_method(empty when it sets none). They replace the options each of its structures was computed with, so a DFT and an MLIP method can use different scaling and qRRHO settings in one evaluation.
- pes_options() PESOptions[source]
- plot(*, series=None, pathways=None, layout: str | None = None, ax=None, label_points: bool | None = None, connector: str | None = None, title: str | None = None, annotations: bool = True, preset: str | None = None, **kw)[source]
Draw the document with
goodvibes.plot.plot_profile.series(ids) defaults to every series; the document’sstylegives the preset, layout, connector, decimals, figure size and whether to label points unless overridden here;annotationsdraws the document’s barrier / span annotations. A computed series needs levels (an evaluated document) or embedded conformers. Saving the result as SVG embeds the drawn document.
- selectivity: List[dict]
reaction-profile 1.1 selectivity blocks (see
evaluate_selectivity())
- source_methods() List[str][source]
Methods that GoodVibes can evaluate: those with
goodvibes.sourcesor embeddedgoodvibes.conformers.
- step_table(pathway: str | None = None, series: str | None = None) List[dict][source]
goodvibes.kinetics.step_tablefor a pathway (default the first) and series (default the first with levels on it), at the series’ temperature, in the document’s units; each row also names thepathwayandseries.
- to_dict(*, include_conformers: bool = True) dict[source]
The explicit form, ready for JSON or YAML (plain Python types only: NumPy scalars and arrays from parsed data are converted).
- to_pes_result(thermo_data=None, *, method: str | None = None, temperatures: Sequence[float] | None = None, options: PESOptions | None = None) PESResult[source]
The PESResult model of this document for one method.
With
thermo_data({file: calc_bbe} or a list ofThermoResult) each species’ conformers are the files itsgoodvibes.sourcesentry matches; without it, the embeddedgoodvibes.conformersare used when present; otherwise points have no species and only stored levels can be drawn. Computed series keep their stored levels only when no data is used (the data would recompute them).
- to_rows(layout: str = 'long') List[dict][source]
The levels as table rows.
long: one row per pathway × point × series (pathway, point, display, role, series, label, quantity, temperature, method, value, units, uncertainty).wide: one row per point (in the merged x order) with a column per pathway, or perpathway:serieswhen there are several series;from_tablereads either back.
- validate(strict: bool = False) List[str][source]
Re-validate the explicit form; returns the warnings, raises ProfileError on an error.
- class goodvibes.ProfileAxes(figure: ~typing.Any, axes: ~typing.List[~typing.Any], levels: ~typing.Dict[str, ~typing.Dict[str, ~typing.Dict[str, float | None]]], units: str, order: ~typing.List[str], x: ~typing.Dict[str, float], series: ~typing.List[~typing.Any], pathways: ~typing.List[~typing.Any], colors: ~typing.Dict[str, ~typing.Any], linestyles: ~typing.Dict[str, str], layout: str = 'overlay', uncertainty: ~typing.Dict[str, ~typing.Dict[str, ~typing.Dict[str, float]]] = <factory>, preset: str | None = None, rc: ~typing.Dict[str, ~typing.Any] = <factory>, label_size: ~typing.Any = 'x-small', pes_result: ~typing.Any = None, _ids: ~typing.Any = <factory>)[source]
Bases:
objectWhat
plot_profiledrew, with the numbers behind it.levelsis {series id: {pathway name: {point label: value}}} inunits: the same evaluation the bars were drawn from, so a table written from it cannot disagree with the figure;uncertaintyhas the same shape for the series that carry one.orderis the merged x order (point labels) andxmaps a label to its position.element_idsmaps each drawn element’s SVG id to what it shows.- annotate_barrier(pathway, src: str, dst: str, series=None, *, fmt: str = '{:+.1f}', offset: float = 0.3, color=None, ax=None)[source]
Mark the difference dst − src on one pathway with a vertical double-headed arrow beside
dstand the value inunits. Returns the matplotlib annotation.
- property ax
The (first) matplotlib Axes.
- axes: List[Any]
- colors: Dict[str, Any]
- property element_ids: Dict[str, Dict[str, str]]
- figure: Any
- label_size: Any = 'x-small'
- layout: str = 'overlay'
- level(pathway, point: str, series=None) float | None[source]
A drawn level in
units.seriesdefaults to the first.
- levels: Dict[str, Dict[str, Dict[str, float | None]]]
- linestyles: Dict[str, str]
- order: List[str]
- pathways: List[Any]
- pes_result: Any = None
- preset: str | None = None
- rc: Dict[str, Any]
- save(*paths: str, dpi: int = 200, bbox_inches: str = 'tight', embed: bool = True, **kw) None[source]
Write the figure to each path (format by extension).
An SVG keeps its text as text (
svg.fonttype = 'none'), gives every bar, connector, label and error bar anid(seeelement_ids) and, unlessembed=False, carries the reaction-profile document of what was drawn in a<metadata>element, sogoodvibes.load_profile("figure.svg")reads the numbers back. Output is deterministic (no date, fixed id salt).
- series: List[Any]
- to_document() dict[source]
The reaction-profile document of what was drawn: the drawn series with the drawn levels (and uncertainties), in
units. Needs thePESResultthe figure came from (plot_profilekeeps it).
- uncertainty: Dict[str, Dict[str, Dict[str, float]]]
- units: str
- x: Dict[str, float]
- exception goodvibes.ProfileError(errors: Sequence[str], warnings_: Sequence[str] = ())[source]
Bases:
ValueErrorA reaction-profile document is invalid.
errorslists every problem found (path: message);warningsthe non-fatal ones.
- exception goodvibes.ProfileWarning[source]
Bases:
UserWarningA reaction-profile document was accepted with a caveat (an unknown key, a quantity alias, a v2 shorthand, …).
- class goodvibes.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 = '', level_of_theory: 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, has_oniom: bool = False, atom_nums: List[int] = <factory>, atom_types: List[str] = <factory>, cartesians: List[List[float]] = <factory>, per_atom_masses: 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 = '', sp_level_of_theory: str = '')[source]
Bases:
objectProgram-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 = ''
- frequency_wn: List[float]
- classmethod from_atoms(atoms, energy, *, frequencies=None, energy_units='eV', frequency_units='cm-1', name=None, method=None, charge=None, multiplicity=None, symm='auto', masses='isotopic', job_type=None, solvation_model='gas phase', empirical_dispersion='', linear_mol=None, zpe=None)[source]
Build a QCData from an ASE
Atoms(or anything withget_chemical_symbols()andget_positions()), an electronic energy and, optionally, vibrational frequencies; no file involved.- Parameters:
atoms – geometry;
atoms.infomay supplycharge,multiplicity,nameandlevel_of_theorydefaults.energy – electronic energy in
energy_units(‘eV’ default, also ‘hartree’, ‘kcal/mol’, ‘kJ/mol’).frequencies – vibrational wavenumbers in
frequency_units(‘cm-1’ default; ‘eV’ / ‘meV’ are converted). A negative or complex value is an imaginary mode. Translational and rotational modes must already be removed (seefrom_vibrations(), which does that for an ASEVibrationsData).name – display name (
ThermoResult.name); defaultatoms.info['name']or ‘atoms’.method – model chemistry label, e.g. ‘MACE-OFF23’ or ‘B3LYP/6-31G(d)’. Stored as
level_of_theory; when it matches an entry of the Truhlar scaling database the same scale factors as for a file are applied, otherwise 1.0 (by design for an MLIP, a method naming no basis set; with a ScaleFactorWarning for a QM level).charge – default
atoms.infovalues, else 0 / 1.multiplicity – default
atoms.infovalues, else 0 / 1.symm – ‘auto’ (default) detects the point group and symmetry number with pymsym when it is installed, an int sets the symmetry number directly, None leaves it at 1 (C1).
masses – ‘isotopic’ (most-abundant-isotope masses, the QC-program convention), ‘atoms’ (
atoms.get_masses(), standard atomic weights) or a sequence of per-atom masses in amu.job_type – ‘Freq’, ‘TSFreq’ (‘TS’), ‘SP’; inferred from the frequencies when None (an imaginary mode → ‘TSFreq’).
linear_mol – force linearity; detected from the geometry when None.
zpe – zero-point energy in
energy_unitsto record as parsed metadata (GoodVibes recomputes the ZPE it reports from the frequencies, as for every program).
- classmethod from_vibrations(atoms, vibdata, energy, *, energy_units='eV', drop_tr_modes=True, imag_threshold_cm1=-15.0, **kw)[source]
Build a QCData from an ASE vibrational analysis.
vibdatais anase.vibrations.VibrationsData(or aVibrationsobject, or any sequence of wavenumbers in cm⁻¹; complex or negative values are imaginary). The 3N modes of a finite-difference Hessian include the 6 (5 for a linear molecule) translations and rotations, whichdrop_tr_modesremoves as the modes of smallest magnitude. An imaginary mode smaller in magnitude than|imag_threshold_cm1|is numerical noise on a slightly rough (MLIP) surface and is taken as real at|ν|with aRuntimeWarning; larger ones stay imaginary. ARuntimeWarningalso reports a transition state (job_type='TS') without exactly one imaginary mode, a minimum with any, or more than one imaginary mode when nojob_typewas given. Other keywords go tofrom_atoms().
- has_oniom: bool = False
- im_frequency_wn: List[float]
- job_type: str = ''
- level_of_theory: str = ''
- linear_mol: bool = False
- linear_warning: bool = False
- molecular_mass: float = 0.0
- multiplicity: int = 1
- nprocs: int = 1
- per_atom_masses: List[float]
- 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_level_of_theory: str = ''
- sp_multiplicity: int | None = None
- sp_solvation_model: str = ''
- sp_suffix: str = ''
- sp_version_program: str = ''
- symmno: int = 1
- version_program: str = ''
- with_single_point(energy, units, method=None, *, solvation_model=None, charge=None, multiplicity=None)[source]
A copy of this QCData whose enthalpies and free energies use
energy, a single point at another level, in place of the electronic energy of the frequency calculation: a composite such as DFT//MLIP (DFT energy, MLIP geometry and frequencies) with no single-point output file.The single point is applied the way
spcapplies one read from a file (ThermoResult.spc_appliedis True andsp_energyisenergy) without passingspc; an explicitspcstill wins. The copy keeps the attached energy throughcompute_thermo, re-evaluation at other temperatures,--exportcaches and embedded conformers. The vibrational scale factor is still looked up for the level of the frequencies.- Parameters:
energy – the single-point electronic energy.
units – its units, ‘hartree’, ‘eV’, ‘kcal/mol’ or ‘kJ/mol’ (required, so an energy is never read in the wrong units).
method – level of theory of the single point, e.g. ‘DLPNO-CCSD(T)/def2-TZVP’; recorded as
sp_level_of_theory.solvation_model – of the single point; default those of this QCData.
charge – of the single point; default those of this QCData.
multiplicity – of the single point; default those of this QCData.
- zero_point_corr: float | None = None
- class goodvibes.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, major: str | None = None, ee_signed: float | None = None, ratio: float | None = None, ensemble_energies: Dict[str, float] | None = None, quantity: str | None = None, name: str | None = None, series: str | None = None, reference: str | None = None, barriers: Dict[str, float] | None = None, curtin_hammett: str | None = None, warnings: Tuple[str, ...] = ())[source]
Bases:
objectN-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.
Conventions (v2, GoodVibes 5.1):
majoris the label with the largest population; on an exact tie, the first listed.preferredis the same label (kept from v1).eeis the excess of the major over the minor for two labels,|p1 - p2| * 100, always ≥ 0;ee_signedis(p1 - p2) * 100with the labels in the order given, so it is positive when the first label is the major one. Both are None for more than two labels.ddG(two labels) andratio(any number) compare the major with the runner-up:ddG = RT ln(p_major / p_runner_up)in Hartree, ≥ 0, andratio = p_major / p_runner_up.ensemble_energiesis each label’s ensemble energy-RT ln Σ exp(-E_i / RT)over its conformers, in Hartree: absolute when computed from structures, relative to the reference point when computed from a reaction-profile document (then equal tobarriers).
- barriers: Dict[str, float] | None = None
- curtin_hammett: str | None = None
- ddG: float | None = None
- ee: float | None = None
- ee_signed: float | None = None
- ensemble_energies: Dict[str, float] | None = None
- files_per_label: Dict[str, List[str]]
- key: str
- labels: List[str]
- major: str | None = None
- name: str | None = None
- populations: Dict[str, float]
- preferred: str
- quantity: str | None = None
- ratio: float | None = None
- raw_boltzmann: Dict[str, float]
- reference: str | None = None
- series: str | None = None
- temperature: float
- warnings: Tuple[str, ...] = ()
- exception goodvibes.SelectivityWarning[source]
Bases:
UserWarningA selectivity rests on an assumption the data do not support (a Curtin–Hammett precondition that fails, a branch that is not a transition state).
- class goodvibes.Series(id: str, label: str, quantity: str = 'qh_gibbs', temperature: float | None = None, method: str | None = None, style: Dict[str, ~typing.Any]=<factory>, levels: Dict[str, ~typing.Dict[str, float | None]] | None=None, declared: bool = False, units: str | None = None, uncertainty: Dict[str, ~typing.Dict[str, float]] | None=None, standard_state: Dict[str, ~typing.Any] | None=None, extensions: Dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectOne quantity at one temperature: a line set on the axes, a column set in a table.
A computed series (
declared=False) is evaluated from the model throughPathway.levels(temperature, quantity);temperatureNone means the result’s first temperature. Itslevels, when set, are a stored evaluation (a reaction-profile document written byProfile.evaluate) and are used as they are. A declared series (declared=True) carries typed-inlevels— literature values, a hand-entered table — as {pathway name: {point label: value}} inunits(default: the result’s units) and is never re-evaluated; a point missing from its levels is simply absent from the figure.styleholds matplotlib overrides (linestyle,color, …).- declared: bool = False
- classmethod declared_from(id: str, label: str, levels: Mapping[str, Mapping[str, float | None]], *, quantity: str = 'gibbs', temperature: float | None = None, method: str | None = None, units: str | None = None, style: Dict[str, Any] | None = None) Series[source]
A declared series from {pathway: {point label: value}}.
- evaluate(result: PESResult, pathway: Pathway) Dict[str, float | None][source]
{point label: value in the result’s units} for one pathway.
Stored levels (every declared series, and a computed series read from an evaluated document) are returned converted to the result’s units; otherwise a computed series goes through
Pathway.levels.
- evaluate_uncertainty(result: PESResult, pathway: Pathway) Dict[str, float][source]
{point label: uncertainty in the result’s units} for one pathway (empty when the series carries none).
- extensions: Dict[str, Any]
- id: str
- label: str
- levels: Dict[str, Dict[str, float | None]] | None = None
- method: str | None = None
- quantity: str = 'qh_gibbs'
- standard_state: Dict[str, Any] | None = None
- style: Dict[str, Any]
- temperature: float | None = None
- uncertainty: Dict[str, Dict[str, float]] | None = None
- units: str | None = None
- 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, inertia: str = 'global', strict_spc: bool = False, scale_factor_source: str | None = None)[source]
Bases:
objectBundle 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
- s_freq_cutoff: float = 100.0
- scale_factor_source: str | None = None
- solv: str | None = None
- spc: str | None = None
- strict_spc: bool = False
- 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, spc_applied: bool = False, temperature: float | None = None, options: Any = None, freq_scale_factor: float | None = None, zpe_scale_factor: float | None = None, scale_factor_source: str | None = None, symmetry_source: str | None = None, n_imag: int | None = None)[source]
Bases:
objectBundle 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
- freq_scale_factor: float | None = None
- 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
- n_imag: int | None = None
- name: str
- options: Any = None
- point_group: str | None
- program: str | None
- qcdata: Any
- qh_enthalpy: float | None
- qh_entropy: float | None
- qh_gibbs_free_energy: float | None
- scale_factor_source: str | None = None
- scf_energy: float | None
- sp_energy: float | None
- spc_applied: bool = False
- symmetry_source: str | None = None
- symmno: int | None
- temperature: float | None = None
- zpe: float | None
- zpe_scale_factor: float | None = None
- class goodvibes.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:
objectBundle 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, and every field but scf_energy is None for an energy-only structure (no frequencies); arithmetic propagates None (if either operand has a None field, the result does).
- enthalpy: float
- entropy: float
- get(quantity, T: float | None = None) float | None[source]
Value of a registry quantity in Hartree (see goodvibes.quantities).
Entropy quantities are returned as T·S and therefore need
T.e_zpeis (sp_energy if present else scf_energy) + zpe, matching the table convention that H and G are SPC-substituted when –spc is used. Returns None where the underlying value is None (no SPC).
- 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.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.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.
- class goodvibes.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, inertia='global', qcdata=None, zpe_scale_fac=None, strict_spc=False, _from_options=False)[source]
Bases:
objectCompute “black box” entropy and enthalpy values along with all other thermochemical quantities.
Computes H, S from partition functions, applying quasi-harmonic corrections
- 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/importable.
- classmethod from_options(qcdata_or_path, options, *, scale_factor_source=None)[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.
The result’s
scale_factor_sourcerecords where the harmonic factor came from: ‘user’ (passed in), ‘truhlar’ (database lookup), ‘mlip-unscaled’ (an ASE input whose level names no basis set, i.e. an MLIP, with no database entry: 1.0 by design) or ‘none-found’ (1.0 because the level of theory is not in the database; a ScaleFactorWarning says so). A caller that resolved the factors itself (the CLI) passesscale_factor_source.
- 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.compute_batch(paths: Sequence[Any], *, jobs: int = 1, **kwargs: Any) List[ThermoResult][source]
Compute thermochemistry for a list of files or QCData objects.
- Parameters:
paths – QC output file paths and/or parsed
QCDatainstances (e.g. fromQCData.from_atomsin an MLIP workflow).jobs – parallelism level.
1(default) is sequential — no process-pool overhead.> 1spawns that many worker processes viaconcurrent.futures.ProcessPoolExecutor.0or negative usesos.cpu_count()(or 1 if unknown).**kwargs – forwarded unchanged to every
compute_thermocall.
- Returns:
Results in input order (matches
executor.map’s contract). Workers can’t write to the orchestrator’s logger, so per-filelog.infofrom insidecalc_bbeis silenced underjobs > 1; warnings that surface throughThermoResult(e.g. missing-frequency files) still come through unchanged.
- goodvibes.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.compute_selectivity_batch(jobs: Mapping[str, Mapping[str, Any]], temperatures: Sequence[float] = (298.15,), *, quantity: str = 'qh_gibbs', s_freq_cutoffs: Sequence[float] | None = None, conformer_windows: Sequence[float] | None = None, records: bool = False, workers: int = 1, **thermo_options: Any)[source]
Selectivities of many jobs over temperatures, entropy cutoffs and conformer windows.
- Parameters:
jobs –
{job: {label: structures}}, the labels of each job in the order that sets the sign ofee(positive when the first label is major).structuresis a glob pattern or path, aConformerSet, or a list of paths, patterns,ThermoResult,QCDataorComputedEntryobjects.temperatures –
quantity – the registry quantity the conformers are weighted by (
'qh_gibbs';'electronic'for energy-only ensembles such asread_xyz_framesframes).s_freq_cutoffs – quasi-harmonic entropy cutoffs (cm⁻¹) to sweep; the nominal cutoff (
s_freq_cutoffinthermo_options, 100 by default) is always included.conformer_windows – energy windows (kcal/mol) to sweep: a label keeps the conformers within the window of its lowest one at that condition (0 keeps only the lowest). All conformers (window None, the nominal) are always included.
records – return a list of dicts instead of a pandas DataFrame.
workers – processes used to parse the files (
compute_batch’sjobs).thermo_options –
compute_thermokeywords (QS,QH,spc,freq_scale_factor, …), applied to every file.
- Returns:
job,temperature,s_freq_cutoff,conformer_window,nominal(the nominal cutoff and all conformers),quantity,labels(comma-joined),major,ee(signed, two labels),ddG(kcal/mol, major over runner-up),ratio, and per labelpopulation[<label>](0–1) andn[<label>](conformers kept). Rows follow the job order, then temperature, cutoff and window (None first).- Return type:
One row per job, temperature, cutoff and window
- 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, inertia: str = 'global', strict_spc: bool = False) 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 strict_spc raise MissingSinglePointError when spc is set but
the single-point energy is missing or unparseable (default: RuntimeWarning and the frequency-level energy is used)
- goodvibes.energy_span(levels: Mapping[str, float], transition_states: Iterable[str], *, reaction_energy: float | None = None, temperature: float = 298.15, units: str = 'kcal/mol') EnergySpan[source]
The energy span of a catalytic cycle.
- Parameters:
levels – the states of one turnover in order,
{point: level}. Withoutreaction_energy, the last state is the first one regenerated after the turnover (catalyst plus products): it setsΔG_r = level(last) - level(first)and is not a state of the cycle.transition_states – the points of
levelsthat are transition states; the others are intermediates.reaction_energy – ΔG_r of one turnover, when every point of
levelsis a state of the cycle.temperature –
units – of the levels and of the results.
- goodvibes.eyring_rate(dG: float, temperature: float = 298.15, *, units: str = 'kcal/mol', kappa: float = 1.0) float[source]
The Eyring rate constant
κ kB T / h exp(-ΔG‡ / RT)for a free energy of activationdGinunits(s⁻¹ for a unimolecular step).
- goodvibes.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.
Three formats are accepted: a reaction-profile document (
schema: reaction-profile/1.x, YAML or JSON; seegoodvibes.profile), the v2 PES YAML, and the legacy--- # PEStext (deprecated; emits a DeprecationWarning). The returned result carries the document it was read from asresult.source(agoodvibes.profile.Profile; the two older formats are upgraded).
- goodvibes.load_profile(source, *, strict: bool = False, **table_options) Profile[source]
Read a reaction-profile document.
sourceis a Profile (returned as is), a mapping, or a path to: a reaction-profile document (.yaml / .yml / .json), an SVG figure saved by GoodVibes (it embeds the drawn document), a GoodVibes--json/--exportpayload with aprofileblock, a CSV/TSV table of relative energies (table_optionsgo toProfile.from_table()), a GoodVibes v2 PES YAML or a legacy--- # PEStext file (both upgraded to the explicit form).
- goodvibes.merge_point_order(sequences: Sequence[Sequence[str]]) List[str][source]
Merge several ordered label sequences into one order that keeps every sequence’s own order. A label new to the merged list is inserted just before the earliest already-placed label that follows it in its own sequence, or appended when none does; so
[R, Int1, TS1, P]and[R, TS1, P]merge to the first, and two branches[R, TS_R, P_R]/[R, TS_S, P_S]keep each branch contiguous.
- goodvibes.plot_pes(pes_result: Any, *, ax=None, pathway_index: int | Sequence[int] | None = None, connector_style: str = 'bezier', colors: Sequence[str] | None = None, show_conformers: bool = False, thermo_lookup: Mapping[str, float] | Callable[[str], float] | None = None, title: str | None = None, label_points: bool = False, quantity: str = 'qh_gibbs')[source]
Plot one or more pathways from a PESResult as a reaction profile.
A thin wrapper over
plot_profile()kept for the 4.2–4.5 API; it returns the matplotlib Axes. New code should callplot_profile, which also returns the drawn levels and supports several series (temperatures, quantities, declared values) on one axes.- Parameters:
pes_result – a PESResult from goodvibes.pes_loader.load_pes.
ax – optional matplotlib Axes; new figure if None.
pathway_index – which pathway(s) to draw (None → all, an int, or a list of ints).
connector_style – ‘bezier’ (default) or ‘linear’.
colors – per-pathway colors in pathway order.
show_conformers – scatter individual conformer values around each level.
thermo_lookup – deprecated and ignored (conformer values are read from the PESResult); accepted for backwards compatibility.
title – figure title; defaults to the pathway names + temperature.
label_points – annotate each point’s value next to its bar.
quantity – which relative quantity to draw, by registry id or alias (see goodvibes.quantities); the y-label follows the choice.
- Returns:
The matplotlib Axes the profile(s) were drawn on.
- goodvibes.plot_profile(pes_result: Any, *, series=None, quantity: str | None = None, temperatures: Sequence[float] | None = None, pathways=None, layout: str = 'overlay', ax=None, style: Mapping[str, Any] | None = None, colors=None, show_conformers: bool = False, label_points: bool = False, title: str | None = None, order: Sequence[str] | None = None, preset: str | None = None, uncertainty: bool = True) ProfileAxes[source]
Draw a reaction profile from a
PESResult.Every pathway is drawn on a shared x axis whose order is the merge of the pathways’ point sequences (
PESResult.merged_order), so pathways of different lengths, or branches sharing a reactant, line up by point label. Colour encodes the pathway; linestyle encodes the series (a quantity at a temperature, computed from the model or declared). Transition-state points (role='ts') carry their value label above the bar, other points below; abarrierlessedge is a dotted connector; an edge of kindnonedraws no connector.- Parameters:
pes_result – a PESResult (
goodvibes.load_pesor built by hand).series – what to draw. A
Series/ list ofSeries(computed or declared), or series ids frompes_result.series. Default:pes_result.serieswhen it declares any, else one computed series ofquantityper temperature.quantity – registry id or alias for the implicit series (default ‘qh_gibbs’); the y-label follows it.
temperatures – temperatures of the implicit series (default: the result’s); several give a temperature overlay.
pathways – which pathways to draw (names, indices or objects); default all.
layout – ‘overlay’ (all pathways on one axes) or ‘panels’ (one axes per pathway, shared y).
ax – an Axes to draw on (overlay only); a new figure otherwise.
style – {‘connector’: ‘bezier’ | ‘linear’ | ‘step’, ‘bar_half’: float, ‘decimals’: int, ‘figsize’: (w, h), ‘linestyles’: […], ‘preset’: name}.
colors – per-pathway colours, a sequence in pathway order or a {name: colour} mapping. Default: black for one pathway, the matplotlib cycle otherwise.
show_conformers – scatter each conformer at level + (conformer − species rollup) in the plotted quantity (computed series only).
label_points – print each level’s value next to its bar.
title – figure title (default: pathway names, and the temperature when there is one).
order – explicit x order of point labels (overrides the result’s).
preset – a
STYLE_PRESETSname (‘single-column’, ‘double-column’, ‘slide’; defaultstyle['preset']or ‘none’): figure size, fonts and line widths for that target, applied to this figure only. An explicitstyle['figsize']still wins.uncertainty – draw each series’
uncertaintyas an error bar (± the value) on its levels.
- Returns:
A
ProfileAxeswith the figure, axes and the drawn levels.
- goodvibes.rate_ratio(dG_a: float, dG_b: float, temperature: float = 298.15, *, units: str = 'kcal/mol') float[source]
k_a / k_b = exp(-(ΔG‡_a - ΔG‡_b) / RT).
- goodvibes.read_xyz_frames(path, *, energy_units=None, energy_key=None, method=None, charge=None, multiplicity=None)[source]
Read every frame of a multi-frame
.xyzor.extxyzfile as an energy-only QCData: a geometry and an electronic energy, no frequencies.For conformer ensembles (CREST
crest_conformers.xyz, xtb trajectories) and MLIP sweeps (ase.io.writeof many frames).compute_batchevaluates the list, and aConformerSetof the results weighted by'electronic'gives Boltzmann populations and the ensemble energy; free energies need frequencies, so the thermochemical quantities are None.The energy of a frame comes from its comment line: in an extxyz one (
key=valuepairs) fromenergy_keyor, by default, the first ofenergy,free_energy,total_energy(eV, the ASE convention),scf_energy(hartree) andE(eV), a named key other than these being in eV; in a plain one fromenergy: <value>(xtb) or a bare number (CREST), in hartree. Anenergy_units(orscf_energy_units) key, or theenergy_unitsargument, overrides the units. A frame without an energy is an error.- Parameters:
path – the
.xyz/.extxyzfile.energy_units – units of every frame’s energy (‘hartree’, ‘eV’, ‘kcal/mol’, ‘kJ/mol’); default as above.
energy_key – the extxyz key holding the energy.
method – level of theory of the energies (
level_of_theory), e.g. ‘GFN2-xTB’ or ‘MACE-OFF23’; default the frame’slevel_of_theorykey.charge – default the frame’s keys, else 0 / 1.
multiplicity – default the frame’s keys, else 0 / 1.
- Returns:
A list of QCData, one per frame, named
<stem>_<n>(nfrom 1, zero-padded) unless a frame has anamekey;job_type‘SP’.
- goodvibes.resolve_quantity(name) Quantity[source]
Return the Quantity for an id or alias (case-insensitive), or raise ValueError.
- goodvibes.selectivity_from_energies(energies, temperature, *, key=None, quantity=None, files_per_label=None, **extra)[source]
A SelectivityResult from each label’s conformer energies.
- Parameters:
energies (Mapping[str, Sequence[float]]) – ordered label -> the energies (Hartree) of its conformers; one value per label for a single structure or an already rolled-up level.
temperature (float) – Kelvin.
key (str) – recorded on the result (
quantityis the registry id;keydefaults to it).quantity (str) – recorded on the result (
quantityis the registry id;keydefaults to it).files_per_label (dict, optional) – label -> file paths, recorded.
extra – further SelectivityResult fields (
name,series,reference,barriers,curtin_hammett,warnings).
A label with no energy gets population 0.
- Raises:
ValueError – fewer than two labels, or no energy at all.
- goodvibes.si_rows(results: Sequence[Any], *, units: str = 'hartree', n_lowest: int = 3) List[Dict[str, Any]][source]
One row per result (
ThermoResultfromcompute_thermo/compute_batch); columnsSI_COLUMNS, energies inunits(absolute, Hartree by default), T·S and T·qh-S at the result’s temperature,imaginaryandlowest(then_lowestlowest real modes) as comma-separated cm⁻¹. A structure without frequencies has None for the thermal quantities.
- goodvibes.step_table(levels: Mapping[str, float], transition_states: Iterable[str], *, temperature: float = 298.15, units: str = 'kcal/mol') List[dict][source]
One row per transition state along a pathway (
levelsin order):ts,from(the last intermediate before it),to(the first intermediate after it, or None);barrier:level(ts) - level(from), inunits;barrier_from_lowest: from the lowest point before the TS (the effective barrier when an earlier state is deeper);step_energy:level(to) - level(from);k(s⁻¹) andhalf_life(s) of the Eyring rate overbarrier.
- goodvibes.summarize_selectivity(table, value: str = 'ee', *, decimals: int = 0) List[Dict[str, Any]][source]
The nominal value of each job at each temperature and its range over the sweep:
{job, temperature, value, nominal, low, high, text}, withtextsuch as"ee +92 % (88 to 94 % over s_freq_cutoff 50–150 cm⁻¹, conformer window 0–2 kcal/mol)".tableis whatcompute_selectivity_batchreturned (DataFrame or records);valueis a numeric column (ee,ddG,ratioor apopulation[<label>]).
- 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 and the frequency lists). The imaginary frequencies are kept as
im_frequency_wn: a space-separated string in cm⁻¹, as--imagprints them, or None when the structure has none.
- 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.
- goodvibes.validate_document(data, *, strict: bool = False, use_jsonschema: bool = True) Tuple[List[str], List[str]][source]
(errors, warnings) for a document mapping.
Runs the reference Python validator and, for a document in the explicit form (one with a
schemakey) whenjsonschemais installed, the JSON Schema too; errors from the latter are prefixedschema:. Never raises for an invalid document.
- goodvibes.write_si(results: Sequence[Any], path, *, units: str = 'hartree', decimals: int = 6, n_lowest: int = 3, coordinates: bool = True) List[str][source]
Write the SI table for
resultsand return the paths written.The format follows the extension:
.md(a Markdown table, then a “Cartesian coordinates” section with one block per structure),.tex(a booktabs tabular, then the coordinates in a verbatim block),.csv(the table, all columns; the coordinates go to<stem>_coordinates.xyz) or.xyz(the coordinates only).coordinates=Falseleaves them out.