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
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])
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)
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)
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)