mirror of https://github.com/phonopy/phonopy.git
549 lines
15 KiB
Python
549 lines
15 KiB
Python
"""Tests of routines in cells.py."""
|
|
|
|
import os
|
|
from collections.abc import Callable
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from phonopy import Phonopy
|
|
from phonopy.interface.phonopy_yaml import read_cell_yaml
|
|
from phonopy.structure.atoms import PhonopyAtoms
|
|
from phonopy.structure.cells import (
|
|
Primitive,
|
|
ShortestPairs,
|
|
TrimmedCell,
|
|
compute_all_sg_permutations,
|
|
compute_permutation_for_rotation,
|
|
convert_to_phonopy_primitive,
|
|
dense_to_sparse_svecs,
|
|
get_angles,
|
|
get_cell_matrix_from_lattice,
|
|
get_cell_parameters,
|
|
get_primitive,
|
|
get_primitive_matrix,
|
|
get_supercell,
|
|
isclose,
|
|
sparse_to_dense_svecs,
|
|
)
|
|
|
|
data_dir = os.path.dirname(os.path.abspath(__file__))
|
|
primitive_matrix_nacl = [[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]
|
|
|
|
multi_nacl_ref = [
|
|
1,
|
|
1,
|
|
2,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
1,
|
|
4,
|
|
1,
|
|
8,
|
|
1,
|
|
1,
|
|
1,
|
|
2,
|
|
1,
|
|
1,
|
|
2,
|
|
2,
|
|
2,
|
|
1,
|
|
2,
|
|
2,
|
|
2,
|
|
1,
|
|
4,
|
|
2,
|
|
4,
|
|
1,
|
|
1,
|
|
1,
|
|
2,
|
|
2,
|
|
1,
|
|
2,
|
|
2,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
2,
|
|
2,
|
|
2,
|
|
4,
|
|
1,
|
|
1,
|
|
1,
|
|
2,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
2,
|
|
1,
|
|
2,
|
|
2,
|
|
2,
|
|
2,
|
|
2,
|
|
4,
|
|
1,
|
|
1,
|
|
1,
|
|
2,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
1,
|
|
4,
|
|
1,
|
|
8,
|
|
1,
|
|
1,
|
|
1,
|
|
2,
|
|
2,
|
|
1,
|
|
2,
|
|
2,
|
|
2,
|
|
1,
|
|
2,
|
|
2,
|
|
4,
|
|
1,
|
|
4,
|
|
2,
|
|
1,
|
|
1,
|
|
2,
|
|
1,
|
|
1,
|
|
2,
|
|
2,
|
|
2,
|
|
2,
|
|
1,
|
|
4,
|
|
1,
|
|
2,
|
|
2,
|
|
4,
|
|
2,
|
|
1,
|
|
1,
|
|
2,
|
|
1,
|
|
2,
|
|
1,
|
|
4,
|
|
1,
|
|
1,
|
|
2,
|
|
2,
|
|
2,
|
|
2,
|
|
2,
|
|
4,
|
|
2,
|
|
]
|
|
svecs_nacl_ref10 = [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0]]
|
|
svecs_nacl_ref30 = [
|
|
[-0.5, -0.5, 0.0],
|
|
[-0.5, 0.5, 0.0],
|
|
[0.5, -0.5, 0.0],
|
|
[0.5, 0.5, 0.0],
|
|
]
|
|
|
|
|
|
def test_compute_permutation_sno2(ph_sno2: Phonopy):
|
|
"""Test of compute_permutation by SnO2."""
|
|
_test_compute_permutation(ph_sno2)
|
|
|
|
|
|
def test_compute_permutation_tio2(ph_tio2: Phonopy):
|
|
"""Test of compute_permutation by TiO2."""
|
|
_test_compute_permutation(ph_tio2)
|
|
|
|
|
|
def test_compute_permutation_nacl(ph_nacl: Phonopy):
|
|
"""Test of compute_permutation by NaCl."""
|
|
_test_compute_permutation(ph_nacl)
|
|
|
|
|
|
def _test_compute_permutation(ph: Phonopy):
|
|
symmetry = ph.primitive_symmetry
|
|
ppos = ph.primitive.scaled_positions
|
|
plat = ph.primitive.cell.T
|
|
symprec = symmetry.tolerance
|
|
rots = symmetry.symmetry_operations["rotations"]
|
|
trans = symmetry.symmetry_operations["translations"]
|
|
perms = compute_all_sg_permutations(ppos, rots, trans, plat, symprec)
|
|
for i, (r, t) in enumerate(zip(rots, trans)):
|
|
ppos_rot = np.dot(ppos, r.T) + t
|
|
perm = compute_permutation_for_rotation(ppos, ppos_rot, plat, symprec)
|
|
np.testing.assert_array_equal(perms[i], perm)
|
|
diff = ppos[perm] - ppos_rot
|
|
diff -= np.rint(diff)
|
|
assert ((np.dot(diff, plat) ** 2).sum(axis=1) < symprec).all()
|
|
|
|
|
|
@pytest.mark.parametrize("nosnf", [True, False])
|
|
def test_get_supercell_convcell_sio2(
|
|
convcell_sio2: PhonopyAtoms, nosnf, helper_methods
|
|
):
|
|
"""Test of get_supercell with/without SNF by SiO2."""
|
|
_test_get_supercell_convcell_sio2(convcell_sio2, helper_methods, is_old_style=nosnf)
|
|
|
|
|
|
@pytest.mark.parametrize("nosnf", [True, False])
|
|
def test_get_supercell_primcell_si(
|
|
primcell_si: PhonopyAtoms, nosnf, helper_methods: Callable
|
|
):
|
|
"""Test of get_supercell with/without SNF by Si."""
|
|
_test_get_supercell_primcell_si(primcell_si, helper_methods, is_old_style=nosnf)
|
|
|
|
|
|
def test_get_supercell_nacl_snf(
|
|
nacl_unitcell_order1: PhonopyAtoms, helper_methods: Callable
|
|
):
|
|
"""Test of get_supercell using SNF by NaCl."""
|
|
cell = nacl_unitcell_order1
|
|
smat = [[-1, 1, 1], [1, -1, 1], [1, 1, -1]]
|
|
scell = get_supercell(cell, smat, is_old_style=True)
|
|
scell_snf = get_supercell(cell, smat, is_old_style=False)
|
|
helper_methods.compare_cells(scell, scell_snf)
|
|
|
|
|
|
@pytest.mark.parametrize("is_ncl", [False, True])
|
|
def test_get_supercell_Cr_with_magmoms(
|
|
convcell_cr: PhonopyAtoms, is_ncl: bool, helper_methods: Callable
|
|
):
|
|
"""Test of get_supercell using SNF by Cr with magnetic moments."""
|
|
if is_ncl:
|
|
convcell_cr.magnetic_moments = [[0, 0, 1], [0, 0, -1]]
|
|
ref_magmoms = [[0, 0, 1]] * 4 + [[0, 0, -1]] * 4
|
|
else:
|
|
convcell_cr.magnetic_moments = [1, -1]
|
|
ref_magmoms = [1.0] * 4 + [-1.0] * 4
|
|
|
|
smat = [[-1, 1, 1], [1, -1, 1], [1, 1, -1]]
|
|
scell = get_supercell(convcell_cr, smat, is_old_style=True)
|
|
|
|
np.testing.assert_allclose(
|
|
scell.magnetic_moments,
|
|
ref_magmoms,
|
|
atol=1e-8,
|
|
)
|
|
scell_snf = get_supercell(convcell_cr, smat, is_old_style=False)
|
|
helper_methods.compare_cells(scell, scell_snf)
|
|
convcell_cr.magnetic_moments = None
|
|
|
|
|
|
def _test_get_supercell_convcell_sio2(
|
|
convcell_sio2: PhonopyAtoms, helper_methods, is_old_style=True
|
|
):
|
|
smat = np.diag([1, 2, 3])
|
|
fname = "SiO2-123.yaml"
|
|
scell = get_supercell(convcell_sio2, smat, is_old_style=is_old_style)
|
|
cell_ref = read_cell_yaml(os.path.join(data_dir, fname))
|
|
if is_old_style is True:
|
|
helper_methods.compare_cells_with_order(scell, cell_ref)
|
|
else:
|
|
helper_methods.compare_cells(scell, cell_ref)
|
|
|
|
|
|
def _test_get_supercell_primcell_si(
|
|
primcell_si: PhonopyAtoms, helper_methods, is_old_style=True
|
|
):
|
|
smat = [[-1, 1, 1], [1, -1, 1], [1, 1, -1]]
|
|
fname = "Si-conv.yaml"
|
|
scell = get_supercell(primcell_si, smat, is_old_style=is_old_style)
|
|
cell_ref = read_cell_yaml(os.path.join(data_dir, fname))
|
|
if is_old_style is True:
|
|
helper_methods.compare_cells_with_order(scell, cell_ref)
|
|
else:
|
|
helper_methods.compare_cells(scell, cell_ref)
|
|
|
|
|
|
def test_get_primitive_convcell_nacl(
|
|
nacl_unitcell_order1: PhonopyAtoms, primcell_nacl: PhonopyAtoms, helper_methods
|
|
):
|
|
"""Test get_primitive by NaCl."""
|
|
pcell = get_primitive(nacl_unitcell_order1, primitive_matrix=primitive_matrix_nacl)
|
|
helper_methods.compare_cells_with_order(pcell, primcell_nacl)
|
|
|
|
|
|
def test_get_primitive_convcell_nacl_with_cetring_symbol(
|
|
nacl_unitcell_order1: PhonopyAtoms, primcell_nacl: PhonopyAtoms, helper_methods
|
|
):
|
|
"""Test get_primitive by NaCl."""
|
|
pcell = get_primitive(nacl_unitcell_order1, primitive_matrix="F")
|
|
helper_methods.compare_cells_with_order(pcell, primcell_nacl)
|
|
|
|
|
|
@pytest.mark.parametrize("is_ncl", [False, True])
|
|
def test_get_primitive_convcell_Cr_with_magmoms(
|
|
convcell_cr: PhonopyAtoms, is_ncl: bool, helper_methods: Callable
|
|
):
|
|
"""Test get_primitive by Cr with magmoms."""
|
|
if is_ncl:
|
|
convcell_cr.magnetic_moments = [[0, 0, 1], [0, 0, -1]]
|
|
else:
|
|
convcell_cr.magnetic_moments = [1, -1]
|
|
smat = [[2, 0, 0], [0, 2, 0], [0, 0, 2]]
|
|
scell = get_supercell(convcell_cr, smat, is_old_style=True)
|
|
pmat = np.linalg.inv(smat)
|
|
pcell = get_primitive(scell, primitive_matrix=pmat)
|
|
helper_methods.compare_cells(convcell_cr, pcell)
|
|
convcell_cr.magnetic_moments = None
|
|
|
|
|
|
@pytest.mark.parametrize("store_dense_svecs", [True, False])
|
|
def test_get_primitive_convcell_nacl_svecs(
|
|
nacl_unitcell_order1: PhonopyAtoms, store_dense_svecs
|
|
):
|
|
"""Test shortest vectors by NaCl."""
|
|
pcell = get_primitive(
|
|
nacl_unitcell_order1,
|
|
primitive_matrix=primitive_matrix_nacl,
|
|
store_dense_svecs=store_dense_svecs,
|
|
)
|
|
svecs, multi = pcell.get_smallest_vectors()
|
|
if store_dense_svecs:
|
|
assert svecs.shape == (54, 3)
|
|
assert multi.shape == (8, 2, 2)
|
|
assert np.sum(multi[:, :, 0]) == 54
|
|
assert np.sum(multi[-1:, -1, :]) == 54
|
|
else:
|
|
assert svecs.shape == (8, 2, 27, 3)
|
|
assert multi.shape == (8, 2)
|
|
|
|
|
|
def test_TrimmedCell(nacl_unitcell_order1: PhonopyAtoms, helper_methods: Callable):
|
|
"""Test TrimmedCell by NaCl."""
|
|
pmat = [[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]
|
|
smat2 = np.eye(3, dtype="intc") * 2
|
|
pmat2 = np.dot(np.linalg.inv(smat2), pmat)
|
|
smat3 = np.eye(3, dtype="intc") * 3
|
|
pmat3 = np.dot(np.linalg.inv(smat3), pmat)
|
|
|
|
cell = nacl_unitcell_order1
|
|
scell2 = get_supercell(cell, smat2)
|
|
scell3 = get_supercell(cell, smat3)
|
|
n = len(scell3) // 2
|
|
# swap first and last half of atomic order
|
|
indices = [i + n for i in range(n)] + list(range(n))
|
|
scell3_swap = PhonopyAtoms(
|
|
cell=scell3.cell,
|
|
scaled_positions=scell3.scaled_positions[indices],
|
|
symbols=[scell3.symbols[i] for i in indices],
|
|
)
|
|
tcell2 = TrimmedCell(pmat2, scell2)
|
|
tcell3 = TrimmedCell(
|
|
pmat3, scell3_swap, positions_to_reorder=tcell2.scaled_positions
|
|
)
|
|
helper_methods.compare_cells_with_order(tcell2, tcell3)
|
|
|
|
|
|
def test_ShortestPairs_sparse_nacl(ph_nacl: Phonopy, helper_methods: Callable):
|
|
"""Test ShortestPairs (parse) by NaCl."""
|
|
scell = ph_nacl.supercell
|
|
pcell = ph_nacl.primitive
|
|
pos = scell.scaled_positions
|
|
spairs = ShortestPairs(scell.cell, pos, pos[pcell.p2s_map], store_dense_svecs=False)
|
|
svecs = spairs.shortest_vectors
|
|
multi = spairs.multiplicities
|
|
np.testing.assert_array_equal(multi.ravel(), multi_nacl_ref)
|
|
pos_from_svecs = svecs[:, 0, 0, :] + pos[0]
|
|
np.testing.assert_allclose(svecs_nacl_ref10, svecs[1, 0, :2], atol=1e-8)
|
|
np.testing.assert_allclose(svecs_nacl_ref30, svecs[3, 0, :4], atol=1e-8)
|
|
helper_methods.compare_positions_with_order(pos_from_svecs, pos, scell.cell)
|
|
|
|
|
|
def test_ShortestPairs_dense_nacl(ph_nacl: Phonopy, helper_methods: Callable):
|
|
"""Test ShortestPairs (dense) by NaCl."""
|
|
scell = ph_nacl.supercell
|
|
pcell = ph_nacl.primitive
|
|
pos = scell.scaled_positions
|
|
spairs = ShortestPairs(scell.cell, pos, pos[pcell.p2s_map], store_dense_svecs=True)
|
|
svecs = spairs.shortest_vectors
|
|
multi = spairs.multiplicities
|
|
assert multi[-1, -1, :].sum() == multi[:, :, 0].sum()
|
|
np.testing.assert_array_equal(multi[:, :, 0].ravel(), multi_nacl_ref)
|
|
np.testing.assert_allclose(
|
|
svecs_nacl_ref10, svecs[multi[1, 0, 1] : multi[1, 0, :].sum()], atol=1e-8
|
|
)
|
|
np.testing.assert_allclose(
|
|
svecs_nacl_ref30, svecs[multi[3, 0, 1] : multi[3, 0, :].sum()], atol=1e-8
|
|
)
|
|
pos_from_svecs = svecs[multi[:, 0, 1], :] + pos[0]
|
|
helper_methods.compare_positions_with_order(pos_from_svecs, pos, scell.cell)
|
|
|
|
|
|
def test_sparse_to_dense_and_dense_to_sparse_nacl(ph_nacl: Phonopy):
|
|
"""Test for sparse_to_dense_svecs and dense_to_sparse_svecs by NaCl."""
|
|
_test_sparse_to_dense_and_dense_to_sparse(ph_nacl.supercell, ph_nacl.primitive)
|
|
|
|
|
|
def test_sparse_to_dense_and_dense_to_sparse_tipn3(ph_tipn3: Phonopy):
|
|
"""Test for sparse_to_dense_svecs and dense_to_sparse_svecs by TiPN3."""
|
|
_test_sparse_to_dense_and_dense_to_sparse(ph_tipn3.supercell, ph_tipn3.primitive)
|
|
|
|
|
|
def test_sparse_to_dense_and_dense_to_sparse_al2o3(convcell_al2o3: PhonopyAtoms):
|
|
"""Test for sparse_to_dense_svecs and dense_to_sparse_svecs by Al2O3."""
|
|
smat = np.diag([3, 3, 2])
|
|
scell = get_supercell(convcell_al2o3, smat)
|
|
|
|
pmat = np.diag([1.0 / 3, 1.0 / 3, 1.0 / 2]) @ get_primitive_matrix("R")
|
|
pcell = get_primitive(scell, primitive_matrix=pmat)
|
|
_test_sparse_to_dense_and_dense_to_sparse(scell, pcell)
|
|
|
|
pmat = np.diag([1.0 / 3, 1.0 / 3, 1.0 / 2])
|
|
pcell = get_primitive(scell, primitive_matrix=pmat)
|
|
_test_sparse_to_dense_and_dense_to_sparse(scell, pcell)
|
|
|
|
|
|
def _test_sparse_to_dense_and_dense_to_sparse(scell: PhonopyAtoms, pcell: Primitive):
|
|
pos = scell.scaled_positions
|
|
|
|
spairs = ShortestPairs(scell.cell, pos, pos[pcell.p2s_map], store_dense_svecs=False)
|
|
svecs = spairs.shortest_vectors
|
|
multi = spairs.multiplicities
|
|
|
|
spairs = ShortestPairs(scell.cell, pos, pos[pcell.p2s_map], store_dense_svecs=True)
|
|
dsvecs = spairs.shortest_vectors
|
|
dmulti = spairs.multiplicities
|
|
|
|
_dsvecs, _dmulti = sparse_to_dense_svecs(svecs, multi)
|
|
|
|
np.testing.assert_array_equal(dmulti, _dmulti)
|
|
np.testing.assert_allclose(dsvecs, _dsvecs, rtol=0, atol=1e-8)
|
|
|
|
_ssvecs, _smulti = dense_to_sparse_svecs(dsvecs, dmulti)
|
|
|
|
np.testing.assert_array_equal(multi, _smulti)
|
|
np.testing.assert_allclose(svecs, _ssvecs, rtol=0, atol=1e-8)
|
|
|
|
|
|
def test_isclose(ph_nacl: Phonopy):
|
|
"""Test of isclose wit same order of atoms.."""
|
|
scell = ph_nacl.supercell
|
|
pcell = ph_nacl.primitive
|
|
assert isclose(pcell, pcell)
|
|
assert isclose(scell, scell)
|
|
assert not isclose(scell, pcell)
|
|
|
|
|
|
def test_isclose_with_arbitrary_order(
|
|
nacl_unitcell_order1: PhonopyAtoms, nacl_unitcell_order2: PhonopyAtoms
|
|
):
|
|
"""Test of isclose with different order."""
|
|
cell1 = nacl_unitcell_order1
|
|
cell2 = nacl_unitcell_order2
|
|
assert not isclose(cell1, cell2)
|
|
_isclose = isclose(cell1, cell2, with_arbitrary_order=True)
|
|
assert isinstance(_isclose, bool)
|
|
assert _isclose
|
|
order = isclose(cell1, cell2, with_arbitrary_order=True, return_order=True)
|
|
np.testing.assert_array_equal(order, [0, 4, 1, 5, 2, 6, 3, 7])
|
|
|
|
|
|
def test_convert_to_phonopy_primitive(ph_nacl: Phonopy):
|
|
"""Test for convert_to_phonopy_primitive."""
|
|
scell = ph_nacl.supercell
|
|
pcell = ph_nacl.primitive
|
|
_pcell = convert_to_phonopy_primitive(scell, pcell)
|
|
assert isclose(pcell, _pcell)
|
|
|
|
# Changing order of atoms is not allowed.
|
|
points = pcell.scaled_positions[[1, 0]]
|
|
symbols = [pcell.symbols[i] for i in (1, 0)]
|
|
cell = pcell.cell
|
|
pcell_mode = PhonopyAtoms(cell=cell, scaled_positions=points, symbols=symbols)
|
|
with pytest.raises(RuntimeError):
|
|
_pcell = convert_to_phonopy_primitive(scell, pcell_mode)
|
|
|
|
|
|
def test_get_cell_matrix_from_lattice(primcell_nacl: PhonopyAtoms):
|
|
"""Test for test_get_cell_matrix_from_lattice."""
|
|
pcell = primcell_nacl
|
|
lattice = get_cell_matrix_from_lattice(pcell.cell)
|
|
np.testing.assert_allclose(
|
|
get_angles(lattice, is_radian=False),
|
|
get_angles(pcell.cell, is_radian=False),
|
|
atol=1e-8,
|
|
)
|
|
np.testing.assert_allclose(
|
|
get_angles(lattice, is_radian=True),
|
|
get_angles(pcell.cell, is_radian=True),
|
|
atol=1e-8,
|
|
)
|
|
np.testing.assert_allclose(
|
|
get_cell_parameters(lattice), get_cell_parameters(pcell.cell), atol=1e-8
|
|
)
|
|
np.testing.assert_allclose(
|
|
[
|
|
[4.02365076, 0.0, 0.0],
|
|
[2.01182538, 3.48458377, 0.0],
|
|
[2.01182538, 1.16152792, 3.28529709],
|
|
],
|
|
lattice,
|
|
atol=1e-7,
|
|
)
|
|
|
|
|
|
def test_get_supercell_with_Xn_symbol(ph_nacl: Phonopy):
|
|
"""Test of get_supercell with Xn symbol."""
|
|
symbols = ph_nacl.unitcell.symbols
|
|
symbols[-1] = "Cl1"
|
|
masses = ph_nacl.unitcell.masses
|
|
masses[-1] = 70.0
|
|
cell = PhonopyAtoms(
|
|
cell=ph_nacl.unitcell.cell,
|
|
scaled_positions=ph_nacl.unitcell.scaled_positions,
|
|
symbols=symbols,
|
|
masses=masses,
|
|
)
|
|
scell = get_supercell(cell, np.diag([2, 2, 2]))
|
|
assert scell.symbols[-8:] == ["Cl1"] * 8
|
|
np.testing.assert_allclose(scell.masses[-8:], [70.0] * 8)
|
|
|
|
|
|
def test_get_primitive_with_Xn_symbol(ph_nacl: Phonopy):
|
|
"""Test of get_primitive with Xn symbol.
|
|
|
|
Symbols with index breaks symmetry to make primitive cell.
|
|
|
|
Can not make primitive cell like:
|
|
["Na", "Na", "Na", "Na", "Cl", "Cl", "Cl", "Cl1"] -> ["Na", "Cl"]
|
|
|
|
"""
|
|
symbols = ph_nacl.unitcell.symbols
|
|
symbols[-1] = "Cl1"
|
|
masses = ph_nacl.unitcell.masses
|
|
masses[-1] = 70.0
|
|
cell = PhonopyAtoms(
|
|
cell=ph_nacl.unitcell.cell,
|
|
scaled_positions=ph_nacl.unitcell.scaled_positions,
|
|
symbols=symbols,
|
|
masses=masses,
|
|
)
|
|
with pytest.raises(RuntimeError) as e:
|
|
get_primitive(cell, primitive_matrix="F")
|
|
assert str(e.value).split("\n")[0] == "Atom symbol mapping failure."
|