Compare commits

...

138 Commits

Author SHA1 Message Date
Atsushi Togo ff95c4dd2e
Merge pull request #420 from phonopy/refactor
Merge CUI for pypolymlp to phonopy
2025-07-29 16:22:34 +09:00
Atsushi Togo 3ec495f05a Merge CUI for pypolymlp to phonopy 2025-07-29 16:11:59 +09:00
Atsushi Togo de2e6d3025
Merge pull request #419 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-07-29 09:21:58 +09:00
pre-commit-ci[bot] 2195be17f9
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.4 → v0.12.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.4...v0.12.5)
2025-07-28 20:57:00 +00:00
Atsushi Togo 499ba37b43 Update document on pypolymlp 2025-07-26 14:59:11 +09:00
Atsushi Togo 5e254fa516 Update document on pypolymlp 2025-07-26 14:52:32 +09:00
Atsushi Togo 8e7bc01229 Merge branch 'develop' 2025-07-22 16:15:29 +09:00
Atsushi Togo a12bb6becd
Merge pull request #418 from phonopy/release
Release v3.18.0
2025-07-22 16:06:34 +09:00
Atsushi Togo 21d4d86698 Avoid redundant calculation of fc2 2025-07-22 15:57:20 +09:00
Atsushi Togo b7a71c2883 Minor fix for reading polymlp.yaml 2025-07-22 15:34:55 +09:00
Atsushi Togo 006e63d799 Minor fix for reading polymlp.yaml 2025-07-22 15:18:40 +09:00
Atsushi Togo 5a14780e6b Set version 3.18.0 2025-07-22 15:13:54 +09:00
Atsushi Togo e736838528 Merge branch 'develop' of github.com:phonopy/phono3py into develop 2025-07-22 15:12:07 +09:00
Atsushi Togo 4181111479 Prevent phono3py-kaccum from reading POSCAR 2025-07-22 15:11:57 +09:00
Atsushi Togo baf34031eb
Merge pull request #416 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-07-22 14:23:52 +09:00
pre-commit-ci[bot] 69d6fcec75
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.3 → v0.12.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.3...v0.12.4)
2025-07-21 21:04:31 +00:00
Atsushi Togo 9ddbbbfa56 Very minor update 2025-07-21 17:21:47 +09:00
Atsushi Togo 12dc119f56 Update phonopy version dependency 2025-07-21 12:05:13 +09:00
Atsushi Togo e26f3ecbbd
Merge pull request #415 from phonopy/relax
Implement relaxation using pypolymlp in CUI
2025-07-20 19:28:08 +09:00
Atsushi Togo 3564186adc Implement relaxation using pypolymlp in CUI 2025-07-20 18:42:07 +09:00
Atsushi Togo cbcf9e84e5
Merge pull request #414 from phonopy/refactor
Refactoring
2025-07-19 19:15:26 +09:00
Atsushi Togo 9eb09dd01e Minor refactoring 2025-07-19 18:25:13 +09:00
Atsushi Togo 7dc202dde7 Further refactoring 2025-07-19 18:19:29 +09:00
Atsushi Togo dd9f626bb8 Set __all__ for imports in __init__.py 2025-07-19 17:22:08 +09:00
Atsushi Togo 630d09f174 Add from __future__ import annotations 2025-07-19 17:17:05 +09:00
Atsushi Togo 8d9e72c130 Minor typehint update 2025-07-19 17:13:30 +09:00
Atsushi Togo 096269a8b6 Refactoring 2025-07-19 17:11:28 +09:00
Atsushi Togo 35dd083b3a
Merge pull request #413 from phonopy/refactoring
Refactoring and adding typehints
2025-07-18 15:02:04 +09:00
Atsushi Togo 6eceedc3bb Add from __future__ import annotations 2025-07-18 14:53:37 +09:00
Atsushi Togo d67c4d81db Add from __future__ import annotations 2025-07-18 14:48:36 +09:00
Atsushi Togo 818ea508c8 Refactoring and adding typehints 2025-07-18 14:44:39 +09:00
Atsushi Togo 8568191817
Merge pull request #412 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-07-15 08:16:39 +09:00
pre-commit-ci[bot] 6a1239408a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.3)
2025-07-14 20:49:53 +00:00
Atsushi Togo fdc6aece55
Merge pull request #411 from phonopy/symmetrize-api
Update Phono3py.symmetrize_fc3
2025-07-08 19:22:07 +09:00
Atsushi Togo 7effdf6c26 Update test 2025-07-08 18:32:23 +09:00
Atsushi Togo cfcd64fb9f Add test for Phono3py.symmetrize_fc3 and symmetrize_fc2 2025-07-08 18:12:43 +09:00
Atsushi Togo 8488ed0424 Update Phono3py.symmetrize_fc3 2025-07-08 16:08:41 +09:00
Atsushi Togo c4c6d14ca2
Merge pull request #410 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-07-08 11:32:25 +09:00
pre-commit-ci[bot] 9825b8c171
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2)
2025-07-07 21:31:08 +00:00
Atsushi Togo 7e6e2ce628 Merge branch 'develop' 2025-07-05 10:46:58 +09:00
Atsushi Togo 47482e4116
Merge pull request #409 from phonopy/release
Set version 3.17.1
2025-07-05 10:45:15 +09:00
Atsushi Togo 9951f5bfaf Set version 3.17.1 2025-07-05 10:34:26 +09:00
Atsushi Togo 48105c5f52
Merge pull request #408 from phonopy/fix-407
Fix issue 407
2025-07-04 16:01:15 +09:00
Atsushi Togo 617a6e9162 Fix issue 407 2025-07-04 15:52:21 +09:00
Atsushi Togo 8eeb09d934
Merge pull request #406 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-07-01 10:57:37 +09:00
pre-commit-ci[bot] b4fa5458d5
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.1)
2025-06-30 20:38:25 +00:00
Atsushi Togo d41603560d
Merge pull request #405 from phonopy/symmetrize-fc-methods
Add symmetrize_fc2 and symmetrize_fc3 in Phono3py class
2025-06-29 16:06:34 +09:00
Atsushi Togo 260e4a5e75 Add symmetrize_fc2 and symmetrize_fc3 in Phono3py class 2025-06-29 15:56:55 +09:00
Atsushi Togo 93a5e0ed3c Minor update of typehint 2025-06-27 16:12:18 +09:00
Atsushi Togo ad9cdcc64b Merge branch 'develop' 2025-06-26 19:25:00 +09:00
Atsushi Togo ea6b96d594
Merge pull request #404 from phonopy/release
Set version 3.17.0
2025-06-26 19:05:42 +09:00
Atsushi Togo dff1e08232 Set version 3.17.0 2025-06-26 18:56:55 +09:00
Atsushi Togo a001c65e6c Update dependency of phonopy version 2025-06-26 10:58:57 +09:00
Atsushi Togo 0269254fb9
Merge pull request #403 from phonopy/run-test
Commit to run tests on github
2025-06-25 15:38:59 +09:00
Atsushi Togo b42f1c374c Commit to run tests on github 2025-06-25 14:43:21 +09:00
Atsushi Togo 9441266bbb Call symfc project for FD when phonopy-load and fc-calculator is blank 2025-06-25 14:41:26 +09:00
Atsushi Togo ac2653147e
Merge pull request #402 from phonopy/symfc-projector
Use symfc-projector for FD when phono3py-load
2025-06-24 15:38:18 +09:00
Atsushi Togo 22fa0e1862 Use symfc-projector for FD when phono3py-load 2025-06-24 15:25:26 +09:00
Atsushi Togo 3ee42f3b81
Merge pull request #400 from phonopy/merge-nac-cui-routines
Merge cui traditional and phono3py-load rotines
2025-06-23 16:55:13 +09:00
Atsushi Togo 072e195940 Adjustment to let both CUI and phno3py.load work nicely 2025-06-23 16:25:31 +09:00
Atsushi Togo 7f44ce3594 Merge produe fc routines in CUI 2025-06-23 15:04:08 +09:00
Atsushi Togo bb1012784d Merge CUI routines to handle NAC 2025-06-23 11:08:27 +09:00
Atsushi Togo d4dcd0465e
Merge pull request #399 from phonopy/cell-info
Update following the change of collect_cell_info in phonopy
2025-06-23 10:50:18 +09:00
Atsushi Togo 1a243e2316 Add from __future__ import annotations 2025-06-22 17:36:34 +09:00
Atsushi Togo 166f012147 Update following the change of collect_cell_info in phonopy 2025-06-22 17:33:22 +09:00
Atsushi Togo 1426752ad8
Merge pull request #398 from phonopy/refactor-settings-py
Refactor settings.py following that of phonopy
2025-06-17 20:37:11 +09:00
Atsushi Togo 9b88998e3a Add from __future__ import annotations 2025-06-17 20:07:42 +09:00
Atsushi Togo 1441cbc155 Refactor settings.py following that of phonopy 2025-06-17 20:03:31 +09:00
Atsushi Togo 36ae0c1ffb Merge branch 'develop' of github.com:phonopy/phono3py into develop 2025-06-17 15:59:35 +09:00
Atsushi Togo 518b6feebf Update phonopy version dependency 2025-06-17 15:59:25 +09:00
Atsushi Togo 685d5f5e55
Merge pull request #397 from phonopy/tune-rec2normal
Performance optimization of reciprocal_to_normal in C
2025-06-14 18:24:21 +09:00
Atsushi Togo 55c334e6c2 Minor code clean-up 2025-06-14 18:15:03 +09:00
Atsushi Togo 26cac3aef5 Performance optimization of reciprocal_to_normal in C 2025-06-14 18:07:46 +09:00
Atsushi Togo 200e1b92a9
Merge pull request #396 from phonopy/number-estimation-factor
Add number_estimation_factor parameter for rd displacement generation
2025-06-13 14:01:49 +09:00
Atsushi Togo 788fab3cdf Avoid writing files in running tests 2025-06-13 13:43:04 +09:00
Atsushi Togo e02e7c9921 Update test_random_disps_agno2 2025-06-13 13:38:22 +09:00
Atsushi Togo d601796e05 Add number_estimation_factor parameter for rd displacement generation 2025-06-13 12:57:02 +09:00
Atsushi Togo bbd055d9b7 Merge branch 'develop' 2025-06-12 16:22:01 +09:00
Atsushi Togo dd60053a74
Merge pull request #395 from phonopy/release
Set version 3.16.0
2025-06-12 11:57:54 +09:00
Atsushi Togo c3a755ecb0 Set version 3.16.0 2025-06-12 11:31:02 +09:00
Atsushi Togo 1ec0116c10
Merge pull request #393 from phonopy/tune-recip2normal
Introduce memorization in reciprocal_to_normal for optimization
2025-06-11 12:57:59 +09:00
Atsushi Togo e4f37dba71 Zero clear of temporary array 2025-06-11 12:47:58 +09:00
Atsushi Togo cedd2477d8 Remove unused variables 2025-06-11 09:59:15 +09:00
Atsushi Togo 29cda14982 Replace memset 2025-06-11 09:56:42 +09:00
Atsushi Togo 5f75510927 Introduce memorization in eciprocal_to_normal for performance optimization 2025-06-11 09:44:34 +09:00
Atsushi Togo a3b8ad3dbd
Merge pull request #392 from phonopy/fix-distance-in-BZ
Fix distance calculation in BZ
2025-06-10 18:41:28 +09:00
Atsushi Togo 71bb34aa4a Fix distance calculation in BZ 2025-06-10 18:32:13 +09:00
Atsushi Togo ea1d083056
Merge pull request #391 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-06-10 10:28:21 +09:00
pre-commit-ci[bot] 13c83d6d6a
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.12 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.12...v0.11.13)
2025-06-09 20:42:14 +00:00
Atsushi Togo 2d30f9baf0 Fix comment in reciprocal_to_normal.c 2025-06-09 16:05:46 +09:00
Atsushi Togo 8bf63c030c
Merge pull request #390 from phonopy/fc3-cutoff-in-fdf5
Add fc3_cutoff entry in fc3.hdf5
2025-06-06 18:23:30 +09:00
Atsushi Togo 3eab5fbcbe Add fc3_cutoff entry in fc3.hdf5 2025-06-06 18:14:19 +09:00
Atsushi Togo 2e67d46379
Merge pull request #389 from phonopy/fc3-non-zero-indices
Run ph-ph interaction calculation with fc3-nonzero-indices
2025-06-06 13:21:17 +09:00
Atsushi Togo cbd410ac4c Run ph-ph interaction calculation with fc3-nonzero-indices 2025-06-06 13:05:44 +09:00
Atsushi Togo 748a9692aa
Merge pull request #388 from phonopy/refactor-read-write-fc3-fc2
Refactor reading and writing fc2 and fc3
2025-06-05 18:15:17 +09:00
Atsushi Togo 48f52c762c Refactor reading and writing fc2 and fc3 2025-06-05 18:07:18 +09:00
Atsushi Togo 83a96ebce6 Minor refactoring 2025-06-05 17:13:45 +09:00
Atsushi Togo ca30fcc101 Minor refactoring 2025-06-05 16:56:37 +09:00
Atsushi Togo c4a70293d2
Merge pull request #387 from phonopy/fc3-nonzero-indices
Write fc3_nonzero_indices in fc3.hdf5
2025-06-05 16:34:52 +09:00
Atsushi Togo 731ea60afe Update test_phono3py_load_with_typeII_dataset 2025-06-05 16:27:14 +09:00
Atsushi Togo 2732a1a0f0 Write fc3_nonzero_indices in fc3.hdf5 2025-06-05 16:23:26 +09:00
Atsushi Togo d202f2c27b
Merge pull request #386 from phonopy/fc3-nonzero-indices
Add Phono3py.fc3_nonzero_indices
2025-06-04 17:50:16 +09:00
Atsushi Togo 8d0db5c396 Add Phono3py.fc3_nonzero_indices 2025-06-04 17:40:45 +09:00
Atsushi Togo 03a26d1b6d Add self._fc3_nonzero_elems in API 2025-06-03 18:09:34 +09:00
Atsushi Togo 6a1a1a984a
Merge pull request #385 from phonopy/refactor-conductivity
Refactor conductivity
2025-06-03 15:40:42 +09:00
Atsushi Togo 238c537f31 [refactoring of conductivity] Wigner LBTE 2025-06-03 15:28:28 +09:00
Atsushi Togo 300e08d1d3 [refactoring of conductivity] Wigner RTA 2025-06-03 14:42:57 +09:00
Atsushi Togo 28dc226ee9
Merge pull request #384 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-06-03 10:51:48 +09:00
pre-commit-ci[bot] 91c9c5c619
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.11 → v0.11.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.11...v0.11.12)
2025-06-02 21:00:32 +00:00
Atsushi Togo 8be1b4cf95 [refactoring of conductivity] LBTE full-colmat 2025-06-02 17:00:09 +09:00
Atsushi Togo 18dac50c78 [refactoring of conductivity] LBTE 2025-06-02 08:48:54 +09:00
Atsushi Togo bcc4227415 [refactoring of conductivity] RTA 2025-06-02 08:26:59 +09:00
Atsushi Togo 39f4d09e24
Merge pull request #383 from phonopy/typehints
Improve type hints
2025-06-01 18:13:46 +09:00
Atsushi Togo 7122307413 Minor fix for type annotation 2025-06-01 09:20:15 +09:00
Atsushi Togo d0101dbb4b Improve type hints 2025-05-30 17:30:24 +09:00
Atsushi Togo 91ae3b89fc
Merge pull request #382 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-05-27 07:21:28 +09:00
pre-commit-ci[bot] 8a606ebf13
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.10 → v0.11.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.10...v0.11.11)
2025-05-26 20:43:46 +00:00
Atsushi Togo d86b3ed24d Update document to read pp-*.hdf5 and handling triplets 2025-05-20 19:30:21 +09:00
Atsushi Togo 9fc5186267 Update document to read pp-*.hdf5 and handling triplets 2025-05-20 19:20:24 +09:00
Atsushi Togo 16957da192
Merge pull request #381 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-05-20 07:24:37 +09:00
pre-commit-ci[bot] d8b2a2cd83
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.9 → v0.11.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.9...v0.11.10)
2025-05-19 20:45:28 +00:00
Atsushi Togo 8d0785c191 Minor fix 2025-05-17 18:56:51 +09:00
Atsushi Togo bacf5c2c5f Minor fix 2025-05-17 18:06:11 +09:00
Atsushi Togo 8ed85bff78
Merge pull request #379 from phonopy/symfc-memsize
Refine behaviour of symfc-memsize
2025-05-17 11:22:16 +09:00
Atsushi Togo e10b20260a Refine behaviour of symfc-memsize 2025-05-16 22:35:48 +09:00
Atsushi Togo 4a735f317e
Merge pull request #378 from phonopy/option-memsize
Add CUI option for rd=auto along with symfc memsize
2025-05-16 13:19:09 +09:00
Atsushi Togo cc0879dde0 Add tests 2025-05-16 08:54:22 +09:00
Atsushi Togo 426a193729 Support memsize displacement generation for MLP 2025-05-16 06:44:20 +09:00
Atsushi Togo f465e6ebab Refactoring of handling fc_calculator and cutoff input strings 2025-05-15 12:18:25 +09:00
Atsushi Togo e350a93a79 Add CUI option for rd=auto along with symfc memsize 2025-05-15 08:41:12 +09:00
Atsushi Togo 2a6239979c
Merge pull request #377 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-05-13 09:17:29 +09:00
pre-commit-ci[bot] d9e1a6c834
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9)
2025-05-12 20:38:08 +00:00
Atsushi Togo 8ccb13b081
Merge pull request #376 from phonopy/symfc
Update symfc wrapper to implement cutoff estimation by memory limit
2025-05-11 18:12:54 +09:00
Atsushi Togo e078306390 Update symfc wrapper to implement cutoff estimation by memory limit 2025-05-11 18:03:37 +09:00
Atsushi Togo 6521e406c5 Merge branch 'develop' of github.com:phonopy/phono3py into develop 2025-05-06 11:16:29 +09:00
Atsushi Togo 3ad068b34a Show pypolymlp version 2025-05-06 11:16:19 +09:00
Atsushi Togo 00d5ef756b
Merge pull request #375 from phonopy/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-05-06 11:00:06 +09:00
pre-commit-ci[bot] d5c88263d5
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8)
2025-05-05 20:56:40 +00:00
81 changed files with 5770 additions and 4334 deletions

View File

@ -11,7 +11,7 @@ repos:
exclude: ^example/AlN-LDA/
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.7
rev: v0.12.5
hooks:
- id: ruff
args: [ "--fix", "--show-fixes" ]

View File

@ -50,18 +50,17 @@ static Darray *convert_to_darray(nb::ndarray<> npyary) {
// printf("Data shift:%lu [%lu, %lu]\n", adrs_shift, i_sigma, i_temp);
// }
void py_get_interaction(nb::ndarray<> py_fc3_normal_squared,
nb::ndarray<> py_g_zero, nb::ndarray<> py_frequencies,
nb::ndarray<> py_eigenvectors,
nb::ndarray<> py_triplets,
nb::ndarray<> py_bz_grid_addresses,
nb::ndarray<> py_D_diag, nb::ndarray<> py_Q,
nb::ndarray<> py_fc3, nb::ndarray<> py_svecs,
nb::ndarray<> py_multi, nb::ndarray<> py_masses,
nb::ndarray<> py_p2s_map, nb::ndarray<> py_s2p_map,
nb::ndarray<> py_band_indices, int64_t symmetrize_fc3_q,
int64_t make_r0_average, nb::ndarray<> py_all_shortest,
double cutoff_frequency, int64_t openmp_per_triplets) {
void py_get_interaction(
nb::ndarray<> py_fc3_normal_squared, nb::ndarray<> py_g_zero,
nb::ndarray<> py_frequencies, nb::ndarray<> py_eigenvectors,
nb::ndarray<> py_triplets, nb::ndarray<> py_bz_grid_addresses,
nb::ndarray<> py_D_diag, nb::ndarray<> py_Q, nb::ndarray<> py_fc3,
nb::ndarray<> py_fc3_nonzero_indices, nb::ndarray<> py_svecs,
nb::ndarray<> py_multi, nb::ndarray<> py_masses, nb::ndarray<> py_p2s_map,
nb::ndarray<> py_s2p_map, nb::ndarray<> py_band_indices,
int64_t symmetrize_fc3_q, int64_t make_r0_average,
nb::ndarray<> py_all_shortest, double cutoff_frequency,
int64_t openmp_per_triplets) {
Darray *fc3_normal_squared;
Darray *freqs;
_lapack_complex_double *eigvecs;
@ -72,6 +71,7 @@ void py_get_interaction(nb::ndarray<> py_fc3_normal_squared,
int64_t *D_diag;
int64_t (*Q)[3];
double *fc3;
char *fc3_nonzero_indices;
double (*svecs)[3];
int64_t (*multi)[2];
double *masses;
@ -100,6 +100,7 @@ void py_get_interaction(nb::ndarray<> py_fc3_normal_squared,
} else {
is_compact_fc3 = 1;
}
fc3_nonzero_indices = (char *)py_fc3_nonzero_indices.data();
svecs = (double (*)[3])py_svecs.data();
for (i = 0; i < 2; i++) {
multi_dims[i] = py_multi.shape(i);
@ -113,9 +114,10 @@ void py_get_interaction(nb::ndarray<> py_fc3_normal_squared,
ph3py_get_interaction(fc3_normal_squared, g_zero, freqs, eigvecs, triplets,
num_triplets, bz_grid_addresses, D_diag, Q, fc3,
is_compact_fc3, svecs, multi_dims, multi, masses, p2s,
s2p, band_indices, symmetrize_fc3_q, make_r0_average,
all_shortest, cutoff_frequency, openmp_per_triplets);
fc3_nonzero_indices, is_compact_fc3, svecs,
multi_dims, multi, masses, p2s, s2p, band_indices,
symmetrize_fc3_q, make_r0_average, all_shortest,
cutoff_frequency, openmp_per_triplets);
free(fc3_normal_squared);
fc3_normal_squared = NULL;
@ -129,8 +131,9 @@ void py_get_pp_collision(
nb::ndarray<> py_triplets, nb::ndarray<> py_triplet_weights,
nb::ndarray<> py_bz_grid_addresses, nb::ndarray<> py_bz_map,
int64_t bz_grid_type, nb::ndarray<> py_D_diag, nb::ndarray<> py_Q,
nb::ndarray<> py_fc3, nb::ndarray<> py_svecs, nb::ndarray<> py_multi,
nb::ndarray<> py_masses, nb::ndarray<> py_p2s_map, nb::ndarray<> py_s2p_map,
nb::ndarray<> py_fc3, nb::ndarray<> py_fc3_nonzero_indices,
nb::ndarray<> py_svecs, nb::ndarray<> py_multi, nb::ndarray<> py_masses,
nb::ndarray<> py_p2s_map, nb::ndarray<> py_s2p_map,
nb::ndarray<> py_band_indices, nb::ndarray<> py_temperatures_THz,
int64_t is_NU, int64_t symmetrize_fc3_q, int64_t make_r0_average,
nb::ndarray<> py_all_shortest, double cutoff_frequency,
@ -147,6 +150,7 @@ void py_get_pp_collision(
int64_t *D_diag;
int64_t (*Q)[3];
double *fc3;
char *fc3_nonzero_indices;
double (*svecs)[3];
int64_t (*multi)[2];
double *masses;
@ -176,6 +180,7 @@ void py_get_pp_collision(
} else {
is_compact_fc3 = 1;
}
fc3_nonzero_indices = (char *)py_fc3_nonzero_indices.data();
svecs = (double (*)[3])py_svecs.data();
for (i = 0; i < 2; i++) {
multi_dims[i] = py_multi.shape(i);
@ -191,9 +196,10 @@ void py_get_pp_collision(
ph3py_get_pp_collision(
gamma, relative_grid_address, frequencies, eigenvectors, triplets,
num_triplets, triplet_weights, bz_grid_addresses, bz_map, bz_grid_type,
D_diag, Q, fc3, is_compact_fc3, svecs, multi_dims, multi, masses, p2s,
s2p, band_indices, temperatures_THz, is_NU, symmetrize_fc3_q,
make_r0_average, all_shortest, cutoff_frequency, openmp_per_triplets);
D_diag, Q, fc3, fc3_nonzero_indices, is_compact_fc3, svecs, multi_dims,
multi, masses, p2s, s2p, band_indices, temperatures_THz, is_NU,
symmetrize_fc3_q, make_r0_average, all_shortest, cutoff_frequency,
openmp_per_triplets);
free(band_indices);
band_indices = NULL;
@ -206,7 +212,8 @@ void py_get_pp_collision_with_sigma(
nb::ndarray<> py_frequencies, nb::ndarray<> py_eigenvectors,
nb::ndarray<> py_triplets, nb::ndarray<> py_triplet_weights,
nb::ndarray<> py_bz_grid_addresses, nb::ndarray<> py_D_diag,
nb::ndarray<> py_Q, nb::ndarray<> py_fc3, nb::ndarray<> py_svecs,
nb::ndarray<> py_Q, nb::ndarray<> py_fc3,
nb::ndarray<> py_fc3_nonzero_indices, nb::ndarray<> py_svecs,
nb::ndarray<> py_multi, nb::ndarray<> py_masses, nb::ndarray<> py_p2s_map,
nb::ndarray<> py_s2p_map, nb::ndarray<> py_band_indices,
nb::ndarray<> py_temperatures_THz, int64_t is_NU, int64_t symmetrize_fc3_q,
@ -222,6 +229,7 @@ void py_get_pp_collision_with_sigma(
int64_t *D_diag;
int64_t (*Q)[3];
double *fc3;
char *fc3_nonzero_indices;
double (*svecs)[3];
int64_t (*multi)[2];
double *masses;
@ -249,6 +257,7 @@ void py_get_pp_collision_with_sigma(
} else {
is_compact_fc3 = 1;
}
fc3_nonzero_indices = (char *)py_fc3_nonzero_indices.data();
svecs = (double (*)[3])py_svecs.data();
for (i = 0; i < 2; i++) {
multi_dims[i] = py_multi.shape(i);
@ -264,8 +273,8 @@ void py_get_pp_collision_with_sigma(
ph3py_get_pp_collision_with_sigma(
gamma, sigma, sigma_cutoff, frequencies, eigenvectors, triplets,
num_triplets, triplet_weights, bz_grid_addresses, D_diag, Q, fc3,
is_compact_fc3, svecs, multi_dims, multi, masses, p2s, s2p,
band_indices, temperatures_THz, is_NU, symmetrize_fc3_q,
fc3_nonzero_indices, is_compact_fc3, svecs, multi_dims, multi, masses,
p2s, s2p, band_indices, temperatures_THz, is_NU, symmetrize_fc3_q,
make_r0_average, all_shortest, cutoff_frequency, openmp_per_triplets);
free(band_indices);
@ -795,13 +804,11 @@ int64_t py_tpl_get_triplets_reciprocal_mesh_at_q(
return num_ir;
}
int64_t py_tpl_get_BZ_triplets_at_q(nb::ndarray<> py_triplets,
int64_t grid_point,
nb::ndarray<> py_bz_grid_address,
nb::ndarray<> py_bz_map,
nb::ndarray<> py_map_triplets,
nb::ndarray<> py_D_diag, nb::ndarray<> py_Q,
int64_t bz_grid_type) {
int64_t py_tpl_get_BZ_triplets_at_q(
nb::ndarray<> py_triplets, int64_t grid_point,
nb::ndarray<> py_bz_grid_address, nb::ndarray<> py_bz_map,
nb::ndarray<> py_map_triplets, nb::ndarray<> py_D_diag, nb::ndarray<> py_Q,
nb::ndarray<> py_reciprocal_lattice, int64_t bz_grid_type) {
int64_t (*triplets)[3];
int64_t (*bz_grid_address)[3];
int64_t *bz_map;
@ -809,6 +816,7 @@ int64_t py_tpl_get_BZ_triplets_at_q(nb::ndarray<> py_triplets,
int64_t num_map_triplets;
int64_t *D_diag;
int64_t (*Q)[3];
double (*reciprocal_lattice)[3];
int64_t num_ir;
triplets = (int64_t (*)[3])py_triplets.data();
@ -818,10 +826,11 @@ int64_t py_tpl_get_BZ_triplets_at_q(nb::ndarray<> py_triplets,
num_map_triplets = (int64_t)py_map_triplets.shape(0);
D_diag = (int64_t *)py_D_diag.data();
Q = (int64_t (*)[3])py_Q.data();
reciprocal_lattice = (double (*)[3])py_reciprocal_lattice.data();
num_ir = ph3py_get_BZ_triplets_at_q(triplets, grid_point, bz_grid_address,
bz_map, map_triplets, num_map_triplets,
D_diag, Q, bz_grid_type);
num_ir = ph3py_get_BZ_triplets_at_q(
triplets, grid_point, bz_grid_address, bz_map, map_triplets,
num_map_triplets, D_diag, Q, reciprocal_lattice, bz_grid_type);
return num_ir;
}

View File

@ -138,7 +138,7 @@ int64_t gridsys_get_bz_grid_addresses(
if (!niggli_reduce(niggli_lattice, GRIDSYS_NIGGLI_TOLERANCE)) {
return 0;
}
if (!lagmat_inverse_matrix_d3(inv_Lr, (double(*)[3])niggli_lattice,
if (!lagmat_inverse_matrix_d3(inv_Lr, (double (*)[3])niggli_lattice,
GRIDSYS_NIGGLI_TOLERANCE)) {
return 0;
}
@ -156,7 +156,7 @@ int64_t gridsys_get_bz_grid_addresses(
bzgrid->bzg2grg = bzg2grg;
bzgrid->type = bz_grid_type;
lagmat_multiply_matrix_l3(bzgrid->Q, inv_Mpr_int, Q);
lagmat_copy_matrix_d3(bzgrid->reclat, (double(*)[3])niggli_lattice);
lagmat_copy_matrix_d3(bzgrid->reclat, (double (*)[3])niggli_lattice);
for (i = 0; i < 3; i++) {
bzgrid->D_diag[i] = D_diag[i];
bzgrid->PS[i] = PS[i];
@ -220,7 +220,7 @@ int64_t gridsys_get_bz_triplets_at_q(
const int64_t (*bz_grid_addresses)[3], const int64_t *bz_map,
const int64_t *map_triplets, const int64_t num_map_triplets,
const int64_t D_diag[3], const int64_t Q[3][3],
const int64_t bz_grid_type) {
const double reciprocal_lattice[3][3], const int64_t bz_grid_type) {
RecgridConstBZGrid *bzgrid;
int64_t i, j, num_ir;
@ -237,6 +237,7 @@ int64_t gridsys_get_bz_triplets_at_q(
bzgrid->D_diag[i] = D_diag[i];
bzgrid->PS[i] = 0;
for (j = 0; j < 3; j++) {
bzgrid->reclat[i][j] = reciprocal_lattice[i][j];
bzgrid->Q[i][j] = Q[i][j];
}
}

View File

@ -272,7 +272,8 @@ int64_t gridsys_get_bz_triplets_at_q(
int64_t (*ir_triplets)[3], const int64_t bz_grid_index,
const int64_t (*bz_grid_addresses)[3], const int64_t *bz_map,
const int64_t *map_triplets, const int64_t num_map_triplets,
const int64_t D_diag[3], const int64_t Q[3][3], const int64_t bz_grid_type);
const int64_t D_diag[3], const int64_t Q[3][3],
const double reciprocal_lattice[3][3], const int64_t bz_grid_type);
/**
* @brief Return integration weight of linear tetrahedron method

View File

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

View File

@ -63,12 +63,13 @@ int64_t ph3py_get_interaction(
const _lapack_complex_double *eigenvectors, const int64_t (*triplets)[3],
const int64_t num_triplets, const int64_t (*bz_grid_addresses)[3],
const int64_t D_diag[3], const int64_t Q[3][3], const double *fc3,
const int64_t is_compact_fc3, const double (*svecs)[3],
const int64_t multi_dims[2], const int64_t (*multiplicity)[2],
const double *masses, const int64_t *p2s_map, const int64_t *s2p_map,
const int64_t *band_indices, const int64_t symmetrize_fc3_q,
const int64_t make_r0_average, const char *all_shortest,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
const char *fc3_nonzero_indices, const int64_t is_compact_fc3,
const double (*svecs)[3], const int64_t multi_dims[2],
const int64_t (*multiplicity)[2], const double *masses,
const int64_t *p2s_map, const int64_t *s2p_map, const int64_t *band_indices,
const int64_t symmetrize_fc3_q, const int64_t make_r0_average,
const char *all_shortest, const double cutoff_frequency,
const int64_t openmp_per_triplets) {
RecgridConstBZGrid *bzgrid;
AtomTriplets *atom_triplets;
int64_t i, j;
@ -102,6 +103,7 @@ int64_t ph3py_get_interaction(
atom_triplets->s2p_map = s2p_map;
atom_triplets->make_r0_average = make_r0_average;
atom_triplets->all_shortest = all_shortest;
atom_triplets->nonzero_indices = fc3_nonzero_indices;
itr_get_interaction(fc3_normal_squared, g_zero, frequencies,
(lapack_complex_double *)eigenvectors, triplets,
@ -127,7 +129,8 @@ int64_t ph3py_get_pp_collision(
const int64_t (*bz_grid_addresses)[3], /* thm */
const int64_t *bz_map, /* thm */
const int64_t bz_grid_type, const int64_t D_diag[3], const int64_t Q[3][3],
const double *fc3, const int64_t is_compact_fc3, const double (*svecs)[3],
const double *fc3, const char *fc3_nonzero_indices,
const int64_t is_compact_fc3, const double (*svecs)[3],
const int64_t multi_dims[2], const int64_t (*multiplicity)[2],
const double *masses, const int64_t *p2s_map, const int64_t *s2p_map,
const Larray *band_indices, const Darray *temperatures_THz,
@ -169,6 +172,7 @@ int64_t ph3py_get_pp_collision(
atom_triplets->s2p_map = s2p_map;
atom_triplets->make_r0_average = make_r0_average;
atom_triplets->all_shortest = all_shortest;
atom_triplets->nonzero_indices = fc3_nonzero_indices;
ppc_get_pp_collision(imag_self_energy, relative_grid_address, frequencies,
(lapack_complex_double *)eigenvectors, triplets,
@ -192,13 +196,14 @@ int64_t ph3py_get_pp_collision_with_sigma(
const int64_t (*triplets)[3], const int64_t num_triplets,
const int64_t *triplet_weights, const int64_t (*bz_grid_addresses)[3],
const int64_t D_diag[3], const int64_t Q[3][3], const double *fc3,
const int64_t is_compact_fc3, const double (*svecs)[3],
const int64_t multi_dims[2], const int64_t (*multiplicity)[2],
const double *masses, const int64_t *p2s_map, const int64_t *s2p_map,
const Larray *band_indices, const Darray *temperatures_THz,
const int64_t is_NU, const int64_t symmetrize_fc3_q,
const int64_t make_r0_average, const char *all_shortest,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
const char *fc3_nonzero_indices, const int64_t is_compact_fc3,
const double (*svecs)[3], const int64_t multi_dims[2],
const int64_t (*multiplicity)[2], const double *masses,
const int64_t *p2s_map, const int64_t *s2p_map, const Larray *band_indices,
const Darray *temperatures_THz, const int64_t is_NU,
const int64_t symmetrize_fc3_q, const int64_t make_r0_average,
const char *all_shortest, const double cutoff_frequency,
const int64_t openmp_per_triplets) {
RecgridConstBZGrid *bzgrid;
AtomTriplets *atom_triplets;
int64_t i, j;
@ -232,6 +237,7 @@ int64_t ph3py_get_pp_collision_with_sigma(
atom_triplets->s2p_map = s2p_map;
atom_triplets->make_r0_average = make_r0_average;
atom_triplets->all_shortest = all_shortest;
atom_triplets->nonzero_indices = fc3_nonzero_indices;
ppc_get_pp_collision_with_sigma(
imag_self_energy, sigma, sigma_cutoff, frequencies,
@ -405,7 +411,7 @@ int64_t ph3py_get_BZ_triplets_at_q(
const int64_t (*bz_grid_addresses)[3], const int64_t *bz_map,
const int64_t *map_triplets, const int64_t num_map_triplets,
const int64_t D_diag[3], const int64_t Q[3][3],
const int64_t bz_grid_type) {
const double reciprocal_lattice[3][3], const int64_t bz_grid_type) {
RecgridConstBZGrid *bzgrid;
int64_t i, j, num_ir;
@ -423,6 +429,7 @@ int64_t ph3py_get_BZ_triplets_at_q(
bzgrid->PS[i] = 0;
for (j = 0; j < 3; j++) {
bzgrid->Q[i][j] = Q[i][j];
bzgrid->reclat[i][j] = reciprocal_lattice[i][j];
}
}
bzgrid->size = num_map_triplets;

View File

@ -53,12 +53,13 @@ int64_t ph3py_get_interaction(
const _lapack_complex_double *eigenvectors, const int64_t (*triplets)[3],
const int64_t num_triplets, const int64_t (*bz_grid_addresses)[3],
const int64_t D_diag[3], const int64_t Q[3][3], const double *fc3,
const int64_t is_compact_fc3, const double (*svecs)[3],
const int64_t multi_dims[2], const int64_t (*multi)[2],
const double *masses, const int64_t *p2s_map, const int64_t *s2p_map,
const int64_t *band_indices, const int64_t symmetrize_fc3_q,
const int64_t make_r0_average, const char *all_shortest,
const double cutoff_frequency, const int64_t openmp_per_triplets);
const char *fc3_nonzero_indices, const int64_t is_compact_fc3,
const double (*svecs)[3], const int64_t multi_dims[2],
const int64_t (*multi)[2], const double *masses, const int64_t *p2s_map,
const int64_t *s2p_map, const int64_t *band_indices,
const int64_t symmetrize_fc3_q, const int64_t make_r0_average,
const char *all_shortest, const double cutoff_frequency,
const int64_t openmp_per_triplets);
int64_t ph3py_get_pp_collision(
double *imag_self_energy,
const int64_t relative_grid_address[24][4][3], /* thm */
@ -68,7 +69,8 @@ int64_t ph3py_get_pp_collision(
const int64_t (*bz_grid_addresses)[3], /* thm */
const int64_t *bz_map, /* thm */
const int64_t bz_grid_type, const int64_t D_diag[3], const int64_t Q[3][3],
const double *fc3, const int64_t is_compact_fc3, const double (*svecs)[3],
const double *fc3, const char *fc3_nonzero_indices,
const int64_t is_compact_fc3, const double (*svecs)[3],
const int64_t multi_dims[2], const int64_t (*multi)[2],
const double *masses, const int64_t *p2s_map, const int64_t *s2p_map,
const Larray *band_indices, const Darray *temperatures_THz,
@ -81,13 +83,14 @@ int64_t ph3py_get_pp_collision_with_sigma(
const int64_t (*triplets)[3], const int64_t num_triplets,
const int64_t *triplet_weights, const int64_t (*bz_grid_addresses)[3],
const int64_t D_diag[3], const int64_t Q[3][3], const double *fc3,
const int64_t is_compact_fc3, const double (*svecs)[3],
const int64_t multi_dims[2], const int64_t (*multi)[2],
const double *masses, const int64_t *p2s_map, const int64_t *s2p_map,
const Larray *band_indices, const Darray *temperatures_THz,
const int64_t is_NU, const int64_t symmetrize_fc3_q,
const int64_t make_r0_average, const char *all_shortest,
const double cutoff_frequency, const int64_t openmp_per_triplets);
const char *fc3_nonzero_indices, const int64_t is_compact_fc3,
const double (*svecs)[3], const int64_t multi_dims[2],
const int64_t (*multi)[2], const double *masses, const int64_t *p2s_map,
const int64_t *s2p_map, const Larray *band_indices,
const Darray *temperatures_THz, const int64_t is_NU,
const int64_t symmetrize_fc3_q, const int64_t make_r0_average,
const char *all_shortest, const double cutoff_frequency,
const int64_t openmp_per_triplets);
void ph3py_get_imag_self_energy_at_bands_with_g(
double *imag_self_energy, const Darray *fc3_normal_squared,
const double *frequencies, const int64_t (*triplets)[3],
@ -170,7 +173,8 @@ int64_t ph3py_get_BZ_triplets_at_q(
int64_t (*triplets)[3], const int64_t grid_point,
const int64_t (*bz_grid_addresses)[3], const int64_t *bz_map,
const int64_t *map_triplets, const int64_t num_map_triplets,
const int64_t D_diag[3], const int64_t Q[3][3], const int64_t bz_grid_type);
const int64_t D_diag[3], const int64_t Q[3][3],
const double reciprocal_lattice[3][3], const int64_t bz_grid_type);
int64_t ph3py_get_integration_weight(
double *iw, char *iw_zero, const double *frequency_points,
const int64_t num_band0, const int64_t relative_grid_address[24][4][3],

View File

@ -75,7 +75,7 @@ static lapack_complex_double get_pre_phase_factor(
static lapack_complex_double sum_lapack_complex_double(lapack_complex_double a,
lapack_complex_double b);
/* fc3_reciprocal[num_patom, num_patom, num_patom, 3, 3, 3] */
/* fc3_reciprocal[num_patom, 3, num_patom, 3, num_patom, 3] */
void r2r_real_to_reciprocal(lapack_complex_double *fc3_reciprocal,
const double q_vecs[3][3], const double *fc3,
const int64_t is_compact_fc3,
@ -277,7 +277,7 @@ static void real_to_reciprocal_elements(
const int64_t pi0, const int64_t pi1, const int64_t pi2,
const int64_t leg_index) {
int64_t i, j, k, l;
int64_t num_satom, adrs_shift;
int64_t num_satom, adrs_shift_atoms, adrs_shift_fc3;
lapack_complex_double phase_factor;
double fc3_rec_real[27], fc3_rec_imag[27];
@ -309,8 +309,11 @@ static void real_to_reciprocal_elements(
continue;
}
}
adrs_shift =
i * 27 * num_satom * num_satom + j * 27 * num_satom + k * 27;
adrs_shift_atoms = i * num_satom * num_satom + j * num_satom + k;
if (!atom_triplets->nonzero_indices[adrs_shift_atoms]) {
continue;
}
adrs_shift_fc3 = adrs_shift_atoms * 27;
phase_factor =
phonoc_complex_prod(phase_factor1[j], phase_factor2[k]);
@ -320,19 +323,19 @@ static void real_to_reciprocal_elements(
for (l = 0; l < 27; l++) {
fc3_rec_real[l] +=
lapack_complex_double_real(phase_factor) *
fc3[adrs_shift + l] * 3;
fc3[adrs_shift_fc3 + l] * 3;
fc3_rec_imag[l] +=
lapack_complex_double_imag(phase_factor) *
fc3[adrs_shift + l] * 3;
fc3[adrs_shift_fc3 + l] * 3;
}
} else {
for (l = 0; l < 27; l++) {
fc3_rec_real[l] +=
lapack_complex_double_real(phase_factor) *
fc3[adrs_shift + l];
fc3[adrs_shift_fc3 + l];
fc3_rec_imag[l] +=
lapack_complex_double_imag(phase_factor) *
fc3[adrs_shift + l];
fc3[adrs_shift_fc3 + l];
}
}
}

View File

@ -48,6 +48,7 @@ typedef struct {
const int64_t *s2p_map;
int64_t make_r0_average;
const char *all_shortest;
const char *nonzero_indices; // for compact fc3
} AtomTriplets;
void r2r_real_to_reciprocal(lapack_complex_double *fc3_reciprocal,

View File

@ -193,20 +193,11 @@ double recgrid_get_tolerance_for_BZ_reduction(const RecgridBZGrid *bzgrid) {
int64_t i, j;
double tolerance;
double length[3];
double reclatQ[3][3];
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
reclatQ[i][j] = bzgrid->reclat[i][0] * bzgrid->Q[0][j] +
bzgrid->reclat[i][1] * bzgrid->Q[1][j] +
bzgrid->reclat[i][2] * bzgrid->Q[2][j];
}
}
for (i = 0; i < 3; i++) {
length[i] = 0;
for (j = 0; j < 3; j++) {
length[i] += reclatQ[j][i] * reclatQ[j][i];
length[i] += bzgrid->reclat[j][i] * bzgrid->reclat[j][i];
}
length[i] /= bzgrid->D_diag[i] * bzgrid->D_diag[i];
}
@ -233,8 +224,8 @@ RecgridMats *recgrid_alloc_RotMats(const int64_t size) {
rotmats->size = size;
if (size > 0) {
if ((rotmats->mat = (int64_t(*)[3][3])malloc(sizeof(int64_t[3][3]) *
size)) == NULL) {
if ((rotmats->mat = (int64_t (*)[3][3])malloc(sizeof(int64_t[3][3]) *
size)) == NULL) {
warning_print("Memory could not be allocated ");
warning_print("(RecgridMats, line %d, %s).\n", __LINE__, __FILE__);
free(rotmats);

View File

@ -49,16 +49,28 @@
#include "lapack_wrapper.h"
#ifdef MEASURE_R2N
#include <time.h>
#include <unistd.h>
#endif
static double get_fc3_sum(const lapack_complex_double *e0,
const lapack_complex_double *e1,
static void get_fc3_e0_e1_e2(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
const double *freqs0, const double *freqs1, const double *freqs2,
const lapack_complex_double *e0, const lapack_complex_double *e1,
const lapack_complex_double *e2, const int64_t *band_indices,
const int64_t num_band0, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets);
static void get_fc3_e0(lapack_complex_double *fc3_e0,
const lapack_complex_double *fc3_reciprocal,
const lapack_complex_double *e0,
const int64_t band_index_0, const int64_t num_band);
static double get_fc3_sum(const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
const int64_t num_band);
static double get_fc3_sum_blas_like(const lapack_complex_double *e0,
const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
const int64_t num_band);
// Testing efficiency of BLAS
#ifdef MULTITHREADED_BLAS
static double get_fc3_sum_blas(const lapack_complex_double *e0,
const lapack_complex_double *e1,
@ -66,11 +78,7 @@ static double get_fc3_sum_blas(const lapack_complex_double *e0,
const lapack_complex_double *fc3_reciprocal,
const int64_t num_band);
#endif
static double get_fc3_sum_blas_like(const lapack_complex_double *e0,
const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
const int64_t num_band);
void reciprocal_to_normal_squared(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
@ -78,17 +86,19 @@ void reciprocal_to_normal_squared(
const lapack_complex_double *eigvecs0,
const lapack_complex_double *eigvecs1,
const lapack_complex_double *eigvecs2, const double *masses,
const int64_t *band_indices, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
int64_t i, j, ij, num_atom, use_multithreaded_blas;
const int64_t *band_indices, const int64_t num_band0,
const int64_t num_band, const double cutoff_frequency,
const int64_t openmp_per_triplets) {
int64_t i, j, ij, num_atom;
double *inv_sqrt_masses;
lapack_complex_double *e0, *e1, *e2;
/* Inverse sqrt mass is multiplied with eigenvectors to reduce number
* of */
/* operations in get_fc3_sum. Three eigenvector matrices are looped
* by */
/* first loop leveraging contiguous memory layout of [e0, e1, e2].
/* Inverse sqrt mass is multiplied with eigenvectors to reduce
* number of */
/* operations in get_fc3_sum. Three eigenvector matrices are
* looped by */
/* first loop leveraging contiguous memory layout of [e0,
* e1, e2].
*/
num_atom = num_band / 3;
inv_sqrt_masses = (double *)malloc(sizeof(double) * num_band);
@ -132,64 +142,10 @@ void reciprocal_to_normal_squared(
free(inv_sqrt_masses);
inv_sqrt_masses = NULL;
#ifdef MEASURE_R2N
double loopTotalCPUTime, loopTotalWallTime;
time_t loopStartWallTime;
clock_t loopStartCPUTime;
#endif
#ifdef MEASURE_R2N
loopStartWallTime = time(NULL);
loopStartCPUTime = clock();
#endif
#ifdef MULTITHREADED_BLAS
if (openmp_per_triplets) {
use_multithreaded_blas = 0;
} else {
use_multithreaded_blas = 1;
}
#else
use_multithreaded_blas = 0;
#ifdef _OPENMP
#pragma omp parallel for if (!openmp_per_triplets)
#endif
#endif
for (i = 0; i < num_g_pos; i++) {
if (freqs0[band_indices[g_pos[i][0]]] > cutoff_frequency &&
freqs1[g_pos[i][1]] > cutoff_frequency &&
freqs2[g_pos[i][2]] > cutoff_frequency) {
#ifdef MULTITHREADED_BLAS
if (use_multithreaded_blas) {
fc3_normal_squared[g_pos[i][3]] =
get_fc3_sum_blas(e0 + band_indices[g_pos[i][0]] * num_band,
e1 + g_pos[i][1] * num_band,
e2 + g_pos[i][2] * num_band,
fc3_reciprocal, num_band) /
(freqs0[band_indices[g_pos[i][0]]] * freqs1[g_pos[i][1]] *
freqs2[g_pos[i][2]]);
} else {
#endif
fc3_normal_squared[g_pos[i][3]] =
get_fc3_sum_blas_like(
e0 + band_indices[g_pos[i][0]] * num_band,
e1 + g_pos[i][1] * num_band,
e2 + g_pos[i][2] * num_band, fc3_reciprocal, num_band) /
(freqs0[band_indices[g_pos[i][0]]] * freqs1[g_pos[i][1]] *
freqs2[g_pos[i][2]]);
#ifdef MULTITHREADED_BLAS
}
#endif
} else {
fc3_normal_squared[g_pos[i][3]] = 0;
}
}
#ifdef MEASURE_R2N
loopTotalCPUTime = (double)(clock() - loopStartCPUTime) / CLOCKS_PER_SEC;
loopTotalWallTime = difftime(time(NULL), loopStartWallTime);
printf(" %1.3fs (%1.3fs CPU)\n", loopTotalWallTime, loopTotalCPUTime);
#endif
get_fc3_e0_e1_e2(fc3_normal_squared, g_pos, num_g_pos, fc3_reciprocal,
freqs0, freqs1, freqs2, e0, e1, e2, band_indices,
num_band0, num_band, cutoff_frequency,
openmp_per_triplets);
free(e0);
e0 = NULL;
@ -197,41 +153,158 @@ void reciprocal_to_normal_squared(
e2 = NULL;
}
static double get_fc3_sum(const lapack_complex_double *e0,
const lapack_complex_double *e1,
// This is less efficient than the one without multithreading
// but can be called when unit cell is large.
static void get_fc3_e0_e1_e2(
double *fc3_normal_squared, const int64_t (*g_pos)[4],
const int64_t num_g_pos, const lapack_complex_double *fc3_reciprocal,
const double *freqs0, const double *freqs1, const double *freqs2,
const lapack_complex_double *e0, const lapack_complex_double *e1,
const lapack_complex_double *e2, const int64_t *band_indices,
const int64_t num_band0, const int64_t num_band,
const double cutoff_frequency, const int64_t openmp_per_triplets) {
int64_t i;
lapack_complex_double *fc3_e0, zero;
zero = lapack_make_complex_double(0, 0);
fc3_e0 = (lapack_complex_double *)malloc(sizeof(lapack_complex_double) *
num_band0 * num_band * num_band);
for (i = 0; i < num_band0 * num_band * num_band; i++) {
fc3_e0[i] = zero;
}
#ifdef _OPENMP
#pragma omp parallel for if (!openmp_per_triplets)
#endif
for (i = 0; i < num_band0; i++) {
get_fc3_e0(fc3_e0 + i * num_band * num_band, fc3_reciprocal, e0,
band_indices[i], num_band);
}
#ifdef _OPENMP
#pragma omp parallel for if (!openmp_per_triplets)
#endif
for (i = 0; i < num_g_pos; i++) {
if (freqs0[band_indices[g_pos[i][0]]] > cutoff_frequency &&
freqs1[g_pos[i][1]] > cutoff_frequency &&
freqs2[g_pos[i][2]] > cutoff_frequency) {
fc3_normal_squared[g_pos[i][3]] =
get_fc3_sum(
e1 + g_pos[i][1] * num_band, e2 + g_pos[i][2] * num_band,
fc3_e0 + g_pos[i][0] * num_band * num_band, num_band) /
(freqs0[band_indices[g_pos[i][0]]] * freqs1[g_pos[i][1]] *
freqs2[g_pos[i][2]]);
} else {
fc3_normal_squared[g_pos[i][3]] = 0;
}
}
free(fc3_e0);
fc3_e0 = NULL;
}
static void get_fc3_e0(lapack_complex_double *fc3_e0,
const lapack_complex_double *fc3_reciprocal,
const lapack_complex_double *e0,
const int64_t band_index_0, const int64_t num_band) {
int64_t j, k;
lapack_complex_double fc3_elem;
for (j = 0; j < num_band; j++) {
for (k = 0; k < num_band * num_band; k++) {
fc3_elem =
phonoc_complex_prod(fc3_reciprocal[j * num_band * num_band + k],
e0[band_index_0 * num_band + j]);
fc3_e0[k] = lapack_make_complex_double(
lapack_complex_double_real(fc3_e0[k]) +
lapack_complex_double_real(fc3_elem),
lapack_complex_double_imag(fc3_e0[k]) +
lapack_complex_double_imag(fc3_elem));
}
}
}
static double get_fc3_sum(const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
const lapack_complex_double *fc3_e0,
const int64_t num_band) {
int64_t i, j, jk;
int64_t i, j;
double sum_real, sum_imag;
lapack_complex_double e_012_fc3, fc3_i_e_12, *e_12_cache;
const lapack_complex_double *fc3_i;
lapack_complex_double *fc3_e0_e1, fc3_elem;
e_12_cache = (lapack_complex_double *)malloc(sizeof(lapack_complex_double) *
num_band * num_band);
fc3_e0_e1 = (lapack_complex_double *)malloc(sizeof(lapack_complex_double) *
num_band);
for (i = 0; i < num_band; i++) {
fc3_e0_e1[i] = lapack_make_complex_double(0, 0);
}
for (i = 0; i < num_band; i++) {
for (j = 0; j < num_band; j++) {
fc3_elem = phonoc_complex_prod(fc3_e0[i * num_band + j], e1[i]);
fc3_e0_e1[j] = lapack_make_complex_double(
lapack_complex_double_real(fc3_e0_e1[j]) +
lapack_complex_double_real(fc3_elem),
lapack_complex_double_imag(fc3_e0_e1[j]) +
lapack_complex_double_imag(fc3_elem));
}
}
sum_real = 0;
sum_imag = 0;
for (i = 0; i < num_band; i++) {
for (j = 0; j < num_band; j++) {
e_12_cache[i * num_band + j] = phonoc_complex_prod(e1[i], e2[j]);
}
fc3_elem = phonoc_complex_prod(fc3_e0_e1[i], e2[i]);
sum_real += lapack_complex_double_real(fc3_elem);
sum_imag += lapack_complex_double_imag(fc3_elem);
}
free(fc3_e0_e1);
fc3_e0_e1 = NULL;
return (sum_real * sum_real + sum_imag * sum_imag);
}
static double get_fc3_sum_blas_like(const lapack_complex_double *e0,
const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
const int64_t num_band) {
int64_t i, j;
double sum_real, sum_imag, retval_real, retval_imag;
lapack_complex_double *e_12, fc3_e_12, fc3_e_012;
e_12 = (lapack_complex_double *)malloc(sizeof(lapack_complex_double) *
num_band * num_band);
for (i = 0; i < num_band; i++) {
fc3_i = fc3_reciprocal + i * num_band * num_band;
for (jk = 0; jk < num_band * num_band; jk++) {
fc3_i_e_12 = phonoc_complex_prod(fc3_i[jk], e_12_cache[jk]);
e_012_fc3 = phonoc_complex_prod(e0[i], fc3_i_e_12);
sum_real += lapack_complex_double_real(e_012_fc3);
sum_imag += lapack_complex_double_imag(e_012_fc3);
memcpy(e_12 + i * num_band, e2,
sizeof(lapack_complex_double) * num_band);
for (j = 0; j < num_band; j++) {
e_12[i * num_band + j] =
phonoc_complex_prod(e1[i], e_12[i * num_band + j]);
}
}
free(e_12_cache);
e_12_cache = NULL;
return (sum_real * sum_real + sum_imag * sum_imag);
retval_real = 0;
retval_imag = 0;
for (i = 0; i < num_band; i++) {
sum_real = 0;
sum_imag = 0;
for (j = 0; j < num_band * num_band; j++) {
fc3_e_12 = phonoc_complex_prod(
fc3_reciprocal[i * num_band * num_band + j], e_12[j]);
sum_real += lapack_complex_double_real(fc3_e_12);
sum_imag += lapack_complex_double_imag(fc3_e_12);
}
fc3_e_012 = phonoc_complex_prod(
e0[i], lapack_make_complex_double(sum_real, sum_imag));
retval_real += lapack_complex_double_real(fc3_e_012);
retval_imag += lapack_complex_double_imag(fc3_e_012);
}
free(e_12);
e_12 = NULL;
return retval_real * retval_real + retval_imag * retval_imag;
}
#ifdef MULTITHREADED_BLAS
@ -271,46 +344,3 @@ static double get_fc3_sum_blas(const lapack_complex_double *e0,
lapack_complex_double_imag(retval);
}
#endif
static double get_fc3_sum_blas_like(const lapack_complex_double *e0,
const lapack_complex_double *e1,
const lapack_complex_double *e2,
const lapack_complex_double *fc3_reciprocal,
const int64_t num_band) {
int64_t i, j;
double sum_real, sum_imag, retval_real, retval_imag;
lapack_complex_double *e_12, fc3_e_12, fc3_e_012;
e_12 = (lapack_complex_double *)malloc(sizeof(lapack_complex_double) *
num_band * num_band);
for (i = 0; i < num_band; i++) {
memcpy(e_12 + i * num_band, e2, 16 * num_band);
for (j = 0; j < num_band; j++) {
e_12[i * num_band + j] =
phonoc_complex_prod(e1[i], e_12[i * num_band + j]);
}
}
retval_real = 0;
retval_imag = 0;
for (i = 0; i < num_band; i++) {
sum_real = 0;
sum_imag = 0;
for (j = 0; j < num_band * num_band; j++) {
fc3_e_12 = phonoc_complex_prod(
fc3_reciprocal[i * num_band * num_band + j], e_12[j]);
sum_real += lapack_complex_double_real(fc3_e_12);
sum_imag += lapack_complex_double_imag(fc3_e_12);
}
fc3_e_012 = phonoc_complex_prod(
e0[i], lapack_make_complex_double(sum_real, sum_imag));
retval_real += lapack_complex_double_real(fc3_e_012);
retval_imag += lapack_complex_double_imag(fc3_e_012);
}
free(e_12);
e_12 = NULL;
return retval_real * retval_real + retval_imag * retval_imag;
}

View File

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

View File

@ -262,7 +262,7 @@ static void get_BZ_triplets_at_q_type1(int64_t (*triplets)[3],
int64_t bzgp[3], G[3];
int64_t bz_adrs0[3], bz_adrs1[3], bz_adrs2[3];
const int64_t *gp_map;
const int64_t(*bz_adrs)[3];
const int64_t (*bz_adrs)[3];
double d2, min_d2, tolerance;
double LQD_inv[3][3];
@ -348,13 +348,14 @@ static void get_BZ_triplets_at_q_type2(int64_t (*triplets)[3],
int64_t bzgp[3], G[3];
int64_t bz_adrs0[3], bz_adrs1[3], bz_adrs2[3];
const int64_t *gp_map;
const int64_t(*bz_adrs)[3];
const int64_t (*bz_adrs)[3];
double d2, min_d2, tolerance;
double LQD_inv[3][3];
gp_map = bzgrid->gp_map;
bz_adrs = bzgrid->addresses;
get_LQD_inv(LQD_inv, bzgrid);
/* This tolerance is used to be consistent to BZ reduction in bzgrid. */
tolerance = recgrid_get_tolerance_for_BZ_reduction((RecgridBZGrid *)bzgrid);
@ -423,10 +424,15 @@ static void get_LQD_inv(double LQD_inv[3][3],
int64_t i, j, k;
/* LQD^-1 */
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
LQD_inv[i][j] = 0;
}
}
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
for (k = 0; k < 3; k++) {
LQD_inv[i][k] =
LQD_inv[i][k] +=
bzgrid->reclat[i][j] * bzgrid->Q[j][k] / bzgrid->D_diag[k];
}
}

View File

@ -2,6 +2,29 @@
# Change Log
## Jul-22-2025: Version 3.18.0
- Changed `Phono3py.run_imag_self_energy()` to return `ImagSelfEnergyValues`.
- Traditional force constants symmetrizer now applies translational and
permutation symmetries alternately 3 times in succession (previously once).
## Jul-5-2025: Version 3.17.1
- Fix direct-solution crashing when executed via command line
## Jun-26-2025: Version 3.17.0
- Major refactoring of command-user interface. Most of routines behind the
`phono3py` and `phono3py-load` commands were unified.
- For `phono3py-load`, symfc-projector is used to symmetrize force constants
calculated by finite difference approach as the default behavior. The previous
behavior of the symmetrization can be recovered by `--fc-calculator
traditional` option.
## Jun-12-2025: Version 3.16.0
- Release to follow the change of phonopy
## Apr-30-2025: Version 3.15.1
- Release to follow the change of phonopy
@ -357,8 +380,7 @@ loader.
`FORCES_FC3` is assumed.
- TURBOMOLE interface is provided by Antti Karttunen (`--turbomole`).
- Compatibility of `fc2.hdf5` and `force_constants.hdf5` was improved for all
calculators to store physical unit information in the hdf5 file. See
{ref}`file_format_compatibility`.
calculators to store physical unit information in the hdf5 file.
## Mar-24-2019: Version 1.16.0

View File

@ -197,7 +197,6 @@ created from `FORCES_FC2` and `phono3py_disp.yaml` instead of `FORCES_FC3` and
```
(sp_option)=
### `--sp` or `--save-params`
Instead of `FORCES_FC3`, `phono3py_params.yaml` is generated. This option must
@ -329,16 +328,15 @@ information about primitive cell (`primitive_matrix` key) in
```
(random_displacements_option)=
### `--rd` (`RANDOM_DISPLACEMENTS`), `--rd-fc2` (`RANDOM_DISPLACEMENTS_FC2`) and `--random-seed` (`RANDOM_SEED`)
**`phono3py-load` doesn't have this option.**
See also {ref}`random-displacements`.
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
`--rd` and `--rd-fc2`, respectively. `--rd auto` can estimate a possible number
of supercells required (see {ref}`rd_number_estimation_factor_option`).
`--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).
Like `-d` option, it is recommended to specify `--pa auto` together with `--rd`
@ -348,6 +346,15 @@ and/or `--rd-fc2`,
% phono3py -c POSCAR-unitcell --dim 2 2 2 --dim-fc2 4 4 4 --rd 100 --rd-fc2 2 --pa auto
```
(rd_number_estimation_factor_option)=
### `--rd-auto-factor` (`RD_NUMBER_ESTIMATION_FACTOR`)
This scales the number of supercells generated by `--rd auto` by the specified
factor.
See
[RD_NUMBER_ESTIMATION_FACTOR](https://phonopy.github.io/phonopy/setting-tags.html#rd-number-estimation-factor).
(amplitude_option)=
### `--amplitude` (`DISPLACEMENT_DISTANCE`)
@ -596,7 +603,7 @@ expected.
### `--sigma` (`SIGMA`)
$\sigma$ value of Gaussian function for smearing when calculating imaginary part
of self energy. See the detail at {ref}`brillouinzone_sum`.
of self energy.
Multiple $\sigma$ values are also specified by space separated numerical values.
This is used when we want to test several $\sigma$ values simultaneously.
@ -760,8 +767,8 @@ where the averaged phonon-phonon interaction that is read from
`kappa-mxxx(-sx-sdx).hdf5` file is used if it exists in the file. Therefore the
averaged phonon-phonon interaction has to be stored before using this option
(see {ref}`--full-pp <full_pp_option>`). The calculation result **overwrites**
`kappa-mxxx(-sx-sdx).hdf5` file. Therefore to use this option together with `-o`
option is strongly recommended.
`kappa-mxxx(-sx-sdx).hdf5` file. Therefore the original
`kappa-mxxx(-sx-sdx).hdf5` file should be backed up.
First, run full conductivity calculation,
@ -806,48 +813,8 @@ $\Gamma_\lambda(\omega_\lambda) =
\Gamma^\text{U}_\lambda(\omega_\lambda)$
and this is used to calculate thermal conductivity in single-mode RTA. The
separation, i.e., the choice of G-vector, is made based on the first Brillouin
zone.
zone. See {ref}`iofile_kappa_hdf5_gamma_NU`.
The data are stored in `kappa-mxxx(-gx-sx-sdx).hdf5` file and accessed by
`gamma_N` and `gamma_U` keys. The shape of the arrays is the same as that of
`gamma` (see {ref}`kappa_hdf5_file_gamma`). An example (Si-PBEsol) is shown
below:
```bash
% phono3py-load --mesh 11 11 11 --fc3 --fc2 --br --nu
...
% ipython
In [1]: import h5py
In [2]: f = h5py.File("kappa-m111111.hdf5", 'r')
In [3]: list(f)
Out[3]:
['frequency',
'gamma',
'gamma_N',
'gamma_U',
'group_velocity',
'gv_by_gv',
'heat_capacity',
'kappa',
'kappa_unit_conversion',
'mesh',
'mode_kappa',
'qpoint',
'temperature',
'weight']
In [4]: f['gamma'].shape
Out[4]: (101, 56, 6)
In [5]: f['gamma_N'].shape
Out[5]: (101, 56, 6)
In [6]: f['gamma_U'].shape
Out[6]: (101, 56, 6)
```
### `--scattering-event-class` (`SCATTERING_EVENT_CLASS`)
@ -1202,68 +1169,9 @@ not found, `kappa-mxxx-gx-bx(-sx-sdx).hdf5` files for band indices are searched.
### `--write-gamma-detail` (`WRITE_GAMMA_DETAIL = .TRUE.`)
Each q-point triplet contribution to imaginary part of self energy is written
into `gamma_detail-mxxx-gx(-sx-sdx).hdf5` file. Be careful that this is large
data.
into `gamma_detail-mxxx-gx(-sx-sdx).hdf5` file. Be careful that this can be a
large file. See {ref}`iofile_gamma_detail_hdf5`.
In the output file in hdf5, following keys are used to extract the detailed
information.
```{table}
| dataset | Array shape |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| gamma_detail for `--ise` | (temperature, sampling frequency point, symmetry reduced set of triplets at a grid point, band1, band2, band3) in THz (without $2\pi$) |
| gamma_detail for `--br` | (temperature, symmetry reduced set of triplets at a grid point, band1, band2, band3) in THz (without $2\pi$) |
| mesh | Numbers of sampling mesh along reciprocal axes. |
| frequency_point for `--ise` | Sampling frequency points in THz (without $2\pi$), i.e., $\omega$ in $\Gamma_\lambda(\omega)$ |
| temperature | (temperature,), Temperatures in K |
| triplet | (symmetry reduced set of triplets at a grid point, 3), Triplets are given by the grid point indices (see below). |
| weight | (symmetry reduced set of triplets at a grid point,), Weight of each triplet to imaginary part of self energy |
```
Imaginary part of self energy (linewidth/2) is recovered by the following
script:
```python
import h5py
import numpy as np
gd = h5py.File("gamma_detail-mxxx-gx.hdf5")
temp_index = 30 # index of temperature
temperature = gd['temperature'][temp_index]
gamma_tp = gd['gamma_detail'][:].sum(axis=-1).sum(axis=-1)
weight = gd['weight'][:]
gamma = np.dot(weight, gamma_tp[temp_index])
```
For example, for `--br`, this `gamma` gives $\Gamma_\lambda(\omega_\lambda)$ of
the band indices at the grid point indicated by $\lambda$ at the temperature of
index 30. If any bands are degenerated, those `gamma` in
`kappa-mxxx-gx(-sx-sdx).hdf5` or `gamma-mxxx-gx(-sx-sdx).hdf5` type file are
averaged, but the `gamma` obtained here in this way are not symmetrized. Apart
from this symmetrization, the values must be equivalent between them.
To understand each contribution of triptle to imaginary part of self energy,
reading `phonon-mxxx.hdf5` is useful (see {ref}`write_phonon_option`). For
example, phonon triplets of three phonon scatterings are obtained by
```python
import h5py
import numpy as np
gd = h5py.File("gamma_detail-mxxx-gx.hdf5", 'r')
ph = h5py.File("phonon-mxxx.hdf5", 'r')
gp1 = gd['grid_point'][()]
triplets = gd['triplet'][:] # Sets of (gp1, gp2, gp3) where gp1 is fixed
mesh = gd['mesh'][:]
grid_address = ph['grid_address'][:]
q_triplets = grid_address[triplets] / mesh.astype('double')
# Phonons of triplets[2]
phonon_tp = [(ph['frequency'][i], ph['eigenvector'][i]) for i in triplets[2]]
# Fractions of contributions of triplets at this grid point and temperature index 30
gamma_sum_over_bands = np.dot(weight, gd['gamma_detail'][30].sum(axis=-1).sum(axis=-1).sum(axis=-1))
contrib_tp = [gd['gamma_detail'][30, i].sum() / gamma_sum_over_bands for i in range(len(weight))]
np.dot(weight, contrib_tp) # is one
```
(write_phonon_option)=
@ -1271,45 +1179,12 @@ np.dot(weight, contrib_tp) # is one
Phonon frequencies, eigenvectors, and grid point addresses are stored in
`phonon-mxxx.hdf5` file. {ref}`--pa <pa_option>` and {ref}`--nac <nac_option>`
may be required depending on calculation setting.
may be required depending on calculation setting. See {ref}`iofile_phonon_hdf5`.
```bash
% phono3py-load --mesh 11 11 11 --nac --write-phoonon
```
Contents of `phonon-mxxx.hdf5` are watched by:
```bash
In [1]: import h5py
In [2]: ph = h5py.File("phonon-m111111.hdf5", 'r')
In [3]: list(ph)
Out[3]: ['eigenvector', 'frequency', 'grid_address', 'mesh']
In [4]: ph['mesh'][:]
Out[4]: array([11, 11, 11], dtype=int32)
In [5]: ph['grid_address'].shape
Out[5]: (1367, 3)
In [6]: ph['frequency'].shape
Out[6]: (1367, 6)
In [7]: ph['eigenvector'].shape
Out[7]: (1367, 6, 6)
```
The first axis of `ph['grid_address']`, `ph['frequency']`, and
`ph['eigenvector']` corresponds to the number of q-points where phonons are
calculated. Here the number of phonons may not be equal to product of mesh
numbers ($1367 \neq 11^3$). This is because all q-points on Brillouin zone
boundary are included, i.e., even if multiple q-points are translationally
equivalent, those phonons are stored separately though these phonons are
physically equivalent within the equations employed in phono3py. Here Brillouin
zone is defined by WignerSeitz cell of reciprocal primitive basis vectors. This
is convenient to categorize phonon triplets into Umklapp and Normal scatterings
based on the Brillouin zone.
(read_phonon_option)=
@ -1358,10 +1233,6 @@ 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>).
### `-o`, `-i`, `--io`
These options are deprecated.
<!-- (output_filename_option)=
### `-o` (command option only)

View File

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

303
doc/grid.md Normal file
View File

@ -0,0 +1,303 @@
(grid)=
# Grids in reciprocal space
The regular grid can be a conventional regular grid or a generalized regular
grid. Here the conventional regular grid means that the grids are cut parallel
to the reciprocal basis vectors. In most cases, the conventional regular grid is
used. In special case, e.g., for crystals with body center tetragonal symmetry,
the generalized regular grid can be useful. In phono3py, the generalized regular
grid is defined to be cut parallel to the reciprocal basis vectors of the
conventional unit cell.
Two types of grid data structure are used in phono3py. Normal regular grid
contains translationally unique grid points (regular grid). The other grid
includes the points on Brillouin zone (BZ) boundary (BZ grid).
## `BZGrid` class instance
Grid point information in reciprocal space is stored in the `BZGrid` class. This
class instance can be easily accessed in the following way.
```python
In [1]: import phono3py
In [2]: ph3 = phono3py.load("phono3py.yaml", produce_fc=False)
In [3]: ph3.mesh_numbers = [11, 11, 11]
In [4]: ph3.grid
Out[4]: <phono3py.phonon.grid.BZGrid at 0x1070f3b60>
```
It is recommended to read docstring in `BZGrid` by
```python
In [5]: help(ph3.grid)
```
Some attributes of this class are presented below.
```python
In [6]: ph3.grid.addresses.shape
Out[6]: (1367, 3)
In [7]: ph3.grid.D_diag
Out[7]: array([11, 11, 11])
In [8]: ph3.grid.P
Out[8]:
array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
In [9]: ph3.grid.Q
Out[9]:
array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
In [10]: ph3.grid.QDinv
Out[10]:
array([[0.09090909, 0. , 0. ],
[0. , 0.09090909, 0. ],
[0. , 0. , 0.09090909]])
In [11]: ph3.grid.PS
Out[11]: array([0, 0, 0])
```
The integer array `addresses` contains grid point addresses. Every grid point
address is represented by the unique series of three integers. These addresses
are converted to the q-points in fractional coordinates as explained in the
{ref}`section below<grid_address_to_q>`.
Unless generalized regular grid is employed, the other attributes are not
important. `D_diag` is equivalent to the three integer numbers of the specified
conventional regular grid. `P` and `Q` are are the left and right unimodular
matrix after Smith normal form: $\mathrm{D}=\mathrm{PAQ}$, respectively, where
$\mathrm{A}$ is the grid matrix. `D_diag` is the three diagonal elements of the
matrix $\mathrm{D}$. The grid matrix is usually a diagonal matrix, then
$\mathrm{P}$ and $\mathrm{Q}$ are chosen as identity matrix. `QDinv` is given
by $\mathrm{Q}\mathrm{D}^{-1}$. `PS` represents half-grid-shifts (usually always
`[0, 0, 0]` in phono3py).
## Find grid point index corresponding to grid point address
Grid point index corresponding to a grid point address is obtained using the
instance method `BZGrid.get_indices_from_addresses` as follows:
```python
In [1]: import phono3py
In [2]: ph3 = phono3py.load("phono3py_disp.yaml")
In [3]: ph3.mesh_numbers = [20, 20, 20]
In [4]: ph3.grid.get_indices_from_addresses([0, 10, 10])
Out[4]: 4448
```
This index number is different between phono3py version 1.x and 2.x.
To get the number corresponding to the phono3py version 1.x,
`store_dense_gp_map=False` should be specified in `phono3py.load`,
```python
In [5]: ph3 = phono3py.load("phono3py_disp.yaml", store_dense_gp_map=False)
In [6]: ph3.mesh_numbers = [20, 20, 20]
In [7]: ph3.grid.get_indices_from_addresses([0, 10, 10])
Out[7]: 4200
```
(grid_address_to_q)=
## q-points in fractional coordinates corresponding to grid addresses
For Gamma centered regular grid, q-points in fractional coordinates
are obtained by
```python
qpoints = addresses @ QDinv.T
```
For shifted regular grid (usually unused in phono3py),
```python
qpoints = (addresses * 2 + PS) @ (QDinv.T / 2.0)
```
The grid addresses are stored in `phonon-*.hdf5`. So for conventional
Gamma-centered regular grid, those information can be used to recover the
corresponding q-points. For example,
```python
In [1]: import h5py
In [2]: f = h5py.File("phonon-m111111.hdf5")
In [3]: import numpy as np
In [8]: f['grid_address'][:] @ np.diag(1.0 / f['mesh'][:])
Out[8]:
array([[ 0. , 0. , 0. ],
[ 0.09090909, 0. , 0. ],
[ 0.18181818, 0. , 0. ],
...,
[-0.27272727, -0.09090909, -0.09090909],
[-0.18181818, -0.09090909, -0.09090909],
[-0.09090909, -0.09090909, -0.09090909]], shape=(1367, 3))
```
(grid_triplets)=
## Grid point triplets
Three grid point indices are used to represent a q-point triplet. For example
the following command generates `gamma_detail-m111111-g5.hdf5`,
```bash
% phono3py-load phono3py.yaml --gp 5 --br --mesh 11 11 11 --write-gamma-detail
```
This file contains various information:
```python
In [1]: import h5py
In [2]: f = h5py.File("gamma_detail-m111111-g5.hdf5")
In [3]: list(f)
Out[3]:
['gamma_detail',
'grid_point',
'mesh',
'temperature',
'triplet',
'triplet_all',
'version',
'weight']
In [4]: f['gamma_detail'].shape
Out[4]: (101, 146, 6, 6, 6)
```
For the detailed analysis of contributions of triplets to imaginary part of
self energy a phonon mode of the grid point, it is necessary to understand the
data structure of `triplet` and `weight`.
```python
In [5]: f['triplet'].shape
Out[5]: (146, 3)
In [6]: f['weight'].shape
Out[6]: (146,)
In [7]: f['triplet'][:10]
Out[7]:
array([[ 5, 0, 6],
[ 5, 1, 5],
[ 5, 2, 4],
[ 5, 3, 3],
[ 5, 7, 10],
[ 5, 8, 9],
[ 5, 11, 118],
[ 5, 12, 117],
[ 5, 13, 116],
[ 5, 14, 115]])
```
The second index of `gamma_detail` corresponds to the first index of `triplet`.
Three integers of each triplet are the grid point indices, which means, the grid
addresses and their q-points are recovered by
```python
In [8]: import phono3py
In [9]: ph3 = phono3py.load("phono3py.yaml", produce_fc=False)
In [10]: ph3.mesh_numbers = [11, 11, 11]
In [11]: ph3.grid.addresses[f['triplet'][:10]]
Out[11]:
array([[[ 5, 0, 0],
[ 0, 0, 0],
[-5, 0, 0]],
[[ 5, 0, 0],
[ 1, 0, 0],
[ 5, 0, 0]],
[[ 5, 0, 0],
[ 2, 0, 0],
[ 4, 0, 0]],
[[ 5, 0, 0],
[ 3, 0, 0],
[ 3, 0, 0]],
[[ 5, 0, 0],
[-4, 0, 0],
[-1, 0, 0]],
[[ 5, 0, 0],
[-3, 0, 0],
[-2, 0, 0]],
[[ 5, 0, 0],
[ 0, 1, 0],
[-5, -1, 0]],
[[ 5, 0, 0],
[ 1, 1, 0],
[ 5, -1, 0]],
[[ 5, 0, 0],
[ 2, 1, 0],
[ 4, -1, 0]],
[[ 5, 0, 0],
[ 3, 1, 0],
[ 3, -1, 0]]])
n [14]: ph3.grid.addresses[f['triplet'][:10]] @ ph3.grid.QDinv
Out[14]:
array([[[ 0.45454545, 0. , 0. ],
[ 0. , 0. , 0. ],
[-0.45454545, 0. , 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0.09090909, 0. , 0. ],
[ 0.45454545, 0. , 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0.18181818, 0. , 0. ],
[ 0.36363636, 0. , 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0.27272727, 0. , 0. ],
[ 0.27272727, 0. , 0. ]],
[[ 0.45454545, 0. , 0. ],
[-0.36363636, 0. , 0. ],
[-0.09090909, 0. , 0. ]],
[[ 0.45454545, 0. , 0. ],
[-0.27272727, 0. , 0. ],
[-0.18181818, 0. , 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0. , 0.09090909, 0. ],
[-0.45454545, -0.09090909, 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0.09090909, 0.09090909, 0. ],
[ 0.45454545, -0.09090909, 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0.18181818, 0.09090909, 0. ],
[ 0.36363636, -0.09090909, 0. ]],
[[ 0.45454545, 0. , 0. ],
[ 0.27272727, 0.09090909, 0. ],
[ 0.27272727, -0.09090909, 0. ]]])
```

View File

@ -1,23 +1,18 @@
(hdf5_howto)=
# How to read the results stored in hdf5 files
# Using phono3py hdf5 files
```{contents}
:depth: 3
:local:
```
## Using `h5py` in ipython
## How to use HDF5 python library
It is assumed that `python-h5py` is installed on the computer you interactively
use. In the following, how to see the contents of `.hdf5` files in the
interactive mode of Python. The basic usage of reading `.hdf5` files using
`h5py` is found at
[here](http://docs.h5py.org/en/latest/high/dataset.html#reading-writing-data>).
In the following example, an MgO result of thermal conductivity calculation
stored in `kappa-m111111.hdf5` (see {ref}`iofile_kappa_hdf5`) is loaded and
thermal conductivity tensor at 300 K is watched.
It is assumed that `python-h5py` is installed on the computer you
interactively use. In the following, how to see the contents of
`.hdf5` files in the interactive mode of Python. The basic usage of
reading `.hdf5` files using `h5py` is found at [here](http://docs.h5py.org/en/latest/high/dataset.html#reading-writing-data>).
Usually for running interactive python, `ipython` is recommended to
use but not the plain python. In the following example, an MgO result
of thermal conductivity calculation is loaded and thermal conductivity
tensor at 300 K is watched.
```bash
```python
In [1]: import h5py
In [2]: f = h5py.File("kappa-m111111.hdf5")
@ -90,256 +85,3 @@ In [11]: g = np.where(g > 0, g, -1)
In [12]: lifetime = np.where(g > 0, 1.0 / (2 * 2 * np.pi * g), 0)
```
(kappa_hdf5_file)=
## Details of `kappa-*.hdf5` file
Files name, e.g. `kappa-m323220.hdf5`, is determined by some
specific options. `mxxx`, show the numbers of sampling
mesh. `sxxx` and `gxxx` appear optionally. `sxxx` gives the
smearing width in the smearing method for Brillouin zone integration
for phonon lifetime, and `gxxx` denotes the grid number. Using the
command option of `-o`, the file name can be modified slightly. For
example `-o nac` gives `kappa-m323220.nac.hdf5` to
memorize the option `--nac` was used.
Currently `kappa-*.hdf5` file (not for the specific grid points)
contains the properties shown below.
### mesh
(Versions 1.10.11 or later)
The numbers of mesh points for reciprocal space sampling along
reciprocal axes, $a^*, b^*, c^*$.
### frequency
Phonon frequencies. The physical unit is THz, where THz
is in the ordinal frequency not the angular frequency.
The array shape is (irreducible q-point, phonon band).
(kappa_hdf5_file_gamma)=
### gamma
Imaginary part of self energy of phonon bubble diagram (phonon-phonon
scattering). The physical unit is THz, where THz is in the ordinal frequency not
the angular frequency.
The array shape for all grid-points (irreducible q-points) is
(temperature, irreducible q-point, phonon band).
The array shape for a specific grid-point is
(temperature, phonon band).
Phonon lifetime ($\tau_\lambda=1/2\Gamma_\lambda(\omega_\lambda)$) may
be estimated from `gamma`. $2\pi$ has to be multiplied with
`gamma` values in the hdf5 file to convert the unit of ordinal
frequency to angular frequency. Zeros in `gamma` values mean that
those elements were not calculated such as for three acoustic modes at
$\Gamma$ point. The below is the copy-and-paste from the
previous section to show how to obtain phonon lifetime in pico
second:
```bash
In [8]: g = f['gamma'][30]
In [9]: import numpy as np
In [10]: g = np.where(g > 0, g, -1)
In [11]: lifetime = np.where(g > 0, 1.0 / (2 * 2 * np.pi * g), 0)
```
### gamma_isotope
Isotope scattering of $1/2\tau^\mathrm{iso}_\lambda$.
The physical unit is same as that of gamma.
The array shape is same as that of frequency.
### group_velocity
Phonon group velocity, $\nabla_\mathbf{q}\omega_\lambda$. The
physical unit is $\text{THz}\cdot\text{Angstrom}$, where THz
is in the ordinal frequency not the angular frequency.
The array shape is (irreducible q-point, phonon band, 3 = Cartesian coordinates).
### heat_capacity
Mode-heat-capacity defined by
$$
C_\lambda = k_\mathrm{B}
\left(\frac{\hbar\omega_\lambda}{k_\mathrm{B} T} \right)^2
\frac{\exp(\hbar\omega_\lambda/k_\mathrm{B}
T)}{[\exp(\hbar\omega_\lambda/k_\mathrm{B} T)-1]^2}.
$$
The physical unit is eV/K.
The array shape is (temperature, irreducible q-point, phonon band).
(output_kappa)=
### kappa
Thermal conductivity tensor. The physical unit is W/m-K.
The array shape is (temperature, 6 = (xx, yy, zz, yz, xz, xy)).
(output_mode_kappa)=
### mode-kappa
Thermal conductivity tensors at k-stars (${}^*\mathbf{k}$):
$$
\sum_{\mathbf{q} \in {}^*\mathbf{k}} \kappa_{\mathbf{q}j}.
$$
The sum of this over ${}^*\mathbf{k}$ corresponding to
irreducible q-points divided by number of grid points gives
$\kappa$ ({ref}`output_kappa`), e.g.,:
```python
kappa_xx_at_index_30 = mode_kappa[30, :, :, 0].sum()/ weight.sum()
```
Be careful that until version 1.12.7, mode-kappa values were divided
by number of grid points.
The physical unit is W/m-K. Each tensor element is the sum of tensor
elements on the members of ${}^*\mathbf{k}$, i.e., symmetrically
equivalent q-points by crystallographic point group and time reversal
symmetry.
The array shape is (temperature, irreducible q-point, phonon band, 6 =
(xx, yy, zz, yz, xz, xy)).
### gv_by_gv
Outer products of group velocities for k-stars
(${}^*\mathbf{k}$) for each irreducible q-point and phonon band
($j$):
$$
\sum_{\mathbf{q} \in {}^*\mathbf{k}} \mathbf{v}_{\mathbf{q}j} \otimes
\mathbf{v}_{\mathbf{q}j}.
$$
The physical unit is
$\text{THz}^2\cdot\text{Angstrom}^2$, where THz is in the
ordinal frequency not the angular frequency.
The array shape is (irreducible q-point, phonon band, 6 = (xx, yy, zz,
yz, xz, xy)).
### q-point
Irreducible q-points in reduced coordinates.
The array shape is (irreducible q-point, 3 = reduced
coordinates in reciprocal space).
### temperature
Temperatures where thermal conductivities are calculated. The physical
unit is K.
### weight
Weights corresponding to irreducible q-points. Sum of weights equals to
the number of mesh grid points.
### ave_pp
Averaged phonon-phonon interaction in $\text{eV}^2,
$P_{\mathbf{q}j}$:
$$
P_{\mathbf{q}j} = \frac{1}{(3n_\mathrm{a})^2} \sum_{\lambda'\lambda''}
|\Phi_{\lambda\lambda'\lambda''}|^2.
$$
This is not going to be calculated in the RTA thermal coductivity
calculation mode by default. To calculate this, `--full-pp` option
has to be specified (see {ref}`full_pp_option`).
### boundary_mfp
A value specified by {ref}`boundary_mfp_option`. The physical unit is
micrometer.
When `--boundary-mfp` option is explicitly specified, its value is stored here.
### kappa_unit_conversion
This is used to convert the physical unit of lattice thermal
conductivity made of `heat_capacity`, `group_velocity`, and
`gamma`, to W/m-K. In the single mode relaxation time (SMRT) method,
a mode contribution to the lattice thermal conductivity is given by
$$
\kappa_\lambda = \frac{1}{V_0} C_\lambda \mathbf{v}_\lambda \otimes
\mathbf{v}_\lambda \tau_\lambda^{\mathrm{SMRT}}.
$$
For example, $\kappa_{\lambda,{xx}}$ is calculated by:
```bash
In [1]: import h5py
In [2]: f = h5py.File("kappa-m111111.hdf5")
In [3]: kappa_unit_conversion = f['kappa_unit_conversion'][()]
In [4]: weight = f['weight'][:]
In [5]: heat_capacity = f['heat_capacity'][:]
In [6]: gv_by_gv = f['gv_by_gv'][:]
In [7]: gamma = f['gamma'][:]
In [8]: kappa_unit_conversion * heat_capacity[30, 2, 0] * gv_by_gv[2, 0] / (2 * gamma[30, 2, 0])
Out[8]:
array([ 1.02050241e+03, 1.02050241e+03, 1.02050241e+03,
4.40486382e-15, 0.00000000e+00, -4.40486382e-15])
In [9]: f['mode_kappa'][30, 2, 0]
Out[9]:
array([ 1.02050201e+03, 1.02050201e+03, 1.02050201e+03,
4.40486209e-15, 0.00000000e+00, -4.40486209e-15])
```
## How to know grid point index number corresponding to grid address
Running with `--write-gamma`, hdf5 files are written out with file names
such as `kappa-m202020-g4448.hdf5`. You may want to know the grid point
index number with given grid address. This is done as follows:
```bash
In [1]: import phono3py
In [2]: ph3 = phono3py.load("phono3py_disp.yaml")
In [3]: ph3.mesh_numbers = [20, 20, 20]
In [4]: ph3.grid.get_indices_from_addresses([0, 10, 10])
Out[4]: 4448
```
This index number is different between phono3py version 1.x and 2.x.
To get the number corresponding to the phono3py version 1.x,
`store_dense_gp_map=False` should be specified in `phono3py.load`,
```bash
In [5]: ph3 = phono3py.load("phono3py_disp.yaml", store_dense_gp_map=False)
In [6]: ph3.mesh_numbers = [20, 20, 20]
In [7]: ph3.grid.get_indices_from_addresses([0, 10, 10])
Out[7]: 4200
```

View File

@ -38,7 +38,6 @@ examples
Interfaces to calculators (VASP, QE, CRYSTAL, Abinit, TURBOMOLE) <interfaces>
command-options
input-output-files
hdf5_howto
auxiliary-tools
direct-solution
wigner-solution
@ -49,7 +48,8 @@ cutoff-pair
external-tools
phono3py-api
phono3py-load
tips
hdf5_howto
grid
citation
reference
changelog

View File

@ -2,34 +2,30 @@
# Input / Output files
```{contents}
:depth: 3
:local:
```
The calculation results are written into files. Mostly the data are stored in
HDF5 format, therefore how to read the data from HDF5 files is also shown.
## Intermediate text files
The following files are not compatible with phonopy. But phonopy's `FORCE_SETS`
file can be created using phono3py command options from the following files. See
the detail at {ref}`file_format_compatibility`.
### `phono3py_disp.yaml`
This is created with `-d` option. See {ref}`create_displacements_option`.
## `phono3py_disp.yaml`
This is created with {ref}`-d <create_displacements_option>` or
{ref}`--rd <random_displacements_option>` option.
This file contains displacement dataset and crystal structure information.
Parameters for non-analytical term correction can be also included.
(input-output_files_FORCES_FC3)=
## `phono3py_params.yaml`
### `FORCES_FC3`
This is created with `--cf3` option. See {ref}`cf3_option`.
This is created with the combination of {ref}`--cf3 <cf3_option>` and {ref}`--sp
<sp_option>` options. This file contains displacement-force dataset and crystal
structure information. In addition, energies of supercells may be included in
the dataset. Parameters for non-analytical term correction can be also included.
There are two formats of `FORCES_FC3`. The type-I format is like that shown
below
(iofile_FORCES_FC3)=
## `FORCES_FC3`
This is created with {ref}`--cf3 <cf3_option>` option . There are two formats of
`FORCES_FC3`. The type-I format is like that shown below
```
# File: 1
@ -79,22 +75,13 @@ The type-II format is the same as
[phonopy's type-II format](https://phonopy.github.io/phonopy/input-files.html#type-2)
of `FORCE_SETS`.
### `FORCES_FC2`
## `FORCES_FC2`
This is created with `--cf2` option. See {ref}`cf2_option` and
{ref}`dim_fc2_option`.
This is created with {ref}`--cf2 <dim_fc2_option>` option. The file formats
(type-I and type-II) are same as those of `FORCES_FC3`.
The file formats (type-I and type-II) are same as those of `FORCES_FC3`.
## HDF5 files
### `kappa-*.hdf5`
See the detail at {ref}`kappa_hdf5_file`.
(fc3_hdf5_file)=
### `fc3.hdf5`
(iofile_fc3_hdf5)=
## `fc3.hdf5`
Third order force constants (in real space) are stored in
$\mathrm{eV}/\text{Angstrom}^3$.
@ -145,9 +132,8 @@ $$
So what you have to set is `--pa="0 1/4 1/4 1/4 0 1/4 1/4 1/4 0"`.
(fc2_hdf5_file)=
### `fc2.hdf5`
(iofile_fc2_hdf5)=
## `fc2.hdf5`
Second order force constants are stored in $\mathrm{eV}/\text{Angstrom}^2$.
@ -159,29 +145,434 @@ in the shape of:
```
against $\Phi_{\alpha\beta}(l\kappa, l'\kappa')$. More detail is similar to the
case for {ref}`fc3_hdf5_file`.
case for {ref}`iofile_fc3_hdf5`.
### `gamma-*.hdf5`
(iofile_kappa_hdf5)=
## `kappa-*.hdf5`
Files name, e.g. `kappa-m323220.hdf5`, is determined by some
specific options. `mxxx`, show the numbers of sampling
mesh. `sxxx` and `gxxx` appear optionally. `sxxx` gives the
smearing width in the smearing method for Brillouin zone integration
for phonon lifetime, and `gxxx` denotes the grid number. Using the
command option of `-o`, the file name can be modified slightly. For
example `-o nac` gives `kappa-m323220.nac.hdf5` to
memorize the option `--nac` was used.
### `mesh`
(Versions 1.10.11 or later)
The numbers of mesh points for reciprocal space sampling along
reciprocal axes, $a^*, b^*, c^*$.
### `frequency`
Phonon frequencies. The physical unit is THz, where THz
is in the ordinal frequency not the angular frequency.
The array shape is (irreducible q-point, phonon band).
(iofile_kappa_hdf5_gamma)=
### `gamma`
Imaginary part of self energy of phonon bubble diagram (phonon-phonon
scattering). The physical unit is THz, where THz is in the ordinal frequency not
the angular frequency.
The array shape for all grid-points (irreducible q-points) is
(temperature, irreducible q-point, phonon band).
The array shape for a specific grid-point is
(temperature, phonon band).
Phonon lifetime ($\tau_\lambda=1/2\Gamma_\lambda(\omega_\lambda)$) may
be estimated from `gamma`. $2\pi$ has to be multiplied with
`gamma` values in the hdf5 file to convert the unit of ordinal
frequency to angular frequency. Zeros in `gamma` values mean that
those elements were not calculated such as for three acoustic modes at
$\Gamma$ point. The below is the copy-and-paste from the
previous section to show how to obtain phonon lifetime in pico
second:
```python
In [8]: g = f['gamma'][30]
In [9]: import numpy as np
In [10]: g = np.where(g > 0, g, -1)
In [11]: lifetime = np.where(g > 0, 1.0 / (2 * 2 * np.pi * g), 0)
```
### `gamma_isotope`
Isotope scattering of $1/2\tau^\mathrm{iso}_\lambda$.
The physical unit is same as that of gamma.
The array shape is same as that of frequency.
### `group_velocity`
Phonon group velocity, $\nabla_\mathbf{q}\omega_\lambda$. The
physical unit is $\text{THz}\cdot\text{Angstrom}$, where THz
is in the ordinal frequency not the angular frequency.
The array shape is (irreducible q-point, phonon band, 3 = Cartesian coordinates).
### `heat_capacity`
Mode-heat-capacity defined by
$$
C_\lambda = k_\mathrm{B}
\left(\frac{\hbar\omega_\lambda}{k_\mathrm{B} T} \right)^2
\frac{\exp(\hbar\omega_\lambda/k_\mathrm{B}
T)}{[\exp(\hbar\omega_\lambda/k_\mathrm{B} T)-1]^2}.
$$
The physical unit is eV/K.
The array shape is (temperature, irreducible q-point, phonon band).
(iofile_kappa_hdf5_kappa)=
### `kappa`
Thermal conductivity tensor. The physical unit is W/m-K.
The array shape is (temperature, 6 = (xx, yy, zz, yz, xz, xy)).
### `mode-kappa`
Thermal conductivity tensors at k-stars (${}^*\mathbf{k}$):
$$
\sum_{\mathbf{q} \in {}^*\mathbf{k}} \kappa_{\mathbf{q}j}.
$$
The sum of this over ${}^*\mathbf{k}$ corresponding to
irreducible q-points divided by number of grid points gives
$\kappa$ ({ref}`iofile_kappa_hdf5_kappa`), e.g.,:
```python
kappa_xx_at_index_30 = mode_kappa[30, :, :, 0].sum()/ weight.sum()
```
Be careful that until version 1.12.7, mode-kappa values were divided
by number of grid points.
The physical unit is W/m-K. Each tensor element is the sum of tensor
elements on the members of ${}^*\mathbf{k}$, i.e., symmetrically
equivalent q-points by crystallographic point group and time reversal
symmetry.
The array shape is (temperature, irreducible q-point, phonon band, 6 =
(xx, yy, zz, yz, xz, xy)).
### `gv_by_gv`
Outer products of group velocities for k-stars
(${}^*\mathbf{k}$) for each irreducible q-point and phonon band
($j$):
$$
\sum_{\mathbf{q} \in {}^*\mathbf{k}} \mathbf{v}_{\mathbf{q}j} \otimes
\mathbf{v}_{\mathbf{q}j}.
$$
The physical unit is
$\text{THz}^2\cdot\text{Angstrom}^2$, where THz is in the
ordinal frequency not the angular frequency.
The array shape is (irreducible q-point, phonon band, 6 = (xx, yy, zz,
yz, xz, xy)).
### `q-point`
Irreducible q-points in reduced coordinates.
The array shape is (irreducible q-point, 3 = reduced
coordinates in reciprocal space).
### `temperature`
Temperatures where thermal conductivities are calculated. The physical
unit is K.
### `weight`
Weights corresponding to irreducible q-points. Sum of weights equals to
the number of mesh grid points.
### `ave_pp`
Averaged phonon-phonon interaction $P_{\mathbf{q}j}$ in $\text{eV}^2$:
$$
P_{\mathbf{q}j} = \frac{1}{(3n_\mathrm{a})^2} \sum_{\lambda'\lambda''}
|\Phi_{\lambda\lambda'\lambda''}|^2.
$$
This is not going to be calculated in the RTA thermal coductivity
calculation mode by default. To calculate this, `--full-pp` option
has to be specified (see {ref}`full_pp_option`).
### `boundary_mfp`
A value specified by {ref}`boundary_mfp_option`. The physical unit is
micrometer.
When `--boundary-mfp` option is explicitly specified, its value is stored here.
### `kappa_unit_conversion`
This is used to convert the physical unit of lattice thermal
conductivity made of `heat_capacity`, `group_velocity`, and
`gamma`, to W/m-K. In the single mode relaxation time (SMRT) method,
a mode contribution to the lattice thermal conductivity is given by
$$
\kappa_\lambda = \frac{1}{V_0} C_\lambda \mathbf{v}_\lambda \otimes
\mathbf{v}_\lambda \tau_\lambda^{\mathrm{SMRT}}.
$$
For example, $\kappa_{\lambda,{xx}}$ is calculated by:
```python
In [1]: import h5py
In [2]: f = h5py.File("kappa-m111111.hdf5")
In [3]: kappa_unit_conversion = f['kappa_unit_conversion'][()]
In [4]: weight = f['weight'][:]
In [5]: heat_capacity = f['heat_capacity'][:]
In [6]: gv_by_gv = f['gv_by_gv'][:]
In [7]: gamma = f['gamma'][:]
In [8]: kappa_unit_conversion * heat_capacity[30, 2, 0] * gv_by_gv[2, 0] / (2 * gamma[30, 2, 0])
Out[8]:
array([ 1.02050241e+03, 1.02050241e+03, 1.02050241e+03,
4.40486382e-15, 0.00000000e+00, -4.40486382e-15])
In [9]: f['mode_kappa'][30, 2, 0]
Out[9]:
array([ 1.02050201e+03, 1.02050201e+03, 1.02050201e+03,
4.40486209e-15, 0.00000000e+00, -4.40486209e-15])
```
(iofile_kappa_hdf5_gamma_NU)=
### `gamma_N` and `gamma_U`
The data are stored in `kappa-mxxx(-gx-sx-sdx).hdf5` file and accessed by
`gamma_N` and `gamma_U` keys. The shape of the arrays is the same as that of
`gamma` (see {ref}`iofile_kappa_hdf5_gamma`). An example (Si-PBEsol) is shown
below:
```bash
% phono3py-load --mesh 11 11 11 --fc3 --fc2 --br --nu
...
% ipython
```
```python
In [1]: import h5py
In [2]: f = h5py.File("kappa-m111111.hdf5", 'r')
In [3]: list(f)
Out[3]:
['frequency',
'gamma',
'gamma_N',
'gamma_U',
'group_velocity',
'gv_by_gv',
'heat_capacity',
'kappa',
'kappa_unit_conversion',
'mesh',
'mode_kappa',
'qpoint',
'temperature',
'weight']
In [4]: f['gamma'].shape
Out[4]: (101, 56, 6)
In [5]: f['gamma_N'].shape
Out[5]: (101, 56, 6)
In [6]: f['gamma_U'].shape
Out[6]: (101, 56, 6)
```
## `gamma-*.hdf5`
Imaginary parts of self energies at harmonic phonon frequencies
($\Gamma_\lambda(\omega_\lambda)$ = half linewidths) are stored in THz. See
{ref}`write_gamma_option`.
### `gamma_detail-*.hdf5`
(iofile_gamma_detail_hdf5)=
## `gamma_detail-*.hdf5`
Q-point triplet contributions to imaginary parts of self energies at phonon
frequencies (half linewidths) are stored in THz. See
{ref}`write_detailed_gamma_option`.
{ref}`--write-gamma-detail <write_detailed_gamma_option>` option.
## Simple text file
In the output file in hdf5, following keys are used to extract the detailed
information.
### `gammas-*.dat`
```{table}
| dataset | Array shape |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| gamma_detail for `--ise` | (temperature, sampling frequency point, symmetry reduced set of triplets at given grid point, band1, band2, band3) in THz (without $2\pi$) |
| gamma_detail for `--br` | (temperature, symmetry reduced set of triplets at gvien grid point, band1, band2, band3) in THz (without $2\pi$) |
| mesh | Numbers of sampling mesh along reciprocal axes. |
| frequency_point for `--ise` | Sampling frequency points in THz (without $2\pi$), i.e., $\omega$ in $\Gamma_\lambda(\omega)$ |
| temperature | (temperature,), Temperatures in K |
| triplet | (symmetry reduced set of triplets at given grid point, 3), Triplets are given by the grid point indices (see below). |
| weight | (symmetry reduced set of triplets at given grid point,), Weight of each triplet to imaginary part of self energy |
| triplet_all | (triplets at given grid point, 3), symmetry non-reduced version of the triplet information. |
```
See {ref}`grid_triplets` to recover the q-points of each triplet.
Imaginary part of self energy (linewidth/2) is recovered by the following
script:
```python
import h5py
import numpy as np
gd = h5py.File("gamma_detail-mxxx-gx.hdf5")
temp_index = 30 # index of temperature
temperature = gd['temperature'][temp_index]
gamma_tp = gd['gamma_detail'][:].sum(axis=-1).sum(axis=-1)
weight = gd['weight'][:]
gamma = np.dot(weight, gamma_tp[temp_index])
```
For example, for `--br`, this `gamma` gives $\Gamma_\lambda(\omega_\lambda)$ of
the band indices at the grid point indicated by $\lambda$ at the temperature of
index 30. If any bands are degenerated, those `gamma` in
`kappa-mxxx-gx(-sx-sdx).hdf5` or `gamma-mxxx-gx(-sx-sdx).hdf5` type file are
averaged, but the `gamma` obtained here in this way are not symmetrized. Apart
from this symmetrization, the values must be equivalent between them.
To understand each contribution of triptle to imaginary part of self energy,
reading `phonon-mxxx.hdf5` is useful (see {ref}`write_phonon_option`). For
example, phonon triplets of three phonon scatterings are obtained by
```python
import h5py
import numpy as np
gd = h5py.File("gamma_detail-mxxx-gx.hdf5", 'r')
ph = h5py.File("phonon-mxxx.hdf5", 'r')
gp1 = gd['grid_point'][()]
triplets = gd['triplet'][:] # Sets of (gp1, gp2, gp3) where gp1 is fixed
mesh = gd['mesh'][:]
grid_address = ph['grid_address'][:]
q_triplets = grid_address[triplets] / mesh.astype('double') # For conventional regular grid
# Phonons of triplets[2]
phonon_tp = [(ph['frequency'][i], ph['eigenvector'][i]) for i in triplets[2]]
# Fractions of contributions of triplets at this grid point and temperature index 30
gamma_sum_over_bands = np.dot(weight, gd['gamma_detail'][30].sum(axis=-1).sum(axis=-1).sum(axis=-1))
contrib_tp = [gd['gamma_detail'][30, i].sum() / gamma_sum_over_bands for i in range(len(weight))]
np.dot(weight, contrib_tp) # is one
```
(iofile_phonon_hdf5)=
## `phonon-*.hdf5`
Contents of `phonon-mxxx.hdf5` are watched by:
```python
In [1]: import h5py
In [2]: f = h5py.File("phonon-m111111.hdf5")
In [3]: list(f)
Out[3]:
['eigenvector',
'frequency',
'grid_address',
'ir_grid_points',
'ir_grid_weights',
'mesh',
'version']
In [4]: f['mesh'][:]
Out[4]: array([11, 11, 11])
In [5]: f['grid_address'].shape
Out[5]: (1367, 3)
In [6]: f['frequency'].shape
Out[6]: (1367, 6)
In [7]: f['eigenvector'].shape
Out[7]: (1367, 6, 6)
In [8]: f['ir_grid_points'].shape
Out[8]: (56,)
```
The first axis of `ph['grid_address']`, `ph['frequency']`, and
`ph['eigenvector']` corresponds to the number of q-points where phonons are
calculated. Here the number of phonons may not be equal to product of mesh
numbers ($1367 \neq 11^3$). This is because all q-points on Brillouin zone
boundary are included, i.e., even if multiple q-points are translationally
equivalent, those phonons are stored separately though these phonons are
physically equivalent within the equations employed in phono3py. Here Brillouin
zone is defined by WignerSeitz cell of reciprocal primitive basis vectors. This
is convenient to categorize phonon triplets into Umklapp and Normal scatterings
based on the Brillouin zone.
## `pp-*.hdf5`
This file contains phonon-phonon interaction strength
$\bigl|\Phi_{\lambda\lambda'\lambda''}\bigl|^2$. To use the data in this
file, it is recommended to generate with `--full-pp` option because the data
structure to access becomes simpler.
```bash
% phono3py-load phono3py.yaml --gp 5 --br --mesh 11 11 11 --write-pp --full-pp
```
```python
In [1]: import h5py
In [2]: f = h5py.File("pp-m111111-g5.hdf5")
In [3]: list(f)
Out[3]: ['pp', 'triplet', 'triplet_all', 'version', 'weight']
In [4]: f['pp'].shape
Out[4]: (146, 6, 6, 6)
```
Indices of the `pp` array are (symmetry reduced set of triplets at given grid
point, band1, band2, band3), and the values are given in $\text{eV}^2$. See
{ref}`grid_triplets` to recover the q-points of each triplet.
Except for `pp`, all the other information are equivalent to those found in
{ref}`iofile_gamma_detail_hdf5`.
## `gammas-*.dat`
Imaginary parts of self energies with respect to frequency
$\Gamma_\lambda(\omega)$ are stored in THz. See {ref}`ise_option`.
### `jdos-*.dat`
## `jdos-*.dat`
Joint densities of states are stored in Thz. See {ref}`jdos_option`.
### `linewidth-*.dat`
## `linewidth-*.dat`

View File

@ -148,7 +148,7 @@ shape of `(num_supercells, num_atoms_in_supercell, 3)`. In the above example,
the array shape is `(1254, 72, 3)`.
If the calculated force sets are stored in the
{ref}`input-output_files_FORCES_FC3` file, the numpy array of `forces` is
{ref}`iofile_FORCES_FC3` file, the numpy array of `forces` is
obtained by
```python

View File

@ -2,8 +2,6 @@
# Force constants calculation using pypolymlp (machine learning potential)
**This is an experimental feature.**
With the `--pypolymlp` option, phono3py can interface with the polynomial
machine learning potential (MLP) code,
[pypolymlp](https://github.com/sekocha/pypolymlp), to perform training and
@ -20,8 +18,8 @@ For further details on combining phono3py calculations with pypolymlp, refer to
[[doi](https://doi.org/10.1063/5.0211296)]
[[arxiv](https://arxiv.org/abs/2401.17531)].
An example of its usage can be found in the `example/NaCl-pypolymlp` directory
in the distribution from GitHub or PyPI.
Examples of its usage can be found in the `example/NaCl-pypolymlp` and
`example/AlN-rd` directories in the distribution from GitHub or PyPI.
## Citation of pypolymlp
@ -42,15 +40,12 @@ in the distribution from GitHub or PyPI.
## Requirements
- [pypolymlp](https://github.com/sekocha/pypolymlp) < 0.9
- [pypolymlp](https://github.com/sekocha/pypolymlp)
For linux (x86-64), a compiled package of pypolymlp can be installed via
conda-forge (recommended). Otherwise, pypolymlp can be installed from
source-code.
From pypolymlp 0.9, its file format was changed. This will be taken care of
by the future version of phonopy and phono3py.
## How to calculate
### Workflow
@ -156,28 +151,28 @@ obtained by
```
``````
### Step 4: Development of MLPs
The `phono3py_params.yaml` file contains the training data required for
developing polynomial MLPs when running with the `--pypolymlp` option.
```
% phono3py-load --pypolymlp phono3py_params.yaml
phono3py-load --pypolymlp phono3py_params.yaml
_ _____
_ __ | |__ ___ _ __ ___|___ / _ __ _ _
| '_ \| '_ \ / _ \| '_ \ / _ \ |_ \| '_ \| | | |
| |_) | | | | (_) | | | | (_) |__) | |_) | |_| |
| .__/|_| |_|\___/|_| |_|\___/____/| .__/ \__, |
|_| |_| |___/
3.11.3
3.18.0
-------------------------[time 2025-01-19 18:02:40]-------------------------
-------------------------[time 2025-07-26 13:59:10]-------------------------
Compiled with OpenMP support (max 10 threads).
Running in phono3py.load mode.
Python version 3.12.6
Spglib version 2.5.0
Python version 3.13.3
Spglib version 2.6.1
----------------------------- General settings -----------------------------
Run mode: pypolymlp
HDF5 data compression filter: gzip
Crystal structure was read from "phono3py_params.yaml".
Supercell (dim): [2 2 2]
@ -190,6 +185,7 @@ Use -v option to watch primitive cell, unit cell, and supercell structures.
NAC parameters were read from "phono3py_params.yaml".
Displacement dataset for fc3 was read from "phono3py_params.yaml".
----------------------------- pypolymlp start ------------------------------
Pypolymlp version 0.12.9
Pypolymlp is a generator of polynomial machine learning potentials.
Please cite the paper: A. Seko, J. Appl. Phys. 133, 011101 (2023).
Pypolymlp is developed at https://github.com/sekocha/pypolymlp.
@ -202,26 +198,11 @@ Parameters:
gaussian_params1: (1.0, 1.0, 1)
gaussian_params2: (0.0, 7.0, 10)
Developing MLPs by pypolymlp...
Regression: cholesky decomposition ...
- alpha: 0.001
- alpha: 0.01
- alpha: 0.1
- alpha: 1.0
- alpha: 10.0
Clear training X.T @ X
Calculate X.T @ X for test data
Clear test X.T @ X
Regression: model selection ...
- alpha = 1.000e-03 : rmse (train, test) = 1.12211e+15 1.12211e+15
- alpha = 1.000e-02 : rmse (train, test) = 1.12211e+15 1.12211e+15
- alpha = 1.000e-01 : rmse (train, test) = 0.00002 0.00002
- alpha = 1.000e+00 : rmse (train, test) = 0.00002 0.00002
- alpha = 1.000e+01 : rmse (train, test) = 0.00002 0.00002
MLPs were written into "polymlp.yaml"
------------------------------ pypolymlp end -------------------------------
Generate displacements (--rd or -d) for proceeding to phonon calculations.
Summary of calculation was written in "phono3py.yaml".
-------------------------[time 2025-01-19 18:03:44]-------------------------
-------------------------[time 2025-07-26 14:00:12]-------------------------
_
___ _ __ __| |
/ _ \ '_ \ / _` |
@ -252,14 +233,15 @@ structure file.
| |_) | | | | (_) | | | | (_) |__) | |_) | |_| |
| .__/|_| |_|\___/|_| |_|\___/____/| .__/ \__, |
|_| |_| |___/
3.11.3
3.18.0
-------------------------[time 2025-01-19 18:06:46]-------------------------
-------------------------[time 2025-07-26 14:00:49]-------------------------
Compiled with OpenMP support (max 10 threads).
Running in phono3py.load mode.
Python version 3.12.6
Spglib version 2.5.0
Python version 3.13.3
Spglib version 2.6.1
----------------------------- General settings -----------------------------
Run mode: pypolymlp + force constants
HDF5 data compression filter: gzip
Crystal structure was read from "phono3py.yaml".
Supercell (dim): [2 2 2]
@ -271,6 +253,7 @@ Spacegroup: Fm-3m (225)
Use -v option to watch primitive cell, unit cell, and supercell structures.
NAC parameters were read from "phono3py.yaml".
----------------------------- pypolymlp start ------------------------------
Pypolymlp version 0.12.9
Pypolymlp is a generator of polynomial machine learning potentials.
Please cite the paper: A. Seko, J. Appl. Phys. 133, 011101 (2023).
Pypolymlp is developed at https://github.com/sekocha/pypolymlp.
@ -279,6 +262,7 @@ Load MLPs from "polymlp.yaml".
Generate displacements
Displacement distance: 0.01
Evaluate forces in 292 supercells by pypolymlp
Dataset generated using MLPs was written in "phono3py_mlp_eval_dataset.yaml".
----------------------------- Force constants ------------------------------
Computing fc3[ 1, x, x ] using numpy.linalg.pinv.
Displacements (in Angstrom):
@ -289,16 +273,26 @@ Displacements (in Angstrom):
[ 0.0100 0.0000 0.0000]
[-0.0100 0.0000 0.0000]
Expanding fc3.
fc3 was symmetrized.
fc2 was symmetrized.
Max drift of fc3: 0.000000 (zzz) 0.000000 (zzz) 0.000000 (zzz)
Max drift of fc2: 0.000000 (zz) 0.000000 (zz)
Symmetrizing fc3 by symfc projector.
Symfc version 1.5.3 (https://github.com/symfc/symfc)
Citation: A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024)
Symmetrizing fc2 by symfc projector.
Symfc version 1.5.3 (https://github.com/symfc/symfc)
Citation: A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024)
Max drift of fc3: 0.00000000 (zyz) 0.00000000 (yzz) 0.00000000 (yzz)
Max drift of fc2: -0.00000000 (yy) -0.00000000 (yy)
fc3 was written into "fc3.hdf5".
fc2 was written into "fc2.hdf5".
--------------------------- Calculation settings ---------------------------
Non-analytical term correction (NAC): True
NAC unit conversion factor: 14.39965
BZ integration: Tetrahedron-method
Temperatures: 0.0 300.0
Cutoff frequency: 0.01
Frequency conversion factor to THz: 15.63330
----------- None of ph-ph interaction calculation was performed. -----------
Dataset generated using MLPs was written in "phono3py_mlp_eval_dataset.yaml".
Summary of calculation was written in "phono3py.yaml".
-------------------------[time 2025-01-19 18:06:55]-------------------------
-------------------------[time 2025-07-26 14:00:58]-------------------------
_
___ _ __ __| |
/ _ \ '_ \ / _` |
@ -315,29 +309,28 @@ corresponding forces are stored in the `phono3py_mlp_eval_dataset.yaml` file.
### Steps 5-7: Force constants calculation (random displacements in step 5)
Random displacements are generated by specifying {ref}`--rd
<random_displacements_option>` option. To compute force constants with random
displacements, an external force constants calculator is necessary. By default,
symfc is used unless another force constants solver is explicitly specified.
When running with the `--pypolymlp` option, MLPs are read from `polymlp.yaml`
if the file exists. In this case, training data is no longer required, and files
such as `phono3py.yaml` can be used as the input structure file.
<random_displacements_option>` option. When running with the `--pypolymlp`
option, MLPs are read from `polymlp.yaml` if the file exists. In this case,
training data is no longer required, and files such as `phono3py.yaml` can be
used as the input structure file.
```
% phono3py-load --pypolymlp --rd 200 phono3py.yaml
% phono3py-load --pypolymlp --rd auto phono3py.yaml
_ _____
_ __ | |__ ___ _ __ ___|___ / _ __ _ _
| '_ \| '_ \ / _ \| '_ \ / _ \ |_ \| '_ \| | | |
| |_) | | | | (_) | | | | (_) |__) | |_) | |_| |
| .__/|_| |_|\___/|_| |_|\___/____/| .__/ \__, |
|_| |_| |___/
3.11.3
3.18.0
-------------------------[time 2025-01-19 18:13:21]-------------------------
-------------------------[time 2025-07-26 14:02:24]-------------------------
Compiled with OpenMP support (max 10 threads).
Running in phono3py.load mode.
Python version 3.12.6
Spglib version 2.5.0
Python version 3.13.3
Spglib version 2.6.1
----------------------------- General settings -----------------------------
Run mode: pypolymlp + force constants
HDF5 data compression filter: gzip
Crystal structure was read from "phono3py.yaml".
Supercell (dim): [2 2 2]
@ -349,6 +342,7 @@ Spacegroup: Fm-3m (225)
Use -v option to watch primitive cell, unit cell, and supercell structures.
NAC parameters were read from "phono3py.yaml".
----------------------------- pypolymlp start ------------------------------
Pypolymlp version 0.12.9
Pypolymlp is a generator of polynomial machine learning potentials.
Please cite the paper: A. Seko, J. Appl. Phys. 133, 011101 (2023).
Pypolymlp is developed at https://github.com/sekocha/pypolymlp.
@ -357,31 +351,34 @@ Load MLPs from "polymlp.yaml".
Generate random displacements
Twice of number of snapshots will be generated for plus-minus displacements.
Displacement distance: 0.01
Evaluate forces in 400 supercells by pypolymlp
Evaluate forces in 32 supercells by pypolymlp
Dataset generated using MLPs was written in "phono3py_mlp_eval_dataset.yaml".
----------------------------- Force constants ------------------------------
Symfc will be used to handle general (or random) displacements.
Type-II dataset for displacements and forces was provided,
but the selected force constants calculator cannot process it.
Use another force constants calculator, e.g., symfc,
to generate force constants.
Try symfc to handle general (or random) displacements.
-------------------------------- Symfc start -------------------------------
Symfc is a force constants calculator. See the following paper:
A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024).
Symfc is developed at https://github.com/symfc/symfc.
Symfc version 1.5.3 (https://github.com/symfc/symfc)
Citation: A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024)
Computing [2, 3] order force constants.
Increase log-level to watch detailed symfc log.
--------------------------------- Symfc end --------------------------------
-------------------------------- Symfc start -------------------------------
Symfc is a force constants calculator. See the following paper:
A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024).
Symfc is developed at https://github.com/symfc/symfc.
Computing [2] order force constants.
Increase log-level to watch detailed symfc log.
--------------------------------- Symfc end --------------------------------
Max drift of fc3: -0.000000 (zxz) -0.000000 (xzz) -0.000000 (xzz)
Max drift of fc2: -0.000000 (yy) -0.000000 (yy)
Max drift of fc3: -0.00000000 (xyx) -0.00000000 (yxx) -0.00000000 (yxx)
Max drift of fc2: -0.00000000 (yy) -0.00000000 (yy)
fc3 was written into "fc3.hdf5".
fc2 was written into "fc2.hdf5".
--------------------------- Calculation settings ---------------------------
Non-analytical term correction (NAC): True
NAC unit conversion factor: 14.39965
BZ integration: Tetrahedron-method
Temperatures: 0.0 300.0
Cutoff frequency: 0.01
Frequency conversion factor to THz: 15.63330
----------- None of ph-ph interaction calculation was performed. -----------
Dataset generated using MLPs was written in "phono3py_mlp_eval_dataset.yaml".
Summary of calculation was written in "phono3py.yaml".
-------------------------[time 2025-01-19 18:13:34]-------------------------
-------------------------[time 2025-07-26 14:02:29]-------------------------
_
___ _ __ __| |
/ _ \ '_ \ / _` |
@ -389,88 +386,121 @@ Summary of calculation was written in "phono3py.yaml".
\___|_| |_|\__,_|
```
After the MLPs are read, 200 supercells with random directional displacements
are generated. These displacements are then inverted (such as $\Delta
\mathbf{u}_i$ and $-\Delta \mathbf{u}_i$ of all atoms $i$ in each supercell),
resulting in an additional 200 supercells. In total, 400 supercells are created.
The forces for these supercells are then evaluated. Finally, the force constants
are calculated using symfc.
After the MLPs are read, 16 supercells with random directional displacements are
generated by the option `--rd auto`. These displacements are then inverted (such
as $\Delta \mathbf{u}_i$ and $-\Delta \mathbf{u}_i$ of all atoms $i$ in each
supercell), resulting in an additional 16 supercells. In total, 32 supercells
are created. The forces for these supercells are then evaluated. Finally, the
force constants are calculated using symfc. The `--rd-auto-factor` option can
change the number of supercells generated.
### Command options for force constants calculation
## Command options for force constants calculation
After obtaining the MLPs, displacements are generated using those MLPs, and the
resulting forces are computed accordingly. The displacement distance is set by
the `--amplitude` option, whose default value is 0.01 Angstrom. When the `--rd`
option is used, it specifies the number of supercells with random directional
displacements. Note that to achieve accurate force constants, the actual number
of generated supercells is twice the specified number. If `--rd` is omitted,
systematic displacements are introduced.
After obtaining the MLPs, displacements are generated using these MLPs, and the
resulting forces are computed. The displacement distance is controlled by the
`--amplitude` option, with a default value of 0.01 Angstrom. When `-d` is
specified, systematic displacements are introduced. When the `--rd` option is
used, it specifies the number of supercells with random directional
displacements. To ensure accurate force constants, the actual number of
generated supercells is twice the specified value.
Once the file `polymlp.yaml` is obtained, force constants can be calculated
using MLPs from `polymlp.yaml`. After removing the `fc3.hdf5` and `fc2.hdf5`
files, `phono3py-load` will detect `polymlp.yaml` and then compute the force
constants by loading the MLPs from `polymlp.yaml` as follows:
When atoms in the unit cell have positional degrees of freedom within the
crystal symmetry, the `--relax-atomic-positions` option can relax their
positions using MLPs. In the `example/AlN-rd` case, the following command
develops polynomial MLPs and then relaxes atomic positions using these MLPs. The
force constants are calculated using supercells with 0.005 Angstrom systematic
displacements.
```
% phono3py-load --pypolymlp --rd 100 --amplitude 0.005 phono3py.yaml
% phono3py-load phonopy_params_mp-661.yaml.xz --pypolymlp --relax-atomic-positions -d
_ _____
_ __ | |__ ___ _ __ ___|___ / _ __ _ _
| '_ \| '_ \ / _ \| '_ \ / _ \ |_ \| '_ \| | | |
| |_) | | | | (_) | | | | (_) |__) | |_) | |_| |
| .__/|_| |_|\___/|_| |_|\___/____/| .__/ \__, |
|_| |_| |___/
3.11.3
3.18.0-dev21+ge26f3ecb
-------------------------[time 2025-01-19 18:19:02]-------------------------
-------------------------[time 2025-07-26 14:29:16]-------------------------
Compiled with OpenMP support (max 10 threads).
Running in phono3py.load mode.
Python version 3.12.6
Spglib version 2.5.0
Python version 3.13.3
Spglib version 2.6.1
----------------------------- General settings -----------------------------
Run mode: pypolymlp + force constants
HDF5 data compression filter: gzip
Crystal structure was read from "phono3py.yaml".
Supercell (dim): [2 2 2]
Crystal structure was read from "phonopy_params_mp-661.yaml.xz".
Supercell (dim): [4 4 2]
Primitive matrix:
[0. 0.5 0.5]
[0.5 0. 0.5]
[0.5 0.5 0. ]
Spacegroup: Fm-3m (225)
[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]
Spacegroup: P6_3mc (186)
Use -v option to watch primitive cell, unit cell, and supercell structures.
NAC parameters were read from "phono3py.yaml".
NAC parameters were read from "phonopy_params_mp-661.yaml.xz".
Displacement dataset for fc3 was read from "phonopy_params_mp-661.yaml.xz".
----------------------------- pypolymlp start ------------------------------
Pypolymlp version 0.12.9.post0
Pypolymlp is a generator of polynomial machine learning potentials.
Please cite the paper: A. Seko, J. Appl. Phys. 133, 011101 (2023).
Pypolymlp is developed at https://github.com/sekocha/pypolymlp.
Load MLPs from "polymlp.yaml".
Parameters:
cutoff: 8.0
model_type: 3
max_p: 2
gtinv_order: 3
gtinv_maxl: (8, 8)
gaussian_params1: (1.0, 1.0, 1)
gaussian_params2: (0.0, 7.0, 10)
Developing MLPs by pypolymlp...
MLPs were written into "polymlp.yaml"
------------------------------ pypolymlp end -------------------------------
Generate random displacements
Twice of number of snapshots will be generated for plus-minus displacements.
Relaxing atomic positions using polynomial MLPs...
Change in fractional position and in distance:
1 N : 0.00000000 0.00000000 -0.00000021 (|d|=0.00000105)
2 N : 0.00000000 0.00000000 -0.00000021 (|d|=0.00000105)
3 Al: 0.00000000 0.00000000 0.00000021 (|d|=0.00000105)
4 Al: 0.00000000 0.00000000 0.00000021 (|d|=0.00000105)
----------------------------------------------------------------------------
Generate displacements
Displacement distance: 0.005
Evaluate forces in 200 supercells by pypolymlp
Evaluate forces in 3720 supercells by pypolymlp
Dataset generated using MLPs was written in "phono3py_mlp_eval_dataset.yaml".
----------------------------- Force constants ------------------------------
Symfc will be used to handle general (or random) displacements.
-------------------------------- Symfc start -------------------------------
Symfc is a force constants calculator. See the following paper:
A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024).
Symfc is developed at https://github.com/symfc/symfc.
Computing [2, 3] order force constants.
Increase log-level to watch detailed symfc log.
--------------------------------- Symfc end --------------------------------
-------------------------------- Symfc start -------------------------------
Symfc is a force constants calculator. See the following paper:
A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024).
Symfc is developed at https://github.com/symfc/symfc.
Computing [2] order force constants.
Increase log-level to watch detailed symfc log.
--------------------------------- Symfc end --------------------------------
Max drift of fc3: -0.000000 (zxz) -0.000000 (xzz) -0.000000 (xzz)
Max drift of fc2: -0.000000 (yy) -0.000000 (yy)
Computing fc3[ 1, x, x ] using numpy.linalg.pinv.
Displacements (in Angstrom):
[ 0.0050 0.0000 0.0000]
[-0.0050 0.0000 0.0000]
[ 0.0000 0.0000 0.0050]
[ 0.0000 0.0000 -0.0050]
Computing fc3[ 65, x, x ] using numpy.linalg.pinv.
Displacements (in Angstrom):
[ 0.0050 0.0000 0.0000]
[-0.0050 0.0000 0.0000]
[ 0.0000 0.0000 0.0050]
[ 0.0000 0.0000 -0.0050]
Expanding fc3.
Symmetrizing fc3 by symfc projector.
Symfc version 1.5.3 (https://github.com/symfc/symfc)
Citation: A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024)
Symmetrizing fc2 by symfc projector.
Symfc version 1.5.3 (https://github.com/symfc/symfc)
Citation: A. Seko and A. Togo, Phys. Rev. B, 110, 214302 (2024)
Max drift of fc3: -0.00000000 (xxz) -0.00000000 (xxz) -0.00000000 (xzx)
Max drift of fc2: -0.00000000 (xx) -0.00000000 (xx)
fc3 was written into "fc3.hdf5".
fc2 was written into "fc2.hdf5".
--------------------------- Calculation settings ---------------------------
Non-analytical term correction (NAC): True
NAC unit conversion factor: 14.39965
BZ integration: Tetrahedron-method
Temperatures: 0.0 300.0
Cutoff frequency: 0.01
Frequency conversion factor to THz: 15.63330
----------- None of ph-ph interaction calculation was performed. -----------
Dataset generated using MLPs was written in "phono3py_mlp_eval_dataset.yaml".
Summary of calculation was written in "phono3py.yaml".
-------------------------[time 2025-01-19 18:19:09]-------------------------
-------------------------[time 2025-07-26 14:39:11]-------------------------
_
___ _ __ __| |
/ _ \ '_ \ / _` |
@ -478,7 +508,6 @@ Summary of calculation was written in "phono3py.yaml".
\___|_| |_|\__,_|
```
## Parameters for developing MLPs
A few parameters can be specified using the `--mlp-params` option for the

View File

@ -1,113 +0,0 @@
(tips)=
# Tips
```{contents}
:depth: 2
:local:
```
(brillouinzone_sum)=
## Brillouin zone summation
Brillouin zone (BZ) summations appear at different two points in
phonon lifetime calculation. First it is used for the Fourier
transform of force constants, and second to obtain imaginary part of
phonon-self-energy. For the summation, usually uniform sampling meshes
are employed. To obtain more accurate result, it is always better to
use denser meshes. But the denser mesh requires more computational
demand.
The second BZ summation contains delta functions. In
phono3py calculation, a linear tetrahedron method ({ref}`thm option <thm_option>`, default option) and a smearing method ({ref}`sigma option <sigma_option>`) can be used for this BZ
integration. In most cases, the tetrahedron method is better. Especially in high
thermal conductivity materials, the smearing method results in
underestimation of lattice thermal conductivity.
The figure below shows Si thermal conductivity convergence with
respect to number of mesh points along an axis from n=19 to 65. This
is calculated with RTA and the linear tetrahedron method. Within the
methods and phono3py implementation, it is converging at around n=55,
however this computational demand is not trivial. As far as observing
this result, an extrapolation to $1/n \rightarrow 0$ seems not a
good idea, since it gives overestimation in the case of this Si
example. This plot tells that we have to decide how much value is
acceptable as lattice thermal conductivity value. Therefore it
important to describe the number of sampling mesh and method of BZ
integration to let other people reproduce the computational results.
```{image} Si-convergence.png
:width: 25%
```
In case the smearing method is necessary to use, the convergence of
q-point mesh together with smearing width has to be checked
carefully. Since smearing parameter is used to approximate delta
functions, small `sigma` value is better to describe the detailed
structure of three-phonon-space, however it requires a denser sampling
mesh to converge the result. To check the convergence with respect to
the `sigma` value, multiple sigma values can be set. This can be
computationally efficient, since it is avoided to re-calculate
phonon-phonon interaction strength for different `sigma` values in
this case. Convergence with respect to the sampling mesh and smearing
parameter strongly depends on materials. For Si example, a
$20\times 20\times 20$ sampling mesh (or 8000 reducible sampling
points) and 0.1 THz smearing value for reciprocal of the volume of an
atom may be a good starting choice. The tetrahedron method requires no
such parameter as the smearing width.
## Importance of numerical quality of force constants
Third-order force constants (fc3) are much weaker to numerical noise
of a force calculator than second-order force constants
(fc2). Therefore supercell force calculations have to be done very
carefully.
Numerical quality of forces given by force calculators is the most
important factor for the numerical quality of lattice thermal
conductivity calculation. We may be able to apply symmetry constraints
to force constants, however even if force constants fulfill those
symmetries, the numerical quality of force constants is not guaranteed
since elements of force constants just suffice the symmetries but most
of those intensities are not constrained.
It is important to use the best possible force calculator in the
possibly best way. The knowledge of the force calculator from the
theory and method to the practical usage is required to obtain
good results of lattice thermal conductivity calculation.
In the following, a few things that may be good to know are
presented.
### A practical way to check lattice thermal conductivity result
Some feeling whether our calculation result is OK or not may be
obtained by comparing lattice thermal conductivities calculated with
and without {ref}`symmetrizations of force constants <symmetrization_option>`. If they are enough different, e.g., more
than twice different, it is better to re-consider about the force
calculation. In the case of DFT calculations, the choice of input
settings such as k-point sampling mesh, plane-wave energy cutoff, and
exchange-correlational potential, etc, should be reconsidered.
### Displacement distance of atoms
The phono3py default displacement distance is 0.03
$\text{Angstrom}$. In some cases, accurate result may not be obtained
due to the numerical noise of the force calculator. Usually increasing
the displacement distance by the {ref}`amplitude option <amplitude_option>` reduces the numerical noise, but as its drawback
higher order anharmonicity is involved (renormalized) into fc3 and fc2.
(file_format_compatibility)=
## File format compatibility with phonopy
- `FORCES_FC3` and `FORCES_FC2` are not
compatible with phonopy's `FORCE_SETS`.
- `FORCE_SETS` can be created using {ref}`--cfs <cfs_option>` from
`FORCES_FC3` and `phono3py_disp.yaml` or `FORCES_FC2` and
`phono3py_disp.yaml` (needs to specify `--dim-fc2`).
- `FORCES_FC2` can be created using {ref}`--fs2f2 <fs2f2_option>` from `FORCE_SETS`.
- `fc2.hdf5` can be used in phonopy in the `hdf5` mode when it is
renamed to `force_constants.hdf5`. In the previous combinations of
phonopy and phono3py, depending on the physical unit of force
constants of calculators, the direct compatibility is not guaranteed.

63
example/AlN-rd/README.md Normal file
View File

@ -0,0 +1,63 @@
# AlN lattice thermal conductivity calculation from dataset for pypolymlp
## Computational setting of VASP calculations
For supercell forces and energies
- Supercell 4x4x2 of wurtzite unit cell
- Random directional displacements of 0.03 Angstrom
- PBE-sol
- 520 eV cutoff energy
- Gamma centered 2x2x2 kpoint mesh
- LREAL = .FALSE.
- ADDGRID = .TRUE.
For parameters of non-analytical term correction,
- PBE-sol
- 520 eV cutoff energy
- Gamma centered 7x7x4 kpoint mesh
- LEPSION = .TRUE.
- LREAL = .FALSE.
These data are stored in `phonopy_params_mp-661.yaml.xz`.
## Example of lattice thermal conductivity calculation
MLPs by pypolymlp are developed by
```bash
% phono3py-load phonopy_params_mp-661.yaml.xz --pypolymlp -v
```
Dataset with 180 supercells is used for training and 20 for the test. This
calculation will take 5-10 minutes depending on computer resource.
`pypolymlp.yaml` is made by this command.
Force constants are calculated by
```bash
% phono3py-load phonopy_params_mp-661.yaml.xz --pypolymlp --relax-atomic-positions -d
```
With the `--relax-atomic-positions` option, internal atomic positions in unit
cell are optimized by pypolymlp. The displacement-force dataset is stored in
`phono3py_mlp_eval_dataset.yaml`. Force constants are symmetried using symfc,
but the phono3py's traditional symmetrizer can be used with the option
`--fc-calculator traditional`. The symmetry constraints applied by this
traditional symmetrizer is weaker, but the calculation demands less memory
space.
Lattice thermal conductivity is calculated by
```bash
% phono3py-load phonopy_params_mp-661.yaml.xz --mesh 40 --br
```
Steps written above are performed in one-shot by
```bash
% phono3py-load phonopy_params_mp-661.yaml.xz --pypolymlp --relax-atomic-positions -d --mesh 40 --br
```
The lattice thermal conductivity calculated at 300 K will be around k_xx=252 and k_zz=232.

Binary file not shown.

View File

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

View File

@ -34,8 +34,16 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from phono3py.api_isotope import Phono3pyIsotope # noqa F401
from phono3py.api_jointdos import Phono3pyJointDos # noqa F401
from phono3py.api_phono3py import Phono3py # noqa F401
from phono3py.cui.load import load # noqa F401
from phono3py.version import __version__ # noqa F401
from phono3py.api_isotope import Phono3pyIsotope
from phono3py.api_jointdos import Phono3pyJointDos
from phono3py.api_phono3py import Phono3py
from phono3py.cui.load import load
from phono3py.version import __version__
__all__ = [
"Phono3pyIsotope",
"Phono3pyJointDos",
"Phono3py",
"load",
"__version__",
]

View File

@ -37,6 +37,7 @@
import numpy as np
from phono3py.other.isotope import Isotope
from phono3py.phonon.grid import BZGrid
class Phono3pyIsotope:
@ -81,7 +82,7 @@ class Phono3pyIsotope:
return self._iso.dynamical_matrix
@property
def grid(self):
def grid(self) -> BZGrid:
"""Return BZGrid class instance."""
return self._iso.bz_grid

View File

@ -34,7 +34,10 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import numpy as np
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix
from phonopy.physical_units import get_physical_units
from phonopy.structure.cells import Primitive, Supercell
from phonopy.structure.symmetry import Symmetry
@ -116,7 +119,7 @@ class Phono3pyJointDos:
self.initialize(mesh)
@property
def grid(self):
def grid(self) -> BZGrid | None:
"""Return BZGrid class instance."""
return self._bz_grid
@ -293,7 +296,7 @@ class Phono3pyJointDos:
print('JDOS is written into "%s".' % filename)
@property
def dynamical_matrix(self):
def dynamical_matrix(self) -> DynamicalMatrix:
"""Return DynamicalMatrix class instance."""
return self._jdos.dynamical_matrix

File diff suppressed because it is too large Load Diff

View File

@ -34,18 +34,24 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import textwrap
import warnings
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, Union
from typing import Optional
import numpy as np
from numpy.typing import ArrayLike, NDArray
from phonopy.phonon.group_velocity import GroupVelocity
from phonopy.phonon.thermal_properties import mode_cv
from phonopy.physical_units import get_physical_units
from phono3py.other.isotope import Isotope
from phono3py.phonon.grid import get_grid_points_by_rotations, get_ir_grid_points
from phono3py.phonon.grid import (
get_grid_points_by_rotations,
get_ir_grid_points,
get_qpoints_from_bz_grid_points,
)
from phono3py.phonon3.collision_matrix import CollisionMatrix
from phono3py.phonon3.imag_self_energy import ImagSelfEnergy
from phono3py.phonon3.interaction import Interaction
@ -63,12 +69,135 @@ def get_unit_to_WmK() -> float:
return unit_to_WmK
class HeatCapacityMixIn:
"""Heat capacity mix-in.
def get_multiplicity_at_q(
gp: int,
pp: Interaction,
point_operations: np.ndarray,
) -> int:
"""Return multiplicity (order of site-symmetry) of q-point."""
q = get_qpoints_from_bz_grid_points(gp, pp.bz_grid)
reclat = np.linalg.inv(pp.primitive.cell)
multi = 0
for q_rot in [np.dot(r, q) for r in point_operations]:
diff = q - q_rot
diff -= np.rint(diff)
dist = np.linalg.norm(np.dot(reclat, diff))
if dist < pp.primitive_symmetry.tolerance:
multi += 1
return multi
Used by other mix-in.
def get_kstar_order(
grid_weight: int, multi: int, point_operations: np.ndarray, verbose: bool = False
) -> int:
"""Return order (number of arms) of kstar.
multi : int
Multiplicity of grid point.
"""
order_kstar = len(point_operations) // multi
if order_kstar != grid_weight:
if verbose:
text = (
"Number of elements in k* is unequal "
"to number of equivalent grid-points. "
"This means that the mesh sampling grids break "
"symmetry. Please check carefully "
"the convergence over grid point densities."
)
msg = textwrap.fill(
text, initial_indent=" ", subsequent_indent=" ", width=70
)
print("*" * 30 + "Warning" + "*" * 30)
print(msg)
print("*" * 67)
return order_kstar
def get_heat_capacities(
grid_point: int,
pp: Interaction,
temperatures: NDArray[np.float64],
):
"""Return mode heat capacity.
cv returned should be given to self._cv by
self._cv[:, i_data, :] = cv
"""
if not pp.phonon_all_done:
raise RuntimeError(
"Phonon calculation has not been done yet. "
"Run phono3py.run_phonon_solver() before this method."
)
frequencies, _, _ = pp.get_phonons()
freqs = (
frequencies[grid_point][pp.band_indices] # type: ignore
* get_physical_units().THzToEv
)
cutoff = pp.cutoff_frequency * get_physical_units().THzToEv
cv = np.zeros((len(temperatures), len(freqs)), dtype="double")
# x=freq/T has to be small enough to avoid overflow of exp(x).
# x < 100 is the hard-corded criterion.
# Otherwise just set 0.
for i, f in enumerate(freqs):
if f > cutoff:
condition = f < 100 * temperatures * get_physical_units().KB
cv[:, i] = np.where(
condition,
mode_cv(np.where(condition, temperatures, 10000), f), # type: ignore
0,
)
return cv
class ConductivityComponentsBase(ABC):
"""Base class of ConductivityComponents."""
def __init__(
self,
pp: Interaction,
grid_points: NDArray[np.int64],
grid_weights: NDArray[np.int64],
point_operations: NDArray[np.int64],
rotations_cartesian: NDArray[np.int64],
temperatures: Optional[NDArray[np.float64]] = None,
average_gv_over_kstar: bool = False,
is_kappa_star: bool = True,
gv_delta_q: float | None = None,
is_reducible_collision_matrix: bool = False,
log_level: int = 0,
):
"""Init method.
Parameters
----------
gv_delta_q : float, optional, default is None, # for group velocity
With non-analytical correction, group velocity is calculated
by central finite difference method. This value gives the distance
in both directions in 1/Angstrom. The default value will be 1e-5.
"""
self._pp = pp
self._grid_points = grid_points
self._grid_weights = grid_weights
self._point_operations = point_operations
self._rotations_cartesian = rotations_cartesian
self._temperatures = temperatures
self._average_gv_over_kstar = average_gv_over_kstar
self._gv_delta_q = gv_delta_q
self._is_kappa_star = is_kappa_star
self._is_reducible_collision_matrix = is_reducible_collision_matrix
self._log_level = log_level
self._gv: np.ndarray
self._cv: np.ndarray
self._num_sampling_grid_points = 0
@property
def mode_heat_capacities(self):
@ -79,87 +208,6 @@ class HeatCapacityMixIn:
"""
return self._cv
def get_mode_heat_capacities(self):
"""Return mode heat capacity at constant volume at grid points.
Grid points are those at mode kappa are calculated.
"""
warnings.warn(
"Use attribute, Conductivity.mode_heat_capacities "
"instead of Conductivity.get_mode_heat_capacities().",
DeprecationWarning,
stacklevel=2,
)
return self.mode_heat_capacities
def _set_cv(self, i_gp, i_data):
"""Set mode heat capacity.
The array has to be allocated somewhere out of the mix-in.
self._cv = np.zeros(
(num_temp, num_grid_points, num_band0), order="C", dtype="double"
)
"""
grid_point = self._grid_points[i_gp]
freqs = (
self._frequencies[grid_point][self._pp.band_indices]
* get_physical_units().THzToEv
)
cutoff = self._pp.cutoff_frequency * get_physical_units().THzToEv
cv = np.zeros((len(self._temperatures), len(freqs)), dtype="double")
# x=freq/T has to be small enough to avoid overflow of exp(x).
# x < 100 is the hard-corded criterion.
# Otherwise just set 0.
for i, f in enumerate(freqs):
if f > cutoff:
condition = f < 100 * self._temperatures * get_physical_units().KB
cv[:, i] = np.where(
condition,
mode_cv(np.where(condition, self._temperatures, 10000), f),
0,
)
self._cv[:, i_data, :] = cv
class ConductivityMixIn(HeatCapacityMixIn):
"""Thermal conductivity mix-in.
Used by ConductivityRTA and ConductivityLBTE.
"""
@property
def kappa(self):
"""Return kappa."""
return self._kappa
def get_kappa(self):
"""Return kappa."""
warnings.warn(
"Use attribute, Conductivity.kappa instead of Conductivity.get_kappa().",
DeprecationWarning,
stacklevel=2,
)
return self.kappa
@property
def mode_kappa(self):
"""Return mode_kappa."""
return self._mode_kappa
def get_mode_kappa(self):
"""Return mode_kappa."""
warnings.warn(
"Use attribute, Conductivity.mode_kappa "
"instead of Conductivity.get_mode_kappa().",
DeprecationWarning,
stacklevel=2,
)
return self.mode_kappa
@property
def group_velocities(self):
"""Return group velocities at grid points.
@ -169,36 +217,96 @@ class ConductivityMixIn(HeatCapacityMixIn):
"""
return self._gv
def get_group_velocities(self):
"""Return group velocities at grid points.
Grid points are those at mode kappa are calculated.
"""
warnings.warn(
"Use attribute, Conductivity.group_velocities "
"instead of Conductivity.get_group_velocities().",
DeprecationWarning,
stacklevel=2,
)
return self.group_velocities
@property
def gv_delta_q(self):
"""Return delta q for group velocity."""
return self._gv_delta_q
@property
def gv_by_gv(self):
"""Return gv_by_gv at grid points where mode kappa are calculated."""
return self._gv_sum2
def number_of_sampling_grid_points(self):
"""Return number of grid points.
def get_gv_by_gv(self):
"""Return gv_by_gv at grid points where mode kappa are calculated."""
warnings.warn(
"Use attribute, Conductivity.gv_by_gv "
"instead of Conductivity.get_gv_by_gv().",
DeprecationWarning,
stacklevel=2,
This is calculated by the sum of numbers of arms of k-start.
"""
return self._num_sampling_grid_points
def set_heat_capacities(self, i_gp, i_data):
"""Set heat capacity at grid point and at data location."""
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
cv = get_heat_capacities(self._grid_points[i_gp], self._pp, self._temperatures)
self._cv[:, i_data, :] = cv
@abstractmethod
def set_velocities(self, i_gp, i_data):
"""Set velocities at grid point and at data location."""
raise NotImplementedError()
def _allocate_values(self):
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
num_band0 = len(self._pp.band_indices)
if self._is_reducible_collision_matrix:
num_grid_points = np.prod(self._pp.mesh_numbers)
else:
num_grid_points = len(self._grid_points)
num_temp = len(self._temperatures)
self._cv = np.zeros(
(num_temp, num_grid_points, num_band0), order="C", dtype="double"
)
return self.gv_by_gv
self._gv = np.zeros((num_grid_points, num_band0, 3), order="C", dtype="double")
def _init_velocity(self, gv_delta_q):
class ConductivityComponents(ConductivityComponentsBase):
"""Thermal conductivity components.
Used by ConductivityRTA and ConductivityLBTE.
"""
def __init__(
self,
pp: Interaction,
grid_points: NDArray[np.int64],
grid_weights: NDArray[np.int64],
point_operations: NDArray[np.int64],
rotations_cartesian: NDArray[np.int64],
temperatures: Optional[NDArray[np.float64]] = None,
average_gv_over_kstar: bool = False,
is_kappa_star: bool = True,
gv_delta_q: Optional[float] = None,
is_reducible_collision_matrix: bool = False,
log_level: int = 0,
):
"""Init method."""
super().__init__(
pp,
grid_points,
grid_weights,
point_operations,
rotations_cartesian,
temperatures=temperatures,
average_gv_over_kstar=average_gv_over_kstar,
is_kappa_star=is_kappa_star,
gv_delta_q=gv_delta_q,
is_reducible_collision_matrix=is_reducible_collision_matrix,
log_level=log_level,
)
self._gv_by_gv: NDArray
if self._pp.dynamical_matrix is None:
raise RuntimeError("Interaction.init_dynamical_matrix() has to be called.")
self._velocity_obj = GroupVelocity(
self._pp.dynamical_matrix,
q_length=gv_delta_q,
@ -206,7 +314,16 @@ class ConductivityMixIn(HeatCapacityMixIn):
frequency_factor_to_THz=self._pp.frequency_factor_to_THz,
)
def _set_velocities(self, i_gp, i_data):
if self._temperatures is not None:
self._allocate_values()
@property
def gv_by_gv(self) -> NDArray:
"""Return gv_by_gv at grid points where mode kappa are calculated."""
return self._gv_by_gv
def set_velocities(self, i_gp, i_data):
"""Set group velocities at grid point and at data location."""
self._gv[i_data] = self._get_gv(i_gp)
self._set_gv_by_gv(i_gp, i_data)
@ -222,8 +339,11 @@ class ConductivityMixIn(HeatCapacityMixIn):
unique_gps = np.unique(gps_rotated)
gvs = {}
for bz_gp in unique_gps.tolist(): # To convert to int type.
self._velocity_obj.run([self._get_qpoint_from_gp_index(bz_gp)])
for bz_gp in unique_gps:
self._velocity_obj.run(
[get_qpoints_from_bz_grid_points(bz_gp, self._pp.bz_grid)]
)
assert self._velocity_obj.group_velocities is not None
gvs[bz_gp] = self._velocity_obj.group_velocities[
0, self._pp.band_indices, :
]
@ -232,7 +352,10 @@ class ConductivityMixIn(HeatCapacityMixIn):
gv += np.dot(gvs[bz_gp], r) # = dot(r_inv, gv)
return gv / len(self._point_operations)
else:
self._velocity_obj.run([self._get_qpoint_from_gp_index(irgp)])
self._velocity_obj.run(
[get_qpoints_from_bz_grid_points(irgp, self._pp.bz_grid)]
)
assert self._velocity_obj.group_velocities is not None
return self._velocity_obj.group_velocities[0, self._pp.band_indices, :]
def _set_gv_by_gv(self, i_gp, i_data):
@ -246,17 +369,42 @@ class ConductivityMixIn(HeatCapacityMixIn):
# Sum all vxv at k*
for j, vxv in enumerate(([0, 0], [1, 1], [2, 2], [1, 2], [0, 2], [0, 1])):
self._gv_sum2[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]
self._gv_by_gv[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]
def _get_gv_by_gv(self, i_gp, i_data):
multi = self._get_multiplicity_at_q(i_gp)
if self._is_kappa_star:
multi = get_multiplicity_at_q(
self._grid_points[i_gp], # type: ignore
self._pp,
self._point_operations,
)
else:
multi = 1
gv = self._gv[i_data]
gv_by_gv = np.zeros((len(gv), 3, 3), dtype="double")
for r in self._rotations_cartesian:
gvs_rot = np.dot(gv, r.T)
gv_by_gv += [np.outer(r_gv, r_gv) for r_gv in gvs_rot]
gv_by_gv /= multi
return gv_by_gv, self._get_kstar_order(i_gp, multi)
kstar_order = get_kstar_order(
self._grid_weights[i_gp], # type: ignore
multi,
self._point_operations,
verbose=self._log_level > 0,
)
return gv_by_gv, kstar_order
def _allocate_values(self):
super()._allocate_values()
num_band0 = len(self._pp.band_indices)
if self._is_reducible_collision_matrix:
num_grid_points = np.prod(self._pp.mesh_numbers)
else:
num_grid_points = len(self._grid_points)
self._gv_by_gv = np.zeros(
(num_grid_points, num_band0, 6), order="C", dtype="double"
)
class ConductivityBase(ABC):
@ -264,8 +412,6 @@ class ConductivityBase(ABC):
All Conductivity* classes have to inherit this base class.
self._gv has to be allocated in the inherited classes.
"""
_average_gv_over_kstar = False
@ -273,17 +419,16 @@ class ConductivityBase(ABC):
def __init__(
self,
interaction: Interaction,
grid_points=None,
temperatures: Optional[Union[List, np.ndarray]] = None,
sigmas: Optional[Union[List, np.ndarray]] = None,
sigma_cutoff: Optional[float] = None,
grid_points: ArrayLike | None = None,
temperatures: ArrayLike | None = None,
sigmas: ArrayLike | None = None,
sigma_cutoff: float | None = None,
is_isotope=False,
mass_variances: Optional[Union[List, np.ndarray]] = None,
boundary_mfp: Optional[float] = None,
is_kappa_star=True,
gv_delta_q=None,
is_full_pp=False,
log_level=0,
mass_variances: ArrayLike | None = None,
boundary_mfp: float | None = None,
is_kappa_star: bool = True,
is_full_pp: bool = False,
log_level: int = 0,
):
"""Init method.
@ -319,10 +464,6 @@ class ConductivityBase(ABC):
iterating over specific grid points. With `is_kappa_star=True`
and `grid_points=None`, ir-grid points are used for the iteration.
Default is True.
gv_delta_q : float, optional, default is None, # for group velocity
With non-analytical correction, group velocity is calculated
by central finite difference method. This value gives the distance
in both directions in 1/Angstrom. The default value will be 1e-5.
is_full_pp : bool, optional, default is False
With True, full elements of phonon-phonon interaction strength
are computed. However with tetrahedron method, part of them are
@ -347,17 +488,14 @@ class ConductivityBase(ABC):
self._ir_grid_points,
self._grid_weights,
) = self._get_grid_info(grid_points)
self._grid_point_count: int = 0
self._num_sampling_grid_points: int = 0
self._grid_point_count = 0
self._sigmas: List
if sigmas is None:
self._sigmas = []
else:
self._sigmas = list(sigmas)
self._sigma_cutoff = sigma_cutoff
self._collision: Union[ImagSelfEnergy, CollisionMatrix]
self._temperatures: Optional[np.ndarray]
self._collision: ImagSelfEnergy | CollisionMatrix
if temperatures is None:
self._temperatures = None
else:
@ -370,7 +508,7 @@ class ConductivityBase(ABC):
self._eigenvectors,
self._phonon_done,
) = self._pp.get_phonons()
if (self._phonon_done == 0).any():
if not self._pp.phonon_all_done:
self._pp.run_phonon_solver()
self._is_isotope = is_isotope
@ -385,20 +523,13 @@ class ConductivityBase(ABC):
self._read_gamma_iso = False
# Allocated in self._allocate_values.
self._gv: np.ndarray
self._gamma: np.ndarray
self._gamma_iso: Optional[np.ndarray] = None
volume = self._pp.primitive.volume
self._conversion_factor = get_unit_to_WmK() / volume
self._conversion_factor = get_unit_to_WmK() / self._pp.primitive.volume
self._averaged_pp_interaction = None
# `self._velocity_obj` is the instance of an inherited class of
# `GroupVelocity`. `self._init_velocity()` is the method setup the instance,
# which must be implemented in the inherited class of `ConductivityBase`.
self._velocity_obj: GroupVelocity
self._init_velocity(gv_delta_q)
self._conductivity_components: ConductivityComponentsBase
def __iter__(self):
"""Calculate mode kappa at each grid point."""
@ -418,21 +549,29 @@ class ConductivityBase(ABC):
self._grid_point_count += 1
return self._grid_point_count - 1
@property
def mode_heat_capacities(self):
"""Return mode heat capacity at constant volume at grid points.
Grid points are those at mode kappa are calculated.
"""
return self._conductivity_components.mode_heat_capacities
@property
def group_velocities(self):
"""Return group velocities at grid points.
Grid points are those at mode kappa are calculated.
"""
return self._conductivity_components.group_velocities
@property
def mesh_numbers(self):
"""Return mesh numbers of GR-grid."""
return self._pp.mesh_numbers
def get_mesh_numbers(self):
"""Return mesh numbers of GR-grid."""
warnings.warn(
"Use attribute, Conductivity.mesh_numbers "
"instead of Conductivity.get_mesh_numbers().",
DeprecationWarning,
stacklevel=2,
)
return self.mesh_numbers
@property
def bz_grid(self):
"""Return GR-grid."""
@ -445,41 +584,18 @@ class ConductivityBase(ABC):
Grid points are those at mode kappa are calculated.
"""
assert self._frequencies is not None
return self._frequencies[self._grid_points]
def get_frequencies(self):
"""Return frequencies at grid points.
Grid points are those at mode kappa are calculated.
"""
warnings.warn(
"Use attribute, Conductivity.frequencies "
"instead of Conductivity.get_frequencies().",
DeprecationWarning,
stacklevel=2,
)
return self.frequencies
@property
def qpoints(self):
"""Return q-points where mode kappa are calculated."""
return np.array(
self._get_qpoint_from_gp_index(self._grid_points),
get_qpoints_from_bz_grid_points(self._grid_points, self._pp.bz_grid),
dtype="double",
order="C",
)
def get_qpoints(self):
"""Return q-points where mode kappa are calculated."""
warnings.warn(
"Use attribute, Conductivity.qpoints "
"instead of Conductivity.get_qpoints().",
DeprecationWarning,
stacklevel=2,
)
return self.qpoints
@property
def grid_points(self):
"""Return grid point indices where mode kappa are calculated.
@ -489,35 +605,11 @@ class ConductivityBase(ABC):
"""
return self._grid_points
def get_grid_points(self):
"""Return grid point indices where mode kappa are calculated.
Grid point indices are given in BZ-grid.
"""
warnings.warn(
"Use attribute, Conductivity.grid_points "
"instead of Conductivity.get_grid_points().",
DeprecationWarning,
stacklevel=2,
)
return self.grid_points
@property
def grid_weights(self):
"""Return grid point weights where mode kappa are calculated."""
return self._grid_weights
def get_grid_weights(self):
"""Return grid point weights where mode kappa are calculated."""
warnings.warn(
"Use attribute, Conductivity.grid_weights "
"instead of Conductivity.get_grid_weights().",
DeprecationWarning,
stacklevel=2,
)
return self.grid_weights
@property
def temperatures(self):
"""Setter and getter of temperatures."""
@ -528,26 +620,6 @@ class ConductivityBase(ABC):
self._temperatures = np.array(temperatures, dtype="double")
self._allocate_values()
def get_temperatures(self):
"""Return temperatures."""
warnings.warn(
"Use attribute, Conductivity.temperatures "
"instead of Conductivity.get_temperatures().",
DeprecationWarning,
stacklevel=2,
)
return self.temperatures
def set_temperatures(self, temperatures):
"""Set temperatures."""
warnings.warn(
"Use attribute, Conductivity.temperatures "
"instead of Conductivity.set_temperatures().",
DeprecationWarning,
stacklevel=2,
)
self.temperatures = temperatures
@property
def gamma(self):
"""Setter and getter of gamma."""
@ -558,24 +630,6 @@ class ConductivityBase(ABC):
self._gamma = gamma
self._read_gamma = True
def get_gamma(self):
"""Return gamma."""
warnings.warn(
"Use attribute, Conductivity.gamma instead of Conductivity.get_gamma().",
DeprecationWarning,
stacklevel=2,
)
return self.gamma
def set_gamma(self, gamma):
"""Set gamma."""
warnings.warn(
"Use attribute, Conductivity.gamma instead of Conductivity.set_gamma().",
DeprecationWarning,
stacklevel=2,
)
self.gamma = gamma
@property
def gamma_isotope(self):
"""Setter and getter of gamma from isotope."""
@ -586,100 +640,42 @@ class ConductivityBase(ABC):
self._gamma_iso = gamma_iso
self._read_gamma_iso = True
def get_gamma_isotope(self):
"""Return gamma from isotope."""
warnings.warn(
"Use attribute, Conductivity.gamma_isotope "
"instead of Conductivity.get_gamma_isotope().",
DeprecationWarning,
stacklevel=2,
)
return self.gamma_isotope
def set_gamma_isotope(self, gamma_iso):
"""Set gamma from isotope."""
warnings.warn(
"Use attribute, Conductivity.gamma_isotope "
"instead of Conductivity.set_gamma_isotope().",
DeprecationWarning,
stacklevel=2,
)
self.gamma_isotope = gamma_iso
@property
def sigmas(self):
"""Return sigmas."""
return self._sigmas
def get_sigmas(self):
"""Return sigmas."""
warnings.warn(
"Use attribute, Conductivity.sigmas instead of Conductivity.get_sigmas().",
DeprecationWarning,
stacklevel=2,
)
return self.sigmas
@property
def sigma_cutoff_width(self):
"""Return smearing width cutoff."""
return self._sigma_cutoff
def get_sigma_cutoff_width(self):
"""Return smearing width cutoff."""
warnings.warn(
"Use attribute, Conductivity.sigma_cutoff_width "
"instead of Conductivity.get_sigma_cutoff_width().",
DeprecationWarning,
stacklevel=2,
)
return self.sigma_cutoff_width
@property
def grid_point_count(self):
"""Return iterator count of self."""
return self._grid_point_count
def get_grid_point_count(self):
"""Return iterator count of self."""
warnings.warn(
"Use attribute, Conductivity.grid_point_count "
"instead of Conductivity.get_grid_point_count().",
DeprecationWarning,
stacklevel=2,
)
return self.grid_point_count
@property
def averaged_pp_interaction(self):
"""Return averaged pp strength."""
return self._averaged_pp_interaction
def get_averaged_pp_interaction(self):
"""Return averaged pp interaction strength."""
warnings.warn(
"Use attribute, Conductivity.averaged_pp_interaction "
"instead of Conductivity.get_averaged_pp_interaction().",
DeprecationWarning,
stacklevel=2,
)
return self.averaged_pp_interaction
@property
def boundary_mfp(self) -> float:
def boundary_mfp(self) -> Optional[float]:
"""Return boundary MFP."""
return self._boundary_mfp
def get_number_of_sampling_grid_points(self):
@property
def number_of_sampling_grid_points(self):
"""Return number of grid points.
This is calculated by the sum of numbers of arms of k-start in
`Conductivity._set_gv_by_gv`.
"""
return self._num_sampling_grid_points
return self._conductivity_components.number_of_sampling_grid_points
def _get_point_operations(self) -> Tuple[np.ndarray, np.ndarray]:
def _get_point_operations(self) -> tuple[np.ndarray, np.ndarray]:
"""Return reciprocal point group operations.
Returns
@ -705,7 +701,7 @@ class ConductivityBase(ABC):
return point_operations, rotations_cartesian
def _get_grid_info(self, grid_points) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
def _get_grid_info(self, grid_points) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Return grid point information in BZGrid.
Returns
@ -738,47 +734,22 @@ class ConductivityBase(ABC):
@abstractmethod
def _run_at_grid_point(self):
"""Run at conductivity calculation at specified grid point.
Should be implemented in Conductivity* class.
"""
"""Run at conductivity calculation at specified grid point."""
raise NotImplementedError()
@abstractmethod
def _allocate_values(self):
"""Allocate necessary data arrays.
Should be implemented in Conductivity* class.
"""
"""Allocate necessary data arrays."""
raise NotImplementedError()
@abstractmethod
def _set_velocities(self, i_gp, i_data):
"""Set velocities at grid point and at data location.
Should be implemented in Conductivity*MixIn.
"""
raise NotImplementedError()
@abstractmethod
def _init_velocity(self, gv_delta_q):
"""Initialize velocity class instance.
Should be implemented in Conductivity*MixIn.
"""
"""Set velocities at grid point and at data location."""
raise NotImplementedError()
@abstractmethod
def _set_cv(self, i_gp, i_data):
"""Set heat capacity at grid point and at data location.
Should be implemented in Conductivity*MixIn.
"""
"""Set heat capacity at grid point and at data location."""
raise NotImplementedError()
def _get_ir_grid_points(self, grid_points):
@ -802,60 +773,6 @@ class ConductivityBase(ABC):
return ir_grid_points, grid_weights
def _get_qpoint_from_gp_index(self, i_gps):
"""Return q-point(s) in reduced coordinates of grid point(s).
Parameters
----------
i_gps : int or ndarray
BZ-grid index (int) or indices (ndarray).
"""
return np.dot(self._pp.bz_grid.addresses[i_gps], self._pp.bz_grid.QDinv.T)
def _get_multiplicity_at_q(self, i_gp):
"""Return multiplicity (order of site-symmetry) of q-point."""
if self._is_kappa_star:
q = self._get_qpoint_from_gp_index(self._grid_points[i_gp])
reclat = np.linalg.inv(self._pp.primitive.cell)
multi = 0
for q_rot in [np.dot(r, q) for r in self._point_operations]:
diff = q - q_rot
diff -= np.rint(diff)
dist = np.linalg.norm(np.dot(reclat, diff))
if dist < self._pp.primitive_symmetry.tolerance:
multi += 1
else:
multi = 1
return multi
def _get_kstar_order(self, i_gp, multi):
"""Return order (number of arms) of kstar.
multi : int
Multiplicity of q-point of `i_gp`, which can be obtained by
`self._get_multiplicity_at_q(i_gp)`.
"""
order_kstar = len(self._point_operations) // multi
if order_kstar != self._grid_weights[i_gp]:
if self._log_level:
text = (
"Number of elements in k* is unequal "
"to number of equivalent grid-points. "
"This means that the mesh sampling grids break "
"symmetry. Please check carefully "
"the convergence over grid point densities."
)
msg = textwrap.fill(
text, initial_indent=" ", subsequent_indent=" ", width=70
)
print("*" * 30 + "Warning" + "*" * 30)
print(msg)
print("*" * 67)
return order_kstar
def _get_gamma_isotope_at_sigmas(self, i):
gamma_iso = []
for sigma in self._sigmas:
@ -906,7 +823,7 @@ class ConductivityBase(ABC):
num_band = len(self._pp.primitive) * 3
g_boundary = np.zeros(num_band, dtype="double")
try:
gv = self._gv
gv = self._conductivity_components.group_velocities
except AttributeError:
print("(_get_boundary_scattering) _gv has to be implemented.")
return g_boundary
@ -927,7 +844,7 @@ class ConductivityBase(ABC):
"======================= Grid point %d (%d/%d) "
"=======================" % (bzgp, i_gp + 1, len(self._grid_points))
)
qpoint = self._get_qpoint_from_gp_index(bzgp)
qpoint = get_qpoints_from_bz_grid_points(bzgp, self._pp.bz_grid)
print("q-point: (%5.2f %5.2f %5.2f)" % tuple(qpoint))
if self._boundary_mfp is not None:
if self._boundary_mfp > 1000:

View File

@ -34,11 +34,14 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import warnings
from __future__ import annotations
import os
import numpy as np
from numpy.typing import ArrayLike
from phono3py.conductivity.base import ConductivityMixIn
from phono3py.conductivity.base import ConductivityComponents
from phono3py.conductivity.direct_solution_base import (
ConductivityLBTEBase,
diagonalize_collision_matrix,
@ -46,38 +49,37 @@ from phono3py.conductivity.direct_solution_base import (
from phono3py.phonon3.interaction import Interaction
class ConductivityLBTE(ConductivityMixIn, ConductivityLBTEBase):
class ConductivityLBTE(ConductivityLBTEBase):
"""Lattice thermal conductivity calculation by direct solution."""
def __init__(
self,
interaction: Interaction,
grid_points=None,
temperatures=None,
sigmas=None,
sigma_cutoff=None,
is_isotope=False,
mass_variances=None,
boundary_mfp=None,
solve_collective_phonon=False,
is_reducible_collision_matrix=False,
is_kappa_star=True,
gv_delta_q=None,
is_full_pp=False,
read_pp=False,
pp_filename=None,
pinv_cutoff=1.0e-8,
pinv_solver=0,
pinv_method=0,
log_level=0,
lang="C",
grid_points: ArrayLike | None = None,
temperatures: ArrayLike | None = None,
sigmas: ArrayLike | None = None,
sigma_cutoff: float | None = None,
is_isotope: bool = False,
mass_variances: ArrayLike | None = None,
boundary_mfp: float | None = None, # in micrometer
solve_collective_phonon: bool = False,
is_reducible_collision_matrix: bool = False,
is_kappa_star: bool = True,
gv_delta_q: float | None = None,
is_full_pp: bool = False,
read_pp: bool = False,
pp_filename: str | os.PathLike | None = None,
pinv_cutoff: float = 1.0e-8,
pinv_solver: int = 0,
pinv_method: int = 0,
log_level: int = 0,
lang: str = "C",
):
"""Init method."""
self._kappa = None
self._kappa_RTA = None
self._mode_kappa = None
self._mode_kappa_RTA = None
self._gv_sum2 = None
super().__init__(
interaction,
@ -91,7 +93,6 @@ class ConductivityLBTE(ConductivityMixIn, ConductivityLBTEBase):
solve_collective_phonon=solve_collective_phonon,
is_reducible_collision_matrix=is_reducible_collision_matrix,
is_kappa_star=is_kappa_star,
gv_delta_q=gv_delta_q,
is_full_pp=is_full_pp,
read_pp=read_pp,
pp_filename=pp_filename,
@ -102,35 +103,52 @@ class ConductivityLBTE(ConductivityMixIn, ConductivityLBTEBase):
lang=lang,
)
self._conductivity_components: ConductivityComponents = ConductivityComponents(
self._pp,
self._grid_points,
self._grid_weights,
self._point_operations,
self._rotations_cartesian,
temperatures=self._temperatures,
average_gv_over_kstar=self._average_gv_over_kstar,
is_kappa_star=self._is_kappa_star,
gv_delta_q=gv_delta_q,
is_reducible_collision_matrix=self._is_reducible_collision_matrix,
log_level=self._log_level,
)
@property
def kappa(self):
"""Return kappa."""
return self._kappa
@property
def mode_kappa(self):
"""Return mode_kappa."""
return self._mode_kappa
@property
def kappa_RTA(self):
"""Return RTA lattice thermal conductivity."""
return self._kappa_RTA
def get_kappa_RTA(self):
"""Return RTA lattice thermal conductivity."""
warnings.warn(
"Use attribute, Conductivity_LBTE.kappa_RTA "
"instead of Conductivity_LBTE.get_kappa_RTA().",
DeprecationWarning,
stacklevel=2,
)
return self.kappa_RTA
@property
def mode_kappa_RTA(self):
"""Return RTA mode lattice thermal conductivities."""
return self._mode_kappa_RTA
def get_mode_kappa_RTA(self):
"""Return RTA mode lattice thermal conductivities."""
warnings.warn(
"Use attribute, Conductivity_LBTE.mode_kappa_RTA "
"instead of Conductivity_LBTE.get_mode_kappa_RTA().",
DeprecationWarning,
stacklevel=2,
)
return self.mode_kappa_RTA
@property
def gv_by_gv(self):
"""Return gv_by_gv at grid points where mode kappa are calculated."""
return self._conductivity_components.gv_by_gv
def _set_cv(self, i_gp, i_data):
"""Set cv for conductivity components."""
self._conductivity_components.set_heat_capacities(i_gp, i_data)
def _set_velocities(self, i_gp, i_data):
"""Set velocities for conductivity components."""
self._conductivity_components.set_velocities(i_gp, i_data)
def _allocate_local_values(self, num_grid_points):
"""Allocate grid point local arrays.
@ -140,6 +158,12 @@ class ConductivityLBTE(ConductivityMixIn, ConductivityLBTEBase):
grid points to be iterated over.
"""
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
num_band0 = len(self._pp.band_indices)
num_temp = len(self._temperatures)
super()._allocate_local_values(num_grid_points)
@ -150,9 +174,6 @@ class ConductivityLBTE(ConductivityMixIn, ConductivityLBTEBase):
self._kappa_RTA = np.zeros(
(len(self._sigmas), num_temp, 6), dtype="double", order="C"
)
self._gv_sum2 = np.zeros(
(num_grid_points, num_band0, 6), dtype="double", order="C"
)
self._mode_kappa = np.zeros(
(len(self._sigmas), num_temp, num_grid_points, num_band0, 6), dtype="double"
)

View File

@ -34,13 +34,16 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import os
import sys
import time
import warnings
from abc import abstractmethod
from typing import Optional
import numpy as np
from numpy.typing import ArrayLike
from phonopy.phonon.degeneracy import degenerate_sets
from phonopy.physical_units import get_physical_units
@ -62,25 +65,24 @@ class ConductivityLBTEBase(ConductivityBase):
def __init__(
self,
interaction: Interaction,
grid_points=None,
temperatures=None,
sigmas=None,
sigma_cutoff=None,
is_isotope=False,
mass_variances=None,
boundary_mfp=None, # in micrometer
solve_collective_phonon=False,
is_reducible_collision_matrix=False,
is_kappa_star=True,
gv_delta_q=None, # finite difference for group velocity
is_full_pp=False,
read_pp=False,
pp_filename=None,
pinv_cutoff=1.0e-8,
pinv_solver=0,
pinv_method=0,
log_level=0,
lang="C",
grid_points: ArrayLike | None = None,
temperatures: ArrayLike | None = None,
sigmas: ArrayLike | None = None,
sigma_cutoff: float | None = None,
is_isotope: bool = False,
mass_variances: ArrayLike | None = None,
boundary_mfp: float | None = None, # in micrometer
solve_collective_phonon: bool = False,
is_reducible_collision_matrix: bool = False,
is_kappa_star: bool = True,
is_full_pp: bool = False,
read_pp: bool = False,
pp_filename: str | os.PathLike | None = None,
pinv_cutoff: float = 1.0e-8,
pinv_solver: int = 0,
pinv_method: int = 0,
log_level: int = 0,
lang: str = "C",
):
"""Init method."""
super().__init__(
@ -97,8 +99,6 @@ class ConductivityLBTEBase(ConductivityBase):
log_level=log_level,
)
self._init_velocity(gv_delta_q)
self._lang = lang
self._collision_eigenvalues = None
self._is_reducible_collision_matrix = is_reducible_collision_matrix
@ -150,41 +150,11 @@ class ConductivityLBTEBase(ConductivityBase):
def collision_matrix(self, collision_matrix):
self._collision_matrix = collision_matrix
def get_collision_matrix(self):
"""Return collision matrix."""
warnings.warn(
"Use attribute, Conductivity_LBTE.collision_matrix "
"instead of Conductivity_LBTE.get_collision_matrix().",
DeprecationWarning,
stacklevel=2,
)
return self.collision_matrix
def set_collision_matrix(self, collision_matrix):
"""Set collision matrix."""
warnings.warn(
"Use attribute, Conductivity_LBTE.collision_matrix "
"instead of Conductivity_LBTE.set_collision_matrix().",
DeprecationWarning,
stacklevel=2,
)
self.collision_matrix = collision_matrix
@property
def collision_eigenvalues(self):
"""Return eigenvalues of collision matrix."""
return self._collision_eigenvalues
def get_collision_eigenvalues(self):
"""Return eigenvalues of collision matrix."""
warnings.warn(
"Use attribute, Conductivity_LBTE.collision_eigenvalues "
"instead of Conductivity_LBTE.get_collision_eigenvalues().",
DeprecationWarning,
stacklevel=2,
)
return self.collision_eigenvalues
def get_frequencies_all(self):
"""Return phonon frequencies on GR-grid."""
return self._frequencies[self._pp.bz_grid.grg2bzg]
@ -239,7 +209,7 @@ class ConductivityLBTEBase(ConductivityBase):
kappa and mode_kappa are overwritten.
"""
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
if self._solve_collective_phonon:
self._set_mode_kappa_Chaput(mode_kappa, i_sigma, i_temp, weights)
else:
@ -269,7 +239,7 @@ class ConductivityLBTEBase(ConductivityBase):
kappa and mode_kappa are overwritten.
"""
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
X = self._get_X(i_temp, weights)
num_mesh_points = np.prod(self._pp.mesh_numbers)
Y = self._get_Y(i_sigma, i_temp, weights, X)
@ -314,10 +284,6 @@ class ConductivityLBTEBase(ConductivityBase):
"""Allocate grid point local arrays."""
num_band0 = len(self._pp.band_indices)
num_temp = len(self._temperatures)
self._gv = np.zeros((num_grid_points, num_band0, 3), dtype="double", order="C")
self._cv = np.zeros(
(num_temp, num_grid_points, num_band0), dtype="double", order="C"
)
self._gamma = np.zeros(
(len(self._sigmas), num_temp, num_grid_points, num_band0),
dtype="double",
@ -625,12 +591,20 @@ class ConductivityLBTEBase(ConductivityBase):
sys.stdout.flush()
def _expand_local_values(self, ir_gr_grid_points, rot_grid_points):
"""Fill elements of local properties at grid points."""
"""Fill elements of local properties at grid points.
Note
----
Internal state of self._conductivity_components is updated.
"""
cv = self._conductivity_components.mode_heat_capacities
gv = self._conductivity_components.group_velocities
for ir_gp in ir_gr_grid_points:
cv_irgp = self._cv[:, ir_gp, :].copy()
self._cv[:, ir_gp, :] = 0
gv_irgp = self._gv[ir_gp].copy()
self._gv[ir_gp] = 0
cv_irgp = cv[:, ir_gp, :].copy()
cv[:, ir_gp, :] = 0
gv_irgp = gv[ir_gp].copy()
gv[ir_gp] = 0
gamma_irgp = self._gamma[:, :, ir_gp, :].copy()
self._gamma[:, :, ir_gp, :] = 0
multi = (rot_grid_points[:, ir_gp] == ir_gp).sum()
@ -642,8 +616,8 @@ class ConductivityLBTEBase(ConductivityBase):
self._gamma[:, :, gp_r, :] += gamma_irgp / multi
if self._is_isotope:
self._gamma_iso[:, gp_r, :] += gamma_iso_irgp / multi
self._cv[:, gp_r, :] += cv_irgp / multi
self._gv[gp_r] += np.dot(gv_irgp, r.T) / multi
cv[:, gp_r, :] += cv_irgp / multi
gv[gp_r] += np.dot(gv_irgp, r.T) / multi
def _get_weights(self):
"""Return weights used for collision matrix and |X> and |f>.
@ -782,7 +756,7 @@ class ConductivityLBTEBase(ConductivityBase):
def _get_X(self, i_temp, weights):
"""Calculate X in Chaput's paper."""
num_band = len(self._pp.primitive) * 3
X = self._gv.copy()
X = self._conductivity_components.group_velocities.copy()
if self._is_reducible_collision_matrix:
freqs = self._frequencies[self._pp.bz_grid.grg2bzg]
else:
@ -963,7 +937,7 @@ class ConductivityLBTEBase(ConductivityBase):
This RTA is supposed to be the same as conductivity_RTA.
"""
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
num_band = len(self._pp.primitive) * 3
X = self._get_X(i_temp, weights)
Y = np.zeros_like(X)
@ -1013,7 +987,7 @@ class ConductivityLBTEBase(ConductivityBase):
`kappa` and `mode_kappa` are overwritten.
"""
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
num_band = len(self._pp.primitive) * 3
X = self._get_X(i_temp, weights)
Y = np.zeros_like(X)
@ -1156,7 +1130,7 @@ class ConductivityLBTEBase(ConductivityBase):
# shape = (num_grid_points, num_band, 3),
for i, f_gp in enumerate(self._f_vectors):
for j, f in enumerate(f_gp):
cv = self._cv[i_temp, i, j]
cv = self._conductivity_components.mode_heat_capacities[i_temp, i, j]
if cv < 1e-10:
continue
self._mfp[i_sigma, i_temp, i, j] = (
@ -1167,19 +1141,21 @@ class ConductivityLBTEBase(ConductivityBase):
gp = self._grid_points[i]
frequencies = self._frequencies[gp]
if self._is_reducible_collision_matrix:
gv = self._gv[self._pp.bz_grid.bzg2grg[gp]]
gv = self._conductivity_components.group_velocities[
self._pp.bz_grid.bzg2grg[gp]
]
else:
gv = self._gv[i]
gv = self._conductivity_components.group_velocities[i]
if self._is_full_pp:
ave_pp = self._averaged_pp_interaction[i]
text = "Frequency group velocity (x, y, z) |gv| Pqj"
else:
text = "Frequency group velocity (x, y, z) |gv|"
if self._velocity_obj.q_length is None:
if self._conductivity_components.gv_delta_q is None:
pass
else:
text += " (dq=%3.1e)" % self._velocity_obj.q_length
text += " (dq=%3.1e)" % self._conductivity_components.gv_delta_q
print(text)
if self._is_full_pp:
for f, v, pp in zip(frequencies, gv, ave_pp):

View File

@ -34,8 +34,14 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import os
import sys
from typing import Optional, Union
from collections.abc import Sequence
from typing import Union
from numpy.typing import ArrayLike
from phono3py.conductivity.base import get_unit_to_WmK
from phono3py.conductivity.direct_solution import ConductivityLBTE
@ -59,32 +65,32 @@ cond_LBTE_type = Union[ConductivityLBTE, ConductivityWignerLBTE]
def get_thermal_conductivity_LBTE(
interaction: Interaction,
temperatures=None,
sigmas=None,
sigma_cutoff=None,
is_isotope=False,
mass_variances=None,
grid_points=None,
boundary_mfp=None, # in micrometer
solve_collective_phonon=False,
is_reducible_collision_matrix=False,
is_kappa_star=True,
gv_delta_q=None,
is_full_pp=False,
conductivity_type=None,
pinv_cutoff=1.0e-8,
pinv_solver=0, # default: dsyev in lapacke
pinv_method=0, # default: abs(eig) < cutoff
write_collision=False,
read_collision=False,
write_kappa=False,
write_pp=False,
read_pp=False,
write_LBTE_solution=False,
compression="gzip",
input_filename=None,
output_filename=None,
log_level=0,
temperatures: Sequence | None = None,
sigmas: Sequence | None = None,
sigma_cutoff: float | None = None,
is_isotope: bool = False,
mass_variances: Sequence | None = None,
grid_points: ArrayLike | None = None,
boundary_mfp: float | None = None, # in micrometer
solve_collective_phonon: bool = False,
is_reducible_collision_matrix: bool = False,
is_kappa_star: bool = True,
gv_delta_q: float | None = None,
is_full_pp: bool = False,
conductivity_type: str | None = None,
pinv_cutoff: float = 1.0e-8,
pinv_solver: int = 0, # default: dsyev in lapacke
pinv_method: int = 0, # default: abs(eig) < cutoff
write_collision: bool = False,
read_collision: str | Sequence | None = None,
write_kappa: bool = False,
write_pp: bool = False,
read_pp: bool = False,
write_LBTE_solution: bool = False,
compression: str = "gzip",
input_filename: str | os.PathLike | None = None,
output_filename: str | os.PathLike | None = None,
log_level: int = 0,
):
"""Calculate lattice thermal conductivity by direct solution."""
if temperatures is None:
@ -292,9 +298,9 @@ class ConductivityLBTEWriter:
volume: float,
is_reducible_collision_matrix: bool = False,
write_LBTE_solution: bool = False,
pinv_solver: Optional[int] = None,
pinv_solver: int | None = None,
compression: str = "gzip",
filename: Optional[str] = None,
filename: str | os.PathLike | None = None,
log_level: int = 0,
):
"""Write kappa related properties into a hdf5 file."""
@ -473,7 +479,7 @@ class ConductivityLBTEWriter:
def _set_collision_from_file(
lbte: ConductivityLBTEBase,
indices="all",
indices: str | Sequence | None = "all",
is_reducible_collision_matrix=False,
filename=None,
log_level=0,

View File

@ -182,7 +182,7 @@ class ShowCalcProgress:
kappa = br.kappa
num_ignored_phonon_modes = br.number_of_ignored_phonon_modes
num_band = br.frequencies.shape[1]
num_phonon_modes = br.get_number_of_sampling_grid_points() * num_band
num_phonon_modes = br.number_of_sampling_grid_points * num_band
for i, sigma in enumerate(sigmas):
text = "----------- Thermal conductivity (W/m-k) "
if sigma:
@ -223,7 +223,7 @@ class ShowCalcProgress:
kappa_C = br.kappa_C
num_ignored_phonon_modes = br.number_of_ignored_phonon_modes
num_band = br.frequencies.shape[1]
num_phonon_modes = br.get_number_of_sampling_grid_points() * num_band
num_phonon_modes = br.number_of_sampling_grid_points * num_band
for i, sigma in enumerate(sigmas):
text = "----------- Thermal conductivity (W/m-k) "
if sigma:

View File

@ -37,6 +37,8 @@
import numpy as np
from phonopy.physical_units import get_physical_units
from phono3py.conductivity.base import get_kstar_order, get_multiplicity_at_q
from phono3py.phonon.grid import get_qpoints_from_bz_grid_points
from phono3py.phonon.group_velocity_matrix import GroupVelocityMatrix
from phono3py.phonon.heat_capacity_matrix import mode_cv_matrix
@ -102,7 +104,9 @@ class ConductivityKuboMixIn:
"""
irgp = self._grid_points[i_gp]
self._velocity_obj.run([self._get_qpoint_from_gp_index(irgp)])
self._velocity_obj.run(
[get_qpoints_from_bz_grid_points(irgp, self._pp.bz_grid)]
)
gvm = np.zeros(self._gv_mat.shape[1:], dtype=self._complex_dtype, order="C")
gv = np.zeros(self._gv.shape[1:], dtype="double", order="C")
for i in range(3):
@ -136,8 +140,15 @@ class ConductivityKuboMixIn:
Number of kstar arms.
"""
multi = self._get_multiplicity_at_q(i_gp)
q = self._get_qpoint_from_gp_index(self._grid_points[i_gp])
if self._is_kappa_star:
multi = get_multiplicity_at_q(
self._grid_points[i_gp],
self._pp,
self._point_operations,
)
else:
multi = 1
q = get_qpoints_from_bz_grid_points(self._grid_points[i_gp], self._pp.bz_grid)
qpoints = [np.dot(r, q) for r in self._point_operations]
self._velocity_obj.run(qpoints)
@ -151,5 +162,10 @@ class ConductivityKuboMixIn:
gvm_by_gvm = np.multiply(gvm[a], gvm[b].T)
gvm_sum2[:, :, i_pair] += gvm_by_gvm[self._pp.band_indices, :]
gvm_sum2 /= multi
return gvm_sum2, self._get_kstar_order(i_gp, multi)
kstar_order = get_kstar_order(
self._grid_weights[i_gp],
multi,
self._point_operations,
verbose=self._log_level > 0,
)
return gvm_sum2, kstar_order

View File

@ -34,43 +34,45 @@
# 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 phono3py.conductivity.base import ConductivityMixIn
from phono3py.conductivity.base import ConductivityComponents
from phono3py.conductivity.rta_base import ConductivityRTABase
from phono3py.phonon3.interaction import Interaction
class ConductivityRTA(ConductivityMixIn, ConductivityRTABase):
class ConductivityRTA(ConductivityRTABase):
"""Lattice thermal conductivity calculation with RTA."""
def __init__(
self,
interaction: Interaction,
grid_points=None,
temperatures=None,
sigmas=None,
sigma_cutoff=None,
is_isotope=False,
mass_variances=None,
boundary_mfp=None, # in micrometer
use_ave_pp=False,
is_kappa_star=True,
gv_delta_q=None,
is_full_pp=False,
read_pp=False,
store_pp=False,
pp_filename=None,
is_N_U=False,
is_gamma_detail=False,
is_frequency_shift_by_bubble=False,
log_level=0,
grid_points: Optional[np.ndarray] = None,
temperatures: Optional[Union[list, np.ndarray]] = None,
sigmas: Optional[Union[list, np.ndarray]] = None,
sigma_cutoff: Optional[float] = None,
is_isotope: bool = False,
mass_variances: Optional[Union[list, np.ndarray]] = None,
boundary_mfp: Optional[float] = None, # in micrometer
use_ave_pp: bool = False,
is_kappa_star: bool = True,
gv_delta_q: Optional[float] = None,
is_full_pp: bool = False,
read_pp: bool = False,
store_pp: bool = False,
pp_filename: Optional[float] = None,
is_N_U: bool = False,
is_gamma_detail: bool = False,
is_frequency_shift_by_bubble: bool = False,
log_level: int = 0,
):
"""Init method."""
self._cv = None
self._kappa = None
self._mode_kappa = None
self._gv_sum2 = None
super().__init__(
interaction,
@ -83,7 +85,6 @@ class ConductivityRTA(ConductivityMixIn, ConductivityRTABase):
boundary_mfp=boundary_mfp,
use_ave_pp=use_ave_pp,
is_kappa_star=is_kappa_star,
gv_delta_q=gv_delta_q,
is_full_pp=is_full_pp,
read_pp=read_pp,
store_pp=store_pp,
@ -94,13 +95,62 @@ class ConductivityRTA(ConductivityMixIn, ConductivityRTABase):
log_level=log_level,
)
self._conductivity_components = ConductivityComponents(
self._pp,
self._grid_points,
self._grid_weights,
self._point_operations,
self._rotations_cartesian,
temperatures=self._temperatures,
average_gv_over_kstar=self._average_gv_over_kstar,
is_kappa_star=self._is_kappa_star,
gv_delta_q=gv_delta_q,
log_level=self._log_level,
)
@property
def kappa(self):
"""Return kappa."""
return self._kappa
@property
def mode_kappa(self):
"""Return mode_kappa."""
return self._mode_kappa
@property
def gv_by_gv(self):
"""Return gv_by_gv at grid points where mode kappa are calculated."""
return self._conductivity_components.gv_by_gv
def _set_cv(self, i_gp, i_data):
"""Set cv for conductivity components."""
self._conductivity_components.set_heat_capacities(i_gp, i_data)
def _set_velocities(self, i_gp, i_data):
"""Set velocities for conductivity components."""
self._conductivity_components.set_velocities(i_gp, i_data)
def set_kappa_at_sigmas(self):
"""Calculate kappa from ph-ph interaction results."""
if not self._pp.phonon_all_done:
raise RuntimeError(
"Phonon calculation has not been done yet. "
"Run phono3py.run_phonon_solver() before this method."
)
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
num_band = len(self._pp.primitive) * 3
mode_heat_capacities = self._conductivity_components.mode_heat_capacities
gv_by_gv = self._conductivity_components.gv_by_gv
for i, _ in enumerate(self._grid_points):
cv = self._cv[:, i, :]
cv = mode_heat_capacities[:, i, :]
gp = self._grid_points[i]
frequencies = self._frequencies[gp]
frequencies = self._frequencies[gp] # type: ignore
# Kappa
for j in range(len(self._sigmas)):
@ -108,13 +158,13 @@ class ConductivityRTA(ConductivityMixIn, ConductivityRTABase):
g_sum = self._get_main_diagonal(i, j, k)
for ll in range(num_band):
if frequencies[ll] < self._pp.cutoff_frequency:
self._num_ignored_phonon_modes[j, k] += 1
self._num_ignored_phonon_modes[j, k] += 1 # type: ignore
continue
old_settings = np.seterr(all="raise")
try:
self._mode_kappa[j, k, i, ll] = (
self._gv_sum2[i, ll]
self._mode_kappa[j, k, i, ll] = ( # type: ignore
gv_by_gv[i, ll]
* cv[k, ll]
/ (g_sum[ll] * 2)
* self._conversion_factor
@ -135,24 +185,22 @@ class ConductivityRTA(ConductivityMixIn, ConductivityRTABase):
print("=" * 61)
np.seterr(**old_settings)
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
self._kappa = self._mode_kappa.sum(axis=2).sum(axis=2) / N
def _allocate_values(self):
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
super()._allocate_values()
num_band0 = len(self._pp.band_indices)
num_band = len(self._pp.primitive) * 3
num_grid_points = len(self._grid_points)
num_temp = len(self._temperatures)
self._cv = np.zeros(
(num_temp, num_grid_points, num_band0), order="C", dtype="double"
)
self._gv_sum2 = np.zeros(
(num_grid_points, num_band0, 6), order="C", dtype="double"
)
# kappa* and mode_kappa* are accessed when all bands exist, i.e.,
# num_band0==num_band.
self._kappa = np.zeros(

View File

@ -34,6 +34,8 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import warnings
from abc import abstractmethod
@ -43,7 +45,10 @@ from phonopy.physical_units import get_physical_units
from phono3py.conductivity.base import ConductivityBase
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.phonon.grid import (
get_grid_points_by_rotations,
get_qpoints_from_bz_grid_points,
)
from phono3py.phonon3.imag_self_energy import ImagSelfEnergy, average_by_degeneracy
from phono3py.phonon3.interaction import Interaction
@ -58,24 +63,23 @@ class ConductivityRTABase(ConductivityBase):
def __init__(
self,
interaction: Interaction,
grid_points=None,
temperatures=None,
sigmas=None,
sigma_cutoff=None,
is_isotope=False,
mass_variances=None,
boundary_mfp=None, # in micrometer
use_ave_pp=False,
is_kappa_star=True,
gv_delta_q=None,
is_full_pp=False,
read_pp=False,
store_pp=False,
pp_filename=None,
is_N_U=False,
is_gamma_detail=False,
is_frequency_shift_by_bubble=False,
log_level=0,
grid_points: np.ndarray | None = None,
temperatures: list | np.ndarray | None = None,
sigmas: list | np.ndarray | None = None,
sigma_cutoff: float | None = None,
is_isotope: bool = False,
mass_variances: list | np.ndarray | None = None,
boundary_mfp: float | None = None, # in micrometer
use_ave_pp: bool = False,
is_kappa_star: bool = True,
is_full_pp: bool = False,
read_pp: bool = False,
store_pp: bool = False,
pp_filename: float | None = None,
is_N_U: bool = False,
is_gamma_detail: bool = False,
is_frequency_shift_by_bubble: bool = False,
log_level: int = 0,
):
"""Init method."""
self._is_N_U = is_N_U
@ -99,7 +103,6 @@ class ConductivityRTABase(ConductivityBase):
mass_variances=mass_variances,
boundary_mfp=boundary_mfp,
is_kappa_star=is_kappa_star,
gv_delta_q=gv_delta_q,
is_full_pp=is_full_pp,
log_level=log_level,
)
@ -153,6 +156,12 @@ class ConductivityRTABase(ConductivityBase):
raise NotImplementedError()
def _allocate_values(self):
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
num_band0 = len(self._pp.band_indices)
num_grid_points = len(self._grid_points)
num_temp = len(self._temperatures)
@ -166,7 +175,6 @@ class ConductivityRTABase(ConductivityBase):
self._gamma_N = np.zeros_like(self._gamma)
self._gamma_U = np.zeros_like(self._gamma)
self._gv = np.zeros((num_grid_points, num_band0, 3), order="C", dtype="double")
if self._is_isotope:
self._gamma_iso = np.zeros(
(len(self._sigmas), num_grid_points, num_band0),
@ -196,7 +204,7 @@ class ConductivityRTABase(ConductivityBase):
self._collision.set_grid_point(grid_point)
num_triplets = len(self._pp.get_triplets_at_q()[0])
if self._log_level:
print("Number of triplets: %d" % num_triplets)
print("Number of triplets: %d" % num_triplets, flush=True)
if (
self._is_full_pp
@ -280,7 +288,7 @@ class ConductivityRTABase(ConductivityBase):
if self._is_gamma_detail:
num_temp = len(self._temperatures)
self._gamma_detail_at_q = np.empty(
((num_temp,) + self._pp.get_interaction_strength().shape),
((num_temp,) + self._pp.interaction_strength.shape),
dtype="double",
order="C",
)
@ -320,7 +328,6 @@ class ConductivityRTABase(ConductivityBase):
s2p,
masses,
) = self._pp.get_primitive_and_supercell_correspondence()
fc3 = self._pp.fc3
triplets_at_q, weights_at_q, _, _ = self._pp.get_triplets_at_q()
if None in self._sigmas:
@ -376,7 +383,8 @@ class ConductivityRTABase(ConductivityBase):
self._pp.bz_grid.store_dense_gp_map * 1 + 1,
self._pp.bz_grid.D_diag,
self._pp.bz_grid.Q,
fc3,
self._pp.fc3,
self._pp.fc3_nonzero_indices,
svecs,
multi,
masses,
@ -407,7 +415,8 @@ class ConductivityRTABase(ConductivityBase):
self._pp.bz_grid.addresses,
self._pp.bz_grid.D_diag,
self._pp.bz_grid.Q,
fc3,
self._pp.fc3,
self._pp.fc3_nonzero_indices,
svecs,
multi,
masses,
@ -449,10 +458,10 @@ class ConductivityRTABase(ConductivityBase):
)
def _show_log(self, i_gp):
q = self._get_qpoint_from_gp_index(i_gp)
q = get_qpoints_from_bz_grid_points(i_gp, self._pp.bz_grid)
gp = self._grid_points[i_gp]
frequencies = self._frequencies[gp][self._pp.band_indices]
gv = self._gv[i_gp]
gv = self._conductivity_components.group_velocities[i_gp]
if self._averaged_pp_interaction is not None:
ave_pp = self._averaged_pp_interaction[i_gp]
@ -512,8 +521,8 @@ class ConductivityRTABase(ConductivityBase):
text = "Frequency group velocity (x, y, z) |gv| Pqj"
else:
text = "Frequency group velocity (x, y, z) |gv|"
if self._velocity_obj.q_length is None:
if self._conductivity_components.gv_delta_q is None:
pass
else:
text += " (dq=%3.1e)" % self._velocity_obj.q_length
text += " (dq=%3.1e)" % self._conductivity_components.gv_delta_q
print(text)

View File

@ -35,47 +35,83 @@
# POSSIBILITY OF SUCH DAMAGE.
import textwrap
from typing import Optional
import numpy as np
from numpy.typing import NDArray
from phonopy.phonon.degeneracy import degenerate_sets
from phonopy.physical_units import get_physical_units
from phono3py.conductivity.base import HeatCapacityMixIn
from phono3py.phonon.grid import get_grid_points_by_rotations
from phono3py.conductivity.base import ConductivityComponentsBase, get_heat_capacities
from phono3py.phonon.grid import (
get_grid_points_by_rotations,
get_qpoints_from_bz_grid_points,
)
from phono3py.phonon.velocity_operator import VelocityOperator
from phono3py.phonon3.interaction import Interaction
class ConductivityWignerMixIn(HeatCapacityMixIn):
"""Thermal conductivity mix-in for velocity operator.
def get_conversion_factor_WTE(volume):
"""Return conversion factor of thermal conductivity."""
return (
(get_physical_units().THz * get_physical_units().Angstrom)
** 2 # --> group velocity
* get_physical_units().EV # --> specific heat is in eV/
* get_physical_units().Hbar # --> transform lorentzian_div_hbar from eV^-1 to s
/ (volume * get_physical_units().Angstrom ** 3)
) # --> unit cell volume
This mix-in is included in `ConductivityWignerRTA` and `ConductivityWignerLBTE`.
class ConductivityWignerComponents(ConductivityComponentsBase):
"""Thermal conductivity components for velocity operator.
Used by `ConductivityWignerRTA` and `ConductivityWignerLBTE`.
"""
@property
def kappa_TOT_RTA(self):
"""Return kappa."""
return self._kappa_TOT_RTA
def __init__(
self,
pp: Interaction,
grid_points: NDArray[np.int64],
grid_weights: NDArray[np.int64],
point_operations: NDArray[np.int64],
rotations_cartesian: NDArray[np.int64],
temperatures: Optional[NDArray[np.float64]] = None,
is_kappa_star: bool = True,
gv_delta_q: Optional[float] = None,
is_reducible_collision_matrix: bool = False,
log_level: int = 0,
):
"""Init method."""
super().__init__(
pp,
grid_points=grid_points,
grid_weights=grid_weights,
point_operations=point_operations,
rotations_cartesian=rotations_cartesian,
temperatures=temperatures,
is_kappa_star=is_kappa_star,
is_reducible_collision_matrix=is_reducible_collision_matrix,
log_level=log_level,
)
@property
def kappa_P_RTA(self):
"""Return kappa."""
return self._kappa_P_RTA
self._gv_operator: np.ndarray
self._gv_operator_sum2: np.ndarray
@property
def kappa_C(self):
"""Return kappa."""
return self._kappa_C
if self._pp.dynamical_matrix is None:
raise RuntimeError("Interaction.init_dynamical_matrix() has to be called.")
self._velocity_obj = VelocityOperator(
self._pp.dynamical_matrix,
q_length=gv_delta_q,
symmetry=self._pp.primitive_symmetry,
frequency_factor_to_THz=self._pp.frequency_factor_to_THz,
)
@property
def mode_kappa_P_RTA(self):
"""Return mode_kappa."""
return self._mode_kappa_P_RTA
self._num_sampling_grid_points = 0
self._complex_dtype = "c%d" % (np.dtype("double").itemsize * 2)
@property
def mode_kappa_C(self):
"""Return mode_kappa."""
return self._mode_kappa_C
if self._temperatures is not None:
self._allocate_values()
@property
def velocity_operator(self):
@ -91,27 +127,34 @@ class ConductivityWignerMixIn(HeatCapacityMixIn):
"""Return gv_by_gv operator at grid points where mode kappa are calculated."""
return self._gv_operator_sum2
def _init_velocity(self, gv_delta_q):
self._velocity_obj = VelocityOperator(
self._pp.dynamical_matrix,
q_length=gv_delta_q,
symmetry=self._pp.primitive_symmetry,
frequency_factor_to_THz=self._pp.frequency_factor_to_THz,
)
def _set_velocities(self, i_gp, i_data):
def set_velocities(self, i_gp, i_data):
"""Set velocities at a grid point."""
self._set_gv_operator(i_gp, i_data)
self._set_gv_by_gv_operator(i_gp, i_data)
def set_heat_capacities(self, i_gp: int, i_data: int):
"""Set heat capacity at grid point and at data location."""
if self._temperatures is None:
raise RuntimeError(
"Temperatures have not been set yet. "
"Set temperatures before this method."
)
cv = get_heat_capacities(self._grid_points[i_gp], self._pp, self._temperatures)
self._cv[:, i_data, :] = cv
def _set_gv_operator(self, i_irgp, i_data):
"""Set velocity operator."""
irgp = self._grid_points[i_irgp]
self._velocity_obj.run([self._get_qpoint_from_gp_index(irgp)])
frequencies, _, _ = self._pp.get_phonons()
self._velocity_obj.run(
[get_qpoints_from_bz_grid_points(irgp, self._pp.bz_grid)]
)
gv_operator = self._velocity_obj.velocity_operators[0, :, :, :]
self._gv_operator[i_data] = gv_operator[self._pp.band_indices, :, :]
#
gv = np.einsum("iij->ij", gv_operator).real
deg_sets = degenerate_sets(self._frequencies[irgp])
deg_sets = degenerate_sets(frequencies[irgp])
# group velocities in the degenerate subspace are obtained diagonalizing the
# velocity operator in the subspace of degeneracy.
for id_dir in range(3):
@ -141,14 +184,14 @@ class ConductivityWignerMixIn(HeatCapacityMixIn):
# Sum all vxv at k*
for j, vxv in enumerate(([0, 0], [1, 1], [2, 2], [1, 2], [0, 2], [0, 1])):
# self._gv_sum2[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]
# self._gv_by_gv[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]
# here it is storing the 6 independent components of the v^i x v^j tensor
# i_data is the q-point index
# j indexes the 6 independent component of the symmetric tensor v^i x v^j
self._gv_operator_sum2[i_data, :, :, j] = gv_by_gv_operator_tensor[
:, :, vxv[0], vxv[1]
]
# self._gv_sum2[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]
# self._gv_by_gv[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]
def _get_gv_by_gv_operator(self, i_irgp, i_data):
if self._is_kappa_star:
@ -211,13 +254,23 @@ class ConductivityWignerMixIn(HeatCapacityMixIn):
return gv_by_gv_operator, order_kstar
def _allocate_values(self):
super()._allocate_values()
def get_conversion_factor_WTE(volume):
"""Return conversion factor of thermal conductivity."""
return (
(get_physical_units().THz * get_physical_units().Angstrom)
** 2 # --> group velocity
* get_physical_units().EV # --> specific heat is in eV/
* get_physical_units().Hbar # --> transform lorentzian_div_hbar from eV^-1 to s
/ (volume * get_physical_units().Angstrom ** 3)
) # --> unit cell volume
num_band0 = len(self._pp.band_indices)
if self._is_reducible_collision_matrix:
num_grid_points = np.prod(self._pp.mesh_numbers)
else:
num_grid_points = len(self._grid_points)
num_band = len(self._pp.primitive) * 3
self._gv_operator = np.zeros(
(num_grid_points, num_band0, num_band, 3),
order="C",
dtype=self._complex_dtype,
)
self._gv_operator_sum2 = np.zeros(
(num_grid_points, num_band0, num_band, 6),
order="C",
dtype=self._complex_dtype,
)

View File

@ -34,6 +34,8 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import numpy as np
from phonopy.physical_units import get_physical_units
@ -42,13 +44,13 @@ from phono3py.conductivity.direct_solution import (
diagonalize_collision_matrix,
)
from phono3py.conductivity.wigner_base import (
ConductivityWignerMixIn,
ConductivityWignerComponents,
get_conversion_factor_WTE,
)
from phono3py.phonon3.interaction import Interaction
class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
class ConductivityWignerLBTE(ConductivityLBTEBase):
"""Class of Wigner lattice thermal conductivity under direct-solution.
Authors
@ -104,7 +106,6 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
solve_collective_phonon=solve_collective_phonon,
is_reducible_collision_matrix=is_reducible_collision_matrix,
is_kappa_star=is_kappa_star,
gv_delta_q=gv_delta_q,
is_full_pp=is_full_pp,
read_pp=read_pp,
pp_filename=pp_filename,
@ -114,10 +115,49 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
log_level=log_level,
lang=lang,
)
self._conversion_factor_WTE = get_conversion_factor_WTE(
self._pp.primitive.volume
)
self._conductivity_components = ConductivityWignerComponents(
self._pp,
self._grid_points,
self._grid_weights,
self._point_operations,
self._rotations_cartesian,
temperatures=self._temperatures,
is_kappa_star=self._is_kappa_star,
gv_delta_q=gv_delta_q,
is_reducible_collision_matrix=self._is_reducible_collision_matrix,
log_level=self._log_level,
)
@property
def kappa_TOT_RTA(self):
"""Return kappa."""
return self._kappa_TOT_RTA
@property
def kappa_P_RTA(self):
"""Return kappa."""
return self._kappa_P_RTA
@property
def kappa_C(self):
"""Return kappa."""
return self._kappa_C
@property
def mode_kappa_P_RTA(self):
"""Return mode_kappa."""
return self._mode_kappa_P_RTA
@property
def mode_kappa_C(self):
"""Return mode_kappa."""
return self._mode_kappa_C
@property
def kappa_TOT_exact(self):
"""Return kappa."""
@ -133,6 +173,14 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
"""Return mode_kappa."""
return self._mode_kappa_P_exact
def _set_cv(self, i_gp, i_data):
"""Set cv for conductivity components."""
self._conductivity_components.set_heat_capacities(i_gp, i_data)
def _set_velocities(self, i_gp, i_data):
"""Set velocities for conductivity components."""
self._conductivity_components.set_velocities(i_gp, i_data)
def _allocate_local_values(self, num_grid_points):
"""Allocate grid point local arrays."""
num_band0 = len(self._pp.band_indices)
@ -163,12 +211,6 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
)
complex_dtype = "c%d" % (np.dtype("double").itemsize * 2)
self._gv_operator = np.zeros(
(num_grid_points, num_band0, nat3, 3), order="C", dtype=complex_dtype
)
self._gv_operator_sum2 = np.zeros(
(num_grid_points, num_band0, nat3, 6), order="C", dtype=complex_dtype
)
# one more index because we have off-diagonal terms (second index not
# parallelized)
self._mode_kappa_C = np.zeros(
@ -279,7 +321,7 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
def _set_kappa_C_ir_colmat(self, i_sigma, i_temp):
"""Calculate coherence term of the thermal conductivity."""
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
num_band = len(self._pp.primitive) * 3
# num_ir_grid_points = len(self._ir_grid_points)
THzToEv = get_physical_units().THzToEv
@ -287,7 +329,7 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
# linewidths at qpoint i, sigma i_sigma, and temperature i_temp
g = self._get_main_diagonal(i, i_sigma, i_temp) * 2.0 # linewidth (FWHM)
frequencies = self._frequencies[gp]
cv = self._cv[i_temp, i, :]
cv = self._conductivity_components.mode_heat_capacities[i_temp, i, :]
for s1 in range(num_band):
for s2 in range(num_band):
hbar_omega_eV_s1 = (
@ -317,7 +359,7 @@ class ConductivityWignerLBTE(ConductivityWignerMixIn, ConductivityLBTEBase):
* (cv[s1] / hbar_omega_eV_s1 + cv[s2] / hbar_omega_eV_s2)
)
self._mode_kappa_C[i_sigma, i_temp, i, s1, s2] = (
(self._gv_operator_sum2[i, s1, s2])
(self._conductivity_components.gv_by_gv_operator[i, s1, s2])
* prefactor
* lorentzian_divided_by_hbar
* self._conversion_factor_WTE

View File

@ -34,18 +34,20 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import numpy as np
from phonopy.physical_units import get_physical_units
from phono3py.conductivity.rta_base import ConductivityRTABase
from phono3py.conductivity.wigner_base import (
ConductivityWignerMixIn,
ConductivityWignerComponents,
get_conversion_factor_WTE,
)
from phono3py.phonon3.interaction import Interaction
class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
class ConductivityWignerRTA(ConductivityRTABase):
"""Class of Wigner lattice thermal conductivity under RTA.
Authors
@ -57,24 +59,24 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
def __init__(
self,
interaction: Interaction,
grid_points=None,
temperatures=None,
sigmas=None,
sigma_cutoff=None,
is_isotope=False,
mass_variances=None,
boundary_mfp=None, # in micrometer
use_ave_pp=False,
is_kappa_star=True,
gv_delta_q=None,
is_full_pp=False,
read_pp=False,
store_pp=False,
pp_filename=None,
is_N_U=False,
is_gamma_detail=False,
is_frequency_shift_by_bubble=False,
log_level=0,
grid_points: np.ndarray | None = None,
temperatures: list | np.ndarray | None = None,
sigmas: list | np.ndarray | None = None,
sigma_cutoff: float | None = None,
is_isotope: bool = False,
mass_variances: list | np.ndarray | None = None,
boundary_mfp: float | None = None, # in micrometer
use_ave_pp: bool = False,
is_kappa_star: bool = True,
gv_delta_q: float | None = None,
is_full_pp: bool = False,
read_pp: bool = False,
store_pp: bool = False,
pp_filename: float | None = None,
is_N_U: bool = False,
is_gamma_detail: bool = False,
is_frequency_shift_by_bubble: bool = False,
log_level: int = 0,
):
"""Init method."""
self._cv = None
@ -98,7 +100,6 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
boundary_mfp=boundary_mfp,
use_ave_pp=use_ave_pp,
is_kappa_star=is_kappa_star,
gv_delta_q=gv_delta_q,
is_full_pp=is_full_pp,
read_pp=read_pp,
store_pp=store_pp,
@ -112,6 +113,43 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
self._pp.primitive.volume
)
self._conductivity_components = ConductivityWignerComponents(
self._pp,
self._grid_points,
self._grid_weights,
self._point_operations,
self._rotations_cartesian,
temperatures=self._temperatures,
is_kappa_star=self._is_kappa_star,
gv_delta_q=gv_delta_q,
log_level=self._log_level,
)
@property
def kappa_TOT_RTA(self):
"""Return kappa."""
return self._kappa_TOT_RTA
@property
def kappa_P_RTA(self):
"""Return kappa."""
return self._kappa_P_RTA
@property
def kappa_C(self):
"""Return kappa."""
return self._kappa_C
@property
def mode_kappa_P_RTA(self):
"""Return mode_kappa."""
return self._mode_kappa_P_RTA
@property
def mode_kappa_C(self):
"""Return mode_kappa."""
return self._mode_kappa_C
def set_kappa_at_sigmas(self):
"""Calculate the Wigner thermal conductivity.
@ -120,8 +158,10 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
"""
num_band = len(self._pp.primitive) * 3
THzToEv = get_physical_units().THzToEv
gv_by_gv = self._conductivity_components.gv_by_gv_operator
cv = self._conductivity_components.mode_heat_capacities
for i, _ in enumerate(self._grid_points):
cv = self._cv[:, i, :]
gp = self._grid_points[i]
frequencies = self._frequencies[gp]
# Kappa
@ -156,15 +196,15 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
0.25
* (hbar_omega_eV_s1 + hbar_omega_eV_s2)
* (
cv[k, s1] / hbar_omega_eV_s1
+ cv[k, s2] / hbar_omega_eV_s2
cv[k, i, s1] / hbar_omega_eV_s1
+ cv[k, i, s2] / hbar_omega_eV_s2
)
)
if np.abs(frequencies[s1] - frequencies[s2]) < 1e-4:
# degenerate or diagonal s1=s2 modes contribution
# determine k_P
contribution = (
(self._gv_operator_sum2[i, s1, s2])
(gv_by_gv[i, s1, s2])
* prefactor
* lorentzian_divided_by_hbar
* self._conversion_factor_WTE
@ -182,7 +222,7 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
# conductivity
else:
self._mode_kappa_C[j, k, i, s1, s2] += (
(self._gv_operator_sum2[i, s1, s2])
(gv_by_gv[i, s1, s2])
* prefactor
* lorentzian_divided_by_hbar
* self._conversion_factor_WTE
@ -191,13 +231,21 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
elif s1 == s2:
self._num_ignored_phonon_modes[j, k] += 1
N = self._num_sampling_grid_points
N = self.number_of_sampling_grid_points
self._kappa_P_RTA = self._mode_kappa_P_RTA.sum(axis=2).sum(axis=2) / N
#
self._kappa_C = self._mode_kappa_C.sum(axis=2).sum(axis=2).sum(axis=2) / N
#
self._kappa_TOT_RTA = self._kappa_P_RTA + self._kappa_C
def _set_cv(self, i_gp, i_data):
"""Set cv for conductivity components."""
self._conductivity_components.set_heat_capacities(i_gp, i_data)
def _set_velocities(self, i_gp, i_data):
"""Set velocities for conductivity components."""
self._conductivity_components.set_velocities(i_gp, i_data)
def _allocate_values(self):
super()._allocate_values()
@ -206,16 +254,6 @@ class ConductivityWignerRTA(ConductivityWignerMixIn, ConductivityRTABase):
num_temp = len(self._temperatures)
nat3 = len(self._pp.primitive) * 3
self._cv = np.zeros(
(num_temp, num_grid_points, num_band0), order="C", dtype="double"
)
self._gv_operator = np.zeros(
(num_grid_points, num_band0, nat3, 3), order="C", dtype=self._complex_dtype
)
self._gv_operator_sum2 = np.zeros(
(num_grid_points, num_band0, nat3, 6), order="C", dtype=self._complex_dtype
)
# kappa* and mode_kappa* are accessed when all bands exist, i.e.,
# num_band0==num_band.
self._kappa_TOT_RTA = np.zeros(

View File

@ -37,198 +37,40 @@
from __future__ import annotations
import copy
import dataclasses
import os
import pathlib
import sys
from dataclasses import asdict
from typing import Literal, Optional, Union
from typing import Literal, cast
import numpy as np
from phonopy.cui.phonopy_script import file_exists, print_error
from phonopy.file_IO import get_dataset_type2
from phonopy.harmonic.force_constants import (
show_drift_force_constants,
symmetrize_compact_force_constants,
symmetrize_force_constants,
from phonopy import Phonopy
from phonopy.cui.load_helper import (
develop_or_load_pypolymlp as develop_or_load_pypolymlp_phonopy,
)
from phonopy.file_IO import get_dataset_type2
from phonopy.interface.calculator import get_calculator_physical_units
from phonopy.interface.fc_calculator import fc_calculator_names
from phonopy.interface.pypolymlp import PypolymlpParams, parse_mlp_params
from phonopy.interface.symfc import parse_symfc_options
from phono3py import Phono3py
from phono3py.cui.settings import Phono3pySettings
from phono3py.cui.show_log import show_phono3py_force_constants_settings
from phono3py.file_IO import (
get_length_of_first_line,
parse_FORCES_FC2,
parse_FORCES_FC3,
read_fc2_from_hdf5,
read_fc3_from_hdf5,
write_fc2_to_hdf5,
write_fc3_to_hdf5,
)
from phono3py.interface.fc_calculator import extract_fc2_fc3_calculators
from phono3py.interface.fc_calculator import determine_cutoff_pair_distance
from phono3py.interface.phono3py_yaml import Phono3pyYaml
from phono3py.phonon3.dataset import forces_in_dataset
from phono3py.phonon3.fc3 import (
set_permutation_symmetry_fc3,
set_translational_invariance_fc3,
show_drift_fc3,
)
def get_cutoff_pair_distance(settings: Phono3pySettings) -> Optional[float]:
"""Return cutoff_pair_distance from settings."""
_, fc_calculator_options = get_fc_calculator_params(settings)
if settings.cutoff_pair_distance is None:
cutoff = parse_symfc_options(
extract_fc2_fc3_calculators(fc_calculator_options, 3)
).get("cutoff")
if cutoff is None:
cutoff_pair_distance = None
else:
cutoff_pair_distance = cutoff.get(3)
else:
cutoff_pair_distance = settings.cutoff_pair_distance
return cutoff_pair_distance
def create_phono3py_force_constants(
phono3py: Phono3py,
settings,
ph3py_yaml: Optional[Phono3pyYaml] = None,
phono3py_yaml_filename: Optional[str] = None,
calculator: Optional[str] = None,
input_filename: Optional[str] = None,
output_filename: Optional[str] = None,
log_level=1,
):
"""Read or calculate force constants.
This function is for the 'phonopy' command only and not for the
'phonopy-load' command.
"""
# Only for built-in fc calculator.
# These are not applied to external fc calculators.
symmetrize_fc3r = settings.is_symmetrize_fc3_r or settings.fc_symmetry
symmetrize_fc2 = settings.is_symmetrize_fc2 or settings.fc_symmetry
if log_level:
show_phono3py_force_constants_settings(settings)
#######
# fc3 #
#######
if (
settings.is_joint_dos
or (settings.is_isotope and not (settings.is_bterta or settings.is_lbte))
or settings.read_gamma
or settings.read_pp
or (not settings.is_bterta and settings.write_phonon)
or settings.constant_averaged_pp_interaction is not None
):
pass
else:
(fc_calculator, fc_calculator_options) = get_fc_calculator_params(
settings, log_level=(not settings.read_fc3) * 1
)
if settings.read_fc3:
_read_phono3py_fc3(phono3py, symmetrize_fc3r, input_filename, log_level)
else: # fc3 from FORCES_FC3 or ph3py_yaml
dataset = _read_dataset_fc3(
phono3py,
ph3py_yaml,
phono3py_yaml_filename,
settings.cutoff_pair_distance,
calculator,
log_level,
)
phono3py.dataset = dataset
phono3py.produce_fc3(
symmetrize_fc3r=symmetrize_fc3r,
is_compact_fc=settings.is_compact_fc,
fc_calculator=extract_fc2_fc3_calculators(fc_calculator, 3),
fc_calculator_options=extract_fc2_fc3_calculators(
fc_calculator_options, 3
),
)
cutoff_distance = settings.cutoff_fc3_distance
if cutoff_distance is not None and cutoff_distance > 0:
if log_level:
print(
"Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance
)
phono3py.cutoff_fc3_by_zero(cutoff_distance)
if not settings.read_fc3:
if output_filename is None:
filename = "fc3.hdf5"
else:
filename = "fc3." + output_filename + ".hdf5"
if log_level:
print('Writing fc3 to "%s".' % filename)
write_fc3_to_hdf5(
phono3py.fc3,
filename=filename,
p2s_map=phono3py.primitive.p2s_map,
compression=settings.hdf5_compression,
)
if log_level:
show_drift_fc3(phono3py.fc3, primitive=phono3py.primitive)
#######
# fc2 #
#######
phonon_primitive = phono3py.phonon_primitive
p2s_map = phonon_primitive.p2s_map
if settings.read_fc2:
_read_phono3py_fc2(phono3py, symmetrize_fc2, input_filename, log_level)
else:
if phono3py.dataset is None or phono3py.phonon_supercell_matrix is not None:
_read_dataset_fc2(
phono3py,
ph3py_yaml,
calculator,
log_level,
)
phono3py.produce_fc2(
symmetrize_fc2=symmetrize_fc2,
is_compact_fc=settings.is_compact_fc,
fc_calculator=extract_fc2_fc3_calculators(fc_calculator, 2),
fc_calculator_options=extract_fc2_fc3_calculators(fc_calculator_options, 2),
)
if output_filename is None:
filename = "fc2.hdf5"
else:
filename = "fc2." + output_filename + ".hdf5"
if log_level:
print('Writing fc2 to "%s".' % filename)
write_fc2_to_hdf5(
phono3py.fc2,
filename=filename,
p2s_map=p2s_map,
physical_unit="eV/angstrom^2",
compression=settings.hdf5_compression,
)
if log_level:
show_drift_force_constants(phono3py.fc2, primitive=phonon_primitive, name="fc2")
def parse_forces(
phono3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
cutoff_pair_distance=None,
force_filename: str = "FORCES_FC3",
phono3py_yaml_filename: Optional[str] = None,
ph3py_yaml: Phono3pyYaml | None = None,
cutoff_pair_distance: float | None = None,
force_filename: str | os.PathLike = "FORCES_FC3",
phono3py_yaml_filename: str | os.PathLike | None = None,
fc_type: Literal["fc3", "phonon_fc2"] = "fc3",
calculator: Optional[str] = None,
log_level=0,
calculator: str | None = None,
log_level: int = 0,
) -> dict:
"""Read displacements and forces.
@ -239,7 +81,7 @@ def parse_forces(
without writing calculator name in it.
"""
filename_read_from: Optional[str] = None
filename_read_from = None
dataset = None
if phono3py.phonon_supercell is None or fc_type == "fc3":
@ -266,6 +108,9 @@ def parse_forces(
if dataset:
filename_read_from = force_filename
if dataset is None:
raise RuntimeError("Dataset is not found.")
# Units of displacements and forces are converted. If forces don't
# exist, the conversion will not be performed for forces.
if calculator is not None:
@ -274,8 +119,6 @@ def parse_forces(
distance_to_A=physical_units["distance_to_A"],
force_to_eVperA=physical_units["force_to_eVperA"],
)
if dataset is None:
raise RuntimeError("Dataset is not found.")
if "natom" in dataset and dataset["natom"] != natom:
raise RuntimeError(
@ -307,140 +150,13 @@ def parse_forces(
return dataset
def get_fc_calculator_params(
settings: Phono3pySettings, log_level: int = 0
) -> tuple[Optional[str], Optional[str]]:
"""Return fc_calculator and fc_calculator_params from settings."""
fc_calculator = None
fc_calculator_list = []
if settings.fc_calculator is not None:
for fc_calculatr_str in settings.fc_calculator.split("|"):
if fc_calculatr_str == "": # No external calculator
fc_calculator_list.append(fc_calculatr_str.lower())
elif fc_calculatr_str.lower() in fc_calculator_names:
fc_calculator_list.append(fc_calculatr_str.lower())
if fc_calculator_list:
fc_calculator = "|".join(fc_calculator_list)
fc_calculator_options = settings.fc_calculator_options
if settings.cutoff_pair_distance:
if fc_calculator_list and fc_calculator_list[-1] in ("alm", "symfc"):
if fc_calculator_list[-1] == "alm":
cutoff_str = f"-1 {settings.cutoff_pair_distance}"
if fc_calculator_list[-1] == "symfc":
cutoff_str = f"{settings.cutoff_pair_distance}"
fc_calculator_options = _set_cutoff_in_fc_calculator_options(
fc_calculator_options,
cutoff_str,
log_level,
)
return fc_calculator, fc_calculator_options
def _set_cutoff_in_fc_calculator_options(
fc_calculator_options: Optional[str],
cutoff_str: str,
log_level: int,
):
str_appended = f"cutoff={cutoff_str}"
calc_opts = fc_calculator_options
if calc_opts is None:
calc_opts = "|"
if "|" in calc_opts:
calc_opts_fc2, calc_opts_fc3 = [v.strip() for v in calc_opts.split("|")][:2]
else:
calc_opts_fc2 = calc_opts
calc_opts_fc3 = calc_opts
if calc_opts_fc3 == "":
calc_opts_fc3 += f"{str_appended}"
if log_level:
print(f'Set "{str_appended}" to fc_calculator_options for fc3.')
elif "cutoff" not in calc_opts_fc3:
calc_opts_fc3 += f", {str_appended}"
if log_level:
print(f'Appended "{str_appended}" to fc_calculator_options for fc3.')
return f"{calc_opts_fc2}|{calc_opts_fc3}"
def _read_phono3py_fc3(phono3py: Phono3py, symmetrize_fc3r, input_filename, log_level):
if input_filename is None:
filename = "fc3.hdf5"
else:
filename = "fc3." + input_filename + ".hdf5"
file_exists(filename, log_level=log_level)
if log_level:
print('Reading fc3 from "%s".' % filename)
p2s_map = phono3py.primitive.p2s_map
try:
fc3 = read_fc3_from_hdf5(filename=filename, p2s_map=p2s_map)
except RuntimeError:
import traceback
traceback.print_exc()
if log_level:
print_error()
sys.exit(1)
num_atom = len(phono3py.supercell)
if fc3.shape[1] != num_atom:
print("Matrix shape of fc3 doesn't agree with supercell size.")
if log_level:
print_error()
sys.exit(1)
if symmetrize_fc3r:
set_translational_invariance_fc3(fc3)
set_permutation_symmetry_fc3(fc3)
phono3py.fc3 = fc3
def _read_phono3py_fc2(phono3py, symmetrize_fc2, input_filename, log_level):
if input_filename is None:
filename = "fc2.hdf5"
else:
filename = "fc2." + input_filename + ".hdf5"
file_exists(filename, log_level=log_level)
if log_level:
print('Reading fc2 from "%s".' % filename)
num_atom = len(phono3py.phonon_supercell)
p2s_map = phono3py.phonon_primitive.p2s_map
try:
phonon_fc2 = read_fc2_from_hdf5(filename=filename, p2s_map=p2s_map)
except RuntimeError:
import traceback
traceback.print_exc()
if log_level:
print_error()
sys.exit(1)
if phonon_fc2.shape[1] != num_atom:
print("Matrix shape of fc2 doesn't agree with supercell size.")
if log_level:
print_error()
sys.exit(1)
if symmetrize_fc2:
if phonon_fc2.shape[0] == phonon_fc2.shape[1]:
symmetrize_force_constants(phonon_fc2)
else:
symmetrize_compact_force_constants(phonon_fc2, phono3py.phonon_primitive)
phono3py.fc2 = phonon_fc2
def _read_FORCES_FC3_or_FC2(
natom: int,
dataset: Optional[dict],
dataset: dict | None,
fc_type: str,
filename: str = "FORCES_FC3",
filename: str | os.PathLike = "FORCES_FC3",
log_level: int = 0,
) -> Optional[dict]:
) -> dict | None:
"""Read FORCES_FC3 or FORCES_FC2.
Read the first line of forces file to determine the type of the file.
@ -458,7 +174,9 @@ def _read_FORCES_FC3_or_FC2(
print(f'{n_disp} snapshots were found in "{filename}".')
return _dataset
# Type-1
# Try reading type-1 dataset
if dataset is None:
raise RuntimeError("Type-1 displacement dataset is not given.")
if fc_type == "fc3":
parse_FORCES_FC3(dataset, filename)
else:
@ -471,175 +189,108 @@ def _read_FORCES_FC3_or_FC2(
return dataset
def _read_dataset_fc3(
phono3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
phono3py_yaml_filename: Optional[str],
cutoff_pair_distance: Optional[float],
calculator: Optional[str],
log_level: int,
) -> dict:
"""Read or calculate fc3.
Note
----
cutoff_pair_distance is the parameter to determine each displaced
supercell is included to the computation of fc3. It is assumed that
cutoff_pair_distance is stored in the step to create sets of
displacements and the value is stored n the displacement dataset and
also as the parameter 'included': True or False for each displacement.
The parameter cutoff_pair_distance here can be used in the step to
create fc3 by overwriting original cutoff_pair_distance value only
when the former value is smaller than the later.
"""
try:
dataset = parse_forces(
phono3py,
ph3py_yaml=ph3py_yaml,
cutoff_pair_distance=cutoff_pair_distance,
force_filename="FORCES_FC3",
phono3py_yaml_filename=phono3py_yaml_filename,
fc_type="fc3",
calculator=calculator,
log_level=log_level,
)
except RuntimeError as e:
# from _parse_forces_type1
if log_level:
print(str(e))
print_error()
sys.exit(1)
except FileNotFoundError as e:
# from _get_type2_dataset
file_exists(e.filename, log_level=log_level)
return dataset
def run_pypolymlp_to_compute_forces(
def develop_or_load_pypolymlp(
ph3py: Phono3py,
mlp_params: Union[str, dict, PypolymlpParams],
displacement_distance: Optional[float] = None,
number_of_snapshots: Optional[int] = None,
random_seed: Optional[int] = None,
prepare_dataset: bool = False,
cutoff_pair_distance: Optional[float] = None,
mlp_filename: Optional[str] = None,
mlp_params: str | dict | PypolymlpParams | None = None,
mlp_filename: str | os.PathLike | None = 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.")
develop_or_load_pypolymlp_phonopy(
cast(Phonopy, ph3py),
mlp_params=mlp_params,
mlp_filename=mlp_filename,
log_level=log_level,
)
mlp_loaded = False
for mlp_filename in ["polymlp.yaml", "phono3py.pmlp"]:
_mlp_filename_list = list(pathlib.Path().glob(f"{mlp_filename}*"))
if _mlp_filename_list:
_mlp_filename = _mlp_filename_list[0]
print(_mlp_filename)
if _mlp_filename.suffix not in [
".yaml",
".pmlp",
".xz",
".gz",
".bz2",
"lzma",
]:
continue
if log_level:
print(f'Load MLPs from "{mlp_filename}".')
ph3py.load_mlp(mlp_filename)
mlp_loaded = True
if log_level and mlp_filename == "phono3py.pmlp":
print(f'Loading MLPs from "{_mlp_filename}" is obsolete.')
break
mlp_filename = "polymlp.yaml"
if not mlp_loaded:
if forces_in_dataset(ph3py.mlp_dataset):
if log_level:
if mlp_params is None:
pmlp_params = PypolymlpParams()
else:
pmlp_params = parse_mlp_params(mlp_params)
print("Parameters:")
for k, v in asdict(pmlp_params).items():
if v is not None:
print(f" {k}: {v}")
print("Developing MLPs by pypolymlp...", flush=True)
ph3py.develop_mlp(params=mlp_params)
ph3py.save_mlp(filename=mlp_filename)
if log_level:
print(f'MLPs were written into "{mlp_filename}"', flush=True)
else:
raise RuntimeError(f'"{mlp_filename}" is not found.')
def generate_displacements_and_evaluate_pypolymlp(
ph3py: Phono3py,
displacement_distance: float | None = None,
number_of_snapshots: int | Literal["auto"] | None = None,
number_estimation_factor: int | None = None,
random_seed: int | None = None,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
cutoff_pair_distance: float | None = None,
symfc_memory_size: float | None = None,
log_level: int = 0,
):
"""Generate displacements and evaluate forces by pypolymlp."""
if displacement_distance is None:
_displacement_distance = 0.01
else:
_displacement_distance = displacement_distance
if log_level:
print("-" * 30 + " pypolymlp end " + "-" * 31, flush=True)
if prepare_dataset:
if displacement_distance is None:
_displacement_distance = 0.01
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")
if number_of_snapshots:
print("Generate random displacements")
print(
f" Displacement distance: {_displacement_distance:.5f}".rstrip(
"0"
).rstrip(".")
" 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,
cutoff_pair_distance=cutoff_pair_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,
)
cutoff_pair_distance = determine_cutoff_pair_distance(
fc_calculator=fc_calculator,
fc_calculator_options=fc_calculator_options,
cutoff_pair_distance=cutoff_pair_distance,
symfc_memory_size=symfc_memory_size,
random_displacements=number_of_snapshots,
supercell=ph3py.supercell,
primitive=ph3py.primitive,
symmetry=ph3py.symmetry,
log_level=log_level,
)
ph3py.generate_displacements(
distance=_displacement_distance,
cutoff_pair_distance=cutoff_pair_distance,
is_plusminus=True,
number_of_snapshots=number_of_snapshots,
random_seed=random_seed,
number_estimation_factor=number_estimation_factor,
)
if ph3py.supercells_with_displacements is None:
raise RuntimeError("Displacements are not set. Run generate_displacements.")
if log_level:
print(
f"Evaluate forces in {ph3py.displacements.shape[0]} supercells "
"by pypolymlp",
flush=True,
)
ph3py.evaluate_mlp()
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,
mlp_params: str | dict | PypolymlpParams | None = None,
displacement_distance: float | None = None,
number_of_snapshots: int | None = None,
random_seed: int | None = None,
log_level: int = 0,
):
"""Run pypolymlp to compute phonon forces."""
if ph3py.phonon_mlp_dataset is not None:
if log_level:
import pypolymlp
print("-" * 29 + " pypolymlp start " + "-" * 30)
print(f"Pypolymlp version {pypolymlp.__version__}")
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():
for k, v in dataclasses.asdict(parse_mlp_params(mlp_params)).items():
if v is not None:
print(f" {k}: {v}")
if log_level:
@ -676,61 +327,10 @@ def run_pypolymlp_to_compute_phonon_forces(
ph3py.evaluate_phonon_mlp()
def _read_dataset_fc2(
phono3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
calculator,
log_level,
):
"""Read forces and produce fc2.
force_filename is either "FORCES_FC2" or "FORCES_FC3".
"""
# _ph3py_yaml = _get_default_ph3py_yaml(ph3py_yaml)
if phono3py.phonon_supercell_matrix is not None:
force_filename = "FORCES_FC2"
elif phono3py.dataset is None:
force_filename = "FORCES_FC3"
else:
raise RuntimeError("Force filename is not determined.")
try:
dataset = parse_forces(
phono3py,
ph3py_yaml=ph3py_yaml,
force_filename=force_filename,
fc_type="phonon_fc2",
calculator=calculator,
log_level=log_level,
)
except RuntimeError as e:
if log_level:
print(str(e))
print_error()
sys.exit(1)
except FileNotFoundError as e:
file_exists(e.filename, log_level=log_level)
if phono3py.phonon_supercell_matrix is not None:
phono3py.phonon_dataset = dataset
elif phono3py.dataset is None:
phono3py.dataset = dataset
def _get_default_ph3py_yaml(ph3py_yaml: Optional[Phono3pyYaml]):
_ph3py_yaml = ph3py_yaml
if _ph3py_yaml is None and pathlib.Path("phono3py_disp.yaml").exists():
_ph3py_yaml = Phono3pyYaml()
_ph3py_yaml.read("phono3py_disp.yaml")
return _ph3py_yaml
def _convert_unit_in_dataset(
dataset: dict,
distance_to_A: Optional[float] = None,
force_to_eVperA: Optional[float] = None,
distance_to_A: float | None = None,
force_to_eVperA: float | None = None,
) -> None:
"""Convert physical units of displacements and forces in dataset.
@ -769,9 +369,7 @@ def _to_ndarray(array, dtype="double"):
return array
def _extract_dataset_from_ph3py_yaml(
ph3py_yaml: Optional[Phono3pyYaml], fc_type
) -> Optional[dict]:
def _extract_dataset_from_ph3py_yaml(ph3py_yaml: Phono3pyYaml, fc_type) -> dict | None:
if ph3py_yaml.phonon_supercell is None or fc_type == "fc3":
if ph3py_yaml.dataset is not None:
return copy.deepcopy(ph3py_yaml.dataset)

View File

@ -200,17 +200,15 @@ def create_FORCES_FC2_from_FORCE_SETS(log_level):
def create_FORCE_SETS_from_FORCES_FCx(
phonon_smat, input_filename: Optional[str], cell_filename: Optional[str], log_level
phonon_smat, cell_filename: Optional[str], log_level
):
"""Convert FORCES_FC3 or FORCES_FC2 to FORCE_SETS."""
if cell_filename is not None and is_file_phonopy_yaml(
cell_filename, keyword="phono3py"
):
disp_filename = cell_filename
elif input_filename is None:
disp_filename = "phono3py_disp.yaml"
else:
disp_filename = f"phono3py_disp.{input_filename}.yaml"
disp_filename = "phono3py_disp.yaml"
if phonon_smat is not None:
forces_filename = "FORCES_FC2"
else:

View File

@ -34,98 +34,161 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import dataclasses
import os
import numpy as np
from numpy.typing import ArrayLike
from phonopy.cui.collect_cell_info import CellInfoResult
from phonopy.cui.collect_cell_info import get_cell_info as phonopy_get_cell_info
from phonopy.interface.calculator import write_supercells_with_displacements
from phonopy.structure.cells import print_cell
from phono3py import Phono3py
from phono3py.cui.settings import Phono3pySettings
from phono3py.cui.show_log import print_supercell_matrix
from phono3py.interface.calculator import (
get_additional_info_to_write_fc2_supercells,
get_additional_info_to_write_supercells,
get_default_displacement_distance,
)
from phono3py.interface.fc_calculator import determine_cutoff_pair_distance
from phono3py.interface.phono3py_yaml import Phono3pyYaml
@dataclasses.dataclass
class Phono3pyCellInfoResult(CellInfoResult):
"""Phono3py cell info result.
This is a subclass of CellInfoResult to add phonon supercell matrix.
"""
phono3py_yaml: Phono3pyYaml | None = None
phonon_supercell_matrix: ArrayLike | None = None
def get_cell_info(
settings: Phono3pySettings,
cell_filename: str | os.PathLike | None,
log_level: int,
load_phonopy_yaml: bool = True,
) -> Phono3pyCellInfoResult:
"""Return calculator interface and crystal structure information."""
cell_info = phonopy_get_cell_info(
settings,
cell_filename,
log_level=log_level,
load_phonopy_yaml=load_phonopy_yaml,
phonopy_yaml_cls=Phono3pyYaml,
)
cell_info_dict = dataclasses.asdict(cell_info)
cell_info_dict["phono3py_yaml"] = cell_info_dict.pop("phonopy_yaml")
cell_info = Phono3pyCellInfoResult(
**cell_info_dict,
phonon_supercell_matrix=settings.phonon_supercell_matrix,
)
ph3py_yaml = cell_info.phono3py_yaml
if cell_info.phonon_supercell_matrix is None and ph3py_yaml:
cell_info.phonon_supercell_matrix = ph3py_yaml.phonon_supercell_matrix
return cell_info
def create_phono3py_supercells(
cell_info,
settings,
symprec,
interface_mode="vasp",
log_level=1,
cell_info: Phono3pyCellInfoResult,
settings: Phono3pySettings,
symprec: float,
interface_mode: str | None = "vasp",
log_level: int = 1,
):
"""Create displacements and supercells.
Distance unit used is that for the calculator interface.
The default unit is Angstron.
The default unit is Angstrom.
"""
optional_structure_info = cell_info["optional_structure_info"]
optional_structure_info = cell_info.optional_structure_info
if settings.displacement_distance is None:
distance = get_default_displacement_distance(interface_mode)
else:
distance = settings.displacement_distance
phono3py = Phono3py(
cell_info["unitcell"],
cell_info["supercell_matrix"],
primitive_matrix=cell_info["primitive_matrix"],
phonon_supercell_matrix=cell_info["phonon_supercell_matrix"],
ph3 = Phono3py(
cell_info.unitcell,
cell_info.supercell_matrix,
primitive_matrix=cell_info.primitive_matrix,
phonon_supercell_matrix=cell_info.phonon_supercell_matrix,
is_symmetry=settings.is_symmetry,
symprec=symprec,
calculator=interface_mode,
)
phono3py.generate_displacements(
distance=distance,
if log_level:
print("")
print('Unit cell was read from "%s".' % optional_structure_info[0])
print("-" * 32 + " unit cell " + "-" * 33) # 32 + 11 + 33 = 76
print_cell(ph3.unitcell)
print("-" * 76)
print_supercell_matrix(ph3.supercell_matrix, ph3.phonon_supercell_matrix)
if ph3.primitive_matrix is not None:
print("Primitive matrix:")
for v in ph3.primitive_matrix:
print(" %s" % v)
print("Displacement distance: %s" % distance)
cutoff_pair_distance = determine_cutoff_pair_distance(
fc_calculator=settings.fc_calculator,
fc_calculator_options=settings.fc_calculator_options,
cutoff_pair_distance=settings.cutoff_pair_distance,
symfc_memory_size=settings.symfc_memory_size,
random_displacements=settings.random_displacements,
supercell=ph3.supercell,
primitive=ph3.primitive,
symmetry=ph3.symmetry,
log_level=log_level,
)
ph3.generate_displacements(
distance=distance,
cutoff_pair_distance=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,
number_estimation_factor=settings.rd_number_estimation_factor,
)
if (
settings.random_displacements_fc2
or settings.phonon_supercell_matrix is not None
):
phono3py.generate_fc2_displacements(
ph3.generate_fc2_displacements(
distance=distance,
is_plusminus=settings.is_plusminus_displacement_fc2,
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])
print("-" * 32 + " unit cell " + "-" * 33) # 32 + 11 + 33 = 76
print_cell(phono3py.unitcell)
print("-" * 76)
print_supercell_matrix(
phono3py.supercell_matrix, phono3py.phonon_supercell_matrix
)
if phono3py.primitive_matrix is not None:
print("Primitive matrix:")
for v in phono3py.primitive_matrix:
print(" %s" % v)
print("Displacement distance: %s" % distance)
ids = []
disp_cells = []
for i, cell in enumerate(phono3py.supercells_with_displacements):
for i, cell in enumerate(ph3.supercells_with_displacements):
if cell is not None:
ids.append(i + 1)
disp_cells.append(cell)
additional_info = get_additional_info_to_write_supercells(
interface_mode, phono3py.supercell_matrix
interface_mode, ph3.supercell_matrix
)
additional_info["supercell_matrix"] = phono3py.supercell_matrix
additional_info["supercell_matrix"] = ph3.supercell_matrix
write_supercells_with_displacements(
interface_mode,
phono3py.supercell,
ph3.supercell,
disp_cells,
optional_structure_info,
displacement_ids=ids,
@ -134,24 +197,25 @@ def create_phono3py_supercells(
)
if log_level:
num_disps = len(phono3py.supercells_with_displacements)
num_disps = len(ph3.supercells_with_displacements)
num_disp_files = len(disp_cells)
print("Number of displacements: %d" % num_disps)
if settings.cutoff_pair_distance is not None:
print(
"Cutoff distance for displacements: %s" % settings.cutoff_pair_distance
)
print("Number of displacement supercell files created: %d" % num_disp_files)
print(f"Number of displacements: {num_disps}")
if cutoff_pair_distance is not None:
print(f"Cutoff distance for displacements: {cutoff_pair_distance}")
print(f"Number of displacement supercell files created: {num_disp_files}")
if phono3py.phonon_supercell_matrix is not None:
num_disps = len(phono3py.phonon_supercells_with_displacements)
if (
ph3.phonon_supercell_matrix is not None
and ph3.phonon_supercells_with_displacements is not None
):
num_disps = len(ph3.phonon_supercells_with_displacements)
additional_info = get_additional_info_to_write_fc2_supercells(
interface_mode, phono3py.phonon_supercell_matrix
interface_mode, ph3.phonon_supercell_matrix
)
write_supercells_with_displacements(
interface_mode,
phono3py.phonon_supercell,
phono3py.phonon_supercells_with_displacements,
ph3.phonon_supercell,
ph3.phonon_supercells_with_displacements,
optional_structure_info,
zfill_width=5,
additional_info=additional_info,
@ -165,11 +229,11 @@ def create_phono3py_supercells(
n_pure_trans = sum(
[
(r == identity).all()
for r in phono3py.symmetry.symmetry_operations["rotations"]
for r in ph3.symmetry.symmetry_operations["rotations"]
]
)
if len(phono3py.supercell) // len(phono3py.primitive) != n_pure_trans:
if len(ph3.supercell) // len(ph3.primitive) != n_pure_trans:
print("*" * 72)
print(
"Note: "
@ -177,4 +241,4 @@ def create_phono3py_supercells(
)
print("*" * 72)
return phono3py
return ph3

View File

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

View File

@ -38,10 +38,11 @@ from __future__ import annotations
import os
import pathlib
from collections.abc import Sequence
from typing import Optional, Union
from typing import Literal
import numpy as np
import phonopy.cui.load_helper as load_helper
from numpy.typing import NDArray
from phonopy.harmonic.force_constants import show_drift_force_constants
from phonopy.interface.calculator import get_calculator_physical_units
from phonopy.physical_units import get_physical_units
@ -50,12 +51,13 @@ from phonopy.structure.cells import determinant
from phono3py import Phono3py
from phono3py.cui.create_force_constants import (
develop_or_load_pypolymlp,
parse_forces,
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,
extract_fc2_fc3_calculators_options,
update_cutoff_fc_calculator_options,
)
from phono3py.interface.phono3py_yaml import Phono3pyYaml
@ -64,34 +66,34 @@ from phono3py.phonon3.fc3 import show_drift_fc3
def load(
phono3py_yaml: Optional[
Union[str, bytes, os.PathLike]
] = None, # phono3py.yaml-like must be the first argument.
supercell_matrix: Optional[Union[Sequence, np.ndarray]] = None,
primitive_matrix: Optional[Union[Sequence, np.ndarray]] = None,
phonon_supercell_matrix: Optional[Union[Sequence, np.ndarray]] = None,
phono3py_yaml: str
| os.PathLike
| None = None, # phono3py.yaml-like must be the first argument.
supercell_matrix: Sequence | NDArray | None = None,
primitive_matrix: Sequence | NDArray | None = None,
phonon_supercell_matrix: Sequence | NDArray | None = None,
is_nac: bool = True,
calculator: Optional[str] = None,
unitcell: Optional[PhonopyAtoms] = None,
supercell: Optional[PhonopyAtoms] = None,
nac_params: Optional[dict] = None,
unitcell_filename: Optional[Union[str, bytes, os.PathLike]] = None,
supercell_filename: Optional[Union[str, bytes, os.PathLike]] = None,
born_filename: Optional[Union[str, bytes, os.PathLike]] = None,
forces_fc3_filename: Optional[Union[str, bytes, os.PathLike]] = None,
forces_fc2_filename: Optional[Union[str, bytes, os.PathLike]] = None,
fc3_filename: Optional[Union[str, bytes, os.PathLike]] = None,
fc2_filename: Optional[Union[str, bytes, os.PathLike]] = None,
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[str] = None,
factor: Optional[float] = None,
calculator: str | None = None,
unitcell: PhonopyAtoms | None = None,
supercell: PhonopyAtoms | None = None,
nac_params: dict | None = None,
unitcell_filename: str | os.PathLike | None = None,
supercell_filename: str | os.PathLike | None = None,
born_filename: str | os.PathLike | None = None,
forces_fc3_filename: str | os.PathLike | None = None,
forces_fc2_filename: str | os.PathLike | None = None,
fc3_filename: str | os.PathLike | None = None,
fc2_filename: str | os.PathLike | None = None,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
factor: float | None = None,
produce_fc: bool = True,
is_symmetry: bool = True,
symmetrize_fc: bool = True,
is_mesh_symmetry: bool = True,
is_compact_fc: bool = False,
use_pypolymlp: bool = False,
mlp_params: Optional[dict] = None,
mlp_params: dict | None = None,
use_grg: bool = False,
make_r0_average: bool = True,
symprec: float = 1e-5,
@ -291,6 +293,7 @@ def load(
elif phono3py_yaml is not None:
ph3py_yaml = Phono3pyYaml()
ph3py_yaml.read(phono3py_yaml)
assert ph3py_yaml.unitcell is not None
cell = ph3py_yaml.unitcell.copy()
_calculator = ph3py_yaml.calculator
smat = ph3py_yaml.supercell_matrix
@ -346,24 +349,32 @@ def load(
load_fc2_and_fc3(
ph3py, fc3_filename=fc3_filename, fc2_filename=fc2_filename, log_level=log_level
)
load_dataset_and_phonon_dataset(
ph3py.dataset = select_and_load_dataset(
ph3py,
ph3py_yaml,
ph3py_yaml=ph3py_yaml,
forces_fc3_filename=forces_fc3_filename,
forces_fc2_filename=forces_fc2_filename,
phono3py_yaml_filename=phono3py_yaml,
calculator=_calculator,
log_level=log_level,
)
ph3py.phonon_dataset = select_and_load_phonon_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc2_filename=forces_fc2_filename,
calculator=_calculator,
log_level=log_level,
)
if use_pypolymlp and ph3py.fc3 is None and forces_in_dataset(ph3py.dataset):
assert ph3py.dataset is not None
ph3py.mlp_dataset = ph3py.dataset
ph3py.dataset = None
if produce_fc:
if ph3py.fc3 is None and use_pypolymlp:
run_pypolymlp_to_compute_forces(
ph3py, mlp_params=mlp_params, log_level=log_level
)
develop_or_load_pypolymlp(ph3py, mlp_params=mlp_params, log_level=log_level)
compute_force_constants_from_datasets(
ph3py,
@ -371,7 +382,6 @@ def load(
fc_calculator_options=fc_calculator_options,
symmetrize_fc=symmetrize_fc,
is_compact_fc=is_compact_fc,
log_level=log_level,
)
if log_level and ph3py.fc3 is not None:
@ -386,62 +396,28 @@ def load(
def load_fc2_and_fc3(
ph3py: Phono3py,
fc3_filename: Optional[os.PathLike] = None,
fc2_filename: Optional[os.PathLike] = None,
fc3_filename: str | os.PathLike | None = None,
fc2_filename: str | os.PathLike | None = None,
read_fc3: bool = True,
read_fc2: bool = True,
log_level: int = 0,
):
"""Set force constants."""
if fc3_filename is not None or pathlib.Path("fc3.hdf5").exists():
fc3 = _load_fc3(ph3py, fc3_filename=fc3_filename, log_level=log_level)
ph3py.fc3 = fc3
if read_fc3 and (fc3_filename is not None or pathlib.Path("fc3.hdf5").exists()):
_load_fc3(ph3py, fc3_filename=fc3_filename, log_level=log_level)
if fc2_filename is not None or pathlib.Path("fc2.hdf5").exists():
fc2 = _load_fc2(ph3py, fc2_filename=fc2_filename, log_level=log_level)
ph3py.fc2 = fc2
def load_dataset_and_phonon_dataset(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
forces_fc3_filename: Optional[Union[os.PathLike, Sequence]] = None,
forces_fc2_filename: Optional[Union[os.PathLike, Sequence]] = None,
phono3py_yaml_filename: Optional[os.PathLike] = None,
cutoff_pair_distance: Optional[float] = None,
calculator: Optional[str] = None,
log_level: int = 0,
):
"""Set displacements, forces, and create force constants."""
dataset = _select_and_load_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc3_filename=forces_fc3_filename,
phono3py_yaml_filename=phono3py_yaml_filename,
cutoff_pair_distance=cutoff_pair_distance,
calculator=calculator,
log_level=log_level,
)
if dataset is not None:
ph3py.dataset = dataset
phonon_dataset = _select_and_load_phonon_dataset(
ph3py,
ph3py_yaml=ph3py_yaml,
forces_fc2_filename=forces_fc2_filename,
calculator=calculator,
log_level=log_level,
)
if phonon_dataset is not None:
ph3py.phonon_dataset = phonon_dataset
if read_fc2 and (fc2_filename is not None or pathlib.Path("fc2.hdf5").exists()):
_load_fc2(ph3py, fc2_filename=fc2_filename, log_level=log_level)
def compute_force_constants_from_datasets(
ph3py: Phono3py,
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[Union[dict, str]] = None,
cutoff_pair_distance: Optional[float] = None,
fc_calculator: Literal["traditional", "symfc", "alm"] | str | None = None,
fc_calculator_options: str | None = None,
cutoff_pair_distance: float | None = None,
symmetrize_fc: bool = True,
is_compact_fc: bool = True,
log_level: int = 0,
load_phono3py_yaml: bool = False,
):
"""Compute force constants from datasets.
@ -457,24 +433,21 @@ def compute_force_constants_from_datasets(
"""
fc3_calculator = extract_fc2_fc3_calculators(fc_calculator, 3)
fc2_calculator = extract_fc2_fc3_calculators(fc_calculator, 2)
fc3_calc_opts = extract_fc2_fc3_calculators(fc_calculator_options, 3)
fc3_calc_opts = extract_fc2_fc3_calculators_options(fc_calculator_options, 3)
fc3_calc_opts = update_cutoff_fc_calculator_options(
fc3_calc_opts, cutoff_pair_distance
)
fc2_calc_opts = extract_fc2_fc3_calculators(fc_calculator_options, 2)
exist_fc2 = ph3py.fc2 is not None
fc2_calc_opts = extract_fc2_fc3_calculators_options(fc_calculator_options, 2)
if ph3py.fc3 is None and forces_in_dataset(ph3py.dataset):
ph3py.produce_fc3(
symmetrize_fc3r=symmetrize_fc,
is_compact_fc=is_compact_fc,
fc_calculator=fc3_calculator,
fc_calculator_options=fc3_calc_opts,
use_symfc_projector=load_phono3py_yaml,
)
if log_level and symmetrize_fc and fc_calculator is None:
print("fc3 was symmetrized.")
if not exist_fc2:
if ph3py.fc2 is None or fc3_calculator != fc2_calculator:
if (
ph3py.phonon_supercell_matrix is None and forces_in_dataset(ph3py.dataset)
) or (
@ -486,38 +459,48 @@ def compute_force_constants_from_datasets(
is_compact_fc=is_compact_fc,
fc_calculator=fc2_calculator,
fc_calculator_options=fc2_calc_opts,
use_symfc_projector=load_phono3py_yaml,
)
if log_level and symmetrize_fc and fc_calculator is None:
print("fc2 was symmetrized.")
def _load_fc3(
ph3py: Phono3py,
fc3_filename: Optional[os.PathLike] = None,
fc3_filename: str | os.PathLike | None = None,
log_level: int = 0,
) -> np.ndarray:
):
p2s_map = ph3py.primitive.p2s_map
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)
if log_level:
print(f'fc3 was read from "{_fc3_filename}".')
return fc3
if isinstance(fc3, dict):
# fc3 is read from a file with type-1 format.
assert "fc3" in fc3
_check_fc3_shape(ph3py, fc3["fc3"], filename=_fc3_filename)
ph3py.fc3 = fc3["fc3"]
assert "fc3_nonzero_indices" in fc3
ph3py.fc3_nonzero_indices = fc3["fc3_nonzero_indices"]
if log_level:
print(f'fc3 and fc3 nonzero indices were read from "{_fc3_filename}".')
else:
_check_fc3_shape(ph3py, fc3, filename=_fc3_filename)
ph3py.fc3 = fc3
if log_level:
print(f'fc3 was read from "{_fc3_filename}".')
def _select_and_load_dataset(
def select_and_load_dataset(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
forces_fc3_filename: Optional[Union[os.PathLike, Sequence]] = None,
phono3py_yaml_filename: Optional[os.PathLike] = None,
cutoff_pair_distance: Optional[float] = None,
calculator: Optional[str] = None,
ph3py_yaml: Phono3pyYaml | None = None,
forces_fc3_filename: str | os.PathLike | Sequence | None = None,
phono3py_yaml_filename: str | os.PathLike | None = None,
cutoff_pair_distance: float | None = None,
calculator: str | None = None,
log_level: int = 0,
) -> Optional[dict]:
dataset = None
) -> dict | None:
"""Select and load dataset for fc3."""
# displacements and forces are in phono3py-yaml-like file
if (
ph3py_yaml is not None
and ph3py_yaml.dataset is not None
@ -532,7 +515,10 @@ def _select_and_load_dataset(
calculator,
log_level,
)
elif forces_fc3_filename is not None or pathlib.Path("FORCES_FC3").exists():
return dataset
# displacements and forces are in FORCES_FC3-like file
if forces_fc3_filename is not None or pathlib.Path("FORCES_FC3").exists():
if forces_fc3_filename is None:
force_filename = "FORCES_FC3"
else:
@ -546,7 +532,10 @@ def _select_and_load_dataset(
calculator,
log_level,
)
elif ph3py_yaml is not None and ph3py_yaml.dataset is not None:
return dataset
# dataset is in phono3py-yaml-like file
if ph3py_yaml is not None and ph3py_yaml.dataset is not None:
# not forces_in_dataset(ph3py_yaml.dataset)
# but want to read displacement dataset.
dataset = _get_dataset_for_fc3(
@ -558,13 +547,14 @@ def _select_and_load_dataset(
calculator,
log_level,
)
return dataset
return dataset
return None
def _load_fc2(
ph3py: Phono3py, fc2_filename: Optional[os.PathLike] = None, log_level: int = 0
) -> np.ndarray:
ph3py: Phono3py, fc2_filename: str | os.PathLike | None = None, log_level: int = 0
):
phonon_p2s_map = ph3py.phonon_primitive.p2s_map
if fc2_filename is None:
_fc2_filename = "fc2.hdf5"
@ -574,22 +564,21 @@ def _load_fc2(
_check_fc2_shape(ph3py, fc2, filename=_fc2_filename)
if log_level:
print(f'fc2 was read from "{_fc2_filename}".')
return fc2
ph3py.fc2 = fc2
def _select_and_load_phonon_dataset(
def select_and_load_phonon_dataset(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml] = None,
forces_fc2_filename: Optional[Union[os.PathLike, Sequence]] = None,
calculator: Optional[str] = None,
ph3py_yaml: Phono3pyYaml | None = None,
forces_fc2_filename: str | os.PathLike | Sequence | None = None,
calculator: str | None = None,
log_level: int = 0,
) -> Optional[dict]:
phonon_dataset = None
if (
ph3py_yaml is not None
and ph3py_yaml.phonon_dataset is not None
and forces_in_dataset(ph3py_yaml.phonon_dataset)
):
) -> dict | None:
"""Select and load phonon dataset for fc2."""
if ph3py.phonon_supercell_matrix is None:
return None
if ph3py_yaml is not None and forces_in_dataset(ph3py_yaml.phonon_dataset):
phonon_dataset = _get_dataset_for_fc2(
ph3py,
ph3py_yaml,
@ -598,9 +587,9 @@ def _select_and_load_phonon_dataset(
calculator,
log_level,
)
elif (
forces_fc2_filename is not None or pathlib.Path("FORCES_FC2").exists()
) and ph3py.phonon_supercell_matrix is not None:
return phonon_dataset
if forces_fc2_filename is not None or pathlib.Path("FORCES_FC2").exists():
if forces_fc2_filename is None:
force_filename = "FORCES_FC2"
else:
@ -613,7 +602,9 @@ def _select_and_load_phonon_dataset(
calculator,
log_level,
)
elif ph3py_yaml is not None and ph3py_yaml.phonon_dataset is not None:
return phonon_dataset
if ph3py_yaml is not None:
# not forces_in_dataset(ph3py_yaml.dataset)
# but want to read displacement dataset.
phonon_dataset = _get_dataset_for_fc2(
@ -624,13 +615,14 @@ def _select_and_load_phonon_dataset(
calculator,
log_level,
)
return phonon_dataset
return phonon_dataset
return None
def _get_dataset_for_fc3(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
ph3py_yaml: Phono3pyYaml | None,
force_filename,
phono3py_yaml_filename,
cutoff_pair_distance,
@ -652,7 +644,7 @@ def _get_dataset_for_fc3(
def _get_dataset_for_fc2(
ph3py: Phono3py,
ph3py_yaml: Optional[Phono3pyYaml],
ph3py_yaml: Phono3pyYaml | None,
force_filename,
fc_type,
calculator,
@ -669,7 +661,7 @@ def _get_dataset_for_fc2(
return dataset
def _check_fc2_shape(ph3py: Phono3py, fc2, filename="fc2.hdf5"):
def _check_fc2_shape(ph3py: Phono3py, fc2, filename: str | os.PathLike = "fc2.hdf5"):
if ph3py.phonon_supercell_matrix is None:
smat = ph3py.supercell_matrix
else:
@ -677,7 +669,9 @@ def _check_fc2_shape(ph3py: Phono3py, fc2, filename="fc2.hdf5"):
_check_fc_shape(ph3py, fc2, smat, filename)
def _check_fc3_shape(ph3py: Phono3py, fc3, filename="fc3.hdf5"):
def _check_fc3_shape(
ph3py: Phono3py, fc3: NDArray, filename: str | os.PathLike = "fc3.hdf5"
):
smat = ph3py.supercell_matrix
_check_fc_shape(ph3py, fc3, smat, filename)

View File

@ -34,12 +34,14 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import sys
from phonopy.cui.phonopy_argparse import fix_deprecated_option_names
def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
def get_parser(load_phono3py_yaml: bool = False):
"""Return ArgumentParser instance."""
deprecated = fix_deprecated_option_names(sys.argv)
import argparse
@ -56,7 +58,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--alm",
dest="use_alm",
action="store_true",
default=False,
default=None,
help=("Use ALM for generating 2nd and 3rd force constants in one fitting"),
)
parser.add_argument(
@ -70,7 +72,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--ave-pp",
dest="use_ave_pp",
action="store_true",
default=False,
default=None,
help="Use averaged ph-ph interaction",
)
parser.add_argument(
@ -113,7 +115,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--bterta",
dest="is_bterta",
action="store_true",
default=False,
default=None,
help="Calculate thermal conductivity in BTE-RTA",
)
if not load_phono3py_yaml:
@ -155,7 +157,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
metavar="FILE",
dest="subtract_forces",
default=None,
help="Subtract recidual forces from supercell forces",
help="Subtract residual forces from supercell forces",
)
parser.add_argument(
"--cfz-fc2",
@ -163,22 +165,22 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
metavar="FILE",
dest="subtract_forces_fc2",
default=None,
help="Subtract recidual forces from supercell forces for fc2",
help="Subtract residual forces from supercell forces for fc2",
)
parser.add_argument(
"--cfc",
"--compact-fc",
dest="is_compact_fc",
action="store_true",
default=False,
help="Use compact force cosntants",
default=None,
help="Use compact force constants",
)
parser.add_argument(
"--cfs",
"--create-force-sets",
dest="force_sets_mode",
action="store_true",
default=False,
default=None,
help="Create phonopy FORCE_SETS from FORCES_FC2",
)
parser.add_argument(
@ -186,7 +188,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--collective-phonon",
dest="solve_collective_phonon",
action="store_true",
default=False,
default=None,
help="Solve collective phonons",
)
if load_phono3py_yaml:
@ -241,7 +243,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--disp",
dest="is_displacement",
action="store_true",
default=False,
default=None,
help="As first stage, get least displacements",
)
if not load_phono3py_yaml:
@ -263,7 +265,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
# "--emulate-v2",
# dest="emulate_v2",
# action="store_true",
# default=False,
# default=None,
# help="Emulate v2.x behavior.",
# )
parser.add_argument(
@ -278,23 +280,16 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--fc2",
dest="read_fc2",
action="store_true",
default=False,
default=None,
help="Read second order force constants",
)
parser.add_argument(
"--fc3",
dest="read_fc3",
action="store_true",
default=False,
default=None,
help="Read third order force constants",
)
parser.add_argument(
"--v2",
dest="is_fc3_r0_average",
action="store_false",
default=True,
help="Take average in fc3-r2q transformation around three atoms",
)
parser.add_argument(
"--fc-calc",
"--fc-calculator",
@ -312,7 +307,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"string with the style of key = values"
),
)
if not fc_symmetry:
if not load_phono3py_yaml:
parser.add_argument(
"--fc-symmetry",
"--sym-fc",
@ -343,7 +338,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--fs2f2",
"--force-sets-to-forces-fc2",
dest="force_sets_to_forces_fc2_mode",
default=False,
default=None,
action="store_true",
help="Create FORCES_FC2 from FORCE_SETS",
)
@ -351,7 +346,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--full-pp",
dest="is_full_pp",
action="store_true",
default=False,
default=None,
help=(
"Calculate full ph-ph interaction for RTA conductivity."
"This may be activated when full elements of ph-ph interaction "
@ -388,14 +383,14 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--generalized-regular-grid",
dest="use_grg",
action="store_true",
default=False,
default=None,
help="Use generalized regular grid.",
)
parser.add_argument(
"--gruneisen",
dest="is_gruneisen",
action="store_true",
default=False,
default=None,
help="Calculate phonon Gruneisen parameter",
)
parser.add_argument(
@ -411,21 +406,11 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="hdf5 compression filter (default: gzip)",
)
if not load_phono3py_yaml:
parser.add_argument(
"-i", dest="input_filename", default=None, help="Input filename extension"
)
parser.add_argument(
"--io",
dest="input_output_filename",
default=None,
help="Input and output filename extension",
)
parser.add_argument(
"--ion-clamped",
dest="ion_clamped",
action="store_true",
default=False,
default=None,
help=(
"Atoms are clamped under applied strain in Gruneisen parameter calculation"
),
@ -434,35 +419,35 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--ise",
dest="is_imag_self_energy",
action="store_true",
default=False,
default=None,
help="Calculate imaginary part of self energy",
)
parser.add_argument(
"--isotope",
dest="is_isotope",
action="store_true",
default=False,
default=None,
help="Isotope scattering lifetime",
)
parser.add_argument(
"--jdos",
dest="is_joint_dos",
action="store_true",
default=False,
default=None,
help="Calculate joint density of states",
)
parser.add_argument(
"--kubo",
dest="is_kubo_kappa",
action="store_true",
default=False,
default=None,
help="Choose Kubo lattice thermal conductivity.",
)
parser.add_argument(
"--lbte",
dest="is_lbte",
action="store_true",
default=False,
default=None,
help="Calculate thermal conductivity LBTE with Chaput's method",
)
parser.add_argument(
@ -494,7 +479,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Mass variance parameters for isotope scattering",
)
if not is_nac:
if not load_phono3py_yaml:
parser.add_argument(
"--nac",
dest="is_nac",
@ -508,7 +493,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Non-analytical term correction method: Gonze (default) or Wang",
)
if fc_symmetry:
if load_phono3py_yaml:
parser.add_argument(
"--no-fc-symmetry",
"--no-sym-fc",
@ -517,29 +502,44 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Do not symmetrize force constants",
)
parser.add_argument(
"--no-read-fc2",
dest="read_fc2",
action="store_false",
default=None,
help="Read second order force constants",
)
parser.add_argument(
"--no-read-fc3",
dest="read_fc3",
action="store_false",
default=None,
help="Read third order force constants",
)
parser.add_argument(
"--nodiag",
dest="is_nodiag",
action="store_true",
default=False,
default=None,
help="Set displacements parallel to axes",
)
parser.add_argument(
"--noks",
"--no-kappa-stars",
dest="no_kappa_stars",
action="store_true",
default=False,
dest="kappa_star",
action="store_false",
default=None,
help="Deactivate summation of partial kappa at q-stars",
)
parser.add_argument(
"--nomeshsym",
dest="is_nomeshsym",
action="store_true",
default=False,
default=None,
help="No symmetrization of triplets is made.",
)
if is_nac:
if load_phono3py_yaml:
parser.add_argument(
"--nonac",
dest="is_nac",
@ -551,14 +551,14 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--nosym",
dest="is_nosym",
action="store_true",
default=False,
default=None,
help="Symmetry is not imposed.",
)
parser.add_argument(
"--nu",
dest="is_N_U",
action="store_true",
default=False,
default=None,
help="Split Gamma into Normal and Umklapp processes",
)
parser.add_argument(
@ -585,10 +585,6 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Output yaml filename instead of default filename of phono3py.yaml",
)
else:
parser.add_argument(
"-o", dest="output_filename", default=None, help="Output filename extension"
)
parser.add_argument(
"--pa",
"--primitive-axis",
@ -623,14 +619,14 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--pm",
dest="is_plusminus_displacements",
action="store_true",
default=False,
default=None,
help="Set plus minus displacements",
)
parser.add_argument(
"--pm-fc2",
dest="is_plusminus_displacements_fc2",
action="store_true",
default=False,
default=None,
help="Set plus minus displacements for extra fc2",
)
parser.add_argument(
@ -644,7 +640,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--pypolymlp",
dest="use_pypolymlp",
action="store_true",
default=False,
default=None,
help="Use pypolymlp and symfc for generating force constants",
)
parser.add_argument(
@ -666,7 +662,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--quiet",
dest="quiet",
action="store_true",
default=False,
default=None,
help="Print out smallest information",
)
parser.add_argument(
@ -683,6 +679,13 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help='Number of supercells with random displacements or "auto"',
)
parser.add_argument(
"--rd-auto-factor",
dest="rd_number_estimation_factor",
type=float,
default=None,
help="Factor to estimate number of supercells with random displacements",
)
parser.add_argument(
"--rd-fc2",
"--random-displacements-fc2",
@ -700,35 +703,42 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--read-gamma",
dest="read_gamma",
action="store_true",
default=False,
default=None,
help="Read Gammas from files",
)
parser.add_argument(
"--read-phonon",
dest="read_phonon",
action="store_true",
default=False,
default=None,
help="Read phonons from files",
)
parser.add_argument(
"--read-pp",
dest="read_pp",
action="store_true",
default=False,
default=None,
help="Read phonon-phonon interaction strength",
)
parser.add_argument(
"--reducible-colmat",
dest="is_reducible_collision_matrix",
action="store_true",
default=False,
default=None,
help="Solve reducible collision matrix",
)
parser.add_argument(
"--relax-atomic-positions",
dest="relax_atomic_positions",
action="store_true",
default=None,
help="Relax atomic positions using polynomial MLPs",
)
parser.add_argument(
"--rse",
dest="is_real_self_energy",
action="store_true",
default=False,
default=None,
help="Calculate real part of self energy",
)
parser.add_argument(
@ -778,11 +788,18 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
default=None,
help="Show symfc memory usage with respect to cutoff distance",
)
parser.add_argument(
"--symfc-memsize",
dest="symfc_memory_size",
type=float,
default=None,
help="Memory size to estimate cutoff pair distance (GB)",
)
parser.add_argument(
"--spf",
dest="is_spectral_function",
action="store_true",
default=False,
default=None,
help="Calculate spectral function",
)
parser.add_argument(
@ -790,7 +807,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--show-num-triplets",
dest="show_num_triplets",
action="store_true",
default=False,
default=None,
help=(
"Show reduced number of triplets to be calculated at specified grid points"
),
@ -800,21 +817,21 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--sym-fc2",
dest="is_symmetrize_fc2",
action="store_true",
default=False,
default=None,
help="Symmetrize fc2 by index exchange",
)
parser.add_argument(
"--sym-fc3r",
dest="is_symmetrize_fc3_r",
action="store_true",
default=False,
default=None,
help="Symmetrize fc3 in real space by index exchange",
)
parser.add_argument(
"--sym-fc3q",
dest="is_symmetrize_fc3_q",
action="store_true",
default=False,
default=None,
help="Symmetrize fc3 in reciprocal space by index exchange",
)
parser.add_argument(
@ -822,7 +839,7 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--tetrahedron-method",
dest="is_tetrahedron_method",
action="store_true",
default=False,
default=None,
help="Use tetrahedron method.",
)
parser.add_argument(
@ -859,15 +876,22 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--verbose",
dest="verbose",
action="store_true",
default=False,
default=None,
help="Detailed run-time information is displayed",
)
parser.add_argument(
"--v2",
dest="is_fc3_r0_average",
action="store_false",
default=None,
help="Take average in fc3-r2q transformation around three atoms",
)
parser.add_argument(
"--wgp",
"--write-grid-points",
dest="write_grid_points",
action="store_true",
default=False,
default=None,
help=(
"Write grid address of irreducible grid points for specified "
"mesh numbers to ir_grid_address.yaml"
@ -877,21 +901,21 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--wigner",
dest="is_wigner_kappa",
action="store_true",
default=False,
default=None,
help="Choose Wigner lattice thermal conductivity.",
)
parser.add_argument(
"--write-collision",
dest="write_collision",
action="store_true",
default=False,
default=None,
help="Write collision matrix and Gammas to files",
)
parser.add_argument(
"--write-gamma",
dest="write_gamma",
action="store_true",
default=False,
default=None,
help="Write imag-part of self energy to files",
)
parser.add_argument(
@ -899,28 +923,28 @@ def get_parser(fc_symmetry=False, is_nac=False, load_phono3py_yaml=False):
"--write_detailed_gamma",
dest="write_gamma_detail",
action="store_true",
default=False,
default=None,
help="Write out detailed imag-part of self energy",
)
parser.add_argument(
"--write-phonon",
dest="write_phonon",
action="store_true",
default=False,
default=None,
help="Write all phonons on grid points to files",
)
parser.add_argument(
"--write-pp",
dest="write_pp",
action="store_true",
default=False,
default=None,
help="Write phonon-phonon interaction strength",
)
parser.add_argument(
"--write-lbte-solution",
dest="write_LBTE_solution",
action="store_true",
default=False,
default=None,
help="Write direct solution of LBTE to hdf5 files",
)
if load_phono3py_yaml:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -34,9 +34,10 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import sys
from collections.abc import Sequence
from typing import Optional, Union
import numpy as np
from phonopy.structure.cells import print_cell
@ -46,13 +47,11 @@ from phono3py.cui.settings import Phono3pySettings
def show_general_settings(
settings,
run_mode,
phono3py,
cell_filename,
input_filename,
output_filename,
interface_mode,
settings: Phono3pySettings,
run_mode: str,
phono3py: Phono3py,
cell_filename: str,
interface_mode: str | None,
):
"""Show general setting information."""
is_primitive_axes_auto = (
@ -64,12 +63,13 @@ def show_general_settings(
phonon_supercell_matrix = phono3py.phonon_supercell_matrix
print("-" * 29 + " General settings " + "-" * 29)
if run_mode:
if settings.use_pypolymlp:
if settings.create_displacements or settings.random_displacements is not None:
print(f"Run mode: pypolymlp + {run_mode}")
else:
print("Run mode: pypolymlp")
else:
print(f"Run mode: {run_mode}")
if output_filename:
print(f"Output filename is modified by {output_filename}.")
if input_filename:
print(f"Input filename is modified by {input_filename}.")
if settings.hdf5_compression:
print(f"HDF5 data compression filter: {settings.hdf5_compression}")
if interface_mode:
@ -82,19 +82,18 @@ def show_general_settings(
print_supercell_matrix(supercell_matrix, phonon_supercell_matrix)
if is_primitive_axes_auto:
print("Primitive matrix (Auto):")
for v in primitive_matrix:
print(f" {v}")
elif primitive_matrix is not None:
print("Primitive matrix:")
if primitive_matrix is not None:
if is_primitive_axes_auto:
print("Primitive matrix (Auto):")
else:
print("Primitive matrix:")
for v in primitive_matrix:
print(f" {v}")
def print_supercell_matrix(
supercell_matrix: Union[Sequence, np.ndarray],
phonon_supercell_matrix: Optional[Union[Sequence, np.ndarray]] = None,
supercell_matrix: Sequence | np.ndarray,
phonon_supercell_matrix: Sequence | np.ndarray | None = None,
):
"""Print supercell matrix."""
if (np.diag(np.diag(supercell_matrix)) - supercell_matrix).any():

View File

@ -34,10 +34,12 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from collections.abc import Sequence
from typing import Optional, Union
from __future__ import annotations
import os
import numpy as np
from numpy.typing import ArrayLike
from phonopy.structure.atoms import PhonopyAtoms
from phono3py.file_IO import write_grid_address_to_hdf5, write_ir_grid_points
@ -48,13 +50,13 @@ from phono3py.phonon3.triplets import get_triplets_at_q
def write_grid_points(
primitive: PhonopyAtoms,
bz_grid: BZGrid,
band_indices: Optional[Union[Sequence, np.ndarray]] = None,
sigmas: Optional[Union[Sequence, np.ndarray]] = None,
temperatures: Optional[Union[Sequence, np.ndarray]] = None,
band_indices: ArrayLike | None = None,
sigmas: ArrayLike | None = None,
temperatures: ArrayLike | None = None,
is_kappa_star: bool = True,
is_lbte: bool = False,
compression: Union[str, int] = "gzip",
filename: bool = None,
compression: str | int = "gzip",
filename: str | os.PathLike | None = None,
):
"""Write grid points into files."""
ir_grid_points, ir_grid_weights = _get_ir_grid_points(
@ -109,8 +111,8 @@ def write_grid_points(
def show_num_triplets(
primitive: PhonopyAtoms,
bz_grid: BZGrid,
band_indices: Optional[Union[Sequence, np.ndarray]] = None,
grid_points: Optional[Union[Sequence, np.ndarray]] = None,
band_indices: ArrayLike | None = None,
grid_points: ArrayLike | None = None,
is_kappa_star: bool = True,
):
"""Show numbers of triplets at grid points."""

View File

@ -36,12 +36,14 @@
from __future__ import annotations
import os
import pathlib
import warnings
from collections.abc import Sequence
from typing import Optional, TextIO, Union
import h5py
import numpy as np
from numpy.typing import NDArray
from phonopy.cui.load_helper import read_force_constants_from_hdf5
# This import is deactivated for a while.
@ -52,6 +54,7 @@ from phonopy.file_IO import (
write_FORCE_SETS,
)
from phono3py.phonon.grid import BZGrid
from phono3py.version import __version__
@ -300,34 +303,54 @@ def _write_FORCES_FC3_typeI(
count += 1
def write_fc3_to_hdf5(fc3, filename="fc3.hdf5", p2s_map=None, compression="gzip"):
def write_fc3_to_hdf5(
fc3: NDArray,
fc3_nonzero_indices: NDArray | None = None,
filename: str = "fc3.hdf5",
p2s_map: NDArray | None = None,
fc3_cutoff: float | None = None,
compression: str = "gzip",
):
"""Write fc3 in fc3.hdf5.
Parameters
----------
force_constants : ndarray
Force constants
shape=(n_satom, n_satom, n_satom, 3, 3, 3) or
(n_patom, n_satom, n_satom,3,3,3), dtype=double
fc3 : ndarray
Force constants shape=(n_satom, n_satom, n_satom, 3, 3, 3) or (n_patom,
n_satom, n_satom,3,3,3), dtype=double
fc3_nonzero_indices : ndarray, optional
Non-zero indices of fc3. shape=(n_satom, n_satom, n_satom) or (n_patom,
n_satom, n_satom), dtype="byte". If this is given, fc3 is in compact
format. Otherwise, it is in full format.
filename : str
Filename to be used.
p2s_map : ndarray, optional
Primitive atom indices in supercell index system
shape=(n_patom,), dtype=intc
Primitive atom indices in supercell index system shape=(n_patom,),
dtype=intc
fc3_cutoff : float, optional
Cutoff distance for fc3.
compression : str or int, optional
h5py's lossless compression filters (e.g., "gzip", "lzf"). None gives
no compression. See the detail at docstring of
h5py.Group.create_dataset. Default is "gzip".
h5py's lossless compression filters (e.g., "gzip", "lzf"). None gives no
compression. See the detail at docstring of h5py.Group.create_dataset.
Default is "gzip".
"""
with h5py.File(filename, "w") as w:
w.create_dataset("version", data=np.bytes_(__version__))
w.create_dataset("fc3", data=fc3, compression=compression)
if fc3_nonzero_indices is not None:
w.create_dataset(
"fc3_nonzero_indices", data=fc3_nonzero_indices, compression=compression
)
if fc3_cutoff is not None:
w.create_dataset("fc3_cutoff", data=fc3_cutoff)
if p2s_map is not None:
w.create_dataset("p2s_map", data=p2s_map)
def read_fc3_from_hdf5(filename="fc3.hdf5", p2s_map=None):
def read_fc3_from_hdf5(
filename: str | os.PathLike = "fc3.hdf5", p2s_map: NDArray | None = None
) -> NDArray | dict:
"""Read fc3 from fc3.hdf5.
fc3 can be in full or compact format. They are distinguished by
@ -339,20 +362,44 @@ def read_fc3_from_hdf5(filename="fc3.hdf5", p2s_map=None):
"""
with h5py.File(filename, "r") as f:
fc3 = f["fc3"][:]
if "fc3" not in f:
raise KeyError(
f"{filename} does not have 'fc3' dataset. "
"This file is not a valid fc3.hdf5."
)
fc3: NDArray = f["fc3"][:] # type: ignore
if fc3.dtype == np.dtype("double") and fc3.flags.c_contiguous:
pass
else:
raise TypeError(
f"{filename} has to be read by h5py as numpy ndarray of "
"dtype='double' and c_contiguous."
)
if "p2s_map" in f:
p2s_map_in_file = f["p2s_map"][:]
check_force_constants_indices(
fc3.shape[:2], p2s_map_in_file, p2s_map, filename
)
if fc3.dtype == np.dtype("double") and fc3.flags.c_contiguous:
fc3_nonzero_indices = None # type: ignore
if "fc3_nonzero_indices" in f:
fc3_nonzero_indices: NDArray = f["fc3_nonzero_indices"][:] # type: ignore
if (
fc3_nonzero_indices.dtype == np.dtype("byte")
and fc3_nonzero_indices.flags.c_contiguous
):
pass
else:
raise TypeError(
f"{filename} has to be read by h5py as numpy ndarray of "
"dtype='byte' and c_contiguous."
)
if fc3_nonzero_indices is None:
return fc3
else:
msg = (
"%s has to be read by h5py as numpy ndarray of "
"dtype='double' and c_contiguous." % filename
)
raise TypeError(msg)
return {"fc3": fc3, "fc3_nonzero_indices": fc3_nonzero_indices}
def write_fc2_to_hdf5(
@ -406,7 +453,7 @@ def write_fc2_to_hdf5(
)
def read_fc2_from_hdf5(filename="fc2.hdf5", p2s_map=None):
def read_fc2_from_hdf5(filename: str | os.PathLike = "fc2.hdf5", p2s_map=None):
"""Read fc2 from fc2.hdf5."""
return read_force_constants_from_hdf5(
filename=filename, p2s_map=p2s_map, calculator="vasp"
@ -415,7 +462,7 @@ def read_fc2_from_hdf5(filename="fc2.hdf5", p2s_map=None):
def write_datasets_to_hdf5(
dataset: dict,
phonon_dataset: dict = None,
phonon_dataset: dict | None = None,
filename: str = "datasets.hdf5",
compression: str = "gzip",
):
@ -437,9 +484,9 @@ def write_grid_address_to_hdf5(
grid_address,
mesh,
grid_mapping_table,
bz_grid=None,
compression: Union[str, int] = "gzip",
filename=None,
bz_grid: BZGrid | None = None,
compression: str | int = "gzip",
filename: str | os.PathLike | None = None,
):
"""Write grid addresses to grid_address.hdf5."""
suffix = _get_filename_suffix(mesh, filename=filename)
@ -1090,7 +1137,7 @@ def read_gamma_from_hdf5(
filename=filename,
)
full_filename = "kappa" + suffix + ".hdf5"
if not os.path.exists(full_filename):
if not pathlib.Path(full_filename).exists():
return None, full_filename
read_data = {}
@ -1109,7 +1156,7 @@ def read_gamma_from_hdf5(
def read_collision_from_hdf5(
mesh,
indices=None,
indices: str | Sequence = "all",
grid_point=None,
band_index=None,
sigma=None,
@ -1117,8 +1164,8 @@ def read_collision_from_hdf5(
filename=None,
only_temperatures=False,
verbose=True,
):
"""Read colliction matrix.
) -> tuple:
"""Read collision matrix.
indices : array_like of int
Indices of temperatures.
@ -1137,10 +1184,8 @@ def read_collision_from_hdf5(
filename=filename,
)
full_filename = "collision" + suffix + ".hdf5"
if not os.path.exists(full_filename):
if verbose:
print("%s not found." % full_filename)
return None
if not pathlib.Path(full_filename).exists():
raise FileNotFoundError(f"{full_filename} not found.")
if only_temperatures:
with h5py.File(full_filename, "r") as f:
@ -1290,7 +1335,7 @@ def read_pp_from_hdf5(
filename=None,
verbose=True,
check_consistency=False,
):
) -> tuple:
"""Read ph-ph interaction strength from its hdf5 file."""
suffix = _get_filename_suffix(
mesh,
@ -1300,10 +1345,8 @@ def read_pp_from_hdf5(
filename=filename,
)
full_filename = "pp" + suffix + ".hdf5"
if not os.path.exists(full_filename):
if verbose:
print("%s not found." % full_filename)
return None
if not pathlib.Path(full_filename).exists():
raise FileNotFoundError(f"{full_filename} not found.")
with h5py.File(full_filename, "r") as f:
if "nonzero_pp" in f:
@ -1346,7 +1389,7 @@ def read_pp_from_hdf5(
return pp, g_zero
return None
raise RuntimeError(f"pp data not found in {full_filename}.")
def write_gamma_detail_to_hdf5(
@ -1479,14 +1522,12 @@ def write_phonon_to_hdf5(
return None
def read_phonon_from_hdf5(mesh, filename=None, verbose=True):
def read_phonon_from_hdf5(mesh, filename=None, verbose=True) -> tuple:
"""Read phonon from its hdf5 file."""
suffix = _get_filename_suffix(mesh, filename=filename)
full_filename = "phonon" + suffix + ".hdf5"
if not os.path.exists(full_filename):
if verbose:
print("%s not found." % full_filename)
return None
if not pathlib.Path(full_filename).exists():
raise FileNotFoundError(f"{full_filename} not found.")
with h5py.File(full_filename, "r") as f:
frequencies = np.array(f["frequency"][:], dtype="double", order="C")
@ -1504,8 +1545,6 @@ def read_phonon_from_hdf5(mesh, filename=None, verbose=True):
return frequencies, eigenvectors, grid_address
return None
def write_ir_grid_points(bz_grid, grid_points, grid_weights, primitive_lattice):
"""Write ir-grid-points in yaml."""
@ -1605,7 +1644,11 @@ def parse_disp_fc3_yaml(filename="disp_fc3.yaml", return_cell=False):
return new_dataset
def parse_FORCES_FC2(disp_dataset, filename="FORCES_FC2", unit_conversion_factor=None):
def parse_FORCES_FC2(
disp_dataset: dict,
filename: str | os.PathLike = "FORCES_FC2",
unit_conversion_factor: float | None = None,
):
"""Parse type1 FORCES_FC2 file and store forces in disp_dataset."""
num_atom = disp_dataset["natom"]
num_disp = len(disp_dataset["first_atoms"])
@ -1626,7 +1669,10 @@ def parse_FORCES_FC2(disp_dataset, filename="FORCES_FC2", unit_conversion_factor
def parse_FORCES_FC3(
disp_dataset, filename="FORCES_FC3", use_loadtxt=False, unit_conversion_factor=None
disp_dataset: dict,
filename: str | os.PathLike = "FORCES_FC3",
use_loadtxt: bool = False,
unit_conversion_factor: float | None = None,
):
"""Parse type1 FORCES_FC3 and store forces in disp_dataset."""
num_atom = disp_dataset["natom"]

View File

@ -36,11 +36,11 @@
from __future__ import annotations
from typing import Optional, Union
from typing import Literal
import numpy as np
from phonopy.interface.fc_calculator import FCSolver
from phonopy.interface.symfc import SymfcFCSolver
from phonopy.interface.fc_calculator import FCSolver, fc_calculator_names
from phonopy.interface.symfc import parse_symfc_options, update_symfc_cutoff_by_memsize
from phonopy.structure.atoms import PhonopyAtoms
from phonopy.structure.cells import Primitive
from phonopy.structure.symmetry import Symmetry
@ -49,137 +49,6 @@ from phono3py.phonon3.dataset import get_displacements_and_forces_fc3
from phono3py.phonon3.fc3 import get_fc3
def get_fc3_solver(
supercell: PhonopyAtoms,
primitive: Primitive,
dataset: dict,
fc_calculator: Optional[str] = None,
fc_calculator_options: Optional[str] = None,
is_compact_fc: bool = False,
symmetry: Optional[Symmetry] = None,
log_level: int = 0,
) -> FC3Solver:
"""Return force constants solver for fc3.
Parameters
----------
supercell : PhonopyAtoms
Supercell
primitive : Primitive
Primitive cell
dataset : dict, optional
Dataset that contains displacements, forces, and optionally
energies. Default is None.
fc_calculator : str, optional
Currently only 'alm' is supported. Default is None, meaning invoking
'alm'.
fc_calculator_options : str, optional
This is arbitrary string.
is_compact_fc : bool, optional
If True, force constants are returned in the compact form.
symmetry : Symmetry, optional
Symmetry of supercell. This is used for the traditional and symfc FC
solver. Default is None.
log_level : integer or bool, optional
Verbosity level. False or 0 means quiet. True or 1 means normal level
of log to stdout. 2 gives verbose mode.
Returns
-------
FC3Solver
Force constants solver for fc3.
"""
fc_solver_name = fc_calculator if fc_calculator is not None else "traditional"
fc_solver = FC3Solver(
fc_solver_name,
supercell,
symmetry=symmetry,
dataset=dataset,
is_compact_fc=is_compact_fc,
primitive=primitive,
orders=[2, 3],
options=fc_calculator_options,
log_level=log_level,
)
return fc_solver
def extract_fc2_fc3_calculators(
fc_calculator: Optional[Union[str, dict]],
order: int,
) -> Optional[Union[str, dict]]:
"""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, dict, or None.")
def update_cutoff_fc_calculator_options(
fc_calc_opts: Optional[Union[str, dict]],
cutoff_pair_distance: Optional[float],
) -> Optional[Union[str, dict]]:
"""Update fc_calculator_options with cutoff distances.
Parameters
----------
fc_calc_opts : str or dict
FC calculator options.
cutoff_pair_distance : float, optional
Cutoff distance for pair interaction.
"""
if cutoff_pair_distance is not None:
if not isinstance(fc_calc_opts, (str, dict)) and fc_calc_opts is not None:
raise RuntimeError("fc_calculator_options should be str, dict, or None.")
if isinstance(fc_calc_opts, dict) and "cutoff" not in fc_calc_opts:
fc_calc_opts["cutoff"] = float(cutoff_pair_distance)
elif isinstance(fc_calc_opts, str) and "cutoff" not in fc_calc_opts:
fc_calc_opts = f"{fc_calc_opts}, cutoff = {cutoff_pair_distance}"
elif fc_calc_opts is None:
fc_calc_opts = f"cutoff = {cutoff_pair_distance}"
return fc_calc_opts
def estimate_symfc_memory_usage(
supercell: PhonopyAtoms, symmetry: Symmetry, cutoff: float, batch_size: int = 100
):
"""Estimate memory usage to run symfc for fc3 with cutoff.
Total memory usage is memsize + memsize2. These are separated because
they behave differently with respect to cutoff distance.
batch_size is hardcoded to 100 because it is so in symfc.
"""
symfc_solver = SymfcFCSolver(supercell, symmetry, options={"cutoff": {3: cutoff}})
basis_size = symfc_solver.estimate_basis_size(orders=[3])[3]
memsize = basis_size**2 * 3 * 8 / 10**9
memsize2 = len(supercell) * 3 * batch_size * basis_size * 8 / 10**9
return memsize, memsize2
class FDFC3Solver:
"""Finite difference type force constants calculator.
@ -239,9 +108,229 @@ class FDFC3Solver:
class FC3Solver(FCSolver):
"""Force constants solver for fc3."""
def _set_traditional_solver(self, solver_class: Optional[type] = FDFC3Solver):
def _set_traditional_solver(self, solver_class: type | None = FDFC3Solver):
return super()._set_traditional_solver(solver_class=solver_class)
def _set_symfc_solver(self):
return super()._set_symfc_solver(order=3)
def _get_displacements_and_forces(self):
"""Return displacements and forces for fc3."""
assert self._dataset is not None
return get_displacements_and_forces_fc3(self._dataset)
def extract_fc2_fc3_calculators(
fc_calculator: Literal["traditional", "symfc", "alm"] | str | None,
order: int,
) -> Literal["traditional", "symfc", "alm"] | None:
"""Extract fc_calculator 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 fc_calculator is None:
return None
else:
_fc_calculator = _split_fc_calculator_str(fc_calculator, order)
if _fc_calculator is None:
return None
fc_calculator_lower = _fc_calculator.lower()
if fc_calculator_lower not in ("traditional", "symfc", "alm"):
raise ValueError(
f"Unknown fc_calculator: {_fc_calculator}. "
"Available calculators are 'traditional', 'symfc', and 'alm'."
)
return fc_calculator_lower
def extract_fc2_fc3_calculators_options(
fc_calculator_opts: str | None,
order: int,
) -> str | None:
"""Extract fc_calculator_options for fc2 and fc3.
fc_calculator_opts : str
FC calculator options. "|" 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 fc_calculator_opts is None:
return None
else:
_fc_calculator_opts = _split_fc_calculator_str(fc_calculator_opts, order)
return _fc_calculator_opts
def _split_fc_calculator_str(fc_calculator: str, order: int) -> str | None:
if "|" in fc_calculator:
_fc_calculator = fc_calculator.split("|")[order - 2]
if _fc_calculator == "":
return None
else:
if fc_calculator.strip() == "":
return None
else:
_fc_calculator = fc_calculator
return _fc_calculator
def update_cutoff_fc_calculator_options(
fc_calc_opts: str | None,
cutoff_pair_distance: float | None,
) -> str | None:
"""Update fc_calculator_options with cutoff distances.
Parameters
----------
fc_calc_opts : str or None
FC calculator options.
cutoff_pair_distance : float, optional
Cutoff distance for pair interaction.
"""
if cutoff_pair_distance is not None:
if isinstance(fc_calc_opts, str) and "cutoff" not in fc_calc_opts:
fc_calc_opts = f"{fc_calc_opts}, cutoff = {cutoff_pair_distance}"
elif fc_calc_opts is None:
fc_calc_opts = f"cutoff = {cutoff_pair_distance}"
return fc_calc_opts
def get_fc_calculator_params(
fc_calculator: str | None,
fc_calculator_options: str | None,
cutoff_pair_distance: float | None,
log_level: int = 0,
) -> tuple[str | None, str | None]:
"""Compile fc_calculator and fc_calculator_options from input settings."""
_fc_calculator = None
fc_calculator_list = []
if fc_calculator is not None:
for fc_calculatr_str in fc_calculator.split("|"):
if fc_calculatr_str == "": # No external calculator
fc_calculator_list.append(fc_calculatr_str.lower())
elif fc_calculatr_str.lower() in fc_calculator_names:
fc_calculator_list.append(fc_calculatr_str.lower())
if fc_calculator_list:
_fc_calculator = "|".join(fc_calculator_list)
_fc_calculator_options = fc_calculator_options
if cutoff_pair_distance:
if fc_calculator_list and fc_calculator_list[-1] in ("alm", "symfc"):
if fc_calculator_list[-1] == "alm":
cutoff_str = f"-1 {cutoff_pair_distance}"
if fc_calculator_list[-1] == "symfc":
cutoff_str = f"{cutoff_pair_distance}"
_fc_calculator_options = _set_cutoff_in_fc_calculator_options(
_fc_calculator_options,
cutoff_str,
log_level,
)
return _fc_calculator, _fc_calculator_options
def determine_cutoff_pair_distance(
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
cutoff_pair_distance: float | None = None,
symfc_memory_size: float | None = None,
random_displacements: int | str | None = None,
supercell: PhonopyAtoms | None = None,
primitive: Primitive | None = None,
symmetry: Symmetry | None = None,
log_level: int = 0,
) -> float | None:
"""Determine cutoff pair distance for displacements."""
_cutoff_pair_distance, _symfc_memory_size = _get_cutoff_pair_distance(
fc_calculator,
fc_calculator_options,
cutoff_pair_distance,
symfc_memory_size,
)
if random_displacements is not None and random_displacements != "auto":
_symfc_memory_size = None
if _symfc_memory_size is not None:
if fc_calculator is None:
pass
elif fc_calculator != "symfc":
raise RuntimeError(
"Estimation of cutoff_pair_distance by memory size is only "
"available for symfc calculator."
)
symfc_options = {"memsize": {3: _symfc_memory_size}}
if supercell is None or primitive is None or symmetry is None:
raise RuntimeError(
"supercell, primitive, and symmetry are required to estimate "
"cutoff_pair_distance by memory size."
)
update_symfc_cutoff_by_memsize(
symfc_options, supercell, primitive, symmetry, verbose=log_level > 0
)
if symfc_options["cutoff"] is not None:
_cutoff_pair_distance = symfc_options["cutoff"][3]
return _cutoff_pair_distance
def _set_cutoff_in_fc_calculator_options(
fc_calculator_options: str | None,
cutoff_str: str,
log_level: int,
):
str_appended = f"cutoff={cutoff_str}"
calc_opts = fc_calculator_options
if calc_opts is None:
calc_opts = "|"
if "|" in calc_opts:
calc_opts_fc2, calc_opts_fc3 = [v.strip() for v in calc_opts.split("|")][:2]
else:
calc_opts_fc2 = calc_opts
calc_opts_fc3 = calc_opts
if calc_opts_fc3 == "":
calc_opts_fc3 += f"{str_appended}"
if log_level:
print(f'Set "{str_appended}" to fc_calculator_options for fc3.')
elif "cutoff" not in calc_opts_fc3:
calc_opts_fc3 += f", {str_appended}"
if log_level:
print(f'Appended "{str_appended}" to fc_calculator_options for fc3.')
return f"{calc_opts_fc2}|{calc_opts_fc3}"
def _get_cutoff_pair_distance(
fc_calculator: str | None,
fc_calculator_options: str | None,
cutoff_pair_distance: float | None,
symfc_memory_size: float | None,
) -> tuple[float | None, float | None]:
"""Return cutoff_pair_distance from settings."""
_, _fc_calculator_options = get_fc_calculator_params(
fc_calculator,
fc_calculator_options,
cutoff_pair_distance,
)
symfc_options = parse_symfc_options(
extract_fc2_fc3_calculators_options(_fc_calculator_options, 3), 3
)
_cutoff_pair_distance = cutoff_pair_distance
cutoff = symfc_options.get("cutoff")
if cutoff is not None:
_cutoff_pair_distance = cutoff.get(3)
_symfc_memory_size = symfc_memory_size
memsize = symfc_options.get("memsize")
if memsize is not None:
_symfc_memory_size = memsize.get(3)
return _cutoff_pair_distance, _symfc_memory_size

View File

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

View File

@ -36,9 +36,8 @@
from __future__ import annotations
from typing import Optional, Union
import numpy as np
from numpy.typing import ArrayLike
from phonopy.harmonic.dynamical_matrix import get_dynamical_matrix
from phonopy.phonon.tetrahedron_mesh import get_tetrahedra_frequencies
from phonopy.physical_units import get_physical_units
@ -58,9 +57,9 @@ from phono3py.phonon.solver import run_phonon_solver_c, run_phonon_solver_py
def get_mass_variances(
primitive: Optional[PhonopyAtoms] = None,
symbols: Optional[Union[list[str], tuple[str]]] = None,
isotope_data: Optional[dict] = None,
primitive: PhonopyAtoms | None = None,
symbols: list[str] | tuple[str] | None = None,
isotope_data: dict | None = None,
):
"""Calculate mass variances."""
if primitive is not None:
@ -93,14 +92,14 @@ class Isotope:
def __init__(
self,
mesh,
primitive,
mesh: float | ArrayLike,
primitive: Primitive,
mass_variances=None, # length of list is num_atom.
isotope_data=None,
band_indices=None,
sigma=None,
bz_grid=None,
frequency_factor_to_THz=None,
bz_grid: BZGrid | None = None,
frequency_factor_to_THz: float | None = None,
use_grg=False,
symprec=1e-5,
cutoff_frequency=None,
@ -116,7 +115,6 @@ class Isotope:
self._mass_variances = np.array(mass_variances, dtype="double")
self._primitive = primitive
self._sigma = sigma
self._bz_grid = bz_grid
self._symprec = symprec
if cutoff_frequency is None:
self._cutoff_frequency = 0
@ -143,7 +141,7 @@ class Isotope:
else:
self._band_indices = np.array(band_indices, dtype="int64")
if self._bz_grid is None:
if bz_grid is None:
primitive_symmetry = Symmetry(self._primitive, self._symprec)
self._bz_grid = BZGrid(
self._mesh,
@ -152,6 +150,8 @@ class Isotope:
use_grg=use_grg,
store_dense_gp_map=True,
)
else:
self._bz_grid = bz_grid
def set_grid_point(self, grid_point):
"""Initialize grid points."""
@ -196,8 +196,9 @@ class Isotope:
return self._gamma
@property
def bz_grid(self):
def bz_grid(self) -> BZGrid:
"""Return BZgrid class instance."""
assert self._bz_grid is not None
return self._bz_grid
@property

View File

@ -39,12 +39,14 @@ from __future__ import annotations
import warnings
from collections.abc import Sequence
from numpy.typing import ArrayLike, NDArray
try:
from spglib import SpglibDataset
from spglib import SpglibDataset # type: ignore
except ImportError:
from types import SimpleNamespace as SpglibDataset
from types import SimpleNamespace
from typing import Optional, Union
import numpy as np
@ -132,6 +134,7 @@ class BZGrid:
D_diag : ndarray
P : ndarray
Q : ndarray
PS : ndarray
QDinv : ndarray
grid_matrix : ndarray
microzone_lattice : ndarray
@ -141,12 +144,12 @@ class BZGrid:
def __init__(
self,
mesh: Union[int, float, Sequence, np.ndarray],
reciprocal_lattice=None,
lattice=None,
symmetry_dataset: Optional[Union[SpglibDataset]] = None,
transformation_matrix: Optional[Union[Sequence, np.ndarray]] = None,
is_shift: Optional[Union[list, np.ndarray]] = None,
mesh: float | ArrayLike,
reciprocal_lattice: ArrayLike | None = None,
lattice: ArrayLike | None = None,
symmetry_dataset: SpglibDataset | None = None,
transformation_matrix: ArrayLike | None = None,
is_shift: ArrayLike | None = None,
is_time_reversal: bool = True,
use_grg: bool = False,
force_SNF: bool = False,
@ -202,17 +205,18 @@ class BZGrid:
self._is_shift = [v * 1 for v in is_shift]
self._is_time_reversal = is_time_reversal
self._store_dense_gp_map = store_dense_gp_map
self._addresses = None
self._gp_map = None
self._addresses: np.ndarray
self._gp_map: np.ndarray
self._grid_matrix = None
self._D_diag = np.ones(3, dtype="int64")
self._Q = np.eye(3, dtype="int64", order="C")
self._P = np.eye(3, dtype="int64", order="C")
self._QDinv = None
self._microzone_lattice = None
self._rotations = None
self._reciprocal_operations = None
self._gp_Gamma = None
self._QDinv: np.ndarray
self._microzone_lattice: np.ndarray
self._rotations: np.ndarray
self._reciprocal_operations: np.ndarray
self._rotations_cartesian: np.ndarray
self._gp_Gamma: int
if reciprocal_lattice is not None:
self._reciprocal_lattice = np.array(
@ -281,7 +285,7 @@ class BZGrid:
return np.array(np.dot(self.P, self._is_shift), dtype="int64")
@property
def grid_matrix(self):
def grid_matrix(self) -> Optional[np.ndarray]:
"""Grid generating matrix to be represented by SNF.
Grid generating matrix used for SNF.
@ -292,7 +296,7 @@ class BZGrid:
return self._grid_matrix
@property
def addresses(self):
def addresses(self) -> np.ndarray:
"""BZ-grid addresses.
Integer grid address of the points in Brillouin zone including
@ -315,7 +319,7 @@ class BZGrid:
return self._gp_map
@property
def gp_Gamma(self):
def gp_Gamma(self) -> int:
"""Return grid point index of Gamma-point."""
return self._gp_Gamma
@ -345,7 +349,7 @@ class BZGrid:
return self._grg2bzg
@property
def microzone_lattice(self):
def microzone_lattice(self) -> np.ndarray:
"""Basis vectors of microzone.
Basis vectors of microzone of GR-grid in column vectors.
@ -354,6 +358,16 @@ class BZGrid:
"""
return self._microzone_lattice
@property
def reciprocal_lattice(self) -> np.ndarray:
"""Reciprocal basis vectors of primitive cell.
Reciprocal basis vectors of primitive cell in column vectors.
shape=(3, 3), dtype='double', order='C'.
"""
return self._reciprocal_lattice
@property
def store_dense_gp_map(self):
"""Return gp_map type.
@ -364,7 +378,7 @@ class BZGrid:
return self._store_dense_gp_map
@property
def rotations(self):
def rotations(self) -> np.ndarray:
"""Return rotation matrices for grid points.
Rotation matrices for GR-grid addresses (g) defined as g'=Rg. This can
@ -376,12 +390,12 @@ class BZGrid:
return self._rotations
@property
def rotations_cartesian(self):
def rotations_cartesian(self) -> np.ndarray:
"""Return rotations in Cartesian coordinates."""
return self._rotations_cartesian
@property
def reciprocal_operations(self):
def reciprocal_operations(self) -> np.ndarray:
"""Return reciprocal rotations.
Reciprocal space rotation matrices in fractional coordinates defined as
@ -392,7 +406,7 @@ class BZGrid:
return self._reciprocal_operations
@property
def symmetry_dataset(self) -> SpglibDataset:
def symmetry_dataset(self) -> Optional[SpglibDataset]:
"""Return Symmetry.dataset."""
return self._symmetry_dataset
@ -539,7 +553,7 @@ class GridMatrix:
mesh: Union[int, float, Sequence, np.ndarray],
lattice: Union[Sequence, np.ndarray],
symmetry_dataset: Optional[SpglibDataset] = None,
transformation_matrix: Optional[Union[list, np.ndarray]] = None,
transformation_matrix: Optional[Union[Sequence, np.ndarray]] = None,
use_grg: bool = True,
force_SNF: bool = False,
SNF_coordinates: str = "reciprocal",
@ -591,7 +605,7 @@ class GridMatrix:
)
@property
def grid_matrix(self):
def grid_matrix(self) -> Optional[np.ndarray]:
"""Grid generating matrix to be represented by SNF.
Grid generating matrix used for SNF.
@ -602,7 +616,7 @@ class GridMatrix:
return self._grid_matrix
@property
def D_diag(self):
def D_diag(self) -> np.ndarray:
"""Diagonal elements of diagonal matrix after SNF: D=PAQ.
This corresponds to the mesh numbers in transformed reciprocal
@ -637,7 +651,7 @@ class GridMatrix:
mesh: Union[int, float, Sequence, np.ndarray],
use_grg: bool = False,
symmetry_dataset: Optional[SpglibDataset] = None,
transformation_matrix: Optional[Union[list, np.ndarray]] = None,
transformation_matrix: Optional[Union[Sequence, np.ndarray]] = None,
force_SNF=False,
coordinates="reciprocal",
) -> None:
@ -665,9 +679,8 @@ class GridMatrix:
have similar lengths.
"""
num_values = len(np.ravel(mesh))
if num_values == 1:
length = float(mesh)
try:
length = float(mesh) # type: ignore
if use_grg:
found_grg = self._run_grg(
symmetry_dataset,
@ -685,17 +698,20 @@ class GridMatrix:
length, self._lattice, rotations=symmetry_dataset.rotations
)
self._D_diag = np.array(mesh_numbers, dtype="int64")
if num_values == 9:
self._run_grg(
symmetry_dataset,
transformation_matrix,
None,
mesh,
force_SNF,
coordinates,
)
if num_values == 3:
self._D_diag = np.array(mesh, dtype="int64")
except (ValueError, TypeError):
num_values = len(np.ravel(mesh))
if num_values == 9:
self._run_grg(
symmetry_dataset,
transformation_matrix,
None,
mesh,
force_SNF,
coordinates,
)
if num_values == 3:
self._D_diag = np.array(mesh, dtype="int64")
def _run_grg(
self,
@ -730,7 +746,7 @@ class GridMatrix:
)
return False
def _get_mock_symmetry_dataset(self, transformation_matrix) -> dict:
def _get_mock_symmetry_dataset(self, transformation_matrix) -> SimpleNamespace:
"""Return mock symmetry_dataset containing transformation matrix.
Assuming self._lattice as standardized cell, and inverse of
@ -753,8 +769,6 @@ class GridMatrix:
)
raise RuntimeError(msg)
from types import SimpleNamespace
sym_dataset = SimpleNamespace(
**{
"rotations": np.eye(3, dtype="intc", order="C").reshape(1, 3, 3),
@ -768,7 +782,7 @@ class GridMatrix:
def _set_GRG_mesh(
self,
sym_dataset: SpglibDataset,
sym_dataset: Union[SpglibDataset, SimpleNamespace],
length: Optional[float] = None,
grid_matrix=None,
force_SNF=False,
@ -802,7 +816,10 @@ class GridMatrix:
self._grid_matrix = _grid_matrix
def _get_grid_matrix(
self, sym_dataset: dict, length: float, coordinates: str = "reciprocal"
self,
sym_dataset: Union[SpglibDataset, SimpleNamespace],
length: float,
coordinates: str = "reciprocal",
):
"""Return grid matrix.
@ -855,6 +872,22 @@ class GridMatrix:
return grid_matrix
def get_qpoints_from_bz_grid_points(
gps: Union[int, np.ndarray], bz_grid: BZGrid
) -> np.ndarray:
"""Return q-point(s) in reduced coordinates of grid point(s).
Parameters
----------
i_gps : int or ndarray
BZ-grid index (int) or indices (ndarray).
bz_grid : BZGrid
BZ-grid instance.
"""
return bz_grid.addresses[gps] @ bz_grid.QDinv.T
def get_grid_point_from_address_py(addresses, D_diag):
"""Return GR-grid point index from addresses.
@ -1041,12 +1074,12 @@ def _get_grid_points_by_bz_rotations_py(bz_gp, bz_grid: BZGrid, rotations):
)
+ num_grgp
).tolist()
gps.insert(0, gp)
gps.insert(0, gp) # type: ignore
indices = np.where((bz_grid.addresses[gps] == adrs).all(axis=1))[0]
if len(indices) == 0:
msg = "with_surface did not work properly."
raise RuntimeError(msg)
bzgps[i] = gps[indices[0]]
bzgps[i] = gps[indices[0]] # type: ignore
return bzgps
@ -1151,21 +1184,15 @@ def _relocate_BZ_grid_address(
else:
bz_map = np.zeros(np.prod(D_diag) * 9 + 1, dtype="int64")
# Mpr^-1 = Lr^-1 Lp
reclat_T = np.array(np.transpose(reciprocal_lattice), dtype="double", order="C")
reduced_basis = get_reduced_bases(reclat_T)
tmat_inv = np.dot(np.linalg.inv(reduced_basis.T), reclat_T.T)
tmat_inv_int = np.rint(tmat_inv).astype("int64")
assert (np.abs(tmat_inv - tmat_inv_int) < 1e-5).all()
reduced_basis, tmat_inv_int = get_reduced_bases_and_tmat_inv(reciprocal_lattice)
num_gp = recgrid.bz_grid_addresses(
bz_grid_addresses,
bz_map,
bzg2grg,
np.array(D_diag, dtype="int64"),
np.array(np.dot(tmat_inv_int, Q), dtype="int64", order="C"),
np.array(tmat_inv_int @ Q, dtype="int64", order="C"),
_PS,
np.array(reduced_basis.T, dtype="double", order="C"),
reduced_basis,
store_dense_gp_map * 1 + 1,
)
@ -1174,6 +1201,39 @@ def _relocate_BZ_grid_address(
return bz_grid_addresses, bz_map, bzg2grg
def get_reduced_bases_and_tmat_inv(
reciprocal_lattice: ArrayLike,
) -> tuple[NDArray, NDArray]:
"""Return reduced bases and inverse transformation matrix.
Parameters
----------
reciprocal_lattice : ArrayLike
Reciprocal lattice vectors in column vectors.
shape=(3, 3), dtype='double'
Returns
-------
reduced_basis : ndarray
Reduced basis vectors in column vectors.
shape=(3, 3), dtype='double', order='C'
tmat_inv_int : ndarray
Inverse transformation matrix in integer.
This is used to transform reciprocal lattice vectors to
conventional lattice vectors.
shape=(3, 3), dtype='int64'
"""
# Mpr^-1 = Lr^-1 Lp
reclat_T = np.array(np.transpose(reciprocal_lattice), dtype="double", order="C")
reduced_basis = get_reduced_bases(reclat_T)
assert reduced_basis is not None, "Reduced basis is not found."
tmat_inv = np.linalg.inv(reduced_basis.T) @ reclat_T.T
tmat_inv_int = np.rint(tmat_inv).astype("int64")
assert (np.abs(tmat_inv - tmat_inv_int) < 1e-5).all()
return np.array(reduced_basis.T, dtype="double", order="C"), tmat_inv_int
def _get_ir_grid_map(
D_diag: Union[np.ndarray, Sequence],
grg_rotations: Union[np.ndarray, Sequence],

View File

@ -34,7 +34,14 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import numpy as np
from phonopy.harmonic.dynamical_matrix import (
DynamicalMatrix,
DynamicalMatrixGL,
DynamicalMatrixNAC,
)
from phonopy.physical_units import get_physical_units
from phonopy.structure.cells import sparse_to_dense_svecs
@ -81,8 +88,8 @@ def run_phonon_solver_c(
'U' or 'L' for lapack zheev solver. Default is 'L'.
"""
import phono3py._phono3py as phono3c
import phono3py._phononcalc as phononcalc
import phono3py._phono3py as phono3c # type: ignore[import-untyped]
import phono3py._phononcalc as phononcalc # type: ignore[import-untyped]
if frequency_conversion_factor is None:
_frequency_conversion_factor = get_physical_units().DefaultToTHz
@ -100,7 +107,7 @@ def run_phonon_solver_c(
dielectric,
) = _extract_params(dm)
if dm.is_nac() and dm.nac_method == "gonze":
if isinstance(dm, DynamicalMatrixGL):
gonze_nac_dataset = dm.Gonze_nac_dataset
if gonze_nac_dataset[0] is None:
dm.make_Gonze_nac_dataset()
@ -112,6 +119,7 @@ def run_phonon_solver_c(
G_list, # List of G points where d-d interactions are integrated.
Lambda,
) = gonze_nac_dataset # Convergence parameter
assert Lambda is not None
fc = gonze_fc
use_GL_NAC = True
else:
@ -120,7 +128,7 @@ def run_phonon_solver_c(
dd_q0 = np.zeros(2) # dummy variable
G_list = np.zeros(3) # dummy variable
Lambda = 0 # dummy variable
if not dm.is_nac():
if not isinstance(dm, DynamicalMatrixNAC):
born = np.zeros((3, 3)) # dummy variable
dielectric = np.zeros(3) # dummy variable
fc = dm.force_constants
@ -168,7 +176,7 @@ def run_phonon_solver_c(
dd_q0,
G_list,
float(Lambda),
dm.is_nac() * 1,
isinstance(dm, DynamicalMatrixNAC) * 1,
is_nac_q_zero * 1,
use_GL_NAC * 1,
lapack_zheev_uplo,
@ -207,14 +215,14 @@ def run_phonon_solver_py(
dynamical_matrix.run(q)
dm = dynamical_matrix.dynamical_matrix
eigvals, eigvecs = np.linalg.eigh(dm, UPLO=lapack_zheev_uplo)
eigvals = eigvals.real
eigvals = eigvals.real # type: ignore[no-untyped-call]
frequencies[gp] = (
np.sqrt(np.abs(eigvals)) * np.sign(eigvals) * frequency_conversion_factor
)
eigenvectors[gp] = eigvecs
def _extract_params(dm):
def _extract_params(dm: DynamicalMatrix | DynamicalMatrixNAC):
svecs, multi = dm.primitive.get_smallest_vectors()
if dm.primitive.store_dense_svecs:
_svecs = svecs
@ -225,7 +233,7 @@ def _extract_params(dm):
masses = np.array(dm.primitive.masses, dtype="double")
rec_lattice = np.array(np.linalg.inv(dm.primitive.cell), dtype="double", order="C")
positions = np.array(dm.primitive.positions, dtype="double", order="C")
if dm.is_nac():
if isinstance(dm, DynamicalMatrixNAC):
born = dm.born
nac_factor = dm.nac_factor
dielectric = dm.dielectric_constant

View File

@ -35,6 +35,7 @@
# POSSIBILITY OF SUCH DAMAGE.
import numpy as np
from phonopy.harmonic.dynamical_matrix import DynamicalMatrixGL
from phonopy.phonon.group_velocity import GroupVelocity
from phonopy.physical_units import get_physical_units
@ -210,11 +211,7 @@ class VelocityOperator(GroupVelocity):
if np.linalg.norm(q) < np.linalg.norm(delta_q):
flag_gamma = True
if (
(self._dynmat.is_nac())
and (self._dynmat.nac_method == "gonze")
and flag_gamma
):
if isinstance(dynmat, DynamicalMatrixGL) and flag_gamma:
dynmat.run(
q - delta_q, q_direction=(q - delta_q) / np.linalg.norm(q - delta_q)
)

View File

@ -34,6 +34,10 @@
# 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 Literal
import numpy as np
from phonopy.harmonic.displacement import (
directions_axis,
@ -131,7 +135,10 @@ def direction_to_displacement(
def get_third_order_displacements(
cell: PhonopyAtoms, symmetry: Symmetry, is_plusminus="auto", is_diagonal=False
cell: PhonopyAtoms,
symmetry: Symmetry,
is_plusminus: bool | Literal["auto"] = "auto",
is_diagonal: bool = False,
):
"""Create displacement dataset.

View File

@ -34,10 +34,13 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import logging
import sys
import numpy as np
from numpy.typing import NDArray
from phonopy.harmonic.force_constants import (
distribute_force_constants,
get_nsym_list_and_s2pp,
@ -573,30 +576,50 @@ def cutoff_fc3_by_zero(fc3, supercell, cutoff_distance, p2s_map=None, symprec=1e
break
def show_drift_fc3(fc3, primitive=None, name="fc3"):
"""Show drift of fc3."""
def show_drift_fc3(
fc3: NDArray, primitive: Primitive | None = None, name: str = "fc3", digit: int = 8
):
"""Show max drift of fc3."""
maxval1, maxval2, maxval3, xyz1, xyz2, xyz3 = get_drift_fc3(
fc3, primitive=primitive
)
text = f"Max drift of {name}: "
text += f"{maxval1:.{digit}f} ({'xyz'[xyz1[0]]}{'xyz'[xyz1[1]]}{'xyz'[xyz1[2]]}) "
text += f"{maxval2:.{digit}f} ({'xyz'[xyz2[0]]}{'xyz'[xyz2[1]]}{'xyz'[xyz2[2]]}) "
text += f"{maxval3:.{digit}f} ({'xyz'[xyz3[0]]}{'xyz'[xyz3[1]]}{'xyz'[xyz3[2]]})"
print(text)
def get_drift_fc3(
fc3: NDArray, primitive: Primitive | None = None
) -> tuple[float, float, float, list[int], list[int], list[int]]:
"""Return max drift of fc3."""
if fc3.shape[0] == fc3.shape[1]:
num_atom = fc3.shape[0]
maxval1 = 0
maxval2 = 0
maxval3 = 0
klm1 = [0, 0, 0]
klm2 = [0, 0, 0]
klm3 = [0, 0, 0]
xyz1 = [0, 0, 0]
xyz2 = [0, 0, 0]
xyz3 = [0, 0, 0]
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, ll, m]
xyz1 = [k, ll, m]
if abs(val2) > abs(maxval2):
maxval2 = val2
klm2 = [k, ll, m]
xyz2 = [k, ll, m]
if abs(val3) > abs(maxval3):
maxval3 = val3
klm3 = [k, ll, m]
xyz3 = [k, ll, m]
else:
if primitive is None:
raise RuntimeError(
"Primitive cell is required to get drift of compact fc3."
)
try:
import phono3py._phono3py as phono3c
@ -614,9 +637,9 @@ def show_drift_fc3(fc3, primitive=None, name="fc3"):
maxval1 = 0
maxval2 = 0
maxval3 = 0
klm1 = [0, 0, 0]
klm2 = [0, 0, 0]
klm3 = [0, 0, 0]
xyz1 = [0, 0, 0]
xyz2 = [0, 0, 0]
xyz3 = [0, 0, 0]
phono3c.transpose_compact_fc3(
fc3, permutations, s2pp_map, p2s_map, nsym_list, 0
) # dim[0] <--> dim[1]
@ -624,7 +647,7 @@ def show_drift_fc3(fc3, primitive=None, name="fc3"):
val1 = fc3[i, :, j, k, ll, m].sum()
if abs(val1) > abs(maxval1):
maxval1 = val1
klm1 = [k, ll, m]
xyz1 = [k, ll, m]
phono3c.transpose_compact_fc3(
fc3, permutations, s2pp_map, p2s_map, nsym_list, 0
) # dim[0] <--> dim[1]
@ -633,10 +656,10 @@ def show_drift_fc3(fc3, primitive=None, name="fc3"):
val3 = fc3[i, j, :, k, ll, m].sum()
if abs(val2) > abs(maxval2):
maxval2 = val2
klm2 = [k, ll, m]
xyz2 = [k, ll, m]
if abs(val3) > abs(maxval3):
maxval3 = val3
klm3 = [k, ll, m]
xyz3 = [k, ll, m]
except ImportError as exc:
text = (
"Import error at phono3c.tranpose_compact_fc3. "
@ -644,11 +667,7 @@ def show_drift_fc3(fc3, primitive=None, name="fc3"):
)
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]])
text += "%f (%s%s%s) " % (maxval2, "xyz"[klm2[0]], "xyz"[klm2[1]], "xyz"[klm2[2]])
text += "%f (%s%s%s)" % (maxval3, "xyz"[klm3[0]], "xyz"[klm3[1]], "xyz"[klm3[2]])
print(text)
return maxval1, maxval2, maxval3, xyz1, xyz2, xyz3
def _set_permutation_symmetry_fc3_elem_with_cutoff(fc3, fc3_done, a, b, c):

View File

@ -37,7 +37,11 @@
import sys
import numpy as np
from phonopy.harmonic.dynamical_matrix import get_dynamical_matrix
from phonopy.harmonic.dynamical_matrix import (
DynamicalMatrixGL,
DynamicalMatrixNAC,
get_dynamical_matrix,
)
from phonopy.physical_units import get_physical_units
from phonopy.structure.atoms import PhonopyAtoms
from phonopy.structure.cells import Primitive
@ -96,8 +100,8 @@ def run_gruneisen_parameters(
if log_level > 0:
dm = gruneisen.dynamical_matrix
if dm.is_nac() and dm.nac_method == "gonze":
dm.show_Gonze_nac_message()
if isinstance(dm, DynamicalMatrixGL):
dm.show_nac_message()
if mesh is not None:
gruneisen.set_sampling_mesh(mesh, rotations=rotations, is_gamma_center=True)
@ -310,7 +314,7 @@ class Gruneisen:
gruneisen_parameters = []
frequencies = []
for i, q in enumerate(qpoints):
if self._dm.is_nac():
if isinstance(self._dm, DynamicalMatrixNAC):
if (np.abs(q) < 1e-5).all(): # If q is almost at Gamma
if self._run_mode == "band":
# Direction estimated from neighboring point

View File

@ -34,11 +34,14 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import sys
import warnings
from typing import List, Optional
from collections.abc import Sequence
import numpy as np
from numpy.typing import ArrayLike, NDArray
from phonopy.phonon.degeneracy import degenerate_sets
from phonopy.physical_units import get_physical_units
@ -576,20 +579,20 @@ class ImagSelfEnergy:
def get_imag_self_energy(
interaction: Interaction,
grid_points,
temperatures,
sigmas=None,
frequency_points=None,
frequency_step=None,
num_frequency_points=None,
frequency_points_at_bands=False,
num_points_in_batch=None,
scattering_event_class=None, # class 1 or 2
write_gamma_detail=False,
return_gamma_detail=False,
output_filename=None,
log_level=0,
):
grid_points: ArrayLike,
temperatures: ArrayLike,
sigmas: Sequence[float | None] | None = None,
frequency_points: ArrayLike | None = None,
frequency_step: float | None = None,
num_frequency_points: int | None = None,
frequency_points_at_bands: bool = False,
num_points_in_batch: int | None = None,
scattering_event_class: int | None = None, # class 1 or 2
write_gamma_detail: bool = False,
return_gamma_detail: bool = False,
output_filename: str | None = None,
log_level: int = 0,
) -> tuple[NDArray | None, NDArray, Sequence]:
"""Imaginary-part of self-energy at frequency points.
Band indices to be calculated at are found in Interaction instance.
@ -599,12 +602,12 @@ def get_imag_self_energy(
interaction : Interaction
Ph-ph interaction.
grid_points : array_like
Grid-point indices where imag-self-energeis are caclculated.
Grid-point indices where imag-self-energies are calculated.
dtype=int, shape=(grid_points,)
temperatures : array_like
Temperatures where imag-self-energies are calculated.
dtype=float, shape=(temperatures,)
sigmas : array_like, optional
sigmas : Sequence, optional
A set of sigmas. simgas=[None, ] means to use tetrahedron method,
otherwise smearing method with real positive value of sigma.
For example, sigmas=[None, 0.01, 0.03] is possible. Default is None,
@ -671,9 +674,7 @@ def get_imag_self_energy(
"""
if sigmas is None:
_sigmas = [
None,
]
_sigmas = [None]
else:
_sigmas = sigmas
@ -718,7 +719,7 @@ def get_imag_self_energy(
order="C",
)
detailed_gamma: List[Optional[np.ndarray]] = []
detailed_gamma = []
ise = ImagSelfEnergy(
interaction, with_detail=(write_gamma_detail or return_gamma_detail)
@ -771,10 +772,7 @@ def get_imag_self_energy(
log_level,
)
if return_gamma_detail:
return _frequency_points, gamma, detailed_gamma
else:
return _frequency_points, gamma
return _frequency_points, gamma, detailed_gamma
def _get_imag_self_energy_at_gp(

View File

@ -35,11 +35,11 @@
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
import warnings
from collections.abc import Sequence
from typing import Literal, Optional, Union
from typing import Literal
import numpy as np
from numpy.typing import NDArray
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, get_dynamical_matrix
from phonopy.physical_units import get_physical_units
from phonopy.structure.cells import Primitive, compute_all_sg_permutations
@ -94,26 +94,26 @@ class Interaction:
primitive: Primitive,
bz_grid: BZGrid,
primitive_symmetry: Symmetry,
fc3: Optional[np.ndarray] = None,
band_indices: Optional[Union[np.ndarray, Sequence]] = None,
constant_averaged_interaction: Optional[float] = None,
frequency_factor_to_THz: Optional[float] = None,
frequency_scale_factor: Optional[float] = None,
unit_conversion: Optional[float] = None,
fc3: NDArray | None = None,
fc3_nonzero_indices: NDArray | None = None,
band_indices: NDArray | Sequence | None = None,
constant_averaged_interaction: float | None = None,
frequency_factor_to_THz: float | None = None,
frequency_scale_factor: float | None = None,
unit_conversion: float | None = None,
is_mesh_symmetry: bool = True,
symmetrize_fc3q: bool = False,
make_r0_average: bool = False,
cutoff_frequency: Optional[float] = None,
cutoff_frequency: float | None = None,
lapack_zheev_uplo: Literal["L", "U"] = "L",
openmp_per_triplets: Optional[bool] = None,
openmp_per_triplets: bool | None = None,
):
"""Init method."""
self._primitive = primitive
self._bz_grid = bz_grid
self._primitive_symmetry = primitive_symmetry
self._band_indices = None
self._set_band_indices(band_indices)
self._band_indices = self._get_band_indices(band_indices)
self._constant_averaged_interaction = constant_averaged_interaction
if frequency_factor_to_THz is None:
self._frequency_factor_to_THz = get_physical_units().DefaultToTHz
@ -122,12 +122,12 @@ class Interaction:
self._frequency_scale_factor = frequency_scale_factor
if fc3 is not None:
self._set_fc3(fc3)
self._set_fc3(fc3, fc3_nonzero_indices=fc3_nonzero_indices)
# Unit to eV^2
if unit_conversion is None:
num_grid = np.prod(self.mesh_numbers)
self._unit_conversion = (
self._unit_conversion = float(
(get_physical_units().Hbar * get_physical_units().EV) ** 3
/ 36
/ 8
@ -147,7 +147,7 @@ class Interaction:
self._is_mesh_symmetry = is_mesh_symmetry
self._symmetrize_fc3q = symmetrize_fc3q
self._make_r0_average = make_r0_average
self._lapack_zheev_uplo = lapack_zheev_uplo
self._lapack_zheev_uplo: Literal["L", "U"] = lapack_zheev_uplo
self._openmp_per_triplets = openmp_per_triplets
self._symprec = self._primitive_symmetry.tolerance
@ -160,6 +160,7 @@ class Interaction:
self._g_zero = None
self._phonon_done = None
self._phonon_all_done = False
self._done_nac_at_gamma = False # Phonon at Gamma is calculated with NAC.
self._frequencies = None
self._eigenvectors = None
@ -181,13 +182,14 @@ class Interaction:
)
self._get_all_shortest()
def run(
self, lang: Literal["C", "Python"] = "C", g_zero: Optional[np.ndarray] = None
):
def run(self, lang: Literal["C", "Python"] = "C", g_zero: NDArray | None = None):
"""Run ph-ph interaction calculation."""
if (self._phonon_done == 0).any():
if self._phonon_all_done:
self.run_phonon_solver()
if self._triplets_at_q is None:
raise RuntimeError("Set grid point first by set_grid_point().")
num_band = len(self._primitive) * 3
num_triplets = len(self._triplets_at_q)
@ -207,7 +209,7 @@ class Interaction:
)
@property
def interaction_strength(self) -> np.ndarray:
def interaction_strength(self) -> NDArray | None:
"""Return ph-ph interaction strength.
Returns
@ -219,18 +221,8 @@ class Interaction:
"""
return self._interaction_strength
def get_interaction_strength(self):
"""Return ph-ph interaction strength."""
warnings.warn(
"Use attribute, Interaction.interaction_strength "
"instead of Interaction.get_interaction_strength().",
DeprecationWarning,
stacklevel=2,
)
return self.interaction_strength
@property
def mesh_numbers(self) -> np.ndarray:
def mesh_numbers(self) -> NDArray:
"""Return mesh numbers.
Returns
@ -241,73 +233,56 @@ class Interaction:
"""
return self._bz_grid.D_diag
def get_mesh_numbers(self):
"""Return mesh numbers."""
warnings.warn(
"Use attribute, Interaction.mesh_numbers "
"instead of Interaction.get_mesh_numbers().",
DeprecationWarning,
stacklevel=2,
)
return self.mesh_numbers
@property
def is_mesh_symmetry(self) -> bool:
"""Whether symmetry of grid is utilized or not."""
return self._is_mesh_symmetry
@property
def fc3(self) -> np.ndarray:
def fc3(self) -> NDArray:
"""Return fc3."""
return self._fc3
def get_fc3(self):
"""Return fc3."""
warnings.warn(
"Use attribute, Interaction.fc3 instead of Interaction.get_fc3().",
DeprecationWarning,
stacklevel=2,
)
return self.fc3
@property
def fc3_nonzero_indices(self) -> NDArray:
"""Return fc3_nonzero_indices."""
return self._fc3_nonzero_indices
@property
def dynamical_matrix(self) -> Optional[DynamicalMatrix]:
def dynamical_matrix(self) -> DynamicalMatrix | None:
"""Return DynamicalMatrix class instance."""
return self._dm
def get_dynamical_matrix(self):
"""Return DynamicalMatrix class instance."""
warnings.warn(
"Use attribute, Interaction.dynamical_matrix "
"instead of Interaction.get_dynamical_matrix().",
DeprecationWarning,
stacklevel=2,
)
return self.dynamical_matrix
@property
def primitive(self) -> Primitive:
"""Return Primitive class instance."""
return self._primitive
def get_primitive(self):
"""Return Primitive class instance."""
warnings.warn(
"Use attribute, Interaction.primitive "
"instead of Interaction.get_primitive().",
DeprecationWarning,
stacklevel=2,
)
return self.primitive
@property
def primitive_symmetry(self) -> Symmetry:
"""Return Symmetry class instance of primitive cell."""
return self._primitive_symmetry
@property
def phonon_all_done(self) -> bool:
"""Return whether phonon calculation is done at all grid points.
True value is set in run_phonon_solver(). Even when False, it is
possible that all phonons are already calculated, but it is safer to
think some of phonons are not calculated yet. To be sure, check
(self._phonon_done == 0).any().
"""
return self._phonon_all_done
def get_triplets_at_q(
self,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
) -> tuple[
NDArray | None,
NDArray | None,
NDArray | None,
NDArray | None,
]:
"""Return grid point triplets information.
triplets_at_q is in BZ-grid.
@ -329,7 +304,7 @@ class Interaction:
return self._bz_grid
@property
def band_indices(self) -> np.ndarray:
def band_indices(self) -> NDArray:
"""Return band indices.
Returns
@ -340,23 +315,13 @@ class Interaction:
"""
return self._band_indices
def get_band_indices(self):
"""Return band indices."""
warnings.warn(
"Use attribute, Interaction.band_indices "
"instead of Interaction.get_band_indices().",
DeprecationWarning,
stacklevel=2,
)
return self.band_indices
@property
def nac_params(self) -> dict:
def nac_params(self) -> dict | None:
"""Return NAC params."""
return self._nac_params
@property
def nac_q_direction(self) -> Optional[np.ndarray]:
def nac_q_direction(self) -> NDArray | None:
"""Return q-direction used for NAC at q->0.
Direction of q-vector watching from Gamma point used for
@ -376,28 +341,8 @@ class Interaction:
else:
self._nac_q_direction = np.array(nac_q_direction, copy=True, dtype="double")
def get_nac_q_direction(self):
"""Return q-direction used for NAC at q->0."""
warnings.warn(
"Use attribute, Interaction.nac_q_direction "
"instead of Interaction.get_nac_q_direction().",
DeprecationWarning,
stacklevel=2,
)
return self.nac_q_direction
def set_nac_q_direction(self, nac_q_direction=None):
"""Set NAC q-point direction valid at q->0."""
warnings.warn(
"Use attribute, Interaction.nac_q_direction "
"instead of Interaction.set_nac_q_direction().",
DeprecationWarning,
stacklevel=2,
)
self.nac_q_direction = nac_q_direction
@property
def zero_value_positions(self) -> Optional[np.ndarray]:
def zero_value_positions(self) -> NDArray | None:
"""Return zero ph-ph interaction elements information.
Returns
@ -407,17 +352,9 @@ class Interaction:
"""
return self._g_zero
def get_zero_value_positions(self):
"""Return zero ph-ph interaction elements information."""
warnings.warn(
"Use attribute, Interaction.zero_value_positions "
"instead of Interaction.get_zero_value_positions().",
DeprecationWarning,
stacklevel=2,
)
return self.zero_value_positions
def get_phonons(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
def get_phonons(
self,
) -> tuple[NDArray | None, NDArray | None, NDArray | None]:
"""Return phonons on grid.
Returns
@ -442,48 +379,18 @@ class Interaction:
"""Return phonon frequency conversion factor to THz."""
return self._frequency_factor_to_THz
def get_frequency_factor_to_THz(self):
"""Return phonon frequency conversion factor to THz."""
warnings.warn(
"Use attribute, Interaction.frequency_factor_to_THz ",
"instead of Interaction.get_frequency_factor_to_THz().",
DeprecationWarning,
stacklevel=2,
)
return self.frequency_factor_to_THz
@property
def lapack_zheev_uplo(self) -> Literal["L", "U"]:
"""Return U or L for lapack zheev solver."""
return self._lapack_zheev_uplo
def get_lapack_zheev_uplo(self):
"""Return U or L for lapack zheev solver."""
warnings.warn(
"Use attribute, Interaction.lapack_zheev_uplo "
"instead of Interaction.get_lapack_zheev_uplo().",
DeprecationWarning,
stacklevel=2,
)
return self.lapack_zheev_uplo
@property
def cutoff_frequency(self) -> float:
"""Return cutoff phonon frequency to judge imaginary phonon."""
return self._cutoff_frequency
def get_cutoff_frequency(self):
"""Return cutoff phonon frequency to judge imaginary phonon."""
warnings.warn(
"Use attribute, Interaction.cutoff_frequency "
"instead of Interaction.get_cutoff_frequency().",
DeprecationWarning,
stacklevel=2,
)
return self.cutoff_frequency
@property
def openmp_per_triplets(self) -> bool:
def openmp_per_triplets(self) -> bool | None:
"""Return whether OpenMP distribution over triplets or bands."""
return self._openmp_per_triplets
@ -505,7 +412,7 @@ class Interaction:
return self._make_r0_average
@property
def all_shortest(self) -> np.ndarray:
def all_shortest(self) -> NDArray:
"""Return boolean of make_r0_average.
This flag is used to activate averaging of fc3 transformation
@ -517,31 +424,27 @@ class Interaction:
return self._all_shortest
@property
def averaged_interaction(self) -> np.ndarray:
def averaged_interaction(self) -> NDArray:
"""Return sum over phonon triplets of interaction strength.
See Eq.(21) of PRB 91, 094306 (2015)
"""
if self._interaction_strength is None or self._weights_at_q is None:
raise RuntimeError(
"Interaction strength and weights at q are not set. "
"Run Interaction.run() first."
)
# v[triplet, band0, band, band]
v = self._interaction_strength
w = self._weights_at_q
v_sum = np.dot(w, v.sum(axis=2).sum(axis=2))
return v_sum / np.prod(v.shape[2:])
def get_averaged_interaction(self):
"""Return sum over phonon triplets of interaction strength."""
warnings.warn(
"Use attribute, Interaction.averaged_interaction "
"instead of Interaction.get_averaged_interaction().",
DeprecationWarning,
stacklevel=2,
)
return self.averaged_interaction
def get_primitive_and_supercell_correspondence(
self,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
) -> tuple[NDArray, NDArray, NDArray, NDArray, NDArray]:
"""Return atomic pair information."""
return (self._svecs, self._multi, self._p2s, self._s2p, self._masses)
@ -550,31 +453,11 @@ class Interaction:
"""Return unit conversion factor."""
return self._unit_conversion
def get_unit_conversion_factor(self):
"""Return unit conversion factor."""
warnings.warn(
"Use attribute, Interaction.unit_conversion_factor "
"instead of Interaction.get_unit_conversion_factor().",
DeprecationWarning,
stacklevel=2,
)
return self.unit_conversion_factor
@property
def constant_averaged_interaction(self) -> float:
def constant_averaged_interaction(self) -> float | None:
"""Return constant averaged interaction."""
return self._constant_averaged_interaction
def get_constant_averaged_interaction(self):
"""Return constant averaged interaction."""
warnings.warn(
"Use attribute, Interaction.constant_averaged_interaction "
"instead of Interaction.get_constant_averaged_interaction().",
DeprecationWarning,
stacklevel=2,
)
return self.constant_averaged_interaction
def set_interaction_strength(self, pp_strength, g_zero=None):
"""Set interaction strength."""
self._interaction_strength = pp_strength
@ -704,6 +587,14 @@ class Interaction:
"Input grid addresses are inconsistent. Setting phonons failed."
)
else:
if (
self._phonon_done is None
or self._frequencies is None
or self._eigenvectors is None
):
raise RuntimeError(
"Phonons are not initialized. Call init_dynamical_matrix() first."
)
self._phonon_done[:] = 1
self._frequencies[:] = frequencies
self._eigenvectors[:] = eigenvectors
@ -720,6 +611,7 @@ class Interaction:
self._run_phonon_solver_c(
np.arange(len(self._bz_grid.addresses), dtype="int64")
)
self._phonon_all_done = True
else:
self._run_phonon_solver_c(grid_points)
@ -737,6 +629,15 @@ class Interaction:
otherwise without NAC. Default is False.
"""
if (
self._phonon_done is None
or self._frequencies is None
or self._eigenvectors is None
):
raise RuntimeError(
"Phonons are not initialized. Call init_dynamical_matrix() first."
)
if not is_nac and self._frequencies_at_gamma is not None:
gp_Gamma = self._bz_grid.gp_Gamma
self._frequencies[gp_Gamma] = self._frequencies_at_gamma
@ -765,6 +666,11 @@ class Interaction:
self._phonon_done
"""
if self._phonon_done is None:
raise RuntimeError(
"Phonons are not initialized. Call init_dynamical_matrix() first."
)
self._phonon_done[:] = 0
ir_grid_points, _, _ = get_ir_grid_points(self._bz_grid)
ir_bz_grid_points = self._bz_grid.grg2bzg[ir_grid_points]
@ -775,8 +681,8 @@ class Interaction:
# perms.shape = (len(spg_ops), len(primitive)), dtype='intc'
perms = compute_all_sg_permutations(
self._primitive.scaled_positions,
self._bz_grid.symmetry_dataset.rotations,
self._bz_grid.symmetry_dataset.translations,
self._bz_grid.symmetry_dataset.rotations, # type: ignore
self._bz_grid.symmetry_dataset.translations, # type: ignore
np.array(self._primitive.cell.T, dtype="double", order="C"),
symprec=self._symprec,
)
@ -824,13 +730,13 @@ class Interaction:
"""
d2r_map = []
for r in self._bz_grid.symmetry_dataset.rotations:
for r in self._bz_grid.symmetry_dataset.rotations: # type: ignore
for i, rec_r in enumerate(self._bz_grid.reciprocal_operations):
if (rec_r.T == r).all():
d2r_map.append(i)
break
assert len(d2r_map) == len(self._bz_grid.symmetry_dataset.rotations)
assert len(d2r_map) == len(self._bz_grid.symmetry_dataset.rotations) # type: ignore
return d2r_map
@ -840,8 +746,12 @@ class Interaction:
e_j'(Rq) = R e_j(q) exp(-iRq.\tau)
"""
assert self._phonon_done is not None
assert self._frequencies is not None
assert self._eigenvectors is not None
Rq = np.dot(self._bz_grid.QDinv, self._bz_grid.addresses[bzgp])
tau = self._bz_grid.symmetry_dataset.translations[t_i]
tau = self._bz_grid.symmetry_dataset.translations[t_i] # type: ignore
phase_factor = np.exp(-2j * np.pi * np.dot(Rq, tau))
self._phonon_done[bzgp] = 1
self._frequencies[bzgp, :] = self._frequencies[orig_gp, :]
@ -863,6 +773,10 @@ class Interaction:
in this method.
"""
assert self._phonon_done is not None
assert self._frequencies is not None
assert self._eigenvectors is not None
r_inv = -np.eye(3, dtype="int64")
bz_grid_points_solved = []
for bzgp, done in enumerate(self._phonon_done):
@ -908,7 +822,7 @@ class Interaction:
self._interaction_strength = None
self._g_zero = None
def _set_fc3(self, fc3):
def _set_fc3(self, fc3: NDArray, fc3_nonzero_indices: NDArray | None = None):
if (
isinstance(fc3, np.ndarray)
and fc3.dtype == np.dtype("double")
@ -925,20 +839,42 @@ class Interaction:
fc3 * self._frequency_scale_factor**2, dtype="double", order="C"
)
def _set_band_indices(self, band_indices):
if fc3_nonzero_indices is None:
self._fc3_nonzero_indices = np.ones(
self._fc3.shape[:3], dtype="byte", order="C"
)
elif (
isinstance(fc3_nonzero_indices, np.ndarray)
and fc3_nonzero_indices.dtype == np.dtype("byte")
and fc3_nonzero_indices.flags.aligned
and fc3_nonzero_indices.flags.owndata
and fc3_nonzero_indices.flags.c_contiguous
):
self._fc3_nonzero_indices = fc3_nonzero_indices
else:
self._fc3_nonzero_indices = np.array(
fc3_nonzero_indices, dtype="byte", order="C"
)
def _get_band_indices(self, band_indices) -> NDArray:
num_band = len(self._primitive) * 3
if band_indices is None:
self._band_indices = np.arange(num_band, dtype="int64")
return np.arange(num_band, dtype="int64")
else:
self._band_indices = np.array(band_indices, dtype="int64")
return np.array(band_indices, dtype="int64")
def _run_c(self, g_zero):
import phono3py._phono3py as phono3c
assert self._interaction_strength is not None
assert self._triplets_at_q is not None
num_band = len(self._primitive) * 3
if g_zero is None or self._symmetrize_fc3q:
_g_zero = np.zeros(
self._interaction_strength.shape, dtype="byte", order="C"
self._interaction_strength.shape,
dtype="byte",
order="C",
)
else:
_g_zero = g_zero
@ -963,6 +899,7 @@ class Interaction:
self._bz_grid.D_diag,
self._bz_grid.Q,
self._fc3,
self._fc3_nonzero_indices,
self._svecs,
self._multi,
self._masses,
@ -993,6 +930,11 @@ class Interaction:
)
def _run_py(self):
assert self._interaction_strength is not None
assert self._triplets_at_q is not None
assert self._frequencies is not None
assert self._eigenvectors is not None
r2r = RealToReciprocal(
self._fc3, self._primitive, self.mesh_numbers, symprec=self._symprec
)
@ -1011,8 +953,10 @@ class Interaction:
for gp in grid_triplet:
self._run_phonon_solver_py(gp)
r2n.run(fc3_reciprocal, grid_triplet)
fc3_normal = r2n.get_reciprocal_to_normal()
assert fc3_normal is not None
self._interaction_strength[i] = (
np.abs(r2n.get_reciprocal_to_normal()) ** 2 * self._unit_conversion
np.abs(fc3_normal) ** 2 * self._unit_conversion
)
def _run_phonon_solver_py(self, grid_point):
@ -1038,6 +982,7 @@ class Interaction:
num_band = len(self._primitive) * 3
num_grid = len(self._bz_grid.addresses)
self._phonon_done = np.zeros(num_grid, dtype="byte")
self._phonon_all_done = False
self._frequencies = np.zeros((num_grid, num_band), dtype="double", order="C")
complex_dtype = "c%d" % (np.dtype("double").itemsize * 2)
self._eigenvectors = np.zeros(

View File

@ -34,7 +34,11 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from collections.abc import Sequence
from typing import Optional, Union
import numpy as np
from phonopy.structure.atoms import PhonopyAtoms
class ReciprocalToNormal:
@ -47,7 +51,12 @@ class ReciprocalToNormal:
"""
def __init__(
self, primitive, frequencies, eigenvectors, band_indices, cutoff_frequency=0
self,
primitive: PhonopyAtoms,
frequencies: np.ndarray,
eigenvectors: np.ndarray,
band_indices: Union[Sequence[int], np.ndarray],
cutoff_frequency: float = 0,
):
"""Init method."""
self._primitive = primitive
@ -56,10 +65,8 @@ class ReciprocalToNormal:
self._band_indices = band_indices
self._cutoff_frequency = cutoff_frequency
self._masses = self._primitive.masses
self._fc3_normal = None
self._fc3_reciprocal = None
self._fc3_normal: np.ndarray
self._fc3_reciprocal: np.ndarray
def run(self, fc3_reciprocal, grid_triplet):
"""Calculate fc3 in phonon coordinates."""
@ -71,7 +78,7 @@ class ReciprocalToNormal:
)
self._reciprocal_to_normal(grid_triplet)
def get_reciprocal_to_normal(self):
def get_reciprocal_to_normal(self) -> Optional[np.ndarray]:
"""Return fc3 in phonon coordinates."""
return self._fc3_normal
@ -102,7 +109,7 @@ class ReciprocalToNormal:
* e3[k * 3 + n, b3]
* self._fc3_reciprocal[i, j, k, ll, m, n]
)
mass_sqrt = np.sqrt(np.prod(self._masses[[i, j, k]]))
mass_sqrt = np.sqrt(np.prod(self._primitive.masses[[i, j, k]]))
sum_fc3 += sum_fc3_cart / mass_sqrt
return sum_fc3

View File

@ -42,7 +42,11 @@ from phonopy.structure.tetrahedron_method import TetrahedronMethod
from phono3py.other.tetrahedron_method import get_tetrahedra_relative_grid_address
from phono3py.phonon.func import gaussian
from phono3py.phonon.grid import BZGrid, get_grid_point_from_address_py
from phono3py.phonon.grid import (
BZGrid,
get_grid_point_from_address_py,
get_reduced_bases_and_tmat_inv,
)
if TYPE_CHECKING:
from phono3py.phonon3.interaction import Interaction
@ -327,14 +331,18 @@ def _get_BZ_triplets_at_q(bz_grid_index, bz_grid: BZGrid, map_triplets):
weights[g] += 1
ir_weights = np.extract(weights > 0, weights)
triplets = -np.ones((len(ir_weights), 3), dtype="int64", order="C")
reduced_basis, tmat_inv_int = get_reduced_bases_and_tmat_inv(
bz_grid.reciprocal_lattice
)
num_ir_ret = phono3c.BZ_triplets_at_q(
triplets,
bz_grid_index,
bz_grid.addresses,
bz_grid.gp_map,
map_triplets,
np.array(bz_grid.D_diag, dtype="int64"),
bz_grid.Q,
bz_grid.D_diag,
np.array(tmat_inv_int @ bz_grid.Q, dtype="int64", order="C"),
reduced_basis,
bz_grid.store_dense_gp_map * 1 + 1,
)

View File

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

View File

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

View File

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

View File

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

View File

@ -6,9 +6,11 @@ from pathlib import Path
import numpy as np
import pytest
from phonopy.harmonic.force_constants import get_drift_force_constants
from phonopy.interface.pypolymlp import PypolymlpParams
from phono3py import Phono3py
from phono3py.phonon3.fc3 import get_drift_fc3
cwd = Path(__file__).parent
@ -207,3 +209,63 @@ def test_use_pypolymlp_mgo(mgo_222rd_444rd: Phono3py):
assert (
pytest.approx(63.0018546, abs=1e-1) == ph3.thermal_conductivity.kappa[0, 0, 0]
)
@pytest.mark.parametrize("is_compact_fc", [True, False])
def test_symmetrize_fc_traditional(si_pbesol: Phono3py, is_compact_fc: bool):
"""Test symmetrize_fc3 and symmetrize_fc2 with traditional approach."""
ph3 = Phono3py(
si_pbesol.unitcell,
supercell_matrix=si_pbesol.supercell_matrix,
primitive_matrix=si_pbesol.primitive_matrix,
log_level=2,
)
ph3.dataset = si_pbesol.dataset
ph3.produce_fc3(is_compact_fc=is_compact_fc)
assert ph3.fc3 is not None
assert ph3.fc2 is not None
v1, v2, v3, _, _, _ = get_drift_fc3(ph3.fc3, primitive=ph3.primitive)
np.testing.assert_allclose(
np.abs([v1, v2, v3]), [1.755065e-01, 1.749287e-01, 3.333333e-05], atol=1e-6
)
ph3.symmetrize_fc3(options="level=3")
v1_sym, v2_sym, v3_sym, _, _, _ = get_drift_fc3(ph3.fc3, primitive=ph3.primitive)
if is_compact_fc:
np.testing.assert_allclose(
np.abs([v1_sym, v2_sym, v3_sym]), 1.217081e-05, atol=1e-6
)
else:
np.testing.assert_allclose(
np.abs([v1_sym, v2_sym, v3_sym]), 1.421085e-14, atol=1e-6
)
v1_sym, v2_sym, _, _ = get_drift_force_constants(ph3.fc2, primitive=ph3.primitive)
if is_compact_fc:
np.testing.assert_allclose(np.abs([v1_sym, v2_sym]), 1.0e-06, atol=1e-6)
else:
np.testing.assert_allclose(np.abs([v1_sym, v2_sym]), 1.0e-06, atol=1e-6)
@pytest.mark.parametrize("is_compact_fc", [True, False])
def test_symmetrize_fc_symfc(si_pbesol: Phono3py, is_compact_fc: bool):
"""Test symmetrize_fc3 and symmetrize_fc2 with symfc."""
pytest.importorskip("symfc")
ph3 = Phono3py(
si_pbesol.unitcell,
supercell_matrix=si_pbesol.supercell_matrix,
primitive_matrix=si_pbesol.primitive_matrix,
log_level=2,
)
ph3.dataset = si_pbesol.dataset
ph3.produce_fc3(is_compact_fc=is_compact_fc)
assert ph3.fc3 is not None
assert ph3.fc2 is not None
ph3.symmetrize_fc3(use_symfc_projector=True)
v1_sym, v2_sym, v3_sym, _, _, _ = get_drift_fc3(ph3.fc3, primitive=ph3.primitive)
np.testing.assert_allclose([v1_sym, v2_sym, v3_sym], 0, atol=1e-6)
ph3.symmetrize_fc2(use_symfc_projector=True)
v1_sym, v2_sym, _, _ = get_drift_force_constants(ph3.fc2, primitive=ph3.primitive)
np.testing.assert_allclose([v1_sym, v2_sym], 0, atol=1e-6)

View File

@ -276,6 +276,18 @@ def test_kappa_RTA_nacl(nacl_pbe: Phono3py):
np.testing.assert_allclose(ref_kappa_RTA, kappa, atol=0.5)
def test_kappa_RTA_mgo(
mgo_222rd_444rd_symfc: Phono3py, mgo_222rd_444rd_symfc_compact_fc: Phono3py
):
"""Test RTA by MgO cutoff 4."""
for ph3 in (mgo_222rd_444rd_symfc, mgo_222rd_444rd_symfc_compact_fc):
ref_kappa_RTA = [63.75, 63.75, 63.75, 0, 0, 0]
kappa = _get_kappa(ph3, [11, 11, 11]).ravel()
np.testing.assert_allclose(ref_kappa_RTA, kappa, atol=0.5)
kappa = _get_kappa(ph3, [11, 11, 11], is_full_pp=True).ravel()
np.testing.assert_allclose(ref_kappa_RTA, kappa, atol=0.5)
def test_kappa_RTA_nacl_with_sigma(nacl_pbe: Phono3py):
"""Test RTA with smearing method by NaCl."""
if nacl_pbe._make_r0_average:

View File

@ -322,6 +322,49 @@ def si_pbesol_111_222_alm_cutoff(request) -> Phono3py:
)
@pytest.fixture(scope="session")
def si_pbesol_111_222_symfc_cutoff() -> Phono3py:
"""Return Phono3py instance of Si 1x1x1.
* with symmetry
* full fc
* use symfc if available on test side
* cutoff=3
"""
pytest.importorskip("symfc")
yaml_filename = cwd / "phono3py_params_Si-111-222.yaml"
return phono3py.load(
yaml_filename,
fc_calculator="symfc",
fc_calculator_options="cutoff = 3",
log_level=1,
)
@pytest.fixture(scope="session")
def si_pbesol_111_222_symfc_cutoff_compact_fc() -> Phono3py:
"""Return Phono3py instance of Si 1x1x1.
* with symmetry
* compact fc
* use symfc if available on test side
* cutoff=3
"""
pytest.importorskip("symfc")
yaml_filename = cwd / "phono3py_params_Si-111-222.yaml"
return phono3py.load(
yaml_filename,
fc_calculator="symfc",
fc_calculator_options="cutoff = 3",
is_compact_fc=True,
log_level=1,
)
@pytest.fixture(scope="session")
def si_pbesol_111_222_alm_cutoff_fc2(request) -> Phono3py:
"""Return Phono3py instance of Si 1x1x1.
@ -523,6 +566,48 @@ def mgo_222rd_444rd() -> Phono3py:
return phono3py.load(yaml_filename, produce_fc=False, log_level=1)
@pytest.fixture(scope="session")
def mgo_222rd_444rd_symfc() -> Phono3py:
"""Return Phono3py instance of MgO-2x2x2-4x4x4 RD-RD.
* with symmetry
* full fc
* use symfc if available on test side
"""
pytest.importorskip("symfc")
yaml_filename = cwd / "phono3py_params_MgO-222rd-444rd.yaml.xz"
return phono3py.load(
yaml_filename,
is_compact_fc=False,
fc_calculator="symfc",
fc_calculator_options="|cutoff = 4",
log_level=1,
)
@pytest.fixture(scope="session")
def mgo_222rd_444rd_symfc_compact_fc() -> Phono3py:
"""Return Phono3py instance of MgO-2x2x2-4x4x4 RD-RD.
* with symmetry
* full fc
* use symfc if available on test side
"""
pytest.importorskip("symfc")
yaml_filename = cwd / "phono3py_params_MgO-222rd-444rd.yaml.xz"
return phono3py.load(
yaml_filename,
is_compact_fc=True,
fc_calculator="symfc",
fc_calculator_options="|cutoff = 4",
log_level=1,
)
@pytest.fixture(scope="session")
def ph_nacl() -> Phonopy:
"""Return Phonopy class instance of NaCl 2x2x2."""

View File

@ -4,42 +4,48 @@ from __future__ import annotations
import os
import pathlib
import tempfile
from collections.abc import Sequence
from dataclasses import dataclass, fields
from typing import Optional, Union
import h5py
import numpy as np
import pytest
import phono3py
from phono3py.cui.phono3py_script import main
cwd = pathlib.Path(__file__).parent
cwd_called = pathlib.Path.cwd()
@dataclass
class MockArgs:
"""Mock args of ArgumentParser."""
filename: Optional[Sequence[os.PathLike]] = None
conf_filename: Optional[os.PathLike] = None
fc_calculator: Optional[str] = None
cell_filename: str | os.PathLike | None = None
conf_filename: str | os.PathLike | None = None
fc_calculator: str | None = None
fc_calculator_options: str | None = None
fc_symmetry: bool = True
filename: Sequence[str | os.PathLike] | None = None
force_sets_mode: bool = False
force_sets_to_forces_fc2_mode: bool = False
log_level: Optional[int] = None
output_yaml_filename: Optional[os.PathLike] = None
show_num_triplets: bool = False
write_grid_points: bool = False
fc_symmetry: bool = True
cell_filename: Optional[str] = None
is_bterta: Optional[bool] = None
mesh_numbers: Optional[Sequence] = None
temperatures: Optional[Sequence] = None
use_pypolymlp: bool = False
input_filename = None
output_filename = None
input_output_filename = None
log_level: int | None = None
is_bterta: bool | None = None
is_lbte: bool | None = None
is_wigner_kappa: bool | None = None
mesh_numbers: Sequence | None = None
mlp_params: str | None = None
rd_number_estimation_factor: float | None = None
output_filename = None
output_yaml_filename: str | os.PathLike | None = None
random_displacements: int | str | None = None
show_num_triplets: bool = False
temperatures: Sequence | None = None
use_pypolymlp: bool = False
write_grid_points: bool = False
def __iter__(self):
"""Make self iterable to support in."""
@ -52,71 +58,272 @@ class MockArgs:
def test_phono3py_load():
"""Test phono3py-load script."""
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml"
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
# Clean files created by phono3py-load script.
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
file_path.unlink()
try:
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["5", "5", "5"],
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m555.hdf5",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
@pytest.mark.parametrize("fc_calculator,exit_code", [(None, 0), ("symfc", 0)])
def test_phono3py_load_with_typeII_dataset(fc_calculator, exit_code):
def test_phono3py_load_lbte():
"""Test phono3py-load script running direct solution."""
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
is_lbte=True,
temperatures=[
"300",
],
mesh_numbers=["5", "5", "5"],
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m555.hdf5",
"coleigs-m555.hdf5",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def test_phono3py_load_wigner_rta():
"""Test phono3py-load script running wigner rta."""
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
is_bterta=True,
is_wigner_kappa=True,
temperatures=[
"300",
],
mesh_numbers=["5", "5", "5"],
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# for filename in pathlib.Path.cwd().iterdir():
# print(filename)
# Clean files created by phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m555.hdf5",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def test_phono3py_load_wigner_lbte():
"""Test phono3py-load script running wigner rta."""
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
# Check sys.exit(0)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222.yaml",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
is_lbte=True,
is_wigner_kappa=True,
temperatures=[
"300",
],
mesh_numbers=["5", "5", "5"],
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# for filename in pathlib.Path.cwd().iterdir():
# print(filename)
# Clean files created by phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m555.hdf5",
"coleigs-m555.hdf5",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
@pytest.mark.parametrize(
"load_phono3py_yaml,fc_calculator,fc_calculator_options",
[
(True, None, None),
(True, "symfc", None),
(True, "symfc", "|cutoff=4.0"),
(False, "symfc", "|cutoff=4.0"),
],
)
def test_phono3py_load_with_typeII_dataset(
fc_calculator: str | None,
fc_calculator_options: str | None,
load_phono3py_yaml: bool,
):
"""Test phono3py-load script with typeII dataset.
When None, fallback to symfc.
"""
pytest.importorskip("symfc")
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params-Si111-rd.yaml.xz", fc_calculator=fc_calculator
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == exit_code
# Clean files created by phono3py-load script.
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
file_path.unlink()
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params-Si111-rd.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
fc_calculator=fc_calculator,
fc_calculator_options=fc_calculator_options,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
# Clean files created by phono3py-load script.
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(created_filename)
if file_path.exists():
if created_filename == "fc3.hdf5":
with h5py.File(file_path, "r") as f:
if fc_calculator_options is None:
assert "fc3_nonzero_indices" not in f
else:
assert "fc3_nonzero_indices" in f
assert "fc3_cutoff" in f
assert f["fc3_cutoff"][()] == pytest.approx(4.0)
file_path.unlink()
finally:
os.chdir(original_cwd)
@pytest.mark.parametrize("load_phono3py_yaml", [True, False])
def test_phono3py_with_QE_calculator(load_phono3py_yaml):
"""Test phono3py-load script with QE calculator."""
argparse_control = _get_phono3py_load_args(
cwd / "phono3py_params-qe-Si222.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["11", "11", "11"],
)
with pytest.raises(SystemExit):
main(**argparse_control)
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
with h5py.File(cwd_called / "kappa-m111111.hdf5", "r") as f:
np.testing.assert_almost_equal(f["kappa"][0, 0], 118.93, decimal=1)
try:
argparse_control = _get_phono3py_load_args(
cwd / "phono3py_params-qe-Si222.yaml.xz",
load_phono3py_yaml=load_phono3py_yaml,
is_bterta=True,
temperatures=[
"300",
],
mesh_numbers=["11", "11", "11"],
)
with pytest.raises(SystemExit):
main(**argparse_control)
# Clean files created by phono3py/phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m111111.hdf5",
):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
file_path.unlink()
with h5py.File("kappa-m111111.hdf5", "r") as f:
np.testing.assert_almost_equal(f["kappa"][0, 0], 118.93, decimal=1)
# Clean files created by phono3py/phono3py-load script.
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"kappa-m111111.hdf5",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def test_phono3py_load_with_pypolymlp_si():
@ -129,73 +336,259 @@ def test_phono3py_load_with_pypolymlp_si():
pytest.importorskip("pypolymlp", minversion="0.9.2")
pytest.importorskip("symfc")
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
fc_calculator="symfc",
use_pypolymlp=True,
)
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
try:
# Create fc2.hdf5
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
fc_calculator="symfc",
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in ("phono3py.yaml", "fc2.hdf5", "fc3.hdf5"):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
pathlib.Path("fc3.hdf5").unlink()
# phono3py.yaml and fc2.hd5 are used in the next run. So they are not deleted.
for created_filename in ("fc3.hdf5", "phono3py_mlp_eval_dataset.yaml"):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
file_path.unlink()
# Create MLP (polymlp.yaml)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_Si-111-222-rd.yaml.xz",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in ("phono3py.yaml", "polymlp.yaml"):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
argparse_control = _get_phono3py_load_args(
cwd_called / "phono3py.yaml",
fc_calculator="symfc",
use_pypolymlp=True,
)
# Create phono3py_mlp_eval_dataset.yaml
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(cwd_called / created_filename)
if file_path.exists():
file_path.unlink()
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 8
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def test_phono3py_load_with_pypolymlp_nacl():
"""Test phono3py-load script with pypolymlp using NaCl.
First run generates polymlp.yaml.
Second run uses polymlp.yaml.
"""
pytest.importorskip("pypolymlp", minversion="0.9.2")
pytest.importorskip("symfc")
with tempfile.TemporaryDirectory() as temp_dir:
original_cwd = pathlib.Path.cwd()
os.chdir(temp_dir)
try:
# Stage1 (preparation)
argparse_control = _get_phono3py_load_args(
cwd / ".." / "phono3py_params_MgO-222rd-444rd.yaml.xz",
mlp_params="cutoff=4.0,gtinv_maxl=4 4,max_p=1,gtinv_order=2",
fc_calculator="symfc",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 32
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
# Stage2 (cutoff test)
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|cutoff=4.0",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 8
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
# Stage3 (memsize test)
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|memsize=0.05",
random_displacements="auto",
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 16
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
for created_filename in (
"fc3.hdf5",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
# Stage4 (number_estimation_factor)
argparse_control = _get_phono3py_load_args(
"phono3py.yaml",
fc_calculator="symfc",
fc_calculator_options="|cutoff=4.0",
random_displacements="auto",
rd_number_estimation_factor=2.0,
use_pypolymlp=True,
)
with pytest.raises(SystemExit) as excinfo:
main(**argparse_control)
assert excinfo.value.code == 0
ph3 = phono3py.load("phono3py_mlp_eval_dataset.yaml")
assert len(ph3.displacements) == 4
for created_filename in (
"phono3py.yaml",
"fc2.hdf5",
"fc3.hdf5",
"polymlp.yaml",
"phono3py_mlp_eval_dataset.yaml",
):
file_path = pathlib.Path(created_filename)
assert file_path.exists()
file_path.unlink()
finally:
os.chdir(original_cwd)
def _get_phono3py_load_args(
phono3py_yaml_filepath: Union[str, pathlib.Path],
fc_calculator: Optional[str] = None,
phono3py_yaml_filepath: str | os.PathLike | None,
fc_calculator: str | None = None,
fc_calculator_options: str | None = None,
load_phono3py_yaml: bool = True,
is_bterta: bool = False,
temperatures: Optional[Sequence] = None,
mesh_numbers: Optional[Sequence] = None,
is_lbte: bool = False,
is_wigner_kappa: bool = False,
mesh_numbers: Sequence | None = None,
mlp_params: str | None = None,
rd_number_estimation_factor: float | None = None,
random_displacements: int | str | None = None,
temperatures: Sequence | None = None,
use_pypolymlp: bool = False,
):
# Mock of ArgumentParser.args.
if load_phono3py_yaml:
assert phono3py_yaml_filepath is not None
mockargs = MockArgs(
filename=[phono3py_yaml_filepath],
fc_calculator=fc_calculator,
fc_calculator_options=fc_calculator_options,
is_bterta=is_bterta,
temperatures=temperatures,
mesh_numbers=mesh_numbers,
use_pypolymlp=use_pypolymlp,
is_lbte=is_lbte,
is_wigner_kappa=is_wigner_kappa,
log_level=1,
mesh_numbers=mesh_numbers,
mlp_params=mlp_params,
rd_number_estimation_factor=rd_number_estimation_factor,
random_displacements=random_displacements,
temperatures=temperatures,
use_pypolymlp=use_pypolymlp,
)
else:
mockargs = MockArgs(
filename=[],
fc_calculator=fc_calculator,
fc_calculator_options=fc_calculator_options,
log_level=1,
cell_filename=phono3py_yaml_filepath,
is_bterta=is_bterta,
temperatures=temperatures,
is_lbte=is_lbte,
is_wigner_kappa=is_wigner_kappa,
mesh_numbers=mesh_numbers,
mlp_params=mlp_params,
rd_number_estimation_factor=rd_number_estimation_factor,
random_displacements=random_displacements,
temperatures=temperatures,
use_pypolymlp=use_pypolymlp,
)

View File

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

View File

@ -0,0 +1,69 @@
"""Tests of functions in fc_calculator."""
import pytest
from phonopy.structure.atoms import PhonopyAtoms
from phono3py.api_phono3py import Phono3py
from phono3py.interface.fc_calculator import determine_cutoff_pair_distance
def test_determine_cutoff_pair_distance() -> None:
"""Test determine_cutoff_pair_distance."""
cutoff = determine_cutoff_pair_distance(fc_calculator_options="|cutoff=4")
assert cutoff == pytest.approx(4.0)
cutoff = determine_cutoff_pair_distance(cutoff_pair_distance=5.0)
assert cutoff == pytest.approx(5.0)
cutoff = determine_cutoff_pair_distance(
fc_calculator_options="|cutoff=4", cutoff_pair_distance=5.0
)
assert cutoff == pytest.approx(4.0)
def test_determine_cutoff_pair_distance_with_memsize(aln_cell: PhonopyAtoms) -> None:
"""Test determine_cutoff_pair_distance estimated by memsize."""
pytest.importorskip("symfc")
ph3 = Phono3py(aln_cell, supercell_matrix=[3, 3, 2])
cutoff = determine_cutoff_pair_distance(
fc_calculator="symfc",
fc_calculator_options="|memsize=0.1",
supercell=ph3.supercell,
primitive=ph3.primitive,
symmetry=ph3.symmetry,
log_level=1,
)
assert cutoff == pytest.approx(3.2)
cutoff = determine_cutoff_pair_distance(
fc_calculator="symfc",
symfc_memory_size=0.2,
supercell=ph3.supercell,
primitive=ph3.primitive,
symmetry=ph3.symmetry,
log_level=1,
)
assert cutoff == pytest.approx(3.7)
cutoff = determine_cutoff_pair_distance(
fc_calculator="symfc",
fc_calculator_options="|memsize=0.1",
symfc_memory_size=0.2,
supercell=ph3.supercell,
primitive=ph3.primitive,
symmetry=ph3.symmetry,
log_level=1,
)
assert cutoff == pytest.approx(3.2)
with pytest.raises(RuntimeError):
cutoff = determine_cutoff_pair_distance(
fc_calculator="alm",
fc_calculator_options="|memsize=0.1",
symfc_memory_size=0.2,
supercell=ph3.supercell,
primitive=ph3.primitive,
symmetry=ph3.symmetry,
log_level=1,
)

View File

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

View File

@ -72,7 +72,7 @@ def test_cutoff_fc3_zero_compact_fc(nacl_pbe_compact_fc: Phono3py):
fc3 = ph.fc3.copy()
cutoff_fc3_by_zero(fc3, ph.supercell, 5, p2s_map=ph.primitive.p2s_map)
abs_delta = np.abs(ph.fc3 - fc3).sum()
assert np.abs(164.359250 - abs_delta) < 1e-3
assert np.abs(164.350663 - abs_delta) < 1e-3
def test_fc3(si_pbesol_111: Phono3py):
@ -218,10 +218,40 @@ def test_phonon_smat_fd_symfc(si_pbesol_111_222_fd_symfc: 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."""
ph = si_pbesol_111_222_alm_cutoff
assert ph.fc3 is not None
assert ph.fc2 is not None
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_symfc_cutoff(si_pbesol_111_222_symfc_cutoff: Phono3py):
"""Test phonon smat and symfc with Si PBEsol 1x1x1-2x2x2 cutoff."""
ph = si_pbesol_111_222_symfc_cutoff
assert ph.fc3 is not None
assert ph.fc2 is not None
assert ph.fc3_nonzero_indices is not None
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)
assert ph.fc3.shape == (8, 8, 8, 3, 3, 3)
assert ph.fc3_nonzero_indices.shape == (8, 8, 8)
assert ph.fc3_nonzero_indices.sum() == 104
def test_phonon_smat_symfc_cutoff_compact_fc(
si_pbesol_111_222_symfc_cutoff_compact_fc: Phono3py,
):
"""Test phonon smat and symfc with Si PBEsol 1x1x1-2x2x2 cutoff."""
ph = si_pbesol_111_222_symfc_cutoff_compact_fc
assert ph.fc3 is not None
assert ph.fc2 is not None
assert ph.fc3_nonzero_indices is not None
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)
assert ph.fc3.shape == (2, 8, 8, 3, 3, 3)
assert ph.fc3_nonzero_indices.shape == (2, 8, 8)
assert ph.fc3_nonzero_indices.sum() == 26
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."""
ph = si_pbesol_111_222_alm_cutoff_fc2

View File

@ -25,7 +25,7 @@ def test_imag_self_energy_at_bands(si_pbesol: Phono3py):
[0.00382813, 0.0049497, 0.02727924, 0.01382784, 0.04133946, 0.02980282],
]
for i, grgp in enumerate((1, 103)):
_, gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
[
si_pbesol.grid.grg2bzg[grgp],
],
@ -35,7 +35,9 @@ def test_imag_self_energy_at_bands(si_pbesol: Phono3py):
frequency_points_at_bands=True,
)
# print(gammas.ravel())
np.testing.assert_allclose(gammas.ravel(), gammas_ref[i], rtol=0, atol=1e-2)
np.testing.assert_allclose(
ise_params.gammas.ravel(), gammas_ref[i], rtol=0, atol=1e-2
)
def test_imag_self_energy_at_bands_detailed(si_pbesol: Phono3py):
@ -47,7 +49,7 @@ def test_imag_self_energy_at_bands_detailed(si_pbesol: Phono3py):
"""
si_pbesol.mesh_numbers = [9, 9, 9]
si_pbesol.init_phph_interaction()
_, gammas, detailed_gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
si_pbesol.grid.grg2bzg[[1, 103]],
[
300,
@ -145,10 +147,14 @@ def test_imag_self_energy_at_bands_detailed(si_pbesol: Phono3py):
]
weights_103 = [2] * 364 + [1]
gammas_1_ref = gammas[:, :, 0].ravel()
gammas_103_ref = gammas[:, :, 1].ravel()
gammas_1 = np.dot(weights_1, detailed_gammas[0][0, 0].sum(axis=-1).sum(axis=-1))
gammas_103 = np.dot(weights_103, detailed_gammas[1][0, 0].sum(axis=-1).sum(axis=-1))
gammas_1_ref = ise_params.gammas[:, :, 0].ravel()
gammas_103_ref = ise_params.gammas[:, :, 1].ravel()
gammas_1 = np.dot(
weights_1, ise_params.detailed_gammas[0][0, 0].sum(axis=-1).sum(axis=-1)
)
gammas_103 = np.dot(
weights_103, ise_params.detailed_gammas[1][0, 0].sum(axis=-1).sum(axis=-1)
)
np.testing.assert_allclose(
gammas_1[:2].sum(), gammas_1_ref[:2].sum(), rtol=0, atol=1e-2
)
@ -477,7 +483,7 @@ def test_imag_self_energy_npoints(si_pbesol: Phono3py, with_given_freq_points: b
si_pbesol.mesh_numbers = [9, 9, 9]
si_pbesol.init_phph_interaction()
if with_given_freq_points:
fpoints, gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
si_pbesol.grid.grg2bzg[[1, 103]],
[
300,
@ -485,7 +491,7 @@ def test_imag_self_energy_npoints(si_pbesol: Phono3py, with_given_freq_points: b
frequency_points=ref_freq_points,
)
else:
fpoints, gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
si_pbesol.grid.grg2bzg[[1, 103]],
[
300,
@ -497,8 +503,12 @@ def test_imag_self_energy_npoints(si_pbesol: Phono3py, with_given_freq_points: b
# for line in gammas.reshape(-1, 10):
# print("[", ",".join([f"{val:.8f}" for val in line]), "],")
np.testing.assert_allclose(ref_gammas, gammas.reshape(-1, 10), rtol=0, atol=1e-2)
np.testing.assert_allclose(ref_freq_points, fpoints.ravel(), rtol=0, atol=1e-5)
np.testing.assert_allclose(
ref_gammas, ise_params.gammas.reshape(-1, 10), rtol=0, atol=1e-2
)
np.testing.assert_allclose(
ref_freq_points, ise_params.frequency_points.ravel(), rtol=0, atol=1e-5
)
def test_imag_self_energy_npoints_with_sigma(si_pbesol: Phono3py):
@ -820,7 +830,7 @@ def test_imag_self_energy_npoints_with_sigma(si_pbesol: Phono3py):
]
si_pbesol.mesh_numbers = [9, 9, 9]
si_pbesol.init_phph_interaction()
fpoints, gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
si_pbesol.grid.grg2bzg[[1, 103]],
[
300,
@ -832,8 +842,12 @@ def test_imag_self_energy_npoints_with_sigma(si_pbesol: Phono3py):
# for line in gammas.reshape(-1, 10):
# print("[", ",".join([f"{val:.8f}" for val in line]), "],")
np.testing.assert_allclose(ref_gammas, gammas.reshape(-1, 10), rtol=0, atol=1e-2)
np.testing.assert_allclose(ref_freq_points, fpoints.ravel(), rtol=0, atol=1e-5)
np.testing.assert_allclose(
ref_gammas, ise_params.gammas.reshape(-1, 10), rtol=0, atol=1e-2
)
np.testing.assert_allclose(
ref_freq_points, ise_params.frequency_points.ravel(), rtol=0, atol=1e-5
)
si_pbesol.sigmas = None
@ -884,7 +898,7 @@ def test_imag_self_energy_detailed(si_pbesol: Phono3py):
]
si_pbesol.mesh_numbers = [9, 9, 9]
si_pbesol.init_phph_interaction()
_, _, detailed_gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
si_pbesol.grid.grg2bzg[
[
1,
@ -898,13 +912,16 @@ def test_imag_self_energy_detailed(si_pbesol: Phono3py):
)
print(
",".join(
[f"{val:.8f}" for val in detailed_gammas[0][0, 0].sum(axis=(1, 2, 3, 4))]
[
f"{val:.8f}"
for val in ise_params.detailed_gammas[0][0, 0].sum(axis=(1, 2, 3, 4))
]
)
)
np.testing.assert_allclose(
ref_detailed_gamma,
detailed_gammas[0][0, 0].sum(axis=(1, 2, 3, 4)),
ise_params.detailed_gammas[0][0, 0].sum(axis=(1, 2, 3, 4)),
rtol=0,
atol=1e-2,
)
@ -1428,7 +1445,7 @@ def test_imag_self_energy_scat_classes(si_pbesol: Phono3py, scattering_class: in
si_pbesol.mesh_numbers = [9, 9, 9]
si_pbesol.init_phph_interaction()
_, gammas = si_pbesol.run_imag_self_energy(
ise_params = si_pbesol.run_imag_self_energy(
si_pbesol.grid.grg2bzg[[1, 103]],
[
300,
@ -1441,7 +1458,7 @@ def test_imag_self_energy_scat_classes(si_pbesol: Phono3py, scattering_class: in
np.testing.assert_allclose(
gammas_classes[scattering_class - 1],
gammas.ravel(),
ise_params.gammas.ravel(),
rtol=0,
atol=1e-2,
)
@ -1714,7 +1731,7 @@ def test_imag_self_energy_nacl_npoints(nacl_pbe: Phono3py):
nacl_pbe.mesh_numbers = [9, 9, 9]
nacl_pbe.init_phph_interaction()
fpoints, gammas = nacl_pbe.run_imag_self_energy(
ise_params = nacl_pbe.run_imag_self_energy(
nacl_pbe.grid.grg2bzg[[1, 103]],
[
300,
@ -1724,8 +1741,12 @@ def test_imag_self_energy_nacl_npoints(nacl_pbe: Phono3py):
# print(",".join([f"{val:.8f}" for val in gammas.ravel()]))
np.testing.assert_allclose(ref_gammas_nacl, gammas.ravel(), rtol=0, atol=2e-2)
np.testing.assert_allclose(ref_freq_points_nacl, fpoints.ravel(), rtol=0, atol=1e-5)
np.testing.assert_allclose(
ref_gammas_nacl, ise_params.gammas.ravel(), rtol=0, atol=2e-2
)
np.testing.assert_allclose(
ref_freq_points_nacl, ise_params.frequency_points.ravel(), rtol=0, atol=1e-5
)
def test_imag_self_energy_nacl_nac_npoints(nacl_pbe: Phono3py):
@ -1876,13 +1897,15 @@ def test_imag_self_energy_nacl_nac_npoints(nacl_pbe: Phono3py):
nacl_pbe.mesh_numbers = [9, 9, 9]
nacl_pbe.init_phph_interaction(nac_q_direction=[1, 0, 0])
fpoints, gammas = nacl_pbe.run_imag_self_energy(
ise_params = nacl_pbe.run_imag_self_energy(
[nacl_pbe.grid.gp_Gamma], [300], num_frequency_points=10
)
# print(",".join([f"{val:.8f}" for val in gammas.ravel()]))
np.testing.assert_allclose(
ref_freq_points_nacl_nac, fpoints.ravel(), rtol=0, atol=1e-5
ref_freq_points_nacl_nac, ise_params.frequency_points.ravel(), rtol=0, atol=1e-5
)
np.testing.assert_allclose(
ref_gammas_nacl_nac, ise_params.gammas.ravel(), rtol=0, atol=2e-2
)
np.testing.assert_allclose(ref_gammas_nacl_nac, gammas.ravel(), rtol=0, atol=2e-2)

View File

@ -301,6 +301,7 @@ def test_get_all_shortest(aln_lda: Phono3py):
"""Test Interaction._get_all_shortest."""
ph3 = aln_lda
ph3.mesh_numbers = 30
assert ph3.grid is not None
itr = Interaction(
ph3.primitive,
ph3.grid,
@ -339,6 +340,7 @@ def _get_irt(
make_r0_average: bool = False,
):
ph3.mesh_numbers = mesh
assert ph3.grid is not None
itr = Interaction(
ph3.primitive,
ph3.grid,

View File

@ -911,7 +911,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[1, 36, 60],
[1, 38, 59],
[1, 41, 56],
],
], # 0
[
[1, 0, 4],
[1, 1, 3],
@ -937,7 +937,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[1, 36, 60],
[1, 38, 59],
[1, 41, 56],
],
], # 1
[
[1, 0, 4],
[1, 1, 3],
@ -969,7 +969,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[1, 39, 57],
[1, 41, 56],
[1, 42, 55],
],
], # 2
[
[1, 0, 4],
[1, 1, 3],
@ -1016,7 +1016,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[1, 70, 26],
[1, 72, 25],
[1, 73, 24],
],
], # 3
[
[1, 0, 4],
[1, 1, 3],
@ -1150,11 +1150,11 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 2, 87],
[8, 3, 86],
[9, 4, 92],
[8, 5, 84],
[8, 6, 82],
[8, 8, 81],
[8, 10, 80],
[8, 11, 85],
[9, 5, 84],
[8, 7, 82],
[8, 9, 81],
[9, 10, 80],
[9, 11, 85],
[8, 12, 78],
[8, 13, 76],
[8, 14, 75],
@ -1164,23 +1164,23 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[9, 22, 67],
[8, 24, 65],
[8, 27, 62],
[8, 29, 66],
[9, 30, 66],
[8, 31, 58],
[8, 32, 57],
[8, 40, 50],
[8, 48, 48],
],
], # 0
[
[8, 0, 89],
[8, 1, 88],
[8, 2, 87],
[8, 3, 86],
[9, 4, 92],
[8, 5, 84],
[8, 6, 82],
[8, 8, 81],
[8, 10, 80],
[8, 11, 85],
[9, 5, 84],
[8, 7, 82],
[8, 9, 81],
[9, 10, 80],
[9, 11, 85],
[8, 12, 78],
[8, 13, 76],
[8, 14, 75],
@ -1190,23 +1190,23 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[9, 22, 67],
[8, 24, 65],
[8, 27, 62],
[8, 29, 66],
[9, 30, 66],
[8, 31, 58],
[8, 32, 57],
[8, 40, 50],
[8, 48, 48],
],
], # 1
[
[8, 0, 89],
[8, 1, 88],
[8, 2, 87],
[8, 3, 86],
[9, 4, 92],
[8, 5, 84],
[8, 6, 82],
[8, 8, 81],
[8, 10, 80],
[8, 11, 85],
[9, 5, 84],
[8, 7, 82],
[8, 9, 81],
[9, 10, 80],
[9, 11, 85],
[8, 12, 78],
[8, 13, 76],
[8, 14, 75],
@ -1219,7 +1219,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 24, 65],
[8, 25, 64],
[8, 27, 62],
[8, 29, 66],
[9, 30, 66],
[8, 31, 58],
[8, 32, 57],
[8, 33, 56],
@ -1227,7 +1227,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 40, 50],
[9, 41, 49],
[9, 42, 54],
[8, 43, 46],
[8, 43, 47],
[8, 44, 45],
[8, 48, 48],
[8, 50, 40],
@ -1240,20 +1240,20 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 71, 19],
[9, 72, 18],
[8, 79, 17],
[8, 81, 8],
[8, 81, 9],
[8, 89, 0],
],
], # 2
[
[8, 0, 89],
[8, 1, 88],
[8, 2, 87],
[8, 3, 86],
[9, 4, 92],
[8, 5, 84],
[8, 6, 82],
[8, 8, 81],
[8, 10, 80],
[8, 11, 85],
[9, 5, 84],
[8, 7, 82],
[8, 9, 81],
[9, 10, 80],
[9, 11, 85],
[8, 12, 78],
[8, 13, 76],
[8, 14, 75],
@ -1266,7 +1266,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 24, 65],
[8, 25, 64],
[8, 27, 62],
[8, 29, 66],
[9, 30, 66],
[8, 31, 58],
[8, 32, 57],
[8, 33, 56],
@ -1274,7 +1274,7 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 40, 50],
[9, 41, 49],
[9, 42, 54],
[8, 43, 46],
[8, 43, 47],
[8, 44, 45],
[8, 48, 48],
[8, 50, 40],
@ -1287,9 +1287,9 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
[8, 71, 19],
[9, 72, 18],
[8, 79, 17],
[8, 81, 8],
[8, 81, 9],
[8, 89, 0],
],
], # 3
[
[7, 0, 28],
[8, 1, 27],
@ -1794,22 +1794,23 @@ def test_get_BZ_triplets_at_q(aln_cell: PhonopyAtoms, params):
ir_weights_with_zeros = np.zeros(45, dtype=int)
ir_weights_with_zeros[: len(ir_weights)] = ir_weights
np.testing.assert_equal(ref_triplets[i][params[3]], ir_triplets)
np.testing.assert_equal(ref_ir_weights[i][params[3]], ir_weights)
if i == 1 and params[0]:
# print("{")
# for j, tp in enumerate(triplets_with_zeros):
# print("%d, %d, %d, " % tuple(tp), end="")
# if (j + 1) % 5 == 0:
# print("&")
print(", ".join(["%d" % x for x in ir_weights_with_zeros]))
# print("},")
# print(len(ir_triplets))
# print("[")
# print("[")
# for tp in ir_triplets:
# print("[%d, %d, %d]," % tuple(tp))
# print("],")
np.testing.assert_equal(ref_triplets[i][params[3]], ir_triplets)
np.testing.assert_equal(ref_ir_weights[i][params[3]], ir_weights)
# if i == 1 and params[0]:
# print("{")
# for j, tp in enumerate(triplets_with_zeros):
# print("%d, %d, %d, " % tuple(tp), end="")
# if (j + 1) % 5 == 0:
# print("&")
# print(", ".join(["%d" % x for x in ir_weights_with_zeros]))
# print("},")
# print(len(ir_triplets))
# print("[")
# print("]")
# print("[")
# print(",".join(["%d" % x for x in ir_weights]))