Ejemplo n.º 1
0
def test_temp_energy_adjustment():
    ea = TemperatureEnergyAdjustment(-0.1, 298, 5, uncertainty_per_deg=0, name="entropy")
    assert ea.name == "entropy"
    assert ea.value == -0.1 * 298 * 5
    assert ea.n_atoms == 5
    assert ea.temp == 298
    assert ea.explain == "Temperature-based energy adjustment (-0.1000 eV/K/atom x 298 K x 5 atoms)"
    ead = ea.as_dict()
    ea2 = TemperatureEnergyAdjustment.from_dict(ead)
    assert str(ead) == str(ea2.as_dict())
Ejemplo n.º 2
0
def test_temp_energy_adjustment():
    ea = TemperatureEnergyAdjustment(-0.1, 298, 5, "entropy")
    assert ea.name == "entropy"
    assert ea.value == -0.1 * 298 * 5
    assert ea.n_atoms == 5
    assert ea.temp == 298
    assert ea.description == "Temperature-based energy adjustment (-0.1000 eV/K/atom x 298 K x 5 atoms)"
Ejemplo n.º 3
0
 def test_normalize_energy_adjustments(self):
     ealist = [ManualEnergyAdjustment(5),
               ConstantEnergyAdjustment(5),
               CompositionEnergyAdjustment(1, 5, uncertainty_per_atom=0, name="Na"),
               TemperatureEnergyAdjustment(0.005, 100, 10, uncertainty_per_degK=0)
               ]
     entry = ComputedEntry("Na5Cl5", 6.9, energy_adjustments=ealist)
     assert entry.correction == 20
     entry.normalize()
     assert entry.correction == 4
     for ea in entry.energy_adjustments:
         assert ea.value == 1
Ejemplo n.º 4
0
    def test_normalize_not_in_place(self):
        ealist = [
            ManualEnergyAdjustment(5),
            ConstantEnergyAdjustment(5),
            CompositionEnergyAdjustment(1, 5, uncertainty_per_atom=0, name="Na"),
            TemperatureEnergyAdjustment(0.005, 100, 10, uncertainty_per_deg=0),
        ]
        entry = ComputedEntry("Na5Cl5", 6.9, energy_adjustments=ealist)

        normed_entry = entry.normalize(inplace=False)
        entry.normalize()

        self.assertEqual(normed_entry.as_dict(), entry.as_dict())
Ejemplo n.º 5
0
    def get_adjustments(self, entry: ComputedEntry):
        """
        Returns the corrections applied to a particular entry.

        Args:
            entry: A ComputedEntry object.

        Returns:
            [EnergyAdjustment]: Energy adjustments to be applied to entry.

        Raises:
            CompatibilityError if the required O2 and H2O energies have not been provided to
            MaterialsProjectAqueousCompatibility during init or in the list of entries passed to process_entries.
        """
        adjustments = []
        if self.o2_energy is None or self.h2o_energy is None or self.h2o_adjustments is None:
            raise CompatibilityError(
                "You did not provide the required O2 and H2O energies. "
                "{} needs these energies in order to compute "
                "the appropriate energy adjustments. Either specify the energies as arguments "
                "to {}.__init__ or run process_entries on a list that includes ComputedEntry for "
                "the ground state of O2 and H2O.".format(
                    type(self).__name__,
                    type(self).__name__))

        # compute the free energies of H2 and H2O (eV/atom) to guarantee that the
        # formationfree energy of H2O is equal to -2.4583 eV/H2O from experiments
        # (MU_H2O from pourbaix module)

        # Free energy of H2 in eV/atom, fitted using Eq. 40 of Persson et al. PRB 2012 85(23)
        # for this calculation ONLY, we need the (corrected) DFT energy of water
        self.h2_energy = round(
            0.5 * (3 * (self.h2o_energy - self.cpd_entropies["H2O"]) -
                   (self.o2_energy - self.cpd_entropies["O2"]) - MU_H2O), 6)

        # Free energy of H2O, fitted for consistency with the O2 and H2 energies.
        self.fit_h2o_energy = round(
            (2 * self.h2_energy +
             (self.o2_energy - self.cpd_entropies["O2"]) + MU_H2O) / 3, 6)

        comp = entry.composition
        rform = comp.reduced_formula

        # pin the energy of all H2 entries to h2_energy
        if rform == "H2":
            adjustments.append(
                ConstantEnergyAdjustment(
                    self.h2_energy * comp.num_atoms - entry.energy,
                    name="MP Aqueous H2 / H2O referencing",
                    cls=self.as_dict(),
                    description=
                    "Adjusts the H2 and H2O energy to reproduce the experimental "
                    "Gibbs formation free energy of H2O, based on the DFT energy "
                    "of Oxygen"))

        # pin the energy of all H2O entries to fit_h2o_energy
        elif rform == "H2O":
            adjustments.append(
                ConstantEnergyAdjustment(
                    self.fit_h2o_energy * comp.num_atoms - entry.energy,
                    name="MP Aqueous H2 / H2O referencing",
                    cls=self.as_dict(),
                    description=
                    "Adjusts the H2 and H2O energy to reproduce the experimental "
                    "Gibbs formation free energy of H2O, based on the DFT energy "
                    "of Oxygen"))

        # add minus T delta S to the DFT energy (enthalpy) of compounds that are
        # molecular-like at room temperature
        elif rform in self.cpd_entropies and rform != "H2O":
            adjustments.append(
                TemperatureEnergyAdjustment(
                    -1 * self.cpd_entropies[rform] / 298,
                    298,
                    comp.num_atoms,
                    name="Compound entropy at room temperature",
                    cls=self.as_dict(),
                    description=
                    "Adds the entropy (T delta S) to energies of compounds that "
                    "are gaseous or liquid at standard state"))

        # TODO - detection of embedded water molecules is not very sophisticated
        # Should be replaced with some kind of actual structure detection

        # For any compound except water, check to see if it is a hydrate (contains)
        # H2O in its structure. If so, adjust the energy to remove MU_H2O ev per
        # embedded water molecule.
        # in other words, we assume that the DFT energy of such a compound is really
        # a superposition of the "real" solid DFT energy (FeO in this case) and the free
        # energy of some water molecules
        # e.g. that E_FeO.nH2O = E_FeO + n * g_H2O
        # so, to get the most accurate gibbs free energy, we want to replace
        # g_FeO.nH2O = E_FeO.nH2O + dE_Fe + (n+1) * dE_O + 2n dE_H
        # with
        # g_FeO = E_FeO.nH2O + dE_Fe + dE_O + n g_H2O
        # where E is DFT energy, dE is an energy correction, and g is gibbs free energy
        # This means we have to 1) remove energy corrections associated with H and O in water
        # and then 2) remove the free energy of the water molecules
        if not rform == "H2O":
            # count the number of whole water molecules in the composition
            nH2O = int(min(comp["H"] / 2.0, comp["O"]))
            if nH2O > 0:
                # first, remove any H or O corrections already applied to H2O in the
                # formation energy so that we don't double count them
                # next, remove MU_H2O for each water molecule present
                hydrate_adjustment = -1 * (self.h2o_adjustments * 3 + MU_H2O)

                adjustments.append(
                    CompositionEnergyAdjustment(
                        hydrate_adjustment,
                        nH2O,
                        name="MP Aqueous hydrate",
                        cls=self.as_dict(),
                        description=
                        "Adjust the energy of solid hydrate compounds (compounds "
                        "containing H2O molecules in their structure) so that the "
                        "free energies of embedded H2O molecules match the experimental"
                        " value enforced by the MP Aqueous energy referencing scheme."
                    ))

        return adjustments