Effective Quasi-Harmonic#

class directupsampling.effqh.effqh.EffQH(atoms: Atoms | list[Atoms], fit_order: int | None = None, cutoffs: list[float] | None = None, comm: DummyMPI = <directupsampling.parallel.MPI object>)[source]#

Bases: object

Class representing an effective quasi-harmonic model.

property calc: EffQHCalculator#
change_reference(new_atoms: Atoms) EffQH[source]#

Return a new EffQH instance for new_atoms.

Parameters#

new_atomsAtoms

Must have the same symmetry as effqh.atoms and be large enough for the cutoff to fit inside, but can differ in number or order of atoms, and cell shape.

Returns#

EffQH

change_size(new_atoms: Atoms) EffQH[source]#

Return a new EffQH instance for new_atoms.

Parameters#

new_atomsAtoms

Must have the same symmetry as effqh.atoms and be large enough for the cutoff to fit inside, but can differ in number of atoms and cell shape.

Returns#

EffQH

property exact_free_energy_surface#
property exact_phonons#
extract_fc_potentials() None[source]#

Extract ForceConstantPotential.

The ForceConstantPotential instances is extracted from self.fc_fit.raw_data into self.fc_potentials.

fit_snapshots(snapshots: SnapshotContainer | list[list[Atoms]], method: str | None = None) None[source]#

Fits the EffQH to snapshots over volumes.

Internally fits a hiphive ForceConstantPotential to the snapshots for each volume, and then fits each resulting force-constant component with a polynomial over volume.

Parameters#

snapshotsSnapshotContainer or list of list of Atoms

Either a SnapshotContainer with volumes in the index or a list grouped by volume with lists of Atoms.

methodstr (optional)

Method for fitting the effective force constants. Defaults to ‘least-squares’ in general and lasso for underdetermined systems.

Notes#

For large systems, the ‘least-squares’ method is preferred for speed reasons.

property mesh_free_energy_surface#
property mesh_phonons#
classmethod read_fc_fit(filename: str, atoms: Atoms | list[Atoms] | None = None)[source]#
classmethod read_fcp(pathname: str, atoms: Atoms | list[Atoms], fit_order: int | None = None)[source]#
write_fc_fit(filename: str) None[source]#
write_hessian(volume: float, file: os.PathLike | str, filetype: str | None = None, fmt: str = 'sphinx', unit: str = 'eVperAng2') None[source]#

Write the force-constant matrix for a given volume to file.

Parameters#

volumefloat

Volume for which to write the force-constant matrix.

fileos.PathLike or str

Path to the output file.

filetypestr

Filetype to write. Must be ‘txt’, ‘hdf5’ or None. If None, the filetype is guessed from the file suffix.

fmtstr

Format of the output file. Must be ‘sphinx’ or ‘phonopy’. Default is ‘sphinx’.

unitstr

Unit of the force constants, only relevant for fmt="sphinx". Must be ‘eVperAng2’ or ‘HaperBohr2’. Default is ‘eVperAng2’.

directupsampling.effqh.utils.get_ref_atoms(atoms: Atoms | list[Atoms], volume: float) Atoms[source]#

Return atoms scaled to volume per atom, or the list entry closest to it.

Returns#

Atoms

Raises#

TypeError

If no entry in the list matches volume.

directupsampling.effqh.utils.reshape_phonopy_to_sphinx(fc_matrix: ndarray) ndarray[source]#
directupsampling.effqh.utils.reshape_sphinx_to_phonopy(fc_matrix: ndarray) ndarray[source]#
class directupsampling.effqh.fcfit.ForceConstantFit(fit_order: int | None = None)[source]#

Bases: object

Class for fitting force constants.

property coefficients: ndarray[Any, dtype[float64]]#

Fitted force constant coefficients.

directupsampling_objtype = 'forceconstantfit'#
fit_force_constants(volumes, force_constants)[source]#

Fit each element of force constants to polynomials of volumes

classmethod fit_from_hiphive(volumes: list[float], force_constant_potentials: list[ForceConstantPotential], atoms: Atoms | list[Atoms], fit_order: int | None = None) Self[source]#

Fit a ForceConstantFit from a list of hiphive ForceConstantPotentials.

Parameters#

volumes: list of float

List of volumes corresponding to the ForceConstantPotentials.

force_constant_potentials: list of ForceConstantPotential

List of hiphive ForceConstantPotentials to fit to.

atoms: Atoms or list of Atoms

Reference Atoms or list of Atoms to get the force constants with.

fit_order: int, optional

The fitting order of polynomials. If None, the order is 3 or less.

Returns#

ForceConstantFit

classmethod fromdict(dct: dict) Self[source]#

Reconstruct a ForceConstantFit from its todict() form.

Returns#

ForceConstantFit

get_force_constants(volume: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) ndarray[Any, dtype[float64]][source]#

Get force constants at the given volume(s) by interpolation.

classmethod read(filename: os.PathLike | str) Self[source]#

Read force constants from a .fcfit (HDF5) file or phonopy txt file(s).

Parameters#

filenamestr or list of str

The filename(s) to read.

todict() dict[source]#
write(file: os.PathLike | str) None[source]#
class directupsampling.effqh.phonons.BasePhonons(atoms: Atoms, volume_bounds: list | None = None, freq_coeffs: ndarray | None = None, weights: ndarray | None = None, *, invert_neg: bool = False, freq_cutoff: float = 1e-05)[source]#

Bases: object

Base class for phonon models fitted as polynomial functions of volume.

Subclasses implement force_constants_to_frequencies for a specific diagonalisation scheme (Gamma-point exact diagonalisation or BZ mesh via phonopy).

FREQ_WARN_TOL = 1e-05#
abstractmethod force_constants_to_frequencies(force_constants: ndarray) ndarray[source]#

Return frequencies in eV and set self.weights.

Parameters#

force_constants :

Force-constant matrix for the structure.

Returns#

np.ndarray

Frequencies in eV, sorted in ascending order.

classmethod from_fcfit(atoms: Atoms, force_constant_fit: ForceConstantFit, mesh: list | None = None, *, invert_neg: bool = False, freq_cutoff: float = 1e-05) BasePhonons[source]#

Construct a phonon model by fitting frequencies from a ForceConstantFit.

Parameters#

atoms :

Reference structure.

force_constant_fit :

Fitted force constants as a function of volume.

mesh :

BZ mesh passed to MeshPhonons; omit for ExactPhonons.

invert_neg :

Forwarded to __init__.

freq_cutoff :

Forwarded to __init__.

Returns#

BasePhonons

Instance with freq_coeffs and weights set.

get_energy(volume: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], *, classical: bool = False) ndarray[source]#

Harmonic internal energy per atom.

Parameters#

volume :

Volume(s) in ų.

temperature :

Temperature(s) in Kelvin.

classical :

Use the classical (k_B*T) expression instead of quantum.

Returns#

np.ndarray

Internal energy in eV/atom.

get_free_energy(volume: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], *, classical: bool = False) ndarray[source]#

Harmonic Helmholtz energy per atom.

Parameters#

volume :

Volume(s) in ų.

temperature :

Temperature(s) in Kelvin.

classical :

Use the classical expression instead of quantum.

Returns#

np.ndarray

Helmholtz energy in eV/atom.

get_frequencies(volume: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) ndarray[source]#

Evaluate the polynomial frequency model at the given volume(s).

Parameters#

volume :

Volume(s) in ų.

Returns#

np.ndarray

Frequencies in eV with shape (n_freq, *volume.shape).

sum_over_frequencies(fun: Callable[[ndarray, ndarray], ndarray], volume: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) ndarray[source]#

Weighted sum of fun over modes above freq_cutoff.

Normalised per vibrational atom (see denominator note below).

Modes at or below freq_cutoff are excluded from the sum via a binary mask; they contribute zero to the numerator regardless of fun.

The denominator is weights.sum() / 3 - 1, equal to N_atoms - 1 (ExactPhonons) or N_atoms * N_kpoints - 1 (MeshPhonons). This normalises per vibrational atom: after removing the 3 translational modes the system has 3N-3 physical degrees of freedom, giving N-1 effective 3-DOF “atoms”. This matches the MD convention where COM kinetic energy is subtracted before normalising. For large supercells the difference from 1/N is negligible; for small unit cells (e.g. N=2) the factor of 2 is intentional and ensures consistency with MD.

Parameters#

fun :

Function (temperature, frequency) -> energy.

volume :

Volume(s) in ų.

temperature :

Temperature(s) in Kelvin.

Returns#

np.ndarray

Weighted sum of fun over active frequencies, per atom.

Warns#

RuntimeWarning

If any frequency remains below -FREQ_WARN_TOL after optional inversion.

todict() dict[source]#
class directupsampling.effqh.phonons.ExactPhonons(atoms: Atoms, volume_bounds: list | None = None, freq_coeffs: ndarray | None = None, weights: ndarray | None = None, *, invert_neg: bool = False, freq_cutoff: float = 1e-05)[source]#

Bases: BasePhonons

Gamma-point phonons via exact diagonalisation of the dynamical matrix.

directupsampling_objtype = 'exactphonons'#
force_constants_to_frequencies(force_constants: ndarray) ndarray[source]#

Diagonalise the dynamical matrix and return frequencies in eV.

Uses eigvalsh (real-symmetric solver) for numerical stability. Negative eigenvalues are mapped to negative frequencies via sqrt(|lambda|) * sign(lambda) rather than producing imaginary values. Sets self.weights = ones(3N).

Parameters#

force_constants :

Force-constant matrix for the structure.

Returns#

np.ndarray

Frequencies in eV sorted in ascending order, shape (3N,).

class directupsampling.effqh.phonons.MeshPhonons(*args: Any, mesh: list, **kwargs: Any)[source]#

Bases: BasePhonons

BZ-mesh phonons computed via phonopy.

directupsampling_objtype = 'meshphonons'#
force_constants_to_frequencies(force_constants: ndarray) ndarray[source]#

Compute BZ-mesh frequencies via phonopy and return them in eV.

Sets self.weights to the phonopy k-point weights repeated over branches (shape: n_qpoints * 3 * N_primitive).

Parameters#

force_constants :

Force-constant matrix for the structure.

Returns#

np.ndarray

Frequencies in eV, shape (n_qpoints * 3 * N_primitive,).

class directupsampling.effqh.phonons.PhonopyHandler(atoms: Atoms, force_constants: ndarray, mesh: list)[source]#

Bases: object

Thin wrapper around a phonopy Phonopy object for a single structure.

get_frequencies() ndarray[source]#

Return flattened mesh frequencies in eV.

get_weights() ndarray[source]#

Return k-point weights repeated over branches (3 * N_primitive per q-point).

make_phonons()[source]#

Build and run the phonopy Phonopy object.

property phonons#
plot_phonon_dispersion()[source]#
directupsampling.effqh.phonons.evib_classical(temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], frequency: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] = None) ndarray[source]#

Classical harmonic internal energy: k_B * T.

Parameters#

temperature :

Temperature in Kelvin.

frequency :

Ignored; present for interface compatibility with the QM form.

Returns#

np.ndarray

k_B * T.

directupsampling.effqh.phonons.evib_qm(temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], frequency: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) ndarray[source]#

Quantum harmonic internal energy.

Parameters#

temperature :

Temperature in Kelvin.

frequency :

Frequency in eV.

Returns#

np.ndarray

omega/2 + omega / (exp(omega / (k_B*T)) - 1). At T=0 returns the zero-point energy omega/2.

directupsampling.effqh.phonons.fvib_classical(temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], frequency: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) ndarray[source]#

Classical harmonic vibrational Helmholtz energy.

Parameters#

temperature :

Temperature in Kelvin.

frequency :

Frequency in eV.

Returns#

np.ndarray

k_B * T * ln(omega / (k_B*T)).

directupsampling.effqh.phonons.fvib_qm(temperature: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], frequency: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) ndarray[source]#

Quantum harmonic vibrational Helmholtz energy.

Parameters#

temperature :

Temperature in Kelvin.

frequency :

Frequency in eV.

Returns#

np.ndarray

omega/2 + k_B*T * ln(1 - exp(-omega / (k_B*T))). At T=0 returns the zero-point energy omega/2.