1# -*- coding: utf-8 -*- 2# 3# Copyright (c) 2019, the cclib development team 4# 5# This file is part of cclib (http://cclib.github.io) and is distributed under 6# the terms of the BSD 3-Clause License. 7 8"""Test single point logfiles in cclib.""" 9 10import datetime 11import os 12import unittest 13 14import numpy 15import packaging 16 17from common import get_minimum_carbon_separation 18 19from skip import skipForParser 20from skip import skipForLogfile 21 22 23__filedir__ = os.path.realpath(os.path.dirname(__file__)) 24 25 26class GenericSPTest(unittest.TestCase): 27 """Generic restricted single point unittest""" 28 29 # Molecular mass of DVB in mD, and expected precision. 30 molecularmass = 130078.25 31 mass_precision = 0.10 32 33 # In STO-3G, H has 1, C has 5 (1 S and 4 SP). 34 nbasisdict = {1:1, 6:5} 35 36 # Approximate B3LYP energy of dvb after SCF in STO-3G. 37 b3lyp_energy = -10365 38 39 # Overlap first two atomic orbitals. 40 overlap01 = 0.24 41 42 # Generally, one criteria for SCF energy convergence. 43 num_scf_criteria = 1 44 45 def testnatom(self): 46 """Is the number of atoms equal to 20?""" 47 self.assertEqual(self.data.natom, 20) 48 49 def testatomnos(self): 50 """Are the atomnos correct?""" 51 52 # The nuclear charges should be integer values in a NumPy array. 53 self.assertTrue(numpy.alltrue([numpy.issubdtype(atomno, numpy.signedinteger) 54 for atomno in self.data.atomnos])) 55 self.assertEqual(self.data.atomnos.dtype.char, 'i') 56 57 self.assertEqual(self.data.atomnos.shape, (20,) ) 58 self.assertEqual(sum(self.data.atomnos == 6) + sum(self.data.atomnos == 1), 20) 59 60 @skipForParser('DALTON', 'DALTON has a very low accuracy for the printed values of all populations (2 decimals rounded in a weird way), so let it slide for now') 61 @skipForParser('FChk', 'The parser is still being developed so we skip this test') 62 @skipForLogfile('Jaguar/basicJaguar7', 'We did not print the atomic partial charges in the unit tests for this version') 63 @skipForLogfile('Molpro/basicMolpro2006', "These tests were run a long time ago and since we don't have access to Molpro 2006 anymore, we can skip this test (it is tested in 2012)") 64 @skipForParser('Turbomole','The parser is still being developed so we skip this test') 65 def testatomcharges(self): 66 """Are atomcharges (at least Mulliken) consistent with natom and sum to zero?""" 67 for type in set(['mulliken'] + list(self.data.atomcharges.keys())): 68 charges = self.data.atomcharges[type] 69 self.assertEqual(len(charges), self.data.natom) 70 self.assertAlmostEqual(sum(charges), 0.0, delta=0.001) 71 72 def testatomcoords(self): 73 """Are the dimensions of atomcoords 1 x natom x 3?""" 74 expected_shape = (1, self.data.natom, 3) 75 self.assertEqual(self.data.atomcoords.shape, expected_shape) 76 77 def testatomcoords_units(self): 78 """Are atomcoords consistent with Angstroms?""" 79 min_carbon_dist = get_minimum_carbon_separation(self.data) 80 dev = abs(min_carbon_dist - 1.34) 81 self.assertTrue(dev < 0.03, "Minimum carbon dist is %.2f (not 1.34)" % min_carbon_dist) 82 83 @skipForParser('Molcas', 'missing mult') 84 def testcharge_and_mult(self): 85 """Are the charge and multiplicity correct?""" 86 self.assertEqual(self.data.charge, 0) 87 self.assertEqual(self.data.mult, 1) 88 89 def testnbasis(self): 90 """Is the number of basis set functions correct?""" 91 count = sum([self.nbasisdict[n] for n in self.data.atomnos]) 92 self.assertEqual(self.data.nbasis, count) 93 94 @skipForParser('ADF', 'ADF parser does not extract atombasis') 95 @skipForLogfile('Jaguar/basicJaguar7', 'Data file does not contain enough information. Can we make a new one?') 96 @skipForParser('Molcas','The parser is still being developed so we skip this test') 97 @skipForParser('Turbomole','The parser is still being developed so we skip this test') 98 def testatombasis(self): 99 """Are the indices in atombasis the right amount and unique?""" 100 all = [] 101 for i, atom in enumerate(self.data.atombasis): 102 self.assertEqual(len(atom), self.nbasisdict[self.data.atomnos[i]]) 103 all += atom 104 # Test if there are as many indices as atomic orbitals. 105 self.assertEqual(len(all), self.data.nbasis) 106 # Check if all are different (every orbital indexed once). 107 self.assertEqual(len(set(all)), len(all)) 108 109 @skipForParser('FChk', 'Formatted checkpoint files do not have a section for atommasses') 110 @skipForParser('GAMESS', 'atommasses not implemented yet') 111 @skipForParser('GAMESSUK', 'atommasses not implemented yet') 112 @skipForParser('Jaguar', 'atommasses not implemented yet') 113 @skipForParser('Molcas','The parser is still being developed so we skip this test') 114 @skipForParser('Molpro', 'atommasses not implemented yet') 115 @skipForParser('NWChem', 'atommasses not implemented yet') 116 @skipForLogfile('Psi4/basicPsi4.0b5', 'atommasses not implemented yet') 117 @skipForParser('QChem', 'atommasses not implemented yet') 118 @skipForParser('Turbomole','The parser is still being developed so we skip this test') 119 def testatommasses(self): 120 """Do the atom masses sum up to the molecular mass?""" 121 mm = 1000*sum(self.data.atommasses) 122 msg = "Molecule mass: %f not %f +- %fmD" % (mm, self.molecularmass, self.mass_precision) 123 self.assertAlmostEqual(mm, self.molecularmass, delta=self.mass_precision, msg=msg) 124 125 @skipForParser('Turbomole','The parser is still being developed so we skip this test') 126 def testcoreelectrons(self): 127 """Are the coreelectrons all 0?""" 128 ans = numpy.zeros(self.data.natom, 'i') 129 numpy.testing.assert_array_equal(self.data.coreelectrons, ans) 130 131 @skipForParser('FChk', 'Formatted checkpoint files do not have a section for symmetry') 132 @skipForParser('Molcas','The parser is still being developed so we skip this test') 133 @skipForParser('Molpro', '?') 134 @skipForParser('ORCA', 'ORCA has no support for symmetry yet') 135 def testsymlabels(self): 136 """Are all the symmetry labels either Ag/u or Bg/u?""" 137 sumwronglabels = sum([x not in ['Ag', 'Bu', 'Au', 'Bg'] for x in self.data.mosyms[0]]) 138 self.assertEqual(sumwronglabels, 0) 139 140 def testhomos(self): 141 """Is the index of the HOMO equal to 34?""" 142 numpy.testing.assert_array_equal(self.data.homos, numpy.array([34],"i"), "%s != array([34],'i')" % numpy.array_repr(self.data.homos)) 143 144 @skipForParser('FChk', 'Formatted Checkpoint files do not have a section for SCF energy') 145 def testscfvaluetype(self): 146 """Are scfvalues and its elements the right type??""" 147 self.assertEqual(type(self.data.scfvalues),type([])) 148 self.assertEqual(type(self.data.scfvalues[0]),type(numpy.array([]))) 149 150 @skipForParser('FChk', 'Formatted Checkpoint files do not have a section for SCF energy') 151 def testscfenergy(self): 152 """Is the SCF energy within the target?""" 153 self.assertAlmostEqual(self.data.scfenergies[-1], self.b3lyp_energy, delta=40, msg="Final scf energy: %f not %i +- 40eV" %(self.data.scfenergies[-1], self.b3lyp_energy)) 154 155 @skipForParser('FChk', 'Formatted Checkpoint files do not have a section for SCF convergence') 156 def testscftargetdim(self): 157 """Do the scf targets have the right dimensions?""" 158 self.assertEqual(self.data.scftargets.shape, (len(self.data.scfvalues), len(self.data.scfvalues[0][0]))) 159 160 @skipForParser('FChk', 'Formatted Checkpoint files do not have a section for SCF convergence') 161 def testscftargets(self): 162 """Are correct number of SCF convergence criteria being parsed?""" 163 self.assertEqual(len(self.data.scftargets[0]), self.num_scf_criteria) 164 165 def testlengthmoenergies(self): 166 """Is the number of evalues equal to nmo?""" 167 if hasattr(self.data, "moenergies"): 168 self.assertEqual(len(self.data.moenergies[0]), self.data.nmo) 169 170 def testtypemoenergies(self): 171 """Is moenergies a list containing one numpy array?""" 172 if hasattr(self.data, "moenergies"): 173 self.assertIsInstance(self.data.moenergies, list) 174 self.assertIsInstance(self.data.moenergies[0], numpy.ndarray) 175 176 @skipForParser('DALTON', 'mocoeffs not implemented yet') 177 @skipForLogfile('Jaguar/basicJaguar7', 'Data file does not contain enough information. Can we make a new one?') 178 @skipForParser('Turbomole', 'Use of symmetry has reduced the number of mo coeffs') 179 def testdimmocoeffs(self): 180 """Are the dimensions of mocoeffs equal to 1 x nmo x nbasis?""" 181 if hasattr(self.data, "mocoeffs"): 182 self.assertIsInstance(self.data.mocoeffs, list) 183 self.assertEqual(len(self.data.mocoeffs), 1) 184 self.assertEqual(self.data.mocoeffs[0].shape, 185 (self.data.nmo, self.data.nbasis)) 186 187 @skipForParser('DALTON', 'mocoeffs not implemented yet') 188 @skipForLogfile('Jaguar/basicJaguar7', 'Data file does not contain enough information. Can we make a new one?') 189 def testfornoormo(self): 190 """Do we have NOs or MOs?""" 191 self.assertTrue( 192 hasattr(self.data, "nocoeffs") or hasattr(self.data, "mocoeffs") 193 ) 194 195 def testdimnoccnos(self): 196 """Is the length of nooccnos equal to nmo?""" 197 if hasattr(self.data, "nooccnos"): 198 self.assertIsInstance(self.data.nooccnos, numpy.ndarray) 199 self.assertEqual(len(self.data.nooccnos), self.data.nmo) 200 201 def testdimnocoeffs(self): 202 """Are the dimensions of nocoeffs equal to nmo x nmo?""" 203 if hasattr(self.data, "nocoeffs"): 204 self.assertIsInstance(self.data.nocoeffs, numpy.ndarray) 205 self.assertEqual( 206 self.data.nocoeffs.shape, (self.data.nmo, self.data.nmo) 207 ) 208 209 @skipForParser('DALTON', 'To print: **INTEGRALS\n.PROPRI') 210 @skipForParser('Molcas','The parser is still being developed so we skip this test') 211 @skipForParser('Psi4', 'Psi4 does not currently have the option to print the overlap matrix') 212 @skipForParser('QChem', 'QChem cannot print the overlap matrix') 213 @skipForParser('Turbomole','The parser is still being developed so we skip this test') 214 def testaooverlaps(self): 215 """Are the dims and values of the overlap matrix correct?""" 216 217 self.assertEqual(self.data.aooverlaps.shape, (self.data.nbasis, self.data.nbasis)) 218 219 # The matrix is symmetric. 220 row = self.data.aooverlaps[0,:] 221 col = self.data.aooverlaps[:,0] 222 self.assertEqual(sum(col - row), 0.0) 223 224 # All values on diagonal should be exactly one. 225 for i in range(self.data.nbasis): 226 self.assertEqual(self.data.aooverlaps[i,i], 1.0) 227 228 # Check some additional values that don't seem to move around between programs. 229 self.assertAlmostEqual(self.data.aooverlaps[0, 1], self.overlap01, delta=0.01) 230 self.assertAlmostEqual(self.data.aooverlaps[1, 0], self.overlap01, delta=0.01) 231 self.assertAlmostEqual(self.data.aooverlaps[3,0], 0.0) 232 self.assertAlmostEqual(self.data.aooverlaps[0,3], 0.0) 233 234 def testoptdone(self): 235 """There should be no optdone attribute set.""" 236 self.assertFalse(hasattr(self.data, 'optdone')) 237 238 @skipForParser('FChk', 'The parser is still being developed so we skip this test') 239 @skipForParser('Gaussian', 'Logfile needs to be updated') 240 @skipForParser('Jaguar', 'No dipole moments in the logfile') 241 @skipForParser('Molcas','The parser is still being developed so we skip this test') 242 def testmoments(self): 243 """Does the dipole and possible higher molecular moments look reasonable?""" 244 245 # The reference point is always a vector, but not necessarily the 246 # origin or center of mass. In this case, however, the center of mass 247 # is at the origin, so we now what to expect. 248 reference = self.data.moments[0] 249 self.assertEqual(len(reference), 3) 250 for x in reference: 251 self.assertEqual(x, 0.0) 252 253 # Length and value of dipole moment should always be correct (zero for this test). 254 dipole = self.data.moments[1] 255 self.assertEqual(len(dipole), 3) 256 for d in dipole: 257 self.assertAlmostEqual(d, 0.0, places=7) 258 259 # If the quadrupole is there, we can expect roughly -50B for the XX moment, 260 # -50B for the YY moment and and -60B for the ZZ moment. 261 if len(self.data.moments) > 2: 262 quadrupole = self.data.moments[2] 263 self.assertEqual(len(quadrupole), 6) 264 self.assertAlmostEqual(quadrupole[0], -50, delta=2.5) 265 self.assertAlmostEqual(quadrupole[3], -50, delta=2.5) 266 self.assertAlmostEqual(quadrupole[5], -60, delta=3) 267 268 # If the octupole is there, it should have 10 components and be zero. 269 if len(self.data.moments) > 3: 270 octupole = self.data.moments[3] 271 self.assertEqual(len(octupole), 10) 272 for m in octupole: 273 self.assertAlmostEqual(m, 0.0, delta=0.001) 274 275 # The hexadecapole should have 15 elements, an XXXX component of around -1900 Debye*ang^2, 276 # a YYYY component of -330B and a ZZZZ component of -50B. 277 if len(self.data.moments) > 4: 278 hexadecapole = self.data.moments[4] 279 self.assertEqual(len(hexadecapole), 15) 280 self.assertAlmostEqual(hexadecapole[0], -1900, delta=90) 281 self.assertAlmostEqual(hexadecapole[10], -330, delta=11) 282 self.assertAlmostEqual(hexadecapole[14], -50, delta=2.5) 283 284 # The are 21 unique 32-pole moments, and all are zero in this test case. 285 if len(self.data.moments) > 5: 286 moment32 = self.data.moments[5] 287 self.assertEqual(len(moment32), 21) 288 for m in moment32: 289 self.assertEqual(m, 0.0) 290 291 @skipForParser('ADF', 'reading basis set names is not implemented') 292 @skipForParser('GAMESSUK', 'reading basis set names is not implemented') 293 @skipForParser('Molcas', 'reading basis set names is not implemented') 294 @skipForParser('ORCA', 'reading basis set names is not implemented') 295 @skipForParser('Psi4', 'reading basis set names is not implemented') 296 def testmetadata_basis_set(self): 297 """Does metadata have expected keys and values?""" 298 self.assertEqual(self.data.metadata["basis_set"].lower(), "sto-3g") 299 300 @skipForParser('ADF', 'reading input file contents and name is not implemented') 301 @skipForParser('DALTON', 'reading input file contents and name is not implemented') 302 @skipForParser('FChk', 'Formatted checkpoint files do not have an input file section') 303 @skipForParser('GAMESS', 'reading input file contents and name is not implemented') 304 @skipForParser('GAMESSUK', 'reading input file contents and name is not implemented') 305 @skipForParser('Gaussian', 'reading input file contents and name is not implemented') 306 @skipForParser('Jaguar', 'reading input file contents and name is not implemented') 307 @skipForParser('Molcas', 'reading input file contents and name is not implemented') 308 @skipForParser('Molpro', 'reading input file contents and name is not implemented') 309 @skipForParser('NWChem', 'reading input file contents and name is not implemented') 310 @skipForParser('Psi4', 'reading input file contents and name is not implemented') 311 @skipForParser('QChem', 'reading input file contents and name is not implemented') 312 @skipForParser('Turbomole', 'reading input file contents and name is not implemented') 313 def testmetadata_input_file(self): 314 """Does metadata have expected keys and values?""" 315 self.assertIn("input_file_contents", self.data.metadata) 316 # TODO make input file names consistent where possible, though some 317 # programs do not allow arbitrary file extensions; for example, DALTON 318 # must end in `dal`. 319 self.assertIn("dvb_sp.in", self.data.metadata["input_file_name"]) 320 321 def testmetadata_methods(self): 322 """Does metadata have expected keys and values?""" 323 # TODO implement and unify across parsers; current values are [], 324 # ["HF"], ["RHF"], and ["DFT"] 325 self.assertIn("methods", self.data.metadata) 326 327 def testmetadata_package(self): 328 """Does metadata have expected keys and values?""" 329 # TODO How can the value be tested when the package name comes from 330 # the parser and isn't stored on ccData? 331 self.assertIn("package", self.data.metadata) 332 333 @skipForParser('FChk', 'Formatted Checkpoint files do not have section for legacy package version') 334 def testmetadata_legacy_package_version(self): 335 """Does metadata have expected keys and values?""" 336 # TODO Test specific values for each unit test. 337 self.assertIn("legacy_package_version", self.data.metadata) 338 339 @skipForParser('FChk', 'Formatted Checkpoint files do not have section for package version') 340 def testmetadata_package_version(self): 341 """Does metadata have expected keys and values?""" 342 # TODO Test specific values for each unit test. 343 self.assertIsInstance( 344 packaging.version.parse(self.data.metadata["package_version"]), 345 packaging.version.Version 346 ) 347 348 @skipForParser('ADF', 'reading point group symmetry and name is not implemented') 349 @skipForParser('FChk', 'point group symmetry cannot be printed') 350 @skipForParser('GAMESS', 'reading point group symmetry and name is not implemented') 351 @skipForParser('GAMESSUK', 'reading point group symmetry and name is not implemented') 352 @skipForParser('Gaussian', 'reading point group symmetry and name is not implemented') 353 @skipForParser('Jaguar', 'reading point group symmetry and name is not implemented') 354 @skipForParser('Molcas', 'reading point group symmetry and name is not implemented') 355 @skipForParser('Molpro', 'reading point group symmetry and name is not implemented') 356 @skipForParser('MOPAC', 'reading point group symmetry and name is not implemented') 357 @skipForParser('NWChem', 'reading point group symmetry and name is not implemented') 358 @skipForParser('ORCA', 'reading point group symmetry and name is not implemented') 359 @skipForParser('Psi3', 'reading point group symmetry and name is not implemented') 360 @skipForParser('Psi4', 'reading point group symmetry and name is not implemented') 361 @skipForParser('QChem', 'reading point group symmetry and name is not implemented') 362 @skipForParser('Turbomole', 'reading point group symmetry and name is not implemented') 363 def testmetadata_symmetry_detected(self): 364 """Does metadata have expected keys and values?""" 365 self.assertEqual(self.data.metadata["symmetry_detected"], "c2h") 366 367 @skipForParser('ADF', 'reading point group symmetry and name is not implemented') 368 @skipForParser('FChk', 'point group symmetry cannot be printed') 369 @skipForParser('GAMESS', 'reading point group symmetry and name is not implemented') 370 @skipForParser('GAMESSUK', 'reading point group symmetry and name is not implemented') 371 @skipForParser('Gaussian', 'reading point group symmetry and name is not implemented') 372 @skipForParser('Jaguar', 'reading point group symmetry and name is not implemented') 373 @skipForParser('Molcas', 'reading point group symmetry and name is not implemented') 374 @skipForParser('Molpro', 'reading point group symmetry and name is not implemented') 375 @skipForParser('MOPAC', 'reading point group symmetry and name is not implemented') 376 @skipForParser('NWChem', 'reading point group symmetry and name is not implemented') 377 @skipForParser('ORCA', 'reading point group symmetry and name is not implemented') 378 @skipForParser('Psi3', 'reading point group symmetry and name is not implemented') 379 @skipForParser('Psi4', 'reading point group symmetry and name is not implemented') 380 @skipForParser('QChem', 'reading point group symmetry and name is not implemented') 381 @skipForParser('Turbomole', 'reading point group symmetry and name is not implemented') 382 def testmetadata_symmetry_used(self): 383 """Does metadata have expected keys and values?""" 384 self.assertEqual(self.data.metadata["symmetry_used"], "c2h") 385 386 @skipForParser('ADF', 'reading cpu/wall time is not implemented for this parser') 387 @skipForParser('DALTON', 'reading cpu/wall time is not implemented for this parser') 388 @skipForParser('FChk', 'reading cpu/wall time is not implemented for this parser') 389 @skipForParser('GAMESS', 'reading cpu/wall time is not implemented for this parser') 390 @skipForParser('GAMESSUK', 'reading cpu/wall time is not implemented for this parser') 391 @skipForParser('GAMESSUS', 'reading cpu/wall time is not implemented for this parser') 392 @skipForParser('Jaguar', 'reading cpu/wall time is not implemented for this parser') 393 @skipForParser('Molcas', ' reading cpu/wall time is not implemented for this parser') 394 @skipForParser('Molpro', 'reading cpu/wall time is not implemented for this parser') 395 @skipForParser('NWChem', 'reading cpu/wall time is not implemented for this parser') 396 @skipForParser('ORCA', 'reading cpu not implemented for this parser, wall time not available') 397 @skipForParser('Psi3', 'reading cpu/wall time is not implemented for this parser') 398 @skipForParser('Psi4', 'reading cpu/wall time is not implemented for this parser') 399 @skipForParser('Turbomole', 'reading cpu/wall time is not implemented for this parser') 400 def testmetadata_times(self): 401 """Does metadata have expected keys and values of correct types?""" 402 if "wall_time" in self.data.metadata: 403 assert self.data.metadata["wall_time"] 404 assert all(isinstance(wall_time, datetime.timedelta) 405 for wall_time in self.data.metadata["wall_time"]) 406 if "cpu_time" in self.data.metadata: 407 assert self.data.metadata["cpu_time"] 408 assert all(isinstance(cpu_time, datetime.timedelta) 409 for cpu_time in self.data.metadata["cpu_time"]) 410 411class ADFSPTest(GenericSPTest): 412 """Customized restricted single point unittest""" 413 414 # ADF only prints up to 0.1mD per atom, so the precision here is worse than 0.1mD. 415 mass_precision = 0.3 416 417 foverlap00 = 1.00003 418 foverlap11 = 1.02672 419 foverlap22 = 1.03585 420 num_scf_criteria = 2 421 b3lyp_energy = -140 422 423 def testfoverlaps(self): 424 """Are the dims and values of the fragment orbital overlap matrix correct?""" 425 426 self.assertEqual(self.data.fooverlaps.shape, (self.data.nbasis, self.data.nbasis)) 427 428 # The matrix is symmetric. 429 row = self.data.fooverlaps[0,:] 430 col = self.data.fooverlaps[:,0] 431 self.assertEqual(sum(col - row), 0.0) 432 433 # Although the diagonal elements are close to zero, the SFOs 434 # are generally not normalized, so test for a few specific values. 435 self.assertAlmostEqual(self.data.fooverlaps[0, 0], self.foverlap00, delta=0.0001) 436 self.assertAlmostEqual(self.data.fooverlaps[1, 1], self.foverlap11, delta=0.0001) 437 self.assertAlmostEqual(self.data.fooverlaps[2, 2], self.foverlap22, delta=0.0001) 438 439class GaussianSPTest(GenericSPTest): 440 """Customized restricted single point unittest""" 441 442 num_scf_criteria = 3 443 444class JaguarSPTest(GenericSPTest): 445 """Customized restricted single point unittest""" 446 447 num_scf_criteria = 2 448 449class Jaguar7SPTest(JaguarSPTest): 450 """Customized restricted single point unittest""" 451 452 # Jaguar prints only 10 virtual MOs by default. Can we re-run with full output? 453 def testlengthmoenergies(self): 454 """Is the number of evalues equal to the number of occ. MOs + 10?""" 455 self.assertEqual(len(self.data.moenergies[0]), self.data.homos[0]+11) 456 457class MolcasSPTest(GenericSPTest): 458 """Customized restricted single point unittest""" 459 460 num_scf_criteria = 4 461 462class MolproSPTest(GenericSPTest): 463 """Customized restricted single point unittest""" 464 465 num_scf_criteria = 2 466 467class NWChemKSSPTest(GenericSPTest): 468 """Customized restricted single point unittest""" 469 470 num_scf_criteria = 3 471 472class PsiSPTest(GenericSPTest): 473 """Customized restricted single point HF/KS unittest""" 474 475 num_scf_criteria = 2 476 477class OrcaSPTest(GenericSPTest): 478 """Customized restricted single point unittest""" 479 480 # Orca has different weights for the masses 481 molecularmass = 130190 482 483 num_scf_criteria = 3 484 485class TurbomoleSPTest(GenericSPTest): 486 """Customized restricted single point unittest""" 487 488 num_scf_criteria = 2 489 490 def testmetadata_basis_set(self): 491 """Does metadata have expected keys and values?""" 492 # One of our test cases used sto-3g hondo 493 valid_basis = self.data.metadata["basis_set"].lower() in ("sto-3g", "sto-3g hondo") 494 self.assertTrue(valid_basis) 495 496 497class GenericDispersionTest(unittest.TestCase): 498 """Generic single-geometry dispersion correction unittest""" 499 500 dispersionenergy = -0.4005496 501 502 def testdispersionenergies(self): 503 """Is the dispersion energy parsed correctly?""" 504 self.assertTrue(len(self.data.dispersionenergies), 1) 505 self.assertAlmostEqual( 506 self.data.dispersionenergies[0], 507 self.dispersionenergy, 508 delta=2.0e-7 509 ) 510 511 512class FireflyDispersionTest(GenericDispersionTest): 513 """Customized single-geometry dispersion correction unittest""" 514 dispersionenergy = -0.4299821 515 516 517if __name__ == "__main__": 518 519 import sys 520 sys.path.insert(1, os.path.join(__filedir__, "..")) 521 522 from test_data import DataSuite 523 suite = DataSuite(['SP']) 524 suite.testall() 525