Skip to content

Symmetry Operations

SymmetryOperation

Class to represent a crystallographic symmetry operation, composed of a rotation and a translation.

Attributes:

Name Type Description
rotation np.ndarray

(3, 3) rotation matrix in fractional coordinates

translation np.ndarray

(3) translation vector in fractional coordinates

cif_form: str property readonly

Represent this SymmetryOperation in string form e.g. '+x,+y,+z'

integer_code: int property readonly

Represent this SymmetryOperation as a packed integer

seitz_matrix: ndarray property readonly

The Seitz matrix form of this SymmetryOperation

__add__(self, value) special

Add a vector to this symmetry operation's translation vector.

Returns:

Type Description
SymmetryOperation

a copy of this symmetry operation under additional translation"

Source code in chmpy/crystal/symmetry_operation.py
254
255
256
257
258
259
260
261
def __add__(self, value: np.ndarray):
    """
    Add a vector to this symmetry operation's translation vector.

    Returns:
        SymmetryOperation: a copy of this symmetry operation under additional translation"
    """
    return SymmetryOperation(self.rotation, self.translation + value)

__init__(self, rotation, translation) special

Construct a new symmetry operation from a rotation matrix and a translation vector

Parameters:

Name Type Description Default
rotation np.ndarray

(3, 3) rotation matrix

required
translation np.ndarray

(3) translation vector

required

Returns:

Type Description
SymmetryOperation

a new SymmetryOperation

Source code in chmpy/crystal/symmetry_operation.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def __init__(self, rotation, translation):
    """
    Construct a new symmetry operation from a rotation matrix and
    a translation vector

    Arguments:
        rotation (np.ndarray): (3, 3) rotation matrix
        translation (np.ndarray): (3) translation vector

    Returns:
        SymmetryOperation: a new SymmetryOperation
    """
    self.rotation = rotation
    self.translation = translation % 1

__sub__(self, value) special

Subtract a vector from this symmetry operation's translation.

Returns:

Type Description
SymmetryOperation

a copy of this symmetry operation under additional translation"

Source code in chmpy/crystal/symmetry_operation.py
263
264
265
266
267
268
269
270
def __sub__(self, value: np.ndarray):
    """
    Subtract a vector from this symmetry operation's translation.

    Returns:
        SymmetryOperation: a copy of this symmetry operation under additional translation"
    """
    return SymmetryOperation(self.rotation, self.translation - value)

apply(self, coordinates)

Apply this symmetry operation to a set of fractional coordinates.

Parameters:

Name Type Description Default
coordinates ndarray

(N,3) or (N,4) array of fractional coordinates or homogeneous fractional coordinates.

required

Returns:

Type Description
ndarray

np.ndarray: (N, 3) array of transformed coordinates

Source code in chmpy/crystal/symmetry_operation.py
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
def apply(self, coordinates: np.ndarray) -> np.ndarray:
    """
    Apply this symmetry operation to a set of fractional coordinates.

    Args:
        coordinates (np.ndarray): (N,3) or (N,4) array of fractional coordinates or homogeneous
            fractional coordinates.

    Returns:
        np.ndarray: (N, 3) array of transformed coordinates
    """
    if coordinates.shape[1] == 4:
        return np.dot(coordinates, self.seitz_matrix.T)
    else:
        return np.dot(coordinates, self.rotation.T) + self.translation

from_integer_code(code) classmethod

Alternative constructor from an integer-encoded symmetry operation e.g. 16484

See also the encode_symm_int, decode_symm_int methods.

Parameters:

Name Type Description Default
code int

integer-encoded symmetry operation

required

Returns:

Type Description
SymmetryOperation

a new symmetry operation from the provided integer code

Source code in chmpy/crystal/symmetry_operation.py
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
@classmethod
def from_integer_code(cls, code: int):
    """
    Alternative constructor from an integer-encoded
    symmetry operation e.g. 16484

    See also  the `encode_symm_int`, `decode_symm_int` methods.

    Args:
        code (int): integer-encoded symmetry operation

    Returns:
        SymmetryOperation: a new symmetry operation from the provided integer code
    """

    rot, trans = decode_symm_int(code)
    s = SymmetryOperation(rot, trans)
    setattr(s, "_integer_code", code)
    return s

from_string_code(code) classmethod

Alternative constructor from a string encoded symmetry operation e.g. '+x,+y,+z'.

See also the encode_symm_str, decode_symm_str methods.

Parameters:

Name Type Description Default
code str

string-encoded symmetry operation

required

Returns:

Type Description
SymmetryOperation

a new symmetry operation from the provided string code

Source code in chmpy/crystal/symmetry_operation.py
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
@classmethod
def from_string_code(cls, code: str):
    """
    Alternative constructor from a string encoded
    symmetry operation e.g. '+x,+y,+z'.

    See also the `encode_symm_str`, `decode_symm_str` methods.

    Args:
        code (str): string-encoded symmetry operation

    Returns:
        SymmetryOperation: a new symmetry operation from the provided string code
    """
    rot, trans = decode_symm_str(code)
    s = SymmetryOperation(rot, trans)
    setattr(s, "_string_code", code)
    return s

identity() classmethod

Alternative constructor for the the identity symop i.e. x,y,z

Source code in chmpy/crystal/symmetry_operation.py
353
354
355
356
@classmethod
def identity(cls):
    "Alternative constructor for the the identity symop i.e. x,y,z"
    return cls.from_integer_code(16484)

inverted(self)

" A copy of this symmetry operation under inversion

Returns:

Type Description
SymmetryOperation

an inverted copy of this symmetry operation

Source code in chmpy/crystal/symmetry_operation.py
245
246
247
248
249
250
251
252
def inverted(self):
    """"
    A copy of this symmetry operation under inversion

    Returns:
        SymmetryOperation: an inverted copy of this symmetry operation
    """
    return SymmetryOperation(-self.rotation, -self.translation)

is_identity(self)

Returns true if this is the identity symmetry operation '+x,+y,+z'

Source code in chmpy/crystal/symmetry_operation.py
349
350
351
def is_identity(self) -> bool:
    "Returns true if this is the identity symmetry operation '+x,+y,+z'"
    return self.integer_code == 16484

decode_symm_int(coded_integer)

Decode an integer encoded symmetry operation.

A space group operation is compressed using ternary numerical system for rotation and duodecimal system for translation. This is achieved because each element of rotation matrix can have only one of {-1,0,1}, and the translation can have one of {0,2,3,4,6,8,9,10} divided by 12. Therefore 3^9 * 12^3 = 34012224 different values can map space group operations. In principle, octal numerical system can be used for translation, but duodecimal system is more convenient.

encode_symm_str(*decode_symm_int(16484)) '+x,+y,+z'

Parameters:

Name Type Description Default
coded_integer int

integer encoding a symmetry operation

required

Returns:

Type Description
Tuple[np.ndarray, np.ndarray]

(3,3) rotation matrix, (3) translation vector

Source code in chmpy/crystal/symmetry_operation.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def decode_symm_int(coded_integer):
    """
    Decode an integer encoded symmetry operation. 

    A space group operation is compressed using ternary numerical system for
    rotation and duodecimal system for translation. This is achieved because
    each element of rotation matrix can have only one of {-1,0,1}, and the
    translation can have one of {0,2,3,4,6,8,9,10} divided by 12.  Therefore
    3^9 * 12^3 = 34012224 different values can map space group operations. In
    principle, octal numerical system can be used for translation, but
    duodecimal system is more convenient.

    >>> encode_symm_str(*decode_symm_int(16484))
    '+x,+y,+z'

    Args:
        coded_integer (int): integer encoding a symmetry operation

    Returns:
        Tuple[np.ndarray, np.ndarray]: (3,3) rotation matrix, (3) translation vector
    """
    r = coded_integer % 19683  # 19683 = 3**9
    shift = 6561  # 6561 = 3**8
    rotation = np.empty((3, 3), dtype=np.float64)
    translation = np.empty(3, dtype=np.float64)
    for i in (0, 1, 2):
        for j in (0, 1, 2):
            # we need integer division here
            rotation[i, j] = (r % (shift * 3)) // shift - 1
            shift //= 3

    t = coded_integer // 19683
    shift = 144
    for i in (0, 1, 2):
        # we need integer division here by shift
        translation[i] = ((t % (shift * 12)) // shift) / 12
        shift //= 12
    return rotation, translation

decode_symm_str(s)

Decode a symmetry operation represented in the string form e.g. '1/2 + x, y, -z -0.25' into a rotation matrix and translation vector.

encode_symm_str(decode_symm_str("x,y,z")) '+x,+y,+z' encode_symm_str(decode_symm_str("1/2 - x,y-0.3333333,z")) '1/2-x,2/3+y,+z'

Parameters:

Name Type Description Default
s str

the encoded symmetry operation string

required

Returns:

Type Description
Tuple[np.ndarray, np.ndarray]

a (3,3) rotation matrix and a (3) translation vector

Source code in chmpy/crystal/symmetry_operation.py
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def decode_symm_str(s):
    """
    Decode a symmetry operation represented in the string
    form e.g. '1/2 + x, y, -z -0.25' into a rotation matrix
    and translation vector.

    >>> encode_symm_str(*decode_symm_str("x,y,z"))
    '+x,+y,+z'
    >>> encode_symm_str(*decode_symm_str("1/2 - x,y-0.3333333,z"))
    '1/2-x,2/3+y,+z'

    Args:
        s (str): the encoded symmetry operation string

    Returns:
        Tuple[np.ndarray, np.ndarray]: a (3,3) rotation matrix and a (3) translation vector
    """
    rotation = np.zeros((3, 3), dtype=np.float64)
    translation = np.zeros((3,), dtype=np.float64)
    tokens = s.lower().replace(" ", "").split(",")
    for i, row in enumerate(tokens):
        fac = 1
        row = row.strip()
        symbols = re.findall(SYMM_STR_SYMBOL_REGEX, row)
        for symbol in symbols:
            if "x" in symbol:
                idx = 0
                fac = -1 if "-x" in symbol else 1
                rotation[i, idx] = fac
            elif "y" in symbol:
                idx = 1
                fac = -1 if "-y" in symbol else 1
                rotation[i, idx] = fac
            elif "z" in symbol:
                idx = 2
                fac = -1 if "-z" in symbol else 1
                rotation[i, idx] = fac
            else:
                if "/" in symbol:
                    numerator, denominator = symbol.split("/")
                    translation[i] = Fraction(
                        Fraction(numerator), Fraction(denominator)
                    )
                else:
                    translation[i] += float(Fraction(symbol))
    translation = translation % 1
    return rotation, translation

encode_symm_int(rotation, translation)

Encode an integer encoded symmetry from a rotation matrix and translation vector.

A space group operation is compressed using ternary numerical system for rotation and duodecimal system for translation. This is achieved because each element of rotation matrix can have only one of {-1,0,1}, and the translation can have one of {0,2,3,4,6,8,9,10} divided by 12. Therefore 3^9 * 12^3 = 34012224 different values can map space group operations. In principle, octal numerical system can be used for translation, but duodecimal system is more convenient.

encode_symm_int(((1, 0, 0), (0, 1, 0), (0, 0, 1)), (0, 0, 0)) 16484 encode_symm_int(((1, 0, 0), (0, 1, 0), (0, 1, 1)), (0, 0.5, 0)) 1433663

Args: rotation (array_like): (3,3) matrix of -1, 0, or 1s encoding the rotation component of the symmetry operation translation (array_like): (3) vector of rational numbers encoding the translation component of the symmetry operation

Returns:

Type Description
int

the encoded symmetry operation

Source code in chmpy/crystal/symmetry_operation.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def encode_symm_int(rotation, translation):
    """
    Encode an integer encoded symmetry from a rotation matrix and translation
    vector.

    A space group operation is compressed using ternary numerical system for
    rotation and duodecimal system for translation. This is achieved because
    each element of rotation matrix can have only one of {-1,0,1}, and the
    translation can have one of {0,2,3,4,6,8,9,10} divided by 12.  Therefore
    3^9 * 12^3 = 34012224 different values can map space group operations. In
    principle, octal numerical system can be used for translation, but
    duodecimal system is more convenient.

    >>> encode_symm_int(((1, 0, 0), (0, 1, 0), (0, 0, 1)), (0, 0, 0))
    16484
    >>> encode_symm_int(((1, 0, 0), (0, 1, 0), (0, 1, 1)), (0, 0.5, 0))
    1433663

     Args:
        rotation (array_like): (3,3) matrix of -1, 0, or 1s encoding the rotation component
            of the symmetry operation
        translation (array_like): (3) vector of rational numbers encoding the translation component
            of the symmetry operation

    Returns:
        int: the encoded symmetry operation
    """

    r = 0
    shift = 1
    # encode rotation component
    rotation = np.round(np.array(rotation)).astype(int) + 1
    for i in (2, 1, 0):
        for j in (2, 1, 0):
            r += rotation[i, j] * shift
            shift *= 3
    t = 0
    shift = 1
    translation = np.round(np.array(translation) * 12).astype(int)
    for i in (2, 1, 0):
        t += translation[i] * shift
        shift *= 12
    return r + t * 19683

encode_symm_str(rotation, translation)

Encode a rotation matrix (of -1, 0, 1s) and (rational) translation vector into string form e.g. 1/2-x,z-1/3,-y-1/6

encode_symm_str(((-1, 0, 0), (0, 0, 1), (0, 1, 0)), (0, 0.5, 1/3)) '-x,1/2+z,1/3+y' encode_symm_str(((1, 1, 1), (1, 0, 1), (0, 1, 0)), (0, 0.5, 1/3)) '+x+y+z,1/2+x+z,1/3+y'

Parameters:

Name Type Description Default
rotation array_like

(3,3) matrix of -1, 0, or 1s encoding the rotation component of the symmetry operation

required
translation array_like

(3) vector of rational numbers encoding the translation component of the symmetry operation

required

Returns:

Type Description
str

the encoded symmetry operation

Source code in chmpy/crystal/symmetry_operation.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def encode_symm_str(rotation, translation):
    """
    Encode a rotation matrix (of -1, 0, 1s) and (rational) translation vector
    into string form e.g. 1/2-x,z-1/3,-y-1/6

    >>> encode_symm_str(((-1, 0, 0), (0, 0, 1), (0, 1, 0)), (0, 0.5, 1/3))
    '-x,1/2+z,1/3+y'
    >>> encode_symm_str(((1, 1, 1), (1, 0, 1), (0, 1, 0)), (0, 0.5, 1/3))
    '+x+y+z,1/2+x+z,1/3+y'

    Args:
        rotation (array_like): (3,3) matrix of -1, 0, or 1s encoding the rotation component
            of the symmetry operation
        translation (array_like): (3) vector of rational numbers encoding the translation component
            of the symmetry operation

    Returns:
        str: the encoded symmetry operation
    """
    symbols = "xyz"
    res = []
    for i in (0, 1, 2):
        t = Fraction(translation[i]).limit_denominator(12)
        v = ""
        if t != 0:
            v += str(t)
        for j in range(0, 3):
            c = rotation[i][j]
            if c != 0:
                s = "-" if c < 0 else "+"
                v += s + symbols[j]
        res.append(v)
    res = ",".join(res)
    return res

expanded_symmetry_list(reduced_symops, lattice_type)

Create an expanded list of symmetry operations from the minimum specification given a certain lattice type.

Parameters:

Name Type Description Default
reduced_symops List[SymmetryOperation]

reduced list of symmetry operations

required
lattice_type int

integer encoded lattice type with SHELX conventions, i.e.

1: P,
2: I,
3: rhombohedral obverse on hexagonal axes,
4: F,
5: A,
6: B,
7: C
required

Returns:

Type Description
List[SymmetryOperation]

an expanded list of symmetry operations given lattice type

Source code in chmpy/crystal/symmetry_operation.py
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
def expanded_symmetry_list(reduced_symops, lattice_type):
    """
    Create an expanded list of symmetry operations from the minimum
    specification given a certain lattice type.

    Args:
        reduced_symops (List[SymmetryOperation]): reduced list of symmetry operations
        lattice_type (int): integer encoded lattice type with SHELX conventions, i.e.
            ```
            1: P,
            2: I,
            3: rhombohedral obverse on hexagonal axes,
            4: F,
            5: A,
            6: B,
            7: C
            ```
    Returns:
        List[SymmetryOperation]: an expanded list of symmetry operations given lattice type
    """
    lattice_type_value = abs(lattice_type)
    translations = LATTICE_TYPE_TRANSLATIONS[lattice_type_value]

    identity = SymmetryOperation.identity()
    if identity not in reduced_symops:
        LOG.debug("Appending identity symop %s to reduced_symops")
        reduced_symops.append(identity)
    LOG.debug("Reduced symmetry list contains %d symops", len(reduced_symops))

    full_symops = []

    for symop in reduced_symops:
        full_symops.append(symop)
        for t in translations:
            full_symops.append(symop + t)

    if lattice_type > 0:
        full_symops += [x.inverted() for x in full_symops]

    LOG.debug("Expanded symmetry list contains %d symops", len(full_symops))
    return full_symops

reduced_symmetry_list(full_symops, lattice_type)

Reduce an expanded list of symmetry operations to the minimum specification given a certain lattice type.

Parameters:

Name Type Description Default
full_symops List[SymmetryOperation]

list of symmetry operations

required
lattice_type int

integer encoded lattice type with SHELX conventions, i.e.

1: P,
2: I,
3: rhombohedral obverse on hexagonal axes,
4: F,
5: A,
6: B,
7: C
required

Returns:

Type Description
List[SymmetryOperation]

minimal list of symmetry operations given lattice type

Source code in chmpy/crystal/symmetry_operation.py
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
def reduced_symmetry_list(full_symops, lattice_type):
    """
    Reduce an expanded list of symmetry operations to the minimum
    specification given a certain lattice type.

    Args:
        full_symops (List[SymmetryOperation]): list of symmetry operations
        lattice_type (int): integer encoded lattice type with SHELX conventions, i.e.
            ```
            1: P,
            2: I,
            3: rhombohedral obverse on hexagonal axes,
            4: F,
            5: A,
            6: B,
            7: C
            ```
    Returns:
        List[SymmetryOperation]: minimal list of symmetry operations given lattice type
    """
    lattice_type_value = abs(lattice_type)
    translations = LATTICE_TYPE_TRANSLATIONS[lattice_type_value]

    reduced_symops = [SymmetryOperation.identity()]
    symops_to_process = list(full_symops)

    inversion = lattice_type > 0

    while symops_to_process:
        next_symop = symops_to_process.pop(0)
        if next_symop in reduced_symops:
            continue
        if inversion and next_symop.inverted() in reduced_symops:
            continue
        for t in translations:
            x = next_symop + t
            if inversion and x.inverted() in reduced_symops:
                break
            if x in reduced_symops:
                break
        else:
            reduced_symops.append(next_symop)

    LOG.debug("Reduced symmetry list contains %d symops", len(reduced_symops))
    return reduced_symops