• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

materialsproject / pymatgen / 4075885785

pending completion
4075885785

push

github

Shyue Ping Ong
Merge branch 'master' of github.com:materialsproject/pymatgen

96 of 96 new or added lines in 27 files covered. (100.0%)

81013 of 102710 relevant lines covered (78.88%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.5
/pymatgen/io/tests/test_gaussian.py
1
# Copyright (c) Pymatgen Development Team.
2
# Distributed under the terms of the MIT License.
3

4
from __future__ import annotations
1✔
5

6
import os
1✔
7
import unittest
1✔
8

9
import pytest
1✔
10
from pytest import approx
1✔
11

12
from pymatgen.core.structure import Molecule
1✔
13
from pymatgen.electronic_structure.core import Spin
1✔
14
from pymatgen.io.gaussian import GaussianInput, GaussianOutput
1✔
15
from pymatgen.util.testing import PymatgenTest
1✔
16

17
test_dir = os.path.join(PymatgenTest.TEST_FILES_DIR, "molecules")
1✔
18

19

20
class GaussianInputTest(unittest.TestCase):
1✔
21
    def setUp(self):
1✔
22
        coords = [
1✔
23
            [0.000000, 0.000000, 0.000000],
24
            [0.000000, 0.000000, 1.089000],
25
            [1.026719, 0.000000, -0.363000],
26
            [-0.513360, -0.889165, -0.363000],
27
            [-0.513360, 0.889165, -0.363000],
28
        ]
29
        self.coords = coords
1✔
30
        mol = Molecule(["C", "H", "H", "H", "H"], coords)
1✔
31
        self.gau = GaussianInput(
1✔
32
            mol,
33
            route_parameters={"SP": "", "SCF": "Tight"},
34
            input_parameters={"EPS": 12},
35
        )
36

37
    def test_init(self):
1✔
38
        mol = Molecule(["C", "H", "H", "H", "H"], self.coords)
1✔
39
        gau = GaussianInput(mol, charge=1, route_parameters={"SP": "", "SCF": "Tight"})
1✔
40
        assert gau.spin_multiplicity == 2
1✔
41
        mol = Molecule(["C", "H", "H", "H", "H"], self.coords, charge=-1)
1✔
42
        gau = GaussianInput(mol, route_parameters={"SP": "", "SCF": "Tight"})
1✔
43
        assert gau.spin_multiplicity == 2
1✔
44
        with pytest.raises(ValueError):
1✔
45
            GaussianInput(mol, spin_multiplicity=1)
1✔
46

47
    def test_str_and_from_string(self):
1✔
48
        answer = """#P HF/6-31G(d) SCF=Tight SP
1✔
49

50
H4 C1
51

52
0 1
53
C
54
H 1 B1
55
H 1 B2 2 A2
56
H 1 B3 2 A3 3 D3
57
H 1 B4 2 A4 4 D4
58

59
B1=1.089000
60
B2=1.089000
61
A2=109.471221
62
B3=1.089000
63
A3=109.471213
64
D3=120.000017
65
B4=1.089000
66
A4=109.471213
67
D4=119.999966
68

69
EPS=12
70

71
"""
72
        assert str(self.gau) == answer
1✔
73
        gau = GaussianInput.from_string(answer)
1✔
74
        assert gau.functional == "HF"
1✔
75
        assert gau.input_parameters["EPS"] == "12"
1✔
76

77
    def test_from_file(self):
1✔
78
        filepath = os.path.join(test_dir, "MethylPyrrolidine_drawn.gjf")
1✔
79
        gau = GaussianInput.from_file(filepath)
1✔
80
        assert gau.molecule.composition.formula == "H11 C5 N1"
1✔
81
        assert "opt" in gau.route_parameters
1✔
82
        assert gau.route_parameters["geom"] == "connectivity"
1✔
83
        assert gau.functional == "b3lyp"
1✔
84
        assert gau.basis_set == "6-311+g(d,p)"
1✔
85
        filepath = os.path.join(test_dir, "g305_hb.txt")
1✔
86
        with open(filepath) as f:
1✔
87
            txt = f.read()
1✔
88
        toks = txt.split("--link1--")
1✔
89
        for i, t in enumerate(toks):
1✔
90
            lines = t.strip().split("\n")
1✔
91
            lines = [l.strip() for l in lines]
1✔
92
            gau = GaussianInput.from_string("\n".join(lines))
1✔
93
            assert gau.molecule is not None
1✔
94
            if i == 0:
1✔
95
                mol = gau.molecule
1✔
96
        answer = """Full Formula (H4 O2)
1✔
97
Reduced Formula: H2O
98
Charge = 0, Spin Mult = 1
99
Sites (6)
100
0 O     0.000000     0.000000     0.000000
101
1 O     0.000000     0.000000     2.912902
102
2 H     0.892596     0.000000    -0.373266
103
3 H     0.143970     0.000219     0.964351
104
4 H    -0.582554     0.765401     3.042783
105
5 H    -0.580711    -0.766761     3.043012"""
106
        assert str(mol) == answer
1✔
107

108
    def test_from_string(self):
1✔
109
        gau_str = """%mem=5000000
1✔
110
        %chk=filename
111
        # mp2/6-31g* scf=direct
112

113
        SIH4+ H2---SIH2+ CS //MP2(full)/6-31G* MP2=-290.9225259
114

115
        1,2
116
        Si
117
        X,1,1.
118
        H,1,R1,2,HALF1
119
        H,1,R1,2,HALF1,3,180.,0
120
        X,1,1.,2,90.,3,90.,0
121
        X,1,1.,5,THETA,2,180.,0
122
        H,1,R3,6,HALF3,5,0.,0
123
        H,1,R4,6,HALF3,7,180.,0
124

125
        R1=1.47014
126
        R3=1.890457
127
        R4=1.83514
128
        HALF1=60.633314
129
        THETA=10.35464
130
        HALF3=11.861807"""
131

132
        gau = GaussianInput.from_string(gau_str)
1✔
133
        assert "X3SiH4" == gau.molecule.composition.reduced_formula
1✔
134

135
    def test_gen_basis(self):
1✔
136
        gau_str = """#N B3LYP/Gen Pseudo=Read
1✔
137

138
Test
139

140
0 1
141
C
142
H 1 B1
143
H 1 B2 2 A2
144
H 1 B3 2 A3 3 D3
145
H 1 B4 2 A4 4 D4
146

147
B1=1.089000
148
B2=1.089000
149
A2=109.471221
150
B3=1.089000
151
A3=109.471213
152
D3=120.000017
153
B4=1.089000
154
A4=109.471213
155
D4=119.999966
156

157
C 0
158
6-31G(d,p)
159
****
160
H 0
161
6-31G
162
****
163

164

165

166
"""
167
        mol = Molecule(["C", "H", "H", "H", "H"], self.coords)
1✔
168
        gen_basis = "C 0\n6-31G(d,p)\n****\nH 0\n6-31G\n****"
1✔
169
        gau = GaussianInput(
1✔
170
            mol,
171
            functional="B3LYP",
172
            gen_basis=gen_basis,
173
            dieze_tag="#N",
174
            route_parameters={"Pseudo": "Read"},
175
            title="Test",
176
        )
177
        assert gau.to_string(cart_coords=False) == gau_str
1✔
178

179
    def test_multiple_paramaters(self):
1✔
180
        """
181
        This test makes sure that input files with multi-parameter keywords
182
        and route cards with multiple lines can be parsed accurately.
183
        """
184
        filepath = os.path.join(test_dir, "l-cysteine.inp")
1✔
185
        route = {
1✔
186
            "test": None,
187
            "integral": {"grid": "UltraFine"},
188
            "opt": {"Z-Matrix": None, "maxcycles": "80", "tight": None},
189
        }
190
        gin = GaussianInput.from_file(filepath)
1✔
191
        assert gin.dieze_tag == "#n"
1✔
192
        assert gin.functional == "B3LYP"
1✔
193
        assert gin.basis_set == "6-31+G**"
1✔
194
        assert gin.route_parameters == route
1✔
195
        assert gin.title == "L-cysteine neutral"
1✔
196
        assert gin.charge == 0
1✔
197
        assert gin.spin_multiplicity == 1
1✔
198

199
    def test_no_molecule(self):
1✔
200
        """Test that we can write input files without a geometry"""
201

202
        # Makes a file without geometry
203
        input_file = GaussianInput(None, charge=0, spin_multiplicity=2)
1✔
204
        input_str = input_file.to_string().strip()
1✔
205
        assert input_str.endswith("0 2")
1✔
206

207
    def test_no_molecule_func_bset_charge_mult(self):
1✔
208
        """
209
        Test that we can write inputs files without a geometry,
210
        functional, basis set, charge or multiplicity
211
        (mainly for postprocessing jobs where this info is read from .chk)
212
        """
213
        gau_str = "#P chkbasis geom=allcheck guess=(only,read) pop=naturalorbital\n"
1✔
214
        gau_str += "\n"
1✔
215
        gau_str += "Restart"
1✔
216

217
        input_file = GaussianInput(
1✔
218
            None,
219
            charge=None,
220
            spin_multiplicity=None,
221
            functional=None,
222
            basis_set=None,
223
            route_parameters={
224
                "chkbasis": None,
225
                "geom": "allcheck",
226
                "guess": {"only": None, "read": None},
227
                "pop": "naturalorbital",
228
            },
229
        )
230
        input_str = input_file.to_string().strip()
1✔
231
        assert input_str == gau_str
1✔
232

233

234
class GaussianOutputTest(unittest.TestCase):
1✔
235
    # todo: Add unittest for PCM type output.
236

237
    def setUp(self):
1✔
238
        self.gauout = GaussianOutput(os.path.join(test_dir, "methane.log"))
1✔
239

240
    def test_resume(self):
1✔
241
        resume = self.gauout.resumes[0]
1✔
242
        methane_resume = r"""1\1\GINC-SHYUE-LAPTOP\FOpt\RHF\3-21G\C1H4\SHYUE\27-Feb-2008\0\\#p hf/3
1✔
243
        -21G opt\\Title Card Required\\0,1\C,0.,0.,0.\H,0.,0.,1.0829014152\H,1
244
        .0209692454,0.,-0.3609671384\H,-0.5104846227,-0.884185303,-0.360967138
245
        4\H,-0.5104846227,0.884185303,-0.3609671384\\Version=IA32L-G03RevD.01\
246
        State=1-A1\HF=-39.9768776\RMSD=3.210e-09\RMSF=5.014e-08\Thermal=0.\Dip
247
        ole=0.,0.,0.\PG=TD [O(C1),4C3(H1)]\\@"""
248
        methane_resume = "".join(r.strip() for r in methane_resume.split("\n"))
1✔
249

250
        assert resume == methane_resume
1✔
251

252
    def test_props(self):
1✔
253
        gau = self.gauout
1✔
254
        assert len(gau.energies) == 3
1✔
255
        assert gau.energies[-1] == approx(-39.9768775602)
1✔
256
        assert len(gau.structures) == 4
1✔
257
        for mol in gau.structures:
1✔
258
            assert mol.formula == "H4 C1"
1✔
259
        assert "opt" in gau.route_parameters
1✔
260
        assert "Minimum" == gau.stationary_type
1✔
261
        assert "hf" == gau.functional
1✔
262
        assert "3-21G" == gau.basis_set
1✔
263
        assert 17 == gau.num_basis_func
1✔
264
        d = gau.as_dict()
1✔
265
        assert d["input"]["functional"] == "hf"
1✔
266
        assert d["output"]["final_energy"] == approx(-39.9768775602)
1✔
267
        assert len(gau.cart_forces) == 3
1✔
268
        assert gau.cart_forces[0][5] == 0.009791094
1✔
269
        assert gau.cart_forces[0][-1] == -0.003263698
1✔
270
        assert gau.cart_forces[2][-1] == -0.000000032
1✔
271
        assert gau.eigenvalues[Spin.up][-1] == 1.95586
1✔
272
        assert gau.num_basis_func == 17
1✔
273
        assert gau.is_spin is False
1✔
274

275
        ch2o_co2 = GaussianOutput(os.path.join(test_dir, "CH2O_CO2.log"))
1✔
276
        assert len(ch2o_co2.frequencies) == 2
1✔
277
        assert len(ch2o_co2.frequencies[0]) == 6
1✔
278
        assert len(ch2o_co2.frequencies[1]) == 4
1✔
279
        assert ch2o_co2.frequencies[0][0]["frequency"] == 1203.1940
1✔
280
        assert ch2o_co2.frequencies[0][0]["symmetry"] == 'A"'
1✔
281
        assert ch2o_co2.frequencies[0][3]["IR_intensity"] == 60.9575
1✔
282
        assert ch2o_co2.frequencies[0][3]["r_mass"] == 3.7543
1✔
283
        assert ch2o_co2.frequencies[0][4]["f_constant"] == 5.4175
1✔
284
        assert ch2o_co2.frequencies[0][1]["mode"] == [
1✔
285
            0.15,
286
            0.00,
287
            0.00,
288
            -0.26,
289
            0.65,
290
            0.00,
291
            -0.26,
292
            -0.65,
293
            0.00,
294
            -0.08,
295
            0.00,
296
            0.00,
297
        ]
298
        assert ch2o_co2.frequencies[1][3]["mode"] == [0.00, 0.00, 0.88, 0.00, 0.00, -0.33, 0.00, 0.00, -0.33]
1✔
299
        assert ch2o_co2.frequencies[1][3]["symmetry"] == "SGU"
1✔
300
        assert ch2o_co2.eigenvalues[Spin.up][3] == -1.18394
1✔
301

302
        h2o = GaussianOutput(os.path.join(test_dir, "H2O_gau_vib.out"))
1✔
303
        assert len(h2o.frequencies[0]) == 3
1✔
304
        assert h2o.frequencies[0][0]["frequency"] == 1662.8033
1✔
305
        assert h2o.frequencies[0][1]["symmetry"] == "A'"
1✔
306
        assert h2o.hessian[0, 0] == 0.356872
1✔
307
        assert h2o.hessian.shape == (9, 9)
1✔
308
        assert h2o.hessian[8, :].tolist() == [
1✔
309
            -0.143692e-01,
310
            0.780136e-01,
311
            -0.362637e-01,
312
            -0.176193e-01,
313
            0.277304e-01,
314
            -0.583237e-02,
315
            0.319885e-01,
316
            -0.105744e00,
317
            0.420960e-01,
318
        ]
319

320
    def test_pop(self):
1✔
321
        gau = GaussianOutput(os.path.join(test_dir, "H2O_gau.out"))
1✔
322
        assert gau.num_basis_func == 13
1✔
323
        assert gau.electrons == (5, 5)
1✔
324
        assert gau.is_spin is True
1✔
325
        assert gau.eigenvalues[Spin.down] == [
1✔
326
            -20.55343,
327
            -1.35264,
328
            -0.72655,
329
            -0.54824,
330
            -0.49831,
331
            0.20705,
332
            0.30297,
333
            1.10569,
334
            1.16144,
335
            1.16717,
336
            1.20460,
337
            1.38903,
338
            1.67608,
339
        ]
340
        mo = gau.molecular_orbital
1✔
341
        assert len(mo) == 2  # la 6
1✔
342
        assert len(mo[Spin.down]) == 13
1✔
343
        assert len(mo[Spin.down][0]) == 3
1✔
344
        assert mo[Spin.down][5][0]["1S"] == -0.08771
1✔
345
        assert mo[Spin.down][5][0]["2PZ"] == -0.21625
1✔
346
        assert gau.eigenvectors[Spin.up][:, 5].tolist() == [
1✔
347
            -0.08771,
348
            0.10840,
349
            0.00000,
350
            0.00000,
351
            -0.21625,
352
            1.21165,
353
            0.00000,
354
            0.00000,
355
            -0.44481,
356
            -0.06348,
357
            -1.00532,
358
            -0.06348,
359
            -1.00532,
360
        ]
361

362
        assert gau.atom_basis_labels[0] == ["1S", "2S", "2PX", "2PY", "2PZ", "3S", "3PX", "3PY", "3PZ"]
1✔
363
        assert gau.atom_basis_labels[2] == ["1S", "2S"]
1✔
364

365
        gau = GaussianOutput(os.path.join(test_dir, "H2O_gau_vib.out"))
1✔
366

367
        assert gau.bond_orders[(0, 1)] == 0.7582
1✔
368
        assert gau.bond_orders[(1, 2)] == 0.0002
1✔
369

370
    def test_scan(self):
1✔
371
        gau = GaussianOutput(os.path.join(test_dir, "so2_scan.log"))
1✔
372
        d = gau.read_scan()
1✔
373
        assert -548.02102 == approx(d["energies"][-1])
1✔
374
        assert len(d["coords"]) == 1
1✔
375
        assert len(d["energies"]) == len(gau.energies)
1✔
376
        assert len(d["energies"]) == 21
1✔
377
        gau = GaussianOutput(os.path.join(test_dir, "so2_scan_opt.log"))
1✔
378
        assert 21 == len(gau.opt_structures)
1✔
379
        d = gau.read_scan()
1✔
380
        assert -548.02336 == approx(d["energies"][-1])
1✔
381
        assert len(d["coords"]) == 2
1✔
382
        assert len(d["energies"]) == 21
1✔
383
        assert 1.60000 == approx(d["coords"]["DSO"][6])
1✔
384
        assert 124.01095 == approx(d["coords"]["ASO"][2])
1✔
385
        gau = GaussianOutput(os.path.join(test_dir, "H2O_scan_G16.out"))
1✔
386
        assert 21 == len(gau.opt_structures)
1✔
387
        coords = [
1✔
388
            [0.000000, 0.000000, 0.094168],
389
            [0.000000, 0.815522, -0.376673],
390
            [0.000000, -0.815522, -0.376673],
391
        ]
392
        assert gau.opt_structures[-1].cart_coords.tolist() == coords
1✔
393
        d = gau.read_scan()
1✔
394
        assert -0.00523 == approx(d["energies"][-1])
1✔
395
        assert len(d["coords"]) == 3
1✔
396
        assert len(d["energies"]) == 21
1✔
397
        assert 0.94710 == approx(d["coords"]["R1"][6])
1✔
398
        assert 0.94277 == approx(d["coords"]["R2"][17])
1✔
399

400
    def test_geo_opt(self):
1✔
401
        """
402
        Test an optimization where no "input orientation" is outputted
403
        """
404
        gau = GaussianOutput(os.path.join(test_dir, "acene-n_gaussian09_opt.out"))
1✔
405
        assert -1812.58399675 == approx(gau.energies[-1])
1✔
406
        assert len(gau.structures) == 6
1✔
407
        # Test the first 3 atom coordinates
408
        coords = [
1✔
409
            [-13.642932, 0.715060, 0.000444],
410
            [-13.642932, -0.715060, 0.000444],
411
            [-12.444202, 1.416837, 0.000325],
412
        ]
413
        assert gau.opt_structures[-1].cart_coords[:3].tolist() == coords
1✔
414

415
    def test_td(self):
1✔
416
        gau = GaussianOutput(os.path.join(test_dir, "so2_td.log"))
1✔
417
        transitions = gau.read_excitation_energies()
1✔
418
        assert len(transitions) == 4
1✔
419
        assert transitions[0] == approx((3.9281, 315.64, 0.0054))
1✔
420

421
    def test_multiple_parameters(self):
1✔
422
        """
423
        This test makes sure that input files with multi-parameter keywords
424
        and route cards with multiple lines can be parsed accurately.
425
        """
426
        filepath = os.path.join(test_dir, "l-cysteine.out")
1✔
427
        route = {
1✔
428
            "test": None,
429
            "integral": {"grid": "UltraFine"},
430
            "opt": {"Z-Matrix": None, "maxcycles": "80", "tight": None},
431
        }
432
        gout = GaussianOutput(filepath)
1✔
433
        assert gout.dieze_tag == "#n"
1✔
434
        assert gout.functional == "B3LYP"
1✔
435
        assert gout.basis_set == "6-31+G**"
1✔
436
        assert gout.route_parameters == route
1✔
437
        assert gout.title == "L-cysteine neutral"
1✔
438
        assert gout.charge == 0
1✔
439
        assert gout.spin_multiplicity == 1
1✔
440

441

442
if __name__ == "__main__":
1✔
443
    unittest.main()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc