• 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

89.3
/pymatgen/symmetry/maggroups.py
1
# Copyright (c) Pymatgen Development Team.
2
# Distributed under the terms of the MIT License.
3

4
"""
1✔
5
Magnetic space groups.
6
"""
7

8
from __future__ import annotations
1✔
9

10
import os
1✔
11
import sqlite3
1✔
12
import textwrap
1✔
13
from array import array
1✔
14
from fractions import Fraction
1✔
15

16
import numpy as np
1✔
17
from monty.design_patterns import cached_class
1✔
18

19
from pymatgen.core.lattice import Lattice
1✔
20
from pymatgen.core.operations import MagSymmOp
1✔
21
from pymatgen.electronic_structure.core import Magmom
1✔
22
from pymatgen.symmetry.groups import SymmetryGroup, in_array_list
1✔
23
from pymatgen.symmetry.settings import JonesFaithfulTransformation
1✔
24
from pymatgen.util.string import transformation_to_string
1✔
25

26
__author__ = "Matthew Horton, Shyue Ping Ong"
1✔
27

28

29
MAGSYMM_DATA = os.path.join(os.path.dirname(__file__), "symm_data_magnetic.sqlite")
1✔
30

31

32
@cached_class
1✔
33
class MagneticSpaceGroup(SymmetryGroup):
1✔
34
    """
35
    Representation of a magnetic space group.
36
    """
37

38
    def __init__(self, id, setting_transformation="a,b,c;0,0,0"):
1✔
39
        """
40
        Initializes a MagneticSpaceGroup from its Belov, Neronova and
41
        Smirnova (BNS) number supplied as a list or its label supplied
42
        as a string. To create a magnetic structure in pymatgen, the
43
        Structure.from_magnetic_spacegroup() method can be used, which
44
        relies on this class.
45

46
        The main difference between magnetic space groups and normal
47
        crystallographic space groups is the inclusion of a time reversal
48
        operator that acts on an atom's magnetic moment. This is
49
        indicated by a prime symbol (') next to the respective symmetry
50
        operation in its label, e.g. the standard crystallographic
51
        space group Pnma has magnetic subgroups Pn'ma, Pnm'a, Pnma',
52
        Pn'm'a, Pnm'a', Pn'ma', Pn'm'a'.
53

54
        The magnetic space groups are classified as one of 4 types
55
        where G = magnetic space group, and F = parent crystallographic
56
        space group:
57

58
        1. G=F no time reversal, i.e. the same as corresponding
59
            crystallographic group
60
        2. G=F+F1', "grey" groups, where avg. magnetic moment is zero,
61
            e.g. a paramagnet in zero ext. mag. field
62
        3. G=D+(F-D)1', where D is an equi-translation subgroup of F of
63
            index 2, lattice translations do not include time reversal
64
        4. G=D+(F-D)1', where D is an equi-class subgroup of F of index 2
65

66
        There are two common settings for magnetic space groups, BNS
67
        and OG. In case 4, the BNS setting != OG setting, and so a
68
        transformation to go between the two settings is required:
69
        specifically, the BNS setting is derived from D, and the OG
70
        setting is derived from F.
71

72
        This means that the OG setting refers to the unit cell if magnetic
73
        order is neglected, and requires multiple unit cells to reproduce
74
        the full crystal periodicity when magnetic moments are present.
75
        This does not make the OG setting, in general, useful for
76
        electronic structure calculations and the BNS setting is preferred.
77
        However, this class does contain information on the OG setting and
78
        can be initialized from OG labels or numbers if required.
79

80
        Conventions: ITC monoclinic unique axis b, monoclinic cell choice 1,
81
        hexagonal axis for trigonal groups, origin choice 2 for groups with
82
        more than one origin choice (ISO-MAG).
83

84
        Raw data comes from ISO-MAG, ISOTROPY Software Suite, iso.byu.edu
85
        http://stokes.byu.edu/iso/magnetic_data.txt
86
        with kind permission from Professor Branton Campbell, BYU
87

88
        Data originally compiled from:
89
        (1) Daniel B. Litvin, Magnetic Group Tables (International Union
90
            of Crystallography, 2013) www.iucr.org/publ/978-0-9553602-2-0.
91
        (2) C. J. Bradley and A. P. Cracknell, The Mathematical Theory of
92
            Symmetry in Solids (Clarendon Press, Oxford, 1972).
93

94
        See http://stokes.byu.edu/iso/magneticspacegroupshelp.php for more
95
        information on magnetic symmetry.
96

97
        :param id: BNS number supplied as list of 2 ints or BNS label as
98
            str or index as int (1-1651) to iterate over all space groups
99
        """
100
        self._data = {}
1✔
101

102
        # Datafile is stored as sqlite3 database since (a) it can be easily
103
        # queried for various different indexes (BNS/OG number/labels) and (b)
104
        # allows binary data to be stored in a compact form similar to that in
105
        # the source data file, significantly reducing file size.
106
        # Note that a human-readable JSON format was tested first but was 20x
107
        # larger and required *much* longer initial loading times.
108

109
        # retrieve raw data
110
        db = sqlite3.connect(MAGSYMM_DATA)
1✔
111
        c = db.cursor()
1✔
112
        if isinstance(id, str):
1✔
113
            id = "".join(id.split())  # remove any white space
1✔
114
            c.execute("SELECT * FROM space_groups WHERE BNS_label=?;", (id,))
1✔
115
        elif isinstance(id, list):
1✔
116
            c.execute("SELECT * FROM space_groups WHERE BNS1=? AND BNS2=?;", (id[0], id[1]))
1✔
117
        elif isinstance(id, int):
×
118
            # OG3 index is a 'master' index, going from 1 to 1651
119
            c.execute("SELECT * FROM space_groups WHERE OG3=?;", (id,))
×
120
        raw_data = list(c.fetchone())
1✔
121

122
        # Jones Faithful transformation
123
        self.jf = JonesFaithfulTransformation.from_transformation_string("a,b,c;0,0,0")
1✔
124
        if isinstance(setting_transformation, str):
1✔
125
            if setting_transformation != "a,b,c;0,0,0":
1✔
126
                self.jf = JonesFaithfulTransformation.from_transformation_string(setting_transformation)
1✔
127
        elif isinstance(setting_transformation, JonesFaithfulTransformation):
×
128
            if setting_transformation != self.jf:
×
129
                self.jf = setting_transformation
×
130

131
        self._data["magtype"] = raw_data[0]  # int from 1 to 4
1✔
132
        self._data["bns_number"] = [raw_data[1], raw_data[2]]
1✔
133
        self._data["bns_label"] = raw_data[3]
1✔
134
        self._data["og_number"] = [raw_data[4], raw_data[5], raw_data[6]]
1✔
135
        self._data["og_label"] = raw_data[7]  # can differ from BNS_label
1✔
136

137
        def _get_point_operator(idx):
1✔
138
            """Retrieve information on point operator (rotation matrix and Seitz label)."""
139
            hex = self._data["bns_number"][0] >= 143 and self._data["bns_number"][0] <= 194
1✔
140
            c.execute(
1✔
141
                "SELECT symbol, matrix FROM point_operators WHERE idx=? AND hex=?;",
142
                (idx - 1, hex),
143
            )
144
            op = c.fetchone()
1✔
145
            op = {
1✔
146
                "symbol": op[0],
147
                "matrix": np.array(op[1].split(","), dtype="f").reshape(3, 3),
148
            }
149
            return op
1✔
150

151
        def _parse_operators(b):
1✔
152
            """Parses compact binary representation into list of MagSymmOps."""
153
            if len(b) == 0:  # e.g. if magtype != 4, OG setting == BNS setting, and b == [] for OG symmops
1✔
154
                return None
1✔
155
            raw_symops = [b[i : i + 6] for i in range(0, len(b), 6)]
1✔
156

157
            symops = []
1✔
158

159
            for r in raw_symops:
1✔
160
                point_operator = _get_point_operator(r[0])
1✔
161
                translation_vec = [r[1] / r[4], r[2] / r[4], r[3] / r[4]]
1✔
162
                time_reversal = r[5]
1✔
163
                op = MagSymmOp.from_rotation_and_translation_and_time_reversal(
1✔
164
                    rotation_matrix=point_operator["matrix"],
165
                    translation_vec=translation_vec,
166
                    time_reversal=time_reversal,
167
                )
168
                # store string representation, e.g. (2x|1/2,1/2,1/2)'
169
                seitz = (
1✔
170
                    f"({point_operator['symbol']}|"
171
                    f"{Fraction(translation_vec[0])},{Fraction(translation_vec[1])},{Fraction(translation_vec[2])})"
172
                )
173
                if time_reversal == -1:
1✔
174
                    seitz += "'"
1✔
175
                symops.append({"op": op, "str": seitz})
1✔
176

177
            return symops
1✔
178

179
        def _parse_wyckoff(b):
1✔
180
            """Parses compact binary representation into list of Wyckoff sites."""
181
            if len(b) == 0:
1✔
182
                return None
1✔
183

184
            wyckoff_sites = []
1✔
185

186
            def get_label(idx):
1✔
187
                if idx <= 25:
1✔
188
                    return chr(97 + idx)  # returns a-z when idx 0-25
1✔
189
                return "alpha"  # when a-z labels exhausted, use alpha, only relevant for a few space groups
×
190

191
            o = 0  # offset
1✔
192
            n = 1  # nth Wyckoff site
1✔
193
            num_wyckoff = b[0]
1✔
194
            while len(wyckoff_sites) < num_wyckoff:
1✔
195
                m = b[1 + o]  # multiplicity
1✔
196
                label = str(b[2 + o] * m) + get_label(num_wyckoff - n)
1✔
197
                sites = []
1✔
198
                for j in range(m):
1✔
199
                    s = b[3 + o + (j * 22) : 3 + o + (j * 22) + 22]  # data corresponding to specific Wyckoff position
1✔
200
                    translation_vec = [s[0] / s[3], s[1] / s[3], s[2] / s[3]]
1✔
201
                    matrix = [
1✔
202
                        [s[4], s[7], s[10]],
203
                        [s[5], s[8], s[11]],
204
                        [s[6], s[9], s[12]],
205
                    ]
206
                    matrix_magmom = [
1✔
207
                        [s[13], s[16], s[19]],
208
                        [s[14], s[17], s[20]],
209
                        [s[15], s[18], s[21]],
210
                    ]
211
                    # store string representation, e.g. (x,y,z;mx,my,mz)
212
                    wyckoff_str = (
1✔
213
                        f"({transformation_to_string(matrix, translation_vec)};"
214
                        f"{transformation_to_string(matrix_magmom, c='m')})"
215
                    )
216
                    sites.append(
1✔
217
                        {
218
                            "translation_vec": translation_vec,
219
                            "matrix": matrix,
220
                            "matrix_magnetic": matrix_magmom,
221
                            "str": wyckoff_str,
222
                        }
223
                    )
224

225
                # only keeping string representation of Wyckoff sites for now
226
                # could do something else with these in future
227
                wyckoff_sites.append({"label": label, "str": " ".join(s["str"] for s in sites)})
1✔
228
                n += 1
1✔
229
                o += m * 22 + 2
1✔
230

231
            return wyckoff_sites
1✔
232

233
        def _parse_lattice(b):
1✔
234
            """Parses compact binary representation into list of lattice vectors/centerings."""
235
            if len(b) == 0:
1✔
236
                return None
1✔
237
            raw_lattice = [b[i : i + 4] for i in range(0, len(b), 4)]
1✔
238

239
            lattice = []
1✔
240

241
            for r in raw_lattice:
1✔
242
                lattice.append(
1✔
243
                    {
244
                        "vector": [r[0] / r[3], r[1] / r[3], r[2] / r[3]],
245
                        "str": f"({Fraction(r[0] / r[3]).limit_denominator()},"
246
                        f"{Fraction(r[1] / r[3]).limit_denominator()},"
247
                        f"{Fraction(r[2] / r[3]).limit_denominator()})+",
248
                    }
249
                )
250

251
            return lattice
1✔
252

253
        def _parse_transformation(b):
1✔
254
            """Parses compact binary representation into transformation between OG and BNS settings."""
255
            if len(b) == 0:
1✔
256
                return None
1✔
257
            # capital letters used here by convention,
258
            # IUCr defines P and p specifically
259
            P = [[b[0], b[3], b[6]], [b[1], b[4], b[7]], [b[2], b[5], b[8]]]
1✔
260
            p = [b[9] / b[12], b[10] / b[12], b[11] / b[12]]
1✔
261
            P = np.array(P).transpose()
1✔
262
            P_string = transformation_to_string(P, components=("a", "b", "c"))
1✔
263
            p_string = (
1✔
264
                f"{Fraction(p[0]).limit_denominator()},"
265
                f"{Fraction(p[1]).limit_denominator()},"
266
                f"{Fraction(p[2]).limit_denominator()}"
267
            )
268
            return P_string + ";" + p_string
1✔
269

270
        for i in range(8, 15):
1✔
271
            try:
1✔
272
                raw_data[i] = array("b", raw_data[i])  # construct array from sql binary blobs
1✔
273
            except Exception:
×
274
                # array() behavior changed, need to explicitly convert buffer to str in earlier Python
275
                raw_data[i] = array("b", str(raw_data[i]))
×
276

277
        self._data["og_bns_transform"] = _parse_transformation(raw_data[8])
1✔
278
        self._data["bns_operators"] = _parse_operators(raw_data[9])
1✔
279
        self._data["bns_lattice"] = _parse_lattice(raw_data[10])
1✔
280
        self._data["bns_wyckoff"] = _parse_wyckoff(raw_data[11])
1✔
281
        self._data["og_operators"] = _parse_operators(raw_data[12])
1✔
282
        self._data["og_lattice"] = _parse_lattice(raw_data[13])
1✔
283
        self._data["og_wyckoff"] = _parse_wyckoff(raw_data[14])
1✔
284

285
        db.close()
1✔
286

287
    @classmethod
1✔
288
    def from_og(cls, id):
1✔
289
        """
290
        Initialize from Opechowski and Guccione (OG) label or number.
291

292
        :param id: OG number supplied as list of 3 ints or
293
            or OG label as str
294
        :return:
295
        """
296
        db = sqlite3.connect(MAGSYMM_DATA)
1✔
297
        c = db.cursor()
1✔
298
        if isinstance(id, str):
1✔
299
            c.execute("SELECT BNS_label FROM space_groups WHERE OG_label=?", (id,))
1✔
300
        elif isinstance(id, list):
1✔
301
            c.execute(
1✔
302
                "SELECT BNS_label FROM space_groups WHERE OG1=? and OG2=? and OG3=?",
303
                (id[0], id[1], id[2]),
304
            )
305
        bns_label = c.fetchone()[0]
1✔
306
        db.close()
1✔
307

308
        return cls(bns_label)
1✔
309

310
    def __eq__(self, other: object) -> bool:
1✔
311
        if not isinstance(other, type(self)):
1✔
312
            return NotImplemented
×
313
        return self._data == other._data
1✔
314

315
    @property
1✔
316
    def crystal_system(self):
1✔
317
        """
318
        :return: Crystal system, e.g., cubic, hexagonal, etc.
319
        """
320
        i = self._data["bns_number"][0]
1✔
321
        if i <= 2:
1✔
322
            return "triclinic"
1✔
323
        if i <= 15:
1✔
324
            return "monoclinic"
1✔
325
        if i <= 74:
1✔
326
            return "orthorhombic"
1✔
327
        if i <= 142:
1✔
328
            return "tetragonal"
1✔
329
        if i <= 167:
1✔
330
            return "trigonal"
×
331
        if i <= 194:
1✔
332
            return "hexagonal"
×
333
        return "cubic"
1✔
334

335
    @property
1✔
336
    def sg_symbol(self):
1✔
337
        """
338
        :return: Space group symbol
339
        """
340
        return self._data["bns_label"]
1✔
341

342
    @property
1✔
343
    def symmetry_ops(self):
1✔
344
        """
345
        Retrieve magnetic symmetry operations of the space group.
346
        :return: List of :class:`pymatgen.core.operations.MagSymmOp`
347
        """
348
        ops = [op_data["op"] for op_data in self._data["bns_operators"]]
1✔
349

350
        # add lattice centerings
351
        centered_ops = []
1✔
352
        lattice_vectors = [l["vector"] for l in self._data["bns_lattice"]]
1✔
353

354
        for vec in lattice_vectors:
1✔
355
            if not (np.array_equal(vec, [1, 0, 0]) or np.array_equal(vec, [0, 1, 0]) or np.array_equal(vec, [0, 0, 1])):
1✔
356
                for op in ops:
1✔
357
                    new_vec = op.translation_vector + vec
1✔
358
                    new_op = MagSymmOp.from_rotation_and_translation_and_time_reversal(
1✔
359
                        op.rotation_matrix,
360
                        translation_vec=new_vec,
361
                        time_reversal=op.time_reversal,
362
                    )
363
                    centered_ops.append(new_op)
1✔
364

365
        ops = ops + centered_ops
1✔
366

367
        # apply jones faithful transformation
368
        ops = [self.jf.transform_symmop(op) for op in ops]
1✔
369

370
        return ops
1✔
371

372
    def get_orbit(self, p, magmom, tol: float = 1e-5):
1✔
373
        """
374
        Returns the orbit for a point and its associated magnetic moment.
375

376
        Args:
377
            p: Point as a 3x1 array.
378
            m: A magnetic moment, compatible with
379
            :class:`pymatgen.electronic_structure.core.Magmom`
380
            tol: Tolerance for determining if sites are the same. 1e-5 should
381
                be sufficient for most purposes. Set to 0 for exact matching
382
                (and also needed for symbolic orbits).
383

384
        Returns:
385
            tuple[list, list]: orbit for point and magnetic moments for orbit.
386
        """
387
        orbit: list[np.ndarray] = []
1✔
388
        orbit_magmoms = []
1✔
389
        magmom = Magmom(magmom)
1✔
390
        for sym_op in self.symmetry_ops:
1✔
391
            pp = sym_op.operate(p)
1✔
392
            pp = np.mod(np.round(pp, decimals=10), 1)
1✔
393
            mm = sym_op.operate_magmom(magmom)
1✔
394
            if not in_array_list(orbit, pp, tol=tol):
1✔
395
                orbit.append(pp)
1✔
396
                orbit_magmoms.append(mm)
1✔
397
        return orbit, orbit_magmoms
1✔
398

399
    def is_compatible(self, lattice: Lattice, tol: float = 1e-5, angle_tol: float = 5) -> bool:
1✔
400
        """
401
        Checks whether a particular lattice is compatible with the
402
        *conventional* unit cell.
403

404
        Args:
405
            lattice (Lattice): A Lattice.
406
            tol (float): The tolerance to check for equality of lengths.
407
            angle_tol (float): The tolerance to check for equality of angles
408
                in degrees.
409

410
        Returns:
411
            bool: True if the lattice is compatible with the conventional cell.
412
        """
413
        # function from pymatgen.symmetry.groups.SpaceGroup
414
        abc = lattice.lengths
1✔
415
        angles = lattice.angles
1✔
416
        crys_system = self.crystal_system
1✔
417

418
        def check(param, ref, tolerance):
1✔
419
            return all(abs(i - j) < tolerance for i, j in zip(param, ref) if j is not None)
1✔
420

421
        if crys_system == "cubic":
1✔
422
            a = abc[0]
1✔
423
            return check(abc, [a, a, a], tol) and check(angles, [90, 90, 90], angle_tol)
1✔
424
        if crys_system == "hexagonal" or (crys_system == "trigonal" and self.sg_symbol.endswith("H")):
1✔
425
            a = abc[0]
×
426
            return check(abc, [a, a, None], tol) and check(angles, [90, 90, 120], angle_tol)
×
427
        if crys_system == "trigonal":
1✔
428
            a = abc[0]
×
429
            return check(abc, [a, a, a], tol)
×
430
        if crys_system == "tetragonal":
1✔
431
            a = abc[0]
1✔
432
            return check(abc, [a, a, None], tol) and check(angles, [90, 90, 90], angle_tol)
1✔
433
        if crys_system == "orthorhombic":
1✔
434
            return check(angles, [90, 90, 90], angle_tol)
1✔
435
        if crys_system == "monoclinic":
1✔
436
            return check(angles, [90, None, 90], angle_tol)
1✔
437
        return True
1✔
438

439
    def data_str(self, include_og=True):
1✔
440
        """
441
        Get description of all data, including information for OG setting.
442
        :return: str
443
        """
444
        # __str__() omits information on OG setting to reduce confusion
445
        # as to which set of symops are active, this property gives
446
        # all stored data including OG setting
447

448
        desc = {}  # dictionary to hold description strings
1✔
449
        description = ""
1✔
450

451
        # parse data into strings
452

453
        # indicate if non-standard setting specified
454
        if self.jf != JonesFaithfulTransformation.from_transformation_string("a,b,c;0,0,0"):
1✔
455
            description += "Non-standard setting: .....\n"
×
456
            description += repr(self.jf)
×
457
            description += "\n\nStandard setting information: \n"
×
458

459
        desc["magtype"] = self._data["magtype"]
1✔
460
        desc["bns_number"] = ".".join(map(str, self._data["bns_number"]))
1✔
461
        desc["bns_label"] = self._data["bns_label"]
1✔
462
        desc["og_id"] = (
1✔
463
            "\t\tOG: " + ".".join(map(str, self._data["og_number"])) + " " + self._data["og_label"]
464
            if include_og
465
            else ""
466
        )
467
        desc["bns_operators"] = " ".join(op_data["str"] for op_data in self._data["bns_operators"])
1✔
468

469
        desc["bns_lattice"] = (
1✔
470
            " ".join(lattice_data["str"] for lattice_data in self._data["bns_lattice"][3:])
471
            if len(self._data["bns_lattice"]) > 3
472
            else ""
473
        )  # don't show (1,0,0)+ (0,1,0)+ (0,0,1)+
474

475
        desc["bns_wyckoff"] = "\n".join(
1✔
476
            [
477
                textwrap.fill(
478
                    wyckoff_data["str"],
479
                    initial_indent=wyckoff_data["label"] + "  ",
480
                    subsequent_indent=" " * len(wyckoff_data["label"] + "  "),
481
                    break_long_words=False,
482
                    break_on_hyphens=False,
483
                )
484
                for wyckoff_data in self._data["bns_wyckoff"]
485
            ]
486
        )
487

488
        desc["og_bns_transformation"] = (
1✔
489
            f"OG-BNS Transform: ({self._data['og_bns_transform']})\n" if desc["magtype"] == 4 and include_og else ""
490
        )
491

492
        bns_operators_prefix = f"Operators{' (BNS)' if desc['magtype'] == 4 and include_og else ''}: "
1✔
493
        bns_wyckoff_prefix = f"Wyckoff Positions{' (BNS)' if desc['magtype'] == 4 and include_og else ''}: "
1✔
494

495
        # apply textwrap on long lines
496
        desc["bns_operators"] = textwrap.fill(
1✔
497
            desc["bns_operators"],
498
            initial_indent=bns_operators_prefix,
499
            subsequent_indent=" " * len(bns_operators_prefix),
500
            break_long_words=False,
501
            break_on_hyphens=False,
502
        )
503

504
        description += (
1✔
505
            "BNS: {d[bns_number]} {d[bns_label]}{d[og_id]}\n"
506
            "{d[og_bns_transformation]}"
507
            "{d[bns_operators]}\n"
508
            "{bns_wyckoff_prefix}{d[bns_lattice]}\n"
509
            "{d[bns_wyckoff]}"
510
        ).format(d=desc, bns_wyckoff_prefix=bns_wyckoff_prefix)
511

512
        if desc["magtype"] == 4 and include_og:
1✔
513
            desc["og_operators"] = " ".join(op_data["str"] for op_data in self._data["og_operators"])
1✔
514

515
            # include all lattice vectors because (1,0,0)+ (0,1,0)+ (0,0,1)+
516
            # not always present in OG setting
517
            desc["og_lattice"] = " ".join(lattice_data["str"] for lattice_data in self._data["og_lattice"])
1✔
518

519
            desc["og_wyckoff"] = "\n".join(
1✔
520
                [
521
                    textwrap.fill(
522
                        wyckoff_data["str"],
523
                        initial_indent=wyckoff_data["label"] + "  ",
524
                        subsequent_indent=" " * len(wyckoff_data["label"] + "  "),
525
                        break_long_words=False,
526
                        break_on_hyphens=False,
527
                    )
528
                    for wyckoff_data in self._data["og_wyckoff"]
529
                ]
530
            )
531

532
            og_operators_prefix = "Operators (OG): "
1✔
533

534
            # apply textwrap on long lines
535
            desc["og_operators"] = textwrap.fill(
1✔
536
                desc["og_operators"],
537
                initial_indent=og_operators_prefix,
538
                subsequent_indent=" " * len(og_operators_prefix),
539
                break_long_words=False,
540
                break_on_hyphens=False,
541
            )
542

543
            description += ("\n{d[og_operators]}\nWyckoff Positions (OG): {d[og_lattice]}\n" "{d[og_wyckoff]}").format(
1✔
544
                d=desc
545
            )
546
        elif desc["magtype"] == 4:
1✔
547
            description += "\nAlternative OG setting exists for this space group."
1✔
548

549
        return description
1✔
550

551
    def __str__(self):
1✔
552
        """
553
        String representation of the space group, specifying the setting
554
        of the space group, its magnetic symmetry operators and Wyckoff
555
        positions.
556
        :return: str
557
        """
558
        return self.data_str(include_og=False)
1✔
559

560

561
def _write_all_magnetic_space_groups_to_file(filename):
1✔
562
    """
563
    Write all magnetic space groups to a human-readable text file.
564
    Should contain same information as text files provided by ISO-MAG.
565
    :param filename:
566
    :return:
567
    """
568
    s = (
×
569
        "Data parsed from raw data from:\n"
570
        "ISO-MAG, ISOTROPY Software Suite, iso.byu.edu\n"
571
        "http://stokes.byu.edu/iso/magnetic_data.txt\n"
572
        "Used with kind permission from Professor Branton Campbell, BYU\n\n"
573
    )
574
    all_msgs = []
×
575
    for i in range(1, 1652):
×
576
        all_msgs.append(MagneticSpaceGroup(i))
×
577
    for msg in all_msgs:
×
578
        s += f"\n{msg.data_str()}\n\n--------\n"
×
579
    with open(filename, "w") as f:
×
580
        f.write(s)
×
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