mirror of https://github.com/phonopy/phonopy.git
338 lines
10 KiB
Python
338 lines
10 KiB
Python
"""Tests for symmetry tools."""
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from phonopy import Phonopy
|
|
from phonopy.structure.atoms import PhonopyAtoms
|
|
from phonopy.structure.cells import get_supercell
|
|
from phonopy.structure.symmetry import (
|
|
Symmetry,
|
|
_get_mapping_between_cells,
|
|
collect_unique_rotations,
|
|
symmetrize_borns_and_epsilon,
|
|
)
|
|
|
|
|
|
def test_get_map_operations(nacl_unitcell_order1):
|
|
"""Test get_map_operations()."""
|
|
symprec = 1e-5
|
|
cell = nacl_unitcell_order1
|
|
scell = get_supercell(cell, np.diag([2, 2, 2]), symprec=symprec)
|
|
symmetry = Symmetry(scell, symprec=symprec)
|
|
map_ops = symmetry.get_map_operations().copy()
|
|
# start = time.time()
|
|
# symmetry._set_map_operations()
|
|
# end = time.time()
|
|
# print(end - start)
|
|
# map_ops_old = symmetry.get_map_operations().copy()
|
|
# assert (map_ops == map_ops_old).all()
|
|
map_atoms = symmetry.get_map_atoms()
|
|
positions = scell.scaled_positions
|
|
rotations = symmetry.symmetry_operations["rotations"]
|
|
translations = symmetry.symmetry_operations["translations"]
|
|
for i, (op_i, atom_i) in enumerate(zip(map_ops, map_atoms)):
|
|
r_pos = np.dot(rotations[op_i], positions[i]) + translations[op_i]
|
|
diff = positions[atom_i] - r_pos
|
|
diff -= np.rint(diff)
|
|
assert (diff < symprec).all()
|
|
|
|
|
|
def test_collinear_magnetic_symmetry(convcell_cr: PhonopyAtoms):
|
|
"""Test symmetry search with collinear magnetic moments."""
|
|
symprec = 1e-5
|
|
symmetry_nonspin = Symmetry(convcell_cr, symprec=symprec)
|
|
atom_map_nonspin = symmetry_nonspin.get_map_atoms()
|
|
len_sym_nonspin = len(symmetry_nonspin.symmetry_operations["rotations"])
|
|
|
|
cell_withspin = convcell_cr.copy()
|
|
cell_withspin.magnetic_moments = [1, -1]
|
|
symmetry_withspin = Symmetry(cell_withspin, symprec=symprec)
|
|
atom_map_withspin = symmetry_withspin.get_map_atoms()
|
|
len_sym_withspin = len(symmetry_withspin.symmetry_operations["rotations"])
|
|
|
|
cell_brokenspin = convcell_cr.copy()
|
|
cell_brokenspin.magnetic_moments = [1, -2]
|
|
symmetry_brokenspin = Symmetry(cell_brokenspin, symprec=symprec)
|
|
atom_map_brokenspin = symmetry_brokenspin.get_map_atoms()
|
|
len_sym_brokenspin = len(symmetry_brokenspin.symmetry_operations["rotations"])
|
|
|
|
assert (atom_map_nonspin == atom_map_withspin).all()
|
|
assert (atom_map_nonspin != atom_map_brokenspin).any()
|
|
assert len_sym_nonspin == len_sym_withspin
|
|
assert len_sym_nonspin != len_sym_brokenspin
|
|
|
|
|
|
@pytest.mark.parametrize("is_flat", [False, True])
|
|
def test_non_collinear_magnetic_symmetry(convcell_cr: PhonopyAtoms, is_flat: bool):
|
|
"""Test symmetry search with non-collinear magnetic moments."""
|
|
symprec = 1e-5
|
|
symmetry_nonspin = Symmetry(convcell_cr, symprec=symprec)
|
|
atom_map_nonspin = symmetry_nonspin.get_map_atoms()
|
|
len_sym_nonspin = len(symmetry_nonspin.symmetry_operations["rotations"])
|
|
|
|
cell_withspin = convcell_cr.copy()
|
|
if is_flat:
|
|
cell_withspin.magnetic_moments = [0, 0, 1, 0, 0, -1]
|
|
else:
|
|
cell_withspin.magnetic_moments = [[0, 0, 1], [0, 0, -1]]
|
|
symmetry_withspin = Symmetry(cell_withspin, symprec=symprec)
|
|
atom_map_withspin = symmetry_withspin.get_map_atoms()
|
|
len_sym_withspin = len(symmetry_withspin.symmetry_operations["rotations"])
|
|
|
|
cell_brokenspin = convcell_cr.copy()
|
|
if is_flat:
|
|
cell_brokenspin.magnetic_moments = [1, 1, 1, -2, -2, -2]
|
|
else:
|
|
cell_brokenspin.magnetic_moments = [[1, 1, 1], [-2, -2, -2]]
|
|
symmetry_brokenspin = Symmetry(cell_brokenspin, symprec=symprec)
|
|
atom_map_brokenspin = symmetry_brokenspin.get_map_atoms()
|
|
len_sym_brokenspin = len(symmetry_brokenspin.symmetry_operations["rotations"])
|
|
|
|
assert (atom_map_nonspin == atom_map_withspin).all()
|
|
assert (atom_map_nonspin == atom_map_brokenspin).any()
|
|
assert len_sym_nonspin == 96
|
|
assert len_sym_withspin == 32
|
|
assert len_sym_brokenspin == 12
|
|
|
|
|
|
def test_symmetrize_borns_and_epsilon_nacl(ph_nacl: Phonopy):
|
|
"""Test symmetrization of Born charges and dielectric tensors by NaCl."""
|
|
nac_params = ph_nacl.nac_params
|
|
borns, epsilon = symmetrize_borns_and_epsilon(
|
|
nac_params["born"], nac_params["dielectric"], ph_nacl.primitive
|
|
)
|
|
np.testing.assert_allclose(borns, nac_params["born"], atol=1e-8)
|
|
np.testing.assert_allclose(epsilon, nac_params["dielectric"], atol=1e-8)
|
|
|
|
|
|
def test_symmetrize_borns_and_epsilon_tio2(ph_tio2: Phonopy):
|
|
"""Test symmetrization of Born charges and dielectric tensors by TiO2."""
|
|
nac_params = ph_tio2.nac_params
|
|
borns, epsilon = symmetrize_borns_and_epsilon(
|
|
nac_params["born"], nac_params["dielectric"], ph_tio2.primitive
|
|
)
|
|
# np.testing.assert_allclose(borns, nac_params['born'], atol=1e-8)
|
|
np.testing.assert_allclose(epsilon, nac_params["dielectric"], atol=1e-8)
|
|
|
|
|
|
def test_Symmetry_pointgroup(ph_tio2: Phonopy):
|
|
"""Test for point group symbol."""
|
|
assert ph_tio2.symmetry.pointgroup_symbol == r"4/mmm"
|
|
|
|
|
|
def test_Symmetry_nosym_s2p_map(nacl_unitcell_order1: PhonopyAtoms):
|
|
"""Test Symmetry with is_symmetry=False and s2p_map.
|
|
|
|
This situation happens when making Symmetry with nosym for supercell in
|
|
the Phonopy class.
|
|
|
|
"""
|
|
ph = Phonopy(
|
|
nacl_unitcell_order1,
|
|
supercell_matrix=[2, 2, 2],
|
|
primitive_matrix="F",
|
|
is_symmetry=False,
|
|
)
|
|
# for i, v in enumerate(ph.symmetry.symmetry_operations["translations"]):
|
|
# print("[", ", ".join(f"{x}" for x in v), "],")
|
|
np.testing.assert_equal(
|
|
ph.symmetry.symmetry_operations["translations"],
|
|
[
|
|
[0.0, 0.0, 0.0],
|
|
[0.5, 0.0, 0.0],
|
|
[0.0, 0.5, 0.0],
|
|
[0.5, 0.5, 0.0],
|
|
[0.0, 0.0, 0.5],
|
|
[0.5, 0.0, 0.5],
|
|
[0.0, 0.5, 0.5],
|
|
[0.5, 0.5, 0.5],
|
|
[0.0, 0.25, 0.25],
|
|
[0.5, 0.25, 0.25],
|
|
[0.0, 0.75, 0.25],
|
|
[0.5, 0.75, 0.25],
|
|
[0.0, 0.25, 0.75],
|
|
[0.5, 0.25, 0.75],
|
|
[0.0, 0.75, 0.75],
|
|
[0.5, 0.75, 0.75],
|
|
[0.25, 0.0, 0.25],
|
|
[0.75, 0.0, 0.25],
|
|
[0.25, 0.5, 0.25],
|
|
[0.75, 0.5, 0.25],
|
|
[0.25, 0.0, 0.75],
|
|
[0.75, 0.0, 0.75],
|
|
[0.25, 0.5, 0.75],
|
|
[0.75, 0.5, 0.75],
|
|
[0.25, 0.25, 0.0],
|
|
[0.75, 0.25, 0.0],
|
|
[0.25, 0.75, 0.0],
|
|
[0.75, 0.75, 0.0],
|
|
[0.25, 0.25, 0.5],
|
|
[0.75, 0.25, 0.5],
|
|
[0.25, 0.75, 0.5],
|
|
[0.75, 0.75, 0.5],
|
|
],
|
|
)
|
|
|
|
|
|
def test_with_pmat_and_smat(ph_nacl: Phonopy):
|
|
"""Test Born charges and dielectric tensor symmetrization with pmat and smat."""
|
|
pcell = ph_nacl.primitive
|
|
scell = ph_nacl.supercell
|
|
idx = [scell.u2u_map[i] for i in scell.s2u_map[pcell.p2s_map]]
|
|
uborns, uepsilon = _get_nac_params_in_unitcell(ph_nacl)
|
|
borns, epsilon = symmetrize_borns_and_epsilon(
|
|
uborns,
|
|
uepsilon,
|
|
ph_nacl.unitcell,
|
|
primitive_matrix=ph_nacl.primitive_matrix,
|
|
supercell_matrix=ph_nacl.supercell_matrix,
|
|
)
|
|
np.testing.assert_allclose(borns, uborns[idx], atol=1e-8)
|
|
np.testing.assert_allclose(epsilon, uepsilon, atol=1e-8)
|
|
|
|
|
|
def test_with_pcell(ph_nacl: Phonopy):
|
|
"""Test Born charges and dielectric tensor symmetrization with pcell."""
|
|
pcell = ph_nacl.primitive
|
|
scell = ph_nacl.supercell
|
|
idx = [scell.u2u_map[i] for i in scell.s2u_map[pcell.p2s_map]]
|
|
idx2 = _get_mapping_between_cells(pcell, pcell)
|
|
np.testing.assert_array_equal(idx2, np.arange(len(pcell)))
|
|
|
|
uborns, uepsilon = _get_nac_params_in_unitcell(ph_nacl)
|
|
borns, epsilon = symmetrize_borns_and_epsilon(
|
|
uborns, uepsilon, ph_nacl.unitcell, primitive=pcell
|
|
)
|
|
np.testing.assert_allclose(borns, uborns[idx][idx2], atol=1e-8)
|
|
np.testing.assert_allclose(epsilon, uepsilon, atol=1e-8)
|
|
|
|
|
|
def test_site_symmetry(ph_sno2: Phonopy):
|
|
"""Test site symmetry operations."""
|
|
site_sym0 = ph_sno2.symmetry.get_site_symmetry(0)
|
|
ref0 = [
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
-1,
|
|
0,
|
|
-1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
-1,
|
|
0,
|
|
-1,
|
|
0,
|
|
-1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
-1,
|
|
]
|
|
site_sym36 = ph_sno2.symmetry.get_site_symmetry(36)
|
|
ref36 = [
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
-1,
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
-1,
|
|
]
|
|
np.testing.assert_array_equal(site_sym0.ravel(), ref0)
|
|
np.testing.assert_array_equal(site_sym36.ravel(), ref36)
|
|
|
|
|
|
def test_collect_unique_rotations(ph_nacl: Phonopy):
|
|
"""Test collect_unique_rotations function."""
|
|
rotations = ph_nacl.symmetry.symmetry_operations["rotations"]
|
|
ptg = collect_unique_rotations(rotations)
|
|
assert len(rotations) == 1536
|
|
assert len(ptg) == 48
|
|
assert len(ptg) == len(ph_nacl.symmetry.pointgroup_operations)
|
|
|
|
|
|
def test_reciprocal_operations(ph_zr3n4: Phonopy):
|
|
"""Test reciprocal operations.
|
|
|
|
Zr3N4 is a non-centrosymmetric crystal.
|
|
|
|
"""
|
|
ptg = ph_zr3n4.symmetry.pointgroup_operations
|
|
rops = ph_zr3n4.symmetry.reciprocal_operations
|
|
matches = []
|
|
for r in ptg:
|
|
for i, rec_r in enumerate(rops):
|
|
if (r == rec_r).all():
|
|
matches.append(i)
|
|
break
|
|
assert len(np.unique(matches)) == len(ptg)
|
|
found_inv = False
|
|
for rec_r in rops:
|
|
if (rec_r == -np.eye(3, dtype=int)).all():
|
|
found_inv = True
|
|
break
|
|
assert found_inv
|
|
|
|
|
|
def _get_nac_params_in_unitcell(ph: Phonopy):
|
|
nac_params = ph.nac_params
|
|
uepsilon = nac_params["dielectric"]
|
|
pborns = nac_params["born"]
|
|
s2p_map = ph.primitive.s2p_map
|
|
p2p_map = ph.primitive.p2p_map
|
|
s2pp_map = [p2p_map[i] for i in s2p_map]
|
|
sborns = pborns[s2pp_map]
|
|
uborns = np.array([sborns[i] for i in ph.supercell.u2s_map])
|
|
|
|
return uborns, uepsilon
|