Skip to content

Commit 28f9f0a

Browse files
committed
Merge branch 'hotfix/1.0.17'
2 parents 973ccff + 324c7d4 commit 28f9f0a

File tree

8 files changed

+129
-28
lines changed

8 files changed

+129
-28
lines changed

qha/multi_configurations/different_phonon_dos.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class PartitionFunction:
3434
3535
.. math::
3636
37-
Z_{\\text{all configs}}(T, V) = \sum_{j = 1}^{N_{c}} g_{j} Z_{j}(T, V),
37+
Z_{\\text{all configs}}(T, V) = \\sum_{j = 1}^{N_{c}} g_{j} Z_{j}(T, V),
3838
3939
where :math:`N_{c}` stands for the number of configurations and :math:`g_{j}` stands for degeneracy for the
4040
:math:`j` th configuration.
@@ -113,7 +113,7 @@ def partition_functions_for_each_configuration(self):
113113
114114
.. math::
115115
116-
Z_{j}(T, V) = \exp \\bigg( -\\frac{ F_{j}(T, V) }{ k_B T } \\bigg).
116+
Z_{j}(T, V) = \\exp \\bigg( -\\frac{ F_{j}(T, V) }{ k_B T } \\bigg).
117117
118118
:return: A matrix, the partition function of each configuration of each volume.
119119
"""
@@ -123,7 +123,29 @@ def partition_functions_for_each_configuration(self):
123123
raise ImportError("Install ``bigfloat`` package to use {0} object!".format(self.__class__.__name__))
124124

125125
with bigfloat.precision(self.precision):
126-
return np.array([bigfloat.exp(d) for d in # shape = (# of volumes for each configuration, 1)
126+
# shape = (# of configurations, # of volumes for each configuration)
127+
exp = np.vectorize(bigfloat.exp)
128+
return exp(-self.aligned_free_energies_for_each_configuration / (K * self.temperature))
129+
130+
@LazyProperty
131+
def partition_functions_for_all_configurations(self):
132+
"""
133+
Sum the partition functions for all configurations.
134+
135+
.. math::
136+
137+
Z_{\\text{all configs}}(T, V) = \\sum_{j} Z_{j}(T, V).
138+
139+
:return: A vector, the partition function of each volume.
140+
"""
141+
try:
142+
import bigfloat
143+
except ImportError:
144+
raise ImportError("Install ``bigfloat`` package to use {0} object!".format(self.__class__.__name__))
145+
146+
with bigfloat.precision(self.precision):
147+
# shape = (# of volumes,)
148+
return np.array([bigfloat.exp(d) for d in
127149
logsumexp(-self.aligned_free_energies_for_each_configuration.T / (K * self.temperature),
128150
axis=1, b=self.degeneracies)])
129151

@@ -133,7 +155,7 @@ def get_free_energies(self):
133155
134156
.. math::
135157
136-
F_{\\text{all configs}}(T, V) = - k_B T \ln Z_{\\text{all configs}}(T, V).
158+
F_{\\text{all configs}}(T, V) = - k_B T \\ln Z_{\\text{all configs}}(T, V).
137159
138160
:return: The free energy on a temperature-volume grid.
139161
"""
@@ -143,5 +165,5 @@ def get_free_energies(self):
143165
raise ImportError("Install ``bigfloat`` package to use {0} object!".format(self.__class__.__name__))
144166

145167
with bigfloat.precision(self.precision):
146-
log_z = np.array([bigfloat.log(d) for d in self.partition_functions_for_each_configuration], dtype=float)
168+
log_z = np.array([bigfloat.log(d) for d in self.partition_functions_for_all_configurations], dtype=float)
147169
return -K * self.temperature * log_z

qha/multi_configurations/same_phonon_dos.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ class PartitionFunction:
3333
3434
.. math::
3535
36-
Z_{\\text{all configs}}(T, V) = \sum_{j = 1}^{N_{c}} g_{j}
37-
\\bigg\{
38-
\exp \\Big( -\\frac{ E_j(V) }{ k_B T } \\Big)
39-
\prod_{\mathbf{q}s} \\bigg(
40-
\\tfrac{\exp \\big(-\\tfrac{ \hbar \omega_{\mathbf{ q }s}^j(V) }{ 2 k_B T }\\big)}{1 - \exp \\big(-\\tfrac{ \hbar \omega_{\mathbf{ q }s}^j(V) }{ k_B T }\\big)}
41-
\\bigg)^{w_{\mathbf{ q }}^j}
42-
\\bigg\}.
36+
Z_{\\text{all configs}}(T, V) = \\sum_{j = 1}^{N_{c}} g_{j}
37+
\\bigg\\{
38+
\\exp \\Big( -\\frac{ E_j(V) }{ k_B T } \\Big)
39+
\\prod_{\\mathbf{q}s} \\bigg(
40+
\\tfrac{\\exp \\big(-\\tfrac{ \\hbar \\omega_{\\mathbf{ q }s}^j(V) }{ 2 k_B T }\\big)}{1 - \\exp \\big(-\\tfrac{ \\hbar \\omega_{\\mathbf{ q }s}^j(V) }{ k_B T }\\big)}
41+
\\bigg)^{w_{\\mathbf{ q }}^j}
42+
\\bigg\\}.
4343
4444
:param temperature: The temperature at which the partition function is calculated.
4545
:param degeneracies: An array of degeneracies of each configuration, which will not be normalized in the
@@ -119,7 +119,7 @@ def get_free_energies(self):
119119
120120
.. math::
121121
122-
F_{\\text{all configs}}(T, V) = - k_B T \ln Z_{\\text{all configs}}(T, V).
122+
F_{\\text{all configs}}(T, V) = - k_B T \\ln Z_{\\text{all configs}}(T, V).
123123
124124
:return: The free energy on the temperature-volume grid.
125125
"""
@@ -140,12 +140,12 @@ class FreeEnergy:
140140
141141
.. math::
142142
143-
F_{\\text{all configs}}(T, V) = - k_B T \ln Z_{\\text{all configs}}(T, V)
144-
= - k_B T \ln \\bigg( \sum_{j = 1}^{N_{c}} g_{j} \exp \\Big( -\\frac{ E_j(V) }{ k_B T } \\Big) \\bigg)
145-
+ \sum_{\mathbf{ q }s} w_\mathbf{ q }
146-
\\bigg\{ \\frac{ \hbar \omega_{\mathbf{ q }s}(V) }{ 2 }
147-
+ k_B \ln \\bigg( 1 - \exp \\Big( -\\frac{ \hbar \omega_{\mathbf{ q }s}(V) }{ k_B T } \\Big) \\bigg)
148-
\\bigg\}.
143+
F_{\\text{all configs}}(T, V) = - k_B T \\ln Z_{\\text{all configs}}(T, V)
144+
= - k_B T \\ln \\bigg( \\sum_{j = 1}^{N_{c}} g_{j} \\exp \\Big( -\\frac{ E_j(V) }{ k_B T } \\Big) \\bigg)
145+
+ \\sum_{\\mathbf{ q }s} w_\\mathbf{ q }
146+
\\bigg\\{ \\frac{ \\hbar \\omega_{\\mathbf{ q }s}(V) }{ 2 }
147+
+ k_B \\ln \\bigg( 1 - \\exp \\Big( -\\frac{ \\hbar \\omega_{\\mathbf{ q }s}(V) }{ k_B T } \\Big) \\bigg)
148+
\\bigg\\}.
149149
150150
:param temperature: The temperature at which the partition function is calculated.
151151
:param degeneracies: An array of degeneracies of each configuration, which will not be normalized in the

qha/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def from_yaml(filename: str) -> Settings:
8080
:return: A ``Settings`` class.
8181
"""
8282
with open(filename, 'r') as f:
83-
return Settings(yaml.load(f))
83+
return Settings(yaml.load(f, Loader=yaml.CLoader))
8484

8585

8686
global energy_unit
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import unittest
5+
6+
import numpy as np
7+
8+
from qha.calculator import DifferentPhDOSCalculator
9+
from qha.multi_configurations.different_phonon_dos import PartitionFunction
10+
from qha.settings import from_yaml
11+
12+
13+
class TestPartitionFunction(unittest.TestCase):
14+
def setUp(self) -> None:
15+
os.chdir('../../examples/ice VII/')
16+
self.user_settings = {}
17+
settings = from_yaml("settings.yaml")
18+
19+
for key in ('input', 'calculation',
20+
'thermodynamic_properties', 'static_only', 'energy_unit',
21+
'T_MIN', 'NT', 'DT', 'DT_SAMPLE',
22+
'P_MIN', 'NTV', 'DELTA_P', 'DELTA_P_SAMPLE',
23+
'volume_ratio', 'order', 'p_min_modifier',
24+
'T4FV', 'output_directory', 'high_verbosity'):
25+
try:
26+
self.user_settings.update({key: settings[key]})
27+
except KeyError:
28+
continue
29+
self.calculator = DifferentPhDOSCalculator(self.user_settings)
30+
self.calculator.read_input()
31+
self.partition_function = PartitionFunction(1000, self.calculator.degeneracies, self.calculator.q_weights,
32+
self.calculator.static_energies, self.calculator._volumes,
33+
self.calculator.frequencies)
34+
35+
def test_parse_settings(self):
36+
self.assertDictEqual(self.user_settings, {
37+
'input': {'input_01': 6, 'input_02': 96, 'input_03': 96, 'input_04': 24, 'input_05': 24, 'input_06': 192,
38+
'input_07': 384, 'input_08': 24, 'input_09': 384, 'input_10': 96, 'input_11': 192,
39+
'input_12': 384, 'input_13': 48, 'input_14': 96, 'input_15': 48, 'input_16': 96, 'input_17': 96,
40+
'input_18': 384, 'input_19': 384, 'input_20': 48, 'input_21': 384, 'input_22': 96,
41+
'input_23': 384, 'input_24': 96, 'input_25': 192, 'input_26': 192, 'input_27': 192,
42+
'input_28': 384, 'input_29': 192, 'input_30': 192,
43+
'input_31': 96, 'input_32': 192, 'input_33': 192, 'input_34': 384, 'input_35': 384,
44+
'input_36': 192, 'input_37': 24, 'input_38': 48, 'input_39': 96, 'input_40': 48, 'input_41': 384,
45+
'input_42': 12, 'input_43': 96, 'input_44': 48, 'input_45': 192, 'input_46': 12, 'input_47': 96,
46+
'input_48': 48, 'input_49': 6, 'input_50': 96, 'input_51': 24, 'input_52': 24},
47+
'calculation': 'different phonon dos',
48+
'thermodynamic_properties': ['F', 'G', 'U', 'H', 'V', 'alpha', 'gamma', 'Cp', 'Cv', 'Bt', 'Btp', 'Bs'],
49+
'static_only': False, 'energy_unit': 'ry', 'T_MIN': 0, 'NT': 401, 'DT': 1, 'DT_SAMPLE': 1, 'P_MIN': 0,
50+
'NTV': 701, 'DELTA_P': 0.1, 'DELTA_P_SAMPLE': 1, 'order': 3, 'p_min_modifier': 1.0, 'T4FV': ['0', '300'],
51+
'output_directory': './results/', 'high_verbosity': True})
52+
53+
def test_parameters(self):
54+
self.assertEqual(self.calculator.degeneracies, (
55+
6, 96, 96, 24, 24, 192, 384, 24, 384, 96, 192, 384, 48, 96, 48, 96, 96, 384, 384, 48, 384, 96, 384, 96, 192,
56+
192, 192, 384, 192, 192, 96, 192, 192, 384, 384, 192, 24, 48, 96, 48, 384, 12, 96, 48, 192, 12, 96, 48, 6,
57+
96, 24, 24))
58+
self.assertEqual(self.calculator.frequencies.shape,
59+
(52, 6, 1, 144)) # frequency on each (configuration, volume, q-point and mode)
60+
np.testing.assert_array_equal(self.calculator.q_weights,
61+
np.ones((52, 1))) # We only have Γ points, whose weight is 1.
62+
np.testing.assert_array_equal(self.calculator.volumes,
63+
[2290.7412, 2179.0756, 1666.2973, 1362.8346, 1215.6528, 1120.4173])
64+
self.assertEqual(self.calculator._volumes.shape, (52, 6))
65+
self.assertEqual(self.calculator.static_energies.shape,
66+
(52, 6)) # static energy on each (configuration, volume)
67+
68+
def test_partition_function(self):
69+
self.assertEqual(self.partition_function.unaligned_free_energies_for_each_configuration.shape,
70+
(52, 6)) # (# of configurations, # of volumes)
71+
self.assertEqual(self.partition_function.aligned_free_energies_for_each_configuration.shape,
72+
(52, 6)) # (# of configurations, # of volumes)
73+
self.assertEqual(self.partition_function.partition_functions_for_each_configuration.shape,
74+
(52, 6)) # (# of configurations, # of volumes)
75+
self.assertEqual(self.partition_function.partition_functions_for_all_configurations.shape,
76+
(6,)) # (# of volumes,)
77+
np.testing.assert_array_almost_equal(self.partition_function.get_free_energies(),
78+
[-550.74580132, -550.70964062, -550.37436235, -549.87365787, -549.43586034,
79+
-549.03029969])

qha/tests/test_read_input.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def test_read_si_input(self):
2323

2424
def test_read_ice_input(self):
2525
for i in range(1, 53):
26-
file_path = self.dir / "{0}{1:02d}".format('ice VII/input_conf', i)
26+
file_path = self.dir / "{0}{1:02d}".format('ice VII/input_', i)
2727
nm, volumes, static_energies, frequencies, q_weights = read_input(file_path)
2828
self.assertEqual(nm, 16)
2929

qha/tests/test_single_configuration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33

44
import unittest
55

6-
from qha.single_configuration import ho_free_energy
6+
from qha.statmech import ho_free_energy
77

88

99
class TestSingleConfiguration(unittest.TestCase):
1010
def test_ho_free_energy(self):
1111
self.assertEqual(ho_free_energy(0, 0), 0)
1212
self.assertEqual(ho_free_energy(1, -2), 0)
13-
self.assertEqual(ho_free_energy(0, 1000), 0.004556299262079407)
14-
self.assertEqual(ho_free_energy(100, 1000), 0.0045562989049199466)
13+
self.assertAlmostEqual(ho_free_energy(0, 1000), 0.004556299262079407)
14+
self.assertAlmostEqual(ho_free_energy(100, 1000), 0.0045562989049199466)
1515

1616

1717
if __name__ == '__main__':

qha/tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
def lagrange4(xs: Vector, ys: Vector) -> Callable[[float], float]:
2525
"""
2626
A third-order Lagrange polynomial function. Given 4 points for interpolation:
27-
:math:`(x_0, y_0), \ldots, (x_3, y_3)`, evaluate the Lagrange polynomial on :math:`x`, referenced from
27+
:math:`(x_0, y_0), \\ldots, (x_3, y_3)`, evaluate the Lagrange polynomial on :math:`x`, referenced from
2828
`Wolfram MathWorld <http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html>`_.
2929
3030
:param xs: A vector of the x-coordinates' of the 4 points.
@@ -57,7 +57,7 @@ def f(x: float) -> float:
5757
def lagrange3(xs: Vector, ys: Vector) -> Callable[[float], float]:
5858
"""
5959
A second-order Lagrange polynomial function. Given 3 points for interpolation:
60-
:math:`(x_0, y_0), \ldots, (x_2, y_2)`, evaluate the Lagrange polynomial on :math:`x`, referenced from
60+
:math:`(x_0, y_0), \\ldots, (x_2, y_2)`, evaluate the Lagrange polynomial on :math:`x`, referenced from
6161
`Wolfram MathWorld also <http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html>`_.
6262
6363
.. doctest::
@@ -103,7 +103,7 @@ def find_nearest(array: Vector, value: Scalar) -> int:
103103
Given an *array* , and given a *value* , returns an index ``j`` such that *value* is between ``array[j]``
104104
and ``array[j+1]``. The *array* must be monotonic increasing. ``j=-1`` or ``j=len(array)`` is returned
105105
to indicate that *value* is out of range below and above respectively.
106-
If *array* is unsorted, consider first using an :math:`O(n \log n)` sort and then use this function.
106+
If *array* is unsorted, consider first using an :math:`O(n \\log n)` sort and then use this function.
107107
Referenced from `Stack Overflow <https://stackoverflow.com/questions/2566412/find-nearest-value-in-numpy-array>`_.
108108
109109
:param array: An array of monotonic increasing real numbers.

qha/v2p.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
def _lagrange4(x: float, x0, x1, x2, x3, y0, y1, y2, y3) -> float:
2222
"""
2323
A third-order Lagrange polynomial function. Given 4 points for interpolation:
24-
:math:`(x_0, y_0), \ldots, (x_3, y_3)`, evaluate the Lagrange polynomial on :math:`x`.
24+
:math:`(x_0, y_0), \\ldots, (x_3, y_3)`, evaluate the Lagrange polynomial on :math:`x`.
2525
2626
:param x: The x-coordinate of the point to be evaluated.
2727
:param x0: The x-coordinate of the 1st point.

0 commit comments

Comments
 (0)