1# Copyright (C) 2011 Atsushi Togo
2# All rights reserved.
3#
4# This file is part of phonopy.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10# * Redistributions of source code must retain the above copyright
11#   notice, this list of conditions and the following disclaimer.
12#
13# * Redistributions in binary form must reproduce the above copyright
14#   notice, this list of conditions and the following disclaimer in
15#   the documentation and/or other materials provided with the
16#   distribution.
17#
18# * Neither the name of the phonopy project nor the names of its
19#   contributors may be used to endorse or promote products derived
20#   from this software without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33# POSSIBILITY OF SUCH DAMAGE.
34
35import numpy as np
36import spglib
37from spglib import get_pointgroup
38from phonopy.interface.calculator import (
39    write_crystal_structure, get_default_cell_filename)
40from phonopy.structure.atoms import PhonopyAtoms
41from phonopy.structure.cells import guess_primitive_matrix, get_primitive
42
43
44def check_symmetry(phonon, optional_structure_info):
45    # Assumed that primitive cell is the cell that user is interested in.
46    print(_get_symmetry_yaml(phonon.primitive,
47                             phonon.primitive_symmetry,
48                             phonon.version))
49
50    if phonon.unitcell.magnetic_moments is None:
51        base_fname = get_default_cell_filename(phonon.calculator)
52        symprec = phonon.primitive_symmetry.get_symmetry_tolerance()
53        (bravais_lattice,
54         bravais_pos,
55         bravais_numbers) = spglib.refine_cell(phonon.primitive, symprec)
56        bravais = PhonopyAtoms(numbers=bravais_numbers,
57                               scaled_positions=bravais_pos,
58                               cell=bravais_lattice)
59        filename = 'B' + base_fname
60        print("# Symmetrized conventional unit cell is written into %s."
61              % filename)
62        trans_mat = guess_primitive_matrix(bravais, symprec=symprec)
63        primitive = get_primitive(bravais, trans_mat, symprec=symprec)
64        write_crystal_structure(
65            filename,
66            bravais,
67            interface_mode=phonon.calculator,
68            optional_structure_info=optional_structure_info)
69
70        filename = 'P' + base_fname
71        print("# Symmetrized primitive is written into %s." % filename)
72        write_crystal_structure(
73            filename,
74            primitive,
75            interface_mode=phonon.calculator,
76            optional_structure_info=optional_structure_info)
77
78
79def _get_symmetry_yaml(cell, symmetry, phonopy_version=None):
80    rotations = symmetry.get_symmetry_operations()['rotations']
81    translations = symmetry.get_symmetry_operations()['translations']
82
83    atom_sets = symmetry.get_map_atoms()
84    independent_atoms = symmetry.get_independent_atoms()
85    wyckoffs = symmetry.get_Wyckoff_letters()
86
87    lines = []
88
89    if phonopy_version is not None:
90        lines.append("phonopy_version: '%s'" % phonopy_version)
91
92    if cell.get_magnetic_moments() is None:
93        spg_symbol, spg_number = symmetry.get_international_table().split()
94        spg_number = int(spg_number.replace('(', '').replace(')', ''))
95        lines.append("space_group_type: '%s'" % spg_symbol)
96        lines.append("space_group_number: %d" % spg_number)
97        lines.append("point_group_type: '%s'" % symmetry.get_pointgroup())
98    lines.append("space_group_operations:")
99    for i, (r, t) in enumerate(zip(rotations, translations)):
100        lines.append("- rotation: # %d" % (i + 1))
101        for vec in r:
102            lines.append("  - [%2d, %2d ,%2d]" % tuple(vec))
103        line = "  translation: ["
104        for j, x in enumerate(t):
105            if abs(x - np.rint(x)) < 1e-5:
106                line += " 0.00000"
107            else:
108                line += "%8.5f" % x
109            if j < 2:
110                line += ", "
111            else:
112                line += " ]"
113                lines.append(line)
114    lines.append("atom_mapping:")
115    for i, atom_num in enumerate(atom_sets):
116        lines.append("  %d: %d" % (i + 1, atom_num + 1))
117    lines.append("site_symmetries:")
118    for i in independent_atoms:
119        sitesym = symmetry.get_site_symmetry(i)
120        lines.append("- atom: %d" % (i + 1))
121
122        if cell.get_magnetic_moments() is None:
123            lines.append("  Wyckoff: '%s'" % wyckoffs[i])
124        site_pointgroup = get_pointgroup(sitesym)
125        lines.append("  site_point_group: '%s'" % site_pointgroup[0].strip())
126        lines.append("  orientation:")
127        for v in site_pointgroup[2]:
128            lines.append("  - [%2d, %2d, %2d]" % tuple(v))
129
130        lines.append("  rotations:")
131        for j, r in enumerate(sitesym):
132            lines.append("  - # %d" % (j + 1))
133            for vec in r:
134                lines.append("    - [%2d, %2d, %2d]" % tuple(vec))
135
136    return "\n".join(lines)
137