├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── docs ├── .buildinfo ├── .nojekyll ├── _sources │ ├── index.rst.txt │ ├── index.txt │ ├── output_files.rst.txt │ ├── projection.rst.txt │ ├── visualization.rst.txt │ └── visualization.txt ├── _static │ ├── ajax-loader.gif │ ├── alabaster.css │ ├── basic.css │ ├── comment-bright.png │ ├── comment-close.png │ ├── comment.png │ ├── contents.png │ ├── custom.css │ ├── doctools.js │ ├── documentation_options.js │ ├── down-pressed.png │ ├── down.png │ ├── file.png │ ├── jquery-1.11.1.js │ ├── jquery-3.1.0.js │ ├── jquery-3.4.1.js │ ├── jquery.js │ ├── language_data.js │ ├── minus.png │ ├── navigation.png │ ├── plus.png │ ├── pygments.css │ ├── searchtools.js │ ├── sphinxdoc.css │ ├── underscore-1.3.1.js │ ├── underscore.js │ ├── up-pressed.png │ ├── up.png │ └── websupport.js ├── genindex.html ├── index.html ├── objects.inv ├── output_files.html ├── projection.html ├── search.html ├── searchindex.js └── visualization.html ├── docsrc ├── Makefile ├── conf.py ├── index.rst ├── make.bat ├── output.rst ├── projection.rst └── visualization.rst ├── examples ├── 00.L12_Cu3Au │ ├── FORCE_SETS │ ├── POSCAR │ ├── POSCAR_ideal │ ├── README.md │ ├── band.conf │ ├── plot.py │ ├── sf.orig.svg │ └── writefc.conf └── 01.A1_Cu0.75Au0.25 │ ├── FORCE_CONSTANTS │ ├── POSCAR │ ├── POSCAR_ideal │ ├── README.md │ ├── band.conf │ ├── plot.py │ ├── pre0.py │ ├── pre1.py │ └── sf.orig.svg ├── group ├── __init__.py ├── group.py ├── mathtools.py └── test_mathtools.py ├── scripts ├── run_separation ├── separation ├── upho_fit ├── upho_qpoints ├── upho_sf └── upho_weights ├── setup.py ├── tests ├── cui │ └── test_settings.py ├── group │ └── test_group.py ├── irreps │ ├── test_character_tables.py │ └── test_irreps.py ├── poscars │ ├── POSCAR_A13 │ ├── POSCAR_A3 │ ├── POSCAR_A4_conv │ ├── POSCAR_A_h │ ├── POSCAR_B3_conv │ ├── POSCAR_Cl12Pd6 │ ├── POSCAR_H3S │ ├── POSCAR_L1_2 │ ├── POSCAR_MgNH │ ├── POSCAR_NaGdCu2F8 │ ├── POSCAR_Sc │ ├── POSCAR_ThCl4 │ ├── POSCAR_WC │ ├── POSCAR_fcc │ ├── POSCAR_fcc_2x2x2 │ ├── POSCAR_fcc_prim │ ├── POSCAR_fcc_prim_test │ ├── POSCAR_omega_disordered_1 │ ├── POSCAR_omega_disordered_2 │ └── POSCAR_omega_ideal ├── test_average_masses.py ├── test_element_weights_calculator.py ├── test_functions.py ├── test_get_smallest_vectors.py ├── test_mappings_modifier.py ├── test_rotational_projector.py ├── test_translational_projector.py ├── test_unfolder_symmetry.py └── test_vectors_adjuster.py ├── tox.ini └── upho ├── .DS_Store ├── __init__.py ├── analysis ├── __init__.py ├── fc_analyzer_base.py ├── fc_symmetrizer_spg.py ├── functions.py ├── mappings_modifier.py ├── smearing.py ├── test.py └── time_measurer.py ├── api_unfolding.py ├── cui ├── __init__.py └── settings.py ├── file_io.py ├── harmonic ├── __init__.py └── dynamical_matrix.py ├── irreps ├── __init__.py ├── character_tables.py └── irreps.py ├── phonon ├── __init__.py ├── band_structure.py ├── density_extractor.py ├── dos_unfolding.py ├── eigenstates.py ├── element_weights_calculator.py ├── mesh_unfolding.py ├── rotational_projector.py ├── sf_fitter.py ├── single_point.py ├── star_creator.py ├── translational_projector.py └── vectors_adjuster.py ├── qpoints ├── __init__.py ├── lebedev_write.py └── qpoints_creator.py └── structure ├── __init__.py ├── structure_analyzer.py ├── symtools.py └── unfolder_symmetry.py /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-18.04 12 | strategy: 13 | matrix: 14 | python-version: [3.7] 15 | phonopy-version: [2.7.0, 2.14.0] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | python -m pip install phonopy==${{ matrix.phonopy-version }} 27 | python -m pip install pytest>=4.6 28 | python -m pip install pytest-cov 29 | python -m pip install codecov coverage 30 | - name: Test with pytest 31 | run: | 32 | python -mpytest -ra --cov=./ --log-level=DEBUG tests 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | env: 4 | - PHONOPY_VERSION=2.7.0 5 | - PHONOPY_VERSION=2.8.0 6 | - PHONOPY_VERSION=2.9.0 7 | 8 | install: 9 | - python -mpip install phonopy==$PHONOPY_VERSION 10 | - python -mpip install pytest pytest-cov 11 | - python -mpip install codecov coverage 12 | script: python -mpytest tests -ra --cov=./ --log-level=DEBUG 13 | after_success: 14 | - codecov 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yuji Ikeda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UPHO 2 | 3 | [![GitHubActions](https://github.com/yuzie007/upho/actions/workflows/tests.yml/badge.svg)](https://github.com/yuzie007/upho/actions?query=workflow%3ATests) 4 | 5 | Band unfolding for phonons (http://yuzie007.github.io/upho/) 6 | 7 | ## Requirements 8 | 9 | - numpy 10 | - h5py 11 | - phonopy>=2.7.0 12 | 13 | ## Install 14 | 15 | ```bash 16 | pip install git+https://github.com/yuzie007/upho.git@v0.6.6 17 | ``` 18 | 19 | ## Usage and Tutorial 20 | 21 | See the `examples` directory. 22 | 23 | ## Options (`upho_weights`) 24 | 25 | ### `--average_masses` 26 | 27 | Atomic masses whose sites are equivalent in the underlying structure 28 | are averaged. 29 | 30 | ### `--average_force_constants` 31 | 32 | FC elements which are equivalent under the symmetry operations 33 | for the underlying structure are averaged. 34 | 35 | ## Options (`upho_sf`) 36 | 37 | ### `-f FILENAME`, `--filename FILENAME` 38 | 39 | Filename for the data of weights. 40 | 41 | ### `--format {hdf5,text}` 42 | 43 | Output file format. 44 | 45 | ### `--function {gaussian,lorentzian}` 46 | 47 | Function used for the smearing. 48 | 49 | ### `-s SIGMA`, `--sigma SIGMA` 50 | 51 | Parameter for the smearing function (THz). 52 | For Gaussian, this is the standard deviation. 53 | For Lorentzian, this is the HWHM (gamma). 54 | 55 | ### `--fmax FMAX` 56 | 57 | Maximum frequency (THz). 58 | 59 | ### `--fmin FMIN` 60 | 61 | Minimum frequency (THz). 62 | 63 | ### `--fpitch FPITCH` 64 | 65 | Frequency pitch (THz). 66 | 67 | ### `--squared` 68 | 69 | Use squared frequencies instead of raw frequencies. 70 | 71 | ## Not yet (possible bugs) 72 | 73 | (Projective) representations of little cogroup may be treated in a wrong way 74 | when we consider wave vectors on the BZ boundary and translational parts of 75 | symmetry operations are not equal to zero. 76 | 77 | ## Author(s) 78 | 79 | Yuji Ikeda (yuji.ikeda.ac.jp@gmail.com, Universität Stuttgart, Germany) 80 | 81 | ## How to cite 82 | 83 | When using this code, please cite the following article. 84 | 85 | *Mode decomposition based on crystallographic symmetry in the band-unfolding method*, 86 | Yuji Ikeda, Abel Carreras, Atsuto Seko, Atsushi Togo, and Isao Tanaka, 87 | Phys. Rev. B **95**, 024305 (2017). 88 | http://journals.aps.org/prb/abstract/10.1103/PhysRevB.95.024305 89 | 90 | For high entropy alloy works, you can also consider 91 | 92 | *Phonon Broadening in High Entropy Alloys*, 93 | Fritz Körmann, Yuji Ikeda, Blazej Grabowski, and Marcel H. F. Sluiter, 94 | Npj Comput. Mater. **3**, 1 (2017). 95 | https://www.nature.com/articles/s41524-017-0037-8 96 | -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 5394d5b46c7245b0d3336863a0b426e7 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/.nojekyll -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. upho documentation master file, created by 2 | sphinx-quickstart on Wed Feb 15 12:50:34 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to upho's documentation! 7 | ================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | output_files 15 | projection 16 | visualization 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | 25 | -------------------------------------------------------------------------------- /docs/_sources/index.txt: -------------------------------------------------------------------------------- 1 | .. upho documentation master file, created by 2 | sphinx-quickstart on Wed Feb 15 12:50:34 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to upho's documentation! 7 | ================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | visualization 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /docs/_sources/output_files.rst.txt: -------------------------------------------------------------------------------- 1 | ############ 2 | Output files 3 | ############ 4 | 5 | ``band.hdf5`` 6 | ============= 7 | 8 | .. code-block:: console 9 | 10 | band.hdf5 11 | ├── paths (Dataset {3, 101, 3}) 12 | ├── 0 (Group) 13 | │ ├── 0 (Group) 14 | │ ├── 1 (Group) 15 | │ │ ├── point (Dataset {3}) 16 | │ │ ├── q_star (Dataset {6, 3}) 17 | │ │ ├── distance (Dataset {SCALAR}) 18 | │ │ ├── natoms_primitive (Dataset {SCALAR}) 19 | │ │ ├── elements (Dataset {2}) 20 | │ │ ├── num_arms (Dataset {SCALAR}) 21 | │ │ ├── pointgroup_symbol (Dataset {SCALAR}) 22 | │ │ ├── num_irreps (Dataset {SCALAR}) 23 | │ │ ├── ir_labels (Dataset {10}) 24 | │ │ ├── frequencies (Dataset {6, 12}) 25 | │ │ ├── weights_t (Dataset {6, 12}) 26 | │ │ ├── weights_e (Dataset {6, 1, 2, 1, 2, 12}) 27 | │ │ ├── weights_s (Dataset {6, 5, 12}) 28 | │ │ ├── weights_s_e (Dataset {6, 5, 1, 2, 1, 2, 12}) 29 | │ │ └── weights_e2 (Dataset {6, 1, 2, 12}) 30 | │ ├── 2 (Group) 31 | │ . 32 | │ . 33 | │ . 34 | │ 35 | ├── 1 (Group) 36 | │ ├── 0 (Group) 37 | │ ├── 1 (Group) 38 | │ ├── 2 (Group) 39 | │ . 40 | │ . 41 | │ . 42 | │ 43 | ├── 2 (Group) 44 | │ ├── 0 (Group) 45 | │ ├── 1 (Group) 46 | │ ├── 2 (Group) 47 | │ . 48 | │ . 49 | │ . 50 | 51 | 52 | ``paths`` 53 | --------- 54 | 55 | Array with the shape of ``(NPATHS, BAND_POINTS, 3)``. 56 | 57 | Q-point sets and the shape of band paths. 58 | 59 | ``NPATHS`` corresponds to the number of 3-integer sets in ``BAND`` in ``band.conf``. 60 | 61 | ``BAND_POINTS`` corresponds to that in ``band.conf``. 62 | 63 | Each group corresponds to one of the q-points given here. 64 | 65 | ``point`` 66 | --------- 67 | 68 | Reciprocal space point in fractional coordinates to be computed. 69 | 70 | ``q_star`` 71 | ---------- 72 | 73 | Star (symmetrically equivalent set of points) of ``point`` in the ideal lattice considered in the calculations. 74 | The actual behavior depends on the value of ``star`` in ``input.json``. 75 | 76 | - ``sym`` (default): Symmetrically euqivalent q-points, w/o duplications, are considered. 77 | - ``all``: Symmetrically equivalent q-points, possibly duplicated due to symmetry operations. Maybe used for testing purposes. 78 | - ``none``: Only the given q-point is considered. 79 | 80 | ``distance`` 81 | ------------ 82 | 83 | Distance of the considered reciprocal-space point from the first one along the band paths in fractional coordinates. 84 | 85 | ``natoms_primitive`` 86 | -------------------- 87 | 88 | The number of atoms in the primitive cell of the ideal lattice. (``natoms_p`` in ``eigenstates.py``.) 89 | 90 | ``elements`` 91 | ------------ 92 | 93 | Chemical elements in the disordered-alloy model. 94 | The number of the chemical elements are ``nelms`` in ``eigenstates.py``. 95 | 96 | ``num_arms`` 97 | ------------ 98 | 99 | The number of the arms of the star. 100 | Specifically this equals to ``len(q_star)``. 101 | 102 | ``pointgroup_symbol`` 103 | --------------------- 104 | 105 | Point group symbol in the Hermann–Mauguin notation associated with the little co-group of the considered reciprocal-space point. 106 | 107 | ``num_irreps`` 108 | -------------- 109 | 110 | The number of irreducible representations of the little co-group. 111 | 112 | ``ir_labels`` 113 | ------------- 114 | 115 | Labels of the irreducible representations. 116 | 117 | ``frequencies`` 118 | --------------- 119 | 120 | Array with the shape of ``(num_arms, nfreqs)``, where ``nfreqs`` is equal to ``3 * natoms_primitive``. 121 | 122 | Phonon frequencies projected onto this q-point. 123 | 124 | ``weights_t`` 125 | ------------- 126 | 127 | Array with the shape of ``(num_arms, nfreqs)``. 128 | 129 | Weights of the phonon frequencies determined by the band unfolding. 130 | 131 | ``weights_e`` 132 | ------------- 133 | 134 | Array with the shape of ``(num_arms, natoms_p, nelms, natoms_p, nelms, nfreqs)``. 135 | 136 | Partial weights for chemical pairs. 137 | 138 | ``weights_s`` 139 | ------------- 140 | 141 | Array with the shape of ``(num_arms, num_irreps, nfreqs)``. 142 | 143 | Partial weights for irreducible representations. 144 | Introduced in 145 | `[Y. Ikeda, A. Carreras, A. Seko, A. Togo, and I. Tanaka, Phys. Rev. B 95, 24305 (2017) 146 | `_]. 147 | 148 | ``weights_s_e`` 149 | --------------- 150 | 151 | Array with the shape of 152 | ``(num_arms, num_irreps, natoms_p, nelms, natoms_p, nelms, nfreqs)``. 153 | 154 | Partial weights for irreducible representations and for chemical pairs. 155 | 156 | ``weights_e2`` 157 | -------------- 158 | 159 | Array with the shape of 160 | ``(num_arms, natoms_p, nelms, nfreqs)``. 161 | 162 | Partial weights for chemical elements. Introduced in 163 | [`F. Körmann, Y. Ikeda, B. Grabowski, and M. H. F. Sluiter, Npj Comput. Mater. 3, 36 (2017) 164 | `_]. 165 | -------------------------------------------------------------------------------- /docs/_sources/projection.rst.txt: -------------------------------------------------------------------------------- 1 | Projection 2 | ========== 3 | 4 | Chemical projection 5 | ------------------- 6 | 7 | There may be several possible schemes for the chemical projection. 8 | 9 | E1 10 | ^^ 11 | 12 | .. math:: 13 | :nowrap: 14 | 15 | \begin{align} 16 | w_{\mathbf{k}, \textrm{XX}'} 17 | &= 18 | \left( 19 | \hat{P}^{\mathbf{k}} 20 | \hat{P}^{\textrm{X}} 21 | \tilde{\mathbf{v}} 22 | \right)^{\dagger} 23 | \left( 24 | \hat{P}^{\mathbf{k}} 25 | \hat{P}^{\textrm{X}'} 26 | \tilde{\mathbf{v}} 27 | \right) 28 | \end{align} 29 | 30 | Generally this scheme causes cross terms, which can be negative. 31 | 32 | E2 33 | ^^ 34 | 35 | .. math:: 36 | :nowrap: 37 | 38 | \begin{gather} 39 | w_{\mathbf{k}} 40 | = 41 | \left| 42 | \hat{P}^{\mathbf{k}} 43 | \tilde{\mathbf{v}} 44 | \right|^{2} 45 | \\ 46 | w_{\textrm{X}} 47 | = 48 | \left| 49 | \hat{P}^{\textrm{X}} 50 | \tilde{\mathbf{v}} 51 | \right|^{2} 52 | \\ 53 | 54 | w_{\mathbf{k}, \textrm{X}} 55 | \equiv 56 | w_{\mathbf{k}} 57 | w_{\textrm{X}} 58 | \end{gather} 59 | 60 | This scheme does not cause cross terms. 61 | Instead, :math:`w_{\mathbf{k}, \textrm{X}'}/w_{\mathbf{k}, \textrm{X}}` does no longer depend on 62 | :math:`\mathbf{k}`. 63 | -------------------------------------------------------------------------------- /docs/_sources/visualization.rst.txt: -------------------------------------------------------------------------------- 1 | Visualization of obtained spectral functions 2 | ============================================ 3 | 4 | You may be able to use the plotting tools in 5 | https://github.com/yuzie007/ph_plotter/releases. 6 | Please download the latest one. 7 | To use the tools, you need to run upho_sf with the option **--format hdf5** 8 | and obtain **sf.hdf5**. :: 9 | 10 | /path/to/upho/scripts/upho_sf --fpitch 0.01 -s 0.05 --function lorentzian --format hdf5 11 | 12 | Then run:: 13 | 14 | DOWNLOADED_PATH/ph_plotter/scripts/band_sf.py --sf_with irreps --plot_style mesh --sf_max 0.8 --sf_min 0 --d_sf 0.2 --f_max 10 --f_min 0 --d_freq 2 --colormap_p r 15 | 16 | Then the total spectral functions may be plotted as **band_sf_THz_w_bar.pdf**. 17 | 18 | To plot decomposed spectral functions according to small representations, 19 | we first need to know **pointgroup_symbol** and **ir_labels**. 20 | For example, if you would like to focus on the point 2 on the band path 1, 21 | The pointgroup_symbol of this point is found from the following command: :: 22 | 23 | % h5dump -d 1/2/pointgroup_symbol sf.hdf5 24 | HDF5 "sf.hdf5" { 25 | DATASET "1/2/pointgroup_symbol" { 26 | DATATYPE H5T_STRING { 27 | STRSIZE 3; 28 | STRPAD H5T_STR_NULLPAD; 29 | CSET H5T_CSET_ASCII; 30 | CTYPE H5T_C_S1; 31 | } 32 | DATASPACE SCALAR 33 | DATA { 34 | (0): "mm2" 35 | } 36 | } 37 | } 38 | 39 | Then we find the pointgroup_symbol for this point is “mm2”. 40 | The SRs for this point is found as, :: 41 | 42 | % h5dump -d 1/2/ir_labels sf.hdf5 43 | HDF5 "sf.hdf5" { 44 | DATASET "1/2/ir_labels" { 45 | DATATYPE H5T_STRING { 46 | STRSIZE 2; 47 | STRPAD H5T_STR_NULLPAD; 48 | CSET H5T_CSET_ASCII; 49 | CTYPE H5T_C_S1; 50 | } 51 | DATASPACE SIMPLE { ( 4 ) / ( 4 ) } 52 | DATA { 53 | (0): "A1", "A2", "B1", "B2" 54 | } 55 | } 56 | } 57 | 58 | Then we find “A1”, “A2”, “B1”, and “B2” is the possible SRs for this point. 59 | Note that some of them can have no contribution to the spectral function. 60 | 61 | The decomposed spectral functions for B1 for the pointgroup_symbol of mm2 may be plotted by adding **—selected_irreps '{"mm2":["B1”]}’** for the plotting command as, :: 62 | 63 | DOWNLOADED_PATH/ph_plotter/scripts/band_sf.py --plot_style mesh --sf_max 0.8 --sf_min 0 --d_sf 0.2 --f_max 10 --f_min 0 --d_freq 2 --colormap_p r --selected_irreps '{"mm2":["B1"]}' 64 | 65 | -------------------------------------------------------------------------------- /docs/_sources/visualization.txt: -------------------------------------------------------------------------------- 1 | Visualization of obtained spectral functions 2 | ============================================ 3 | 4 | You may be able to use the plotting tools in 5 | https://github.com/yuzie007/ph_plotter/releases. 6 | Please download the latest one. 7 | To use the tools, you need to run upho_sf with the option **--format hdf5** 8 | and obtain **sf.hdf5**. :: 9 | 10 | /path/to/upho/scripts/upho_sf --fpitch 0.01 -s 0.05 --function lorentzian --nosquared --format hdf5 11 | 12 | Then run:: 13 | 14 | DOWNLOADED_PATH/ph_plotter/scripts/band_sf.py --sf_with irreps --plot_style mesh --sf_max 0.8 --sf_min 0 --d_sf 0.2 --f_max 10 --f_min 0 --d_freq 2 --colormap_p r 15 | 16 | Then the total spectral functions may be plotted as **band_sf_THz_w_bar.pdf**. 17 | 18 | To plot decomposed spectral functions according to small representations, 19 | we first need to know **pointgroup_symbol** and **ir_labels**. 20 | For example, if you would like to focus on the point 2 on the band path 1, 21 | The pointgroup_symbol of this point is found from the following command: :: 22 | 23 | % h5dump -d 1/2/pointgroup_symbol sf.hdf5 24 | HDF5 "sf.hdf5" { 25 | DATASET "1/2/pointgroup_symbol" { 26 | DATATYPE H5T_STRING { 27 | STRSIZE 3; 28 | STRPAD H5T_STR_NULLPAD; 29 | CSET H5T_CSET_ASCII; 30 | CTYPE H5T_C_S1; 31 | } 32 | DATASPACE SCALAR 33 | DATA { 34 | (0): "mm2" 35 | } 36 | } 37 | } 38 | 39 | Then we find the pointgroup_symbol for this point is “mm2”. 40 | The SRs for this point is found as, :: 41 | 42 | % h5dump -d 1/2/ir_labels sf.hdf5 43 | HDF5 "sf.hdf5" { 44 | DATASET "1/2/ir_labels" { 45 | DATATYPE H5T_STRING { 46 | STRSIZE 2; 47 | STRPAD H5T_STR_NULLPAD; 48 | CSET H5T_CSET_ASCII; 49 | CTYPE H5T_C_S1; 50 | } 51 | DATASPACE SIMPLE { ( 4 ) / ( 4 ) } 52 | DATA { 53 | (0): "A1", "A2", "B1", "B2" 54 | } 55 | } 56 | } 57 | 58 | Then we find “A1”, “A2”, “B1”, and “B2” is the possible SRs for this point. 59 | Note that some of them can have no contribution to the spectral function. 60 | 61 | The decomposed spectral functions for B1 for the pointgroup_symbol of mm2 may be plotted by adding **—selected_irreps '{"mm2":["B1”]}’** for the plotting command as, :: 62 | 63 | DOWNLOADED_PATH/ph_plotter/scripts/band_sf.py --sf_with irreps --plot_style mesh --sf_max 0.8 --sf_min 0 --d_sf 0.2 --f_max 10 --f_min 0 --d_freq 2 --colormap_p r --selected_irreps '{"mm2":["B1"]}' 64 | 65 | -------------------------------------------------------------------------------- /docs/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/comment.png -------------------------------------------------------------------------------- /docs/_static/contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/contents.png -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.6.0', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /docs/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/down.png -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/navigation.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #eeffcc; } 8 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 23 | .highlight .go { color: #333333 } /* Generic.Output */ 24 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #902000 } /* Keyword.Type */ 34 | .highlight .m { color: #208050 } /* Literal.Number */ 35 | .highlight .s { color: #4070a0 } /* Literal.String */ 36 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 37 | .highlight .nb { color: #007020 } /* Name.Builtin */ 38 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #60add5 } /* Name.Constant */ 40 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 41 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #007020 } /* Name.Exception */ 43 | .highlight .nf { color: #06287e } /* Name.Function */ 44 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 45 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 48 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 58 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 61 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 65 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 67 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 70 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 71 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 72 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 74 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/_static/up.png -------------------------------------------------------------------------------- /docs/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Index — upho 0.6.0 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | 29 |
30 |
31 |
32 |
33 | 34 | 35 |

Index

36 | 37 |
38 | 39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 | 60 |
61 |
62 | 72 | 76 | 77 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome to upho’s documentation! — upho 0.6.0 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 33 | 34 |
35 |
36 |
37 |
38 | 39 |
40 |

Welcome to upho’s documentation!

41 |

Contents:

42 | 51 |
52 |
53 |

Indices and tables

54 | 59 |
60 | 61 | 62 |
63 |
64 |
65 |
66 | 96 |
97 |
98 | 111 | 115 | 116 | -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/docs/objects.inv -------------------------------------------------------------------------------- /docs/projection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Projection — upho 0.6.0 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 |

Projection

46 |
47 |

Chemical projection

48 |

There may be several possible schemes for the chemical projection.

49 |
50 |

E1

51 |
52 | \begin{align} 53 | w_{\mathbf{k}, \textrm{XX}'} 54 | &= 55 | \left( 56 | \hat{P}^{\mathbf{k}} 57 | \hat{P}^{\textrm{X}} 58 | \tilde{\mathbf{v}} 59 | \right)^{\dagger} 60 | \left( 61 | \hat{P}^{\mathbf{k}} 62 | \hat{P}^{\textrm{X}'} 63 | \tilde{\mathbf{v}} 64 | \right) 65 | \end{align}

Generally this scheme causes cross terms, which can be negative.

66 |
67 |
68 |

E2

69 |
70 | \begin{gather} 71 | w_{\mathbf{k}} 72 | = 73 | \left| 74 | \hat{P}^{\mathbf{k}} 75 | \tilde{\mathbf{v}} 76 | \right|^{2} 77 | \\ 78 | w_{\textrm{X}} 79 | = 80 | \left| 81 | \hat{P}^{\textrm{X}} 82 | \tilde{\mathbf{v}} 83 | \right|^{2} 84 | \\ 85 | 86 | w_{\mathbf{k}, \textrm{X}} 87 | \equiv 88 | w_{\mathbf{k}} 89 | w_{\textrm{X}} 90 | \end{gather}

This scheme does not cause cross terms. 91 | Instead, \(w_{\mathbf{k}, \textrm{X}'}/w_{\mathbf{k}, \textrm{X}}\) does no longer depend on 92 | \(\mathbf{k}\).

93 |
94 |
95 |
96 | 97 | 98 |
99 |
100 |
101 |
102 | 141 |
142 |
143 | 159 | 163 | 164 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Search — upho 0.6.0 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 34 | 35 |
36 |
37 |
38 |
39 | 40 |

Search

41 | 42 |
43 | 44 |

45 | Please activate JavaScript to enable the search 46 | functionality. 47 |

48 |
49 | 50 | 51 |

52 | Searching for multiple words only shows matches that contain 53 | all words. 54 |

55 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 |
66 | 67 |
68 | 69 | 70 |
71 |
72 |
73 |
74 | 78 |
79 |
80 | 90 | 94 | 95 | -------------------------------------------------------------------------------- /docs/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({docnames:["index","output","projection","visualization"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["index.rst","output.rst","projection.rst","visualization.rst"],objects:{},objnames:{},objtypes:{},terms:{"0":[1,3],"01":3,"05":3,"1":[1,3],"10":[1,3],"101":1,"12":1,"2":[1,2,3],"2017":1,"24305":1,"3":[1,3],"36":1,"4":3,"5":1,"6":1,"8":3,"95":1,"default":1,"function":0,"k\u00f6rmann":1,A:1,For:3,The:[1,3],Then:3,There:2,To:3,a1:3,a2:3,abl:3,accord:3,actual:1,ad:3,align:2,all:1,alloi:1,along:1,ar:1,arm:1,arrai:1,associ:1,atom:1,b1:3,b2:3,b:1,band:3,band_point:1,band_sf:3,band_sf_thz_w_bar:3,begin:2,behavior:1,calcul:1,can:[2,3],carrera:1,caus:2,cell:1,chemic:[0,1],co:1,colormap_p:3,com:3,command:3,comput:1,conf:1,consid:1,content:0,contribut:3,coordin:1,correspond:1,cross:2,cset:3,ctype:3,d:3,d_freq:3,d_sf:3,dagger:2,data:3,dataset:[1,3],dataspac:3,datatyp:3,decompos:3,depend:[1,2],determin:1,disord:1,doe:2,download:3,downloaded_path:3,due:1,duplic:1,each:1,eigenst:1,end:2,equal:1,equiv:2,equival:1,euqival:1,exampl:3,f:1,f_max:3,f_min:3,find:3,first:[1,3],focu:3,follow:3,format:3,found:3,fpitch:3,fraction:1,from:[1,3],gather:2,gener:2,github:3,given:1,grabowski:1,group:1,h5dump:3,h5t_c_s1:3,h5t_cset_ascii:3,h5t_str_nullpad:3,h5t_string:3,h:1,hat:2,have:3,hdf5:3,here:1,hermann:1,http:3,i:1,ideal:1,ikeda:1,index:0,input:1,instead:2,integ:1,introduc:1,ir_label:3,irreduc:1,irrep:3,json:1,k:2,know:3,label:1,latest:3,lattic:1,left:2,len:1,like:3,littl:1,longer:2,lorentzian:3,m:1,mai:[2,3],mater:1,mathbf:2,mauguin:1,mayb:1,mesh:3,mm2:3,model:1,modul:0,natoms_p:1,need:3,neg:2,nelm:1,nfreq:1,none:1,notat:1,note:3,npath:1,npj:1,number:1,o:1,obtain:0,one:[1,3],onli:1,onto:1,oper:1,option:3,p:2,page:0,pair:1,partial:1,path:3,pdf:3,ph_plotter:3,phonon:1,phy:1,pleas:3,plot:3,plot_styl:3,point:3,pointgroup_symbol:3,possibl:[1,2,3],primit:1,project:[0,1],purpos:1,py:[1,3],q:1,r:3,reciproc:1,releas:3,represent:[1,3],rev:1,right:2,run:3,s:3,scalar:[1,3],scheme:2,script:3,search:0,seko:1,selected_irrep:3,set:1,sever:2,sf:3,sf_max:3,sf_min:3,sf_with:3,shape:1,simpl:3,sluiter:1,small:3,some:3,space:1,specif:1,spectral:0,sr:3,star:1,strpad:3,strsize:3,sym:1,symbol:1,symmetr:1,symmetri:1,tanaka:1,term:2,test:1,textrm:2,them:3,thi:[1,2,3],tild:2,togo:1,tool:3,total:3,unfold:1,upho:3,upho_sf:3,us:[1,3],v:2,valu:1,visual:0,w:1,w_:2,we:3,weight:1,where:1,which:2,would:3,x:2,xx:2,y:1,you:3,yuzie007:3},titles:["Welcome to upho\u2019s documentation!","Output files","Projection","Visualization of obtained spectral functions"],titleterms:{"function":3,band:1,chemic:2,distanc:1,document:0,e1:2,e2:2,element:1,file:1,frequenc:1,hdf5:1,indic:0,ir_label:1,natoms_primit:1,num_arm:1,num_irrep:1,obtain:3,output:1,path:1,point:1,pointgroup_symbol:1,project:2,q_star:1,s:0,spectral:3,tabl:0,upho:0,visual:3,weights_:1,weights_e2:1,weights_s_:1,weights_t:1,welcom:0}}) -------------------------------------------------------------------------------- /docsrc/index.rst: -------------------------------------------------------------------------------- 1 | .. upho documentation master file, created by 2 | sphinx-quickstart on Wed Feb 15 12:50:34 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to upho's documentation! 7 | ================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | output_files 15 | projection 16 | visualization 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | 25 | -------------------------------------------------------------------------------- /docsrc/output.rst: -------------------------------------------------------------------------------- 1 | ############ 2 | Output files 3 | ############ 4 | 5 | ``band.hdf5`` 6 | ============= 7 | 8 | .. code-block:: console 9 | 10 | band.hdf5 11 | ├── paths (Dataset {3, 101, 3}) 12 | ├── 0 (Group) 13 | │ ├── 0 (Group) 14 | │ ├── 1 (Group) 15 | │ │ ├── point (Dataset {3}) 16 | │ │ ├── q_star (Dataset {6, 3}) 17 | │ │ ├── distance (Dataset {SCALAR}) 18 | │ │ ├── natoms_primitive (Dataset {SCALAR}) 19 | │ │ ├── elements (Dataset {2}) 20 | │ │ ├── num_arms (Dataset {SCALAR}) 21 | │ │ ├── pointgroup_symbol (Dataset {SCALAR}) 22 | │ │ ├── num_irreps (Dataset {SCALAR}) 23 | │ │ ├── ir_labels (Dataset {10}) 24 | │ │ ├── frequencies (Dataset {6, 12}) 25 | │ │ ├── weights_t (Dataset {6, 12}) 26 | │ │ ├── weights_e (Dataset {6, 1, 2, 1, 2, 12}) 27 | │ │ ├── weights_s (Dataset {6, 5, 12}) 28 | │ │ ├── weights_s_e (Dataset {6, 5, 1, 2, 1, 2, 12}) 29 | │ │ └── weights_e2 (Dataset {6, 1, 2, 12}) 30 | │ ├── 2 (Group) 31 | │ . 32 | │ . 33 | │ . 34 | │ 35 | ├── 1 (Group) 36 | │ ├── 0 (Group) 37 | │ ├── 1 (Group) 38 | │ ├── 2 (Group) 39 | │ . 40 | │ . 41 | │ . 42 | │ 43 | ├── 2 (Group) 44 | │ ├── 0 (Group) 45 | │ ├── 1 (Group) 46 | │ ├── 2 (Group) 47 | │ . 48 | │ . 49 | │ . 50 | 51 | 52 | ``paths`` 53 | --------- 54 | 55 | Array with the shape of ``(NPATHS, BAND_POINTS, 3)``. 56 | 57 | Q-point sets and the shape of band paths. 58 | 59 | ``NPATHS`` corresponds to the number of 3-integer sets in ``BAND`` in ``band.conf``. 60 | 61 | ``BAND_POINTS`` corresponds to that in ``band.conf``. 62 | 63 | Each group corresponds to one of the q-points given here. 64 | 65 | ``point`` 66 | --------- 67 | 68 | Reciprocal space point in fractional coordinates to be computed. 69 | 70 | ``q_star`` 71 | ---------- 72 | 73 | Star (symmetrically equivalent set of points) of ``point`` in the ideal lattice considered in the calculations. 74 | The actual behavior depends on the value of ``star`` in ``input.json``. 75 | 76 | - ``sym`` (default): Symmetrically euqivalent q-points, w/o duplications, are considered. 77 | - ``all``: Symmetrically equivalent q-points, possibly duplicated due to symmetry operations. Maybe used for testing purposes. 78 | - ``none``: Only the given q-point is considered. 79 | 80 | ``distance`` 81 | ------------ 82 | 83 | Distance of the considered reciprocal-space point from the first one along the band paths in fractional coordinates. 84 | 85 | ``natoms_primitive`` 86 | -------------------- 87 | 88 | The number of atoms in the primitive cell of the ideal lattice. (``natoms_p`` in ``eigenstates.py``.) 89 | 90 | ``elements`` 91 | ------------ 92 | 93 | Chemical elements in the disordered-alloy model. 94 | The number of the chemical elements are ``nelms`` in ``eigenstates.py``. 95 | 96 | ``num_arms`` 97 | ------------ 98 | 99 | The number of the arms of the star. 100 | Specifically this equals to ``len(q_star)``. 101 | 102 | ``pointgroup_symbol`` 103 | --------------------- 104 | 105 | Point group symbol in the Hermann–Mauguin notation associated with the little co-group of the considered reciprocal-space point. 106 | 107 | ``num_irreps`` 108 | -------------- 109 | 110 | The number of irreducible representations of the little co-group. 111 | 112 | ``ir_labels`` 113 | ------------- 114 | 115 | Labels of the irreducible representations. 116 | 117 | ``frequencies`` 118 | --------------- 119 | 120 | Array with the shape of ``(num_arms, nfreqs)``, where ``nfreqs`` is equal to ``3 * natoms_primitive``. 121 | 122 | Phonon frequencies projected onto this q-point. 123 | 124 | ``weights_t`` 125 | ------------- 126 | 127 | Array with the shape of ``(num_arms, nfreqs)``. 128 | 129 | Weights of the phonon frequencies determined by the band unfolding. 130 | 131 | ``weights_e`` 132 | ------------- 133 | 134 | Array with the shape of ``(num_arms, natoms_p, nelms, natoms_p, nelms, nfreqs)``. 135 | 136 | Partial weights for chemical pairs. 137 | 138 | ``weights_s`` 139 | ------------- 140 | 141 | Array with the shape of ``(num_arms, num_irreps, nfreqs)``. 142 | 143 | Partial weights for irreducible representations. 144 | Introduced in 145 | `[Y. Ikeda, A. Carreras, A. Seko, A. Togo, and I. Tanaka, Phys. Rev. B 95, 24305 (2017) 146 | `_]. 147 | 148 | ``weights_s_e`` 149 | --------------- 150 | 151 | Array with the shape of 152 | ``(num_arms, num_irreps, natoms_p, nelms, natoms_p, nelms, nfreqs)``. 153 | 154 | Partial weights for irreducible representations and for chemical pairs. 155 | 156 | ``weights_e2`` 157 | -------------- 158 | 159 | Array with the shape of 160 | ``(num_arms, natoms_p, nelms, nfreqs)``. 161 | 162 | Partial weights for chemical elements. Introduced in 163 | [`F. Körmann, Y. Ikeda, B. Grabowski, and M. H. F. Sluiter, Npj Comput. Mater. 3, 36 (2017) 164 | `_]. 165 | -------------------------------------------------------------------------------- /docsrc/projection.rst: -------------------------------------------------------------------------------- 1 | Projection 2 | ========== 3 | 4 | Chemical projection 5 | ------------------- 6 | 7 | There may be several possible schemes for the chemical projection. 8 | 9 | E1 10 | ^^ 11 | 12 | .. math:: 13 | :nowrap: 14 | 15 | \begin{align} 16 | w_{\mathbf{k}, \textrm{XX}'} 17 | &= 18 | \left( 19 | \hat{P}^{\mathbf{k}} 20 | \hat{P}^{\textrm{X}} 21 | \tilde{\mathbf{v}} 22 | \right)^{\dagger} 23 | \left( 24 | \hat{P}^{\mathbf{k}} 25 | \hat{P}^{\textrm{X}'} 26 | \tilde{\mathbf{v}} 27 | \right) 28 | \end{align} 29 | 30 | Generally this scheme causes cross terms, which can be negative. 31 | 32 | E2 33 | ^^ 34 | 35 | .. math:: 36 | :nowrap: 37 | 38 | \begin{gather} 39 | w_{\mathbf{k}} 40 | = 41 | \left| 42 | \hat{P}^{\mathbf{k}} 43 | \tilde{\mathbf{v}} 44 | \right|^{2} 45 | \\ 46 | w_{\textrm{X}} 47 | = 48 | \left| 49 | \hat{P}^{\textrm{X}} 50 | \tilde{\mathbf{v}} 51 | \right|^{2} 52 | \\ 53 | 54 | w_{\mathbf{k}, \textrm{X}} 55 | \equiv 56 | w_{\mathbf{k}} 57 | w_{\textrm{X}} 58 | \end{gather} 59 | 60 | This scheme does not cause cross terms. 61 | Instead, :math:`w_{\mathbf{k}, \textrm{X}'}/w_{\mathbf{k}, \textrm{X}}` does no longer depend on 62 | :math:`\mathbf{k}`. 63 | -------------------------------------------------------------------------------- /docsrc/visualization.rst: -------------------------------------------------------------------------------- 1 | Visualization of obtained spectral functions 2 | ============================================ 3 | 4 | You may be able to use the plotting tools in 5 | https://github.com/yuzie007/ph_plotter/releases. 6 | Please download the latest one. 7 | To use the tools, you need to run upho_sf with the option **--format hdf5** 8 | and obtain **sf.hdf5**. :: 9 | 10 | /path/to/upho/scripts/upho_sf --fpitch 0.01 -s 0.05 --function lorentzian --format hdf5 11 | 12 | Then run:: 13 | 14 | DOWNLOADED_PATH/ph_plotter/scripts/band_sf.py --sf_with irreps --plot_style mesh --sf_max 0.8 --sf_min 0 --d_sf 0.2 --f_max 10 --f_min 0 --d_freq 2 --colormap_p r 15 | 16 | Then the total spectral functions may be plotted as **band_sf_THz_w_bar.pdf**. 17 | 18 | To plot decomposed spectral functions according to small representations, 19 | we first need to know **pointgroup_symbol** and **ir_labels**. 20 | For example, if you would like to focus on the point 2 on the band path 1, 21 | The pointgroup_symbol of this point is found from the following command: :: 22 | 23 | % h5dump -d 1/2/pointgroup_symbol sf.hdf5 24 | HDF5 "sf.hdf5" { 25 | DATASET "1/2/pointgroup_symbol" { 26 | DATATYPE H5T_STRING { 27 | STRSIZE 3; 28 | STRPAD H5T_STR_NULLPAD; 29 | CSET H5T_CSET_ASCII; 30 | CTYPE H5T_C_S1; 31 | } 32 | DATASPACE SCALAR 33 | DATA { 34 | (0): "mm2" 35 | } 36 | } 37 | } 38 | 39 | Then we find the pointgroup_symbol for this point is “mm2”. 40 | The SRs for this point is found as, :: 41 | 42 | % h5dump -d 1/2/ir_labels sf.hdf5 43 | HDF5 "sf.hdf5" { 44 | DATASET "1/2/ir_labels" { 45 | DATATYPE H5T_STRING { 46 | STRSIZE 2; 47 | STRPAD H5T_STR_NULLPAD; 48 | CSET H5T_CSET_ASCII; 49 | CTYPE H5T_C_S1; 50 | } 51 | DATASPACE SIMPLE { ( 4 ) / ( 4 ) } 52 | DATA { 53 | (0): "A1", "A2", "B1", "B2" 54 | } 55 | } 56 | } 57 | 58 | Then we find “A1”, “A2”, “B1”, and “B2” is the possible SRs for this point. 59 | Note that some of them can have no contribution to the spectral function. 60 | 61 | The decomposed spectral functions for B1 for the pointgroup_symbol of mm2 may be plotted by adding **—selected_irreps '{"mm2":["B1”]}’** for the plotting command as, :: 62 | 63 | DOWNLOADED_PATH/ph_plotter/scripts/band_sf.py --plot_style mesh --sf_max 0.8 --sf_min 0 --d_sf 0.2 --f_max 10 --f_min 0 --d_freq 2 --colormap_p r --selected_irreps '{"mm2":["B1"]}' 64 | 65 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/FORCE_SETS: -------------------------------------------------------------------------------- 1 | 32 2 | 2 3 | 4 | 1 5 | 0.0070710678118655 0.0070710678118655 0.0000000000000000 6 | -0.0267240600 -0.0512541500 0.0000000000 7 | 0.0002421300 -0.0002515700 0.0000000000 8 | -0.0004353200 0.0016685700 0.0000000000 9 | -0.0001026500 0.0000775800 0.0000000000 10 | -0.0004365700 -0.0005195200 0.0000000000 11 | -0.0001036100 -0.0000231700 0.0000000000 12 | -0.0000846500 0.0002888100 0.0000000000 13 | -0.0002957800 0.0001227100 0.0000000000 14 | -0.0010516600 -0.0001623200 0.0000000000 15 | 0.0081427000 0.0072795600 0.0000000000 16 | 0.0085932100 0.0077277700 0.0000000000 17 | -0.0010269800 -0.0001879900 0.0000000000 18 | 0.0002525100 0.0001376600 0.0000000000 19 | 0.0002303900 0.0003450000 0.0000000000 20 | 0.0002307500 0.0003425400 0.0000000000 21 | 0.0002529900 0.0001408200 0.0000000000 22 | 0.0037012900 -0.0007809600 -0.0038945000 23 | 0.0036239500 -0.0007511000 0.0037823900 24 | 0.0002429800 0.0003315800 -0.0001034800 25 | 0.0002403600 0.0003288400 0.0001020100 26 | 0.0037012900 -0.0007809600 0.0038945000 27 | 0.0036239500 -0.0007511000 -0.0037823900 28 | 0.0002429800 0.0003315800 0.0001034800 29 | 0.0002403600 0.0003288400 -0.0001020100 30 | -0.0015222700 0.0087584800 0.0100838300 31 | 0.0007318700 0.0001644400 0.0001589700 32 | -0.0015954700 0.0089159600 -0.0103267500 33 | 0.0007356000 0.0001666200 -0.0001632100 34 | -0.0015222700 0.0087584800 -0.0100838300 35 | 0.0007318700 0.0001644400 -0.0001589700 36 | -0.0015954700 0.0089159600 0.0103267500 37 | 0.0007356000 0.0001666200 0.0001632100 38 | 39 | 25 40 | 0.0100000000000000 0.0000000000000000 0.0000000000000000 41 | -0.0022076900 -0.0000246700 -0.0000246700 42 | 0.0010385800 0.0000007500 0.0000007500 43 | -0.0022076900 0.0000246700 -0.0000246700 44 | 0.0010385800 -0.0000007500 0.0000007500 45 | -0.0022076900 -0.0000246700 0.0000246700 46 | 0.0010385800 0.0000007500 -0.0000007500 47 | -0.0022076900 0.0000246700 0.0000246700 48 | 0.0010385800 -0.0000007500 -0.0000007500 49 | 0.0126812300 -0.0000000000 0.0147001100 50 | 0.0123153600 -0.0000000000 -0.0141685900 51 | 0.0002264400 0.0000000000 0.0002287600 52 | 0.0002333700 0.0000000000 -0.0002292800 53 | 0.0126812300 -0.0000000000 -0.0147001100 54 | 0.0123153600 0.0000000000 0.0141685900 55 | 0.0002264400 0.0000000000 -0.0002287600 56 | 0.0002333700 0.0000000000 0.0002292800 57 | 0.0126812300 0.0147001100 -0.0000000000 58 | 0.0123153600 -0.0141685900 -0.0000000000 59 | 0.0126812300 -0.0147001100 -0.0000000000 60 | 0.0123153600 0.0141685900 -0.0000000000 61 | 0.0002264400 0.0002287600 -0.0000000000 62 | 0.0002333700 -0.0002292800 -0.0000000000 63 | 0.0002264400 -0.0002287600 -0.0000000000 64 | 0.0002333700 0.0002292800 -0.0000000000 65 | -0.0983508500 0.0000000000 -0.0000000000 66 | 0.0072327100 -0.0000000000 0.0000000000 67 | -0.0015024300 0.0000000000 -0.0000000000 68 | -0.0015556900 0.0000000000 -0.0000000000 69 | -0.0015024300 0.0000000000 -0.0000000000 70 | -0.0015556900 0.0000000000 -0.0000000000 71 | 0.0001331600 0.0000000000 -0.0000000000 72 | -0.0000479400 0.0000000000 -0.0000000000 73 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/POSCAR: -------------------------------------------------------------------------------- 1 | Cu Au 2 | 1.00000000000000 3 | 3.7530000000000001 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 3.7530000000000001 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 3.7530000000000001 6 | Cu Au 7 | 3 1 8 | Direct 9 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 10 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 11 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 12 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 13 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/POSCAR_ideal: -------------------------------------------------------------------------------- 1 | X 2 | 1.00000000000000 3 | 3.7530000000000001 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 3.7530000000000001 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 3.7530000000000001 6 | X 7 | 4 8 | Direct 9 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 10 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 11 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 12 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 13 | 14 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/README.md: -------------------------------------------------------------------------------- 1 | # Example for an ordered alloy 2 | 3 | Here we consider the hypothetical case when Cu3Au with the L12 structure is regarded as a random configuration of the A1 (fcc) structure. You can find the input files in the `examples` directory. 4 | 5 | 1. Create `FORCE_SETS` file for the structure (maybe including disordered chemical configuration) 6 | you want to investigate using ``phonopy`` in a usual way. 7 | Be careful that the number of the structures with atomic displacements to get `FORCE_SETS` can be huge (>100) 8 | for a disordered configuration. 9 | 10 | 2. Create `FORCE_CONSTANTS` file from `FORCE_SETS` file using `phonopy` as 11 | ``` 12 | phonopy writefc.conf 13 | ``` 14 | where `writefc.conf` is a text file like 15 | ``` 16 | FORCE_CONSTANTS = WRITE 17 | DIM = 2 2 2 18 | ``` 19 | ``DIM`` must be the same as that what you used to get `FORCE_SETS`. 20 | 21 | 3. Prepare two VASP-POSCAR-type files, `POSCAR` and `POSCAR_ideal`. 22 | POSCAR includes the original chemical configuration, which may be disordered. 23 | ``` 24 | Cu Au 25 | 1.00000000000000 26 | 3.7530000000000001 0.0000000000000000 0.0000000000000000 27 | 0.0000000000000000 3.7530000000000001 0.0000000000000000 28 | 0.0000000000000000 0.0000000000000000 3.7530000000000001 29 | Cu Au 30 | 3 1 31 | Direct 32 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 33 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 34 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 35 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 36 | ``` 37 | Note that although `FORCE_CONSTANTS` may be obtained using relaxed atomic positions, 38 | here the positions must be the ideal ones. 39 | 40 | `POSCAR_ideal` is the ideal configuration, from which the crystallographic symmetry is extracted. 41 | ``` 42 | X 43 | 1.00000000000000 44 | 3.7530000000000001 0.0000000000000000 0.0000000000000000 45 | 0.0000000000000000 3.7530000000000001 0.0000000000000000 46 | 0.0000000000000000 0.0000000000000000 3.7530000000000001 47 | X 48 | 4 49 | Direct 50 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 51 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 52 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 53 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 54 | ``` 55 | In this file I recommend to use dummy symbols like 'X' to avoid confusion. 56 | 57 | 4. Prepare ``band.conf`` file including something like 58 | ``` 59 | DIM = 2 2 2 60 | PRIMITIVE_AXIS = 0 1/2 1/2 1/2 0 1/2 1/2 1/2 0 61 | BAND = 0 0 0 0 1/2 1/2, 1 1/2 1/2 0 0 0 1/2 1/2 1/2 62 | BAND_POINTS = 101 63 | BAND_LABELS = \Gamma X \Gamma L 64 | FORCE_CONSTANTS = READ 65 | ``` 66 | The style is very similar to that of phonopy conf files, but be careful about the following tags. 67 | 68 | - `DIM` describes the expansion from the original POSCAR to the POSCARs with atomic displacements used to get `FORCE_SETS`. 69 | Therefore, this should be the same as the phonopy option when creating the structures with atomic displacements (1). 70 | 71 | - `PRIMITIVE_AXIS` is the conversion matrix from `POSCAR_ideal` to the primitive cell you expect. 72 | 73 | **Since v0.6.2:** We can also use `BAND = AUTO` like 74 | ``` 75 | DIM = 2 2 2 76 | PRIMITIVE_AXIS = 0 1/2 1/2 1/2 0 1/2 1/2 1/2 0 77 | BAND = AUTO 78 | BAND_POINTS = 101 79 | FORCE_CONSTANTS = READ 80 | ``` 81 | Internally, this uses [SeeK-path](https://seekpath.readthedocs.io/en/latest/) via phonopy. 82 | 83 | 4. Run 84 | ``` 85 | upho_weights band.conf 86 | ``` 87 | You hopefully get `band.hdf5` file. Note that this file can be in the order of GB. 88 | 89 | 5. Run 90 | ``` 91 | upho_sf --fpitch 0.01 -s 0.05 --function lorentzian --format text 92 | ``` 93 | You hopefully get `sf_E1.dat`, `sf_E2.dat`, and `sf_SR.dat` files. 94 | In these files: 95 | - `1st column`: distance in the reciprocal space 96 | - `2nd column`: frequencies 97 | - `3rd column`: values of spectral functions 98 | 99 | Further 100 | 101 | - `sf_E1.dat` has the element-pair-resolved spectral functions. 102 | - `sf_E2.dat` has the element-resolved spectral functions. 103 | - `sf_SR.dat` has the spectral functions decomposed by the small representations. 104 | 105 | 6. Plot the spectral functions. You can refer to `plot.py` in the example directory. Hopefully you get the figure like below: 106 | 107 | ![](sf.orig.svg) 108 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/band.conf: -------------------------------------------------------------------------------- 1 | DIM = 2 2 2 2 | PRIMITIVE_AXIS = 0 1/2 1/2 1/2 0 1/2 1/2 1/2 0 3 | BAND = 0 0 0 0 1/2 1/2, 1 1/2 1/2 0 0 0 1/2 1/2 1/2 4 | BAND_POINTS = 101 5 | BAND_LABELS = \Gamma X \Gamma L 6 | FORCE_CONSTANTS = READ 7 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | 6 | def main(): 7 | distances, frequencies, sfs = np.loadtxt( 8 | "sf_SR.dat", usecols=(0, 1, 2), unpack=True 9 | ) 10 | n2 = len(np.unique(frequencies)) 11 | distances = distances.reshape((-1, n2)) 12 | frequencies = frequencies.reshape((-1, n2)) 13 | sfs = sfs.reshape((-1, n2)) 14 | 15 | fig, ax = plt.subplots() 16 | ax.pcolormesh(distances, frequencies, sfs, rasterized=True) 17 | fig.savefig("sf.svg") 18 | 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /examples/00.L12_Cu3Au/writefc.conf: -------------------------------------------------------------------------------- 1 | FORCE_CONSTANTS = WRITE 2 | DIM = 2 2 2 3 | -------------------------------------------------------------------------------- /examples/01.A1_Cu0.75Au0.25/README.md: -------------------------------------------------------------------------------- 1 | # Example for a disordered-alloy model 2 | 3 | Here we consider a bit more practical case when Cu0.75Au0.25 with the A1 (FCC) structure is modeled using a supercell model. 4 | 5 | 1. Prepare two VASP-POSCAR-type files, `POSCAR` and `POSCAR_ideal`. 6 | 7 | `POSCAR` includes the original chemical configuration, which may be disordered. 8 | ``` 9 | Cu Au 10 | 1.0000000000000000 11 | 11.2590000000000003 0.0000000000000000 0.0000000000000000 12 | 0.0000000000000000 11.2590000000000003 0.0000000000000000 13 | 0.0000000000000000 0.0000000000000000 11.2590000000000003 14 | Cu Au 15 | 81 27 16 | Direct 17 | 0.0005745466020530 0.0006622413878287 -0.0000462844433759 18 | 0.6680892494574818 -0.0013629019985120 0.3295756612905684 19 | 0.8277767934221298 0.1643677436873781 -0.0008634774543910 20 | 0.6665436022106727 0.1669710199760064 0.1658642070302083 21 | ... 22 | ``` 23 | 24 | `POSCAR_ideal` is the ideal configuration, from which the crystallographic symmetry is extracted. 25 | ``` 26 | X 27 | 1.0000000000000000 28 | 11.2590000000000003 0.0000000000000000 0.0000000000000000 29 | 0.0000000000000000 11.2590000000000003 0.0000000000000000 30 | 0.0000000000000000 0.0000000000000000 11.2590000000000003 31 | X 32 | 108 33 | Direct 34 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 35 | 0.0000000000000000 0.1666666666666667 0.1666666666666667 36 | 0.1666666666666667 0.0000000000000000 0.1666666666666667 37 | 0.1666666666666667 0.1666666666666667 0.0000000000000000 38 | ... 39 | ``` 40 | In this file I recommend using dummy symbols like `X` to avoid confusion. 41 | 42 | 2. Create `FORCE_CONSTANTS` file using phonopy etc. 43 | The script `pre1.py` contains how to do using phonopy Python API together with ASE. 44 | 45 | 3. Prepare ``band.conf`` file including something like 46 | ``` 47 | DIM = 1 1 1 48 | PRIMITIVE_AXIS = 0 1/6 1/6 1/6 0 1/6 1/6 1/6 0 49 | BAND = 0 0 0 0 1/2 1/2, 1 1/2 1/2 0 0 0 1/2 1/2 1/2 50 | BAND_POINTS = 101 51 | BAND_LABELS = \Gamma X \Gamma L 52 | FORCE_CONSTANTS = READ 53 | ``` 54 | The style is very similar to that of phonopy conf files, but be careful about the following tags. 55 | 56 | - `DIM` describes the expansion from `POSCAR` to the POSCARs with atomic displacements used to get `FORCE_SETS` and/or `FORCE_CONSTANTS`. 57 | In the present case, since `POSCAR` is already a 108-atom supercell model and since `FORCE_CONSTANTS` corresponds to this supercell model, 58 | we should give `DIM = 1 1 1`. 59 | 60 | - `PRIMITIVE_AXIS` is the conversion matrix from `POSCAR_ideal` to the primitive cell you expect. 61 | In the present case, we need to map from the 108-atom supercell to the 1-atom primitive cell, this tag should be as given above. 62 | 63 | We can also use `PRIMITIVE_AXES = AUTO` and `BAND = AUTO` like 64 | ``` 65 | PRIMITIVE_AXES = AUTO 66 | BAND = AUTO 67 | BAND_POINTS = 101 68 | FORCE_CONSTANTS = READ 69 | ``` 70 | Internally, this uses [SeeK-path](https://seekpath.readthedocs.io/en/latest/) via phonopy. 71 | 72 | 4. Run 73 | ``` 74 | upho_weights band.conf 75 | ``` 76 | You hopefully get `band.hdf5` file. Note that this file can be in the order of GB. 77 | 78 | 5. Run 79 | ``` 80 | upho_sf --fpitch 0.01 -s 0.05 --function lorentzian --format text 81 | ``` 82 | You hopefully get `sf_E1.dat`, `sf_E2.dat`, and `sf_SR.dat` files. 83 | In these files: 84 | - `1st column`: distance in the reciprocal space 85 | - `2nd column`: frequencies 86 | - `3rd column`: values of spectral functions 87 | 88 | Further 89 | 90 | - `sf_E1.dat` has the element-pair-resolved spectral functions. 91 | - `sf_E2.dat` has the element-resolved spectral functions. 92 | - `sf_SR.dat` has the spectral functions decomposed by the small representations. 93 | 94 | 6. Plot the spectral functions. You can refer to `plot.py` in this directory. 95 | Hopefully you get the figure like below: 96 | 97 | ![](sf.orig.svg) 98 | -------------------------------------------------------------------------------- /examples/01.A1_Cu0.75Au0.25/band.conf: -------------------------------------------------------------------------------- 1 | PRIMITIVE_AXIS = AUTO 2 | BAND = AUTO 3 | BAND_POINTS = 51 4 | FORCE_CONSTANTS = READ 5 | -------------------------------------------------------------------------------- /examples/01.A1_Cu0.75Au0.25/plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | 6 | def main(): 7 | distances, frequencies, sfs = np.loadtxt( 8 | "sf_SR.dat", usecols=(0, 1, 2), unpack=True 9 | ) 10 | n2 = len(np.unique(frequencies)) 11 | distances = distances.reshape((-1, n2)) 12 | frequencies = frequencies.reshape((-1, n2)) 13 | sfs = sfs.reshape((-1, n2)) 14 | 15 | fig, ax = plt.subplots() 16 | ax.pcolormesh(distances, frequencies, sfs, rasterized=True) 17 | fig.savefig("sf.svg") 18 | 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /examples/01.A1_Cu0.75Au0.25/pre0.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from ase.build import bulk 3 | from ase.calculators.emt import EMT 4 | from ase.optimize import BFGS 5 | 6 | # Ideal atomic positions with dummy species. 7 | atoms = bulk("X", "fcc", a=3.753, cubic=True) * 3 8 | atoms.write("POSCAR_ideal", direct=True) 9 | 10 | # Replace 3/4 of atoms with Cu and 1/4 with Au randomly. 11 | # (In real researches we should use more sophisticated method like SQS.) 12 | rng = np.random.default_rng(42) 13 | indices = np.arange(len(atoms)) 14 | rng.shuffle(indices) 15 | atoms.symbols[indices[: 3 * len(atoms) // 4]] = "Cu" 16 | atoms.symbols[indices[3 * len(atoms) // 4 :]] = "Au" 17 | 18 | # Relax atomic positions. 19 | atoms.calc = EMT() 20 | with BFGS(atoms) as dyn: 21 | dyn.run(fmax=1e-6) 22 | 23 | # Sort by symbols. 24 | atoms = atoms[atoms.numbers.argsort()] 25 | 26 | atoms.write("POSCAR", direct=True) 27 | 28 | # Fill with the dummy symbol. 29 | atoms.symbols = len(atoms) * ["X"] 30 | -------------------------------------------------------------------------------- /examples/01.A1_Cu0.75Au0.25/pre1.py: -------------------------------------------------------------------------------- 1 | from ase import Atoms 2 | from ase.calculators.emt import EMT 3 | from ase.io import read 4 | from phonopy import Phonopy 5 | from phonopy.structure.atoms import PhonopyAtoms 6 | from phonopy.file_IO import write_FORCE_CONSTANTS 7 | 8 | atoms_ref = read("POSCAR") 9 | 10 | sets_of_forces = [] 11 | unitcell = PhonopyAtoms( 12 | symbols=atoms_ref.symbols, 13 | cell=atoms_ref.cell, 14 | scaled_positions=atoms_ref.get_scaled_positions(), 15 | ) 16 | phonon = Phonopy(unitcell) 17 | phonon.generate_displacements(distance=0.03) 18 | supercells = phonon.supercells_with_displacements 19 | for i, phonopy_atoms in enumerate(supercells): 20 | print(i) 21 | atoms = Atoms( 22 | symbols=phonopy_atoms.symbols, 23 | cell=phonopy_atoms.cell, 24 | pbc=True, 25 | scaled_positions=phonopy_atoms.scaled_positions, 26 | ) 27 | atoms.calc = EMT() 28 | sets_of_forces.append(atoms.get_forces()) 29 | phonon.forces = sets_of_forces 30 | phonon.produce_force_constants() 31 | write_FORCE_CONSTANTS(phonon.force_constants) 32 | -------------------------------------------------------------------------------- /group/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/group/__init__.py -------------------------------------------------------------------------------- /group/mathtools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import (absolute_import, division, 4 | print_function, unicode_literals) 5 | 6 | __author__ = "Yuji Ikeda" 7 | 8 | from functools import reduce 9 | import numpy as np 10 | 11 | 12 | def gcd2(a, b): 13 | while b: 14 | a, b = b, a % b 15 | return a 16 | 17 | 18 | def lcm2(a, b): 19 | return (a * b) // gcd2(a, b) 20 | 21 | 22 | def gcd(*numbers): 23 | return reduce(gcd2, numbers) 24 | 25 | 26 | def lcm(*numbers): 27 | return reduce(lcm2, numbers) 28 | 29 | 30 | def similarity_transformation(rot, mat): 31 | """ R x M x R^-1 """ 32 | return np.dot(rot, np.dot(mat, np.linalg.inv(rot))) 33 | -------------------------------------------------------------------------------- /group/test_mathtools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import (absolute_import, division, 4 | print_function, unicode_literals) 5 | 6 | __author__ = "Yuji Ikeda" 7 | 8 | from mathtools import gcd, lcm 9 | import unittest 10 | 11 | 12 | class TestMathtools(unittest.TestCase): 13 | def setUp(self): 14 | self._integers = [36, 120, 24] 15 | self._gcms = [ 16 | [ 36, 12, 12], 17 | [ 12, 120, 24], 18 | [ 12, 24, 24], 19 | ] 20 | self._lcms = [ 21 | [ 36, 360, 72], 22 | [360, 120, 120], 23 | [ 72, 120, 24], 24 | ] 25 | 26 | # def test_0(self): 27 | # self.assertEqual(gcd(), 0) 28 | # self.assertEqual(lcm(), 0) 29 | 30 | def test_1(self): 31 | for i in self._integers: 32 | self.assertEqual(gcd(i), i) 33 | self.assertEqual(lcm(i), i) 34 | 35 | def test_2(self): 36 | for i, a in enumerate(self._integers): 37 | for j, b in enumerate(self._integers): 38 | self.assertEqual(gcd(a, b), self._gcms[i][j]) 39 | self.assertEqual(lcm(a, b), self._lcms[i][j]) 40 | self.assertEqual(gcd(a, b) * lcm(a, b), a * b) 41 | 42 | def test_3(self): 43 | a, b, c = self._integers 44 | self.assertEqual(gcd(a, b, c), 12) 45 | self.assertEqual(lcm(a, b, c), 360) 46 | 47 | 48 | if __name__ == "__main__": 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /scripts/run_separation: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import subprocess 4 | from upho.file_io import read_band_hdf5 5 | 6 | 7 | def main(): 8 | ph_unfolder = "~/codes/upho/scripts/upho" 9 | str_command = "python -m cProfile " 10 | str_command += ph_unfolder 11 | str_command += " band.conf -i input.json" 12 | 13 | n1, n2 = read_band_hdf5("band.hdf5")["paths"].shape[:2] 14 | root = os.getcwd() 15 | for i1 in range(n1): 16 | for i2 in range(n2): 17 | dirname = "{}/{}/".format(i1, i2) 18 | os.chdir(dirname) 19 | print(dirname) 20 | with open("band.log", "w") as f: 21 | subprocess.call(str_command, stdout=f, shell=True) 22 | os.chdir(root) 23 | 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /scripts/separation: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import h5py 4 | import numpy as np 5 | from phonopy.cui.settings import PhonopyConfParser 6 | from phonopy.interface.vasp import read_vasp 7 | from phonopy.structure.cells import get_primitive 8 | from upho.file_io import read_input 9 | 10 | 11 | class Separation: 12 | def __init__(self, conf_file, input_file, structure_ideal): 13 | self._conf_file = conf_file 14 | self._input_file = input_file 15 | 16 | phonopy_conf = PhonopyConfParser(filename=self._conf_file, 17 | option_list=[]) 18 | self._settings = phonopy_conf.get_settings() 19 | 20 | self._distance = 0. 21 | 22 | self.create_primitive_cell(structure_ideal) 23 | 24 | self._paths = np.array(self._settings.get_bands()) 25 | print(self._paths) 26 | 27 | self.analyze_paths() 28 | 29 | self.write_hdf5() 30 | 31 | self.write_input() 32 | 33 | def analyze_paths(self): 34 | self.create_distances() 35 | 36 | def create_primitive_cell(self, poscar_file): 37 | primitive_matrix = self._settings.get_primitive_matrix() 38 | atoms = read_vasp(poscar_file) 39 | self._cell = get_primitive(atoms, primitive_matrix) 40 | 41 | def _set_initial_point(self, qpoint): 42 | self._lastq = qpoint.copy() 43 | 44 | def _shift_point(self, qpoint): 45 | self._distance += np.linalg.norm( 46 | np.dot(qpoint - self._lastq, 47 | np.linalg.inv(self._cell.get_cell()).T)) 48 | self._lastq = qpoint.copy() 49 | 50 | def write_input(self): 51 | import json 52 | 53 | if self._input_file is not None: 54 | dict_input = read_input(self._input_file) 55 | else: 56 | dict_input = {} 57 | 58 | dict_input["run_mode"] = "single_point" 59 | for ipath, path in enumerate(self._paths): 60 | for iq, qpoint in enumerate(path): 61 | dict_input["qpoint"] = list(qpoint) 62 | dict_input["distance"] = self._distances[ipath, iq] 63 | 64 | dirname = '{}/{}/'.format(ipath, iq) 65 | if not os.path.isdir(dirname): 66 | os.makedirs(dirname) 67 | filename = "{}input.json".format(dirname) 68 | with open(filename, "w") as f: 69 | f.write(json.dumps(dict_input, indent=4)) 70 | 71 | files = [ 72 | "POSCAR", 73 | "POSCAR_ideal", 74 | "FORCE_CONSTANTS", 75 | self._conf_file, 76 | ] 77 | 78 | os.chdir(dirname) 79 | for f in files: 80 | if os.path.isfile(f): 81 | os.remove(f) 82 | os.symlink("../../" + f, f) 83 | os.chdir("../../") 84 | 85 | def create_distances(self): 86 | distances = [] 87 | for path in self._paths: 88 | self._set_initial_point(path[0]) 89 | distances_on_path = [] 90 | for i, q in enumerate(path): 91 | self._shift_point(q) 92 | distances_on_path.append(self._distance) 93 | distances.append(distances_on_path) 94 | distances = np.array(distances) 95 | print(distances) 96 | self._distances = distances 97 | 98 | def write_hdf5(self): 99 | with h5py.File('band.hdf5', 'w') as w: 100 | w.create_dataset('paths', data=self._paths) 101 | 102 | # Create groups using ExternalLink 103 | for ipath, path in enumerate(self._paths): 104 | for ip, point in enumerate(path): 105 | group = '{}/{}/'.format(ipath, ip) 106 | w[group] = h5py.ExternalLink(group + 'point.hdf5', '/') 107 | 108 | 109 | def main(): 110 | import argparse 111 | parser = argparse.ArgumentParser() 112 | parser.add_argument("-i", "--input_file", 113 | type=str, 114 | help="Input file for unfolding.") 115 | parser.add_argument("conf_file", 116 | type=str, 117 | help="Phonopy conf file") 118 | args = parser.parse_args() 119 | 120 | Separation(args.conf_file, args.input_file, structure_ideal="POSCAR_ideal") 121 | 122 | if __name__ == "__main__": 123 | main() 124 | -------------------------------------------------------------------------------- /scripts/upho_fit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from upho.phonon.sf_fitter import SFFitter 3 | 4 | 5 | def main(): 6 | import argparse 7 | parser = argparse.ArgumentParser() 8 | # parser.add_argument("-f", "--filenames", 9 | # nargs="+", 10 | # type=str, 11 | # required=True, 12 | # help="filenames for fitting") 13 | # parser.add_argument("--mode_band", 14 | # action="store_true", 15 | # help="Fitting for each band") 16 | parser.add_argument('--function', 17 | type=str, 18 | default='gaussian', 19 | choices=['gaussian', 'lorentzian'], 20 | help="Fitting function") 21 | args = parser.parse_args() 22 | 23 | SFFitter(name=args.function) 24 | 25 | 26 | if __name__ == "__main__": 27 | main() 28 | -------------------------------------------------------------------------------- /scripts/upho_qpoints: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import numpy as np 3 | from upho.qpoints.qpoints_creator import QpointsCreator 4 | 5 | 6 | def main(): 7 | import argparse 8 | parser = argparse.ArgumentParser() 9 | # http://people.sc.fsu.edu/~jburkardt/datasets/sphere_lebedev_rule/sphere_lebedev_rule.html 10 | parser.add_argument( 11 | '--lebedev', 12 | default=6, 13 | type=int, 14 | help='Number of Lebedev mesh points') 15 | parser.add_argument( 16 | '--rmin', 17 | default=0.0, 18 | type=float, 19 | help='Maximum radius in fractional coordinates') 20 | parser.add_argument( 21 | '--rmax', 22 | default=1.0, 23 | type=float, 24 | help='Maximum radius in fractional coordinates') 25 | parser.add_argument( 26 | '--rstep', 27 | default=0.02, 28 | type=float, 29 | help='Step of radii in fractional coordinates') 30 | args = parser.parse_args() 31 | 32 | nr = int(round(args.rmax - args.rmin) / args.rstep) + 1 33 | radii = np.linspace(args.rmin, args.rmax, nr) 34 | 35 | QpointsCreator(radii=radii, lebedev=args.lebedev).write() 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /scripts/upho_sf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | 4 | 5 | def main(): 6 | parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) 7 | parser.add_argument("-f", "--filename", 8 | default="band.hdf5", 9 | type=str, 10 | help="Filename for the weights data.") 11 | parser.add_argument('--format', 12 | default='hdf5', 13 | type=str, 14 | choices=['hdf5', 'text'], 15 | help='Output file format.') 16 | parser.add_argument("--function", 17 | default="gaussian", 18 | type=str, 19 | choices=["gaussian", "lorentzian", "histogram"], 20 | help="Function used for the smearing.") 21 | parser.add_argument("-s", "--sigma", 22 | default=0.1, 23 | type=float, 24 | help="Parameter for the smearing function (THz).\n" 25 | "For Gaussian, this is the standard deviation.\n" 26 | "For Lorentzian, this is the HWHM (gamma).") 27 | parser.add_argument("--fmax", 28 | default=10.0, 29 | type=float, 30 | help="Maximum frequency (THz).") 31 | parser.add_argument("--fmin", 32 | default=-2.5, 33 | type=float, 34 | help="Minimum frequency (THz).") 35 | parser.add_argument("--fpitch", 36 | default=0.05, 37 | type=float, 38 | help="Frequency pitch (THz).") 39 | parser.add_argument("--squared", dest='is_squared', 40 | action='store_true', 41 | help="Use raw frequencies instead of Squared frequencies.") 42 | parser.add_argument("-g", "--group", 43 | type=str, 44 | help="Group (point) to plot.") 45 | args = parser.parse_args() 46 | 47 | if args.format == 'hdf5': 48 | from upho.phonon.density_extractor \ 49 | import DensityExtractorHDF5 as DensityExtractor 50 | elif args.format == 'text': 51 | from upho.phonon.density_extractor \ 52 | import DensityExtractorText as DensityExtractor 53 | else: 54 | raise ValueError('Invalid format {}'.format(args.format)) 55 | 56 | DensityExtractor( 57 | filename=args.filename, 58 | function=args.function, 59 | fmax=args.fmax, 60 | fmin=args.fmin, 61 | fpitch=args.fpitch, 62 | sigma=args.sigma, 63 | is_squared=args.is_squared, 64 | group=args.group, 65 | ) 66 | 67 | 68 | if __name__ == "__main__": 69 | main() 70 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Always prefer setuptools over distutils 2 | from setuptools import setup, find_packages 3 | import codecs 4 | import os.path 5 | 6 | 7 | def read(rel_path): 8 | here = os.path.abspath(os.path.dirname(__file__)) 9 | with codecs.open(os.path.join(here, rel_path), 'r') as fp: 10 | return fp.read() 11 | 12 | 13 | def get_version(rel_path): 14 | for line in read(rel_path).splitlines(): 15 | if line.startswith('__version__'): 16 | delim = '"' if '"' in line else "'" 17 | return line.split(delim)[1] 18 | else: 19 | raise RuntimeError("Unable to find version string.") 20 | 21 | 22 | scripts = [ 23 | 'scripts/upho_weights', 24 | 'scripts/upho_sf', 25 | 'scripts/upho_qpoints', 26 | 'scripts/upho_fit', 27 | ] 28 | setup( 29 | # This is the name of your project. The first time you publish this 30 | # package, this name will be registered for you. It will determine how 31 | # users can install this project, e.g.: 32 | # 33 | # $ pip install sampleproject 34 | # 35 | # And where it will live on PyPI: https://pypi.org/project/sampleproject/ 36 | # 37 | # There are some restrictions on what makes a valid project name 38 | # specification here: 39 | # https://packaging.python.org/specifications/core-metadata/#name 40 | name='upho', # Required 41 | 42 | # Versions should comply with PEP 440: 43 | # https://www.python.org/dev/peps/pep-0440/ 44 | # 45 | # For a discussion on single-sourcing the version across setup.py and the 46 | # project code, see 47 | # https://packaging.python.org/en/latest/single_source_version.html 48 | version=get_version('upho/__init__.py'), # Required 49 | 50 | # This should be a valid link to your project's main homepage. 51 | # 52 | # This field corresponds to the "Home-Page" metadata field: 53 | # https://packaging.python.org/specifications/core-metadata/#home-page-optional 54 | url='https://github.com/yuzie007/upho', # Optional 55 | 56 | # This should be your name or the name of the organization which owns the 57 | # project. 58 | author='Yuji Ikeda', # Optional 59 | 60 | # This should be a valid email address corresponding to the author listed 61 | # above. 62 | author_email='yuji.ikeda.ac.jp@gmail.com', # Optional 63 | 64 | # You can just specify package directories manually here if your project is 65 | # simple. Or you can use find_packages(). 66 | # 67 | # Alternatively, if you just want to distribute a single Python file, use 68 | # the `py_modules` argument instead as follows, which will expect a file 69 | # called `my_module.py` to exist: 70 | # 71 | # py_modules=["my_module"], 72 | # 73 | packages=find_packages(), # Required 74 | 75 | # This field lists other packages that your project depends on to run. 76 | # Any package you put here will be installed by pip when your project is 77 | # installed, so they must be valid existing projects. 78 | # 79 | # For an analysis of "install_requires" vs pip's requirements files see: 80 | # https://packaging.python.org/en/latest/requirements.html 81 | install_requires=['phonopy>=2.7.0'], # Optional 82 | 83 | scripts=scripts) 84 | -------------------------------------------------------------------------------- /tests/cui/test_settings.py: -------------------------------------------------------------------------------- 1 | import io 2 | import numpy as np 3 | from upho.cui.settings import _parse_phonopy_conf_strings 4 | 5 | 6 | class TestSettings: 7 | def test_comments(self): 8 | s0 = ''.join([ 9 | 'DIM = 2 2 2\n', 10 | 'PRIMITIVE_AXES = 0 1 1 1 0 1 1 1 0\n', 11 | ]) 12 | s1 = ''.join([ 13 | 'DIM = 2 2 2 # supercell\n', 14 | 'PRIMITIVE_AXES = 0 1 1 1 0 1 1 1 0\n', 15 | ]) 16 | with io.StringIO(s0) as f0: 17 | d0 = _parse_phonopy_conf_strings(f0.readlines()) 18 | with io.StringIO(s1) as f1: 19 | d1 = _parse_phonopy_conf_strings(f1.readlines()) 20 | for k0 in d0.keys(): 21 | assert np.all(d0[k0] == d1[k0]) 22 | 23 | def test_continuous_lines(self): 24 | s0 = ''.join([ 25 | 'DIM = 2 2 2\n', 26 | 'PRIMITIVE_AXES = 0 1 1 1 0 1 1 1 0\n', 27 | ]) 28 | s1 = ''.join([ 29 | 'DIM = +++\n', 30 | '2 2 2\n', 31 | 'PRIMITIVE_AXES = 0 1 1 1 0 1 1 1 0\n', 32 | ]) 33 | with io.StringIO(s0) as f0: 34 | d0 = _parse_phonopy_conf_strings(f0.readlines()) 35 | with io.StringIO(s1) as f1: 36 | d1 = _parse_phonopy_conf_strings(f1.readlines()) 37 | for k0 in d0.keys(): 38 | assert np.all(d0[k0] == d1[k0]) 39 | -------------------------------------------------------------------------------- /tests/group/test_group.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import (absolute_import, division, 4 | print_function, unicode_literals) 5 | 6 | __author__ = "Yuji Ikeda" 7 | 8 | import unittest 9 | import numpy as np 10 | from group.group import Group 11 | 12 | 13 | class TestGroup(unittest.TestCase): 14 | def test_1(self): 15 | Cayley_table = [ 16 | [0, 1], 17 | [1, 0], 18 | ] 19 | group = Group(Cayley_table) 20 | 21 | orders_of_conjugacy_classes = group.get_orders_of_conjugacy_classes() 22 | orders_of_conjugacy_classes_expected = [1, 1] 23 | self.assertListEqual( 24 | list(orders_of_conjugacy_classes), 25 | orders_of_conjugacy_classes_expected) 26 | 27 | 28 | def main(): 29 | Cayley_table = [ 30 | [0, 1, 2], 31 | [1, 2, 0], 32 | [2, 0, 1], 33 | ] 34 | run(Cayley_table) 35 | Cayley_table = [ 36 | [0, 1, 2, 3], 37 | [1, 0, 3, 2], 38 | [2, 3, 0, 1], 39 | [3, 2, 1, 0], 40 | ] 41 | run(Cayley_table) 42 | Cayley_table = [ 43 | [0, 1, 2, 3], 44 | [1, 2, 3, 0], 45 | [2, 3, 0, 1], 46 | [3, 0, 1, 2], 47 | ] 48 | run(Cayley_table) 49 | # Symmorphic group: S3 50 | Cayley_table = [ 51 | [0, 1, 2, 3, 4, 5], 52 | [1, 0, 4, 5, 2, 3], 53 | [2, 5, 0, 4, 3, 1], 54 | [3, 4, 5, 0, 1, 2], 55 | [4, 3, 1, 2, 5, 0], 56 | [5, 2, 3, 1, 0, 4], 57 | ] 58 | run(Cayley_table, subset=np.array([4])) 59 | 60 | print("Quaternion group") 61 | Cayley_table = [ 62 | [0, 1, 2, 3, 4, 5, 6, 7], 63 | [1, 0, 3, 2, 5, 4, 7, 6], 64 | [2, 3, 1, 0, 6, 7, 5, 4], 65 | [3, 2, 0, 1, 7, 6, 4, 5], 66 | [4, 5, 7, 6, 1, 0, 2, 3], 67 | [5, 4, 6, 7, 0, 1, 3, 2], 68 | [6, 7, 4, 5, 3, 2, 1, 0], 69 | [7, 6, 5, 4, 2, 3, 0, 1], 70 | ] 71 | run(Cayley_table) 72 | 73 | # Dihedral group: D8 74 | Cayley_table = [ 75 | [0, 1, 2, 3, 4, 5, 6, 7], 76 | [1, 2, 3, 0, 5, 6, 7, 4], 77 | [2, 3, 0, 1, 6, 7, 4, 5], 78 | [3, 0, 1, 2, 7, 4, 5, 6], 79 | [4, 7, 6, 5, 0, 3, 2, 1], 80 | [5, 4, 7, 6, 1, 0, 3, 2], 81 | [6, 5, 4, 7, 2, 1, 0, 3], 82 | [7, 6, 5, 4, 3, 2, 1, 0], 83 | ] 84 | run(Cayley_table) 85 | 86 | 87 | def run(Cayley_table, subset=None): 88 | Cayley_table = np.array(Cayley_table) 89 | group = Group(Cayley_table) 90 | print("identity:") 91 | print(group.get_identity()) 92 | print("center:") 93 | print(group.get_center()) 94 | print("orders_of_elements:") 95 | print(group.get_orders_of_elements()) 96 | print("exponent:") 97 | print(group.get_exponent()) 98 | print("conjugacy_classes:") 99 | print(group.get_conjugacy_classes()) 100 | print("commutator_subgroup:") 101 | print(group.get_commutator_subgroup()) 102 | print("crst:") 103 | print(group.get_crst()) 104 | if subset is not None: 105 | print("subset:") 106 | print(subset) 107 | group.create_centralizer(subset) 108 | group.create_normalizer(subset) 109 | print("centralizer:") 110 | print(group.get_centralizer()) 111 | print("normalizer:") 112 | print(group.get_normalizer()) 113 | 114 | 115 | if __name__ == "__main__": 116 | main() 117 | unittest.main() 118 | -------------------------------------------------------------------------------- /tests/irreps/test_character_tables.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | from upho.irreps.irreps import find_rotation_type 4 | from upho.irreps.character_tables import ( 5 | character_tables, find_rotation_type_from_class_label) 6 | 7 | 8 | pointgroup_symbols = ( 9 | '1' , '-1' , '2' , 'm' , '2/m' , 10 | '222' , 'mm2' , 'mmm' , '4' , '-4' , 11 | '4/m' , '422' , '4mm' , '-42m' , '4/mmm', 12 | '3' , '-3' , '32' , '3m' , '-3m' , 13 | '6' , '-6' , '6/m' , '622' , '6mm' , 14 | '-6m2', '6/mmm', '23' , 'm-3' , '432' , 15 | '-43m', 'm-3m' , 16 | ) 17 | 18 | 19 | class TestCharacterTables: 20 | def test_ir_labels_length(self): 21 | for pg in pointgroup_symbols: 22 | print("{:6s}:".format(pg), end=" ") 23 | current_max = 0 24 | if pg in character_tables: 25 | ir_labels = character_tables[pg]["ir_labels"] 26 | current_max = max(max(len(s) for s in ir_labels), current_max) 27 | print(ir_labels, current_max) 28 | 29 | is_orthogonal = self.is_orthogonal( 30 | character_tables[pg]["character_table"]) 31 | assert is_orthogonal 32 | else: 33 | print("Not implemented yet.") 34 | assert current_max == 3 35 | 36 | @staticmethod 37 | def is_orthogonal(character_table): 38 | character_table = np.array(character_table) 39 | for i0, v0 in enumerate(character_table.T): 40 | for i1, v1 in enumerate(character_table.T): 41 | if i0 != i1 and not np.isclose(np.dot(v0, np.conj(v1)), 0.0): 42 | print(i0, i1, np.dot(v0, v1)) 43 | return False 44 | return True 45 | 46 | @pytest.mark.parametrize('pg', pointgroup_symbols) 47 | def test_class_to_rotations_list(self, pg): 48 | k = 'class_to_rotations_list' 49 | if k not in character_tables[pg]: 50 | return 51 | class_to_rotations_list = character_tables[pg][k] 52 | for class_to_rotations in class_to_rotations_list: 53 | for rotation_label, rotations in class_to_rotations.items(): 54 | tmp0 = find_rotation_type_from_class_label(rotation_label) 55 | for r in rotations: 56 | tmp1 = find_rotation_type(r) 57 | assert rotation_label and tmp0 == tmp1 58 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_A13: -------------------------------------------------------------------------------- 1 | Mn 2 | 1.00000000000000 3 | 6.0069999999999997 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 6.0069999999999997 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 6.0069999999999997 6 | Mn 7 | 20 8 | Direct 9 | 0.0590000000000000 0.0590000000000000 0.0590000000000000 10 | 0.5589999999999999 0.4410000000000000 -0.0590000000000000 11 | 0.4410000000000000 -0.0590000000000000 0.5589999999999999 12 | -0.0590000000000000 0.5589999999999999 0.4410000000000000 13 | 0.6910000000000001 0.6910000000000001 0.6910000000000001 14 | 0.1910000000000000 0.8089999999999999 0.3090000000000000 15 | 0.3090000000000000 0.1910000000000000 0.8089999999999999 16 | 0.8089999999999999 0.3090000000000000 0.1910000000000000 17 | 0.1250000000000000 0.1900000000000000 0.4400000000000000 18 | 0.1900000000000000 0.4400000000000000 0.1250000000000000 19 | 0.4400000000000000 0.1250000000000000 0.1900000000000000 20 | 0.3750000000000000 -0.1900000000000000 0.9399999999999999 21 | -0.1900000000000000 0.9399999999999999 0.3750000000000000 22 | 0.9399999999999999 0.3750000000000000 -0.1900000000000000 23 | 0.6250000000000000 0.3100000000000000 0.5600000000000001 24 | 0.3100000000000000 0.5600000000000001 0.6250000000000000 25 | 0.5600000000000001 0.6250000000000000 0.3100000000000000 26 | 0.8750000000000000 0.6899999999999999 0.0600000000000000 27 | 0.6899999999999999 0.0600000000000000 0.8750000000000000 28 | 0.0600000000000000 0.8750000000000000 0.6899999999999999 29 | 30 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_A3: -------------------------------------------------------------------------------- 1 | Al 2 | 1.00000000000000 3 | 1.4318912319027588 -2.4801083645679678 0.0000000000000000 4 | 1.4318912319027588 2.4801083645679678 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 4.6765371804359690 6 | Al 7 | 2 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.6666666666666666 0.3333333333333333 0.5000000000000000 11 | 12 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_A4_conv: -------------------------------------------------------------------------------- 1 | C 2 | 1.00000000000000 3 | 3.5699999999999998 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 3.5699999999999998 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 3.5699999999999998 6 | C 7 | 8 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 11 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 12 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 13 | 0.2500000000000000 0.2500000000000000 0.2500000000000000 14 | 0.2500000000000000 0.7500000000000000 0.7500000000000000 15 | 0.7500000000000000 0.2500000000000000 0.7500000000000000 16 | 0.7500000000000000 0.7500000000000000 0.2500000000000000 17 | 18 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_A_h: -------------------------------------------------------------------------------- 1 | Po 2 | 1.00000000000000 3 | 3.3500000000000001 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 3.3500000000000001 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 3.3500000000000001 6 | Po 7 | 1 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 11 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_B3_conv: -------------------------------------------------------------------------------- 1 | Zn S 2 | 1.00000000000000 3 | 1.0000000000000000 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 1.0000000000000000 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 1.0000000000000000 6 | Zn S 7 | 4 4 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 11 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 12 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 13 | 0.2500000000000000 0.2500000000000000 0.2500000000000000 14 | 0.2500000000000000 0.7500000000000000 0.7500000000000000 15 | 0.7500000000000000 0.2500000000000000 0.7500000000000000 16 | 0.7500000000000000 0.7500000000000000 0.2500000000000000 17 | 18 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_Cl12Pd6: -------------------------------------------------------------------------------- 1 | Cl12Pd6 2 | 1.00000000000000 3 | 6.5235500000000002 -11.2991200457159486 0.0000000000000000 4 | 6.5235500000000002 11.2991200457159486 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 8.6016999999999992 6 | Cl Pd 7 | 36 18 8 | Direct 9 | 0.0233500000000000 0.2597500000000000 0.0004500000000000 10 | -0.2597500000000000 -0.2364000000000000 0.0004500000000000 11 | 0.2364000000000000 -0.0233500000000000 0.0004500000000000 12 | -0.0233500000000000 -0.2597500000000000 -0.0004500000000000 13 | 0.2597500000000000 0.2364000000000000 -0.0004500000000000 14 | -0.2364000000000000 0.0233500000000000 -0.0004500000000000 15 | 0.6900166666666666 0.5930833333333333 0.3337833333333333 16 | 0.4069166666666666 0.0969333333333333 0.3337833333333333 17 | 0.9030666666666666 0.3099833333333333 0.3337833333333333 18 | 0.6433166666666666 0.0735833333333333 0.3328833333333333 19 | 0.9264166666666667 0.5697333333333333 0.3328833333333333 20 | 0.4302666666666667 0.3566833333333333 0.3328833333333333 21 | 0.3566833333333333 0.9264166666666667 0.6671166666666666 22 | 0.0735833333333333 0.4302666666666667 0.6671166666666666 23 | 0.5697333333333333 0.6433166666666666 0.6671166666666666 24 | 0.3099833333333333 0.4069166666666666 0.6662166666666667 25 | 0.5930833333333333 0.9030666666666666 0.6662166666666667 26 | 0.0969333333333333 0.6900166666666666 0.6662166666666667 27 | 0.0951800000000000 0.1665600000000000 0.3126500000000000 28 | -0.1665600000000000 -0.0713800000000000 0.3126500000000000 29 | 0.0713800000000000 -0.0951800000000000 0.3126500000000000 30 | -0.0951800000000000 -0.1665600000000000 -0.3126500000000000 31 | 0.1665600000000000 0.0713800000000000 -0.3126500000000000 32 | -0.0713800000000000 0.0951800000000000 -0.3126500000000000 33 | 0.7618466666666667 0.4998933333333333 0.6459833333333334 34 | 0.5001066666666666 0.2619533333333333 0.6459833333333334 35 | 0.7380466666666666 0.2381533333333333 0.6459833333333334 36 | 0.5714866666666666 0.1667733333333333 0.0206833333333333 37 | 0.8332266666666667 0.4047133333333333 0.0206833333333333 38 | 0.5952866666666666 0.4285133333333333 0.0206833333333333 39 | 0.4285133333333333 0.8332266666666667 0.9793166666666666 40 | 0.1667733333333333 0.5952866666666666 0.9793166666666666 41 | 0.4047133333333333 0.5714866666666666 0.9793166666666666 42 | 0.2381533333333333 0.5001066666666666 0.3540166666666666 43 | 0.4998933333333333 0.7380466666666666 0.3540166666666666 44 | 0.2619533333333333 0.7618466666666667 0.3540166666666666 45 | 0.1672000000000000 0.0719600000000000 0.1591000000000000 46 | -0.0719600000000000 0.0952400000000000 0.1591000000000000 47 | -0.0952400000000000 -0.1672000000000000 0.1591000000000000 48 | -0.1672000000000000 -0.0719600000000000 -0.1591000000000000 49 | 0.0719600000000000 -0.0952400000000000 -0.1591000000000000 50 | 0.0952400000000000 0.1672000000000000 -0.1591000000000000 51 | 0.8338666666666666 0.4052933333333333 0.4924333333333333 52 | 0.5947066666666666 0.4285733333333333 0.4924333333333333 53 | 0.5714266666666666 0.1661333333333333 0.4924333333333333 54 | 0.4994666666666666 0.2613733333333333 0.1742333333333333 55 | 0.7386266666666667 0.2380933333333333 0.1742333333333333 56 | 0.7619066666666666 0.5005333333333333 0.1742333333333333 57 | 0.5005333333333333 0.7386266666666667 0.8257666666666666 58 | 0.2613733333333333 0.7619066666666666 0.8257666666666666 59 | 0.2380933333333333 0.4994666666666666 0.8257666666666666 60 | 0.1661333333333333 0.5947066666666666 0.5075666666666666 61 | 0.4052933333333333 0.5714266666666666 0.5075666666666666 62 | 0.4285733333333333 0.8338666666666666 0.5075666666666666 63 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_H3S: -------------------------------------------------------------------------------- 1 | I222 H3S 2 | 1.00000000000000 3 | 5.8246300000000000 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 7.2440400000000000 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 7.7033100000000001 6 | S H 7 | 8 24 8 | Direct 9 | -0.2399500000000000 -0.1784000000000000 0.3648800000000000 10 | 0.2399500000000000 0.1784000000000000 0.3648800000000000 11 | 0.2399500000000000 -0.1784000000000000 -0.3648800000000000 12 | -0.2399500000000000 0.1784000000000000 -0.3648800000000000 13 | 0.2600500000000000 0.3216000000000000 0.8648800000000000 14 | 0.7399500000000000 0.6784000000000000 0.8648800000000000 15 | 0.7399500000000000 0.3216000000000000 0.1351200000000000 16 | 0.2600500000000000 0.6784000000000000 0.1351200000000000 17 | -0.2452500000000000 0.4925500000000000 0.2040500000000000 18 | 0.2452500000000000 -0.4925500000000000 0.2040500000000000 19 | 0.2452500000000000 0.4925500000000000 -0.2040500000000000 20 | -0.2452500000000000 -0.4925500000000000 -0.2040500000000000 21 | 0.2547500000000000 0.9925500000000000 0.7040500000000001 22 | 0.7452500000000000 0.0074500000000000 0.7040500000000001 23 | 0.7452500000000000 0.9925500000000000 0.2959500000000000 24 | 0.2547500000000000 0.0074500000000000 0.2959500000000000 25 | 0.4421000000000000 0.2322300000000000 0.2861600000000000 26 | -0.4421000000000000 -0.2322300000000000 0.2861600000000000 27 | -0.4421000000000000 0.2322300000000000 -0.2861600000000000 28 | 0.4421000000000000 -0.2322300000000000 -0.2861600000000000 29 | 0.9420999999999999 0.7322299999999999 0.7861600000000000 30 | 0.0579000000000000 0.2677700000000000 0.7861600000000000 31 | 0.0579000000000000 0.7322299999999999 0.2138400000000000 32 | 0.9420999999999999 0.2677700000000000 0.2138400000000000 33 | 0.0000000000000000 0.5000000000000000 0.0485100000000000 34 | 0.0000000000000000 0.5000000000000000 -0.0485100000000000 35 | 0.5000000000000000 1.0000000000000000 0.5485100000000001 36 | 0.5000000000000000 1.0000000000000000 0.4514900000000000 37 | 0.0000000000000000 0.5000000000000000 0.4515300000000000 38 | 0.0000000000000000 0.5000000000000000 -0.4515300000000000 39 | 0.5000000000000000 1.0000000000000000 0.9515300000000000 40 | 0.5000000000000000 1.0000000000000000 0.0484700000000000 41 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_L1_2: -------------------------------------------------------------------------------- 1 | Cu Au 2 | 1.00000000000000 3 | 1.0000000000000000 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 1.0000000000000000 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 1.0000000000000000 6 | Au Cu 7 | 1 3 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 11 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 12 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 13 | 14 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_MgNH: -------------------------------------------------------------------------------- 1 | Comment 2 | 1.0 3 | 5.7898 -10.028227765662285 0.0 4 | 5.7898 10.028227765662285 0.0 5 | 0.0 0.0 3.6811 6 | Mg N H 7 | 12 12 12 8 | Direct 9 | 0.584 0.8677 0.0 10 | -0.8677 -0.28370000000000006 0.0 11 | 0.28370000000000006 -0.584 0.0 12 | -0.584 -0.8677 0.0 13 | 0.8677 0.28370000000000006 0.0 14 | -0.28370000000000006 0.584 0.0 15 | 0.9285 0.5721 0.5 16 | -0.5721 0.35639999999999994 0.5 17 | -0.35639999999999994 -0.9285 0.5 18 | -0.9285 -0.5721 0.5 19 | 0.5721 -0.35639999999999994 0.5 20 | 0.35639999999999994 0.9285 0.5 21 | 0.6517 0.7279 0.0 22 | -0.7279 -0.07620000000000005 0.0 23 | 0.07620000000000005 -0.6517 0.0 24 | -0.6517 -0.7279 0.0 25 | 0.7279 0.07620000000000005 0.0 26 | -0.07620000000000005 0.6517 0.0 27 | 0.4954 0.8633 0.5 28 | -0.8633 -0.36789999999999995 0.5 29 | 0.36789999999999995 -0.4954 0.5 30 | -0.4954 -0.8633 0.5 31 | 0.8633 0.36789999999999995 0.5 32 | -0.36789999999999995 0.4954 0.5 33 | 0.9402 0.745 0.0 34 | -0.745 0.19520000000000004 0.0 35 | -0.19520000000000004 -0.9402 0.0 36 | -0.9402 -0.745 0.0 37 | 0.745 -0.19520000000000004 0.0 38 | 0.19520000000000004 0.9402 0.0 39 | 0.4273 0.7666 0.5 40 | -0.7666 -0.33929999999999993 0.5 41 | 0.33929999999999993 -0.4273 0.5 42 | -0.4273 -0.7666 0.5 43 | 0.7666 0.33929999999999993 0.5 44 | -0.33929999999999993 0.4273 0.5 45 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_NaGdCu2F8: -------------------------------------------------------------------------------- 1 | NaGdCu2F8 2 | 1.00000000000000 3 | 5.4070000000000000 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 5.4070000000000000 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 10.3819999999999997 6 | Gd Na Cu F 7 | 2 2 4 16 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.5000000000000000 0.5000000000000000 0.5000000000000000 11 | 0.0000000000000000 0.0000000000000000 0.5000000000000000 12 | 0.5000000000000000 0.5000000000000000 1.0000000000000000 13 | 0.0000000000000000 0.5000000000000000 0.2500000000000000 14 | 0.5000000000000000 0.0000000000000000 0.2500000000000000 15 | 0.5000000000000000 1.0000000000000000 0.7500000000000000 16 | 1.0000000000000000 0.5000000000000000 0.7500000000000000 17 | 0.1872000000000000 0.3303000000000000 0.3763000000000000 18 | -0.1872000000000000 -0.3303000000000000 0.3763000000000000 19 | -0.3303000000000000 0.1872000000000000 0.3763000000000000 20 | 0.3303000000000000 -0.1872000000000000 0.3763000000000000 21 | -0.1872000000000000 0.3303000000000000 -0.3763000000000000 22 | 0.1872000000000000 -0.3303000000000000 -0.3763000000000000 23 | 0.3303000000000000 0.1872000000000000 -0.3763000000000000 24 | -0.3303000000000000 -0.1872000000000000 -0.3763000000000000 25 | 0.6872000000000000 0.8303000000000000 0.8763000000000001 26 | 0.3128000000000000 0.1697000000000000 0.8763000000000001 27 | 0.1697000000000000 0.6872000000000000 0.8763000000000001 28 | 0.8303000000000000 0.3128000000000000 0.8763000000000001 29 | 0.3128000000000000 0.8303000000000000 0.1237000000000000 30 | 0.6872000000000000 0.1697000000000000 0.1237000000000000 31 | 0.8303000000000000 0.6872000000000000 0.1237000000000000 32 | 0.1697000000000000 0.3128000000000000 0.1237000000000000 33 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_Sc: -------------------------------------------------------------------------------- 1 | Sc-V 2 | 1.00000000000000 3 | 1.1775000000000000 -2.0394898259123528 0.0000000000000000 4 | 1.1775000000000000 2.0394898259123528 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 10.4459999999999997 6 | Sc 7 | 6 8 | Direct 9 | 0.4610000000000000 0.0000000000000000 0.0000000000000000 10 | 0.4610000000000000 0.4610000000000000 0.1666666666666667 11 | 0.0000000000000000 0.4610000000000000 0.3333333333333333 12 | -0.4610000000000000 0.0000000000000000 0.5000000000000000 13 | -0.4610000000000000 -0.4610000000000000 0.6666666666666666 14 | 0.0000000000000000 -0.4610000000000000 0.8333333333333334 15 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_ThCl4: -------------------------------------------------------------------------------- 1 | alpha ThCl4 2 | 1.00000000000000 3 | 6.4080000000000004 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 6.4080000000000004 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 12.9239999999999995 6 | Th Cl 7 | 4 16 8 | Direct 9 | 0.0000000000000000 0.2500000000000000 0.1250000000000000 10 | 0.5000000000000000 0.2500000000000000 0.3750000000000000 11 | 0.5000000000000000 0.7500000000000000 0.6250000000000000 12 | 1.0000000000000000 0.7500000000000000 0.8750000000000000 13 | 0.3500000000000000 0.4940000000000000 0.2000000000000000 14 | 0.1500000000000000 -0.4940000000000000 0.7000000000000000 15 | 0.2560000000000000 0.6000000000000000 0.4500000000000000 16 | 1.2440000000000000 0.4000000000000000 0.9500000000000000 17 | -0.3500000000000000 -0.4940000000000000 -0.2000000000000000 18 | 0.8500000000000000 0.4940000000000000 0.3000000000000000 19 | 0.7440000000000000 0.4000000000000000 0.5500000000000000 20 | -0.2440000000000000 0.6000000000000000 0.0500000000000000 21 | 0.8500000000000000 0.9940000000000000 0.7000000000000000 22 | 0.6500000000000000 0.0060000000000000 1.2000000000000000 23 | 0.7560000000000000 1.1000000000000001 0.9500000000000000 24 | 1.7440000000000000 0.9000000000000000 1.4500000000000000 25 | 0.1500000000000000 0.0060000000000000 0.3000000000000000 26 | 1.3500000000000001 0.9940000000000000 0.8000000000000000 27 | 1.2440000000000000 0.9000000000000000 1.0500000000000000 28 | 0.2560000000000000 1.1000000000000001 0.5500000000000000 29 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_WC: -------------------------------------------------------------------------------- 1 | W C 2 | 1.00000000000000 3 | 1.4510000000000001 -2.5132057217824411 0.0000000000000000 4 | 1.4510000000000001 2.5132057217824411 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 2.8310000000000000 6 | W C 7 | 1 1 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.6666666666666666 0.3333333333333333 0.5000000000000000 11 | 12 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_fcc: -------------------------------------------------------------------------------- 1 | H 2 | 1.00000000000000 3 | 1.0000000000000000 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 1.0000000000000000 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 1.0000000000000000 6 | H 7 | 4 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 11 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 12 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 13 | 14 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_fcc_2x2x2: -------------------------------------------------------------------------------- 1 | H 2 | 1.00000000000000 3 | 2.0000000000000000 0.0000000000000000 0.0000000000000000 4 | 0.0000000000000000 2.0000000000000000 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 2.0000000000000000 6 | H 7 | 32 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.5000000000000000 0.0000000000000000 0.0000000000000000 11 | 0.0000000000000000 0.5000000000000000 0.0000000000000000 12 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 13 | 0.0000000000000000 0.0000000000000000 0.5000000000000000 14 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 15 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 16 | 0.5000000000000000 0.5000000000000000 0.5000000000000000 17 | 0.0000000000000000 0.2500000000000000 0.2500000000000000 18 | 0.5000000000000000 0.2500000000000000 0.2500000000000000 19 | 0.0000000000000000 0.7500000000000000 0.2500000000000000 20 | 0.5000000000000000 0.7500000000000000 0.2500000000000000 21 | 0.0000000000000000 0.2500000000000000 0.7500000000000000 22 | 0.5000000000000000 0.2500000000000000 0.7500000000000000 23 | 0.0000000000000000 0.7500000000000000 0.7500000000000000 24 | 0.5000000000000000 0.7500000000000000 0.7500000000000000 25 | 0.2500000000000000 0.0000000000000000 0.2500000000000000 26 | 0.7500000000000000 0.0000000000000000 0.2500000000000000 27 | 0.2500000000000000 0.5000000000000000 0.2500000000000000 28 | 0.7500000000000000 0.5000000000000000 0.2500000000000000 29 | 0.2500000000000000 0.0000000000000000 0.7500000000000000 30 | 0.7500000000000000 0.0000000000000000 0.7500000000000000 31 | 0.2500000000000000 0.5000000000000000 0.7500000000000000 32 | 0.7500000000000000 0.5000000000000000 0.7500000000000000 33 | 0.2500000000000000 0.2500000000000000 0.0000000000000000 34 | 0.7500000000000000 0.2500000000000000 0.0000000000000000 35 | 0.2500000000000000 0.7500000000000000 0.0000000000000000 36 | 0.7500000000000000 0.7500000000000000 0.0000000000000000 37 | 0.2500000000000000 0.2500000000000000 0.5000000000000000 38 | 0.7500000000000000 0.2500000000000000 0.5000000000000000 39 | 0.2500000000000000 0.7500000000000000 0.5000000000000000 40 | 0.7500000000000000 0.7500000000000000 0.5000000000000000 41 | 42 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_fcc_prim: -------------------------------------------------------------------------------- 1 | H 2 | 1.00000000000000 3 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 4 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 5 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 6 | H 7 | 1 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 11 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_fcc_prim_test: -------------------------------------------------------------------------------- 1 | H 2 | 1.00000000000000 3 | 0.0000000000000000 0.5000000000000000 0.5000000000000000 4 | 0.5000000000000000 0.0000000000000000 0.5000000000000000 5 | 0.5000000000000000 0.5000000000000000 0.0000000000000000 6 | H 7 | 1 8 | Direct 9 | 0.0000000000000000 1.0000000000000000 0.0000000000000000 10 | 11 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_omega_disordered_1: -------------------------------------------------------------------------------- 1 | Ti 2 | 1.00000000000000 3 | 1.2500000000000000 -2.1650635094610964 0.0000000000000000 4 | 1.2500000000000000 2.1650635094610964 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 2.5000000000000000 6 | Ti Zr 7 | 1 2 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.6666666666666666 0.3333333333333333 0.5000000000000000 11 | 0.3333333333333333 0.6666666666666666 0.5000000000000000 12 | 13 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_omega_disordered_2: -------------------------------------------------------------------------------- 1 | Ti 2 | 1.00000000000000 3 | 1.2500000000000000 -2.1650635094610964 0.0000000000000000 4 | 1.2500000000000000 2.1650635094610964 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 2.5000000000000000 6 | Ti Zr 7 | 2 1 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.6666666666666666 0.3333333333333333 0.5000000000000000 11 | 0.3333333333333333 0.6666666666666666 0.5000000000000000 12 | 13 | -------------------------------------------------------------------------------- /tests/poscars/POSCAR_omega_ideal: -------------------------------------------------------------------------------- 1 | H 2 | 1.00000000000000 3 | 1.2500000000000000 -2.1650635094610964 0.0000000000000000 4 | 1.2500000000000000 2.1650635094610964 0.0000000000000000 5 | 0.0000000000000000 0.0000000000000000 2.5000000000000000 6 | H 7 | 3 8 | Direct 9 | 0.0000000000000000 0.0000000000000000 0.0000000000000000 10 | 0.6666666666666666 0.3333333333333333 0.5000000000000000 11 | 0.3333333333333333 0.6666666666666666 0.5000000000000000 12 | 13 | -------------------------------------------------------------------------------- /tests/test_average_masses.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import numpy as np 4 | from phonopy.interface.vasp import read_vasp 5 | from phonopy.structure.symmetry import Symmetry 6 | from upho.api_unfolding import ( 7 | calculate_average_masses, calculate_mass_variances) 8 | 9 | POSCAR_DIR = os.path.join(os.path.dirname(__file__), 'poscars') 10 | 11 | 12 | def create_msg(list1, list2): 13 | msg = '' 14 | for x1, x2 in zip(list1, list2): 15 | msg += '\n{:12.6f}{:12.6f}'.format(x1, x2) 16 | return msg 17 | 18 | 19 | class TestAverageMasses(unittest.TestCase): 20 | def setUp(self): 21 | unitcell_ideal = read_vasp(os.path.join(POSCAR_DIR, 'POSCAR_omega_ideal')) 22 | self._symmetry = Symmetry(unitcell_ideal) 23 | self._prec = 1e-12 24 | 25 | def test_1(self): 26 | unitcell = read_vasp(os.path.join(POSCAR_DIR, 'POSCAR_omega_disordered_1')) 27 | masses = unitcell.get_masses() 28 | 29 | masses_average = calculate_average_masses(masses, self._symmetry) 30 | masses_expected = np.array([47.867, 91.224, 91.224]) 31 | is_same = (np.abs(masses_average - masses_expected) < self._prec).all() 32 | msg = create_msg(masses_average, masses_expected) 33 | self.assertTrue(is_same, msg=msg) 34 | 35 | mass_variances = calculate_mass_variances(masses, self._symmetry) 36 | mass_variances_expected = np.array([0.0, 0.0, 0.0]) 37 | is_same = (np.abs(mass_variances - mass_variances_expected) < self._prec).all() 38 | msg = create_msg(mass_variances, mass_variances_expected) 39 | self.assertTrue(is_same, msg=msg) 40 | 41 | def test_2(self): 42 | unitcell = read_vasp(os.path.join(POSCAR_DIR, 'POSCAR_omega_disordered_2')) 43 | masses = unitcell.get_masses() 44 | 45 | masses_average = calculate_average_masses(masses, self._symmetry) 46 | masses_expected = np.array([47.867, 69.5455, 69.5455]) 47 | is_same = (np.abs(masses_average - masses_expected) < self._prec).all() 48 | msg = create_msg(masses_average, masses_expected) 49 | self.assertTrue(is_same, msg=msg) 50 | 51 | mass_variances = calculate_mass_variances(masses, self._symmetry) 52 | mass_variances_expected = np.array([0.0, 469.95736225000013, 469.95736225000013]) 53 | is_same = (np.abs(mass_variances - mass_variances_expected) < self._prec).all() 54 | msg = create_msg(mass_variances, mass_variances_expected) 55 | self.assertTrue(is_same, msg=msg) 56 | 57 | # mass_scattering_factor: 58 | # ((47.867 - 69.5455) ** 2 + (91.224 - 69.5455) ** 2) * 0.5 / (69.5455 ** 2) 59 | # 0.09716735699807359 60 | 61 | if __name__ == "__main__": 62 | unittest.main() 63 | -------------------------------------------------------------------------------- /tests/test_element_weights_calculator.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import numpy as np 4 | from phonopy.interface.vasp import read_vasp 5 | from phonopy.structure.cells import get_primitive 6 | from upho.phonon.element_weights_calculator import ( 7 | ElementWeightsCalculator) 8 | 9 | POSCAR_DIR = os.path.join(os.path.dirname(__file__), 'poscars') 10 | 11 | 12 | class TestElementWeightsCalculator(unittest.TestCase): 13 | def test_A4(self): 14 | unitcell = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_A4_conv")) 15 | primitive_matrix = [ 16 | [0.0, 0.5, 0.5], 17 | [0.5, 0.0, 0.5], 18 | [0.5, 0.5, 0.0], 19 | ] 20 | primitive = get_primitive(unitcell, primitive_matrix) 21 | self.check(unitcell, primitive) 22 | 23 | def test_B3(self): 24 | unitcell = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_B3_conv")) 25 | primitive_matrix = [ 26 | [0.0, 0.5, 0.5], 27 | [0.5, 0.0, 0.5], 28 | [0.5, 0.5, 0.0], 29 | ] 30 | primitive = get_primitive(unitcell, primitive_matrix) 31 | self.check(unitcell, primitive) 32 | 33 | def test_L1_2(self): 34 | unitcell, unitcell_ideal, primitive_matrix = self.prepare_L1_2() 35 | primitive = get_primitive(unitcell_ideal, primitive_matrix) 36 | self.check(unitcell, primitive) 37 | 38 | def prepare_L1_2(self): 39 | unitcell = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_L1_2")) 40 | primitive_matrix = [ 41 | [0.0, 0.5, 0.5], 42 | [0.5, 0.0, 0.5], 43 | [0.5, 0.5, 0.0], 44 | ] 45 | unitcell_ideal = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_fcc")) 46 | return unitcell, unitcell_ideal, primitive_matrix 47 | 48 | def check(self, unitcell, primitive): 49 | ews_calculator = ElementWeightsCalculator( 50 | unitcell, primitive) 51 | print(ews_calculator.get_map_elements()) 52 | print(ews_calculator.get_map_atoms_u2p()) 53 | 54 | natoms_u = unitcell.get_number_of_atoms() 55 | ndims = 3 56 | nbands = 30 57 | 58 | vectors = np.random.rand(1, natoms_u * ndims, nbands).astype(complex) 59 | vectors += np.random.rand(1, natoms_u * ndims, nbands) * 1.0j 60 | 61 | weights = ews_calculator.run_star(vectors) 62 | weights_all = np.linalg.norm(vectors, axis=1) ** 2 63 | weights_sum = np.sum(weights, axis=(1, 2)) 64 | 65 | prec = 1e-9 66 | self.assertTrue(np.all(np.abs(weights_sum - weights_all) < prec)) 67 | print(weights) 68 | 69 | def test_project_vectors(self): 70 | unitcell, unitcell_ideal, primitive_matrix = self.prepare_L1_2() 71 | primitive = get_primitive(unitcell_ideal, primitive_matrix) 72 | 73 | natoms_u = unitcell.get_number_of_atoms() 74 | natoms_p = primitive.get_number_of_atoms() 75 | 76 | num_elements = 2 77 | ndims = 3 78 | nbands = 4 79 | vectors = np.arange(ndims * natoms_u * nbands).reshape(-1, nbands) 80 | 81 | elemental_projector = ElementWeightsCalculator(unitcell, primitive) 82 | projected_vectors = elemental_projector.project_vectors(vectors) 83 | print(projected_vectors) 84 | 85 | projected_vectors_expected = [ 86 | [ 87 | [0, 1, 2, 3], 88 | [4, 5, 6, 7], 89 | [8, 9, 10, 11], 90 | [0, 0, 0, 0], 91 | [0, 0, 0, 0], 92 | [0, 0, 0, 0], 93 | [0, 0, 0, 0], 94 | [0, 0, 0, 0], 95 | [0, 0, 0, 0], 96 | [0, 0, 0, 0], 97 | [0, 0, 0, 0], 98 | [0, 0, 0, 0], 99 | ], 100 | [ 101 | [0, 0, 0, 0], 102 | [0, 0, 0, 0], 103 | [0, 0, 0, 0], 104 | [12, 13, 14, 15], 105 | [16, 17, 18, 19], 106 | [20, 21, 22, 23], 107 | [24, 25, 26, 27], 108 | [28, 29, 30, 31], 109 | [32, 33, 34, 35], 110 | [36, 37, 38, 39], 111 | [40, 41, 42, 43], 112 | [44, 45, 46, 47], 113 | ], 114 | ] 115 | 116 | self.assertTrue( 117 | np.all(projected_vectors == projected_vectors_expected)) 118 | 119 | if __name__ == "__main__": 120 | unittest.main() 121 | -------------------------------------------------------------------------------- /tests/test_functions.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import numpy as np 3 | from upho.analysis.functions import lorentzian_unnormalized 4 | 5 | 6 | class TestFunctions(unittest.TestCase): 7 | def test_lorentzian_unnnormalized(self): 8 | # norms = np.linspace(0.0, 10.0, 101) 9 | norms = np.linspace(1.0, 1.0, 1) 10 | widths = np.linspace(0.01, 5.0, 500) 11 | # peak_positions = np.linspace(-2.0, 2.0, 401) 12 | peak_positions = np.linspace(0.0, 0.0, 1) 13 | xs = np.linspace(-500.0, 500.0, 100001) 14 | dx = xs[1] - xs[0] 15 | prec = 1e-2 16 | for peak_position in peak_positions: 17 | for width in widths: 18 | for norm in norms: 19 | ys = lorentzian_unnormalized(xs, peak_position, width, norm) 20 | norm_integration = np.sum(ys) * dx 21 | ratio = norm_integration / norm 22 | print('{:12.3f}'.format(peak_position ), end='') 23 | print('{:12.3f}'.format(width ), end='') 24 | print('{:12.6f}'.format(norm ), end='') 25 | print('{:12.6f}'.format(norm_integration), end='') 26 | print('{:12.6f}'.format(ratio ), end='') 27 | print() 28 | if not np.isnan(ratio): 29 | self.assertTrue(np.abs(ratio - 1.0) < prec) 30 | 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /tests/test_get_smallest_vectors.py: -------------------------------------------------------------------------------- 1 | import time 2 | import unittest 3 | import os 4 | import numpy as np 5 | from phonopy.interface.vasp import read_vasp 6 | from phonopy.structure.cells import get_smallest_vectors, get_primitive 7 | from upho.harmonic.dynamical_matrix import ( 8 | get_smallest_vectors as get_smallest_vectors_upho) 9 | 10 | POSCAR_DIR = os.path.join(os.path.dirname(__file__), 'poscars') 11 | 12 | 13 | # taken from phonopy 2.7.0 14 | def _get_smallest_vectors(supercell, primitive, symprec): 15 | p2s_map = primitive.p2s_map 16 | supercell_pos = supercell.scaled_positions 17 | primitive_pos = supercell_pos[p2s_map] 18 | supercell_bases = supercell.cell 19 | primitive_bases = primitive.cell 20 | svecs, multi = get_smallest_vectors( 21 | supercell_bases, supercell_pos, primitive_pos, symprec=symprec) 22 | trans_mat_float = np.dot(supercell_bases, np.linalg.inv(primitive_bases)) 23 | trans_mat = np.rint(trans_mat_float).astype(int) 24 | assert (np.abs(trans_mat_float - trans_mat) < 1e-8).all() 25 | svecs = np.array(np.dot(svecs, trans_mat), dtype='double', order='C') 26 | return svecs, multi 27 | 28 | 29 | class TestRotationalProjector(unittest.TestCase): 30 | def setUp(self): 31 | self._atoms = read_vasp(os.path.join(POSCAR_DIR, 'POSCAR_fcc_2x2x2')) 32 | self._primitive_matrix = np.array([ 33 | [0.00, 0.25, 0.25], 34 | [0.25, 0.00, 0.25], 35 | [0.25, 0.25, 0.00], 36 | ]) 37 | 38 | def test(self): 39 | natoms = self._atoms.get_number_of_atoms() 40 | primitive = get_primitive(self._atoms, self._primitive_matrix) 41 | symprec = 1e-6 42 | 43 | smallest_vectors0, multiplicity0 = _get_smallest_vectors( 44 | self._atoms, primitive, symprec) 45 | smallest_vectors1, multiplicity1 = get_smallest_vectors_upho( 46 | self._atoms, primitive, symprec) 47 | 48 | dt_old = 0.0 49 | dt_new = 0.0 50 | for i in range(natoms): 51 | for j in range(primitive.get_number_of_atoms()): 52 | t0 = time.time() 53 | tmp0 = smallest_vectors0[i, j, :multiplicity0[i][j]] 54 | t1 = time.time() 55 | dt_old += t1 - t0 56 | 57 | t0 = time.time() 58 | tmp1 = smallest_vectors1[i, j, :multiplicity1[i][j]] 59 | t1 = time.time() 60 | dt_new += t1 - t0 61 | 62 | print(tmp0) 63 | print(tmp1) 64 | self.assertTrue(np.array_equal(tmp0, tmp1)) 65 | print(dt_old) 66 | print(dt_new) 67 | 68 | 69 | if __name__ == "__main__": 70 | unittest.main() 71 | -------------------------------------------------------------------------------- /tests/test_mappings_modifier.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import numpy as np 3 | from upho.analysis.mappings_modifier import MappingsModifier 4 | 5 | 6 | class TestMappingsInverter(unittest.TestCase): 7 | def test_invert_mappings(self): 8 | mappings = [ 9 | [0, 1, 2, 3], 10 | [0, 2, 1, 3], 11 | [1, 2, 3, 0], 12 | ] 13 | mappings_inv_expected = [ 14 | [0, 1, 2, 3], 15 | [0, 2, 1, 3], 16 | [3, 0, 1, 2], 17 | ] 18 | 19 | mappings_inv = MappingsModifier(mappings).invert_mappings() 20 | self.assertTrue(np.all(mappings_inv == mappings_inv_expected)) 21 | 22 | def test_expand_mappings(self): 23 | mappings = [ 24 | [0, 1, 2, 3], 25 | [0, 2, 1, 3], 26 | [1, 2, 3, 0], 27 | ] 28 | n = 3 29 | expanded_mappings_expected = [ 30 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 31 | [0, 1, 2, 6, 7, 8, 3, 4, 5, 9, 10, 11], 32 | [3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2], # Good 33 | ] 34 | 35 | expanded_mappings = MappingsModifier(mappings).expand_mappings(n) 36 | self.assertTrue( 37 | np.all(expanded_mappings == expanded_mappings_expected)) 38 | 39 | def test_expand_mappings_inv(self): 40 | mappings = [ 41 | [0, 1, 2, 3], 42 | [0, 2, 1, 3], 43 | [1, 2, 3, 0], 44 | ] 45 | n = 3 46 | expanded_mappings_inv_expected = [ 47 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 48 | [0, 1, 2, 6, 7, 8, 3, 4, 5, 9, 10, 11], 49 | [9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8], 50 | ] 51 | 52 | mappings_modifier = MappingsModifier(mappings) 53 | expanded_mappings_inv = mappings_modifier.expand_mappings( 54 | n, is_inverse=True) 55 | self.assertTrue( 56 | np.all(expanded_mappings_inv == expanded_mappings_inv_expected)) 57 | 58 | def test_expand_mappings2(self): 59 | mappings = [ 60 | [ 61 | [0, 1, 2, 3], 62 | [0, 2, 1, 3], 63 | ], 64 | [ 65 | [1, 2, 3, 0], 66 | [2, 3, 0, 1], 67 | ], 68 | ] 69 | n = 3 70 | expanded_mappings_expected = [ 71 | [ 72 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 73 | [0, 1, 2, 6, 7, 8, 3, 4, 5, 9, 10, 11], 74 | ], 75 | [ 76 | [3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2], # Good 77 | [6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5], # Good 78 | ], 79 | ] 80 | 81 | expanded_mappings = MappingsModifier(mappings).expand_mappings(n) 82 | self.assertTrue( 83 | np.all(expanded_mappings == expanded_mappings_expected)) 84 | 85 | 86 | if __name__ == "__main__": 87 | unittest.main() 88 | -------------------------------------------------------------------------------- /tests/test_rotational_projector.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import numpy as np 4 | from upho.phonon.rotational_projector import RotationalProjector 5 | from phonopy.interface.vasp import read_vasp 6 | 7 | POSCAR_DIR = os.path.join(os.path.dirname(__file__), 'poscars') 8 | 9 | 10 | class TestRotationalProjector(unittest.TestCase): 11 | def setUp(self): 12 | self._vectors = np.random.rand(3, 100) + 1.0j * np.random.rand(3, 100) 13 | self._vectors = self._vectors[None] # Tests for arbitrary number of dimensions 14 | 15 | def load_sc(self): 16 | atoms = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_A_h")) 17 | self._rotational_projector = RotationalProjector(atoms) 18 | 19 | def load_fcc(self): 20 | atoms = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_fcc_prim_test")) 21 | # atoms = read_vasp("tests/poscars/POSCAR_fcc_prim") 22 | self._rotational_projector = RotationalProjector(atoms) 23 | 24 | def test_0(self): 25 | self.load_sc() 26 | self._kpoint = np.array([0.00, 0.00, 0.00]) 27 | self.check() 28 | 29 | def test_1(self): 30 | self.load_sc() 31 | self._kpoint = np.array([0.00, 0.25, 0.25]) 32 | self.check() 33 | 34 | def test_0_fcc(self): 35 | self.load_fcc() 36 | self._kpoint = np.array([0.00, 0.00, 0.00]) 37 | self.check() 38 | 39 | def test_1_fcc(self): 40 | self.load_fcc() 41 | self._kpoint = np.array([0.00, 0.25, 0.25]) 42 | self.check() 43 | 44 | def test_2_fcc(self): 45 | self.load_fcc() 46 | self._kpoint = np.array([0.50, 0.25, 0.25]) 47 | self.check() 48 | 49 | def test_3_fcc(self): 50 | self.load_fcc() 51 | self._kpoint = np.array([0.25, 0.25, 0.25]) 52 | self.check() 53 | 54 | def test_5_fcc(self): 55 | self.load_fcc() 56 | self._kpoint = np.array([0.00, 0.05, 0.05]) 57 | self.check() 58 | 59 | # def test_4_fcc(self): 60 | # self.load_fcc() 61 | # self._kpoint = np.array([0.75, 0.50, 0.25]) 62 | # self.check() 63 | 64 | def check(self): 65 | prec = 1e-6 66 | kpoint = self._kpoint 67 | vectors = self._vectors 68 | rotational_projector = self._rotational_projector 69 | rotational_projector.create_standard_rotations(kpoint) 70 | r_proj_vectors = rotational_projector.project_vectors( 71 | vectors, kpoint, np.eye(3, dtype=int)) 72 | ir_labels = rotational_projector.get_ir_labels() 73 | print(ir_labels) 74 | 75 | sum_r_proj_vectors = np.sum(r_proj_vectors, axis=0) 76 | self.assertTrue(np.all(np.abs(sum_r_proj_vectors - vectors) < prec)) 77 | 78 | 79 | if __name__ == "__main__": 80 | unittest.main() 81 | -------------------------------------------------------------------------------- /tests/test_unfolder_symmetry.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import numpy as np 4 | from phonopy.interface.vasp import read_vasp 5 | from upho.structure.unfolder_symmetry import UnfolderSymmetry 6 | 7 | POSCAR_DIR = os.path.join(os.path.dirname(__file__), 'poscars') 8 | 9 | 10 | class TestUnfolderSymmetry(unittest.TestCase): 11 | def setUp(self): 12 | atoms = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_A_h")) 13 | self._symmetry = UnfolderSymmetry(atoms) 14 | 15 | def test_000(self): 16 | kpoint = np.array([0.0, 0.0, 0.0]) 17 | (rotations_kpoint, 18 | translations_kpoint) = self._symmetry.create_little_group(kpoint) 19 | 20 | self.assertEqual(len(rotations_kpoint), 48) 21 | 22 | def test_100(self): 23 | kpoint = np.array([0.5, 0.0, 0.0]) # X: D_4h 24 | (rotations_kpoint, 25 | translations_kpoint) = self._symmetry.create_little_group(kpoint) 26 | 27 | self.assertNotEqual(len(rotations_kpoint), 48) 28 | self.assertEqual(len(rotations_kpoint), 16) 29 | 30 | def test_110(self): 31 | kpoint = np.array([0.5, 0.5, 0.0]) # M: D_4h 32 | (rotations_kpoint, 33 | translations_kpoint) = self._symmetry.create_little_group(kpoint) 34 | 35 | self.assertNotEqual(len(rotations_kpoint), 48) 36 | self.assertEqual(len(rotations_kpoint), 16) 37 | 38 | def test_111(self): 39 | kpoint = np.array([0.5, 0.5, 0.5]) # R: O_h 40 | (rotations_kpoint, 41 | translations_kpoint) = self._symmetry.create_little_group(kpoint) 42 | 43 | self.assertEqual(len(rotations_kpoint), 48) 44 | 45 | def test_star_000(self): 46 | symmetry = self._symmetry 47 | prec = 1e-9 48 | kpoint = np.array([0.0, 0.0, 0.0]) 49 | star, transformation_matrices = symmetry.create_star(kpoint) 50 | 51 | star_exp = [ 52 | [0.0, 0.0, 0.0], 53 | ] 54 | 55 | self.assertTrue(np.all(np.abs(star - star_exp) < prec)) 56 | 57 | def test_star_100(self): 58 | symmetry = self._symmetry 59 | prec = 1e-9 60 | kpoint = np.array([0.5, 0.0, 0.0]) 61 | star, transformation_matrices = symmetry.create_star(kpoint) 62 | 63 | self.assertEqual(len(star), 6) 64 | 65 | def test_star_110(self): 66 | symmetry = self._symmetry 67 | prec = 1e-9 68 | kpoint = np.array([0.5, 0.5, 0.0]) 69 | star, transformation_matrices = symmetry.create_star(kpoint) 70 | 71 | self.assertEqual(len(star), 12) 72 | 73 | def test_star_111(self): 74 | symmetry = self._symmetry 75 | prec = 1e-9 76 | kpoint = np.array([0.5, 0.5, 0.5]) 77 | star, transformation_matrices = symmetry.create_star(kpoint) 78 | 79 | self.assertEqual(len(star), 8) 80 | 81 | 82 | if __name__ == "__main__": 83 | unittest.main() 84 | -------------------------------------------------------------------------------- /tests/test_vectors_adjuster.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import numpy as np 4 | from phonopy.structure.cells import get_primitive 5 | from phonopy.interface.vasp import read_vasp 6 | from upho.phonon.vectors_adjuster import VectorsAdjuster 7 | 8 | POSCAR_DIR = os.path.join(os.path.dirname(__file__), 'poscars') 9 | 10 | 11 | class DummyAtoms(object): 12 | def __init__(self, scaled_positions=None, masses=None): 13 | self._scaled_positions = scaled_positions 14 | self._masses = masses 15 | 16 | def get_scaled_positions(self): 17 | return self._scaled_positions 18 | 19 | def get_masses(self): 20 | return self._masses 21 | 22 | 23 | class TestVectorsAdjuster(unittest.TestCase): 24 | def setUp(self): 25 | scaled_positions = np.array( 26 | [[0.00, 0.0, 0.0], 27 | [0.25, 0.0, 0.0], 28 | [0.50, 0.0, 0.0], 29 | [0.75, 0.0, 0.0]]) 30 | masses = [ 31 | 1.0, 32 | 2.0, 33 | 3.0, 34 | 4.0, 35 | ] 36 | atoms = DummyAtoms(scaled_positions=scaled_positions, masses=masses) 37 | self._vectors_adjuster = VectorsAdjuster(atoms) 38 | self._prec = 1e-12 39 | 40 | def test_0_0(self): 41 | eigvecs = self.get_eigvec_0().reshape((-1, 1)) 42 | q = self.get_q_0() 43 | 44 | vectors_adjuster = self._vectors_adjuster 45 | vectors_adjuster.set_q(q) 46 | 47 | recovered_vecs = vectors_adjuster.recover_Bloch(eigvecs) 48 | 49 | recovered_vecs_expected = np.array([ 50 | 0.5, 0.0, 0.0, 51 | 0.5, 0.0, 0.0, 52 | 0.5, 0.0, 0.0, 53 | 0.5, 0.0, 0.0, 54 | ], dtype=complex).reshape(-1, 1) 55 | is_same = ( 56 | np.abs(recovered_vecs - recovered_vecs_expected) < self._prec).all() 57 | self.assertTrue(is_same) 58 | 59 | def test_0_1(self): 60 | eigvecs = self.get_eigvec_0().reshape((-1, 1)) 61 | q = self.get_q_1() 62 | 63 | vectors_adjuster = self._vectors_adjuster 64 | vectors_adjuster.set_q(q) 65 | 66 | print(eigvecs) 67 | recovered_vecs = vectors_adjuster.recover_Bloch(eigvecs) 68 | print(recovered_vecs) 69 | 70 | recovered_vecs_expected = np.array([ 71 | 0.5, 0.0, 0.0, 72 | -0.5, 0.0, 0.0, 73 | 0.5, 0.0, 0.0, 74 | -0.5, 0.0, 0.0, 75 | ], dtype=complex).reshape(-1, 1) 76 | is_same = ( 77 | np.abs(recovered_vecs - recovered_vecs_expected) < self._prec).all() 78 | self.assertTrue(is_same) 79 | 80 | def test_1_1(self): 81 | eigvecs = self.get_eigvec_1().reshape((-1, 1)) 82 | q = self.get_q_1() 83 | 84 | vectors_adjuster = self._vectors_adjuster 85 | vectors_adjuster.set_q(q) 86 | 87 | recovered_vecs = vectors_adjuster.recover_Bloch(eigvecs) 88 | print(recovered_vecs) 89 | 90 | recovered_vecs_expected = np.array([ 91 | 0.5, 0.0, 0.0, 92 | 0.5, 0.0, 0.0, 93 | 0.5, 0.0, 0.0, 94 | 0.5, 0.0, 0.0, 95 | ], dtype=complex).reshape(-1, 1) 96 | is_same = ( 97 | np.abs(recovered_vecs - recovered_vecs_expected) < self._prec).all() 98 | self.assertTrue(is_same) 99 | 100 | def test_reduce_vectors_to_primitive(self): 101 | vectors_adjuster = self._vectors_adjuster 102 | atoms = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_fcc")) 103 | primitive_matrix = [ 104 | [0.0, 0.5, 0.5], 105 | [0.5, 0.0, 0.5], 106 | [0.5, 0.5, 0.0], 107 | ] 108 | primitive = get_primitive(atoms, primitive_matrix) 109 | vectors = self.get_eigvec_0()[:, None] 110 | reduced_vectors = vectors_adjuster.reduce_vectors_to_primitive( 111 | vectors, primitive) 112 | nexpansion = 4 113 | exp = np.array([ 114 | [0.5], 115 | [0.0], 116 | [0.0], 117 | ]) 118 | exp *= np.sqrt(nexpansion) 119 | self.assertTrue(np.all(np.abs(reduced_vectors - exp) < 1e-6)) 120 | 121 | def test_reduce_vectors_to_primitive_for_multidimensional_vectors(self): 122 | vectors_adjuster = self._vectors_adjuster 123 | atoms = read_vasp(os.path.join(POSCAR_DIR, "POSCAR_fcc")) 124 | primitive_matrix = [ 125 | [0.0, 0.5, 0.5], 126 | [0.5, 0.0, 0.5], 127 | [0.5, 0.5, 0.0], 128 | ] 129 | primitive = get_primitive(atoms, primitive_matrix) 130 | vectors = self.get_eigvec_0()[None, :, None] 131 | reduced_vectors = vectors_adjuster.reduce_vectors_to_primitive( 132 | vectors, primitive) 133 | nexpansion = 4 134 | exp = np.array([ 135 | 0.5, 136 | 0.0, 137 | 0.0, 138 | ])[None, :, None] 139 | exp *= np.sqrt(nexpansion) 140 | self.assertTrue(np.all(np.abs(reduced_vectors - exp) < 1e-6)) 141 | 142 | def test_apply_mass_weights(self): 143 | vectors = self.get_eigvec_0().reshape((-1, 1)) 144 | modified_vectors = self._vectors_adjuster.apply_mass_weights(vectors) 145 | modified_vectors_expected = [ 146 | 0.5 , 0.0, 0.0, 147 | 0.35355339059327373, 0.0, 0.0, 148 | 0.28867513459481292, 0.0, 0.0, 149 | 0.25 , 0.0, 0.0, 150 | ] 151 | modified_vectors_expected = np.array(modified_vectors_expected).reshape(-1, 1) 152 | is_same = ( 153 | np.abs(modified_vectors - modified_vectors_expected) < self._prec).all() 154 | print(modified_vectors) 155 | print(modified_vectors_expected) 156 | self.assertTrue(is_same) 157 | 158 | def get_eigvec_0(self): 159 | eigvec = np.array([ 160 | 0.5, 0.0, 0.0, 161 | 0.5, 0.0, 0.0, 162 | 0.5, 0.0, 0.0, 163 | 0.5, 0.0, 0.0, 164 | ], dtype=complex) 165 | return eigvec 166 | 167 | def get_eigvec_1(self): 168 | eigvec = np.array([ 169 | 0.5, 0.0, 0.0, 170 | -0.5, 0.0, 0.0, 171 | 0.5, 0.0, 0.0, 172 | -0.5, 0.0, 0.0, 173 | ], dtype=complex) 174 | return eigvec 175 | 176 | def get_eigvec_2(self): 177 | eigvec = np.array([ 178 | 0.5 , 0.0, 0.0, 179 | 0.5j, 0.0, 0.0, 180 | -0.5 , 0.0, 0.0, 181 | -0.5j, 0.0, 0.0, 182 | ], dtype=complex) 183 | return eigvec 184 | 185 | def get_q_0(self): 186 | q = np.array([0.0, 0.0, 0.0]) 187 | return q 188 | 189 | def get_q_1(self): 190 | q = np.array([2.0, 0.0, 0.0]) 191 | return q 192 | 193 | def get_q_2(self): 194 | q = np.array([1.0, 0.0, 0.0]) 195 | return q 196 | 197 | 198 | if __name__ == "__main__": 199 | unittest.main() 200 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # content of: tox.ini , put in same dir as setup.py 2 | [tox] 3 | envlist = phonopy{2.7.0,2.14.0} 4 | 5 | [testenv] 6 | # install pytest in the virtualenv where commands will be executed 7 | deps = 8 | pytest 9 | phonopy2.7.0: phonopy==2.7.0 10 | phonopy2.14.0: phonopy==2.14.0 11 | commands = 12 | # NOTE: you can run any command line tool here - not just tests 13 | pytest tests 14 | -------------------------------------------------------------------------------- /upho/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/.DS_Store -------------------------------------------------------------------------------- /upho/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.6.6' 2 | -------------------------------------------------------------------------------- /upho/analysis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/analysis/__init__.py -------------------------------------------------------------------------------- /upho/analysis/fc_analyzer_base.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from phonopy.file_IO import write_FORCE_CONSTANTS 3 | from phonopy.structure.cells import Supercell 4 | from phonopy.harmonic.force_constants import symmetrize_force_constants 5 | 6 | 7 | class FCAnalyzerBase: 8 | def __init__(self, 9 | force_constants=None, 10 | atoms=None, 11 | atoms_ideal=None, 12 | supercell_matrix=None, 13 | is_symmetrized=True): 14 | """ 15 | 16 | Parameters 17 | ---------- 18 | force_constants: (natoms, natoms, 3, 3) array 19 | atoms: The "Atoms" object 20 | This is used to extract chemical symbols. 21 | atoms_ideal: The "Atoms" object 22 | This is used to judge the expected crystallographic symmetry. 23 | supercell_matrix: (3, 3) array 24 | """ 25 | if supercell_matrix is None: 26 | supercell_matrix = np.eye(3) 27 | 28 | print("supercell_matrix:") 29 | print(supercell_matrix) 30 | 31 | self.set_force_constants(force_constants) 32 | 33 | if atoms is not None: 34 | self.set_atoms(Supercell(atoms, supercell_matrix)) 35 | if atoms_ideal is not None: 36 | self.set_atoms_ideal(Supercell(atoms_ideal, supercell_matrix)) 37 | 38 | if is_symmetrized: 39 | self.symmetrize_force_constants() 40 | 41 | self._fc_distribution_analyzer = None 42 | 43 | self.check_consistency() 44 | 45 | def check_consistency(self): 46 | number_of_atoms = self.get_atoms().get_number_of_atoms() 47 | number_of_atoms_fc = self.get_force_constants().shape[0] 48 | if number_of_atoms != number_of_atoms_fc: 49 | print(number_of_atoms, number_of_atoms_fc) 50 | raise ValueError("Atoms, Dim, and FC are not consistent.") 51 | 52 | def symmetrize_force_constants(self, iteration=3): 53 | symmetrize_force_constants(self._force_constants, iteration) 54 | return self 55 | 56 | def set_force_constants(self, force_constants): 57 | self._force_constants = force_constants 58 | 59 | def get_force_constants(self): 60 | return self._force_constants 61 | 62 | def set_atoms(self, atoms): 63 | self._atoms = atoms 64 | 65 | def get_atoms(self): 66 | return self._atoms 67 | 68 | def set_atoms_ideal(self, atoms_ideal): 69 | self._atoms_ideal = atoms_ideal 70 | 71 | def get_atoms_ideal(self): 72 | return self._atoms_ideal 73 | 74 | def write_force_constants(self, filename_write): 75 | write_FORCE_CONSTANTS(self._force_constants, filename_write) 76 | -------------------------------------------------------------------------------- /upho/analysis/functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def lorentzian_old(x, position, width): 5 | return 1.0 / (np.pi * width * (1.0 + ((x - position) / width) ** 2)) 6 | 7 | 8 | def lorentzian(x, position, width): 9 | """This is faster than lorentzian_old""" 10 | return width / (np.pi * (width ** 2 + (x - position) ** 2)) 11 | 12 | 13 | def lorentzian_unnormalized(x, position, width, norm): 14 | return norm * lorentzian(x, position, width) 15 | 16 | 17 | def gaussian(x, position, width): 18 | sigma = width / np.sqrt(2.0 * np.log(2.0)) 19 | tmp = np.exp(- (x - position) ** 2 / (2.0 * sigma ** 2)) 20 | return 1.0 / np.sqrt(2.0 * np.pi) / sigma * tmp 21 | 22 | 23 | def gaussian_unnormalized(x, position, width, norm): 24 | return norm * gaussian(x, position, width) 25 | 26 | 27 | class FittingFunctionFactory(object): 28 | def __init__(self, name, is_normalized): 29 | """ 30 | 31 | Parameters 32 | ---------- 33 | name : str 34 | is_normalized : bool 35 | """ 36 | self._name = name 37 | self._is_normalized = is_normalized 38 | 39 | def create(self): 40 | name = self._name 41 | is_normalized = self._is_normalized 42 | if name == 'lorentzian': 43 | if is_normalized: 44 | return lorentzian 45 | else: 46 | return lorentzian_unnormalized 47 | elif name == 'gaussian': 48 | if is_normalized: 49 | return gaussian 50 | else: 51 | return gaussian_unnormalized 52 | else: 53 | raise ValueError('Unknown name', name) 54 | -------------------------------------------------------------------------------- /upho/analysis/mappings_modifier.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class MappingsModifier: 5 | def __init__(self, mappings): 6 | self.set_mappings(mappings) 7 | 8 | def set_mappings(self, mappings): 9 | self._mappings = np.array(mappings) 10 | 11 | def get_mappings(self): 12 | return self._mappings.copy() 13 | 14 | def invert_mappings(self): 15 | mappings = self._mappings 16 | mappings_inverse = -1 * np.zeros_like(mappings) 17 | for i, mapping in enumerate(mappings): 18 | mappings_inverse[i] = self._invert_mapping(mapping) 19 | return mappings_inverse 20 | 21 | def _invert_mapping(self, mapping): 22 | mapping_inverse = -1 * np.zeros_like(mapping) 23 | for index, value in enumerate(mapping): 24 | mapping_inverse[value] = index 25 | return mapping_inverse 26 | 27 | def expand_mappings(self, n, is_inverse=False): 28 | """Expand the last dimension of mappings by n""" 29 | if is_inverse: 30 | mappings = self.invert_mappings() 31 | else: 32 | mappings = self._mappings 33 | 34 | expanded_mappings = np.repeat(mappings, n, axis=-1) 35 | expanded_mappings *= n 36 | for i in range(n): 37 | expanded_mappings[..., i::n] += i 38 | return expanded_mappings 39 | -------------------------------------------------------------------------------- /upho/analysis/smearing.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from .functions import lorentzian 3 | 4 | 5 | def gaussian(x, mu, sigma): 6 | tmp = np.exp(- (x - mu) ** 2 / (2.0 * sigma ** 2)) 7 | return 1.0 / np.sqrt(2.0 * np.pi) / sigma * tmp 8 | 9 | 10 | def histogram_old(x, positions, width): 11 | tmp = np.zeros((x.shape[0], positions.shape[-1])) 12 | x_tmp = np.zeros(x.shape[0] + 1) 13 | x_tmp[:-1] = x[:, 0] - width * 0.5 14 | x_tmp[-1] = x[-1, 0] + width * 0.5 15 | for i, p in enumerate(positions.T): # only one value for each loop 16 | tmp[:, i] = np.histogram(p, x_tmp)[0] 17 | tmp /= width 18 | return tmp 19 | 20 | 21 | def histogram(x, positions, width): 22 | """ 23 | 24 | Note 25 | ---- 26 | 'x' must have the same intervals that is the same as 'width'. 27 | """ 28 | lower = x - width * 0.5 29 | upper = x + width * 0.5 30 | tmp = np.where(np.logical_and(positions >= lower, positions < upper), 1.0, 0.0) 31 | tmp = np.array(tmp) / width 32 | return tmp 33 | 34 | 35 | def create_points(xmin, xmax, xpitch): 36 | n = int(round((xmax - xmin) / xpitch)) + 1 37 | points = np.linspace(xmin, xmax, n) 38 | return points 39 | 40 | 41 | class Smearing: 42 | def __init__(self, 43 | function_name="gaussian", 44 | sigma=0.1, 45 | xmin=None, 46 | xmax=None, 47 | xpitch=None): 48 | 49 | self._function_name = function_name 50 | self.set_smearing_function(function_name) 51 | self.set_sigma(sigma) 52 | if xmin is not None and xmax is not None and xpitch is not None: 53 | self.build_xs(xmin, xmax, xpitch) 54 | elif not (xmin is None and xmax is None and xpitch is None): 55 | raise ValueError('Some of xmin, xmax, and xpitch are None') 56 | 57 | def set_smearing_function(self, function_name): 58 | if function_name == "gaussian": 59 | self._smearing_function = gaussian 60 | elif function_name == "lorentzian": 61 | self._smearing_function = lorentzian 62 | elif function_name == "histogram": 63 | self._smearing_function = histogram 64 | else: 65 | raise ValueError("Invalid smaering function name.") 66 | return self 67 | 68 | def build_xs(self, xmin, xmax, xpitch): 69 | self.set_xs(create_points(xmin, xmax, xpitch)) 70 | return self 71 | 72 | def set_xs(self, xs): 73 | self._xs = xs 74 | 75 | def get_xs(self): 76 | return self._xs 77 | 78 | def set_sigma(self, sigma): 79 | self._sigma = sigma 80 | 81 | def get_sigma(self): 82 | return self._sigma 83 | 84 | def get_function_name(self): 85 | return self._function_name 86 | 87 | def run(self, peaks, weights=None): 88 | """Get smeared values. 89 | 90 | Args: 91 | peaks: 92 | weights: 93 | Weight factors for "peaks". 94 | Now this can be one-dimeansional and multi-dimensional arrays. 95 | The last dimension must have the same order as the "peaks". 96 | """ 97 | smearing_function = self._smearing_function 98 | xs = self._xs 99 | sigma = self._sigma 100 | 101 | tmp = smearing_function(xs[:, None], peaks[None, :], sigma) 102 | if weights is not None: 103 | values = np.inner(tmp, weights) 104 | else: 105 | values = np.sum(tmp, axis=1) 106 | 107 | return values 108 | -------------------------------------------------------------------------------- /upho/analysis/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from unfolding.analysis.smearing import Smearing 3 | 4 | xs = np.linspace(-10.0, 10.0, 21) 5 | sigma = 1.0 6 | mu = np.arange(1) 7 | 8 | print(xs) 9 | print(mu) 10 | 11 | smearing = Smearing() 12 | smearing.set_sigma(sigma) 13 | smearing.set_xs(xs) 14 | values = smearing.run(mu) 15 | 16 | print(values) 17 | 18 | weights = np.array([2.0]) 19 | values = smearing.run(mu, weights) 20 | 21 | print(values) 22 | 23 | smearing.build_xs(-2.5, 10.0, 0.1) 24 | print(smearing.get_xs()) 25 | -------------------------------------------------------------------------------- /upho/analysis/time_measurer.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class TimeMeasurer: 5 | """Measure method execution time. 6 | 7 | This class is made based on the suggestion in the following reference. 8 | http://preshing.com/20110924/timing-your-code-using-pythons-with-statement/ 9 | """ 10 | def __init__(self, time_string): 11 | self._time_string = time_string 12 | 13 | def __enter__(self): 14 | self._start = time.time() 15 | return self 16 | 17 | def __exit__(self, *args): 18 | time_string = self._time_string 19 | 20 | self._finish = time.time() 21 | interval = self._finish - self._start 22 | 23 | print('{:36s} (sec.): {:12.4f}'.format(time_string, interval)) 24 | -------------------------------------------------------------------------------- /upho/cui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/cui/__init__.py -------------------------------------------------------------------------------- /upho/cui/settings.py: -------------------------------------------------------------------------------- 1 | import re 2 | import numpy as np 3 | 4 | 5 | def parse_phonopy_conf(filename): 6 | with open(filename, "r") as f: 7 | return _parse_phonopy_conf_strings(f.readlines()) 8 | 9 | 10 | def _parse_phonopy_conf_strings(lines): 11 | lines = _remove_comments(lines) 12 | lines = _parse_continuation_lines(lines) 13 | d = _create_dictionary(lines) 14 | return _parse_dictionary(d) 15 | 16 | 17 | def _remove_comments(lines): 18 | return [re.sub('#.*', '', _) for _ in lines] 19 | 20 | 21 | def _parse_continuation_lines(lines): 22 | lines_new = [] 23 | tmp = "" 24 | for line in lines: 25 | end = line.find('+++') 26 | if end == -1: 27 | lines_new.append(tmp + line) 28 | tmp = "" 29 | else: 30 | tmp += line[:end] 31 | return lines_new 32 | 33 | 34 | def _create_dictionary(lines): 35 | d = dict() 36 | for line in lines: 37 | if line.find('=') != -1: 38 | tmp = [x.strip() for x in line.split("=")] 39 | d[tmp[0].lower()] = tmp[1] 40 | return d 41 | 42 | 43 | def _parse_dictionary(d): 44 | d_new = dict() 45 | for k, v in d.items(): 46 | if k == 'dim': 47 | d_new['supercell_matrix'] = _parse_dim(v) 48 | elif k in ('primitive_axis', 'primitive_axes'): 49 | k_new = 'primitive_matrix' 50 | if v.lower() == 'auto': 51 | d_new['is_primitive_auto'] = True 52 | d_new[k_new] = v 53 | else: 54 | d_new['is_primitive_auto'] = False 55 | d_new[k_new] = _parse_primitive_axes(v) 56 | elif k == 'band': 57 | if v.lower() == 'auto': 58 | d_new['is_band_auto'] = True 59 | d_new['band_paths'] = v 60 | else: 61 | d_new['is_band_auto'] = False 62 | d_new['band_paths'] = _parse_band(v) 63 | elif k in ('band_points', ): 64 | d_new[k] = int(v) 65 | else: 66 | d_new[k] = v 67 | return d_new 68 | 69 | 70 | def _parse_dim(v): 71 | matrix = [int(_) for _ in v.split()] 72 | length = len(matrix) 73 | if length == 9: 74 | return np.array(matrix).reshape(3, 3) 75 | elif length == 3: 76 | return np.diag(matrix) 77 | else: 78 | raise ValueError('Number of elements of DIM tag has to be 3 or 9.') 79 | 80 | 81 | def _parse_primitive_axes(v): 82 | k = 'PRIMITIVE_AXES' 83 | matrix = [float(eval(_)) for _ in v.split()] 84 | length = len(matrix) 85 | if length == 9: 86 | matrix = np.array(matrix).reshape(3, 3) 87 | if np.linalg.det(matrix) < 1e-8: 88 | raise ValueError('{} has to have positive determinant.'.format(k)) 89 | return matrix 90 | else: 91 | raise ValueError('Number of elements in {} has to be 9.'.format(k)) 92 | 93 | 94 | def _parse_band(v): 95 | bands = [] 96 | for section in v.split(','): 97 | points = [eval(x) for x in section.split()] 98 | if len(points) % 3 != 0 or len(points) < 6: 99 | raise ValueError("BAND is incorrectly set.") 100 | bands.append(np.array(points).reshape(-1, 3)) 101 | return bands 102 | -------------------------------------------------------------------------------- /upho/file_io.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def read_band_yaml(yaml_file="band.yaml"): 5 | import yaml 6 | data = yaml.load(open(yaml_file, "r")) 7 | nqpoint = data['nqpoint'] 8 | npath = data['npath'] 9 | distance = np.zeros(nqpoint) 10 | frequency = [] 11 | weight = [] 12 | for iqpoint in range(nqpoint): 13 | distance[iqpoint] = data['phonon'][iqpoint]['distance'] 14 | nband = len(data['phonon'][iqpoint]['band']) 15 | f = np.zeros(nband) 16 | w = np.zeros(nband) 17 | for i in range(nband): 18 | f[i] = data['phonon'][iqpoint]['band'][i]['frequency'] 19 | w[i] = data['phonon'][iqpoint]['band'][i]['weight'] 20 | frequency.append(f) 21 | weight.append(w) 22 | 23 | nsep = nqpoint // npath 24 | 25 | return distance, frequency, weight, nsep 26 | 27 | 28 | def read_band_hdf5(hdf5_file="band.hdf5"): 29 | import h5py 30 | band_data = {} 31 | with h5py.File(hdf5_file, "r") as f: 32 | for key in f.keys(): 33 | band_data[key] = np.array(f[key]) 34 | return band_data 35 | 36 | 37 | def read_input(filename_input): 38 | import yaml 39 | with open(filename_input, 'r') as f: 40 | dict_input = yaml.load(f) 41 | return dict_input 42 | -------------------------------------------------------------------------------- /upho/harmonic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/harmonic/__init__.py -------------------------------------------------------------------------------- /upho/harmonic/dynamical_matrix.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from phonopy.structure.cells import get_reduced_bases 3 | from phonopy.harmonic.dynamical_matrix import DynamicalMatrix 4 | 5 | 6 | class UnfolderDynamicalMatrix(DynamicalMatrix): 7 | """Dynamical matrix class 8 | 9 | When prmitive and supercell lattices are L_p and L_s, respectively, 10 | frame F is defined by 11 | L_p = dot(F, L_s), then L_s = dot(F^-1, L_p). 12 | where lattice matrix is defined by axies a,b,c in Cartesian: 13 | [ a1 a2 a3 ] 14 | L = [ b1 b2 b3 ] 15 | [ c1 c2 c3 ] 16 | 17 | Phase difference in primitive cell unit 18 | between atoms 1 and 2 in supercell is calculated by, e.g., 19 | 1j * dot((x_s(2) - x_s(1)), F^-1) * 2pi 20 | where x_s is reduced atomic coordinate in supercell unit. 21 | """ 22 | 23 | def __init__(self, 24 | supercell, 25 | primitive, 26 | force_constants, 27 | decimals=None, 28 | symprec=1e-5): 29 | self._scell = supercell 30 | self._pcell = primitive 31 | self._force_constants = np.array(force_constants, 32 | dtype='double', order='C') 33 | self._decimals = decimals 34 | self._symprec = symprec 35 | 36 | self._p2s_map = primitive.get_primitive_to_supercell_map() 37 | self._s2p_map = primitive.get_supercell_to_primitive_map() 38 | p2p_map = primitive.get_primitive_to_primitive_map() 39 | self._p2p_map = [p2p_map[self._s2p_map[i]] 40 | for i in range(len(self._s2p_map))] 41 | self._smallest_vectors, self._multiplicity = \ 42 | get_smallest_vectors(supercell, primitive, symprec) 43 | self._mass = self._pcell.get_masses() 44 | # Non analytical term correction 45 | self._nac = False 46 | 47 | 48 | # Helper methods 49 | def get_equivalent_smallest_vectors_np( 50 | atom_number_supercell, 51 | atom_number_primitive, 52 | supercell, 53 | primitive_lattice, 54 | symprec): 55 | distances = [] 56 | differences = [] 57 | reduced_bases = get_reduced_bases(supercell.get_cell(), symprec) 58 | positions = np.dot(supercell.get_positions(), np.linalg.inv(reduced_bases)) 59 | 60 | # Atomic positions are confined into the lattice made of reduced bases. 61 | positions -= np.rint(positions) 62 | 63 | p_pos = positions[atom_number_primitive] 64 | s_pos = positions[atom_number_supercell] 65 | sc_range_1 = np.array([-1, 0, 1])[:, None] * np.array([1, 0, 0])[None, :] 66 | sc_range_2 = np.array([-1, 0, 1])[:, None] * np.array([0, 1, 0])[None, :] 67 | sc_range_3 = np.array([-1, 0, 1])[:, None] * np.array([0, 0, 1])[None, :] 68 | # The vector arrow is from the atom in primitive to 69 | # the atom in supercell cell plus a supercell lattice 70 | # point. This is related to determine the phase 71 | # convension when building dynamical matrix. 72 | differences = (s_pos 73 | + sc_range_1[:, None, None] 74 | + sc_range_2[None, :, None] 75 | + sc_range_3[None, None, :] 76 | - p_pos) 77 | vecs = np.dot(differences, reduced_bases) 78 | distances = np.linalg.norm(vecs, axis=-1) 79 | 80 | relative_scale = np.dot(reduced_bases, np.linalg.inv(primitive_lattice)) 81 | minimum = np.min(distances) 82 | indices = np.where(np.abs(minimum - distances) < symprec) 83 | smallest_vectors = np.dot(differences[indices], relative_scale) 84 | smallest_vectors = smallest_vectors.reshape(-1, 3) 85 | 86 | return smallest_vectors 87 | 88 | 89 | def get_smallest_vectors(supercell, primitive, symprec): 90 | """ 91 | shortest_vectors: 92 | 93 | Shortest vectors from an atom in primitive cell to an atom in 94 | supercell in the fractional coordinates. If an atom in supercell 95 | is on the border centered at an atom in primitive and there are 96 | multiple vectors that have the same distance and different 97 | directions, several shortest vectors are stored. The 98 | multiplicity is stored in another array, "multiplicity". 99 | [atom_super, atom_primitive, multiple-vectors, 3] 100 | 101 | multiplicity: 102 | Number of multiple shortest vectors (third index of "shortest_vectors") 103 | [atom_super, atom_primitive] 104 | """ 105 | 106 | p2s_map = primitive.get_primitive_to_supercell_map() 107 | size_super = supercell.get_number_of_atoms() 108 | size_prim = primitive.get_number_of_atoms() 109 | shortest_vectors = np.zeros((size_super, size_prim, 27, 3), dtype='double') 110 | multiplicity = np.zeros((size_super, size_prim), dtype='intc') 111 | 112 | for i in range(size_super): # run in supercell 113 | for j, s_j in enumerate(p2s_map): # run in primitive 114 | vectors = get_equivalent_smallest_vectors_np(i, 115 | s_j, 116 | supercell, 117 | primitive.get_cell(), 118 | symprec) 119 | multiplicity[i][j] = len(vectors) 120 | for k, elem in enumerate(vectors): 121 | shortest_vectors[i][j][k] = elem 122 | 123 | return shortest_vectors, multiplicity 124 | -------------------------------------------------------------------------------- /upho/irreps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/irreps/__init__.py -------------------------------------------------------------------------------- /upho/irreps/irreps.py: -------------------------------------------------------------------------------- 1 | """ 2 | Note 3 | ---- 4 | Characters are generally not integer. 5 | """ 6 | import numpy as np 7 | import spglib 8 | from upho.irreps.character_tables import ( 9 | character_tables, find_rotation_type_from_class_label) 10 | from group.mathtools import similarity_transformation 11 | 12 | 13 | def find_rotation_type(rotation): 14 | # https://doi.org/10.1107/S0108767398010186 15 | tr = np.trace(rotation) 16 | det = int(round(np.linalg.det(rotation))) 17 | return { 18 | (-2, -1): -6, 19 | (-1, -1): -4, 20 | ( 0, -1): -3, 21 | (+1, -1): -2, 22 | (-3, -1): -1, 23 | (+3, +1): +1, 24 | (-1, +1): +2, 25 | ( 0, +1): +3, 26 | (+1, +1): +4, 27 | (+2, +1): +6, 28 | }[(tr, det)] 29 | 30 | 31 | def assign_class_labels_to_rotations(class_labels, rotations): 32 | rotation_types = [find_rotation_type(r) for r in rotations] 33 | d = {find_rotation_type_from_class_label(_): _ for _ in class_labels} 34 | if len(set(d.keys())) != len(class_labels): 35 | raise ValueError( 36 | 'Classes and rotation types do not have a 1-to-1 correspondence.') 37 | return [d[_] for _ in rotation_types] 38 | 39 | 40 | def extract_degeneracy_from_ir_label(ir_label): 41 | if ir_label[0] == 'E': 42 | degeneracy = 2 43 | elif ir_label[0] == 'T': 44 | degeneracy = 3 45 | else: 46 | degeneracy = 1 47 | return degeneracy 48 | 49 | 50 | class Irreps: 51 | """ 52 | 53 | Note 54 | ---- 55 | This does not work correctly for nonsymmorphic space groups. 56 | """ 57 | def __init__(self, rotations): 58 | """ 59 | 60 | Parameters 61 | ---------- 62 | rotations : A set of rotational symmetry operations. 63 | This can be a set of symmetry operations of the point group of 64 | a wave vector. 65 | """ 66 | self._rotations = rotations 67 | self._run() 68 | 69 | def _run(self): 70 | self._create_conventional_rotations() 71 | self._assign_character_table_data() 72 | self._assign_class_labels_to_rotations() 73 | 74 | self._characters = self.assign_characters_to_rotations( 75 | self._rotation_labels) 76 | 77 | def _create_conventional_rotations(self): 78 | rotations = self._rotations 79 | 80 | pointgroup = spglib.get_pointgroup(rotations) 81 | pointgroup_symbol = pointgroup[0].strip() 82 | transformation_matrix = pointgroup[2] 83 | 84 | conventional_rotations = self._transform_rotations( 85 | transformation_matrix, rotations) 86 | 87 | self._conventional_rotations = conventional_rotations 88 | self._transformation_matrix = transformation_matrix 89 | self._pointgroup_symbol = pointgroup_symbol 90 | 91 | def _assign_character_table_data(self): 92 | self._character_table_data = character_tables[self._pointgroup_symbol] 93 | 94 | def _assign_class_labels_to_rotations(self): 95 | if 'class_to_rotations_list' not in self._character_table_data: 96 | self._rotation_labels = assign_class_labels_to_rotations( 97 | self._character_table_data['rotation_labels'], self._rotations) 98 | else: 99 | class_to_rotations_list = ( 100 | self._character_table_data["class_to_rotations_list"]) 101 | for class_to_rotations in class_to_rotations_list: 102 | rotation_labels = [] 103 | for rconv in self._conventional_rotations: 104 | label = self._assign_class_label_to_rotation( 105 | rconv, class_to_rotations) 106 | rotation_labels.append(label) 107 | if False not in rotation_labels: 108 | self._rotation_labels = rotation_labels 109 | return 110 | msg = "Class labels cannot be assigned to rotations.\n" 111 | msg += str(rotation_labels) 112 | raise ValueError(msg) 113 | 114 | def _assign_class_label_to_rotation(self, rconv, class_to_rotations): 115 | """ 116 | 117 | Parameters 118 | ---------- 119 | rconv : 120 | Conventional rotation obtained using the transformation matrix. 121 | class_to_rotations : Dictionary 122 | Keys are class labels and values are corresponding 123 | conventional rotations. 124 | """ 125 | for label, rotations in class_to_rotations.items(): 126 | for rotation in rotations: 127 | if np.all(rconv == rotation): 128 | return label 129 | return False 130 | 131 | def assign_characters_to_rotations(self, rotation_labels): 132 | """ 133 | 134 | Parameters 135 | ---------- 136 | rotation_labels : 1d list 137 | Elements : Class labels. 138 | 139 | Returns 140 | ------- 141 | characters : 2d list 142 | Row : IR labels 143 | Column : Rotations 144 | """ 145 | character_table_data = self._character_table_data 146 | 147 | character_table = np.array(character_table_data["character_table"]) 148 | label_order = character_table_data["rotation_labels"] 149 | 150 | num_rotations = len(rotation_labels) 151 | num_irreps = len(character_table_data["ir_labels"]) 152 | 153 | characters = np.zeros((num_rotations, num_irreps), dtype=complex) 154 | for i, rotation_label in enumerate(rotation_labels): 155 | rotation_index = label_order.index(rotation_label) 156 | characters[i] = character_table[:, rotation_index] 157 | return characters 158 | 159 | def _transform_rotations(self, tmat, rotations): 160 | trans_rots = [] 161 | for r in rotations: 162 | r_conv = similarity_transformation(np.linalg.inv(tmat), r) 163 | trans_rots.append(r_conv) 164 | return np.rint(trans_rots).astype(int) 165 | 166 | def get_character_table_data(self): 167 | return self._character_table_data 168 | 169 | def get_pointgroup_symbol(self): 170 | return self._pointgroup_symbol 171 | 172 | def get_ir_labels(self): 173 | return self._character_table_data['ir_labels'] 174 | 175 | def get_conventional_rotations(self): 176 | return self._conventional_rotations 177 | 178 | def get_transformation_matrix(self): 179 | return self._transformation_matrix 180 | 181 | def get_rotation_labels(self): 182 | return self._rotation_labels 183 | 184 | def get_characters(self): 185 | return self._characters 186 | -------------------------------------------------------------------------------- /upho/phonon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/phonon/__init__.py -------------------------------------------------------------------------------- /upho/phonon/band_structure.py: -------------------------------------------------------------------------------- 1 | import h5py 2 | import numpy as np 3 | from phonopy.units import VaspToTHz 4 | from phonopy.structure.cells import get_primitive 5 | from upho.phonon.eigenstates import Eigenstates 6 | 7 | 8 | class BandStructure: 9 | def __init__(self, 10 | paths, 11 | dynamical_matrix, 12 | unitcell_ideal, 13 | primitive_matrix_ideal, 14 | is_eigenvectors=False, 15 | is_band_connection=False, 16 | group_velocity=None, 17 | factor=VaspToTHz, 18 | star="none", 19 | mode="eigenvector", 20 | verbose=False): 21 | """ 22 | 23 | Args: 24 | dynamical_matrix: 25 | Dynamical matrix for the (disordered) supercell. 26 | primitive_ideal_wrt_unitcell: 27 | Primitive cell w.r.t. the unitcell (not the supercell). 28 | """ 29 | # ._dynamical_matrix must be assigned for calculating DOS 30 | # using the tetrahedron method. 31 | self._dynamical_matrix = dynamical_matrix 32 | 33 | # self._cell is used for write_yaml and _shift_point. 34 | # This must correspond to the "ideal" primitive cell. 35 | primitive_ideal_wrt_unitcell = ( 36 | get_primitive(unitcell_ideal, primitive_matrix_ideal)) 37 | self._cell = primitive_ideal_wrt_unitcell 38 | 39 | self._factor = factor 40 | self._is_eigenvectors = is_eigenvectors 41 | self._is_band_connection = is_band_connection 42 | if is_band_connection: 43 | self._is_eigenvectors = True 44 | self._group_velocity = group_velocity 45 | 46 | self._paths = [np.array(path) for path in paths] 47 | self._distances = [] 48 | self._distance = 0. 49 | self._special_point = [0.] 50 | self._eigenvalues = None 51 | self._eigenvectors = None 52 | self._frequencies = None 53 | 54 | self._star = star 55 | self._mode = mode 56 | 57 | self._eigenstates = Eigenstates( 58 | dynamical_matrix, 59 | unitcell_ideal, 60 | primitive_matrix_ideal, 61 | mode=mode, 62 | star=star, 63 | verbose=verbose) 64 | 65 | with h5py.File('band.hdf5', 'w') as f: 66 | self._hdf5_file = f 67 | self._write_hdf5_header() 68 | self._set_band(verbose=verbose) 69 | 70 | def _write_hdf5_header(self): 71 | self._hdf5_file.create_dataset('paths', data=self._paths) 72 | 73 | def _set_initial_point(self, qpoint): 74 | self._lastq = qpoint.copy() 75 | 76 | def _shift_point(self, qpoint): 77 | self._distance += np.linalg.norm( 78 | np.dot(qpoint - self._lastq, 79 | np.linalg.inv(self._cell.get_cell()).T)) 80 | self._lastq = qpoint.copy() 81 | 82 | def _set_band(self, verbose=False): 83 | for ipath, path in enumerate(self._paths): 84 | self._set_initial_point(path[0]) 85 | self._solve_dm_on_path(ipath, path, verbose) 86 | 87 | self._special_point.append(self._distance) 88 | 89 | def _solve_dm_on_path(self, ipath, path, verbose): 90 | eigenstates = self._eigenstates 91 | 92 | is_nac = self._dynamical_matrix.is_nac() 93 | 94 | for ip, q in enumerate(path): 95 | self._shift_point(q) 96 | 97 | if is_nac: 98 | raise ValueError('NAC is not implemented yet for unfolding') 99 | 100 | eigenstates.set_distance(self._distance) 101 | eigenstates.extract_eigenstates(q) 102 | 103 | group = '{}/{}/'.format(ipath, ip) 104 | eigenstates.write_hdf5(self._hdf5_file, group=group) 105 | 106 | def get_unitcell_orig(self): 107 | unitcell_orig = self._dynamical_matrix.get_primitive() 108 | return unitcell_orig 109 | 110 | def get_reduced_elements(self): 111 | unitcell_orig = self.get_unitcell_orig() 112 | elements = unitcell_orig.get_chemical_symbols() 113 | reduced_elements = sorted(set(elements), key=elements.index) 114 | return reduced_elements 115 | -------------------------------------------------------------------------------- /upho/phonon/dos_unfolding.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from phonopy.phonon.dos import TotalDos 3 | 4 | 5 | class TotalDosUnfolding(TotalDos): 6 | def __init__(self, mesh_object, sigma=None, tetrahedron_method=False): 7 | TotalDos.__init__( 8 | self, 9 | mesh_object, 10 | sigma=sigma, 11 | tetrahedron_method=tetrahedron_method) 12 | self._pr_weights = mesh_object.get_pr_weights() 13 | 14 | def _get_density_of_states_at_freq(self, f): 15 | tmp = self._smearing_function.calc(self._frequencies - f) 16 | tmp *= self._pr_weights 17 | return np.sum(np.dot(self._weights, tmp)) / np.sum(self._weights) 18 | -------------------------------------------------------------------------------- /upho/phonon/element_weights_calculator.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from upho.analysis.mappings_modifier import MappingsModifier 3 | 4 | 5 | class ElementWeightsCalculator(object): 6 | """Extract weights on elements from eigenvectors""" 7 | def __init__(self, unitcell, primitive): 8 | """ 9 | 10 | Parameters 11 | ---------- 12 | unitcell : Phonopy Atoms object 13 | This may have a disordered atomic configuration. 14 | primitive : Phonopy Primitive object 15 | """ 16 | self._extract_map_elements(unitcell) 17 | self._extract_map_atoms_u2p(primitive) 18 | 19 | def _extract_map_elements(self, unitcell): 20 | natoms_u = unitcell.get_number_of_atoms() 21 | elements = unitcell.get_chemical_symbols() 22 | reduced_elements = sorted(set(elements), key=elements.index) 23 | 24 | map_elements = [] 25 | for re in reduced_elements: 26 | map_elements.append( 27 | [i for i, v in enumerate(elements) if v == re]) 28 | 29 | if sum(len(v) for v in map_elements) != natoms_u: 30 | raise ValueError("Mapping of elements is failed.") 31 | 32 | self._map_elements = map_elements 33 | self._reduced_elements = reduced_elements 34 | 35 | def _extract_map_atoms_u2p(self, primitive): 36 | p2s_map = primitive.get_primitive_to_supercell_map() 37 | s2p_map = primitive.get_supercell_to_primitive_map() 38 | natoms_u = len(s2p_map) 39 | 40 | map_atoms_u2p = [] 41 | for iatom_s in p2s_map: 42 | map_atoms_u2p.append( 43 | [i for i, v in enumerate(s2p_map) if v == iatom_s]) 44 | 45 | if sum(len(v) for v in map_atoms_u2p) != natoms_u: 46 | raise ValueError("Mapping of atoms_u2p is failed.") 47 | 48 | self._map_atoms_u2p = map_atoms_u2p 49 | 50 | def get_map_elements(self): 51 | return self._map_elements 52 | 53 | def get_map_atoms_u2p(self): 54 | return self._map_atoms_u2p 55 | 56 | def get_reduced_elements(self): 57 | return self._reduced_elements 58 | 59 | def get_number_of_elements(self): 60 | return len(self._reduced_elements) 61 | 62 | def run_star(self, vectors, ndims=3): 63 | """ 64 | 65 | Parameters 66 | ---------- 67 | vectors : (narms, natoms_u * ndims, nbands) array 68 | ndims : Integer 69 | number of dimensions of the space. 70 | 71 | Returns 72 | ------- 73 | weights : (narms, nelements, natoms_p, nbands) array 74 | """ 75 | weights = [] 76 | for vectors_arm in vectors: 77 | weights_arm = self.run(vectors_arm, ndims) 78 | weights.append(weights_arm) 79 | return np.array(weights) 80 | 81 | def run(self, vectors, ndims=3): 82 | """ 83 | 84 | Parameters 85 | ---------- 86 | vectors : (natoms_u * ndims, nbands) array 87 | ndims : Integer 88 | number of dimensions of the space. 89 | 90 | Returns 91 | ------- 92 | weights : (natoms_p, nelements, nbands) array 93 | """ 94 | map_atoms_u2p = self._map_atoms_u2p 95 | map_elements = self._map_elements 96 | 97 | shape = vectors.shape 98 | nbands = shape[1] 99 | tmp = vectors.reshape(shape[0] // ndims, ndims, nbands) 100 | weights_atoms = np.linalg.norm(tmp, axis=1) ** 2 101 | 102 | shape_weights = (len(map_atoms_u2p), len(map_elements), nbands) 103 | weights = np.full(shape_weights, np.nan) # Initialization 104 | 105 | for ip, lp in enumerate(map_atoms_u2p): 106 | for ie, le in enumerate(map_elements): 107 | indices = sorted(set(lp) & set(le)) 108 | weights[ip, ie] = np.sum(weights_atoms[indices], axis=0) 109 | 110 | return weights 111 | 112 | def project_vectors(self, vectors, ndims=3): 113 | map_atoms_u2p = self._map_atoms_u2p 114 | map_elements = self._map_elements 115 | 116 | natoms_p = len(map_atoms_u2p) 117 | num_elements = len(map_elements) 118 | 119 | tmp = np.zeros_like(vectors[None, None]) # Add two dimensions 120 | projected_vectors = ( 121 | np.repeat(np.repeat(tmp, natoms_p, axis=0), num_elements, axis=1)) 122 | 123 | for ip, lp in enumerate(map_atoms_u2p): 124 | for ie, le in enumerate(map_elements): 125 | indices_tmp = sorted(set(lp) & set(le)) 126 | indices = MappingsModifier(indices_tmp).expand_mappings(ndims) 127 | if len(indices) > 0: # The element "le" exists on the sublattice. 128 | projected_vectors[ip, ie, indices] = vectors[indices] 129 | 130 | return projected_vectors 131 | -------------------------------------------------------------------------------- /upho/phonon/mesh_unfolding.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from phonopy.units import VaspToTHz 3 | from phonopy.structure.grid_points import GridPoints 4 | from phonopy.phonon.mesh import Mesh 5 | from phonopy.structure.cells import get_primitive 6 | from upho.phonon.eigenstates import Eigenstates 7 | 8 | 9 | class MeshUnfolding(Mesh): 10 | def __init__(self, 11 | dynamical_matrix, 12 | unitcell_ideal, 13 | primitive_matrix_ideal, 14 | mesh, 15 | shift=None, 16 | is_time_reversal=True, 17 | is_mesh_symmetry=True, 18 | is_eigenvectors=False, 19 | is_gamma_center=False, 20 | star="none", 21 | group_velocity=None, 22 | rotations=None, # Point group operations in real space 23 | factor=VaspToTHz, 24 | use_lapack_solver=False, 25 | mode="eigenvector"): 26 | 27 | self._mesh = np.array(mesh, dtype='intc') 28 | self._is_eigenvectors = is_eigenvectors 29 | self._factor = factor 30 | 31 | primitive_ideal_wrt_unitcell = ( 32 | get_primitive(unitcell_ideal, primitive_matrix_ideal)) 33 | self._cell = primitive_ideal_wrt_unitcell 34 | 35 | # ._dynamical_matrix must be assigned for calculating DOS 36 | # using the tetrahedron method. 37 | # In the "DOS", this is used to get "primitive". 38 | # In the "TetrahedronMesh", this is used just as the "Atoms". 39 | # Currently, we must not use the tetrahedron method, 40 | # because now we do not give the primitive but the unitcell for the 41 | # self._dynamical_matrix. 42 | self._dynamical_matrix = dynamical_matrix 43 | self._use_lapack_solver = use_lapack_solver 44 | 45 | self._gp = GridPoints(self._mesh, 46 | np.linalg.inv(self._cell.get_cell()), 47 | q_mesh_shift=shift, 48 | is_gamma_center=is_gamma_center, 49 | is_time_reversal=is_time_reversal, 50 | rotations=rotations, 51 | is_mesh_symmetry=is_mesh_symmetry) 52 | 53 | self._qpoints = self._gp.get_ir_qpoints() 54 | self._weights = self._gp.get_ir_grid_weights() 55 | 56 | self._star = star 57 | 58 | self._eigenstates_unfolding = Eigenstates( 59 | dynamical_matrix, 60 | unitcell_ideal, 61 | primitive_matrix_ideal, 62 | mode=mode, 63 | star=star, 64 | verbose=False) 65 | 66 | self._frequencies = None 67 | self._eigenvalues = None 68 | self._eigenvectors = None 69 | self._set_phonon() 70 | 71 | self._group_velocities = None 72 | if group_velocity is not None: 73 | self._set_group_velocities(group_velocity) 74 | 75 | def get_pr_weights(self): 76 | return self._pr_weights 77 | 78 | def write_yaml(self): 79 | w = open('mesh.yaml', 'w') 80 | eigenvalues = self._eigenvalues 81 | natom = self._cell.get_number_of_atoms() 82 | lattice = np.linalg.inv(self._cell.get_cell()) # column vectors 83 | w.write("mesh: [ %5d, %5d, %5d ]\n" % tuple(self._mesh)) 84 | w.write("nqpoint: %-7d\n" % self._qpoints.shape[0]) 85 | w.write("natom: %-7d\n" % natom) 86 | w.write("reciprocal_lattice:\n") 87 | for vec, axis in zip(lattice.T, ('a*', 'b*', 'c*')): 88 | w.write("- [ %12.8f, %12.8f, %12.8f ] # %2s\n" % 89 | (tuple(vec) + (axis,))) 90 | w.write("phonon:\n") 91 | 92 | for i, q in enumerate(self._qpoints): 93 | w.write("- q-position: [ %12.7f, %12.7f, %12.7f ]\n" % tuple(q)) 94 | w.write(" weight: %-5d\n" % self._weights[i]) 95 | w.write(" band:\n") 96 | 97 | for j, freq in enumerate(self._frequencies[i]): 98 | w.write(" - # %d\n" % (j + 1)) 99 | w.write(" frequency: %15.10f\n" % freq) 100 | w.write(" pr_weight: %15.10f\n" % self._pr_weights[i, j]) 101 | 102 | if self._group_velocities is not None: 103 | w.write(" group_velocity: ") 104 | w.write("[ %13.7f, %13.7f, %13.7f ]\n" % 105 | tuple(self._group_velocities[i, j])) 106 | 107 | if self._is_eigenvectors: 108 | w.write(" eigenvector:\n") 109 | for k in range(natom): 110 | w.write(" - # atom %d\n" % (k+1)) 111 | for l in (0,1,2): 112 | w.write(" - [ %17.14f, %17.14f ]\n" % 113 | (self._eigenvectors[i,k*3+l,j].real, 114 | self._eigenvectors[i,k*3+l,j].imag)) 115 | w.write("\n") 116 | 117 | def _set_phonon(self): 118 | # For the unfolding method. 119 | # num_band = self._cell.get_number_of_atoms() * 3 120 | cell = self._dynamical_matrix.get_primitive() 121 | num_band = cell.get_number_of_atoms() * 3 122 | num_qpoints = len(self._qpoints) 123 | 124 | self._eigenvalues = np.zeros((num_qpoints, num_band), dtype='double') 125 | self._frequencies = np.zeros_like(self._eigenvalues) 126 | # TODO(ikeda): the folling is very bad if we turn on is_star 127 | self._pr_weights = np.zeros_like(self._eigenvalues) 128 | if self._is_eigenvectors or self._use_lapack_solver: 129 | self._eigenvectors = np.zeros( 130 | (num_qpoints, num_band, num_band,), dtype='complex128') 131 | 132 | if self._use_lapack_solver: 133 | print("ERROR: _use_lapack_solver is not considered for this script.") 134 | raise ValueError 135 | else: 136 | for i, q in enumerate(self._qpoints): 137 | eigvals, eigvecs, self._pr_weights[i], nstar = ( 138 | self._eigenstates_unfolding.extract_eigenstates(q) 139 | ) 140 | self._eigenvalues[i] = eigvals.real 141 | if self._is_eigenvectors: 142 | self._eigenvectors[i] = eigvecs 143 | self._frequencies = np.array(np.sqrt(abs(self._eigenvalues)) * 144 | np.sign(self._eigenvalues), 145 | dtype='double', 146 | order='C') * self._factor 147 | -------------------------------------------------------------------------------- /upho/phonon/sf_fitter.py: -------------------------------------------------------------------------------- 1 | import h5py 2 | import numpy as np 3 | from scipy.optimize import curve_fit 4 | from upho.analysis.functions import FittingFunctionFactory 5 | from upho.irreps.irreps import extract_degeneracy_from_ir_label 6 | 7 | __author__ = 'Yuji Ikeda' 8 | 9 | 10 | class SFFitter: 11 | def __init__(self, filename='sf.hdf5', name='gaussian'): 12 | self._name = name 13 | 14 | with h5py.File(filename, 'r') as f: 15 | self._band_data = f 16 | self._run() 17 | 18 | def _run(self): 19 | band_data = self._band_data 20 | 21 | npaths, npoints = band_data['paths'].shape[:2] 22 | frequencies = band_data['frequencies'] 23 | frequencies = np.array(frequencies) 24 | self._is_squared = np.array(band_data['is_squared']) 25 | 26 | filename_sf = 'sf_fit.hdf5' 27 | with h5py.File(filename_sf, 'w') as f: 28 | self.print_header(f) 29 | for ipath in range(npaths): 30 | for ip in range(npoints): 31 | print(ipath, ip) 32 | group = '{}/{}/'.format(ipath, ip) 33 | 34 | peak_positions, widths, norms, fiterrs, sf_fittings = ( 35 | self._fit_spectral_functions( 36 | frequencies, 37 | point_data=band_data[group], 38 | ) 39 | ) 40 | 41 | self._write(f, group, peak_positions, widths, norms, fiterrs, sf_fittings) 42 | 43 | def _fit_spectral_functions(self, frequencies, point_data, prec=1e-6): 44 | partial_sf_s = point_data['partial_sf_s'] 45 | num_irreps = np.array(point_data['num_irreps']) 46 | dfreq = frequencies[1] - frequencies[0] 47 | 48 | fitting_function = FittingFunctionFactory( 49 | name=self._name, 50 | is_normalized=False).create() 51 | 52 | peak_positions = [] 53 | widths = [] 54 | norms = [] 55 | fiterrs = [] 56 | sf_fittings = [] 57 | for i in range(num_irreps): 58 | sf = partial_sf_s[:, i] 59 | if np.sum(sf) < prec: 60 | peak_position = np.nan 61 | width = np.nan 62 | norm = np.nan 63 | fiterr = np.nan 64 | sf_fitting = np.full(frequencies.shape, np.nan) 65 | else: 66 | peak_position = self._create_initial_peak_position(frequencies, sf) 67 | width = self._create_initial_width() 68 | 69 | if self._is_squared: 70 | norm = self._create_initial_norm(frequencies, sf) 71 | else: 72 | ir_label = str(point_data['ir_labels'][i], encoding='ascii') 73 | norm = float(extract_degeneracy_from_ir_label(ir_label)) 74 | 75 | def f(x, p, w): 76 | return fitting_function(x, p, w, norm) 77 | 78 | p0 = [peak_position, width] 79 | maxfev = create_maxfev(p0) 80 | fit_params, pcov = curve_fit( 81 | f, frequencies, sf, p0=p0, maxfev=maxfev) 82 | fiterr = np.sqrt(np.sum((f(frequencies, *fit_params) - sf) ** 2)) * dfreq 83 | 84 | peak_position = fit_params[0] 85 | width = fit_params[1] 86 | norm = fit_params[2] if len(fit_params) == 3 else norm 87 | sf_fitting = f(frequencies, *fit_params) 88 | 89 | peak_positions.append(peak_position) 90 | widths .append(width) 91 | norms .append(norm) 92 | fiterrs.append(fiterr) 93 | sf_fittings.append(sf_fitting) 94 | 95 | peak_positions = np.array(peak_positions) 96 | widths = np.array(widths) 97 | norms = np.array(norms) 98 | fiterrs = np.asarray(fiterrs) 99 | sf_fittings = np.asarray(sf_fittings) 100 | 101 | return peak_positions, widths, norms, fiterrs, sf_fittings 102 | 103 | def _create_initial_peak_position(self, frequencies, sf, prec=1e-12): 104 | position = frequencies[np.argmax(sf)] 105 | # "curve_fit" does not work well for extremely small initial guess. 106 | # To avoid this problem, "position" is rounded. 107 | # See also "http://stackoverflow.com/questions/15624070" 108 | if abs(position) < prec: 109 | position = 0.0 110 | return position 111 | 112 | def _create_initial_width(self): 113 | width = 0.1 114 | return width 115 | 116 | def _create_initial_norm(self, frequencies, sf): 117 | dfreq = frequencies[1] - frequencies[0] 118 | norm = np.sum(sf) * dfreq 119 | return norm 120 | 121 | def print_header(self, file_output): 122 | file_output.create_dataset('function' , data=self._name) 123 | file_output.create_dataset('is_squared', data=self._is_squared) 124 | file_output.create_dataset('paths' , data=self._band_data['paths']) 125 | file_output['frequencies'] = self._band_data['frequencies'][...] 126 | 127 | def _write(self, file_out, group_name, peak_positions_s, widths_s, norms_s, fiterrs, sf_fittings): 128 | group = file_out.create_group(group_name) 129 | 130 | keys = [ 131 | 'natoms_primitive', 132 | 'elements', 133 | 'distance', 134 | 'pointgroup_symbol', 135 | 'num_irreps', 136 | 'ir_labels', 137 | ] 138 | 139 | for k in keys: 140 | file_out.create_dataset( 141 | group_name + k, data=np.array(self._band_data[group_name + k]) 142 | ) 143 | group.create_dataset('peaks_s', data=peak_positions_s) 144 | group.create_dataset('widths_s', data=widths_s) 145 | group.create_dataset('norms_s', data=norms_s) 146 | group['fitting_errors'] = fiterrs 147 | group['partial_sf_s'] = sf_fittings 148 | group['total_sf'] = np.nansum(sf_fittings, axis=0) 149 | 150 | 151 | def create_maxfev(p0): 152 | maxfev = 20000 * (len(p0) + 1) 153 | return maxfev -------------------------------------------------------------------------------- /upho/phonon/single_point.py: -------------------------------------------------------------------------------- 1 | import h5py 2 | from phonopy.units import VaspToTHz 3 | from upho.phonon.eigenstates import Eigenstates 4 | 5 | 6 | class SinglePoint: 7 | def __init__(self, 8 | qpoint, 9 | distance, 10 | dynamical_matrix, 11 | unitcell_ideal, 12 | primitive_matrix_ideal, 13 | factor=VaspToTHz, 14 | star="none", 15 | mode="eigenvector", 16 | verbose=False): 17 | 18 | self._qpoint = qpoint 19 | self._distance = distance 20 | 21 | self._factor = factor 22 | 23 | self._eigenstates = Eigenstates( 24 | dynamical_matrix, 25 | unitcell_ideal, 26 | primitive_matrix_ideal, 27 | mode=mode, 28 | star=star, 29 | verbose=verbose) 30 | 31 | with h5py.File('point.hdf5', 'w') as f: 32 | self._hdf5_file = f 33 | self.run() 34 | 35 | def run(self): 36 | qpoint = self._qpoint 37 | distance = self._distance 38 | 39 | eigenstates = self._eigenstates 40 | 41 | eigenstates.set_distance(distance) 42 | eigenstates.extract_eigenstates(qpoint) 43 | 44 | eigenstates.write_hdf5(self._hdf5_file, group='') 45 | -------------------------------------------------------------------------------- /upho/phonon/star_creator.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from phonopy.structure.symmetry import Symmetry 3 | 4 | 5 | class StarCreator: 6 | def __init__(self, is_overlapping=False, atoms=None, symprec=1e-6): 7 | """ 8 | 9 | Parameters 10 | ---------- 11 | atoms : Phonopy Atoms object 12 | Atoms for primitive cell. 13 | """ 14 | self.set_is_overlapping(is_overlapping) 15 | self._atoms = atoms 16 | self._symprec = symprec 17 | self._create_symmetry() 18 | 19 | def set_is_overlapping(self, is_overlapping): 20 | """ 21 | 22 | Parameters 23 | ---------- 24 | is_overlapping : Bool 25 | If True, it allows the overlapping arms of the star. 26 | The number of the arms equals to that of the rotational 27 | operations. 28 | """ 29 | self._is_overlapping = is_overlapping 30 | 31 | def _create_symmetry(self): 32 | symmetry = Symmetry( 33 | self._atoms, symprec=self._symprec, is_symmetry=True) 34 | self._symmetry = symmetry 35 | 36 | def get_rotations(self): 37 | return self._symmetry.get_dataset()["rotations"] 38 | 39 | def create_star(self, kpoint): 40 | """Create the star of the given kpoint 41 | 42 | Definition of the star of k follows that in ITB 2010 Chap. 1.5. 43 | 44 | Parameters 45 | ---------- 46 | kpoint : Reciprocal space point 47 | 48 | Returns 49 | ------- 50 | star : n x 3 array 51 | Star of the given kpoint. 52 | transformation_matrices : n x 3 x 3 array 53 | Matrices to obtain arms of the star from the given kpoint. 54 | """ 55 | rotations = self._symmetry.get_dataset()["rotations"] 56 | lattice = self._atoms.get_cell() 57 | 58 | def get_dist(tmp, arm): 59 | diff = tmp - arm 60 | diff -= np.rint(diff) 61 | dist = np.linalg.norm(np.dot(np.linalg.inv(lattice), diff)) 62 | return dist 63 | 64 | star = [] 65 | transformation_matrices = [] 66 | for r in rotations: 67 | tmp = np.dot(kpoint, r) 68 | if (self._is_overlapping or 69 | all(get_dist(tmp, arm) > self._symprec for arm in star)): 70 | star.append(tmp) 71 | transformation_matrices.append(r) 72 | 73 | return np.array(star), np.array(transformation_matrices) 74 | -------------------------------------------------------------------------------- /upho/phonon/translational_projector.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from upho.structure.structure_analyzer import ( 3 | StructureAnalyzer, find_lattice_vectors) 4 | from upho.analysis.mappings_modifier import MappingsModifier 5 | 6 | 7 | class TranslationalProjector: 8 | """This class makes the projection of the given vectors. 9 | 10 | The given vectors could be both eigenvectors and displacements. 11 | This class can treat spaces with any numbers of dimensions. 12 | The number of dimensions is determined from the given k. 13 | """ 14 | def __init__(self, primitive, unitcell_ideal, ndim=3): 15 | """ 16 | 17 | Parameters 18 | ---------- 19 | primitive : Phonopy Primitive object 20 | Primitive w.r.t. ideal unit cell 21 | unitcell_ideal : Phonopy Atoms object 22 | Ideal (or average) unit cell 23 | ndim : Integer 24 | The number of dimensions of space 25 | """ 26 | self._primitive = primitive 27 | self._unitcell_ideal = unitcell_ideal 28 | self._ndim = ndim 29 | 30 | lattice_vectors = self._create_lattice_vectors_in_sc() 31 | mappings = self._create_mappings(lattice_vectors) 32 | 33 | print("lattice_vectors:", lattice_vectors.shape) 34 | print(lattice_vectors) 35 | print("mappings:", mappings.shape) 36 | print(mappings) 37 | if np.any(mappings == -1): 38 | raise ValueError("Mapping is failed.") 39 | 40 | self._create_expanded_mappings(mappings, ndim) 41 | 42 | self._ncells = mappings.shape[0] 43 | 44 | def _create_expanded_mappings(self, mappings, ndim): 45 | mappings_modifier = MappingsModifier(mappings) 46 | self._expanded_mappings = mappings_modifier.expand_mappings(ndim) 47 | 48 | def _create_lattice_vectors_in_sc(self): 49 | """ 50 | 51 | Returns 52 | ------- 53 | lattice_vectors : (ncells, 3) array 54 | Lattice vectors in SC in fractional coordinates for "SC". 55 | 56 | TODO 57 | ---- 58 | Creations of lattice_vectors and mappings should be separated. 59 | """ 60 | primitive_matrix = self._primitive.get_primitive_matrix() 61 | supercell_matrix = np.linalg.inv(primitive_matrix) 62 | lattice_vectors = find_lattice_vectors(supercell_matrix) 63 | return lattice_vectors 64 | 65 | def _create_mappings(self, lattice_vectors): 66 | """ 67 | 68 | Parameters 69 | ---------- 70 | lattice_vectors : (ncells, 3) array 71 | Lattice vectors in SC in fractional coordinates for "SC". 72 | 73 | Returns 74 | ------- 75 | mappings : (ncells, natoms) array 76 | Indices are for atoms after symmetry operations and 77 | elements are for atoms before symmetry opeerations. 78 | """ 79 | structure_analyzer = StructureAnalyzer(self._unitcell_ideal) 80 | 81 | eye = np.eye(3, dtype=int) 82 | mappings = [] 83 | for lv in lattice_vectors: 84 | mapping = structure_analyzer.extract_mapping_for_symopr(eye, lv) 85 | mappings.append(mapping) 86 | 87 | mappings = np.array(mappings) 88 | 89 | return mappings 90 | 91 | def project_vectors(self, vectors, kpoint): 92 | """Project vectors onto kpoint 93 | 94 | Parameters 95 | ---------- 96 | vectors : (..., natoms * ndim, nbands) array 97 | Vectors for SC at kpoint. Each "column" vector is an eigenvector. 98 | kpoint : (ndim) array 99 | Reciprocal space point in fractional coordinates for SC. 100 | 101 | Returns 102 | ------- 103 | projected_vectors : (..., natoms_primitive * ndim, nbands) array 104 | Projection of the given vectors. 105 | This is reduced into the primitive cell. 106 | """ 107 | ncells = self._ncells 108 | ndim = self._ndim 109 | primitive = self._primitive 110 | 111 | p2s_map = primitive.get_primitive_to_supercell_map() 112 | indices = MappingsModifier(p2s_map).expand_mappings(ndim) 113 | 114 | expanded_mappings = self._expanded_mappings 115 | 116 | shape = list(vectors.shape) 117 | shape[-2] //= ncells 118 | projected_vectors = np.zeros(shape, dtype=vectors.dtype) 119 | 120 | for expanded_mapping in expanded_mappings: 121 | jndices = expanded_mapping.take(indices) 122 | projected_vectors += vectors.take(jndices, axis=-2) 123 | 124 | # The following intend; 125 | # # Definition of projection operators 126 | # projected_vectors /= ncells 127 | # # Reduction into primitive 128 | # projected_vectors *= np.sqrt(ncells) 129 | projected_vectors /= np.sqrt(ncells) 130 | 131 | return projected_vectors 132 | 133 | def project_vectors_full(self, vectors, kpoint): 134 | """ 135 | Project vectors onto kpoint 136 | 137 | Parameters 138 | ---------- 139 | vectors : (..., natoms * ndim, nbands) array 140 | Vectors for SC at kpoint. Each "column" vector is an eigenvector. 141 | kpoint : (ndim) array 142 | Reciprocal space point in fractional coordinates for SC. 143 | 144 | Returns 145 | ------- 146 | projected_vectors : (..., natoms * ndim, nbands) array 147 | Projection of the given vectors. 148 | """ 149 | ncells = self._ncells 150 | ndim = self._ndim 151 | 152 | expanded_mappings = self._expanded_mappings 153 | 154 | projected_vectors = np.zeros_like(vectors) 155 | for expanded_mapping in expanded_mappings: 156 | projected_vectors += vectors.take(expanded_mapping, axis=-2) 157 | 158 | projected_vectors /= ncells 159 | 160 | return projected_vectors 161 | -------------------------------------------------------------------------------- /upho/phonon/vectors_adjuster.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from upho.analysis.mappings_modifier import MappingsModifier 3 | 4 | 5 | class VectorsAdjuster: 6 | def __init__(self, atoms): 7 | """ 8 | 9 | Parameters 10 | ---------- 11 | atoms : Phonopy Atoms object 12 | Disordered supercell. 13 | Eigenvectors must correspond to the size of the "atoms". 14 | """ 15 | self._scaled_positions = atoms.get_scaled_positions() 16 | self._masses = atoms.get_masses() 17 | 18 | def set_q(self, q): 19 | """ 20 | 21 | Args: 22 | q: Reciprocal space point in fractional coordinates for SC. 23 | """ 24 | self._q = q 25 | 26 | def recover_Bloch(self, vecs): 27 | """Recorver the properties of Bloch's waves. 28 | 29 | Args: 30 | vecs: Vectors to be recovered. 31 | 32 | Returns: 33 | recovered_vecs: Vectors having the properties of Bloch's waves. 34 | """ 35 | recovered_vecs = np.zeros_like(vecs) * np.nan 36 | for i, vec in enumerate(vecs): 37 | iatom = i // 3 38 | p = self._scaled_positions[iatom] 39 | phase = np.exp(2.0j * np.pi * np.dot(p, self._q)) 40 | recovered_vecs[i] = vec * phase 41 | return recovered_vecs 42 | 43 | def remove_phase_factors(self, vectors, kpoint): 44 | """ 45 | Remove phase factors from given vectors. 46 | 47 | Parameters 48 | ---------- 49 | vectors : array 50 | Vectors whose phase factors are removed. 51 | kpiont : 52 | Reciprocal space point in fractional coordinates for SC. 53 | """ 54 | phases = np.exp(-2.0j * np.pi * np.dot(self._scaled_positions, kpoint)) 55 | phases = np.repeat(phases, 3) 56 | modified_vectors = phases[:, None] * vectors 57 | return modified_vectors 58 | 59 | def reduce_vectors_to_primitive(self, vectors, primitive): 60 | """ 61 | Reduce size of vectors to primitive. 62 | 63 | Parameters 64 | ---------- 65 | vectors : (..., natoms_u * ndims, nbands) array 66 | Vectors which will be reduced. 67 | Phase factors must be removed in advance. 68 | primitive : Phonopy Primitive object. 69 | 70 | Returns 71 | ------- 72 | reduced_vectors : (..., natoms_p, ndims, nbands) array 73 | Reduced vectors. 74 | """ 75 | ndim = 3 76 | p2s_map = primitive.get_primitive_to_supercell_map() 77 | indices = MappingsModifier(p2s_map).expand_mappings(ndim) 78 | reduced_vectors = vectors[..., indices, :] 79 | 80 | # Renormalization of the reduced vectors 81 | relative_size = vectors.shape[-2] / reduced_vectors.shape[-2] 82 | reduced_vectors *= np.sqrt(relative_size) 83 | 84 | return reduced_vectors 85 | 86 | def apply_mass_weights(self, vectors, ndim=3): 87 | """Multiply vectors by mass weights 88 | 89 | Parameters 90 | ---------- 91 | vectors : (ndim * natoms, nbands) array 92 | masses : (natoms) array 93 | 94 | Returns 95 | ------- 96 | modified_vectors : (ndim * natoms, nbands) array 97 | mass weights are multiplied. 98 | """ 99 | masses = self._masses 100 | modified_vectors = vectors / np.sqrt(np.repeat(masses, ndim))[:, None] 101 | return modified_vectors 102 | 103 | def get_masses(self): 104 | return self._masses 105 | -------------------------------------------------------------------------------- /upho/qpoints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/qpoints/__init__.py -------------------------------------------------------------------------------- /upho/qpoints/qpoints_creator.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from .lebedev_write import Lebedev 3 | 4 | 5 | class QpointsCreator: 6 | def __init__(self, radii, lebedev): 7 | self._radii = np.asarray(radii) 8 | self._lebedev = lebedev 9 | self._create() 10 | 11 | def _create(self): 12 | radii = self._radii 13 | xyzw = np.array(Lebedev(self._lebedev)) 14 | self._qpoints = (radii[:, None, None] * xyzw[None, :, :3]).reshape(-1, 3) 15 | self._weights = np.tile(xyzw[:, 3], radii.size) 16 | self._groups = np.repeat(np.arange(radii.size), xyzw.shape[0]) 17 | 18 | def write(self): 19 | self._write_qpoints() 20 | self._write_weights() 21 | self._write_groups() 22 | 23 | def _write_qpoints(self): 24 | with open('QPOINTS', 'w') as f: 25 | for q in self._qpoints: 26 | for x in q: 27 | f.write('{:20.16f}'.format(x)) 28 | f.write('\n') 29 | 30 | def _write_weights(self): 31 | with open('WEIGHTS', 'w') as f: 32 | for w in self._weights: 33 | f.write('{:20.16f}'.format(w)) 34 | f.write('\n') 35 | 36 | def _write_groups(self): 37 | with open('GROUPS', 'w') as f: 38 | for g in self._groups: 39 | f.write('{:6d}'.format(g)) 40 | f.write('\n') 41 | 42 | def get_qpoints(self): 43 | return self._qpoints 44 | 45 | def get_weights(self): 46 | return self._weights 47 | 48 | def get_groups(self): 49 | return self._groups 50 | -------------------------------------------------------------------------------- /upho/structure/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuzie007/upho/d06643cbfb83b0bd0b318aa73849d431d43c5e35/upho/structure/__init__.py -------------------------------------------------------------------------------- /upho/structure/symtools.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import spglib 3 | 4 | 5 | def get_rotations_cart(atoms): 6 | cell = atoms.get_cell() 7 | dataset = spglib.get_symmetry_dataset(atoms) 8 | rotations = dataset["rotations"] 9 | 10 | rotations_cart = [ 11 | np.dot(np.dot(cell.T, r), np.linalg.inv(cell.T)) for r in rotations 12 | ] 13 | rotations_cart = np.array(rotations_cart) 14 | 15 | return rotations_cart 16 | -------------------------------------------------------------------------------- /upho/structure/unfolder_symmetry.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from phonopy.structure.symmetry import Symmetry 3 | 4 | 5 | class UnfolderSymmetry(Symmetry): 6 | def create_little_group(self, kpoint): 7 | rotations = self._symmetry_operations["rotations"] 8 | translations = self._symmetry_operations["translations"] 9 | lattice = self._cell.get_cell() 10 | 11 | rotations_kpoint = [] 12 | translations_kpoint = [] 13 | for r, t in zip(rotations, translations): 14 | diff = np.dot(kpoint, r) - kpoint 15 | diff -= np.rint(diff) 16 | dist = np.linalg.norm(np.dot(np.linalg.inv(lattice), diff)) 17 | if dist < self._symprec: 18 | rotations_kpoint.append(r) 19 | translations_kpoint.append(t) 20 | 21 | return np.array(rotations_kpoint), np.array(translations_kpoint) 22 | 23 | get_group_of_wave_vector = create_little_group 24 | 25 | def create_star(self, kpoint): 26 | """ 27 | Create the star of the given kpoint 28 | 29 | Parameters 30 | ---------- 31 | kpoint : Reciprocal space point 32 | 33 | Returns 34 | ------- 35 | star : n x 3 array 36 | Star of the given kpoint. 37 | transformation_matrices : n x 3 x 3 array 38 | Matrices to obtain arms of the star from the given kpoint. 39 | """ 40 | rotations = self._symmetry_operations["rotations"] 41 | lattice = self._cell.get_cell() 42 | 43 | def get_dist(tmp, arm): 44 | diff = tmp - arm 45 | # diff -= np.rint(diff) # TODO(ikeda): Check definition. 46 | dist = np.linalg.norm(np.dot(np.linalg.inv(lattice), diff)) 47 | return dist 48 | 49 | star = [] 50 | transformation_matrices = [] 51 | for r in rotations: 52 | tmp = np.dot(kpoint, r) 53 | if all(get_dist(tmp, arm) > self._symprec for arm in star): 54 | star.append(tmp) 55 | transformation_matrices.append(r) 56 | 57 | return np.array(star), np.array(transformation_matrices) 58 | --------------------------------------------------------------------------------