• 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

10.34
/pymatgen/io/tests/test_packmol.py
1
from __future__ import annotations
1✔
2

3
import os
1✔
4
import tempfile
1✔
5
from pathlib import Path
1✔
6
from subprocess import TimeoutExpired
1✔
7

8
import pytest
1✔
9

10
from pymatgen.analysis.molecule_matcher import MoleculeMatcher
1✔
11
from pymatgen.core import Molecule
1✔
12
from pymatgen.io.packmol import PackmolBoxGen
1✔
13
from pymatgen.util.testing import PymatgenTest
1✔
14

15
test_dir = os.path.join(PymatgenTest.TEST_FILES_DIR, "packmol")
1✔
16

17

18
# Just skip this whole test for now since packmol is problematic.
19
if True:  # if which("packmol") is None:
20
    pytest.skip("packmol executable not present", allow_module_level=True)
1✔
21

22

23
@pytest.fixture
×
24
def ethanol():
×
25
    """
26
    Returns a Molecule of ethanol
27
    """
28
    ethanol_coords = [
×
29
        [0.00720, -0.56870, 0.00000],
30
        [-1.28540, 0.24990, 0.00000],
31
        [1.13040, 0.31470, 0.00000],
32
        [0.03920, -1.19720, 0.89000],
33
        [0.03920, -1.19720, -0.89000],
34
        [-1.31750, 0.87840, 0.89000],
35
        [-1.31750, 0.87840, -0.89000],
36
        [-2.14220, -0.42390, -0.00000],
37
        [1.98570, -0.13650, -0.00000],
38
    ]
39
    ethanol_atoms = ["C", "C", "O", "H", "H", "H", "H", "H", "H"]
×
40

41
    return Molecule(ethanol_atoms, ethanol_coords)
×
42

43

44
@pytest.fixture
×
45
def water():
×
46
    """
47
    Returns a Molecule of water
48
    """
49
    water_coords = [
×
50
        [9.626, 6.787, 12.673],
51
        [9.626, 8.420, 12.673],
52
        [10.203, 7.604, 12.673],
53
    ]
54
    water_atoms = ["H", "H", "O"]
×
55

56
    return Molecule(water_atoms, water_coords)
×
57

58

59
class TestPackmolSet:
×
60
    def test_packmol_with_molecule(self, water, ethanol):
×
61
        """
62
        Test coords input as Molecule
63
        """
64
        with tempfile.TemporaryDirectory() as scratch_dir:
×
65
            pw = PackmolBoxGen().get_input_set(
×
66
                molecules=[
67
                    {"name": "water", "number": 10, "coords": water},
68
                    {"name": "ethanol", "number": 20, "coords": ethanol},
69
                ],
70
            )
71
            pw.write_input(scratch_dir)
×
72
            pw.run(scratch_dir)
×
73
            assert os.path.exists(os.path.join(scratch_dir, "packmol_out.xyz"))
×
74
            out = Molecule.from_file(os.path.join(scratch_dir, "packmol_out.xyz"))
×
75
            assert out.composition.num_atoms == 10 * 3 + 20 * 9
×
76

77
    def test_packmol_with_str(self):
×
78
        """
79
        Test coords input as strings
80
        """
81
        with tempfile.TemporaryDirectory() as scratch_dir:
×
82
            pw = PackmolBoxGen().get_input_set(
×
83
                molecules=[
84
                    {"name": "EMC", "number": 10, "coords": os.path.join(test_dir, "subdir with spaces", "EMC.xyz")},
85
                    {"name": "LiTFSi", "number": 20, "coords": os.path.join(test_dir, "LiTFSi.xyz")},
86
                ],
87
            )
88
            pw.write_input(scratch_dir)
×
89
            pw.run(scratch_dir)
×
90
            assert os.path.exists(os.path.join(scratch_dir, "packmol_out.xyz"))
×
91
            out = Molecule.from_file(os.path.join(scratch_dir, "packmol_out.xyz"))
×
92
            assert out.composition.num_atoms == 10 * 15 + 20 * 16
×
93

94
    def test_packmol_with_path(self):
×
95
        """
96
        Test coords input as Path. Use a subdirectory with spaces.
97
        """
98
        p1 = Path(os.path.join(test_dir, "subdir with spaces", "EMC.xyz"))
×
99
        p2 = Path(os.path.join(test_dir, "LiTFSi.xyz"))
×
100
        with tempfile.TemporaryDirectory() as scratch_dir:
×
101
            pw = PackmolBoxGen().get_input_set(
×
102
                molecules=[
103
                    {"name": "EMC", "number": 10, "coords": p1},
104
                    {"name": "LiTFSi", "number": 20, "coords": p2},
105
                ],
106
            )
107
            pw.write_input(scratch_dir)
×
108
            pw.run(scratch_dir)
×
109
            assert os.path.exists(os.path.join(scratch_dir, "packmol_out.xyz"))
×
110
            out = Molecule.from_file(os.path.join(scratch_dir, "packmol_out.xyz"))
×
111
            assert out.composition.num_atoms == 10 * 15 + 20 * 16
×
112

113
    def test_control_params(self, water, ethanol):
×
114
        """
115
        Check that custom control_params work and that ValueError
116
        is raised when 'ERROR' appears in stdout (even if return code is 0)
117
        """
118
        with tempfile.TemporaryDirectory() as scratch_dir:
×
119
            pw = PackmolBoxGen(
×
120
                control_params={"maxit": 0, "nloop": 0},
121
            ).get_input_set(
122
                molecules=[
123
                    {"name": "water", "number": 1000, "coords": water},
124
                    {"name": "ethanol", "number": 2000, "coords": ethanol},
125
                ],
126
            )
127
            pw.write_input(scratch_dir)
×
128
            with open(os.path.join(scratch_dir, "packmol.inp")) as f:
×
129
                input_string = f.read()
×
130
                assert "maxit 0" in input_string
×
131
                assert "nloop 0" in input_string
×
132
            with pytest.raises(ValueError):
×
133
                pw.run(scratch_dir)
×
134

135
    def test_timeout(self, water, ethanol):
×
136
        """
137
        Check that the timeout works
138
        """
139
        with tempfile.TemporaryDirectory() as scratch_dir:
×
140
            pw = PackmolBoxGen().get_input_set(
×
141
                molecules=[
142
                    {"name": "water", "number": 1000, "coords": water},
143
                    {"name": "ethanol", "number": 2000, "coords": ethanol},
144
                ],
145
            )
146
            pw.write_input(scratch_dir)
×
147
            with pytest.raises(TimeoutExpired):
×
148
                pw.run(scratch_dir, 1)
×
149

150
    def test_no_return_and_box(self, water, ethanol):
×
151
        """
152
        Make sure the code raises an error if packmol doesn't
153
        exit cleanly. Also verify the box arg works properly.
154
        """
155
        with tempfile.TemporaryDirectory() as scratch_dir:
×
156
            pw = PackmolBoxGen().get_input_set(
×
157
                molecules=[
158
                    {"name": "water", "number": 1000, "coords": water},
159
                    {"name": "ethanol", "number": 2000, "coords": ethanol},
160
                ],
161
                box=[0, 0, 0, 2, 2, 2],
162
            )
163
            pw.write_input(scratch_dir)
×
164
            with open(os.path.join(scratch_dir, "packmol.inp")) as f:
×
165
                input_string = f.read()
×
166
                assert "inside box 0 0 0 2 2 2" in input_string
×
167
            with pytest.raises(ValueError):
×
168
                pw.run(scratch_dir)
×
169

170
    def test_chdir_behavior(self, water, ethanol):
×
171
        """
172
        Make sure the code returns to the starting directory whether
173
        or not packmol exits cleanly.
174
        """
175
        startdir = str(Path.cwd())
×
176
        with tempfile.TemporaryDirectory() as scratch_dir:
×
177
            # this one will not exit cleanly b/c the box is too small
178
            pw = PackmolBoxGen().get_input_set(
×
179
                molecules=[
180
                    {"name": "water", "number": 1000, "coords": water},
181
                    {"name": "ethanol", "number": 2000, "coords": ethanol},
182
                ],
183
                box=[0, 0, 0, 2, 2, 2],
184
            )
185
            pw.write_input(scratch_dir)
×
186
            with pytest.raises(ValueError):
×
187
                pw.run(scratch_dir)
×
188
            assert str(Path.cwd()) == startdir
×
189

190
        with tempfile.TemporaryDirectory() as scratch_dir:
×
191
            # this one will exit cleanly
192
            pw = PackmolBoxGen().get_input_set(
×
193
                molecules=[
194
                    {"name": "water", "number": 1000, "coords": water},
195
                    {"name": "ethanol", "number": 2000, "coords": ethanol},
196
                ],
197
            )
198
            pw.write_input(scratch_dir)
×
199
            pw.run(scratch_dir)
×
200
            assert str(Path.cwd()) == startdir
×
201

202
    def test_random_seed(self, water, ethanol):
×
203
        """
204
        Confirm that seed = -1 generates random structures
205
        while seed = 1 is deterministic
206
        """
207
        mm = MoleculeMatcher()
×
208

209
        # deterministic output
210
        with tempfile.TemporaryDirectory() as scratch_dir:
×
211
            pw = PackmolBoxGen(
×
212
                seed=1,
213
                inputfile="input.in",
214
                outputfile="output.xyz",
215
            ).get_input_set(
216
                # scratch_dir,
217
                molecules=[
218
                    {"name": "water", "number": 10, "coords": water},
219
                    {"name": "ethanol", "number": 20, "coords": ethanol},
220
                ],
221
            )
222
            pw.write_input(scratch_dir)
×
223
            pw.run(scratch_dir)
×
224
            out1 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
×
225
            pw.run(scratch_dir)
×
226
            out2 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
×
227
            assert mm.fit(out1, out2)
×
228

229
        # randomly generated structures
230
        with tempfile.TemporaryDirectory() as scratch_dir:
×
231
            pw = PackmolBoxGen(
×
232
                seed=-1,
233
                inputfile="input.in",
234
                outputfile="output.xyz",
235
            ).get_input_set(
236
                molecules=[
237
                    {"name": "water", "number": 10, "coords": water},
238
                    {"name": "ethanol", "number": 20, "coords": ethanol},
239
                ],
240
            )
241
            pw.write_input(scratch_dir)
×
242
            pw.run(scratch_dir)
×
243
            out1 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
×
244
            pw.run(scratch_dir)
×
245
            out2 = Molecule.from_file(os.path.join(scratch_dir, "output.xyz"))
×
246
            assert not mm.fit(out1, out2)
×
247

248
    def test_arbitrary_filenames(self, water, ethanol):
×
249
        """
250
        Make sure custom input and output filenames work.
251
        Use a subdirectory with spaces.
252
        """
253
        with tempfile.TemporaryDirectory() as scratch_dir:
×
254
            os.mkdir(os.path.join(scratch_dir, "subdirectory with spaces"))
×
255
            pw = PackmolBoxGen(
×
256
                inputfile="input.in", outputfile=Path("output.xyz"), stdoutfile=Path("stdout.txt")
257
            ).get_input_set(
258
                molecules=[
259
                    {"name": "water", "number": 10, "coords": water},
260
                    {"name": "ethanol", "number": 20, "coords": ethanol},
261
                ],
262
            )
263
            pw.write_input(
×
264
                os.path.join(scratch_dir, "subdirectory with spaces"),
265
            )
266
            assert os.path.exists(os.path.join(scratch_dir, "subdirectory with spaces", "input.in"))
×
267
            pw.run(
×
268
                os.path.join(scratch_dir, "subdirectory with spaces"),
269
            )
270
            assert os.path.exists(os.path.join(scratch_dir, "subdirectory with spaces", "output.xyz"))
×
271
            assert os.path.exists(os.path.join(scratch_dir, "subdirectory with spaces", "stdout.txt"))
×
272
            out = Molecule.from_file(os.path.join(scratch_dir, "subdirectory with spaces", "output.xyz"))
×
273
            assert out.composition.num_atoms == 10 * 3 + 20 * 9
×
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