Beispiel #1
0
def free_energy(temperature: Scalar,
                q_weights: Vector,
                static_energies: Vector,
                frequencies: Array3D,
                static_only: bool = False) -> Vector:
    """
    The total free energy at a certain temperature.

    :param temperature: A ``float`` that represents the temperature at which the total free energy is calculated. This
        value is in unit 'Kelvin'.
    :param q_weights: An :math:`m \\times 1` vector that represents the weight of each q-point in Brillouin zone
        sampling. This vector can be non-normalized since a normalization will be done internally.
    :param static_energies: An :math:`n \\times 1` vector that represents the static energy of the system with
        :math:`n` different volumes. This should be the same unit as user-defined in the "settings" file.
    :param frequencies: An :math:`n \\times m \\times l` 3D array that represents the frequency read from file.
    :param static_only: If ``True``, directly return the static energies, i.e., the *static_energies* parameter itself.
        This is useful when the user just wants to see static result while keeping all other functions unchanged.
    :return: An :math:`n \\times 1` vector that represents the total free energy of the system with :math:`n` different
        volumes. The default unit is the same as in function ``ho_free_energy``.
    """
    if not np.all(np.greater_equal(q_weights, 0)):
        raise ValueError('Weights should all be greater equal than 0!')

    if static_only:
        return static_energies

    scaled_q_weights: Vector = q_weights / np.sum(q_weights)
    vibrational_energies: Vector = np.dot(
        ho_free_energy(temperature, frequencies).sum(axis=2), scaled_q_weights)
    return static_energies + vibrational_energies
Beispiel #2
0
    def on_band(self, i: int) -> Matrix:
        """
        Sample free energy on the :math:`i` th band.

        :param i: An integer labeling :math:`i` th band.
        :return: The accumulated free energy on the :math:`i`th q-point.
        """
        return ho_free_energy(self.temperature, self.omegas[:, :, i])
Beispiel #3
0
    def harmonic_part(self) -> Vector:
        """
        Calculate the harmonic contribution to the free energy.

        :return: The harmonic contribution on the temperature-volume grid.
        """
        sum_modes = np.sum(ho_free_energy(self.temperature, self.frequencies),
                           axis=2)
        return np.dot(sum_modes, self._scaled_q_weights)
Beispiel #4
0
    def on_volume(self, i: int) -> Scalar:
        """
        Sample free energy on the :math:`i` th volume.

        :param i: An integer labeling :math:`i` th volume.
        :return: The accumulated free energy on the :math:`i` th volume.
        """
        return np.vdot(
            ho_free_energy(self.temperature, self.omegas[i]).sum(axis=1),
            self._scaled_q_weights)
Beispiel #5
0
    def on_all_volumes(self) -> Vector:
        """
        Sample free energies on every volume.

        :return: A vector with length equals the number of volumes. Each element is the free energy of
            one volume.
        """
        # First calculate free energies on a 3D array, then sum along the third axis (bands),
        # at last contract weights and free energies on all q-points.
        return np.dot(
            ho_free_energy(self.temperature, self.omegas).sum(axis=2),
            self._scaled_q_weights)
 def test_ho_free_energy(self):
     self.assertEqual(ho_free_energy(0, 0), 0)
     self.assertEqual(ho_free_energy(1, -2), 0)
     self.assertAlmostEqual(ho_free_energy(0, 1000), 0.004556299262079407)
     self.assertAlmostEqual(ho_free_energy(100, 1000),
                            0.0045562989049199466)