From 78df49b5cbf91042b1eb08ed388ca0454b80003c Mon Sep 17 00:00:00 2001 From: Atsushi Togo Date: Wed, 31 Jul 2024 21:45:49 +0900 Subject: [PATCH] Refactoring around API --- src/phelel/api_phelel.py | 151 +++++++----------- src/phelel/cui/phelel_argparse.py | 4 +- src/phelel/cui/phelel_script.py | 38 ++--- src/phelel/cui/settings.py | 5 +- src/phelel/interface/vasp/derivatives.py | 16 +- .../velph/cli/supercell/differentiate.py | 6 +- test/base/test_Dij_qji.py | 8 +- test/conftest.py | 21 ++- test/cui/test_phelel_cui.py | 6 + 9 files changed, 111 insertions(+), 144 deletions(-) diff --git a/src/phelel/api_phelel.py b/src/phelel/api_phelel.py index d7a14f8..8e44835 100644 --- a/src/phelel/api_phelel.py +++ b/src/phelel/api_phelel.py @@ -16,19 +16,20 @@ from phelel.base.Dij_qij import DDijQij from phelel.base.local_potential import DLocalPotential -from phelel.file_IO import write_dDijdu_hdf5, write_dVdu_hdf5, write_phelel_params_hdf5 +from phelel.file_IO import write_phelel_params_hdf5 from phelel.version import __version__ @dataclass -class PhelelInput: +class PhelelDataset: """Data structure of input data to run derivatives.""" - local_potentials: list - Dijs: list - qijs: list + local_potentials: list[np.ndarray] + Dijs: list[np.ndarray] + qijs: list[np.ndarray] lm_channels: list[dict] dataset: Optional[dict] = None + phonon_dataset: Optional[dict] = None forces: Optional[np.ndarray] = None @@ -79,6 +80,8 @@ def __init__( symprec: float = 1e-5, is_symmetry: bool = True, calculator: Optional[str] = None, + nufft: Optional[str] = None, + finufft_eps: Optional[float] = None, log_level: int = 0, ): """Init method. @@ -115,18 +118,21 @@ def __init__( Use crystal symmetry or not. Default is True. calculator : A dummy parameter. + nufft : str or None, optional + 'finufft' only. Default is None, which corresponds to 'finufft'. + finufft_eps : float or None, optional + Accuracy of finufft interpolation. Default is None, which + corresponds to 1e-6. log_level : int, optional Log level. 0 is most quiet. Default is 0. """ self._unitcell = unitcell - if fft_mesh is None: - self._fft_mesh = None - else: - self.fft_mesh = fft_mesh self._symprec = symprec self._is_symmetry = is_symmetry self._calculator = calculator + self._nufft = nufft + self._finufft_eps = finufft_eps self._log_level = log_level ph = self._get_phonopy(supercell_matrix, primitive_matrix) @@ -156,7 +162,17 @@ def __init__( self._dVdu = None self._dDijdu = None - self._raw_data = None + if fft_mesh is None: + self._fft_mesh = None + else: + self.fft_mesh = fft_mesh + + self._dDijdu = DDijQij( + self._supercell, + symmetry=self._symmetry, + atom_indices=self._atom_indices_in_derivatives, + verbose=self._log_level > 0, + ) @property def version(self) -> str: @@ -306,12 +322,22 @@ def phonon(self) -> Phonopy: @property def fft_mesh(self) -> np.ndarray: - """Return FFT mesh numbers.""" + """Setter and getter of FFT mesh numbers.""" return self._fft_mesh @fft_mesh.setter - def fft_mesh(self, fft_mesh): + def fft_mesh(self, fft_mesh: Union[Sequence, np.ndarray]): self._fft_mesh = np.array(fft_mesh, dtype="int_") + self._dVdu = DLocalPotential( + self._fft_mesh, + self._p2s_matrix, + self._supercell, + symmetry=self._symmetry, + atom_indices=self._atom_indices_in_derivatives, + nufft=self._nufft, + finufft_eps=self._finufft_eps, + verbose=self._log_level > 0, + ) @property def dVdu(self) -> Optional[DLocalPotential]: @@ -323,7 +349,7 @@ def dVdu(self, dVdu: DLocalPotential): self._dVdu = dVdu @property - def dDijdu(self) -> Optional[DDijQij]: + def dDijdu(self) -> DDijQij: """Return DDijQij class instance.""" return self._dDijdu @@ -368,7 +394,7 @@ def generate_displacements( ) self._dataset = ph.dataset - def run_derivatives(self, phe_input: PhelelInput, nufft=None, finufft_eps=None): + def run_derivatives(self, phe_input: PhelelDataset): """Run displacement derivatives calculations from temporary raw data. Note @@ -383,20 +409,27 @@ def run_derivatives(self, phe_input: PhelelInput, nufft=None, finufft_eps=None): ) raise RuntimeError(msg) - self.prepare_phonon(dataset=phe_input.dataset, forces=phe_input.forces) - self.run_dVdu( - phe_input.local_potentials, - dataset=phe_input.dataset, - nufft=nufft, - finufft_eps=finufft_eps, - ) - self.run_dDijdu( - phe_input.Dijs, - phe_input.qijs, + if phe_input.dataset is not None: + self._dataset = phe_input.dataset + loc_pots = phe_input.local_potentials + Dijs = phe_input.Dijs + qijs = phe_input.qijs + + if phe_input.phonon_dataset is not None: + self._prepare_phonon( + dataset=phe_input.phonon_dataset, forces=phe_input.forces + ) + else: + self._prepare_phonon(dataset=self._dataset, forces=phe_input.forces) + self._dVdu.run(loc_pots[0], loc_pots[1:], self._dataset["first_atoms"]) + self._dDijdu.run( + Dijs[0], + Dijs[1:], + qijs[0], + qijs[1:], + self._dataset["first_atoms"], phe_input.lm_channels, - dataset=phe_input.dataset, ) - self._raw_data = None def save_hdf5( self, filename: Union[str, bytes, os.PathLike, io.IOBase] = "phelel_params.hdf5" @@ -421,71 +454,7 @@ def save_hdf5( filename=filename, ) - def run_dVdu( - self, - loc_pots, - dataset=None, - nufft=None, - finufft_eps=None, - write_hdf5=False, - ): - """Calculate dV/du. - - Parameters - ---------- - nufft : str or None, optional - 'finufft' only. Default is None, which corresponds to 'finufft'. - finufft_eps : float or None, optional - Accuracy of finufft interpolation. Default is None, which - corresponds to 1e-6. - - """ - dVdu = DLocalPotential( - self._fft_mesh, - self._p2s_matrix, - self._supercell, - symmetry=self._symmetry, - atom_indices=self._atom_indices_in_derivatives, - nufft=nufft, - finufft_eps=finufft_eps, - verbose=True, - ) - if dataset is not None: - self._dataset = dataset - displacements = self._dataset["first_atoms"] - dVdu.run(loc_pots[0], loc_pots[1:], displacements) - - if write_hdf5: - write_dVdu_hdf5( - dVdu, - self._supercell_matrix, - self._primitive_matrix, - self._primitive, - self._unitcell, - self._supercell, - filename="dVdu.hdf5", - ) - self._dVdu = dVdu - - def run_dDijdu(self, Dijs, qijs, lm_channels, dataset=None, write_hdf5=False): - """Calculate dDij/du.""" - dDijdu = DDijQij( - self._supercell, - symmetry=self._symmetry, - atom_indices=self._atom_indices_in_derivatives, - verbose=True, - ) - if dataset is not None: - self._dataset = dataset - displacements = self._dataset["first_atoms"] - dDijdu.run(Dijs[0], Dijs[1:], qijs[0], qijs[1:], displacements, lm_channels) - - if write_hdf5: - write_dDijdu_hdf5(dDijdu) - - self._dDijdu = dDijdu - - def prepare_phonon( + def _prepare_phonon( self, dataset: Optional[dict] = None, forces: Optional[ diff --git a/src/phelel/cui/phelel_argparse.py b/src/phelel/cui/phelel_argparse.py index af15f1c..4f0f3ed 100644 --- a/src/phelel/cui/phelel_argparse.py +++ b/src/phelel/cui/phelel_argparse.py @@ -56,7 +56,6 @@ def get_parser(): "--dim-phonon", nargs="+", dest="phonon_supercell_dimension", - metavar="INT", default=None, help=( "Supercell dimensions for phonon with three integers or " @@ -65,9 +64,8 @@ def get_parser(): ) parser.add_argument( "--fft-mesh", - nargs=3, + nargs="+", dest="fft_mesh_numbers", - metavar="INT", default=None, help="FFT mesh numbers used in primitive cell", ) diff --git a/src/phelel/cui/phelel_script.py b/src/phelel/cui/phelel_script.py index a9b6f9b..06616b1 100644 --- a/src/phelel/cui/phelel_script.py +++ b/src/phelel/cui/phelel_script.py @@ -134,6 +134,21 @@ def main(**argparse_control): cell_info["phonon_supercell_matrix"] = ph_smat phonon_supercell_matrix = cell_info["phonon_supercell_matrix"] + if settings.create_displacements: + phelel = create_phelel_supercells( + cell_info, + settings, + symprec, + log_level=log_level, + ) + finalize_phelel( + phelel, + confs=phelel_conf.confs, + log_level=log_level, + displacements_mode=True, + filename="phelel_disp.yaml", + ) + fft_mesh = settings.fft_mesh_numbers phelel = Phelel( unitcell, @@ -143,6 +158,7 @@ def main(**argparse_control): fft_mesh=fft_mesh, symprec=symprec, is_symmetry=settings.is_symmetry, + finufft_eps=settings.finufft_eps, ) if log_level > 0: @@ -179,20 +195,6 @@ def main(**argparse_control): print_cell(phelel.phonon.supercell) print("-" * 76) - if settings.create_displacements: - phelel = create_phelel_supercells( - cell_info, - settings, - symprec, - log_level=log_level, - ) - finalize_phelel( - phelel, - confs=phelel_conf.confs, - log_level=log_level, - displacements_mode=True, - filename="phelel_disp.yaml", - ) ################################## # Create dV/du, dDij/du, dqij/du # ################################## @@ -229,13 +231,13 @@ def main(**argparse_control): create_derivatives( phelel, settings.create_derivatives, - finufft_eps=settings.finufft_eps, subtract_rfs=settings.subtract_rfs, log_level=log_level, ) - phelel.save_hdf5(filename="phelel_params.hdf5") - if log_level > 0: - print('"phelel_params.hdf5" has been created.') + if phelel.fft_mesh is not None: + phelel.save_hdf5(filename="phelel_params.hdf5") + if log_level > 0: + print('"phelel_params.hdf5" has been created.') print_end() sys.exit(0) diff --git a/src/phelel/cui/settings.py b/src/phelel/cui/settings.py index 6914fed..ea393c4 100644 --- a/src/phelel/cui/settings.py +++ b/src/phelel/cui/settings.py @@ -77,10 +77,7 @@ def _read_options(self): self._confs["create_derivatives"] = " ".join(dir_names) if "fft_mesh_numbers" in self._args: if self._args.fft_mesh_numbers: - if len(self._args.fft_mesh_numbers) == 1: - self._confs["fft_mesh"] = self._args.fft_mesh_numbers[0] - elif len(self._args.fft_mesh_numbers) == 3: - self._confs["fft_mesh"] = " ".join(self._args.fft_mesh_numbers) + self._confs["fft_mesh"] = " ".join(self._args.fft_mesh_numbers) if "finufft_eps" in self._args: if self._args.finufft_eps is not None: self._confs["finufft_eps"] = self._args.finufft_eps diff --git a/src/phelel/interface/vasp/derivatives.py b/src/phelel/interface/vasp/derivatives.py index 6b2afac..01eb01a 100644 --- a/src/phelel/interface/vasp/derivatives.py +++ b/src/phelel/interface/vasp/derivatives.py @@ -13,7 +13,7 @@ from phonopy.interface.vasp import parse_set_of_forces from phelel import Phelel -from phelel.api_phelel import PhelelInput +from phelel.api_phelel import PhelelDataset from phelel.interface.vasp.file_IO import ( read_inwap_vaspouth5, read_inwap_yaml, @@ -30,7 +30,7 @@ def read_files( phonon_dir_names: Optional[Sequence[str]] = None, subtract_rfs: bool = False, log_level: int = 0, -) -> PhelelInput: +) -> PhelelDataset: """Load files needed to create derivatives.""" inwap_path = pathlib.Path(dir_names[0]) / "inwap.yaml" if inwap_path.exists(): @@ -75,7 +75,7 @@ def read_files( phelel.phonon.forces = forces if nac_params: phelel.phonon.nac_params = nac_params - return PhelelInput( + return PhelelDataset( local_potentials=loc_pots, Dijs=Dijs, qijs=qijs, @@ -88,8 +88,6 @@ def read_files( def create_derivatives( phelel: Phelel, dir_names: Sequence, - nufft: Optional[str] = None, - finufft_eps: Optional[float] = None, subtract_rfs=False, log_level=0, ): @@ -128,7 +126,7 @@ def create_derivatives( log_level=log_level, ) if phelel.fft_mesh is not None: - phelel.run_derivatives(phe_input, nufft=nufft, finufft_eps=finufft_eps) + phelel.run_derivatives(phe_input) # phelel.Rij = read_Rij(dir_names[0], inwap_per) @@ -202,7 +200,7 @@ def _get_datasets(phelel: Phelel) -> tuple: def _read_local_potentials( dir_names: Union[str, bytes, os.PathLike], inwap_per: dict, log_level: int = 0 -) -> np.ndarray: +) -> list[np.ndarray]: loc_pots = [] for dir_name in dir_names: # Note glob returns a generator. @@ -222,7 +220,9 @@ def _read_local_potentials( return loc_pots -def _read_PAW_strength_and_overlap(dir_names, inwap_per, log_level=0): +def _read_PAW_strength_and_overlap( + dir_names, inwap_per, log_level=0 +) -> tuple[list[np.ndarray], list[np.ndarray]]: Dijs = [] qijs = [] for dir_name in dir_names: diff --git a/src/phelel/velph/cli/supercell/differentiate.py b/src/phelel/velph/cli/supercell/differentiate.py index 331272e..83f8da1 100644 --- a/src/phelel/velph/cli/supercell/differentiate.py +++ b/src/phelel/velph/cli/supercell/differentiate.py @@ -2,7 +2,7 @@ import os import pathlib -from typing import Optional, Union +from typing import Union import click @@ -15,8 +15,6 @@ def run_derivatives( phe: Phelel, hdf5_filename: Union[str, bytes, os.PathLike] = "supercell/phelel_params.hdf5", subtract_residual_forces: bool = True, - nufft: Optional[str] = None, - finufft_eps: Optional[float] = None, dir_name: Union[str, bytes, os.PathLike] = "supercell", ) -> None: """Calculate derivatives and write phelel_params.hdf5.""" @@ -61,8 +59,6 @@ def run_derivatives( create_derivatives( phe, dir_names, - nufft=nufft, - finufft_eps=finufft_eps, subtract_rfs=subtract_residual_forces, log_level=0, ) diff --git a/test/base/test_Dij_qji.py b/test/base/test_Dij_qji.py index dc37897..077ec48 100644 --- a/test/base/test_Dij_qji.py +++ b/test/base/test_Dij_qji.py @@ -2,14 +2,14 @@ import pathlib -from phelel.api_phelel import Phelel, PhelelInput +from phelel.api_phelel import Phelel, PhelelDataset from phelel.base.Dij_qij import DDijQij, DDijQijFit, DeltaDijQij cwd = pathlib.Path(__file__).parent def test_DeltaDijQij( - phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelInput + phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelDataset ): """Test DeltaDijQij.""" phe_in = phelel_input_CdAs2_111 @@ -26,7 +26,7 @@ def test_DeltaDijQij( def test_DDijQijFit( - phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelInput + phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelDataset ): """Test DDijQijFit.""" phe = phelel_empty_CdAs2_111 @@ -51,7 +51,7 @@ def test_DDijQijFit( ddijqij.run() -def test_DDijQij(phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelInput): +def test_DDijQij(phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelDataset): """Test DDijQij.""" phe = phelel_empty_CdAs2_111 phe_in = phelel_input_CdAs2_111 diff --git a/test/conftest.py b/test/conftest.py index 241e3ef..123bf59 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -7,7 +7,7 @@ import phelel import pytest from phelel import Phelel -from phelel.api_phelel import PhelelInput +from phelel.api_phelel import PhelelDataset from phelel.interface.vasp.file_IO import ( read_inwap_yaml, read_local_potential, @@ -57,7 +57,7 @@ def phelel_input_filenames_C111() -> list: @pytest.fixture(scope="session") -def phelel_input_C111(phelel_input_filenames_C111) -> PhelelInput: +def phelel_input_C111(phelel_input_filenames_C111) -> PhelelDataset: """Return diamond phelel input.""" inwap_per, locpot_filenames, Dij_filenames, qij_filenames = ( phelel_input_filenames_C111 @@ -67,7 +67,7 @@ def phelel_input_C111(phelel_input_filenames_C111) -> PhelelInput: @pytest.fixture(scope="session") -def phelel_input_NaCl111() -> PhelelInput: +def phelel_input_NaCl111() -> PhelelDataset: """Return NaCl phelel input.""" inwap_filename = cwd / "inwap_NaCl111.yaml" inwap_per = read_inwap_yaml(inwap_filename) @@ -91,7 +91,7 @@ def phelel_input_NaCl111() -> PhelelInput: @pytest.fixture(scope="session") -def phelel_input_CdAs2_111() -> PhelelInput: +def phelel_input_CdAs2_111() -> PhelelDataset: """Return CdAs2 phelel input.""" inwap_filename = cwd / "inwap_CdAs2_111.yaml" inwap_per = read_inwap_yaml(inwap_filename) @@ -111,14 +111,14 @@ def phelel_input_CdAs2_111() -> PhelelInput: @pytest.fixture(scope="session") -def phelel_C111(phelel_empty_C111: Phelel, phelel_input_C111: PhelelInput) -> Phelel: +def phelel_C111(phelel_empty_C111: Phelel, phelel_input_C111: PhelelDataset) -> Phelel: """Run diamond test.""" return _get_phelel(phelel_empty_C111, phelel_input_C111) @pytest.fixture(scope="session") def phelel_NaCl111( - phelel_empty_NaCl111: Phelel, phelel_input_NaCl111: PhelelInput + phelel_empty_NaCl111: Phelel, phelel_input_NaCl111: PhelelDataset ) -> Phelel: """Run NaCl test.""" return _get_phelel(phelel_empty_NaCl111, phelel_input_NaCl111) @@ -126,17 +126,16 @@ def phelel_NaCl111( @pytest.fixture(scope="session") def phelel_CdAs2_111( - phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelInput + phelel_empty_CdAs2_111: Phelel, phelel_input_CdAs2_111: PhelelDataset ) -> Phelel: """Run CdAs2 test.""" return _get_phelel(phelel_empty_CdAs2_111, phelel_input_CdAs2_111) -def _get_phelel(phe: Phelel, phe_input: PhelelInput): +def _get_phelel(phe: Phelel, phe_input: PhelelDataset): phei = phe_input phe.fft_mesh = [14, 14, 14] - phe.run_dVdu(phei.local_potentials) - phe.run_dDijdu(phei.Dijs, phei.qijs, phei.lm_channels) + phe.run_derivatives(phei) return phe @@ -153,7 +152,7 @@ def _get_phelel_input(inwap_per, locpot_filenames, Dij_filenames, qij_filenames) for filename in qij_filenames: qijs.append(read_PAW_Dij_qij(inwap_per, filename)) - return PhelelInput( + return PhelelDataset( local_potentials=loc_pots, Dijs=Dijs, qijs=qijs, diff --git a/test/cui/test_phelel_cui.py b/test/cui/test_phelel_cui.py index 5e7c7f4..5dede75 100644 --- a/test/cui/test_phelel_cui.py +++ b/test/cui/test_phelel_cui.py @@ -26,6 +26,7 @@ class MockArgs: create_derivatives: Optional[Sequence[str]] = None is_displacement: bool = False is_plusminus_displacements: bool = False + fft_mesh_numbers: Optional[str] = None def __iter__(self): """Make self iterable to support in.""" @@ -73,11 +74,14 @@ def test_phelel_script_create_derivatives(use_poscar: bool): cell_filename = str(dirname / "phelel_disp_C111.yaml") supercell_dimension = None + fft_mesh_numbers = "1 1 1" + dispdirs = [str(dirname / "C111_disp-000"), str(dirname / "C111_disp-001")] argparse_control = _get_phelel_load_args( cell_filename=cell_filename, create_derivatives=dispdirs, supercell_dimenstion=supercell_dimension, + fft_mesh_numbers=fft_mesh_numbers, ) if use_poscar: @@ -149,6 +153,7 @@ def _get_phelel_load_args( create_derivatives: Optional[Sequence[str]] = None, is_displacement: bool = False, is_plusminus_displacements: bool = False, + fft_mesh_numbers: Optional[str] = None, ): # Mock of ArgumentParser.args. mockargs = MockArgs( @@ -159,6 +164,7 @@ def _get_phelel_load_args( create_derivatives=create_derivatives, is_displacement=is_displacement, is_plusminus_displacements=is_plusminus_displacements, + fft_mesh_numbers=fft_mesh_numbers, ) # See phonopy-load script.