Source code for vasppy.calculation

import yaml
import re
from collections import Counter


[docs]class Calculation: """ class describing a single VASP calculation """ def __init__(self, title, energy, stoichiometry): """ Initialise a Calculation object Args: title (Str): The title string for this calculation. energy (Float): Final energy in eV. stoichiometry (Dict{Str:Int}): A dict desribing the calculation stoichiometry, e.g. { 'Ti': 1, 'O': 2 } Returns: None """ self.title = title self.energy = energy self.stoichiometry = Counter(stoichiometry) def __mul__(self, scaling): """ "Multiply" this Calculation by a scaling factor. Returns a new Calculation with the same title, but scaled energy and stoichiometry. Args: scaling (float): The scaling factor. Returns: (vasppy.Calculation): The scaled Calculation. """ new_calculation = Calculation( title=self.title, energy=self.energy * scaling, stoichiometry=self.scale_stoichiometry(scaling), ) return new_calculation def __truediv__(self, scaling): """ Implements division by a scaling factor. Returns a new Calculation with the same title, but scaled energy and stoichiometry. Args: scaling (float): The scaling factor. Returns: (vasppy.Calculation): The scaled Calculation. """ return self * (1 / scaling)
[docs] def scale_stoichiometry(self, scaling): """ Scale the Calculation stoichiometry Returns the stoichiometry, scaled by the argument scaling. Args: scaling (float): The scaling factor. Returns: (Counter(Str:Int)): The scaled stoichiometry as a :obj:`Counter` of ``label: stoichiometry`` pairs """ return {k: v * scaling for k, v in self.stoichiometry.items()}
[docs]def delta_E(reactants, products, check_balance=True): """ Calculate the change in energy for reactants --> products. Args: reactants (list(:obj:`vasppy.Calculation`)): A `list` of :obj:`vasppy.Calculation` objects. The initial state. products (list(:obj:`vasppy.Calculation`)): A `list` of :obj:`vasppy.Calculation` objects. The final state. check_balance (:obj:`bool`, optional): Check that the reaction stoichiometry is balanced. Default is ``True``. Returns: (float) The change in energy. """ if check_balance: if delta_stoichiometry(reactants, products) != {}: raise ValueError( "reaction is not balanced: {}".format( delta_stoichiometry(reactants, products) ) ) return sum([r.energy for r in products]) - sum([r.energy for r in reactants])
[docs]def delta_stoichiometry(reactants, products): """ Calculate the change in stoichiometry for reactants --> products. Args: reactants (list(:obj:`vasppy.Calculation`): A `list` of :obj:`vasppy.Calculation objects.` The initial state. products (list(:obj:`vasppy.Calculation`): A `list` of :obj:`vasppy.Calculation objects.` The final state. Returns: (Counter): The change in stoichiometry. """ totals = Counter() for r in reactants: totals.update((r * -1.0).stoichiometry) for p in products: totals.update(p.stoichiometry) to_return = {} for c in totals: if totals[c] != 0: to_return[c] = totals[c] return to_return
[docs]def energy_string_to_float(string): """ Convert a string of a calculation energy, e.g. '-1.2345 eV' to a float. Args: string (str): The string to convert. Return (float) """ energy_re = re.compile(r"(-?\d+\.\d+)") return float(energy_re.match(string).group(0))
[docs]def import_calculations_from_file(filename, skip_incomplete_records=False): """ Construct a list of :obj:`Calculation` objects by reading a YAML file. Each YAML document should include ``title``, ``stoichiometry``, and ``energy`` fields, e.g.:: title: my calculation stoichiometry: - A: 1 - B: 2 energy: -0.1234 eV Separate calculations should be distinct YAML documents, separated by `---` Args: filename (str): Name of the YAML file to read. skip_incomplete_records (bool): Do not parse YAML documents missing one or more of the required keys. Default is ``False``. Returns: (dict(vasppy.Calculation)): A dictionary of :obj:`Calculation` objects. For each :obj:`Calculation` object, the ``title`` field from the YAML input is used as the dictionary key. """ calcs = {} with open(filename, "r") as stream: docs = yaml.load_all(stream, Loader=yaml.SafeLoader) for d in docs: if skip_incomplete_records: if ( ("title" not in d) or ("stoichiometry" not in d) or ("energy" not in d) ): continue if "stoichiometry" in d: stoichiometry = Counter() for s in d["stoichiometry"]: stoichiometry.update(s) else: raise ValueError('stoichiometry not found for "{d["title"]}"') calcs[d["title"]] = Calculation( title=d["title"], stoichiometry=stoichiometry, energy=energy_string_to_float(d["energy"]), ) return calcs