Upsampling / Free Energy Perturbation

Contents

Upsampling / Free Energy Perturbation#

class directupsampling.upsampling.Upsampling(snapshot_container: SnapshotContainer = None, calc: str | Calculator = None, vt_grid: Grid | Iterable[tuple[float]] | None = None, energy_differences: dict[tuple, list] | None = None, natoms: int | None = None, per_atom: bool | None = None, vibrational: bool = False, restart: bool = False)[source]#

Bases: object

calculate_snapshots()[source]#

Calculates self.snapshot_container with self.calc.

property energy_differences#
property free_energy_differences#

Property that gives the results of the free energy perturbation.

classmethod read_folders(path: str | Path, per_atom: bool = True, vibrational: bool | None = None, **kwargs)[source]#

Reads in calculation in a folder structure through SnapshotSampler.

Parameters#

path: str

The path to the root of the folder structure.

per_atom: bool (optional)

If False the results are given per cell, instead of per_atom. Default is True.

vibrational: bool (optional)

Default is True if not “old_fermi_folders”. Decides if the normalization (if per_atom is True) is done with natoms-1 (True) or natoms (False).

old_fermi_folders: bool (optional)

If reading old folders with calc_0K and calc_kBT subfolders, setting this to True will read the Fermi smeared reasults in calc_kBT.

Returns#

A new Upsampling instance. For how to extract the electronic free energies, see Examples for the Upsampling class.

property reference_snapshot_container#
property snapshot_container#
write_folders(**kwargs)[source]#

Writes folders. See SnapshotSampler.write_folders.

Note: Only “energy” is saved from the results of self.reference_snapshots_container.

directupsampling.upsampling.calculate_fep(energies, temperature)[source]#

Returns a pandas Series with results.

directupsampling.upsampling.perturbation_average(energies, t)[source]#
directupsampling.upsampling.rescale_to_different_supercell(value, natoms, natoms_new)[source]#

Returns a rescaled energy (vib) in ene/atom from one natoms to another

The reasoning for this is that the upsampled energy per cell is approximated to be the same as for the cell with natoms_new atoms (usually larger). In this case, the energy should not be normalized with natoms-1 but instead with natoms_new-1, to be consistent with the larger supercell size, so that it can be added together with cancelling references.

Having canceling references resolves e.g. the problem of having to use MTP 0K references, which might not be a nice thing to do, while still keeping the correct normalization of the vibrational energy from the MD with the larger supercell size.

class directupsampling.fecontrib.FreeEnergyContrib(name: str = '', bounds: list | None = None, units: dict = {'energy': 'eV/atom', 'temperature': 'K', 'volume': 'Å$^3$'})[source]#

Bases: object

property bounds#

Defaults to [[5, 50], [1, 1500]] if not set manually or by fit_data().

directupsampling_objtype = 'freeenergycontrib'#
fit_data(volumes, temperatures, free_energies, errors=[], **kwargs)[source]#

Parameters#

kwargs:

Keyword arguments sent to FreeEnergyFit: kind: {“poly”, “fah”, “fvib”, “fel”}, vt_basis: str, mean_freqs: tuple[list[float], list[float]]

classmethod fromdict(dct)[source]#
get_free_energy(volume, temperature)[source]#
read_old_format(file)[source]#
todict()[source]#
class directupsampling.fecontrib.FreeEnergyFit(volumes=[], temperatures=[], free_energies=[], errors=[], kind: str = 'poly', vt_basis: str = '1 + v + t + v**2 + vt + t**2 + v**3 + v**2*t + v*t**2 + v**3*t + v**2*t**2', mean_freqs: tuple[list[float], list[float]] | None = None)[source]#

Bases: object

directupsampling_objtype = 'freeenergyfit'#
fit()[source]#

Fit to self.raw_data.

classmethod fromdict(dct)[source]#
get_free_energy(volume, temperature)[source]#
todict()[source]#
class directupsampling.fecontrib.OldFormatFreeEnergy(tdict)[source]#

Bases: object

directupsampling_objtype = 'oldformatfreeenergy'#
get_free_energy(volume, temperature)[source]#
todict()[source]#
class directupsampling.fecontrib.RawData(volume: ndarray, temperature: ndarray, free_energy: ndarray, error: ndarray)[source]#

Bases: object

Container for raw data used in free energy fitting.

error: ndarray#
free_energy: ndarray#
temperature: ndarray#
volume: ndarray#
class directupsampling.fecontrib.VTBasis(expression: str, domain=None)[source]#

Bases: object

build_function()[source]#
directupsampling_objtype = 'vtbasis'#
parse_expression()[source]#
rescale(v_range, t_range)[source]#
todict()[source]#
directupsampling.fecontrib.get_fah_fit_func(basis: ndarray, mean_freqs: tuple[list | ndarray] | None = None, mean_freqs_fit_order: int = 2)[source]#
directupsampling.fecontrib.get_fel_fit_func(basis)[source]#
directupsampling.fecontrib.get_fit_func(basis: Callable, fit_type: str, **kwargs: dict) Callable[source]#

Return function for fitting.

Parameters#

basiscallable

Callable that takes arguments (volume, temperature) and return a basis with the length of coefficients.

fit_typestr

One of ‘poly’, ‘fvib’, ‘fah’ and ‘fel’. Defaults to ‘poly’

kwargsdict

keyword arguments sent to the respective ‘get_fit_function’ function: ‘fah’; mean_freqs: tuple[np.ndarray, np.ndarray] the arrays contain volume and frequencies respectively. If mean_freqs is None, 10 meV is used for all volumes.

Returns#

Callable

Raises#

ValueError

directupsampling.fecontrib.get_fvib_fit_func(basis)[source]#
directupsampling.fecontrib.get_poly_fit_func(basis)[source]#
class directupsampling.fesurface.FreeEnergySurface(contributions: list[FreeEnergyContrib | EOS | Callable] = [], bounds: list[list] | None = None)[source]#

Bases: object

Class representing a free energy surface with contributions.

Takes contributions as argument which should be a list of FreeEnergyContrib’s.

add_contribution(contribution)[source]#
property bounds#
directupsampling_objtype = 'freeenergysurface'#
get_bounds_from_contributions()[source]#
get_eos() EOS[source]#
get_free_energy(volume, temperature=None)[source]#
get_gibbs_energy(pressure: float, temperature: float | None = None, pressure_unit: str = 'eV_per_Angstrom3')[source]#

Gives Gibbs energy at p and T by Legendre transform from FES.

Parameters#

pressure, temperature: float

Conditions for which the Gibbs energy should be returned.

pressure_unit: {“eV_per_Angstrom3”, “GPa”}

Unit in which the pressure is supplied. The default is eV/Ang^3

get_pressure(volume, temperature=None, unit='eV_per_Angstrom3')[source]#

Calculates pressure by central differences.

Parameters#

volumefloat

The volume at which to calculate the pressure.

temperaturefloat, optional

The temperature at which to calculate the pressure. If the FreeEenrgySurface only contains an EOS contribution, the temperature is optional and ignored if given.

unit{“eV_per_Angstrom3”, “GPa”}

Specifies the unit of the returned pressure. The default is eV/Ang^3.

Returns#

pressurefloat

Returns the calculated pressure.

get_volume(pressure: float, temperature: float, pressure_unit: str = 'eV_per_Angstrom3') float[source]#

Tries to find volume at p and T by optimization.

Parameters#

pressure, temperature: float

The given conditions for which to optimize the volume.

pressure_unit{“eV_per_Angstrom3”, “GPa”}

The unit in which the input pressure is given. The default is eV/Ang^3.

insert_contribution(index, contribution)[source]#
classmethod read(file: str | Path)[source]#

Read a .fes file written with free_energy_surface.write().

classmethod read_old_format(files: list[str | Path] | dict[str | Path])[source]#

Reads and returns a FreeEnergySurface instance.

Takes a list or dict of file paths for the respective parametrization. If a dict, then the keys should be contribution/component names, and the values file paths. If a list, then the names are determined from the file names (without preceding path).

todict()[source]#
write(file: str | Path)[source]#

Writes the FreeEnergySurface instance to a JSON formatted file.

directupsampling.fesurface.optimize_global(function, range)[source]#
directupsampling.fesurface.read_contrib_file(file: Path)[source]#