PageRenderTime 51ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/rmgpy/statmech/conformerTest.py

https://github.com/GreenGroup/RMG-Py
Python | 310 lines | 181 code | 30 blank | 99 comment | 11 complexity | b31e4df3ec73d7b0c6246f61581a8162 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. #!/usr/bin/env python3
  2. ###############################################################################
  3. # #
  4. # RMG - Reaction Mechanism Generator #
  5. # #
  6. # Copyright (c) 2002-2021 Prof. William H. Green (whgreen@mit.edu), #
  7. # Prof. Richard H. West (r.west@neu.edu) and the RMG Team (rmg_dev@mit.edu) #
  8. # #
  9. # Permission is hereby granted, free of charge, to any person obtaining a #
  10. # copy of this software and associated documentation files (the 'Software'), #
  11. # to deal in the Software without restriction, including without limitation #
  12. # the rights to use, copy, modify, merge, publish, distribute, sublicense, #
  13. # and/or sell copies of the Software, and to permit persons to whom the #
  14. # Software is furnished to do so, subject to the following conditions: #
  15. # #
  16. # The above copyright notice and this permission notice shall be included in #
  17. # all copies or substantial portions of the Software. #
  18. # #
  19. # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
  20. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
  21. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
  22. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
  23. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #
  24. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER #
  25. # DEALINGS IN THE SOFTWARE. #
  26. # #
  27. ###############################################################################
  28. """
  29. This script contains unit tests of the :mod:`rmgpy.statmech.conformer` module.
  30. """
  31. import unittest
  32. import numpy as np
  33. import rmgpy.constants as constants
  34. from rmgpy.statmech import Conformer, HarmonicOscillator, HinderedRotor, \
  35. IdealGasTranslation, LinearRotor, NonlinearRotor
  36. ################################################################################
  37. class TestConformer(unittest.TestCase):
  38. """
  39. Contains unit tests of the :class:`Conformer` class.
  40. """
  41. def setUp(self):
  42. """
  43. A function run before each unit test in this class.
  44. """
  45. self.ethylene = Conformer(
  46. E0=(0.0, "kJ/mol"),
  47. modes=[
  48. IdealGasTranslation(mass=(28.03, "amu")),
  49. NonlinearRotor(inertia=([3.41526, 16.6498, 20.065], "amu*angstrom^2"), symmetry=4),
  50. HarmonicOscillator(frequencies=([828.397, 970.652, 977.223, 1052.93, 1233.55, 1367.56, 1465.09,
  51. 1672.25, 3098.46, 3111.7, 3165.79, 3193.54], "cm^-1")),
  52. ],
  53. spin_multiplicity=1,
  54. optical_isomers=1,
  55. )
  56. self.oxygen = Conformer(
  57. E0=(0.0, "kJ/mol"),
  58. modes=[
  59. IdealGasTranslation(mass=(31.99, "amu")),
  60. LinearRotor(inertia=(11.6056, "amu*angstrom^2"), symmetry=2),
  61. HarmonicOscillator(frequencies=([1621.54], "cm^-1")),
  62. ],
  63. spin_multiplicity=3,
  64. optical_isomers=1,
  65. )
  66. # The following data is for ethane at the CBS-QB3 level
  67. self.coordinates = np.array([
  68. [0.0000, 0.0000, 0.0000],
  69. [-0.0000, -0.0000, 1.0936],
  70. [1.0430, -0.0000, -0.3288],
  71. [-0.4484, 0.9417, -0.3288],
  72. [-0.7609, -1.2051, -0.5580],
  73. [-0.7609, -1.2051, -1.6516],
  74. [-0.3125, -2.1468, -0.2292],
  75. [-1.8039, -1.2051, -0.2293],
  76. ])
  77. self.number = np.array([6, 1, 1, 1, 6, 1, 1, 1])
  78. self.mass = np.array([12, 1.007825, 1.007825, 1.007825, 12, 1.007825, 1.007825, 1.007825])
  79. self.E0 = -93.5097
  80. self.conformer = Conformer(
  81. E0=(self.E0, "kJ/mol"),
  82. modes=[
  83. IdealGasTranslation(mass=(30.0469, "amu")),
  84. NonlinearRotor(inertia=([6.27071, 25.3832, 25.3833], "amu*angstrom^2"), symmetry=6),
  85. HarmonicOscillator(frequencies=([818.917, 819.479, 987.099, 1206.76, 1207.05, 1396, 1411.35, 1489.73,
  86. 1489.95, 1492.49, 1492.66, 2995.36, 2996.06, 3040.77, 3041, 3065.86,
  87. 3066.02], "cm^-1")),
  88. HinderedRotor(inertia=(1.56768, "amu*angstrom^2"), symmetry=3,
  89. barrier=(2.69401, "kcal/mol"), quantum=False, semiclassical=False),
  90. ],
  91. spin_multiplicity=1,
  92. optical_isomers=1,
  93. coordinates=(self.coordinates, "angstrom"),
  94. number=self.number,
  95. mass=(self.mass, "amu"),
  96. )
  97. def test_get_partition_function_ethylene(self):
  98. """
  99. Test the StatMech.get_partition_function() method for ethylene.
  100. """
  101. t_list = np.array([300, 500, 1000, 1500, 2000])
  102. q_exp_list = np.array([4.05311e+09, 4.19728e+10, 2.82309e+12, 7.51135e+13, 1.16538e+15])
  103. for temperature, q_exp in zip(t_list, q_exp_list):
  104. q_act = self.ethylene.get_partition_function(temperature)
  105. self.assertAlmostEqual(q_exp, q_act, delta=1e-4 * q_exp)
  106. def test_get_heat_capacity_ethylene(self):
  107. """
  108. Test the StatMech.get_heat_capacity() method for ethylene.
  109. """
  110. t_list = np.array([300, 500, 1000, 1500, 2000])
  111. cv_exp_list = np.array([5.11186, 7.40447, 11.1659, 13.1221, 14.1617]) * constants.R
  112. for temperature, cv_exp in zip(t_list, cv_exp_list):
  113. cv_act = self.ethylene.get_heat_capacity(temperature)
  114. self.assertAlmostEqual(cv_exp, cv_act, 3)
  115. def test_get_enthalpy_ethylene(self):
  116. """
  117. Test the StatMech.get_enthalpy() method for ethylene.
  118. """
  119. t_list = np.array([300, 500, 1000, 1500, 2000])
  120. h_exp_list = np.array([4.23129, 5.04826, 7.27337, 8.93167, 10.1223]) * constants.R * t_list
  121. for temperature, h_exp in zip(t_list, h_exp_list):
  122. h_act = self.ethylene.get_enthalpy(temperature)
  123. self.assertAlmostEqual(h_exp, h_act, delta=1e-4 * h_exp)
  124. def test_get_entropy_ethylene(self):
  125. """
  126. Test the StatMech.get_entropy() method for ethylene.
  127. """
  128. t_list = np.array([300, 500, 1000, 1500, 2000])
  129. s_exp_list = np.array([26.3540, 29.5085, 35.9422, 40.8817, 44.8142]) * constants.R
  130. for temperature, s_exp in zip(t_list, s_exp_list):
  131. s_act = self.ethylene.get_entropy(temperature)
  132. self.assertAlmostEqual(s_exp, s_act, 3)
  133. def test_get_sum_of_states_ethylene(self):
  134. """
  135. Test the StatMech.get_sum_of_states() method for ethylene.
  136. """
  137. e_list = np.arange(0, 5000 * 11.96, 2 * 11.96)
  138. sum_states = self.ethylene.get_sum_of_states(e_list)
  139. dens_states = self.ethylene.get_density_of_states(e_list)
  140. for n in range(10, len(e_list)):
  141. self.assertTrue(0.8 < np.sum(dens_states[0:n + 1]) / sum_states[n] < 1.25,
  142. '{0} != {1}'.format(np.sum(dens_states[0:n + 1]), sum_states[n]))
  143. def test_get_density_of_states_ethylene(self):
  144. """
  145. Test the StatMech.get_density_of_states() method for ethylene.
  146. """
  147. e_list = np.arange(0, 5000 * 11.96, 2 * 11.96)
  148. dens_states = self.ethylene.get_density_of_states(e_list)
  149. temperature = 100
  150. q_act = np.sum(dens_states * np.exp(-e_list / constants.R / temperature))
  151. q_exp = self.ethylene.get_partition_function(temperature)
  152. self.assertAlmostEqual(q_exp, q_act, delta=1e-1 * q_exp)
  153. def test_get_partition_function_oxygen(self):
  154. """
  155. Test the StatMech.get_partition_function() method for oxygen.
  156. """
  157. t_list = np.array([300, 500, 1000, 1500, 2000])
  158. q_exp_list = np.array([1.55584e+09, 9.38339e+09, 1.16459e+11, 5.51016e+11, 1.72794e+12])
  159. for temperature, q_exp in zip(t_list, q_exp_list):
  160. q_act = self.oxygen.get_partition_function(temperature)
  161. self.assertAlmostEqual(q_exp, q_act, delta=1e-4 * q_exp)
  162. def test_get_heat_capacity_oxygen(self):
  163. """
  164. Test the StatMech.get_heat_capacity() method for oxygen.
  165. """
  166. t_list = np.array([300, 500, 1000, 1500, 2000])
  167. cv_exp_list = np.array([3.52538, 3.70877, 4.14751, 4.32063, 4.39392]) * constants.R
  168. for temperature, Cv_exp in zip(t_list, cv_exp_list):
  169. cv_act = self.oxygen.get_heat_capacity(temperature)
  170. self.assertAlmostEqual(Cv_exp, cv_act, 3)
  171. def test_get_enthalpy_oxygen(self):
  172. """
  173. Test the StatMech.get_enthalpy() method for oxygen.
  174. """
  175. t_list = np.array([300, 500, 1000, 1500, 2000])
  176. h_exp_list = np.array([3.50326, 3.54432, 3.75062, 3.91623, 4.02765]) * constants.R * t_list
  177. for temperature, h_exp in zip(t_list, h_exp_list):
  178. h_act = self.oxygen.get_enthalpy(temperature)
  179. self.assertAlmostEqual(h_exp, h_act, delta=1e-4 * h_exp)
  180. def test_get_entropy_oxygen(self):
  181. """
  182. Test the StatMech.get_entropy() method for oxygen.
  183. """
  184. t_list = np.array([300, 500, 1000, 1500, 2000])
  185. s_exp_list = np.array([24.6685, 26.5065, 29.2314, 30.9513, 32.2056]) * constants.R
  186. for temperature, s_exp in zip(t_list, s_exp_list):
  187. s_act = self.oxygen.get_entropy(temperature)
  188. self.assertAlmostEqual(s_exp, s_act, 3)
  189. def test_get_sum_of_states_oxygen(self):
  190. """
  191. Test the StatMech.get_sum_of_states() method for oxygen.
  192. """
  193. e_list = np.arange(0, 5000 * 11.96, 2 * 11.96)
  194. sum_states = self.oxygen.get_sum_of_states(e_list)
  195. dens_states = self.oxygen.get_density_of_states(e_list)
  196. for n in range(10, len(e_list)):
  197. self.assertTrue(0.8 < np.sum(dens_states[0:n + 1]) / sum_states[n] < 1.25,
  198. '{0} != {1}'.format(np.sum(dens_states[0:n + 1]), sum_states[n]))
  199. def test_get_density_of_states_oxygen(self):
  200. """
  201. Test the StatMech.get_density_of_states() method for oxygen.
  202. """
  203. e_list = np.arange(0, 5000 * 11.96, 2 * 11.96)
  204. dens_states = self.oxygen.get_density_of_states(e_list)
  205. temperature = 100
  206. q_act = np.sum(dens_states * np.exp(-e_list / constants.R / temperature))
  207. q_exp = self.oxygen.get_partition_function(temperature)
  208. self.assertAlmostEqual(q_exp, q_act, delta=1e-1 * q_exp)
  209. def test_get_total_mass(self):
  210. """
  211. Test the Conformer.get_total_mass() method.
  212. """
  213. self.assertAlmostEqual(self.conformer.get_total_mass() * constants.Na * 1000.,
  214. np.sum(self.mass), 6)
  215. def test_get_center_of_mass(self):
  216. """
  217. Test the Conformer.get_center_of_mass() method.
  218. """
  219. cm = self.conformer.get_center_of_mass()
  220. self.assertAlmostEqual(cm[0] * 1e10, -0.38045, 4)
  221. self.assertAlmostEqual(cm[1] * 1e10, -0.60255, 4)
  222. self.assertAlmostEqual(cm[2] * 1e10, -0.27900, 4)
  223. def test_get_moment_of_inertia_tensor(self):
  224. """
  225. Test the Conformer.get_moment_of_inertia_tensor() method.
  226. """
  227. inertia = self.conformer.get_moment_of_inertia_tensor()
  228. self.assertAlmostEqual(inertia[0, 0] * constants.Na * 1e23, 20.65968, 4)
  229. self.assertAlmostEqual(inertia[0, 1] * constants.Na * 1e23, -7.48115, 4)
  230. self.assertAlmostEqual(inertia[0, 2] * constants.Na * 1e23, -3.46416, 4)
  231. self.assertAlmostEqual(inertia[1, 0] * constants.Na * 1e23, -7.48115, 4)
  232. self.assertAlmostEqual(inertia[1, 1] * constants.Na * 1e23, 13.53472, 4)
  233. self.assertAlmostEqual(inertia[1, 2] * constants.Na * 1e23, -5.48630, 4)
  234. self.assertAlmostEqual(inertia[2, 0] * constants.Na * 1e23, -3.46416, 4)
  235. self.assertAlmostEqual(inertia[2, 1] * constants.Na * 1e23, -5.48630, 4)
  236. self.assertAlmostEqual(inertia[2, 2] * constants.Na * 1e23, 22.84296, 4)
  237. def test_get_principal_moments_of_inertia(self):
  238. """
  239. Test the Conformer.get_principal_moments_of_inertia() method.
  240. """
  241. inertia, axes = self.conformer.get_principal_moments_of_inertia()
  242. self.assertAlmostEqual(inertia[0] * constants.Na * 1e23, 6.27074, 4)
  243. self.assertAlmostEqual(inertia[1] * constants.Na * 1e23, 25.38321, 3)
  244. self.assertAlmostEqual(inertia[2] * constants.Na * 1e23, 25.38341, 3)
  245. # For some reason the axes seem to jump around (positioning and signs change)
  246. # but the absolute values should be the same as we expect
  247. expected = sorted([0.497140,
  248. 0.610114,
  249. 0.616938,
  250. 0.787360,
  251. 0.018454,
  252. 0.616218,
  253. 0.364578,
  254. 0.792099,
  255. 0.489554])
  256. result = sorted(abs(axes).flat)
  257. for i, j in zip(expected, result):
  258. self.assertAlmostEqual(i, j, 4)
  259. return
  260. def test_get_internal_reduced_moment_of_inertia(self):
  261. """
  262. Test the Conformer.get_internal_reduced_moment_of_inertia() method.
  263. """
  264. inertia = self.conformer.get_internal_reduced_moment_of_inertia(pivots=[1, 5], top1=[1, 2, 3, 4])
  265. self.assertAlmostEqual(inertia * constants.Na * 1e23, 1.56768, 4)
  266. def test_get_number_degrees_of_freedom(self):
  267. """
  268. Test the Conformer.get_number_degrees_of_freedom() method.
  269. """
  270. # this is for ethane:
  271. number_degrees_of_freedom = self.conformer.get_number_degrees_of_freedom()
  272. self.assertEqual(number_degrees_of_freedom, 24)
  273. # this is for ethylene:
  274. # It doesn't check against 3 * n_atoms, because n_atoms is not declared.
  275. number_degrees_of_freedom = self.ethylene.get_number_degrees_of_freedom()
  276. self.assertEqual(number_degrees_of_freedom, 18)
  277. # this is for CO
  278. # It doesn't check against 3 * n_atoms, because n_atoms is not declared.
  279. number_degrees_of_freedom = self.oxygen.get_number_degrees_of_freedom()
  280. self.assertEqual(number_degrees_of_freedom, 6)