Merge branch 'develop'

This commit is contained in:
Atsushi Togo 2025-06-26 19:25:00 +09:00
commit ad9cdcc64b
25 changed files with 1669 additions and 1784 deletions

View File

@ -56,9 +56,10 @@ static void real_to_normal(
const lapack_complex_double *eigvecs2, const double *fc3,
const int64_t is_compact_fc3, const double q_vecs[3][3], /* q0, q1, q2 */
const AtomTriplets *atom_triplets, const double *masses,
const int64_t *band_indices, const int64_t num_band,
const double cutoff_frequency, const int64_t triplet_index,
const int64_t num_triplets, const int64_t openmp_per_triplets);
const int64_t *band_indices, const int64_t num_band0,
const int64_t num_band, const double cutoff_frequency,
const int64_t triplet_index, const int64_t num_triplets,
const int64_t openmp_per_triplets);
static void real_to_normal_sym_q(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, double *const freqs[3],
@ -79,7 +80,7 @@ void itr_get_interaction(
const AtomTriplets *atom_triplets, const double *masses,
const int64_t *band_indices, const int64_t symmetrize_fc3_q,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
int64_t(*g_pos)[4];
int64_t (*g_pos)[4];
int64_t i;
int64_t num_band, num_band0, num_band_prod, num_g_pos;
@ -90,11 +91,11 @@ void itr_get_interaction(
num_band_prod = num_band0 * num_band * num_band;
#ifdef _OPENMP
#pragma omp parallel for schedule(guided) private( \
num_g_pos, g_pos) if (openmp_per_triplets)
#pragma omp parallel for schedule(guided) \
private(num_g_pos, g_pos) if (openmp_per_triplets)
#endif
for (i = 0; i < num_triplets; i++) {
g_pos = (int64_t(*)[4])malloc(sizeof(int64_t[4]) * num_band_prod);
g_pos = (int64_t (*)[4])malloc(sizeof(int64_t[4]) * num_band_prod);
num_g_pos = ise_set_g_pos(g_pos, num_band0, num_band,
g_zero + i * num_band_prod);
@ -177,7 +178,7 @@ void itr_get_interaction_at_triplet(
eigenvectors + triplet[1] * num_band * num_band,
eigenvectors + triplet[2] * num_band * num_band, fc3,
is_compact_fc3, q_vecs, /* q0, q1, q2 */
atom_triplets, masses, band_indices, num_band,
atom_triplets, masses, band_indices, num_band0, num_band,
cutoff_frequency, triplet_index, num_triplets,
openmp_per_triplets);
}
@ -191,9 +192,10 @@ static void real_to_normal(
const lapack_complex_double *eigvecs2, const double *fc3,
const int64_t is_compact_fc3, const double q_vecs[3][3], /* q0, q1, q2 */
const AtomTriplets *atom_triplets, const double *masses,
const int64_t *band_indices, const int64_t num_band,
const double cutoff_frequency, const int64_t triplet_index,
const int64_t num_triplets, const int64_t openmp_per_triplets) {
const int64_t *band_indices, const int64_t num_band0,
const int64_t num_band, const double cutoff_frequency,
const int64_t triplet_index, const int64_t num_triplets,
const int64_t openmp_per_triplets) {
lapack_complex_double *fc3_reciprocal;
lapack_complex_double comp_zero;
int64_t i;
@ -215,8 +217,8 @@ static void real_to_normal(
#endif
reciprocal_to_normal_squared(
fc3_normal_squared, g_pos, num_g_pos, fc3_reciprocal, freqs0, freqs1,
freqs2, eigvecs0, eigvecs1, eigvecs2, masses, band_indices, num_band,
cutoff_frequency, openmp_per_triplets);
freqs2, eigvecs0, eigvecs1, eigvecs2, masses, band_indices, num_band0,
num_band, cutoff_frequency, openmp_per_triplets);
free(fc3_reciprocal);
fc3_reciprocal = NULL;
@ -256,8 +258,8 @@ static void real_to_normal_sym_q(
freqs[index_exchange[i][2]], eigvecs[index_exchange[i][0]],
eigvecs[index_exchange[i][1]], eigvecs[index_exchange[i][2]], fc3,
is_compact_fc3, q_vecs_ex, /* q0, q1, q2 */
atom_triplets, masses, band_indices, num_band, cutoff_frequency,
triplet_index, num_triplets, openmp_per_triplets);
atom_triplets, masses, band_indices, num_band0, num_band,
cutoff_frequency, triplet_index, num_triplets, openmp_per_triplets);
for (j = 0; j < num_band0; j++) {
for (k = 0; k < num_band; k++) {
for (l = 0; l < num_band; l++) {

View File

@ -49,21 +49,18 @@
#include "lapack_wrapper.h"
static void reciprocal_to_normal_squared_no_threading(
static void get_fc3_e0_e1_e2(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
const double *freqs0, const double *freqs1, const double *freqs2,
const lapack_complex_double *e0, const lapack_complex_double *e1,
const lapack_complex_double *e2, const int64_t *band_indices,
const int64_t num_band, const double cutoff_frequency);
static void reciprocal_to_normal_squared_g_threading(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
const double *freqs0, const double *freqs1, const double *freqs2,
const lapack_complex_double *e0, const lapack_complex_double *e1,
const lapack_complex_double *e2, const int64_t *band_indices,
const int64_t num_band, const double cutoff_frequency);
const int64_t num_band0, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets);
static void get_fc3_e0(lapack_complex_double *fc3_e0,
const lapack_complex_double *fc3_reciprocal,
const lapack_complex_double *e0,
const int64_t band_index_0, const int64_t num_band);
static double get_fc3_sum(const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
@ -89,8 +86,9 @@ void reciprocal_to_normal_squared(
const lapack_complex_double *eigvecs0,
const lapack_complex_double *eigvecs1,
const lapack_complex_double *eigvecs2, const double *masses,
const int64_t *band_indices, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
const int64_t *band_indices, const int64_t num_band0,
const int64_t num_band, const double cutoff_frequency,
const int64_t openmp_per_triplets) {
int64_t i, j, ij, num_atom;
double *inv_sqrt_masses;
lapack_complex_double *e0, *e1, *e2;
@ -144,17 +142,10 @@ void reciprocal_to_normal_squared(
free(inv_sqrt_masses);
inv_sqrt_masses = NULL;
if (openmp_per_triplets) {
reciprocal_to_normal_squared_no_threading(
fc3_normal_squared, g_pos, num_g_pos, fc3_reciprocal, freqs0,
freqs1, freqs2, e0, e1, e2, band_indices, num_band,
cutoff_frequency);
} else {
reciprocal_to_normal_squared_g_threading(
fc3_normal_squared, g_pos, num_g_pos, fc3_reciprocal, freqs0,
freqs1, freqs2, e0, e1, e2, band_indices, num_band,
cutoff_frequency);
}
get_fc3_e0_e1_e2(fc3_normal_squared, g_pos, num_g_pos, fc3_reciprocal,
freqs0, freqs1, freqs2, e0, e1, e2, band_indices,
num_band0, num_band, cutoff_frequency,
openmp_per_triplets);
free(e0);
e0 = NULL;
@ -162,54 +153,45 @@ void reciprocal_to_normal_squared(
e2 = NULL;
}
// This function is more efficient than the one with multithreading
// getting rid of no concurrency over g.
static void reciprocal_to_normal_squared_no_threading(
// This is less efficient than the one without multithreading
// but can be called when unit cell is large.
static void get_fc3_e0_e1_e2(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
const double *freqs0, const double *freqs1, const double *freqs2,
const lapack_complex_double *e0, const lapack_complex_double *e1,
const lapack_complex_double *e2, const int64_t *band_indices,
const int64_t num_band, const double cutoff_frequency) {
int64_t i, j, k, ll, bi_prev;
lapack_complex_double *fc3_e0, fc3_elem, zero;
const int64_t num_band0, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
int64_t i;
lapack_complex_double *fc3_e0, zero;
zero = lapack_make_complex_double(0, 0);
bi_prev = -1;
fc3_e0 = (lapack_complex_double *)malloc(sizeof(lapack_complex_double) *
num_band * num_band);
num_band0 * num_band * num_band);
for (i = 0; i < num_band0 * num_band * num_band; i++) {
fc3_e0[i] = zero;
}
#ifdef _OPENMP
#pragma omp parallel for if (!openmp_per_triplets)
#endif
for (i = 0; i < num_band0; i++) {
get_fc3_e0(fc3_e0 + i * num_band * num_band, fc3_reciprocal, e0,
band_indices[i], num_band);
}
#ifdef _OPENMP
#pragma omp parallel for if (!openmp_per_triplets)
#endif
for (i = 0; i < num_g_pos; i++) {
if (freqs0[band_indices[g_pos[i][0]]] > cutoff_frequency &&
freqs1[g_pos[i][1]] > cutoff_frequency &&
freqs2[g_pos[i][2]] > cutoff_frequency) {
if (bi_prev != g_pos[i][0]) {
bi_prev = g_pos[i][0];
for (j = 0; j < num_band * num_band; j++) {
fc3_e0[j] = zero;
}
for (j = 0; j < num_band; j++) {
for (k = 0; k < num_band; k++) {
for (ll = 0; ll < num_band; ll++) {
fc3_elem = phonoc_complex_prod(
fc3_reciprocal[j * num_band * num_band +
k * num_band + ll],
e0[band_indices[g_pos[i][0]] * num_band + j]);
fc3_e0[k * num_band + ll] =
lapack_make_complex_double(
lapack_complex_double_real(
fc3_e0[k * num_band + ll]) +
lapack_complex_double_real(fc3_elem),
lapack_complex_double_imag(
fc3_e0[k * num_band + ll]) +
lapack_complex_double_imag(fc3_elem));
}
}
}
}
fc3_normal_squared[g_pos[i][3]] =
get_fc3_sum(e1 + g_pos[i][1] * num_band,
e2 + g_pos[i][2] * num_band, fc3_e0, num_band) /
get_fc3_sum(
e1 + g_pos[i][1] * num_band, e2 + g_pos[i][2] * num_band,
fc3_e0 + g_pos[i][0] * num_band * num_band, num_band) /
(freqs0[band_indices[g_pos[i][0]]] * freqs1[g_pos[i][1]] *
freqs2[g_pos[i][2]]);
} else {
@ -221,33 +203,23 @@ static void reciprocal_to_normal_squared_no_threading(
fc3_e0 = NULL;
}
// This is less efficient than the one without multithreading
// but can be called when unit cell is large.
static void reciprocal_to_normal_squared_g_threading(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
const double *freqs0, const double *freqs1, const double *freqs2,
const lapack_complex_double *e0, const lapack_complex_double *e1,
const lapack_complex_double *e2, const int64_t *band_indices,
const int64_t num_band, const double cutoff_frequency) {
int64_t i;
static void get_fc3_e0(lapack_complex_double *fc3_e0,
const lapack_complex_double *fc3_reciprocal,
const lapack_complex_double *e0,
const int64_t band_index_0, const int64_t num_band) {
int64_t j, k;
lapack_complex_double fc3_elem;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (i = 0; i < num_g_pos; i++) {
if (freqs0[band_indices[g_pos[i][0]]] > cutoff_frequency &&
freqs1[g_pos[i][1]] > cutoff_frequency &&
freqs2[g_pos[i][2]] > cutoff_frequency) {
fc3_normal_squared[g_pos[i][3]] =
get_fc3_sum_blas_like(e0 + band_indices[g_pos[i][0]] * num_band,
e1 + g_pos[i][1] * num_band,
e2 + g_pos[i][2] * num_band,
fc3_reciprocal, num_band) /
(freqs0[band_indices[g_pos[i][0]]] * freqs1[g_pos[i][1]] *
freqs2[g_pos[i][2]]);
} else {
fc3_normal_squared[g_pos[i][3]] = 0;
for (j = 0; j < num_band; j++) {
for (k = 0; k < num_band * num_band; k++) {
fc3_elem =
phonoc_complex_prod(fc3_reciprocal[j * num_band * num_band + k],
e0[band_index_0 * num_band + j]);
fc3_e0[k] = lapack_make_complex_double(
lapack_complex_double_real(fc3_e0[k]) +
lapack_complex_double_real(fc3_elem),
lapack_complex_double_imag(fc3_e0[k]) +
lapack_complex_double_imag(fc3_elem));
}
}
}

View File

@ -46,7 +46,8 @@ void reciprocal_to_normal_squared(
const lapack_complex_double *eigvecs0,
const lapack_complex_double *eigvecs1,
const lapack_complex_double *eigvecs2, const double *masses,
const int64_t *band_indices, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets);
const int64_t *band_indices, const int64_t num_band0,
const int64_t num_band, const double cutoff_frequency,
const int64_t openmp_per_triplets);
#endif

View File

@ -2,6 +2,15 @@
# Change Log
## Jun-26-2025: Version 3.17.0
- Major refactoring of command-user interface. Most of routines behind the
`phono3py` and `phono3py-load` commands were unified.
- For `phono3py-load`, symfc-projector is used to symmetrize force constants
calculated by finite difference approach as the default behavior. The previous
behavior of the symmetrization can be recovered by `--fc-calculator
traditional` option.
## Jun-12-2025: Version 3.16.0
- Release to follow the change of phonopy

View File

@ -58,9 +58,9 @@ copyright = "2015, Atsushi Togo"
# built documents.
#
# The short X.Y version.
version = "3.15"
version = "3.17"
# The full version, including alpha/beta/rc tags.
release = "3.15.1"
release = "3.17.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -0,0 +1,13 @@
import phonopy
import phono3py
ph3 = phono3py.load("phono3py_params_NaCl.yaml.xz", produce_fc=False, log_level=2)
ph = phonopy.Phonopy(
unitcell=ph3.unitcell,
supercell_matrix=ph3.phonon_supercell_matrix,
primitive_matrix=ph3.primitive_matrix,
)
ph.dataset = ph3.phonon_dataset
ph.nac_params = ph3.nac_params
ph.save("phonopy_params_NaCl.yaml")

View File

@ -59,7 +59,7 @@ from phonopy.interface.mlp import PhonopyMLP
from phonopy.interface.pypolymlp import (
PypolymlpParams,
)
from phonopy.interface.symfc import SymfcFCSolver
from phonopy.interface.symfc import SymfcFCSolver, symmetrize_by_projector
from phonopy.physical_units import get_physical_units
from phonopy.structure.atoms import PhonopyAtoms
from phonopy.structure.cells import (
@ -744,16 +744,21 @@ class Phono3py:
self._phonon_mlp_dataset = mlp_dataset
@property
def mlp(self) -> Optional[PhonopyMLP]:
def mlp(self) -> PhonopyMLP | None:
"""Setter and getter of PhonopyMLP dataclass."""
return self._mlp
@mlp.setter
def mlp(self, mlp) -> Optional[PhonopyMLP]:
def mlp(self, mlp: PhonopyMLP):
self._mlp = mlp
@property
def band_indices(self) -> list[NDArray]:
def phonon_mlp(self):
"""Return MLP instance for fc2."""
return self._phonon_mlp
@property
def band_indices(self) -> list[NDArray] | None:
"""Setter and getter of band indices.
list[NDArray]
@ -811,7 +816,7 @@ class Phono3py:
return self._supercells_with_displacements
@property
def phonon_supercells_with_displacements(self):
def phonon_supercells_with_displacements(self) -> list[PhonopyAtoms] | None:
"""Return supercells with displacements for fc2.
list of PhonopyAtoms
@ -1084,16 +1089,6 @@ class Phono3py:
"""
return self._bz_grid
@property
def mlp(self):
"""Return MLP instance."""
return self._mlp
@property
def phonon_mlp(self):
"""Return MLP instance for fc2."""
return self._phonon_mlp
def init_phph_interaction(
self,
nac_q_direction=None,
@ -1253,13 +1248,14 @@ class Phono3py:
def generate_displacements(
self,
distance: Optional[float] = None,
cutoff_pair_distance: Optional[float] = None,
is_plusminus: Union[bool, str] = "auto",
distance: float | None = None,
cutoff_pair_distance: float | None = None,
is_plusminus: bool | str = "auto",
is_diagonal: bool = True,
number_of_snapshots: Optional[Union[int, Literal["auto"]]] = None,
random_seed: Optional[int] = None,
max_distance: Optional[float] = None,
number_of_snapshots: int | Literal["auto"] | None = None,
random_seed: int | None = None,
max_distance: float | None = None,
number_estimation_factor: float | None = None,
):
"""Generate displacement dataset in supercell for fc3.
@ -1307,7 +1303,7 @@ class Phono3py:
pair of displacements is considered to calculate fc3 or not. Default
is None, which means cutoff is not used.
is_plusminus : True, False, or 'auto', optional
With True, atomis are displaced in both positive and negative
With True, atoms are displaced in both positive and negative
directions. With False, only one direction. With 'auto', mostly
equivalent to is_plusminus=True, but only one direction is chosen
when the displacements in both directions are symmetrically
@ -1328,9 +1324,15 @@ class Phono3py:
random_seed : int or None, optional
Random seed for random displacements generation. Default is None.
max_distance : float or None, optional
In random direction and distance displacements generation, this
value is specified. In random direction and random distance
displacements generation, this value is used as `max_distance`.
When specified, displacements are generated with random direction
and random distance. This value serves as the maximum distance,
while the `distance` parameter sets the minimum distance. The
displacement distance is randomly sampled from a uniform
distribution between these two bounds.
number_estimation_factor : float, optional
This factor multiplies the number of snapshots estimated by symfc
when `number_of_snapshots` is set to "auto". Default is None, which
sets this factor to 8 when `max_distance` is specified, otherwise 4.
"""
if distance is None:
@ -1346,14 +1348,19 @@ class Phono3py:
options = None
else:
options = {"cutoff": {3: cutoff_pair_distance}}
_number_of_snapshots = (
SymfcFCSolver(
self._supercell,
symmetry=self._symmetry,
options=options,
).estimate_numbers_of_supercells(orders=[3])[3]
* 2
)
_number_of_snapshots = SymfcFCSolver(
self._supercell,
symmetry=self._symmetry,
options=options,
).estimate_numbers_of_supercells(orders=[3])[3]
if number_estimation_factor is None:
if max_distance is None:
_number_of_snapshots *= 4
else:
_number_of_snapshots *= 8
else:
_number_of_snapshots *= number_estimation_factor
_number_of_snapshots = int(_number_of_snapshots)
else:
_number_of_snapshots = number_of_snapshots
self._dataset = self._generate_random_displacements(
@ -1484,8 +1491,9 @@ class Phono3py:
self,
symmetrize_fc3r: bool = False,
is_compact_fc: bool = False,
fc_calculator: Optional[Union[str, dict]] = None,
fc_calculator_options: Optional[Union[str, dict]] = None,
fc_calculator: Literal["traditional", "symfc", "alm"] | None = None,
fc_calculator_options: str | None = None,
use_symfc_projector: bool = False,
):
"""Calculate fc3 from displacements and forces.
@ -1506,8 +1514,11 @@ class Phono3py:
cells. Default is False.
fc_calculator : str, optional
Force constants calculator given by str.
fc_calculator_options : dict or str, optional
fc_calculator_options : str, optional
Options for external force constants calculator.
use_symfc_projector : bool, optional
If True, the force constants are symmetrized by symfc projector
instead of traditional approach.
"""
fc_solver_name = fc_calculator if fc_calculator is not None else "traditional"
@ -1525,17 +1536,44 @@ class Phono3py:
fc2 = fc_solver.force_constants[2]
fc3 = fc_solver.force_constants[3]
if fc_calculator is None or fc_calculator == "traditional":
if symmetrize_fc3r:
if symmetrize_fc3r:
if use_symfc_projector and fc_calculator is None:
if self._log_level:
print("Symmetrizing fc3 by symfc projector.", flush=True)
fc3 = symmetrize_by_projector(
self._supercell,
fc3,
3,
primitive=self._primitive,
log_level=self._log_level,
show_credit=True,
)
if self._fc2 is None:
if self._log_level:
print("Symmetrizing fc2 by symfc projector.", flush=True)
fc2 = symmetrize_by_projector(
self._supercell,
fc2,
2,
primitive=self._primitive,
log_level=self._log_level,
)
elif fc_calculator is None or fc_calculator == "traditional":
if self._log_level:
print("Symmetrizing fc3 by traditional approach.", flush=True)
if is_compact_fc:
set_translational_invariance_compact_fc3(fc3, self._primitive)
set_permutation_symmetry_compact_fc3(fc3, self._primitive)
if self._fc2 is None:
symmetrize_compact_force_constants(fc2, self._primitive)
else:
set_translational_invariance_fc3(fc3)
set_permutation_symmetry_fc3(fc3)
if self._fc2 is None:
if self._fc2 is None:
if self._log_level:
print("Symmetrizing fc2 by traditional approach.", flush=True)
if is_compact_fc:
symmetrize_compact_force_constants(fc2, self._primitive)
else:
symmetrize_force_constants(fc2)
self._fc3 = fc3
@ -1573,10 +1611,11 @@ class Phono3py:
def produce_fc2(
self,
symmetrize_fc2=False,
is_compact_fc=False,
fc_calculator=None,
fc_calculator_options=None,
symmetrize_fc2: bool = False,
is_compact_fc: bool = False,
fc_calculator: Literal["traditional", "symfc", "alm"] | None = None,
fc_calculator_options: str | None = None,
use_symfc_projector: bool = False,
):
"""Calculate fc2 from displacements and forces.
@ -1597,8 +1636,11 @@ class Phono3py:
cells. Default is False.
fc_calculator : str or None
Force constants calculator given by str.
fc_calculator_options : dict
fc_calculator_options : str or None
Options for external force constants calculator.
use_symfc_projector : bool, optional
If True, the force constants are symmetrized by symfc projector
instead of traditional approach.
"""
if self._phonon_dataset is None:
@ -1606,6 +1648,8 @@ class Phono3py:
else:
disp_dataset = self._phonon_dataset
if disp_dataset is None:
raise RuntimeError("Displacement dataset is not set.")
if not forces_in_dataset(disp_dataset):
raise RuntimeError("Forces are not set in the dataset.")
@ -1620,8 +1664,16 @@ class Phono3py:
log_level=self._log_level,
)
if fc_calculator is None or fc_calculator == "traditional":
if symmetrize_fc2:
if symmetrize_fc2 and (fc_calculator is None or fc_calculator == "traditional"):
if use_symfc_projector:
self._fc2 = symmetrize_by_projector(
self._phonon_supercell,
self._fc2,
2,
primitive=self._primitive,
log_level=self._log_level,
)
else:
if is_compact_fc:
symmetrize_compact_force_constants(
self._fc2, self._phonon_primitive
@ -2229,7 +2281,7 @@ class Phono3py:
)
def save(
self, filename: str = "phono3py_params.yaml", settings: Optional[dict] = None
self, filename: str = "phono3py_params.yaml", settings: dict | None = None
):
"""Save parameters in Phono3py instants into file.
@ -2256,7 +2308,7 @@ class Phono3py:
def develop_mlp(
self,
params: Optional[Union[PypolymlpParams, dict, str]] = None,
params: PypolymlpParams | dict | str | None = None,
test_size: float = 0.1,
):
"""Develop machine learning potential.
@ -2473,7 +2525,7 @@ class Phono3py:
def _build_phonon_supercells_with_displacements(
self, supercell: PhonopyAtoms, dataset
):
) -> list[PhonopyAtoms]:
supercells = []
positions = supercell.positions
magmoms = supercell.magnetic_moments
@ -2733,8 +2785,8 @@ class Phono3py:
number_of_atoms: int,
distance: float = 0.03,
is_plusminus: bool = False,
random_seed: Optional[int] = None,
max_distance: Optional[float] = None,
random_seed: int | None = None,
max_distance: float | None = None,
):
if random_seed is not None and random_seed >= 0 and random_seed < 2**32:
_random_seed = random_seed

View File

@ -204,7 +204,7 @@ class ConductivityRTABase(ConductivityBase):
self._collision.set_grid_point(grid_point)
num_triplets = len(self._pp.get_triplets_at_q()[0])
if self._log_level:
print("Number of triplets: %d" % num_triplets)
print("Number of triplets: %d" % num_triplets, flush=True)
if (
self._is_full_pp
@ -288,7 +288,7 @@ class ConductivityRTABase(ConductivityBase):
if self._is_gamma_detail:
num_temp = len(self._temperatures)
self._gamma_detail_at_q = np.empty(
((num_temp,) + self._pp.get_interaction_strength().shape),
((num_temp,) + self._pp.interaction_strength.shape),
dtype="double",
order="C",
)

View File

@ -37,189 +37,36 @@
from __future__ import annotations
import copy
import dataclasses
import os
import pathlib
import sys
from dataclasses import asdict
from typing import Literal, Optional, Union
from typing import Literal
import numpy as np
from phonopy.cui.phonopy_script import file_exists, print_error
from phonopy.file_IO import get_dataset_type2
from phonopy.harmonic.force_constants import (
show_drift_force_constants,
symmetrize_compact_force_constants,
symmetrize_force_constants,
)
from phonopy.interface.calculator import get_calculator_physical_units
from phonopy.interface.pypolymlp import PypolymlpParams, parse_mlp_params
from phono3py import Phono3py
from phono3py.cui.show_log import show_phono3py_force_constants_settings
from phono3py.file_IO import (
get_length_of_first_line,
parse_FORCES_FC2,
parse_FORCES_FC3,
read_fc2_from_hdf5,
read_fc3_from_hdf5,
write_fc2_to_hdf5,
write_fc3_to_hdf5,
)
from phono3py.interface.fc_calculator import (
determine_cutoff_pair_distance,
extract_fc2_fc3_calculators,
get_fc_calculator_params,
)
from phono3py.interface.fc_calculator import determine_cutoff_pair_distance
from phono3py.interface.phono3py_yaml import Phono3pyYaml
from phono3py.phonon3.dataset import forces_in_dataset
from phono3py.phonon3.fc3 import (
set_permutation_symmetry_fc3,
set_translational_invariance_fc3,
show_drift_fc3,
)
def create_phono3py_force_constants(
phono3py: Phono3py,
settings,
ph3py_yaml: Optional[Phono3pyYaml] = None,
phono3py_yaml_filename: Optional[str] = None,
calculator: Optional[str] = None,
input_filename: Optional[str] = None,
output_filename: Optional[str] = None,
log_level=1,
):
"""Read or calculate force constants.
This function is for the 'phonopy' command only and not for the
'phonopy-load' command.
"""
# Only for built-in fc calculator.
# These are not applied to external fc calculators.
symmetrize_fc3r = settings.is_symmetrize_fc3_r or settings.fc_symmetry
symmetrize_fc2 = settings.is_symmetrize_fc2 or settings.fc_symmetry
if log_level:
show_phono3py_force_constants_settings(settings)
#######
# fc3 #
#######
if (
settings.is_joint_dos
or (settings.is_isotope and not (settings.is_bterta or settings.is_lbte))
or settings.read_gamma
or settings.read_pp
or (not settings.is_bterta and settings.write_phonon)
or settings.constant_averaged_pp_interaction is not None
):
pass
else:
(fc_calculator, fc_calculator_options) = get_fc_calculator_params(
settings.fc_calculator,
settings.fc_calculator_options,
settings.cutoff_pair_distance,
log_level=(not settings.read_fc3) * 1,
)
if settings.read_fc3:
_read_phono3py_fc3(phono3py, symmetrize_fc3r, input_filename, log_level)
else: # fc3 from FORCES_FC3 or ph3py_yaml
dataset = _read_dataset_fc3(
phono3py,
ph3py_yaml,
phono3py_yaml_filename,
settings.cutoff_pair_distance,
calculator,
log_level,
)
phono3py.dataset = dataset
phono3py.produce_fc3(
symmetrize_fc3r=symmetrize_fc3r,
is_compact_fc=settings.is_compact_fc,
fc_calculator=extract_fc2_fc3_calculators(fc_calculator, 3),
fc_calculator_options=extract_fc2_fc3_calculators(
fc_calculator_options, 3
),
)
assert phono3py.fc3 is not None, "fc3 is not set."
cutoff_distance = settings.cutoff_fc3_distance
if cutoff_distance is not None and cutoff_distance > 0:
if log_level:
print(
"Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance
)
phono3py.cutoff_fc3_by_zero(cutoff_distance)
if not settings.read_fc3:
if output_filename is None:
filename = "fc3.hdf5"
else:
filename = "fc3." + output_filename + ".hdf5"
if log_level:
print(f'Writing fc3 to "{filename}".')
write_fc3_to_hdf5(
phono3py.fc3,
fc3_nonzero_indices=phono3py.fc3_nonzero_indices,
filename=filename,
p2s_map=phono3py.primitive.p2s_map,
fc3_cutoff=phono3py.fc3_cutoff,
compression=settings.hdf5_compression,
)
if log_level:
show_drift_fc3(phono3py.fc3, primitive=phono3py.primitive)
#######
# fc2 #
#######
phonon_primitive = phono3py.phonon_primitive
p2s_map = phonon_primitive.p2s_map
if settings.read_fc2:
_read_phono3py_fc2(phono3py, symmetrize_fc2, input_filename, log_level)
else:
if phono3py.dataset is None or phono3py.phonon_supercell_matrix is not None:
_read_dataset_fc2(
phono3py,
ph3py_yaml,
calculator,
log_level,
)
phono3py.produce_fc2(
symmetrize_fc2=symmetrize_fc2,
is_compact_fc=settings.is_compact_fc,
fc_calculator=extract_fc2_fc3_calculators(fc_calculator, 2),
fc_calculator_options=extract_fc2_fc3_calculators(fc_calculator_options, 2),
)
if output_filename is None:
filename = "fc2.hdf5"
else:
filename = "fc2." + output_filename + ".hdf5"
if log_level:
print('Writing fc2 to "%s".' % filename)
write_fc2_to_hdf5(
phono3py.fc2,
filename=filename,
p2s_map=p2s_map,
physical_unit="eV/angstrom^2",
compression=settings.hdf5_compression,
)
if log_level:
show_drift_force_constants(phono3py.fc2, primitive=phonon_primitive, name="fc2")
def parse_forces(
phono3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
cutoff_pair_distance=None,
force_filename: str = "FORCES_FC3",
phono3py_yaml_filename: Optional[str] = None,
ph3py_yaml: Phono3pyYaml | None = None,
cutoff_pair_distance: float | None = None,
force_filename: str | os.PathLike = "FORCES_FC3",
phono3py_yaml_filename: str | os.PathLike | None = None,
fc_type: Literal["fc3", "phonon_fc2"] = "fc3",
calculator: Optional[str] = None,
log_level=0,
calculator: str | None = None,
log_level: int = 0,
) -> dict:
"""Read displacements and forces.
@ -230,7 +77,7 @@ def parse_forces(
without writing calculator name in it.
"""
filename_read_from: Optional[str] = None
filename_read_from: str | None = None
dataset = None
if phono3py.phonon_supercell is None or fc_type == "fc3":
@ -257,6 +104,9 @@ def parse_forces(
if dataset:
filename_read_from = force_filename
if dataset is None:
raise RuntimeError("Dataset is not found.")
# Units of displacements and forces are converted. If forces don't
# exist, the conversion will not be performed for forces.
if calculator is not None:
@ -265,8 +115,6 @@ def parse_forces(
distance_to_A=physical_units["distance_to_A"],
force_to_eVperA=physical_units["force_to_eVperA"],
)
if dataset is None:
raise RuntimeError("Dataset is not found.")
if "natom" in dataset and dataset["natom"] != natom:
raise RuntimeError(
@ -298,82 +146,13 @@ def parse_forces(
return dataset
def _read_phono3py_fc3(phono3py: Phono3py, symmetrize_fc3r, input_filename, log_level):
if input_filename is None:
filename = "fc3.hdf5"
else:
filename = "fc3." + input_filename + ".hdf5"
file_exists(filename, log_level=log_level)
if log_level:
print('Reading fc3 from "%s".' % filename)
p2s_map = phono3py.primitive.p2s_map
try:
fc3 = read_fc3_from_hdf5(filename=filename, p2s_map=p2s_map)
except RuntimeError:
import traceback
traceback.print_exc()
if log_level:
print_error()
sys.exit(1)
num_atom = len(phono3py.supercell)
if fc3.shape[1] != num_atom:
print("Matrix shape of fc3 doesn't agree with supercell size.")
if log_level:
print_error()
sys.exit(1)
if symmetrize_fc3r:
set_translational_invariance_fc3(fc3)
set_permutation_symmetry_fc3(fc3)
phono3py.fc3 = fc3
def _read_phono3py_fc2(phono3py, symmetrize_fc2, input_filename, log_level):
if input_filename is None:
filename = "fc2.hdf5"
else:
filename = "fc2." + input_filename + ".hdf5"
file_exists(filename, log_level=log_level)
if log_level:
print('Reading fc2 from "%s".' % filename)
num_atom = len(phono3py.phonon_supercell)
p2s_map = phono3py.phonon_primitive.p2s_map
try:
phonon_fc2 = read_fc2_from_hdf5(filename=filename, p2s_map=p2s_map)
except RuntimeError:
import traceback
traceback.print_exc()
if log_level:
print_error()
sys.exit(1)
if phonon_fc2.shape[1] != num_atom:
print("Matrix shape of fc2 doesn't agree with supercell size.")
if log_level:
print_error()
sys.exit(1)
if symmetrize_fc2:
if phonon_fc2.shape[0] == phonon_fc2.shape[1]:
symmetrize_force_constants(phonon_fc2)
else:
symmetrize_compact_force_constants(phonon_fc2, phono3py.phonon_primitive)
phono3py.fc2 = phonon_fc2
def _read_FORCES_FC3_or_FC2(
natom: int,
dataset: Optional[dict],
dataset: dict | None,
fc_type: str,
filename: str = "FORCES_FC3",
filename: str | os.PathLike = "FORCES_FC3",
log_level: int = 0,
) -> Optional[dict]:
) -> dict | None:
"""Read FORCES_FC3 or FORCES_FC2.
Read the first line of forces file to determine the type of the file.
@ -404,64 +183,19 @@ def _read_FORCES_FC3_or_FC2(
return dataset
def _read_dataset_fc3(
phono3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
phono3py_yaml_filename: Optional[str],
cutoff_pair_distance: Optional[float],
calculator: Optional[str],
log_level: int,
) -> dict:
"""Read or calculate fc3.
Note
----
cutoff_pair_distance is the parameter to determine each displaced
supercell is included to the computation of fc3. It is assumed that
cutoff_pair_distance is stored in the step to create sets of
displacements and the value is stored n the displacement dataset and
also as the parameter 'included': True or False for each displacement.
The parameter cutoff_pair_distance here can be used in the step to
create fc3 by overwriting original cutoff_pair_distance value only
when the former value is smaller than the later.
"""
try:
dataset = parse_forces(
phono3py,
ph3py_yaml=ph3py_yaml,
cutoff_pair_distance=cutoff_pair_distance,
force_filename="FORCES_FC3",
phono3py_yaml_filename=phono3py_yaml_filename,
fc_type="fc3",
calculator=calculator,
log_level=log_level,
)
except RuntimeError as e:
# from _parse_forces_type1
if log_level:
print(str(e))
print_error()
sys.exit(1)
except FileNotFoundError as e:
# from _get_type2_dataset
file_exists(e.filename, log_level=log_level)
return dataset
def run_pypolymlp_to_compute_forces(
ph3py: Phono3py,
mlp_params: Union[str, dict, PypolymlpParams],
displacement_distance: Optional[float] = None,
number_of_snapshots: Optional[int] = None,
random_seed: Optional[int] = None,
mlp_params: str | dict | PypolymlpParams | None = None,
displacement_distance: float | None = None,
number_of_snapshots: int | Literal["auto"] | None = None,
number_estimation_factor: int | None = None,
random_seed: int | None = None,
prepare_dataset: bool = False,
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[str] = None,
cutoff_pair_distance: Optional[float] = None,
symfc_memory_size: Optional[float] = None,
mlp_filename: Optional[str] = None,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
cutoff_pair_distance: float | None = None,
symfc_memory_size: float | None = None,
mlp_filename: str | os.PathLike | None = None,
log_level: int = 0,
):
"""Run pypolymlp to compute forces."""
@ -505,7 +239,7 @@ def run_pypolymlp_to_compute_forces(
else:
pmlp_params = parse_mlp_params(mlp_params)
print("Parameters:")
for k, v in asdict(pmlp_params).items():
for k, v in dataclasses.asdict(pmlp_params).items():
if v is not None:
print(f" {k}: {v}")
print("Developing MLPs by pypolymlp...", flush=True)
@ -557,6 +291,7 @@ def run_pypolymlp_to_compute_forces(
is_plusminus=True,
number_of_snapshots=number_of_snapshots,
random_seed=random_seed,
number_estimation_factor=number_estimation_factor,
)
if log_level:
@ -574,10 +309,10 @@ def run_pypolymlp_to_compute_forces(
def run_pypolymlp_to_compute_phonon_forces(
ph3py: Phono3py,
mlp_params: Optional[Union[str, dict, PypolymlpParams]] = None,
displacement_distance: Optional[float] = None,
number_of_snapshots: Optional[int] = None,
random_seed: Optional[int] = None,
mlp_params: str | dict | PypolymlpParams | None = None,
displacement_distance: float | None = None,
number_of_snapshots: int | None = None,
random_seed: int | None = None,
log_level: int = 0,
):
"""Run pypolymlp to compute phonon forces."""
@ -592,7 +327,7 @@ def run_pypolymlp_to_compute_phonon_forces(
print("Pypolymlp is developed at https://github.com/sekocha/pypolymlp.")
if mlp_params:
print("Parameters:")
for k, v in asdict(parse_mlp_params(mlp_params)).items():
for k, v in dataclasses.asdict(parse_mlp_params(mlp_params)).items():
if v is not None:
print(f" {k}: {v}")
if log_level:
@ -629,61 +364,10 @@ def run_pypolymlp_to_compute_phonon_forces(
ph3py.evaluate_phonon_mlp()
def _read_dataset_fc2(
phono3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
calculator,
log_level,
):
"""Read forces and produce fc2.
force_filename is either "FORCES_FC2" or "FORCES_FC3".
"""
# _ph3py_yaml = _get_default_ph3py_yaml(ph3py_yaml)
if phono3py.phonon_supercell_matrix is not None:
force_filename = "FORCES_FC2"
elif phono3py.dataset is None:
force_filename = "FORCES_FC3"
else:
raise RuntimeError("Force filename is not determined.")
try:
dataset = parse_forces(
phono3py,
ph3py_yaml=ph3py_yaml,
force_filename=force_filename,
fc_type="phonon_fc2",
calculator=calculator,
log_level=log_level,
)
except RuntimeError as e:
if log_level:
print(str(e))
print_error()
sys.exit(1)
except FileNotFoundError as e:
file_exists(e.filename, log_level=log_level)
if phono3py.phonon_supercell_matrix is not None:
phono3py.phonon_dataset = dataset
elif phono3py.dataset is None:
phono3py.dataset = dataset
def _get_default_ph3py_yaml(ph3py_yaml: Optional[Phono3pyYaml]):
_ph3py_yaml = ph3py_yaml
if _ph3py_yaml is None and pathlib.Path("phono3py_disp.yaml").exists():
_ph3py_yaml = Phono3pyYaml()
_ph3py_yaml.read("phono3py_disp.yaml")
return _ph3py_yaml
def _convert_unit_in_dataset(
dataset: dict,
distance_to_A: Optional[float] = None,
force_to_eVperA: Optional[float] = None,
distance_to_A: float | None = None,
force_to_eVperA: float | None = None,
) -> None:
"""Convert physical units of displacements and forces in dataset.
@ -722,9 +406,7 @@ def _to_ndarray(array, dtype="double"):
return array
def _extract_dataset_from_ph3py_yaml(
ph3py_yaml: Optional[Phono3pyYaml], fc_type
) -> Optional[dict]:
def _extract_dataset_from_ph3py_yaml(ph3py_yaml: Phono3pyYaml, fc_type) -> dict | None:
if ph3py_yaml.phonon_supercell is None or fc_type == "fc3":
if ph3py_yaml.dataset is not None:
return copy.deepcopy(ph3py_yaml.dataset)

View File

@ -34,11 +34,20 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import dataclasses
import os
import numpy as np
from numpy.typing import ArrayLike
from phonopy.cui.collect_cell_info import CellInfoResult
from phonopy.cui.collect_cell_info import get_cell_info as phonopy_get_cell_info
from phonopy.interface.calculator import write_supercells_with_displacements
from phonopy.structure.cells import print_cell
from phono3py import Phono3py
from phono3py.cui.settings import Phono3pySettings
from phono3py.cui.show_log import print_supercell_matrix
from phono3py.interface.calculator import (
get_additional_info_to_write_fc2_supercells,
@ -46,14 +55,56 @@ from phono3py.interface.calculator import (
get_default_displacement_distance,
)
from phono3py.interface.fc_calculator import determine_cutoff_pair_distance
from phono3py.interface.phono3py_yaml import Phono3pyYaml
@dataclasses.dataclass
class Phono3pyCellInfoResult(CellInfoResult):
"""Phono3py cell info result.
This is a subclass of CellInfoResult to add phonon supercell matrix.
"""
phono3py_yaml: Phono3pyYaml | None = None
phonon_supercell_matrix: ArrayLike | None = None
def get_cell_info(
settings: Phono3pySettings,
cell_filename: str | os.PathLike | None,
log_level: int,
load_phonopy_yaml: bool = True,
) -> Phono3pyCellInfoResult:
"""Return calculator interface and crystal structure information."""
cell_info = phonopy_get_cell_info(
settings,
cell_filename,
log_level=log_level,
load_phonopy_yaml=load_phonopy_yaml,
phonopy_yaml_cls=Phono3pyYaml,
)
cell_info_dict = dataclasses.asdict(cell_info)
cell_info_dict["phono3py_yaml"] = cell_info_dict.pop("phonopy_yaml")
cell_info = Phono3pyCellInfoResult(
**cell_info_dict,
phonon_supercell_matrix=settings.phonon_supercell_matrix,
)
ph3py_yaml = cell_info.phono3py_yaml
if cell_info.phonon_supercell_matrix is None and ph3py_yaml:
cell_info.phonon_supercell_matrix = ph3py_yaml.phonon_supercell_matrix
return cell_info
def create_phono3py_supercells(
cell_info,
settings,
symprec,
interface_mode="vasp",
log_level=1,
cell_info: Phono3pyCellInfoResult,
settings: Phono3pySettings,
symprec: float,
interface_mode: str | None = "vasp",
log_level: int = 1,
):
"""Create displacements and supercells.
@ -61,17 +112,17 @@ def create_phono3py_supercells(
The default unit is Angstrom.
"""
optional_structure_info = cell_info["optional_structure_info"]
optional_structure_info = cell_info.optional_structure_info
if settings.displacement_distance is None:
distance = get_default_displacement_distance(interface_mode)
else:
distance = settings.displacement_distance
ph3 = Phono3py(
cell_info["unitcell"],
cell_info["supercell_matrix"],
primitive_matrix=cell_info["primitive_matrix"],
phonon_supercell_matrix=cell_info["phonon_supercell_matrix"],
cell_info.unitcell,
cell_info.supercell_matrix,
primitive_matrix=cell_info.primitive_matrix,
phonon_supercell_matrix=cell_info.phonon_supercell_matrix,
is_symmetry=settings.is_symmetry,
symprec=symprec,
calculator=interface_mode,
@ -108,6 +159,7 @@ def create_phono3py_supercells(
is_diagonal=settings.is_diagonal_displacement,
number_of_snapshots=settings.random_displacements,
random_seed=settings.random_seed,
number_estimation_factor=settings.rd_number_estimation_factor,
)
if (
@ -152,7 +204,10 @@ def create_phono3py_supercells(
print(f"Cutoff distance for displacements: {cutoff_pair_distance}")
print(f"Number of displacement supercell files created: {num_disp_files}")
if ph3.phonon_supercell_matrix is not None:
if (
ph3.phonon_supercell_matrix is not None
and ph3.phonon_supercells_with_displacements is not None
):
num_disps = len(ph3.phonon_supercells_with_displacements)
additional_info = get_additional_info_to_write_fc2_supercells(
interface_mode, ph3.phonon_supercell_matrix

View File

@ -224,18 +224,18 @@ def _get_parser():
return args
def _read_files(args: argparse.Namespace) -> tuple[h5py.File, PhonopyAtoms]:
def _read_files(args: argparse.Namespace) -> tuple[h5py.File, PhonopyAtoms | None]:
primitive = None
cell_info = collect_cell_info(
supercell_matrix=np.eye(3, dtype=int),
phonopy_yaml_cls=Phono3pyYaml,
)
cell_filename = cell_info["optional_structure_info"][0]
cell_filename = cell_info.optional_structure_info[0]
print(f'# Crystal structure was read from "{cell_filename}".')
cell = cell_info["unitcell"]
phpy_yaml = cell_info.get("phonopy_yaml", None)
cell = cell_info.unitcell
phpy_yaml = cell_info.phonopy_yaml
if phpy_yaml is not None:
primitive = cell_info["phonopy_yaml"].primitive
primitive = phpy_yaml.primitive
if primitive is None:
primitive = cell
f_kappa = h5py.File(args.filenames[0], "r")

View File

@ -38,7 +38,7 @@ from __future__ import annotations
import os
import pathlib
from collections.abc import Sequence
from typing import Optional, Union
from typing import Literal
import numpy as np
import phonopy.cui.load_helper as load_helper
@ -65,34 +65,34 @@ from phono3py.phonon3.fc3 import show_drift_fc3
def load(
phono3py_yaml: Optional[
Union[str, bytes, os.PathLike]
] = None, # phono3py.yaml-like must be the first argument.
supercell_matrix: Optional[Union[Sequence, np.ndarray]] = None,
primitive_matrix: Optional[Union[Sequence, np.ndarray]] = None,
phonon_supercell_matrix: Optional[Union[Sequence, np.ndarray]] = None,
phono3py_yaml: str
| os.PathLike
| None = None, # phono3py.yaml-like must be the first argument.
supercell_matrix: Sequence | NDArray | None = None,
primitive_matrix: Sequence | NDArray | None = None,
phonon_supercell_matrix: Sequence | NDArray | None = None,
is_nac: bool = True,
calculator: Optional[str] = None,
unitcell: Optional[PhonopyAtoms] = None,
supercell: Optional[PhonopyAtoms] = None,
nac_params: Optional[dict] = None,
unitcell_filename: Optional[Union[str, bytes, os.PathLike]] = None,
supercell_filename: Optional[Union[str, bytes, os.PathLike]] = None,
born_filename: Optional[Union[str, bytes, os.PathLike]] = None,
forces_fc3_filename: Optional[Union[str, bytes, os.PathLike]] = None,
forces_fc2_filename: Optional[Union[str, bytes, os.PathLike]] = None,
fc3_filename: Optional[Union[str, bytes, os.PathLike]] = None,
fc2_filename: Optional[Union[str, bytes, os.PathLike]] = None,
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[str] = None,
factor: Optional[float] = None,
calculator: str | None = None,
unitcell: PhonopyAtoms | None = None,
supercell: PhonopyAtoms | None = None,
nac_params: dict | None = None,
unitcell_filename: str | os.PathLike | None = None,
supercell_filename: str | os.PathLike | None = None,
born_filename: str | os.PathLike | None = None,
forces_fc3_filename: str | os.PathLike | None = None,
forces_fc2_filename: str | os.PathLike | None = None,
fc3_filename: str | os.PathLike | None = None,
fc2_filename: str | os.PathLike | None = None,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
factor: float | None = None,
produce_fc: bool = True,
is_symmetry: bool = True,
symmetrize_fc: bool = True,
is_mesh_symmetry: bool = True,
is_compact_fc: bool = False,
use_pypolymlp: bool = False,
mlp_params: Optional[dict] = None,
mlp_params: dict | None = None,
use_grg: bool = False,
make_r0_average: bool = True,
symprec: float = 1e-5,
@ -347,15 +347,24 @@ def load(
load_fc2_and_fc3(
ph3py, fc3_filename=fc3_filename, fc2_filename=fc2_filename, log_level=log_level
)
load_dataset_and_phonon_dataset(
ph3py.dataset = select_and_load_dataset(
ph3py,
ph3py_yaml,
ph3py_yaml=ph3py_yaml,
forces_fc3_filename=forces_fc3_filename,
forces_fc2_filename=forces_fc2_filename,
phono3py_yaml_filename=phono3py_yaml,
calculator=_calculator,
log_level=log_level,
)
ph3py.phonon_dataset = select_and_load_phonon_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc2_filename=forces_fc2_filename,
calculator=_calculator,
log_level=log_level,
)
if use_pypolymlp and ph3py.fc3 is None and forces_in_dataset(ph3py.dataset):
ph3py.mlp_dataset = ph3py.dataset
ph3py.dataset = None
@ -372,7 +381,6 @@ def load(
fc_calculator_options=fc_calculator_options,
symmetrize_fc=symmetrize_fc,
is_compact_fc=is_compact_fc,
log_level=log_level,
)
if log_level and ph3py.fc3 is not None:
@ -387,60 +395,28 @@ def load(
def load_fc2_and_fc3(
ph3py: Phono3py,
fc3_filename: Optional[os.PathLike] = None,
fc2_filename: Optional[os.PathLike] = None,
fc3_filename: str | os.PathLike | None = None,
fc2_filename: str | os.PathLike | None = None,
read_fc3: bool = True,
read_fc2: bool = True,
log_level: int = 0,
):
"""Set force constants."""
if fc3_filename is not None or pathlib.Path("fc3.hdf5").exists():
if read_fc3 and (fc3_filename is not None or pathlib.Path("fc3.hdf5").exists()):
_load_fc3(ph3py, fc3_filename=fc3_filename, log_level=log_level)
if fc2_filename is not None or pathlib.Path("fc2.hdf5").exists():
if read_fc2 and (fc2_filename is not None or pathlib.Path("fc2.hdf5").exists()):
_load_fc2(ph3py, fc2_filename=fc2_filename, log_level=log_level)
def load_dataset_and_phonon_dataset(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
forces_fc3_filename: Optional[Union[os.PathLike, Sequence]] = None,
forces_fc2_filename: Optional[Union[os.PathLike, Sequence]] = None,
phono3py_yaml_filename: Optional[os.PathLike] = None,
cutoff_pair_distance: Optional[float] = None,
calculator: Optional[str] = None,
log_level: int = 0,
):
"""Set displacements, forces, and create force constants."""
dataset = _select_and_load_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc3_filename=forces_fc3_filename,
phono3py_yaml_filename=phono3py_yaml_filename,
cutoff_pair_distance=cutoff_pair_distance,
calculator=calculator,
log_level=log_level,
)
if dataset is not None:
ph3py.dataset = dataset
phonon_dataset = _select_and_load_phonon_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc2_filename=forces_fc2_filename,
calculator=calculator,
log_level=log_level,
)
if phonon_dataset is not None:
ph3py.phonon_dataset = phonon_dataset
def compute_force_constants_from_datasets(
ph3py: Phono3py,
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[Union[dict, str]] = None,
cutoff_pair_distance: Optional[float] = None,
fc_calculator: Literal["traditional", "symfc", "alm"] | str | None = None,
fc_calculator_options: str | None = None,
cutoff_pair_distance: float | None = None,
symmetrize_fc: bool = True,
is_compact_fc: bool = True,
log_level: int = 0,
load_phono3py_yaml: bool = False,
):
"""Compute force constants from datasets.
@ -468,11 +444,9 @@ def compute_force_constants_from_datasets(
is_compact_fc=is_compact_fc,
fc_calculator=fc3_calculator,
fc_calculator_options=fc3_calc_opts,
use_symfc_projector=load_phono3py_yaml,
)
if log_level and symmetrize_fc and fc_calculator is None:
print("fc3 was symmetrized.")
if not exist_fc2:
if (
ph3py.phonon_supercell_matrix is None and forces_in_dataset(ph3py.dataset)
@ -485,9 +459,8 @@ def compute_force_constants_from_datasets(
is_compact_fc=is_compact_fc,
fc_calculator=fc2_calculator,
fc_calculator_options=fc2_calc_opts,
use_symfc_projector=load_phono3py_yaml,
)
if log_level and symmetrize_fc and fc_calculator is None:
print("fc2 was symmetrized.")
def _load_fc3(
@ -517,16 +490,17 @@ def _load_fc3(
print(f'fc3 was read from "{_fc3_filename}".')
def _select_and_load_dataset(
def select_and_load_dataset(
ph3py: Phono3py,
ph3py_yaml: Phono3pyYaml | None = None,
forces_fc3_filename: os.PathLike | Sequence | None = None,
phono3py_yaml_filename: os.PathLike | None = None,
forces_fc3_filename: str | os.PathLike | Sequence | None = None,
phono3py_yaml_filename: str | os.PathLike | None = None,
cutoff_pair_distance: float | None = None,
calculator: str | None = None,
log_level: int = 0,
) -> dict | None:
dataset = None
"""Select and load dataset for fc3."""
# displacements and forces are in phono3py-yaml-like file
if (
ph3py_yaml is not None
and ph3py_yaml.dataset is not None
@ -541,7 +515,10 @@ def _select_and_load_dataset(
calculator,
log_level,
)
elif forces_fc3_filename is not None or pathlib.Path("FORCES_FC3").exists():
return dataset
# displacements and forces are in FORCES_FC3-like file
if forces_fc3_filename is not None or pathlib.Path("FORCES_FC3").exists():
if forces_fc3_filename is None:
force_filename = "FORCES_FC3"
else:
@ -555,7 +532,10 @@ def _select_and_load_dataset(
calculator,
log_level,
)
elif ph3py_yaml is not None and ph3py_yaml.dataset is not None:
return dataset
# dataset is in phono3py-yaml-like file
if ph3py_yaml is not None and ph3py_yaml.dataset is not None:
# not forces_in_dataset(ph3py_yaml.dataset)
# but want to read displacement dataset.
dataset = _get_dataset_for_fc3(
@ -567,12 +547,13 @@ def _select_and_load_dataset(
calculator,
log_level,
)
return dataset
return dataset
return None
def _load_fc2(
ph3py: Phono3py, fc2_filename: os.PathLike | None = None, log_level: int = 0
ph3py: Phono3py, fc2_filename: str | os.PathLike | None = None, log_level: int = 0
):
phonon_p2s_map = ph3py.phonon_primitive.p2s_map
if fc2_filename is None:
@ -586,19 +567,18 @@ def _load_fc2(
ph3py.fc2 = fc2
def _select_and_load_phonon_dataset(
def select_and_load_phonon_dataset(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
forces_fc2_filename: Optional[Union[os.PathLike, Sequence]] = None,
calculator: Optional[str] = None,
ph3py_yaml: Phono3pyYaml | None = None,
forces_fc2_filename: str | os.PathLike | Sequence | None = None,
calculator: str | None = None,
log_level: int = 0,
) -> Optional[dict]:
phonon_dataset = None
if (
ph3py_yaml is not None
and ph3py_yaml.phonon_dataset is not None
and forces_in_dataset(ph3py_yaml.phonon_dataset)
):
) -> dict | None:
"""Select and load phonon dataset for fc2."""
if ph3py.phonon_supercell_matrix is None:
return None
if ph3py_yaml is not None and forces_in_dataset(ph3py_yaml.phonon_dataset):
phonon_dataset = _get_dataset_for_fc2(
ph3py,
ph3py_yaml,
@ -607,9 +587,9 @@ def _select_and_load_phonon_dataset(
calculator,
log_level,
)
elif (
forces_fc2_filename is not None or pathlib.Path("FORCES_FC2").exists()
) and ph3py.phonon_supercell_matrix is not None:
return phonon_dataset
if forces_fc2_filename is not None or pathlib.Path("FORCES_FC2").exists():
if forces_fc2_filename is None:
force_filename = "FORCES_FC2"
else:
@ -622,7 +602,9 @@ def _select_and_load_phonon_dataset(
calculator,
log_level,
)
elif ph3py_yaml is not None and ph3py_yaml.phonon_dataset is not None:
return phonon_dataset
if ph3py_yaml is not None:
# not forces_in_dataset(ph3py_yaml.dataset)
# but want to read displacement dataset.
phonon_dataset = _get_dataset_for_fc2(
@ -633,13 +615,14 @@ def _select_and_load_phonon_dataset(
calculator,
log_level,
)
return phonon_dataset
return phonon_dataset
return None
def _get_dataset_for_fc3(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
ph3py_yaml: Phono3pyYaml | None,
force_filename,
phono3py_yaml_filename,
cutoff_pair_distance,
@ -661,7 +644,7 @@ def _get_dataset_for_fc3(
def _get_dataset_for_fc2(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
ph3py_yaml: Phono3pyYaml | None,
force_filename,
fc_type,
calculator,

View File

@ -39,7 +39,7 @@ import sys
from phonopy.cui.phonopy_argparse import fix_deprecated_option_names
def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
def get_parser(load_phono3py_yaml: bool = False):
"""Return ArgumentParser instance."""
deprecated = fix_deprecated_option_names(sys.argv)
import argparse
@ -70,7 +70,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--ave-pp",
dest="use_ave_pp",
action="store_true",
default=False,
default=None,
help="Use averaged ph-ph interaction",
)
parser.add_argument(
@ -113,7 +113,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--bterta",
dest="is_bterta",
action="store_true",
default=False,
default=None,
help="Calculate thermal conductivity in BTE-RTA",
)
if not load_phono3py_yaml:
@ -155,7 +155,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
metavar="FILE",
dest="subtract_forces",
default=None,
help="Subtract recidual forces from supercell forces",
help="Subtract residual forces from supercell forces",
)
parser.add_argument(
"--cfz-fc2",
@ -163,15 +163,15 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
metavar="FILE",
dest="subtract_forces_fc2",
default=None,
help="Subtract recidual forces from supercell forces for fc2",
help="Subtract residual forces from supercell forces for fc2",
)
parser.add_argument(
"--cfc",
"--compact-fc",
dest="is_compact_fc",
action="store_true",
default=False,
help="Use compact force cosntants",
default=None,
help="Use compact force constants",
)
parser.add_argument(
"--cfs",
@ -241,7 +241,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--disp",
dest="is_displacement",
action="store_true",
default=False,
default=None,
help="As first stage, get least displacements",
)
if not load_phono3py_yaml:
@ -263,7 +263,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
# "--emulate-v2",
# dest="emulate_v2",
# action="store_true",
# default=False,
# default=None,
# help="Emulate v2.x behavior.",
# )
parser.add_argument(
@ -305,7 +305,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"string with the style of key = values"
),
)
if not fc_symmetry:
if not load_phono3py_yaml:
parser.add_argument(
"--fc-symmetry",
"--sym-fc",
@ -487,7 +487,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Mass variance parameters for isotope scattering",
)
if not is_nac:
if not load_phono3py_yaml:
parser.add_argument(
"--nac",
dest="is_nac",
@ -501,7 +501,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Non-analytical term correction method: Gonze (default) or Wang",
)
if fc_symmetry:
if load_phono3py_yaml:
parser.add_argument(
"--no-fc-symmetry",
"--no-sym-fc",
@ -510,19 +510,34 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Do not symmetrize force constants",
)
parser.add_argument(
"--no-read-fc2",
dest="read_fc2",
action="store_false",
default=None,
help="Read second order force constants",
)
parser.add_argument(
"--no-read-fc3",
dest="read_fc3",
action="store_false",
default=None,
help="Read third order force constants",
)
parser.add_argument(
"--nodiag",
dest="is_nodiag",
action="store_true",
default=False,
default=None,
help="Set displacements parallel to axes",
)
parser.add_argument(
"--noks",
"--no-kappa-stars",
dest="no_kappa_stars",
action="store_true",
default=False,
dest="kappa_star",
action="store_false",
default=None,
help="Deactivate summation of partial kappa at q-stars",
)
parser.add_argument(
@ -532,7 +547,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=False,
help="No symmetrization of triplets is made.",
)
if is_nac:
if load_phono3py_yaml:
parser.add_argument(
"--nonac",
dest="is_nac",
@ -544,14 +559,14 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--nosym",
dest="is_nosym",
action="store_true",
default=False,
default=None,
help="Symmetry is not imposed.",
)
parser.add_argument(
"--nu",
dest="is_N_U",
action="store_true",
default=False,
default=None,
help="Split Gamma into Normal and Umklapp processes",
)
parser.add_argument(
@ -616,14 +631,14 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--pm",
dest="is_plusminus_displacements",
action="store_true",
default=False,
default=None,
help="Set plus minus displacements",
)
parser.add_argument(
"--pm-fc2",
dest="is_plusminus_displacements_fc2",
action="store_true",
default=False,
default=None,
help="Set plus minus displacements for extra fc2",
)
parser.add_argument(
@ -637,7 +652,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--pypolymlp",
dest="use_pypolymlp",
action="store_true",
default=False,
default=None,
help="Use pypolymlp and symfc for generating force constants",
)
parser.add_argument(
@ -659,7 +674,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--quiet",
dest="quiet",
action="store_true",
default=False,
default=None,
help="Print out smallest information",
)
parser.add_argument(
@ -676,6 +691,13 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help='Number of supercells with random displacements or "auto"',
)
parser.add_argument(
"--rd-auto-factor",
dest="rd_number_estimation_factor",
type=float,
default=None,
help="Factor to estimate number of supercells with random displacements",
)
parser.add_argument(
"--rd-fc2",
"--random-displacements-fc2",
@ -693,35 +715,35 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--read-gamma",
dest="read_gamma",
action="store_true",
default=False,
default=None,
help="Read Gammas from files",
)
parser.add_argument(
"--read-phonon",
dest="read_phonon",
action="store_true",
default=False,
default=None,
help="Read phonons from files",
)
parser.add_argument(
"--read-pp",
dest="read_pp",
action="store_true",
default=False,
default=None,
help="Read phonon-phonon interaction strength",
)
parser.add_argument(
"--reducible-colmat",
dest="is_reducible_collision_matrix",
action="store_true",
default=False,
default=None,
help="Solve reducible collision matrix",
)
parser.add_argument(
"--rse",
dest="is_real_self_energy",
action="store_true",
default=False,
default=None,
help="Calculate real part of self energy",
)
parser.add_argument(
@ -782,7 +804,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--spf",
dest="is_spectral_function",
action="store_true",
default=False,
default=None,
help="Calculate spectral function",
)
parser.add_argument(
@ -790,7 +812,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--show-num-triplets",
dest="show_num_triplets",
action="store_true",
default=False,
default=None,
help=(
"Show reduced number of triplets to be calculated at specified grid points"
),
@ -800,21 +822,21 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--sym-fc2",
dest="is_symmetrize_fc2",
action="store_true",
default=False,
default=None,
help="Symmetrize fc2 by index exchange",
)
parser.add_argument(
"--sym-fc3r",
dest="is_symmetrize_fc3_r",
action="store_true",
default=False,
default=None,
help="Symmetrize fc3 in real space by index exchange",
)
parser.add_argument(
"--sym-fc3q",
dest="is_symmetrize_fc3_q",
action="store_true",
default=False,
default=None,
help="Symmetrize fc3 in reciprocal space by index exchange",
)
parser.add_argument(
@ -822,7 +844,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--tetrahedron-method",
dest="is_tetrahedron_method",
action="store_true",
default=False,
default=None,
help="Use tetrahedron method.",
)
parser.add_argument(
@ -859,14 +881,14 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--verbose",
dest="verbose",
action="store_true",
default=False,
default=None,
help="Detailed run-time information is displayed",
)
parser.add_argument(
"--v2",
dest="is_fc3_r0_average",
action="store_false",
default=True,
default=None,
help="Take average in fc3-r2q transformation around three atoms",
)
parser.add_argument(
@ -874,7 +896,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--write-grid-points",
dest="write_grid_points",
action="store_true",
default=False,
default=None,
help=(
"Write grid address of irreducible grid points for specified "
"mesh numbers to ir_grid_address.yaml"
@ -884,21 +906,21 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--wigner",
dest="is_wigner_kappa",
action="store_true",
default=False,
default=None,
help="Choose Wigner lattice thermal conductivity.",
)
parser.add_argument(
"--write-collision",
dest="write_collision",
action="store_true",
default=False,
default=None,
help="Write collision matrix and Gammas to files",
)
parser.add_argument(
"--write-gamma",
dest="write_gamma",
action="store_true",
default=False,
default=None,
help="Write imag-part of self energy to files",
)
parser.add_argument(
@ -906,28 +928,28 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--write_detailed_gamma",
dest="write_gamma_detail",
action="store_true",
default=False,
default=None,
help="Write out detailed imag-part of self energy",
)
parser.add_argument(
"--write-phonon",
dest="write_phonon",
action="store_true",
default=False,
default=None,
help="Write all phonons on grid points to files",
)
parser.add_argument(
"--write-pp",
dest="write_pp",
action="store_true",
default=False,
default=None,
help="Write phonon-phonon interaction strength",
)
parser.add_argument(
"--write-lbte-solution",
dest="write_LBTE_solution",
action="store_true",
default=False,
default=None,
help="Write direct solution of LBTE to hdf5 files",
)
if load_phono3py_yaml:

View File

@ -37,13 +37,16 @@
from __future__ import annotations
import argparse
import os
import pathlib
import sys
import warnings
from typing import Optional
from collections.abc import Sequence
from typing import Optional, cast
import numpy as np
from phonopy.cui.collect_cell_info import collect_cell_info
from numpy.typing import NDArray
from phonopy.api_phonopy import Phonopy
from phonopy.cui.phonopy_argparse import show_deprecated_option_warnings
from phonopy.cui.phonopy_script import (
file_exists,
@ -52,10 +55,10 @@ from phonopy.cui.phonopy_script import (
print_error_message,
print_time,
print_version,
set_magnetic_moments,
store_nac_params,
)
from phonopy.exception import ForceCalculatorRequiredError
from phonopy.cui.settings import PhonopySettings
from phonopy.exception import CellNotFoundError, ForceCalculatorRequiredError
from phonopy.file_IO import is_file_phonopy_yaml
from phonopy.harmonic.force_constants import show_drift_force_constants
from phonopy.interface.calculator import get_calculator_physical_units
@ -65,21 +68,22 @@ from phonopy.physical_units import get_physical_units
from phonopy.structure.cells import isclose as cells_isclose
from phono3py import Phono3py, Phono3pyIsotope, Phono3pyJointDos
from phono3py.cui.create_force_constants import (
create_phono3py_force_constants,
get_fc_calculator_params,
run_pypolymlp_to_compute_forces,
)
from phono3py.cui.create_force_constants import run_pypolymlp_to_compute_forces
from phono3py.cui.create_force_sets import (
create_FORCE_SETS_from_FORCES_FCx,
create_FORCES_FC2_from_FORCE_SETS,
create_FORCES_FC3_and_FORCES_FC2,
)
from phono3py.cui.create_supercells import create_phono3py_supercells
from phono3py.cui.create_supercells import (
Phono3pyCellInfoResult,
create_phono3py_supercells,
get_cell_info,
)
from phono3py.cui.load import (
compute_force_constants_from_datasets,
load_dataset_and_phonon_dataset,
load_fc2_and_fc3,
select_and_load_dataset,
select_and_load_phonon_dataset,
)
from phono3py.cui.phono3py_argparse import get_parser
from phono3py.cui.settings import Phono3pyConfParser, Phono3pySettings
@ -95,9 +99,12 @@ from phono3py.file_IO import (
write_fc3_to_hdf5,
write_phonon_to_hdf5,
)
from phono3py.interface.fc_calculator import determine_cutoff_pair_distance
from phono3py.interface.fc_calculator import (
determine_cutoff_pair_distance,
get_fc_calculator_params,
)
from phono3py.interface.phono3py_yaml import Phono3pyYaml
from phono3py.phonon.grid import get_grid_point_from_address, get_ir_grid_points
from phono3py.phonon.grid import BZGrid, get_grid_point_from_address, get_ir_grid_points
from phono3py.phonon3.dataset import forces_in_dataset
from phono3py.phonon3.fc3 import show_drift_fc3
from phono3py.phonon3.gruneisen import run_gruneisen_parameters
@ -129,12 +136,12 @@ def print_end_phono3py():
print_end()
def finalize_phono3py(
def _finalize_phono3py(
phono3py: Phono3py,
confs_dict,
log_level,
write_displacements=False,
filename=None,
confs_dict: dict,
log_level: int,
write_displacements: bool = False,
filename: str | None = None,
):
"""Write phono3py.yaml and then exit.
@ -183,7 +190,7 @@ def finalize_phono3py(
sys.exit(0)
def get_run_mode(settings):
def _get_run_mode(settings: Phono3pySettings):
"""Extract run mode from settings."""
run_mode = None
if settings.is_gruneisen:
@ -211,9 +218,9 @@ def get_run_mode(settings):
return run_mode
def start_phono3py(**argparse_control) -> tuple[argparse.Namespace, int]:
def _start_phono3py(**argparse_control) -> tuple[argparse.Namespace, int]:
"""Parse arguments and set some basic parameters."""
parser, deprecated = get_parser(**argparse_control)
parser, deprecated = get_parser(argparse_control.get("load_phono3py_yaml", False))
args = parser.parse_args()
# Log level
@ -242,9 +249,9 @@ def start_phono3py(**argparse_control) -> tuple[argparse.Namespace, int]:
import spglib
try: # spglib.get_version() is deprecated.
print(f"Spglib version {spglib.spg_get_version()}")
print(f"Spglib version {spglib.spg_get_version()}") # type: ignore
except AttributeError:
print("Spglib version %d.%d.%d" % spglib.get_version())
print("Spglib version %d.%d.%d" % spglib.get_version()) # type: ignore
if deprecated:
show_deprecated_option_warnings(deprecated)
@ -252,7 +259,9 @@ def start_phono3py(**argparse_control) -> tuple[argparse.Namespace, int]:
return args, log_level
def read_phono3py_settings(args, argparse_control, log_level):
def _read_phono3py_settings(
args: argparse.Namespace, argparse_control: dict, log_level: int
):
"""Read phono3py settings.
From:
@ -269,20 +278,20 @@ def read_phono3py_settings(args, argparse_control, log_level):
phono3py_conf_parser = Phono3pyConfParser(
filename=args.conf_filename,
args=args,
default_settings=argparse_control,
load_phono3py_yaml=load_phono3py_yaml,
)
cell_filename = args.filename[0]
else:
if is_file_phonopy_yaml(args.filename[0], keyword="phono3py"):
phono3py_conf_parser = Phono3pyConfParser(
args=args, default_settings=argparse_control
args=args, load_phono3py_yaml=load_phono3py_yaml
)
cell_filename = args.filename[0]
else: # args.filename[0] is assumed to be phono3py-conf file.
phono3py_conf_parser = Phono3pyConfParser(
filename=args.filename[0],
args=args,
default_settings=argparse_control,
load_phono3py_yaml=load_phono3py_yaml,
)
cell_filename = phono3py_conf_parser.settings.cell_filename
else:
@ -290,11 +299,11 @@ def read_phono3py_settings(args, argparse_control, log_level):
phono3py_conf_parser = Phono3pyConfParser(
args=args,
filename=args.conf_filename,
default_settings=argparse_control,
load_phono3py_yaml=load_phono3py_yaml,
)
else:
phono3py_conf_parser = Phono3pyConfParser(
args=args, default_settings=argparse_control
args=args, load_phono3py_yaml=load_phono3py_yaml
)
cell_filename = phono3py_conf_parser.settings.cell_filename
@ -304,7 +313,7 @@ def read_phono3py_settings(args, argparse_control, log_level):
return settings, confs_dict, cell_filename
def get_input_output_filenames_from_args(args):
def _get_input_output_filenames_from_args(args: argparse.Namespace):
"""Return strings inserted to input and output filenames."""
if args.input_filename is not None:
warnings.warn(
@ -331,36 +340,7 @@ def get_input_output_filenames_from_args(args):
return input_filename, output_filename
def get_cell_info(
settings: Phono3pySettings, cell_filename: str, log_level: int
) -> dict:
"""Return calculator interface and crystal structure information."""
cell_info = collect_cell_info(
supercell_matrix=settings.supercell_matrix,
primitive_matrix=settings.primitive_matrix,
interface_mode=settings.calculator,
cell_filename=cell_filename,
chemical_symbols=settings.chemical_symbols,
phonopy_yaml_cls=Phono3pyYaml,
)
if "error_message" in cell_info:
print_error_message(cell_info["error_message"])
if log_level > 0:
print_error()
sys.exit(1)
set_magnetic_moments(cell_info, settings, log_level)
cell_info["phonon_supercell_matrix"] = settings.phonon_supercell_matrix
ph3py_yaml: Phono3pyYaml = cell_info["phonopy_yaml"]
if cell_info["phonon_supercell_matrix"] is None and ph3py_yaml:
ph_smat = ph3py_yaml.phonon_supercell_matrix
cell_info["phonon_supercell_matrix"] = ph_smat
return cell_info
def get_default_values(settings):
def _get_default_values(settings: Phono3pySettings):
"""Set default values."""
# Brillouin zone integration: Tetrahedron (default) or smearing method
sigma = settings.sigma
@ -434,52 +414,59 @@ def get_default_values(settings):
return params
def check_supercell_in_yaml(cell_info, ph3, distance_to_A, log_level):
def _check_supercell_in_yaml(
cell_info: Phono3pyCellInfoResult,
ph3: Phono3py,
distance_to_A: float | None,
log_level: int,
):
"""Check consistency between generated cells and cells in yaml."""
if cell_info["phonopy_yaml"] is not None:
if cell_info.phono3py_yaml is not None:
if distance_to_A is None:
d2A = 1.0
else:
d2A = distance_to_A
if (
cell_info["phonopy_yaml"].supercell is not None
and ph3.supercell is not None
): # noqa E129
yaml_cell = cell_info["phonopy_yaml"].supercell.copy()
phono3py_yaml = cell_info.phono3py_yaml
if phono3py_yaml.supercell is not None and ph3.supercell is not None: # noqa E129
yaml_cell = phono3py_yaml.supercell.copy()
yaml_cell.cell = yaml_cell.cell * d2A
if not cells_isclose(yaml_cell, ph3.supercell):
if log_level:
print(
"Generated supercell is inconsistent with "
'that in "%s".' % cell_info["optional_structure_info"][0]
f'that in "{cell_info.optional_structure_info[0]}".'
)
print_error()
sys.exit(1)
if (
cell_info["phonopy_yaml"].phonon_supercell is not None
phono3py_yaml.phonon_supercell is not None
and ph3.phonon_supercell is not None
): # noqa E129
yaml_cell = cell_info["phonopy_yaml"].phonon_supercell.copy()
yaml_cell = phono3py_yaml.phonon_supercell.copy()
yaml_cell.cell = yaml_cell.cell * d2A
if not cells_isclose(yaml_cell, ph3.phonon_supercell):
if log_level:
print(
"Generated phonon supercell is inconsistent with "
'that in "%s".' % cell_info["optional_structure_info"][0]
f'that in "{cell_info.optional_structure_info[0]}".'
)
print_error()
sys.exit(1)
def init_phono3py(
settings, cell_info, interface_mode, symprec, log_level
def _init_phono3py(
settings: Phono3pySettings,
cell_info: Phono3pyCellInfoResult,
interface_mode: str | None,
symprec: float,
log_level: int,
) -> tuple[Phono3py, dict]:
"""Initialize phono3py and update settings by default values."""
physical_units = get_calculator_physical_units(interface_mode)
distance_to_A = physical_units["distance_to_A"]
# Change unit of lattice parameters to angstrom
unitcell = cell_info["unitcell"].copy()
unitcell = cell_info.unitcell.copy()
if distance_to_A is not None:
lattice = unitcell.cell
lattice *= distance_to_A
@ -490,13 +477,13 @@ def init_phono3py(
# 'frequency_factor_to_THz', 'num_frequency_points',
# 'frequency_step', 'frequency_scale_factor',
# 'cutoff_frequency')
updated_settings = get_default_values(settings)
updated_settings = _get_default_values(settings)
phono3py = Phono3py(
unitcell,
cell_info["supercell_matrix"],
primitive_matrix=cell_info["primitive_matrix"],
phonon_supercell_matrix=cell_info["phonon_supercell_matrix"],
cell_info.supercell_matrix,
primitive_matrix=cell_info.primitive_matrix,
phonon_supercell_matrix=cell_info.phonon_supercell_matrix,
cutoff_frequency=updated_settings["cutoff_frequency"],
frequency_factor_to_THz=updated_settings["frequency_factor_to_THz"],
is_symmetry=settings.is_symmetry,
@ -511,15 +498,15 @@ def init_phono3py(
phono3py.sigmas = updated_settings["sigmas"]
phono3py.sigma_cutoff = settings.sigma_cutoff_width
check_supercell_in_yaml(cell_info, phono3py, distance_to_A, log_level)
_check_supercell_in_yaml(cell_info, phono3py, distance_to_A, log_level)
return phono3py, updated_settings
def settings_to_grid_points(settings, bz_grid):
def _settings_to_grid_points(settings: Phono3pySettings, bz_grid: BZGrid):
"""Read or set grid point indices."""
if settings.grid_addresses is not None:
grid_points = grid_addresses_to_grid_points(settings.grid_addresses, bz_grid)
grid_points = _grid_addresses_to_grid_points(settings.grid_addresses, bz_grid)
elif settings.grid_points is not None:
grid_points = settings.grid_points
else:
@ -527,7 +514,7 @@ def settings_to_grid_points(settings, bz_grid):
return grid_points
def grid_addresses_to_grid_points(grid_addresses, bz_grid):
def _grid_addresses_to_grid_points(grid_addresses: NDArray, bz_grid: BZGrid):
"""Return grid point indices from grid addresses."""
grid_points = [
get_grid_point_from_address(ga, bz_grid.D_diag) for ga in grid_addresses
@ -535,13 +522,12 @@ def grid_addresses_to_grid_points(grid_addresses, bz_grid):
return bz_grid.grg2bzg[grid_points]
def create_supercells_with_displacements(
def _create_supercells_with_displacements(
settings: Phono3pySettings,
cell_info: dict,
cell_info: Phono3pyCellInfoResult,
confs_dict: dict,
unitcell_filename: str,
interface_mode: Optional[str],
load_phono3py_yaml: bool,
symprec: float,
log_level: int,
):
@ -561,13 +547,12 @@ def create_supercells_with_displacements(
if pathlib.Path("BORN").exists():
store_nac_params(
phono3py,
settings,
cell_info["phonopy_yaml"],
cast(Phonopy, phono3py),
cast(PhonopySettings, settings),
cell_info.phono3py_yaml,
unitcell_filename,
log_level,
nac_factor=get_physical_units().Hartree * get_physical_units().Bohr,
load_phonopy_yaml=load_phono3py_yaml,
)
if log_level:
@ -579,7 +564,7 @@ def create_supercells_with_displacements(
% len(phono3py.symmetry.symmetry_operations["rotations"])
)
finalize_phono3py(
_finalize_phono3py(
phono3py,
confs_dict,
log_level,
@ -588,18 +573,21 @@ def create_supercells_with_displacements(
)
def _store_force_constants(ph3py: Phono3py, settings: Phono3pySettings, log_level: int):
def _produce_force_constants(
ph3py: Phono3py,
settings: Phono3pySettings,
log_level: int,
load_phono3py_yaml: bool,
):
"""Calculate, read, and write force constants."""
if log_level:
print("-" * 29 + " Force constants " + "-" * 30)
load_fc2_and_fc3(ph3py, log_level=log_level)
read_fc3 = ph3py.fc3 is not None
read_fc2 = ph3py.fc2 is not None
cutoff_pair_distance = None
if settings.use_pypolymlp:
if settings.use_pypolymlp and ph3py.dataset is not None:
cutoff_pair_distance = ph3py.dataset.get("cutoff_distance")
if cutoff_pair_distance is None:
cutoff_pair_distance = determine_cutoff_pair_distance(
@ -629,11 +617,17 @@ def _store_force_constants(ph3py: Phono3py, settings: Phono3pySettings, log_leve
cutoff_pair_distance=cutoff_pair_distance,
symmetrize_fc=settings.fc_symmetry,
is_compact_fc=settings.is_compact_fc,
log_level=log_level,
load_phono3py_yaml=load_phono3py_yaml,
)
except ForceCalculatorRequiredError:
if log_level:
print("Symfc will be used to handle general (or random) displacements.")
except ForceCalculatorRequiredError as e:
if load_phono3py_yaml:
if log_level:
print("Symfc will be used to handle general (or random) displacements.")
else:
print_error_message(str(e))
if log_level:
print_error()
sys.exit(1)
compute_force_constants_from_datasets(
ph3py,
@ -642,17 +636,13 @@ def _store_force_constants(ph3py: Phono3py, settings: Phono3pySettings, log_leve
cutoff_pair_distance=cutoff_pair_distance,
symmetrize_fc=settings.fc_symmetry,
is_compact_fc=settings.is_compact_fc,
log_level=log_level,
)
# _show_fc_calculator_not_found(log_level)
if log_level:
if ph3py.fc3 is None:
print("fc3 could not be obtained.")
if not forces_in_dataset(ph3py.dataset):
print("Forces were not found.")
else:
show_drift_fc3(ph3py.fc3, primitive=ph3py.primitive)
if ph3py.fc2 is None:
print("fc2 could not be obtained.")
if ph3py.phonon_supercell_matrix is None:
@ -661,15 +651,17 @@ def _store_force_constants(ph3py: Phono3py, settings: Phono3pySettings, log_leve
else:
if not forces_in_dataset(ph3py.phonon_dataset):
print("Forces for dim-fc2 were not found.")
else:
show_drift_force_constants(
ph3py.fc2, primitive=ph3py.phonon_primitive, name="fc2"
)
if ph3py.fc3 is None or ph3py.fc2 is None:
print_error()
sys.exit(1)
if log_level:
show_drift_fc3(ph3py.fc3, primitive=ph3py.primitive)
show_drift_force_constants(
ph3py.fc2, primitive=ph3py.phonon_primitive, name="fc2"
)
cutoff_distance = settings.cutoff_fc3_distance
if cutoff_distance is not None and cutoff_distance > 0:
if log_level:
@ -697,20 +689,9 @@ def _store_force_constants(ph3py: Phono3py, settings: Phono3pySettings, log_leve
print('fc2 was written into "fc2.hdf5".')
def _show_fc_calculator_not_found(log_level):
if log_level:
print("")
print(
"Built-in force constants calculator doesn't support the "
"displacements-forces dataset. "
"An external force calculator, e.g., symfc (--symfc_ or ALM (--alm), "
"has to be used to compute force constants."
)
print_error()
sys.exit(1)
def run_gruneisen_then_exit(phono3py, settings, output_filename, log_level):
def _run_gruneisen_then_exit(
phono3py: Phono3py, settings: Phono3pySettings, output_filename: str, log_level: int
):
"""Run mode Grueneisen parameter calculation from fc3."""
if (
settings.mesh_numbers is None
@ -762,8 +743,12 @@ def run_gruneisen_then_exit(phono3py, settings, output_filename, log_level):
sys.exit(0)
def run_jdos_then_exit(
phono3py: Phono3py, settings, updated_settings, output_filename, log_level
def _run_jdos_then_exit(
phono3py: Phono3py,
settings: Phono3pySettings,
updated_settings: dict,
output_filename: str | None,
log_level: int,
):
"""Run joint-DOS calculation."""
joint_dos = Phono3pyJointDos(
@ -793,7 +778,7 @@ def run_jdos_then_exit(
if dm.is_nac() and dm.nac_method == "gonze":
dm.show_Gonze_nac_message()
grid_points = settings_to_grid_points(settings, joint_dos.grid)
grid_points = _settings_to_grid_points(settings, joint_dos.grid)
joint_dos.run(grid_points, write_jdos=True)
if log_level:
@ -801,7 +786,12 @@ def run_jdos_then_exit(
sys.exit(0)
def run_isotope_then_exit(phono3py, settings, updated_settings, log_level):
def _run_isotope_then_exit(
phono3py: Phono3py,
settings: Phono3pySettings,
updated_settings: dict,
log_level: int,
):
"""Run isotope scattering calculation."""
mass_variances = settings.mass_variances
if settings.band_indices is not None:
@ -832,7 +822,7 @@ def run_isotope_then_exit(phono3py, settings, updated_settings, log_level):
if dm.is_nac() and dm.nac_method == "gonze":
dm.show_Gonze_nac_message()
grid_points = settings_to_grid_points(settings, iso.grid)
grid_points = _settings_to_grid_points(settings, iso.grid)
iso.run(grid_points)
if log_level:
@ -840,13 +830,13 @@ def run_isotope_then_exit(phono3py, settings, updated_settings, log_level):
sys.exit(0)
def init_phph_interaction(
def _init_phph_interaction(
phono3py: Phono3py,
settings,
updated_settings,
input_filename,
output_filename,
log_level,
settings: Phono3pySettings,
updated_settings: dict,
input_filename: str | None,
output_filename: str | None,
log_level: int,
):
"""Initialize ph-ph interaction and phonons on grid."""
if log_level:
@ -930,6 +920,44 @@ def init_phph_interaction(
sys.exit(1)
def _load_dataset_and_phonon_dataset(
ph3py: Phono3py,
ph3py_yaml: Phono3pyYaml | None = None,
forces_fc3_filename: str | os.PathLike | Sequence | None = None,
forces_fc2_filename: str | os.PathLike | Sequence | None = None,
phono3py_yaml_filename: str | os.PathLike | None = None,
cutoff_pair_distance: float | None = None,
calculator: str | None = None,
log_level: int = 0,
):
"""Set displacements, forces, and create force constants."""
if ph3py.fc3 is None or (
ph3py.fc2 is None and ph3py.phonon_supercell_matrix is None
):
dataset = select_and_load_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc3_filename=forces_fc3_filename,
phono3py_yaml_filename=phono3py_yaml_filename,
cutoff_pair_distance=cutoff_pair_distance,
calculator=calculator,
log_level=log_level,
)
if dataset is not None:
ph3py.dataset = dataset
if ph3py.fc2 is None and ph3py.phonon_supercell_matrix is not None:
phonon_dataset = select_and_load_phonon_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc2_filename=forces_fc2_filename,
calculator=calculator,
log_level=log_level,
)
if phonon_dataset is not None:
ph3py.phonon_dataset = phonon_dataset
def main(**argparse_control):
"""Phono3py main part of command line interface."""
# import warnings
@ -941,17 +969,17 @@ def main(**argparse_control):
args = argparse_control["args"]
log_level = args.log_level
else:
args, log_level = start_phono3py(**argparse_control)
args, log_level = _start_phono3py(**argparse_control)
if load_phono3py_yaml:
input_filename = None
output_filename = None
output_yaml_filename = args.output_yaml_filename
else:
(input_filename, output_filename) = get_input_output_filenames_from_args(args)
(input_filename, output_filename) = _get_input_output_filenames_from_args(args)
output_yaml_filename = None
settings, confs_dict, cell_filename = read_phono3py_settings(
settings, confs_dict, cell_filename = _read_phono3py_settings(
args, argparse_control, log_level
)
@ -999,25 +1027,35 @@ def main(**argparse_control):
else:
symprec = settings.symmetry_tolerance
cell_info = get_cell_info(settings, cell_filename, log_level)
unitcell_filename = cell_info["optional_structure_info"][0]
interface_mode = cell_info["interface_mode"]
# ph3py_yaml = cell_info['phonopy_yaml']
try:
cell_info = get_cell_info(
settings,
cell_filename,
log_level=log_level,
load_phonopy_yaml=load_phono3py_yaml,
)
except CellNotFoundError as e:
print_error_message(str(e))
if log_level:
print_error()
sys.exit(1)
unitcell_filename = cell_info.optional_structure_info[0]
interface_mode = cell_info.interface_mode
if run_mode is None:
run_mode = get_run_mode(settings)
run_mode = _get_run_mode(settings)
######################################################
# Create supercells with displacements and then exit #
######################################################
if not settings.use_pypolymlp:
create_supercells_with_displacements(
_create_supercells_with_displacements(
settings,
cell_info,
confs_dict,
unitcell_filename,
interface_mode,
load_phono3py_yaml,
symprec,
log_level,
)
@ -1030,7 +1068,7 @@ def main(**argparse_control):
# 'frequency_factor_to_THz', 'num_frequency_points',
# 'frequency_step', 'frequency_scale_factor',
# 'cutoff_frequency')
ph3py, updated_settings = init_phono3py(
ph3py, updated_settings = _init_phono3py(
settings, cell_info, interface_mode, symprec, log_level
)
@ -1138,7 +1176,7 @@ def main(**argparse_control):
################################################################
if run_mode == "show_triplets_info":
ph3py.mesh_numbers = settings.mesh_numbers
grid_points = settings_to_grid_points(settings, ph3py.grid)
grid_points = _settings_to_grid_points(settings, ph3py.grid)
show_num_triplets(
ph3py.primitive,
ph3py.grid,
@ -1156,38 +1194,45 @@ def main(**argparse_control):
##################################
if settings.is_nac:
store_nac_params(
ph3py,
settings,
cell_info["phonopy_yaml"],
cast(Phonopy, ph3py),
cast(PhonopySettings, settings),
cell_info.phono3py_yaml,
unitcell_filename,
log_level,
nac_factor=get_physical_units().Hartree * get_physical_units().Bohr,
load_phonopy_yaml=load_phono3py_yaml,
)
########################
# Read force constants #
########################
load_fc2_and_fc3(
ph3py,
read_fc3=settings.read_fc3,
read_fc2=settings.read_fc2,
log_level=log_level,
)
############
# Datasets #
############
if load_phono3py_yaml:
assert ph3py.dataset is None
assert ph3py.phonon_dataset is None
load_dataset_and_phonon_dataset(
ph3py,
ph3py_yaml=cell_info["phonopy_yaml"],
phono3py_yaml_filename=unitcell_filename,
cutoff_pair_distance=settings.cutoff_pair_distance,
calculator=interface_mode,
log_level=log_level,
)
_load_dataset_and_phonon_dataset(
ph3py,
ph3py_yaml=cell_info.phono3py_yaml,
phono3py_yaml_filename=unitcell_filename,
cutoff_pair_distance=settings.cutoff_pair_distance,
calculator=interface_mode,
log_level=log_level,
)
###################
# polynomial MLPs #
###################
if load_phono3py_yaml and settings.use_pypolymlp:
if settings.use_pypolymlp:
assert ph3py.mlp_dataset is None
if ph3py.dataset is not None:
if ph3py.dataset is not None: # If None, load mlp from polymlp.yaml.
ph3py.mlp_dataset = ph3py.dataset
ph3py.dataset = None
prepare_dataset = (
settings.create_displacements or settings.random_displacements is not None
)
@ -1196,6 +1241,7 @@ def main(**argparse_control):
mlp_params=settings.mlp_params,
displacement_distance=settings.displacement_distance,
number_of_snapshots=settings.random_displacements,
number_estimation_factor=settings.rd_number_estimation_factor,
random_seed=settings.random_seed,
fc_calculator=settings.fc_calculator,
fc_calculator_options=settings.fc_calculator_options,
@ -1221,37 +1267,20 @@ def main(**argparse_control):
"Generate displacements (--rd or -d) for proceeding to phonon "
"calculations."
)
finalize_phono3py(
_finalize_phono3py(
ph3py, confs_dict, log_level, filename=output_yaml_filename
)
###################
# Force constants #
###################
if load_phono3py_yaml:
assert ph3py.fc2 is None
assert ph3py.fc3 is None
_store_force_constants(ph3py, settings, log_level)
else:
try:
create_phono3py_force_constants(
ph3py,
settings,
ph3py_yaml=cell_info["phonopy_yaml"],
phono3py_yaml_filename=unitcell_filename,
calculator=interface_mode,
input_filename=input_filename,
output_filename=output_filename,
log_level=log_level,
)
except ForceCalculatorRequiredError:
_show_fc_calculator_not_found(log_level)
###########################
# Produce force constants #
###########################
_produce_force_constants(ph3py, settings, log_level, load_phono3py_yaml)
############################################
# Phonon Gruneisen parameter and then exit #
############################################
if settings.is_gruneisen:
run_gruneisen_then_exit(ph3py, settings, output_filename, log_level)
_run_gruneisen_then_exit(ph3py, settings, output_filename, log_level)
#################
# Show settings #
@ -1263,7 +1292,7 @@ def main(**argparse_control):
# Joint DOS and then exit #
###########################
if run_mode == "jdos":
run_jdos_then_exit(
_run_jdos_then_exit(
ph3py, settings, updated_settings, output_filename, log_level
)
@ -1290,13 +1319,13 @@ def main(**argparse_control):
# Phonon-isotope lifetime and then exit #
#########################################
if run_mode == "isotope":
run_isotope_then_exit(ph3py, settings, updated_settings, log_level)
_run_isotope_then_exit(ph3py, settings, updated_settings, log_level)
########################################
# Initialize phonon-phonon interaction #
########################################
if run_mode is not None:
init_phph_interaction(
_init_phph_interaction(
ph3py,
settings,
updated_settings,
@ -1310,7 +1339,7 @@ def main(**argparse_control):
#######################################################
if run_mode == "imag_self_energy":
ph3py.run_imag_self_energy(
settings_to_grid_points(settings, ph3py.grid),
_settings_to_grid_points(settings, ph3py.grid),
updated_settings["temperature_points"],
frequency_step=updated_settings["frequency_step"],
num_frequency_points=updated_settings["num_frequency_points"],
@ -1326,7 +1355,7 @@ def main(**argparse_control):
#####################################################
elif run_mode == "real_self_energy":
ph3py.run_real_self_energy(
settings_to_grid_points(settings, ph3py.grid),
_settings_to_grid_points(settings, ph3py.grid),
updated_settings["temperature_points"],
frequency_step=updated_settings["frequency_step"],
num_frequency_points=updated_settings["num_frequency_points"],
@ -1340,7 +1369,7 @@ def main(**argparse_control):
#######################################################
elif run_mode == "spectral_function":
ph3py.run_spectral_function(
settings_to_grid_points(settings, ph3py.grid),
_settings_to_grid_points(settings, ph3py.grid),
updated_settings["temperature_points"],
frequency_step=updated_settings["frequency_step"],
num_frequency_points=updated_settings["num_frequency_points"],
@ -1354,7 +1383,7 @@ def main(**argparse_control):
# Run lattice thermal conductivity #
####################################
elif run_mode == "conductivity-RTA" or run_mode == "conductivity-LBTE":
grid_points = settings_to_grid_points(settings, ph3py.grid)
grid_points = _settings_to_grid_points(settings, ph3py.grid)
ph3py.run_thermal_conductivity(
is_LBTE=settings.is_lbte,
temperatures=updated_settings["temperatures"],
@ -1394,4 +1423,4 @@ def main(**argparse_control):
+ "-" * 11
)
finalize_phono3py(ph3py, confs_dict, log_level, filename=output_yaml_filename)
_finalize_phono3py(ph3py, confs_dict, log_level, filename=output_yaml_filename)

File diff suppressed because it is too large Load Diff

View File

@ -452,7 +452,7 @@ def write_fc2_to_hdf5(
)
def read_fc2_from_hdf5(filename="fc2.hdf5", p2s_map=None):
def read_fc2_from_hdf5(filename: str | os.PathLike = "fc2.hdf5", p2s_map=None):
"""Read fc2 from fc2.hdf5."""
return read_force_constants_from_hdf5(
filename=filename, p2s_map=p2s_map, calculator="vasp"

View File

@ -36,7 +36,7 @@
from __future__ import annotations
from typing import Optional, Union
from typing import Literal
import numpy as np
from phonopy.interface.fc_calculator import FCSolver, fc_calculator_names
@ -108,7 +108,7 @@ class FDFC3Solver:
class FC3Solver(FCSolver):
"""Force constants solver for fc3."""
def _set_traditional_solver(self, solver_class: Optional[type] = FDFC3Solver):
def _set_traditional_solver(self, solver_class: type | None = FDFC3Solver):
return super()._set_traditional_solver(solver_class=solver_class)
def _set_symfc_solver(self):
@ -120,9 +120,9 @@ class FC3Solver(FCSolver):
def extract_fc2_fc3_calculators(
fc_calculator: Optional[Union[str, dict]],
fc_calculator: Literal["traditional", "symfc", "alm"] | str | None,
order: int,
) -> Optional[Union[str, dict]]:
) -> Literal["traditional", "symfc", "alm"] | str | None:
"""Extract fc_calculator and fc_calculator_options for fc2 and fc3.
fc_calculator : str
@ -132,43 +132,39 @@ def extract_fc2_fc3_calculators(
2 and 3 indicate fc2 and fc3, respectively.
"""
if isinstance(fc_calculator, dict) or fc_calculator is None:
if fc_calculator is None:
return fc_calculator
elif isinstance(fc_calculator, str):
if "|" in fc_calculator:
_fc_calculator = fc_calculator.split("|")[order - 2]
if _fc_calculator == "":
return None
return _fc_calculator
else:
if fc_calculator.strip() == "":
return None
return fc_calculator
else:
_fc_calculator = fc_calculator
return _fc_calculator
else:
raise RuntimeError("fc_calculator should be str, dict, or None.")
raise RuntimeError("fc_calculator should be str or None.")
def update_cutoff_fc_calculator_options(
fc_calc_opts: Optional[Union[str, dict]],
cutoff_pair_distance: Optional[float],
) -> Optional[Union[str, dict]]:
fc_calc_opts: str | None,
cutoff_pair_distance: float | None,
) -> str | None:
"""Update fc_calculator_options with cutoff distances.
Parameters
----------
fc_calc_opts : str or dict
fc_calc_opts : str or None
FC calculator options.
cutoff_pair_distance : float, optional
Cutoff distance for pair interaction.
"""
if cutoff_pair_distance is not None:
if not isinstance(fc_calc_opts, (str, dict)) and fc_calc_opts is not None:
raise RuntimeError("fc_calculator_options should be str, dict, or None.")
if isinstance(fc_calc_opts, dict) and "cutoff" not in fc_calc_opts:
fc_calc_opts["cutoff"] = float(cutoff_pair_distance)
elif isinstance(fc_calc_opts, str) and "cutoff" not in fc_calc_opts:
if isinstance(fc_calc_opts, str) and "cutoff" not in fc_calc_opts:
fc_calc_opts = f"{fc_calc_opts}, cutoff = {cutoff_pair_distance}"
elif fc_calc_opts is None:
fc_calc_opts = f"cutoff = {cutoff_pair_distance}"
@ -177,11 +173,11 @@ def update_cutoff_fc_calculator_options(
def get_fc_calculator_params(
fc_calculator: Optional[str],
fc_calculator_options: Optional[str],
cutoff_pair_distance: Optional[float],
fc_calculator: str | None,
fc_calculator_options: str | None,
cutoff_pair_distance: float | None,
log_level: int = 0,
) -> tuple[Optional[str], Optional[str]]:
) -> tuple[str | None, str | None]:
"""Compile fc_calculator and fc_calculator_options from input settings."""
_fc_calculator = None
fc_calculator_list = []
@ -211,16 +207,16 @@ def get_fc_calculator_params(
def determine_cutoff_pair_distance(
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[str] = None,
cutoff_pair_distance: Optional[float] = None,
symfc_memory_size: Optional[float] = None,
random_displacements: Optional[Union[int, str]] = None,
supercell: Optional[PhonopyAtoms] = None,
primitive: Optional[Primitive] = None,
symmetry: Optional[Symmetry] = None,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
cutoff_pair_distance: float | None = None,
symfc_memory_size: float | None = None,
random_displacements: int | str | None = None,
supercell: PhonopyAtoms | None = None,
primitive: Primitive | None = None,
symmetry: Symmetry | None = None,
log_level: int = 0,
) -> float:
) -> float | None:
"""Determine cutoff pair distance for displacements."""
_cutoff_pair_distance, _symfc_memory_size = _get_cutoff_pair_distance(
fc_calculator,
@ -248,7 +244,7 @@ def determine_cutoff_pair_distance(
def _set_cutoff_in_fc_calculator_options(
fc_calculator_options: Optional[str],
fc_calculator_options: str | None,
cutoff_str: str,
log_level: int,
):
@ -275,11 +271,11 @@ def _set_cutoff_in_fc_calculator_options(
def _get_cutoff_pair_distance(
fc_calculator: Optional[str],
fc_calculator_options: Optional[str],
cutoff_pair_distance: Optional[float],
symfc_memory_size: Optional[float] = None,
) -> Optional[float]:
fc_calculator: str | None,
fc_calculator_options: str | None,
cutoff_pair_distance: float | None,
symfc_memory_size: float | None,
) -> tuple[float | None, float | None]:
"""Return cutoff_pair_distance from settings."""
_, _fc_calculator_options = get_fc_calculator_params(
fc_calculator,

View File

@ -37,15 +37,15 @@
from __future__ import annotations
import dataclasses
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, cast
import numpy as np
from numpy.typing import ArrayLike, NDArray
from phonopy.interface.phonopy_yaml import (
PhonopyYaml,
PhonopyYamlDumperBase,
PhonopyYamlLoaderBase,
load_yaml,
phonopy_yaml_property_factory,
)
if TYPE_CHECKING:
@ -60,26 +60,26 @@ from phonopy.structure.symmetry import Symmetry
class Phono3pyYamlData:
"""PhonopyYaml data structure."""
configuration: Optional[dict] = None
calculator: Optional[str] = None
physical_units: Optional[dict] = None
unitcell: Optional[PhonopyAtoms] = None
primitive: Optional[Primitive] = None
supercell: Optional[Supercell] = None
dataset: Optional[dict] = None
supercell_matrix: Optional[np.ndarray] = None
primitive_matrix: Optional[np.ndarray] = None
nac_params: Optional[dict] = None
force_constants: Optional[np.ndarray] = None
symmetry: Optional[Symmetry] = None # symmetry of supercell
frequency_unit_conversion_factor: Optional[float] = None
version: Optional[str] = None
configuration: dict | None = None
calculator: str | None = None
physical_units: dict | None = None
unitcell: PhonopyAtoms | None = None
primitive: Primitive | PhonopyAtoms | None = None
supercell: Supercell | PhonopyAtoms | None = None
dataset: dict | None = None
supercell_matrix: NDArray | None = None
primitive_matrix: NDArray | None = None
nac_params: dict | None = None
force_constants: NDArray | None = None
symmetry: Symmetry | None = None # symmetry of supercell
frequency_unit_conversion_factor: float | None = None
version: str | None = None
command_name: str = "phono3py"
phonon_supercell_matrix: Optional[np.ndarray] = None
phonon_dataset: Optional[dict] = None
phonon_supercell: Optional[Supercell] = None
phonon_primitive: Optional[Primitive] = None
phonon_supercell_matrix: NDArray | None = None
phonon_dataset: dict | None = None
phonon_supercell: Supercell | PhonopyAtoms | None = None
phonon_primitive: Primitive | PhonopyAtoms | None = None
class Phono3pyYamlLoader(PhonopyYamlLoaderBase):
@ -156,10 +156,12 @@ class Phono3pyYamlLoader(PhonopyYamlLoaderBase):
and "phonon_displacements" not in self._yaml
and "displacements" in self._yaml
): # old type1
self._data.phonon_dataset = self._get_dataset(self._data.phonon_supercell)
self._data.phonon_dataset = self._get_dataset(
cast(PhonopyAtoms, self._data.phonon_supercell)
)
else:
self._data.phonon_dataset = self._get_dataset(
self._data.phonon_supercell, key_prefix="phonon_"
cast(PhonopyAtoms, self._data.phonon_supercell), key_prefix="phonon_"
)
def _parse_fc3_dataset(self):
@ -181,7 +183,7 @@ class Phono3pyYamlLoader(PhonopyYamlLoaderBase):
elif isinstance(disp, list): # type2
if "displacement" in disp[0]:
dataset = self._parse_force_sets_type2()
if "displacement_pair_info" in self._yaml:
if "displacement_pair_info" in self._yaml and dataset is not None:
info_yaml = self._yaml["displacement_pair_info"]
if "cutoff_pair_distance" in info_yaml:
dataset["cutoff_distance"] = info_yaml["cutoff_pair_distance"]
@ -296,7 +298,7 @@ class Phono3pyYamlDumper(PhonopyYamlDumperBase):
"dielectric_constant": True,
}
def __init__(self, data: Phono3pyYamlData, dumper_settings=None):
def __init__(self, data: Phono3pyYamlData, dumper_settings: dict | None = None):
"""Init method."""
self._data = data
self._init_dumper_settings(dumper_settings)
@ -402,30 +404,12 @@ class Phono3pyYaml(PhonopyYaml):
default_filenames = ("phono3py_disp.yaml", "phono3py.yaml")
command_name = "phono3py"
configuration = phonopy_yaml_property_factory("configuration")
calculator = phonopy_yaml_property_factory("calculator")
physical_units = phonopy_yaml_property_factory("physical_units")
unitcell = phonopy_yaml_property_factory("unitcell")
primitive = phonopy_yaml_property_factory("primitive")
supercell = phonopy_yaml_property_factory("supercell")
dataset = phonopy_yaml_property_factory("dataset")
supercell_matrix = phonopy_yaml_property_factory("supercell_matrix")
primitive_matrix = phonopy_yaml_property_factory("primitive_matrix")
nac_params = phonopy_yaml_property_factory("nac_params")
force_constants = phonopy_yaml_property_factory("force_constants")
symmetry = phonopy_yaml_property_factory("symmetry")
frequency_unit_conversion_factor = phonopy_yaml_property_factory(
"frequency_unit_conversion_factor"
)
version = phonopy_yaml_property_factory("version")
phonon_supercell_matrix = phonopy_yaml_property_factory("phonon_supercell_matrix")
phonon_dataset = phonopy_yaml_property_factory("phonon_dataset")
phonon_supercell = phonopy_yaml_property_factory("phonon_supercell")
phonon_primitive = phonopy_yaml_property_factory("phonon_primitive")
def __init__(
self, configuration=None, calculator=None, physical_units=None, settings=None
self,
configuration: dict | None = None,
calculator: str | None = None,
physical_units: dict | None = None,
settings: dict | None = None,
):
"""Init method."""
self._data = Phono3pyYamlData(
@ -435,6 +419,193 @@ class Phono3pyYaml(PhonopyYaml):
)
self._dumper_settings = settings
@property
def configuration(self) -> dict | None:
"""Return configuration of phonopy calculation."""
return self._data.configuration
@configuration.setter
def configuration(self, value: dict):
"""Set configuration of phonopy calculation."""
self._data.configuration = value
@property
def calculator(self) -> str | None:
"""Return calculator of phonopy calculation."""
return self._data.calculator
@calculator.setter
def calculator(self, value: str):
"""Set calculator of phonopy calculation."""
self._data.calculator = value
@property
def physical_units(self) -> dict | None:
"""Return physical units of phonopy calculation."""
return self._data.physical_units
@physical_units.setter
def physical_units(self, value: dict):
"""Set physical units of phonopy calculation."""
self._data.physical_units = value
@property
def unitcell(self) -> PhonopyAtoms | None:
"""Return unit cell of phonopy calculation."""
return self._data.unitcell
@unitcell.setter
def unitcell(self, value: PhonopyAtoms):
"""Set unit cell of phonopy calculation."""
self._data.unitcell = value
@property
def primitive(self) -> PhonopyAtoms | None:
"""Return primitive cell of phonopy calculation."""
return self._data.primitive
@primitive.setter
def primitive(self, value: PhonopyAtoms):
"""Set primitive cell of phonopy calculation."""
self._data.primitive = value
@property
def supercell(self) -> PhonopyAtoms | None:
"""Return supercell of phonopy calculation."""
return self._data.supercell
@supercell.setter
def supercell(self, value: PhonopyAtoms):
"""Set supercell of phonopy calculation."""
self._data.supercell = value
@property
def dataset(self) -> dict | None:
"""Return dataset of phonopy calculation."""
return self._data.dataset
@dataset.setter
def dataset(self, value: dict):
"""Set dataset of phonopy calculation."""
self._data.dataset = value
@property
def supercell_matrix(self) -> NDArray | None:
"""Return supercell matrix of phonopy calculation."""
return self._data.supercell_matrix
@supercell_matrix.setter
def supercell_matrix(self, value: ArrayLike):
"""Set supercell matrix of phonopy calculation."""
self._data.supercell_matrix = np.array(value, dtype="intc", order="C")
@property
def primitive_matrix(self) -> NDArray | None:
"""Return primitive matrix of phonopy calculation."""
return self._data.primitive_matrix
@primitive_matrix.setter
def primitive_matrix(self, value: ArrayLike):
"""Set primitive matrix of phonopy calculation."""
self._data.primitive_matrix = np.array(value, dtype="double", order="C")
@property
def nac_params(self) -> dict | None:
"""Return non-analytical term correction parameters."""
return self._data.nac_params
@nac_params.setter
def nac_params(self, value: dict):
"""Set non-analytical term correction parameters."""
if value is not None:
if "born" in value:
value["born"] = np.array(value["born"], dtype="double", order="C")
if "dielectric" in value:
value["dielectric"] = np.array(
value["dielectric"], dtype="double", order="C"
)
self._data.nac_params = value
@property
def force_constants(self) -> NDArray | None:
"""Return force constants of phonopy calculation."""
return self._data.force_constants
@force_constants.setter
def force_constants(self, value: ArrayLike):
"""Set force constants of phonopy calculation."""
self._data.force_constants = np.array(value, dtype="double", order="C")
@property
def symmetry(self) -> Symmetry | None:
"""Return symmetry of phonopy calculation."""
return self._data.symmetry
@symmetry.setter
def symmetry(self, value: Symmetry):
"""Set symmetry of phonopy calculation."""
self._data.symmetry = value
@property
def frequency_unit_conversion_factor(self) -> float | None:
"""Return frequency unit conversion factor."""
return self._data.frequency_unit_conversion_factor
@frequency_unit_conversion_factor.setter
def frequency_unit_conversion_factor(self, value: float):
"""Set frequency unit conversion factor."""
self._data.frequency_unit_conversion_factor = value
@property
def version(self) -> str | None:
"""Return version of phonopy calculation."""
return self._data.version
@version.setter
def version(self, value: str):
"""Set version of phonopy calculation."""
self._data.version = value
@property
def phonon_primitive(self) -> PhonopyAtoms | None:
"""Return phonon primitive cell of phonopy calculation."""
return self._data.phonon_primitive
@phonon_primitive.setter
def phonon_primitive(self, value: PhonopyAtoms):
"""Set phonon primitive cell of phonopy calculation."""
self._data.phonon_primitive = value
@property
def phonon_supercell(self) -> PhonopyAtoms | None:
"""Return phonon supercell of phonopy calculation."""
return self._data.phonon_supercell
@phonon_supercell.setter
def phonon_supercell(self, value: PhonopyAtoms):
"""Set phonon supercell of phonopy calculation."""
self._data.phonon_supercell = value
@property
def phonon_dataset(self) -> dict | None:
"""Return phonon dataset of phonopy calculation."""
return self._data.phonon_dataset
@phonon_dataset.setter
def phonon_dataset(self, value: dict):
"""Set phonon dataset of phonopy calculation."""
self._data.phonon_dataset = value
@property
def phonon_supercell_matrix(self) -> NDArray | None:
"""Return phonon supercell matrix of phonopy calculation."""
return self._data.phonon_supercell_matrix
@phonon_supercell_matrix.setter
def phonon_supercell_matrix(self, value: ArrayLike):
"""Set supercell matrix of phonopy calculation."""
self._data.phonon_supercell_matrix = np.array(value, dtype="intc", order="C")
def __str__(self):
"""Return string text of yaml output."""
ph3yml_dumper = Phono3pyYamlDumper(

View File

@ -37,9 +37,5 @@ from phono3py.cui.phono3py_script import main
def run():
"""Run phono3py script."""
argparse_control = {
"fc_symmetry": False,
"is_nac": False,
"load_phono3py_yaml": False,
}
argparse_control = {"load_phono3py_yaml": False}
main(**argparse_control)

View File

@ -37,5 +37,5 @@ from phono3py.cui.phono3py_script import main
def run():
"""Run phono3py-load script."""
argparse_control = {"fc_symmetry": True, "is_nac": True, "load_phono3py_yaml": True}
argparse_control = {"load_phono3py_yaml": True}
main(**argparse_control)

View File

@ -34,4 +34,4 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
__version__ = "3.16.0"
__version__ = "3.17.0"

View File

@ -16,7 +16,7 @@ dependencies = [
"matplotlib",
"h5py",
"spglib",
"phonopy>=2.39,<2.40",
"phonopy>=2.41,<2.42",
]
license = "BSD-3-Clause"
license-files = ["LICENSE"]

View File

@ -4,9 +4,9 @@ from __future__ import annotations
import os
import pathlib
import tempfile
from collections.abc import Sequence
from dataclasses import dataclass, fields
from typing import Optional, Union
import h5py
import numpy as np
@ -16,32 +16,32 @@ import phono3py
from phono3py.cui.phono3py_script import main
cwd = pathlib.Path(__file__).parent
cwd_called = pathlib.Path.cwd()
@dataclass
class MockArgs:
"""Mock args of ArgumentParser."""
cell_filename: Optional[str] = None
conf_filename: Optional[os.PathLike] = None
fc_calculator: Optional[str] = None
fc_calculator_options: Optional[str] = None
cell_filename: str | None = None
conf_filename: os.PathLike | None = None
fc_calculator: str | None = None
fc_calculator_options: str | None = None
fc_symmetry: bool = True
filename: Optional[Sequence[os.PathLike]] = None
filename: Sequence[os.PathLike] | None = None
force_sets_mode: bool = False
force_sets_to_forces_fc2_mode: bool = False
input_filename = None
input_output_filename = None
log_level: Optional[int] = None
is_bterta: Optional[bool] = None
mesh_numbers: Optional[Sequence] = None
mlp_params: Optional[str] = None
log_level: int | None = None
is_bterta: bool | None = None
mesh_numbers: Sequence | None = None
mlp_params: str | None = None
rd_number_estimation_factor: float | None = None
output_filename = None
output_yaml_filename: Optional[os.PathLike] = None
random_displacements: Optional[Union[int, str]] = None
output_yaml_filename: os.PathLike | None = None
random_displacements: int | str | None = None
show_num_triplets: bool = False
temperatures: Optional[Sequence] = None
temperatures: Sequence | None = None
use_pypolymlp: bool = False
write_grid_points: bool = False
@ -56,36 +56,44 @@ class MockArgs:
def test_phono3py_load():
"""Test phono3py-load script."""
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
argparse_control = _get_phono3py_load_args(
cwd_called / "phono3py.yaml",
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["5", "5", "5"],
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
try:
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m555.hdf5",
):
file_path = cwd_called / created_filename
if file_path.exists():
file_path.unlink()
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["5", "5", "5"],
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m555.hdf5",
):
file_path = pathlib.Path(created_filename)
if file_path.exists():
file_path.unlink()
finally:
os.chdir(original_cwd)
@pytest.mark.parametrize(
@ -108,59 +116,76 @@ def test_phono3py_load_with_typeII_dataset(
"""
pytest.importorskip("symfc")
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params-Si111-rd.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
fc_calculator=fc_calculator,
fc_calculator_options=fc_calculator_options,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
if created_filename == "fc3.hdf5":
with h5py.File(file_path, "r") as f:
if fc_calculator_options is None:
assert "fc3_nonzero_indices" not in f
else:
assert "fc3_nonzero_indices" in f
assert "fc3_cutoff" in f
assert f["fc3_cutoff"][()] == pytest.approx(4.0)
file_path.unlink()
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params-Si111-rd.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
fc_calculator=fc_calculator,
fc_calculator_options=fc_calculator_options,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(created_filename)
if file_path.exists():
if created_filename == "fc3.hdf5":
with h5py.File(file_path, "r") as f:
if fc_calculator_options is None:
assert "fc3_nonzero_indices" not in f
else:
assert "fc3_nonzero_indices" in f
assert "fc3_cutoff" in f
assert f["fc3_cutoff"][()] == pytest.approx(4.0)
file_path.unlink()
finally:
os.chdir(original_cwd)
@pytest.mark.parametrize("load_phono3py_yaml", [True, False])
def test_phono3py_with_QE_calculator(load_phono3py_yaml):
"""Test phono3py-load script with QE calculator."""
argparse_control = _get_phono3py_load_args(
cwd / "phono3py_params-qe-Si222.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["11", "11", "11"],
)
with pytest.raises(SystemExit):
main(**argparse_control)
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
with h5py.File(cwd_called / "kappa-m111111.hdf5", "r") as f:
np.testing.assert_almost_equal(f["kappa"][0, 0], 118.93, decimal=1)
try:
argparse_control = _get_phono3py_load_args(
cwd / "phono3py_params-qe-Si222.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["11", "11", "11"],
)
with pytest.raises(SystemExit):
main(**argparse_control)
# Clean files created by phono3py/phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m111111.hdf5",
):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
file_path.unlink()
with h5py.File("kappa-m111111.hdf5", "r") as f:
np.testing.assert_almost_equal(f["kappa"][0, 0], 118.93, decimal=1)
# Clean files created by phono3py/phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m111111.hdf5",
):
file_path = pathlib.Path(created_filename)
if file_path.exists():
file_path.unlink()
finally:
os.chdir(original_cwd)
def test_phono3py_load_with_pypolymlp_si():
@ -173,56 +198,64 @@ def test_phono3py_load_with_pypolymlp_si():
pytest.importorskip("pypolymlp", minversion="0.9.2")
pytest.importorskip("symfc")
# Create fc2.hdf5
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
fc_calculator="symfc",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
pathlib.Path(cwd_called / "fc3.hdf5").unlink()
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
# Create MLP (polymlp.yaml)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in ("phono3py.yaml", "polymlp.yaml"):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
try:
# Create fc2.hdf5
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
fc_calculator="symfc",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
pathlib.Path("fc3.hdf5").unlink()
# Create phono3py_mlp_eval_dataset.yaml
argparse_control = _get_phono3py_load_args(
cwd_called / "phono3py.yaml",
fc_calculator="symfc",
random_displacements="auto",
use_pypolymlp=True,
)
# Create MLP (polymlp.yaml)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in ("phono3py.yaml", "polymlp.yaml"):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Create phono3py_mlp_eval_dataset.yaml
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
random_displacements="auto",
use_pypolymlp=True,
)
ph3 = phono3py.load(cwd_called / "phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 4
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
file_path.unlink()
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 8
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def test_phono3py_load_with_pypolymlp_nacl():
@ -235,109 +268,152 @@ def test_phono3py_load_with_pypolymlp_nacl():
pytest.importorskip("pypolymlp", minversion="0.9.2")
pytest.importorskip("symfc")
# Stage1 (preparation)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_MgO-222rd-444rd.yaml.xz",
mlp_params="cutoff=4.0,gtinv_maxl=4 4,max_p=1,gtinv_order=2",
fc_calculator="symfc",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
ph3 = phono3py.load(cwd_called / "phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 16
try:
# Stage1 (preparation)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_MgO-222rd-444rd.yaml.xz",
mlp_params="cutoff=4.0,gtinv_maxl=4 4,max_p=1,gtinv_order=2",
fc_calculator="symfc",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 32
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
file_path.unlink()
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
# Stage2 (cutoff test)
argparse_control = _get_phono3py_load_args(
cwd_called / "phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|cutoff=4.0",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
ph3 = phono3py.load(cwd_called / "phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 4
# Stage2 (cutoff test)
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|cutoff=4.0",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 8
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
file_path.unlink()
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
# Stage3 (memsize test)
argparse_control = _get_phono3py_load_args(
cwd_called / "phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|memsize=0.05",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
ph3 = phono3py.load(cwd_called / "phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 8
# Stage3 (memsize test)
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|memsize=0.05",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
assert file_path.exists()
file_path.unlink()
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 16
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
# Stage4 (number_estimation_factor)
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|cutoff=4.0",
random_displacements="auto",
rd_number_estimation_factor=2.0,
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 4
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def _get_phono3py_load_args(
phono3py_yaml_filepath: Union[str, pathlib.Path],
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[str] = None,
phono3py_yaml_filepath: str | pathlib.Path,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
load_phono3py_yaml: bool = True,
is_bterta: bool = False,
mesh_numbers: Optional[Sequence] = None,
mlp_params: Optional[str] = None,
random_displacements: Optional[Union[int, str]] = None,
temperatures: Optional[Sequence] = None,
mesh_numbers: Sequence | None = None,
mlp_params: str | None = None,
rd_number_estimation_factor: float | None = None,
random_displacements: int | str | None = None,
temperatures: Sequence | None = None,
use_pypolymlp: bool = False,
):
# Mock of ArgumentParser.args.
@ -350,6 +426,7 @@ def _get_phono3py_load_args(
log_level=1,
mesh_numbers=mesh_numbers,
mlp_params=mlp_params,
rd_number_estimation_factor=rd_number_estimation_factor,
random_displacements=random_displacements,
temperatures=temperatures,
use_pypolymlp=use_pypolymlp,
@ -364,6 +441,7 @@ def _get_phono3py_load_args(
is_bterta=is_bterta,
mesh_numbers=mesh_numbers,
mlp_params=mlp_params,
rd_number_estimation_factor=rd_number_estimation_factor,
random_displacements=random_displacements,
temperatures=temperatures,
use_pypolymlp=use_pypolymlp,

View File

@ -1,6 +1,8 @@
"""Tests of Phono3py API."""
import os
import pathlib
import tempfile
from collections.abc import Sequence
from typing import Optional
@ -11,7 +13,6 @@ from phono3py import Phono3py
from phono3py.file_IO import _get_filename_suffix
cwd = pathlib.Path(__file__).parent
cwd_called = pathlib.Path.cwd()
def test_kappa_filename():
@ -60,16 +61,25 @@ def test_kappa_hdf5_with_boundary_mpf(si_pbesol: Phono3py):
]
boundary_mfp = 10000.0
kappa_filename = _set_kappa(
si_pbesol, [4, 4, 4], write_kappa=True, boundary_mfp=boundary_mfp
)
file_path = pathlib.Path(cwd_called / kappa_filename)
with h5py.File(file_path, "r") as f:
np.testing.assert_almost_equal(f["boundary_mfp"][()], boundary_mfp)
assert set(list(f)) == set(key_ref)
if file_path.exists():
file_path.unlink()
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
kappa_filename = _set_kappa(
si_pbesol, [4, 4, 4], write_kappa=True, boundary_mfp=boundary_mfp
)
file_path = pathlib.Path(kappa_filename)
with h5py.File(file_path, "r") as f:
np.testing.assert_almost_equal(f["boundary_mfp"][()], boundary_mfp)
assert set(list(f)) == set(key_ref)
if file_path.exists():
file_path.unlink()
finally:
os.chdir(original_cwd)
def _set_kappa(

View File

@ -97,6 +97,7 @@ def test_random_disps_agno2(
number_of_snapshots=number_of_snapshots,
distance=distance,
is_plusminus=is_plusminus,
number_estimation_factor=2,
)
ph3.generate_fc2_displacements(