diff --git a/.github/workflows/phono3py-pytest-conda-mkl-phphmtblas.yml b/.github/workflows/phono3py-pytest-conda-mkl-phphmtblas.yml index eff7c597..36bc64eb 100644 --- a/.github/workflows/phono3py-pytest-conda-mkl-phphmtblas.yml +++ b/.github/workflows/phono3py-pytest-conda-mkl-phphmtblas.yml @@ -15,9 +15,9 @@ jobs: python-version: ["3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Use conda-incubator/setup-miniconda for precise control of conda infrastructure - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: miniforge-version: latest - name: Install dependent packages @@ -26,6 +26,13 @@ jobs: conda install --yes python=${{ matrix.python-version }} #conda install --yes matplotlib-base pyyaml "libblas=*=*openblas" openblas h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler conda install --yes matplotlib-base pyyaml "libblas=*=*mkl" mkl-include h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler + - name: Install symfc develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/symfc/symfc.git + cd symfc + pip install -e . -vvv + cd .. - name: Install phonopy develop branch run: | conda activate test diff --git a/.github/workflows/phono3py-pytest-conda-mkl-v2.yml b/.github/workflows/phono3py-pytest-conda-mkl-v2.yml index 1b80bb99..4e755b19 100644 --- a/.github/workflows/phono3py-pytest-conda-mkl-v2.yml +++ b/.github/workflows/phono3py-pytest-conda-mkl-v2.yml @@ -15,9 +15,9 @@ jobs: python-version: ["3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Use conda-incubator/setup-miniconda for precise control of conda infrastructure - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: miniforge-version: latest - name: Install dependent packages @@ -26,6 +26,13 @@ jobs: conda install --yes python=${{ matrix.python-version }} #conda install --yes matplotlib-base pyyaml "libblas=*=*openblas" openblas h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler conda install --yes matplotlib-base pyyaml "libblas=*=*mkl" mkl-include h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler + - name: Install symfc develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/symfc/symfc.git + cd symfc + pip install -e . -vvv + cd .. - name: Install phonopy develop branch run: | conda activate test diff --git a/.github/workflows/phono3py-pytest-conda-mkl.yml b/.github/workflows/phono3py-pytest-conda-mkl.yml index e436e7f5..486af093 100644 --- a/.github/workflows/phono3py-pytest-conda-mkl.yml +++ b/.github/workflows/phono3py-pytest-conda-mkl.yml @@ -15,9 +15,9 @@ jobs: python-version: ["3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Use conda-incubator/setup-miniconda for precise control of conda infrastructure - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: miniforge-version: latest - name: Install dependent packages @@ -26,6 +26,13 @@ jobs: conda install --yes python=${{ matrix.python-version }} #conda install --yes matplotlib-base pyyaml "libblas=*=*openblas" openblas h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler conda install --yes matplotlib-base pyyaml "libblas=*=*mkl" mkl-include h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler + - name: Install symfc develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/symfc/symfc.git + cd symfc + pip install -e . -vvv + cd .. - name: Install phonopy develop branch run: | conda activate test diff --git a/.github/workflows/phono3py-pytest-conda-numpy2.yml b/.github/workflows/phono3py-pytest-conda-numpy2.yml new file mode 100644 index 00000000..cc1798ff --- /dev/null +++ b/.github/workflows/phono3py-pytest-conda-numpy2.yml @@ -0,0 +1,59 @@ +name: Pytest with openblas and numpy 2.0 + +on: + pull_request: + branches: [ develop ] + +jobs: + build-linux: + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} + strategy: + matrix: + python-version: ["3.12"] + + steps: + - uses: actions/checkout@v4 + # Use conda-incubator/setup-miniconda for precise control of conda infrastructure + - uses: conda-incubator/setup-miniconda@v3 + with: + miniforge-version: latest + - name: Install dependent packages + run: | + conda activate test + conda install --yes python=${{ matrix.python-version }} + conda install --yes matplotlib-base pyyaml "libblas=*=*openblas" openblas h5py "numpy=2" scipy pytest codecov pytest-cov cmake c-compiler + - name: Install spglib develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/spglib/spglib.git + cd spglib + pip install -e . -vvv + cd .. + - name: Install symfc develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/symfc/symfc.git + cd symfc + pip install -e . -vvv + cd .. + - name: Install phonopy develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/phonopy/phonopy.git + cd phonopy + PHONOPY_USE_OPENMP=true pip install -e . -vvv + cd .. + - name: Install phono3py + run: | + conda activate test + pip install -e . -vvv + - name: Run pytest + run: | + pytest -v test + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + verbose: true diff --git a/.github/workflows/phono3py-pytest-conda-phphmtblas.yml b/.github/workflows/phono3py-pytest-conda-phphmtblas.yml index c427aaeb..5d6aac02 100644 --- a/.github/workflows/phono3py-pytest-conda-phphmtblas.yml +++ b/.github/workflows/phono3py-pytest-conda-phphmtblas.yml @@ -15,9 +15,9 @@ jobs: python-version: ["3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Use conda-incubator/setup-miniconda for precise control of conda infrastructure - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: miniforge-version: latest - name: Install dependent packages @@ -26,6 +26,13 @@ jobs: conda install --yes python=${{ matrix.python-version }} conda install --yes matplotlib-base pyyaml "libblas=*=*openblas" openblas h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler #conda install --yes matplotlib-base pyyaml "libblas=*=*mkl" mkl-include h5py scipy pytest codecov pytest-cov spglib alm cmake c-compiler + - name: Install symfc develop branch + run: | + conda activate test + git clone --depth 1 https://github.com/symfc/symfc.git + cd symfc + pip install -e . -vvv + cd .. - name: Install phonopy develop branch run: | conda activate test diff --git a/.github/workflows/phono3py-pytest-conda.yml b/.github/workflows/phono3py-pytest-conda.yml index 15b5b4df..05eb347b 100644 --- a/.github/workflows/phono3py-pytest-conda.yml +++ b/.github/workflows/phono3py-pytest-conda.yml @@ -15,9 +15,9 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Use conda-incubator/setup-miniconda for precise control of conda infrastructure - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: miniforge-version: latest - name: Install dependent packages diff --git a/.github/workflows/publish-gh-pages.yml b/.github/workflows/publish-gh-pages.yml index 1a844230..4e397cb2 100644 --- a/.github/workflows/publish-gh-pages.yml +++ b/.github/workflows/publish-gh-pages.yml @@ -15,8 +15,8 @@ jobs: python-version: ["3.11"] steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 + - uses: actions/checkout@v4 + - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true channels: conda-forge diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index d57d1c18..64a45448 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -17,9 +17,9 @@ jobs: python-version: ["3.10", ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # Use conda-incubator/setup-miniconda for precise control of conda infrastructure - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true channels: conda-forge diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a3a4942..5da3a44d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,31 +10,12 @@ repos: exclude: ^conda/ - id: check-added-large-files -- repo: https://github.com/pycqa/flake8 - rev: 7.0.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.10 hooks: - - id: flake8 - args: - - "--max-line-length=88" - - "--ignore=E203,W503" - -- repo: https://github.com/psf/black - rev: 24.4.2 - hooks: - - id: black - args: - - --line-length=88 - -- repo: https://github.com/pycqa/pydocstyle - rev: 6.3.0 - hooks: - - id: pydocstyle - -- repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - name: isort (python) + - id: ruff + args: [ "--fix", "--show-fixes" ] + - id: ruff-format - repo: https://github.com/Takishima/cmake-pre-commit-hooks rev: v1.9.6 diff --git a/README.md b/README.md index 87be7617..00c5d0d5 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,20 @@ # phono3py -A simulation package of phonon-phonon interaction related properties. Phono3py -user documentation is found at http://phonopy.github.io/phono3py/. +A simulation package of phonon-phonon interaction related properties mainly +written in python. Phono3py user documentation is found at +http://phonopy.github.io/phono3py/. ## Mailing list for questions Usual phono3py questions should be sent to phonopy mailing list (https://sourceforge.net/p/phonopy/mailman/). +## Dependency + +See `requirements.txt`. Optionally `symfc` and `scipy` are required +for using additional features. + ## Development The development of phono3py is managed on the `develop` branch of github @@ -21,26 +27,19 @@ phono3py repository. - Github issues is the place to discuss about phono3py issues. - Github pull request is the place to request merging source code. -- Python 3.7 is the minimum requirement. -- Formatting is written in `pyproject.toml`. -- Not strictly, but VSCode's `settings.json` may be written like +- Formatting rules are found in `pyproject.toml`. +- Not strictly, but VSCode's `settings.json` may be written like below ```json - "python.linting.flake8Enabled": true, - "python.linting.flake8Args": ["--max-line-length=88", "--ignore=E203,W503"], - "python.linting.enabled": true, - "python.linting.pylintEnabled": false, - "python.linting.mypyEnabled": true, - "python.linting.pycodestyleEnabled": false, - "python.linting.pydocstyleEnabled": true, - "python.formatting.provider": "black", - "python.formatting.blackArgs": ["--line-length=88"], - "python.sortImports.args": ["--profile", "black"], + "ruff.lint.args": [ + "--config=${workspaceFolder}/pyproject.toml", + ], "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", "editor.codeActionsOnSave": { - "source.organizeImports": true - }, - } + "source.organizeImports": "explicit" + } + }, ``` - Use of pre-commit (https://pre-commit.com/) is encouraged. @@ -57,7 +56,8 @@ stored in `doc` directory. Please see how to write the documentation at ## How to run tests -You need pytest. At home directory of phono3py after setup, +Tests are written using pytest. To run tests, pytest has to be installed. The +tests can be run by ```bash % pytest diff --git a/doc/changelog.md b/doc/changelog.md index 009929cc..9fba0f68 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,16 @@ # Change Log +## Jun-29-2024: Version 3.2.0 + +- `--rd` and `--rd-fc2` options for generating random directional displacements. +- Experimental implementation for using pypolymlp. + +## Jun-19-2024: Version 3.1.2 + +- Treatment of numpy 2.0. +- Experimental support of symfc. + ## Jun-7-2024: Version 3.1.1 - Minor fix related to typehint for python-3.8. diff --git a/doc/command-options.md b/doc/command-options.md index 1c564020..9851d73e 100644 --- a/doc/command-options.md +++ b/doc/command-options.md @@ -181,7 +181,7 @@ created from `FORCES_FC2` and `phono3py_disp.yaml` instead of `FORCES_FC3` and ### `--dim` (`DIM`) Supercell dimension is specified. See the detail at -http://phonopy.github.io/phonopy/setting-tags.html#dim. When a proper +. When a proper `phono3py_disp.yaml` exists in the current directory, this is unnecessary to be specified. @@ -233,7 +233,7 @@ that created in the usual phono3py run without `--dim-fc2` option. Transformation matrix from a non-primitive cell to the primitive cell. See phonopy `PRIMITIVE_AXES` tag (`--pa` option) at -http://phonopy.github.io/phonopy/setting-tags.html#primitive-axis. When a proper +. When a proper `phono3py_disp.yaml` exists in the current directory, this is unnecessary to be specified. @@ -261,10 +261,17 @@ e.g., `--pa="F"` if the input unit cell has F-centring. (amplitude_option)= +### `--rd` (`RANDOM_DISPLACEMENTS`), `--rd-fc2` (`RANDOM_DISPLACEMENTS_FC2`) and `--random-seed` (`RANDOM_SEED`) + +Random directional displacements are generated for fc3 and fc2 supercells by +`--rd` and `--rd-fc2`, respectively. `--amplitude` and `--random-seed` options +may be used together. These are used in the equivalent way to [`--rd` of +phonopy](https://phonopy.github.io/phonopy/setting-tags.html#random-displacements). + ### `--amplitude` (`DISPLACEMENT_DISTANCE`) Atomic displacement distance is specified. This value may be increased for the -weak interaction systems and descreased when the force calculator is numerically +weak interaction systems and decreased when the force calculator is numerically very accurate. The default value depends on calculator. See @@ -279,7 +286,7 @@ The default value depends on calculator. See When creating force constants from `FORCES_FC3` and/or `FORCES_FC2`, force constants that use smaller data size are created. The shape of the data array is `(num_patom, num_satom)` for fc2 and `(num_patom, num_satom, num_satom)` for -fc3, where `num_patom` and `num_satom` are the numbers of atoms in primtive cell +fc3, where `num_patom` and `num_satom` are the numbers of atoms in primitive cell and supercell. In the full size force constants case, `num_patom` is replaced by `num_satom`. Therefore if the supercell dimension is large, this reduction of data size becomes large. If the input crystal structure has centring @@ -298,7 +305,7 @@ imperfect symmetrization scheme that phono3py employs. ### `--sym-fc` (`FC_SYMMETRY = .TRUE.`) Second- and third-order force constants are symmetrized. The index exchange of -real space force constantsand translational invariance symmetry are applied in a +real space force constants and translational invariance symmetry are applied in a simple way. This symmetrization just removes drift force constants evenly from all elements and then applies averaging index-exchange equivalent elements. Therefore the different symmetries are not simultaneously enforced. For better @@ -336,7 +343,7 @@ or computed with less numerical accuracy. More details are found at This invokes ALM as the force constants calculator for fc2 and fc3. See the detail at -[phonopy documentaton](https://phonopy.github.io/phonopy/setting-tags.html#alm). +[phonopy documentation](https://phonopy.github.io/phonopy/setting-tags.html#alm). This option is useful for fitting random displacement dataset or MD data to force constants. Phono3py doesn't provide command-line interface to generate random displacements. Instead simply @@ -359,7 +366,7 @@ as written ### `--gp` (`GRID_POINTS`) Grid points are specified by their unique indices, e.g., for selecting the -q-points where imaginary parts of self energees are calculated. For thermal +q-points where imaginary parts of self energies are calculated. For thermal conductivity calculation, this can be used to distribute its calculation over q-points (see {ref}`workload_distribution`). @@ -382,7 +389,7 @@ This is used to specify grid points like `--gp` option but in their addresses represented by integer numbers. For example with `--mesh="16 16 16"`, a q-point of (0.5, 0.5, 0.5) is given by `--ga="8 8 8"`. The values have to be integers. If you want to specify the point on a path, -`--ga="0 0 0 1 1 1 2 2 2 3 3 3 ..."`, where each three values are recogninzed as +`--ga="0 0 0 1 1 1 2 2 2 3 3 3 ..."`, where each three values are recognized as a grid point. The grid points given by `--ga` option are translated to grid point indices as given by `--gp` option, and the values given by `--ga` option will not be shown in log files. @@ -394,7 +401,7 @@ will not be shown in log files. Band indices are specified. The calculated values at indices separated by space are averaged, and those separated by comma are separately calculated. The output file name will be, e.g., `gammas-mxxx-gxx(-sx)-bx.dat` where `bxbx...` shows the -band indices where the values are calcualted and summed and averaged over those +band indices where the values are calculated and summed and averaged over those bands. ```bash @@ -489,7 +496,7 @@ $$ \delta(\omega-\omega_{\lambda'}-\omega_{\lambda''}) \right. + (n_{\lambda'}-n_{\lambda''}) \left[\delta(\omega+\omega_{\lambda'}-\omega_{\lambda''}) -- \left. \delta(\omega-\omega_{\lambda'}+\omega_{\lambda''}) ++ \left. \delta(\omega-\omega_{\lambda'}+\omega_{\lambda''}) \right]\right\}. $$ @@ -524,16 +531,17 @@ equivalent to that of `--br`. More detail is documented at ### `--wigner` -Run calculation of lattice thermal conductivity tensor computing the coherences (wave-like) contribution -to the thermal conductivity, obtained solving the Wigner transport equation equation. -This option can be combined with `--lbte` or `--br`; in the former case the populations -conductivity (particle-like, equivalent to the conductivity obtained solving the LBTE) is computed exactly, -in the latter case the populations conductivity is -computed in the relaxation-time approximation (RTA). -The coherences contribution to the conductivity is always computed exactly. -The coherences conductivity is usually non-negligible compared to the particle-like conductivity in materials -with ultralow or glass-like conductivity. -More details can be found at {ref}`wigner_solution`. +Run calculation of lattice thermal conductivity tensor computing the coherences +(wave-like) contribution to the thermal conductivity, obtained solving the +Wigner transport equation equation. This option can be combined with `--lbte` or +`--br`; in the former case the populations conductivity (particle-like, +equivalent to the conductivity obtained solving the LBTE) is computed exactly, +in the latter case the populations conductivity is computed in the +relaxation-time approximation (RTA). The coherences contribution to the +conductivity is always computed exactly. The coherences conductivity is usually +non-negligible compared to the particle-like conductivity in materials with +ultralow or glass-like conductivity. More details can be found at +{ref}`wigner_solution`. ## Scattering @@ -718,7 +726,7 @@ $$ \bigl|\Phi_{-\lambda\lambda_1\lambda_2}\bigl|^2 (n_{\lambda_1}-n_{\lambda_2}) \left[\delta(\omega+\omega_{\lambda_1}-\omega_{\lambda_2}) - - \delta(\omega-\omega_{\lambda_1}+\omega_{\lambda_2}) + + \delta(\omega-\omega_{\lambda_1}+\omega_{\lambda_2}) \right] \end{align*} $$ @@ -732,7 +740,7 @@ $$ \bigl|\Phi_{-\lambda\lambda_1\lambda_2}\bigl|^2 (n_{\lambda_1}+ n_{\lambda_2}+1) \left[ \delta(\omega-\omega_{\lambda_1}-\omega_{\lambda_2}) - - \delta(\omega + \omega_{\lambda_1} + \omega_{\lambda_2}) \right] + + \delta(\omega + \omega_{\lambda_1} + \omega_{\lambda_2}) \right] \end{align*}, $$ @@ -758,7 +766,7 @@ Specific temperatures are specified by `--ts`. Temperatures at equal interval are specified by `--tmax`, `--tmin`, `--tstep`. See phonopy's document for the same tags at -http://phonopy.github.io/phonopy/setting-tags.html#tprop-tmin-tmax-tstep. +. ```bash % phono3py --fc3 --fc2 --dim="2 2 2" -v --mesh="11 11 11" -c POSCAR-unitcell --br --tmin=100 --tmax=1000 --tstep=50 @@ -779,7 +787,7 @@ file. This is used with `--nac` to specify reciprocal-space direction at $\mathbf{q}\rightarrow \mathbf{0}$. See the detail at -http://phonopy.github.io/phonopy/setting-tags.html#q-direction. +. (self_energy_options)= @@ -800,11 +808,11 @@ $$ \bigl|\Phi_{-\lambda\lambda_1\lambda_2}\bigl|^2 & \left\{(n_{\lambda_1}+ n_{\lambda_2}+1) \left[ \delta(\omega-\omega_{\lambda_1}-\omega_{\lambda_2}) - - \delta(\omega+\omega_{\lambda_1}+\omega_{\lambda_2}) \right] \right. + + \delta(\omega+\omega_{\lambda_1}+\omega_{\lambda_2}) \right] \right. \\ & + (n_{\lambda_1}-n_{\lambda_2}) \left[\delta(\omega+\omega_{\lambda_1}-\omega_{\lambda_2}) - - \left. \delta(\omega-\omega_{\lambda_1}+\omega_{\lambda_2}) + + \left. \delta(\omega-\omega_{\lambda_1}+\omega_{\lambda_2}) \right]\right\}, \end{align*} $$ @@ -819,14 +827,14 @@ $$ \left\{ \left[ \frac{(n_{\lambda_1}+ n_{\lambda_2}+1)}{ (\omega-\omega_{\lambda_1}-\omega_{\lambda_2})_\mathrm{p}} - - \frac{(n_{\lambda_1}+ n_{\lambda_2}+1)}{ + + \frac{(n_{\lambda_1}+ n_{\lambda_2}+1)}{ (\omega+\omega_{\lambda_1}+\omega_{\lambda_2})_\mathrm{p}} \right] \right. \\ & + \left[ \frac{(n_{\lambda_1}-n_{\lambda_2})}{(\omega + \omega_{\lambda_1} - \omega_{\lambda_2})_\mathrm{p}} - - \left. \frac{(n_{\lambda_1}-n_{\lambda_2})}{(\omega - + + \left. \frac{(n_{\lambda_1}-n_{\lambda_2})}{(\omega - \omega_{\lambda_1} + \omega_{\lambda_2})_\mathrm{p}} \right]\right\}, \end{align*} @@ -1186,7 +1194,7 @@ different CPU architectures. {ref}`--pa ` and ### `--write-pp` (`WRITE_PP = .TRUE.`) and `--read-pp` (`READ_PP = .TRUE.`) -Phonon-phonon (ph-ph) intraction strengths are written to and read from +Phonon-phonon (ph-ph) interaction strengths are written to and read from `pp-mxxx-gx.hdf5`. This works only in the calculation of lattice thermal conductivity, i.e., usable only with `--br` or `--lbte`. The stored data are different with and without specifying `--full-pp` option. In the former case, @@ -1212,7 +1220,7 @@ Most of phono3py HDF5 output file is compressed by default with the `gzip` compression filter. To avoid compression, `--hdf5-compression=None` has to be set. Other filters (`lzf` or integer values of 0 to 9) may be used, see h5py documentation -(http://docs.h5py.org/en/stable/high/dataset.html#filter-pipeline). +(). (output_filename_option)= @@ -1226,12 +1234,12 @@ Using this option, output file names are slightly modified. For example, with This rule is applied to -- `fc3.hdf5` -- `fc2.hdf5` -- `kappa-xxx.hdf5` -- `phonon-xxx.hdf5` -- `pp-xxx.hdf5` -- `gamma_detail-xxx.hdf5` (write only) ++ `fc3.hdf5` ++ `fc2.hdf5` ++ `kappa-xxx.hdf5` ++ `phonon-xxx.hdf5` ++ `pp-xxx.hdf5` ++ `gamma_detail-xxx.hdf5` (write only) (input_filename_option)= @@ -1245,11 +1253,11 @@ specifying `-i iso --fc3`, a file name `fc3.iso.hdf5` is read instead of This rule is applied to -- `fc3.hdf5` -- `fc2.hdf5` -- `kappa-xxx.hdf5` -- `phonon-xxx.hdf5` -- `pp-xxx.hdf5` ++ `fc3.hdf5` ++ `fc2.hdf5` ++ `kappa-xxx.hdf5` ++ `phonon-xxx.hdf5` ++ `pp-xxx.hdf5` ### `--io` (command option only) diff --git a/doc/conf.py b/doc/conf.py index b9600efd..ef0f8f99 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -58,9 +58,9 @@ # built documents. # # The short X.Y version. -version = "3.1" +version = "3.2" # The full version, including alpha/beta/rc tags. -release = "3.1.1" +release = "3.2.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/phono3py/api_jointdos.py b/phono3py/api_jointdos.py index 6b4841eb..a69146a9 100644 --- a/phono3py/api_jointdos.py +++ b/phono3py/api_jointdos.py @@ -40,12 +40,12 @@ from phonopy.units import VaspToTHz from phono3py.file_IO import write_joint_dos +from phono3py.phonon.grid import BZGrid from phono3py.phonon3.imag_self_energy import ( get_freq_points_batches, get_frequency_points, ) from phono3py.phonon3.joint_dos import JointDos -from phono3py.phonon.grid import BZGrid class Phono3pyJointDos: diff --git a/phono3py/api_phono3py.py b/phono3py/api_phono3py.py index f720965f..42b83762 100644 --- a/phono3py/api_phono3py.py +++ b/phono3py/api_phono3py.py @@ -44,6 +44,7 @@ from phonopy.harmonic.displacement import ( directions_to_displacement_dataset, get_least_displacements, + get_random_displacements_dataset, ) from phonopy.harmonic.dynamical_matrix import DynamicalMatrix from phonopy.harmonic.force_constants import get_fc2 as get_phonopy_fc2 @@ -54,6 +55,13 @@ symmetrize_force_constants, ) from phonopy.interface.fc_calculator import get_fc2 +from phonopy.interface.pypolymlp import ( + PypolymlpData, + PypolymlpParams, + develop_polymlp, + evalulate_polymlp, + parse_mlp_params, +) from phonopy.structure.atoms import PhonopyAtoms from phonopy.structure.cells import ( Primitive, @@ -72,19 +80,20 @@ from phono3py.conductivity.rta import get_thermal_conductivity_RTA from phono3py.interface.fc_calculator import get_fc3 from phono3py.interface.phono3py_yaml import Phono3pyYaml +from phono3py.phonon.grid import BZGrid from phono3py.phonon3.dataset import get_displacements_and_forces_fc3 from phono3py.phonon3.displacement_fc3 import ( direction_to_displacement, get_third_order_displacements, ) -from phono3py.phonon3.fc3 import cutoff_fc3_by_zero -from phono3py.phonon3.fc3 import get_fc3 as get_phono3py_fc3 from phono3py.phonon3.fc3 import ( + cutoff_fc3_by_zero, set_permutation_symmetry_compact_fc3, set_permutation_symmetry_fc3, set_translational_invariance_compact_fc3, set_translational_invariance_fc3, ) +from phono3py.phonon3.fc3 import get_fc3 as get_phono3py_fc3 from phono3py.phonon3.imag_self_energy import ( get_imag_self_energy, write_imag_self_energy, @@ -95,7 +104,6 @@ write_real_self_energy, ) from phono3py.phonon3.spectral_function import run_spectral_function -from phono3py.phonon.grid import BZGrid from phono3py.version import __version__ @@ -288,10 +296,16 @@ def __init__( self._frequency_points = None self._temperatures = None - # Other variables + # Force constants self._fc2 = None self._fc3 = None + # MLP + self._mlp = None + self._mlp_dataset = None + self._phonon_mlp = None + self._phonon_mlp_dataset = None + # Setup interaction self._interaction = None self._band_indices = None @@ -675,6 +689,38 @@ def phonon_dataset(self, dataset): self._phonon_supercells_with_displacements = None + @property + def mlp_dataset(self) -> Optional[dict]: + """Return displacement-force dataset. + + The supercell matrix is equal to that of usual displacement-force + dataset. Only type 2 format is supported. "displacements", + "forces", and "supercell_energies" should be contained. + + """ + return self._mlp_dataset + + @mlp_dataset.setter + def mlp_dataset(self, mlp_dataset: dict): + self._check_mlp_dataset(mlp_dataset) + self._mlp_dataset = mlp_dataset + + @property + def phonon_mlp_dataset(self) -> Optional[dict]: + """Return phonon displacement-force dataset. + + The phonon supercell matrix is equal to that of usual displacement-force + dataset. Only type 2 format is supported. "displacements", "forces", and + "supercell_energies" should be contained. + + """ + return self._phonon_mlp_dataset + + @phonon_mlp_dataset.setter + def phonon_mlp_dataset(self, mlp_dataset: dict): + self._check_mlp_dataset(mlp_dataset) + self._phonon_mlp_dataset = mlp_dataset + @property def band_indices(self) -> list[np.ndarray]: """Setter and getter of band indices. @@ -805,7 +851,7 @@ def displacements(self): for disp1 in dataset["first_atoms"]: num_scells += len(disp1["second_atoms"]) displacements = np.zeros( - (num_scells, self._supercell.get_number_of_atoms(), 3), + (num_scells, len(self._supercell), 3), dtype="double", order="C", ) @@ -909,18 +955,18 @@ def phonon_displacements(self): shape=(supercells, natom, 3), dtype='double', order='C' """ - if self._phonon_supercell_matrix is None: - raise RuntimeError("phonon_supercell_matrix is not set.") - - dataset = self._phonon_dataset - if "first_atoms" in dataset: - num_scells = len(dataset["first_atoms"]) + if self._phonon_dataset is None: + raise RuntimeError("phonon_dataset is not set.") + if "first_atoms" in self._phonon_dataset: + num_scells = len(self._phonon_dataset["first_atoms"]) natom = len(self._phonon_supercell) displacements = np.zeros((num_scells, natom, 3), dtype="double", order="C") - for i, disp1 in enumerate(dataset["first_atoms"]): + for i, disp1 in enumerate(self._phonon_dataset["first_atoms"]): displacements[i, disp1["number"]] = disp1["displacement"] - elif "forces" in dataset or "displacements" in dataset: - displacements = dataset["displacements"] + elif ( + "forces" in self._phonon_dataset or "displacements" in self._phonon_dataset + ): + displacements = self._phonon_dataset["displacements"] else: raise RuntimeError("displacement dataset has wrong format.") @@ -1004,6 +1050,16 @@ def grid(self): """ 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, @@ -1166,65 +1222,105 @@ def generate_displacements( cutoff_pair_distance=None, is_plusminus="auto", is_diagonal=True, + number_of_snapshots: Optional[int] = None, + random_seed: Optional[int] = None, + is_random_distance: bool = False, + min_distance: Optional[float] = None, ): """Generate displacement dataset in supercell for fc3. - This systematically generates single and pair atomic displacements - in supercells to calculate fc3 considering crystal symmetry. - When this method is called, existing cache of supercells with - displacements for fc3 are removed. - - For fc3, two atoms are displaced for each configuration - considering crystal symmetry. The first displacement is chosen - in the perfect supercell, and the second displacement in the - displaced supercell. The first displacements are taken along - the basis vectors of the supercell. This is because the - symmetry is expected to be less broken by the introduced first - displacement, and as the result, the number of second - displacements may become smaller than the case that the first - atom is displaced not along the basis vectors. + Systematic displacements + ------------------------ + + Unless number_of_snapshots is specified, this method systematically + generates single and pair atomic displacements in supercells to + calculate fc3 considering crystal symmetry. + + For fc3, two atoms are displaced for each configuration considering + crystal symmetry. The first displacement is chosen in the perfect + supercell, and the second displacement in the displaced supercell. The + first displacements are taken along the basis vectors of the supercell. + This is because the symmetry is expected to be less broken by the + introduced first displacement, and as the result, the number of second + displacements may become smaller than the case that the first atom is + displaced not along the basis vectors. + + Random displacements + -------------------- + Unless number_of_snapshots is specified, displacements are generated + randomly. There are several options how the random displacements are + generated. Note ---- - When phonon_supercell_matrix is not given, fc2 is also - computed from the same set of the displacements for fc3 and - respective supercell forces. When phonon_supercell_matrix is - set, the displacements in phonon_supercell are generated unless - those already exist. + When phonon_supercell_matrix is not given, fc2 is also computed from the + same set of the displacements for fc3 and respective supercell forces. + When phonon_supercell_matrix is set, the displacements in + phonon_supercell are generated unless those already exist. If a specific + set of displacements for fc2 is expected, generate_fc2_displacements + should be called. Parameters ---------- distance : float, optional Constant displacement Euclidean distance. Default is 0.03. cutoff_pair_distance : float, optional - This is used as a cutoff Euclidean distance to determine if - each pair of displacements is considered to calculate fc3 or not. - Default is None, which means cutoff is not used. + This is used as a cutoff Euclidean distance to determine if each + 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 - 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 equivalent. Default is 'auto'. + 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 + equivalent. Default is 'auto'. is_diagonal : Bool, optional With False, the second displacements are made along the basis vectors of the supercell. With True, direction not along the basis - vectors can be chosen when the number of the displacements - may be reduced. + vectors can be chosen when the number of the displacements may be + reduced. + number_of_snapshots : int or None, optional + Number of snapshots of supercells with random displacements. Random + displacements are generated displacing all atoms in random + directions with a fixed displacement distance specified by + 'distance' parameter, i.e., all atoms in supercell are displaced + with the same displacement distance in direct space. Default is + None. + random_seed : int or None, optional + Random seed for random displacements generation. Default is None. + is_random_distance : bool, optional + Random direction displacements are generated also with random + amplitudes. The maximum value is defined by `distance` and minimum + value is given by `min_distance`. Default is False. + min_distance : float or None, optional + In random direction displacements generation with random distance + (`is_random_distance=True`), the minimum distance is given by this + value. """ - direction_dataset = get_third_order_displacements( - self._supercell, - self._symmetry, - is_plusminus=is_plusminus, - is_diagonal=is_diagonal, - ) - self._dataset = direction_to_displacement( - direction_dataset, - distance, - self._supercell, - cutoff_distance=cutoff_pair_distance, - ) + if number_of_snapshots is not None and number_of_snapshots > 0: + self._dataset = self._generate_random_displacements( + number_of_snapshots, + len(self._supercell), + distance=distance, + is_plusminus=is_plusminus is True, + random_seed=random_seed, + is_random_distance=is_random_distance, + min_distance=min_distance, + ) + else: + direction_dataset = get_third_order_displacements( + self._supercell, + self._symmetry, + is_plusminus=is_plusminus, + is_diagonal=is_diagonal, + ) + self._dataset = direction_to_displacement( + direction_dataset, + distance, + self._supercell, + cutoff_distance=cutoff_pair_distance, + ) self._supercells_with_displacements = None if self._phonon_supercell_matrix is not None and self._phonon_dataset is None: @@ -1233,7 +1329,14 @@ def generate_displacements( ) def generate_fc2_displacements( - self, distance=0.03, is_plusminus="auto", is_diagonal=False + self, + distance: float = 0.03, + is_plusminus: str = "auto", + is_diagonal: bool = False, + number_of_snapshots: Optional[int] = None, + random_seed: Optional[int] = None, + is_random_distance: bool = False, + min_distance: Optional[float] = None, ): """Generate displacement dataset in phonon supercell for fc2. @@ -1263,76 +1366,91 @@ def generate_fc2_displacements( vectors of the supercell. With True, direction not along the basis vectors can be chosen when the number of the displacements may be reduced. Default is False. + number_of_snapshots : int or None, optional + Number of snapshots of supercells with random displacements. Random + displacements are generated displacing all atoms in random + directions with a fixed displacement distance specified by + 'distance' parameter, i.e., all atoms in supercell are displaced + with the same displacement distance in direct space. Default is + None. + random_seed : int or None, optional + Random seed for random displacements generation. Default is None. + is_random_distance : bool, optional + Random direction displacements are generated also with random + amplitudes. The maximum value is defined by `distance` and minimum + value is given by `min_distance`. Default is False. + min_distance : float or None, optional + In random direction displacements generation with random distance + (`is_random_distance=True`), the minimum distance is given by this + value. """ - if self._phonon_supercell_matrix is None: - msg = ( - "phonon_supercell_matrix is not set. " - "This method is used to generate displacements to " - "calculate phonon_fc2." + if number_of_snapshots is not None and number_of_snapshots > 0: + self._phonon_dataset = self._generate_random_displacements( + number_of_snapshots, + len(self._phonon_supercell), + distance=distance, + is_plusminus=is_plusminus is True, + random_seed=random_seed, + is_random_distance=is_random_distance, + min_distance=min_distance, + ) + else: + phonon_displacement_directions = get_least_displacements( + self._phonon_supercell_symmetry, + is_plusminus=is_plusminus, + is_diagonal=is_diagonal, + ) + self._phonon_dataset = directions_to_displacement_dataset( + phonon_displacement_directions, distance, self._phonon_supercell ) - raise RuntimeError(msg) - - phonon_displacement_directions = get_least_displacements( - self._phonon_supercell_symmetry, - is_plusminus=is_plusminus, - is_diagonal=is_diagonal, - ) - self._phonon_dataset = directions_to_displacement_dataset( - phonon_displacement_directions, distance, self._phonon_supercell - ) self._phonon_supercells_with_displacements = None def produce_fc3( self, - symmetrize_fc3r=False, - is_compact_fc=False, - fc_calculator=None, - fc_calculator_options=None, + symmetrize_fc3r: bool = False, + is_compact_fc: bool = False, + fc_calculator: Optional[str] = None, + fc_calculator_options: Optional[Union[str, dict]] = None, ): """Calculate fc3 from displacements and forces. Parameters ---------- - symmetrize_fc3r : bool - Only for type 1 displacement_dataset, translational and - permutation symmetries are applied after creating fc3. This - symmetrization is not very sophisticated and can break space - group symmetry, but often useful. If better symmetrization is - expected, it is recommended to use external force constants - calculator such as ALM. Default is False. - is_compact_fc : bool + symmetrize_fc3r : bool, optional + Only for type 1 displacement_dataset, translational and permutation + symmetries are applied after creating fc3. This symmetrization is + not very sophisticated and can break space group symmetry, but often + useful. If better symmetrization is expected, it is recommended to + use external force constants calculator such as ALM. Default is + False. + is_compact_fc : bool, optional fc3 shape is - False: (supercell, supercell, supecell, 3, 3, 3) - True: (primitive, supercell, supecell, 3, 3, 3) + False: (supercell, supercell, supecell, 3, 3, 3) True: + (primitive, supercell, supecell, 3, 3, 3) where 'supercell' and 'primitive' indicate number of atoms in these cells. Default is False. - fc_calculator : str or None + fc_calculator : str, optional Force constants calculator given by str. - fc_calculator_options : dict + fc_calculator_options : dict or str, optional Options for external force constants calculator. """ - disp_dataset = self._dataset - - fc3_calculator, fc3_calculator_options = self._extract_fc2_fc3_calculators( - fc_calculator, fc_calculator_options, 3 - ) - - if fc3_calculator is not None: - disps, forces = get_displacements_and_forces_fc3(disp_dataset) + if fc_calculator is not None: + disps, forces = get_displacements_and_forces_fc3(self._dataset) fc2, fc3 = get_fc3( self._supercell, self._primitive, disps, forces, - fc_calculator=fc3_calculator, - fc_calculator_options=fc3_calculator_options, + fc_calculator=fc_calculator, + fc_calculator_options=fc_calculator_options, is_compact_fc=is_compact_fc, + symmetry=self._symmetry, log_level=self._log_level, ) else: - if "displacements" in disp_dataset: + if "displacements" in self._dataset: msg = ( "fc_calculator has to be set to produce force " "constans from this dataset." @@ -1341,7 +1459,7 @@ def produce_fc3( fc2, fc3 = get_phono3py_fc3( self._supercell, self._primitive, - disp_dataset, + self._dataset, self._symmetry, is_compact_fc=is_compact_fc, verbose=self._log_level, @@ -1411,20 +1529,18 @@ def produce_fc2( else: p2s_map = None - fc2_calculator, fc2_calculator_options = self._extract_fc2_fc3_calculators( - fc_calculator, fc_calculator_options, 2 - ) - - if fc2_calculator is not None: + if fc_calculator is not None: disps, forces = get_displacements_and_forces(disp_dataset) self._fc2 = get_fc2( self._phonon_supercell, self._phonon_primitive, disps, forces, - fc_calculator=fc2_calculator, - fc_calculator_options=fc2_calculator_options, + fc_calculator=fc_calculator, + fc_calculator_options=fc_calculator_options, atom_list=p2s_map, + symmetry=self._phonon_supercell_symmetry, + symprec=self._symprec, log_level=self._log_level, ) else: @@ -2052,6 +2168,136 @@ def save(self, filename="phono3py_params.yaml", settings=None): with open(filename, "w") as w: w.write(str(ph3py_yaml)) + def develop_mlp(self, params: Optional[Union[PypolymlpParams, dict, str]] = None): + """Develop MLP of pypolymlp. + + Parameters + ---------- + params : PypolymlpParams or dict, optional + Parameters for developing MLP. Default is None. When dict is given, + PypolymlpParams instance is created from the dict. + + """ + if self._mlp_dataset is None: + raise RuntimeError("MLP dataset is not set.") + + if params is not None: + _params = parse_mlp_params(params) + else: + _params = params + + disps = self._mlp_dataset["displacements"] + forces = self._mlp_dataset["forces"] + energies = self._mlp_dataset["supercell_energies"] + n = int(len(disps) * 0.9) + train_data = PypolymlpData( + displacements=disps[:n], forces=forces[:n], supercell_energies=energies[:n] + ) + test_data = PypolymlpData( + displacements=disps[n:], forces=forces[n:], supercell_energies=energies[n:] + ) + self._mlp = develop_polymlp( + self._supercell, + train_data, + test_data, + params=_params, + verbose=self._log_level - 1 > 0, + ) + + def evaluate_mlp(self): + """Evaluate the machine learning potential of pypolymlp. + + This method calculates the supercell energies and forces from the MLP + for the displacements in self._dataset of type 2. The results are stored + in self._dataset. + + The displacements may be generated by the produce_force_constants method + with number_of_snapshots > 0. With MLP, a small distance parameter, such + as 0.001, can be numerically stable for the computation of force + constants. + + """ + if self._mlp is None: + raise RuntimeError("MLP is not developed yet.") + + if self.supercells_with_displacements is None: + raise RuntimeError("Displacements are not set. Run generate_displacements.") + + energies, forces, _ = evalulate_polymlp( + self._mlp, self.supercells_with_displacements + ) + self.supercell_energies = energies + self.forces = forces + + def develop_phonon_mlp( + self, params: Optional[Union[PypolymlpParams, dict, str]] = None + ): + """Develop MLP of pypolymlp for fc2. + + Parameters + ---------- + params : PypolymlpParams or dict, optional + Parameters for developing MLP. Default is None. When dict is given, + PypolymlpParams instance is created from the dict. + + """ + if self._phonon_mlp_dataset is None: + raise RuntimeError("MLP dataset is not set.") + + if params is not None: + _params = parse_mlp_params(params) + else: + _params = params + + disps = self._phonon_mlp_dataset["displacements"] + forces = self._phonon_mlp_dataset["forces"] + energies = self._phonon_mlp_dataset["supercell_energies"] + n = int(len(disps) * 0.9) + train_data = PypolymlpData( + displacements=disps[:n], forces=forces[:n], supercell_energies=energies[:n] + ) + test_data = PypolymlpData( + displacements=disps[n:], forces=forces[n:], supercell_energies=energies[n:] + ) + self._phonon_mlp = develop_polymlp( + self._phonon_supercell, + train_data, + test_data, + params=_params, + verbose=self._log_level - 1 > 0, + ) + + def evaluate_phonon_mlp(self): + """Evaluate the machine learning potential of pypolymlp. + + This method calculates the supercell energies and forces from the MLP + for the displacements in self._dataset of type 2. The results are stored + in self._dataset. + + The displacements may be generated by the produce_force_constants method + with number_of_snapshots > 0. With MLP, a small distance parameter, such + as 0.001, can be numerically stable for the computation of force + constants. + + """ + if self._mlp is None and self._phonon_mlp is None: + raise RuntimeError("MLP is not developed yet.") + + if self.phonon_supercells_with_displacements is None: + raise RuntimeError( + "Displacements are not set. Run generate_fc2_displacements." + ) + + if self._phonon_mlp is None: + mlp = self._mlp + else: + mlp = self._phonon_mlp + energies, forces, _ = evalulate_polymlp( + mlp, self.phonon_supercells_with_displacements + ) + self.phonon_supercell_energies = energies + self.phonon_forces = forces + ################### # private methods # ################### @@ -2132,32 +2378,44 @@ def _build_phonon_primitive_cell(self): raise RuntimeError def _build_phonon_supercells_with_displacements( - self, supercell: PhonopyAtoms, displacement_dataset + self, supercell: PhonopyAtoms, dataset ): supercells = [] + positions = supercell.positions magmoms = supercell.magnetic_moments masses = supercell.masses numbers = supercell.numbers lattice = supercell.cell - for disp1 in displacement_dataset["first_atoms"]: - disp_cart1 = disp1["displacement"] - positions = supercell.positions - positions[disp1["number"]] += disp_cart1 - supercells.append( - PhonopyAtoms( - numbers=numbers, - masses=masses, - magnetic_moments=magmoms, - positions=positions, - cell=lattice, + if "displacements" in dataset: + for disp in dataset["displacements"]: + supercells.append( + PhonopyAtoms( + numbers=numbers, + masses=masses, + magnetic_moments=magmoms, + positions=positions + disp, + cell=lattice, + ) + ) + else: + for disp1 in dataset["first_atoms"]: + disp_cart1 = disp1["displacement"] + positions = supercell.positions + positions[disp1["number"]] += disp_cart1 + supercells.append( + PhonopyAtoms( + numbers=numbers, + masses=masses, + magnetic_moments=magmoms, + positions=positions, + cell=lattice, + ) ) - ) return supercells def _build_supercells_with_displacements(self): - supercells = [] magmoms = self._supercell.magnetic_moments masses = self._supercell.masses numbers = self._supercell.numbers @@ -2167,28 +2425,29 @@ def _build_supercells_with_displacements(self): self._supercell, self._dataset ) - for disp1 in self._dataset["first_atoms"]: - disp_cart1 = disp1["displacement"] - for disp2 in disp1["second_atoms"]: - if "included" in disp2: - included = disp2["included"] - else: - included = True - if included: - positions = self._supercell.positions - positions[disp1["number"]] += disp_cart1 - positions[disp2["number"]] += disp2["displacement"] - supercells.append( - PhonopyAtoms( - numbers=numbers, - masses=masses, - magnetic_moments=magmoms, - positions=positions, - cell=lattice, + if "first_atoms" in self._dataset: + for disp1 in self._dataset["first_atoms"]: + disp_cart1 = disp1["displacement"] + for disp2 in disp1["second_atoms"]: + if "included" in disp2: + included = disp2["included"] + else: + included = True + if included: + positions = self._supercell.positions + positions[disp1["number"]] += disp_cart1 + positions[disp2["number"]] += disp2["displacement"] + supercells.append( + PhonopyAtoms( + numbers=numbers, + masses=masses, + magnetic_moments=magmoms, + positions=positions, + cell=lattice, + ) ) - ) - else: - supercells.append(None) + else: + supercells.append(None) self._supercells_with_displacements = supercells @@ -2254,41 +2513,6 @@ def _init_dynamical_matrix(self): print(" But this frequency is forced to be zero.") print("=" * 61) - def _extract_fc2_fc3_calculators(self, fc_calculator, fc_calculator_options, order): - """Extract fc_calculator and fc_calculator_options for fc2 and fc3. - - fc_calculator : str - FC calculator. "|" separates fc2 and fc3. First and last - parts separated correspond to fc2 and fc3 calculators, respectively. - fc_calculator_options : str - FC calculator options. "|" separates fc2 and fc3. First and last - parts separated correspond to fc2 and fc3 options, respectively. - order : int = 2 or 3 - 2 and 3 indicate fc2 and fc3, respectively. - - """ - if fc_calculator is not None: - if "|" in fc_calculator: - _fc_calculator = fc_calculator.split("|")[order - 2] - if _fc_calculator == "": - _fc_calculator = None - else: - _fc_calculator = fc_calculator - else: - _fc_calculator = None - - if fc_calculator_options is not None: - if "|" in fc_calculator_options: - _fc_calculator_options = fc_calculator_options.split("|")[order - 2] - if _fc_calculator_options == "": - _fc_calculator_options = None - else: - _fc_calculator_options = fc_calculator_options - else: - _fc_calculator_options = None - - return _fc_calculator, _fc_calculator_options - def _get_forces_energies( self, target: Literal["forces", "supercell_energies"] ) -> Optional[np.ndarray]: @@ -2401,3 +2625,47 @@ def _set_phonon_forces_energies( self._phonon_dataset[target] = _values else: raise RuntimeError("Set of FC2 displacements is not available.") + + def _generate_random_displacements( + self, + number_of_snapshots: int, + number_of_atoms: int, + distance: float = 0.03, + is_plusminus: bool = False, + random_seed: Optional[int] = None, + is_random_distance: bool = False, + min_distance: Optional[float] = None, + ): + if random_seed is not None and random_seed >= 0 and random_seed < 2**32: + _random_seed = random_seed + dataset = {"random_seed": _random_seed} + else: + _random_seed = None + dataset = {} + d = get_random_displacements_dataset( + number_of_snapshots, + number_of_atoms, + distance, + random_seed=_random_seed, + is_plusminus=is_plusminus, + is_random_distance=is_random_distance, + min_distance=min_distance, + ) + dataset["displacements"] = d + return dataset + + def _check_mlp_dataset(self, mlp_dataset: dict): + if not isinstance(mlp_dataset, dict): + raise TypeError("mlp_dataset has to be a dictionary.") + if "displacements" not in mlp_dataset: + raise RuntimeError("Displacements have to be given.") + if "forces" not in mlp_dataset: + raise RuntimeError("Forces have to be given.") + if "supercell_energy" in mlp_dataset: + raise RuntimeError("Supercell energies have to be given.") + if len(mlp_dataset["displacements"]) != len(mlp_dataset["forces"]): + raise RuntimeError("Length of displacements and forces are different.") + if len(mlp_dataset["displacements"]) != len(mlp_dataset["supercell_energies"]): + raise RuntimeError( + "Length of displacements and supercell_energies are different." + ) diff --git a/phono3py/conductivity/base.py b/phono3py/conductivity/base.py index 05c14912..24478392 100644 --- a/phono3py/conductivity/base.py +++ b/phono3py/conductivity/base.py @@ -45,10 +45,10 @@ from phonopy.units import EV, Angstrom, Kb, THz, THzToEv from phono3py.other.isotope import Isotope +from phono3py.phonon.grid import get_grid_points_by_rotations, get_ir_grid_points from phono3py.phonon3.collision_matrix import CollisionMatrix from phono3py.phonon3.imag_self_energy import ImagSelfEnergy from phono3py.phonon3.interaction import Interaction -from phono3py.phonon.grid import get_grid_points_by_rotations, get_ir_grid_points unit_to_WmK = ( (THz * Angstrom) ** 2 / (Angstrom**3) * EV / THz / (2 * np.pi) @@ -81,6 +81,7 @@ def get_mode_heat_capacities(self): "Use attribute, Conductivity.mode_heat_capacities " "instead of Conductivity.get_mode_heat_capacities().", DeprecationWarning, + stacklevel=2, ) return self.mode_heat_capacities @@ -129,6 +130,7 @@ def get_kappa(self): warnings.warn( "Use attribute, Conductivity.kappa " "instead of Conductivity.get_kappa().", DeprecationWarning, + stacklevel=2, ) return self.kappa @@ -143,6 +145,7 @@ def get_mode_kappa(self): "Use attribute, Conductivity.mode_kappa " "instead of Conductivity.get_mode_kappa().", DeprecationWarning, + stacklevel=2, ) return self.mode_kappa @@ -165,6 +168,7 @@ def get_group_velocities(self): "Use attribute, Conductivity.group_velocities " "instead of Conductivity.get_group_velocities().", DeprecationWarning, + stacklevel=2, ) return self.group_velocities @@ -179,6 +183,7 @@ def get_gv_by_gv(self): "Use attribute, Conductivity.gv_by_gv " "instead of Conductivity.get_gv_by_gv().", DeprecationWarning, + stacklevel=2, ) return self.gv_by_gv @@ -413,6 +418,7 @@ def get_mesh_numbers(self): "Use attribute, Conductivity.mesh_numbers " "instead of Conductivity.get_mesh_numbers().", DeprecationWarning, + stacklevel=2, ) return self.mesh_numbers @@ -440,6 +446,7 @@ def get_frequencies(self): "Use attribute, Conductivity.frequencies " "instead of Conductivity.get_frequencies().", DeprecationWarning, + stacklevel=2, ) return self.frequencies @@ -458,6 +465,7 @@ def get_qpoints(self): "Use attribute, Conductivity.qpoints " "instead of Conductivity.get_qpoints().", DeprecationWarning, + stacklevel=2, ) return self.qpoints @@ -480,6 +488,7 @@ def get_grid_points(self): "Use attribute, Conductivity.grid_points " "instead of Conductivity.get_grid_points().", DeprecationWarning, + stacklevel=2, ) return self.grid_points @@ -494,6 +503,7 @@ def get_grid_weights(self): "Use attribute, Conductivity.grid_weights " "instead of Conductivity.get_grid_weights().", DeprecationWarning, + stacklevel=2, ) return self.grid_weights @@ -513,6 +523,7 @@ def get_temperatures(self): "Use attribute, Conductivity.temperatures " "instead of Conductivity.get_temperatures().", DeprecationWarning, + stacklevel=2, ) return self.temperatures @@ -522,6 +533,7 @@ def set_temperatures(self, temperatures): "Use attribute, Conductivity.temperatures " "instead of Conductivity.set_temperatures().", DeprecationWarning, + stacklevel=2, ) self.temperatures = temperatures @@ -540,6 +552,7 @@ def get_gamma(self): warnings.warn( "Use attribute, Conductivity.gamma " "instead of Conductivity.get_gamma().", DeprecationWarning, + stacklevel=2, ) return self.gamma @@ -548,6 +561,7 @@ def set_gamma(self, gamma): warnings.warn( "Use attribute, Conductivity.gamma " "instead of Conductivity.set_gamma().", DeprecationWarning, + stacklevel=2, ) self.gamma = gamma @@ -567,6 +581,7 @@ def get_gamma_isotope(self): "Use attribute, Conductivity.gamma_isotope " "instead of Conductivity.get_gamma_isotope().", DeprecationWarning, + stacklevel=2, ) return self.gamma_isotope @@ -576,6 +591,7 @@ def set_gamma_isotope(self, gamma_iso): "Use attribute, Conductivity.gamma_isotope " "instead of Conductivity.set_gamma_isotope().", DeprecationWarning, + stacklevel=2, ) self.gamma_isotope = gamma_iso @@ -590,6 +606,7 @@ def get_sigmas(self): "Use attribute, Conductivity.sigmas " "instead of Conductivity.get_sigmas().", DeprecationWarning, + stacklevel=2, ) return self.sigmas @@ -604,6 +621,7 @@ def get_sigma_cutoff_width(self): "Use attribute, Conductivity.sigma_cutoff_width " "instead of Conductivity.get_sigma_cutoff_width().", DeprecationWarning, + stacklevel=2, ) return self.sigma_cutoff_width @@ -618,6 +636,7 @@ def get_grid_point_count(self): "Use attribute, Conductivity.grid_point_count " "instead of Conductivity.get_grid_point_count().", DeprecationWarning, + stacklevel=2, ) return self.grid_point_count @@ -632,6 +651,7 @@ def get_averaged_pp_interaction(self): "Use attribute, Conductivity.averaged_pp_interaction " "instead of Conductivity.get_averaged_pp_interaction().", DeprecationWarning, + stacklevel=2, ) return self.averaged_pp_interaction diff --git a/phono3py/conductivity/direct_solution.py b/phono3py/conductivity/direct_solution.py index b8e027cb..0b292431 100644 --- a/phono3py/conductivity/direct_solution.py +++ b/phono3py/conductivity/direct_solution.py @@ -56,9 +56,9 @@ get_conversion_factor_WTE, ) from phono3py.file_IO import read_pp_from_hdf5 +from phono3py.phonon.grid import get_grid_points_by_rotations from phono3py.phonon3.collision_matrix import CollisionMatrix from phono3py.phonon3.interaction import Interaction, all_bands_exist -from phono3py.phonon.grid import get_grid_points_by_rotations class ConductivityLBTEBase(ConductivityBase): @@ -163,6 +163,7 @@ def get_collision_matrix(self): "Use attribute, Conductivity_LBTE.collision_matrix " "instead of Conductivity_LBTE.get_collision_matrix().", DeprecationWarning, + stacklevel=2, ) return self.collision_matrix @@ -172,6 +173,7 @@ def set_collision_matrix(self, collision_matrix): "Use attribute, Conductivity_LBTE.collision_matrix " "instead of Conductivity_LBTE.set_collision_matrix().", DeprecationWarning, + stacklevel=2, ) self.collision_matrix = collision_matrix @@ -1271,6 +1273,7 @@ def get_kappa_RTA(self): "Use attribute, Conductivity_LBTE.kappa_RTA " "instead of Conductivity_LBTE.get_kappa_RTA().", DeprecationWarning, + stacklevel=2, ) return self.kappa_RTA @@ -1285,6 +1288,7 @@ def get_mode_kappa_RTA(self): "Use attribute, Conductivity_LBTE.mode_kappa_RTA " "instead of Conductivity_LBTE.get_mode_kappa_RTA().", DeprecationWarning, + stacklevel=2, ) return self.mode_kappa_RTA diff --git a/phono3py/conductivity/rta.py b/phono3py/conductivity/rta.py index 64ac8bbe..44a786e8 100644 --- a/phono3py/conductivity/rta.py +++ b/phono3py/conductivity/rta.py @@ -54,9 +54,9 @@ ) from phono3py.file_IO import read_pp_from_hdf5 from phono3py.other.tetrahedron_method import get_tetrahedra_relative_grid_address +from phono3py.phonon.grid import get_grid_points_by_rotations from phono3py.phonon3.imag_self_energy import ImagSelfEnergy, average_by_degeneracy from phono3py.phonon3.interaction import Interaction, all_bands_exist -from phono3py.phonon.grid import get_grid_points_by_rotations class ConductivityRTABase(ConductivityBase): @@ -921,7 +921,7 @@ def _set_kappa_at_sigmas(self, j, k, i_gp, i_band, g_sum, frequencies): return g = g_sum[i_band] + g_sum[j_band] - for i_pair, (a, b) in enumerate( + for i_pair, _ in enumerate( ([0, 0], [1, 1], [2, 2], [1, 2], [0, 2], [0, 1]) ): old_settings = np.seterr(all="raise") diff --git a/phono3py/conductivity/utils.py b/phono3py/conductivity/utils.py index 62b62801..dcc4c63b 100644 --- a/phono3py/conductivity/utils.py +++ b/phono3py/conductivity/utils.py @@ -34,7 +34,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. - import sys from typing import TYPE_CHECKING, Optional, Union @@ -367,7 +366,7 @@ def write_gamma_detail( all_triplets = get_all_triplets(gp, interaction.bz_grid) if all_bands_exist(interaction): - for j, sigma in enumerate(sigmas): + for sigma in sigmas: write_gamma_detail_to_hdf5( temperatures, mesh, @@ -384,7 +383,7 @@ def write_gamma_detail( verbose=verbose, ) else: - for j, sigma in enumerate(sigmas): + for sigma in sigmas: for k, bi in enumerate(interaction.get_band_indices()): write_gamma_detail_to_hdf5( temperatures, @@ -871,7 +870,7 @@ def kappa_RTA(br: "ConductivityRTA", log_level): ("#%6s " + " %-10s" * 6) % ("T(K)", "xx", "yy", "zz", "yz", "xz", "xy") ) - for j, (t, k) in enumerate(zip(temperatures, kappa[i])): + for t, k in zip(temperatures, kappa[i]): print(("%7.1f " + " %10.3f" * 6) % ((t,) + tuple(k))) print("") @@ -936,13 +935,13 @@ def kappa_Wigner_RTA(br: "ConductivityWignerRTA", log_level): % (" \t T(K)", "xx", "yy", "zz", "yz", "xz", "xy") ) if kappa_P_RTA is not None: - for j, (t, k) in enumerate(zip(temperatures, kappa_P_RTA[i])): + for t, k in zip(temperatures, kappa_P_RTA[i]): print("K_P\t" + ("%7.1f " + " %10.3f" * 6) % ((t,) + tuple(k))) print(" ") - for j, (t, k) in enumerate(zip(temperatures, kappa_C[i])): + for t, k in zip(temperatures, kappa_C[i]): print("K_C\t" + ("%7.1f " + " %10.3f" * 6) % ((t,) + tuple(k))) print(" ") - for j, (t, k) in enumerate(zip(temperatures, kappa_TOT_RTA[i])): + for t, k in zip(temperatures, kappa_TOT_RTA[i]): print("K_T\t" + ("%7.1f " + " %10.3f" * 6) % ((t,) + tuple(k))) print("") diff --git a/phono3py/cui/create_force_constants.py b/phono3py/cui/create_force_constants.py index d3bae975..9f6f778a 100644 --- a/phono3py/cui/create_force_constants.py +++ b/phono3py/cui/create_force_constants.py @@ -40,7 +40,8 @@ import os import pathlib import sys -from typing import Optional +from dataclasses import asdict +from typing import Optional, Union import numpy as np from phonopy.cui.phonopy_script import file_exists, print_error @@ -52,6 +53,7 @@ ) from phonopy.interface.calculator import get_default_physical_units from phonopy.interface.fc_calculator import fc_calculator_names +from phonopy.interface.pypolymlp import PypolymlpParams, parse_mlp_params from phono3py import Phono3py from phono3py.cui.show_log import show_phono3py_force_constants_settings @@ -64,6 +66,7 @@ write_fc2_to_hdf5, write_fc3_to_hdf5, ) +from phono3py.interface.fc_calculator import extract_fc2_fc3_calculators from phono3py.interface.phono3py_yaml import Phono3pyYaml from phono3py.phonon3.fc3 import ( set_permutation_symmetry_fc3, @@ -122,6 +125,11 @@ def create_phono3py_force_constants( settings.cutoff_pair_distance, fc_calculator, fc_calculator_options, + settings.use_pypolymlp, + settings.mlp_params, + settings.displacement_distance, + settings.random_displacements, + settings.random_seed, log_level, ) @@ -159,29 +167,19 @@ def create_phono3py_force_constants( _read_phono3py_fc2(phono3py, symmetrize_fc2, input_filename, log_level) else: if phono3py.phonon_supercell_matrix is None: - if fc_calculator == "alm" and phono3py.fc2 is not None: - if log_level: - print("fc2 that was fit simultaneously with fc3 " "by ALM is used.") - else: - _create_phono3py_fc2( - phono3py, - ph3py_yaml, - symmetrize_fc2, - settings.is_compact_fc, - fc_calculator, - fc_calculator_options, - log_level, - ) + force_filename = "FORCES_FC3" else: - _create_phono3py_phonon_fc2( - phono3py, - ph3py_yaml, - symmetrize_fc2, - settings.is_compact_fc, - fc_calculator, - fc_calculator_options, - log_level, - ) + force_filename = "FORCES_FC2" + _create_phono3py_fc2( + phono3py, + ph3py_yaml, + force_filename, + symmetrize_fc2, + settings.is_compact_fc, + fc_calculator, + fc_calculator_options, + log_level, + ) if output_filename is None: filename = "fc2.hdf5" else: @@ -249,7 +247,7 @@ def parse_forces( # Try to read FORCES_FC* if type-2 and return dataset. # None is returned unless type-2. # can emit FileNotFoundError. - if dataset is None or dataset is not None and not forces_in_dataset(dataset): + if dataset is None or (dataset is not None and not forces_in_dataset(dataset)): _dataset = read_type2_dataset( natom, filename=force_filename, log_level=log_level ) @@ -321,8 +319,10 @@ def forces_in_dataset(dataset: dict) -> bool: ) -def displacements_in_dataset(dataset: dict) -> bool: +def displacements_in_dataset(dataset: Optional[dict]) -> bool: """Return whether displacements in dataset or not.""" + if dataset is None: + return False return "displacements" in dataset or "first_atoms" in dataset @@ -443,6 +443,11 @@ def _create_phono3py_fc3( cutoff_pair_distance: Optional[float], fc_calculator: Optional[str], fc_calculator_options: Optional[str], + use_pypolymlp: bool, + mlp_params: Union[str, dict, PypolymlpParams], + displacement_distance: Optional[float], + number_of_snapshots: Optional[int], + random_seed: Optional[int], log_level: int, ): """Read or calculate fc3. @@ -481,31 +486,175 @@ def _create_phono3py_fc3( # from _get_type2_dataset file_exists(e.filename, log_level) - phono3py.dataset = dataset + if use_pypolymlp: + phono3py.mlp_dataset = dataset + run_pypolymlp_to_compute_forces( + phono3py, + mlp_params, + displacement_distance=displacement_distance, + number_of_snapshots=number_of_snapshots, + random_seed=random_seed, + log_level=log_level, + ) + else: + phono3py.dataset = dataset phono3py.produce_fc3( symmetrize_fc3r=symmetrize_fc3r, is_compact_fc=is_compact_fc, - fc_calculator=fc_calculator, - fc_calculator_options=fc_calculator_options, + fc_calculator=extract_fc2_fc3_calculators(fc_calculator, 3), + fc_calculator_options=extract_fc2_fc3_calculators(fc_calculator_options, 3), + ) + + +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, + log_level: int = 0, +): + """Run pypolymlp to compute forces.""" + if log_level: + print("-" * 29 + " pypolymlp start " + "-" * 30) + print("Pypolymlp is a generator of polynomial machine learning potentials.") + print("Please cite the paper: A. Seko, J. Appl. Phys. 133, 011101 (2023).") + 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(): + if v is not None: + print(f" {k}: {v}") + if log_level > 1: + print("") + if log_level: + print("Developing MLPs by pypolymlp...", flush=True) + + ph3py.develop_mlp(params=mlp_params) + + if log_level: + print("-" * 30 + " pypolymlp end " + "-" * 31, flush=True) + + if displacement_distance is None: + _displacement_distance = 0.001 + else: + _displacement_distance = displacement_distance + + if log_level: + if number_of_snapshots: + print("Generate random displacements") + print( + " Twice of number of snapshots will be generated " + "for plus-minus displacements." + ) + else: + print("Generate displacements") + print( + f" Displacement distance: {_displacement_distance:.5f}".rstrip("0").rstrip( + "." + ) + ) + ph3py.generate_displacements( + distance=_displacement_distance, + is_plusminus=True, + number_of_snapshots=number_of_snapshots, + random_seed=random_seed, + ) + + if log_level: + print( + f"Evaluate forces in {ph3py.displacements.shape[0]} supercells " + "by pypolymlp", + flush=True, + ) + + if ph3py.mlp_dataset is None: + msg = "mlp_dataset has to be set before calling this method." + raise RuntimeError(msg) + if ph3py.supercells_with_displacements is None: + raise RuntimeError("Displacements are not set. Run generate_displacements.") + + ph3py.evaluate_mlp() + + +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, + log_level: int = 0, +): + """Run pypolymlp to compute phonon forces.""" + if ph3py.phonon_mlp_dataset is not None: + if log_level: + print("-" * 29 + " pypolymlp start " + "-" * 30) + print("Pypolymlp is a generator of polynomial machine learning potentials.") + print("Please cite the paper: A. Seko, J. Appl. Phys. 133, 011101 (2023).") + 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(): + if v is not None: + print(f" {k}: {v}") + if log_level > 1: + print("") + if log_level: + print("Developing MLPs by pypolymlp...", flush=True) + + ph3py.develop_phonon_mlp(params=mlp_params) + + if log_level: + print("-" * 30 + " pypolymlp end " + "-" * 31, flush=True) + + if displacement_distance is None: + _displacement_distance = 0.001 + else: + _displacement_distance = displacement_distance + if log_level: + print("Generate random displacements for fc2") + print( + f" Displacement distance: {_displacement_distance:.5f}".rstrip("0").rstrip( + "." + ) + ) + ph3py.generate_fc2_displacements( + distance=_displacement_distance, + is_plusminus=True, + number_of_snapshots=number_of_snapshots, + random_seed=random_seed, ) + if log_level: + print( + f"Evaluate forces in {ph3py.phonon_displacements.shape[0]} " + "supercells by pypolymlp", + flush=True, + ) + ph3py.evaluate_phonon_mlp() def _create_phono3py_fc2( phono3py: Phono3py, ph3py_yaml: Optional[Phono3pyYaml], + force_filename, symmetrize_fc2, is_compact_fc, fc_calculator, fc_calculator_options, log_level, ): + """Read forces and produce fc2. + + force_filename is either "FORCES_FC2" or "FORCES_FC3". + + """ _ph3py_yaml = _get_default_ph3py_yaml(ph3py_yaml) try: dataset = parse_forces( phono3py, ph3py_yaml=_ph3py_yaml, - force_filename="FORCES_FC3", + force_filename=force_filename, fc_type="fc2", log_level=log_level, ) @@ -521,8 +670,8 @@ def _create_phono3py_fc2( phono3py.produce_fc2( symmetrize_fc2=symmetrize_fc2, is_compact_fc=is_compact_fc, - fc_calculator=fc_calculator, - fc_calculator_options=fc_calculator_options, + fc_calculator=extract_fc2_fc3_calculators(fc_calculator, 2), + fc_calculator_options=extract_fc2_fc3_calculators(fc_calculator_options, 2), ) @@ -534,42 +683,6 @@ def _get_default_ph3py_yaml(ph3py_yaml: Optional[Phono3pyYaml]): return _ph3py_yaml -def _create_phono3py_phonon_fc2( - phono3py: Phono3py, - ph3py_yaml: Optional[Phono3pyYaml], - symmetrize_fc2: bool, - is_compact_fc: bool, - fc_calculator: Optional[str], - fc_calculator_options: Optional[str], - log_level: int, -): - _ph3py_yaml = _get_default_ph3py_yaml(ph3py_yaml) - - try: - dataset = parse_forces( - phono3py, - ph3py_yaml=_ph3py_yaml, - force_filename="FORCES_FC2", - fc_type="phonon_fc2", - 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) - - phono3py.phonon_dataset = dataset - phono3py.produce_fc2( - symmetrize_fc2=symmetrize_fc2, - is_compact_fc=is_compact_fc, - fc_calculator=fc_calculator, - fc_calculator_options=fc_calculator_options, - ) - - def _convert_unit_in_dataset( dataset: dict, distance_to_A: Optional[float] = None, diff --git a/phono3py/cui/create_supercells.py b/phono3py/cui/create_supercells.py index 3d2e3e84..a09f3dcf 100644 --- a/phono3py/cui/create_supercells.py +++ b/phono3py/cui/create_supercells.py @@ -77,8 +77,18 @@ def create_phono3py_supercells( cutoff_pair_distance=settings.cutoff_pair_distance, is_plusminus=settings.is_plusminus_displacement, is_diagonal=settings.is_diagonal_displacement, + number_of_snapshots=settings.random_displacements, + random_seed=settings.random_seed, ) + if settings.random_displacements_fc2: + phono3py.generate_fc2_displacements( + distance=distance, + is_plusminus=settings.is_plusminus_displacement, + number_of_snapshots=settings.random_displacements_fc2, + random_seed=settings.random_seed, + ) + if log_level: print("") print('Unit cell was read from "%s".' % optional_structure_info[0]) diff --git a/phono3py/cui/load.py b/phono3py/cui/load.py index 6bde5592..14730c85 100644 --- a/phono3py/cui/load.py +++ b/phono3py/cui/load.py @@ -49,12 +49,12 @@ from phono3py import Phono3py from phono3py.cui.create_force_constants import ( - displacements_in_dataset, forces_in_dataset, parse_forces, - read_type2_dataset, + run_pypolymlp_to_compute_forces, ) from phono3py.file_IO import read_fc2_from_hdf5, read_fc3_from_hdf5 +from phono3py.interface.fc_calculator import extract_fc2_fc3_calculators from phono3py.interface.phono3py_yaml import Phono3pyYaml from phono3py.phonon3.fc3 import show_drift_fc3 @@ -86,6 +86,8 @@ def load( symmetrize_fc: bool = True, is_mesh_symmetry: bool = True, is_compact_fc: bool = False, + use_pypolymlp: bool = False, + mlp_params: Optional[dict] = None, use_grg: bool = False, make_r0_average: bool = True, symprec: float = 1e-5, @@ -236,6 +238,10 @@ def load( True: (primitive, supecell, 3, 3) False: (supercell, supecell, 3, 3) where 'supercell' and 'primitive' indicate number of atoms in these cells. Default is False. + use_pypolymlp : bool, optional + Use pypolymlp for generating force constants. Default is False. + mlp_params : dict, optional + A set of parameters used by machine learning potentials. use_grg : bool, optional Use generalized regular grid when True. Default is False. make_r0_average : bool, optional @@ -337,6 +343,7 @@ def load( forces_fc3_filename=forces_fc3_filename, forces_fc2_filename=forces_fc2_filename, phono3py_yaml_filename=phono3py_yaml, + use_pypolymlp=use_pypolymlp, log_level=log_level, ) @@ -348,6 +355,8 @@ def load( fc_calculator_options=fc_calculator_options, symmetrize_fc=symmetrize_fc, is_compact_fc=is_compact_fc, + use_pypolymlp=use_pypolymlp, + mlp_params=mlp_params, log_level=log_level, ) @@ -370,6 +379,7 @@ def set_dataset_and_force_constants( forces_fc2_filename: Optional[Union[os.PathLike, Sequence]] = None, phono3py_yaml_filename: Optional[os.PathLike] = None, cutoff_pair_distance: Optional[float] = None, + use_pypolymlp: bool = False, log_level: int = 0, ) -> dict: """Set displacements, forces, and create force constants. @@ -387,7 +397,7 @@ def set_dataset_and_force_constants( """ read_fc = {"fc2": False, "fc3": False} - read_fc["fc3"] = _set_dataset_or_fc3( + read_fc["fc3"], dataset = _get_dataset_or_fc3( ph3py, ph3py_yaml=ph3py_yaml, fc3_filename=fc3_filename, @@ -396,20 +406,20 @@ def set_dataset_and_force_constants( cutoff_pair_distance=cutoff_pair_distance, log_level=log_level, ) - read_fc["fc2"] = _set_dataset_phonon_dataset_or_fc2( + if not read_fc["fc3"]: + if use_pypolymlp: + ph3py.mlp_dataset = dataset + else: + ph3py.dataset = dataset + read_fc["fc2"], phonon_dataset = _get_dataset_phonon_dataset_or_fc2( ph3py, ph3py_yaml=ph3py_yaml, fc2_filename=fc2_filename, forces_fc2_filename=forces_fc2_filename, log_level=log_level, ) - - # Cases that dataset is in phono3py.yaml but not forces. - if ph3py.dataset is None: - if ph3py_yaml is not None and ph3py_yaml.dataset is not None: - ph3py.dataset = ph3py_yaml.dataset - if ph3py_yaml is not None and ph3py_yaml.phonon_dataset is not None: - ph3py.phonon_dataset = ph3py_yaml.phonon_dataset + if not read_fc["fc2"]: + ph3py.phonon_dataset = phonon_dataset return read_fc @@ -418,9 +428,14 @@ def compute_force_constants_from_datasets( ph3py: Phono3py, read_fc: dict, fc_calculator: Optional[str] = None, - fc_calculator_options: Optional[dict] = None, + fc_calculator_options: Optional[Union[dict, str]] = None, symmetrize_fc: bool = True, is_compact_fc: bool = True, + use_pypolymlp: bool = False, + mlp_params: Optional[Union[dict, str]] = None, + displacement_distance: Optional[float] = None, + number_of_snapshots: Optional[int] = None, + random_seed: Optional[int] = None, log_level: int = 0, ): """Compute force constants from datasets. @@ -435,36 +450,40 @@ def compute_force_constants_from_datasets( fc2 : bool """ - if not read_fc["fc3"] and ph3py.dataset: + fc3_calculator = extract_fc2_fc3_calculators(fc_calculator, 3) + fc2_calculator = extract_fc2_fc3_calculators(fc_calculator, 2) + if not read_fc["fc3"] and (ph3py.dataset or ph3py.mlp_dataset): + if ph3py.mlp_dataset and use_pypolymlp: + run_pypolymlp_to_compute_forces( + ph3py, + mlp_params=mlp_params, + displacement_distance=displacement_distance, + number_of_snapshots=number_of_snapshots, + random_seed=random_seed, + log_level=log_level, + ) ph3py.produce_fc3( symmetrize_fc3r=symmetrize_fc, is_compact_fc=is_compact_fc, - fc_calculator=fc_calculator, - fc_calculator_options=fc_calculator_options, + fc_calculator=fc3_calculator, + fc_calculator_options=extract_fc2_fc3_calculators(fc_calculator_options, 3), ) - if log_level and symmetrize_fc: + + if log_level and symmetrize_fc and fc_calculator is None: print("fc3 was symmetrized.") if not read_fc["fc2"] and (ph3py.dataset or ph3py.phonon_dataset): - if ( - ph3py.phonon_supercell_matrix is None - and fc_calculator == "alm" - and ph3py.fc2 is not None - ): - if log_level: - print("fc2 that was fit simultaneously with fc3 by ALM is used.") - else: - ph3py.produce_fc2( - symmetrize_fc2=symmetrize_fc, - is_compact_fc=is_compact_fc, - fc_calculator=fc_calculator, - fc_calculator_options=fc_calculator_options, - ) - if log_level and symmetrize_fc: - print("fc2 was symmetrized.") + ph3py.produce_fc2( + symmetrize_fc2=symmetrize_fc, + is_compact_fc=is_compact_fc, + fc_calculator=fc2_calculator, + fc_calculator_options=extract_fc2_fc3_calculators(fc_calculator_options, 2), + ) + if log_level and symmetrize_fc and fc_calculator is None: + print("fc2 was symmetrized.") -def _set_dataset_or_fc3( +def _get_dataset_or_fc3( ph3py: Phono3py, ph3py_yaml: Optional[Phono3pyYaml] = None, fc3_filename: Optional[os.PathLike] = None, @@ -472,19 +491,40 @@ def _set_dataset_or_fc3( phono3py_yaml_filename: Optional[os.PathLike] = None, cutoff_pair_distance: Optional[float] = None, log_level: int = 0, -) -> bool: +) -> tuple[bool, dict]: p2s_map = ph3py.primitive.p2s_map read_fc3 = False - if fc3_filename is not None: - fc3 = read_fc3_from_hdf5(filename=fc3_filename, p2s_map=p2s_map) - _check_fc3_shape(ph3py, fc3, filename=fc3_filename) + dataset = None + if fc3_filename is not None or pathlib.Path("fc3.hdf5").exists(): + if fc3_filename is None: + _fc3_filename = "fc3.hdf5" + else: + _fc3_filename = fc3_filename + fc3 = read_fc3_from_hdf5(filename=_fc3_filename, p2s_map=p2s_map) + _check_fc3_shape(ph3py, fc3, filename=_fc3_filename) ph3py.fc3 = fc3 read_fc3 = True if log_level: - print('fc3 was read from "%s".' % fc3_filename) - elif forces_fc3_filename is not None: - force_filename = forces_fc3_filename - _set_dataset_for_fc3( + print(f'fc3 was read from "{_fc3_filename}".') + elif ( + ph3py_yaml is not None + and ph3py_yaml.dataset is not None + and forces_in_dataset(ph3py_yaml.dataset) + ): + dataset = _get_dataset_for_fc3( + ph3py, + ph3py_yaml, + None, + phono3py_yaml_filename, + cutoff_pair_distance, + log_level, + ) + elif forces_fc3_filename is not None or pathlib.Path("FORCES_FC3").exists(): + if forces_fc3_filename is None: + force_filename = "FORCES_FC3" + else: + force_filename = forces_fc3_filename + dataset = _get_dataset_for_fc3( ph3py, ph3py_yaml, force_filename, @@ -492,90 +532,39 @@ def _set_dataset_or_fc3( cutoff_pair_distance, log_level, ) - elif pathlib.Path("fc3.hdf5").exists(): - fc3 = read_fc3_from_hdf5(filename="fc3.hdf5", p2s_map=p2s_map) - _check_fc3_shape(ph3py, fc3) - ph3py.fc3 = fc3 - read_fc3 = True - if log_level: - print('fc3 was read from "fc3.hdf5".') - elif dataset := read_type2_dataset( - len(ph3py.supercell), "FORCES_FC3", log_level=log_level - ): # := Assignment Expressions (Python>=3.8) - ph3py.dataset = dataset - elif ph3py_yaml is not None and ph3py_yaml.dataset is not None: - if pathlib.Path("FORCES_FC3").exists() and displacements_in_dataset( - ph3py_yaml.dataset - ): - _set_dataset_for_fc3( - ph3py, - ph3py_yaml, - "FORCES_FC3", - phono3py_yaml_filename, - cutoff_pair_distance, - log_level, - ) - if forces_in_dataset(ph3py_yaml.dataset): - _set_dataset_for_fc3( - ph3py, - ph3py_yaml, - None, - phono3py_yaml_filename, - cutoff_pair_distance, - log_level, - ) - return read_fc3 + if not forces_in_dataset(dataset): + dataset = None + + return read_fc3, dataset -def _set_dataset_phonon_dataset_or_fc2( +def _get_dataset_phonon_dataset_or_fc2( ph3py: Phono3py, ph3py_yaml: Optional[Phono3pyYaml] = None, fc2_filename: Optional[os.PathLike] = None, forces_fc2_filename: Optional[Union[os.PathLike, Sequence]] = None, log_level: int = 0, -) -> bool: +) -> tuple[bool, dict, dict]: phonon_p2s_map = ph3py.phonon_primitive.p2s_map read_fc2 = False - if fc2_filename is not None: - fc2 = read_fc2_from_hdf5(filename=fc2_filename, p2s_map=phonon_p2s_map) - _check_fc2_shape(ph3py, fc2, filename=fc2_filename) - ph3py.fc2 = fc2 - read_fc2 = True - if log_level: - print('fc2 was read from "%s".' % fc2_filename) - elif forces_fc2_filename is not None: - force_filename = forces_fc2_filename - _set_dataset_for_fc2( - ph3py, - ph3py_yaml, - force_filename, - "phonon_fc2", - log_level, - ) - elif pathlib.Path("fc2.hdf5").exists(): - fc2 = read_fc2_from_hdf5(filename="fc2.hdf5", p2s_map=phonon_p2s_map) - _check_fc2_shape(ph3py, fc2) + phonon_dataset = None + if fc2_filename is not None or pathlib.Path("fc2.hdf5").exists(): + if fc2_filename is None: + _fc2_filename = "fc2.hdf5" + else: + _fc2_filename = fc2_filename + fc2 = read_fc2_from_hdf5(filename=_fc2_filename, p2s_map=phonon_p2s_map) + _check_fc2_shape(ph3py, fc2, filename=_fc2_filename) ph3py.fc2 = fc2 read_fc2 = True if log_level: - print('fc2 was read from "fc2.hdf5".') - elif ( - pathlib.Path("FORCES_FC2").exists() - and ph3py.phonon_supercell_matrix is not None - ): - _set_dataset_for_fc2( - ph3py, - ph3py_yaml, - "FORCES_FC2", - "phonon_fc2", - log_level, - ) + print(f'fc2 was read from "{fc2_filename}".') elif ( ph3py_yaml is not None and ph3py_yaml.phonon_dataset is not None and forces_in_dataset(ph3py_yaml.phonon_dataset) ): - _set_dataset_for_fc2( + phonon_dataset = _get_dataset_for_fc2( ph3py, ph3py_yaml, None, @@ -583,41 +572,34 @@ def _set_dataset_phonon_dataset_or_fc2( log_level, ) elif ( - ph3py_yaml is not None - and ph3py_yaml.dataset is not None - and forces_in_dataset(ph3py_yaml.dataset) - ): - _set_dataset_for_fc2( - ph3py, - ph3py_yaml, - None, - "fc2", - log_level, - ) - elif ( - pathlib.Path("FORCES_FC3").exists() - and ph3py.phonon_supercell_matrix is not None - ): - # suppose fc3.hdf5 is read but fc2.hdf5 doesn't exist. - _set_dataset_for_fc2( + forces_fc2_filename is not None or pathlib.Path("FORCES_FC2").exists() + ) and ph3py.phonon_supercell_matrix: + if forces_fc2_filename is None: + force_filename = forces_fc2_filename + else: + force_filename = forces_fc2_filename + phonon_dataset = _get_dataset_for_fc2( ph3py, ph3py_yaml, - "FORCES_FC3", - "fc2", + force_filename, + "phonon_fc2", log_level, ) - return read_fc2 + if not forces_in_dataset(phonon_dataset): + phonon_dataset = None + + return read_fc2, phonon_dataset -def _set_dataset_for_fc3( +def _get_dataset_for_fc3( ph3py: Phono3py, ph3py_yaml: Optional[Phono3pyYaml], force_filename, phono3py_yaml_filename, cutoff_pair_distance, log_level, -): - ph3py.dataset = parse_forces( +) -> dict: + dataset = parse_forces( ph3py, ph3py_yaml=ph3py_yaml, cutoff_pair_distance=cutoff_pair_distance, @@ -626,9 +608,10 @@ def _set_dataset_for_fc3( fc_type="fc3", log_level=log_level, ) + return dataset -def _set_dataset_for_fc2( +def _get_dataset_for_fc2( ph3py: Phono3py, ph3py_yaml: Optional[Phono3pyYaml], force_filename, @@ -642,10 +625,7 @@ def _set_dataset_for_fc2( fc_type=fc_type, log_level=log_level, ) - if fc_type == "phonon_fc2": - ph3py.phonon_dataset = dataset - else: - ph3py.dataset = dataset + return dataset def _check_fc2_shape(ph3py: Phono3py, fc2, filename="fc2.hdf5"): diff --git a/phono3py/cui/phono3py_argparse.py b/phono3py/cui/phono3py_argparse.py index 960f8639..0c31dde6 100644 --- a/phono3py/cui/phono3py_argparse.py +++ b/phono3py/cui/phono3py_argparse.py @@ -471,6 +471,15 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False): parser.add_argument( "--mesh", nargs="+", dest="mesh_numbers", default=None, help="Mesh numbers" ) + parser.add_argument( + "--mlp-params", + dest="mlp_params", + default=None, + help=( + "Parameters for machine learning potentials as comma separated " + "string with the style of key = values" + ), + ) parser.add_argument( "--mv", "--mass-variances", @@ -516,7 +525,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False): action="store_true", default=False, help="Deactivate summation of partial kappa at q-stars", - ), + ) parser.add_argument( "--nomeshsym", dest="is_nomeshsym", @@ -618,6 +627,13 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False): default=None, help="Conversion factor for ph-ph interaction", ) + parser.add_argument( + "--pypolymlp", + dest="use_pypolymlp", + action="store_true", + default=False, + help="Use pypolymlp and symfc for generating force constants", + ) parser.add_argument( "--qpoints", nargs="+", @@ -640,6 +656,29 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False): default=False, help="Print out smallest information", ) + parser.add_argument( + "--random-seed", + dest="random_seed", + type=int, + default=None, + help="Random seed by a 32 bit unsigned integer", + ) + parser.add_argument( + "--rd", + "--random-displacements", + dest="random_displacements", + type=int, + default=None, + help="Number of supercells with random displacements", + ) + parser.add_argument( + "--rd-fc2", + "--random-displacements-fc2", + dest="random_displacements_fc2", + type=int, + default=None, + help="Number of phonon supercells with random displacements", + ) parser.add_argument( "--read-collision", dest="read_collision", @@ -713,6 +752,13 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False): default=None, help="Cutoff width of smearing function (ratio to sigma value)", ) + parser.add_argument( + "--symfc", + dest="use_symfc", + action="store_true", + default=None, + help="Use symfc for generating force constants", + ) parser.add_argument( "--spf", dest="is_spectral_function", diff --git a/phono3py/cui/phono3py_script.py b/phono3py/cui/phono3py_script.py index 3fc35cc1..a1fc34be 100644 --- a/phono3py/cui/phono3py_script.py +++ b/phono3py/cui/phono3py_script.py @@ -89,9 +89,9 @@ write_phonon_to_hdf5, ) from phono3py.interface.phono3py_yaml import Phono3pyYaml +from phono3py.phonon.grid import get_grid_point_from_address, get_ir_grid_points from phono3py.phonon3.fc3 import show_drift_fc3 from phono3py.phonon3.gruneisen import run_gruneisen_parameters -from phono3py.phonon.grid import get_grid_point_from_address, get_ir_grid_points from phono3py.version import __version__ # import logging @@ -160,11 +160,18 @@ def finalize_phono3py( yaml_filename = filename _physical_units = get_default_physical_units(phono3py.calculator) + + write_force_sets = phono3py.mlp is not None + _write_displacements = write_displacements or phono3py.mlp is not None + ph3py_yaml = Phono3pyYaml( configuration=confs_dict, calculator=phono3py.calculator, physical_units=_physical_units, - settings={"force_sets": False, "displacements": write_displacements}, + settings={ + "force_sets": write_force_sets, + "displacements": _write_displacements, + }, ) ph3py_yaml.set_phonon_info(phono3py) with open(yaml_filename, "w") as w: @@ -528,6 +535,7 @@ def store_force_constants( ph3py_yaml=ph3py_yaml, phono3py_yaml_filename=phono3py_yaml_filename, cutoff_pair_distance=settings.cutoff_pair_distance, + use_pypolymlp=settings.use_pypolymlp, log_level=log_level, ) try: @@ -538,6 +546,11 @@ def store_force_constants( fc_calculator_options=fc_calculator_options, symmetrize_fc=settings.fc_symmetry, is_compact_fc=settings.is_compact_fc, + use_pypolymlp=settings.use_pypolymlp, + mlp_params=settings.mlp_params, + displacement_distance=settings.displacement_distance, + number_of_snapshots=settings.random_displacements, + random_seed=settings.random_seed, log_level=log_level, ) except ForceCalculatorRequiredError: @@ -605,8 +618,8 @@ def _show_fc_calculator_not_found(log_level): print( "Built-in force constants calculator doesn't support the " "dispalcements-forces dataset. " - "An external force calculator, e.g., ALM (--alm), has to be used " - "to compute force constants." + "An external force calculator, e.g., symfc (--symfc_ or ALM (--alm), " + "has to be used to compute force constants." ) print_error() sys.exit(1) diff --git a/phono3py/cui/settings.py b/phono3py/cui/settings.py index 5f35b37f..9e5ebe40 100644 --- a/phono3py/cui/settings.py +++ b/phono3py/cui/settings.py @@ -89,6 +89,7 @@ class Phono3pySettings(Settings): "pinv_solver": 0, "pinv_method": 0, "pp_conversion_factor": None, + "random_displacements_fc2": None, "scattering_event_class": None, # scattering event class 1 or 2 "sigma_cutoff_width": None, "solve_collective_phonon": False, @@ -276,6 +277,10 @@ def set_pp_conversion_factor(self, val): """Set pp_conversion_factor.""" self._v["pp_conversion_factor"] = val + def set_random_displacements_fc2(self, val): + """Set random_displacements_fc2.""" + self._v["random_displacements_fc2"] = val + def set_read_collision(self, val): """Set read_collision.""" self._v["read_collision"] = val @@ -553,6 +558,11 @@ def _read_options(self): if pp_conv_factor is not None: self._confs["pp_conversion_factor"] = pp_conv_factor + if "random_displacements_fc2" in self._args: + rd_fc2 = self._args.random_displacements_fc2 + if rd_fc2 is not None: + self._confs["random_displacements_fc2"] = rd_fc2 + if "read_fc2" in self._args: if self._args.read_fc2: self._confs["read_fc2"] = ".true." @@ -695,6 +705,7 @@ def _parse_conf(self): "pinv_method", "pinv_solver", "num_points_in_batch", + "random_displacements_fc2", "scattering_event_class", ): self.set_parameter(conf_key, int(confs[conf_key])) @@ -709,7 +720,7 @@ def _parse_conf(self): # specials if conf_key in ("create_forces_fc2", "create_forces_fc3"): - if type(confs[conf_key]) is str: + if isinstance(confs[conf_key], str): fnames = confs[conf_key].split() else: fnames = confs[conf_key] @@ -943,6 +954,12 @@ def _set_settings(self): if "pp_conversion_factor" in params: self._settings.set_pp_conversion_factor(params["pp_conversion_factor"]) + # Random displacements for fc2 + if "random_displacements_fc2" in params: + self._settings.set_random_displacements_fc2( + params["random_displacements_fc2"] + ) + # Calculate real_self_energys if "real_self_energy" in params: self._settings.set_is_real_self_energy(params["real_self_energy"]) diff --git a/phono3py/cui/show_log.py b/phono3py/cui/show_log.py index 2283e065..bc731832 100644 --- a/phono3py/cui/show_log.py +++ b/phono3py/cui/show_log.py @@ -48,7 +48,8 @@ def show_general_settings( ): """Show general setting information.""" is_primitive_axes_auto = ( - type(phono3py.primitive_matrix) is str and phono3py.primitive_matrix == "auto" + isinstance(phono3py.primitive_matrix, str) + and phono3py.primitive_matrix == "auto" ) primitive_matrix = phono3py.primitive_matrix supercell_matrix = phono3py.supercell_matrix @@ -106,7 +107,7 @@ def show_phono3py_cells(phono3py: Phono3py): print_cell(phonon_primitive) print("-" * 21 + " supercell for harmonic phonon " + "-" * 22) print_cell(phonon_supercell, mapping=phonon_primitive.s2p_map) - print("-" * 76) + print("-" * 76, flush=True) def show_phono3py_force_constants_settings(settings: Phono3pySettings): diff --git a/phono3py/cui/triplets_info.py b/phono3py/cui/triplets_info.py index 50ced6a5..1e85bbc9 100644 --- a/phono3py/cui/triplets_info.py +++ b/phono3py/cui/triplets_info.py @@ -37,8 +37,8 @@ import numpy as np from phono3py.file_IO import write_grid_address_to_hdf5, write_ir_grid_points -from phono3py.phonon3.triplets import get_triplets_at_q from phono3py.phonon.grid import get_ir_grid_points +from phono3py.phonon3.triplets import get_triplets_at_q def write_grid_points( diff --git a/phono3py/file_IO.py b/phono3py/file_IO.py index a101c87e..8875fb35 100644 --- a/phono3py/file_IO.py +++ b/phono3py/file_IO.py @@ -57,7 +57,9 @@ def write_disp_fc3_yaml(dataset, supercell, filename="disp_fc3.yaml"): This function should not be called from phono3py script from version 3. """ - warnings.warn("write_disp_fc3_yaml() is deprecated.", DeprecationWarning) + warnings.warn( + "write_disp_fc3_yaml() is deprecated.", DeprecationWarning, stacklevel=2 + ) w = open(filename, "w") w.write("natom: %d\n" % dataset["natom"]) @@ -155,7 +157,9 @@ def write_disp_fc2_yaml(dataset, supercell, filename="disp_fc2.yaml"): This function should not be called from phono3py script from version 3. """ - warnings.warn("write_disp_fc2_yaml() is deprecated.", DeprecationWarning) + warnings.warn( + "write_disp_fc2_yaml() is deprecated.", DeprecationWarning, stacklevel=2 + ) w = open(filename, "w") w.write("natom: %d\n" % dataset["natom"]) @@ -251,7 +255,7 @@ def write_FORCES_FC3(disp_dataset, forces_fc3=None, fp=None, filename="FORCES_FC else: # for forces in forces_fc3[i]: # w.write("%15.10f %15.10f %15.10f\n" % (tuple(forces))) - for j in range(natom): + for _ in range(natom): w.write("%15.10f %15.10f %15.10f\n" % (0, 0, 0)) count += 1 @@ -280,7 +284,7 @@ def write_fc3_to_hdf5(fc3, filename="fc3.hdf5", p2s_map=None, compression="gzip" """ with h5py.File(filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("fc3", data=fc3, compression=compression) if p2s_map is not None: w.create_dataset("p2s_map", data=p2s_map) @@ -339,8 +343,8 @@ def write_force_constants_to_hdf5( ): try: import h5py - except ImportError: - raise ModuleNotFoundError("You need to install python-h5py.") + except ImportError as exc: + raise ModuleNotFoundError("You need to install python-h5py.") from exc with h5py.File(filename, "w") as w: w.create_dataset( @@ -352,9 +356,9 @@ def write_force_constants_to_hdf5( dset = w.create_dataset( "physical_unit", (1,), dtype="S%d" % len(physical_unit) ) - dset[0] = np.string_(physical_unit) + dset[0] = np.bytes_(physical_unit) if version is not None: - w.create_dataset("version", data=np.string_(version)) + w.create_dataset("version", data=np.bytes_(version)) write_force_constants_to_hdf5( force_constants, @@ -385,7 +389,7 @@ def write_grid_address_to_hdf5( suffix = _get_filename_suffix(mesh, filename=filename) full_filename = "grid_address" + suffix + ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("mesh", data=mesh) if bz_grid is not None and bz_grid.grid_matrix is not None: w.create_dataset("grid_matrix", data=bz_grid.grid_matrix) @@ -577,7 +581,7 @@ def write_real_self_energy_to_hdf5( full_filename += ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("grid_point", data=grid_point) w.create_dataset("mesh", data=mesh) if bz_grid is not None and bz_grid.grid_matrix is not None: @@ -666,7 +670,7 @@ def write_spectral_function_to_hdf5( full_filename += ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("grid_point", data=grid_point) w.create_dataset("mesh", data=mesh) if bz_grid is not None and bz_grid.grid_matrix is not None: @@ -713,7 +717,7 @@ def write_collision_to_hdf5( ) full_filename = "collision" + suffix + ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("temperature", data=temperature) if gamma is not None: w.create_dataset("gamma", data=gamma) @@ -751,7 +755,7 @@ def write_collision_to_hdf5( def write_full_collision_matrix(collision_matrix, filename="fcm.hdf5"): """Write full (non-symmetrized) collision matrix to collision-*.hdf5.""" with h5py.File(filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("collision_matrix", data=collision_matrix) @@ -776,7 +780,7 @@ def write_unitary_matrix_to_hdf5( ) hdf5_filename = "unitary" + suffix + ".hdf5" with h5py.File(hdf5_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("temperature", data=temperature) if unitary_matrix is not None: w.create_dataset("unitary_matrix", data=unitary_matrix) @@ -816,7 +820,7 @@ def write_collision_eigenvalues_to_hdf5( mesh, sigma=sigma, sigma_cutoff=sigma_cutoff, filename=filename ) with h5py.File("coleigs" + suffix + ".hdf5", "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("temperature", data=temperatures) w.create_dataset("collision_eigenvalues", data=collision_eigenvalues) w.close() @@ -885,7 +889,7 @@ def write_kappa_to_hdf5( ) full_filename = "kappa" + suffix + ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("temperature", data=temperature) w.create_dataset("mesh", data=mesh) if bz_grid is not None and bz_grid.grid_matrix is not None: @@ -1159,7 +1163,7 @@ def write_pp_to_hdf5( full_filename = "pp" + suffix + ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) if pp is not None: if g_zero is None: w.create_dataset("pp", data=pp, compression=compression) @@ -1323,7 +1327,7 @@ def write_gamma_detail_to_hdf5( full_filename = "gamma_detail" + suffix + ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("temperature", data=temperature) w.create_dataset("mesh", data=mesh) if bz_grid is not None and bz_grid.grid_matrix is not None: @@ -1397,7 +1401,7 @@ def write_phonon_to_hdf5( full_filename = "phonon" + suffix + ".hdf5" with h5py.File(full_filename, "w") as w: - w.create_dataset("version", data=np.string_(__version__)) + w.create_dataset("version", data=np.bytes_(__version__)) w.create_dataset("mesh", data=mesh) if bz_grid is not None and bz_grid.grid_matrix is not None: w.create_dataset("grid_matrix", data=bz_grid.grid_matrix) @@ -1480,7 +1484,9 @@ def parse_disp_fc2_yaml(filename="disp_fc2.yaml", return_cell=False): This function should not be called from phono3py script from version 3. """ - warnings.warn("parse_disp_fc2_yaml() is deprecated.", DeprecationWarning) + warnings.warn( + "parse_disp_fc2_yaml() is deprecated.", DeprecationWarning, stacklevel=2 + ) dataset = _parse_yaml(filename) natom = dataset["natom"] @@ -1507,7 +1513,9 @@ def parse_disp_fc3_yaml(filename="disp_fc3.yaml", return_cell=False): This function should not be called from phono3py script from version 3. """ - warnings.warn("parse_disp_fc3_yaml() is deprecated.", DeprecationWarning) + warnings.warn( + "parse_disp_fc3_yaml() is deprecated.", DeprecationWarning, stacklevel=2 + ) dataset = _parse_yaml(filename) natom = dataset["natom"] @@ -1547,7 +1555,7 @@ def parse_FORCES_FC2(disp_dataset, filename="FORCES_FC2", unit_conversion_factor num_disp = len(disp_dataset["first_atoms"]) forces_fc2 = [] with open(filename, "r") as f2: - for i in range(num_disp): + for _ in range(num_disp): forces = _parse_force_lines(f2, num_atom) if forces is None: return [] @@ -1681,7 +1689,7 @@ def _parse_yaml(file_yaml): So this is obsolete at v2 and later versions. """ - warnings.warn("_parse_yaml() is deprecated.", DeprecationWarning) + warnings.warn("_parse_yaml() is deprecated.", DeprecationWarning, stacklevel=2) import yaml @@ -1720,7 +1728,7 @@ def _write_cell_yaml(w, supercell): These methods are also deprecated. """ - warnings.warn("write_cell_yaml() is deprecated.", DeprecationWarning) + warnings.warn("write_cell_yaml() is deprecated.", DeprecationWarning, stacklevel=2) w.write("lattice:\n") for axis in supercell.get_cell(): diff --git a/phono3py/interface/calculator.py b/phono3py/interface/calculator.py index 15f0014d..e0b52d51 100644 --- a/phono3py/interface/calculator.py +++ b/phono3py/interface/calculator.py @@ -34,7 +34,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. - calculator_info = { "abinit": {"option": {"name": "--abinit", "help": "Invoke Abinit mode"}}, # 'aims': {'option': {'name': "--aims", diff --git a/phono3py/interface/fc_calculator.py b/phono3py/interface/fc_calculator.py index 239f1fc0..ecdbc2b7 100644 --- a/phono3py/interface/fc_calculator.py +++ b/phono3py/interface/fc_calculator.py @@ -34,16 +34,26 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from __future__ import annotations + +from typing import Optional, Union + +import numpy as np +from phonopy.structure.atoms import PhonopyAtoms +from phonopy.structure.cells import Primitive +from phonopy.structure.symmetry import Symmetry + def get_fc3( - supercell, - primitive, - displacements, - forces, - fc_calculator=None, - fc_calculator_options=None, - is_compact_fc=False, - log_level=0, + supercell: PhonopyAtoms, + primitive: Primitive, + displacements: np.ndarray, + forces: np.ndarray, + fc_calculator: Optional[str] = None, + fc_calculator_options: Optional[str] = None, + is_compact_fc: bool = False, + symmetry: Optional[Symmetry] = None, + log_level: int = 0, ): """Supercell 2nd order force constants (fc2) are calculated. @@ -86,9 +96,9 @@ def get_fc3( """ if fc_calculator == "alm": - from phono3py.interface.alm import get_fc3 + from phono3py.interface.alm import get_fc3 as get_fc3_alm - return get_fc3( + return get_fc3_alm( supercell, primitive, displacements, @@ -97,6 +107,46 @@ def get_fc3( is_compact_fc=is_compact_fc, log_level=log_level, ) + elif fc_calculator == "symfc": + from phonopy.interface.symfc import run_symfc + + return run_symfc( + supercell, + primitive, + displacements, + forces, + orders=[2, 3], + is_compact_fc=is_compact_fc, + symmetry=symmetry, + options=fc_calculator_options, + log_level=log_level, + ) else: msg = "Force constants calculator of %s was not found ." % fc_calculator raise RuntimeError(msg) + + +def extract_fc2_fc3_calculators(fc_calculator: Optional[Union[str, dict]], order: int): + """Extract fc_calculator and fc_calculator_options for fc2 and fc3. + + fc_calculator : str + FC calculator. "|" separates fc2 and fc3. First and last + parts separated correspond to fc2 and fc3 calculators, respectively. + order : int = 2 or 3 + 2 and 3 indicate fc2 and fc3, respectively. + + """ + if isinstance(fc_calculator, dict) or 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: + raise RuntimeError("fc_calculator should be str or dict.") diff --git a/phono3py/interface/phono3py_yaml.py b/phono3py/interface/phono3py_yaml.py index 9369e282..36fdff8e 100644 --- a/phono3py/interface/phono3py_yaml.py +++ b/phono3py/interface/phono3py_yaml.py @@ -152,9 +152,9 @@ def _parse_fc3_dataset(self): dataset = None if "displacement_pairs" in self._yaml: disp = self._yaml["displacement_pairs"][0] - if type(disp) is dict: # type1 + if isinstance(disp, dict): # type1 dataset = self._parse_fc3_dataset_type1(len(self._data.supercell)) - elif type(disp) is list: # type2 + elif isinstance(disp, list): # type2 if "displacement" in disp[0]: dataset = self._parse_force_sets_type2() if "displacement_pair_info" in self._yaml: @@ -326,12 +326,13 @@ def _displacements_yaml_lines(self, with_forces: bool = False) -> list: """ lines = [] - if self._data.phonon_supercell_matrix is not None: + if self._data.phonon_dataset is not None: lines += self._displacements_yaml_lines_2types( self._data.phonon_dataset, with_forces=with_forces, key_prefix="phonon_", ) + lines.append("") lines += self._displacements_yaml_lines_2types( self._data.dataset, with_forces=with_forces ) @@ -507,7 +508,7 @@ def _displacements_yaml_lines_type1_info(dataset): if "duplicates" in dataset and dataset["duplicates"]: lines.append(" duplicated_supercell_ids: " "# 0 means perfect supercell") # Backward compatibility for dict type - if type(dataset["duplicates"]) is dict: + if isinstance(dataset["duplicates"], dict): for disp1_id, j in dataset["duplicates"].items(): lines.append(" - [ %d, %d ]" % (int(disp1_id), j)) else: @@ -580,7 +581,7 @@ def read_phono3py_yaml( ) -> Phono3pyYamlData: """Read phono3py.yaml like file.""" yaml_data = load_yaml(filename) - if type(yaml_data) is str: + if isinstance(yaml_data, str): msg = f'Could not load "{filename}" properly.' raise TypeError(msg) return load_phono3py_yaml( @@ -597,7 +598,7 @@ def load_phono3py_yaml( """Return Phono3pyYamlData instance loading yaml data. Parameters - ----------- + ---------- yaml_data : dict """ diff --git a/phono3py/phonon/grid.py b/phono3py/phonon/grid.py index ea7bc30f..b42112ea 100644 --- a/phono3py/phonon/grid.py +++ b/phono3py/phonon/grid.py @@ -716,6 +716,7 @@ def _run_grg( warnings.warn( "Non primitive cell input. Unable to use GR-grid.", RuntimeWarning, + stacklevel=2, ) return False diff --git a/phono3py/phonon3/displacement_fc3.py b/phono3py/phonon3/displacement_fc3.py index 4f539977..f769c900 100644 --- a/phono3py/phonon3/displacement_fc3.py +++ b/phono3py/phonon3/displacement_fc3.py @@ -350,13 +350,11 @@ def _find_duplicates(direction_dataset): duplucates = [] done = [] - for i, direction1 in enumerate(direction_dataset): + for direction1 in direction_dataset: n1 = direction1["number"] for directions2 in direction1["second_atoms"]: n2 = directions2["number"] - if ( - n2 > n1 and (n2, n1) not in done and (n2, n1) in direction_sets - ): # noqa E129 + if n2 > n1 and (n2, n1) not in done and (n2, n1) in direction_sets: # noqa E129 done.append((n2, n1)) duplucates += _compare( n1, @@ -368,14 +366,14 @@ def _find_duplicates(direction_dataset): ) done = [] - for i, direction1 in enumerate(direction_dataset): + for direction1 in direction_dataset: n1 = direction1["number"] for directions2 in direction1["second_atoms"]: n2 = directions2["number"] if n1 == n2 and n1 not in done: done.append(n1) duplucates += _compare_opposite( - n1, direction_sets[(n1, n1)], pair_idx[(n1, n1)] + direction_sets[(n1, n1)], pair_idx[(n1, n1)] ) return duplucates @@ -393,10 +391,10 @@ def _compare(n1, n2, dset1, dset2, pidx1, pidx2): return [[i, j] for (i, j) in duplucates if i > j] -def _compare_opposite(n1, dset1, pidx1): +def _compare_opposite(dset1, pidx1): flip_sets = np.array(dset1)[:, [3, 4, 5, 0, 1, 2]] duplucates = [] - for i, d1 in enumerate(dset1): + for d1 in dset1: eq_indices = np.where(np.abs(flip_sets + d1).sum(axis=1) == 0)[0] if len(eq_indices) > 0: duplucates += [[pidx1[j], 0] for j in eq_indices] diff --git a/phono3py/phonon3/fc3.py b/phono3py/phonon3/fc3.py index d7204971..817a06ff 100644 --- a/phono3py/phonon3/fc3.py +++ b/phono3py/phonon3/fc3.py @@ -276,12 +276,12 @@ def set_permutation_symmetry_compact_fc3(fc3, primitive): np.array(p2s_map, dtype="int_"), np.array(nsym_list, dtype="int_"), ) - except ImportError: + except ImportError as exc: text = ( "Import error at phono3c.permutation_symmetry_compact_fc3. " "Corresponding python code is not implemented." ) - raise RuntimeError(text) + raise RuntimeError(text) from exc def _copy_permutation_symmetry_fc3_elem(fc3, fc3_elem, a, b, c): @@ -339,29 +339,29 @@ def set_translational_invariance_compact_fc3(fc3, primitive: Primitive): _set_translational_invariance_fc3_per_index(fc3, index=1) _set_translational_invariance_fc3_per_index(fc3, index=2) - except ImportError: + except ImportError as exc: text = ( "Import error at phono3c.tranpose_compact_fc3. " "Corresponding python code is not implemented." ) - raise RuntimeError(text) + raise RuntimeError(text) from exc def _set_translational_invariance_fc3_per_index(fc3, index=0): for i in range(fc3.shape[(1 + index) % 3]): for j in range(fc3.shape[(2 + index) % 3]): - for k, l, m in list(np.ndindex(3, 3, 3)): + for k, ll, m in list(np.ndindex(3, 3, 3)): if index == 0: - fc3[:, i, j, k, l, m] -= ( - np.sum(fc3[:, i, j, k, l, m]) / fc3.shape[0] + fc3[:, i, j, k, ll, m] -= ( + np.sum(fc3[:, i, j, k, ll, m]) / fc3.shape[0] ) elif index == 1: - fc3[j, :, i, k, l, m] -= ( - np.sum(fc3[j, :, i, k, l, m]) / fc3.shape[1] + fc3[j, :, i, k, ll, m] -= ( + np.sum(fc3[j, :, i, k, ll, m]) / fc3.shape[1] ) elif index == 2: - fc3[i, j, :, k, l, m] -= ( - np.sum(fc3[i, j, :, k, l, m]) / fc3.shape[2] + fc3[i, j, :, k, ll, m] -= ( + np.sum(fc3[i, j, :, k, ll, m]) / fc3.shape[2] ) @@ -465,7 +465,7 @@ def _solve_fc3( print("Displacements (in Angstrom):") else: print("One displacement (in Angstrom):") - for i, v in enumerate(displacements_first): + for v in displacements_first: print(" [%7.4f %7.4f %7.4f]" % tuple(v)) sys.stdout.flush() if verbose > 2: @@ -577,19 +577,19 @@ def show_drift_fc3(fc3, primitive=None, name="fc3"): klm1 = [0, 0, 0] klm2 = [0, 0, 0] klm3 = [0, 0, 0] - for i, j, k, l, m in list(np.ndindex((num_atom, num_atom, 3, 3, 3))): - val1 = fc3[:, i, j, k, l, m].sum() - val2 = fc3[i, :, j, k, l, m].sum() - val3 = fc3[i, j, :, k, l, m].sum() + for i, j, k, ll, m in list(np.ndindex((num_atom, num_atom, 3, 3, 3))): + val1 = fc3[:, i, j, k, ll, m].sum() + val2 = fc3[i, :, j, k, ll, m].sum() + val3 = fc3[i, j, :, k, ll, m].sum() if abs(val1) > abs(maxval1): maxval1 = val1 - klm1 = [k, l, m] + klm1 = [k, ll, m] if abs(val2) > abs(maxval2): maxval2 = val2 - klm2 = [k, l, m] + klm2 = [k, ll, m] if abs(val3) > abs(maxval3): maxval3 = val3 - klm3 = [k, l, m] + klm3 = [k, ll, m] else: try: import phono3py._phono3py as phono3c @@ -614,29 +614,29 @@ def show_drift_fc3(fc3, primitive=None, name="fc3"): phono3c.transpose_compact_fc3( fc3, permutations, s2pp_map, p2s_map, nsym_list, 0 ) # dim[0] <--> dim[1] - for i, j, k, l, m in np.ndindex((num_patom, num_satom, 3, 3, 3)): - val1 = fc3[i, :, j, k, l, m].sum() + for i, j, k, ll, m in np.ndindex((num_patom, num_satom, 3, 3, 3)): + val1 = fc3[i, :, j, k, ll, m].sum() if abs(val1) > abs(maxval1): maxval1 = val1 - klm1 = [k, l, m] + klm1 = [k, ll, m] phono3c.transpose_compact_fc3( fc3, permutations, s2pp_map, p2s_map, nsym_list, 0 ) # dim[0] <--> dim[1] - for i, j, k, l, m in np.ndindex((num_patom, num_satom, 3, 3, 3)): - val2 = fc3[i, :, j, k, l, m].sum() - val3 = fc3[i, j, :, k, l, m].sum() + for i, j, k, ll, m in np.ndindex((num_patom, num_satom, 3, 3, 3)): + val2 = fc3[i, :, j, k, ll, m].sum() + val3 = fc3[i, j, :, k, ll, m].sum() if abs(val2) > abs(maxval2): maxval2 = val2 - klm2 = [k, l, m] + klm2 = [k, ll, m] if abs(val3) > abs(maxval3): maxval3 = val3 - klm3 = [k, l, m] - except ImportError: + klm3 = [k, ll, m] + except ImportError as exc: text = ( "Import error at phono3c.tranpose_compact_fc3. " "Corresponding python code is not implemented." ) - raise RuntimeError(text) + raise RuntimeError(text) from exc text = "Max drift of %s: " % name text += "%f (%s%s%s) " % (maxval1, "xyz"[klm1[0]], "xyz"[klm1[1]], "xyz"[klm1[2]]) diff --git a/phono3py/phonon3/gruneisen.py b/phono3py/phonon3/gruneisen.py index bc47a3f9..e001f998 100644 --- a/phono3py/phonon3/gruneisen.py +++ b/phono3py/phonon3/gruneisen.py @@ -270,9 +270,7 @@ def _write_band_yaml(self, filename): ): f.write("- nqpoint: %d\n" % len(path)) f.write(" phonon:\n") - for i, (q, d, g_at_q, freqs_at_q) in enumerate( - zip(path, distances, gs, fs) - ): # noqa E125 + for q, d, g_at_q, freqs_at_q in zip(path, distances, gs, fs): f.write(" - q-position: [ %10.7f, %10.7f, %10.7f ]\n" % tuple(q)) f.write(" distance: %10.7f\n" % d) f.write(" band:\n") @@ -415,7 +413,7 @@ def _get_dPhidu(self): for j in range(3): for k in range(3): for ll in range(3): - for m in range(3): + for _ in range(3): dPhidu[nu, pi, i, j, k, ll] = ( fc3[p2s[nu], pi, :, i, j, :] * Y[:, :, k, ll] ).sum() diff --git a/phono3py/phonon3/imag_self_energy.py b/phono3py/phonon3/imag_self_energy.py index 563ba6a6..6c06a86b 100644 --- a/phono3py/phonon3/imag_self_energy.py +++ b/phono3py/phonon3/imag_self_energy.py @@ -46,9 +46,9 @@ write_gamma_detail_to_hdf5, write_imag_self_energy_at_grid_point, ) +from phono3py.phonon.func import bose_einstein from phono3py.phonon3.interaction import Interaction from phono3py.phonon3.triplets import get_triplets_integration_weights -from phono3py.phonon.func import bose_einstein class ImagSelfEnergy: @@ -170,6 +170,7 @@ def get_imag_self_energy(self): "Use attribute, ImagSelfEnergy.imag_self_energy " "instead of ImagSelfEnergy.get_imag_self_energy().", DeprecationWarning, + stacklevel=2, ) return self.imag_self_energy @@ -200,6 +201,7 @@ def get_detailed_imag_self_energy(self): "Use attribute, ImagSelfEnergy.detailed_imag_self_energy " "instead of ImagSelfEnergy.get_detailed_imag_self_energy().", DeprecationWarning, + stacklevel=2, ) return self.detailed_imag_self_energy @@ -222,6 +224,7 @@ def get_unit_conversion_factor(self): "Use attribute, ImagSelfEnergy.unit_conversion_factor " "instead of ImagSelfEnergy.get_unit_conversion_factor().", DeprecationWarning, + stacklevel=2, ) return self.unit_conversion_factor @@ -268,6 +271,7 @@ def set_frequency_points(self, frequency_points): "Use attribute, ImagSelfEnergy.frequency_points " "instead of ImagSelfEnergy.set_frequency_points().", DeprecationWarning, + stacklevel=2, ) self.frequency_points = frequency_points @@ -289,6 +293,7 @@ def set_temperature(self, temperature): "Use attribute, ImagSelfEnergy.temperature " "instead of ImagSelfEnergy.set_temperature().", DeprecationWarning, + stacklevel=2, ) self.temperature = temperature diff --git a/phono3py/phonon3/interaction.py b/phono3py/phonon3/interaction.py index 575088e6..6461fc0c 100644 --- a/phono3py/phonon3/interaction.py +++ b/phono3py/phonon3/interaction.py @@ -45,15 +45,15 @@ from phonopy.structure.symmetry import Symmetry from phonopy.units import AMU, EV, Angstrom, Hbar, THz, VaspToTHz -from phono3py.phonon3.real_to_reciprocal import RealToReciprocal -from phono3py.phonon3.reciprocal_to_normal import ReciprocalToNormal -from phono3py.phonon3.triplets import get_nosym_triplets_at_q, get_triplets_at_q from phono3py.phonon.grid import ( BZGrid, get_grid_points_by_rotations, get_ir_grid_points, ) from phono3py.phonon.solver import run_phonon_solver_c, run_phonon_solver_py +from phono3py.phonon3.real_to_reciprocal import RealToReciprocal +from phono3py.phonon3.reciprocal_to_normal import ReciprocalToNormal +from phono3py.phonon3.triplets import get_nosym_triplets_at_q, get_triplets_at_q class Interaction: @@ -222,6 +222,7 @@ def get_interaction_strength(self): "Use attribute, Interaction.interaction_strength " "instead of Interaction.get_interaction_strength().", DeprecationWarning, + stacklevel=2, ) return self.interaction_strength @@ -243,6 +244,7 @@ def get_mesh_numbers(self): "Use attribute, Interaction.mesh_numbers " "instead of Interaction.get_mesh_numbers().", DeprecationWarning, + stacklevel=2, ) return self.mesh_numbers @@ -261,6 +263,7 @@ def get_fc3(self): warnings.warn( "Use attribute, Interaction.fc3 " "instead of Interaction.get_fc3().", DeprecationWarning, + stacklevel=2, ) return self.fc3 @@ -275,6 +278,7 @@ def get_dynamical_matrix(self): "Use attribute, Interaction.dynamical_matrix " "instead of Interaction.get_dynamical_matrix().", DeprecationWarning, + stacklevel=2, ) return self.dynamical_matrix @@ -289,6 +293,7 @@ def get_primitive(self): "Use attribute, Interaction.primitive " "instead of Interaction.get_primitive().", DeprecationWarning, + stacklevel=2, ) return self.primitive @@ -338,6 +343,7 @@ def get_band_indices(self): "Use attribute, Interaction.band_indices " "instead of Interaction.get_band_indices().", DeprecationWarning, + stacklevel=2, ) return self.band_indices @@ -373,6 +379,7 @@ def get_nac_q_direction(self): "Use attribute, Interaction.nac_q_direction " "instead of Interaction.get_nac_q_direction().", DeprecationWarning, + stacklevel=2, ) return self.nac_q_direction @@ -382,6 +389,7 @@ def set_nac_q_direction(self, nac_q_direction=None): "Use attribute, Interaction.nac_q_direction " "instead of Interaction.set_nac_q_direction().", DeprecationWarning, + stacklevel=2, ) self.nac_q_direction = nac_q_direction @@ -402,6 +410,7 @@ def get_zero_value_positions(self): "Use attribute, Interaction.zero_value_positions " "instead of Interaction.get_zero_value_positions().", DeprecationWarning, + stacklevel=2, ) return self.zero_value_positions @@ -436,6 +445,7 @@ def get_frequency_factor_to_THz(self): "Use attribute, Interaction.frequency_factor_to_THz ", "instead of Interaction.get_frequency_factor_to_THz().", DeprecationWarning, + stacklevel=2, ) return self.frequency_factor_to_THz @@ -450,6 +460,7 @@ def get_lapack_zheev_uplo(self): "Use attribute, Interaction.lapack_zheev_uplo " "instead of Interaction.get_lapack_zheev_uplo().", DeprecationWarning, + stacklevel=2, ) return self.lapack_zheev_uplo @@ -464,6 +475,7 @@ def get_cutoff_frequency(self): "Use attribute, Interaction.cutoff_frequency " "instead of Interaction.get_cutoff_frequency().", DeprecationWarning, + stacklevel=2, ) return self.cutoff_frequency @@ -520,6 +532,7 @@ def get_averaged_interaction(self): "Use attribute, Interaction.averaged_interaction " "instead of Interaction.get_averaged_interaction().", DeprecationWarning, + stacklevel=2, ) return self.averaged_interaction @@ -540,6 +553,7 @@ def get_unit_conversion_factor(self): "Use attribute, Interaction.unit_conversion_factor " "instead of Interaction.get_unit_conversion_factor().", DeprecationWarning, + stacklevel=2, ) return self.unit_conversion_factor @@ -554,6 +568,7 @@ def get_constant_averaged_interaction(self): "Use attribute, Interaction.constant_averaged_interaction " "instead of Interaction.get_constant_averaged_interaction().", DeprecationWarning, + stacklevel=2, ) return self.constant_averaged_interaction diff --git a/phono3py/phonon3/joint_dos.py b/phono3py/phonon3/joint_dos.py index 66d2e4da..2bb1add0 100644 --- a/phono3py/phonon3/joint_dos.py +++ b/phono3py/phonon3/joint_dos.py @@ -42,14 +42,14 @@ from phonopy.structure.cells import Primitive, Supercell from phonopy.units import VaspToTHz +from phono3py.phonon.func import bose_einstein +from phono3py.phonon.grid import BZGrid, get_grid_point_from_address +from phono3py.phonon.solver import run_phonon_solver_c from phono3py.phonon3.triplets import ( get_nosym_triplets_at_q, get_triplets_at_q, get_triplets_integration_weights, ) -from phono3py.phonon.func import bose_einstein -from phono3py.phonon.grid import BZGrid, get_grid_point_from_address -from phono3py.phonon.solver import run_phonon_solver_c class JointDos: @@ -129,7 +129,7 @@ def joint_dos(self): def get_joint_dos(self): """Return joint-density-of-states.""" - warnings.warn("Use attribute, joint_dos", DeprecationWarning) + warnings.warn("Use attribute, joint_dos", DeprecationWarning, stacklevel=2) return self.joint_dos @property @@ -143,7 +143,9 @@ def frequency_points(self, frequency_points): def get_frequency_points(self): """Return frequency points.""" - warnings.warn("Use attribute, frequency_points", DeprecationWarning) + warnings.warn( + "Use attribute, frequency_points", DeprecationWarning, stacklevel=2 + ) return self.frequency_points def get_phonons(self): @@ -157,7 +159,7 @@ def primitive(self) -> Primitive: def get_primitive(self): """Return primitive cell.""" - warnings.warn("Use attribute, primitive", DeprecationWarning) + warnings.warn("Use attribute, primitive", DeprecationWarning, stacklevel=2) return self.primitive @property @@ -172,7 +174,7 @@ def mesh_numbers(self): def get_mesh_numbers(self): """Return mesh numbers by three integer values.""" - warnings.warn("Use attribute, mesh_numbers", DeprecationWarning) + warnings.warn("Use attribute, mesh_numbers", DeprecationWarning, stacklevel=2) return self.mesh @property @@ -189,7 +191,9 @@ def nac_q_direction(self, nac_q_direction=None): def set_nac_q_direction(self, nac_q_direction=None): """Set q-direction for NAC.""" - warnings.warn("Use attribute, nac_q_direction", DeprecationWarning) + warnings.warn( + "Use attribute, nac_q_direction", DeprecationWarning, stacklevel=2 + ) self.nac_q_direction = nac_q_direction @property @@ -209,6 +213,7 @@ def set_sigma(self, sigma): warnings.warn( "Use attribute, JointDOS.sigma instead of JointDOS.set_sigma()", DeprecationWarning, + stacklevel=2, ) self.sigma = sigma @@ -401,14 +406,16 @@ def _run_occupation(self): def _run_py_with_g_at_temperature(self, jdos, i): g = self._g n = self._occupations - for k, l in list(np.ndindex(g.shape[3:])): + for k, ll in list(np.ndindex(g.shape[3:])): weights = np.where( - np.logical_or(n[:, 0, k] < 0, n[:, 1, l] < 0), 0, self._weights_at_q + np.logical_or(n[:, 0, k] < 0, n[:, 1, ll] < 0), 0, self._weights_at_q ) jdos[i, 1] += np.dot( - (n[:, 0, k] + n[:, 1, l] + 1) * g[0, :, i, k, l], weights + (n[:, 0, k] + n[:, 1, ll] + 1) * g[0, :, i, k, ll], weights + ) + jdos[i, 0] += np.dot( + (n[:, 0, k] - n[:, 1, ll]) * g[1, :, i, k, ll], weights ) - jdos[i, 0] += np.dot((n[:, 0, k] - n[:, 1, l]) * g[1, :, i, k, l], weights) def _init_dynamical_matrix(self): self._dm = get_dynamical_matrix( diff --git a/phono3py/phonon3/real_self_energy.py b/phono3py/phonon3/real_self_energy.py index a49ee932..c2c8d95b 100644 --- a/phono3py/phonon3/real_self_energy.py +++ b/phono3py/phonon3/real_self_energy.py @@ -44,9 +44,9 @@ write_real_self_energy_at_grid_point, write_real_self_energy_to_hdf5, ) +from phono3py.phonon.func import bose_einstein from phono3py.phonon3.imag_self_energy import get_frequency_points from phono3py.phonon3.interaction import Interaction -from phono3py.phonon.func import bose_einstein class RealSelfEnergy: @@ -247,8 +247,8 @@ def _run_c_with_band_indices(self): ) def _run_py_with_band_indices(self): - for i, (triplet, w, interaction) in enumerate( - zip(self._triplets_at_q, self._weights_at_q, self._pp_strength) + for triplet, w, interaction in zip( + self._triplets_at_q, self._weights_at_q, self._pp_strength ): freqs = self._frequencies[triplet] for j, bi in enumerate(self._band_indices): @@ -286,22 +286,22 @@ def _run_c_with_frequency_points(self): def _run_py_with_frequency_points(self): for k, fpoint in enumerate(self._frequency_points): - for i, (triplet, w, interaction) in enumerate( - zip(self._triplets_at_q, self._weights_at_q, self._pp_strength) + for triplet, w, interaction in zip( + self._triplets_at_q, self._weights_at_q, self._pp_strength ): freqs = self._frequencies[triplet] - for j, bi in enumerate(self._band_indices): + for j, _ in enumerate(self._band_indices): if self._temperature > 0: - self._real_self_energies[ - k, j - ] += self._real_self_energies_at_bands( - j, fpoint, freqs, interaction, w + self._real_self_energies[k, j] += ( + self._real_self_energies_at_bands( + j, fpoint, freqs, interaction, w + ) ) else: - self._real_self_energies[ - k, j - ] += self._real_self_energies_at_bands_0K( - j, fpoint, freqs, interaction, w + self._real_self_energies[k, j] += ( + self._real_self_energies_at_bands_0K( + j, fpoint, freqs, interaction, w + ) ) self._real_self_energies *= self._unit_conversion @@ -442,7 +442,7 @@ def _half_shift(self): fpoints = [] coef = self._df / np.pi i_zero = (len(self._im_part) - 1) // 2 - for i, im_part_at_i in enumerate(self._im_part): + for i, _ in enumerate(self._im_part): if i < i_zero: continue fpoint = self._all_frequency_points[i] + self._df / 2 diff --git a/phono3py/phonon3/spectral_function.py b/phono3py/phonon3/spectral_function.py index 4b0b9f99..95f4de33 100644 --- a/phono3py/phonon3/spectral_function.py +++ b/phono3py/phonon3/spectral_function.py @@ -214,7 +214,7 @@ def __init__( def run(self): """Calculate spectral function over grid points.""" - for gp_index in self: + for _ in self: pass def __iter__(self): @@ -354,7 +354,7 @@ def _run_spectral_function(self, i, grid_point, sigma_i): if self._log_level: print("* Spectral function") frequencies = self._pp.get_phonons()[0] - for j, temp in enumerate(self._temperatures): + for j, _ in enumerate(self._temperatures): for k, bi in enumerate(self._pp.band_indices): freq = frequencies[grid_point, bi] gammas = self._gammas[sigma_i, j, i, k] diff --git a/phono3py/phonon3/triplets.py b/phono3py/phonon3/triplets.py index 139240a6..aa75bcad 100644 --- a/phono3py/phonon3/triplets.py +++ b/phono3py/phonon3/triplets.py @@ -109,9 +109,10 @@ def get_triplets_at_q( ) triplets_at_q, weights = _get_BZ_triplets_at_q(grid_point, bz_grid, map_triplets) - assert ( - np.prod(bz_grid.D_diag) == weights.sum() - ), "Num grid points %d, sum of weight %d" % (np.prod(bz_grid.D_diag), weights.sum()) + assert np.prod(bz_grid.D_diag) == weights.sum(), ( + "Num grid points %d, sum of weight %d" + % (np.prod(bz_grid.D_diag), weights.sum()) + ) return triplets_at_q, weights, map_triplets, map_q diff --git a/phono3py/version.py b/phono3py/version.py index 6a98d983..a490a3d8 100644 --- a/phono3py/version.py +++ b/phono3py/version.py @@ -34,4 +34,4 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -__version__ = "3.1.1" +__version__ = "3.2.0" diff --git a/pyproject.toml b/pyproject.toml index df2609e6..52340c86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,19 @@ [build-system] requires = ["setuptools", "wheel", "numpy"] -#requires = ["setuptools", "wheel", "numpy", "oldest-supported-numpy"] -#build-backend = "setuptools.build_meta" -[tool.flake8] -max-line-length = 88 -extend-ignore = "E203,W503" - -[tool.black] +[tool.ruff] line-length = 88 +lint.select = [ + "F", # Flake8 + "B", # Black + "I", # isort + "E", # pycodestyle-errors + "D", # pydocstyle +] +lint.extend-ignore = [ + "D417", + "D100", +] -[tool.isort] -profile = "black" +[tool.ruff.lint.pydocstyle] +convention = "numpy" diff --git a/requirements.txt b/requirements.txt index b7fdc84d..146789f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ numpy >= 1.17.0 PyYAML >= 5.3 matplotlib >= 2.2.2 h5py >= 3.0 -phonopy >=2.24,<2.25 +phonopy >=2.25,<2.26 diff --git a/scripts/phono3py-coleigplot b/scripts/phono3py-coleigplot index bb60bcd8..22d1894b 100755 --- a/scripts/phono3py-coleigplot +++ b/scripts/phono3py-coleigplot @@ -11,7 +11,7 @@ epsilon = 1.0e-8 def get_options(): - # Arg-parser + """Parse command line options.""" parser = argparse.ArgumentParser(description="Plot collision matrix eigenvalues") parser.add_argument( "--temperature", @@ -33,6 +33,7 @@ def get_options(): def get_t_index(temperatures, args): + """Return temperature index.""" if len(temperatures) > 29: t_index = 30 else: @@ -45,6 +46,7 @@ def get_t_index(temperatures, args): def plot_one(ax, coleigs, temperatures, args): + """Plot collision matrix eigenvalues at one temperature.""" t_index = get_t_index(temperatures, args) coleigs_p = coleigs[t_index].copy() coleigs_n = coleigs[t_index].copy() @@ -56,6 +58,7 @@ def plot_one(ax, coleigs, temperatures, args): def plot_one_file(ax, args): + """Plot collision matrix eigenvalues at one temperature from file.""" filename = args.filenames[0] if os.path.isfile(filename): with h5py.File(filename, "r") as f: @@ -68,6 +71,7 @@ def plot_one_file(ax, args): def plot_more(ax, coleigs, temperatures, args): + """Update plot by collision matrix eigenvalues from one file.""" t_index = get_t_index(temperatures[0], args) y = [np.abs(v[t_index]) for v in coleigs] ax.semilogy(np.transpose(y), ".", markersize=5) @@ -75,6 +79,7 @@ def plot_more(ax, coleigs, temperatures, args): def plot_more_files(ax, args): + """Plot collision matrix eigenvalues from multiple files.""" temperatures = [] coleigs = [] for filename in args.filenames: @@ -89,6 +94,7 @@ def plot_more_files(ax, args): def plot(args: argparse.Namespace): + """Plot collision matrix eigenvalues.""" import matplotlib.pyplot as plt _, ax = plt.subplots() @@ -108,6 +114,7 @@ def plot(args: argparse.Namespace): def write_dat(args: argparse.Namespace): + """Write collision matrix eigenvalues to a file in gnuplot style.""" with open("coleigs.dat", "w") as w: for filename in args.filenames: with h5py.File(filename) as f: @@ -125,6 +132,7 @@ def write_dat(args: argparse.Namespace): def main(args: argparse.Namespace): + """Run phono3py-coleigplot.""" if args.savetxt: write_dat(args) else: diff --git a/scripts/phono3py-kdeplot b/scripts/phono3py-kdeplot index ca284900..184bbc88 100755 --- a/scripts/phono3py-kdeplot +++ b/scripts/phono3py-kdeplot @@ -12,6 +12,7 @@ epsilon = 1.0e-8 def collect_data(gamma, weights, frequencies, cutoff, max_freq): + """Collect data for making input of Gaussian-KDE.""" freqs = [] mode_prop = [] for w, freq, g in zip(weights, frequencies, gamma): @@ -37,8 +38,7 @@ def collect_data(gamma, weights, frequencies, cutoff, max_freq): def run_KDE(x, y, nbins, x_max=None, y_max=None, density_ratio=0.1): - """Running Gaussian-KDE by scipy""" - + """Run Gaussian-KDE by scipy.""" x_min = 0 if x_max is None: _x_max = np.rint(x.max() * 1.1) @@ -95,6 +95,7 @@ def plot( point_size=5, title=None, ): + """Plot lifetime distribution.""" xmax = np.max(x) ymax = np.max(y) x_cut = [] @@ -159,7 +160,7 @@ def plot( def get_options(): - # Arg-parser + """Parse command-line arguments.""" parser = argparse.ArgumentParser( description="Plot property density with gaussian KDE" ) @@ -206,7 +207,7 @@ def get_options(): "Number of bins in which data are assigned, " "i.e., determining resolution of plot" ), - ), + ) parser.add_argument( "--no-points", dest="no_points", action="store_true", help="Do not show points" ) @@ -246,9 +247,7 @@ def get_options(): def main(args): - # - # Matplotlib setting - # + """Run phono3py-kdeplot.""" import matplotlib matplotlib.use("Agg") diff --git a/setup.py b/setup.py index 04f071d8..1784f761 100644 --- a/setup.py +++ b/setup.py @@ -188,12 +188,12 @@ def _get_extensions(build_dir): found_libs[key] = line.split()[3].split(";") if f"{key} flags" in line and len(line.split()) > 3: found_flags[key] = line.split()[3].split(";") - for key, value in found_libs.items(): + for _, value in found_libs.items(): found_extra_link_args += value for element in value: if "libmkl" in element: use_mkl_lapacke = True - for key, value in found_flags.items(): + for _, value in found_flags.items(): found_extra_compile_args += value if use_mkl_lapacke: params["define_macros"].append(("MKL_LAPACKE", None)) @@ -326,7 +326,7 @@ def main(build_dir): "matplotlib>=2.2.2", "h5py>=3.0", "spglib>=2.0", - "phonopy>=2.24,<2.25", + "phonopy>=2.25,<2.26", ], provides=["phono3py"], scripts=scripts_phono3py, diff --git a/test/api/test_api_phono3py.py b/test/api/test_api_phono3py.py index cbed29eb..beb63677 100644 --- a/test/api/test_api_phono3py.py +++ b/test/api/test_api_phono3py.py @@ -5,6 +5,8 @@ from pathlib import Path import numpy as np +import pytest +from phonopy.interface.pypolymlp import PypolymlpParams from phono3py import Phono3py @@ -166,3 +168,41 @@ def test_type2_forces_energies_setter_Si(si_111_222_rd: Phono3py): ph3.phonon_forces = ph3_in.phonon_forces + 1 np.testing.assert_allclose(ph3_in.forces + 1, ph3.forces) np.testing.assert_allclose(ph3_in.phonon_forces + 1, ph3.phonon_forces) + + +def test_use_pypolymlp_mgo(mgo_222rd_444rd: Phono3py): + """Test use_pypolymlp in produce_fc3.""" + pytest.importorskip("pypolymlp") + + ph3_in = mgo_222rd_444rd + ph3 = Phono3py( + ph3_in.unitcell, + supercell_matrix=ph3_in.supercell_matrix, + phonon_supercell_matrix=ph3_in.phonon_supercell_matrix, + primitive_matrix=ph3_in.primitive_matrix, + log_level=2, + ) + ph3.mlp_dataset = { + "displacements": ph3_in.displacements[:10], + "forces": ph3_in.forces[:10], + "supercell_energies": ph3_in.supercell_energies[:10], + } + ph3.phonon_dataset = ph3_in.phonon_dataset + ph3.nac_params = ph3_in.nac_params + ph3.displacements = ph3_in.displacements[:100] + + atom_energies = {"Mg": -0.00896717, "O": -0.95743902} + params = PypolymlpParams(gtinv_maxl=(4, 4), atom_energies=atom_energies) + + ph3.generate_displacements(distance=0.001, is_plusminus=True) + ph3.develop_mlp(params=params) + ph3.evaluate_mlp() + ph3.produce_fc3(fc_calculator="symfc") + ph3.produce_fc2(fc_calculator="symfc") + + ph3.mesh_numbers = 30 + ph3.init_phph_interaction() + ph3.run_thermal_conductivity(temperatures=[300]) + assert ( + pytest.approx(63.0018546, abs=1e-1) == ph3.thermal_conductivity.kappa[0, 0, 0] + ) diff --git a/test/conductivity/test_kappa_RTA.py b/test/conductivity/test_kappa_RTA.py index 46643819..ea82c728 100644 --- a/test/conductivity/test_kappa_RTA.py +++ b/test/conductivity/test_kappa_RTA.py @@ -111,7 +111,6 @@ def test_kappa_RTA_si_nosym(si_pbesol: Phono3py, si_pbesol_nosym: Phono3py): def test_kappa_RTA_si_nomeshsym(si_pbesol: Phono3py, si_pbesol_nomeshsym: Phono3py): """Test RTA without considering mesh symmetry by Si.""" - if si_pbesol_nomeshsym._make_r0_average: ref_kappa_RTA_si_nomeshsym = [81.147, 81.147, 81.147, 0.000, 0.000, 0.000] else: diff --git a/test/conductivity/test_kappa_RTA_Wigner.py b/test/conductivity/test_kappa_RTA_Wigner.py index 37426406..b8d88190 100644 --- a/test/conductivity/test_kappa_RTA_Wigner.py +++ b/test/conductivity/test_kappa_RTA_Wigner.py @@ -92,7 +92,6 @@ def test_kappa_RTA_si_with_sigma_iso(si_pbesol: Phono3py): def test_kappa_RTA_si_nosym(si_pbesol: Phono3py, si_pbesol_nosym: Phono3py): """Test RTA without considering symmetry by Si.""" - if si_pbesol_nosym._make_r0_average: ref_kappa_P_RTA_si_nosym = [39.396, 39.222, 39.368, -0.096, -0.022, 0.026] ref_kappa_C_si_nosym = [0.009, 0.009, 0.009, 0.000, 0.000, 0.000] diff --git a/test/conftest.py b/test/conftest.py index 9b7403f1..61c2408d 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -170,21 +170,21 @@ def si_pbesol_111(request) -> Phono3py: @pytest.fixture(scope="session") -def si_pbesol_111_alm(request) -> Phono3py: +def si_pbesol_111_symfc(request) -> Phono3py: """Return Phono3py instance of Si 1x1x1. * with symmetry * full fc - * use alm if available on test side + * use symfc if available on test side """ - pytest.importorskip("alm") + pytest.importorskip("symfc") yaml_filename = cwd / "phono3py_params_Si111.yaml" enable_v2 = request.config.getoption("--v2") return phono3py.load( yaml_filename, - fc_calculator="alm", + fc_calculator="symfc", make_r0_average=not enable_v2, log_level=1, ) @@ -208,7 +208,7 @@ def si_pbesol_111_222_fd(request) -> Phono3py: * with symmetry * full fc - * use alm if available on test side + * use symfc if available on test side """ yaml_filename = cwd / "phono3py_params_Si-111-222.yaml" @@ -221,63 +221,63 @@ def si_pbesol_111_222_fd(request) -> Phono3py: @pytest.fixture(scope="session") -def si_pbesol_111_222_alm(request) -> Phono3py: +def si_pbesol_111_222_symfc(request) -> Phono3py: """Return Phono3py instance of Si 1x1x1. * with symmetry * full fc - * use alm if available on test side + * use symfc if available on test side """ - pytest.importorskip("alm") + pytest.importorskip("symfc") yaml_filename = cwd / "phono3py_params_Si-111-222.yaml" enable_v2 = request.config.getoption("--v2") return phono3py.load( yaml_filename, - fc_calculator="alm", + fc_calculator="symfc", make_r0_average=not enable_v2, log_level=1, ) @pytest.fixture(scope="session") -def si_pbesol_111_222_alm_fd(request) -> Phono3py: +def si_pbesol_111_222_symfc_fd(request) -> Phono3py: """Return Phono3py instance of Si 1x1x1. * with symmetry * full fc - * use alm for fc2 if available on test side + * use symfc for fc2 if available on test side """ - pytest.importorskip("alm") + pytest.importorskip("symfc") yaml_filename = cwd / "phono3py_params_Si-111-222.yaml" enable_v2 = request.config.getoption("--v2") return phono3py.load( yaml_filename, - fc_calculator="alm|", + fc_calculator="symfc|", make_r0_average=not enable_v2, log_level=1, ) @pytest.fixture(scope="session") -def si_pbesol_111_222_fd_alm(request) -> Phono3py: +def si_pbesol_111_222_fd_symfc(request) -> Phono3py: """Return Phono3py instance of Si 1x1x1. * with symmetry * full fc - * use alm for fc3 if available on test side + * use symfc for fc3 if available on test side """ - pytest.importorskip("alm") + pytest.importorskip("symfc") yaml_filename = cwd / "phono3py_params_Si-111-222.yaml" enable_v2 = request.config.getoption("--v2") return phono3py.load( yaml_filename, - fc_calculator="|alm", + fc_calculator="|symfc", make_r0_average=not enable_v2, log_level=1, ) @@ -415,7 +415,6 @@ def nacl_pbe_cutoff_fc3(request) -> Phono3py: count += 1 ph3.dataset = dataset ph3.produce_fc3() - # ph3.produce_fc3(symmetrize_fc3r=True) return ph3 @@ -497,6 +496,17 @@ def si_111_222_rd() -> Phono3py: return phono3py.load(yaml_filename, produce_fc=False, log_level=1) +@pytest.fixture(scope="session") +def mgo_222rd_444rd() -> Phono3py: + """Return Phono3py class instance of MgO-2x2x2-4x4x4 RD-RD. + + 4 and 400 supercells for fc2 and fc3, respectively. + + """ + yaml_filename = cwd / "phono3py_params_MgO-222rd-444rd.yaml.xz" + return phono3py.load(yaml_filename, produce_fc=False, log_level=1) + + @pytest.fixture(scope="session") def ph_nacl() -> Phonopy: """Return Phonopy class instance of NaCl 2x2x2.""" diff --git a/test/cui/test_phono3py_load_script.py b/test/cui/test_phono3py_load_script.py index e80627dd..e6987194 100644 --- a/test/cui/test_phono3py_load_script.py +++ b/test/cui/test_phono3py_load_script.py @@ -45,6 +45,7 @@ def __iter__(self): return (getattr(self, field.name) for field in fields(self)) def __contains__(self, item): + """Implement in operator.""" return item in (field.name for field in fields(self)) @@ -65,12 +66,10 @@ def test_phono3py_load(): file_path.unlink() -@pytest.mark.parametrize("fc_calculator,exit_code", [(None, 1), ("alm", 0)]) +@pytest.mark.parametrize("fc_calculator,exit_code", [(None, 1), ("symfc", 0)]) def test_phono3py_load_with_typeII_dataset(fc_calculator, exit_code): """Test phono3py-load script with typeII dataset.""" - # Check sys.exit(0) - if fc_calculator == "alm": - pytest.importorskip("alm") + pytest.importorskip("symfc") argparse_control = _get_phono3py_load_args( cwd / ".." / "phono3py_params-Si111-rd.yaml.xz", fc_calculator=fc_calculator ) diff --git a/test/other/test_kaccum.py b/test/other/test_kaccum.py index 503d78af..becd2b13 100644 --- a/test/other/test_kaccum.py +++ b/test/other/test_kaccum.py @@ -298,6 +298,7 @@ def test_GammaDOSsmearing(nacl_pbe: Phono3py): def test_run_prop_dos(si_pbesol: Phono3py): + """Test of run_prop_dos.""" ph3 = si_pbesol ph3.mesh_numbers = [7, 7, 7] ph3.init_phph_interaction() diff --git a/test/phono3py_params_MgO-222rd-444rd.yaml.xz b/test/phono3py_params_MgO-222rd-444rd.yaml.xz new file mode 100644 index 00000000..59148cb0 Binary files /dev/null and b/test/phono3py_params_MgO-222rd-444rd.yaml.xz differ diff --git a/test/phonon/test_grid.py b/test/phonon/test_grid.py index 23905353..a770d047 100644 --- a/test/phonon/test_grid.py +++ b/test/phonon/test_grid.py @@ -2470,7 +2470,6 @@ def test_GridMatrix_with_supercell_symmetry(ph_nacl: Phonopy): np.testing.assert_array_equal(gm.D_diag, [4, 4, 4]) -@pytest.mark.filterwarnings("error") def test_GridMatrix_with_supercell_symmetry_grg_false(ph_nacl: Phonopy): """Test of GridMatrix with supercell symmetry. diff --git a/test/phonon3/test_displacements.py b/test/phonon3/test_displacements.py index 992772e0..91e2e1e8 100644 --- a/test/phonon3/test_displacements.py +++ b/test/phonon3/test_displacements.py @@ -3,6 +3,7 @@ import numpy as np import phono3py +from phono3py import Phono3py from phono3py.phonon3.displacement_fc3 import get_smallest_vector_of_atom_pair distances_NaCl = [ @@ -97,9 +98,13 @@ def test_duplicates_agno2(agno2_cell): np.testing.assert_equal(duplicates_ref, ph3.dataset["duplicates"]) -def test_nacl_pbe(nacl_pbe): +def test_nacl_pbe(nacl_pbe: Phono3py): """Test generated displacements and duplicates.""" - ph3 = nacl_pbe + ph3 = Phono3py( + nacl_pbe.unitcell, + supercell_matrix=nacl_pbe.supercell_matrix, + primitive_matrix=nacl_pbe.primitive_matrix, + ) ph3.generate_displacements() duplicates_ref = [[77, 41]] ph3.dataset["duplicates"] diff --git a/test/phonon3/test_fc3.py b/test/phonon3/test_fc3.py index 65ba7533..5f358277 100644 --- a/test/phonon3/test_fc3.py +++ b/test/phonon3/test_fc3.py @@ -127,9 +127,9 @@ def test_phonon_smat_fd(si_pbesol_111_222_fd: Phono3py): np.testing.assert_allclose(ph.fc2[0, 33], fc2_ref, atol=1e-6, rtol=0) -def test_phonon_smat_alm(si_pbesol_111_222_alm: Phono3py): - """Test phonon smat and ALM with Si PBEsol 1x1x1-2x2x2.""" - ph = si_pbesol_111_222_alm +def test_phonon_smat_symfc(si_pbesol_111_222_symfc: Phono3py): + """Test phonon smat and symfc with Si PBEsol 1x1x1-2x2x2.""" + ph = si_pbesol_111_222_symfc fc3_ref = [ [ [0.10725082, 0.0, 0.0], @@ -156,9 +156,9 @@ def test_phonon_smat_alm(si_pbesol_111_222_alm: Phono3py): np.testing.assert_allclose(ph.fc2[0, 33], fc2_ref, atol=1e-6, rtol=0) -def test_phonon_smat_alm_fd(si_pbesol_111_222_alm_fd: Phono3py): - """Test phonon smat and ALM (fc2) FD (fc3) with Si PBEsol 1x1x1-2x2x2.""" - ph = si_pbesol_111_222_alm_fd +def test_phonon_smat_symfc_fd(si_pbesol_111_222_symfc_fd: Phono3py): + """Test phonon smat and symfc (fc2) FD (fc3) with Si PBEsol 1x1x1-2x2x2.""" + ph = si_pbesol_111_222_symfc_fd fc3_ref = [ [ [1.07250822e-01, 1.86302073e-17, -4.26452855e-18], @@ -185,9 +185,9 @@ def test_phonon_smat_alm_fd(si_pbesol_111_222_alm_fd: Phono3py): np.testing.assert_allclose(ph.fc2[0, 33], fc2_ref, atol=1e-6, rtol=0) -def test_phonon_smat_fd_alm(si_pbesol_111_222_fd_alm: Phono3py): - """Test phonon smat and FD (fc2) ALM (fc3) with Si PBEsol 1x1x1-2x2x2.""" - ph = si_pbesol_111_222_fd_alm +def test_phonon_smat_fd_symfc(si_pbesol_111_222_fd_symfc: Phono3py): + """Test phonon smat and FD (fc2) symfc (fc3) with Si PBEsol 1x1x1-2x2x2.""" + ph = si_pbesol_111_222_fd_symfc fc3_ref = [ [ [0.10725082, 0.0, 0.0], @@ -215,14 +215,14 @@ def test_phonon_smat_fd_alm(si_pbesol_111_222_fd_alm: Phono3py): def test_phonon_smat_alm_cutoff(si_pbesol_111_222_alm_cutoff: Phono3py): - """Test phonon smat and ALM with Si PBEsol 1x1x1-2x2x2 cutoff.""" + """Test phonon smat and alm with Si PBEsol 1x1x1-2x2x2 cutoff.""" ph = si_pbesol_111_222_alm_cutoff np.testing.assert_allclose(ph.fc3[0, 1, 7], 0, atol=1e-6, rtol=0) np.testing.assert_allclose(ph.fc2[0, 33], 0, atol=1e-6, rtol=0) def test_phonon_smat_alm_cutoff_fc2(si_pbesol_111_222_alm_cutoff_fc2: Phono3py): - """Test phonon smat and ALM with Si PBEsol 1x1x1-2x2x2 cutoff fc2.""" + """Test phonon smat and alm with Si PBEsol 1x1x1-2x2x2 cutoff fc2.""" ph = si_pbesol_111_222_alm_cutoff_fc2 fc3_ref = [ [ @@ -246,7 +246,7 @@ def test_phonon_smat_alm_cutoff_fc2(si_pbesol_111_222_alm_cutoff_fc2: Phono3py): def test_phonon_smat_alm_cutoff_fc3(si_pbesol_111_222_alm_cutoff_fc3: Phono3py): - """Test phonon smat and ALM with Si PBEsol 1x1x1-2x2x2 cutoff fc3.""" + """Test phonon smat and alm with Si PBEsol 1x1x1-2x2x2 cutoff fc3.""" ph = si_pbesol_111_222_alm_cutoff_fc3 np.testing.assert_allclose(ph.fc3[0, 1, 7], 0, atol=1e-6, rtol=0) fc2_ref = [ @@ -293,10 +293,9 @@ def test_fc3_lapacke_solver(si_pbesol_111: Phono3py, pinv_solver: str): np.testing.assert_allclose(fc3[0, 1, 7], fc3_ref, atol=1e-8, rtol=0) -# @pytest.mark.skipif(not FC_CALCULATOR_ALM_AVAILABLE, reason="not found ALM package") -def test_fc3_alm(si_pbesol_111_alm: Phono3py): - """Test fc3 with Si PBEsol 1x1x1 calcualted using ALM.""" - ph = si_pbesol_111_alm +def test_fc3_symfc(si_pbesol_111_symfc: Phono3py): + """Test fc3 with Si PBEsol 1x1x1 calcualted using symfc.""" + ph = si_pbesol_111_symfc fc3_ref = [ [ [0.10725082233069763, 0.0, 0.0], diff --git a/test/phonon3/test_joint_dos.py b/test/phonon3/test_joint_dos.py index cb48cb31..9167f236 100644 --- a/test/phonon3/test_joint_dos.py +++ b/test/phonon3/test_joint_dos.py @@ -5,8 +5,8 @@ from phono3py import Phono3py from phono3py.api_jointdos import Phono3pyJointDos -from phono3py.phonon3.joint_dos import JointDos from phono3py.phonon.grid import BZGrid +from phono3py.phonon3.joint_dos import JointDos si_freq_points = [ 0.0000000, diff --git a/test/phonon3/test_triplets.py b/test/phonon3/test_triplets.py index dceb62a8..ef5ddf5d 100644 --- a/test/phonon3/test_triplets.py +++ b/test/phonon3/test_triplets.py @@ -7,12 +7,12 @@ from phonopy.structure.symmetry import Symmetry from phono3py import Phono3py +from phono3py.phonon.grid import BZGrid, get_grid_point_from_address from phono3py.phonon3.triplets import ( _get_BZ_triplets_at_q, _get_triplets_reciprocal_mesh_at_q, get_triplets_at_q, ) -from phono3py.phonon.grid import BZGrid, get_grid_point_from_address def test_get_triplets_at_q_type1(si_pbesol_111): diff --git a/test/sscha/test_sscha.py b/test/sscha/test_sscha.py index 88388cc9..a1d80eaf 100644 --- a/test/sscha/test_sscha.py +++ b/test/sscha/test_sscha.py @@ -13,11 +13,6 @@ get_sscha_matrices, ) -try: - ModuleNotFoundError -except NameError: - ModuleNotFoundError = ImportError - si_pbesol_upsilon0_0 = [[3.849187e02, 0, 0], [0, 3.849187e02, 0], [0, 0, 3.849187e02]] si_pbesol_upsilon1_34 = [ [1.886404, -1.549705, -1.126055], @@ -277,13 +272,12 @@ def _test_disp_corr_matrix(ph3): def test_fc3(si_pbesol_iterha_111): """Test of ThirdOrderFC class.""" - try: - import alm # noqa F401 - except ModuleNotFoundError: - pytest.skip("Skip this test because ALM module was not found.") + pytest.importorskip("symfc") ph = si_pbesol_iterha_111 - ph.produce_force_constants(calculate_full_force_constants=True, fc_calculator="alm") + ph.produce_force_constants( + calculate_full_force_constants=True, fc_calculator="symfc" + ) supercell_phonon = SupercellPhonon( ph.supercell, ph.force_constants,