New --rd auto option

This commit is contained in:
Atsushi Togo 2025-01-01 10:43:28 +09:00
parent ae4ad6fa18
commit 1d3d085db7
5 changed files with 183 additions and 41 deletions

View File

@ -1225,11 +1225,11 @@ class Phono3py:
def generate_displacements(
self,
distance: float = 0.03,
distance: Optional[float] = None,
cutoff_pair_distance: Optional[float] = None,
is_plusminus: Union[bool, str] = "auto",
is_diagonal: bool = True,
number_of_snapshots: Optional[int] = None,
number_of_snapshots: Optional[Union[int, Literal["auto"]]] = None,
random_seed: Optional[int] = None,
max_distance: Optional[float] = None,
):
@ -1269,10 +1269,11 @@ class Phono3py:
Parameters
----------
distance : float, optional
Constant displacement Euclidean distance. Default is 0.03. For
random direction and random distance displacements generation, this
value is also used as `min_distance`, is used to replace generated
random distances smaller than this value by this value.
Constant displacement Euclidean distance. Default is None, which
gives 0.03. For random direction and random distance displacements
generation, this value is also used as `min_distance`, is used to
replace generated random distances smaller than this value by this
value.
cutoff_pair_distance : float, optional
This is used as a cutoff Euclidean distance to determine if each
pair of displacements is considered to calculate fc3 or not. Default
@ -1288,13 +1289,14 @@ class Phono3py:
vectors of the supercell. With True, direction not along the basis
vectors can be chosen when the number of the displacements may be
reduced.
number_of_snapshots : int or None, optional
number_of_snapshots : int, "auto", or None, optional
Number of snapshots of supercells with random displacements. Random
displacements are generated displacing all atoms in random
directions with a fixed displacement distance specified by
'distance' parameter, i.e., all atoms in supercell are displaced
with the same displacement distance in direct space. Default is
None.
displacements are generated by shifting all atoms in random
directions by a fixed distance specified by the `distance`
parameter. In other words, all atoms in the supercell are displaced
by the same distance in direct space. When auto, the minimum
required number of snapshots is estimated using symfc and then
doubled. The default is None.
random_seed : int or None, optional
Random seed for random displacements generation. Default is None.
max_distance : float or None, optional
@ -1303,11 +1305,29 @@ class Phono3py:
displacements generation, this value is used as `max_distance`.
"""
if number_of_snapshots is not None and number_of_snapshots > 0:
if distance is None:
_distance = 0.03
else:
_distance = distance
if number_of_snapshots is not None and (
number_of_snapshots == "auto" or number_of_snapshots > 0
):
if number_of_snapshots == "auto":
from phonopy.interface.symfc import SymfcFCSolver
_number_of_snapshots = (
SymfcFCSolver(
self._supercell, self._symmetry
).estimate_numbers_of_supercells(orders=[3])[3]
* 2
)
else:
_number_of_snapshots = number_of_snapshots
self._dataset = self._generate_random_displacements(
number_of_snapshots,
_number_of_snapshots,
len(self._supercell),
distance=distance,
distance=_distance,
is_plusminus=is_plusminus is True,
random_seed=random_seed,
max_distance=max_distance,
@ -1321,7 +1341,7 @@ class Phono3py:
)
self._dataset = direction_to_displacement(
direction_dataset,
distance,
_distance,
self._supercell,
cutoff_distance=cutoff_pair_distance,
)
@ -1329,15 +1349,15 @@ class Phono3py:
if self._phonon_supercell_matrix is not None and self._phonon_dataset is None:
self.generate_fc2_displacements(
distance=distance, is_plusminus=is_plusminus, is_diagonal=False
distance=_distance, is_plusminus=is_plusminus, is_diagonal=False
)
def generate_fc2_displacements(
self,
distance: float = 0.03,
distance: Optional[float] = None,
is_plusminus: str = "auto",
is_diagonal: bool = False,
number_of_snapshots: Optional[int] = None,
number_of_snapshots: Optional[Union[int, Literal["auto"]]] = None,
random_seed: Optional[int] = None,
max_distance: Optional[float] = None,
):
@ -1357,10 +1377,11 @@ class Phono3py:
Parameters
----------
distance : float, optional
Constant displacement Euclidean distance. Default is 0.03. For
random direction and random distance displacements generation, this
value is also used as `min_distance`, is used to replace generated
random distances smaller than this value by this value.
Constant displacement Euclidean distance. Default is None, which
gives 0.03. For random direction and random distance displacements
generation, this value is also used as `min_distance`, is used to
replace generated random distances smaller than this value by this
value.
is_plusminus : True, False, or 'auto', optional
With True, atomis are displaced in both positive and negative
directions. With False, only one direction. With 'auto', mostly
@ -1372,13 +1393,14 @@ class Phono3py:
the supercell. With True, direction not along the basis vectors can
be chosen when the number of the displacements may be reduced.
Default is False.
number_of_snapshots : int or None, optional
number_of_snapshots : int, "auto", or None, optional
Number of snapshots of supercells with random displacements. Random
displacements are generated displacing all atoms in random
directions with a fixed displacement distance specified by
'distance' parameter, i.e., all atoms in supercell are displaced
with the same displacement distance in direct space. Default is
None.
displacements are generated by shifting all atoms in random
directions by a fixed distance specified by the `distance`
parameter. In other words, all atoms in the supercell are displaced
by the same distance in direct space. When auto, the minimum
required number of snapshots is estimated using symfc and then
doubled. The default is None.
random_seed : int or None, optional
Random seed for random displacements generation. Default is None.
max_distance : float or None, optional
@ -1387,11 +1409,30 @@ class Phono3py:
displacements generation, this value is used as `max_distance`.
"""
if number_of_snapshots is not None and number_of_snapshots > 0:
if distance is None:
_distance = 0.03
else:
_distance = distance
if number_of_snapshots is not None and (
number_of_snapshots == "auto" or number_of_snapshots > 0
):
if number_of_snapshots == "auto":
from phonopy.interface.symfc import SymfcFCSolver
_number_of_snapshots = (
SymfcFCSolver(
self._supercell, self._symmetry
).estimate_numbers_of_supercells(orders=[2])[2]
* 2
)
else:
_number_of_snapshots = number_of_snapshots
self._phonon_dataset = self._generate_random_displacements(
number_of_snapshots,
_number_of_snapshots,
len(self._phonon_supercell),
distance=distance,
distance=_distance,
is_plusminus=is_plusminus is True,
random_seed=random_seed,
max_distance=max_distance,
@ -1403,7 +1444,7 @@ class Phono3py:
is_diagonal=is_diagonal,
)
self._phonon_dataset = directions_to_displacement_dataset(
phonon_displacement_directions, distance, self._phonon_supercell
phonon_displacement_directions, _distance, self._phonon_supercell
)
self._phonon_supercells_with_displacements = None

View File

@ -34,6 +34,7 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import numpy as np
from phonopy.interface.calculator import write_supercells_with_displacements
from phonopy.structure.cells import print_cell
@ -83,10 +84,13 @@ def create_phono3py_supercells(
random_seed=settings.random_seed,
)
if settings.random_displacements_fc2:
if (
settings.random_displacements_fc2
or settings.phonon_supercell_matrix is not None
):
phono3py.generate_fc2_displacements(
distance=distance,
is_plusminus=settings.is_plusminus_displacement,
is_plusminus=settings.is_plusminus_displacement_fc2,
number_of_snapshots=settings.random_displacements_fc2,
random_seed=settings.random_seed,
)
@ -156,4 +160,21 @@ def create_phono3py_supercells(
if log_level:
print("Number of displacements for special fc2: %d" % num_disps)
if log_level:
identity = np.eye(3, dtype=int)
n_pure_trans = sum(
[
(r == identity).all()
for r in phono3py.symmetry.symmetry_operations["rotations"]
]
)
if len(phono3py.supercell) // len(phono3py.primitive) != n_pure_trans:
print("*" * 72)
print(
"Note: "
'A better primitive cell can be chosen by using "--pa auto" option.'
)
print("*" * 72)
return phono3py

View File

@ -629,6 +629,13 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=False,
help="Set plus minus displacements",
)
parser.add_argument(
"--pm-fc2",
dest="is_plusminus_displacements_fc2",
action="store_true",
default=False,
help="Set plus minus displacements for extra fc2",
)
parser.add_argument(
"--pp-unit-conversion",
dest="pp_unit_conversion",
@ -676,17 +683,15 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--rd",
"--random-displacements",
dest="random_displacements",
type=int,
default=None,
help="Number of supercells with random displacements",
help='Number of supercells with random displacements or "auto"',
)
parser.add_argument(
"--rd-fc2",
"--random-displacements-fc2",
dest="random_displacements_fc2",
type=int,
default=None,
help="Number of phonon supercells with random displacements",
help='Number of phonon supercells with random displacements or "auto"',
)
parser.add_argument(
"--read-collision",

View File

@ -66,6 +66,7 @@ class Phono3pySettings(Settings):
"is_kappa_star": True,
"is_lbte": False,
"is_N_U": False,
"is_plusminus_displacement_fc2": "auto",
"is_real_self_energy": False,
"is_reducible_collision_matrix": False,
"is_spectral_function": False,
@ -214,6 +215,10 @@ class Phono3pySettings(Settings):
"""Set is_N_U."""
self._v["is_N_U"] = val
def set_is_plusminus_displacement_fc2(self, val):
"""Set is_plusminus_displacement_fc2."""
self._v["is_plusminus_displacement_fc2"] = val
def set_is_real_self_energy(self, val):
"""Set is_real_self_energy."""
self._v["is_real_self_energy"] = val
@ -488,6 +493,10 @@ class Phono3pyConfParser(ConfParser):
if self._args.is_N_U:
self._confs["N_U"] = ".true."
if "is_plusminus_displacements_fc2" in self._args:
if self._args.is_plusminus_displacements_fc2:
self._confs["pm_fc2"] = ".true."
if "is_real_self_energy" in self._args:
if self._args.is_real_self_energy:
self._confs["real_self_energy"] = ".true."
@ -714,7 +723,6 @@ class Phono3pyConfParser(ConfParser):
"pinv_method",
"pinv_solver",
"num_points_in_batch",
"random_displacements_fc2",
"scattering_event_class",
):
self.set_parameter(conf_key, int(confs[conf_key]))
@ -803,6 +811,22 @@ class Phono3pyConfParser(ConfParser):
else:
self.set_parameter("temperatures", vals)
if conf_key == "random_displacements_fc2":
rd = confs["random_displacements_fc2"]
if rd.lower() == "auto":
self.set_parameter("random_displacements_fc2", "auto")
else:
try:
self.set_parameter("random_displacements_fc2", int(rd))
except ValueError:
self.setting_error(f"{conf_key.upper()} is incorrectly set.")
if conf_key == "pm_fc2":
if confs["pm_fc2"].lower() == ".false.":
self.set_parameter("pm_fc2", False)
elif confs["pm_fc2"].lower() == ".true.":
self.set_parameter("pm_fc2", True)
def _set_settings(self):
self.set_settings()
params = self._parameters
@ -919,6 +943,10 @@ class Phono3pyConfParser(ConfParser):
if "N_U" in params:
self._settings.set_is_N_U(params["N_U"])
# Plus minus displacement for fc2
if "pm_fc2" in params:
self._settings.set_is_plusminus_displacement_fc2(params["pm_fc2"])
# Solve reducible collision matrix but not reduced matrix
if "reducible_collision_matrix" in params:
self._settings.set_is_reducible_collision_matrix(

View File

@ -1,6 +1,11 @@
"""Tests of displacements.py."""
import itertools
from typing import Literal, Optional, Union
import numpy as np
import pytest
from phonopy.structure.atoms import PhonopyAtoms
import phono3py
from phono3py import Phono3py
@ -74,7 +79,49 @@ distances_NaCl = [
]
def test_duplicates_agno2(agno2_cell):
@pytest.mark.parametrize(
"is_plusminus,distance,number_of_snapshots",
itertools.product([False, True], [None, 0.06], [8, "auto"]),
)
def test_random_disps_agno2(
agno2_cell: PhonopyAtoms,
is_plusminus: bool,
distance: Optional[float],
number_of_snapshots: Union[int, Literal["auto"]],
):
"""Test duplicated pairs of displacements."""
ph3 = phono3py.load(unitcell=agno2_cell, supercell_matrix=[2, 1, 2])
ph3.generate_displacements(
number_of_snapshots=number_of_snapshots,
distance=distance,
is_plusminus=is_plusminus,
)
ph3.generate_fc2_displacements(
number_of_snapshots=number_of_snapshots,
distance=distance,
is_plusminus=is_plusminus,
)
for d, n_d in zip((ph3.displacements, ph3.phonon_displacements), (92, 4)):
if number_of_snapshots == "auto":
assert len(d) == n_d * (is_plusminus + 1)
else:
assert len(d) == 8 * (is_plusminus + 1)
if distance is None:
np.testing.assert_allclose(
np.linalg.norm(d, axis=2).ravel(), 0.03, atol=1e-8
)
else:
np.testing.assert_allclose(
np.linalg.norm(d, axis=2).ravel(), 0.06, atol=1e-8
)
if is_plusminus:
np.testing.assert_allclose(d[: len(d) // 2], -d[len(d) // 2 :], atol=1e-8)
def test_duplicates_agno2(agno2_cell: PhonopyAtoms):
"""Test duplicated pairs of displacements."""
ph3 = phono3py.load(unitcell=agno2_cell, supercell_matrix=[1, 1, 1])
ph3.generate_displacements()