• 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

14.89
/pymatgen/command_line/gulp_caller.py
1
# Copyright (c) Pymatgen Development Team.
2
# Distributed under the terms of the MIT License.
3

4
"""
1✔
5
Interface with command line GULP.
6
http://projects.ivec.org
7
WARNING: you need to have GULP installed on your system.
8
"""
9

10
from __future__ import annotations
1✔
11

12
import os
1✔
13
import re
1✔
14
import subprocess
1✔
15

16
from monty.tempfile import ScratchDir
1✔
17

18
from pymatgen.analysis.bond_valence import BVAnalyzer
1✔
19
from pymatgen.core.lattice import Lattice
1✔
20
from pymatgen.core.periodic_table import Element
1✔
21
from pymatgen.core.structure import Structure
1✔
22
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
1✔
23

24
__author__ = "Bharat Medasani, Wenhao Sun"
1✔
25
__copyright__ = "Copyright 2013, The Materials Project"
1✔
26
__version__ = "1.0"
1✔
27
__maintainer__ = "Bharat Medasani"
1✔
28
__email__ = "bkmedasani@lbl.gov,wenhao@mit.edu"
1✔
29
__status__ = "Production"
1✔
30
__date__ = "Jun 22, 2013M"
1✔
31

32
_anions = set(map(Element, ["O", "S", "F", "Cl", "Br", "N", "P"]))
1✔
33
_cations = set(
1✔
34
    map(
35
        Element,
36
        [
37
            "Li",
38
            "Na",
39
            "K",  # alkali metals
40
            "Be",
41
            "Mg",
42
            "Ca",  # alkaline metals
43
            "Al",
44
            "Sc",
45
            "Ti",
46
            "V",
47
            "Cr",
48
            "Mn",
49
            "Fe",
50
            "Co",
51
            "Ni",
52
            "Cu",
53
            "Zn",
54
            "Ge",
55
            "As",
56
            "Y",
57
            "Zr",
58
            "Nb",
59
            "Mo",
60
            "Tc",
61
            "Ru",
62
            "Rh",
63
            "Pd",
64
            "Ag",
65
            "Cd",
66
            "In",
67
            "Sn",
68
            "Sb",
69
            "Hf",
70
            "Ta",
71
            "W",
72
            "Re",
73
            "Os",
74
            "Ir",
75
            "Pt",
76
            "Au",
77
            "Hg",
78
            "Tl",
79
            "Pb",
80
            "Bi",
81
            "La",
82
            "Ce",
83
            "Pr",
84
            "Nd",
85
            "Pm",
86
            "Sm",
87
            "Eu",
88
            "Gd",
89
            "Tb",
90
            "Dy",
91
            "Ho",
92
            "Er",
93
            "Tm",
94
            "Yb",
95
            "Lu",
96
        ],
97
    )
98
)
99
_gulp_kw = {
1✔
100
    # Control of calculation type
101
    "angle",
102
    "bond",
103
    "cosmo",
104
    "cosmic",
105
    "cost",
106
    "defect",
107
    "distance",
108
    "eem",
109
    "efg",
110
    "fit",
111
    "free_energy",
112
    "gasteiger",
113
    "genetic",
114
    "gradients",
115
    "md",
116
    "montecarlo",
117
    "noautobond",
118
    "noenergy",
119
    "optimise",
120
    "pot",
121
    "predict",
122
    "preserve_Q",
123
    "property",
124
    "phonon",
125
    "qeq",
126
    "qbond",
127
    "single",
128
    "sm",
129
    "static_first",
130
    "torsion",
131
    "transition_state",
132
    # Geometric variable specification
133
    "breathe",
134
    "bulk_noopt",
135
    "cellonly",
136
    "conp",
137
    "conv",
138
    "isotropic",
139
    "orthorhombic",
140
    "nobreathe",
141
    "noflgs",
142
    "shell",
143
    "unfix",
144
    # Algorithm
145
    "c6",
146
    "dipole",
147
    "fbfgs",
148
    "fix_molecule",
149
    "full",
150
    "hill",
151
    "kfull",
152
    "marvinSE",
153
    "madelung",
154
    "minimum_image",
155
    "molecule",
156
    "molmec",
157
    "molq",
158
    "newda",
159
    "noanisotropic_2b",
160
    "nod2sym",
161
    "nodsymmetry",
162
    "noelectrostatics",
163
    "noexclude",
164
    "nofcentral",
165
    "nofirst_point",
166
    "noksymmetry",
167
    "nolist_md",
168
    "nomcediff",
169
    "nonanal",
170
    "noquicksearch",
171
    "noreal",
172
    "norecip",
173
    "norepulsive",
174
    "nosasinitevery",
175
    "nosderv",
176
    "nozeropt",
177
    "numerical",
178
    "qiter",
179
    "qok",
180
    "spatial",
181
    "storevectors",
182
    "nomolecularinternalke",
183
    "voight",
184
    "zsisa",
185
    # Optimisation method
186
    "conjugate",
187
    "dfp",
188
    "lbfgs",
189
    "numdiag",
190
    "positive",
191
    "rfo",
192
    "unit",
193
    # Output control
194
    "average",
195
    "broaden_dos",
196
    "cartesian",
197
    "compare",
198
    "conserved",
199
    "dcharge",
200
    "dynamical_matrix",
201
    "eigenvectors",
202
    "global",
203
    "hessian",
204
    "hexagonal",
205
    "intensity",
206
    "linmin",
207
    "meanke",
208
    "nodensity_out",
209
    "nodpsym",
210
    "nofirst_point",
211
    "nofrequency",
212
    "nokpoints",
213
    "operators",
214
    "outcon",
215
    "prt_eam",
216
    "prt_two",
217
    "prt_regi_before",
218
    "qsas",
219
    "restore",
220
    "save",
221
    "terse",
222
    # Structure control
223
    "full",
224
    "hexagonal",
225
    "lower_symmetry",
226
    "nosymmetry",
227
    # PDF control
228
    "PDF",
229
    "PDFcut",
230
    "PDFbelow",
231
    "PDFkeep",
232
    "coreinfo",
233
    "nowidth",
234
    "nopartial",
235
    # Miscellaneous
236
    "nomodcoord",
237
    "oldunits",
238
    "zero_potential",
239
}
240

241

242
class GulpIO:
1✔
243
    """
244
    To generate GULP input and process output
245
    """
246

247
    @staticmethod
1✔
248
    def keyword_line(*args):
1✔
249
        """
250
        Checks if the input args are proper gulp keywords and
251
        generates the 1st line of gulp input. Full keywords are expected.
252

253
        Args:
254
            args: 1st line keywords
255
        """
256
        # if len(list(filter(lambda x: x in _gulp_kw, args))) != len(args):
257
        #    raise GulpError("Wrong keywords given")
258
        gin = " ".join(args)
×
259
        gin += "\n"
×
260
        return gin
×
261

262
    @staticmethod
1✔
263
    def structure_lines(
1✔
264
        structure: Structure,
265
        cell_flg: bool = True,
266
        frac_flg: bool = True,
267
        anion_shell_flg: bool = True,
268
        cation_shell_flg: bool = False,
269
        symm_flg: bool = True,
270
    ):
271
        """
272
        Generates GULP input string corresponding to pymatgen structure.
273

274
        Args:
275
            structure: pymatgen Structure object
276
            cell_flg (default = True): Option to use lattice parameters.
277
            fractional_flg (default = True): If True, fractional coordinates
278
                are used. Else, Cartesian coordinates in Angstroms are used.
279
                ******
280
                GULP convention is to use fractional coordinates for periodic
281
                structures and Cartesian coordinates for non-periodic
282
                structures.
283
                ******
284
            anion_shell_flg (default = True): If True, anions are considered
285
                polarizable.
286
            cation_shell_flg (default = False): If True, cations are
287
                considered polarizable.
288
            symm_flg (default = True): If True, symmetry information is also
289
                written.
290

291
        Returns:
292
            string containing structure for GULP input
293
        """
294
        gin = ""
×
295
        if cell_flg:
×
296
            gin += "cell\n"
×
297
            lattice = structure.lattice
×
298
            alpha, beta, gamma = lattice.angles
×
299
            a, b, c = lattice.lengths
×
300
            lat_str = f"{a:6f} {b:6f} {c:6f} {alpha:6f} {beta:6f} {gamma:6f}"
×
301
            gin += lat_str + "\n"
×
302

303
        if frac_flg:
×
304
            gin += "frac\n"
×
305
            coord_attr = "frac_coords"
×
306
        else:
307
            gin += "cart\n"
×
308
            coord_attr = "coords"
×
309
        for site in structure.sites:
×
310
            coord = [str(i) for i in getattr(site, coord_attr)]
×
311
            specie = site.specie
×
312
            core_site_desc = specie.symbol + " core " + " ".join(coord) + "\n"
×
313
            gin += core_site_desc
×
314
            if (specie in _anions and anion_shell_flg) or (specie in _cations and cation_shell_flg):
×
315
                shel_site_desc = specie.symbol + " shel " + " ".join(coord) + "\n"
×
316
                gin += shel_site_desc
×
317
            else:
318
                pass
×
319

320
        if symm_flg:
×
321
            gin += "space\n"
×
322
            gin += str(SpacegroupAnalyzer(structure).get_space_group_number()) + "\n"
×
323
        return gin
×
324

325
    @staticmethod
1✔
326
    def specie_potential_lines(structure, potential, **kwargs):
1✔
327
        """
328
        Generates GULP input specie and potential string for pymatgen
329
        structure.
330

331
        Args:
332
            structure: pymatgen.core.structure.Structure object
333
            potential: String specifying the type of potential used
334
            kwargs: Additional parameters related to potential. For
335
                potential == "buckingham",
336
                anion_shell_flg (default = False):
337
                If True, anions are considered polarizable.
338
                anion_core_chrg=float
339
                anion_shell_chrg=float
340
                cation_shell_flg (default = False):
341
                If True, cations are considered polarizable.
342
                cation_core_chrg=float
343
                cation_shell_chrg=float
344

345
        Returns:
346
            string containing specie and potential specification for gulp
347
            input.
348
        """
349
        raise NotImplementedError("gulp_specie_potential not yet implemented.\nUse library_line instead")
×
350

351
    @staticmethod
1✔
352
    def library_line(file_name):
1✔
353
        """
354
        Specifies GULP library file to read species and potential parameters.
355
        If using library don't specify species and potential
356
        in the input file and vice versa. Make sure the elements of
357
        structure are in the library file.
358

359
        Args:
360
            file_name: Name of GULP library file
361

362
        Returns:
363
            GULP input string specifying library option
364
        """
365
        gulplib_set = "GULP_LIB" in os.environ
×
366

367
        def readable(f):
×
368
            return os.path.isfile(f) and os.access(f, os.R_OK)
×
369

370
        gin = ""
×
371
        dirpath, fname = os.path.split(file_name)
×
372
        if dirpath and readable(file_name):  # Full path specified
×
373
            gin = "library " + file_name
×
374
        else:
375
            fpath = os.path.join(os.getcwd(), file_name)  # Check current dir
×
376
            if readable(fpath):
×
377
                gin = "library " + fpath
×
378
            elif gulplib_set:  # Check the GULP_LIB path
×
379
                fpath = os.path.join(os.environ["GULP_LIB"], file_name)
×
380
                if readable(fpath):
×
381
                    gin = "library " + file_name
×
382
        if gin:
×
383
            return gin + "\n"
×
384
        raise GulpError("GULP Library not found")
×
385

386
    def buckingham_input(self, structure: Structure, keywords, library=None, uc=True, valence_dict=None):
1✔
387
        """
388
        Gets a GULP input for an oxide structure and buckingham potential
389
        from library.
390

391
        Args:
392
            structure: pymatgen.core.structure.Structure
393
            keywords: GULP first line keywords.
394
            library (Default=None): File containing the species and potential.
395
            uc (Default=True): Unit Cell Flag.
396
            valence_dict: {El: valence}
397
        """
398
        gin = self.keyword_line(*keywords)
×
399
        gin += self.structure_lines(structure, symm_flg=not uc)
×
400
        if not library:
×
401
            gin += self.buckingham_potential(structure, valence_dict)
×
402
        else:
403
            gin += self.library_line(library)
×
404
        return gin
×
405

406
    @staticmethod
1✔
407
    def buckingham_potential(structure, val_dict=None):
1✔
408
        """
409
        Generate species, buckingham, and spring options for an oxide structure
410
        using the parameters in default libraries.
411

412
        Ref:
413
            1. G.V. Lewis and C.R.A. Catlow, J. Phys. C: Solid State Phys.,
414
               18, 1149-1161 (1985)
415
            2. T.S.Bush, J.D.Gale, C.R.A.Catlow and P.D. Battle,
416
               J. Mater Chem., 4, 831-837 (1994)
417

418
        Args:
419
            structure: pymatgen.core.structure.Structure
420
            val_dict (Needed if structure is not charge neutral): {El:valence}
421
                dict, where El is element.
422
        """
423
        if not val_dict:
×
424
            try:
×
425
                # If structure is oxidation state decorated, use that first.
426
                el = [site.specie.symbol for site in structure]
×
427
                valences = [site.specie.oxi_state for site in structure]
×
428
                val_dict = dict(zip(el, valences))
×
429
            except AttributeError:
×
430
                bv = BVAnalyzer()
×
431
                el = [site.specie.symbol for site in structure]
×
432
                valences = bv.get_valences(structure)
×
433
                val_dict = dict(zip(el, valences))
×
434

435
        # Try bush library first
436
        bpb = BuckinghamPotential("bush")
×
437
        bpl = BuckinghamPotential("lewis")
×
438
        gin = ""
×
439
        for key in val_dict:
×
440
            use_bush = True
×
441
            el = re.sub(r"[1-9,+,\-]", "", key)
×
442
            if el not in bpb.species_dict:
×
443
                use_bush = False
×
444
            elif val_dict[key] != bpb.species_dict[el]["oxi"]:
×
445
                use_bush = False
×
446
            if use_bush:
×
447
                gin += "species \n"
×
448
                gin += bpb.species_dict[el]["inp_str"]
×
449
                gin += "buckingham \n"
×
450
                gin += bpb.pot_dict[el]
×
451
                gin += "spring \n"
×
452
                gin += bpb.spring_dict[el]
×
453
                continue
×
454

455
            # Try lewis library next if element is not in bush
456
            # use_lewis = True
457
            if el != "O":  # For metals the key is "Metal_OxiState+"
×
458
                k = el + "_" + str(int(val_dict[key])) + "+"
×
459
                if k not in bpl.species_dict:
×
460
                    # use_lewis = False
461
                    raise GulpError(f"Element {k} not in library")
×
462
                gin += "species\n"
×
463
                gin += bpl.species_dict[k]
×
464
                gin += "buckingham\n"
×
465
                gin += bpl.pot_dict[k]
×
466
            else:
467
                gin += "species\n"
×
468
                k = "O_core"
×
469
                gin += bpl.species_dict[k]
×
470
                k = "O_shel"
×
471
                gin += bpl.species_dict[k]
×
472
                gin += "buckingham\n"
×
473
                gin += bpl.pot_dict[key]
×
474
                gin += "spring\n"
×
475
                gin += bpl.spring_dict[key]
×
476
        return gin
×
477

478
    def tersoff_input(self, structure: Structure, periodic=False, uc=True, *keywords):
1✔
479
        """
480
        Gets a GULP input with Tersoff potential for an oxide structure
481

482
        Args:
483
            structure: pymatgen.core.structure.Structure
484
            periodic (Default=False): Flag denoting whether periodic
485
                boundary conditions are used
486
            library (Default=None): File containing the species and potential.
487
            uc (Default=True): Unit Cell Flag.
488
            keywords: GULP first line keywords.
489
        """
490
        # gin="static noelectrostatics \n "
491
        gin = self.keyword_line(*keywords)
×
492
        gin += self.structure_lines(
×
493
            structure,
494
            cell_flg=periodic,
495
            frac_flg=periodic,
496
            anion_shell_flg=False,
497
            cation_shell_flg=False,
498
            symm_flg=not uc,
499
        )
500
        gin += self.tersoff_potential(structure)
×
501
        return gin
×
502

503
    @staticmethod
1✔
504
    def tersoff_potential(structure):
1✔
505
        """
506
        Generate the species, tersoff potential lines for an oxide structure
507

508
        Args:
509
            structure: pymatgen.core.structure.Structure
510
        """
511
        bv = BVAnalyzer()
×
512
        el = [site.specie.symbol for site in structure]
×
513
        valences = bv.get_valences(structure)
×
514
        el_val_dict = dict(zip(el, valences))
×
515

516
        gin = "species \n"
×
517
        qerfstring = "qerfc\n"
×
518

519
        for key, value in el_val_dict.items():
×
520
            if key != "O" and value % 1 != 0:
×
521
                raise SystemError("Oxide has mixed valence on metal")
×
522
            specie_string = key + " core " + str(value) + "\n"
×
523
            gin += specie_string
×
524
            qerfstring += key + " " + key + " 0.6000 10.0000 \n"
×
525

526
        gin += "# noelectrostatics \n Morse \n"
×
527
        met_oxi_ters = TersoffPotential().data
×
528
        for key, value in el_val_dict.items():
×
529
            if key != "O":
×
530
                metal = key + "(" + str(int(value)) + ")"
×
531
                ters_pot_str = met_oxi_ters[metal]
×
532
                gin += ters_pot_str
×
533

534
        gin += qerfstring
×
535
        return gin
×
536

537
    @staticmethod
1✔
538
    def get_energy(gout):
1✔
539
        """
540
        Args:
541
            gout ():
542

543
        Returns:
544
            Energy
545
        """
546
        energy = None
×
547
        for line in gout.split("\n"):
×
548
            if "Total lattice energy" in line and "eV" in line:
×
549
                energy = line.split()
×
550
            elif "Non-primitive unit cell" in line and "eV" in line:
×
551
                energy = line.split()
×
552
        if energy:
×
553
            return float(energy[4])
×
554
        raise GulpError("Energy not found in Gulp output")
×
555

556
    @staticmethod
1✔
557
    def get_relaxed_structure(gout):
1✔
558
        """
559
        Args:
560
            gout ():
561

562
        Returns:
563
            (Structure) relaxed structure.
564
        """
565
        # Find the structure lines
566
        structure_lines = []
×
567
        cell_param_lines = []
×
568
        output_lines = gout.split("\n")
×
569
        no_lines = len(output_lines)
×
570
        i = 0
×
571
        # Compute the input lattice parameters
572
        while i < no_lines:
×
573
            line = output_lines[i]
×
574
            if "Full cell parameters" in line:
×
575
                i += 2
×
576
                line = output_lines[i]
×
577
                a = float(line.split()[8])
×
578
                alpha = float(line.split()[11])
×
579
                line = output_lines[i + 1]
×
580
                b = float(line.split()[8])
×
581
                beta = float(line.split()[11])
×
582
                line = output_lines[i + 2]
×
583
                c = float(line.split()[8])
×
584
                gamma = float(line.split()[11])
×
585
                i += 3
×
586
                break
×
587
            if "Cell parameters" in line:
×
588
                i += 2
×
589
                line = output_lines[i]
×
590
                a = float(line.split()[2])
×
591
                alpha = float(line.split()[5])
×
592
                line = output_lines[i + 1]
×
593
                b = float(line.split()[2])
×
594
                beta = float(line.split()[5])
×
595
                line = output_lines[i + 2]
×
596
                c = float(line.split()[2])
×
597
                gamma = float(line.split()[5])
×
598
                i += 3
×
599
                break
×
600
            i += 1
×
601

602
        while i < no_lines:
×
603
            line = output_lines[i]
×
604
            if "Final fractional coordinates of atoms" in line:
×
605
                # read the site coordinates in the following lines
606
                i += 6
×
607
                line = output_lines[i]
×
608
                while line[0:2] != "--":
×
609
                    structure_lines.append(line)
×
610
                    i += 1
×
611
                    line = output_lines[i]
×
612
                    # read the cell parameters
613
                i += 9
×
614
                line = output_lines[i]
×
615
                if "Final cell parameters" in line:
×
616
                    i += 3
×
617
                    for del_i in range(6):
×
618
                        line = output_lines[i + del_i]
×
619
                        cell_param_lines.append(line)
×
620

621
                break
×
622
            i += 1
×
623

624
        # Process the structure lines
625
        if structure_lines:
×
626
            sp = []
×
627
            coords = []
×
628
            for line in structure_lines:
×
629
                fields = line.split()
×
630
                if fields[2] == "c":
×
631
                    sp.append(fields[1])
×
632
                    coords.append(list(float(x) for x in fields[3:6]))
×
633
        else:
634
            raise OSError("No structure found")
×
635

636
        if cell_param_lines:
×
637
            a = float(cell_param_lines[0].split()[1])
×
638
            b = float(cell_param_lines[1].split()[1])
×
639
            c = float(cell_param_lines[2].split()[1])
×
640
            alpha = float(cell_param_lines[3].split()[1])
×
641
            beta = float(cell_param_lines[4].split()[1])
×
642
            gamma = float(cell_param_lines[5].split()[1])
×
643
        latt = Lattice.from_parameters(a, b, c, alpha, beta, gamma)
×
644

645
        return Structure(latt, sp, coords)
×
646

647

648
class GulpCaller:
1✔
649
    """
650
    Class to run gulp from commandline
651
    """
652

653
    def __init__(self, cmd="gulp"):
1✔
654
        """
655
        Initialize with the executable if not in the standard path
656

657
        Args:
658
            cmd: Command. Defaults to gulp.
659
        """
660

661
        def is_exe(f) -> bool:
×
662
            return os.path.isfile(f) and os.access(f, os.X_OK)
×
663

664
        fpath, fname = os.path.split(cmd)
×
665
        if fpath:
×
666
            if is_exe(cmd):
×
667
                self._gulp_cmd = cmd
×
668
                return
×
669
        else:
670
            for path in os.environ["PATH"].split(os.pathsep):
×
671
                path = path.strip('"')
×
672
                file = os.path.join(path, cmd)
×
673
                if is_exe(file):
×
674
                    self._gulp_cmd = file
×
675
                    return
×
676
        raise GulpError("Executable not found")
×
677

678
    def run(self, gin):
1✔
679
        """
680
        Run GULP using the gin as input
681

682
        Args:
683
            gin: GULP input string
684

685
        Returns:
686
            gout: GULP output string
687
        """
688
        with ScratchDir("."):
×
689
            with subprocess.Popen(
×
690
                self._gulp_cmd,
691
                stdout=subprocess.PIPE,
692
                stdin=subprocess.PIPE,
693
                stderr=subprocess.PIPE,
694
            ) as p:
695
                out, err = p.communicate(bytearray(gin, "utf-8"))
×
696
            out = out.decode("utf-8")
×
697
            err = err.decode("utf-8")
×
698

699
            if "Error" in err or "error" in err:
×
700
                print(gin)
×
701
                print("----output_0---------")
×
702
                print(out)
×
703
                print("----End of output_0------\n\n\n")
×
704
                print("----output_1--------")
×
705
                print(out)
×
706
                print("----End of output_1------")
×
707
                raise GulpError(err)
×
708

709
            # We may not need this
710
            if "ERROR" in out:
×
711
                raise GulpError(out)
×
712

713
            # Sometimes optimisation may fail to reach convergence
714
            conv_err_string = "Conditions for a minimum have not been satisfied"
×
715
            if conv_err_string in out:
×
716
                raise GulpConvergenceError()
×
717

718
            gout = ""
×
719
            for line in out.split("\n"):
×
720
                gout = gout + line + "\n"
×
721
            return gout
×
722

723

724
def get_energy_tersoff(structure, gulp_cmd="gulp"):
1✔
725
    """
726
    Compute the energy of a structure using Tersoff potential.
727

728
    Args:
729
        structure: pymatgen.core.structure.Structure
730
        gulp_cmd: GULP command if not in standard place
731
    """
732
    gio = GulpIO()
×
733
    gc = GulpCaller(gulp_cmd)
×
734
    gin = gio.tersoff_input(structure)
×
735
    gout = gc.run(gin)
×
736
    return gio.get_energy(gout)
×
737

738

739
def get_energy_buckingham(structure, gulp_cmd="gulp", keywords=("optimise", "conp", "qok"), valence_dict=None):
1✔
740
    """
741
    Compute the energy of a structure using Buckingham potential.
742

743
    Args:
744
        structure: pymatgen.core.structure.Structure
745
        gulp_cmd: GULP command if not in standard place
746
        keywords: GULP first line keywords
747
        valence_dict: {El: valence}. Needed if the structure is not charge
748
            neutral.
749
    """
750
    gio = GulpIO()
×
751
    gc = GulpCaller(gulp_cmd)
×
752
    gin = gio.buckingham_input(structure, keywords, valence_dict=valence_dict)
×
753
    gout = gc.run(gin)
×
754
    return gio.get_energy(gout)
×
755

756

757
def get_energy_relax_structure_buckingham(structure, gulp_cmd="gulp", keywords=("optimise", "conp"), valence_dict=None):
1✔
758
    """
759
    Relax a structure and compute the energy using Buckingham potential.
760

761
    Args:
762
        structure: pymatgen.core.structure.Structure
763
        gulp_cmd: GULP command if not in standard place
764
        keywords: GULP first line keywords
765
        valence_dict: {El: valence}. Needed if the structure is not charge
766
            neutral.
767
    """
768
    gio = GulpIO()
×
769
    gc = GulpCaller(gulp_cmd)
×
770
    gin = gio.buckingham_input(structure, keywords, valence_dict=valence_dict)
×
771
    gout = gc.run(gin)
×
772
    energy = gio.get_energy(gout)
×
773
    relax_structure = gio.get_relaxed_structure(gout)
×
774
    return energy, relax_structure
×
775

776

777
class GulpError(Exception):
1✔
778
    """
779
    Exception class for GULP.
780
    Raised when the GULP gives an error
781
    """
782

783
    def __init__(self, msg):
1✔
784
        """
785
        Args:
786
            msg (str): Message
787
        """
788
        self.msg = msg
×
789

790
    def __str__(self):
1✔
791
        return "GulpError : " + self.msg
×
792

793

794
class GulpConvergenceError(Exception):
1✔
795
    """
796
    Exception class for GULP.
797
    Raised when proper convergence is not reached in Mott-Littleton
798
    defect energy optimisation procedure in GULP
799
    """
800

801
    def __init__(self, msg=""):
1✔
802
        """
803
        Args:
804
            msg (str): Message
805
        """
806
        self.msg = msg
×
807

808
    def __str__(self):
1✔
809
        return self.msg
×
810

811

812
class BuckinghamPotential:
1✔
813
    """
814
    Generate the Buckingham Potential Table from the bush.lib and lewis.lib.
815

816
    Ref:
817
    T.S.Bush, J.D.Gale, C.R.A.Catlow and P.D. Battle,  J. Mater Chem.,
818
    4, 831-837 (1994).
819
    G.V. Lewis and C.R.A. Catlow, J. Phys. C: Solid State Phys., 18,
820
    1149-1161 (1985)
821
    """
822

823
    def __init__(self, bush_lewis_flag):
1✔
824
        """
825
        Args:
826
            bush_lewis_flag (str): Flag for using Bush or Lewis potential.
827
        """
828
        assert bush_lewis_flag in {"bush", "lewis"}
×
829
        pot_file = "bush.lib" if bush_lewis_flag == "bush" else "lewis.lib"
×
830
        with open(os.path.join(os.environ["GULP_LIB"], pot_file)) as f:
×
831
            # In lewis.lib there is no shell for cation
832
            species_dict, pot_dict, spring_dict = {}, {}, {}
×
833
            sp_flg, pot_flg, spring_flg = False, False, False
×
834
            for row in f:
×
835
                if row[0] == "#":
×
836
                    continue
×
837
                if row.split()[0] == "species":
×
838
                    sp_flg, pot_flg, spring_flg = True, False, False
×
839
                    continue
×
840
                if row.split()[0] == "buckingham":
×
841
                    sp_flg, pot_flg, spring_flg = False, True, False
×
842
                    continue
×
843
                if row.split()[0] == "spring":
×
844
                    sp_flg, pot_flg, spring_flg = False, False, True
×
845
                    continue
×
846

847
                elmnt = row.split()[0]
×
848
                if sp_flg:
×
849
                    if bush_lewis_flag == "bush":
×
850
                        if elmnt not in species_dict:
×
851
                            species_dict[elmnt] = {"inp_str": "", "oxi": 0}
×
852
                        species_dict[elmnt]["inp_str"] += row
×
853
                        species_dict[elmnt]["oxi"] += float(row.split()[2])
×
854
                    elif bush_lewis_flag == "lewis":
×
855
                        if elmnt == "O":
×
856
                            if row.split()[1] == "core":
×
857
                                species_dict["O_core"] = row
×
858
                            if row.split()[1] == "shel":
×
859
                                species_dict["O_shel"] = row
×
860
                        else:
861
                            metal = elmnt.split("_")[0]
×
862
                            # oxi_state = metaloxi.split('_')[1][0]
863
                            species_dict[elmnt] = metal + " core " + row.split()[2] + "\n"
×
864
                    continue
×
865

866
                if pot_flg:
×
867
                    if bush_lewis_flag == "bush":
×
868
                        pot_dict[elmnt] = row
×
869
                    elif bush_lewis_flag == "lewis":
×
870
                        if elmnt == "O":
×
871
                            pot_dict["O"] = row
×
872
                        else:
873
                            metal = elmnt.split("_")[0]
×
874
                            # oxi_state = metaloxi.split('_')[1][0]
875
                            pot_dict[elmnt] = metal + " " + " ".join(row.split()[1:]) + "\n"
×
876
                    continue
×
877

878
                if spring_flg:
×
879
                    spring_dict[elmnt] = row
×
880

881
            if bush_lewis_flag == "bush":
×
882
                # Fill the null keys in spring dict with empty strings
883
                for key in pot_dict:
×
884
                    if key not in spring_dict:
×
885
                        spring_dict[key] = ""
×
886

887
            self.species_dict = species_dict
×
888
            self.pot_dict = pot_dict
×
889
            self.spring_dict = spring_dict
×
890

891

892
class TersoffPotential:
1✔
893
    """
894
    Generate Tersoff Potential Table from "OxideTersoffPotentialentials" file
895
    """
896

897
    def __init__(self):
1✔
898
        """
899
        Init TersoffPotential
900
        """
901
        module_dir = os.path.dirname(os.path.abspath(__file__))
×
902
        with open(os.path.join(module_dir, "OxideTersoffPotentials")) as f:
×
903
            data = {}
×
904
            for row in f:
×
905
                metaloxi = row.split()[0]
×
906
                line = row.split(")")
×
907
                data[metaloxi] = line[1]
×
908
        self.data = data
×
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