mirror of https://github.com/phonopy/phonopy.git
Merge branch 'refactoring' into develop
This commit is contained in:
commit
ac7abb7e4b
|
@ -6,29 +6,28 @@ jobs:
|
|||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
max-parallel: 5
|
||||
matrix:
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Add conda to system path
|
||||
run: |
|
||||
# $CONDA is an environment variable pointing to the root of the miniconda directory
|
||||
echo $CONDA/bin >> $GITHUB_PATH
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
conda install --yes -c conda-forge python=3.8
|
||||
conda install --yes -c conda-forge gcc_linux-64 gxx_linux-64 matplotlib-base pyyaml scipy numpy spglib h5py setuptools_scm conda-build pip pytest codecov pytest-cov pytest
|
||||
conda install --yes -c conda-forge python=${{ matrix.python-version }}
|
||||
conda install --yes -c conda-forge gcc_linux-64 gxx_linux-64 matplotlib-base pyyaml scipy numpy spglib h5py pip pytest codecov pytest-cov pytest
|
||||
- name: Install cp2k-input-tools
|
||||
run: |
|
||||
pip install cp2k-input-tools
|
||||
- name: Setup phonopy
|
||||
run: |
|
||||
./get_nanoversion.sh
|
||||
cat __nanoversion__.txt
|
||||
python setup.py build
|
||||
pip install -e .
|
||||
- name: Test with pytest
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
name: publish PyPI and TestPyPI
|
||||
|
||||
on: push
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- rc
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
max-parallel: 5
|
||||
matrix:
|
||||
python-version: [3.9, ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Make sdist
|
||||
run: |
|
||||
./get_nanoversion.sh
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# See https://pre-commit.com for more informatio
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.0.1
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
exclude: ^conda/
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: |
|
||||
(?x)^(
|
||||
test/phonon/test_irreps.py|
|
||||
test/qha/test_electron.py|
|
||||
phonopy/interface/cp2k.py
|
||||
)$
|
||||
args:
|
||||
- "--max-line-length=88"
|
||||
- "--ignore=E203,W503"
|
||||
- id: flake8
|
||||
files: |
|
||||
(?x)^(
|
||||
test/phonon/test_irreps.py|
|
||||
test/qha/test_electron.py|
|
||||
phonopy/interface/cp2k.py
|
||||
)$
|
||||
args:
|
||||
- "--max-line-length=88"
|
||||
- "--ignore=E203,W503,E501"
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.9b0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
- --line-length=88
|
||||
|
||||
- repo: https://github.com/pycqa/pydocstyle
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: pydocstyle
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.8.0
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
|
@ -33,6 +33,13 @@ repository.
|
|||
"python.linting.pycodestyleEnabled": false,
|
||||
"python.linting.pydocstyleEnabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.blackArgs": ["--line-length=88"],
|
||||
"python.sortImports.args": ["--profile", "black"],
|
||||
"[python]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
|
|
@ -93,4 +93,3 @@ important part of the implementation is the symmetry handling. In
|
|||
fropho, at first the symmetry finder in Abinit code was employed, but
|
||||
later the symmetry finder was replaced by spglib
|
||||
(http://spglib.sourceforge.net/).
|
||||
|
||||
|
|
125
doc/conf.py
125
doc/conf.py
|
@ -1,3 +1,4 @@
|
|||
"""Sphinx configuration of phonopy documentation."""
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# phonopy documentation build configuration file, created by
|
||||
|
@ -22,69 +23,69 @@ import sphinx_bootstrap_theme
|
|||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.mathjax']
|
||||
extensions = ["sphinx.ext.mathjax"]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
source_encoding = 'utf-8'
|
||||
source_encoding = "utf-8"
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = u'phonopy'
|
||||
copyright = u'2009, Atsushi Togo'
|
||||
project = u"phonopy"
|
||||
copyright = u"2009, Atsushi Togo"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.11'
|
||||
version = "2.11"
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.11.0'
|
||||
release = "2.11.0"
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
# unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['_build']
|
||||
exclude_trees = ["_build"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
# modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
@ -93,20 +94,18 @@ pygments_style = 'sphinx'
|
|||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme = 'default'
|
||||
# html_theme = 'nature'
|
||||
html_theme = 'bootstrap'
|
||||
html_theme = "bootstrap"
|
||||
# html_theme = 'sphinxbootstrap4theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
# html_theme_options = {}
|
||||
html_theme_options = {
|
||||
# Navigation bar title. (Default: ``project`` value)
|
||||
'navbar_title': "Phonopy",
|
||||
|
||||
"navbar_title": "Phonopy",
|
||||
# Tab name for entire site. (Default: "Site")
|
||||
'navbar_site_name': "Site",
|
||||
|
||||
"navbar_site_name": "Site",
|
||||
# A list of tuples containing pages or urls to link to.
|
||||
# Valid tuples should be in the following forms:
|
||||
# (name, page) # a link to a page
|
||||
|
@ -122,17 +121,13 @@ html_theme_options = {
|
|||
# ("Tags", "setting-tags"),
|
||||
# ("Options", "command-options"),
|
||||
# ],
|
||||
|
||||
# Render the next and previous page links in navbar. (Default: true)
|
||||
'navbar_sidebarrel': True,
|
||||
|
||||
"navbar_sidebarrel": True,
|
||||
# Render the current pages TOC in the navbar. (Default: true)
|
||||
'navbar_pagenav': True,
|
||||
|
||||
"navbar_pagenav": True,
|
||||
# Global TOC depth for "site" navbar tab. (Default: 1)
|
||||
# Switching to -1 shows all levels.
|
||||
'globaltoc_depth': 1,
|
||||
|
||||
"globaltoc_depth": 1,
|
||||
# Include hidden TOCs in Site navbar?
|
||||
#
|
||||
# Note: If this is "false", you cannot have mixed ``:hidden:`` and
|
||||
|
@ -140,32 +135,27 @@ html_theme_options = {
|
|||
# will break.
|
||||
#
|
||||
# Values: "true" (default) or "false"
|
||||
'globaltoc_includehidden': "true",
|
||||
|
||||
"globaltoc_includehidden": "true",
|
||||
# HTML navbar class (Default: "navbar") to attach to <div> element.
|
||||
# For black navbar, do "navbar navbar-inverse"
|
||||
# 'navbar_class': "navbar navbar-inverse",
|
||||
'navbar_class': "navbar",
|
||||
|
||||
"navbar_class": "navbar",
|
||||
# Fix navigation bar to top of page?
|
||||
# Values: "true" (default) or "false"
|
||||
'navbar_fixed_top': "true",
|
||||
|
||||
"navbar_fixed_top": "true",
|
||||
# Location of link to source.
|
||||
# Options are "nav" (default), "footer" or anything else to exclude.
|
||||
# 'source_link_position': "nav",
|
||||
'source_link_position': "footer",
|
||||
|
||||
"source_link_position": "footer",
|
||||
# Bootswatch (http://bootswatch.com/) theme.
|
||||
#
|
||||
# Options are nothing with "" (default) or the name of a valid theme
|
||||
# such as "amelia" or "cosmo".
|
||||
# 'bootswatch_theme': "united",
|
||||
'bootswatch_theme': "cerulean",
|
||||
|
||||
"bootswatch_theme": "cerulean",
|
||||
# Choose Bootstrap version.
|
||||
# Values: "3" (default) or "2" (in quotes)
|
||||
'bootstrap_version': "3",
|
||||
"bootstrap_version": "3",
|
||||
}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
|
@ -175,10 +165,10 @@ html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
|
|||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = 'Phonopy v.%s' % release
|
||||
html_title = "Phonopy v.%s" % release
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sideba.
|
||||
|
@ -187,80 +177,79 @@ html_title = 'Phonopy v.%s' % release
|
|||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
# html_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
# html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'phonopydoc'
|
||||
htmlhelp_basename = "phonopydoc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
# latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
# latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('documentation', 'phonopy.tex', u'phonopy manual',
|
||||
u'Atsushi Togo', 'manual'),
|
||||
("documentation", "phonopy.tex", u"phonopy manual", u"Atsushi Togo", "manual"),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
# latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
# latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
# latex_use_modindex = True
|
||||
|
|
|
@ -134,8 +134,7 @@ DFPT
|
|||
Currently there are several many implementations such as `Abinit
|
||||
<http://www.abinit.org/>`_, `Quantum espresso
|
||||
<http://www.quantum-espresso.org/>`_, `Elk
|
||||
<http://elk.sourceforge.net/>`_, `Fleur
|
||||
<https://www.flapw.de/>`_, etc. VASP can calculate force constants
|
||||
<http://elk.sourceforge.net/>`_, etc. VASP can calculate force constants
|
||||
using DFPT however only at Gamma-point.
|
||||
|
||||
For the study of basics
|
||||
|
|
|
@ -121,4 +121,3 @@ parameter is not needed.
|
|||
For further settings and command options, see the general Phonopy documentation
|
||||
:ref:`setting_tags` and :ref:`command_options`, respectively, and
|
||||
for examples, see :ref:`examples_link`.
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""Example of QHA calculation by Al."""
|
||||
import numpy as np
|
||||
import yaml
|
||||
from yaml import CLoader as Loader
|
||||
import numpy as np
|
||||
|
||||
from phonopy import PhonopyQHA
|
||||
|
||||
volumes = []
|
||||
|
@ -15,24 +15,25 @@ for line in open("e-v.dat"):
|
|||
entropy = []
|
||||
cv = []
|
||||
fe = []
|
||||
for index in range(-5,6):
|
||||
for index in range(-5, 6):
|
||||
filename = "thermal_properties.yaml-%d" % index
|
||||
print("Reading %s" % filename)
|
||||
thermal_properties = yaml.load(open(filename),
|
||||
Loader=Loader)['thermal_properties']
|
||||
temperatures = [v['temperature'] for v in thermal_properties]
|
||||
cv.append([v['heat_capacity'] for v in thermal_properties])
|
||||
entropy.append([v['entropy'] for v in thermal_properties])
|
||||
fe.append([v['free_energy'] for v in thermal_properties])
|
||||
thermal_properties = yaml.load(open(filename), Loader=Loader)["thermal_properties"]
|
||||
temperatures = [v["temperature"] for v in thermal_properties]
|
||||
cv.append([v["heat_capacity"] for v in thermal_properties])
|
||||
entropy.append([v["entropy"] for v in thermal_properties])
|
||||
fe.append([v["free_energy"] for v in thermal_properties])
|
||||
|
||||
qha = PhonopyQHA(volumes,
|
||||
qha = PhonopyQHA(
|
||||
volumes,
|
||||
energies,
|
||||
temperatures=temperatures,
|
||||
free_energy=np.transpose(fe),
|
||||
cv=np.transpose(cv),
|
||||
entropy=np.transpose(entropy),
|
||||
t_max=400,
|
||||
verbose=True)
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
# qha.plot_helmholtz_volume().show()
|
||||
# qha.plot_volume_temperature().show()
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -378.9467176
|
||||
entropy: 289.4736650
|
||||
heat_capacity: 99.5473036
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -366.7327306
|
||||
entropy: 283.3949646
|
||||
heat_capacity: 99.5190804
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -355.1311761
|
||||
entropy: 277.6245626
|
||||
heat_capacity: 99.4887894
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -343.8208732
|
||||
entropy: 272.0025819
|
||||
heat_capacity: 99.4557211
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -333.0629769
|
||||
entropy: 266.6590471
|
||||
heat_capacity: 99.4204298
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -391.4467304
|
||||
entropy: 295.6979666
|
||||
heat_capacity: 99.5729516
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -404.3758647
|
||||
entropy: 302.1390626
|
||||
heat_capacity: 99.5963768
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -417.8333877
|
||||
entropy: 308.8463804
|
||||
heat_capacity: 99.6177832
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -431.7193088
|
||||
entropy: 315.7698942
|
||||
heat_capacity: 99.6372002
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -446.4389062
|
||||
entropy: 323.1118308
|
||||
heat_capacity: 99.6550383
|
||||
|
||||
|
|
|
@ -5015,4 +5015,3 @@ thermal_properties:
|
|||
free_energy: -462.0680669
|
||||
entropy: 330.9100940
|
||||
heat_capacity: 99.6713363
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import phonopy
|
||||
"""Example by corundum Al2O3."""
|
||||
import numpy as np
|
||||
|
||||
phonon = phonopy.load(unitcell_filename="POSCAR-unitcell",
|
||||
supercell_matrix=[2, 2, 1],
|
||||
log_level=1)
|
||||
import phonopy
|
||||
|
||||
phonon = phonopy.load(
|
||||
unitcell_filename="POSCAR-unitcell", supercell_matrix=[2, 2, 1], log_level=1
|
||||
)
|
||||
print("Space group: %s" % phonon.symmetry.get_international_table())
|
||||
|
||||
# Example to obtain dynamical matrix
|
||||
|
@ -12,7 +14,7 @@ print(dmat)
|
|||
|
||||
# Example of band structure calculation
|
||||
bands = []
|
||||
q_start = np.array([1./3, 1./3, 0])
|
||||
q_start = np.array([1.0 / 3, 1.0 / 3, 0])
|
||||
q_end = np.array([0, 0, 0])
|
||||
band = []
|
||||
for i in range(51):
|
||||
|
@ -20,30 +22,26 @@ for i in range(51):
|
|||
bands.append(band)
|
||||
|
||||
q_start = np.array([0, 0, 0])
|
||||
q_end = np.array([1./3, 1./3, 1./2])
|
||||
q_end = np.array([1.0 / 3, 1.0 / 3, 1.0 / 2])
|
||||
band = []
|
||||
for i in range(51):
|
||||
band.append(q_start + (q_end - q_start) / 50 * i)
|
||||
bands.append(band)
|
||||
|
||||
print("\nPhonon dispersion:")
|
||||
phonon.run_band_structure(bands,
|
||||
with_eigenvectors=True,
|
||||
labels=["X", r"$\Gamma$", "L"])
|
||||
phonon.run_band_structure(bands, with_eigenvectors=True, labels=["X", r"$\Gamma$", "L"])
|
||||
band_plot = phonon.plot_band_structure()
|
||||
band_plot.show()
|
||||
|
||||
bs = phonon.get_band_structure_dict()
|
||||
distances = bs['distances']
|
||||
frequencies = bs['frequencies']
|
||||
qpoints = bs['qpoints']
|
||||
distances = bs["distances"]
|
||||
frequencies = bs["frequencies"]
|
||||
qpoints = bs["qpoints"]
|
||||
|
||||
for (qs_at_segments,
|
||||
dists_at_segments,
|
||||
freqs_at_segments) in zip(qpoints, distances, frequencies):
|
||||
for (qs_at_segments, dists_at_segments, freqs_at_segments) in zip(
|
||||
qpoints, distances, frequencies
|
||||
):
|
||||
|
||||
for q, d, f in zip(qs_at_segments,
|
||||
dists_at_segments,
|
||||
freqs_at_segments):
|
||||
for q, d, f in zip(qs_at_segments, dists_at_segments, freqs_at_segments):
|
||||
print("# %f %f %f" % tuple(q))
|
||||
print(("%s " + "%f " * len(f)) % ((d,) + tuple(f)))
|
||||
|
|
|
@ -24,4 +24,3 @@ SYMMETRY_TOL : 0.000001
|
|||
kpoint_mp_grid : 8 8 8
|
||||
|
||||
kpoint_mp_offset : 0.00000000000000 0.00000000000000 0.00000000000000
|
||||
|
||||
|
|
|
@ -120,4 +120,3 @@ SYMMETRY_TOL : 0.001000
|
|||
kpoint_mp_grid : 8 8 8
|
||||
|
||||
kpoint_mp_offset : 0.500000000000000 0.500000000000000 0.500000000000000
|
||||
|
||||
|
|
|
@ -39,4 +39,3 @@ AtomicCoordinatesFormat NotScaledCartesianAng
|
|||
0.00000000000000 0.00000000000000 0.00000000000000 1 12.011000
|
||||
1.27500000000000 0.73612159321677 0.00000000000000 1 12.011000
|
||||
%endblock AtomicCoordinatesAndAtomicSpecies
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import phonopy
|
||||
"""Example of calculation of irreps of MgB2."""
|
||||
import numpy as np
|
||||
|
||||
phonon = phonopy.load(unitcell_filename="POSCAR-unitcell",
|
||||
supercell_matrix=[3, 3, 2])
|
||||
import phonopy
|
||||
|
||||
phonon = phonopy.load(unitcell_filename="POSCAR-unitcell", supercell_matrix=[3, 3, 2])
|
||||
print("Space group: %s" % phonon.symmetry.get_international_table())
|
||||
|
||||
# Character table
|
||||
phonon.set_irreps([1./3, 1./3, 0], 1e-4)
|
||||
phonon.set_irreps([1.0 / 3, 1.0 / 3, 0], 1e-4)
|
||||
ct = phonon.get_irreps()
|
||||
band_indices = ct.get_band_indices()
|
||||
characters = np.rint(ct.get_characters()).real
|
||||
|
|
|
@ -23,4 +23,3 @@ SYMMETRY_TOL : 0.000001
|
|||
Na ../Na.upf
|
||||
Cl ../Cl.upf
|
||||
%ENDBLOCK SPECIES_POT
|
||||
|
||||
|
|
|
@ -1000,4 +1000,3 @@ SYMMETRY_TOL : 0.001000
|
|||
kpoint_mp_grid : 6 6 6
|
||||
|
||||
kpoint_mp_offset : 0.500000000000000 0.500000000000000 0.500000000000000
|
||||
|
||||
|
|
|
@ -1,35 +1,38 @@
|
|||
"""Example of band structure and group velocity calculation by NaCl."""
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
|
||||
import phonopy
|
||||
|
||||
|
||||
def append_band(bands, q_start, q_end):
|
||||
def _append_band(bands, q_start, q_end):
|
||||
band = []
|
||||
nq = 51
|
||||
for i in range(nq):
|
||||
band.append(np.array(q_start) +
|
||||
(np.array(q_end) - np.array(q_start)) / (nq - 1) * i)
|
||||
band.append(
|
||||
np.array(q_start) + (np.array(q_end) - np.array(q_start)) / (nq - 1) * i
|
||||
)
|
||||
bands.append(band)
|
||||
|
||||
|
||||
phonon = phonopy.load(unitcell_filename="POSCAR-unitcell",
|
||||
phonon = phonopy.load(
|
||||
unitcell_filename="POSCAR-unitcell",
|
||||
born_filename="BORN",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
supercell_matrix=[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]])
|
||||
supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
)
|
||||
|
||||
bands = []
|
||||
append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.0, 0.0])
|
||||
append_band(bands, [0.5, 0.0, 0.0], [0.5, 0.5, 0.0])
|
||||
append_band(bands, [0.5, 0.5, 0.0], [0.0, 0.0, 0.0])
|
||||
append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
|
||||
bands = List[List]
|
||||
_append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.0, 0.0])
|
||||
_append_band(bands, [0.5, 0.0, 0.0], [0.5, 0.5, 0.0])
|
||||
_append_band(bands, [0.5, 0.5, 0.0], [0.0, 0.0, 0.0])
|
||||
_append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
|
||||
phonon.run_band_structure(bands, with_group_velocities=True)
|
||||
band_dict = phonon.get_band_structure_dict()
|
||||
frequencies = band_dict['frequencies']
|
||||
group_velocities = band_dict['group_velocities']
|
||||
frequencies = band_dict["frequencies"]
|
||||
group_velocities = band_dict["group_velocities"]
|
||||
print(len(frequencies))
|
||||
print(frequencies[0].shape)
|
||||
print(len(group_velocities))
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
"""NaCl band structure calculation example."""
|
||||
import phonopy
|
||||
from phonopy.phonon.band_structure import get_band_qpoints
|
||||
|
||||
phonon = phonopy.load(supercell_matrix=[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]],
|
||||
phonon = phonopy.load(
|
||||
supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
unitcell_filename="POSCAR-unitcell",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
born_filename="BORN")
|
||||
born_filename="BORN",
|
||||
)
|
||||
points = get_band_qpoints(
|
||||
[[[0.5, 0, 0.5], [0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.25, 0.75]]], 51)
|
||||
phonon.run_band_structure(points, labels=['X', '$\Gamma$', 'L', 'W'])
|
||||
[[[0.5, 0, 0.5], [0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.25, 0.75]]], 51
|
||||
)
|
||||
phonon.run_band_structure(points, labels=["X", r"$\Gamma$", "L", "W"])
|
||||
phonon.plot_band_structure().show()
|
||||
|
|
|
@ -1,35 +1,37 @@
|
|||
#!/usr/bin/env python
|
||||
"""NaCl example to obtain dynamical matrix."""
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
import phonopy
|
||||
import yaml
|
||||
import numpy as np
|
||||
|
||||
phonon = phonopy.load(supercell_matrix=[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]],
|
||||
phonon = phonopy.load(
|
||||
supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
unitcell_filename="POSCAR-unitcell",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
born_filename="BORN")
|
||||
born_filename="BORN",
|
||||
)
|
||||
|
||||
q = [0.1, 0.1, 0.1]
|
||||
dynmat = phonon.get_dynamical_matrix_at_q(q)
|
||||
print(dynmat)
|
||||
phonon.run_qpoints(q, with_dynamical_matrices=True)
|
||||
print(phonon.get_qpoints_dict()['frequencies'][0])
|
||||
print(phonon.get_qpoints_dict()["frequencies"][0])
|
||||
phonon.write_yaml_qpoints_phonon()
|
||||
|
||||
data = yaml.load(open("qpoints.yaml"), Loader=yaml.FullLoader)
|
||||
dynmat_from_yaml = []
|
||||
dynmat_data = data['phonon'][0]['dynamical_matrix']
|
||||
dynmat_data = data["phonon"][0]["dynamical_matrix"]
|
||||
for row in dynmat_data:
|
||||
vals = np.reshape(row, (-1, 2))
|
||||
dynmat_from_yaml.append(vals[:, 0] + vals[:, 1] * 1j)
|
||||
dynmat_from_yaml = np.array(dynmat_from_yaml)
|
||||
print(dynmat_from_yaml)
|
||||
eigvals, eigvecs, = np.linalg.eigh(dynmat)
|
||||
(
|
||||
eigvals,
|
||||
eigvecs,
|
||||
) = np.linalg.eigh(dynmat)
|
||||
frequencies = np.sqrt(np.abs(eigvals.real)) * np.sign(eigvals.real)
|
||||
conversion_factor_to_THz = 15.633302
|
||||
print(frequencies * conversion_factor_to_THz)
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import numpy as np
|
||||
import phonopy
|
||||
"""Group velocity example by NaCl."""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
phonon = phonopy.load(unitcell_filename="POSCAR-unitcell",
|
||||
import phonopy
|
||||
|
||||
phonon = phonopy.load(
|
||||
unitcell_filename="POSCAR-unitcell",
|
||||
born_filename="BORN",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
supercell_matrix=[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]])
|
||||
supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
)
|
||||
phonon.run_mesh([31, 31, 31], with_group_velocities=True)
|
||||
phonon_mesh = phonon.get_mesh_dict()
|
||||
frequencies = phonon_mesh['frequencies']
|
||||
group_velocity = phonon_mesh['group_velocities']
|
||||
frequencies = phonon_mesh["frequencies"]
|
||||
group_velocity = phonon_mesh["group_velocities"]
|
||||
gv_norm = np.sqrt((group_velocity ** 2).sum(axis=2))
|
||||
for i, (f, g) in enumerate(zip(frequencies.T, gv_norm.T)):
|
||||
plt.plot(f, g, 'o', label=('band%d' % (i + 1)))
|
||||
plt.plot(f, g, "o", label=("band%d" % (i + 1)))
|
||||
plt.legend()
|
||||
plt.xlabel("Frequency (THz)")
|
||||
plt.ylabel("|group-velocity| (THz.A)")
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
"""Example to read and write FORCE_CONSTANTS file."""
|
||||
import phonopy
|
||||
from phonopy.file_IO import parse_FORCE_CONSTANTS, write_FORCE_CONSTANTS
|
||||
|
||||
phonon = phonopy.load(supercell_matrix=[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]],
|
||||
phonon = phonopy.load(
|
||||
supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
unitcell_filename="POSCAR-unitcell",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
born_filename="BORN")
|
||||
write_FORCE_CONSTANTS(phonon.get_force_constants(),
|
||||
filename="FORCE_CONSTANTS")
|
||||
born_filename="BORN",
|
||||
)
|
||||
write_FORCE_CONSTANTS(phonon.force_constants, filename="FORCE_CONSTANTS")
|
||||
|
||||
force_constants = parse_FORCE_CONSTANTS()
|
||||
phonon.force_constants = force_constants
|
||||
phonon.symmetrize_force_constants()
|
||||
write_FORCE_CONSTANTS(phonon.force_constants,
|
||||
filename="FORCE_CONSTANTS_NEW")
|
||||
write_FORCE_CONSTANTS(phonon.force_constants, filename="FORCE_CONSTANTS_NEW")
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
"""Example to obtain PhonopyYaml instance."""
|
||||
import phonopy
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
|
||||
phonon = phonopy.load(supercell_matrix=[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]],
|
||||
phonon = phonopy.load(
|
||||
supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
unitcell_filename="POSCAR-unitcell",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
born_filename="BORN")
|
||||
phpy_yaml = PhonopyYaml(calculator='vasp',
|
||||
settings={'force_constants': True})
|
||||
born_filename="BORN",
|
||||
)
|
||||
phpy_yaml = PhonopyYaml(calculator="vasp", settings={"force_constants": True})
|
||||
phpy_yaml.set_phonon_info(phonon)
|
||||
print(phpy_yaml)
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import numpy as np
|
||||
from phonopy import Phonopy
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
from phonopy.file_IO import parse_FORCE_SETS, parse_BORN
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
"""Example of NaCl calculation."""
|
||||
from typing import List
|
||||
|
||||
def append_band(bands, q_start, q_end):
|
||||
import numpy as np
|
||||
|
||||
from phonopy import Phonopy
|
||||
from phonopy.file_IO import parse_BORN, parse_FORCE_SETS
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
|
||||
# from phonopy.structure.atoms import PhonopyAtoms
|
||||
|
||||
|
||||
def _append_band(bands, q_start, q_end):
|
||||
band = []
|
||||
for i in range(51):
|
||||
band.append(np.array(q_start) +
|
||||
(np.array(q_end) - np.array(q_start)) / 50 * i)
|
||||
band.append(np.array(q_start) + (np.array(q_end) - np.array(q_start)) / 50 * i)
|
||||
bands.append(band)
|
||||
|
||||
|
||||
|
@ -26,13 +31,11 @@ unitcell = read_vasp("POSCAR-unitcell")
|
|||
# [0, 0.5, 0],
|
||||
# [0, 0, 0.5]])
|
||||
|
||||
phonon = Phonopy(unitcell,
|
||||
[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]])
|
||||
phonon = Phonopy(
|
||||
unitcell,
|
||||
[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
)
|
||||
|
||||
symmetry = phonon.get_symmetry()
|
||||
print("Space group: %s" % symmetry.get_international_table())
|
||||
|
@ -61,34 +64,34 @@ nac_params = parse_BORN(primitive, filename="BORN")
|
|||
phonon.nac_params = nac_params
|
||||
|
||||
# BAND = 0.0 0.0 0.0 0.5 0.0 0.0 0.5 0.5 0.0 0.0 0.0 0.0 0.5 0.5 0.5
|
||||
bands = []
|
||||
append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.0, 0.0])
|
||||
append_band(bands, [0.5, 0.0, 0.0], [0.5, 0.5, 0.0])
|
||||
append_band(bands, [0.5, 0.5, 0.0], [0.0, 0.0, 0.0])
|
||||
append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
|
||||
bands = List[List]
|
||||
_append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.0, 0.0])
|
||||
_append_band(bands, [0.5, 0.0, 0.0], [0.5, 0.5, 0.0])
|
||||
_append_band(bands, [0.5, 0.5, 0.0], [0.0, 0.0, 0.0])
|
||||
_append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
|
||||
phonon.set_band_structure(bands)
|
||||
band_dict = phonon.get_band_structure_dict()
|
||||
q_points = band_dict['qpoints']
|
||||
distances = band_dict['distances']
|
||||
frequencies = band_dict['frequencies']
|
||||
eigvecs = band_dict['eigenvectors']
|
||||
q_points = band_dict["qpoints"]
|
||||
distances = band_dict["distances"]
|
||||
frequencies = band_dict["frequencies"]
|
||||
eigvecs = band_dict["eigenvectors"]
|
||||
for q_path, d_path, freq_path in zip(q_points, distances, frequencies):
|
||||
for q, d, freq in zip(q_path, d_path, freq_path):
|
||||
print(("%10.5f %5.2f %5.2f %5.2f " + (" %7.3f" * len(freq)))
|
||||
% ((d, q[0], q[1], q[2]) + tuple(freq)))
|
||||
print(
|
||||
("%10.5f %5.2f %5.2f %5.2f " + (" %7.3f" * len(freq)))
|
||||
% ((d, q[0], q[1], q[2]) + tuple(freq))
|
||||
)
|
||||
|
||||
phonon.plot_band_structure().show()
|
||||
|
||||
# Mesh sampling 20x20x20
|
||||
phonon.run_mesh(mesh=[20, 20, 20])
|
||||
phonon.run_thermal_properties(t_step=10,
|
||||
t_max=1000,
|
||||
t_min=0)
|
||||
phonon.run_thermal_properties(t_step=10, t_max=1000, t_min=0)
|
||||
|
||||
# DOS
|
||||
phonon.run_total_dos(sigma=0.1)
|
||||
dos_dict = phonon.get_total_dos_dict()
|
||||
for omega, dos in zip(dos_dict['frequency_points'], dos_dict['total_dos']):
|
||||
for omega, dos in zip(dos_dict["frequency_points"], dos_dict["total_dos"]):
|
||||
print("%15.7f%15.7f" % (omega, dos))
|
||||
phonon.plot_total_dos().show()
|
||||
|
||||
|
@ -96,21 +99,19 @@ phonon.plot_total_dos().show()
|
|||
tprop_dict = phonon.get_thermal_properties_dict()
|
||||
|
||||
for t, free_energy, entropy, cv in zip(
|
||||
tprop_dict['temperatures'],
|
||||
tprop_dict['free_energy'],
|
||||
tprop_dict['entropy'],
|
||||
tprop_dict['heat_capacity']):
|
||||
tprop_dict["temperatures"],
|
||||
tprop_dict["free_energy"],
|
||||
tprop_dict["entropy"],
|
||||
tprop_dict["heat_capacity"],
|
||||
):
|
||||
print(("%12.3f " + "%15.7f" * 3) % (t, free_energy, entropy, cv))
|
||||
phonon.plot_thermal_properties().show()
|
||||
|
||||
# PDOS
|
||||
phonon.run_mesh(mesh=[10, 10, 10],
|
||||
is_mesh_symmetry=False,
|
||||
with_eigenvectors=True)
|
||||
phonon.run_mesh(mesh=[10, 10, 10], is_mesh_symmetry=False, with_eigenvectors=True)
|
||||
phonon.run_projected_dos(use_tetrahedron_method=True)
|
||||
pdos_dict = phonon.get_projected_dos_dict()
|
||||
omegas = pdos_dict['frequency_points']
|
||||
pdos = pdos_dict['projected_dos']
|
||||
omegas = pdos_dict["frequency_points"]
|
||||
pdos = pdos_dict["projected_dos"]
|
||||
pdos_indices = [[0], [1]]
|
||||
phonon.plot_projected_dos(pdos_indices=pdos_indices,
|
||||
legend=pdos_indices).show()
|
||||
phonon.plot_projected_dos(pdos_indices=pdos_indices, legend=pdos_indices).show()
|
||||
|
|
|
@ -24,4 +24,3 @@ for the CRYSTAL interface, so, the -c crystal.o parameter is not needed
|
|||
|
||||
Create a formatted plot (here band.yaml is in cm^{-1) units):
|
||||
phonopy-bandplot --fmin=0 --line --ylabel="Frequency (cm\$^{-1}\$)" --band-labels="`grep BAND_LABELS band.conf | cut -d= -f2-`" -o dispersion.pdf
|
||||
|
||||
|
|
|
@ -1,34 +1,55 @@
|
|||
from phonopy import Phonopy
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
from phonopy.file_IO import parse_FORCE_SETS
|
||||
"""QHA example of Si."""
|
||||
import tarfile
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
def get_frequency(poscar_filename, force_sets_filename):
|
||||
from phonopy import Phonopy
|
||||
from phonopy.interface.vasp import Vasprun, read_vasp
|
||||
|
||||
|
||||
def get_force_sets(index):
|
||||
"""Parse vasprun.xml and return forces."""
|
||||
with tarfile.open("vasprun_xmls.tar.lzma", "r:xz") as tr:
|
||||
with tr.extractfile("vasprun_xmls/vasprun.xml-%d" % index) as fp:
|
||||
vasprun = Vasprun(fp, use_expat=True)
|
||||
forces = vasprun.read_forces()
|
||||
return forces
|
||||
|
||||
|
||||
def get_frequency(poscar_filename, force_sets):
|
||||
"""Calculate phonons and return frequencies."""
|
||||
unitcell = read_vasp(poscar_filename)
|
||||
volume = unitcell.get_volume()
|
||||
phonon = Phonopy(unitcell,
|
||||
[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]])
|
||||
force_sets = parse_FORCE_SETS(filename=force_sets_filename)
|
||||
phonon.set_displacement_dataset(force_sets)
|
||||
volume = unitcell.volume
|
||||
phonon = Phonopy(
|
||||
unitcell,
|
||||
[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
)
|
||||
disps = np.zeros_like(force_sets)
|
||||
disps[0] = [0.01, 0, 0]
|
||||
phonon.dataset = {
|
||||
"natom": len(force_sets),
|
||||
"first_atoms": [
|
||||
{"number": 0, "displacement": [0.01, 0, 0], "forces": force_sets}
|
||||
],
|
||||
}
|
||||
phonon.produce_force_constants()
|
||||
return phonon.get_frequencies([0.5, 0.5, 0]), volume
|
||||
|
||||
frequencies = []
|
||||
volumes = []
|
||||
for i in range(-10, 6):
|
||||
|
||||
def main():
|
||||
"""Run QHA."""
|
||||
frequencies = []
|
||||
volumes = []
|
||||
for i in range(-5, 6):
|
||||
poscar_filename = "POSCAR-%d" % i
|
||||
force_sets_filename = "FORCE_SETS-%d" % i
|
||||
fs, v = get_frequency(poscar_filename, force_sets_filename)
|
||||
force_sets = get_force_sets(i)
|
||||
fs, v = get_frequency(poscar_filename, force_sets)
|
||||
frequencies.append(fs)
|
||||
volumes.append(v)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
for freq_at_X in np.array(frequencies).T:
|
||||
for freq_at_X in np.array(frequencies).T:
|
||||
freq_squared = freq_at_X ** 2 * np.sign(freq_at_X)
|
||||
# np.sign is used to treat imaginary mode, since
|
||||
# imaginary frequency is returned as a negative value from phonopy.
|
||||
|
@ -37,5 +58,9 @@ for freq_at_X in np.array(frequencies).T:
|
|||
# print("%f %f" % (v, f2))
|
||||
# print('')
|
||||
# print('')
|
||||
plt.title("Frequeny squared (THz^2) at X-point vs volume (A^3)")
|
||||
plt.show()
|
||||
plt.title("Frequeny squared (THz^2) at X-point vs volume (A^3)")
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -35,4 +35,3 @@ PRIMITIVE_AXES is defined in band.conf to create the phonon dispersions for the
|
|||
|
||||
Create a formatted plot (here band.yaml is in cm^{-1) units):
|
||||
phonopy-bandplot --fmin=0 --line --ylabel="Frequency (cm\$^{-1}\$)" --band-labels "`grep BAND_LABELS band.conf | cut -d= -f2-`" -o dispersion.pdf
|
||||
|
||||
|
|
|
@ -8,4 +8,3 @@ $coord
|
|||
5.17898184151 0.00000000000 5.17898184151 si
|
||||
0.00000000000 5.17898184151 5.17898184151 si
|
||||
$end
|
||||
|
||||
|
|
|
@ -2002,4 +2002,3 @@
|
|||
|
||||
|
||||
2019-03-24 12:48:27.712
|
||||
|
||||
|
|
|
@ -1,50 +1,55 @@
|
|||
import numpy as np
|
||||
from phonopy import Phonopy, PhonopyGruneisen
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
from phonopy.file_IO import parse_FORCE_SETS
|
||||
"""Example to calculate mode Grueneisen parameters."""
|
||||
from typing import List
|
||||
|
||||
def append_band(bands, q_start, q_end):
|
||||
import numpy as np
|
||||
|
||||
from phonopy import Phonopy, PhonopyGruneisen
|
||||
from phonopy.file_IO import parse_FORCE_SETS
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
|
||||
|
||||
def _append_band(bands: List[List], q_start, q_end):
|
||||
band = []
|
||||
for i in range(51):
|
||||
band.append(np.array(q_start) +
|
||||
(np.array(q_end) - np.array(q_start)) / 50 * i)
|
||||
points = np.array(q_start) + (np.array(q_end) - np.array(q_start)) / 50 * i
|
||||
band.append(points.tolist())
|
||||
bands.append(band)
|
||||
|
||||
|
||||
phonons = {}
|
||||
for vol in ("orig", "plus", "minus"):
|
||||
unitcell = read_vasp("%s/POSCAR-unitcell" % vol)
|
||||
phonon = Phonopy(unitcell,
|
||||
[[2, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]])
|
||||
phonon = Phonopy(
|
||||
unitcell,
|
||||
[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
|
||||
primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]],
|
||||
)
|
||||
force_sets = parse_FORCE_SETS(filename="%s/FORCE_SETS" % vol)
|
||||
phonon.set_displacement_dataset(force_sets)
|
||||
phonon.produce_force_constants()
|
||||
phonons[vol] = phonon
|
||||
|
||||
gruneisen = PhonopyGruneisen(phonons["orig"],
|
||||
phonons["plus"],
|
||||
phonons["minus"])
|
||||
gruneisen = PhonopyGruneisen(phonons["orig"], phonons["plus"], phonons["minus"])
|
||||
|
||||
gruneisen.set_mesh([2, 2, 2])
|
||||
q_points, _, frequencies, _, gammas = gruneisen.get_mesh()
|
||||
for q, freq, g in zip(q_points, frequencies, gammas):
|
||||
print(("%5.2f %5.2f %5.2f " + (" %7.3f" * len(freq)))
|
||||
% ((q[0], q[1], q[2]) + tuple(freq)))
|
||||
print(
|
||||
("%5.2f %5.2f %5.2f " + (" %7.3f" * len(freq)))
|
||||
% ((q[0], q[1], q[2]) + tuple(freq))
|
||||
)
|
||||
print(((" " * 18) + (" %7.3f" * len(g))) % tuple(g))
|
||||
|
||||
bands = []
|
||||
append_band(bands, [0.5, 0.5, 0.0], [0.0, 0.0, 0.0])
|
||||
append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
|
||||
bands: List[List] = []
|
||||
_append_band(bands, [0.5, 0.5, 0.0], [0.0, 0.0, 0.0])
|
||||
_append_band(bands, [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
|
||||
gruneisen.set_band_structure(bands)
|
||||
|
||||
q_points, distances, frequencies, _, gammas = gruneisen.get_band_structure()
|
||||
for q_path, d_path, freq_path, g_path in zip(q_points, distances,
|
||||
frequencies, gammas):
|
||||
for q_path, d_path, freq_path, g_path in zip(q_points, distances, frequencies, gammas):
|
||||
for q, d, freq, g in zip(q_path, d_path, freq_path, g_path):
|
||||
print(("%10.5f %5.2f %5.2f %5.2f " + (" %7.3f" * len(freq)))
|
||||
% ((d, q[0], q[1], q[2]) + tuple(freq)))
|
||||
print(
|
||||
("%10.5f %5.2f %5.2f %5.2f " + (" %7.3f" * len(freq)))
|
||||
% ((d, q[0], q[1], q[2]) + tuple(freq))
|
||||
)
|
||||
print(((" " * 30) + (" %7.3f" * len(g))) % tuple(g))
|
||||
|
|
|
@ -46,4 +46,3 @@ LatticeConstant 5.430 Ang
|
|||
0.00 0.00 0.00 1 # Si 1
|
||||
0.25 0.25 0.25 1 # Si 2
|
||||
%endblock AtomicCoordinatesAndAtomicSpecies
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@ Phonon calculation for TiO2-anatase. Supercell dimension is 4x4x2 and forces wer
|
|||
Band structure is drawn with Seek-Path (pip install seekpath is needed) by
|
||||
|
||||
% phonopy band.conf -p --nac
|
||||
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
from gpaw import GPAW, PW
|
||||
from ase import Atoms
|
||||
from phonopy import Phonopy
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
import numpy as np
|
||||
|
||||
def get_gpaw(kpts_size=None):
|
||||
if kpts_size is None:
|
||||
calc = GPAW(mode=PW(300),
|
||||
kpts={'size': (2, 2, 2)})
|
||||
else:
|
||||
calc = GPAW(mode=PW(300),
|
||||
kpts={'size': kpts_size})
|
||||
return calc
|
||||
|
||||
def get_crystal():
|
||||
a = 5.404
|
||||
cell = PhonopyAtoms(symbols=(['Si'] * 8),
|
||||
cell=np.diag((a, a, a)),
|
||||
scaled_positions=[(0, 0, 0),
|
||||
(0, 0.5, 0.5),
|
||||
(0.5, 0, 0.5),
|
||||
(0.5, 0.5, 0),
|
||||
(0.25, 0.25, 0.25),
|
||||
(0.25, 0.75, 0.75),
|
||||
(0.75, 0.25, 0.75),
|
||||
(0.75, 0.75, 0.25)])
|
||||
return cell
|
||||
|
||||
def phonopy_pre_process(cell, supercell_matrix=None):
|
||||
|
||||
if supercell_matrix is None:
|
||||
smat = [[2,0,0], [0,2,0], [0,0,2]],
|
||||
else:
|
||||
smat = supercell_matrix
|
||||
phonon = Phonopy(cell,
|
||||
smat,
|
||||
primitive_matrix=[[0, 0.5, 0.5],
|
||||
[0.5, 0, 0.5],
|
||||
[0.5, 0.5, 0]])
|
||||
phonon.generate_displacements(distance=0.03)
|
||||
print("[Phonopy] Atomic displacements:")
|
||||
disps = phonon.get_displacements()
|
||||
for d in disps:
|
||||
print("[Phonopy] %d %s" % (d[0], d[1:]))
|
||||
return phonon
|
||||
|
||||
def run_gpaw(calc, phonon):
|
||||
supercells = phonon.get_supercells_with_displacements()
|
||||
# Force calculations by calculator
|
||||
set_of_forces = []
|
||||
for scell in supercells:
|
||||
cell = Atoms(symbols=scell.get_chemical_symbols(),
|
||||
scaled_positions=scell.get_scaled_positions(),
|
||||
cell=scell.get_cell(),
|
||||
pbc=True)
|
||||
cell.set_calculator(calc)
|
||||
forces = cell.get_forces()
|
||||
drift_force = forces.sum(axis=0)
|
||||
print(("[Phonopy] Drift force:" + "%11.5f" * 3) % tuple(drift_force))
|
||||
# Simple translational invariance
|
||||
for force in forces:
|
||||
force -= drift_force / forces.shape[0]
|
||||
set_of_forces.append(forces)
|
||||
return set_of_forces
|
||||
|
||||
def phonopy_post_process(phonon, set_of_forces):
|
||||
phonon.produce_force_constants(forces=set_of_forces)
|
||||
print('')
|
||||
print("[Phonopy] Phonon frequencies at Gamma:")
|
||||
for i, freq in enumerate(phonon.get_frequencies((0, 0, 0))):
|
||||
print("[Phonopy] %3d: %10.5f THz" % (i + 1, freq)) # THz
|
||||
|
||||
# DOS
|
||||
phonon.set_mesh([21, 21, 21])
|
||||
phonon.set_total_DOS(tetrahedron_method=True)
|
||||
print('')
|
||||
print("[Phonopy] Phonon DOS:")
|
||||
for omega, dos in np.array(phonon.get_total_DOS()).T:
|
||||
print("%15.7f%15.7f" % (omega, dos))
|
||||
|
||||
def main():
|
||||
cell = get_crystal()
|
||||
|
||||
# 1x1x1 supercell of conventional unit cell
|
||||
calc = get_gpaw(kpts_size=(4, 4, 4))
|
||||
phonon = phonopy_pre_process(cell, supercell_matrix=np.eye(3, dtype='intc'))
|
||||
|
||||
# # 2x2x2 supercell of conventional unit cell
|
||||
# calc = get_gpaw(kpts_size=(2, 2, 2))
|
||||
# phonon = phonopy_pre_process(cell,
|
||||
# supercell_matrix=(np.eye(3, dtype='intc') * 2))
|
||||
|
||||
set_of_forces = run_gpaw(calc, phonon)
|
||||
phonopy_post_process(phonon, set_of_forces)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,3 +1,4 @@
|
|||
"""Phonon calculation code: Phonopy."""
|
||||
# Copyright (C) 2015 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,8 +33,8 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from phonopy.version import __version__
|
||||
from phonopy.api_phonopy import Phonopy
|
||||
from phonopy.api_gruneisen import PhonopyGruneisen
|
||||
from phonopy.api_qha import PhonopyQHA
|
||||
from phonopy.cui.load import load
|
||||
from phonopy.api_gruneisen import PhonopyGruneisen # noqa F401
|
||||
from phonopy.api_phonopy import Phonopy # noqa F401
|
||||
from phonopy.api_qha import PhonopyQHA # noqa F401
|
||||
from phonopy.cui.load import load # noqa F401
|
||||
from phonopy.version import __version__ # noqa F401
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""API of mode Grueneisen parameter calculation."""
|
||||
# Copyright (C) 2015 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,17 +33,25 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from phonopy.gruneisen import GruneisenMesh
|
||||
from phonopy.gruneisen import GruneisenBandStructure
|
||||
from phonopy.gruneisen import GruneisenThermalProperties
|
||||
from phonopy.gruneisen.band_structure import GruneisenBandStructure
|
||||
from phonopy.gruneisen.mesh import GruneisenMesh
|
||||
|
||||
|
||||
class PhonopyGruneisen(object):
|
||||
def __init__(self,
|
||||
phonon,
|
||||
phonon_plus,
|
||||
phonon_minus,
|
||||
delta_strain=None):
|
||||
class PhonopyGruneisen:
|
||||
"""Class to calculate mode Grueneisen parameters."""
|
||||
|
||||
def __init__(self, phonon, phonon_plus, phonon_minus, delta_strain=None):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
phonon, phonon_plus, phonon_minus : Phonopy
|
||||
Phonopy instances of the same crystal with differet volumes,
|
||||
V_0, V_0 + dV, V_0 - dV.
|
||||
delta_strain : float, optional
|
||||
Default is None, which gives dV / V_0.
|
||||
|
||||
"""
|
||||
self._phonon = phonon
|
||||
self._phonon_plus = phonon_plus
|
||||
self._phonon_minus = phonon_minus
|
||||
|
@ -50,28 +59,31 @@ class PhonopyGruneisen(object):
|
|||
|
||||
self._mesh = None
|
||||
self._band_structure = None
|
||||
self._thermal_properties = None
|
||||
|
||||
def get_phonon(self):
|
||||
"""Return Phonopy class instance at dV=0."""
|
||||
return self._phonon
|
||||
|
||||
def set_mesh(self,
|
||||
def set_mesh(
|
||||
self,
|
||||
mesh,
|
||||
shift=None,
|
||||
is_time_reversal=True,
|
||||
is_gamma_center=False,
|
||||
is_mesh_symmetry=True):
|
||||
is_mesh_symmetry=True,
|
||||
):
|
||||
"""Set sampling mesh."""
|
||||
for phonon in (self._phonon, self._phonon_plus, self._phonon_minus):
|
||||
if phonon.get_dynamical_matrix() is None:
|
||||
if phonon.dynamical_matrix is None:
|
||||
print("Warning: Dynamical matrix has not yet built.")
|
||||
return False
|
||||
|
||||
symmetry = phonon.get_primitive_symmetry()
|
||||
rotations = symmetry.get_pointgroup_operations()
|
||||
symmetry = phonon.primitive_symmetry
|
||||
rotations = symmetry.pointgroup_operations
|
||||
self._mesh = GruneisenMesh(
|
||||
self._phonon.get_dynamical_matrix(),
|
||||
self._phonon_plus.get_dynamical_matrix(),
|
||||
self._phonon_minus.get_dynamical_matrix(),
|
||||
self._phonon.dynamical_matrix,
|
||||
self._phonon_plus.dynamical_matrix,
|
||||
self._phonon_minus.dynamical_matrix,
|
||||
mesh,
|
||||
delta_strain=self._delta_strain,
|
||||
shift=shift,
|
||||
|
@ -79,94 +91,86 @@ class PhonopyGruneisen(object):
|
|||
is_gamma_center=is_gamma_center,
|
||||
is_mesh_symmetry=is_mesh_symmetry,
|
||||
rotations=rotations,
|
||||
factor=self._phonon.get_unit_conversion_factor())
|
||||
factor=self._phonon.unit_conversion_factor,
|
||||
)
|
||||
return True
|
||||
|
||||
def get_mesh(self):
|
||||
"""Return mode Grueneisen parameters calculated on sampling mesh."""
|
||||
if self._mesh is None:
|
||||
return None
|
||||
else:
|
||||
return (self._mesh.get_qpoints(),
|
||||
return (
|
||||
self._mesh.get_qpoints(),
|
||||
self._mesh.get_weights(),
|
||||
self._mesh.get_frequencies(),
|
||||
self._mesh.get_eigenvectors(),
|
||||
self._mesh.get_gruneisen())
|
||||
self._mesh.get_gruneisen(),
|
||||
)
|
||||
|
||||
def write_yaml_mesh(self):
|
||||
"""Write mesh sampling calculation results to file in yaml."""
|
||||
self._mesh.write_yaml()
|
||||
|
||||
def write_hdf5_mesh(self):
|
||||
"""Write mesh sampling calculation results to file in hdf5."""
|
||||
self._mesh.write_hdf5()
|
||||
|
||||
def plot_mesh(self,
|
||||
cutoff_frequency=None,
|
||||
color_scheme=None,
|
||||
marker='o',
|
||||
markersize=None):
|
||||
def plot_mesh(
|
||||
self, cutoff_frequency=None, color_scheme=None, marker="o", markersize=None
|
||||
):
|
||||
"""Return pyplot of mesh sampling calculation results."""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.xaxis.set_ticks_position('both')
|
||||
ax.yaxis.set_ticks_position('both')
|
||||
ax.xaxis.set_tick_params(which='both', direction='in')
|
||||
ax.yaxis.set_tick_params(which='both', direction='in')
|
||||
self._mesh.plot(plt,
|
||||
ax.xaxis.set_ticks_position("both")
|
||||
ax.yaxis.set_ticks_position("both")
|
||||
ax.xaxis.set_tick_params(which="both", direction="in")
|
||||
ax.yaxis.set_tick_params(which="both", direction="in")
|
||||
self._mesh.plot(
|
||||
plt,
|
||||
cutoff_frequency=cutoff_frequency,
|
||||
color_scheme=color_scheme,
|
||||
marker=marker,
|
||||
markersize=markersize)
|
||||
markersize=markersize,
|
||||
)
|
||||
return plt
|
||||
|
||||
def set_band_structure(self, bands):
|
||||
"""Set band structure paths."""
|
||||
self._band_structure = GruneisenBandStructure(
|
||||
bands,
|
||||
self._phonon.get_dynamical_matrix(),
|
||||
self._phonon_plus.get_dynamical_matrix(),
|
||||
self._phonon_minus.get_dynamical_matrix(),
|
||||
self._phonon.dynamical_matrix,
|
||||
self._phonon_plus.dynamical_matrix,
|
||||
self._phonon_minus.dynamical_matrix,
|
||||
delta_strain=self._delta_strain,
|
||||
factor=self._phonon.get_unit_conversion_factor())
|
||||
factor=self._phonon.unit_conversion_factor,
|
||||
)
|
||||
|
||||
def get_band_structure(self):
|
||||
"""Return band structure calculation results."""
|
||||
band = self._band_structure
|
||||
return (band.get_qpoints(),
|
||||
return (
|
||||
band.get_qpoints(),
|
||||
band.get_distances(),
|
||||
band.get_frequencies(),
|
||||
band.get_eigenvectors(),
|
||||
band.get_gruneisen())
|
||||
band.get_gruneisen(),
|
||||
)
|
||||
|
||||
def write_yaml_band_structure(self):
|
||||
"""Write band structure calculation results to file in yaml."""
|
||||
self._band_structure.write_yaml()
|
||||
|
||||
def plot_band_structure(self,
|
||||
epsilon=1e-4,
|
||||
color_scheme=None):
|
||||
def plot_band_structure(self, epsilon=1e-4, color_scheme=None):
|
||||
"""Return pyplot of band structure calculation results."""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, axarr = plt.subplots(2, 1)
|
||||
for ax in axarr:
|
||||
ax.xaxis.set_ticks_position('both')
|
||||
ax.yaxis.set_ticks_position('both')
|
||||
ax.xaxis.set_tick_params(which='both', direction='in')
|
||||
ax.yaxis.set_tick_params(which='both', direction='in')
|
||||
self._band_structure.plot(axarr,
|
||||
epsilon=epsilon,
|
||||
color_scheme=color_scheme)
|
||||
ax.xaxis.set_ticks_position("both")
|
||||
ax.yaxis.set_ticks_position("both")
|
||||
ax.xaxis.set_tick_params(which="both", direction="in")
|
||||
ax.yaxis.set_tick_params(which="both", direction="in")
|
||||
self._band_structure.plot(axarr, epsilon=epsilon, color_scheme=color_scheme)
|
||||
return plt
|
||||
|
||||
def set_thermal_properties(self,
|
||||
volumes,
|
||||
t_step=2,
|
||||
t_max=2004,
|
||||
t_min=0,
|
||||
cutoff_frequency=None):
|
||||
self._thermal_properties = GruneisenThermalProperties(
|
||||
self._mesh,
|
||||
volumes,
|
||||
t_step=t_step,
|
||||
t_max=t_max,
|
||||
t_min=t_min,
|
||||
cutoff_frequency=cutoff_frequency)
|
||||
|
||||
def get_thermal_properties(self):
|
||||
return self._thermal_properties
|
||||
|
||||
def write_yaml_thermal_properties(self, filename='thermal_properties'):
|
||||
self._thermal_properties.write_yaml(filename=filename)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
|||
"""API for QHA calculation."""
|
||||
# Copyright (C) 2015 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,23 +34,26 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
from phonopy.qha import BulkModulus, QHA
|
||||
|
||||
from phonopy.qha.core import QHA, BulkModulus
|
||||
|
||||
|
||||
class PhonopyQHA(object):
|
||||
class PhonopyQHA:
|
||||
"""PhonopyQHA API."""
|
||||
|
||||
def __init__(self,
|
||||
def __init__(
|
||||
self,
|
||||
volumes=None,
|
||||
electronic_energies=None,
|
||||
temperatures=None,
|
||||
free_energy=None,
|
||||
cv=None,
|
||||
entropy=None,
|
||||
eos='vinet',
|
||||
eos="vinet",
|
||||
t_max=None,
|
||||
energy_plot_factor=None,
|
||||
verbose=False):
|
||||
verbose=False,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Notes
|
||||
|
@ -98,12 +102,11 @@ class PhonopyQHA(object):
|
|||
Show log or not.
|
||||
|
||||
"""
|
||||
self._bulk_modulus = BulkModulus(volumes,
|
||||
electronic_energies,
|
||||
eos=eos)
|
||||
self._bulk_modulus = BulkModulus(volumes, electronic_energies, eos=eos)
|
||||
|
||||
if temperatures is not None:
|
||||
self._qha = QHA(volumes,
|
||||
self._qha = QHA(
|
||||
volumes,
|
||||
electronic_energies,
|
||||
temperatures,
|
||||
cv,
|
||||
|
@ -111,7 +114,8 @@ class PhonopyQHA(object):
|
|||
free_energy,
|
||||
eos=eos,
|
||||
t_max=t_max,
|
||||
energy_plot_factor=energy_plot_factor)
|
||||
energy_plot_factor=energy_plot_factor,
|
||||
)
|
||||
self._qha.run(verbose=verbose)
|
||||
|
||||
@property
|
||||
|
@ -250,179 +254,224 @@ class PhonopyQHA(object):
|
|||
"""
|
||||
return self._bulk_modulus.get_parameters()
|
||||
|
||||
def write_helmholtz_volume(self, filename='helmholtz-volume.dat'):
|
||||
def write_helmholtz_volume(self, filename="helmholtz-volume.dat"):
|
||||
"""Write Helmholtz free energy vs volume in file."""
|
||||
self._qha.write_helmholtz_volume(filename=filename)
|
||||
|
||||
def write_helmholtz_volume_fitted(self,
|
||||
thin_number,
|
||||
filename='helmholtz-volume_fitted.dat'):
|
||||
def write_helmholtz_volume_fitted(
|
||||
self, thin_number, filename="helmholtz-volume_fitted.dat"
|
||||
):
|
||||
"""Write Helmholtz free energy (fitted) vs volume in file."""
|
||||
self._qha.write_helmholtz_volume_fitted(thin_number, filename=filename)
|
||||
|
||||
def write_volume_temperature(self, filename='volume-temperature.dat'):
|
||||
def write_volume_temperature(self, filename="volume-temperature.dat"):
|
||||
"""Write volume vs temperature in file."""
|
||||
self._qha.write_volume_temperature(filename=filename)
|
||||
|
||||
def write_thermal_expansion(self,
|
||||
filename='thermal_expansion.dat'):
|
||||
def write_thermal_expansion(self, filename="thermal_expansion.dat"):
|
||||
"""Write thermal expansion vs temperature in file."""
|
||||
self._qha.write_thermal_expansion(filename=filename)
|
||||
|
||||
def write_gibbs_temperature(self, filename='gibbs-temperature.dat'):
|
||||
def write_gibbs_temperature(self, filename="gibbs-temperature.dat"):
|
||||
"""Write Gibbs free energy vs temperature in file."""
|
||||
self._qha.write_gibbs_temperature(filename=filename)
|
||||
|
||||
def write_bulk_modulus_temperature(self,
|
||||
filename='bulk_modulus-temperature.dat'):
|
||||
def write_bulk_modulus_temperature(self, filename="bulk_modulus-temperature.dat"):
|
||||
"""Write bulk modulus vs temperature in file."""
|
||||
self._qha.write_bulk_modulus_temperature(filename=filename)
|
||||
|
||||
def plot_bulk_modulus(self):
|
||||
"""Returns matplotlib.pyplot of bulk modulus fitting curve"""
|
||||
"""Return pyplot of bulk modulus fitting curve."""
|
||||
return self._bulk_modulus.plot()
|
||||
|
||||
def plot_qha(self, thin_number=10, volume_temp_exp=None):
|
||||
"""Returns matplotlib.pyplot of QHA fitting curves at temperatures"""
|
||||
return self._qha.plot(thin_number=thin_number,
|
||||
volume_temp_exp=volume_temp_exp)
|
||||
"""Return pyplot of QHA fitting curves at temperatures."""
|
||||
return self._qha.plot(thin_number=thin_number, volume_temp_exp=volume_temp_exp)
|
||||
|
||||
def plot_helmholtz_volume(self,
|
||||
thin_number=10,
|
||||
xlabel=r'Volume $(\AA^3)$',
|
||||
ylabel='Free energy'):
|
||||
return self._qha.plot_helmholtz_volume(thin_number=thin_number,
|
||||
xlabel=xlabel,
|
||||
ylabel=ylabel)
|
||||
def plot_helmholtz_volume(
|
||||
self, thin_number=10, xlabel=r"Volume $(\AA^3)$", ylabel="Free energy"
|
||||
):
|
||||
"""Return pyplot of Helmholtz free energes vs volume at temperatures."""
|
||||
return self._qha.plot_helmholtz_volume(
|
||||
thin_number=thin_number, xlabel=xlabel, ylabel=ylabel
|
||||
)
|
||||
|
||||
def plot_pdf_helmholtz_volume(self,
|
||||
thin_number=10,
|
||||
filename='helmholtz-volume.pdf'):
|
||||
self._qha.plot_pdf_helmholtz_volume(thin_number=thin_number,
|
||||
filename=filename)
|
||||
def plot_pdf_helmholtz_volume(
|
||||
self, thin_number=10, filename="helmholtz-volume.pdf"
|
||||
):
|
||||
"""Plot Helmholtz free energes vs volume at temperatures in pdf."""
|
||||
self._qha.plot_pdf_helmholtz_volume(thin_number=thin_number, filename=filename)
|
||||
|
||||
def plot_volume_temperature(self, exp_data=None):
|
||||
"""Return pyplot of volume vs temperature."""
|
||||
return self._qha.plot_volume_temperature(exp_data=exp_data)
|
||||
|
||||
def plot_pdf_volume_temperature(self,
|
||||
exp_data=None,
|
||||
filename='volume-temperature.pdf'):
|
||||
self._qha.plot_pdf_volume_temperature(exp_data=exp_data,
|
||||
filename=filename)
|
||||
def plot_pdf_volume_temperature(
|
||||
self, exp_data=None, filename="volume-temperature.pdf"
|
||||
):
|
||||
"""Plot volume vs temperature in pdf."""
|
||||
self._qha.plot_pdf_volume_temperature(exp_data=exp_data, filename=filename)
|
||||
|
||||
def plot_thermal_expansion(self):
|
||||
"""Return pyplot of thermal expansion vs temperature."""
|
||||
return self._qha.plot_thermal_expansion()
|
||||
|
||||
def plot_pdf_thermal_expansion(self,
|
||||
filename='thermal_expansion.pdf'):
|
||||
def plot_pdf_thermal_expansion(self, filename="thermal_expansion.pdf"):
|
||||
"""Plot thermal expansion vs temperature in pdf."""
|
||||
self._qha.plot_pdf_thermal_expansion(filename=filename)
|
||||
|
||||
def plot_gibbs_temperature(self,
|
||||
xlabel='Temperature (K)',
|
||||
ylabel='Gibbs free energy'):
|
||||
return self._qha.plot_gibbs_temperature(xlabel=xlabel,
|
||||
ylabel=ylabel)
|
||||
def plot_gibbs_temperature(
|
||||
self, xlabel="Temperature (K)", ylabel="Gibbs free energy"
|
||||
):
|
||||
"""Return pyplot of Gibbs free energy vs temperature."""
|
||||
return self._qha.plot_gibbs_temperature(xlabel=xlabel, ylabel=ylabel)
|
||||
|
||||
def plot_pdf_gibbs_temperature(self, filename='gibbs-temperature.pdf'):
|
||||
def plot_pdf_gibbs_temperature(self, filename="gibbs-temperature.pdf"):
|
||||
"""Plot Gibbs free energy vs temperature in pdf."""
|
||||
self._qha.plot_pdf_gibbs_temperature(filename=filename)
|
||||
|
||||
def plot_bulk_modulus_temperature(self,
|
||||
xlabel='Temperature (K)',
|
||||
ylabel='Bulk modulus'):
|
||||
return self._qha.plot_bulk_modulus_temperature(xlabel=xlabel,
|
||||
ylabel=ylabel)
|
||||
def plot_bulk_modulus_temperature(
|
||||
self, xlabel="Temperature (K)", ylabel="Bulk modulus"
|
||||
):
|
||||
"""Return pyplot of bulk modulus vs temperature."""
|
||||
return self._qha.plot_bulk_modulus_temperature(xlabel=xlabel, ylabel=ylabel)
|
||||
|
||||
def plot_pdf_bulk_modulus_temperature(self,
|
||||
filename='bulk_modulus-temperature.pdf'):
|
||||
def plot_pdf_bulk_modulus_temperature(
|
||||
self, filename="bulk_modulus-temperature.pdf"
|
||||
):
|
||||
"""Plot bulk modulus vs temperature in pdf."""
|
||||
self._qha.plot_pdf_bulk_modulus_temperature(filename=filename)
|
||||
|
||||
def plot_heat_capacity_P_numerical(self, Z=1, exp_data=None):
|
||||
return self._qha.plot_heat_capacity_P_numerical(Z=Z,
|
||||
exp_data=exp_data)
|
||||
"""Return pyplot of C_P by numerical difference vs temperature."""
|
||||
return self._qha.plot_heat_capacity_P_numerical(Z=Z, exp_data=exp_data)
|
||||
|
||||
def plot_pdf_heat_capacity_P_numerical(self,
|
||||
exp_data=None,
|
||||
filename='Cp-temperature.pdf'):
|
||||
self._qha.plot_pdf_heat_capacity_P_numerical(exp_data=exp_data,
|
||||
filename=filename)
|
||||
def plot_pdf_heat_capacity_P_numerical(
|
||||
self, exp_data=None, filename="Cp-temperature.pdf"
|
||||
):
|
||||
"""Plot C_P by numerical difference vs temperature in pdf."""
|
||||
self._qha.plot_pdf_heat_capacity_P_numerical(
|
||||
exp_data=exp_data, filename=filename
|
||||
)
|
||||
|
||||
def write_heat_capacity_P_numerical(self, filename='Cp-temperature.dat'):
|
||||
def write_heat_capacity_P_numerical(self, filename="Cp-temperature.dat"):
|
||||
"""Write C_P by numerical difference vs temperature in file."""
|
||||
self._qha.write_heat_capacity_P_numerical(filename=filename)
|
||||
|
||||
def plot_heat_capacity_P_polyfit(self, exp_data=None, Z=1):
|
||||
return self._qha.plot_heat_capacity_P_polyfit(
|
||||
Z=Z, exp_data=exp_data)
|
||||
"""Return pyplot of C_P by fittings vs temperature."""
|
||||
return self._qha.plot_heat_capacity_P_polyfit(Z=Z, exp_data=exp_data)
|
||||
|
||||
def plot_pdf_heat_capacity_P_polyfit(self,
|
||||
exp_data=None,
|
||||
filename='Cp-temperature_polyfit.pdf'):
|
||||
self._qha.plot_pdf_heat_capacity_P_polyfit(exp_data=exp_data,
|
||||
filename=filename)
|
||||
def plot_pdf_heat_capacity_P_polyfit(
|
||||
self, exp_data=None, filename="Cp-temperature_polyfit.pdf"
|
||||
):
|
||||
"""Plot C_P by fittings vs temperature in pdf."""
|
||||
self._qha.plot_pdf_heat_capacity_P_polyfit(exp_data=exp_data, filename=filename)
|
||||
|
||||
def write_heat_capacity_P_polyfit(self,
|
||||
filename='Cp-temperature_polyfit.dat',
|
||||
filename_ev='entropy-volume.dat',
|
||||
filename_cvv='Cv-volume.dat',
|
||||
filename_dsdvt='dsdv-temperature.dat'):
|
||||
self._qha.write_heat_capacity_P_polyfit(filename=filename,
|
||||
def write_heat_capacity_P_polyfit(
|
||||
self,
|
||||
filename="Cp-temperature_polyfit.dat",
|
||||
filename_ev="entropy-volume.dat",
|
||||
filename_cvv="Cv-volume.dat",
|
||||
filename_dsdvt="dsdv-temperature.dat",
|
||||
):
|
||||
"""Write C_P by fittings vs temperature in file."""
|
||||
self._qha.write_heat_capacity_P_polyfit(
|
||||
filename=filename,
|
||||
filename_ev=filename_ev,
|
||||
filename_cvv=filename_cvv,
|
||||
filename_dsdvt=filename_dsdvt)
|
||||
filename_dsdvt=filename_dsdvt,
|
||||
)
|
||||
|
||||
def plot_gruneisen_temperature(self):
|
||||
"""Return pyplot of Grueneisen parameter vs temperature."""
|
||||
return self._qha.plot_gruneisen_temperature()
|
||||
|
||||
def plot_pdf_gruneisen_temperature(self,
|
||||
filename='gruneisen-temperature.pdf'):
|
||||
def plot_pdf_gruneisen_temperature(self, filename="gruneisen-temperature.pdf"):
|
||||
"""Plot Grueneisen parameter vs temperature in pdf."""
|
||||
self._qha.plot_pdf_gruneisen_temperature(filename=filename)
|
||||
|
||||
def write_gruneisen_temperature(self, filename='gruneisen-temperature.dat'):
|
||||
def write_gruneisen_temperature(self, filename="gruneisen-temperature.dat"):
|
||||
"""Write Grueneisen parameter vs temperature in file."""
|
||||
self._qha.write_gruneisen_temperature(filename=filename)
|
||||
|
||||
def get_bulk_modulus(self):
|
||||
warnings.warn("PhonopyQHA.get_bulk_modulus() is deprecated."
|
||||
"""Return bulk moduli with no phonon contribution."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_bulk_modulus() is deprecated."
|
||||
"Use bulk_modulus attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.bulk_modulus
|
||||
|
||||
def get_helmholtz_volume(self):
|
||||
warnings.warn("PhonopyQHA.get_helmholtz_volume() is deprecated."
|
||||
"""Return Helmholtz free energies at temperatures and volumes."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_helmholtz_volume() is deprecated."
|
||||
"Use helmholtz_volume attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.helmholtz_volume
|
||||
|
||||
def get_volume_temperature(self):
|
||||
warnings.warn("PhonopyQHA.get_volume_temperature() is deprecated."
|
||||
"""Return equilibrium volumes at temperatures."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_volume_temperature() is deprecated."
|
||||
"Use volume_temperature attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.volume_temperature
|
||||
|
||||
def get_thermal_expansion(self):
|
||||
warnings.warn("PhonopyQHA.get_thermal_expansion() is deprecated."
|
||||
"""Return thermal expansion coefficients at temperatures."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_thermal_expansion() is deprecated."
|
||||
"Use thermal_expansion attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.thermal_expansion
|
||||
|
||||
def get_gibbs_temperature(self):
|
||||
warnings.warn("PhonopyQHA.get_gibbs_temperature() is deprecated."
|
||||
"""Return Gibbs free energies at temperatures."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_gibbs_temperature() is deprecated."
|
||||
"Use gibbs_temperature attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.gibbs_temperature
|
||||
|
||||
def get_bulk_modulus_temperature(self):
|
||||
warnings.warn("PhonopyQHA.get_bulk_modulus_temperature() is deprecated."
|
||||
"""Return bulk moduli at temperatures."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_bulk_modulus_temperature() is deprecated."
|
||||
"Use bulk_modulus_temperature attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.bulk_modulus_temperature
|
||||
|
||||
def get_heat_capacity_P_numerical(self):
|
||||
warnings.warn("PhonopyQHA.get_heat_capacity_P_numerical() is deprecated."
|
||||
"""Return C_P calculated by numerical differentiation."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_heat_capacity_P_numerical() is deprecated."
|
||||
"Use heat_capacity_P_numerical attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.heat_capacity_P_numerical
|
||||
|
||||
def get_heat_capacity_P_polyfit(self):
|
||||
warnings.warn("PhonopyQHA.get_heat_capacity_P_polyfit() is deprecated."
|
||||
"""Return C_P calculated by fittings."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_heat_capacity_P_polyfit() is deprecated."
|
||||
"Use heat_capacity_P_polyfit attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.heat_capacity_P_polyfit
|
||||
|
||||
def get_gruneisen_temperature(self):
|
||||
warnings.warn("PhonopyQHA.get_gruneisen_temperature() is deprecated."
|
||||
"""Return Grueneisen paramters at temperatures."""
|
||||
warnings.warn(
|
||||
"PhonopyQHA.get_gruneisen_temperature() is deprecated."
|
||||
"Use gruneisen_temperature attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.gruneisen_temperature
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
"""Routines for command user interface."""
|
||||
# Copyright (C) 2021 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of phonopy.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# * Neither the name of the phonopy project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,3 +1,4 @@
|
|||
"""Routines to collect crystal structure information."""
|
||||
# Copyright (C) 2018 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,13 +34,17 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.interface.calculator import (
|
||||
read_crystal_structure, get_default_cell_filename)
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
get_default_cell_filename,
|
||||
read_crystal_structure,
|
||||
)
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
|
||||
|
||||
def collect_cell_info(supercell_matrix=None,
|
||||
def collect_cell_info(
|
||||
supercell_matrix=None,
|
||||
primitive_matrix=None,
|
||||
interface_mode=None,
|
||||
cell_filename=None,
|
||||
|
@ -47,15 +52,16 @@ def collect_cell_info(supercell_matrix=None,
|
|||
enforce_primitive_matrix_auto=False,
|
||||
phonopy_yaml_cls=None,
|
||||
symprec=1e-5,
|
||||
return_dict=False):
|
||||
return_dict=False,
|
||||
):
|
||||
"""Collect crystal structure information from inputs."""
|
||||
# In some cases, interface mode falls back to phonopy_yaml mode.
|
||||
fallback_reason = _fallback_to_phonopy_yaml(
|
||||
supercell_matrix,
|
||||
interface_mode,
|
||||
cell_filename)
|
||||
supercell_matrix, interface_mode, cell_filename
|
||||
)
|
||||
|
||||
if fallback_reason:
|
||||
_interface_mode = 'phonopy_yaml'
|
||||
_interface_mode = "phonopy_yaml"
|
||||
elif interface_mode is None:
|
||||
_interface_mode = None
|
||||
else:
|
||||
|
@ -65,34 +71,40 @@ def collect_cell_info(supercell_matrix=None,
|
|||
filename=cell_filename,
|
||||
interface_mode=_interface_mode,
|
||||
chemical_symbols=chemical_symbols,
|
||||
phonopy_yaml_cls=phonopy_yaml_cls)
|
||||
phonopy_yaml_cls=phonopy_yaml_cls,
|
||||
)
|
||||
|
||||
# Error check
|
||||
if unitcell is None:
|
||||
err_msg = _get_error_message(optional_structure_info,
|
||||
err_msg = _get_error_message(
|
||||
optional_structure_info,
|
||||
interface_mode,
|
||||
fallback_reason,
|
||||
cell_filename,
|
||||
phonopy_yaml_cls)
|
||||
phonopy_yaml_cls,
|
||||
)
|
||||
return err_msg
|
||||
|
||||
# Retrieve more information on cells
|
||||
(interface_mode_out,
|
||||
(
|
||||
interface_mode_out,
|
||||
supercell_matrix_out,
|
||||
primitive_matrix_out) = _collect_cells_info(
|
||||
primitive_matrix_out,
|
||||
) = _collect_cells_info(
|
||||
_interface_mode,
|
||||
optional_structure_info,
|
||||
interface_mode,
|
||||
supercell_matrix,
|
||||
primitive_matrix,
|
||||
enforce_primitive_matrix_auto)
|
||||
enforce_primitive_matrix_auto,
|
||||
)
|
||||
|
||||
# Another error check
|
||||
msg_list = ["Crystal structure was read from \"%s\"."
|
||||
% optional_structure_info[0], ]
|
||||
msg_list = [
|
||||
'Crystal structure was read from "%s".' % optional_structure_info[0],
|
||||
]
|
||||
if supercell_matrix_out is None:
|
||||
msg_list.append(
|
||||
"Supercell matrix (DIM or --dim) information was not found.")
|
||||
msg_list.append("Supercell matrix (DIM or --dim) information was not found.")
|
||||
return "\n".join(msg_list)
|
||||
|
||||
if np.linalg.det(unitcell.get_cell()) < 0.0:
|
||||
|
@ -100,27 +112,33 @@ def collect_cell_info(supercell_matrix=None,
|
|||
return "\n".join(msg_list)
|
||||
|
||||
# Succeeded!
|
||||
if _interface_mode == 'phonopy_yaml':
|
||||
phpy_yaml = optional_structure_info[1]
|
||||
if _interface_mode == "phonopy_yaml":
|
||||
phpy_yaml: PhonopyYaml = optional_structure_info[1]
|
||||
else:
|
||||
phpy_yaml = None
|
||||
|
||||
if return_dict:
|
||||
return {'unitcell': unitcell,
|
||||
'supercell_matrix': supercell_matrix_out,
|
||||
'primitive_matrix': primitive_matrix_out,
|
||||
'optional_structure_info': optional_structure_info,
|
||||
'interface_mode': interface_mode_out,
|
||||
'phonopy_yaml': phpy_yaml}
|
||||
return {
|
||||
"unitcell": unitcell,
|
||||
"supercell_matrix": supercell_matrix_out,
|
||||
"primitive_matrix": primitive_matrix_out,
|
||||
"optional_structure_info": optional_structure_info,
|
||||
"interface_mode": interface_mode_out,
|
||||
"phonopy_yaml": phpy_yaml,
|
||||
}
|
||||
else:
|
||||
return (unitcell, supercell_matrix_out, primitive_matrix_out,
|
||||
optional_structure_info, interface_mode_out, phpy_yaml)
|
||||
return (
|
||||
unitcell,
|
||||
supercell_matrix_out,
|
||||
primitive_matrix_out,
|
||||
optional_structure_info,
|
||||
interface_mode_out,
|
||||
phpy_yaml,
|
||||
)
|
||||
|
||||
|
||||
def _fallback_to_phonopy_yaml(supercell_matrix,
|
||||
interface_mode,
|
||||
cell_filename):
|
||||
"""Find possibility to fallback to phonopy.yaml mode
|
||||
def _fallback_to_phonopy_yaml(supercell_matrix, interface_mode, cell_filename):
|
||||
"""Find possibility to fallback to phonopy.yaml mode.
|
||||
|
||||
Fallback happens in any of the following cases.
|
||||
|
||||
|
@ -145,7 +163,6 @@ def _fallback_to_phonopy_yaml(supercell_matrix,
|
|||
None means fallback to phonopy.yaml mode will not happen.
|
||||
|
||||
"""
|
||||
|
||||
fallback_reason = None
|
||||
|
||||
if interface_mode is None:
|
||||
|
@ -159,7 +176,7 @@ def _fallback_to_phonopy_yaml(supercell_matrix,
|
|||
|
||||
|
||||
def _poscar_failed(cell_filename):
|
||||
"""Determine if fall back happens
|
||||
"""Determine if fall back happens.
|
||||
|
||||
1) read_vasp (parsing POSCAR-style file) is failed. --> fallback
|
||||
|
||||
|
@ -181,11 +198,10 @@ def _poscar_failed(cell_filename):
|
|||
handled properly (read_crystal_structure).
|
||||
|
||||
"""
|
||||
|
||||
fallback_reason = None
|
||||
try:
|
||||
if cell_filename is None:
|
||||
read_vasp(get_default_cell_filename('vasp'))
|
||||
read_vasp(get_default_cell_filename("vasp"))
|
||||
else:
|
||||
read_vasp(cell_filename)
|
||||
except ValueError:
|
||||
|
@ -201,17 +217,16 @@ def _poscar_failed(cell_filename):
|
|||
return fallback_reason
|
||||
|
||||
|
||||
def _collect_cells_info(_interface_mode,
|
||||
def _collect_cells_info(
|
||||
_interface_mode,
|
||||
optional_structure_info,
|
||||
interface_mode,
|
||||
supercell_matrix,
|
||||
primitive_matrix,
|
||||
enforce_primitive_matrix_auto):
|
||||
"""This is a method just to wrap up and exclude dirty stuffs."""
|
||||
|
||||
if (_interface_mode == 'phonopy_yaml' and
|
||||
optional_structure_info[1] is not None):
|
||||
phpy = optional_structure_info[1]
|
||||
enforce_primitive_matrix_auto,
|
||||
):
|
||||
if _interface_mode == "phonopy_yaml" and optional_structure_info[1] is not None:
|
||||
phpy: PhonopyYaml = optional_structure_info[1]
|
||||
if phpy.calculator is None:
|
||||
interface_mode_out = interface_mode
|
||||
else:
|
||||
|
@ -225,17 +240,17 @@ def _collect_cells_info(_interface_mode,
|
|||
elif phpy.primitive_matrix is not None:
|
||||
_primitive_matrix = phpy.primitive_matrix
|
||||
else:
|
||||
_primitive_matrix = 'auto'
|
||||
_primitive_matrix = "auto"
|
||||
else:
|
||||
interface_mode_out = _interface_mode
|
||||
_supercell_matrix = supercell_matrix
|
||||
_primitive_matrix = primitive_matrix
|
||||
|
||||
if enforce_primitive_matrix_auto:
|
||||
_primitive_matrix = 'auto'
|
||||
_primitive_matrix = "auto"
|
||||
|
||||
if _supercell_matrix is None and _primitive_matrix == 'auto':
|
||||
supercell_matrix_out = np.eye(3, dtype='intc')
|
||||
if _supercell_matrix is None and _primitive_matrix == "auto":
|
||||
supercell_matrix_out = np.eye(3, dtype="intc")
|
||||
else:
|
||||
supercell_matrix_out = _supercell_matrix
|
||||
|
||||
|
@ -244,11 +259,13 @@ def _collect_cells_info(_interface_mode,
|
|||
return interface_mode_out, supercell_matrix_out, primitive_matrix_out
|
||||
|
||||
|
||||
def _get_error_message(optional_structure_info,
|
||||
def _get_error_message(
|
||||
optional_structure_info,
|
||||
interface_mode,
|
||||
fallback_reason,
|
||||
cell_filename,
|
||||
phonopy_yaml_cls):
|
||||
phonopy_yaml_cls,
|
||||
):
|
||||
final_cell_filename = optional_structure_info[0]
|
||||
if phonopy_yaml_cls is None:
|
||||
_phonopy_yaml_cls = PhonopyYaml
|
||||
|
@ -258,10 +275,12 @@ def _get_error_message(optional_structure_info,
|
|||
if fallback_reason is None:
|
||||
msg_list = []
|
||||
if cell_filename != final_cell_filename:
|
||||
msg_list.append("Crystal structure file \"%s\" was not found."
|
||||
% cell_filename)
|
||||
msg_list.append("Crystal structure file \"%s\" was not found."
|
||||
% final_cell_filename)
|
||||
msg_list.append(
|
||||
'Crystal structure file "%s" was not found.' % cell_filename
|
||||
)
|
||||
msg_list.append(
|
||||
'Crystal structure file "%s" was not found.' % final_cell_filename
|
||||
)
|
||||
return "\n".join(msg_list)
|
||||
|
||||
####################################
|
||||
|
@ -269,32 +288,32 @@ def _get_error_message(optional_structure_info,
|
|||
####################################
|
||||
|
||||
msg_list = []
|
||||
if fallback_reason in ["default file not found",
|
||||
"read_vasp parsing failed"]:
|
||||
if fallback_reason in ["default file not found", "read_vasp parsing failed"]:
|
||||
if cell_filename:
|
||||
vasp_filename = cell_filename
|
||||
else:
|
||||
vasp_filename = get_default_cell_filename('vasp')
|
||||
vasp_filename = get_default_cell_filename("vasp")
|
||||
|
||||
if fallback_reason == "read_vasp parsing failed":
|
||||
msg_list.append(
|
||||
"Parsing crystal structure file of \"%s\" failed."
|
||||
% vasp_filename)
|
||||
'Parsing crystal structure file of "%s" failed.' % vasp_filename
|
||||
)
|
||||
else:
|
||||
msg_list.append(
|
||||
"Crystal structure file of \"%s\" was not found."
|
||||
% vasp_filename)
|
||||
'Crystal structure file of "%s" was not found.' % vasp_filename
|
||||
)
|
||||
|
||||
elif fallback_reason == "no supercell matrix given":
|
||||
msg_list.append("Supercell matrix (DIM or --dim) was not explicitly "
|
||||
"specified.")
|
||||
msg_list.append(
|
||||
"Supercell matrix (DIM or --dim) was not explicitly " "specified."
|
||||
)
|
||||
|
||||
msg_list.append("By this reason, %s_yaml mode was invoked."
|
||||
% _phonopy_yaml_cls.command_name)
|
||||
msg_list.append(
|
||||
"By this reason, %s_yaml mode was invoked." % _phonopy_yaml_cls.command_name
|
||||
)
|
||||
|
||||
if final_cell_filename is None: # No phonopy*.yaml file was found.
|
||||
filenames = ["\"%s\"" % name
|
||||
for name in _phonopy_yaml_cls.default_filenames]
|
||||
filenames = ['"%s"' % name for name in _phonopy_yaml_cls.default_filenames]
|
||||
if len(filenames) == 1:
|
||||
text = filenames[0]
|
||||
elif len(filenames) == 2:
|
||||
|
@ -308,6 +327,6 @@ def _get_error_message(optional_structure_info,
|
|||
|
||||
phpy = optional_structure_info[1]
|
||||
if phpy is None: # Failed to parse phonopy*.yaml.
|
||||
msg_list.append("But parsing \"%s\" failed." % final_cell_filename)
|
||||
msg_list.append('But parsing "%s" failed.' % final_cell_filename)
|
||||
|
||||
return "\n".join(msg_list)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Utilities to create force sets for main CUI script."""
|
||||
# Copyright (C) 2020 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -34,47 +35,56 @@
|
|||
|
||||
|
||||
import numpy as np
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
from phonopy.interface.calculator import get_force_sets, get_force_sets_wien2k
|
||||
|
||||
from phonopy.file_IO import parse_disp_yaml, write_FORCE_SETS
|
||||
from phonopy.interface.calculator import get_force_sets, get_force_sets_wien2k
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
|
||||
|
||||
def create_FORCE_SETS(interface_mode,
|
||||
def create_FORCE_SETS(
|
||||
interface_mode,
|
||||
force_filenames,
|
||||
symmetry_tolerance=None,
|
||||
wien2k_P1_mode=False,
|
||||
force_sets_zero_mode=False,
|
||||
disp_filename='disp.yaml',
|
||||
force_sets_filename='FORCE_SETS',
|
||||
disp_filename="disp.yaml",
|
||||
force_sets_filename="FORCE_SETS",
|
||||
write_forcesets_yaml=False,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Create FORCE_SETS from phonopy_disp.yaml and calculator output files.
|
||||
|
||||
Reading disp.yaml instead of phonopy_disp.yaml is deprecated.
|
||||
|
||||
"""
|
||||
|
||||
if log_level > 0:
|
||||
if interface_mode:
|
||||
print("Calculator interface: %s" % interface_mode)
|
||||
print("Displacements were read from \"%s\"." % disp_filename)
|
||||
if disp_filename == 'disp.yaml':
|
||||
print('')
|
||||
print('Displacements were read from "%s".' % disp_filename)
|
||||
if disp_filename == "disp.yaml":
|
||||
print("")
|
||||
print("NOTE:")
|
||||
print(" From phonopy v2.0, displacements are written into "
|
||||
"\"phonopy_disp.yaml\".")
|
||||
print(" \"disp.yaml\" is still supported for reading except for "
|
||||
"Wien2k interface, ")
|
||||
print(
|
||||
" From phonopy v2.0, displacements are written into "
|
||||
'"phonopy_disp.yaml".'
|
||||
)
|
||||
print(
|
||||
' "disp.yaml" is still supported for reading except for '
|
||||
"Wien2k interface, "
|
||||
)
|
||||
print(" but is deprecated.")
|
||||
print('')
|
||||
print("")
|
||||
if force_sets_zero_mode:
|
||||
print("Forces in %s are subtracted from forces in all "
|
||||
"other files." % force_filenames[0])
|
||||
print(
|
||||
"Forces in %s are subtracted from forces in all "
|
||||
"other files." % force_filenames[0]
|
||||
)
|
||||
|
||||
if disp_filename == 'disp.yaml':
|
||||
if interface_mode == 'wien2k':
|
||||
disp_dataset, supercell = parse_disp_yaml(filename=disp_filename,
|
||||
return_cell=True)
|
||||
if disp_filename == "disp.yaml":
|
||||
if interface_mode == "wien2k":
|
||||
disp_dataset, supercell = parse_disp_yaml(
|
||||
filename=disp_filename, return_cell=True
|
||||
)
|
||||
else:
|
||||
disp_dataset = parse_disp_yaml(filename=disp_filename)
|
||||
else:
|
||||
|
@ -83,80 +93,83 @@ def create_FORCE_SETS(interface_mode,
|
|||
supercell = phpy_yaml.supercell
|
||||
disp_dataset = phpy_yaml.dataset
|
||||
|
||||
if 'natom' in disp_dataset: # type-1 dataset
|
||||
num_atoms = disp_dataset['natom']
|
||||
num_displacements = len(disp_dataset['first_atoms'])
|
||||
if "natom" in disp_dataset: # type-1 dataset
|
||||
num_atoms = disp_dataset["natom"]
|
||||
num_displacements = len(disp_dataset["first_atoms"])
|
||||
dataset_type = 1
|
||||
elif 'displacements' in disp_dataset: # type-2 dataset
|
||||
num_atoms = disp_dataset['displacements'].shape[1]
|
||||
num_displacements = disp_dataset['displacements'].shape[0]
|
||||
elif "displacements" in disp_dataset: # type-2 dataset
|
||||
num_atoms = disp_dataset["displacements"].shape[1]
|
||||
num_displacements = disp_dataset["displacements"].shape[0]
|
||||
dataset_type = 2
|
||||
else:
|
||||
raise RuntimeError("Number of atoms could not be retrieved from %s"
|
||||
% disp_filename)
|
||||
raise RuntimeError(
|
||||
"Number of atoms could not be retrieved from %s" % disp_filename
|
||||
)
|
||||
if force_sets_zero_mode:
|
||||
num_displacements += 1
|
||||
|
||||
if not check_number_of_force_files(num_displacements,
|
||||
force_filenames,
|
||||
disp_filename):
|
||||
if not check_number_of_force_files(
|
||||
num_displacements, force_filenames, disp_filename
|
||||
):
|
||||
force_sets = []
|
||||
elif interface_mode == 'wien2k':
|
||||
elif interface_mode == "wien2k":
|
||||
force_sets = get_force_sets_wien2k(
|
||||
num_displacements,
|
||||
force_filenames,
|
||||
disp_filename,
|
||||
supercell,
|
||||
disp_dataset,
|
||||
wien2k_P1_mode=wien2k_P1_mode,
|
||||
symmetry_tolerance=symmetry_tolerance,
|
||||
verbose=(log_level > 0))
|
||||
verbose=(log_level > 0),
|
||||
)
|
||||
else:
|
||||
force_sets = get_force_sets(interface_mode,
|
||||
force_sets = get_force_sets(
|
||||
interface_mode,
|
||||
num_atoms,
|
||||
num_displacements,
|
||||
force_filenames,
|
||||
disp_filename=disp_filename,
|
||||
verbose=(log_level > 0))
|
||||
verbose=(log_level > 0),
|
||||
)
|
||||
|
||||
if force_sets:
|
||||
if force_sets_zero_mode:
|
||||
force_sets = _subtract_residual_forces(force_sets)
|
||||
if dataset_type == 1:
|
||||
for forces, disp in zip(force_sets, disp_dataset['first_atoms']):
|
||||
disp['forces'] = forces
|
||||
for forces, disp in zip(force_sets, disp_dataset["first_atoms"]):
|
||||
disp["forces"] = forces
|
||||
elif dataset_type == 2:
|
||||
disp_dataset['forces'] = np.array(
|
||||
force_sets, dtype='double', order='C')
|
||||
disp_dataset["forces"] = np.array(force_sets, dtype="double", order="C")
|
||||
else:
|
||||
raise RuntimeError("FORCE_SETS could not be created.")
|
||||
|
||||
write_FORCE_SETS(disp_dataset, filename=force_sets_filename)
|
||||
|
||||
if log_level > 0:
|
||||
print("\"%s\" has been created." % force_sets_filename)
|
||||
print('"%s" has been created.' % force_sets_filename)
|
||||
|
||||
if disp_filename != 'disp.yaml' and write_forcesets_yaml:
|
||||
if disp_filename != "disp.yaml" and write_forcesets_yaml:
|
||||
phpy_yaml.dataset = disp_dataset
|
||||
with open("phonopy_force_sets.yaml", 'w') as w:
|
||||
with open("phonopy_force_sets.yaml", "w") as w:
|
||||
w.write(str(phpy_yaml))
|
||||
if log_level > 0:
|
||||
print("\"%s\" has been created." % "phonopy_force_sets.yaml")
|
||||
print('"%s" has been created.' % "phonopy_force_sets.yaml")
|
||||
|
||||
else:
|
||||
if log_level > 0:
|
||||
print("%s could not be created." % force_sets_filename)
|
||||
|
||||
|
||||
def check_number_of_force_files(num_displacements,
|
||||
force_filenames,
|
||||
disp_filename):
|
||||
def check_number_of_force_files(num_displacements, force_filenames, disp_filename):
|
||||
"""Verify number of supercell force files.
|
||||
|
||||
This function is public because being used from phono3py.
|
||||
|
||||
"""
|
||||
if num_displacements != len(force_filenames):
|
||||
print('')
|
||||
print("Number of files to be read (%d) don't match to" %
|
||||
len(force_filenames))
|
||||
print("the number of displacements (%d) in %s." %
|
||||
(num_displacements, disp_filename))
|
||||
print("")
|
||||
print("Number of files to be read (%d) don't match to" % len(force_filenames))
|
||||
print(
|
||||
"the number of displacements (%d) in %s."
|
||||
% (num_displacements, disp_filename)
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Phonopy loader."""
|
||||
# Copyright (C) 2018 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,14 +34,16 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import numpy as np
|
||||
from phonopy.api_phonopy import Phonopy
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
from phonopy.interface.calculator import get_default_physical_units
|
||||
|
||||
import phonopy.cui.load_helper as load_helper
|
||||
from phonopy.api_phonopy import Phonopy
|
||||
from phonopy.interface.calculator import get_default_physical_units
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
from phonopy.structure.cells import get_primitive_matrix
|
||||
|
||||
|
||||
def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
||||
def load(
|
||||
phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
||||
supercell_matrix=None,
|
||||
primitive_matrix=None,
|
||||
is_nac=True,
|
||||
|
@ -63,7 +66,8 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
is_compact_fc=True,
|
||||
store_dense_svecs=False,
|
||||
symprec=1e-5,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
) -> Phonopy:
|
||||
"""Create Phonopy instance from parameters and/or input files.
|
||||
|
||||
"phonopy_yaml"-like file is parsed unless crystal structure information
|
||||
|
@ -198,11 +202,12 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
Verbosity control. Default is 0.
|
||||
|
||||
"""
|
||||
|
||||
if (supercell is not None or
|
||||
supercell_filename is not None or
|
||||
unitcell is not None or
|
||||
unitcell_filename is not None): # noqa E129
|
||||
if (
|
||||
supercell is not None
|
||||
or supercell_filename is not None
|
||||
or unitcell is not None
|
||||
or unitcell_filename is not None
|
||||
): # noqa E129
|
||||
cell, smat, pmat = load_helper.get_cell_settings(
|
||||
supercell_matrix=supercell_matrix,
|
||||
primitive_matrix=primitive_matrix,
|
||||
|
@ -212,7 +217,8 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
supercell_filename=supercell_filename,
|
||||
calculator=calculator,
|
||||
symprec=symprec,
|
||||
log_level=log_level)
|
||||
log_level=log_level,
|
||||
)
|
||||
_calculator = calculator
|
||||
_nac_params = nac_params
|
||||
_dataset = None
|
||||
|
@ -223,7 +229,7 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
cell = phpy_yaml.unitcell
|
||||
smat = phpy_yaml.supercell_matrix
|
||||
if smat is None:
|
||||
smat = np.eye(3, dtype='intc', order='C')
|
||||
smat = np.eye(3, dtype="intc", order="C")
|
||||
if primitive_matrix is not None:
|
||||
pmat = get_primitive_matrix(primitive_matrix, symprec=symprec)
|
||||
else:
|
||||
|
@ -241,20 +247,20 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
else:
|
||||
_calculator = calculator
|
||||
else:
|
||||
msg = ("Cell information could not found. "
|
||||
"Phonopy instance loading failed.")
|
||||
msg = "Cell information could not found. " "Phonopy instance loading failed."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
if log_level and _calculator is not None:
|
||||
print("Set \"%s\" mode." % _calculator)
|
||||
print('Set "%s" mode.' % _calculator)
|
||||
|
||||
# units keywords: factor, nac_factor, distance_to_A
|
||||
units = get_default_physical_units(_calculator)
|
||||
if factor is None:
|
||||
_factor = units['factor']
|
||||
_factor = units["factor"]
|
||||
else:
|
||||
_factor = factor
|
||||
phonon = Phonopy(cell,
|
||||
phonon = Phonopy(
|
||||
cell,
|
||||
smat,
|
||||
primitive_matrix=pmat,
|
||||
factor=_factor,
|
||||
|
@ -263,7 +269,8 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
is_symmetry=is_symmetry,
|
||||
store_dense_svecs=store_dense_svecs,
|
||||
calculator=_calculator,
|
||||
log_level=log_level)
|
||||
log_level=log_level,
|
||||
)
|
||||
|
||||
# NAC params
|
||||
if born_filename is not None or _nac_params is not None or is_nac:
|
||||
|
@ -272,8 +279,9 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
nac_params=_nac_params,
|
||||
born_filename=born_filename,
|
||||
is_nac=is_nac,
|
||||
nac_factor=units['nac_factor'],
|
||||
log_level=log_level)
|
||||
nac_factor=units["nac_factor"],
|
||||
log_level=log_level,
|
||||
)
|
||||
if ret_nac_params is not None:
|
||||
phonon.nac_params = ret_nac_params
|
||||
|
||||
|
@ -289,6 +297,7 @@ def load(phonopy_yaml=None, # phonopy.yaml-like must be the first argument.
|
|||
produce_fc=produce_fc,
|
||||
symmetrize_fc=symmetrize_fc,
|
||||
is_compact_fc=is_compact_fc,
|
||||
log_level=log_level)
|
||||
log_level=log_level,
|
||||
)
|
||||
|
||||
return phonon
|
||||
|
|
|
@ -34,17 +34,25 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from phonopy.interface.calculator import read_crystal_structure
|
||||
from phonopy.structure.cells import get_primitive_matrix
|
||||
|
||||
from phonopy.file_IO import (
|
||||
parse_BORN, read_force_constants_hdf5, parse_FORCE_SETS,
|
||||
parse_FORCE_CONSTANTS)
|
||||
parse_BORN,
|
||||
parse_FORCE_CONSTANTS,
|
||||
parse_FORCE_SETS,
|
||||
read_force_constants_hdf5,
|
||||
)
|
||||
from phonopy.interface.calculator import (
|
||||
get_force_constant_conversion_factor,
|
||||
read_crystal_structure,
|
||||
)
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.interface.calculator import get_force_constant_conversion_factor
|
||||
from phonopy.structure.cells import get_primitive_matrix
|
||||
|
||||
|
||||
def get_cell_settings(supercell_matrix=None,
|
||||
def get_cell_settings(
|
||||
supercell_matrix=None,
|
||||
primitive_matrix=None,
|
||||
unitcell=None,
|
||||
supercell=None,
|
||||
|
@ -52,35 +60,41 @@ def get_cell_settings(supercell_matrix=None,
|
|||
supercell_filename=None,
|
||||
calculator=None,
|
||||
symprec=1e-5,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Return crystal structures."""
|
||||
optional_structure_info = None
|
||||
if (primitive_matrix is None or
|
||||
(type(primitive_matrix) is str and primitive_matrix == "auto")): # noqa E129
|
||||
pmat = 'auto'
|
||||
if primitive_matrix is None or (
|
||||
type(primitive_matrix) is str and primitive_matrix == "auto"
|
||||
): # noqa E129
|
||||
pmat = "auto"
|
||||
else:
|
||||
pmat = primitive_matrix
|
||||
|
||||
if unitcell_filename is not None:
|
||||
cell, optional_structure_info = _read_crystal_structure(
|
||||
filename=unitcell_filename, interface_mode=calculator)
|
||||
filename=unitcell_filename, interface_mode=calculator
|
||||
)
|
||||
smat = supercell_matrix
|
||||
if log_level:
|
||||
print("Unit cell structure was read from \"%s\"."
|
||||
% optional_structure_info[0])
|
||||
print(
|
||||
'Unit cell structure was read from "%s".' % optional_structure_info[0]
|
||||
)
|
||||
elif supercell_filename is not None:
|
||||
cell, optional_structure_info = read_crystal_structure(
|
||||
filename=supercell_filename, interface_mode=calculator)
|
||||
smat = np.eye(3, dtype='intc', order='C')
|
||||
filename=supercell_filename, interface_mode=calculator
|
||||
)
|
||||
smat = np.eye(3, dtype="intc", order="C")
|
||||
if log_level:
|
||||
print("Supercell structure was read from \"%s\"."
|
||||
% optional_structure_info[0])
|
||||
print(
|
||||
'Supercell structure was read from "%s".' % optional_structure_info[0]
|
||||
)
|
||||
elif unitcell is not None:
|
||||
cell = PhonopyAtoms(atoms=unitcell)
|
||||
smat = supercell_matrix
|
||||
elif supercell is not None:
|
||||
cell = PhonopyAtoms(atoms=supercell)
|
||||
smat = np.eye(3, dtype='intc', order='C')
|
||||
smat = np.eye(3, dtype="intc", order="C")
|
||||
else:
|
||||
raise RuntimeError("Cell has to be specified.")
|
||||
|
||||
|
@ -94,36 +108,38 @@ def get_cell_settings(supercell_matrix=None,
|
|||
return cell, smat, pmat
|
||||
|
||||
|
||||
def get_nac_params(primitive=None,
|
||||
def get_nac_params(
|
||||
primitive=None,
|
||||
nac_params=None,
|
||||
born_filename=None,
|
||||
is_nac=True,
|
||||
nac_factor=None,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Look for and return NAC parameters."""
|
||||
if born_filename is not None:
|
||||
_nac_params = parse_BORN(primitive, filename=born_filename)
|
||||
if log_level:
|
||||
print("NAC parameters were read from \"%s\"." % born_filename)
|
||||
print('NAC parameters were read from "%s".' % born_filename)
|
||||
elif nac_params is not None: # nac_params input or phonopy_yaml.nac_params
|
||||
_nac_params = nac_params
|
||||
elif is_nac and os.path.isfile("BORN"):
|
||||
_nac_params = parse_BORN(primitive, filename="BORN")
|
||||
if log_level:
|
||||
print("NAC params were read from \"BORN\".")
|
||||
print('NAC params were read from "BORN".')
|
||||
else:
|
||||
_nac_params = None
|
||||
|
||||
if _nac_params is not None:
|
||||
if 'factor' not in _nac_params or _nac_params['factor'] is None:
|
||||
_nac_params['factor'] = nac_factor
|
||||
if "factor" not in _nac_params or _nac_params["factor"] is None:
|
||||
_nac_params["factor"] = nac_factor
|
||||
|
||||
return _nac_params
|
||||
|
||||
|
||||
def read_force_constants_from_hdf5(filename='force_constants.hdf5',
|
||||
p2s_map=None,
|
||||
calculator=None):
|
||||
def read_force_constants_from_hdf5(
|
||||
filename="force_constants.hdf5", p2s_map=None, calculator=None
|
||||
):
|
||||
"""Convert force constants physical unit.
|
||||
|
||||
Each calculator interface has own default force constants physical unit.
|
||||
|
@ -136,9 +152,9 @@ def read_force_constants_from_hdf5(filename='force_constants.hdf5',
|
|||
This method is also used from phonopy script.
|
||||
|
||||
"""
|
||||
fc, fc_unit = read_force_constants_hdf5(filename=filename,
|
||||
p2s_map=p2s_map,
|
||||
return_physical_unit=True)
|
||||
fc, fc_unit = read_force_constants_hdf5(
|
||||
filename=filename, p2s_map=p2s_map, return_physical_unit=True
|
||||
)
|
||||
if fc_unit is None:
|
||||
return fc
|
||||
else:
|
||||
|
@ -157,7 +173,8 @@ def set_dataset_and_force_constants(
|
|||
produce_fc=True,
|
||||
symmetrize_fc=True,
|
||||
is_compact_fc=True,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Set displacement-force dataset and force constants."""
|
||||
natom = len(phonon.supercell)
|
||||
|
||||
|
@ -190,49 +207,52 @@ def set_dataset_and_force_constants(
|
|||
if _fc is not None:
|
||||
phonon.force_constants = _fc
|
||||
if log_level:
|
||||
print("Force constants were read from \"%s\"."
|
||||
% _force_constants_filename)
|
||||
print('Force constants were read from "%s".' % _force_constants_filename)
|
||||
|
||||
if phonon.force_constants is None:
|
||||
# Overwrite dataset
|
||||
if _dataset is not None:
|
||||
phonon.dataset = _dataset
|
||||
if log_level:
|
||||
print("Force sets were read from \"%s\"."
|
||||
% _force_sets_filename)
|
||||
print('Force sets were read from "%s".' % _force_sets_filename)
|
||||
if produce_fc:
|
||||
_produce_force_constants(phonon,
|
||||
_produce_force_constants(
|
||||
phonon,
|
||||
fc_calculator,
|
||||
fc_calculator_options,
|
||||
symmetrize_fc,
|
||||
is_compact_fc,
|
||||
log_level)
|
||||
log_level,
|
||||
)
|
||||
|
||||
|
||||
def _read_force_constants_file(phonon, force_constants_filename):
|
||||
dot_split = force_constants_filename.split('.')
|
||||
dot_split = force_constants_filename.split(".")
|
||||
p2s_map = phonon.primitive.p2s_map
|
||||
if len(dot_split) > 1 and dot_split[-1] == 'hdf5':
|
||||
if len(dot_split) > 1 and dot_split[-1] == "hdf5":
|
||||
_fc = read_force_constants_from_hdf5(
|
||||
filename=force_constants_filename,
|
||||
p2s_map=p2s_map,
|
||||
calculator=phonon.calculator)
|
||||
calculator=phonon.calculator,
|
||||
)
|
||||
else:
|
||||
_fc = parse_FORCE_CONSTANTS(filename=force_constants_filename,
|
||||
p2s_map=p2s_map)
|
||||
_fc = parse_FORCE_CONSTANTS(filename=force_constants_filename, p2s_map=p2s_map)
|
||||
return _fc
|
||||
|
||||
|
||||
def _produce_force_constants(phonon,
|
||||
def _produce_force_constants(
|
||||
phonon,
|
||||
fc_calculator,
|
||||
fc_calculator_options,
|
||||
symmetrize_fc,
|
||||
is_compact_fc,
|
||||
log_level):
|
||||
log_level,
|
||||
):
|
||||
phonon.produce_force_constants(
|
||||
calculate_full_force_constants=(not is_compact_fc),
|
||||
fc_calculator=fc_calculator,
|
||||
fc_calculator_options=fc_calculator_options)
|
||||
fc_calculator_options=fc_calculator_options,
|
||||
)
|
||||
if symmetrize_fc:
|
||||
phonon.symmetrize_force_constants(show_drift=(log_level > 0))
|
||||
if log_level:
|
||||
|
@ -241,17 +261,14 @@ def _produce_force_constants(phonon,
|
|||
|
||||
def _read_crystal_structure(filename=None, interface_mode=None):
|
||||
try:
|
||||
return read_crystal_structure(filename=filename,
|
||||
interface_mode=interface_mode)
|
||||
return read_crystal_structure(filename=filename, interface_mode=interface_mode)
|
||||
except FileNotFoundError:
|
||||
raise
|
||||
except Exception:
|
||||
msg = [
|
||||
"============================ phonopy.load "
|
||||
"============================",
|
||||
"============================ phonopy.load " "============================",
|
||||
" Reading crystal structure file failed in phonopy.load.",
|
||||
" Maybe phonopy.load(..., calculator='<calculator name>') "
|
||||
"expected?",
|
||||
"============================ phonopy.load "
|
||||
"============================"]
|
||||
" Maybe phonopy.load(..., calculator='<calculator name>') " "expected?",
|
||||
"============================ phonopy.load " "============================",
|
||||
]
|
||||
raise RuntimeError("\n".join(msg))
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
|||
"""Show symmetry information invoked by --symmetry command option."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -35,50 +36,56 @@
|
|||
import numpy as np
|
||||
import spglib
|
||||
from spglib import get_pointgroup
|
||||
|
||||
from phonopy import Phonopy
|
||||
from phonopy.interface.calculator import (
|
||||
write_crystal_structure, get_default_cell_filename)
|
||||
get_default_cell_filename,
|
||||
write_crystal_structure,
|
||||
)
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.structure.cells import guess_primitive_matrix, get_primitive
|
||||
from phonopy.structure.cells import get_primitive, guess_primitive_matrix
|
||||
|
||||
|
||||
def check_symmetry(phonon, optional_structure_info):
|
||||
def check_symmetry(phonon: Phonopy, optional_structure_info):
|
||||
"""Show symmetry information and write refined crystals to files."""
|
||||
# Assumed that primitive cell is the cell that user is interested in.
|
||||
print(_get_symmetry_yaml(phonon.primitive,
|
||||
phonon.primitive_symmetry,
|
||||
phonon.version))
|
||||
print(
|
||||
_get_symmetry_yaml(phonon.primitive, phonon.primitive_symmetry, phonon.version)
|
||||
)
|
||||
|
||||
if phonon.unitcell.magnetic_moments is None:
|
||||
base_fname = get_default_cell_filename(phonon.calculator)
|
||||
symprec = phonon.primitive_symmetry.get_symmetry_tolerance()
|
||||
(bravais_lattice,
|
||||
bravais_pos,
|
||||
bravais_numbers) = spglib.refine_cell(phonon.primitive, symprec)
|
||||
bravais = PhonopyAtoms(numbers=bravais_numbers,
|
||||
scaled_positions=bravais_pos,
|
||||
cell=bravais_lattice)
|
||||
filename = 'B' + base_fname
|
||||
print("# Symmetrized conventional unit cell is written into %s."
|
||||
% filename)
|
||||
(bravais_lattice, bravais_pos, bravais_numbers) = spglib.refine_cell(
|
||||
phonon.primitive, symprec
|
||||
)
|
||||
bravais = PhonopyAtoms(
|
||||
numbers=bravais_numbers, scaled_positions=bravais_pos, cell=bravais_lattice
|
||||
)
|
||||
filename = "B" + base_fname
|
||||
print("# Symmetrized conventional unit cell is written into %s." % filename)
|
||||
trans_mat = guess_primitive_matrix(bravais, symprec=symprec)
|
||||
primitive = get_primitive(bravais, trans_mat, symprec=symprec)
|
||||
write_crystal_structure(
|
||||
filename,
|
||||
bravais,
|
||||
interface_mode=phonon.calculator,
|
||||
optional_structure_info=optional_structure_info)
|
||||
optional_structure_info=optional_structure_info,
|
||||
)
|
||||
|
||||
filename = 'P' + base_fname
|
||||
filename = "P" + base_fname
|
||||
print("# Symmetrized primitive is written into %s." % filename)
|
||||
write_crystal_structure(
|
||||
filename,
|
||||
primitive,
|
||||
interface_mode=phonon.calculator,
|
||||
optional_structure_info=optional_structure_info)
|
||||
optional_structure_info=optional_structure_info,
|
||||
)
|
||||
|
||||
|
||||
def _get_symmetry_yaml(cell, symmetry, phonopy_version=None):
|
||||
rotations = symmetry.get_symmetry_operations()['rotations']
|
||||
translations = symmetry.get_symmetry_operations()['translations']
|
||||
rotations = symmetry.get_symmetry_operations()["rotations"]
|
||||
translations = symmetry.get_symmetry_operations()["translations"]
|
||||
|
||||
atom_sets = symmetry.get_map_atoms()
|
||||
independent_atoms = symmetry.get_independent_atoms()
|
||||
|
@ -91,7 +98,7 @@ def _get_symmetry_yaml(cell, symmetry, phonopy_version=None):
|
|||
|
||||
if cell.get_magnetic_moments() is None:
|
||||
spg_symbol, spg_number = symmetry.get_international_table().split()
|
||||
spg_number = int(spg_number.replace('(', '').replace(')', ''))
|
||||
spg_number = int(spg_number.replace("(", "").replace(")", ""))
|
||||
lines.append("space_group_type: '%s'" % spg_symbol)
|
||||
lines.append("space_group_number: %d" % spg_number)
|
||||
lines.append("point_group_type: '%s'" % symmetry.get_pointgroup())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""File I/O related routines."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,58 +34,60 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
import numpy as np
|
||||
from io import StringIO
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader
|
||||
except ImportError:
|
||||
from yaml import Loader
|
||||
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.cui.settings import fracval
|
||||
from phonopy.harmonic.force_constants import similarity_transformation
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.structure.dataset import get_displacements_and_forces
|
||||
from phonopy.structure.symmetry import Symmetry, elaborate_borns_and_epsilon
|
||||
from phonopy.harmonic.force_constants import similarity_transformation
|
||||
|
||||
|
||||
#
|
||||
# FORCE_SETS
|
||||
#
|
||||
def write_FORCE_SETS(dataset, filename='FORCE_SETS'):
|
||||
def write_FORCE_SETS(dataset, filename="FORCE_SETS"):
|
||||
"""Write FORCE_SETS from dataset.
|
||||
|
||||
See more detail in ``get_FORCE_SETS_lines``.
|
||||
|
||||
"""
|
||||
lines = get_FORCE_SETS_lines(dataset)
|
||||
with open(filename, 'w') as w:
|
||||
with open(filename, "w") as w:
|
||||
w.write("\n".join(lines))
|
||||
w.write("\n")
|
||||
|
||||
|
||||
def get_FORCE_SETS_lines(dataset, forces=None):
|
||||
"""Generate FORCE_SETS string
|
||||
"""Generate FORCE_SETS string.
|
||||
|
||||
See the format of dataset in the docstring of
|
||||
Phonopy.set_displacement_dataset. Optionally, sets of forces of supercells
|
||||
Phonopy.dataset. Optionally, sets of forces of supercells
|
||||
can be given. In this case, these forces are unnecessary to be stored
|
||||
in the dataset.
|
||||
|
||||
"""
|
||||
|
||||
if 'first_atoms' in dataset:
|
||||
if "first_atoms" in dataset:
|
||||
return _get_FORCE_SETS_lines_type1(dataset, forces=forces)
|
||||
elif 'displacements' in dataset:
|
||||
elif "displacements" in dataset:
|
||||
if forces is not None:
|
||||
dataset['forces'] = forces
|
||||
dataset["forces"] = forces
|
||||
return _get_FORCE_SETS_lines_type2(dataset)
|
||||
|
||||
|
||||
def _get_FORCE_SETS_lines_type1(dataset, forces=None):
|
||||
num_atom = dataset['natom']
|
||||
displacements = dataset['first_atoms']
|
||||
num_atom = dataset["natom"]
|
||||
displacements = dataset["first_atoms"]
|
||||
if forces is None:
|
||||
_forces = [x['forces'] for x in dataset['first_atoms']]
|
||||
_forces = [x["forces"] for x in dataset["first_atoms"]]
|
||||
else:
|
||||
_forces = forces
|
||||
|
||||
|
@ -93,8 +96,8 @@ def _get_FORCE_SETS_lines_type1(dataset, forces=None):
|
|||
lines.append("%-5d" % len(displacements))
|
||||
for count, disp in enumerate(displacements):
|
||||
lines.append("")
|
||||
lines.append("%-5d" % (disp['number'] + 1))
|
||||
lines.append("%20.16f %20.16f %20.16f" % tuple(disp['displacement']))
|
||||
lines.append("%-5d" % (disp["number"] + 1))
|
||||
lines.append("%20.16f %20.16f %20.16f" % tuple(disp["displacement"]))
|
||||
for f in _forces[count]:
|
||||
lines.append("%15.10f %15.10f %15.10f" % tuple(f))
|
||||
|
||||
|
@ -103,68 +106,59 @@ def _get_FORCE_SETS_lines_type1(dataset, forces=None):
|
|||
|
||||
def _get_FORCE_SETS_lines_type2(dataset):
|
||||
lines = []
|
||||
for displacements, forces in zip(dataset['displacements'],
|
||||
dataset['forces']):
|
||||
for displacements, forces in zip(dataset["displacements"], dataset["forces"]):
|
||||
for d, f in zip(displacements, forces):
|
||||
lines.append(("%15.8f" * 6) % (tuple(d) + tuple(f)))
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
def parse_FORCE_SETS(natom=None,
|
||||
is_translational_invariance=False,
|
||||
filename="FORCE_SETS",
|
||||
to_type2=False):
|
||||
"""
|
||||
def parse_FORCE_SETS(natom=None, filename="FORCE_SETS", to_type2=False):
|
||||
"""Parse FORCE_SETS from file.
|
||||
|
||||
to_type2 : bool
|
||||
dataset of type2 is returned when True.
|
||||
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dataset : dict
|
||||
Displacement dataset. See Phonopy.dataset.
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
"""
|
||||
with open(filename, "r") as f:
|
||||
return _get_dataset(
|
||||
f,
|
||||
natom=natom,
|
||||
is_translational_invariance=is_translational_invariance,
|
||||
to_type2=to_type2)
|
||||
to_type2=to_type2,
|
||||
)
|
||||
|
||||
|
||||
def parse_FORCE_SETS_from_strings(strings,
|
||||
natom=None,
|
||||
is_translational_invariance=False,
|
||||
to_type2=False):
|
||||
return _get_dataset(
|
||||
StringIO(strings),
|
||||
natom=natom,
|
||||
is_translational_invariance=is_translational_invariance,
|
||||
to_type2=to_type2)
|
||||
def parse_FORCE_SETS_from_strings(strings, natom=None, to_type2=False):
|
||||
"""Parse FORCE_SETS from strings."""
|
||||
return _get_dataset(StringIO(strings), natom=natom, to_type2=to_type2)
|
||||
|
||||
|
||||
def _get_dataset(f,
|
||||
natom=None,
|
||||
is_translational_invariance=False,
|
||||
to_type2=False):
|
||||
def _get_dataset(f, natom=None, to_type2=False):
|
||||
first_line_ary = _get_line_ignore_blank(f).split()
|
||||
f.seek(0)
|
||||
if len(first_line_ary) == 1:
|
||||
if natom is None or int(first_line_ary[0]) == natom:
|
||||
dataset = _get_dataset_type1(f, is_translational_invariance)
|
||||
dataset = _get_dataset_type1(f)
|
||||
else:
|
||||
msg = "Number of forces is not consistent with supercell setting."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
if to_type2:
|
||||
disps, forces = get_displacements_and_forces(dataset)
|
||||
return {'displacements': disps, 'forces': forces}
|
||||
return {"displacements": disps, "forces": forces}
|
||||
else:
|
||||
return dataset
|
||||
|
||||
elif len(first_line_ary) == 6:
|
||||
return _get_dataset_type2(f, natom)
|
||||
return get_dataset_type2(f, natom)
|
||||
|
||||
|
||||
def _get_dataset_type1(f, is_translational_invariance):
|
||||
def _get_dataset_type1(f):
|
||||
set_of_forces = []
|
||||
num_atom = int(_get_line_ignore_blank(f))
|
||||
num_displacements = int(_get_line_ignore_blank(f))
|
||||
|
@ -178,28 +172,22 @@ def _get_dataset_type1(f, is_translational_invariance):
|
|||
for j in range(num_atom):
|
||||
line = _get_line_ignore_blank(f).split()
|
||||
forces_tmp.append(np.array([float(x) for x in line]))
|
||||
forces_tmp = np.array(forces_tmp, dtype='double')
|
||||
|
||||
if is_translational_invariance:
|
||||
forces_tmp -= np.sum(forces_tmp, axis=0) / len(forces_tmp)
|
||||
|
||||
forces = {'number': atom_number - 1,
|
||||
'displacement': displacement,
|
||||
'forces': forces_tmp}
|
||||
forces_tmp = np.array(forces_tmp, dtype="double")
|
||||
forces = {
|
||||
"number": atom_number - 1,
|
||||
"displacement": displacement,
|
||||
"forces": forces_tmp,
|
||||
}
|
||||
set_of_forces.append(forces)
|
||||
|
||||
dataset = {'natom': num_atom,
|
||||
'first_atoms': set_of_forces}
|
||||
dataset = {"natom": num_atom, "first_atoms": set_of_forces}
|
||||
|
||||
return dataset
|
||||
|
||||
|
||||
def get_dataset_type2(f, natom):
|
||||
return _get_dataset_type2(f, natom)
|
||||
|
||||
|
||||
def _get_dataset_type2(f, natom):
|
||||
data = np.loadtxt(f, dtype='double')
|
||||
"""Parse type2 FORCE_SETS text and return dataset."""
|
||||
data = np.loadtxt(f, dtype="double")
|
||||
if data.shape[1] != 6 or (natom and data.shape[0] % natom != 0):
|
||||
msg = "Data shape of forces and displacements is incorrect."
|
||||
raise RuntimeError(msg)
|
||||
|
@ -210,27 +198,60 @@ def _get_dataset_type2(f, natom):
|
|||
else:
|
||||
displacements = data[:, :3]
|
||||
forces = data[:, 3:]
|
||||
dataset = {'displacements':
|
||||
np.array(displacements, dtype='double', order='C'),
|
||||
'forces': np.array(forces, dtype='double', order='C')}
|
||||
dataset = {
|
||||
"displacements": np.array(displacements, dtype="double", order="C"),
|
||||
"forces": np.array(forces, dtype="double", order="C"),
|
||||
}
|
||||
return dataset
|
||||
|
||||
|
||||
def _get_line_ignore_blank(f):
|
||||
line = f.readline().strip()
|
||||
if line == '':
|
||||
if line == "":
|
||||
line = _get_line_ignore_blank(f)
|
||||
return line
|
||||
|
||||
|
||||
def collect_forces(f, num_atom, hook, force_pos, word=None):
|
||||
"""General function to collect forces from lines of a text file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
f :
|
||||
Text file pointer such as that returned by ``open(filename)``.
|
||||
num_atom : int
|
||||
Number of atoms in cell. Quit parsing when number of forces reaches this
|
||||
number.
|
||||
hook : str
|
||||
When this word is found at a line, parsing will start from the next line.
|
||||
force_pos : list
|
||||
Positions of force values in `line.split()`.
|
||||
word : str, optional
|
||||
Lines containing this word is only parsed. Default is None.
|
||||
|
||||
Example
|
||||
-------
|
||||
The following is the abinit output.
|
||||
|
||||
...
|
||||
cartesian forces (hartree/bohr) at end:
|
||||
1 -0.00093686935947 -0.00000000000000 -0.00000000000000
|
||||
2 0.00015427277409 -0.00000000000000 -0.00000000000000
|
||||
3 -0.00000200377550 -0.00000000000000 -0.00000000000000
|
||||
4 0.00000619017547 -0.00000000000000 -0.00000000000000
|
||||
...
|
||||
|
||||
hook = "cartesian forces (eV/Angstrom)"
|
||||
force_pos = [1, 2, 3]
|
||||
|
||||
"""
|
||||
for line in f:
|
||||
if hook in line:
|
||||
break
|
||||
|
||||
forces = []
|
||||
for line in f:
|
||||
if line.strip() == '':
|
||||
if line.strip() == "":
|
||||
continue
|
||||
if word is not None:
|
||||
if word not in line:
|
||||
|
@ -252,12 +273,12 @@ def collect_forces(f, num_atom, hook, force_pos, word=None):
|
|||
return forces
|
||||
|
||||
|
||||
def iter_collect_forces(filename,
|
||||
num_atom,
|
||||
hook,
|
||||
force_pos,
|
||||
word=None,
|
||||
max_iter=1000):
|
||||
def iter_collect_forces(filename, num_atom, hook, force_pos, word=None, max_iter=1000):
|
||||
"""Repeat ``collect_forces`` to get the last set of forces in the file.
|
||||
|
||||
Details of parameters are explained in ``collect_forces``.
|
||||
|
||||
"""
|
||||
with open(filename) as f:
|
||||
forces = []
|
||||
prev_forces = []
|
||||
|
@ -271,8 +292,7 @@ def iter_collect_forces(filename,
|
|||
prev_forces = forces[:]
|
||||
|
||||
if i == max_iter - 1:
|
||||
sys.stderr.write("Reached to max number of iterations (%d).\n" %
|
||||
max_iter)
|
||||
sys.stderr.write("Reached to max number of iterations (%d).\n" % max_iter)
|
||||
|
||||
return forces
|
||||
|
||||
|
@ -280,9 +300,7 @@ def iter_collect_forces(filename,
|
|||
#
|
||||
# FORCE_CONSTANTS, force_constants.hdf5
|
||||
#
|
||||
def write_FORCE_CONSTANTS(force_constants,
|
||||
filename='FORCE_CONSTANTS',
|
||||
p2s_map=None):
|
||||
def write_FORCE_CONSTANTS(force_constants, filename="FORCE_CONSTANTS", p2s_map=None):
|
||||
"""Write force constants in text file format.
|
||||
|
||||
Parameters
|
||||
|
@ -298,17 +316,21 @@ def write_FORCE_CONSTANTS(force_constants,
|
|||
dtype=intc
|
||||
|
||||
"""
|
||||
|
||||
lines = get_FORCE_CONSTANTS_lines(force_constants, p2s_map=p2s_map)
|
||||
with open(filename, 'w') as w:
|
||||
with open(filename, "w") as w:
|
||||
w.write("\n".join(lines))
|
||||
|
||||
|
||||
def get_FORCE_CONSTANTS_lines(force_constants, p2s_map=None):
|
||||
"""Return text in FORCE_CONSTANTS format.
|
||||
|
||||
See also ``write_FORCE_CONSTANTS``.
|
||||
|
||||
"""
|
||||
if p2s_map is not None and len(p2s_map) == force_constants.shape[0]:
|
||||
indices = p2s_map
|
||||
else:
|
||||
indices = np.arange(force_constants.shape[0], dtype='intc')
|
||||
indices = np.arange(force_constants.shape[0], dtype="intc")
|
||||
|
||||
lines = []
|
||||
fc_shape = force_constants.shape
|
||||
|
@ -322,11 +344,13 @@ def get_FORCE_CONSTANTS_lines(force_constants, p2s_map=None):
|
|||
return lines
|
||||
|
||||
|
||||
def write_force_constants_to_hdf5(force_constants,
|
||||
filename='force_constants.hdf5',
|
||||
def write_force_constants_to_hdf5(
|
||||
force_constants,
|
||||
filename="force_constants.hdf5",
|
||||
p2s_map=None,
|
||||
physical_unit=None,
|
||||
compression=None):
|
||||
compression=None,
|
||||
):
|
||||
"""Write force constants in hdf5 format.
|
||||
|
||||
Parameters
|
||||
|
@ -349,25 +373,35 @@ def write_force_constants_to_hdf5(force_constants,
|
|||
None.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
import h5py
|
||||
except ImportError:
|
||||
raise ModuleNotFoundError("You need to install python-h5py.")
|
||||
|
||||
with h5py.File(filename, 'w') as w:
|
||||
w.create_dataset('force_constants', data=force_constants,
|
||||
compression=compression)
|
||||
with h5py.File(filename, "w") as w:
|
||||
w.create_dataset(
|
||||
"force_constants", data=force_constants, compression=compression
|
||||
)
|
||||
if p2s_map is not None:
|
||||
w.create_dataset('p2s_map', data=p2s_map)
|
||||
w.create_dataset("p2s_map", data=p2s_map)
|
||||
if physical_unit is not None:
|
||||
dset = w.create_dataset('physical_unit', (1,),
|
||||
dtype='S%d' % len(physical_unit))
|
||||
dset = w.create_dataset(
|
||||
"physical_unit", (1,), dtype="S%d" % len(physical_unit)
|
||||
)
|
||||
dset[0] = np.string_(physical_unit)
|
||||
|
||||
|
||||
def parse_FORCE_CONSTANTS(filename="FORCE_CONSTANTS",
|
||||
p2s_map=None):
|
||||
def parse_FORCE_CONSTANTS(filename="FORCE_CONSTANTS", p2s_map=None):
|
||||
"""Parse FORCE_CONSTANTS.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str, optional
|
||||
Filename.
|
||||
p2s_map : ndarray, optional
|
||||
Primitive.p2s_map. Supplied, this is used to check file format consistency.
|
||||
|
||||
"""
|
||||
with open(filename) as fcfile:
|
||||
idx1 = []
|
||||
|
||||
|
@ -375,7 +409,7 @@ def parse_FORCE_CONSTANTS(filename="FORCE_CONSTANTS",
|
|||
idx = [int(x) for x in line.split()]
|
||||
if len(idx) == 1:
|
||||
idx = [idx[0], idx[0]]
|
||||
force_constants = np.zeros((idx[0], idx[1], 3, 3), dtype='double')
|
||||
force_constants = np.zeros((idx[0], idx[1], 3, 3), dtype="double")
|
||||
for i in range(idx[0]):
|
||||
for j in range(idx[1]):
|
||||
s_i = int(fcfile.readline().split()[0]) - 1
|
||||
|
@ -383,8 +417,7 @@ def parse_FORCE_CONSTANTS(filename="FORCE_CONSTANTS",
|
|||
idx1.append(s_i)
|
||||
tensor = []
|
||||
for k in range(3):
|
||||
tensor.append([float(x)
|
||||
for x in fcfile.readline().split()])
|
||||
tensor.append([float(x) for x in fcfile.readline().split()])
|
||||
force_constants[i, j] = tensor
|
||||
|
||||
check_force_constants_indices(idx, idx1, p2s_map, filename)
|
||||
|
@ -392,34 +425,45 @@ def parse_FORCE_CONSTANTS(filename="FORCE_CONSTANTS",
|
|||
return force_constants
|
||||
|
||||
|
||||
def read_force_constants_hdf5(filename="force_constants.hdf5",
|
||||
p2s_map=None,
|
||||
return_physical_unit=False):
|
||||
def read_force_constants_hdf5(
|
||||
filename="force_constants.hdf5", p2s_map=None, return_physical_unit=False
|
||||
):
|
||||
"""Parse force_constants.hdf5.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str, optional
|
||||
Filename.
|
||||
p2s_map : ndarray, optional
|
||||
Primitive.p2s_map. Supplied, this is used to check file format consistency.
|
||||
return_physical_unit : bool, optional
|
||||
When True and physical_unit is in file, physical unit is returned.
|
||||
Default is False.
|
||||
|
||||
"""
|
||||
try:
|
||||
import h5py
|
||||
except ImportError:
|
||||
raise ModuleNotFoundError("You need to install python-h5py.")
|
||||
|
||||
with h5py.File(filename, 'r') as f:
|
||||
if 'fc2' in f:
|
||||
key = 'fc2'
|
||||
elif 'force_constants' in f:
|
||||
key = 'force_constants'
|
||||
with h5py.File(filename, "r") as f:
|
||||
if "fc2" in f:
|
||||
key = "fc2"
|
||||
elif "force_constants" in f:
|
||||
key = "force_constants"
|
||||
else:
|
||||
raise RuntimeError("%s doesn't contain necessary information" %
|
||||
filename)
|
||||
raise RuntimeError("%s doesn't contain necessary information" % filename)
|
||||
|
||||
fc = f[key][:]
|
||||
if 'p2s_map' in f:
|
||||
p2s_map_in_file = f['p2s_map'][:]
|
||||
check_force_constants_indices(fc.shape[:2],
|
||||
p2s_map_in_file,
|
||||
p2s_map,
|
||||
filename)
|
||||
if "p2s_map" in f:
|
||||
p2s_map_in_file = f["p2s_map"][:]
|
||||
check_force_constants_indices(
|
||||
fc.shape[:2], p2s_map_in_file, p2s_map, filename
|
||||
)
|
||||
|
||||
if return_physical_unit:
|
||||
if 'physical_unit' in f:
|
||||
physical_unit = f['physical_unit'][0].decode('utf-8')
|
||||
if "physical_unit" in f:
|
||||
physical_unit = f["physical_unit"][0].decode("utf-8")
|
||||
else:
|
||||
physical_unit = None
|
||||
return fc, physical_unit
|
||||
|
@ -428,15 +472,18 @@ def read_force_constants_hdf5(filename="force_constants.hdf5",
|
|||
|
||||
|
||||
def check_force_constants_indices(shape, indices, p2s_map, filename):
|
||||
"""Check consistency of force constants data type."""
|
||||
if shape[0] != shape[1] and p2s_map is not None:
|
||||
if len(p2s_map) != len(indices) or (p2s_map != indices).any():
|
||||
text = ("%s file is inconsistent with the calculation setting. "
|
||||
"PRIMITIVE_AXIS may not be set correctly.") % filename
|
||||
text = (
|
||||
"%s file is inconsistent with the calculation setting. "
|
||||
"PRIMITIVE_AXIS may not be set correctly."
|
||||
) % filename
|
||||
raise RuntimeError(text)
|
||||
|
||||
|
||||
def parse_disp_yaml(filename="disp.yaml", return_cell=False):
|
||||
"""Read disp.yaml or phonopy_disp.yaml
|
||||
"""Read disp.yaml or phonopy_disp.yaml.
|
||||
|
||||
This method was originally made for parsing disp.yaml. Later this
|
||||
started to work for phonopy_disp.yaml, too. But now this method is not
|
||||
|
@ -444,34 +491,32 @@ def parse_disp_yaml(filename="disp.yaml", return_cell=False):
|
|||
class.
|
||||
|
||||
"""
|
||||
|
||||
with open(filename) as f:
|
||||
new_dataset = {}
|
||||
dataset = yaml.load(f, Loader=Loader)
|
||||
if 'phonopy' in dataset and 'calculator' in dataset['phonopy']:
|
||||
new_dataset['calculator'] = dataset['phonopy']['calculator']
|
||||
if 'natom' in dataset:
|
||||
natom = dataset['natom']
|
||||
elif 'supercell' and 'points' in dataset['supercell']:
|
||||
natom = len(dataset['supercell']['points'])
|
||||
if "phonopy" in dataset and "calculator" in dataset["phonopy"]:
|
||||
new_dataset["calculator"] = dataset["phonopy"]["calculator"]
|
||||
if "natom" in dataset:
|
||||
natom = dataset["natom"]
|
||||
elif "supercell" and "points" in dataset["supercell"]:
|
||||
natom = len(dataset["supercell"]["points"])
|
||||
else:
|
||||
raise RuntimeError("%s doesn't contain necessary information.")
|
||||
new_dataset['natom'] = natom
|
||||
new_dataset["natom"] = natom
|
||||
new_first_atoms = []
|
||||
|
||||
try:
|
||||
displacements = dataset['displacements']
|
||||
displacements = dataset["displacements"]
|
||||
except KeyError:
|
||||
raise
|
||||
|
||||
if type(displacements[0]) is dict:
|
||||
for first_atoms in displacements:
|
||||
first_atoms['atom'] -= 1
|
||||
atom1 = first_atoms['atom']
|
||||
disp1 = first_atoms['displacement']
|
||||
new_first_atoms.append({'number': atom1,
|
||||
'displacement': disp1})
|
||||
new_dataset['first_atoms'] = new_first_atoms
|
||||
first_atoms["atom"] -= 1
|
||||
atom1 = first_atoms["atom"]
|
||||
disp1 = first_atoms["displacement"]
|
||||
new_first_atoms.append({"number": atom1, "displacement": disp1})
|
||||
new_dataset["first_atoms"] = new_first_atoms
|
||||
|
||||
if return_cell:
|
||||
cell = get_cell_from_disp_yaml(dataset)
|
||||
|
@ -480,23 +525,34 @@ def parse_disp_yaml(filename="disp.yaml", return_cell=False):
|
|||
return new_dataset
|
||||
|
||||
|
||||
def write_disp_yaml_from_dataset(dataset, supercell, filename='disp.yaml'):
|
||||
displacements = [(d['number'],) + tuple(d['displacement'])
|
||||
for d in dataset['first_atoms']]
|
||||
def write_disp_yaml_from_dataset(dataset, supercell, filename="disp.yaml"):
|
||||
"""Write disp.yaml from dataset.
|
||||
|
||||
This function is obsolete, because disp.yaml is obsolete.
|
||||
|
||||
"""
|
||||
displacements = [
|
||||
(d["number"],) + tuple(d["displacement"]) for d in dataset["first_atoms"]
|
||||
]
|
||||
write_disp_yaml(displacements, supercell, filename=filename)
|
||||
|
||||
|
||||
def write_disp_yaml(displacements, supercell, filename='disp.yaml'):
|
||||
def write_disp_yaml(displacements, supercell, filename="disp.yaml"):
|
||||
"""Write disp.yaml from displacements.
|
||||
|
||||
This function is obsolete, because disp.yaml is obsolete.
|
||||
|
||||
"""
|
||||
lines = []
|
||||
lines.append("natom: %4d" % supercell.get_number_of_atoms())
|
||||
lines += get_disp_yaml_lines(displacements, supercell)
|
||||
lines += _get_disp_yaml_lines(displacements, supercell)
|
||||
lines.append(str(supercell))
|
||||
|
||||
with open(filename, 'w') as w:
|
||||
with open(filename, "w") as w:
|
||||
w.write("\n".join(lines))
|
||||
|
||||
|
||||
def get_disp_yaml_lines(displacements, supercell):
|
||||
def _get_disp_yaml_lines(displacements, supercell):
|
||||
lines = []
|
||||
lines.append("displacements:")
|
||||
for i, disp in enumerate(displacements):
|
||||
|
@ -509,14 +565,20 @@ def get_disp_yaml_lines(displacements, supercell):
|
|||
#
|
||||
# DISP (old phonopy displacement format)
|
||||
#
|
||||
def parse_DISP(filename='DISP'):
|
||||
def parse_DISP(filename="DISP"):
|
||||
"""Parse DISP file.
|
||||
|
||||
This function is obsolete, because DISP is obsolete.
|
||||
|
||||
"""
|
||||
with open(filename) as disp:
|
||||
displacements = []
|
||||
for line in disp:
|
||||
if line.strip() != '':
|
||||
if line.strip() != "":
|
||||
a = line.split()
|
||||
displacements.append(
|
||||
[int(a[0])-1, float(a[1]), float(a[2]), float(a[3])])
|
||||
[int(a[0]) - 1, float(a[1]), float(a[2]), float(a[3])]
|
||||
)
|
||||
return displacements
|
||||
|
||||
|
||||
|
@ -524,14 +586,15 @@ def parse_DISP(filename='DISP'):
|
|||
# Parse supercell in disp.yaml
|
||||
#
|
||||
def get_cell_from_disp_yaml(dataset):
|
||||
if 'lattice' in dataset:
|
||||
lattice = dataset['lattice']
|
||||
if 'points' in dataset:
|
||||
data_key = 'points'
|
||||
pos_key = 'coordinates'
|
||||
elif 'atoms' in dataset:
|
||||
data_key = 'atoms'
|
||||
pos_key = 'position'
|
||||
"""Read cell from disp.yaml like file."""
|
||||
if "lattice" in dataset:
|
||||
lattice = dataset["lattice"]
|
||||
if "points" in dataset:
|
||||
data_key = "points"
|
||||
pos_key = "coordinates"
|
||||
elif "atoms" in dataset:
|
||||
data_key = "atoms"
|
||||
pos_key = "position"
|
||||
else:
|
||||
data_key = None
|
||||
pos_key = None
|
||||
|
@ -539,25 +602,27 @@ def get_cell_from_disp_yaml(dataset):
|
|||
try:
|
||||
positions = [x[pos_key] for x in dataset[data_key]]
|
||||
except KeyError:
|
||||
msg = ("\"disp.yaml\" format is too old. "
|
||||
"Please re-create it as \"phonopy_disp.yaml\" to contain "
|
||||
"supercell crystal structure information.")
|
||||
msg = (
|
||||
'"disp.yaml" format is too old. '
|
||||
'Please re-create it as "phonopy_disp.yaml" to contain '
|
||||
"supercell crystal structure information."
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
symbols = [x['symbol'] for x in dataset[data_key]]
|
||||
cell = PhonopyAtoms(cell=lattice,
|
||||
scaled_positions=positions,
|
||||
symbols=symbols,
|
||||
pbc=True)
|
||||
symbols = [x["symbol"] for x in dataset[data_key]]
|
||||
cell = PhonopyAtoms(
|
||||
cell=lattice, scaled_positions=positions, symbols=symbols, pbc=True
|
||||
)
|
||||
return cell
|
||||
else:
|
||||
return get_cell_from_disp_yaml(dataset['supercell'])
|
||||
return get_cell_from_disp_yaml(dataset["supercell"])
|
||||
|
||||
|
||||
#
|
||||
# QPOINTS
|
||||
#
|
||||
def parse_QPOINTS(filename="QPOINTS"):
|
||||
with open(filename, 'r') as f:
|
||||
"""Read QPOINTS file."""
|
||||
with open(filename, "r") as f:
|
||||
num_qpoints = int(f.readline().strip())
|
||||
qpoints = []
|
||||
for i in range(num_qpoints):
|
||||
|
@ -569,25 +634,37 @@ def parse_QPOINTS(filename="QPOINTS"):
|
|||
# BORN
|
||||
#
|
||||
def write_BORN(primitive, borns, epsilon, filename="BORN"):
|
||||
"""Write BORN from NAC paramters."""
|
||||
lines = get_BORN_lines(primitive, borns, epsilon)
|
||||
with open(filename, 'w') as w:
|
||||
w.write('\n'.join(lines))
|
||||
with open(filename, "w") as w:
|
||||
w.write("\n".join(lines))
|
||||
|
||||
|
||||
def get_BORN_lines(unitcell, borns, epsilon,
|
||||
def get_BORN_lines(
|
||||
unitcell,
|
||||
borns,
|
||||
epsilon,
|
||||
factor=None,
|
||||
primitive_matrix=None,
|
||||
supercell_matrix=None,
|
||||
symprec=1e-5):
|
||||
symprec=1e-5,
|
||||
):
|
||||
"""Generate text of BORN file."""
|
||||
borns, epsilon, atom_indices = elaborate_borns_and_epsilon(
|
||||
unitcell, borns, epsilon, symmetrize_tensors=True,
|
||||
unitcell,
|
||||
borns,
|
||||
epsilon,
|
||||
symmetrize_tensors=True,
|
||||
primitive_matrix=primitive_matrix,
|
||||
supercell_matrix=supercell_matrix,
|
||||
symprec=symprec)
|
||||
symprec=symprec,
|
||||
)
|
||||
|
||||
text = "# epsilon and Z* of atoms "
|
||||
text += ' '.join(["%d" % n for n in atom_indices + 1])
|
||||
lines = [text, ]
|
||||
text += " ".join(["%d" % n for n in atom_indices + 1])
|
||||
lines = [
|
||||
text,
|
||||
]
|
||||
lines.append(("%13.8f " * 9) % tuple(epsilon.flatten()))
|
||||
for z in borns:
|
||||
lines.append(("%13.8f " * 9) % tuple(z.flatten()))
|
||||
|
@ -595,12 +672,30 @@ def get_BORN_lines(unitcell, borns, epsilon,
|
|||
|
||||
|
||||
def parse_BORN(primitive, symprec=1e-5, is_symmetry=True, filename="BORN"):
|
||||
with open(filename, 'r') as f:
|
||||
"""Parse BORN file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
primitive : Primitive
|
||||
Primitive cell.
|
||||
symprec : float, optional
|
||||
Symmetry tolerance. Default is 1e-5.
|
||||
is_symmetry : bool, optional
|
||||
When True, parse values are symmetrized. Default is True.
|
||||
filename : str, optional
|
||||
Filename.
|
||||
|
||||
"""
|
||||
with open(filename, "r") as f:
|
||||
return _parse_BORN_from_file_object(f, primitive, symprec, is_symmetry)
|
||||
|
||||
|
||||
def parse_BORN_from_strings(strings, primitive,
|
||||
symprec=1e-5, is_symmetry=True):
|
||||
def parse_BORN_from_strings(strings, primitive, symprec=1e-5, is_symmetry=True):
|
||||
"""Parse BORN file text.
|
||||
|
||||
See `parse_BORN` for parameters.
|
||||
|
||||
"""
|
||||
f = StringIO(strings)
|
||||
return _parse_BORN_from_file_object(f, primitive, symprec, is_symmetry)
|
||||
|
||||
|
@ -611,6 +706,18 @@ def _parse_BORN_from_file_object(f, primitive, symprec, is_symmetry):
|
|||
|
||||
|
||||
def get_born_parameters(f, primitive, prim_symmetry):
|
||||
"""Parse BORN file text.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
f :
|
||||
File pointer of BORN file.
|
||||
primitive : Primitive
|
||||
Primitive cell.
|
||||
prim_symmetry : Symmetry
|
||||
Symmetry of primitive cell.
|
||||
|
||||
"""
|
||||
line_arr = f.readline().split()
|
||||
if len(line_arr) < 1:
|
||||
print("BORN file format of line 1 is incorrect")
|
||||
|
@ -645,8 +752,7 @@ def get_born_parameters(f, primitive, prim_symmetry):
|
|||
|
||||
# Read Born effective charge
|
||||
independent_atoms = prim_symmetry.get_independent_atoms()
|
||||
borns = np.zeros((primitive.get_number_of_atoms(), 3, 3),
|
||||
dtype='double', order='C')
|
||||
borns = np.zeros((primitive.get_number_of_atoms(), 3, 3), dtype="double", order="C")
|
||||
|
||||
for i in independent_atoms:
|
||||
line = f.readline().split()
|
||||
|
@ -661,42 +767,68 @@ def get_born_parameters(f, primitive, prim_symmetry):
|
|||
# Check that the number of atoms in the BORN file was correct
|
||||
line = f.readline().split()
|
||||
if len(line) > 0:
|
||||
print("Too many atoms in the BORN file (it should only contain "
|
||||
"symmetry-independent atoms)")
|
||||
print(
|
||||
"Too many atoms in the BORN file (it should only contain "
|
||||
"symmetry-independent atoms)"
|
||||
)
|
||||
return None
|
||||
|
||||
_expand_borns(borns, primitive, prim_symmetry)
|
||||
non_anal = {'born': borns,
|
||||
'factor': factor,
|
||||
'dielectric': dielectric}
|
||||
non_anal = {"born": borns, "factor": factor, "dielectric": dielectric}
|
||||
if G_cutoff is not None:
|
||||
non_anal['G_cutoff'] = G_cutoff
|
||||
non_anal["G_cutoff"] = G_cutoff
|
||||
if Lambda is not None:
|
||||
non_anal['Lambda'] = Lambda
|
||||
non_anal["Lambda"] = Lambda
|
||||
|
||||
return non_anal
|
||||
|
||||
|
||||
def _expand_borns(borns, primitive, prim_symmetry):
|
||||
def _expand_borns(borns, primitive: PhonopyAtoms, prim_symmetry: Symmetry):
|
||||
# Expand Born effective charges to all atoms in the primitive cell
|
||||
rotations = prim_symmetry.get_symmetry_operations()['rotations']
|
||||
rotations = prim_symmetry.symmetry_operations["rotations"]
|
||||
map_operations = prim_symmetry.get_map_operations()
|
||||
map_atoms = prim_symmetry.get_map_atoms()
|
||||
|
||||
for i in range(primitive.get_number_of_atoms()):
|
||||
for i in range(len(primitive)):
|
||||
# R_cart = L R L^-1
|
||||
rot_cartesian = similarity_transformation(
|
||||
primitive.get_cell().transpose(), rotations[map_operations[i]])
|
||||
primitive.cell.T, rotations[map_operations[i]]
|
||||
)
|
||||
# R_cart^T B R_cart^-T (inverse rotation is required to transform)
|
||||
borns[i] = similarity_transformation(rot_cartesian.transpose(),
|
||||
borns[map_atoms[i]])
|
||||
borns[i] = similarity_transformation(rot_cartesian.T, borns[map_atoms[i]])
|
||||
|
||||
|
||||
#
|
||||
# phonopy.yaml
|
||||
#
|
||||
def is_file_phonopy_yaml(filename, keyword='phonopy'):
|
||||
with open(filename, 'r') as f:
|
||||
def is_file_phonopy_yaml(filename, keyword="phonopy"):
|
||||
"""Check whether the file is phonopy.yaml like file or not.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Filename.
|
||||
keyword : str
|
||||
When this keyword is found in dict keys returned by yaml loader,
|
||||
this function return True.
|
||||
|
||||
Example
|
||||
-------
|
||||
The initial part of phonopy_disp.yaml is like below.
|
||||
|
||||
phonopy:
|
||||
version: 2.7.0
|
||||
frequency_unit_conversion_factor: 15.633302
|
||||
symmetry_tolerance: 1.00000e-05
|
||||
configuration:
|
||||
cell_filename: "POSCAR-unitcell"
|
||||
create_displacements: ".true."
|
||||
primitive_axes: "auto"
|
||||
dim: "2 2 2"
|
||||
...
|
||||
|
||||
"""
|
||||
with open(filename, "r") as f:
|
||||
try:
|
||||
data = yaml.load(f, Loader=Loader)
|
||||
if data is None:
|
||||
|
@ -713,58 +845,63 @@ def is_file_phonopy_yaml(filename, keyword='phonopy'):
|
|||
# e-v.dat, thermal_properties.yaml
|
||||
#
|
||||
def read_thermal_properties_yaml(filenames):
|
||||
"""Read thermal_properties.yaml."""
|
||||
thermal_properties = []
|
||||
num_modes = []
|
||||
num_integrated_modes = []
|
||||
for filename in filenames:
|
||||
with open(filename) as f:
|
||||
tp_yaml = yaml.load(f, Loader=Loader)
|
||||
thermal_properties.append(tp_yaml['thermal_properties'])
|
||||
if 'num_modes' in tp_yaml and 'num_integrated_modes' in tp_yaml:
|
||||
num_modes.append(tp_yaml['num_modes'])
|
||||
num_integrated_modes.append(tp_yaml['num_integrated_modes'])
|
||||
thermal_properties.append(tp_yaml["thermal_properties"])
|
||||
if "num_modes" in tp_yaml and "num_integrated_modes" in tp_yaml:
|
||||
num_modes.append(tp_yaml["num_modes"])
|
||||
num_integrated_modes.append(tp_yaml["num_integrated_modes"])
|
||||
|
||||
temperatures = [v['temperature'] for v in thermal_properties[0]]
|
||||
temperatures = [v["temperature"] for v in thermal_properties[0]]
|
||||
temp = []
|
||||
cv = []
|
||||
entropy = []
|
||||
fe_phonon = []
|
||||
for i, tp in enumerate(thermal_properties):
|
||||
temp.append([v['temperature'] for v in tp])
|
||||
temp.append([v["temperature"] for v in tp])
|
||||
if not np.allclose(temperatures, temp):
|
||||
msg = ['', ]
|
||||
msg = [
|
||||
"",
|
||||
]
|
||||
msg.append("Check your input files")
|
||||
msg.append("Disagreement of temperature range or step")
|
||||
for t, fname in zip(temp, filenames):
|
||||
msg.append("%s: Range [ %d, %d ], Step %f" %
|
||||
(fname, int(t[0]), int(t[-1]), t[1] - t[0]))
|
||||
msg.append('')
|
||||
msg.append(
|
||||
"%s: Range [ %d, %d ], Step %f"
|
||||
% (fname, int(t[0]), int(t[-1]), t[1] - t[0])
|
||||
)
|
||||
msg.append("")
|
||||
msg.append("Stop phonopy-qha")
|
||||
raise RuntimeError(msg)
|
||||
cv.append([v['heat_capacity'] for v in tp])
|
||||
entropy.append([v['entropy'] for v in tp])
|
||||
fe_phonon.append([v['free_energy'] for v in tp])
|
||||
cv.append([v["heat_capacity"] for v in tp])
|
||||
entropy.append([v["entropy"] for v in tp])
|
||||
fe_phonon.append([v["free_energy"] for v in tp])
|
||||
|
||||
# shape=(temperatures, volumes)
|
||||
cv = np.array(cv).T
|
||||
entropy = np.array(entropy).T
|
||||
fe_phonon = np.array(fe_phonon).T
|
||||
|
||||
return (temperatures, cv, entropy, fe_phonon, num_modes,
|
||||
num_integrated_modes)
|
||||
return (temperatures, cv, entropy, fe_phonon, num_modes, num_integrated_modes)
|
||||
|
||||
|
||||
def read_v_e(filename):
|
||||
"""Read v-e.dat file."""
|
||||
data = _parse_QHA_data(filename)
|
||||
if data.shape[1] != 2:
|
||||
msg = ("File format of %s is incorrect for reading e-v data." %
|
||||
filename)
|
||||
msg = "File format of %s is incorrect for reading e-v data." % filename
|
||||
raise RuntimeError(msg)
|
||||
volumes, electronic_energies = data.T
|
||||
return volumes, electronic_energies
|
||||
|
||||
|
||||
def read_efe(filename):
|
||||
"""Read fe-v.dat (efe) file."""
|
||||
data = _parse_QHA_data(filename)
|
||||
temperatures = data[:, 0]
|
||||
free_energies = data[:, 1:]
|
||||
|
@ -775,10 +912,10 @@ def _parse_QHA_data(filename):
|
|||
data = []
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
if line.strip() == '' or line.strip()[0] == '#':
|
||||
if line.strip() == "" or line.strip()[0] == "#":
|
||||
continue
|
||||
if '#' in line:
|
||||
data.append([float(x) for x in line.split('#')[0].split()])
|
||||
if "#" in line:
|
||||
data.append([float(x) for x in line.split("#")[0].split()])
|
||||
else:
|
||||
data.append([float(x) for x in line.split()])
|
||||
return np.array(data)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Mode Grueneisen paramater calculation."""
|
||||
# Copyright (C) 2017 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -31,7 +32,3 @@
|
|||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from .mesh import GruneisenMesh
|
||||
from .band_structure import GruneisenBandStructure
|
||||
from .thermal_properties import GruneisenThermalProperties
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Mode Grueneisen parameter band structure calculation."""
|
||||
# Copyright (C) 2012 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,16 +33,21 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import gzip
|
||||
import yaml
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from .core import GruneisenBase
|
||||
import yaml
|
||||
|
||||
from phonopy.gruneisen.core import GruneisenBase
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
class GruneisenBandStructure(GruneisenBase):
|
||||
def __init__(self,
|
||||
"""Class to calculate mode Grueneisen parameter along band structure paths."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
paths,
|
||||
dynmat,
|
||||
dynmat_plus,
|
||||
|
@ -49,15 +55,18 @@ class GruneisenBandStructure(GruneisenBase):
|
|||
delta_strain=None,
|
||||
path_connections=None,
|
||||
labels=None,
|
||||
factor=VaspToTHz):
|
||||
GruneisenBase.__init__(self,
|
||||
factor=VaspToTHz,
|
||||
):
|
||||
"""Init method."""
|
||||
super().__init__(
|
||||
dynmat,
|
||||
dynmat_plus,
|
||||
dynmat_minus,
|
||||
delta_strain=delta_strain,
|
||||
is_band_connection=True)
|
||||
self._cell = dynmat.get_primitive()
|
||||
rec_lattice = np.linalg.inv(self._cell.get_cell())
|
||||
is_band_connection=True,
|
||||
)
|
||||
self._cell = dynmat.primitive
|
||||
rec_lattice = np.linalg.inv(self._cell.cell)
|
||||
distance_shift = 0.0
|
||||
|
||||
self._paths = []
|
||||
|
@ -66,7 +75,8 @@ class GruneisenBandStructure(GruneisenBase):
|
|||
distances = np.zeros(len(qpoints))
|
||||
delta_qpoints = qpoints[1:] - qpoints[:-1]
|
||||
delta_distances = np.sqrt(
|
||||
(np.dot(delta_qpoints, rec_lattice) ** 2).sum(axis=1))
|
||||
(np.dot(delta_qpoints, rec_lattice) ** 2).sum(axis=1)
|
||||
)
|
||||
for i, dd in enumerate(delta_distances):
|
||||
distances[i + 1] = distances[i] + dd
|
||||
|
||||
|
@ -75,67 +85,84 @@ class GruneisenBandStructure(GruneisenBase):
|
|||
frequencies = np.sqrt(abs(eigenvalues)) * np.sign(eigenvalues) * factor
|
||||
distances_with_shift = distances + distance_shift
|
||||
|
||||
self._paths.append([qpoints,
|
||||
self._paths.append(
|
||||
[
|
||||
qpoints,
|
||||
distances,
|
||||
self._gruneisen,
|
||||
eigenvalues,
|
||||
self._eigenvectors,
|
||||
frequencies,
|
||||
distances_with_shift])
|
||||
distances_with_shift,
|
||||
]
|
||||
)
|
||||
|
||||
distance_shift = distances_with_shift[-1]
|
||||
self._labels = None
|
||||
self._path_connections = None
|
||||
if path_connections is None:
|
||||
self._path_connections = [True, ] * len(self._paths)
|
||||
self._path_connections = [
|
||||
True,
|
||||
] * len(self._paths)
|
||||
self._path_connections[-1] = False
|
||||
else:
|
||||
self._path_connections = path_connections
|
||||
if (labels is not None and
|
||||
len(labels) == (2 - np.array(self._path_connections)).sum()):
|
||||
if (
|
||||
labels is not None
|
||||
and len(labels) == (2 - np.array(self._path_connections)).sum()
|
||||
):
|
||||
self._labels = labels
|
||||
|
||||
def get_qpoints(self):
|
||||
"""Return q-points."""
|
||||
return [path[0] for path in self._paths]
|
||||
|
||||
def get_distances(self):
|
||||
"""Return distances."""
|
||||
return [path[6] for path in self._paths]
|
||||
|
||||
def get_gruneisen(self):
|
||||
"""Return mode Gruneisen parameters."""
|
||||
return [path[2] for path in self._paths]
|
||||
|
||||
def get_eigenvalues(self):
|
||||
"""Return eigenvalues."""
|
||||
return [path[3] for path in self._paths]
|
||||
|
||||
def get_eigenvectors(self):
|
||||
"""Return eigenvectors."""
|
||||
return [path[4] for path in self._paths]
|
||||
|
||||
def get_frequencies(self):
|
||||
"""Return frequencies."""
|
||||
return [path[5] for path in self._paths]
|
||||
|
||||
def write_yaml(self, comment=None, filename=None, compression=None):
|
||||
"""Write results to file in yaml."""
|
||||
if filename is not None:
|
||||
_filename = filename
|
||||
|
||||
if compression is None:
|
||||
if filename is None:
|
||||
_filename = "gruneisen.yaml"
|
||||
with open(_filename, 'w') as w:
|
||||
with open(_filename, "w") as w:
|
||||
self._write_yaml(w, comment)
|
||||
elif compression == 'gzip':
|
||||
elif compression == "gzip":
|
||||
if filename is None:
|
||||
_filename = "gruneisen.yaml.gz"
|
||||
with gzip.open(_filename, 'wb') as w:
|
||||
with gzip.open(_filename, "wb") as w:
|
||||
self._write_yaml(w, comment, is_binary=True)
|
||||
elif compression == 'lzma':
|
||||
elif compression == "lzma":
|
||||
try:
|
||||
import lzma
|
||||
except ImportError:
|
||||
raise("Reading a lzma compressed file is not supported "
|
||||
"by this python version.")
|
||||
raise (
|
||||
"Reading a lzma compressed file is not supported "
|
||||
"by this python version."
|
||||
)
|
||||
if filename is None:
|
||||
_filename = "gruneisen.yaml.xz"
|
||||
with lzma.open(_filename, 'w') as w:
|
||||
with lzma.open(_filename, "w") as w:
|
||||
self._write_yaml(w, comment, is_binary=True)
|
||||
|
||||
def _write_yaml(self, w, comment, is_binary=False):
|
||||
|
@ -155,41 +182,43 @@ class GruneisenBandStructure(GruneisenBase):
|
|||
text.append("labels:")
|
||||
if self._is_legacy_plot:
|
||||
for i in range(len(self._paths)):
|
||||
text.append("- [ \'%s\', \'%s\' ]" %
|
||||
(self._labels[i], self._labels[i + 1]))
|
||||
text.append(
|
||||
"- [ '%s', '%s' ]" % (self._labels[i], self._labels[i + 1])
|
||||
)
|
||||
else:
|
||||
i = 0
|
||||
for c in self._path_connections:
|
||||
text.append("- [ \'%s\', \'%s\' ]" %
|
||||
(self._labels[i], self._labels[i + 1]))
|
||||
text.append(
|
||||
"- [ '%s', '%s' ]" % (self._labels[i], self._labels[i + 1])
|
||||
)
|
||||
if c:
|
||||
i += 1
|
||||
else:
|
||||
i += 2
|
||||
text.append("reciprocal_lattice:")
|
||||
for vec, axis in zip(rec_lattice.T, ('a*', 'b*', 'c*')):
|
||||
text.append("- [ %12.8f, %12.8f, %12.8f ] # %2s" %
|
||||
(tuple(vec) + (axis,)))
|
||||
for vec, axis in zip(rec_lattice.T, ("a*", "b*", "c*")):
|
||||
text.append("- [ %12.8f, %12.8f, %12.8f ] # %2s" % (tuple(vec) + (axis,)))
|
||||
text.append("natom: %-7d" % (natom))
|
||||
text.append(str(self._cell))
|
||||
text.append('')
|
||||
text.append("")
|
||||
text.append("path:")
|
||||
text.append('')
|
||||
text.append("")
|
||||
|
||||
for band_structure in self._paths:
|
||||
(qpoints,
|
||||
(
|
||||
qpoints,
|
||||
distances,
|
||||
gamma,
|
||||
eigenvalues,
|
||||
_,
|
||||
frequencies,
|
||||
distances_with_shift) = band_structure
|
||||
distances_with_shift,
|
||||
) = band_structure
|
||||
|
||||
text.append("- nqpoint: %d" % len(qpoints))
|
||||
text.append(" phonon:")
|
||||
for q, d, gs, freqs in zip(qpoints, distances, gamma, frequencies):
|
||||
text.append(" - q-position: [ %10.7f, %10.7f, %10.7f ]" %
|
||||
tuple(q))
|
||||
text.append(" - q-position: [ %10.7f, %10.7f, %10.7f ]" % tuple(q))
|
||||
text.append(" distance: %10.7f" % d)
|
||||
text.append(" band:")
|
||||
for i, (g, freq) in enumerate(zip(gs, freqs)):
|
||||
|
@ -206,25 +235,25 @@ class GruneisenBandStructure(GruneisenBase):
|
|||
if sys.version_info < (3, 0):
|
||||
w.write(bytes(text))
|
||||
else:
|
||||
w.write(bytes(text, 'utf8'))
|
||||
w.write(bytes(text, "utf8"))
|
||||
else:
|
||||
w.write(text)
|
||||
|
||||
def plot(self,
|
||||
axarr,
|
||||
epsilon=None,
|
||||
color_scheme=None):
|
||||
def plot(self, axarr, epsilon=None, color_scheme=None):
|
||||
"""Return pyplot of band structure calculation results."""
|
||||
for band_structure in self._paths:
|
||||
self._plot(axarr, band_structure, epsilon, color_scheme)
|
||||
|
||||
def _plot(self, axarr, band_structure, epsilon, color_scheme):
|
||||
(qpoints,
|
||||
(
|
||||
qpoints,
|
||||
distances,
|
||||
gamma,
|
||||
eigenvalues,
|
||||
_,
|
||||
frequencies,
|
||||
distances_with_shift) = band_structure
|
||||
distances_with_shift,
|
||||
) = band_structure
|
||||
|
||||
n = len(gamma.T) - 1
|
||||
ax1, ax2 = axarr
|
||||
|
@ -254,25 +283,25 @@ class GruneisenBandStructure(GruneisenBase):
|
|||
if abs(freqs[j]) < abs(max(freqs)) / 10:
|
||||
curve[j] = curve[cutoff_index]
|
||||
|
||||
self._plot_a_band(ax1, curve, distances_with_shift, i, n,
|
||||
color_scheme)
|
||||
self._plot_a_band(ax1, curve, distances_with_shift, i, n, color_scheme)
|
||||
ax1.set_xlim(0, distances_with_shift[-1])
|
||||
|
||||
for i, freqs in enumerate(frequencies.T):
|
||||
self._plot_a_band(ax2, freqs, distances_with_shift, i, n,
|
||||
color_scheme)
|
||||
self._plot_a_band(ax2, freqs, distances_with_shift, i, n, color_scheme)
|
||||
ax2.set_xlim(0, distances_with_shift[-1])
|
||||
|
||||
def _plot_a_band(self, ax, curve, distances_with_shift, i, n, color_scheme):
|
||||
color = None
|
||||
if color_scheme == 'RB':
|
||||
color = (1. / n * i, 0, 1./ n * (n - i))
|
||||
elif color_scheme == 'RG':
|
||||
color = (1. / n * i, 1./ n * (n - i), 0)
|
||||
elif color_scheme == 'RGB':
|
||||
color = (max(2./ n * (i - n / 2.), 0),
|
||||
min(2./ n * i, 2./ n * (n - i)),
|
||||
max(2./ n * (n / 2. - i), 0))
|
||||
if color_scheme == "RB":
|
||||
color = (1.0 / n * i, 0, 1.0 / n * (n - i))
|
||||
elif color_scheme == "RG":
|
||||
color = (1.0 / n * i, 1.0 / n * (n - i), 0)
|
||||
elif color_scheme == "RGB":
|
||||
color = (
|
||||
max(2.0 / n * (i - n / 2.0), 0),
|
||||
min(2.0 / n * i, 2.0 / n * (n - i)),
|
||||
max(2.0 / n * (n / 2.0 - i), 0),
|
||||
)
|
||||
if color:
|
||||
ax.plot(distances_with_shift, curve, color=color)
|
||||
else:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Mode Grueneisen parameter calculation."""
|
||||
# Copyright (C) 2012 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,26 +33,35 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
from phonopy.phonon.band_structure import estimate_band_connection
|
||||
from phonopy.phonon.degeneracy import rotate_eigenvectors
|
||||
|
||||
|
||||
class GruneisenBase(object):
|
||||
def __init__(self,
|
||||
dynmat,
|
||||
dynmat_plus,
|
||||
dynmat_minus,
|
||||
class GruneisenBase:
|
||||
"""Base class of mode Grueneisen parameter calculation classes."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dynmat: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
dynmat_plus: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
dynmat_minus: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
delta_strain=None,
|
||||
qpoints=None,
|
||||
is_band_connection=False):
|
||||
is_band_connection=False,
|
||||
):
|
||||
"""Init method."""
|
||||
self._dynmat = dynmat
|
||||
self._dynmat_plus = dynmat_plus
|
||||
self._dynmat_minus = dynmat_minus
|
||||
if delta_strain is None:
|
||||
volume = dynmat.get_primitive().get_volume()
|
||||
volume_plus = dynmat_plus.get_primitive().get_volume()
|
||||
volume_minus = dynmat_minus.get_primitive().get_volume()
|
||||
volume = dynmat.primitive.volume
|
||||
volume_plus = dynmat_plus.primitive.volume
|
||||
volume_minus = dynmat_minus.primitive.volume
|
||||
dV = volume_plus - volume_minus
|
||||
self._delta_strain = dV / volume
|
||||
else:
|
||||
|
@ -60,25 +70,25 @@ class GruneisenBase(object):
|
|||
self._qpoints = qpoints
|
||||
|
||||
self._gruneisen = None
|
||||
self._gamma_prime = None
|
||||
self._eigenvalues = None
|
||||
if qpoints is not None:
|
||||
self._set_gruneisen()
|
||||
|
||||
def set_qpoints(self, qpoints):
|
||||
"""Set q-points."""
|
||||
self._qpoints = qpoints
|
||||
self._set_gruneisen()
|
||||
|
||||
def get_gruneisen(self):
|
||||
"""Return mode Grueneisen parameters."""
|
||||
return self._gruneisen
|
||||
|
||||
def get_gamma_prime(self):
|
||||
return self._gamma_prime
|
||||
|
||||
def get_eigenvalues(self):
|
||||
"""Return eigenvalues."""
|
||||
return self._eigenvalues
|
||||
|
||||
def get_eigenvectors(self):
|
||||
"""Return eigenvectors."""
|
||||
return self._eigenvectors
|
||||
|
||||
def _set_gruneisen(self):
|
||||
|
@ -96,7 +106,7 @@ class GruneisenBase(object):
|
|||
else:
|
||||
self._dynmat.run(q)
|
||||
|
||||
dm = self._dynmat.get_dynamical_matrix()
|
||||
dm = self._dynmat.dynamical_matrix
|
||||
evals, evecs = np.linalg.eigh(dm)
|
||||
evals_at_q = evals.real
|
||||
dD = self._get_dD(q, self._dynmat_minus, self._dynmat_plus)
|
||||
|
@ -105,9 +115,8 @@ class GruneisenBase(object):
|
|||
if self._is_band_connection:
|
||||
if prev_eigvecs is not None:
|
||||
band_order = estimate_band_connection(
|
||||
prev_eigvecs,
|
||||
evecs_at_q,
|
||||
band_order)
|
||||
prev_eigvecs, evecs_at_q, band_order
|
||||
)
|
||||
eigvals.append(evals_at_q[band_order])
|
||||
eigvecs.append(evecs_at_q[:, band_order])
|
||||
edDe.append(edDe_at_q[band_order])
|
||||
|
@ -117,20 +126,26 @@ class GruneisenBase(object):
|
|||
eigvecs.append(evecs_at_q)
|
||||
edDe.append(edDe_at_q)
|
||||
|
||||
edDe = np.array(edDe, dtype='double', order='C')
|
||||
self._eigenvalues = np.array(eigvals, dtype='double', order='C')
|
||||
edDe = np.array(edDe, dtype="double", order="C")
|
||||
self._eigenvalues = np.array(eigvals, dtype="double", order="C")
|
||||
itemsize = self._eigenvalues.itemsize
|
||||
self._eigenvectors = np.array(eigvecs,
|
||||
dtype=("c%d" % (itemsize * 2)), order='C')
|
||||
self._eigenvectors = np.array(
|
||||
eigvecs, dtype=("c%d" % (itemsize * 2)), order="C"
|
||||
)
|
||||
self._gruneisen = -edDe / self._delta_strain / self._eigenvalues / 2
|
||||
|
||||
def _get_dD(self, q, d_a, d_b):
|
||||
if (self._is_band_connection and d_a.is_nac() and d_b.is_nac()):
|
||||
def _get_dD(
|
||||
self,
|
||||
q,
|
||||
d_a: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
d_b: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
):
|
||||
if self._is_band_connection and d_a.is_nac() and d_b.is_nac():
|
||||
d_a.run(q, q_direction=self._q_direction)
|
||||
d_b.run(q, q_direction=self._q_direction)
|
||||
else:
|
||||
d_a.run(q)
|
||||
d_b.run(q)
|
||||
dm_a = d_a.get_dynamical_matrix()
|
||||
dm_b = d_b.get_dynamical_matrix()
|
||||
return (dm_b - dm_a)
|
||||
dm_a = d_a.dynamical_matrix
|
||||
dm_b = d_b.dynamical_matrix
|
||||
return dm_b - dm_a
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Mode Grueneisen parameters calculation on sampling mesh."""
|
||||
# Copyright (C) 2012 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,18 +33,21 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import gzip
|
||||
import yaml
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from .core import GruneisenBase
|
||||
|
||||
from phonopy.gruneisen.core import GruneisenBase
|
||||
from phonopy.structure.grid_points import get_qpoints
|
||||
from phonopy.phonon.thermal_properties import mode_cv
|
||||
from phonopy.units import THzToEv, VaspToTHz
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
class GruneisenMesh(GruneisenBase):
|
||||
def __init__(self,
|
||||
"""Class to calculate mode Grueneisen parameters on sampling mesh."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dynmat,
|
||||
dynmat_plus,
|
||||
dynmat_minus,
|
||||
|
@ -54,81 +58,82 @@ class GruneisenMesh(GruneisenBase):
|
|||
is_gamma_center=False,
|
||||
is_mesh_symmetry=True,
|
||||
rotations=None, # Point group operations in real space
|
||||
factor=VaspToTHz):
|
||||
GruneisenBase.__init__(self,
|
||||
dynmat,
|
||||
dynmat_plus,
|
||||
dynmat_minus,
|
||||
delta_strain=delta_strain)
|
||||
self._mesh = np.array(mesh, dtype='intc')
|
||||
factor=VaspToTHz,
|
||||
):
|
||||
"""Init method."""
|
||||
super().__init__(dynmat, dynmat_plus, dynmat_minus, delta_strain=delta_strain)
|
||||
self._mesh = np.array(mesh, dtype="intc")
|
||||
self._factor = factor
|
||||
self._cell = dynmat.get_primitive()
|
||||
self._cell = dynmat.primitive
|
||||
self._qpoints, self._weights = get_qpoints(
|
||||
self._mesh,
|
||||
np.linalg.inv(self._cell.get_cell()),
|
||||
np.linalg.inv(self._cell.cell),
|
||||
q_mesh_shift=shift,
|
||||
is_time_reversal=is_time_reversal,
|
||||
is_gamma_center=is_gamma_center,
|
||||
rotations=rotations,
|
||||
is_mesh_symmetry=is_mesh_symmetry)
|
||||
is_mesh_symmetry=is_mesh_symmetry,
|
||||
)
|
||||
self.set_qpoints(self._qpoints)
|
||||
self._gamma = self._gruneisen
|
||||
self._frequencies = np.sqrt(
|
||||
abs(self._eigenvalues)) * np.sign(self._eigenvalues) * self._factor
|
||||
self._frequencies = (
|
||||
np.sqrt(abs(self._eigenvalues)) * np.sign(self._eigenvalues) * self._factor
|
||||
)
|
||||
|
||||
def get_gruneisen(self):
|
||||
"""Return mode Grueneisen parameters."""
|
||||
return self._gamma
|
||||
|
||||
def get_gamma_prime(self):
|
||||
return self._gamma_prime
|
||||
|
||||
def get_mesh_numbers(self):
|
||||
"""Return mesh numbers."""
|
||||
return self._mesh
|
||||
|
||||
def get_qpoints(self):
|
||||
"""Return (irreducible) q-points."""
|
||||
return self._qpoints
|
||||
|
||||
def get_weights(self):
|
||||
"""Return weights of (irreducible) q-points."""
|
||||
return self._weights
|
||||
|
||||
def get_eigenvalues(self):
|
||||
"""Return eigenvalues of dynamical matrices."""
|
||||
return self._eigenvalues
|
||||
|
||||
def get_eigenvectors(self):
|
||||
"""Return phonon eigenvectors."""
|
||||
return self._eigenvectors
|
||||
|
||||
def get_frequencies(self):
|
||||
"""Return phonon frequencies."""
|
||||
return self._frequencies
|
||||
|
||||
def get_eigenvectors(self):
|
||||
"""
|
||||
See the detail of array shape in phonopy.phonon.mesh.
|
||||
"""
|
||||
return self._eigenvectors
|
||||
|
||||
def write_yaml(self, comment=None, filename=None, compression=None):
|
||||
"""Write results in yaml file."""
|
||||
if filename is not None:
|
||||
_filename = filename
|
||||
|
||||
if compression is None:
|
||||
if filename is None:
|
||||
_filename = "gruneisen.yaml"
|
||||
with open(_filename, 'w') as w:
|
||||
with open(_filename, "w") as w:
|
||||
self._write_yaml(w, comment)
|
||||
elif compression == 'gzip':
|
||||
elif compression == "gzip":
|
||||
if filename is None:
|
||||
_filename = "gruneisen.yaml.gz"
|
||||
with gzip.open(_filename, 'wb') as w:
|
||||
with gzip.open(_filename, "wb") as w:
|
||||
self._write_yaml(w, comment, is_binary=True)
|
||||
elif compression == 'lzma':
|
||||
elif compression == "lzma":
|
||||
try:
|
||||
import lzma
|
||||
except ImportError:
|
||||
raise("Reading a lzma compressed file is not supported "
|
||||
"by this python version.")
|
||||
raise (
|
||||
"Reading a lzma compressed file is not supported "
|
||||
"by this python version."
|
||||
)
|
||||
if filename is None:
|
||||
_filename = "gruneisen.yaml.xz"
|
||||
with lzma.open(_filename, 'w') as w:
|
||||
with lzma.open(_filename, "w") as w:
|
||||
self._write_yaml(w, comment, is_binary=True)
|
||||
|
||||
def _write_yaml(self, w, comment, is_binary=False):
|
||||
|
@ -138,17 +143,15 @@ class GruneisenMesh(GruneisenBase):
|
|||
text.append("mesh: [ %5d, %5d, %5d ]" % tuple(self._mesh))
|
||||
text.append("nqpoint: %d" % len(self._qpoints))
|
||||
text.append("reciprocal_lattice:")
|
||||
for vec, axis in zip(rec_lattice.T, ('a*', 'b*', 'c*')):
|
||||
text.append("- [ %12.8f, %12.8f, %12.8f ] # %2s" %
|
||||
(tuple(vec) + (axis,)))
|
||||
for vec, axis in zip(rec_lattice.T, ("a*", "b*", "c*")):
|
||||
text.append("- [ %12.8f, %12.8f, %12.8f ] # %2s" % (tuple(vec) + (axis,)))
|
||||
text.append("natom: %-7d" % natom)
|
||||
text.append(str(self._cell))
|
||||
text.append('')
|
||||
text.append("")
|
||||
text.append("phonon:")
|
||||
for q, m, gs, freqs in zip(self._qpoints,
|
||||
self._weights,
|
||||
self._gamma,
|
||||
self._frequencies):
|
||||
for q, m, gs, freqs in zip(
|
||||
self._qpoints, self._weights, self._gamma, self._frequencies
|
||||
):
|
||||
text.append("- q-position: [ %10.7f, %10.7f, %10.7f ]" % tuple(q))
|
||||
text.append(" multiplicity: %d" % m)
|
||||
text.append(" band:")
|
||||
|
@ -166,54 +169,52 @@ class GruneisenMesh(GruneisenBase):
|
|||
if sys.version_info < (3, 0):
|
||||
w.write(bytes(text))
|
||||
else:
|
||||
w.write(bytes(text, 'utf8'))
|
||||
w.write(bytes(text, "utf8"))
|
||||
else:
|
||||
w.write(text)
|
||||
|
||||
def write_hdf5(self, filename="gruneisen.hdf5"):
|
||||
"""Write results in hdf5 file."""
|
||||
import h5py
|
||||
w = h5py.File(filename, 'w')
|
||||
w.create_dataset('mesh', data=self._mesh)
|
||||
w.create_dataset('gruneisen', data=self._gamma)
|
||||
w.create_dataset('weight', data=self._weights)
|
||||
w.create_dataset('frequency', data=self._frequencies)
|
||||
w.create_dataset('qpoint', data=self._qpoints)
|
||||
|
||||
w = h5py.File(filename, "w")
|
||||
w.create_dataset("mesh", data=self._mesh)
|
||||
w.create_dataset("gruneisen", data=self._gamma)
|
||||
w.create_dataset("weight", data=self._weights)
|
||||
w.create_dataset("frequency", data=self._frequencies)
|
||||
w.create_dataset("qpoint", data=self._qpoints)
|
||||
w.close()
|
||||
|
||||
def plot(self,
|
||||
plt,
|
||||
cutoff_frequency=None,
|
||||
color_scheme=None,
|
||||
marker='o',
|
||||
markersize=None):
|
||||
def plot(
|
||||
self, plt, cutoff_frequency=None, color_scheme=None, marker="o", markersize=None
|
||||
):
|
||||
"""Return pyplot of calculation results."""
|
||||
n = len(self._gamma.T) - 1
|
||||
for i, (g, freqs) in enumerate(zip(self._gamma.T,
|
||||
self._frequencies.T)):
|
||||
for i, (g, freqs) in enumerate(zip(self._gamma.T, self._frequencies.T)):
|
||||
if cutoff_frequency:
|
||||
g = np.extract(freqs > cutoff_frequency, g)
|
||||
freqs = np.extract(freqs > cutoff_frequency, freqs)
|
||||
|
||||
if color_scheme == 'RB':
|
||||
color = (1. / n * i, 0, 1./ n * (n - i))
|
||||
if color_scheme == "RB":
|
||||
color = (1.0 / n * i, 0, 1.0 / n * (n - i))
|
||||
if markersize:
|
||||
plt.plot(freqs, g, marker,
|
||||
color=color, markersize=markersize)
|
||||
plt.plot(freqs, g, marker, color=color, markersize=markersize)
|
||||
else:
|
||||
plt.plot(freqs, g, marker, color=color)
|
||||
elif color_scheme == 'RG':
|
||||
color = (1. / n * i, 1./ n * (n - i), 0)
|
||||
elif color_scheme == "RG":
|
||||
color = (1.0 / n * i, 1.0 / n * (n - i), 0)
|
||||
if markersize:
|
||||
plt.plot(freqs, g, marker,
|
||||
color=color, markersize=markersize)
|
||||
plt.plot(freqs, g, marker, color=color, markersize=markersize)
|
||||
else:
|
||||
plt.plot(freqs, g, marker, color=color)
|
||||
elif color_scheme == 'RGB':
|
||||
color = (max(2./ n * (i - n / 2.), 0),
|
||||
min(2./ n * i, 2./ n * (n - i)),
|
||||
max(2./ n * (n / 2. - i), 0))
|
||||
elif color_scheme == "RGB":
|
||||
color = (
|
||||
max(2.0 / n * (i - n / 2.0), 0),
|
||||
min(2.0 / n * i, 2.0 / n * (n - i)),
|
||||
max(2.0 / n * (n / 2.0 - i), 0),
|
||||
)
|
||||
if markersize:
|
||||
plt.plot(freqs, g, marker,
|
||||
color=color, markersize=markersize)
|
||||
plt.plot(freqs, g, marker, color=color, markersize=markersize)
|
||||
else:
|
||||
plt.plot(freqs, g, marker, color=color)
|
||||
else:
|
||||
|
@ -221,27 +222,3 @@ class GruneisenMesh(GruneisenBase):
|
|||
plt.plot(freqs, g, marker, markersize=markersize)
|
||||
else:
|
||||
plt.plot(freqs, g, marker)
|
||||
|
||||
def get_thermodynamic_Gruneisen_parameter(gammas,
|
||||
frequencies,
|
||||
multiplicities,
|
||||
t):
|
||||
if t > 0:
|
||||
conditions = (frequencies > 0)
|
||||
freq_temp = np.where(conditions, x, 1)
|
||||
cv_temp = mode_cv(t, frequencies * THzToEv)
|
||||
cv = np.where(conditions, x, 0)
|
||||
return (np.dot(multiplicities, cv * gammas).sum() /
|
||||
np.dot(multiplicities, cv).sum())
|
||||
else:
|
||||
return 0.
|
||||
|
||||
def get_thermal_expansion_coefficient(gammas,
|
||||
frequencies,
|
||||
multiplicities,
|
||||
t):
|
||||
if t > 0:
|
||||
return np.dot(multiplicities,
|
||||
mode_cv(t, frequencies * THzToEv) * gammas).sum()
|
||||
else:
|
||||
return 0.
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright (C) 2015 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of phonopy.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# * Neither the name of the phonopy project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import numpy as np
|
||||
from phonopy.phonon.thermal_properties import ThermalProperties
|
||||
|
||||
class GruneisenThermalProperties(object):
|
||||
def __init__(self,
|
||||
gruneisen_mesh,
|
||||
volumes,
|
||||
t_step=2,
|
||||
t_max=2004,
|
||||
t_min=0,
|
||||
cutoff_frequency=None):
|
||||
phonon = gruneisen_mesh.get_phonon()
|
||||
self._cutoff_frequency = cutoff_frequency
|
||||
self._factor = phonon.get_unit_conversion_factor(),
|
||||
self._V0 = phonon.get_primitive().get_volume()
|
||||
self._gamma = gruneisen_mesh.get_gruneisen()
|
||||
self._gamma_prime = gruneisen_mesh.get_gamma_prime()
|
||||
self._weights = gruneisen_mesh.get_weights()
|
||||
self._eigenvalues = gruneisen_mesh.get_eigenvalues()
|
||||
self._frequencies = gruneisen_mesh.get_frequencies()
|
||||
self._thermal_properties = []
|
||||
for V in volumes:
|
||||
tp = self._get_thermal_properties_at_V(V)
|
||||
tp.set_temperature_range(t_min=t_min, t_max=t_max, t_step=t_step)
|
||||
tp.run()
|
||||
self._thermal_properties.append(tp)
|
||||
|
||||
def get_thermal_properties(self):
|
||||
"""Return a set of phonopy.phonon::ThermalProperties object"""
|
||||
return self._thermal_properties
|
||||
|
||||
def write_yaml(self, filename='thermal_properties'):
|
||||
for i, tp in enumerate(self._thermal_properties):
|
||||
tp.write_yaml(filename="%s-%02d.yaml" % (filename, i))
|
||||
|
||||
def _get_thermal_properties_at_V(self, V):
|
||||
frequencies = self._get_frequencies_at_V(V)
|
||||
tp = ThermalProperties(frequencies,
|
||||
weights=self._weights,
|
||||
cutoff_frequency=self._cutoff_frequency)
|
||||
return tp
|
||||
|
||||
def _get_frequencies_at_V(self, V):
|
||||
return self._get_frequencies_at_V_analytical_solution(V)
|
||||
|
||||
def _get_frequencies_at_V_analytical_solution(self, V):
|
||||
eigvals = self._eigenvalues * np.exp(-2 * self._gamma *
|
||||
np.log(V / self._V0))
|
||||
return np.sqrt(abs(eigvals)) * np.sign(eigvals) * self._factor
|
||||
|
||||
def _get_frequencies_at_V_analytical_solution_with_1st_correction(self, V):
|
||||
V0 = self._V0
|
||||
g_prime = self._gamma_prime / V0
|
||||
eigvals = self._eigenvalues * np.exp(
|
||||
-2 * ((self._gamma - g_prime * V0) * np.log(V / V0)
|
||||
+ g_prime * (V - V0)))
|
||||
return np.sqrt(abs(eigvals)) * np.sign(eigvals) * self._factor
|
||||
|
||||
def _get_frequencies_at_V_Taylor_expansion_to_1st_order(self, V):
|
||||
return self._frequencies * (
|
||||
1.0
|
||||
- self._gamma * (V - self._V0) / self._V0)
|
||||
|
||||
def _get_frequencies_at_V_Taylor_expansion_to_2nd_order(self, V):
|
||||
return self._frequencies * (
|
||||
1.0
|
||||
- self._gamma * (V - self._V0) / self._V0
|
||||
- self._gamma_prime * ((V - self._V0) / self._V0) ** 2 / 2)
|
|
@ -0,0 +1,34 @@
|
|||
"""Routines of pre-phonon calculations."""
|
||||
# Copyright (C) 2021 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of phonopy.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# * Neither the name of the phonopy project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
@ -33,11 +33,16 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
from phonopy.structure.cells import sparse_to_dense_svecs
|
||||
|
||||
|
||||
class DerivativeOfDynamicalMatrix(object):
|
||||
class DerivativeOfDynamicalMatrix:
|
||||
"""Compute analytical derivative of dynamical matrix.
|
||||
|
||||
This can be used dynamical matrix without NAC or with Wang-NAC.
|
||||
|
@ -49,7 +54,7 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, dynamical_matrix):
|
||||
def __init__(self, dynamical_matrix: Union[DynamicalMatrix, DynamicalMatrixNAC]):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
|
@ -63,13 +68,13 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
self._scell = self._dynmat.supercell
|
||||
self._pcell = self._dynmat.primitive
|
||||
|
||||
dtype = 'int_'
|
||||
dtype = "int_"
|
||||
self._p2s_map = np.array(self._pcell.p2s_map, dtype=dtype)
|
||||
self._s2p_map = np.array(self._pcell.s2p_map, dtype=dtype)
|
||||
p2p_map = self._pcell.p2p_map
|
||||
self._s2pp_map = np.array(
|
||||
[p2p_map[self._s2p_map[i]] for i in range(len(self._s2p_map))],
|
||||
dtype=dtype)
|
||||
[p2p_map[self._s2p_map[i]] for i in range(len(self._s2p_map))], dtype=dtype
|
||||
)
|
||||
|
||||
svecs, multi = self._pcell.get_smallest_vectors()
|
||||
if self._pcell.store_dense_svecs:
|
||||
|
@ -86,9 +91,9 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
# 2. Python implementation
|
||||
self._derivative_order = None
|
||||
|
||||
def run(self, q, q_direction=None, lang='C'):
|
||||
def run(self, q, q_direction=None, lang="C"):
|
||||
"""Run at q."""
|
||||
if self._derivative_order is not None or lang != 'C':
|
||||
if self._derivative_order is not None or lang != "C":
|
||||
self._run_py(q, q_direction=q_direction)
|
||||
else:
|
||||
self._run_c(q, q_direction=q_direction)
|
||||
|
@ -116,15 +121,23 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
|
||||
def get_derivative_of_dynamical_matrix(self):
|
||||
"""Return derivative of dynamical matrix."""
|
||||
warnings.warn(
|
||||
"DerivativeOfDynamicalMatrix.get_derivative_of_dynamical_matrix() is "
|
||||
"deprecated. Use d_dynamical_matrix attribute instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.d_dynamical_matrix
|
||||
|
||||
def _run_c(self, q, q_direction=None):
|
||||
import phonopy._phonopy as phonoc
|
||||
|
||||
num_patom = len(self._p2s_map)
|
||||
|
||||
fc = self._force_constants
|
||||
ddm = np.zeros((3, num_patom * 3, num_patom * 3),
|
||||
dtype=("c%d" % (np.dtype('double').itemsize * 2)))
|
||||
ddm = np.zeros(
|
||||
(3, num_patom * 3, num_patom * 3),
|
||||
dtype=("c%d" % (np.dtype("double").itemsize * 2)),
|
||||
)
|
||||
if self._dynmat.is_nac():
|
||||
born = self._dynmat.born
|
||||
dielectric = self._dynmat.dielectric_constant
|
||||
|
@ -132,7 +145,7 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
if q_direction is None:
|
||||
q_dir = None
|
||||
else:
|
||||
q_dir = np.array(q_direction, dtype='double', order='C')
|
||||
q_dir = np.array(q_direction, dtype="double", order="C")
|
||||
else:
|
||||
born = None
|
||||
dielectric = None
|
||||
|
@ -140,11 +153,11 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
q_dir = None
|
||||
|
||||
if fc.shape[0] == fc.shape[1]: # full fc
|
||||
phonoc.derivative_dynmat(ddm.view(dtype='double'),
|
||||
phonoc.derivative_dynmat(
|
||||
ddm.view(dtype="double"),
|
||||
fc,
|
||||
np.array(q, dtype='double'),
|
||||
np.array(self._pcell.cell.T,
|
||||
dtype='double', order='C'),
|
||||
np.array(q, dtype="double"),
|
||||
np.array(self._pcell.cell.T, dtype="double", order="C"),
|
||||
self._svecs,
|
||||
self._multi,
|
||||
self._pcell.masses,
|
||||
|
@ -153,23 +166,24 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
nac_factor,
|
||||
born,
|
||||
dielectric,
|
||||
q_dir)
|
||||
q_dir,
|
||||
)
|
||||
else:
|
||||
phonoc.derivative_dynmat(ddm.view(dtype='double'),
|
||||
phonoc.derivative_dynmat(
|
||||
ddm.view(dtype="double"),
|
||||
fc,
|
||||
np.array(q, dtype='double'),
|
||||
np.array(self._pcell.cell.T,
|
||||
dtype='double', order='C'),
|
||||
np.array(q, dtype="double"),
|
||||
np.array(self._pcell.cell.T, dtype="double", order="C"),
|
||||
self._svecs,
|
||||
self._multi,
|
||||
self._pcell.masses,
|
||||
self._s2pp_map,
|
||||
np.arange(len(self._p2s_map),
|
||||
dtype='int_'),
|
||||
np.arange(len(self._p2s_map), dtype="int_"),
|
||||
nac_factor,
|
||||
born,
|
||||
dielectric,
|
||||
q_dir)
|
||||
q_dir,
|
||||
)
|
||||
|
||||
self._ddm = ddm
|
||||
|
||||
|
@ -193,16 +207,16 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
else:
|
||||
num_elem = 3
|
||||
|
||||
itemsize = np.dtype('double').itemsize
|
||||
ddm = np.zeros((num_elem, 3 * num_patom, 3 * num_patom),
|
||||
dtype=("c%d" % (itemsize * 2)))
|
||||
itemsize = np.dtype("double").itemsize
|
||||
ddm = np.zeros(
|
||||
(num_elem, 3 * num_patom, 3 * num_patom), dtype=("c%d" % (itemsize * 2))
|
||||
)
|
||||
|
||||
for i, j in list(np.ndindex(num_patom, num_patom)):
|
||||
s_i = self._p2s_map[i]
|
||||
s_j = self._p2s_map[j]
|
||||
mass = np.sqrt(self._pcell.masses[i] * self._pcell.masses[j])
|
||||
ddm_local = np.zeros((num_elem, 3, 3),
|
||||
dtype=("c%d" % (itemsize * 2)))
|
||||
ddm_local = np.zeros((num_elem, 3, 3), dtype=("c%d" % (itemsize * 2)))
|
||||
|
||||
for k in range(num_satom):
|
||||
if s_j != self._s2p_map[k]:
|
||||
|
@ -210,14 +224,16 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
|
||||
multi = multiplicity[k, i]
|
||||
vecs_multi = vecs[k, i, :multi]
|
||||
phase_multi = np.exp([np.vdot(vec, q) * 2j * np.pi
|
||||
for vec in vecs_multi])
|
||||
phase_multi = np.exp(
|
||||
[np.vdot(vec, q) * 2j * np.pi for vec in vecs_multi]
|
||||
)
|
||||
vecs_multi_cart = np.dot(vecs_multi, self._pcell.cell)
|
||||
coef_order1 = 2j * np.pi * vecs_multi_cart
|
||||
if self._derivative_order == 2:
|
||||
coef_order2 = [np.outer(co1, co1) for co1 in coef_order1]
|
||||
coef = np.array([co2.ravel()[[0, 4, 8, 5, 2, 1]]
|
||||
for co2 in coef_order2])
|
||||
coef = np.array(
|
||||
[co2.ravel()[[0, 4, 8, 5, 2, 1]] for co2 in coef_order2]
|
||||
)
|
||||
else:
|
||||
coef = coef_order1
|
||||
|
||||
|
@ -228,22 +244,22 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
|
||||
for ll in range(num_elem):
|
||||
ddm_elem = fc_elem * (coef[:, ll] * phase_multi).sum()
|
||||
if (self._dynmat.is_nac() and
|
||||
not self._derivative_order == 2): # noqa E129
|
||||
if (
|
||||
self._dynmat.is_nac() and not self._derivative_order == 2
|
||||
): # noqa E129
|
||||
ddm_elem += d_nac[ll, i, j] * phase_multi.sum()
|
||||
|
||||
ddm_local[ll] += ddm_elem / mass / multi
|
||||
|
||||
ddm[:, (i * 3):(i * 3 + 3), (j * 3):(j * 3 + 3)] = ddm_local
|
||||
ddm[:, (i * 3) : (i * 3 + 3), (j * 3) : (j * 3 + 3)] = ddm_local
|
||||
|
||||
# Impose Hermite condition
|
||||
self._ddm = np.array([(ddm[i] + ddm[i].conj().T) / 2
|
||||
for i in range(num_elem)])
|
||||
self._ddm = np.array([(ddm[i] + ddm[i].conj().T) / 2 for i in range(num_elem)])
|
||||
|
||||
def _nac(self, q_direction):
|
||||
"""nac_term = (A1 (x) A2) / B * coef."""
|
||||
num_atom = self._pcell.get_number_of_atoms()
|
||||
nac_q = np.zeros((num_atom, num_atom, 3, 3), dtype='double')
|
||||
nac_q = np.zeros((num_atom, num_atom, 3, 3), dtype="double")
|
||||
if (np.abs(q_direction) < 1e-5).all():
|
||||
return nac_q
|
||||
|
||||
|
@ -267,7 +283,7 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
|
||||
def _d_nac(self, q_direction):
|
||||
num_atom = self._pcell.get_number_of_atoms()
|
||||
d_nac_q = np.zeros((3, num_atom, num_atom, 3, 3), dtype='double')
|
||||
d_nac_q = np.zeros((3, num_atom, num_atom, 3, 3), dtype="double")
|
||||
if (np.abs(q_direction) < 1e-5).all():
|
||||
return d_nac_q
|
||||
|
||||
|
@ -287,8 +303,8 @@ class DerivativeOfDynamicalMatrix(object):
|
|||
A_j = self._A(q, Z, j)
|
||||
dA_j = self._dA(Z, j, xyz)
|
||||
d_nac_q[xyz, i, j] = (
|
||||
(np.outer(dA_i, A_j) + np.outer(A_i, dA_j)) / B -
|
||||
np.outer(A_i, A_j) * dB / B ** 2)
|
||||
np.outer(dA_i, A_j) + np.outer(A_i, dA_j)
|
||||
) / B - np.outer(A_i, A_j) * dB / B ** 2
|
||||
|
||||
num_satom = self._scell.get_number_of_atoms()
|
||||
N = num_satom // num_atom
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Routines to handle displacements in supercells."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -34,11 +35,11 @@
|
|||
|
||||
import numpy as np
|
||||
|
||||
directions_axis = np.array([[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]])
|
||||
directions_axis = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
|
||||
directions_diag = np.array([[1, 0, 0],
|
||||
directions_diag = np.array(
|
||||
[
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1],
|
||||
[1, 1, 0],
|
||||
|
@ -50,33 +51,34 @@ directions_diag = np.array([[1, 0, 0],
|
|||
[1, 1, 1],
|
||||
[1, 1, -1],
|
||||
[1, -1, 1],
|
||||
[-1, 1, 1]])
|
||||
[-1, 1, 1],
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def directions_to_displacement_dataset(displacement_directions,
|
||||
distance,
|
||||
supercell):
|
||||
def directions_to_displacement_dataset(displacement_directions, distance, supercell):
|
||||
"""Transform displacement directions to displacements in Cartesian coordinates."""
|
||||
lattice = supercell.get_cell()
|
||||
first_atoms = []
|
||||
for disp in displacement_directions:
|
||||
direction = disp[1:]
|
||||
disp_cartesian = np.dot(direction, lattice)
|
||||
disp_cartesian *= distance / np.linalg.norm(disp_cartesian)
|
||||
first_atoms.append({'number': int(disp[0]),
|
||||
'displacement': disp_cartesian.tolist()})
|
||||
first_atoms.append(
|
||||
{"number": int(disp[0]), "displacement": disp_cartesian.tolist()}
|
||||
)
|
||||
displacement_dataset = {
|
||||
'natom': supercell.get_number_of_atoms(),
|
||||
'first_atoms': first_atoms}
|
||||
"natom": supercell.get_number_of_atoms(),
|
||||
"first_atoms": first_atoms,
|
||||
}
|
||||
|
||||
return displacement_dataset
|
||||
|
||||
|
||||
def get_least_displacements(symmetry,
|
||||
is_plusminus='auto',
|
||||
is_diagonal=True,
|
||||
is_trigonal=False,
|
||||
log_level=0):
|
||||
"""Return a set of displacements
|
||||
def get_least_displacements(
|
||||
symmetry, is_plusminus="auto", is_diagonal=True, is_trigonal=False, log_level=0
|
||||
):
|
||||
"""Return a set of displacements.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
@ -110,53 +112,44 @@ def get_least_displacements(symmetry,
|
|||
for v in rot:
|
||||
print("%2d %2d %2d" % tuple(v))
|
||||
|
||||
for disp in get_displacement(site_symmetry,
|
||||
directions,
|
||||
is_trigonal,
|
||||
log_level):
|
||||
displacements.append([atom_num,
|
||||
disp[0], disp[1], disp[2]])
|
||||
if is_plusminus == 'auto':
|
||||
for disp in get_displacement(site_symmetry, directions, is_trigonal, log_level):
|
||||
displacements.append([atom_num, disp[0], disp[1], disp[2]])
|
||||
if is_plusminus == "auto":
|
||||
if is_minus_displacement(disp, site_symmetry):
|
||||
displacements.append([atom_num,
|
||||
-disp[0], -disp[1], -disp[2]])
|
||||
displacements.append([atom_num, -disp[0], -disp[1], -disp[2]])
|
||||
elif is_plusminus is True:
|
||||
displacements.append([atom_num,
|
||||
-disp[0], -disp[1], -disp[2]])
|
||||
displacements.append([atom_num, -disp[0], -disp[1], -disp[2]])
|
||||
|
||||
return displacements
|
||||
|
||||
|
||||
def get_displacement(site_symmetry,
|
||||
directions=directions_diag,
|
||||
is_trigonal=False,
|
||||
log_level=0):
|
||||
def get_displacement(
|
||||
site_symmetry, directions=directions_diag, is_trigonal=False, log_level=0
|
||||
):
|
||||
"""Return displacement directions for one atom."""
|
||||
# One
|
||||
sitesym_num, disp = get_displacement_one(site_symmetry,
|
||||
directions)
|
||||
sitesym_num, disp = _get_displacement_one(site_symmetry, directions)
|
||||
if disp is not None:
|
||||
if log_level > 2:
|
||||
print("Site symmetry used to expand a direction %s" % disp[0])
|
||||
print(site_symmetry[sitesym_num])
|
||||
return disp
|
||||
# Two
|
||||
sitesym_num, disps = get_displacement_two(site_symmetry,
|
||||
directions)
|
||||
sitesym_num, disps = _get_displacement_two(site_symmetry, directions)
|
||||
if disps is not None:
|
||||
if log_level > 2:
|
||||
print("Site symmetry used to expand directions %s %s" %
|
||||
(disps[0], disps[1]))
|
||||
print(
|
||||
"Site symmetry used to expand directions %s %s" % (disps[0], disps[1])
|
||||
)
|
||||
print(site_symmetry[sitesym_num])
|
||||
|
||||
if is_trigonal:
|
||||
disps_new = [disps[0]]
|
||||
if is_trigonal_axis(site_symmetry[sitesym_num]):
|
||||
if _is_trigonal_axis(site_symmetry[sitesym_num]):
|
||||
if log_level > 2:
|
||||
print("Trigonal axis is found.")
|
||||
disps_new.append(np.dot(disps[0],
|
||||
site_symmetry[sitesym_num].T))
|
||||
disps_new.append(np.dot(disps_new[1],
|
||||
site_symmetry[sitesym_num].T))
|
||||
disps_new.append(np.dot(disps[0], site_symmetry[sitesym_num].T))
|
||||
disps_new.append(np.dot(disps_new[1], site_symmetry[sitesym_num].T))
|
||||
disps_new.append(disps[1])
|
||||
return disps_new
|
||||
else:
|
||||
|
@ -165,25 +158,33 @@ def get_displacement(site_symmetry,
|
|||
return [directions[0], directions[1], directions[2]]
|
||||
|
||||
|
||||
def get_displacement_one(site_symmetry,
|
||||
directions=directions_diag):
|
||||
def _get_displacement_one(site_symmetry, directions=directions_diag):
|
||||
"""Return one displacement.
|
||||
|
||||
This method tries to find three linearly independent displacements by
|
||||
applying site symmetry to an input displacement.
|
||||
|
||||
"""
|
||||
for direction in directions:
|
||||
rot_directions = []
|
||||
for r in site_symmetry:
|
||||
rot_directions.append(np.dot(direction, r.T))
|
||||
num_sitesym = len(site_symmetry)
|
||||
for i in range(num_sitesym):
|
||||
for j in range(i+1, num_sitesym):
|
||||
det = determinant(direction,
|
||||
rot_directions[i],
|
||||
rot_directions[j])
|
||||
for j in range(i + 1, num_sitesym):
|
||||
det = _determinant(direction, rot_directions[i], rot_directions[j])
|
||||
if det != 0:
|
||||
return i, [direction]
|
||||
return None, None
|
||||
|
||||
|
||||
def get_displacement_two(site_symmetry,
|
||||
directions=directions_diag):
|
||||
def _get_displacement_two(site_symmetry, directions=directions_diag):
|
||||
"""Return one displacement.
|
||||
|
||||
This method tries to find three linearly independent displacements by
|
||||
applying site symmetry to two input displacements.
|
||||
|
||||
"""
|
||||
for direction in directions:
|
||||
rot_directions = []
|
||||
for r in site_symmetry:
|
||||
|
@ -191,15 +192,14 @@ def get_displacement_two(site_symmetry,
|
|||
num_sitesym = len(site_symmetry)
|
||||
for i in range(num_sitesym):
|
||||
for second_direction in directions:
|
||||
det = determinant(direction,
|
||||
rot_directions[i],
|
||||
second_direction)
|
||||
det = _determinant(direction, rot_directions[i], second_direction)
|
||||
if det != 0:
|
||||
return i, [direction, second_direction]
|
||||
return None, None
|
||||
|
||||
|
||||
def is_minus_displacement(direction, site_symmetry):
|
||||
"""Symmetrically check if minus displacement is necessary or not."""
|
||||
is_minus = True
|
||||
for r in site_symmetry:
|
||||
rot_direction = np.dot(direction, r.T)
|
||||
|
@ -211,7 +211,12 @@ def is_minus_displacement(direction, site_symmetry):
|
|||
return is_minus
|
||||
|
||||
|
||||
def is_trigonal_axis(r):
|
||||
def _is_trigonal_axis(r):
|
||||
"""Check three folded rotation.
|
||||
|
||||
True if r^3 = identity.
|
||||
|
||||
"""
|
||||
r3 = np.dot(np.dot(r, r), r)
|
||||
if (r3 == np.eye(3, dtype=int)).all():
|
||||
return True
|
||||
|
@ -219,51 +224,55 @@ def is_trigonal_axis(r):
|
|||
return False
|
||||
|
||||
|
||||
def determinant(a, b, c):
|
||||
det = (a[0] * b[1] * c[2] - a[0] * b[2] * c[1]
|
||||
+ a[1] * b[2] * c[0] - a[1] * b[0] * c[2]
|
||||
+ a[2] * b[0] * c[1] - a[2] * b[1] * c[0])
|
||||
def _determinant(a, b, c):
|
||||
"""Return determinant of 3x3 matrix of [a, b, c]."""
|
||||
det = (
|
||||
a[0] * b[1] * c[2]
|
||||
- a[0] * b[2] * c[1]
|
||||
+ a[1] * b[2] * c[0]
|
||||
- a[1] * b[0] * c[2]
|
||||
+ a[2] * b[0] * c[1]
|
||||
- a[2] * b[1] * c[0]
|
||||
)
|
||||
return det
|
||||
|
||||
|
||||
def get_random_displacements_dataset(num_supercells,
|
||||
distance,
|
||||
num_atoms,
|
||||
random_seed=None):
|
||||
if (np.issubdtype(type(random_seed), np.integer) and
|
||||
random_seed >= 0 and random_seed < 2 ** 32):
|
||||
def get_random_displacements_dataset(
|
||||
num_supercells, distance, num_atoms, random_seed=None
|
||||
):
|
||||
"""Return random displacements at constant displacement distance."""
|
||||
if (
|
||||
np.issubdtype(type(random_seed), np.integer)
|
||||
and random_seed >= 0
|
||||
and random_seed < 2 ** 32
|
||||
):
|
||||
seed = random_seed
|
||||
else:
|
||||
seed = None
|
||||
|
||||
disps = get_random_directions(num_atoms * num_supercells,
|
||||
random_seed=random_seed) * distance
|
||||
supercell_disps = np.array(disps.reshape(num_supercells, num_atoms, 3),
|
||||
dtype='double', order='C')
|
||||
dataset = {'displacements': supercell_disps}
|
||||
disps = (
|
||||
_get_random_directions(num_atoms * num_supercells, random_seed=random_seed)
|
||||
* distance
|
||||
)
|
||||
supercell_disps = np.array(
|
||||
disps.reshape(num_supercells, num_atoms, 3), dtype="double", order="C"
|
||||
)
|
||||
dataset = {"displacements": supercell_disps}
|
||||
|
||||
if seed is not None:
|
||||
dataset['random_seed'] = seed
|
||||
dataset["random_seed"] = seed
|
||||
return dataset
|
||||
|
||||
|
||||
def get_random_directions(num_atoms, random_seed=None):
|
||||
"""Returns random directions in sphere with radius 1"""
|
||||
|
||||
if (np.issubdtype(type(random_seed), np.integer) and
|
||||
random_seed >= 0 and random_seed < 2 ** 32):
|
||||
def _get_random_directions(num_atoms, random_seed=None):
|
||||
"""Return random directions in sphere with radius 1."""
|
||||
if (
|
||||
np.issubdtype(type(random_seed), np.integer)
|
||||
and random_seed >= 0
|
||||
and random_seed < 2 ** 32
|
||||
):
|
||||
np.random.seed(random_seed)
|
||||
|
||||
xy = np.random.randn(3, num_atoms)
|
||||
r = np.sqrt((xy ** 2).sum(axis=0))
|
||||
return (xy / r).T
|
||||
|
||||
|
||||
def print_displacements(symmetry,
|
||||
directions=directions_diag):
|
||||
displacements = get_least_displacements(symmetry, directions)
|
||||
print("Least displacements:")
|
||||
print("Atom Directions")
|
||||
print("----------------------------")
|
||||
for key in displacements:
|
||||
print("%4d %s" % (key + 1, displacements[key]))
|
||||
|
|
|
@ -36,58 +36,16 @@
|
|||
|
||||
import sys
|
||||
import warnings
|
||||
from phonopy.structure.cells import sparse_to_dense_svecs
|
||||
from phonopy.harmonic.dynmat_to_fc import DynmatToForceConstants
|
||||
from typing import Type, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def get_dynamical_matrix(fc2,
|
||||
supercell,
|
||||
primitive,
|
||||
nac_params=None,
|
||||
frequency_scale_factor=None,
|
||||
decimals=None,
|
||||
symprec=1e-5,
|
||||
log_level=0):
|
||||
"""Return dynamical matrix.
|
||||
|
||||
The instance of a class inherited from DynamicalMatrix will be returned
|
||||
depending on paramters.
|
||||
|
||||
"""
|
||||
if frequency_scale_factor is None:
|
||||
_fc2 = fc2
|
||||
else:
|
||||
_fc2 = fc2 * frequency_scale_factor ** 2
|
||||
|
||||
if nac_params is None:
|
||||
dm = DynamicalMatrix(
|
||||
supercell,
|
||||
primitive,
|
||||
_fc2,
|
||||
decimals=decimals)
|
||||
else:
|
||||
if 'method' not in nac_params:
|
||||
method = 'gonze'
|
||||
else:
|
||||
method = nac_params['method']
|
||||
|
||||
if method == 'wang':
|
||||
DM_cls = DynamicalMatrixWang
|
||||
else:
|
||||
DM_cls = DynamicalMatrixGL
|
||||
dm = DM_cls(
|
||||
supercell,
|
||||
primitive,
|
||||
_fc2,
|
||||
decimals=decimals,
|
||||
symprec=symprec,
|
||||
log_level=log_level)
|
||||
dm.nac_params = nac_params
|
||||
return dm
|
||||
from phonopy.harmonic.dynmat_to_fc import DynmatToForceConstants
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.structure.cells import Primitive, sparse_to_dense_svecs
|
||||
|
||||
|
||||
class DynamicalMatrix(object):
|
||||
class DynamicalMatrix:
|
||||
"""Dynamical matrix base class.
|
||||
|
||||
When prmitive and supercell lattices are L_p and L_s, respectively,
|
||||
|
@ -108,8 +66,8 @@ class DynamicalMatrix(object):
|
|||
primitive: Primitive
|
||||
Primitive cell instance. Note that Primitive is inherited from
|
||||
PhonopyAtoms.
|
||||
supercell: Supercell
|
||||
Supercell instance. Note that Supercell is inherited from PhonopyAtoms.
|
||||
supercell: PhonopyAtoms.
|
||||
Supercell instance.
|
||||
force_constants: ndarray
|
||||
Supercell force constants. Full and compact shapes of arrays are
|
||||
supported.
|
||||
|
@ -126,16 +84,18 @@ class DynamicalMatrix(object):
|
|||
# Non analytical term correction
|
||||
_nac = False
|
||||
|
||||
def __init__(self,
|
||||
supercell,
|
||||
primitive,
|
||||
def __init__(
|
||||
self,
|
||||
supercell: PhonopyAtoms,
|
||||
primitive: Primitive,
|
||||
force_constants,
|
||||
decimals=None):
|
||||
decimals=None,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
supercell : Supercell
|
||||
supercell : PhonopyAtoms.
|
||||
Supercell.
|
||||
primitive : Primitive
|
||||
Primitive cell.
|
||||
|
@ -156,14 +116,14 @@ class DynamicalMatrix(object):
|
|||
self._force_constants = None
|
||||
self._set_force_constants(force_constants)
|
||||
|
||||
self._dtype_complex = ("c%d" % (np.dtype('double').itemsize * 2))
|
||||
self._dtype_complex = "c%d" % (np.dtype("double").itemsize * 2)
|
||||
|
||||
self._p2s_map = np.array(self._pcell.p2s_map, dtype='int_')
|
||||
self._s2p_map = np.array(self._pcell.s2p_map, dtype='int_')
|
||||
self._p2s_map = np.array(self._pcell.p2s_map, dtype="int_")
|
||||
self._s2p_map = np.array(self._pcell.s2p_map, dtype="int_")
|
||||
p2p_map = self._pcell.p2p_map
|
||||
self._s2pp_map = np.array(
|
||||
[p2p_map[self._s2p_map[i]] for i in range(len(self._s2p_map))],
|
||||
dtype='int_')
|
||||
[p2p_map[self._s2p_map[i]] for i in range(len(self._s2p_map))], dtype="int_"
|
||||
)
|
||||
svecs, multi = self._pcell.get_smallest_vectors()
|
||||
if self._pcell.store_dense_svecs:
|
||||
self._svecs = svecs
|
||||
|
@ -177,8 +137,9 @@ class DynamicalMatrix(object):
|
|||
|
||||
def get_dimension(self):
|
||||
"""Return number of bands."""
|
||||
warnings.warn("DynamicalMatrix.get_dimension() is deprecated.",
|
||||
DeprecationWarning)
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.get_dimension() is deprecated.", DeprecationWarning
|
||||
)
|
||||
return len(self._pcell) * 3
|
||||
|
||||
@property
|
||||
|
@ -188,9 +149,11 @@ class DynamicalMatrix(object):
|
|||
|
||||
def get_decimals(self):
|
||||
"""Return number of decimals of dynamical matrix values."""
|
||||
warnings.warn("DynamicalMatrix.get_decimals() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.get_decimals() is deprecated."
|
||||
"Use DynamicalMatrix.decimals attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.decimals
|
||||
|
||||
@property
|
||||
|
@ -200,21 +163,25 @@ class DynamicalMatrix(object):
|
|||
|
||||
def get_supercell(self):
|
||||
"""Return supercell."""
|
||||
warnings.warn("DynamicalMatrix.get_supercell() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.get_supercell() is deprecated."
|
||||
"Use DynamicalMatrix.supercell attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.supercell
|
||||
|
||||
@property
|
||||
def primitive(self):
|
||||
def primitive(self) -> Primitive:
|
||||
"""Return primitive cell."""
|
||||
return self._pcell
|
||||
|
||||
def get_primitive(self):
|
||||
"""Return primitive cell."""
|
||||
warnings.warn("DynamicalMatrix.get_primitive() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.get_primitive() is deprecated."
|
||||
"Use DynamicalMatrix.primitive attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.primitive
|
||||
|
||||
@property
|
||||
|
@ -224,9 +191,11 @@ class DynamicalMatrix(object):
|
|||
|
||||
def get_force_constants(self):
|
||||
"""Return supercell force constants."""
|
||||
warnings.warn("DynamicalMatrix.get_force_constants() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.get_force_constants() is deprecated."
|
||||
"Use DynamicalMatrix.force_constants attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.force_constants
|
||||
|
||||
@property
|
||||
|
@ -252,13 +221,15 @@ class DynamicalMatrix(object):
|
|||
|
||||
def get_dynamical_matrix(self):
|
||||
"""Return dynamcial matrix calculated at q."""
|
||||
warnings.warn("DynamicalMatrix.get_get_dynamical_matrix() is "
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.get_get_dynamical_matrix() is "
|
||||
"deprecated."
|
||||
"Use DynamicalMatrix.get_dynamical_matrix attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.dynamical_matrix
|
||||
|
||||
def run(self, q, lang='C'):
|
||||
def run(self, q, lang="C"):
|
||||
"""Run dynamical matrix calculation at a q-point.
|
||||
|
||||
q : array_like
|
||||
|
@ -270,27 +241,30 @@ class DynamicalMatrix(object):
|
|||
|
||||
def set_dynamical_matrix(self, q):
|
||||
"""Run dynamical matrix calculation at a q-point."""
|
||||
warnings.warn("DynamicalMatrix.set_dynamical_matrix() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrix.set_dynamical_matrix() is deprecated."
|
||||
"Use DynamicalMatrix.run().",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
self.run(q)
|
||||
|
||||
def _run(self, q, lang='C'):
|
||||
if lang == 'C':
|
||||
import phonopy._phonopy as phonoc # noqa F401
|
||||
def _run(self, q, lang="C"):
|
||||
if lang == "C":
|
||||
self._run_c_dynamical_matrix(q)
|
||||
else:
|
||||
self._run_py_dynamical_matrix(q)
|
||||
|
||||
def _set_force_constants(self, fc):
|
||||
if (type(fc) is np.ndarray and
|
||||
fc.dtype is np.double and
|
||||
fc.flags.aligned and
|
||||
fc.flags.owndata and
|
||||
fc.flags.c_contiguous): # noqa E129
|
||||
if (
|
||||
type(fc) is np.ndarray
|
||||
and fc.dtype is np.double
|
||||
and fc.flags.aligned
|
||||
and fc.flags.owndata
|
||||
and fc.flags.c_contiguous
|
||||
): # noqa E129
|
||||
self._force_constants = fc
|
||||
else:
|
||||
self._force_constants = np.array(fc, dtype='double', order='C')
|
||||
self._force_constants = np.array(fc, dtype="double", order="C")
|
||||
|
||||
def _run_c_dynamical_matrix(self, q):
|
||||
import phonopy._phonopy as phonoc
|
||||
|
@ -298,24 +272,25 @@ class DynamicalMatrix(object):
|
|||
fc = self._force_constants
|
||||
mass = self._pcell.masses
|
||||
size_prim = len(mass)
|
||||
dm = np.zeros((size_prim * 3, size_prim * 3),
|
||||
dtype=self._dtype_complex)
|
||||
dm = np.zeros((size_prim * 3, size_prim * 3), dtype=self._dtype_complex)
|
||||
|
||||
if fc.shape[0] == fc.shape[1]: # full-fc
|
||||
s2p_map = self._s2p_map
|
||||
p2s_map = self._p2s_map
|
||||
else: # compact-fc
|
||||
s2p_map = self._s2pp_map
|
||||
p2s_map = np.arange(len(self._p2s_map), dtype='int_')
|
||||
p2s_map = np.arange(len(self._p2s_map), dtype="int_")
|
||||
|
||||
phonoc.dynamical_matrix(dm.view(dtype='double'),
|
||||
phonoc.dynamical_matrix(
|
||||
dm.view(dtype="double"),
|
||||
fc,
|
||||
np.array(q, dtype='double'),
|
||||
np.array(q, dtype="double"),
|
||||
self._svecs,
|
||||
self._multi,
|
||||
mass,
|
||||
s2p_map,
|
||||
p2s_map)
|
||||
p2s_map,
|
||||
)
|
||||
|
||||
# Data of dm array are stored in memory by the C order of
|
||||
# (size_prim * 3, size_prim * 3, 2), where the last 2 means
|
||||
|
@ -357,7 +332,7 @@ class DynamicalMatrix(object):
|
|||
for k in range(len(self._scell)):
|
||||
if s_j == self._s2p_map[k]:
|
||||
m, adrs = multi[k][i]
|
||||
svecs_at = svecs[adrs:adrs + m]
|
||||
svecs_at = svecs[adrs : adrs + m]
|
||||
phase = []
|
||||
for ll in range(m):
|
||||
vec = svecs_at[ll]
|
||||
|
@ -365,7 +340,7 @@ class DynamicalMatrix(object):
|
|||
phase_factor = np.exp(phase).sum()
|
||||
dm_local += fc_elem[k] * phase_factor / sqrt_mm / m
|
||||
|
||||
dm[(i*3):(i*3+3), (j*3):(j*3+3)] += dm_local
|
||||
dm[(i * 3) : (i * 3 + 3), (j * 3) : (j * 3 + 3)] += dm_local
|
||||
|
||||
# Impose Hermisian condition
|
||||
self._dynamical_matrix = (dm + dm.conj().transpose()) / 2
|
||||
|
@ -376,18 +351,20 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
|
||||
_nac = True
|
||||
|
||||
def __init__(self,
|
||||
supercell,
|
||||
primitive,
|
||||
def __init__(
|
||||
self,
|
||||
supercell: PhonopyAtoms,
|
||||
primitive: Primitive,
|
||||
force_constants,
|
||||
symprec=1e-5,
|
||||
decimals=None,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
supercell : Supercell
|
||||
supercell : PhonopyAtoms
|
||||
Supercell.
|
||||
primitive : Primitive
|
||||
Primitive cell.
|
||||
|
@ -405,11 +382,7 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
Log level.
|
||||
|
||||
"""
|
||||
super(DynamicalMatrixNAC, self).__init__(
|
||||
supercell,
|
||||
primitive,
|
||||
force_constants,
|
||||
decimals=decimals)
|
||||
super().__init__(supercell, primitive, force_constants, decimals=decimals)
|
||||
self._symprec = symprec
|
||||
self._log_level = log_level
|
||||
|
||||
|
@ -448,7 +421,8 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
warnings.warn(
|
||||
"DynamicalMatrixNAC.get_born_effective_charges() is deprecated."
|
||||
"Use DynamicalMatrixNAC.born attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.born
|
||||
|
||||
@property
|
||||
|
@ -458,9 +432,11 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
|
||||
def get_nac_factor(self):
|
||||
"""Return NAC unit conversion factor."""
|
||||
warnings.warn("DynamicalMatrixNAC.get_nac_factor() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrixNAC.get_nac_factor() is deprecated."
|
||||
"Use DynamicalMatrixNAC.nac_factor attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.nac_factor
|
||||
|
||||
@property
|
||||
|
@ -473,7 +449,8 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
warnings.warn(
|
||||
"DynamicalMatrixNAC.get_dielectric_constant() is deprecated."
|
||||
"Use DynamicalMatrixNAC.dielectric_constant attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.dielectric_constant
|
||||
|
||||
@property
|
||||
|
@ -483,17 +460,17 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
|
||||
def get_nac_method(self):
|
||||
"""Return NAC method name."""
|
||||
warnings.warn("DynamicalMatrixNAC.get_nac_method() is deprecated."
|
||||
warnings.warn(
|
||||
"DynamicalMatrixNAC.get_nac_method() is deprecated."
|
||||
"Use DynamicalMatrixNAC.nac_method attribute.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.nac_method
|
||||
|
||||
@property
|
||||
def nac_params(self):
|
||||
"""Return NAC basic parameters."""
|
||||
return {'born': self.born,
|
||||
'factor': self.factor,
|
||||
'dielectric': self.dielectric}
|
||||
return {"born": self.born, "factor": self.factor, "dielectric": self.dielectric}
|
||||
|
||||
@nac_params.setter
|
||||
def nac_params(self, nac_params):
|
||||
|
@ -505,7 +482,8 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
warnings.warn(
|
||||
"DynamicalMatrixNAC.set_nac_params() is deprecated."
|
||||
"Use DynamicalMatrixNAC.nac_params attribute instead.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
self.nac_params = nac_params
|
||||
|
||||
@property
|
||||
|
@ -523,21 +501,21 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
|
||||
def _set_basic_nac_params(self, nac_params):
|
||||
"""Set basic NAC parameters."""
|
||||
self._born = np.array(nac_params['born'], dtype='double', order='C')
|
||||
self._unit_conversion = nac_params['factor']
|
||||
self._dielectric = np.array(nac_params['dielectric'],
|
||||
dtype='double', order='C')
|
||||
self._born = np.array(nac_params["born"], dtype="double", order="C")
|
||||
self._unit_conversion = nac_params["factor"]
|
||||
self._dielectric = np.array(nac_params["dielectric"], dtype="double", order="C")
|
||||
|
||||
def set_dynamical_matrix(self, q, q_direction=None):
|
||||
"""Run dynamical matrix calculation at q-point."""
|
||||
warnings.warn(
|
||||
"DynamicalMatrixNAC.set_dynamical_matrix() is deprecated."
|
||||
"Use DynamicalMatrixNAC.run().",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
self.run(q, q_direction=q_direction)
|
||||
|
||||
def _get_charge_sum(self, num_atom, q, born):
|
||||
nac_q = np.zeros((num_atom, num_atom, 3, 3), dtype='double', order='C')
|
||||
nac_q = np.zeros((num_atom, num_atom, 3, 3), dtype="double", order="C")
|
||||
A = np.dot(q, born)
|
||||
for i in range(num_atom):
|
||||
for j in range(num_atom):
|
||||
|
@ -545,8 +523,9 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
return nac_q
|
||||
|
||||
def _get_constant_factor(self, q, dielectric, volume, unit_conversion):
|
||||
return (unit_conversion * 4.0 * np.pi / volume /
|
||||
np.dot(q.T, np.dot(dielectric, q)))
|
||||
return (
|
||||
unit_conversion * 4.0 * np.pi / volume / np.dot(q.T, np.dot(dielectric, q))
|
||||
)
|
||||
|
||||
def _compute_dynamical_matrix(self, q_red, q_direction):
|
||||
raise NotImplementedError()
|
||||
|
@ -555,17 +534,19 @@ class DynamicalMatrixNAC(DynamicalMatrix):
|
|||
class DynamicalMatrixGL(DynamicalMatrixNAC):
|
||||
"""Non analytical term correction (NAC) by Gonze and Lee."""
|
||||
|
||||
_method = 'gonze'
|
||||
_method = "gonze"
|
||||
|
||||
def __init__(self,
|
||||
supercell,
|
||||
primitive,
|
||||
def __init__(
|
||||
self,
|
||||
supercell: PhonopyAtoms,
|
||||
primitive: Primitive,
|
||||
force_constants,
|
||||
nac_params=None,
|
||||
num_G_points=None, # For Gonze NAC
|
||||
decimals=None,
|
||||
symprec=1e-5,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
|
@ -588,13 +569,14 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
Log level.
|
||||
|
||||
"""
|
||||
super(DynamicalMatrixGL, self).__init__(
|
||||
super().__init__(
|
||||
supercell,
|
||||
primitive,
|
||||
force_constants,
|
||||
symprec=symprec,
|
||||
decimals=decimals,
|
||||
log_level=log_level)
|
||||
log_level=log_level,
|
||||
)
|
||||
|
||||
# For the method by Gonze et al.
|
||||
self._Gonze_force_constants = None
|
||||
|
@ -613,18 +595,21 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
@property
|
||||
def Gonze_nac_dataset(self):
|
||||
"""Return Gonze-Lee NAC dataset."""
|
||||
return (self._Gonze_force_constants,
|
||||
return (
|
||||
self._Gonze_force_constants,
|
||||
self._dd_q0,
|
||||
self._G_cutoff,
|
||||
self._G_list,
|
||||
self._Lambda)
|
||||
self._Lambda,
|
||||
)
|
||||
|
||||
def get_Gonze_nac_dataset(self):
|
||||
"""Return Gonze-Lee NAC dataset."""
|
||||
warnings.warn(
|
||||
"DynamicalMatrixGL.get_Gonze_nac_dataset() is deprecated."
|
||||
"Use DynamicalMatrixGL.Gonze_nac_dataset attribute instead.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.Gonze_nac_dataset
|
||||
|
||||
def _set_nac_params(self, nac_params):
|
||||
|
@ -634,18 +619,19 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
|
||||
"""
|
||||
self._set_basic_nac_params(nac_params)
|
||||
if 'G_cutoff' in nac_params:
|
||||
self._G_cutoff = nac_params['G_cutoff']
|
||||
if "G_cutoff" in nac_params:
|
||||
self._G_cutoff = nac_params["G_cutoff"]
|
||||
else:
|
||||
self._G_cutoff = (3 * self._num_G_points / (4 * np.pi) /
|
||||
self._pcell.volume) ** (1.0 / 3)
|
||||
self._G_cutoff = (
|
||||
3 * self._num_G_points / (4 * np.pi) / self._pcell.volume
|
||||
) ** (1.0 / 3)
|
||||
self._G_list = self._get_G_list(self._G_cutoff)
|
||||
if 'Lambda' in nac_params:
|
||||
self._Lambda = nac_params['Lambda']
|
||||
if "Lambda" in nac_params:
|
||||
self._Lambda = nac_params["Lambda"]
|
||||
else:
|
||||
exp_cutoff = 1e-10
|
||||
GeG = self._G_cutoff ** 2 * np.trace(self._dielectric) / 3
|
||||
self._Lambda = np.sqrt(- GeG / 4 / np.log(exp_cutoff))
|
||||
self._Lambda = np.sqrt(-GeG / 4 / np.log(exp_cutoff))
|
||||
|
||||
# self._H = self._get_H()
|
||||
|
||||
|
@ -658,16 +644,19 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
"""
|
||||
try:
|
||||
import phonopy._phonopy as phonoc # noqa F401
|
||||
|
||||
self._run_c_recip_dipole_dipole_q0()
|
||||
except ImportError:
|
||||
print("Python version of dipole-dipole calculation is not well "
|
||||
"implemented.")
|
||||
print(
|
||||
"Python version of dipole-dipole calculation is not well "
|
||||
"implemented."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
fc_shape = self._force_constants.shape
|
||||
d2f = DynmatToForceConstants(self._pcell,
|
||||
self._scell,
|
||||
is_full_fc=(fc_shape[0] == fc_shape[1]))
|
||||
d2f = DynmatToForceConstants(
|
||||
self._pcell, self._scell, is_full_fc=(fc_shape[0] == fc_shape[1])
|
||||
)
|
||||
dynmat = []
|
||||
num_q = len(d2f.commensurate_points)
|
||||
for i, q_red in enumerate(d2f.commensurate_points):
|
||||
|
@ -685,19 +674,22 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
|
||||
def show_nac_message(self):
|
||||
"""Show message on Gonze-Lee NAC method."""
|
||||
print("Use NAC by Gonze et al. (no real space sum in current "
|
||||
"implementation)")
|
||||
print(
|
||||
"Use NAC by Gonze et al. (no real space sum in current " "implementation)"
|
||||
)
|
||||
print(" PRB 50, 13035(R) (1994), PRB 55, 10355 (1997)")
|
||||
print(" G-cutoff distance: %4.2f, Number of G-points: %d, "
|
||||
"Lambda: %4.2f"
|
||||
% (self._G_cutoff, len(self._G_list), self._Lambda))
|
||||
print(
|
||||
" G-cutoff distance: %4.2f, Number of G-points: %d, "
|
||||
"Lambda: %4.2f" % (self._G_cutoff, len(self._G_list), self._Lambda)
|
||||
)
|
||||
|
||||
def show_Gonze_nac_message(self):
|
||||
"""Show message on Gonze-Lee NAC method."""
|
||||
warnings.warn(
|
||||
"DynamicalMatrixGL.show_Gonze_nac_message() is deprecated."
|
||||
"Use DynamicalMatrixGL.show_nac_message instead.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
self.show_nac_message()
|
||||
|
||||
def _compute_dynamical_matrix(self, q_red, q_direction):
|
||||
|
@ -716,19 +708,21 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
|
||||
def _get_Gonze_dipole_dipole(self, q_red, q_direction):
|
||||
rec_lat = np.linalg.inv(self._pcell.cell) # column vectors
|
||||
q_cart = np.array(np.dot(q_red, rec_lat.T), dtype='double')
|
||||
q_cart = np.array(np.dot(q_red, rec_lat.T), dtype="double")
|
||||
if q_direction is None:
|
||||
q_dir_cart = None
|
||||
else:
|
||||
q_dir_cart = np.array(np.dot(q_direction, rec_lat.T),
|
||||
dtype='double')
|
||||
q_dir_cart = np.array(np.dot(q_direction, rec_lat.T), dtype="double")
|
||||
|
||||
try:
|
||||
import phonopy._phonopy as phonoc # noqa F401
|
||||
|
||||
C_recip = self._get_c_recip_dipole_dipole(q_cart, q_dir_cart)
|
||||
except ImportError:
|
||||
print("Python version of dipole-dipole calculation is not well "
|
||||
"implemented.")
|
||||
print(
|
||||
"Python version of dipole-dipole calculation is not well "
|
||||
"implemented."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Mass weighted
|
||||
|
@ -757,21 +751,21 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
pos = self._pcell.positions
|
||||
num_atom = len(pos)
|
||||
volume = self._pcell.volume
|
||||
dd = np.zeros((num_atom, 3, num_atom, 3),
|
||||
dtype=self._dtype_complex, order='C')
|
||||
dd = np.zeros((num_atom, 3, num_atom, 3), dtype=self._dtype_complex, order="C")
|
||||
|
||||
phonoc.recip_dipole_dipole(
|
||||
dd.view(dtype='double'),
|
||||
self._dd_q0.view(dtype='double'),
|
||||
dd.view(dtype="double"),
|
||||
self._dd_q0.view(dtype="double"),
|
||||
self._G_list,
|
||||
q_cart,
|
||||
q_dir_cart,
|
||||
self._born,
|
||||
self._dielectric,
|
||||
np.array(pos, dtype='double', order='C'),
|
||||
np.array(pos, dtype="double", order="C"),
|
||||
self._unit_conversion * 4.0 * np.pi / volume,
|
||||
self._Lambda,
|
||||
self._symprec)
|
||||
self._symprec,
|
||||
)
|
||||
return dd
|
||||
|
||||
def _run_c_recip_dipole_dipole_q0(self):
|
||||
|
@ -782,18 +776,18 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
"""
|
||||
import phonopy._phonopy as phonoc
|
||||
|
||||
pos = self._pcell.get_positions()
|
||||
self._dd_q0 = np.zeros((len(pos), 3, 3),
|
||||
dtype=self._dtype_complex, order='C')
|
||||
pos = self._pcell.positions
|
||||
self._dd_q0 = np.zeros((len(pos), 3, 3), dtype=self._dtype_complex, order="C")
|
||||
|
||||
phonoc.recip_dipole_dipole_q0(
|
||||
self._dd_q0.view(dtype='double'),
|
||||
self._dd_q0.view(dtype="double"),
|
||||
self._G_list,
|
||||
self._born,
|
||||
self._dielectric,
|
||||
np.array(pos, dtype='double', order='C'),
|
||||
np.array(pos, dtype="double", order="C"),
|
||||
self._Lambda,
|
||||
self._symprec)
|
||||
self._symprec,
|
||||
)
|
||||
|
||||
# Limiting contribution
|
||||
# inv_eps = np.linalg.inv(self._dielectric)
|
||||
|
@ -806,8 +800,7 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
pos = self._pcell.positions
|
||||
num_atom = len(self._pcell)
|
||||
volume = self._pcell.volume
|
||||
C = np.zeros((num_atom, 3, num_atom, 3),
|
||||
dtype=self._dtype_complex, order='C')
|
||||
C = np.zeros((num_atom, 3, num_atom, 3), dtype=self._dtype_complex, order="C")
|
||||
|
||||
for q_K in K_list:
|
||||
if np.linalg.norm(q_K) < self._symprec:
|
||||
|
@ -818,13 +811,13 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
else:
|
||||
dq_K = q_K
|
||||
|
||||
Z_mat = (self._get_charge_sum(num_atom, dq_K, self._born) *
|
||||
self._get_constant_factor(dq_K,
|
||||
self._dielectric,
|
||||
volume,
|
||||
self._unit_conversion))
|
||||
Z_mat = self._get_charge_sum(
|
||||
num_atom, dq_K, self._born
|
||||
) * self._get_constant_factor(
|
||||
dq_K, self._dielectric, volume, self._unit_conversion
|
||||
)
|
||||
for i in range(num_atom):
|
||||
dpos = - pos + pos[i]
|
||||
dpos = -pos + pos[i]
|
||||
phase_factor = np.exp(2j * np.pi * np.dot(dpos, q_K))
|
||||
for j in range(num_atom):
|
||||
C[i, :, j, :] += Z_mat[i, j] * phase_factor[j]
|
||||
|
@ -833,14 +826,14 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
q_G = q_K - q
|
||||
if np.linalg.norm(q_G) < self._symprec:
|
||||
continue
|
||||
Z_mat = (self._get_charge_sum(num_atom, q_G, self._born) *
|
||||
self._get_constant_factor(q_G,
|
||||
self._dielectric,
|
||||
volume,
|
||||
self._unit_conversion))
|
||||
Z_mat = self._get_charge_sum(
|
||||
num_atom, q_G, self._born
|
||||
) * self._get_constant_factor(
|
||||
q_G, self._dielectric, volume, self._unit_conversion
|
||||
)
|
||||
for i in range(num_atom):
|
||||
C_i = np.zeros((3, 3), dtype=self._dtype_complex, order='C')
|
||||
dpos = - pos + pos[i]
|
||||
C_i = np.zeros((3, 3), dtype=self._dtype_complex, order="C")
|
||||
dpos = -pos + pos[i]
|
||||
phase_factor = np.exp(2j * np.pi * np.dot(dpos, q_G))
|
||||
for j in range(num_atom):
|
||||
C_i += Z_mat[i, j] * phase_factor[j]
|
||||
|
@ -853,8 +846,7 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
# g_rad must be greater than 0 for broadcasting.
|
||||
G_vec_list = self._get_G_vec_list(g_rad, rec_lat)
|
||||
G_norm2 = ((G_vec_list) ** 2).sum(axis=1)
|
||||
return np.array(G_vec_list[G_norm2 < G_cutoff ** 2],
|
||||
dtype='double', order='C')
|
||||
return np.array(G_vec_list[G_norm2 < G_cutoff ** 2], dtype="double", order="C")
|
||||
|
||||
def _get_G_vec_list(self, g_rad, rec_lat):
|
||||
pts = np.arange(-g_rad, g_rad + 1)
|
||||
|
@ -877,22 +869,23 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
|
||||
try:
|
||||
from scipy.special import erfc
|
||||
|
||||
erfc_y = erfc(y)
|
||||
except ImportError:
|
||||
from math import erfc
|
||||
|
||||
erfc_y = np.zeros_like(y)
|
||||
for i in np.ndindex(y.shape):
|
||||
erfc_y[i] = erfc(y[i])
|
||||
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
A = (3 * erfc_y / y3 + 2 / np.sqrt(np.pi)
|
||||
* exp_y2 * (3 / y2 + 2)) / y2
|
||||
with np.errstate(divide="ignore", invalid="ignore"):
|
||||
A = (3 * erfc_y / y3 + 2 / np.sqrt(np.pi) * exp_y2 * (3 / y2 + 2)) / y2
|
||||
A[A == np.inf] = 0
|
||||
A = np.nan_to_num(A)
|
||||
B = erfc_y / y3 + 2 / np.sqrt(np.pi) * exp_y2 / y2
|
||||
B[B == np.inf] = 0
|
||||
B = np.nan_to_num(B)
|
||||
H = np.zeros((3, 3) + y.shape, dtype='double', order='C')
|
||||
H = np.zeros((3, 3) + y.shape, dtype="double", order="C")
|
||||
for i, j in np.ndindex((3, 3)):
|
||||
H[i, j] = x[:, :, :, i] * x[:, :, :, j] * A - eps_inv[i, j] * B
|
||||
return H
|
||||
|
@ -901,21 +894,23 @@ class DynamicalMatrixGL(DynamicalMatrixNAC):
|
|||
class DynamicalMatrixWang(DynamicalMatrixNAC):
|
||||
"""Non analytical term correction (NAC) by Wang et al."""
|
||||
|
||||
_method = 'wang'
|
||||
_method = "wang"
|
||||
|
||||
def __init__(self,
|
||||
supercell,
|
||||
primitive,
|
||||
def __init__(
|
||||
self,
|
||||
supercell: PhonopyAtoms,
|
||||
primitive: Primitive,
|
||||
force_constants,
|
||||
nac_params=None,
|
||||
decimals=None,
|
||||
symprec=1e-5,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
supercell : Supercell
|
||||
supercell : PhonopyAtoms
|
||||
Supercell.
|
||||
primitive : Primitive
|
||||
Primitive cell.
|
||||
|
@ -933,13 +928,14 @@ class DynamicalMatrixWang(DynamicalMatrixNAC):
|
|||
Log level.
|
||||
|
||||
"""
|
||||
super(DynamicalMatrixWang, self).__init__(
|
||||
super().__init__(
|
||||
supercell,
|
||||
primitive,
|
||||
force_constants,
|
||||
symprec=symprec,
|
||||
decimals=decimals,
|
||||
log_level=log_level)
|
||||
log_level=log_level,
|
||||
)
|
||||
|
||||
self._symprec = symprec
|
||||
if nac_params is not None:
|
||||
|
@ -948,8 +944,7 @@ class DynamicalMatrixWang(DynamicalMatrixNAC):
|
|||
def show_nac_message(self):
|
||||
"""Show Wang et al.'s paper reference."""
|
||||
if self._log_level:
|
||||
print("NAC by Wang et al., J. Phys. Condens. Matter 22, "
|
||||
"202201 (2010)")
|
||||
print("NAC by Wang et al., J. Phys. Condens. Matter 22, " "202201 (2010)")
|
||||
|
||||
def _set_nac_params(self, nac_params):
|
||||
"""Set NAC parameters.
|
||||
|
@ -967,12 +962,12 @@ class DynamicalMatrixWang(DynamicalMatrixNAC):
|
|||
else:
|
||||
q = np.dot(q_direction, rec_lat.T)
|
||||
|
||||
constant = self._get_constant_factor(q,
|
||||
self._dielectric,
|
||||
self._pcell.volume,
|
||||
self._unit_conversion)
|
||||
constant = self._get_constant_factor(
|
||||
q, self._dielectric, self._pcell.volume, self._unit_conversion
|
||||
)
|
||||
try:
|
||||
import phonopy._phonopy as phonoc # noqa F401
|
||||
|
||||
self._run_c_Wang_dynamical_matrix(q_red, q, constant)
|
||||
except ImportError:
|
||||
num_atom = len(self._pcell)
|
||||
|
@ -988,39 +983,41 @@ class DynamicalMatrixWang(DynamicalMatrixNAC):
|
|||
fc = self._force_constants
|
||||
mass = self._pcell.masses
|
||||
size_prim = len(mass)
|
||||
dm = np.zeros((size_prim * 3, size_prim * 3),
|
||||
dtype=self._dtype_complex)
|
||||
dm = np.zeros((size_prim * 3, size_prim * 3), dtype=self._dtype_complex)
|
||||
|
||||
if fc.shape[0] == fc.shape[1]: # full fc
|
||||
phonoc.nac_dynamical_matrix(dm.view(dtype='double'),
|
||||
phonoc.nac_dynamical_matrix(
|
||||
dm.view(dtype="double"),
|
||||
fc,
|
||||
np.array(q_red, dtype='double'),
|
||||
np.array(q_red, dtype="double"),
|
||||
self._svecs,
|
||||
self._multi,
|
||||
mass,
|
||||
self._s2p_map,
|
||||
self._p2s_map,
|
||||
np.array(q, dtype='double'),
|
||||
np.array(q, dtype="double"),
|
||||
self._born,
|
||||
factor)
|
||||
factor,
|
||||
)
|
||||
else:
|
||||
phonoc.nac_dynamical_matrix(dm.view(dtype='double'),
|
||||
phonoc.nac_dynamical_matrix(
|
||||
dm.view(dtype="double"),
|
||||
fc,
|
||||
np.array(q_red, dtype='double'),
|
||||
np.array(q_red, dtype="double"),
|
||||
self._svecs,
|
||||
self._multi,
|
||||
mass,
|
||||
self._s2pp_map,
|
||||
np.arange(len(self._p2s_map),
|
||||
dtype='int_'),
|
||||
np.array(q, dtype='double'),
|
||||
np.arange(len(self._p2s_map), dtype="int_"),
|
||||
np.array(q, dtype="double"),
|
||||
self._born,
|
||||
factor)
|
||||
factor,
|
||||
)
|
||||
|
||||
self._dynamical_matrix = dm
|
||||
|
||||
def _run_py_Wang_force_constants(self, fc, nac_q):
|
||||
N = (len(self._scell) // len(self._pcell))
|
||||
N = len(self._scell) // len(self._pcell)
|
||||
for s1 in range(len(self._scell)):
|
||||
# This if-statement is the trick.
|
||||
# In contructing dynamical matrix in phonopy
|
||||
|
@ -1032,3 +1029,49 @@ class DynamicalMatrixWang(DynamicalMatrixNAC):
|
|||
for s2 in range(len(self._scell)):
|
||||
p2 = self._s2pp_map[s2]
|
||||
fc[s1, s2] += nac_q[p1, p2] / N
|
||||
|
||||
|
||||
def get_dynamical_matrix(
|
||||
fc2,
|
||||
supercell: PhonopyAtoms,
|
||||
primitive: Primitive,
|
||||
nac_params=None,
|
||||
frequency_scale_factor=None,
|
||||
decimals=None,
|
||||
symprec=1e-5,
|
||||
log_level=0,
|
||||
):
|
||||
"""Return dynamical matrix.
|
||||
|
||||
The instance of a class inherited from DynamicalMatrix will be returned
|
||||
depending on paramters.
|
||||
|
||||
"""
|
||||
if frequency_scale_factor is None:
|
||||
_fc2 = fc2
|
||||
else:
|
||||
_fc2 = fc2 * frequency_scale_factor ** 2
|
||||
|
||||
if nac_params is None:
|
||||
dm = DynamicalMatrix(supercell, primitive, _fc2, decimals=decimals)
|
||||
else:
|
||||
if "method" not in nac_params:
|
||||
method = "gonze"
|
||||
else:
|
||||
method = nac_params["method"]
|
||||
|
||||
DM_cls: Union[Type[DynamicalMatrixGL], Type[DynamicalMatrixWang]]
|
||||
if method == "wang":
|
||||
DM_cls = DynamicalMatrixWang
|
||||
else:
|
||||
DM_cls = DynamicalMatrixGL
|
||||
dm = DM_cls(
|
||||
supercell,
|
||||
primitive,
|
||||
_fc2,
|
||||
decimals=decimals,
|
||||
symprec=symprec,
|
||||
log_level=log_level,
|
||||
)
|
||||
dm.nac_params = nac_params
|
||||
return dm
|
||||
|
|
|
@ -34,14 +34,18 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.harmonic.force_constants import distribute_force_constants_by_translations
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.structure.cells import (
|
||||
get_supercell, get_primitive, shape_supercell_matrix,
|
||||
sparse_to_dense_svecs)
|
||||
get_primitive,
|
||||
get_supercell,
|
||||
shape_supercell_matrix,
|
||||
sparse_to_dense_svecs,
|
||||
)
|
||||
from phonopy.structure.snf import SNF3x3
|
||||
from phonopy.harmonic.force_constants import (
|
||||
distribute_force_constants_by_translations)
|
||||
|
||||
|
||||
def get_commensurate_points(supercell_matrix): # wrt primitive cell
|
||||
|
@ -62,14 +66,14 @@ def get_commensurate_points(supercell_matrix): # wrt primitive cell
|
|||
|
||||
"""
|
||||
smat = np.array(supercell_matrix, dtype=int)
|
||||
rec_primitive = PhonopyAtoms(numbers=[1],
|
||||
scaled_positions=[[0, 0, 0]],
|
||||
cell=np.diag([1, 1, 1]),
|
||||
pbc=True)
|
||||
rec_primitive = PhonopyAtoms(
|
||||
numbers=[1], scaled_positions=[[0, 0, 0]], cell=np.diag([1, 1, 1]), pbc=True
|
||||
)
|
||||
rec_supercell = get_supercell(rec_primitive, smat.T)
|
||||
q_pos = rec_supercell.scaled_positions
|
||||
return np.array(np.where(q_pos > 1 - 1e-15, q_pos - 1, q_pos),
|
||||
dtype='double', order='C')
|
||||
return np.array(
|
||||
np.where(q_pos > 1 - 1e-15, q_pos - 1, q_pos), dtype="double", order="C"
|
||||
)
|
||||
|
||||
|
||||
def get_commensurate_points_in_integers(supercell_matrix):
|
||||
|
@ -99,11 +103,13 @@ def get_commensurate_points_in_integers(supercell_matrix):
|
|||
snf.run()
|
||||
D = snf.D.diagonal()
|
||||
b, c, a = np.meshgrid(range(D[1]), range(D[2]), range(D[0]))
|
||||
lattice_points = np.dot(np.c_[a.ravel() * D[1] * D[2],
|
||||
b.ravel() * D[0] * D[2],
|
||||
c.ravel() * D[0] * D[1]], snf.Q.T)
|
||||
lattice_points = np.array(lattice_points % np.prod(D),
|
||||
dtype='intc', order='C')
|
||||
lattice_points = np.dot(
|
||||
np.c_[
|
||||
a.ravel() * D[1] * D[2], b.ravel() * D[0] * D[2], c.ravel() * D[0] * D[1]
|
||||
],
|
||||
snf.Q.T,
|
||||
)
|
||||
lattice_points = np.array(lattice_points % np.prod(D), dtype="intc", order="C")
|
||||
return lattice_points
|
||||
|
||||
|
||||
|
@ -147,17 +153,17 @@ def ph2fc(ph_orig, supercell_matrix):
|
|||
pcell = get_primitive(
|
||||
scell,
|
||||
np.dot(np.linalg.inv(smat), ph_orig.primitive_matrix),
|
||||
positions_to_reorder=ph_orig.primitive.scaled_positions)
|
||||
positions_to_reorder=ph_orig.primitive.scaled_positions,
|
||||
)
|
||||
d2f = DynmatToForceConstants(pcell, scell)
|
||||
ph_orig.run_qpoints(d2f.commensurate_points,
|
||||
with_dynamical_matrices=True)
|
||||
ph_orig.run_qpoints(d2f.commensurate_points, with_dynamical_matrices=True)
|
||||
ph_dict = ph_orig.get_qpoints_dict()
|
||||
d2f.dynamical_matrices = ph_dict['dynamical_matrices']
|
||||
d2f.dynamical_matrices = ph_dict["dynamical_matrices"]
|
||||
d2f.run()
|
||||
return d2f.force_constants
|
||||
|
||||
|
||||
class DynmatToForceConstants(object):
|
||||
class DynmatToForceConstants:
|
||||
"""Transforms eigensolutions to force constants.
|
||||
|
||||
This is the inverse transform of force constants to eigensolutions.
|
||||
|
@ -211,14 +217,16 @@ class DynmatToForceConstants(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
def __init__(
|
||||
self,
|
||||
primitive,
|
||||
supercell,
|
||||
eigenvalues=None,
|
||||
eigenvectors=None,
|
||||
dynamical_matrices=None,
|
||||
commensurate_points=None,
|
||||
is_full_fc=True):
|
||||
is_full_fc=True,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
|
@ -237,10 +245,9 @@ class DynmatToForceConstants(object):
|
|||
self._pcell = primitive
|
||||
self._scell = supercell
|
||||
supercell_matrix = np.linalg.inv(self._pcell.primitive_matrix)
|
||||
supercell_matrix = np.rint(supercell_matrix).astype('intc')
|
||||
supercell_matrix = np.rint(supercell_matrix).astype("intc")
|
||||
if commensurate_points is None:
|
||||
self._commensurate_points = get_commensurate_points(
|
||||
supercell_matrix)
|
||||
self._commensurate_points = get_commensurate_points(supercell_matrix)
|
||||
else:
|
||||
self._commensurate_points = commensurate_points
|
||||
|
||||
|
@ -261,30 +268,34 @@ class DynmatToForceConstants(object):
|
|||
else:
|
||||
self._fc_shape = (n_p, n_s, 3, 3)
|
||||
|
||||
self._dtype_complex = ("c%d" % (np.dtype('double').itemsize * 2))
|
||||
self._dtype_complex = "c%d" % (np.dtype("double").itemsize * 2)
|
||||
|
||||
if dynamical_matrices is not None or commensurate_points is not None:
|
||||
warnings.warn("Instanciation init parameters of dynamical_matrices"
|
||||
warnings.warn(
|
||||
"Instanciation init parameters of dynamical_matrices"
|
||||
" and commensurate_points are deprecated. Use "
|
||||
"respecitve attributes.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if eigenvalues is not None or eigenvectors is not None:
|
||||
warnings.warn("Instanciation init parameters of eigenvalues and "
|
||||
warnings.warn(
|
||||
"Instanciation init parameters of eigenvalues and "
|
||||
"eigenvectors are deprecated. Use "
|
||||
"create_dynamical_matrices method.",
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if eigenvalues is not None and eigenvectors is not None:
|
||||
self.create_dynamical_matrices(
|
||||
eigenvalues=eigenvalues,
|
||||
eigenvectors=eigenvectors)
|
||||
eigenvalues=eigenvalues, eigenvectors=eigenvectors
|
||||
)
|
||||
elif dynamical_matrices is not None:
|
||||
self.dynamical_matrices = dynamical_matrices
|
||||
|
||||
def run(self, lang='C'):
|
||||
def run(self, lang="C"):
|
||||
"""Run."""
|
||||
self._fc = np.zeros(self._fc_shape, dtype='double', order='C')
|
||||
self._fc = np.zeros(self._fc_shape, dtype="double", order="C")
|
||||
self._inverse_transformation(lang=lang)
|
||||
|
||||
@property
|
||||
|
@ -315,13 +326,11 @@ class DynmatToForceConstants(object):
|
|||
|
||||
@commensurate_points.setter
|
||||
def commensurate_points(self, comm_points):
|
||||
self._commensurate_points = np.array(
|
||||
comm_points, dtype='double', order='C')
|
||||
self._commensurate_points = np.array(comm_points, dtype="double", order="C")
|
||||
|
||||
def get_commensurate_points(self):
|
||||
"""Commensurate points in supercell with respect to primitive cell."""
|
||||
warnings.warn("Use attribute, commensurate_points.",
|
||||
DeprecationWarning)
|
||||
warnings.warn("Use attribute, commensurate_points.", DeprecationWarning)
|
||||
return self.commensurate_points
|
||||
|
||||
@property
|
||||
|
@ -340,7 +349,7 @@ class DynmatToForceConstants(object):
|
|||
|
||||
@dynamical_matrices.setter
|
||||
def dynamical_matrices(self, dynmat):
|
||||
self._dynmat = np.array(dynmat, dtype=self._dtype_complex, order='C')
|
||||
self._dynmat = np.array(dynmat, dtype=self._dtype_complex, order="C")
|
||||
|
||||
def get_dynamical_matrices(self):
|
||||
"""Return numerical matrices of dynamical matrices."""
|
||||
|
@ -373,42 +382,42 @@ class DynmatToForceConstants(object):
|
|||
"""
|
||||
dm = []
|
||||
for eigvals, eigvecs in zip(eigenvalues, eigenvectors):
|
||||
dm.append(np.dot(np.dot(eigvecs, np.diag(eigvals)),
|
||||
eigvecs.T.conj()))
|
||||
dm.append(np.dot(np.dot(eigvecs, np.diag(eigvals)), eigvecs.T.conj()))
|
||||
self.dynamical_matrices = dm
|
||||
|
||||
def _inverse_transformation(self, lang='C'):
|
||||
if lang == 'C':
|
||||
import phonopy._phonopy as phonoc
|
||||
def _inverse_transformation(self, lang="C"):
|
||||
if lang == "C":
|
||||
self._c_inverse_transformation()
|
||||
else:
|
||||
self._py_inverse_transformation()
|
||||
|
||||
if self._fc.shape[0] == self._fc.shape[1]:
|
||||
distribute_force_constants_by_translations(self._fc,
|
||||
self._pcell,
|
||||
self._scell)
|
||||
distribute_force_constants_by_translations(
|
||||
self._fc, self._pcell, self._scell
|
||||
)
|
||||
|
||||
def _c_inverse_transformation(self):
|
||||
import phonopy._phonopy as phonoc
|
||||
|
||||
s2p = np.array(self._pcell.s2p_map, dtype='int_')
|
||||
s2p = np.array(self._pcell.s2p_map, dtype="int_")
|
||||
p2p = self._pcell.p2p_map
|
||||
s2pp = np.array([p2p[i] for i in s2p], dtype='int_')
|
||||
s2pp = np.array([p2p[i] for i in s2p], dtype="int_")
|
||||
|
||||
if self._fc.shape[0] == self._fc.shape[1]:
|
||||
fc_index_map = np.array(self._pcell.p2s_map, dtype='int_')
|
||||
fc_index_map = np.array(self._pcell.p2s_map, dtype="int_")
|
||||
else:
|
||||
fc_index_map = np.arange(self._fc.shape[0], dtype='int_')
|
||||
fc_index_map = np.arange(self._fc.shape[0], dtype="int_")
|
||||
|
||||
phonoc.transform_dynmat_to_fc(self._fc,
|
||||
self._dynmat.view(dtype='double'),
|
||||
phonoc.transform_dynmat_to_fc(
|
||||
self._fc,
|
||||
self._dynmat.view(dtype="double"),
|
||||
self._commensurate_points,
|
||||
self._svecs,
|
||||
self._multi,
|
||||
self._pcell.masses,
|
||||
s2pp,
|
||||
fc_index_map)
|
||||
fc_index_map,
|
||||
)
|
||||
|
||||
def _py_inverse_transformation(self):
|
||||
s2p = self._pcell.s2p_map
|
||||
|
@ -429,12 +438,13 @@ class DynmatToForceConstants(object):
|
|||
|
||||
def _sum_q(self, p_i, s_j, p_j):
|
||||
multi, adrs = self._multi[s_j, p_i]
|
||||
pos = self._svecs[adrs:(adrs + multi)]
|
||||
sum_q = np.zeros((3, 3), dtype=self._dtype_complex, order='C')
|
||||
pos = self._svecs[adrs : (adrs + multi)]
|
||||
sum_q = np.zeros((3, 3), dtype=self._dtype_complex, order="C")
|
||||
phases = -2j * np.pi * np.dot(self._commensurate_points, pos.T)
|
||||
phase_factors = np.exp(phases).sum(axis=1) / multi
|
||||
for i, coef in enumerate(phase_factors):
|
||||
sum_q += self._dynmat[i,
|
||||
(p_i * 3):(p_i * 3 + 3),
|
||||
(p_j * 3):(p_j * 3 + 3)] * coef
|
||||
sum_q += (
|
||||
self._dynmat[i, (p_i * 3) : (p_i * 3 + 3), (p_j * 3) : (p_j * 3 + 3)]
|
||||
* coef
|
||||
)
|
||||
return sum_q.real
|
||||
|
|
|
@ -34,34 +34,34 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import textwrap
|
||||
|
||||
import numpy as np
|
||||
from phonopy.structure.cells import (get_smallest_vectors,
|
||||
compute_permutation_for_rotation)
|
||||
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.structure.cells import (
|
||||
compute_permutation_for_rotation,
|
||||
get_smallest_vectors,
|
||||
)
|
||||
|
||||
|
||||
def get_force_constants(set_of_forces,
|
||||
symmetry,
|
||||
supercell,
|
||||
atom_list=None,
|
||||
decimals=None):
|
||||
def get_force_constants(
|
||||
set_of_forces, symmetry, supercell, atom_list=None, decimals=None
|
||||
):
|
||||
"""Calculate force constants from disp-force dataset."""
|
||||
first_atoms = [{'number': x.get_atom_number(),
|
||||
'displacement': x.get_displacement(),
|
||||
'forces': x.get_forces()} for x in set_of_forces]
|
||||
dataset = {'natom': supercell.get_number_of_atoms(),
|
||||
'first_atoms': first_atoms}
|
||||
force_constants = get_fc2(supercell,
|
||||
symmetry,
|
||||
dataset,
|
||||
atom_list=atom_list)
|
||||
first_atoms = [
|
||||
{
|
||||
"number": x.get_atom_number(),
|
||||
"displacement": x.get_displacement(),
|
||||
"forces": x.get_forces(),
|
||||
}
|
||||
for x in set_of_forces
|
||||
]
|
||||
dataset = {"natom": supercell.get_number_of_atoms(), "first_atoms": first_atoms}
|
||||
force_constants = get_fc2(supercell, symmetry, dataset, atom_list=atom_list)
|
||||
return force_constants
|
||||
|
||||
|
||||
def get_fc2(supercell,
|
||||
symmetry,
|
||||
dataset,
|
||||
atom_list=None,
|
||||
decimals=None):
|
||||
def get_fc2(supercell, symmetry, dataset, atom_list=None, decimals=None):
|
||||
"""Force constants are computed.
|
||||
|
||||
Force constants, Phi, are calculated from sets for forces, F, and
|
||||
|
@ -86,26 +86,26 @@ def get_fc2(supercell,
|
|||
else:
|
||||
fc_dim0 = len(atom_list)
|
||||
|
||||
force_constants = np.zeros((fc_dim0, len(supercell), 3, 3),
|
||||
dtype='double', order='C')
|
||||
force_constants = np.zeros(
|
||||
(fc_dim0, len(supercell), 3, 3), dtype="double", order="C"
|
||||
)
|
||||
|
||||
# Fill force_constants[ displaced_atoms, all_atoms_in_supercell ]
|
||||
atom_list_done = _get_force_constants_disps(
|
||||
force_constants,
|
||||
supercell,
|
||||
dataset,
|
||||
symmetry,
|
||||
atom_list=atom_list)
|
||||
force_constants, supercell, dataset, symmetry, atom_list=atom_list
|
||||
)
|
||||
|
||||
rotations = symmetry.get_symmetry_operations()['rotations']
|
||||
lattice = np.array(supercell.cell.T, dtype='double', order='C')
|
||||
rotations = symmetry.symmetry_operations["rotations"]
|
||||
lattice = np.array(supercell.cell.T, dtype="double", order="C")
|
||||
permutations = symmetry.atomic_permutations
|
||||
distribute_force_constants(force_constants,
|
||||
distribute_force_constants(
|
||||
force_constants,
|
||||
atom_list_done,
|
||||
lattice,
|
||||
rotations,
|
||||
permutations,
|
||||
atom_list=atom_list)
|
||||
atom_list=atom_list,
|
||||
)
|
||||
|
||||
if decimals:
|
||||
force_constants = force_constants.round(decimals=decimals)
|
||||
|
@ -115,22 +115,20 @@ def get_fc2(supercell,
|
|||
|
||||
def compact_fc_to_full_fc(phonon, compact_fc, log_level=0):
|
||||
"""Transform compact fc to full fc."""
|
||||
fc = np.zeros((compact_fc.shape[1], compact_fc.shape[1], 3, 3),
|
||||
dtype='double', order='C')
|
||||
fc = np.zeros(
|
||||
(compact_fc.shape[1], compact_fc.shape[1], 3, 3), dtype="double", order="C"
|
||||
)
|
||||
fc[phonon.primitive.p2s_map] = compact_fc
|
||||
distribute_force_constants_by_translations(
|
||||
fc, phonon.primitive, phonon.supercell)
|
||||
distribute_force_constants_by_translations(fc, phonon.primitive, phonon.supercell)
|
||||
if log_level:
|
||||
print("Force constants were expanded to full format.")
|
||||
|
||||
return fc
|
||||
|
||||
|
||||
def cutoff_force_constants(force_constants,
|
||||
supercell,
|
||||
primitive,
|
||||
cutoff_radius,
|
||||
symprec=1e-5):
|
||||
def cutoff_force_constants(
|
||||
force_constants, supercell, primitive, cutoff_radius, symprec=1e-5
|
||||
):
|
||||
"""Set zero to force constants outside of cutoff distance.
|
||||
|
||||
Note
|
||||
|
@ -146,7 +144,8 @@ def cutoff_force_constants(force_constants,
|
|||
supercell.scaled_positions,
|
||||
supercell.scaled_positions,
|
||||
symprec=symprec,
|
||||
store_dense_svecs=primitive.store_dense_svecs)
|
||||
store_dense_svecs=primitive.store_dense_svecs,
|
||||
)
|
||||
lattice = supercell.cell
|
||||
else:
|
||||
svecs, multi = primitive.get_smallest_vectors()
|
||||
|
@ -190,6 +189,7 @@ def symmetrize_force_constants(force_constants, level=1):
|
|||
"""
|
||||
try:
|
||||
import phonopy._phonopy as phonoc
|
||||
|
||||
phonoc.perm_trans_symmetrize_fc(force_constants, level)
|
||||
except ImportError:
|
||||
for i in range(level):
|
||||
|
@ -198,9 +198,7 @@ def symmetrize_force_constants(force_constants, level=1):
|
|||
set_translational_invariance(force_constants)
|
||||
|
||||
|
||||
def symmetrize_compact_force_constants(force_constants,
|
||||
primitive,
|
||||
level=1):
|
||||
def symmetrize_compact_force_constants(force_constants, primitive, level=1):
|
||||
"""Symmetry force constants by translational and permutation symmetries.
|
||||
|
||||
Parameters
|
||||
|
@ -221,47 +219,52 @@ def symmetrize_compact_force_constants(force_constants,
|
|||
p2s_map = primitive.p2s_map
|
||||
p2p_map = primitive.p2p_map
|
||||
permutations = primitive.atomic_permutations
|
||||
s2pp_map, nsym_list = get_nsym_list_and_s2pp(s2p_map,
|
||||
p2p_map,
|
||||
permutations)
|
||||
s2pp_map, nsym_list = get_nsym_list_and_s2pp(s2p_map, p2p_map, permutations)
|
||||
try:
|
||||
import phonopy._phonopy as phonoc
|
||||
phonoc.perm_trans_symmetrize_compact_fc(force_constants,
|
||||
permutations,
|
||||
s2pp_map,
|
||||
p2s_map,
|
||||
nsym_list,
|
||||
level)
|
||||
|
||||
phonoc.perm_trans_symmetrize_compact_fc(
|
||||
force_constants, permutations, s2pp_map, p2s_map, nsym_list, level
|
||||
)
|
||||
except ImportError:
|
||||
text = ("Import error at phonoc.perm_trans_symmetrize_compact_fc. "
|
||||
"Corresponding pytono code is not implemented.")
|
||||
text = (
|
||||
"Import error at phonoc.perm_trans_symmetrize_compact_fc. "
|
||||
"Corresponding pytono code is not implemented."
|
||||
)
|
||||
raise RuntimeError(text)
|
||||
|
||||
|
||||
def distribute_force_constants(force_constants,
|
||||
def distribute_force_constants(
|
||||
force_constants,
|
||||
atom_list_done,
|
||||
lattice, # column vectors
|
||||
rotations, # scaled (fractional)
|
||||
permutations,
|
||||
atom_list=None):
|
||||
atom_list=None,
|
||||
):
|
||||
"""Fill force constants elements by symmetry."""
|
||||
map_atoms, map_syms = _get_sym_mappings_from_permutations(
|
||||
permutations, atom_list_done)
|
||||
rots_cartesian = np.array([similarity_transformation(lattice, r)
|
||||
for r in rotations],
|
||||
dtype='double', order='C')
|
||||
permutations, atom_list_done
|
||||
)
|
||||
rots_cartesian = np.array(
|
||||
[similarity_transformation(lattice, r) for r in rotations],
|
||||
dtype="double",
|
||||
order="C",
|
||||
)
|
||||
if atom_list is None:
|
||||
targets = np.arange(force_constants.shape[1], dtype='intc')
|
||||
targets = np.arange(force_constants.shape[1], dtype="intc")
|
||||
else:
|
||||
targets = np.array(atom_list, dtype='intc')
|
||||
targets = np.array(atom_list, dtype="intc")
|
||||
import phonopy._phonopy as phonoc
|
||||
|
||||
phonoc.distribute_fc2(force_constants,
|
||||
phonoc.distribute_fc2(
|
||||
force_constants,
|
||||
targets,
|
||||
rots_cartesian,
|
||||
permutations,
|
||||
np.array(map_atoms, dtype='intc'),
|
||||
np.array(map_syms, dtype='intc'))
|
||||
np.array(map_atoms, dtype="intc"),
|
||||
np.array(map_syms, dtype="intc"),
|
||||
)
|
||||
|
||||
|
||||
def distribute_force_constants_by_translations(fc, primitive, supercell):
|
||||
|
@ -280,22 +283,24 @@ def distribute_force_constants_by_translations(fc, primitive, supercell):
|
|||
positions = supercell.scaled_positions
|
||||
lattice = supercell.cell.T
|
||||
diff = positions - positions[p2s[0]]
|
||||
trans = np.array(diff[np.where(s2p == p2s[0])[0]],
|
||||
dtype='double', order='C')
|
||||
rotations = np.array([np.eye(3, dtype='intc')] * len(trans),
|
||||
dtype='intc', order='C')
|
||||
trans = np.array(diff[np.where(s2p == p2s[0])[0]], dtype="double", order="C")
|
||||
rotations = np.array(
|
||||
[np.eye(3, dtype="intc")] * len(trans), dtype="intc", order="C"
|
||||
)
|
||||
permutations = primitive.atomic_permutations
|
||||
distribute_force_constants(fc, p2s, lattice, rotations, permutations)
|
||||
|
||||
|
||||
def solve_force_constants(force_constants,
|
||||
def solve_force_constants(
|
||||
force_constants,
|
||||
disp_atom_number,
|
||||
displacements,
|
||||
sets_of_forces,
|
||||
supercell,
|
||||
site_symmetry,
|
||||
symprec,
|
||||
atom_list=None):
|
||||
atom_list=None,
|
||||
):
|
||||
"""Calculate force constants elements of pairs from an atom."""
|
||||
if atom_list is None:
|
||||
fc_index = disp_atom_number
|
||||
|
@ -311,14 +316,14 @@ def solve_force_constants(force_constants,
|
|||
sets_of_forces,
|
||||
supercell,
|
||||
site_symmetry,
|
||||
symprec)
|
||||
symprec,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_positions_sent_by_rot_inv(lattice, # column vectors
|
||||
positions,
|
||||
site_symmetry,
|
||||
symprec):
|
||||
def get_positions_sent_by_rot_inv(
|
||||
lattice, positions, site_symmetry, symprec # column vectors
|
||||
):
|
||||
"""Return atom indices of positions sent by inverse site symmetries.
|
||||
|
||||
Rotated_positions[rot_map] == positions.
|
||||
|
@ -330,13 +335,12 @@ def get_positions_sent_by_rot_inv(lattice, # column vectors
|
|||
"""
|
||||
rot_map_syms = []
|
||||
for sym in site_symmetry:
|
||||
rot_map = compute_permutation_for_rotation(np.dot(positions, sym.T),
|
||||
positions,
|
||||
lattice,
|
||||
symprec)
|
||||
rot_map = compute_permutation_for_rotation(
|
||||
np.dot(positions, sym.T), positions, lattice, symprec
|
||||
)
|
||||
rot_map_syms.append(rot_map)
|
||||
|
||||
return np.array(rot_map_syms, dtype='intc', order='C')
|
||||
return np.array(rot_map_syms, dtype="intc", order="C")
|
||||
|
||||
|
||||
def get_rotated_displacement(displacements, site_sym_cart):
|
||||
|
@ -350,93 +354,77 @@ def get_rotated_displacement(displacements, site_sym_cart):
|
|||
rot_disps = []
|
||||
for u in displacements:
|
||||
rot_disps.append([np.dot(sym, u) for sym in site_sym_cart])
|
||||
return np.array(np.reshape(rot_disps, (-1, 3)), dtype='double', order='C')
|
||||
return np.array(np.reshape(rot_disps, (-1, 3)), dtype="double", order="C")
|
||||
|
||||
|
||||
def set_tensor_symmetry(force_constants,
|
||||
lattice, # column vectors
|
||||
positions,
|
||||
symmetry):
|
||||
def set_tensor_symmetry(
|
||||
force_constants, lattice, positions, symmetry # column vectors
|
||||
):
|
||||
"""Full force constants are symmetrized using crystal symmetry.
|
||||
|
||||
This method extracts symmetrically equivalent sets of atomic pairs and
|
||||
take sum of their force constants and average the sum.
|
||||
|
||||
"""
|
||||
rotations = symmetry.get_symmetry_operations()['rotations']
|
||||
translations = symmetry.get_symmetry_operations()['translations']
|
||||
rotations = symmetry.get_symmetry_operations()["rotations"]
|
||||
translations = symmetry.get_symmetry_operations()["translations"]
|
||||
map_atoms = symmetry.get_map_atoms()
|
||||
symprec = symmetry.tolerance
|
||||
cart_rot = np.array([similarity_transformation(lattice, rot)
|
||||
for rot in rotations])
|
||||
cart_rot = np.array([similarity_transformation(lattice, rot) for rot in rotations])
|
||||
|
||||
mapa = _get_atom_indices_by_symmetry(lattice,
|
||||
positions,
|
||||
rotations,
|
||||
translations,
|
||||
symprec)
|
||||
mapa = _get_atom_indices_by_symmetry(
|
||||
lattice, positions, rotations, translations, symprec
|
||||
)
|
||||
fc_new = np.zeros_like(force_constants)
|
||||
indep_atoms = symmetry.get_independent_atoms()
|
||||
|
||||
for i in indep_atoms:
|
||||
fc_combined = np.zeros(force_constants.shape[1:], dtype='double')
|
||||
fc_combined = np.zeros(force_constants.shape[1:], dtype="double")
|
||||
num_equiv_atoms = _combine_force_constants_equivalent_atoms(
|
||||
fc_combined,
|
||||
force_constants,
|
||||
i,
|
||||
cart_rot,
|
||||
map_atoms,
|
||||
mapa)
|
||||
num_sitesym = _average_force_constants_by_sitesym(fc_new,
|
||||
fc_combined,
|
||||
i,
|
||||
cart_rot,
|
||||
mapa)
|
||||
fc_combined, force_constants, i, cart_rot, map_atoms, mapa
|
||||
)
|
||||
num_sitesym = _average_force_constants_by_sitesym(
|
||||
fc_new, fc_combined, i, cart_rot, mapa
|
||||
)
|
||||
|
||||
assert num_equiv_atoms * num_sitesym == len(rotations)
|
||||
|
||||
permutations = symmetry.atomic_permutations
|
||||
distribute_force_constants(fc_new,
|
||||
indep_atoms,
|
||||
lattice,
|
||||
rotations,
|
||||
permutations)
|
||||
distribute_force_constants(fc_new, indep_atoms, lattice, rotations, permutations)
|
||||
|
||||
force_constants[:] = fc_new
|
||||
|
||||
|
||||
def set_tensor_symmetry_PJ(force_constants,
|
||||
lattice,
|
||||
positions,
|
||||
symmetry):
|
||||
def set_tensor_symmetry_PJ(force_constants, lattice, positions, symmetry):
|
||||
"""Full force constants are symmetrized using crystal symmetry.
|
||||
|
||||
This method extracts symmetrically equivalent sets of atomic pairs and
|
||||
take sum of their force constants and average the sum.
|
||||
|
||||
"""
|
||||
rotations = symmetry.get_symmetry_operations()['rotations']
|
||||
translations = symmetry.get_symmetry_operations()['translations']
|
||||
rotations = symmetry.get_symmetry_operations()["rotations"]
|
||||
translations = symmetry.get_symmetry_operations()["translations"]
|
||||
symprec = symmetry.tolerance
|
||||
|
||||
N = len(rotations)
|
||||
|
||||
mapa = _get_atom_indices_by_symmetry(lattice,
|
||||
positions,
|
||||
rotations,
|
||||
translations,
|
||||
symprec)
|
||||
cart_rot = np.array([similarity_transformation(lattice, rot).T
|
||||
for rot in rotations])
|
||||
mapa = _get_atom_indices_by_symmetry(
|
||||
lattice, positions, rotations, translations, symprec
|
||||
)
|
||||
cart_rot = np.array(
|
||||
[similarity_transformation(lattice, rot).T for rot in rotations]
|
||||
)
|
||||
cart_rot_inv = np.array([np.linalg.inv(rot) for rot in cart_rot])
|
||||
fcm = np.array([force_constants[mapa[n], :, :, :][:, mapa[n], :, :]
|
||||
for n in range(N)])
|
||||
s = np.transpose(np.array([np.dot(cart_rot[n],
|
||||
np.dot(fcm[n], cart_rot_inv[n]))
|
||||
for n in range(N)]), (0, 2, 3, 1, 4))
|
||||
force_constants[:] = np.array(np.average(s, axis=0),
|
||||
dtype='double',
|
||||
order='C')
|
||||
fcm = np.array(
|
||||
[force_constants[mapa[n], :, :, :][:, mapa[n], :, :] for n in range(N)]
|
||||
)
|
||||
s = np.transpose(
|
||||
np.array(
|
||||
[np.dot(cart_rot[n], np.dot(fcm[n], cart_rot_inv[n])) for n in range(N)]
|
||||
),
|
||||
(0, 2, 3, 1, 4),
|
||||
)
|
||||
force_constants[:] = np.array(np.average(s, axis=0), dtype="double", order="C")
|
||||
|
||||
|
||||
def set_translational_invariance(force_constants):
|
||||
|
@ -457,11 +445,9 @@ def set_translational_invariance_per_index(fc2, index=0):
|
|||
for i in range(fc2.shape[1 - index]):
|
||||
for j, k in list(np.ndindex(3, 3)):
|
||||
if index == 0:
|
||||
fc2[:, i, j, k] -= np.sum(
|
||||
fc2[:, i, j, k]) / fc2.shape[0]
|
||||
fc2[:, i, j, k] -= np.sum(fc2[:, i, j, k]) / fc2.shape[0]
|
||||
else:
|
||||
fc2[i, :, j, k] -= np.sum(
|
||||
fc2[i, :, j, k]) / fc2.shape[1]
|
||||
fc2[i, :, j, k] -= np.sum(fc2[i, :, j, k]) / fc2.shape[1]
|
||||
|
||||
|
||||
def set_permutation_symmetry(force_constants):
|
||||
|
@ -482,8 +468,7 @@ def set_permutation_symmetry(force_constants):
|
|||
fc_copy = force_constants.copy()
|
||||
for i in range(force_constants.shape[0]):
|
||||
for j in range(force_constants.shape[1]):
|
||||
force_constants[i, j] = (force_constants[i, j] +
|
||||
fc_copy[j, i].T) / 2
|
||||
force_constants[i, j] = (force_constants[i, j] + fc_copy[j, i].T) / 2
|
||||
|
||||
|
||||
def similarity_transformation(rot, mat):
|
||||
|
@ -491,10 +476,9 @@ def similarity_transformation(rot, mat):
|
|||
return np.dot(rot, np.dot(mat, np.linalg.inv(rot)))
|
||||
|
||||
|
||||
def show_drift_force_constants(force_constants,
|
||||
primitive=None,
|
||||
name="force constants",
|
||||
values_only=False):
|
||||
def show_drift_force_constants(
|
||||
force_constants, primitive=None, name="force constants", values_only=False
|
||||
):
|
||||
"""Show force constants drift."""
|
||||
if force_constants.shape[0] == force_constants.shape[1]:
|
||||
num_atom = force_constants.shape[0]
|
||||
|
@ -516,42 +500,43 @@ def show_drift_force_constants(force_constants,
|
|||
p2s_map = primitive.p2s_map
|
||||
p2p_map = primitive.p2p_map
|
||||
permutations = primitive.atomic_permutations
|
||||
s2pp_map, nsym_list = get_nsym_list_and_s2pp(s2p_map,
|
||||
p2p_map,
|
||||
permutations)
|
||||
s2pp_map, nsym_list = get_nsym_list_and_s2pp(s2p_map, p2p_map, permutations)
|
||||
|
||||
try:
|
||||
import phonopy._phonopy as phonoc
|
||||
phonoc.transpose_compact_fc(force_constants,
|
||||
permutations,
|
||||
s2pp_map,
|
||||
p2s_map,
|
||||
nsym_list)
|
||||
|
||||
phonoc.transpose_compact_fc(
|
||||
force_constants, permutations, s2pp_map, p2s_map, nsym_list
|
||||
)
|
||||
maxval1, jk1 = _get_drift_per_index(force_constants)
|
||||
phonoc.transpose_compact_fc(force_constants,
|
||||
permutations,
|
||||
s2pp_map,
|
||||
p2s_map,
|
||||
nsym_list)
|
||||
phonoc.transpose_compact_fc(
|
||||
force_constants, permutations, s2pp_map, p2s_map, nsym_list
|
||||
)
|
||||
maxval2, jk2 = _get_drift_per_index(force_constants)
|
||||
|
||||
except ImportError:
|
||||
text = ("Import error at phonoc.tranpose_compact_fc. "
|
||||
"Corresponding python code is not implemented.")
|
||||
text = (
|
||||
"Import error at phonoc.tranpose_compact_fc. "
|
||||
"Corresponding python code is not implemented."
|
||||
)
|
||||
raise RuntimeError(text)
|
||||
|
||||
if values_only:
|
||||
text = ""
|
||||
else:
|
||||
text = "Max drift of %s: " % name
|
||||
text += "%f (%s%s) %f (%s%s)" % (maxval1, "xyz"[jk1[0]], "xyz"[jk1[1]],
|
||||
maxval2, "xyz"[jk2[0]], "xyz"[jk2[1]])
|
||||
text += "%f (%s%s) %f (%s%s)" % (
|
||||
maxval1,
|
||||
"xyz"[jk1[0]],
|
||||
"xyz"[jk1[1]],
|
||||
maxval2,
|
||||
"xyz"[jk2[0]],
|
||||
"xyz"[jk2[1]],
|
||||
)
|
||||
print(text)
|
||||
|
||||
|
||||
def get_nsym_list_and_s2pp(s2p_map,
|
||||
p2p_map,
|
||||
permutations):
|
||||
def get_nsym_list_and_s2pp(s2p_map, p2p_map, permutations):
|
||||
"""Find lattice points corresponding to atoms in s2p_map.
|
||||
|
||||
Parameters
|
||||
|
@ -578,9 +563,14 @@ def get_nsym_list_and_s2pp(s2p_map,
|
|||
This method is public because of being used by phono3py.
|
||||
|
||||
"""
|
||||
s2pp = np.array([p2p_map[i] for i in s2p_map], dtype='intc')
|
||||
nsym_list = np.array([np.where(permutations[:, i] == target)[0][0]
|
||||
for i, target in enumerate(s2p_map)], dtype='intc')
|
||||
s2pp = np.array([p2p_map[i] for i in s2p_map], dtype="intc")
|
||||
nsym_list = np.array(
|
||||
[
|
||||
np.where(permutations[:, i] == target)[0][0]
|
||||
for i, target in enumerate(s2p_map)
|
||||
],
|
||||
dtype="intc",
|
||||
)
|
||||
return s2pp, nsym_list
|
||||
|
||||
|
||||
|
@ -645,44 +635,41 @@ def _get_drift_per_index(force_constants):
|
|||
return maxval, jk
|
||||
|
||||
|
||||
def _solve_force_constants_svd(disp_atom_number,
|
||||
def _solve_force_constants_svd(
|
||||
disp_atom_number,
|
||||
displacements,
|
||||
sets_of_forces,
|
||||
supercell,
|
||||
supercell: PhonopyAtoms,
|
||||
site_symmetry,
|
||||
symprec):
|
||||
lattice = supercell.get_cell().T
|
||||
positions = supercell.get_scaled_positions()
|
||||
symprec,
|
||||
):
|
||||
lattice = supercell.cell.T
|
||||
positions = supercell.scaled_positions
|
||||
pos_center = positions[disp_atom_number]
|
||||
positions -= pos_center
|
||||
rot_map_syms = get_positions_sent_by_rot_inv(lattice,
|
||||
positions,
|
||||
site_symmetry,
|
||||
symprec)
|
||||
site_sym_cart = [similarity_transformation(lattice, sym)
|
||||
for sym in site_symmetry]
|
||||
rot_map_syms = get_positions_sent_by_rot_inv(
|
||||
lattice, positions, site_symmetry, symprec
|
||||
)
|
||||
site_sym_cart = [similarity_transformation(lattice, sym) for sym in site_symmetry]
|
||||
rot_disps = get_rotated_displacement(displacements, site_sym_cart)
|
||||
inv_displacements = np.linalg.pinv(rot_disps)
|
||||
|
||||
fc = np.zeros((supercell.get_number_of_atoms(), 3, 3),
|
||||
dtype='double', order='C')
|
||||
for i in range(supercell.get_number_of_atoms()):
|
||||
fc = np.zeros((len(supercell), 3, 3), dtype="double", order="C")
|
||||
for i in range(len(supercell)):
|
||||
combined_forces = []
|
||||
for forces in sets_of_forces:
|
||||
combined_forces.append(
|
||||
_get_rotated_forces(forces[rot_map_syms[:, i]],
|
||||
site_sym_cart))
|
||||
_get_rotated_forces(forces[rot_map_syms[:, i]], site_sym_cart)
|
||||
)
|
||||
|
||||
combined_forces = np.reshape(combined_forces, (-1, 3))
|
||||
fc[i] = -np.dot(inv_displacements, combined_forces)
|
||||
return fc
|
||||
|
||||
|
||||
def _get_force_constants_disps(force_constants,
|
||||
supercell,
|
||||
dataset,
|
||||
symmetry,
|
||||
atom_list=None):
|
||||
def _get_force_constants_disps(
|
||||
force_constants, supercell, dataset, symmetry, atom_list=None
|
||||
):
|
||||
"""Calculate force constants Phi = -F / d.
|
||||
|
||||
Force constants are obtained by one of the following algorithm.
|
||||
|
@ -693,7 +680,7 @@ def _get_force_constants_disps(force_constants,
|
|||
Force constants
|
||||
shape=(len(atom_list),n_satom,3,3)
|
||||
dtype=double
|
||||
supercell: Supercell
|
||||
supercell: PhonopyAtoms
|
||||
Supercell
|
||||
dataset: dict
|
||||
Distplacement dataset. Forces are also stored.
|
||||
|
@ -705,37 +692,36 @@ def _get_force_constants_disps(force_constants,
|
|||
|
||||
"""
|
||||
symprec = symmetry.tolerance
|
||||
disp_atom_list = np.unique([x['number'] for x in dataset['first_atoms']])
|
||||
disp_atom_list = np.unique([x["number"] for x in dataset["first_atoms"]])
|
||||
for disp_atom_number in disp_atom_list:
|
||||
disps = []
|
||||
sets_of_forces = []
|
||||
|
||||
for x in dataset['first_atoms']:
|
||||
if x['number'] != disp_atom_number:
|
||||
for x in dataset["first_atoms"]:
|
||||
if x["number"] != disp_atom_number:
|
||||
continue
|
||||
disps.append(x['displacement'])
|
||||
sets_of_forces.append(x['forces'])
|
||||
disps.append(x["displacement"])
|
||||
sets_of_forces.append(x["forces"])
|
||||
|
||||
site_symmetry = symmetry.get_site_symmetry(disp_atom_number)
|
||||
|
||||
solve_force_constants(force_constants,
|
||||
solve_force_constants(
|
||||
force_constants,
|
||||
disp_atom_number,
|
||||
disps,
|
||||
sets_of_forces,
|
||||
supercell,
|
||||
site_symmetry,
|
||||
symprec,
|
||||
atom_list=atom_list)
|
||||
atom_list=atom_list,
|
||||
)
|
||||
|
||||
return disp_atom_list
|
||||
|
||||
|
||||
def _combine_force_constants_equivalent_atoms(fc_combined,
|
||||
force_constants,
|
||||
i,
|
||||
cart_rot,
|
||||
map_atoms,
|
||||
mapa):
|
||||
def _combine_force_constants_equivalent_atoms(
|
||||
fc_combined, force_constants, i, cart_rot, map_atoms, mapa
|
||||
):
|
||||
num_equiv_atoms = 0
|
||||
for j, k in enumerate(map_atoms):
|
||||
if k != i:
|
||||
|
@ -745,18 +731,15 @@ def _combine_force_constants_equivalent_atoms(fc_combined,
|
|||
r_i = (mapa[:, j] == i).nonzero()[0][0]
|
||||
for k, l in enumerate(mapa[r_i]):
|
||||
fc_combined[l] += similarity_transformation(
|
||||
cart_rot[r_i], force_constants[j, k])
|
||||
cart_rot[r_i], force_constants[j, k]
|
||||
)
|
||||
|
||||
fc_combined /= num_equiv_atoms
|
||||
|
||||
return num_equiv_atoms
|
||||
|
||||
|
||||
def _average_force_constants_by_sitesym(fc_new,
|
||||
fc_i,
|
||||
i,
|
||||
cart_rot,
|
||||
mapa):
|
||||
def _average_force_constants_by_sitesym(fc_new, fc_i, i, cart_rot, mapa):
|
||||
num_sitesym = 0
|
||||
for r_i, mapa_r in enumerate(mapa):
|
||||
if mapa_r[i] != i:
|
||||
|
@ -764,18 +747,15 @@ def _average_force_constants_by_sitesym(fc_new,
|
|||
num_sitesym += 1
|
||||
for j in range(fc_i.shape[0]):
|
||||
fc_new[i, j] += similarity_transformation(
|
||||
cart_rot[r_i].T, fc_i[mapa[r_i, j]])
|
||||
cart_rot[r_i].T, fc_i[mapa[r_i, j]]
|
||||
)
|
||||
|
||||
fc_new[i] /= num_sitesym
|
||||
|
||||
return num_sitesym
|
||||
|
||||
|
||||
def _get_atom_indices_by_symmetry(lattice,
|
||||
positions,
|
||||
rotations,
|
||||
translations,
|
||||
symprec):
|
||||
def _get_atom_indices_by_symmetry(lattice, positions, rotations, translations, symprec):
|
||||
# To understand this method, understanding numpy broadcasting is mandatory.
|
||||
|
||||
K = len(positions)
|
||||
|
@ -791,9 +771,9 @@ def _get_atom_indices_by_symmetry(lattice,
|
|||
diff -= np.rint(diff)
|
||||
diff = np.dot(diff, lattice.T)
|
||||
# m[N, K(1), K(2)]
|
||||
m = (np.sqrt(np.sum(diff ** 2, axis=3)) < symprec)
|
||||
m = np.sqrt(np.sum(diff ** 2, axis=3)) < symprec
|
||||
# index_array[K(1), K(2)]
|
||||
index_array = np.tile(np.arange(K, dtype='intc'), (K, 1))
|
||||
index_array = np.tile(np.arange(K, dtype="intc"), (K, 1))
|
||||
# Understanding numpy boolean array indexing (extract True elements)
|
||||
# mapa[N, K(1)]
|
||||
mapa = np.array([index_array[mr] for mr in m])
|
||||
|
@ -803,9 +783,9 @@ def _get_atom_indices_by_symmetry(lattice,
|
|||
def _get_shortest_distance_in_PBC(pos_i, pos_j, reduced_bases):
|
||||
distances = []
|
||||
for k in (-1, 0, 1):
|
||||
for l in (-1, 0, 1):
|
||||
for ll in (-1, 0, 1):
|
||||
for m in (-1, 0, 1):
|
||||
diff = pos_j + np.array([k, l, m]) - pos_i
|
||||
diff = pos_j + np.array([k, ll, m]) - pos_i
|
||||
distances.append(np.linalg.norm(np.dot(diff, reduced_bases)))
|
||||
return np.min(distances)
|
||||
|
||||
|
@ -845,8 +825,8 @@ def _get_sym_mappings_from_permutations(permutations, atom_list_done):
|
|||
num_pos = permutations.shape[1]
|
||||
|
||||
# filled with -1
|
||||
map_atoms = np.zeros((num_pos,), dtype='intc') - 1
|
||||
map_syms = np.zeros((num_pos,), dtype='intc') - 1
|
||||
map_atoms = np.zeros((num_pos,), dtype="intc") - 1
|
||||
map_syms = np.zeros((num_pos,), dtype="intc") - 1
|
||||
|
||||
atom_list_done = set(atom_list_done)
|
||||
for atom_todo in range(num_pos):
|
||||
|
@ -856,9 +836,11 @@ def _get_sym_mappings_from_permutations(permutations, atom_list_done):
|
|||
map_syms[atom_todo] = sym_index
|
||||
break
|
||||
else:
|
||||
text = ("Input forces are not enough to calculate force constants,"
|
||||
text = (
|
||||
"Input forces are not enough to calculate force constants,"
|
||||
"or something wrong (e.g. crystal structure does not "
|
||||
"match).")
|
||||
"match)."
|
||||
)
|
||||
print(textwrap.fill(text))
|
||||
raise ValueError
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Routines for user and calculator interfaces to phonopy."""
|
||||
# Copyright (C) 2019 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Abinit calculator interface."""
|
||||
# Copyright (C) 2014 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,18 +34,23 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import collect_forces
|
||||
from phonopy.interface.vasp import (get_scaled_positions_lines, check_forces,
|
||||
get_drift_forces)
|
||||
from phonopy.units import Bohr
|
||||
from phonopy.cui.settings import fracval
|
||||
from phonopy.file_IO import collect_forces
|
||||
from phonopy.interface.vasp import (
|
||||
check_forces,
|
||||
get_drift_forces,
|
||||
get_scaled_positions_lines,
|
||||
)
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.units import Bohr
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
hook = 'cartesian forces (eV/Angstrom)'
|
||||
"""Parse forces from output files."""
|
||||
hook = "cartesian forces (eV/Angstrom)"
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
|
@ -54,9 +60,9 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
f = open(filename)
|
||||
abinit_forces = collect_forces(f, num_atoms, hook, [1, 2, 3])
|
||||
if check_forces(abinit_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(abinit_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
abinit_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(abinit_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -68,51 +74,52 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
|
||||
|
||||
def read_abinit(filename):
|
||||
"""Read crystal structure."""
|
||||
with open(filename) as f:
|
||||
abinit_in = AbinitIn(f.readlines())
|
||||
tags = abinit_in.get_variables()
|
||||
acell = tags['acell']
|
||||
rprim = tags['rprim'].T
|
||||
scalecart = tags['scalecart']
|
||||
acell = tags["acell"]
|
||||
rprim = tags["rprim"].T
|
||||
scalecart = tags["scalecart"]
|
||||
lattice = rprim * acell
|
||||
if scalecart is not None:
|
||||
for i in range(3):
|
||||
lattice[i] *= scalecart[i]
|
||||
|
||||
if tags['xcart'] is not None:
|
||||
pos_bohr = np.transpose(tags['xcart'])
|
||||
if tags["xcart"] is not None:
|
||||
pos_bohr = np.transpose(tags["xcart"])
|
||||
positions = np.dot(np.linalg.inv(lattice), pos_bohr).T
|
||||
elif tags['xangst'] is not None:
|
||||
pos_bohr = np.transpose(tags['xangst']) / Bohr
|
||||
elif tags["xangst"] is not None:
|
||||
pos_bohr = np.transpose(tags["xangst"]) / Bohr
|
||||
positions = np.dot(np.linalg.inv(lattice), pos_bohr).T
|
||||
elif tags['xred'] is not None:
|
||||
positions = tags['xred']
|
||||
elif tags["xred"] is not None:
|
||||
positions = tags["xred"]
|
||||
|
||||
numbers = [tags['znucl'][x - 1] for x in tags['typat']]
|
||||
numbers = [tags["znucl"][x - 1] for x in tags["typat"]]
|
||||
|
||||
return Atoms(numbers=numbers,
|
||||
cell=lattice.T,
|
||||
scaled_positions=positions)
|
||||
return Atoms(numbers=numbers, cell=lattice.T, scaled_positions=positions)
|
||||
|
||||
|
||||
def write_abinit(filename, cell):
|
||||
with open(filename, 'w') as f:
|
||||
"""Write cell to file."""
|
||||
with open(filename, "w") as f:
|
||||
f.write(get_abinit_structure(cell))
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
def write_supercells_with_displacements(
|
||||
supercell, cells_with_displacements, ids, pre_filename="supercell", width=3
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_abinit("%s.in" % pre_filename, supercell)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}.in".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_abinit(filename, cell)
|
||||
|
||||
|
||||
def get_abinit_structure(cell):
|
||||
"""Return abinit structure in text."""
|
||||
znucl = []
|
||||
numbers = cell.get_atomic_numbers()
|
||||
for n in numbers:
|
||||
|
@ -137,40 +144,48 @@ def get_abinit_structure(cell):
|
|||
return lines
|
||||
|
||||
|
||||
class AbinitIn(object):
|
||||
class AbinitIn:
|
||||
"""Class to create Abinit input file."""
|
||||
|
||||
def __init__(self, lines):
|
||||
self._set_methods = {'acell': self._set_acell,
|
||||
'natom': self._set_natom,
|
||||
'ntypat': self._set_ntypat,
|
||||
'rprim': self._set_rprim,
|
||||
'typat': self._set_typat,
|
||||
'scalecart': self._set_scalecart,
|
||||
'xangst': self._set_xangst,
|
||||
'xcart': self._set_xcart,
|
||||
'xred': self._set_xred,
|
||||
'znucl': self._set_znucl}
|
||||
self._tags = {'acell': None,
|
||||
'natom': None,
|
||||
'ntypat': None,
|
||||
'rprim': None,
|
||||
'typat': None,
|
||||
'scalecart': None,
|
||||
'xangst': None,
|
||||
'xcart': None,
|
||||
'xred': None,
|
||||
'znucl': None}
|
||||
"""Init method."""
|
||||
self._set_methods = {
|
||||
"acell": self._set_acell,
|
||||
"natom": self._set_natom,
|
||||
"ntypat": self._set_ntypat,
|
||||
"rprim": self._set_rprim,
|
||||
"typat": self._set_typat,
|
||||
"scalecart": self._set_scalecart,
|
||||
"xangst": self._set_xangst,
|
||||
"xcart": self._set_xcart,
|
||||
"xred": self._set_xred,
|
||||
"znucl": self._set_znucl,
|
||||
}
|
||||
self._tags = {
|
||||
"acell": None,
|
||||
"natom": None,
|
||||
"ntypat": None,
|
||||
"rprim": None,
|
||||
"typat": None,
|
||||
"scalecart": None,
|
||||
"xangst": None,
|
||||
"xcart": None,
|
||||
"xred": None,
|
||||
"znucl": None,
|
||||
}
|
||||
|
||||
self._values = None
|
||||
self._collect(lines)
|
||||
|
||||
def get_variables(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self, lines):
|
||||
elements = {}
|
||||
tag = None
|
||||
for line_tmp in lines:
|
||||
line = line_tmp.replace('!', '#').split('#')[0]
|
||||
line = line_tmp.replace("!", "#").split("#")[0]
|
||||
for val in [x.lower() for x in line.split()]:
|
||||
if val in self._set_methods:
|
||||
tag = val
|
||||
|
@ -178,32 +193,32 @@ class AbinitIn(object):
|
|||
elif tag is not None:
|
||||
elements[tag].append(val)
|
||||
|
||||
for tag in ['natom', 'ntypat']:
|
||||
for tag in ["natom", "ntypat"]:
|
||||
if tag not in elements:
|
||||
print("%s is not found in the input file." % tag)
|
||||
sys.exit(1)
|
||||
|
||||
for tag in elements:
|
||||
self._values = elements[tag]
|
||||
if tag == 'natom' or tag == 'ntypat':
|
||||
if tag == "natom" or tag == "ntypat":
|
||||
self._set_methods[tag]()
|
||||
|
||||
for tag in elements:
|
||||
self._values = elements[tag]
|
||||
if tag != 'natom' and tag != 'ntypat':
|
||||
if tag != "natom" and tag != "ntypat":
|
||||
self._set_methods[tag]()
|
||||
|
||||
def _get_numerical_values(self, char_string, num_type='float'):
|
||||
def _get_numerical_values(self, char_string, num_type="float"):
|
||||
m = 1
|
||||
|
||||
if '*' in char_string:
|
||||
m = int(char_string.split('*')[0])
|
||||
str_val = char_string.split('*')[1]
|
||||
if "*" in char_string:
|
||||
m = int(char_string.split("*")[0])
|
||||
str_val = char_string.split("*")[1]
|
||||
else:
|
||||
m = 1
|
||||
str_val = char_string
|
||||
|
||||
if num_type == 'float':
|
||||
if num_type == "float":
|
||||
a = fracval(str_val)
|
||||
else:
|
||||
a = int(str_val)
|
||||
|
@ -215,20 +230,20 @@ class AbinitIn(object):
|
|||
for val in self._values:
|
||||
if len(acell) >= 3:
|
||||
if len(val) >= 6:
|
||||
if val[:6] == 'angstr':
|
||||
if val[:6] == "angstr":
|
||||
for i in range(3):
|
||||
acell[i] /= Bohr
|
||||
break
|
||||
|
||||
acell += self._get_numerical_values(val)
|
||||
|
||||
self._tags['acell'] = acell[:3]
|
||||
self._tags["acell"] = acell[:3]
|
||||
|
||||
def _set_natom(self):
|
||||
self._tags['natom'] = int(self._values[0])
|
||||
self._tags["natom"] = int(self._values[0])
|
||||
|
||||
def _set_ntypat(self):
|
||||
self._tags['ntypat'] = int(self._values[0])
|
||||
self._tags["ntypat"] = int(self._values[0])
|
||||
|
||||
def _set_rprim(self):
|
||||
rprim = []
|
||||
|
@ -237,7 +252,7 @@ class AbinitIn(object):
|
|||
if len(rprim) >= 9:
|
||||
break
|
||||
|
||||
self._tags['rprim'] = np.reshape(rprim[:9], (3, 3))
|
||||
self._tags["rprim"] = np.reshape(rprim[:9], (3, 3))
|
||||
|
||||
def _set_scalecart(self):
|
||||
scalecart = []
|
||||
|
@ -246,43 +261,43 @@ class AbinitIn(object):
|
|||
if len(scalecart) >= 3:
|
||||
break
|
||||
|
||||
self._tags['scalecart'] = np.array(scalecart[:3])
|
||||
self._tags["scalecart"] = np.array(scalecart[:3])
|
||||
|
||||
def _set_typat(self):
|
||||
typat = []
|
||||
natom = self._tags['natom']
|
||||
natom = self._tags["natom"]
|
||||
for val in self._values:
|
||||
typat += self._get_numerical_values(val, num_type='int')
|
||||
typat += self._get_numerical_values(val, num_type="int")
|
||||
if len(typat) >= natom:
|
||||
break
|
||||
|
||||
self._tags['typat'] = typat[:natom]
|
||||
self._tags["typat"] = typat[:natom]
|
||||
|
||||
def _set_xangst(self):
|
||||
self._set_x_tags('xangst')
|
||||
self._set_x_tags("xangst")
|
||||
|
||||
def _set_xcart(self):
|
||||
self._set_x_tags('xcart')
|
||||
self._set_x_tags("xcart")
|
||||
|
||||
def _set_xred(self):
|
||||
self._set_x_tags('xred')
|
||||
self._set_x_tags("xred")
|
||||
|
||||
def _set_x_tags(self, tagname):
|
||||
xtag = []
|
||||
natom = self._tags['natom']
|
||||
natom = self._tags["natom"]
|
||||
for val in self._values:
|
||||
xtag += self._get_numerical_values(val)
|
||||
if len(xtag) >= natom * 3:
|
||||
break
|
||||
|
||||
self._tags[tagname] = np.reshape(xtag[:natom * 3], (-1, 3))
|
||||
self._tags[tagname] = np.reshape(xtag[: natom * 3], (-1, 3))
|
||||
|
||||
def _set_znucl(self):
|
||||
znucl = []
|
||||
ntypat = self._tags['ntypat']
|
||||
ntypat = self._tags["ntypat"]
|
||||
for val in self._values:
|
||||
znucl += self._get_numerical_values(val, num_type='int')
|
||||
znucl += self._get_numerical_values(val, num_type="int")
|
||||
if len(znucl) >= ntypat:
|
||||
break
|
||||
|
||||
self._tags['znucl'] = znucl[:ntypat]
|
||||
self._tags["znucl"] = znucl[:ntypat]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""FHIaims calculator interface."""
|
||||
# FHIaims.py - IO routines for phonopy-FHI-aims
|
||||
# methods compatible with the corresponding ones from ase.io.aims
|
||||
# only minimal subset of functionality required within phonopy context is implemented
|
||||
|
@ -39,22 +40,26 @@
|
|||
# Modified 2020 by Florian Knoop
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
|
||||
|
||||
# FK 2018/07/19
|
||||
def lmap(func, lis):
|
||||
"""Python2/3 compatibility:
|
||||
"""Python2/3 compatibility.
|
||||
|
||||
replace map(int, list) with lmap(int, list) that always returns a list
|
||||
instead of an iterator. Otherwise conflicts with np.array in python3. """
|
||||
instead of an iterator. Otherwise conflicts with np.array in python3.
|
||||
|
||||
"""
|
||||
return list(map(func, lis))
|
||||
|
||||
|
||||
def read_aims(filename):
|
||||
"""Method to read FHI-aims geometry files in phonopy context."""
|
||||
|
||||
"""Read FHI-aims geometry files in phonopy context."""
|
||||
lines = open(filename, "r").readlines()
|
||||
|
||||
cell = []
|
||||
|
@ -80,16 +85,18 @@ def read_aims(filename):
|
|||
positions.append(pos)
|
||||
symbols.append(sym)
|
||||
magmoms.append(None)
|
||||
# implicitly assuming that initial_moments line adhere to FHI-aims geometry.in specification,
|
||||
# i.e. two subsequent initial_moments lines do not occur
|
||||
# if they do, the value specified in the last line is taken here - without any warning
|
||||
# implicitly assuming that initial_moments line adhere to FHI-aims geometry.in
|
||||
# specification, i.e. two subsequent initial_moments lines do not occur
|
||||
# if they do, the value specified in the last line is taken here - without
|
||||
# any warning
|
||||
elif fields[0] == "initial_moment":
|
||||
magmoms[-1] = float(fields[1])
|
||||
|
||||
for (n, frac) in enumerate(is_frac):
|
||||
if frac:
|
||||
pos = [
|
||||
sum([positions[n][l] * cell[l][i] for l in range(3)]) for i in range(3)
|
||||
sum([positions[n][ll] * cell[ll][i] for ll in range(3)])
|
||||
for i in range(3)
|
||||
]
|
||||
positions[n] = pos
|
||||
if None in magmoms:
|
||||
|
@ -101,8 +108,7 @@ def read_aims(filename):
|
|||
|
||||
|
||||
def write_aims(filename, atoms):
|
||||
"""Method to write FHI-aims geometry files in phonopy context."""
|
||||
|
||||
"""Write FHI-aims geometry files in phonopy context."""
|
||||
lines = ""
|
||||
lines += "# geometry.in for FHI-aims \n"
|
||||
lines += "# | generated by phonopy.FHIaims.write_aims() \n"
|
||||
|
@ -130,29 +136,33 @@ def write_aims(filename, atoms):
|
|||
|
||||
|
||||
class Atoms_with_forces(Atoms):
|
||||
""" Hack to phonopy.atoms to maintain ASE compatibility also for forces."""
|
||||
"""Hack to phonopy.atoms to maintain ASE compatibility also for forces."""
|
||||
|
||||
def get_forces(self):
|
||||
"""Return forces."""
|
||||
return self.forces
|
||||
|
||||
|
||||
def read_aims_output(filename):
|
||||
""" Read FHI-aims output and
|
||||
return geometry, energy and forces from last self-consistency iteration"""
|
||||
"""Read aims output.
|
||||
|
||||
Read FHI-aims output and return geometry, energy and forces
|
||||
from last self-consistency iteration.
|
||||
|
||||
"""
|
||||
lines = open(filename, "r").readlines()
|
||||
|
||||
l = 0
|
||||
ll = 0
|
||||
N = 0
|
||||
while l < len(lines):
|
||||
line = lines[l]
|
||||
while ll < len(lines):
|
||||
line = lines[ll]
|
||||
if "| Number of atoms" in line:
|
||||
N = int(line.split()[5])
|
||||
elif "| Unit cell:" in line:
|
||||
cell = []
|
||||
for i in range(3):
|
||||
l += 1
|
||||
vec = lmap(float, lines[l].split()[1:4])
|
||||
ll += 1
|
||||
vec = lmap(float, lines[ll].split()[1:4])
|
||||
cell.append(vec)
|
||||
elif ("Atomic structure:" in line) or ("Updated atomic structure:" in line):
|
||||
if "Atomic structure:" in line:
|
||||
|
@ -163,12 +173,12 @@ def read_aims_output(filename):
|
|||
i_sym = 4
|
||||
i_pos_min = 1
|
||||
i_pos_max = 4
|
||||
l += 1
|
||||
ll += 1
|
||||
symbols = []
|
||||
positions = []
|
||||
for n in range(N):
|
||||
l += 1
|
||||
fields = lines[l].split()
|
||||
ll += 1
|
||||
fields = lines[ll].split()
|
||||
sym = fields[i_sym]
|
||||
pos = lmap(float, fields[i_pos_min:i_pos_max])
|
||||
symbols.append(sym)
|
||||
|
@ -176,10 +186,10 @@ def read_aims_output(filename):
|
|||
elif "Total atomic forces" in line:
|
||||
forces = []
|
||||
for i in range(N):
|
||||
l += 1
|
||||
force = lmap(float, lines[l].split()[-3:])
|
||||
ll += 1
|
||||
force = lmap(float, lines[ll].split()[-3:])
|
||||
forces.append(force)
|
||||
l += 1
|
||||
ll += 1
|
||||
|
||||
atoms = Atoms_with_forces(cell=cell, symbols=symbols, positions=positions)
|
||||
atoms.forces = forces
|
||||
|
@ -187,31 +197,30 @@ def read_aims_output(filename):
|
|||
return atoms
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
cells_with_disps,
|
||||
ids,
|
||||
pre_filename="geometry.in",
|
||||
width=3):
|
||||
"""Writes perfect supercell and supercells with displacements
|
||||
def write_supercells_with_displacements(
|
||||
supercell, cells_with_disps, ids, pre_filename="geometry.in", width=3
|
||||
):
|
||||
"""Write perfect supercell and supercells with displacements.
|
||||
|
||||
Args:
|
||||
supercell: perfect supercell
|
||||
cells_with_disps: supercells with displaced atoms
|
||||
filename: root-filename
|
||||
"""
|
||||
|
||||
"""
|
||||
# original cell
|
||||
write_aims(pre_filename + ".supercell", supercell)
|
||||
|
||||
# displaced cells
|
||||
for i, cell in zip(ids, cells_with_disps):
|
||||
filename = "{pre_filename}-{0:0{width}}".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_aims(filename, cell)
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
"""parse the forces from output files in `forces_filenames`"""
|
||||
"""Parse the forces from output files in ``forces_filenames``."""
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""ALM force constants interface."""
|
||||
# Copyright (C) 2018 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,42 +34,50 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def get_fc2(supercell,
|
||||
def get_fc2(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
atom_list=None,
|
||||
options=None,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Calculate fc2 using ALM."""
|
||||
p2s_map = primitive.p2s_map
|
||||
is_compact_fc = (atom_list is not None and
|
||||
(atom_list == p2s_map).all())
|
||||
fc2 = run_alm(supercell,
|
||||
is_compact_fc = atom_list is not None and (atom_list == p2s_map).all()
|
||||
fc2 = run_alm(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
1,
|
||||
is_compact_fc=is_compact_fc,
|
||||
options=options,
|
||||
log_level=log_level)[0]
|
||||
log_level=log_level,
|
||||
)[0]
|
||||
|
||||
if not is_compact_fc and atom_list is not None:
|
||||
fc2 = np.array(fc2[atom_list], dtype='double', order='C')
|
||||
fc2 = np.array(fc2[atom_list], dtype="double", order="C")
|
||||
|
||||
return fc2
|
||||
|
||||
|
||||
def run_alm(supercell,
|
||||
def run_alm(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
maxorder,
|
||||
is_compact_fc=False,
|
||||
options=None,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Calculate force constants of arbitrary-order using ALM."""
|
||||
fcs = None # This is returned.
|
||||
|
||||
lattice = supercell.cell
|
||||
|
@ -81,41 +90,43 @@ def run_alm(supercell,
|
|||
alm_options = _update_options(options)
|
||||
num_elems = len(np.unique(numbers))
|
||||
if log_level:
|
||||
print("---------------------------------"
|
||||
print(
|
||||
"---------------------------------"
|
||||
" ALM start "
|
||||
"--------------------------------")
|
||||
print("ALM is a non-trivial force constants calculator. "
|
||||
"Please cite the paper:")
|
||||
print("T. Tadano and S. Tsuneyuki, "
|
||||
"J. Phys. Soc. Jpn. 87, 041015 (2018).")
|
||||
print("ALM is developed at https://github.com/ttadano/ALM by T. "
|
||||
"Tadano.")
|
||||
"--------------------------------"
|
||||
)
|
||||
print(
|
||||
"ALM is a non-trivial force constants calculator. " "Please cite the paper:"
|
||||
)
|
||||
print("T. Tadano and S. Tsuneyuki, " "J. Phys. Soc. Jpn. 87, 041015 (2018).")
|
||||
print("ALM is developed at https://github.com/ttadano/ALM by T. " "Tadano.")
|
||||
if log_level == 1:
|
||||
print("Increase log-level to watch detailed ALM log.")
|
||||
|
||||
if 'norder' in alm_options:
|
||||
_maxorder = alm_options['norder']
|
||||
elif 'maxorder' in alm_options:
|
||||
_maxorder = alm_options['maxorder']
|
||||
if "norder" in alm_options:
|
||||
_maxorder = alm_options["norder"]
|
||||
elif "maxorder" in alm_options:
|
||||
_maxorder = alm_options["maxorder"]
|
||||
else:
|
||||
_maxorder = maxorder
|
||||
|
||||
shape = (_maxorder, num_elems, num_elems)
|
||||
cutoff_radii = -np.ones(shape, dtype='double')
|
||||
if alm_options['cutoff'] is not None:
|
||||
if len(alm_options['cutoff']) == 1:
|
||||
cutoff_radii[:] = alm_options['cutoff'][0]
|
||||
elif np.prod(shape) == len(alm_options['cutoff']):
|
||||
cutoff_radii[:] = np.reshape(alm_options['cutoff'], shape)
|
||||
cutoff_radii = -np.ones(shape, dtype="double")
|
||||
if alm_options["cutoff"] is not None:
|
||||
if len(alm_options["cutoff"]) == 1:
|
||||
cutoff_radii[:] = alm_options["cutoff"][0]
|
||||
elif np.prod(shape) == len(alm_options["cutoff"]):
|
||||
cutoff_radii[:] = np.reshape(alm_options["cutoff"], shape)
|
||||
else:
|
||||
raise RuntimeError("Cutoff is not properly set.")
|
||||
|
||||
_disps, _forces, df_msg = _slice_displacements_and_forces(
|
||||
displacements,
|
||||
forces,
|
||||
alm_options['ndata'],
|
||||
alm_options['nstart'],
|
||||
alm_options['nend'])
|
||||
alm_options["ndata"],
|
||||
alm_options["nstart"],
|
||||
alm_options["nend"],
|
||||
)
|
||||
|
||||
if log_level > 1:
|
||||
print("")
|
||||
|
@ -138,29 +149,30 @@ def run_alm(supercell,
|
|||
|
||||
with ALM(lattice, positions, numbers) as alm:
|
||||
if log_level > 0:
|
||||
if alm_options['cutoff'] is not None:
|
||||
if alm_options["cutoff"] is not None:
|
||||
for i in range(_maxorder):
|
||||
if _maxorder > 1:
|
||||
print("fc%d" % (i + 2))
|
||||
print(("cutoff" + " %6s" * num_elems)
|
||||
% tuple(alm.kind_names.values()))
|
||||
print(
|
||||
("cutoff" + " %6s" * num_elems) % tuple(alm.kind_names.values())
|
||||
)
|
||||
for r, kn in zip(cutoff_radii[i], alm.kind_names.values()):
|
||||
print((" %-3s" + " %6.2f" * num_elems)
|
||||
% ((kn, ) + tuple(r)))
|
||||
print((" %-3s" + " %6.2f" * num_elems) % ((kn,) + tuple(r)))
|
||||
if df_msg is not None:
|
||||
print(df_msg)
|
||||
if log_level > 1:
|
||||
print("")
|
||||
sys.stdout.flush()
|
||||
|
||||
alm.output_filename_prefix = alm_options['output_filename_prefix']
|
||||
alm.output_filename_prefix = alm_options["output_filename_prefix"]
|
||||
alm.verbosity = log_level_alm
|
||||
|
||||
alm.define(
|
||||
_maxorder,
|
||||
cutoff_radii=cutoff_radii,
|
||||
nbody=alm_options['nbody'],
|
||||
symmetrization_basis=alm_options['symmetrization_basis'])
|
||||
nbody=alm_options["nbody"],
|
||||
symmetrization_basis=alm_options["symmetrization_basis"],
|
||||
)
|
||||
alm.displacements = _disps
|
||||
alm.forces = _forces
|
||||
|
||||
|
@ -171,32 +183,30 @@ def run_alm(supercell,
|
|||
optcontrol[key] = alm_options[key]
|
||||
if optcontrol:
|
||||
alm.optimizer_control = optcontrol
|
||||
if ('cross_validation' in optcontrol and
|
||||
optcontrol['cross_validation'] > 0):
|
||||
alm.optimize(solver=alm_options['solver'])
|
||||
optcontrol['cross_validation'] = 0
|
||||
optcontrol['l1_alpha'] = alm.cv_l1_alpha
|
||||
if "cross_validation" in optcontrol and optcontrol["cross_validation"] > 0:
|
||||
alm.optimize(solver=alm_options["solver"])
|
||||
optcontrol["cross_validation"] = 0
|
||||
optcontrol["l1_alpha"] = alm.cv_l1_alpha
|
||||
alm.optimizer_control = optcontrol
|
||||
|
||||
alm.optimize(solver=alm_options['solver'])
|
||||
alm.optimize(solver=alm_options["solver"])
|
||||
|
||||
fcs = _extract_fc_from_alm(alm,
|
||||
natom,
|
||||
maxorder,
|
||||
is_compact_fc,
|
||||
p2s_map=p2s_map,
|
||||
p2p_map=p2p_map)
|
||||
fcs = _extract_fc_from_alm(
|
||||
alm, natom, maxorder, is_compact_fc, p2s_map=p2s_map, p2p_map=p2p_map
|
||||
)
|
||||
|
||||
if log_level:
|
||||
print("----------------------------------"
|
||||
print(
|
||||
"----------------------------------"
|
||||
" ALM end "
|
||||
"---------------------------------")
|
||||
"---------------------------------"
|
||||
)
|
||||
|
||||
return fcs
|
||||
|
||||
|
||||
def _update_options(fc_calculator_options):
|
||||
"""Set ALM options with appropriate data types
|
||||
"""Set ALM options with appropriate data types.
|
||||
|
||||
fc_calculator_options : str
|
||||
This string should be written such as follows:
|
||||
|
@ -208,43 +218,46 @@ def _update_options(fc_calculator_options):
|
|||
is cast to have its appropriate data type for ALM in this method.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
from alm import optimizer_control_data_types
|
||||
except ImportError:
|
||||
raise ImportError("ALM python module was not found.")
|
||||
|
||||
# Default settings.
|
||||
alm_options = {'solver': 'dense',
|
||||
'ndata': None,
|
||||
'nstart': None,
|
||||
'nend': None,
|
||||
'nbody': None,
|
||||
'cutoff': None,
|
||||
'symmetrization_basis': 'Lattice',
|
||||
'output_filename_prefix': None}
|
||||
alm_options = {
|
||||
"solver": "dense",
|
||||
"ndata": None,
|
||||
"nstart": None,
|
||||
"nend": None,
|
||||
"nbody": None,
|
||||
"cutoff": None,
|
||||
"symmetrization_basis": "Lattice",
|
||||
"output_filename_prefix": None,
|
||||
}
|
||||
|
||||
if fc_calculator_options is not None:
|
||||
alm_option_types = {'cutoff': np.double,
|
||||
'maxorder': int,
|
||||
'norder': int,
|
||||
'ndata': int,
|
||||
'nstart': int,
|
||||
'nend': int,
|
||||
'nbody': np.intc,
|
||||
'output_filename_prefix': str,
|
||||
'solver': str,
|
||||
'symmetrization_basis': str}
|
||||
alm_option_types = {
|
||||
"cutoff": np.double,
|
||||
"maxorder": int,
|
||||
"norder": int,
|
||||
"ndata": int,
|
||||
"nstart": int,
|
||||
"nend": int,
|
||||
"nbody": np.intc,
|
||||
"output_filename_prefix": str,
|
||||
"solver": str,
|
||||
"symmetrization_basis": str,
|
||||
}
|
||||
alm_option_types.update(optimizer_control_data_types)
|
||||
for option_str in fc_calculator_options.split(","):
|
||||
key, val = [x.strip() for x in option_str.split('=')[:2]]
|
||||
key, val = [x.strip() for x in option_str.split("=")[:2]]
|
||||
if key.lower() in alm_option_types:
|
||||
if alm_option_types[key.lower()] is np.double:
|
||||
option_value = np.array(
|
||||
[float(x) for x in val.split()], dtype='double')
|
||||
[float(x) for x in val.split()], dtype="double"
|
||||
)
|
||||
elif alm_option_types[key.lower()] is np.intc:
|
||||
option_value = np.array(
|
||||
[int(x) for x in val.split()], dtype='intc')
|
||||
option_value = np.array([int(x) for x in val.split()], dtype="intc")
|
||||
else:
|
||||
option_value = alm_option_types[key.lower()](val)
|
||||
alm_options[key] = option_value
|
||||
|
@ -258,38 +271,40 @@ def _slice_displacements_and_forces(d, f, ndata, nstart, nend):
|
|||
_f = f[:ndata]
|
||||
msg = "Number of displacement supercells: %d" % ndata
|
||||
elif nstart is not None and nend is not None:
|
||||
_d = d[nstart - 1:nend]
|
||||
_f = f[nstart - 1:nend]
|
||||
_d = d[nstart - 1 : nend]
|
||||
_f = f[nstart - 1 : nend]
|
||||
msg = "Supercell index range: %d - %d" % (nstart, nend)
|
||||
else:
|
||||
return d, f, None
|
||||
|
||||
return (np.array(_d, dtype='double', order='C'),
|
||||
np.array(_f, dtype='double', order='C'), msg)
|
||||
return (
|
||||
np.array(_d, dtype="double", order="C"),
|
||||
np.array(_f, dtype="double", order="C"),
|
||||
msg,
|
||||
)
|
||||
|
||||
|
||||
def _extract_fc_from_alm(alm,
|
||||
natom,
|
||||
maxorder,
|
||||
is_compact_fc,
|
||||
p2s_map=None,
|
||||
p2p_map=None):
|
||||
def _extract_fc_from_alm(
|
||||
alm, natom, maxorder, is_compact_fc, p2s_map=None, p2p_map=None
|
||||
):
|
||||
# Harmonic: order=1, 3rd: order=2, ...
|
||||
fcs = []
|
||||
for order in range(1, maxorder + 1):
|
||||
fc = None
|
||||
p2s_map_alm = alm.getmap_primitive_to_supercell()[0]
|
||||
if (is_compact_fc and
|
||||
len(p2s_map_alm) == len(p2s_map) and
|
||||
(p2s_map_alm == p2s_map).all()):
|
||||
fc_shape = (
|
||||
(len(p2s_map), ) + (natom, ) * order + (3, ) * (order + 1))
|
||||
fc = np.zeros(fc_shape, dtype='double', order='C')
|
||||
if (
|
||||
is_compact_fc
|
||||
and len(p2s_map_alm) == len(p2s_map)
|
||||
and (p2s_map_alm == p2s_map).all()
|
||||
):
|
||||
fc_shape = (len(p2s_map),) + (natom,) * order + (3,) * (order + 1)
|
||||
fc = np.zeros(fc_shape, dtype="double", order="C")
|
||||
for fc_elem, indices in zip(
|
||||
*alm.get_fc(order, mode='origin', permutation=1)):
|
||||
*alm.get_fc(order, mode="origin", permutation=1)
|
||||
):
|
||||
v = indices // 3
|
||||
c = indices % 3
|
||||
selection = (p2p_map[v[0]], ) + tuple(v[1:]) + tuple(c)
|
||||
selection = (p2p_map[v[0]],) + tuple(v[1:]) + tuple(c)
|
||||
fc[selection] = fc_elem
|
||||
|
||||
if fc is None:
|
||||
|
@ -297,16 +312,14 @@ def _extract_fc_from_alm(alm,
|
|||
atom_list = p2s_map
|
||||
else:
|
||||
atom_list = np.arange(natom, dtype=int)
|
||||
fc_shape = (
|
||||
(len(atom_list), ) + (natom, ) * order + (3, ) * (order + 1))
|
||||
fc = np.zeros(fc_shape, dtype='double', order='C')
|
||||
for fc_elem, indices in zip(
|
||||
*alm.get_fc(order, mode='all', permutation=1)):
|
||||
fc_shape = (len(atom_list),) + (natom,) * order + (3,) * (order + 1)
|
||||
fc = np.zeros(fc_shape, dtype="double", order="C")
|
||||
for fc_elem, indices in zip(*alm.get_fc(order, mode="all", permutation=1)):
|
||||
v = indices // 3
|
||||
idx = np.where(atom_list == v[0])[0]
|
||||
if len(idx) > 0:
|
||||
c = indices % 3
|
||||
selection = (idx[0], ) + tuple(v[1:]) + tuple(c)
|
||||
selection = (idx[0],) + tuple(v[1:]) + tuple(c)
|
||||
fc[selection] = fc_elem
|
||||
|
||||
fcs.append(fc)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Routines to handle various calculator interfaces."""
|
||||
# Copyright (C) 2014 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,60 +34,71 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
import yaml
|
||||
from argparse import ArgumentParser
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
from phonopy.interface.phonopy_yaml import PhonopyYaml
|
||||
from phonopy.structure.dataset import get_displacements_and_forces
|
||||
from phonopy.structure.cells import determinant
|
||||
from phonopy.interface.vasp import sort_positions_by_symbols
|
||||
from phonopy.structure.cells import determinant
|
||||
from phonopy.structure.dataset import get_displacements_and_forces
|
||||
from phonopy.units import (
|
||||
AbinitToTHz,
|
||||
Bohr,
|
||||
CastepToTHz,
|
||||
CP2KToTHz,
|
||||
CrystalToTHz,
|
||||
DftbpToTHz,
|
||||
ElkToTHz,
|
||||
FleurToTHz,
|
||||
Hartree,
|
||||
PwscfToTHz,
|
||||
Rydberg,
|
||||
SiestaToTHz,
|
||||
TurbomoleToTHz,
|
||||
VaspToTHz,
|
||||
Wien2kToTHz,
|
||||
)
|
||||
|
||||
calculator_info = {
|
||||
'abinit': {'option': {'name': "--abinit",
|
||||
'help': "Invoke Abinit mode"}},
|
||||
'aims': {'option': {'name': "--aims",
|
||||
'help': "Invoke FHI-aims mode"}},
|
||||
'cp2k': {'option': {'name': "--cp2k",
|
||||
'help': "Invoke CP2K mode"}},
|
||||
'crystal': {'option': {'name': "--crystal",
|
||||
'help': "Invoke CRYSTAL mode"}},
|
||||
'dftbp': {'option': {'name': "--dftb+",
|
||||
'help': "Invoke dftb+ mode"}},
|
||||
'elk': {'option': {'name': "--elk",
|
||||
'help': "Invoke elk mode"}},
|
||||
'qe': {'option': {'name': "--qe",
|
||||
'help': "Invoke Quantum espresso (QE) mode"}},
|
||||
'siesta': {'option': {'name': "--siesta",
|
||||
'help': "Invoke Siesta mode"}},
|
||||
'turbomole': {'option': {'name': "--turbomole",
|
||||
'help': "Invoke TURBOMOLE mode"}},
|
||||
'vasp': {'option': {'name': "--vasp",
|
||||
'help': "Invoke Vasp mode"}},
|
||||
'wien2k': {'option': {'name': "--wien2k",
|
||||
'help': "Invoke Wien2k mode"}},
|
||||
'castep': {'option': {'name': "--castep",
|
||||
'help': "Invoke CASTEP mode"}},
|
||||
'fleur': {'option': {'name': "--fleur",
|
||||
'help': "Invoke Fleur mode"}},
|
||||
"abinit": {"option": {"name": "--abinit", "help": "Invoke Abinit mode"}},
|
||||
"aims": {"option": {"name": "--aims", "help": "Invoke FHI-aims mode"}},
|
||||
"cp2k": {"option": {"name": "--cp2k", "help": "Invoke CP2K mode"}},
|
||||
"crystal": {"option": {"name": "--crystal", "help": "Invoke CRYSTAL mode"}},
|
||||
"dftbp": {"option": {"name": "--dftb+", "help": "Invoke dftb+ mode"}},
|
||||
"elk": {"option": {"name": "--elk", "help": "Invoke elk mode"}},
|
||||
"qe": {"option": {"name": "--qe", "help": "Invoke Quantum espresso (QE) mode"}},
|
||||
"siesta": {"option": {"name": "--siesta", "help": "Invoke Siesta mode"}},
|
||||
"turbomole": {"option": {"name": "--turbomole", "help": "Invoke TURBOMOLE mode"}},
|
||||
"vasp": {"option": {"name": "--vasp", "help": "Invoke Vasp mode"}},
|
||||
"wien2k": {"option": {"name": "--wien2k", "help": "Invoke Wien2k mode"}},
|
||||
"castep": {"option": {"name": "--castep", "help": "Invoke CASTEP mode"}},
|
||||
"fleur": {"option": {"name": "--fleur", "help": "Invoke Fleur mode"}},
|
||||
}
|
||||
|
||||
|
||||
def add_arguments_of_calculators(parser, calculator_info):
|
||||
def add_arguments_of_calculators(parser: ArgumentParser, calculator_info):
|
||||
"""Add options of calculators to ArgumentParser class instance."""
|
||||
for calculator in calculator_info:
|
||||
option = calculator_info[calculator]['option']
|
||||
option = calculator_info[calculator]["option"]
|
||||
parser.add_argument(
|
||||
option['name'], dest="%s_mode" % calculator, action="store_true",
|
||||
default=False, help=option['help'])
|
||||
option["name"],
|
||||
dest="%s_mode" % calculator,
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=option["help"],
|
||||
)
|
||||
|
||||
|
||||
def get_interface_mode(args_dict):
|
||||
"""Return calculator name
|
||||
"""Return calculator name.
|
||||
|
||||
The calculator name is obtained from command option arguments where
|
||||
argparse is used. The argument attribute name has to be
|
||||
"{calculator}_mode". Then this method returns "{calculator}".
|
||||
|
||||
"""
|
||||
|
||||
for calculator in calculator_info:
|
||||
mode = "%s_mode" % calculator
|
||||
if mode in args_dict and args_dict[mode]:
|
||||
|
@ -94,25 +106,20 @@ def get_interface_mode(args_dict):
|
|||
return None
|
||||
|
||||
|
||||
def convert_crystal_structure(filename_in,
|
||||
interface_in,
|
||||
filename_out,
|
||||
interface_out):
|
||||
cell, optional_structure_info = read_crystal_structure(
|
||||
filename=filename_in,
|
||||
interface_mode=interface_in)
|
||||
def convert_crystal_structure(filename_in, interface_in, filename_out, interface_out):
|
||||
"""Convert crystal structures between different calculator interfaces."""
|
||||
cell, _ = read_crystal_structure(filename=filename_in, interface_mode=interface_in)
|
||||
units_in = get_default_physical_units(interface_in)
|
||||
units_out = get_default_physical_units(interface_out)
|
||||
factor = units_in['distance_to_A'] / units_out['distance_to_A']
|
||||
factor = units_in["distance_to_A"] / units_out["distance_to_A"]
|
||||
cell.cell = cell.cell * factor
|
||||
write_crystal_structure(filename_out, cell, interface_mode=interface_out)
|
||||
|
||||
|
||||
def write_crystal_structure(filename,
|
||||
cell,
|
||||
interface_mode=None,
|
||||
optional_structure_info=None):
|
||||
"""Utility method to write out a crystal structure
|
||||
def write_crystal_structure(
|
||||
filename, cell, interface_mode=None, optional_structure_info=None
|
||||
):
|
||||
"""Write crystal structure to file in each calculator format.
|
||||
|
||||
filename : str, optional
|
||||
File name to be used to write out the crystal structure.
|
||||
|
@ -126,65 +133,79 @@ def write_crystal_structure(filename,
|
|||
See the docstring. Default is None.
|
||||
|
||||
"""
|
||||
|
||||
if interface_mode is None or interface_mode == 'vasp':
|
||||
if interface_mode is None or interface_mode == "vasp":
|
||||
import phonopy.interface.vasp as vasp
|
||||
|
||||
vasp.write_vasp(filename, cell)
|
||||
elif interface_mode == 'abinit':
|
||||
elif interface_mode == "abinit":
|
||||
import phonopy.interface.abinit as abinit
|
||||
|
||||
abinit.write_abinit(filename, cell)
|
||||
elif interface_mode == 'qe':
|
||||
elif interface_mode == "qe":
|
||||
import phonopy.interface.qe as qe
|
||||
|
||||
pp_filenames = optional_structure_info[1]
|
||||
qe.write_pwscf(filename, cell, pp_filenames)
|
||||
elif interface_mode == 'wien2k':
|
||||
elif interface_mode == "wien2k":
|
||||
import phonopy.interface.wien2k as wien2k
|
||||
|
||||
_, npts, r0s, rmts = optional_structure_info
|
||||
wien2k.write_wein2k(filename, cell, npts, r0s, rmts)
|
||||
elif interface_mode == 'elk':
|
||||
elif interface_mode == "elk":
|
||||
import phonopy.interface.elk as elk
|
||||
|
||||
sp_filenames = optional_structure_info[1]
|
||||
elk.write_elk(filename, cell, sp_filenames)
|
||||
elif interface_mode == 'siesta':
|
||||
elif interface_mode == "siesta":
|
||||
import phonopy.interface.siesta as siesta
|
||||
|
||||
atypes = optional_structure_info[1]
|
||||
siesta.write_siesta(filename, cell, atypes)
|
||||
elif interface_mode == 'cp2k':
|
||||
elif interface_mode == "cp2k":
|
||||
import phonopy.interface.cp2k as cp2k
|
||||
|
||||
_, tree = optional_structure_info
|
||||
cp2k.write_cp2k_by_filename(filename, cell, tree)
|
||||
elif interface_mode == 'crystal':
|
||||
elif interface_mode == "crystal":
|
||||
import phonopy.interface.crystal as crystal
|
||||
|
||||
conv_numbers = optional_structure_info[1]
|
||||
crystal.write_crystal(filename, cell, conv_numbers)
|
||||
elif interface_mode == 'dftbp':
|
||||
elif interface_mode == "dftbp":
|
||||
import phonopy.interface.dftbp as dftbp
|
||||
|
||||
dftbp.write_dftbp(filename, cell)
|
||||
elif interface_mode == 'turbomole':
|
||||
elif interface_mode == "turbomole":
|
||||
import phonopy.interface.turbomole as turbomole
|
||||
|
||||
turbomole.write_turbomole(filename, cell)
|
||||
elif interface_mode == 'aims':
|
||||
elif interface_mode == "aims":
|
||||
import phonopy.interface.aims as aims
|
||||
|
||||
aims.write_aims(filename, cell)
|
||||
elif interface_mode == 'castep':
|
||||
elif interface_mode == "castep":
|
||||
import phonopy.interface.castep as castep
|
||||
|
||||
castep.write_castep(filename, cell)
|
||||
elif interface_mode == 'fleur':
|
||||
elif interface_mode == "fleur":
|
||||
import phonopy.interface.fleur as fleur
|
||||
|
||||
speci, restlines = optional_structure_info
|
||||
fleur.write_fleur(filename, cell, speci, N, restlines)
|
||||
fleur.write_fleur(filename, cell, speci, 1, restlines)
|
||||
else:
|
||||
raise RuntimeError("No calculator interface was found.")
|
||||
|
||||
|
||||
def write_supercells_with_displacements(interface_mode,
|
||||
def write_supercells_with_displacements(
|
||||
interface_mode,
|
||||
supercell,
|
||||
cells_with_disps,
|
||||
optional_structure_info,
|
||||
displacement_ids=None,
|
||||
zfill_width=3,
|
||||
additional_info=None):
|
||||
"""Utility method to write out supercell structures with displacements
|
||||
additional_info=None,
|
||||
):
|
||||
"""Write supercell with displacements to files in each calculator format.
|
||||
|
||||
interface_mode : str
|
||||
Calculator interface such as 'vasp', 'qe', ...
|
||||
|
@ -207,80 +228,91 @@ def write_supercells_with_displacements(interface_mode,
|
|||
Default is None.
|
||||
|
||||
"""
|
||||
|
||||
if displacement_ids is None:
|
||||
ids = np.arange(len(cells_with_disps), dtype=int) + 1
|
||||
else:
|
||||
ids = displacement_ids
|
||||
|
||||
args = (supercell, cells_with_disps, ids)
|
||||
kwargs = {'width': zfill_width}
|
||||
if 'pre_filename' in additional_info:
|
||||
kwargs['pre_filename'] = additional_info['pre_filename']
|
||||
kwargs = {"width": zfill_width}
|
||||
if "pre_filename" in additional_info:
|
||||
kwargs["pre_filename"] = additional_info["pre_filename"]
|
||||
|
||||
if interface_mode is None or interface_mode == 'vasp':
|
||||
if interface_mode is None or interface_mode == "vasp":
|
||||
import phonopy.interface.vasp as vasp
|
||||
|
||||
vasp.write_supercells_with_displacements(*args, **kwargs)
|
||||
write_magnetic_moments(supercell, sort_by_elements=True)
|
||||
elif interface_mode == 'abinit':
|
||||
elif interface_mode == "abinit":
|
||||
import phonopy.interface.abinit as abinit
|
||||
|
||||
abinit.write_supercells_with_displacements(*args, **kwargs)
|
||||
elif interface_mode == 'qe':
|
||||
elif interface_mode == "qe":
|
||||
import phonopy.interface.qe as qe
|
||||
|
||||
pp_filenames = optional_structure_info[1]
|
||||
qe_args = args + (pp_filenames, )
|
||||
qe_args = args + (pp_filenames,)
|
||||
qe.write_supercells_with_displacements(*qe_args, **kwargs)
|
||||
write_magnetic_moments(supercell, sort_by_elements=False)
|
||||
elif interface_mode == 'wien2k':
|
||||
elif interface_mode == "wien2k":
|
||||
import phonopy.interface.wien2k as wien2k
|
||||
|
||||
unitcell_filename, npts, r0s, rmts = optional_structure_info
|
||||
N = abs(determinant(additional_info['supercell_matrix']))
|
||||
N = abs(determinant(additional_info["supercell_matrix"]))
|
||||
w2k_args = args + (npts, r0s, rmts, N)
|
||||
if 'pre_filename' not in kwargs:
|
||||
kwargs['pre_filename'] = unitcell_filename
|
||||
if "pre_filename" not in kwargs:
|
||||
kwargs["pre_filename"] = unitcell_filename
|
||||
wien2k.write_supercells_with_displacements(*w2k_args, **kwargs)
|
||||
elif interface_mode == 'elk':
|
||||
elif interface_mode == "elk":
|
||||
import phonopy.interface.elk as elk
|
||||
|
||||
sp_filenames = optional_structure_info[1]
|
||||
elk_args = args + (sp_filenames, )
|
||||
elk_args = args + (sp_filenames,)
|
||||
elk.write_supercells_with_displacements(*elk_args, **kwargs)
|
||||
elif interface_mode == 'siesta':
|
||||
elif interface_mode == "siesta":
|
||||
import phonopy.interface.siesta as siesta
|
||||
|
||||
atypes = optional_structure_info[1]
|
||||
sst_args = args + (atypes, )
|
||||
sst_args = args + (atypes,)
|
||||
siesta.write_supercells_with_displacements(*sst_args, **kwargs)
|
||||
elif interface_mode == 'cp2k':
|
||||
elif interface_mode == "cp2k":
|
||||
import phonopy.interface.cp2k as cp2k
|
||||
cp2k_args = args + (optional_structure_info, )
|
||||
|
||||
cp2k_args = args + (optional_structure_info,)
|
||||
cp2k.write_supercells_with_displacements(*cp2k_args, **kwargs)
|
||||
elif interface_mode == 'crystal':
|
||||
elif interface_mode == "crystal":
|
||||
import phonopy.interface.crystal as crystal
|
||||
|
||||
if additional_info is None:
|
||||
kwargs['template_file'] = "TEMPLATE"
|
||||
kwargs["template_file"] = "TEMPLATE"
|
||||
else:
|
||||
kwargs['template_file'] = additional_info.get('template_file',
|
||||
"TEMPLATE")
|
||||
kwargs["template_file"] = additional_info.get("template_file", "TEMPLATE")
|
||||
conv_numbers = optional_structure_info[1]
|
||||
N = abs(determinant(additional_info['supercell_matrix']))
|
||||
N = abs(determinant(additional_info["supercell_matrix"]))
|
||||
cst_args = args + (conv_numbers, N)
|
||||
crystal.write_supercells_with_displacements(*cst_args, **kwargs)
|
||||
elif interface_mode == 'dftbp':
|
||||
elif interface_mode == "dftbp":
|
||||
import phonopy.interface.dftbp as dftbp
|
||||
|
||||
dftbp.write_supercells_with_displacements(*args, **kwargs)
|
||||
elif interface_mode == 'turbomole':
|
||||
elif interface_mode == "turbomole":
|
||||
import phonopy.interface.turbomole as turbomole
|
||||
|
||||
turbomole.write_supercells_with_displacements(*args, **kwargs)
|
||||
elif interface_mode == 'aims':
|
||||
elif interface_mode == "aims":
|
||||
import phonopy.interface.aims as aims
|
||||
|
||||
aims.write_supercells_with_displacements(*args, **kwargs)
|
||||
elif interface_mode == 'castep':
|
||||
elif interface_mode == "castep":
|
||||
import phonopy.interface.castep as castep
|
||||
|
||||
castep.write_supercells_with_displacements(*args, **kwargs)
|
||||
elif interface_mode == 'fleur':
|
||||
elif interface_mode == "fleur":
|
||||
import phonopy.interface.fleur as fleur
|
||||
|
||||
speci = optional_structure_info[1]
|
||||
restlines = optional_structure_info[2]
|
||||
N = abs(determinant(additional_info['supercell_matrix']))
|
||||
N = abs(determinant(additional_info["supercell_matrix"]))
|
||||
fleur_args = args + (speci, N, restlines)
|
||||
fleur.write_supercells_with_displacements(*fleur_args, **kwargs)
|
||||
else:
|
||||
|
@ -288,15 +320,17 @@ def write_supercells_with_displacements(interface_mode,
|
|||
|
||||
|
||||
def write_magnetic_moments(cell, sort_by_elements=False):
|
||||
"""Write MAGMOM."""
|
||||
magmoms = cell.magnetic_moments
|
||||
if magmoms is not None:
|
||||
if sort_by_elements:
|
||||
(_, _, _, sort_list) = sort_positions_by_symbols(
|
||||
cell.symbols, cell.scaled_positions)
|
||||
cell.symbols, cell.scaled_positions
|
||||
)
|
||||
else:
|
||||
sort_list = range(cell.get_number_of_atoms())
|
||||
|
||||
with open("MAGMOM", 'w') as w:
|
||||
with open("MAGMOM", "w") as w:
|
||||
w.write(" MAGMOM = ")
|
||||
for i in sort_list:
|
||||
w.write("%f " % magmoms[i])
|
||||
|
@ -304,11 +338,10 @@ def write_magnetic_moments(cell, sort_by_elements=False):
|
|||
w.close()
|
||||
|
||||
|
||||
def read_crystal_structure(filename=None,
|
||||
interface_mode=None,
|
||||
chemical_symbols=None,
|
||||
phonopy_yaml_cls=None):
|
||||
"""Returns crystal structure information
|
||||
def read_crystal_structure(
|
||||
filename=None, interface_mode=None, chemical_symbols=None, phonopy_yaml_cls=None
|
||||
):
|
||||
"""Return crystal structure from file in each calculator format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
@ -336,8 +369,7 @@ def read_crystal_structure(filename=None,
|
|||
and the rest is dependent on calculator interface.
|
||||
|
||||
"""
|
||||
|
||||
if interface_mode == 'phonopy_yaml':
|
||||
if interface_mode == "phonopy_yaml":
|
||||
if phonopy_yaml_cls is None:
|
||||
return _read_phonopy_yaml(filename, PhonopyYaml)
|
||||
else:
|
||||
|
@ -352,125 +384,148 @@ def read_crystal_structure(filename=None,
|
|||
if not os.path.isfile(cell_filename):
|
||||
return None, (cell_filename,)
|
||||
|
||||
if interface_mode is None or interface_mode == 'vasp':
|
||||
if interface_mode is None or interface_mode == "vasp":
|
||||
from phonopy.interface.vasp import read_vasp
|
||||
|
||||
if chemical_symbols is None:
|
||||
unitcell = read_vasp(cell_filename)
|
||||
else:
|
||||
unitcell = read_vasp(cell_filename, symbols=chemical_symbols)
|
||||
return unitcell, (cell_filename,)
|
||||
elif interface_mode == 'abinit':
|
||||
elif interface_mode == "abinit":
|
||||
from phonopy.interface.abinit import read_abinit
|
||||
|
||||
unitcell = read_abinit(cell_filename)
|
||||
return unitcell, (cell_filename,)
|
||||
elif interface_mode == 'qe':
|
||||
elif interface_mode == "qe":
|
||||
from phonopy.interface.qe import read_pwscf
|
||||
|
||||
unitcell, pp_filenames = read_pwscf(cell_filename)
|
||||
return unitcell, (cell_filename, pp_filenames)
|
||||
elif interface_mode == 'wien2k':
|
||||
elif interface_mode == "wien2k":
|
||||
from phonopy.interface.wien2k import parse_wien2k_struct
|
||||
|
||||
unitcell, npts, r0s, rmts = parse_wien2k_struct(cell_filename)
|
||||
return unitcell, (cell_filename, npts, r0s, rmts)
|
||||
elif interface_mode == 'elk':
|
||||
elif interface_mode == "elk":
|
||||
from phonopy.interface.elk import read_elk
|
||||
|
||||
unitcell, sp_filenames = read_elk(cell_filename)
|
||||
return unitcell, (cell_filename, sp_filenames)
|
||||
elif interface_mode == 'siesta':
|
||||
elif interface_mode == "siesta":
|
||||
from phonopy.interface.siesta import read_siesta
|
||||
|
||||
unitcell, atypes = read_siesta(cell_filename)
|
||||
return unitcell, (cell_filename, atypes)
|
||||
elif interface_mode == 'cp2k':
|
||||
elif interface_mode == "cp2k":
|
||||
from phonopy.interface.cp2k import read_cp2k
|
||||
|
||||
unitcell, config_tree = read_cp2k(cell_filename)
|
||||
return unitcell, (cell_filename, config_tree)
|
||||
elif interface_mode == 'crystal':
|
||||
elif interface_mode == "crystal":
|
||||
from phonopy.interface.crystal import read_crystal
|
||||
|
||||
unitcell, conv_numbers = read_crystal(cell_filename)
|
||||
return unitcell, (cell_filename, conv_numbers)
|
||||
elif interface_mode == 'dftbp':
|
||||
elif interface_mode == "dftbp":
|
||||
from phonopy.interface.dftbp import read_dftbp
|
||||
|
||||
unitcell = read_dftbp(cell_filename)
|
||||
return unitcell, (cell_filename,)
|
||||
elif interface_mode == 'turbomole':
|
||||
elif interface_mode == "turbomole":
|
||||
from phonopy.interface.turbomole import read_turbomole
|
||||
|
||||
unitcell = read_turbomole(cell_filename)
|
||||
return unitcell, (cell_filename,)
|
||||
elif interface_mode == 'aims':
|
||||
elif interface_mode == "aims":
|
||||
from phonopy.interface.aims import read_aims
|
||||
|
||||
unitcell = read_aims(cell_filename)
|
||||
return unitcell, (cell_filename,)
|
||||
elif interface_mode == 'castep':
|
||||
elif interface_mode == "castep":
|
||||
from phonopy.interface.castep import read_castep
|
||||
|
||||
unitcell = read_castep(cell_filename)
|
||||
return unitcell, (cell_filename,)
|
||||
elif interface_mode == 'fleur':
|
||||
elif interface_mode == "fleur":
|
||||
from phonopy.interface.fleur import read_fleur
|
||||
|
||||
unitcell, speci, restlines = read_fleur(cell_filename)
|
||||
return unitcell, (cell_filename,speci,restlines)
|
||||
return unitcell, (cell_filename, speci, restlines)
|
||||
else:
|
||||
raise RuntimeError("No calculator interface was found.")
|
||||
|
||||
|
||||
def get_default_cell_filename(interface_mode):
|
||||
if interface_mode is None or interface_mode == 'vasp':
|
||||
"""Return default filename of unit cell structure of each calculator."""
|
||||
if interface_mode is None or interface_mode == "vasp":
|
||||
return "POSCAR"
|
||||
elif interface_mode in ('abinit', 'qe'):
|
||||
elif interface_mode in ("abinit", "qe"):
|
||||
return "unitcell.in"
|
||||
elif interface_mode == 'wien2k':
|
||||
elif interface_mode == "wien2k":
|
||||
return "case.struct"
|
||||
elif interface_mode == 'elk':
|
||||
elif interface_mode == "elk":
|
||||
return "elk.in"
|
||||
elif interface_mode == 'siesta':
|
||||
elif interface_mode == "siesta":
|
||||
return "input.fdf"
|
||||
elif interface_mode == 'cp2k':
|
||||
elif interface_mode == "cp2k":
|
||||
return "unitcell.inp"
|
||||
elif interface_mode == 'crystal':
|
||||
elif interface_mode == "crystal":
|
||||
return "crystal.o"
|
||||
elif interface_mode == 'dftbp':
|
||||
elif interface_mode == "dftbp":
|
||||
return "geo.gen"
|
||||
elif interface_mode == 'turbomole':
|
||||
elif interface_mode == "turbomole":
|
||||
return "control"
|
||||
elif interface_mode == 'aims':
|
||||
elif interface_mode == "aims":
|
||||
return "geometry.in"
|
||||
elif interface_mode in ('castep'):
|
||||
elif interface_mode in ("castep"):
|
||||
return "unitcell.cell"
|
||||
elif interface_mode == 'fleur':
|
||||
elif interface_mode == "fleur":
|
||||
return "fleur.in"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_default_supercell_filename(interface_mode):
|
||||
if interface_mode == 'phonopy_yaml':
|
||||
"""Return default filename of supercell structure of each calculator."""
|
||||
if interface_mode == "phonopy_yaml":
|
||||
return "phonopy_disp.yaml"
|
||||
elif interface_mode is None or interface_mode == 'vasp':
|
||||
elif interface_mode is None or interface_mode == "vasp":
|
||||
return "SPOSCAR"
|
||||
elif interface_mode in ('abinit', 'elk', 'qe', 'fleur'):
|
||||
elif interface_mode in ("abinit", "elk", "qe", "fleur"):
|
||||
return "supercell.in"
|
||||
elif interface_mode == 'wien2k':
|
||||
elif interface_mode == "wien2k":
|
||||
return "case.structS"
|
||||
elif interface_mode == 'siesta':
|
||||
elif interface_mode == "siesta":
|
||||
return "supercell.fdf"
|
||||
elif interface_mode == 'cp2k':
|
||||
elif interface_mode == "cp2k":
|
||||
# CP2K interface generates filenames based on original project name
|
||||
return None
|
||||
elif interface_mode == 'crystal':
|
||||
elif interface_mode == "crystal":
|
||||
return None # supercell.ext can not be parsed by crystal interface.
|
||||
elif interface_mode == 'dftbp':
|
||||
elif interface_mode == "dftbp":
|
||||
return "geo.genS"
|
||||
elif interface_mode == 'turbomole':
|
||||
elif interface_mode == "turbomole":
|
||||
return None # TURBOMOLE interface generates directories with inputs
|
||||
elif interface_mode == 'aims':
|
||||
elif interface_mode == "aims":
|
||||
return "geometry.in.supercell"
|
||||
elif interface_mode in ('castep'):
|
||||
elif interface_mode in ("castep"):
|
||||
return "supercell.cell"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_default_displacement_distance(interface_mode):
|
||||
if interface_mode in ('wien2k', 'abinit', 'elk', 'qe', 'siesta',
|
||||
'turbomole', 'fleur'):
|
||||
"""Return default displacement distance of each calculator."""
|
||||
if interface_mode in (
|
||||
"wien2k",
|
||||
"abinit",
|
||||
"elk",
|
||||
"qe",
|
||||
"siesta",
|
||||
"turbomole",
|
||||
"fleur",
|
||||
):
|
||||
displacement_distance = 0.02
|
||||
else: # default or vasp, crystal, cp2k
|
||||
displacement_distance = 0.01
|
||||
|
@ -478,7 +533,7 @@ def get_default_displacement_distance(interface_mode):
|
|||
|
||||
|
||||
def get_default_physical_units(interface_mode=None):
|
||||
"""Return physical units used for calculators
|
||||
"""Return physical units of eachi calculator.
|
||||
|
||||
Physical units: energy, distance, atomic mass, force, force constants
|
||||
vasp : eV, angstrom, AMU, eV/angstrom, eV/angstrom^2
|
||||
|
@ -499,147 +554,150 @@ def get_default_physical_units(interface_mode=None):
|
|||
the 'get_force_constant_conversion_factor' method.
|
||||
|
||||
"""
|
||||
units = {
|
||||
"factor": None,
|
||||
"nac_factor": None,
|
||||
"distance_to_A": None,
|
||||
"force_to_eVperA": None,
|
||||
"force_constants_unit": None,
|
||||
"length_unit": None,
|
||||
}
|
||||
|
||||
from phonopy.units import (Wien2kToTHz, AbinitToTHz, PwscfToTHz, ElkToTHz,
|
||||
SiestaToTHz, VaspToTHz, CP2KToTHz, CrystalToTHz,
|
||||
DftbpToTHz, TurbomoleToTHz, CastepToTHz, FleurToTHz,
|
||||
Hartree, Bohr, Rydberg)
|
||||
|
||||
units = {'factor': None,
|
||||
'nac_factor': None,
|
||||
'distance_to_A': None,
|
||||
'force_to_eVperA': None,
|
||||
'force_constants_unit': None,
|
||||
'length_unit': None}
|
||||
|
||||
if interface_mode is None or interface_mode in ('vasp', 'aims'):
|
||||
units['factor'] = VaspToTHz
|
||||
units['nac_factor'] = Hartree * Bohr
|
||||
units['distance_to_A'] = 1.0
|
||||
units['force_constants_unit'] = 'eV/angstrom^2'
|
||||
units['length_unit'] = 'angstrom'
|
||||
elif interface_mode == 'abinit':
|
||||
units['factor'] = AbinitToTHz
|
||||
units['nac_factor'] = Hartree / Bohr
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_constants_unit'] = 'eV/angstrom.au'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'qe':
|
||||
units['factor'] = PwscfToTHz
|
||||
units['nac_factor'] = 2.0
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_to_eVperA'] = Rydberg / Bohr
|
||||
units['force_constants_unit'] = 'Ry/au^2'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'wien2k':
|
||||
units['factor'] = Wien2kToTHz
|
||||
units['nac_factor'] = 2000.0
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_constants_unit'] = 'mRy/au^2'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'elk':
|
||||
units['factor'] = ElkToTHz
|
||||
units['nac_factor'] = 1.0
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_constants_unit'] = 'hartree/au^2'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'siesta':
|
||||
units['factor'] = SiestaToTHz
|
||||
units['nac_factor'] = Hartree / Bohr
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_constants_unit'] = 'eV/angstrom.au'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'cp2k':
|
||||
units['factor'] = CP2KToTHz
|
||||
units['nac_factor'] = None # not implemented
|
||||
units['distance_to_A'] = 1.0
|
||||
units['force_constants_unit'] = 'hartree/angstrom.au'
|
||||
units['length_unit'] = 'angstrom'
|
||||
elif interface_mode == 'crystal':
|
||||
units['factor'] = CrystalToTHz
|
||||
units['nac_factor'] = Hartree * Bohr
|
||||
units['distance_to_A'] = 1.0
|
||||
units['force_constants_unit'] = 'eV/angstrom^2'
|
||||
units['length_unit'] = 'angstrom'
|
||||
elif interface_mode == 'dftbp':
|
||||
units['factor'] = DftbpToTHz
|
||||
units['nac_factor'] = Hartree * Bohr
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_constants_unit'] = 'hartree/au^2'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'turbomole':
|
||||
units['factor'] = TurbomoleToTHz
|
||||
units['nac_factor'] = 1.0
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_to_eVperA'] = Hartree / Bohr
|
||||
units['force_constants_unit'] = 'hartree/au^2'
|
||||
units['length_unit'] = 'au'
|
||||
elif interface_mode == 'castep':
|
||||
units['factor'] = CastepToTHz
|
||||
units['nac_factor'] = Hartree * Bohr
|
||||
units['distance_to_A'] = 1.0
|
||||
units['force_constants_unit'] = 'eV/angstrom^2'
|
||||
units['length_unit'] = 'angstrom'
|
||||
elif interface_mode == 'fleur':
|
||||
units['factor'] = FleurToTHz
|
||||
units['nac_factor'] = 1.0
|
||||
units['distance_to_A'] = Bohr
|
||||
units['force_constants_unit'] = 'hartree/au^2'
|
||||
units['length_unit'] = 'au'
|
||||
if interface_mode is None or interface_mode in ("vasp", "aims"):
|
||||
units["factor"] = VaspToTHz
|
||||
units["nac_factor"] = Hartree * Bohr
|
||||
units["distance_to_A"] = 1.0
|
||||
units["force_constants_unit"] = "eV/angstrom^2"
|
||||
units["length_unit"] = "angstrom"
|
||||
elif interface_mode == "abinit":
|
||||
units["factor"] = AbinitToTHz
|
||||
units["nac_factor"] = Hartree / Bohr
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_constants_unit"] = "eV/angstrom.au"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "qe":
|
||||
units["factor"] = PwscfToTHz
|
||||
units["nac_factor"] = 2.0
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_to_eVperA"] = Rydberg / Bohr
|
||||
units["force_constants_unit"] = "Ry/au^2"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "wien2k":
|
||||
units["factor"] = Wien2kToTHz
|
||||
units["nac_factor"] = 2000.0
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_constants_unit"] = "mRy/au^2"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "elk":
|
||||
units["factor"] = ElkToTHz
|
||||
units["nac_factor"] = 1.0
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_constants_unit"] = "hartree/au^2"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "siesta":
|
||||
units["factor"] = SiestaToTHz
|
||||
units["nac_factor"] = Hartree / Bohr
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_constants_unit"] = "eV/angstrom.au"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "cp2k":
|
||||
units["factor"] = CP2KToTHz
|
||||
units["nac_factor"] = None # not implemented
|
||||
units["distance_to_A"] = 1.0
|
||||
units["force_constants_unit"] = "hartree/angstrom.au"
|
||||
units["length_unit"] = "angstrom"
|
||||
elif interface_mode == "crystal":
|
||||
units["factor"] = CrystalToTHz
|
||||
units["nac_factor"] = Hartree * Bohr
|
||||
units["distance_to_A"] = 1.0
|
||||
units["force_constants_unit"] = "eV/angstrom^2"
|
||||
units["length_unit"] = "angstrom"
|
||||
elif interface_mode == "dftbp":
|
||||
units["factor"] = DftbpToTHz
|
||||
units["nac_factor"] = Hartree * Bohr
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_constants_unit"] = "hartree/au^2"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "turbomole":
|
||||
units["factor"] = TurbomoleToTHz
|
||||
units["nac_factor"] = 1.0
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_to_eVperA"] = Hartree / Bohr
|
||||
units["force_constants_unit"] = "hartree/au^2"
|
||||
units["length_unit"] = "au"
|
||||
elif interface_mode == "castep":
|
||||
units["factor"] = CastepToTHz
|
||||
units["nac_factor"] = Hartree * Bohr
|
||||
units["distance_to_A"] = 1.0
|
||||
units["force_constants_unit"] = "eV/angstrom^2"
|
||||
units["length_unit"] = "angstrom"
|
||||
elif interface_mode == "fleur":
|
||||
units["factor"] = FleurToTHz
|
||||
units["nac_factor"] = 1.0
|
||||
units["distance_to_A"] = Bohr
|
||||
units["force_constants_unit"] = "hartree/au^2"
|
||||
units["length_unit"] = "au"
|
||||
|
||||
return units
|
||||
|
||||
|
||||
def get_force_sets(interface_mode,
|
||||
def get_force_sets(
|
||||
interface_mode,
|
||||
num_atoms,
|
||||
num_displacements,
|
||||
force_filenames,
|
||||
disp_filename=None,
|
||||
verbose=True):
|
||||
if interface_mode is None or interface_mode == 'vasp':
|
||||
verbose=True,
|
||||
):
|
||||
"""Read calculator output files and parse force sets.
|
||||
|
||||
Note
|
||||
----
|
||||
Wien2k output is treated by ``get_force_sets_wien2k``.
|
||||
|
||||
"""
|
||||
if interface_mode is None or interface_mode == "vasp":
|
||||
from phonopy.interface.vasp import parse_set_of_forces
|
||||
elif interface_mode == 'abinit':
|
||||
elif interface_mode == "abinit":
|
||||
from phonopy.interface.abinit import parse_set_of_forces
|
||||
elif interface_mode == 'qe':
|
||||
elif interface_mode == "qe":
|
||||
from phonopy.interface.qe import parse_set_of_forces
|
||||
elif interface_mode == 'elk':
|
||||
elif interface_mode == "elk":
|
||||
from phonopy.interface.elk import parse_set_of_forces
|
||||
elif interface_mode == 'siesta':
|
||||
elif interface_mode == "siesta":
|
||||
from phonopy.interface.siesta import parse_set_of_forces
|
||||
elif interface_mode == 'cp2k':
|
||||
elif interface_mode == "cp2k":
|
||||
from phonopy.interface.cp2k import parse_set_of_forces
|
||||
elif interface_mode == 'crystal':
|
||||
elif interface_mode == "crystal":
|
||||
from phonopy.interface.crystal import parse_set_of_forces
|
||||
elif interface_mode == 'dftbp':
|
||||
elif interface_mode == "dftbp":
|
||||
from phonopy.interface.dftbp import parse_set_of_forces
|
||||
elif interface_mode == 'turbomole':
|
||||
elif interface_mode == "turbomole":
|
||||
from phonopy.interface.turbomole import parse_set_of_forces
|
||||
elif interface_mode == 'aims':
|
||||
elif interface_mode == "aims":
|
||||
from phonopy.interface.aims import parse_set_of_forces
|
||||
elif interface_mode == 'castep':
|
||||
elif interface_mode == "castep":
|
||||
from phonopy.interface.castep import parse_set_of_forces
|
||||
elif interface_mode == 'fleur':
|
||||
elif interface_mode == "fleur":
|
||||
from phonopy.interface.fleur import parse_set_of_forces
|
||||
|
||||
else:
|
||||
return []
|
||||
|
||||
force_sets = parse_set_of_forces(num_atoms,
|
||||
force_filenames,
|
||||
verbose=verbose)
|
||||
force_sets = parse_set_of_forces(num_atoms, force_filenames, verbose=verbose)
|
||||
|
||||
return force_sets
|
||||
|
||||
|
||||
def get_force_sets_wien2k(num_displacements,
|
||||
def get_force_sets_wien2k(
|
||||
force_filenames,
|
||||
disp_filename,
|
||||
supercell,
|
||||
disp_dataset,
|
||||
wien2k_P1_mode=False,
|
||||
symmetry_tolerance=None,
|
||||
verbose=False):
|
||||
verbose=False,
|
||||
):
|
||||
"""Read Wien2k output files and parse force sets."""
|
||||
from phonopy.interface.wien2k import parse_set_of_forces
|
||||
|
||||
disps, _ = get_displacements_and_forces(disp_dataset)
|
||||
force_sets = parse_set_of_forces(
|
||||
disps,
|
||||
|
@ -647,22 +705,24 @@ def get_force_sets_wien2k(num_displacements,
|
|||
supercell,
|
||||
wien2k_P1_mode=wien2k_P1_mode,
|
||||
symmetry_tolerance=symmetry_tolerance,
|
||||
verbose=verbose)
|
||||
verbose=verbose,
|
||||
)
|
||||
return force_sets
|
||||
|
||||
|
||||
def get_force_constant_conversion_factor(unit, interface_mode):
|
||||
from phonopy.units import Bohr, Rydberg, Hartree
|
||||
|
||||
_unit = unit.replace('Angstrom', 'angstrom') # backward compatibility
|
||||
"""Return unit conversion factor of force constants."""
|
||||
_unit = unit.replace("Angstrom", "angstrom") # for backward compatibility
|
||||
interface_default_units = get_default_physical_units(interface_mode)
|
||||
default_unit = interface_default_units['force_constants_unit']
|
||||
factor_to_eVperA2 = {'eV/angstrom^2': 1,
|
||||
'eV/angstrom.au': 1 / Bohr,
|
||||
'Ry/au^2': Rydberg / Bohr ** 2,
|
||||
'mRy/au^2': Rydberg / Bohr ** 2 / 1000,
|
||||
'hartree/au^2': Hartree / Bohr ** 2,
|
||||
'hartree/angstrom.au': Hartree / Bohr}
|
||||
default_unit = interface_default_units["force_constants_unit"]
|
||||
factor_to_eVperA2 = {
|
||||
"eV/angstrom^2": 1,
|
||||
"eV/angstrom.au": 1 / Bohr,
|
||||
"Ry/au^2": Rydberg / Bohr ** 2,
|
||||
"mRy/au^2": Rydberg / Bohr ** 2 / 1000,
|
||||
"hartree/au^2": Hartree / Bohr ** 2,
|
||||
"hartree/angstrom.au": Hartree / Bohr,
|
||||
}
|
||||
if default_unit not in factor_to_eVperA2:
|
||||
msg = "Force constant conversion for %s unit is not implemented."
|
||||
raise NotImplementedError(msg)
|
||||
|
@ -692,7 +752,7 @@ def _read_phonopy_yaml(filename, phonopy_yaml_cls):
|
|||
|
||||
def _get_cell_filename(filename, phonopy_yaml_cls):
|
||||
cell_filename = None
|
||||
for fname in ((filename, ) + phonopy_yaml_cls.default_filenames):
|
||||
for fname in (filename,) + phonopy_yaml_cls.default_filenames:
|
||||
if fname and os.path.isfile(fname):
|
||||
cell_filename = fname
|
||||
break
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""CASTEP calculator interface."""
|
||||
# Copyright (C) 2014 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,30 +34,31 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from phonopy.interface.vasp import (get_scaled_positions_lines, check_forces,
|
||||
get_drift_forces)
|
||||
from phonopy.units import Bohr
|
||||
from phonopy.cui.settings import fracval
|
||||
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
|
||||
|
||||
# Castep output contains blank lines surrounded by astericks which should be ignored.
|
||||
# This is not implemented in collect_forces() function from file_IO module.
|
||||
# Below is the reimplementation of the collect_forces() function for Castep with
|
||||
# only one new variable skipafterhook. Setting this variable to zero (default value)
|
||||
# is equivalent to original collect_forces() function.
|
||||
def collect_forces_castep(f, num_atom, hook, force_pos, word=None, skipafterhook=0):
|
||||
"""Collect forces from CASTEP output."""
|
||||
for line in f:
|
||||
if hook in line:
|
||||
break
|
||||
if (skipafterhook>0):
|
||||
if skipafterhook > 0:
|
||||
for i in range(skipafterhook):
|
||||
f.readline()
|
||||
|
||||
forces = []
|
||||
for line in f:
|
||||
if line.strip() == '':
|
||||
if line.strip() == "":
|
||||
continue
|
||||
if word is not None:
|
||||
if word not in line:
|
||||
|
@ -77,9 +79,11 @@ def collect_forces_castep(f, num_atom, hook, force_pos, word=None, skipafterhook
|
|||
|
||||
return forces
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
hook = 'Cartesian components (eV/A)'
|
||||
skipafterhook=3
|
||||
"""Parse forces from output files."""
|
||||
hook = "Cartesian components (eV/A)"
|
||||
# skipafterhook = 3
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
|
@ -87,12 +91,14 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
sys.stdout.write("%d. " % (i + 1))
|
||||
|
||||
f = open(filename)
|
||||
castep_forces = collect_forces_castep(f, num_atoms, hook, [3, 4, 5], skipafterhook=3)
|
||||
castep_forces = collect_forces_castep(
|
||||
f, num_atoms, hook, [3, 4, 5], skipafterhook=3
|
||||
)
|
||||
|
||||
if check_forces(castep_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(castep_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
castep_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(castep_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -102,129 +108,160 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
else:
|
||||
return []
|
||||
|
||||
|
||||
def read_castep(filename):
|
||||
"""Read crystal structure."""
|
||||
f_castep = open(filename)
|
||||
castep_in = CastepIn(f_castep.readlines())
|
||||
f_castep.close()
|
||||
tags = castep_in.get_tags()
|
||||
# 1st stage is to create Atoms object ignoring Spin polarization. General case.
|
||||
cell = Atoms(cell=tags['lattice_vectors'],
|
||||
symbols=tags['atomic_species'],
|
||||
scaled_positions=tags['coordinates'])
|
||||
# Analyse spin states and add data to Atoms instance "cell" if ones exist
|
||||
magmoms = tags['magnetic_moments']
|
||||
# 1st stage is to create Atoms object ignoring Spin polarization. General case.
|
||||
cell = Atoms(
|
||||
cell=tags["lattice_vectors"],
|
||||
symbols=tags["atomic_species"],
|
||||
scaled_positions=tags["coordinates"],
|
||||
)
|
||||
# Analyse spin states and add data to Atoms instance "cell" if ones exist
|
||||
magmoms = tags["magnetic_moments"]
|
||||
if magmoms is not None:
|
||||
# Print out symmetry information for magnetic cases
|
||||
# Original code from structure/symmetry.py
|
||||
symmetry = Symmetry(cell, symprec=1e-5)
|
||||
print("CASTEP-interface: Magnetic structure, number of operations without spin: %d" %
|
||||
len(symmetry.get_symmetry_operations()['rotations']))
|
||||
print("CASTEP-interface: Spacegroup without spin: %s" % symmetry.get_international_table())
|
||||
print(
|
||||
"CASTEP-interface: Magnetic structure, "
|
||||
"number of operations without spin: %d"
|
||||
% len(symmetry.get_symmetry_operations()["rotations"])
|
||||
)
|
||||
print(
|
||||
"CASTEP-interface: Spacegroup without spin: %s"
|
||||
% symmetry.get_international_table()
|
||||
)
|
||||
|
||||
cell.set_magnetic_moments(magmoms)
|
||||
symmetry = Symmetry(cell, symprec=1e-5)
|
||||
print("CASTEP-interface: Magnetic structure, number of operations with spin: %d" %
|
||||
len(symmetry.get_symmetry_operations()['rotations']))
|
||||
print(
|
||||
"CASTEP-interface: Magnetic structure, number of operations with spin: %d"
|
||||
% len(symmetry.get_symmetry_operations()["rotations"])
|
||||
)
|
||||
print("")
|
||||
|
||||
return cell
|
||||
|
||||
|
||||
def write_castep(filename, cell):
|
||||
with open(filename, 'w') as f:
|
||||
"""Write cell to file."""
|
||||
with open(filename, "w") as f:
|
||||
f.write(get_castep_structure(cell))
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
|
||||
def write_supercells_with_displacements(
|
||||
supercell, cells_with_displacements, ids, pre_filename="supercell", width=3
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_castep("%s.cell" % pre_filename, supercell)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}.cell".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_castep(filename, cell)
|
||||
|
||||
|
||||
def get_castep_structure(cell):
|
||||
"""Return CASTEP structure in text."""
|
||||
lines = ""
|
||||
lines += '%BLOCK LATTICE_CART\n'
|
||||
lines += "%BLOCK LATTICE_CART\n"
|
||||
lines += ((" % 20.16f" * 3 + "\n") * 3) % tuple(cell.get_cell().ravel())
|
||||
lines += '%ENDBLOCK LATTICE_CART\n\n'
|
||||
lines += '%BLOCK POSITIONS_FRAC\n'
|
||||
lines += "%ENDBLOCK LATTICE_CART\n\n"
|
||||
lines += "%BLOCK POSITIONS_FRAC\n"
|
||||
magmoms = cell.get_magnetic_moments()
|
||||
|
||||
for i in range(len(cell.get_chemical_symbols())):
|
||||
atpos="".join("% 12.10f " % ap for ap in cell.get_scaled_positions()[i])
|
||||
# Spin polarized case
|
||||
atpos = "".join("% 12.10f " % ap for ap in cell.get_scaled_positions()[i])
|
||||
# Spin polarized case
|
||||
if (magmoms is not None) and (magmoms[i] != 0.0):
|
||||
lines += "".join("%2s %s spin=% 5.2f\n" % (cell.get_chemical_symbols()[i], atpos, magmoms[i]))
|
||||
# No spin ordering
|
||||
lines += "".join(
|
||||
"%2s %s spin=% 5.2f\n"
|
||||
% (cell.get_chemical_symbols()[i], atpos, magmoms[i])
|
||||
)
|
||||
# No spin ordering
|
||||
else:
|
||||
lines += "".join("%2s %s\n" % (cell.get_chemical_symbols()[i], atpos))
|
||||
lines += '%ENDBLOCK POSITIONS_FRAC\n\n'
|
||||
lines += "%ENDBLOCK POSITIONS_FRAC\n\n"
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
class CastepIn:
|
||||
"""Class to create CASTEP input file."""
|
||||
|
||||
def __init__(self, lines):
|
||||
self._tags = {'lattice_vectors': None,
|
||||
'atomic_species': None,
|
||||
'coordinates': None,
|
||||
'magnetic_moments': None}
|
||||
"""Init method."""
|
||||
self._tags = {
|
||||
"lattice_vectors": None,
|
||||
"atomic_species": None,
|
||||
"coordinates": None,
|
||||
"magnetic_moments": None,
|
||||
}
|
||||
|
||||
self._values = None
|
||||
self._collect(lines)
|
||||
|
||||
def get_tags(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self, lines):
|
||||
magmoms = []
|
||||
|
||||
numspins = 0
|
||||
# numspins = 0
|
||||
units = 1.0 # Angstrom units
|
||||
lattvecs = []
|
||||
aspecies = []
|
||||
coords = []
|
||||
isSpinPol=0
|
||||
# Read cell parameters
|
||||
isSpinPol = 0
|
||||
# Read cell parameters
|
||||
for line in lines:
|
||||
if ('%BLOCK LATTICE_CART' in line.upper()):
|
||||
indx=lines.index(line)
|
||||
for i in range(indx+1,len(lines)):
|
||||
if ('ENDBLOCK' in lines[i].upper()):
|
||||
if "%BLOCK LATTICE_CART" in line.upper():
|
||||
indx = lines.index(line)
|
||||
for i in range(indx + 1, len(lines)):
|
||||
if "ENDBLOCK" in lines[i].upper():
|
||||
break
|
||||
if ('BOHR' in lines[i].upper()):
|
||||
if "BOHR" in lines[i].upper():
|
||||
# The lattice vector units is Bohr. Convertion needed.
|
||||
units=0.529177211
|
||||
elif (len(lines[i].split())>=3):
|
||||
lattvecs.append([(float(lines[i].split()[j])*units) for j in range(3)])
|
||||
units = 0.529177211
|
||||
elif len(lines[i].split()) >= 3:
|
||||
lattvecs.append(
|
||||
[(float(lines[i].split()[j]) * units) for j in range(3)]
|
||||
)
|
||||
|
||||
# Read atomic positions
|
||||
# Read atomic positions
|
||||
for line in lines:
|
||||
if ('%BLOCK POSITIONS_FRAC' in line.upper()):
|
||||
indx=lines.index(line)
|
||||
for i in range(indx+1,len(lines)):
|
||||
if ('ENDBLOCK' in lines[i].upper()):
|
||||
if "%BLOCK POSITIONS_FRAC" in line.upper():
|
||||
indx = lines.index(line)
|
||||
for i in range(indx + 1, len(lines)):
|
||||
if "ENDBLOCK" in lines[i].upper():
|
||||
break
|
||||
if (len(lines[i].split())>=3):
|
||||
if len(lines[i].split()) >= 3:
|
||||
aspecies.append(lines[i].split()[0])
|
||||
coords.append([float(lines[i].split()[j]) for j in (1,2,3)])
|
||||
coords.append([float(lines[i].split()[j]) for j in (1, 2, 3)])
|
||||
# If there is magmetic spin
|
||||
if (len(lines[i].split())>4) and ('SPIN' in lines[i].upper()):
|
||||
magmoms.append(float(lines[i].upper().split('SPIN')[1].split('=')[1]))
|
||||
isSpinPol=1
|
||||
if (len(lines[i].split()) > 4) and ("SPIN" in lines[i].upper()):
|
||||
magmoms.append(
|
||||
float(lines[i].upper().split("SPIN")[1].split("=")[1])
|
||||
)
|
||||
isSpinPol = 1
|
||||
else:
|
||||
magmoms.append(0.0)
|
||||
# Set magnetic tags if the structure with magnetic order
|
||||
if (isSpinPol>0):
|
||||
self._tags['magnetic_moments'] = magmoms
|
||||
# Set magnetic tags if the structure with magnetic order
|
||||
if isSpinPol > 0:
|
||||
self._tags["magnetic_moments"] = magmoms
|
||||
|
||||
if len(lattvecs) == 3 and len(aspecies) > 0 and len(aspecies) == len(coords):
|
||||
self._tags['lattice_vectors'] = lattvecs
|
||||
self._tags['atomic_species'] = aspecies
|
||||
self._tags['coordinates'] = coords
|
||||
self._tags["lattice_vectors"] = lattvecs
|
||||
self._tags["atomic_species"] = aspecies
|
||||
self._tags["coordinates"] = coords
|
||||
else:
|
||||
print("CASTEP-interface: Error parsing CASTEP .cell file")
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Tests for CIF tools."""
|
||||
# Copyright (C) 2016 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -34,14 +35,18 @@
|
|||
|
||||
from phonopy.structure.cells import get_angles, get_cell_parameters
|
||||
|
||||
|
||||
def write_cif_P1(cell, U_cif=None, filename=None):
|
||||
"""Write P1 symmetry CIF file."""
|
||||
if filename:
|
||||
with open(filename, 'w') as w:
|
||||
with open(filename, "w") as w:
|
||||
w.write(get_cif_P1(cell, U_cif=U_cif))
|
||||
|
||||
|
||||
def get_cif_P1(cell, U_cif=None):
|
||||
a, b, c = get_cell_parameters(cell.get_cell())
|
||||
alpha, beta, gamma = get_angles(cell.get_cell())
|
||||
"""Return P1 symmetry CIF text."""
|
||||
a, b, c = get_cell_parameters(cell.cell)
|
||||
alpha, beta, gamma = get_angles(cell.cell)
|
||||
|
||||
cif = """data_crystal_structure_P1
|
||||
|
||||
|
@ -67,13 +72,26 @@ _atom_site_type_symbol
|
|||
_atom_site_fract_x
|
||||
_atom_site_fract_y
|
||||
_atom_site_fract_z
|
||||
_atom_site_occupancy\n""" % (a, b, c, alpha, beta, gamma, cell.get_volume())
|
||||
_atom_site_occupancy\n""" % (
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
alpha,
|
||||
beta,
|
||||
gamma,
|
||||
cell.get_volume(),
|
||||
)
|
||||
|
||||
symbols = []
|
||||
for s, p in zip(cell.get_chemical_symbols(), cell.get_scaled_positions()):
|
||||
symbols.append(s)
|
||||
cif += ("%-7s%2s %10.5f%10.5f%10.5f 1.00000\n" %
|
||||
(s + "%d" % symbols.count(s), s, p[0], p[1], p[2]))
|
||||
cif += "%-7s%2s %10.5f%10.5f%10.5f 1.00000\n" % (
|
||||
s + "%d" % symbols.count(s),
|
||||
s,
|
||||
p[0],
|
||||
p[1],
|
||||
p[2],
|
||||
)
|
||||
|
||||
if U_cif is not None:
|
||||
|
||||
|
@ -93,7 +111,8 @@ _atom_site_aniso_U_12\n"""
|
|||
symbols.append(s)
|
||||
m = U_cif[i]
|
||||
vals = (m[0, 0], m[1, 1], m[2, 2], m[1, 2], m[0, 2], m[0, 1])
|
||||
cif += ("%6s %10.5f %10.5f %10.5f %10.5f %10.5f %10.5f\n" %
|
||||
((s + "%d" % symbols.count(s),) + vals))
|
||||
cif += "%6s %10.5f %10.5f %10.5f %10.5f %10.5f %10.5f\n" % (
|
||||
(s + "%d" % symbols.count(s),) + vals
|
||||
)
|
||||
|
||||
return cif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""CP2K calculator interface."""
|
||||
# vim: set fileencoding=utf-8 :
|
||||
# Copyright (C) 2017-2019 Tiziano Müller
|
||||
# All rights reserved.
|
||||
|
@ -38,21 +39,25 @@ from __future__ import print_function
|
|||
import sys
|
||||
from fractions import Fraction
|
||||
from os import path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import iter_collect_forces
|
||||
from phonopy.interface.vasp import (check_forces, get_drift_forces)
|
||||
from phonopy.structure.atoms import (PhonopyAtoms, symbol_map)
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms, symbol_map
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
"""Parse forces from output files."""
|
||||
force_sets = []
|
||||
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
|
||||
forces = iter_collect_forces(filename, num_atoms, '# Atom Kind Element', [3, 4, 5])
|
||||
forces = iter_collect_forces(
|
||||
filename, num_atoms, "# Atom Kind Element", [3, 4, 5]
|
||||
)
|
||||
|
||||
if not check_forces(forces, num_atoms, filename, verbose=verbose):
|
||||
return [] # if one file is invalid, the whole thing is broken
|
||||
|
@ -64,6 +69,7 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
|
||||
|
||||
def read_cp2k(filename):
|
||||
"""Read crystal structure."""
|
||||
from cp2k_input_tools.parser import CP2KInputParser
|
||||
|
||||
with open(filename) as fhandle:
|
||||
|
@ -71,112 +77,143 @@ def read_cp2k(filename):
|
|||
tree = parser.parse(fhandle)
|
||||
|
||||
try:
|
||||
subsys = tree['+force_eval'][0]['+subsys']
|
||||
cp2k_cell = subsys['+cell']
|
||||
subsys = tree["+force_eval"][0]["+subsys"]
|
||||
cp2k_cell = subsys["+cell"]
|
||||
except IndexError:
|
||||
raise RuntimeError("could not find a FORCE_EVAL/SUBSYS/CELL section in the given CP2K input file")
|
||||
raise RuntimeError(
|
||||
"could not find a FORCE_EVAL/SUBSYS/CELL section in the given "
|
||||
"CP2K input file"
|
||||
)
|
||||
|
||||
if len(tree['+force_eval']) > 1:
|
||||
raise NotImplementedError("the given CP2K input file contains multiple FORCE_EVAL sections, which is not (yet) supported")
|
||||
if len(tree["+force_eval"]) > 1:
|
||||
raise NotImplementedError(
|
||||
"the given CP2K input file contains multiple FORCE_EVAL sections, "
|
||||
"which is not (yet) supported"
|
||||
)
|
||||
|
||||
# CP2K can get its cell information in two ways:
|
||||
# - A, B, C: cell vectors
|
||||
# - ABC: scaling of cell vectors, ALPHA_BETA_GAMMA: angles between the cell vectors
|
||||
# We'll parse either of them, but only write A, B, C
|
||||
if 'a' in cp2k_cell:
|
||||
if "a" in cp2k_cell:
|
||||
# unit vectors given
|
||||
unit_cell = np.array([
|
||||
cp2k_cell['a'],
|
||||
cp2k_cell['b'],
|
||||
cp2k_cell['c'],
|
||||
])
|
||||
elif 'abc' in cp2k_cell:
|
||||
unit_cell = np.array(
|
||||
[
|
||||
cp2k_cell["a"],
|
||||
cp2k_cell["b"],
|
||||
cp2k_cell["c"],
|
||||
]
|
||||
)
|
||||
elif "abc" in cp2k_cell:
|
||||
# length of unit vectors given
|
||||
if 'alpha_beta_gamma' in cp2k_cell:
|
||||
if "alpha_beta_gamma" in cp2k_cell:
|
||||
# if we also have the angles, construct the cell
|
||||
|
||||
alpha, beta, gamma = cp2k_cell.pop('alpha_beta_gamma')
|
||||
alpha, beta, gamma = cp2k_cell.pop("alpha_beta_gamma")
|
||||
|
||||
cos_alpha = np.cos(alpha)
|
||||
cos_beta = np.cos(beta)
|
||||
cos_gamma = np.cos(gamma)
|
||||
sin_gamma = np.sin(gamma)
|
||||
|
||||
unit_cell = np.array([
|
||||
[1., 0., 0.],
|
||||
[cos_gamma, sin_gamma, 0.],
|
||||
unit_cell = np.array(
|
||||
[
|
||||
[1.0, 0.0, 0.0],
|
||||
[cos_gamma, sin_gamma, 0.0],
|
||||
[
|
||||
cos_beta,
|
||||
(cos_alpha-cos_gamma*cos_beta)/sin_gamma,
|
||||
np.sqrt(1.-cos_beta**2-((cos_alpha-cos_gamma*cos_beta)/sin_gamma)**2),
|
||||
(cos_alpha - cos_gamma * cos_beta) / sin_gamma,
|
||||
np.sqrt(
|
||||
1.0
|
||||
- cos_beta ** 2
|
||||
- ((cos_alpha - cos_gamma * cos_beta) / sin_gamma) ** 2
|
||||
),
|
||||
],
|
||||
])
|
||||
]
|
||||
)
|
||||
else:
|
||||
unit_cell = np.eye(3)
|
||||
|
||||
a, b, c = cp2k_cell.pop('abc') # remove them from the tree since we pass it along
|
||||
a, b, c = cp2k_cell.pop(
|
||||
"abc"
|
||||
) # remove them from the tree since we pass it along
|
||||
|
||||
unit_cell[0,:] *= a
|
||||
unit_cell[1,:] *= b
|
||||
unit_cell[2,:] *= c
|
||||
unit_cell[0, :] *= a
|
||||
unit_cell[1, :] *= b
|
||||
unit_cell[2, :] *= c
|
||||
|
||||
if '+cell_ref' in cp2k_cell:
|
||||
if "+cell_ref" in cp2k_cell:
|
||||
print("WARNING: the &CELL_REF section must be manually adjusted")
|
||||
|
||||
cp2k_coord = subsys['+coord']
|
||||
cp2k_coord = subsys["+coord"]
|
||||
|
||||
numbers = []
|
||||
positions = []
|
||||
|
||||
for coordline in cp2k_coord['*']:
|
||||
for coordline in cp2k_coord["*"]:
|
||||
# coordinates are a series of strings according to the CP2K schema
|
||||
fields = coordline.split()
|
||||
numbers += [symbol_map[fields[0]]]
|
||||
# positions can also be fractions
|
||||
positions += [[float(Fraction(f)) for f in fields[1:4]]]
|
||||
|
||||
if cp2k_coord.get('scaled', False): # the keyword can be unavailable, true or false
|
||||
return (PhonopyAtoms(numbers=numbers, cell=unit_cell, scaled_positions=positions), tree)
|
||||
if cp2k_coord.get("scaled", False): # the keyword can be unavailable, true or false
|
||||
return (
|
||||
PhonopyAtoms(numbers=numbers, cell=unit_cell, scaled_positions=positions),
|
||||
tree,
|
||||
)
|
||||
else:
|
||||
return (PhonopyAtoms(numbers=numbers, cell=unit_cell, positions=positions), tree)
|
||||
return (
|
||||
PhonopyAtoms(numbers=numbers, cell=unit_cell, positions=positions),
|
||||
tree,
|
||||
)
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
def write_supercells_with_displacements(
|
||||
supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
optional_structure_info,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
|
||||
width=3,
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
orig_fname, tree = optional_structure_info
|
||||
|
||||
fbase, fext = path.splitext(orig_fname)
|
||||
pbase = tree["+global"]["project_name"]
|
||||
|
||||
supercell_ref_name = "{}-supercell{}".format(fbase, fext)
|
||||
with open(supercell_ref_name, 'w') as fhandle:
|
||||
fhandle.write("""\
|
||||
with open(supercell_ref_name, "w") as fhandle:
|
||||
fhandle.write(
|
||||
"""\
|
||||
# Generated by Phonopy, based on {fname}
|
||||
# Original configuration with the generated supercell for comparison
|
||||
""".format(fname=orig_fname))
|
||||
""".format(
|
||||
fname=orig_fname
|
||||
)
|
||||
)
|
||||
write_cp2k(fhandle, "{}-{}".format(pbase, pre_filename), supercell, tree)
|
||||
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
suffix = "{pre_filename}-{0:0{width}}".format(
|
||||
i,
|
||||
pre_filename=pre_filename,
|
||||
width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
|
||||
with open("{}-{}{}".format(fbase, suffix, fext), 'w') as fhandle:
|
||||
fhandle.write("""\
|
||||
with open("{}-{}{}".format(fbase, suffix, fext), "w") as fhandle:
|
||||
fhandle.write(
|
||||
"""\
|
||||
# Generated by Phonopy, based on {fname}
|
||||
# Merged configuration with displacements
|
||||
""".format(fname=orig_fname))
|
||||
""".format(
|
||||
fname=orig_fname
|
||||
)
|
||||
)
|
||||
write_cp2k(fhandle, "{}-{}".format(pbase, suffix), cell, tree)
|
||||
|
||||
|
||||
def write_cp2k_by_filename(filename, cell, tree, header=None):
|
||||
"""Simple wrapper of write_cp2k to write an arbitrary unit cell
|
||||
"""Wrap write_cp2k to write an arbitrary unit cell.
|
||||
|
||||
Note
|
||||
----
|
||||
|
@ -186,7 +223,6 @@ def write_cp2k_by_filename(filename, cell, tree, header=None):
|
|||
Please rewrite if somebody who knows well about cp2k input.
|
||||
|
||||
"""
|
||||
|
||||
fbase, fext = path.splitext(filename)
|
||||
pbase = tree["+global"]["project_name"]
|
||||
project_name = "{}-{}".format(pbase, fbase)
|
||||
|
@ -196,55 +232,65 @@ def write_cp2k_by_filename(filename, cell, tree, header=None):
|
|||
else:
|
||||
_header = header
|
||||
|
||||
with open(filename, 'w') as w:
|
||||
with open(filename, "w") as w:
|
||||
w.write(_header)
|
||||
write_cp2k(w, project_name, cell, tree)
|
||||
|
||||
|
||||
def write_cp2k(fhandle, project_name, atoms, tree):
|
||||
"""Merge the new the atoms structure with the configuration tree to a new CP2K input file
|
||||
"""Merge the new the atoms structure with the configuration tree to a new CP2K input file.
|
||||
|
||||
:param fhandle: open file handle to which the routine will write to
|
||||
:param project_name: the project name to use (CP2K uses that as prefix for generated files)i
|
||||
:param atoms: the Atoms objects to use
|
||||
:param tree: the configuration tree as returned from CP2KInputParser
|
||||
"""
|
||||
|
||||
from cp2k_input_tools.generator import CP2KInputGenerator
|
||||
|
||||
generator = CP2KInputGenerator()
|
||||
|
||||
tree['+global']['run_type'] = 'ENERGY_FORCE'
|
||||
tree['+global']['project_name'] = project_name
|
||||
tree["+global"]["run_type"] = "ENERGY_FORCE"
|
||||
tree["+global"]["project_name"] = project_name
|
||||
|
||||
force_eval = tree['+force_eval'][0]
|
||||
subsys = force_eval['+subsys']
|
||||
force_eval = tree["+force_eval"][0]
|
||||
subsys = force_eval["+subsys"]
|
||||
|
||||
# if the original input contained scaled positions, continue with scaled positions
|
||||
if subsys['+coord'].get('scaled', False):
|
||||
if subsys["+coord"].get("scaled", False):
|
||||
cp2k_coord = {
|
||||
'scaled': True,
|
||||
'*': ["{sym} {x} {y} {z}".format(sym=sym, x=coord[0], y=coord[1], z=coord[2])
|
||||
for sym, coord in zip(atoms.get_chemical_symbols(), atoms.get_scaled_positions())],
|
||||
"scaled": True,
|
||||
"*": [
|
||||
"{sym} {x} {y} {z}".format(sym=sym, x=coord[0], y=coord[1], z=coord[2])
|
||||
for sym, coord in zip(
|
||||
atoms.get_chemical_symbols(), atoms.get_scaled_positions()
|
||||
)
|
||||
],
|
||||
}
|
||||
# ... otherwise use absolute positions
|
||||
else:
|
||||
cp2k_coord = {
|
||||
'*': ["{sym} {x} {y} {z}".format(sym=sym, x=coord[0], y=coord[1], z=coord[2])
|
||||
for sym, coord in zip(atoms.get_chemical_symbols(), atoms.get_positions())],
|
||||
"*": [
|
||||
"{sym} {x} {y} {z}".format(sym=sym, x=coord[0], y=coord[1], z=coord[2])
|
||||
for sym, coord in zip(
|
||||
atoms.get_chemical_symbols(), atoms.get_positions()
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
subsys['+cell']['a'] = list(atoms.get_cell()[0])
|
||||
subsys['+cell']['b'] = list(atoms.get_cell()[1])
|
||||
subsys['+cell']['c'] = list(atoms.get_cell()[2])
|
||||
subsys['+cell']['periodic'] = 'XYZ' # anything else does not make much sense
|
||||
subsys["+cell"]["a"] = list(atoms.get_cell()[0])
|
||||
subsys["+cell"]["b"] = list(atoms.get_cell()[1])
|
||||
subsys["+cell"]["c"] = list(atoms.get_cell()[2])
|
||||
subsys["+cell"]["periodic"] = "XYZ" # anything else does not make much sense
|
||||
|
||||
subsys['+coord'] = cp2k_coord # overwriting the coordinates
|
||||
subsys["+coord"] = cp2k_coord # overwriting the coordinates
|
||||
|
||||
if not '+print' in force_eval:
|
||||
force_eval['+print'] = {}
|
||||
if not '+forces' in force_eval['+print']:
|
||||
force_eval['+print']['+forces'] = {}
|
||||
force_eval['+print']['+forces']['filename'] = "forces" # uses the project name as base with 'forces' as suffix
|
||||
if "+print" not in force_eval:
|
||||
force_eval["+print"] = {}
|
||||
if "+forces" not in force_eval["+print"]:
|
||||
force_eval["+print"]["+forces"] = {}
|
||||
force_eval["+print"]["+forces"][
|
||||
"filename"
|
||||
] = "forces" # uses the project name as base with 'forces' as suffix
|
||||
|
||||
for line in generator.line_iter(tree):
|
||||
fhandle.write("{line}\n".format(line=line))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""CRYSTAL calculator interface."""
|
||||
# Copyright (C) 2016 Antti J. Karttunen (antti.j.karttunen@iki.fi)
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,39 +34,37 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import iter_collect_forces
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.atoms import symbol_map
|
||||
from phonopy.structure.cells import get_cell_parameters, get_angles
|
||||
from phonopy.units import Hartree, Bohr
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
from phonopy.units import Bohr, Hartree
|
||||
|
||||
def parse_set_of_forces(num_atoms,
|
||||
forces_filenames,
|
||||
verbose=True):
|
||||
hook = 'ATOM X Y Z'
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
"""Parse forces from output files."""
|
||||
hook = "ATOM X Y Z"
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
crystal_forces = iter_collect_forces(filename,
|
||||
num_atoms,
|
||||
hook,
|
||||
[2, 3, 4])
|
||||
crystal_forces = iter_collect_forces(filename, num_atoms, hook, [2, 3, 4])
|
||||
if check_forces(crystal_forces, num_atoms, filename, verbose=verbose):
|
||||
is_parsed = True
|
||||
drift_force = get_drift_forces(crystal_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
crystal_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
# Convert forces Hartree / Bohr -> eV / Angstrom
|
||||
# This avoids confusion with the units. CRYSTAL uses Angstroms for
|
||||
# coordinates, but Hartree / Bohr for forces. This would lead in mixed
|
||||
# units hartree / (Angstrom * Bohr) for force constants, requiring
|
||||
# additional tweaks for unit conversions in other parts of the code
|
||||
force_sets.append(np.multiply(np.array(crystal_forces) - drift_force, Hartree / Bohr))
|
||||
force_sets.append(
|
||||
np.multiply(np.array(crystal_forces) - drift_force, Hartree / Bohr)
|
||||
)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
||||
|
@ -74,37 +73,52 @@ def parse_set_of_forces(num_atoms,
|
|||
else:
|
||||
return []
|
||||
|
||||
|
||||
def read_crystal(filename):
|
||||
"""Read crystal structure."""
|
||||
f_crystal = open(filename)
|
||||
crystal_in = CrystalIn(f_crystal.readlines())
|
||||
f_crystal.close()
|
||||
tags = crystal_in.get_tags()
|
||||
|
||||
cell = Atoms(
|
||||
cell=tags["lattice_vectors"],
|
||||
symbols=tags["atomic_species"],
|
||||
scaled_positions=tags["coordinates"],
|
||||
)
|
||||
|
||||
cell = Atoms(cell=tags['lattice_vectors'],
|
||||
symbols=tags['atomic_species'],
|
||||
scaled_positions=tags['coordinates'])
|
||||
|
||||
magmoms = tags['magnetic_moments']
|
||||
magmoms = tags["magnetic_moments"]
|
||||
if magmoms is not None:
|
||||
# Print out symmetry information for magnetic cases
|
||||
# Original code from structure/symmetry.py
|
||||
symmetry = Symmetry(cell, symprec=1e-5)
|
||||
print("CRYSTAL-interface: Magnetic structure, number of operations without spin: %d" %
|
||||
len(symmetry.get_symmetry_operations()['rotations']))
|
||||
print("CRYSTAL-interface: Spacegroup without spin: %s" % symmetry.get_international_table())
|
||||
print(
|
||||
"CRYSTAL-interface: Magnetic structure, "
|
||||
"number of operations without spin: %d"
|
||||
% len(symmetry.get_symmetry_operations()["rotations"])
|
||||
)
|
||||
print(
|
||||
"CRYSTAL-interface: Spacegroup without spin: %s"
|
||||
% symmetry.get_international_table()
|
||||
)
|
||||
|
||||
cell.set_magnetic_moments(magmoms)
|
||||
symmetry = Symmetry(cell, symprec=1e-5)
|
||||
print("CRYSTAL-interface: Magnetic structure, number of operations with spin: %d" %
|
||||
len(symmetry.get_symmetry_operations()['rotations']))
|
||||
print(
|
||||
"CRYSTAL-interface: Magnetic structure, number of operations with spin: %d"
|
||||
% len(symmetry.get_symmetry_operations()["rotations"])
|
||||
)
|
||||
print("")
|
||||
|
||||
return cell, tags['conv_numbers']
|
||||
return cell, tags["conv_numbers"]
|
||||
|
||||
def write_crystal(filename, cell, conv_numbers, template_file="TEMPLATE", write_symmetry=False):
|
||||
|
||||
def write_crystal(
|
||||
filename, cell, conv_numbers, template_file="TEMPLATE", write_symmetry=False
|
||||
):
|
||||
"""Write cell to file."""
|
||||
# Write geometry in EXTERNAL file (fort.34)
|
||||
f_ext = open(filename + '.ext', 'w')
|
||||
f_ext = open(filename + ".ext", "w")
|
||||
f_ext.write(get_crystal_structure(cell, conv_numbers, write_symmetry))
|
||||
f_ext.close()
|
||||
|
||||
|
@ -130,26 +144,30 @@ def write_crystal(filename, cell, conv_numbers, template_file="TEMPLATE", write_
|
|||
for i in range(0, len(magmoms)):
|
||||
if magmoms[i] != 0:
|
||||
N_spins += 1
|
||||
atomspins += ("%d %d " % (i + 1, magmoms[i]))
|
||||
atomspins += "%d %d " % (i + 1, magmoms[i])
|
||||
lines += "ATOMSPIN\n"
|
||||
lines += ("%d\n" % N_spins)
|
||||
lines += "%d\n" % N_spins
|
||||
lines += atomspins + "\n"
|
||||
lines += "GRADCAL\n"
|
||||
lines += "END\n"
|
||||
|
||||
# Write the input file
|
||||
f_inputfile = open(filename + '.d12', 'w')
|
||||
f_inputfile = open(filename + ".d12", "w")
|
||||
f_inputfile.writelines(lines)
|
||||
f_inputfile.close()
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
|
||||
def write_supercells_with_displacements(
|
||||
supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
conv_numbers,
|
||||
num_unitcells_in_supercell,
|
||||
pre_filename="supercell",
|
||||
width=3,
|
||||
template_file="TEMPLATE"):
|
||||
template_file="TEMPLATE",
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
convnum_super = []
|
||||
for i in conv_numbers:
|
||||
for j in range(num_unitcells_in_supercell):
|
||||
|
@ -158,27 +176,36 @@ def write_supercells_with_displacements(supercell,
|
|||
# Currently, symmetry is not used by default
|
||||
# It can be turned on by creating a file called CRY_SYM
|
||||
try:
|
||||
f = open('CRY_SYM')
|
||||
f = open("CRY_SYM")
|
||||
use_symmetry = True
|
||||
f.close()
|
||||
except IOError:
|
||||
use_symmetry = False
|
||||
|
||||
if use_symmetry:
|
||||
print("CRYSTAL-interface: WARNING: Symmetry enabled in EXTERNAL files.\n" +
|
||||
" Check the supercells very carefully, some spacegroups do not work (e.g. R-3m)\n" +
|
||||
" Non-displaced supercell is always written without symmetry")
|
||||
print(
|
||||
"CRYSTAL-interface: WARNING: Symmetry enabled in EXTERNAL files.\n"
|
||||
" Check the supercells very carefully, some spacegroups do not work "
|
||||
"(e.g. R-3m)\n"
|
||||
" Non-displaced supercell is always written without symmetry"
|
||||
)
|
||||
|
||||
write_crystal(pre_filename, supercell, convnum_super, template_file, write_symmetry=False)
|
||||
write_crystal(
|
||||
pre_filename, supercell, convnum_super, template_file, write_symmetry=False
|
||||
)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
write_crystal(filename, cell, convnum_super, template_file, write_symmetry=use_symmetry)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_crystal(
|
||||
filename, cell, convnum_super, template_file, write_symmetry=use_symmetry
|
||||
)
|
||||
|
||||
|
||||
def get_crystal_structure(cell, conv_numbers, write_symmetry=False):
|
||||
"""Return CRYSTAL structure in text."""
|
||||
lattice = cell.get_cell()
|
||||
positions = cell.get_positions()
|
||||
numbers = cell.get_atomic_numbers()
|
||||
|
||||
# Create and EXTERNAL file (fort.34)
|
||||
# Dimensionality, centring, crystal type
|
||||
|
@ -190,8 +217,8 @@ def get_crystal_structure(cell, conv_numbers, write_symmetry=False):
|
|||
# Symmetry operators
|
||||
if write_symmetry:
|
||||
symmetry = Symmetry(cell, symprec=1e-5)
|
||||
rotations = symmetry.get_symmetry_operations()['rotations']
|
||||
translations = symmetry.get_symmetry_operations()['translations']
|
||||
rotations = symmetry.get_symmetry_operations()["rotations"]
|
||||
translations = symmetry.get_symmetry_operations()["translations"]
|
||||
N_symmops = 0
|
||||
symmlines = ""
|
||||
for i in range(0, len(rotations)):
|
||||
|
@ -200,7 +227,7 @@ def get_crystal_structure(cell, conv_numbers, write_symmetry=False):
|
|||
symmlines += (" %5.2f" * 3 + "\n") % tuple(rotations[i][j])
|
||||
symmlines += (" %5.2f" * 3 + "\n") % tuple(translations[i])
|
||||
|
||||
lines += ("%d\n" % N_symmops)
|
||||
lines += "%d\n" % N_symmops
|
||||
lines += symmlines
|
||||
|
||||
else:
|
||||
|
@ -214,80 +241,93 @@ def get_crystal_structure(cell, conv_numbers, write_symmetry=False):
|
|||
lines += ("%d\n") % len(positions)
|
||||
# Conventional atomic number and cartesian coordinates of the atoms
|
||||
for i, pos in zip(conv_numbers, positions):
|
||||
lines += (" %d " + "%16.12f"*3 + "\n") % (i, pos[0], pos[1], pos[2])
|
||||
lines += (" %d " + "%16.12f" * 3 + "\n") % (i, pos[0], pos[1], pos[2])
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
class CrystalIn:
|
||||
"""Class to create CRYSTAL input file."""
|
||||
|
||||
def __init__(self, lines):
|
||||
# conv_numbers = CRYSTAL conventional atomic number mapping: 'Ge' -> 32 or 'Ge' -> 232
|
||||
self._tags = {'lattice_vectors': None,
|
||||
'atomic_species': None,
|
||||
'coordinates': None,
|
||||
'magnetic_moments': None,
|
||||
'conv_numbers': None}
|
||||
"""Init method."""
|
||||
# conv_numbers = CRYSTAL conventional atomic number mapping:
|
||||
# 'Ge' -> 32 or 'Ge' -> 232
|
||||
self._tags = {
|
||||
"lattice_vectors": None,
|
||||
"atomic_species": None,
|
||||
"coordinates": None,
|
||||
"magnetic_moments": None,
|
||||
"conv_numbers": None,
|
||||
}
|
||||
|
||||
self._values = None
|
||||
self._collect(lines)
|
||||
|
||||
def get_tags(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self, lines):
|
||||
# Reads a CRYSTAL output file (lattice vectors, conventional atomic numbers,
|
||||
# fractional atomic positions).
|
||||
# - For optimization outputs, the final geometry in the file is read.
|
||||
# - Dielectric tensor and effective Born charges can be read with script phonopy-crystal-born
|
||||
# - Dielectric tensor and effective Born charges can be read with script
|
||||
# phonopy-crystal-born
|
||||
# - If ATOMSPIN keyword is present, magnetic moments are read from it
|
||||
magmoms = []
|
||||
atomspins = []
|
||||
numspins = 0
|
||||
l = 0
|
||||
while l < len(lines):
|
||||
line = lines[l]
|
||||
if 'PRIMITIVE CELL - CENTRING CODE' in line:
|
||||
ll = 0
|
||||
while ll < len(lines):
|
||||
line = lines[ll]
|
||||
if "PRIMITIVE CELL - CENTRING CODE" in line:
|
||||
aspecies = []
|
||||
coords = []
|
||||
convnum = []
|
||||
l += 4
|
||||
ll += 4
|
||||
# ATOMS IN THE ASYMMETRIC UNIT 2 - ATOMS IN THE UNIT CELL: 6
|
||||
N_asym_atoms = int(lines[l].split()[5])
|
||||
N_atoms = int(lines[l].split()[12])
|
||||
l += 3
|
||||
# 1 T 22 TI 4.721218104494E-21 3.307446203077E-21 1.413771901417E-21
|
||||
N_atoms = int(lines[ll].split()[12])
|
||||
ll += 3
|
||||
# 1 T 22 TI 4.721218104494E-21 3.307446203077E-21 1.413771901417E-21 # noqa E501
|
||||
for atom in range(0, N_atoms):
|
||||
atomdata = lines[l].split()
|
||||
atomdata = lines[ll].split()
|
||||
aspecies.append(atomdata[3].capitalize())
|
||||
coords.append([float(x) for x in atomdata[4:7]])
|
||||
convnum.append(int(atomdata[2]))
|
||||
l += 1
|
||||
elif 'DIRECT LATTICE VECTORS CARTESIAN COMPONENTS' in line:
|
||||
ll += 1
|
||||
elif "DIRECT LATTICE VECTORS CARTESIAN COMPONENTS" in line:
|
||||
lattvecs = []
|
||||
l += 2
|
||||
ll += 2
|
||||
# X Y Z
|
||||
for lattvec in range(1, 4):
|
||||
lattvecs.append([float(x) for x in lines[l].split()])
|
||||
l += 1
|
||||
elif 'ATOMSPIN' in line:
|
||||
lattvecs.append([float(x) for x in lines[ll].split()])
|
||||
ll += 1
|
||||
elif "ATOMSPIN" in line:
|
||||
# Read ATOMSPIN, and save the magnetic moments for later parsing
|
||||
# (not all necessary information is available at this point)
|
||||
# All spins must be entered on one line!
|
||||
# ATOMSPIN
|
||||
# 8
|
||||
# 1 1 2 1 3 -1 4 -1 5 1 6 1 7 -1 8 -1
|
||||
l += 1
|
||||
numspins = int(lines[l])
|
||||
l += 1
|
||||
atomspins = [int(x) for x in lines[l].split()]
|
||||
l += 1
|
||||
ll += 1
|
||||
numspins = int(lines[ll])
|
||||
ll += 1
|
||||
atomspins = [int(x) for x in lines[ll].split()]
|
||||
ll += 1
|
||||
|
||||
l += 1 # while l < len(lines)
|
||||
ll += 1 # while l < len(lines)
|
||||
|
||||
if len(lattvecs) == 3 and len(aspecies) > 0 and len(aspecies) == len(coords) and len(aspecies) == len(convnum):
|
||||
self._tags['lattice_vectors'] = lattvecs
|
||||
self._tags['atomic_species'] = aspecies
|
||||
self._tags['coordinates'] = coords
|
||||
self._tags['conv_numbers'] = convnum
|
||||
if (
|
||||
len(lattvecs) == 3
|
||||
and len(aspecies) > 0
|
||||
and len(aspecies) == len(coords)
|
||||
and len(aspecies) == len(convnum)
|
||||
):
|
||||
self._tags["lattice_vectors"] = lattvecs
|
||||
self._tags["atomic_species"] = aspecies
|
||||
self._tags["coordinates"] = coords
|
||||
self._tags["conv_numbers"] = convnum
|
||||
else:
|
||||
print("CRYSTAL-interface: Error parsing CRYSTAL output file")
|
||||
|
||||
|
@ -297,22 +337,26 @@ class CrystalIn:
|
|||
magmoms = [0] * N_atoms
|
||||
if numspins * 2 == len(atomspins):
|
||||
for i in range(0, numspins):
|
||||
atomnum = atomspins[i*2] - 1
|
||||
magmom = atomspins[i*2 + 1]
|
||||
atomnum = atomspins[i * 2] - 1
|
||||
magmom = atomspins[i * 2 + 1]
|
||||
magmoms[atomnum] = magmom
|
||||
|
||||
self._tags['magnetic_moments'] = magmoms
|
||||
print("CRYSTAL-interface: Following magnetic moments have been read from ATOMSPIN entry:")
|
||||
self._tags["magnetic_moments"] = magmoms
|
||||
print(
|
||||
"CRYSTAL-interface: Following magnetic moments "
|
||||
"have been read from ATOMSPIN entry:"
|
||||
)
|
||||
print(magmoms)
|
||||
else:
|
||||
print("CRYSTAL-interface: Invalid ATOMSPIN entry, magnetic moments have not been set")
|
||||
print(
|
||||
"CRYSTAL-interface: Invalid ATOMSPIN entry, "
|
||||
"magnetic moments have not been set"
|
||||
)
|
||||
else:
|
||||
print("")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
if __name__ == "__main__":
|
||||
cell, conv_numbers = read_crystal(sys.argv[1])
|
||||
symmetry = Symmetry(cell)
|
||||
print("# %s" % symmetry.get_international_table())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""DFTB+ calculator interface."""
|
||||
# Copyright (C) 2015 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,16 +34,18 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import collect_forces
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import Atoms
|
||||
from phonopy.units import dftbpToBohr
|
||||
from phonopy.file_IO import collect_forces
|
||||
from phonopy.interface.vasp import (get_scaled_positions_lines, check_forces,
|
||||
get_drift_forces)
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
hook = 'forces :real:2:'
|
||||
"""Parse forces from output files."""
|
||||
hook = "forces :real:2:"
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
|
@ -52,9 +55,9 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
f = open(filename)
|
||||
dftbp_forces = collect_forces(f, num_atoms, hook, [0, 1, 2])
|
||||
if check_forces(dftbp_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(dftbp_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
dftbp_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(dftbp_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -69,30 +72,33 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
# read dftbp-files
|
||||
#
|
||||
|
||||
def read_dftbp(filename):
|
||||
""" Reads DFTB+ structure files in gen format.
|
||||
|
||||
Args:
|
||||
def read_dftbp(filename):
|
||||
"""Read DFTB+ structure files in gen format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename: name of the gen-file to be read
|
||||
|
||||
Returns:
|
||||
Returns
|
||||
-------
|
||||
atoms: an object of the phonopy.Atoms class, representing the structure
|
||||
found in filename
|
||||
"""
|
||||
|
||||
infile = open(filename, 'r')
|
||||
"""
|
||||
infile = open(filename, "r")
|
||||
|
||||
lines = infile.readlines()
|
||||
|
||||
# remove any comments
|
||||
for ss in lines:
|
||||
if ss.strip().startswith('#'):
|
||||
if ss.strip().startswith("#"):
|
||||
lines.remove(ss)
|
||||
|
||||
natoms = int(lines[0].split()[0])
|
||||
symbols = lines[1].split()
|
||||
|
||||
if (lines[0].split()[1].lower() == 'f'):
|
||||
if lines[0].split()[1].lower() == "f":
|
||||
is_scaled = True
|
||||
scale_pos = 1
|
||||
scale_latvecs = dftbpToBohr
|
||||
|
@ -105,49 +111,49 @@ def read_dftbp(filename):
|
|||
positions = []
|
||||
expaned_symbols = []
|
||||
|
||||
for ii in range(2, natoms+2):
|
||||
for ii in range(2, natoms + 2):
|
||||
lsplit = lines[ii].split()
|
||||
|
||||
expaned_symbols.append(symbols[int(lsplit[1]) - 1])
|
||||
positions.append([float(ss)*scale_pos for ss in lsplit[2:5]])
|
||||
positions.append([float(ss) * scale_pos for ss in lsplit[2:5]])
|
||||
|
||||
# origin is ignored, may be used in future
|
||||
origin = [float(ss) for ss in lines[natoms+2].split()]
|
||||
# origin = [float(ss) for ss in lines[natoms + 2].split()]
|
||||
|
||||
# assign coords of unitcell
|
||||
cell = []
|
||||
|
||||
for ii in range(natoms+3, natoms+6):
|
||||
for ii in range(natoms + 3, natoms + 6):
|
||||
lsplit = lines[ii].split()
|
||||
|
||||
cell.append([float(ss)*scale_latvecs for ss in lsplit[:3]])
|
||||
cell.append([float(ss) * scale_latvecs for ss in lsplit[:3]])
|
||||
cell = np.array(cell)
|
||||
|
||||
if is_scaled:
|
||||
atoms = Atoms(symbols=expaned_symbols,
|
||||
cell=cell,
|
||||
scaled_positions=positions)
|
||||
atoms = Atoms(symbols=expaned_symbols, cell=cell, scaled_positions=positions)
|
||||
else:
|
||||
atoms = Atoms(symbols=expaned_symbols,
|
||||
cell=cell,
|
||||
positions=positions)
|
||||
atoms = Atoms(symbols=expaned_symbols, cell=cell, positions=positions)
|
||||
|
||||
return atoms
|
||||
|
||||
|
||||
#
|
||||
# write dftb+ .gen-file
|
||||
#
|
||||
def get_reduced_symbols(symbols):
|
||||
"""Reduces expanded list of symbols.
|
||||
"""Reduce expanded list of symbols.
|
||||
|
||||
Args:
|
||||
symbols: list containing any chemical symbols as often as
|
||||
the atom appears in the structure
|
||||
Parameters
|
||||
----------
|
||||
symbols:
|
||||
list containing any chemical symbols as often as
|
||||
the atom appears in the structure.
|
||||
|
||||
Returns
|
||||
-------
|
||||
reduced_symbols: any symbols appears only once.
|
||||
|
||||
Returns:
|
||||
reduced_symbols: any symbols appears only once
|
||||
"""
|
||||
|
||||
reduced_symbols = []
|
||||
|
||||
for ss in symbols:
|
||||
|
@ -156,12 +162,15 @@ def get_reduced_symbols(symbols):
|
|||
|
||||
return reduced_symbols
|
||||
|
||||
def write_dftbp(filename, atoms):
|
||||
"""Writes DFTB+ readable, gen-formatted structure files
|
||||
|
||||
Args:
|
||||
def write_dftbp(filename, atoms):
|
||||
"""Write DFTB+ readable, gen-formatted structure files.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename: name of the gen-file to be written
|
||||
atoms: object containing information about structure
|
||||
|
||||
"""
|
||||
scale_pos = dftbpToBohr
|
||||
|
||||
|
@ -170,56 +179,59 @@ def write_dftbp(filename, atoms):
|
|||
# 1. line, use absolute positions
|
||||
natoms = atoms.get_number_of_atoms()
|
||||
lines += str(natoms)
|
||||
lines += ' S \n'
|
||||
lines += " S \n"
|
||||
|
||||
# 2. line
|
||||
expaned_symbols = atoms.get_chemical_symbols()
|
||||
symbols = get_reduced_symbols(expaned_symbols)
|
||||
lines += ' '.join(symbols) + '\n'
|
||||
lines += " ".join(symbols) + "\n"
|
||||
|
||||
atom_numbers = []
|
||||
for ss in expaned_symbols:
|
||||
atom_numbers.append(symbols.index(ss) + 1)
|
||||
|
||||
positions = atoms.get_positions()/scale_pos
|
||||
positions = atoms.get_positions() / scale_pos
|
||||
|
||||
for ii in range(natoms):
|
||||
pos = positions[ii]
|
||||
pos_str = "{:3d} {:3d} {:20.15f} {:20.15f} {:20.15f}\n".format(
|
||||
ii + 1, atom_numbers[ii], pos[0], pos[1], pos[2])
|
||||
ii + 1, atom_numbers[ii], pos[0], pos[1], pos[2]
|
||||
)
|
||||
lines += pos_str
|
||||
|
||||
# origin arbitrary
|
||||
lines +='0.0 0.0 0.0\n'
|
||||
lines += "0.0 0.0 0.0\n"
|
||||
|
||||
cell = atoms.get_cell()/scale_pos
|
||||
cell = atoms.get_cell() / scale_pos
|
||||
|
||||
for ii in range(3):
|
||||
cell_str = "{:20.15f} {:20.15f} {:20.15f}\n".format(
|
||||
cell[ii][0], cell[ii][1], cell[ii][2])
|
||||
cell[ii][0], cell[ii][1], cell[ii][2]
|
||||
)
|
||||
lines += cell_str
|
||||
|
||||
outfile = open(filename, 'w')
|
||||
outfile = open(filename, "w")
|
||||
outfile.write(lines)
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
cells_with_disps,
|
||||
ids,
|
||||
pre_filename="geo.gen",
|
||||
width=3):
|
||||
"""Writes perfect supercell and supercells with displacements
|
||||
|
||||
Args:
|
||||
def write_supercells_with_displacements(
|
||||
supercell, cells_with_disps, ids, pre_filename="geo.gen", width=3
|
||||
):
|
||||
"""Write perfect supercell and supercells with displacements.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
supercell: perfect supercell
|
||||
cells_with_disps: supercells with displaced atoms
|
||||
filename: root-filename
|
||||
"""
|
||||
|
||||
"""
|
||||
# original cell
|
||||
write_dftbp(pre_filename + "S", supercell)
|
||||
|
||||
# displaced cells
|
||||
for i, cell in zip(ids, cells_with_disps):
|
||||
filename = "{pre_filename}S-{0:0{width}}".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_dftbp(filename, cell)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Elk calculator interface."""
|
||||
# Copyright (C) 2015 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,19 +34,23 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import collect_forces
|
||||
from phonopy.interface.vasp import (get_scaled_positions_lines,
|
||||
sort_positions_by_symbols,
|
||||
from phonopy.interface.vasp import (
|
||||
check_forces,
|
||||
get_drift_forces)
|
||||
get_drift_forces,
|
||||
get_scaled_positions_lines,
|
||||
sort_positions_by_symbols,
|
||||
)
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.atoms import symbol_map
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
hook = 'Forces :'
|
||||
"""Parse forces from output files."""
|
||||
hook = "Forces :"
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
|
||||
|
@ -53,15 +58,11 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
f = open(filename)
|
||||
elk_forces = collect_forces(f,
|
||||
num_atoms,
|
||||
hook,
|
||||
[3, 4, 5],
|
||||
word='total force')
|
||||
elk_forces = collect_forces(f, num_atoms, hook, [3, 4, 5], word="total force")
|
||||
if check_forces(elk_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(elk_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
elk_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(elk_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -73,11 +74,12 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
|
||||
|
||||
def read_elk(filename):
|
||||
"""Read crystal structure."""
|
||||
elk_in = ElkIn(open(filename).readlines())
|
||||
tags = elk_in.get_variables()
|
||||
avec = [tags['scale'][i] * np.array(tags['avec'][i]) for i in range(3)]
|
||||
spfnames = tags['atoms']['spfnames']
|
||||
symbols = [x.split('.')[0] for x in spfnames]
|
||||
avec = [tags["scale"][i] * np.array(tags["avec"][i]) for i in range(3)]
|
||||
spfnames = tags["atoms"]["spfnames"]
|
||||
symbols = [x.split(".")[0] for x in spfnames]
|
||||
numbers = []
|
||||
for s in symbols:
|
||||
if s in symbols:
|
||||
|
@ -93,40 +95,42 @@ def read_elk(filename):
|
|||
break
|
||||
pos_all = []
|
||||
num_all = []
|
||||
for num, pos in zip(numbers, tags['atoms']['positions']):
|
||||
for num, pos in zip(numbers, tags["atoms"]["positions"]):
|
||||
pos_all += pos
|
||||
num_all += [num] * len(pos)
|
||||
|
||||
return Atoms(numbers=num_all,
|
||||
cell=avec,
|
||||
scaled_positions=pos_all), spfnames
|
||||
return Atoms(numbers=num_all, cell=avec, scaled_positions=pos_all), spfnames
|
||||
|
||||
|
||||
def write_elk(filename, cell, sp_filenames):
|
||||
f = open(filename, 'w')
|
||||
"""Write cell to file."""
|
||||
f = open(filename, "w")
|
||||
f.write(get_elk_structure(cell, sp_filenames))
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
def write_supercells_with_displacements(
|
||||
supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
sp_filenames,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
width=3,
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_elk("%s.in" % pre_filename, supercell, sp_filenames)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}.in".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_elk(filename, cell, sp_filenames)
|
||||
|
||||
|
||||
def get_elk_structure(cell, sp_filenames=None):
|
||||
"""Return Elk structure in text."""
|
||||
lattice = cell.get_cell()
|
||||
(num_atoms,
|
||||
symbols,
|
||||
scaled_positions,
|
||||
sort_list) = sort_positions_by_symbols(cell.get_chemical_symbols(),
|
||||
cell.get_scaled_positions())
|
||||
(num_atoms, symbols, scaled_positions, sort_list) = sort_positions_by_symbols(
|
||||
cell.get_chemical_symbols(), cell.get_scaled_positions()
|
||||
)
|
||||
|
||||
if sp_filenames is None:
|
||||
spfnames = [s + ".in" for s in symbols]
|
||||
|
@ -140,10 +144,9 @@ def get_elk_structure(cell, sp_filenames=None):
|
|||
n_pos = 0
|
||||
lines += " %d\n" % len(num_atoms)
|
||||
for i, (n, s) in enumerate(zip(num_atoms, spfnames)):
|
||||
lines += " \'%s\'\n" % s
|
||||
lines += " '%s'\n" % s
|
||||
lines += " %d\n" % n
|
||||
lines += get_scaled_positions_lines(
|
||||
scaled_positions[n_pos:(n_pos + n)])
|
||||
lines += get_scaled_positions_lines(scaled_positions[n_pos : (n_pos + n)])
|
||||
if i < len(num_atoms) - 1:
|
||||
lines += "\n"
|
||||
n_pos += n
|
||||
|
@ -151,21 +154,25 @@ def get_elk_structure(cell, sp_filenames=None):
|
|||
return lines
|
||||
|
||||
|
||||
class ElkIn(object):
|
||||
class ElkIn:
|
||||
"""Class to create Elk input file."""
|
||||
|
||||
def __init__(self, lines):
|
||||
self._set_methods = {'atoms': self._set_atoms,
|
||||
'avec': self._set_avec,
|
||||
'scale': self._set_scale,
|
||||
'scale1': self._set_scale1,
|
||||
'scale2': self._set_scale2,
|
||||
'scale3': self._set_scale3}
|
||||
self._tags = {'atoms': None,
|
||||
'avec': None,
|
||||
'scale': [1.0, 1.0, 1.0]}
|
||||
"""Init method."""
|
||||
self._set_methods = {
|
||||
"atoms": self._set_atoms,
|
||||
"avec": self._set_avec,
|
||||
"scale": self._set_scale,
|
||||
"scale1": self._set_scale1,
|
||||
"scale2": self._set_scale2,
|
||||
"scale3": self._set_scale3,
|
||||
}
|
||||
self._tags = {"atoms": None, "avec": None, "scale": [1.0, 1.0, 1.0]}
|
||||
self._lines = lines[:]
|
||||
self._collect()
|
||||
|
||||
def get_variables(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self):
|
||||
|
@ -177,7 +184,7 @@ class ElkIn(object):
|
|||
|
||||
if len(line_str) == 0:
|
||||
continue
|
||||
if line_str[0] == '!':
|
||||
if line_str[0] == "!":
|
||||
continue
|
||||
|
||||
elems = line_str.split()
|
||||
|
@ -189,40 +196,39 @@ class ElkIn(object):
|
|||
spfnames = []
|
||||
positions = []
|
||||
for i in range(nspecies):
|
||||
spfnames.append(self._lines.pop(0).split()[0].strip('\''))
|
||||
spfnames.append(self._lines.pop(0).split()[0].strip("'"))
|
||||
natoms = int(self._lines.pop(0).split()[0])
|
||||
pos_sp = []
|
||||
for j in range(natoms):
|
||||
pos_sp.append(
|
||||
[float(x) for x in self._lines.pop(0).split()[:3]])
|
||||
pos_sp.append([float(x) for x in self._lines.pop(0).split()[:3]])
|
||||
positions.append(pos_sp)
|
||||
|
||||
self._tags['atoms'] = {'spfnames': spfnames,
|
||||
'positions': positions}
|
||||
self._tags["atoms"] = {"spfnames": spfnames, "positions": positions}
|
||||
|
||||
def _set_avec(self):
|
||||
avec = []
|
||||
for i in range(3):
|
||||
avec.append([float(x) for x in self._lines.pop(0).split()[:3]])
|
||||
self._tags['avec'] = avec
|
||||
self._tags["avec"] = avec
|
||||
|
||||
def _set_scale(self):
|
||||
scale = float(self._lines.pop(0).split()[0])
|
||||
for i in range(3):
|
||||
self._tags['scale'][i] = scale
|
||||
self._tags["scale"][i] = scale
|
||||
|
||||
def _set_scale1(self):
|
||||
self._tags['scale'][0] = float(self._lines.pop(0).split()[0])
|
||||
self._tags["scale"][0] = float(self._lines.pop(0).split()[0])
|
||||
|
||||
def _set_scale2(self):
|
||||
self._tags['scale'][1] = float(self._lines.pop(0).split()[0])
|
||||
self._tags["scale"][1] = float(self._lines.pop(0).split()[0])
|
||||
|
||||
def _set_scale3(self):
|
||||
self._tags['scale'][2] = float(self._lines.pop(0).split()[0])
|
||||
self._tags["scale"][2] = float(self._lines.pop(0).split()[0])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
|
||||
cell, sp_filenames = read_elk(sys.argv[1])
|
||||
symmetry = Symmetry(cell)
|
||||
print("# %s" % symmetry.get_international_table())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Force constants calculator interface."""
|
||||
# Copyright (C) 2019 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -34,12 +35,13 @@
|
|||
|
||||
# Each key has to be lowercase. {fc_calculator: name, ...}
|
||||
# name is supporsed to be str and used for text output to stdout.
|
||||
fc_calculator_names = {'alm': 'ALM', 'hiphive': 'hiPhive'}
|
||||
fc_calculator_names = {"alm": "ALM", "hiphive": "hiPhive"}
|
||||
|
||||
|
||||
# get_fc2 is called from
|
||||
# phonopy.api_phonopy.Phonopy._run_force_constants_from_forces.
|
||||
def get_fc2(supercell,
|
||||
def get_fc2(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
|
@ -47,7 +49,8 @@ def get_fc2(supercell,
|
|||
fc_calculator_options=None,
|
||||
atom_list=None,
|
||||
log_level=0,
|
||||
symprec=None):
|
||||
symprec=None,
|
||||
):
|
||||
"""Supercell 2nd order force constants (fc2) are calculated.
|
||||
|
||||
The expected shape of supercell fc2 to be returned is
|
||||
|
@ -90,23 +93,28 @@ def get_fc2(supercell,
|
|||
shape=(len(atom_list), num_atoms, 3, 3), dtype='double', order='C'.
|
||||
|
||||
"""
|
||||
|
||||
if fc_calculator == 'alm' or fc_calculator is None:
|
||||
if fc_calculator == "alm" or fc_calculator is None:
|
||||
from phonopy.interface.alm import get_fc2
|
||||
return get_fc2(supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
atom_list=atom_list,
|
||||
options=fc_calculator_options,
|
||||
log_level=log_level)
|
||||
if fc_calculator == 'hiphive':
|
||||
from phonopy.interface.hiphive_interface import get_fc2
|
||||
return get_fc2(supercell,
|
||||
|
||||
return get_fc2(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
atom_list=atom_list,
|
||||
options=fc_calculator_options,
|
||||
log_level=log_level,
|
||||
symprec=symprec)
|
||||
)
|
||||
if fc_calculator == "hiphive":
|
||||
from phonopy.interface.hiphive_interface import get_fc2
|
||||
|
||||
return get_fc2(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
atom_list=atom_list,
|
||||
options=fc_calculator_options,
|
||||
log_level=log_level,
|
||||
symprec=symprec,
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Fleur calculator interface."""
|
||||
# Copyright (C) 2021 Alexander Neukirchen
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,22 +33,25 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import numpy as np
|
||||
import math
|
||||
import itertools
|
||||
import math
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import collect_forces
|
||||
from phonopy.interface.vasp import (get_scaled_positions_lines,
|
||||
sort_positions_by_symbols,
|
||||
from phonopy.interface.vasp import (
|
||||
check_forces,
|
||||
get_drift_forces)
|
||||
get_drift_forces,
|
||||
sort_positions_by_symbols,
|
||||
)
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.atoms import symbol_map
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
hook = '1 #'
|
||||
"""Parse forces from output files."""
|
||||
hook = "1 #"
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
|
||||
|
@ -55,15 +59,11 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
f = open(filename)
|
||||
fleur_forces = collect_forces(f,
|
||||
num_atoms,
|
||||
hook,
|
||||
[0, 1, 2],
|
||||
word='force')
|
||||
fleur_forces = collect_forces(f, num_atoms, hook, [0, 1, 2], word="force")
|
||||
if check_forces(fleur_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(fleur_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
fleur_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(fleur_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -75,11 +75,15 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
|
||||
|
||||
def read_fleur(filename):
|
||||
"""Read crystal structure."""
|
||||
fleur_in = FleurIn(open(filename).readlines())
|
||||
tags = fleur_in.get_variables()
|
||||
avec = [tags['avec'][i] for i in range(3)]
|
||||
speci = tags['atoms']['speci']
|
||||
symbols = [list(symbol_map.keys())[list(symbol_map.values()).index(math.floor(float(x)))] for x in speci]
|
||||
avec = [tags["avec"][i] for i in range(3)]
|
||||
speci = tags["atoms"]["speci"]
|
||||
symbols = [
|
||||
list(symbol_map.keys())[list(symbol_map.values()).index(math.floor(float(x)))]
|
||||
for x in speci
|
||||
]
|
||||
numbers = [math.floor(float(x)) for x in speci]
|
||||
|
||||
for i, n in enumerate(numbers):
|
||||
|
@ -90,78 +94,88 @@ def read_fleur(filename):
|
|||
break
|
||||
pos_all = []
|
||||
num_all = []
|
||||
for num, pos in zip(numbers, tags['atoms']['positions']):
|
||||
for num, pos in zip(numbers, tags["atoms"]["positions"]):
|
||||
pos_all += pos
|
||||
num_all = [symbol_map[s] for s in symbols]
|
||||
|
||||
return Atoms(numbers=num_all,
|
||||
cell=avec,
|
||||
scaled_positions=pos_all), speci, fleur_in._restlines
|
||||
return (
|
||||
Atoms(numbers=num_all, cell=avec, scaled_positions=pos_all),
|
||||
speci,
|
||||
fleur_in._restlines,
|
||||
)
|
||||
|
||||
|
||||
def write_fleur(filename, cell, speci, N, restlines):
|
||||
f = open(filename, 'w')
|
||||
f.write(get_fleur_structure(cell,speci,N,restlines))
|
||||
"""Write crystal structure to file."""
|
||||
f = open(filename, "w")
|
||||
f.write(get_fleur_structure(cell, speci, N, restlines))
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
def write_supercells_with_displacements(
|
||||
supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
speci,
|
||||
N,
|
||||
restlines,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
width=3,
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_fleur("%s.in" % pre_filename, supercell, speci, N, restlines)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}.in".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_fleur(filename, cell, speci, N, restlines)
|
||||
|
||||
|
||||
def get_fleur_structure(cell,speci,N,restlines):
|
||||
def get_fleur_structure(cell, speci, N, restlines):
|
||||
"""Return Fleur structure in text."""
|
||||
lattice = cell.get_cell()
|
||||
(num_atoms,
|
||||
symbols,
|
||||
scaled_positions,
|
||||
sort_list) = sort_positions_by_symbols(cell.get_chemical_symbols(),
|
||||
cell.get_scaled_positions())
|
||||
specilong=list(itertools.chain.from_iterable(itertools.repeat(x, N) for x in speci))
|
||||
lines = restlines[0]+"\n"
|
||||
(num_atoms, symbols, scaled_positions, sort_list) = sort_positions_by_symbols(
|
||||
cell.get_chemical_symbols(), cell.get_scaled_positions()
|
||||
)
|
||||
specilong = list(
|
||||
itertools.chain.from_iterable(itertools.repeat(x, N) for x in speci)
|
||||
)
|
||||
lines = restlines[0] + "\n"
|
||||
lines += ((" %21.16f" * 3 + "\n") * 3) % tuple(lattice.ravel())
|
||||
lines += '1.0 \n'
|
||||
lines += '1.0 1.0 1.0 \n \n'
|
||||
lines += str(sum(num_atoms)) + '\n'
|
||||
lines += "1.0 \n"
|
||||
lines += "1.0 1.0 1.0 \n \n"
|
||||
lines += str(sum(num_atoms)) + "\n"
|
||||
for i in range(sum(num_atoms)):
|
||||
lines += specilong[i].ljust(6)
|
||||
currentpos = str(scaled_positions[i]).replace("[", "")
|
||||
currentpos = currentpos.replace("]", "").split()
|
||||
|
||||
for j in range(3):
|
||||
lines += ' ' + "{:.10f}".format(float(currentpos[j]))
|
||||
lines += " " + "{:.10f}".format(float(currentpos[j]))
|
||||
if i < sum(num_atoms) - 1:
|
||||
lines += "\n"
|
||||
for x in range(1,len(restlines)):
|
||||
for x in range(1, len(restlines)):
|
||||
if len(restlines[x]) == 0:
|
||||
lines += "\n"
|
||||
continue
|
||||
lines += restlines[x]
|
||||
if x != len(restlines) -1:
|
||||
if x != len(restlines) - 1:
|
||||
lines += "\n"
|
||||
return lines
|
||||
|
||||
|
||||
class FleurIn(object):
|
||||
class FleurIn:
|
||||
"""Class to generate Fleur crystal structure."""
|
||||
|
||||
def __init__(self, lines):
|
||||
self._set_methods = {'a1': self._set_avec,
|
||||
'atoms': self._set_atoms}
|
||||
self._tags = {'atoms': None,
|
||||
'a1': None}
|
||||
"""Init method."""
|
||||
self._set_methods = {"a1": self._set_avec, "atoms": self._set_atoms}
|
||||
self._tags = {"atoms": None, "a1": None}
|
||||
self._lines = lines[:]
|
||||
self._restlines = []
|
||||
self._collect()
|
||||
|
||||
def get_variables(self):
|
||||
"""Return variables."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self):
|
||||
|
@ -172,18 +186,18 @@ class FleurIn(object):
|
|||
except IndexError:
|
||||
break
|
||||
|
||||
if firstline == True:
|
||||
if firstline is True:
|
||||
self._restlines.append(line_str)
|
||||
firstline = False
|
||||
continue
|
||||
if len(line_str) == 0:
|
||||
continue
|
||||
if line_str[0] == '!':
|
||||
if line_str[0] == "!":
|
||||
continue
|
||||
|
||||
elems = line_str.split()
|
||||
if elems[-1] in self._set_methods:
|
||||
self._lines.insert(0,line_str)
|
||||
self._lines.insert(0, line_str)
|
||||
self._set_methods[elems[-1]]()
|
||||
|
||||
def _set_atoms(self):
|
||||
|
@ -192,14 +206,39 @@ class FleurIn(object):
|
|||
positions = []
|
||||
positions1 = []
|
||||
for i in range(natoms):
|
||||
currentline=self._lines.pop(0).split()
|
||||
currentline = self._lines.pop(0).split()
|
||||
speci.append(currentline[0])
|
||||
currentspeci=[float(x) for x in currentline[1:4]]
|
||||
currentspeci = [float(x) for x in currentline[1:4]]
|
||||
positions1.append(currentspeci)
|
||||
positions.append(positions1)
|
||||
|
||||
self._tags['atoms'] = {'speci': speci,
|
||||
'positions': positions}
|
||||
factors = [1.0, 1.0, 1.0]
|
||||
shifts = [0.0, 0.0, 0.0]
|
||||
|
||||
for count, line in enumerate(self._lines):
|
||||
if "&factor" in line:
|
||||
currentline = self._lines.pop(count).split()
|
||||
factors = [
|
||||
float(currentline[1]),
|
||||
float(currentline[2]),
|
||||
float(currentline[3]),
|
||||
]
|
||||
|
||||
for count, line in enumerate(self._lines):
|
||||
if "&shift" in line:
|
||||
currentline = self._lines.pop(count).split()
|
||||
shifts = [
|
||||
float(currentline[1]),
|
||||
float(currentline[2]),
|
||||
float(currentline[3]),
|
||||
]
|
||||
|
||||
for x in positions[0]:
|
||||
x[0] = x[0] / factors[0] + shifts[0]
|
||||
x[1] = x[1] / factors[1] + shifts[1]
|
||||
x[2] = x[2] / factors[2] + shifts[2]
|
||||
|
||||
self._tags["atoms"] = {"speci": speci, "positions": positions}
|
||||
for j in range(len(self._lines)):
|
||||
self._restlines.append(self._lines[j])
|
||||
|
||||
|
@ -207,18 +246,22 @@ class FleurIn(object):
|
|||
avec = []
|
||||
for i in range(3):
|
||||
avec.append([float(x) for x in self._lines.pop(0).split()[:3]])
|
||||
lattcon=float(self._lines.pop(0).split()[0])
|
||||
lattcon = float(self._lines.pop(0).split()[0])
|
||||
scale = [float(x) for x in self._lines.pop(0).split()[:3]]
|
||||
for j in range(3):
|
||||
for k in range(3):
|
||||
if scale[k]<0:
|
||||
if scale[k] < 0:
|
||||
scale[k] = np.sqrt(np.abs(scale[k]))
|
||||
avec[j][k] = lattcon*avec[j][k]*scale[k]
|
||||
self._tags['avec'] = avec
|
||||
if scale[k] == 0.0:
|
||||
scale[k] = 1.0
|
||||
avec[j][k] = lattcon * avec[j][k] * scale[k]
|
||||
self._tags["avec"] = avec
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
|
||||
cell, speci, restlines = read_fleur(sys.argv[1])
|
||||
symmetry = Symmetry(cell)
|
||||
print("# %s" % symmetry.get_international_table())
|
||||
print(get_fleur_structure(cell, speci, N, restlines))
|
||||
print(get_fleur_structure(cell, speci, 1, restlines))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""hiPhive force constants calculator interface."""
|
||||
# Copyright (C) 2018 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,30 +34,35 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import numpy as np
|
||||
from ase.atoms import Atoms
|
||||
from hiphive import ClusterSpace, StructureContainer, ForceConstantPotential
|
||||
from hiphive.fitting import Optimizer
|
||||
from hiphive.cutoffs import estimate_maximum_cutoff
|
||||
from hiphive.input_output.logging_tools import set_config
|
||||
set_config(level=30)
|
||||
|
||||
|
||||
def phonopy_atoms_to_ase(atoms_phonopy):
|
||||
ase_atoms = Atoms(cell=atoms_phonopy.cell,
|
||||
"""Convert PhonopyAtoms to Atoms."""
|
||||
try:
|
||||
from ase.atoms import Atoms
|
||||
except ImportError:
|
||||
raise ImportError("ASE python module was not found.")
|
||||
|
||||
ase_atoms = Atoms(
|
||||
cell=atoms_phonopy.cell,
|
||||
scaled_positions=atoms_phonopy.scaled_positions,
|
||||
numbers=atoms_phonopy.numbers,
|
||||
pbc=True)
|
||||
pbc=True,
|
||||
)
|
||||
return ase_atoms
|
||||
|
||||
|
||||
def get_fc2(supercell,
|
||||
def get_fc2(
|
||||
supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
symprec,
|
||||
atom_list=None,
|
||||
options=None,
|
||||
log_level=0):
|
||||
log_level=0,
|
||||
):
|
||||
"""Calculate fc2 using hiPhive."""
|
||||
if log_level:
|
||||
msg = [
|
||||
"-------------------------------"
|
||||
|
@ -64,47 +70,45 @@ def get_fc2(supercell,
|
|||
"------------------------------",
|
||||
"hiPhive is a non-trivial force constants calculator. "
|
||||
"Please cite the paper:",
|
||||
"\"The Hiphive Package for the Extraction of High‐Order Force "
|
||||
"Constants",
|
||||
" by Machine Learning\"",
|
||||
'"The Hiphive Package for the Extraction of High‐Order Force ' "Constants",
|
||||
' by Machine Learning"',
|
||||
"by Fredrik Eriksson, Erik Fransson, and Paul Erhart,",
|
||||
"Advanced Theory and Simulations, DOI:10.1002/adts.201800184 "
|
||||
"(2019)",
|
||||
""]
|
||||
"Advanced Theory and Simulations, DOI:10.1002/adts.201800184 " "(2019)",
|
||||
"",
|
||||
]
|
||||
print("\n".join(msg))
|
||||
|
||||
fc2 = run_hiphive(supercell=supercell,
|
||||
fc2 = run_hiphive(
|
||||
supercell=supercell,
|
||||
primitive=primitive,
|
||||
displacements=displacements,
|
||||
forces=forces,
|
||||
options=options,
|
||||
symprec=symprec,
|
||||
log_level=log_level)
|
||||
log_level=log_level,
|
||||
)
|
||||
|
||||
p2s_map = primitive.p2s_map
|
||||
is_compact_fc = (atom_list is not None and
|
||||
(atom_list == p2s_map).all())
|
||||
is_compact_fc = atom_list is not None and (atom_list == p2s_map).all()
|
||||
if is_compact_fc:
|
||||
fc2 = np.array(fc2[p2s_map], dtype='double', order='C')
|
||||
fc2 = np.array(fc2[p2s_map], dtype="double", order="C")
|
||||
elif atom_list is not None:
|
||||
fc2 = np.array(fc2[atom_list], dtype='double', order='C')
|
||||
fc2 = np.array(fc2[atom_list], dtype="double", order="C")
|
||||
|
||||
if log_level:
|
||||
print("--------------------------------"
|
||||
print(
|
||||
"--------------------------------"
|
||||
" hiPhive end "
|
||||
"-------------------------------")
|
||||
"-------------------------------"
|
||||
)
|
||||
|
||||
return fc2
|
||||
|
||||
|
||||
def run_hiphive(supercell,
|
||||
primitive,
|
||||
displacements,
|
||||
forces,
|
||||
options,
|
||||
symprec,
|
||||
log_level):
|
||||
"""Run hiphive
|
||||
def run_hiphive(
|
||||
supercell, primitive, displacements, forces, options, symprec, log_level
|
||||
):
|
||||
"""Run hiphive.
|
||||
|
||||
supercell : Supercell
|
||||
Perfect supercell.
|
||||
|
@ -122,6 +126,15 @@ def run_hiphive(supercell,
|
|||
Log control. 0: quiet, 1: normal, 2: verbose 3: debug
|
||||
|
||||
"""
|
||||
try:
|
||||
from hiphive import ClusterSpace, ForceConstantPotential, StructureContainer
|
||||
from hiphive.cutoffs import estimate_maximum_cutoff
|
||||
from hiphive.fitting import Optimizer
|
||||
from hiphive.input_output.logging_tools import set_config
|
||||
except ImportError:
|
||||
raise ImportError("hiPhive python module was not found.")
|
||||
|
||||
set_config(level=30)
|
||||
|
||||
ase_supercell = phonopy_atoms_to_ase(supercell)
|
||||
ase_prim = phonopy_atoms_to_ase(primitive)
|
||||
|
@ -130,8 +143,8 @@ def run_hiphive(supercell,
|
|||
structures = []
|
||||
for d, f in zip(displacements, forces):
|
||||
structure = ase_supercell.copy()
|
||||
structure.new_array('displacements', d)
|
||||
structure.new_array('forces', f)
|
||||
structure.new_array("displacements", d)
|
||||
structure.new_array("forces", f)
|
||||
structures.append(structure)
|
||||
|
||||
# parse options
|
||||
|
@ -142,13 +155,16 @@ def run_hiphive(supercell,
|
|||
|
||||
# select cutoff
|
||||
max_cutoff = estimate_maximum_cutoff(ase_supercell) - 1e-5
|
||||
if 'cutoff' in options_dict:
|
||||
cutoff = options_dict['cutoff']
|
||||
if "cutoff" in options_dict:
|
||||
cutoff = options_dict["cutoff"]
|
||||
if cutoff > max_cutoff:
|
||||
raise ValueError('Cutoff {:.4f} is larger than maximum allowed '
|
||||
'cutoff, {:.4f}, for the given supercell.'
|
||||
'\nDecrease cutoff or provide larger supercells.'
|
||||
.format(cutoff, max_cutoff))
|
||||
raise ValueError(
|
||||
"Cutoff {:.4f} is larger than maximum allowed "
|
||||
"cutoff, {:.4f}, for the given supercell."
|
||||
"\nDecrease cutoff or provide larger supercells.".format(
|
||||
cutoff, max_cutoff
|
||||
)
|
||||
)
|
||||
else:
|
||||
cutoff = max_cutoff
|
||||
|
||||
|
@ -162,15 +178,17 @@ def run_hiphive(supercell,
|
|||
sc.add_structure(structure)
|
||||
n_rows, n_cols = sc.data_shape
|
||||
if n_rows < n_cols:
|
||||
raise ValueError('Fitting problem is under-determined.'
|
||||
'\nProvide more structures or decrease cutoff.')
|
||||
raise ValueError(
|
||||
"Fitting problem is under-determined."
|
||||
"\nProvide more structures or decrease cutoff."
|
||||
)
|
||||
|
||||
# Estimate error
|
||||
opt = Optimizer(sc.get_fit_data(), train_size=0.75)
|
||||
opt.train()
|
||||
print(opt)
|
||||
print('RMSE train : {:.4f}'.format(opt.rmse_train))
|
||||
print('RMSE test : {:.4f}'.format(opt.rmse_test))
|
||||
print("RMSE train : {:.4f}".format(opt.rmse_train))
|
||||
print("RMSE test : {:.4f}".format(opt.rmse_test))
|
||||
|
||||
# Final train
|
||||
opt = Optimizer(sc.get_fit_data(), train_size=1.0)
|
||||
|
@ -185,7 +203,7 @@ def run_hiphive(supercell,
|
|||
|
||||
|
||||
def _decode_options(options):
|
||||
"""This is an example to parse options given in str.
|
||||
"""Parse options given in str.
|
||||
|
||||
When options = 'cutoff = 4.0', options is converted to {'cutoff': 4.0}.
|
||||
|
||||
|
@ -195,10 +213,9 @@ def _decode_options(options):
|
|||
phonopy --hiphiveph --fc-calc-opt "cutoff = 4" ...
|
||||
|
||||
"""
|
||||
|
||||
option_dict = {}
|
||||
for pair in options.split(','):
|
||||
key, value = [x.strip() for x in pair.split('=')]
|
||||
if key == 'cutoff':
|
||||
for pair in options.split(","):
|
||||
key, value = [x.strip() for x in pair.split("=")]
|
||||
if key == "cutoff":
|
||||
option_dict[key] = float(value)
|
||||
return option_dict
|
||||
|
|
|
@ -34,36 +34,59 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader
|
||||
except ImportError:
|
||||
from yaml import Loader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from phonopy import Phonopy
|
||||
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
|
||||
|
||||
def read_cell_yaml(filename, cell_type='unitcell'):
|
||||
"""Read crystal structure from a phonopy.yaml like file.
|
||||
def read_cell_yaml(filename, cell_type="unitcell"):
|
||||
"""Read crystal structure from a phonopy.yaml or PhonopyAtoms.__str__ like file.
|
||||
|
||||
This returns unit cell, primitive cell, or supercell. The first one found
|
||||
in this order is returned.
|
||||
phonopy.yaml like file can contain several different cells, e.g., unit cell,
|
||||
primitive cell, or supercell. In this case, the default preference order of
|
||||
the returned cell is unit cell > primitive cell > supercell. ``cell_type``
|
||||
is used to specify to choose one of them.
|
||||
|
||||
When output of PhonopyAtoms.__str__ is given (like below), this file is
|
||||
parsed and its cell is returned.
|
||||
|
||||
lattice:
|
||||
- [ 0.000000000000000, 2.845150738087836, 2.845150738087836 ] # a
|
||||
- [ 2.845150738087836, 0.000000000000000, 2.845150738087836 ] # b
|
||||
- [ 2.845150738087836, 2.845150738087836, 0.000000000000000 ] # c
|
||||
points:
|
||||
- symbol: Na # 1
|
||||
coordinates: [ 0.000000000000000, 0.000000000000000, 0.000000000000000 ]
|
||||
mass: 22.989769
|
||||
- symbol: Cl # 2
|
||||
coordinates: [ 0.500000000000000, 0.500000000000000, 0.500000000000000 ]
|
||||
mass: 35.453000
|
||||
|
||||
"""
|
||||
ph_yaml = PhonopyYaml()
|
||||
ph_yaml.read(filename)
|
||||
if ph_yaml.unitcell and cell_type == 'unitcell':
|
||||
if ph_yaml.unitcell and cell_type == "unitcell":
|
||||
return ph_yaml.unitcell
|
||||
elif ph_yaml.primitive and cell_type == 'primitive':
|
||||
elif ph_yaml.primitive and cell_type == "primitive":
|
||||
return ph_yaml.primitive
|
||||
elif ph_yaml.supercell and cell_type == 'supercell':
|
||||
elif ph_yaml.supercell and cell_type == "supercell":
|
||||
return ph_yaml.supercell
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class PhonopyYaml(object):
|
||||
class PhonopyYaml:
|
||||
"""PhonopyYaml is a container of phonopy setting.
|
||||
|
||||
This contains the writer (__str__) and reader (read) of phonopy.yaml type
|
||||
|
@ -115,20 +138,18 @@ class PhonopyYaml(object):
|
|||
"""
|
||||
|
||||
command_name = "phonopy"
|
||||
default_filenames = ("phonopy_params.yaml",
|
||||
"phonopy_disp.yaml",
|
||||
"phonopy.yaml")
|
||||
default_settings = {'force_sets': True,
|
||||
'displacements': True,
|
||||
'force_constants': False,
|
||||
'born_effective_charge': True,
|
||||
'dielectric_constant': True}
|
||||
default_filenames = ("phonopy_params.yaml", "phonopy_disp.yaml", "phonopy.yaml")
|
||||
default_settings = {
|
||||
"force_sets": True,
|
||||
"displacements": True,
|
||||
"force_constants": False,
|
||||
"born_effective_charge": True,
|
||||
"dielectric_constant": True,
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
configuration=None,
|
||||
calculator=None,
|
||||
physical_units=None,
|
||||
settings=None):
|
||||
def __init__(
|
||||
self, configuration=None, calculator=None, physical_units=None, settings=None
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
Parameters
|
||||
|
@ -140,7 +161,7 @@ class PhonopyYaml(object):
|
|||
physical_units : dict
|
||||
Physical units used for the calculation.
|
||||
settings : dict
|
||||
See Phonopy.save().
|
||||
This controls amount of information in yaml output. See Phonopy.save().
|
||||
|
||||
"""
|
||||
self.configuration = configuration
|
||||
|
@ -174,7 +195,7 @@ class PhonopyYaml(object):
|
|||
return "\n".join(self.get_yaml_lines())
|
||||
|
||||
def read(self, filename):
|
||||
"""Read phonopy.yaml life file."""
|
||||
"""Read phonopy.yaml like file."""
|
||||
self.yaml_filename = filename
|
||||
self._load(filename)
|
||||
|
||||
|
@ -189,6 +210,7 @@ class PhonopyYaml(object):
|
|||
|
||||
def parse(self):
|
||||
"""Parse raw yaml data."""
|
||||
self._parse_command_header()
|
||||
self._parse_transformation_matrices()
|
||||
self._parse_all_cells()
|
||||
self._parse_force_constants()
|
||||
|
@ -196,7 +218,7 @@ class PhonopyYaml(object):
|
|||
self._parse_nac_params()
|
||||
self._parse_calculator()
|
||||
|
||||
def set_phonon_info(self, phonopy):
|
||||
def set_phonon_info(self, phonopy: "Phonopy"):
|
||||
"""Collect data from Phonopy instance."""
|
||||
self.unitcell = phonopy.unitcell
|
||||
self.primitive = phonopy.primitive
|
||||
|
@ -232,42 +254,44 @@ class PhonopyYaml(object):
|
|||
lines.append("%s:" % self.command_name)
|
||||
if self.version is None:
|
||||
from phonopy.version import __version__
|
||||
|
||||
lines.append(" version: %s" % __version__)
|
||||
else:
|
||||
lines.append(" version: %s" % self.version)
|
||||
if self.calculator:
|
||||
lines.append(" calculator: %s" % self.calculator)
|
||||
if self.frequency_unit_conversion_factor:
|
||||
lines.append(" frequency_unit_conversion_factor: %f" %
|
||||
self.frequency_unit_conversion_factor)
|
||||
lines.append(
|
||||
" frequency_unit_conversion_factor: %f"
|
||||
% self.frequency_unit_conversion_factor
|
||||
)
|
||||
if self.symmetry:
|
||||
lines.append(" symmetry_tolerance: %.5e" %
|
||||
self.symmetry.tolerance)
|
||||
lines.append(" symmetry_tolerance: %.5e" % self.symmetry.tolerance)
|
||||
if self.nac_params:
|
||||
lines.append(" nac_unit_conversion_factor: %f"
|
||||
% self.nac_params['factor'])
|
||||
lines.append(" nac_unit_conversion_factor: %f" % self.nac_params["factor"])
|
||||
if self.configuration is not None:
|
||||
lines.append(" configuration:")
|
||||
for key in self.configuration:
|
||||
val = self.configuration[key]
|
||||
if type(val) is str:
|
||||
val = val.replace('\\', '\\\\')
|
||||
lines.append(" %s: \"%s\"" % (key, val))
|
||||
val = val.replace("\\", "\\\\")
|
||||
lines.append(' %s: "%s"' % (key, val))
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _physical_units_yaml_lines(self):
|
||||
lines = []
|
||||
lines.append("physical_unit:")
|
||||
lines.append(" atomic_mass: \"AMU\"")
|
||||
lines.append(' atomic_mass: "AMU"')
|
||||
units = self.physical_units
|
||||
if units is not None:
|
||||
if units['length_unit'] is not None:
|
||||
lines.append(" length: \"%s\"" % units['length_unit'])
|
||||
if (self.command_name == "phonopy" and
|
||||
units['force_constants_unit'] is not None):
|
||||
lines.append(" force_constants: \"%s\"" %
|
||||
units['force_constants_unit'])
|
||||
if units["length_unit"] is not None:
|
||||
lines.append(' length: "%s"' % units["length_unit"])
|
||||
if (
|
||||
self.command_name == "phonopy"
|
||||
and units["force_constants_unit"] is not None
|
||||
):
|
||||
lines.append(' force_constants: "%s"' % units["force_constants_unit"])
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
@ -275,22 +299,22 @@ class PhonopyYaml(object):
|
|||
lines = []
|
||||
if self.symmetry is not None and self.symmetry.dataset is not None:
|
||||
lines.append("space_group:")
|
||||
lines.append(" type: \"%s\"" %
|
||||
self.symmetry.get_dataset()['international'])
|
||||
lines.append(" number: %d" %
|
||||
self.symmetry.get_dataset()['number'])
|
||||
hall_symbol = self.symmetry.get_dataset()['hall']
|
||||
if "\"" in hall_symbol:
|
||||
hall_symbol = hall_symbol.replace("\"", "\\\"")
|
||||
lines.append(" Hall_symbol: \"%s\"" % hall_symbol)
|
||||
lines.append(' type: "%s"' % self.symmetry.dataset["international"])
|
||||
lines.append(" number: %d" % self.symmetry.dataset["number"])
|
||||
hall_symbol = self.symmetry.dataset["hall"]
|
||||
if '"' in hall_symbol:
|
||||
hall_symbol = hall_symbol.replace('"', '\\"')
|
||||
lines.append(' Hall_symbol: "%s"' % hall_symbol)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _cell_info_yaml_lines(self):
|
||||
lines = self._primitive_matrix_yaml_lines(
|
||||
self.primitive_matrix, "primitive_matrix")
|
||||
self.primitive_matrix, "primitive_matrix"
|
||||
)
|
||||
lines += self._supercell_matrix_yaml_lines(
|
||||
self.supercell_matrix, "supercell_matrix")
|
||||
self.supercell_matrix, "supercell_matrix"
|
||||
)
|
||||
lines += self._primitive_yaml_lines(self.primitive, "primitive_cell")
|
||||
lines += self._unitcell_yaml_lines()
|
||||
lines += self._supercell_yaml_lines()
|
||||
|
@ -317,30 +341,28 @@ class PhonopyYaml(object):
|
|||
def _primitive_yaml_lines(self, primitive, name):
|
||||
lines = []
|
||||
if primitive is not None:
|
||||
lines += self._cell_yaml_lines(
|
||||
self.primitive, name, None)
|
||||
lines += self._cell_yaml_lines(self.primitive, name, None)
|
||||
lines.append(" reciprocal_lattice: # without 2pi")
|
||||
rec_lat = np.linalg.inv(primitive.cell)
|
||||
for v, a in zip(rec_lat.T, ('a*', 'b*', 'c*')):
|
||||
lines.append(" - [ %21.15f, %21.15f, %21.15f ] # %s" %
|
||||
(v[0], v[1], v[2], a))
|
||||
for v, a in zip(rec_lat.T, ("a*", "b*", "c*")):
|
||||
lines.append(
|
||||
" - [ %21.15f, %21.15f, %21.15f ] # %s" % (v[0], v[1], v[2], a)
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _unitcell_yaml_lines(self):
|
||||
lines = []
|
||||
if self.unitcell is not None:
|
||||
lines += self._cell_yaml_lines(
|
||||
self.unitcell, "unit_cell", self.u2p_map)
|
||||
lines += self._cell_yaml_lines(self.unitcell, "unit_cell", self.u2p_map)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _supercell_yaml_lines(self):
|
||||
lines = []
|
||||
if self.supercell is not None:
|
||||
s2p_map = getattr(self.primitive, 's2p_map', None)
|
||||
lines += self._cell_yaml_lines(
|
||||
self.supercell, "supercell", s2p_map)
|
||||
s2p_map = getattr(self.primitive, "s2p_map", None)
|
||||
lines += self._cell_yaml_lines(self.supercell, "supercell", s2p_map)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
@ -351,8 +373,7 @@ class PhonopyYaml(object):
|
|||
for line in cell.get_yaml_lines():
|
||||
lines.append(" " + line)
|
||||
if map_to_primitive is not None and "mass" in line:
|
||||
lines.append(" reduced_to: %d" %
|
||||
(map_to_primitive[count] + 1))
|
||||
lines.append(" reduced_to: %d" % (map_to_primitive[count] + 1))
|
||||
count += 1
|
||||
return lines
|
||||
|
||||
|
@ -365,116 +386,122 @@ class PhonopyYaml(object):
|
|||
def _nac_yaml_lines_given_symbols(self, symbols):
|
||||
lines = []
|
||||
if self.nac_params is not None:
|
||||
if self.settings['born_effective_charge']:
|
||||
if self.settings["born_effective_charge"]:
|
||||
lines.append("born_effective_charge:")
|
||||
for i, z in enumerate(self.nac_params['born']):
|
||||
for i, z in enumerate(self.nac_params["born"]):
|
||||
text = "- # %d" % (i + 1)
|
||||
if symbols:
|
||||
text += " (%s)" % symbols[i]
|
||||
lines.append(text)
|
||||
for v in z:
|
||||
lines.append(" - [ %18.15f, %18.15f, %18.15f ]" %
|
||||
tuple(v))
|
||||
lines.append(" - [ %18.15f, %18.15f, %18.15f ]" % tuple(v))
|
||||
lines.append("")
|
||||
|
||||
if self.settings['dielectric_constant']:
|
||||
if self.settings["dielectric_constant"]:
|
||||
lines.append("dielectric_constant:")
|
||||
for v in self.nac_params['dielectric']:
|
||||
lines.append(
|
||||
" - [ %18.15f, %18.15f, %18.15f ]" % tuple(v))
|
||||
for v in self.nac_params["dielectric"]:
|
||||
lines.append(" - [ %18.15f, %18.15f, %18.15f ]" % tuple(v))
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _dataset_yaml_lines(self):
|
||||
lines = []
|
||||
if self.settings['force_sets'] or self.settings['displacements']:
|
||||
if self.settings["force_sets"] or self.settings["displacements"]:
|
||||
disp_yaml_lines = self._displacements_yaml_lines(
|
||||
with_forces=self.settings['force_sets'])
|
||||
with_forces=self.settings["force_sets"]
|
||||
)
|
||||
lines += disp_yaml_lines
|
||||
return lines
|
||||
|
||||
def _displacements_yaml_lines(self, with_forces=False):
|
||||
return self._displacements_yaml_lines_2types(
|
||||
self.dataset, with_forces=with_forces)
|
||||
self.dataset, with_forces=with_forces
|
||||
)
|
||||
|
||||
def _displacements_yaml_lines_2types(self, dataset, with_forces=False):
|
||||
if dataset is not None:
|
||||
if 'first_atoms' in dataset:
|
||||
if "first_atoms" in dataset:
|
||||
return self._displacements_yaml_lines_type1(
|
||||
dataset, with_forces=with_forces)
|
||||
elif 'displacements' in dataset:
|
||||
dataset, with_forces=with_forces
|
||||
)
|
||||
elif "displacements" in dataset:
|
||||
return self._displacements_yaml_lines_type2(
|
||||
dataset, with_forces=with_forces)
|
||||
dataset, with_forces=with_forces
|
||||
)
|
||||
return []
|
||||
|
||||
def _displacements_yaml_lines_type1(self, dataset, with_forces=False):
|
||||
lines = ["displacements:", ]
|
||||
for i, d in enumerate(dataset['first_atoms']):
|
||||
lines.append("- atom: %4d" % (d['number'] + 1))
|
||||
lines = [
|
||||
"displacements:",
|
||||
]
|
||||
for i, d in enumerate(dataset["first_atoms"]):
|
||||
lines.append("- atom: %4d" % (d["number"] + 1))
|
||||
lines.append(" displacement:")
|
||||
lines.append(" [ %20.16f,%20.16f,%20.16f ]"
|
||||
% tuple(d['displacement']))
|
||||
if with_forces and 'forces' in d:
|
||||
lines.append(" [ %20.16f,%20.16f,%20.16f ]" % tuple(d["displacement"]))
|
||||
if with_forces and "forces" in d:
|
||||
lines.append(" forces:")
|
||||
for f in d['forces']:
|
||||
for f in d["forces"]:
|
||||
lines.append(" - [ %20.16f,%20.16f,%20.16f ]" % tuple(f))
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _displacements_yaml_lines_type2(self, dataset, with_forces=False):
|
||||
if 'random_seed' in dataset:
|
||||
lines = ["random_seed: %d" % dataset['random_seed'],
|
||||
"displacements:"]
|
||||
if "random_seed" in dataset:
|
||||
lines = ["random_seed: %d" % dataset["random_seed"], "displacements:"]
|
||||
else:
|
||||
lines = ["displacements:", ]
|
||||
for i, dset in enumerate(dataset['displacements']):
|
||||
lines = [
|
||||
"displacements:",
|
||||
]
|
||||
for i, dset in enumerate(dataset["displacements"]):
|
||||
lines.append("- # %4d" % (i + 1))
|
||||
for j, d in enumerate(dset):
|
||||
lines.append(" - displacement: # %d" % (j + 1))
|
||||
lines.append(" [ %20.16f,%20.16f,%20.16f ]" % tuple(d))
|
||||
if with_forces and 'forces' in dataset:
|
||||
f = dataset['forces'][i][j]
|
||||
if with_forces and "forces" in dataset:
|
||||
f = dataset["forces"][i][j]
|
||||
lines.append(" force:")
|
||||
lines.append(" [ %20.16f,%20.16f,%20.16f ]"
|
||||
% tuple(f))
|
||||
lines.append(" [ %20.16f,%20.16f,%20.16f ]" % tuple(f))
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
def _force_constants_yaml_lines(self):
|
||||
lines = []
|
||||
if (self.settings['force_constants'] and
|
||||
self.force_constants is not None):
|
||||
if self.settings["force_constants"] and self.force_constants is not None:
|
||||
shape = self.force_constants.shape[:2]
|
||||
lines = ["force_constants:", ]
|
||||
lines = [
|
||||
"force_constants:",
|
||||
]
|
||||
if shape[0] == shape[1]:
|
||||
lines.append(" format: \"full\"")
|
||||
lines.append(' format: "full"')
|
||||
else:
|
||||
lines.append(" format: \"compact\"")
|
||||
lines.append(' format: "compact"')
|
||||
lines.append(" shape: [ %d, %d ]" % shape)
|
||||
lines.append(" elements:")
|
||||
for (i, j) in list(np.ndindex(shape)):
|
||||
lines.append(" - # (%d, %d)" % (i + 1, j + 1))
|
||||
for v in self.force_constants[i, j]:
|
||||
lines.append(" - [ %21.15f, %21.15f, %21.15f ]"
|
||||
% tuple(v))
|
||||
lines.append(" - [ %21.15f, %21.15f, %21.15f ]" % tuple(v))
|
||||
return lines
|
||||
|
||||
def _load(self, filename):
|
||||
_, ext = os.path.splitext(filename)
|
||||
if ext == '.xz' or ext == '.lzma':
|
||||
if ext == ".xz" or ext == ".lzma":
|
||||
try:
|
||||
import lzma
|
||||
except ImportError:
|
||||
raise("Reading a lzma compressed file is not supported "
|
||||
"by this python version.")
|
||||
raise (
|
||||
"Reading a lzma compressed file is not supported "
|
||||
"by this python version."
|
||||
)
|
||||
with lzma.open(filename) as f:
|
||||
self._yaml = yaml.load(f, Loader=Loader)
|
||||
elif ext == '.gz':
|
||||
elif ext == ".gz":
|
||||
import gzip
|
||||
|
||||
with gzip.open(filename) as f:
|
||||
self._yaml = yaml.load(f, Loader=Loader)
|
||||
else:
|
||||
with open(filename, 'r') as f:
|
||||
with open(filename, "r") as f:
|
||||
self._yaml = yaml.load(f, Loader=Loader)
|
||||
|
||||
if type(self._yaml) is str:
|
||||
|
@ -483,50 +510,58 @@ class PhonopyYaml(object):
|
|||
|
||||
self.parse()
|
||||
|
||||
def _parse_command_header(self):
|
||||
if self.command_name in self._yaml:
|
||||
header = self._yaml[self.command_name]
|
||||
self.version = header["version"]
|
||||
|
||||
def _parse_transformation_matrices(self):
|
||||
if 'supercell_matrix' in self._yaml:
|
||||
self.supercell_matrix = np.array(self._yaml['supercell_matrix'],
|
||||
dtype='intc', order='C')
|
||||
if 'primitive_matrix' in self._yaml:
|
||||
self.primitive_matrix = np.array(self._yaml['primitive_matrix'],
|
||||
dtype='double', order='C')
|
||||
if "supercell_matrix" in self._yaml:
|
||||
self.supercell_matrix = np.array(
|
||||
self._yaml["supercell_matrix"], dtype="intc", order="C"
|
||||
)
|
||||
if "primitive_matrix" in self._yaml:
|
||||
self.primitive_matrix = np.array(
|
||||
self._yaml["primitive_matrix"], dtype="double", order="C"
|
||||
)
|
||||
|
||||
def _parse_all_cells(self):
|
||||
if 'unit_cell' in self._yaml:
|
||||
self.unitcell = self._parse_cell(self._yaml['unit_cell'])
|
||||
if 'primitive_cell' in self._yaml:
|
||||
self.primitive = self._parse_cell(self._yaml['primitive_cell'])
|
||||
if 'supercell' in self._yaml:
|
||||
self.supercell = self._parse_cell(self._yaml['supercell'])
|
||||
if "unit_cell" in self._yaml:
|
||||
self.unitcell = self._parse_cell(self._yaml["unit_cell"])
|
||||
if "primitive_cell" in self._yaml:
|
||||
self.primitive = self._parse_cell(self._yaml["primitive_cell"])
|
||||
if "supercell" in self._yaml:
|
||||
self.supercell = self._parse_cell(self._yaml["supercell"])
|
||||
if self.unitcell is None:
|
||||
if ('lattice' in self._yaml and
|
||||
('points' in self._yaml or 'atoms' in self._yaml)):
|
||||
if "lattice" in self._yaml and (
|
||||
"points" in self._yaml or "atoms" in self._yaml
|
||||
):
|
||||
self.unitcell = self._parse_cell(self._yaml)
|
||||
|
||||
def _parse_cell(self, cell_yaml):
|
||||
lattice = None
|
||||
if 'lattice' in cell_yaml:
|
||||
lattice = cell_yaml['lattice']
|
||||
if "lattice" in cell_yaml:
|
||||
lattice = cell_yaml["lattice"]
|
||||
points = []
|
||||
symbols = []
|
||||
masses = []
|
||||
if 'points' in cell_yaml:
|
||||
for x in cell_yaml['points']:
|
||||
if 'coordinates' in x:
|
||||
points.append(x['coordinates'])
|
||||
if 'symbol' in x:
|
||||
symbols.append(x['symbol'])
|
||||
if 'mass' in x:
|
||||
masses.append(x['mass'])
|
||||
if "points" in cell_yaml:
|
||||
for x in cell_yaml["points"]:
|
||||
if "coordinates" in x:
|
||||
points.append(x["coordinates"])
|
||||
if "symbol" in x:
|
||||
symbols.append(x["symbol"])
|
||||
if "mass" in x:
|
||||
masses.append(x["mass"])
|
||||
# For version < 1.10.9
|
||||
elif 'atoms' in cell_yaml:
|
||||
for x in cell_yaml['atoms']:
|
||||
if 'coordinates' not in x and 'position' in x:
|
||||
points.append(x['position'])
|
||||
if 'symbol' in x:
|
||||
symbols.append(x['symbol'])
|
||||
if 'mass' in x:
|
||||
masses.append(x['mass'])
|
||||
elif "atoms" in cell_yaml:
|
||||
for x in cell_yaml["atoms"]:
|
||||
if "coordinates" not in x and "position" in x:
|
||||
points.append(x["position"])
|
||||
if "symbol" in x:
|
||||
symbols.append(x["symbol"])
|
||||
if "mass" in x:
|
||||
masses.append(x["mass"])
|
||||
return self._get_cell(lattice, points, symbols, masses=masses)
|
||||
|
||||
def _get_cell(self, lattice, points, symbols, masses=None):
|
||||
|
@ -548,98 +583,106 @@ class PhonopyYaml(object):
|
|||
_masses = None
|
||||
|
||||
if _lattice and _points and _symbols:
|
||||
return PhonopyAtoms(symbols=_symbols,
|
||||
return PhonopyAtoms(
|
||||
symbols=_symbols,
|
||||
cell=_lattice,
|
||||
masses=_masses,
|
||||
scaled_positions=_points)
|
||||
scaled_positions=_points,
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _parse_force_constants(self):
|
||||
if 'force_constants' in self._yaml:
|
||||
shape = tuple(self._yaml['force_constants']['shape']) + (3, 3)
|
||||
fc = np.reshape(self._yaml['force_constants']['elements'], shape)
|
||||
self.force_constants = np.array(fc, dtype='double', order='C')
|
||||
if "force_constants" in self._yaml:
|
||||
shape = tuple(self._yaml["force_constants"]["shape"]) + (3, 3)
|
||||
fc = np.reshape(self._yaml["force_constants"]["elements"], shape)
|
||||
self.force_constants = np.array(fc, dtype="double", order="C")
|
||||
|
||||
def _parse_dataset(self):
|
||||
self.dataset = self._get_dataset(self.supercell)
|
||||
|
||||
def _get_dataset(self, supercell):
|
||||
dataset = None
|
||||
if 'displacements' in self._yaml:
|
||||
if "displacements" in self._yaml:
|
||||
if supercell is not None:
|
||||
natom = len(supercell)
|
||||
else:
|
||||
natom = None
|
||||
disp = self._yaml['displacements'][0]
|
||||
disp = self._yaml["displacements"][0]
|
||||
if type(disp) is dict: # type1
|
||||
dataset = self._parse_force_sets_type1(natom=natom)
|
||||
elif type(disp) is list: # type2
|
||||
if 'displacement' in disp[0]:
|
||||
if "displacement" in disp[0]:
|
||||
dataset = self._parse_force_sets_type2()
|
||||
return dataset
|
||||
|
||||
def _parse_force_sets_type1(self, natom=None):
|
||||
with_forces = False
|
||||
if 'forces' in self._yaml['displacements'][0]:
|
||||
if "forces" in self._yaml["displacements"][0]:
|
||||
with_forces = True
|
||||
dataset = {'natom': len(self._yaml['displacements'][0]['forces'])}
|
||||
dataset = {"natom": len(self._yaml["displacements"][0]["forces"])}
|
||||
elif natom is not None:
|
||||
dataset = {'natom': natom}
|
||||
elif 'natom' in self._yaml:
|
||||
dataset = {'natom': self._yaml['natom']}
|
||||
dataset = {"natom": natom}
|
||||
elif "natom" in self._yaml:
|
||||
dataset = {"natom": self._yaml["natom"]}
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Number of atoms in supercell could not be found.")
|
||||
raise RuntimeError("Number of atoms in supercell could not be found.")
|
||||
|
||||
first_atoms = []
|
||||
for d in self._yaml['displacements']:
|
||||
for d in self._yaml["displacements"]:
|
||||
data = {
|
||||
'number': d['atom'] - 1,
|
||||
'displacement': np.array(d['displacement'], dtype='double')}
|
||||
"number": d["atom"] - 1,
|
||||
"displacement": np.array(d["displacement"], dtype="double"),
|
||||
}
|
||||
if with_forces:
|
||||
data['forces'] = np.array(d['forces'],
|
||||
dtype='double', order='C')
|
||||
data["forces"] = np.array(d["forces"], dtype="double", order="C")
|
||||
first_atoms.append(data)
|
||||
dataset['first_atoms'] = first_atoms
|
||||
dataset["first_atoms"] = first_atoms
|
||||
|
||||
return dataset
|
||||
|
||||
def _parse_force_sets_type2(self):
|
||||
nsets = len(self._yaml['displacements'])
|
||||
natom = len(self._yaml['displacements'][0])
|
||||
if 'force' in self._yaml['displacements'][0][0]:
|
||||
nsets = len(self._yaml["displacements"])
|
||||
natom = len(self._yaml["displacements"][0])
|
||||
if "force" in self._yaml["displacements"][0][0]:
|
||||
with_forces = True
|
||||
forces = np.zeros((nsets, natom, 3), dtype='double', order='C')
|
||||
forces = np.zeros((nsets, natom, 3), dtype="double", order="C")
|
||||
else:
|
||||
with_forces = False
|
||||
displacements = np.zeros((nsets, natom, 3), dtype='double', order='C')
|
||||
for i, dfset in enumerate(self._yaml['displacements']):
|
||||
displacements = np.zeros((nsets, natom, 3), dtype="double", order="C")
|
||||
for i, dfset in enumerate(self._yaml["displacements"]):
|
||||
for j, df in enumerate(dfset):
|
||||
if with_forces:
|
||||
forces[i, j] = df['force']
|
||||
displacements[i, j] = df['displacement']
|
||||
forces[i, j] = df["force"]
|
||||
displacements[i, j] = df["displacement"]
|
||||
if with_forces:
|
||||
return {'forces': forces, 'displacements': displacements}
|
||||
return {"forces": forces, "displacements": displacements}
|
||||
else:
|
||||
return {'displacements': displacements}
|
||||
return {"displacements": displacements}
|
||||
|
||||
def _parse_nac_params(self):
|
||||
nac_params = {}
|
||||
if 'born_effective_charge' in self._yaml:
|
||||
nac_params['born'] = np.array(self._yaml['born_effective_charge'],
|
||||
dtype='double', order='C')
|
||||
if 'dielectric_constant' in self._yaml:
|
||||
nac_params['dielectric'] = np.array(
|
||||
self._yaml['dielectric_constant'], dtype='double', order='C')
|
||||
if (self.command_name in self._yaml and
|
||||
'nac_unit_conversion_factor' in self._yaml[self.command_name]):
|
||||
nac_params['factor'] = self._yaml[self.command_name][
|
||||
'nac_unit_conversion_factor']
|
||||
if 'born' in nac_params and 'dielectric' in nac_params:
|
||||
if "born_effective_charge" in self._yaml:
|
||||
nac_params["born"] = np.array(
|
||||
self._yaml["born_effective_charge"], dtype="double", order="C"
|
||||
)
|
||||
if "dielectric_constant" in self._yaml:
|
||||
nac_params["dielectric"] = np.array(
|
||||
self._yaml["dielectric_constant"], dtype="double", order="C"
|
||||
)
|
||||
if (
|
||||
self.command_name in self._yaml
|
||||
and "nac_unit_conversion_factor" in self._yaml[self.command_name]
|
||||
):
|
||||
nac_params["factor"] = self._yaml[self.command_name][
|
||||
"nac_unit_conversion_factor"
|
||||
]
|
||||
if "born" in nac_params and "dielectric" in nac_params:
|
||||
self.nac_params = nac_params
|
||||
|
||||
def _parse_calculator(self):
|
||||
if (self.command_name in self._yaml and
|
||||
'calculator' in self._yaml[self.command_name]):
|
||||
self.calculator = self._yaml[self.command_name]['calculator']
|
||||
if (
|
||||
self.command_name in self._yaml
|
||||
and "calculator" in self._yaml[self.command_name]
|
||||
):
|
||||
self.calculator = self._yaml[self.command_name]["calculator"]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""QE calculator interface."""
|
||||
# Copyright (C) 2014 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,41 +34,43 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import numpy as np
|
||||
from collections import OrderedDict
|
||||
|
||||
from phonopy.file_IO import (iter_collect_forces,
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import (
|
||||
iter_collect_forces,
|
||||
write_FORCE_CONSTANTS,
|
||||
write_force_constants_to_hdf5,
|
||||
write_FORCE_CONSTANTS)
|
||||
)
|
||||
from phonopy.harmonic.force_constants import distribute_force_constants_by_translations
|
||||
from phonopy.interface.vasp import (
|
||||
get_scaled_positions_lines, check_forces, get_drift_forces)
|
||||
from phonopy.units import Bohr
|
||||
check_forces,
|
||||
get_drift_forces,
|
||||
get_scaled_positions_lines,
|
||||
)
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.atoms import symbol_map
|
||||
from phonopy.structure.cells import get_supercell, get_primitive
|
||||
from phonopy.harmonic.force_constants import (
|
||||
distribute_force_constants_by_translations)
|
||||
from phonopy.structure.cells import get_primitive, get_supercell
|
||||
from phonopy.units import Bohr
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms,
|
||||
forces_filenames,
|
||||
verbose=True):
|
||||
hook = 'Forces acting on atoms'
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
"""Parse forces from output files."""
|
||||
hook = "Forces acting on atoms"
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
pwscf_forces = iter_collect_forces(filename,
|
||||
num_atoms,
|
||||
hook,
|
||||
[6, 7, 8],
|
||||
word='force')
|
||||
pwscf_forces = iter_collect_forces(
|
||||
filename, num_atoms, hook, [6, 7, 8], word="force"
|
||||
)
|
||||
if check_forces(pwscf_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(pwscf_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
pwscf_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(pwscf_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -79,15 +82,16 @@ def parse_set_of_forces(num_atoms,
|
|||
|
||||
|
||||
def read_pwscf(filename):
|
||||
"""Read crystal structure."""
|
||||
with open(filename) as f:
|
||||
pwscf_in = PwscfIn(f.readlines())
|
||||
tags = pwscf_in.get_tags()
|
||||
lattice = tags['cell_parameters']
|
||||
positions = [pos[1] for pos in tags['atomic_positions']]
|
||||
species = [pos[0] for pos in tags['atomic_positions']]
|
||||
lattice = tags["cell_parameters"]
|
||||
positions = [pos[1] for pos in tags["atomic_positions"]]
|
||||
species = [pos[0] for pos in tags["atomic_positions"]]
|
||||
mass_map = {}
|
||||
pp_map = {}
|
||||
for vals in tags['atomic_species']:
|
||||
for vals in tags["atomic_species"]:
|
||||
mass_map[vals[0]] = vals[1]
|
||||
pp_map[vals[0]] = vals[2]
|
||||
masses = [mass_map[x] for x in species]
|
||||
|
@ -122,18 +126,15 @@ def read_pwscf(filename):
|
|||
if n < 1:
|
||||
numbers[i] = available_numbers[-n]
|
||||
|
||||
cell = Atoms(numbers=numbers,
|
||||
masses=masses,
|
||||
cell=lattice,
|
||||
scaled_positions=positions)
|
||||
cell = Atoms(
|
||||
numbers=numbers, masses=masses, cell=lattice, scaled_positions=positions
|
||||
)
|
||||
else:
|
||||
cell = Atoms(numbers=numbers,
|
||||
cell=lattice,
|
||||
scaled_positions=positions)
|
||||
cell = Atoms(numbers=numbers, cell=lattice, scaled_positions=positions)
|
||||
|
||||
unique_symbols = []
|
||||
pp_filenames = {}
|
||||
for i, symbol in enumerate(cell.get_chemical_symbols()):
|
||||
for i, symbol in enumerate(cell.symbols):
|
||||
if symbol not in unique_symbols:
|
||||
unique_symbols.append(symbol)
|
||||
pp_filenames[symbol] = pp_all_filenames[i]
|
||||
|
@ -142,28 +143,34 @@ def read_pwscf(filename):
|
|||
|
||||
|
||||
def write_pwscf(filename, cell, pp_filenames):
|
||||
f = open(filename, 'w')
|
||||
"""Write cell to file."""
|
||||
f = open(filename, "w")
|
||||
f.write(get_pwscf_structure(cell, pp_filenames=pp_filenames))
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
def write_supercells_with_displacements(
|
||||
supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
pp_filenames,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
width=3,
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_pwscf("%s.in" % pre_filename, supercell, pp_filenames)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}.in".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_pwscf(filename, cell, pp_filenames)
|
||||
|
||||
|
||||
def get_pwscf_structure(cell, pp_filenames=None):
|
||||
lattice = cell.get_cell()
|
||||
positions = cell.get_scaled_positions()
|
||||
masses = cell.get_masses()
|
||||
chemical_symbols = cell.get_chemical_symbols()
|
||||
"""Return QE structure in text."""
|
||||
lattice = cell.cell
|
||||
positions = cell.scaled_positions
|
||||
masses = cell.masses
|
||||
chemical_symbols = cell.symbols
|
||||
unique_symbols = []
|
||||
atomic_species = []
|
||||
for symbol, m in zip(chemical_symbols, masses):
|
||||
|
@ -172,8 +179,10 @@ def get_pwscf_structure(cell, pp_filenames=None):
|
|||
atomic_species.append((symbol, m))
|
||||
|
||||
lines = ""
|
||||
lines += ("! ibrav = 0, nat = %d, ntyp = %d\n" %
|
||||
(len(positions), len(unique_symbols)))
|
||||
lines += "! ibrav = 0, nat = %d, ntyp = %d\n" % (
|
||||
len(positions),
|
||||
len(unique_symbols),
|
||||
)
|
||||
lines += "CELL_PARAMETERS bohr\n"
|
||||
lines += ((" %21.16f" * 3 + "\n") * 3) % tuple(lattice.ravel())
|
||||
lines += "ATOMIC_SPECIES\n"
|
||||
|
@ -181,12 +190,11 @@ def get_pwscf_structure(cell, pp_filenames=None):
|
|||
if pp_filenames is None:
|
||||
lines += " %2s %10.5f %s_PP_filename\n" % (symbol, mass, symbol)
|
||||
else:
|
||||
lines += " %2s %10.5f %s\n" % (symbol, mass,
|
||||
pp_filenames[symbol])
|
||||
lines += " %2s %10.5f %s\n" % (symbol, mass, pp_filenames[symbol])
|
||||
lines += "ATOMIC_POSITIONS crystal\n"
|
||||
for i, (symbol, pos_line) in enumerate(zip(
|
||||
chemical_symbols,
|
||||
get_scaled_positions_lines(positions).split('\n'))):
|
||||
for i, (symbol, pos_line) in enumerate(
|
||||
zip(chemical_symbols, get_scaled_positions_lines(positions).split("\n"))
|
||||
):
|
||||
lines += (" %2s " % symbol) + pos_line
|
||||
if i < len(chemical_symbols) - 1:
|
||||
lines += "\n"
|
||||
|
@ -194,23 +202,30 @@ def get_pwscf_structure(cell, pp_filenames=None):
|
|||
return lines
|
||||
|
||||
|
||||
class PwscfIn(object):
|
||||
class PwscfIn:
|
||||
"""Class to create QE input file."""
|
||||
|
||||
_set_methods = OrderedDict(
|
||||
[('ibrav', '_set_ibrav'),
|
||||
('celldm(1)', '_set_celldm1'),
|
||||
('nat', '_set_nat'),
|
||||
('ntyp', '_set_ntyp'),
|
||||
('atomic_species', '_set_atom_types'),
|
||||
('atomic_positions', '_set_positions'),
|
||||
('cell_parameters', '_set_lattice')])
|
||||
[
|
||||
("ibrav", "_set_ibrav"),
|
||||
("celldm(1)", "_set_celldm1"),
|
||||
("nat", "_set_nat"),
|
||||
("ntyp", "_set_ntyp"),
|
||||
("atomic_species", "_set_atom_types"),
|
||||
("atomic_positions", "_set_positions"),
|
||||
("cell_parameters", "_set_lattice"),
|
||||
]
|
||||
)
|
||||
|
||||
def __init__(self, lines):
|
||||
"""Init method."""
|
||||
self._tags = {}
|
||||
self._current_tag_name = None
|
||||
self._values = None
|
||||
self._collect(lines)
|
||||
|
||||
def get_tags(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self, lines):
|
||||
|
@ -218,32 +233,36 @@ class PwscfIn(object):
|
|||
tag_name = None
|
||||
|
||||
for line in lines:
|
||||
_line = line.split('!')[0]
|
||||
if ('atomic_positions' in _line.lower() or
|
||||
'cell_parameters' in _line.lower()):
|
||||
_line = line.split("!")[0]
|
||||
if (
|
||||
"atomic_positions" in _line.lower()
|
||||
or "cell_parameters" in _line.lower()
|
||||
):
|
||||
if len(_line.split()) == 1:
|
||||
raise RuntimeError(
|
||||
"A unit has to be specified for %s." % _line.strip())
|
||||
"A unit has to be specified for %s." % _line.strip()
|
||||
)
|
||||
else:
|
||||
words = _line.split()[:2]
|
||||
elif 'atomic_species' in _line.lower():
|
||||
elif "atomic_species" in _line.lower():
|
||||
words = _line.split()
|
||||
else: # other tag names and values
|
||||
line_replaced = _line.replace('=', ' ').replace(',', ' ')
|
||||
line_replaced = _line.replace("=", " ").replace(",", " ")
|
||||
words = line_replaced.split()
|
||||
|
||||
for val in words:
|
||||
if val.lower() in self._set_methods: # tag name
|
||||
tag_name = val.lower()
|
||||
elements[tag_name] = [val, ]
|
||||
elements[tag_name] = [
|
||||
val,
|
||||
]
|
||||
elif tag_name is not None: # Ensure some tag name is set.
|
||||
elements[tag_name].append(val)
|
||||
|
||||
# Check if some necessary tag_names exist in elements keys.
|
||||
for tag_name in ['ibrav', 'nat', 'ntyp']:
|
||||
for tag_name in ["ibrav", "nat", "ntyp"]:
|
||||
if tag_name not in elements:
|
||||
raise RuntimeError(
|
||||
"%s is not found in the input file." % tag_name)
|
||||
raise RuntimeError("%s is not found in the input file." % tag_name)
|
||||
|
||||
# Set values in self._tags[tag_name]
|
||||
for tag_name in self._set_methods:
|
||||
|
@ -256,19 +275,18 @@ class PwscfIn(object):
|
|||
def _set_ibrav(self):
|
||||
ibrav = int(self._values[0])
|
||||
if ibrav != 0:
|
||||
raise RuntimeError("Only %s = 0 is supported."
|
||||
% self._current_tag_name)
|
||||
raise RuntimeError("Only %s = 0 is supported." % self._current_tag_name)
|
||||
|
||||
self._tags['ibrav'] = ibrav
|
||||
self._tags["ibrav"] = ibrav
|
||||
|
||||
def _set_celldm1(self):
|
||||
self._tags['celldm(1)'] = float(self._values[0])
|
||||
self._tags["celldm(1)"] = float(self._values[0])
|
||||
|
||||
def _set_nat(self):
|
||||
self._tags['nat'] = int(self._values[0])
|
||||
self._tags["nat"] = int(self._values[0])
|
||||
|
||||
def _set_ntyp(self):
|
||||
self._tags['ntyp'] = int(self._values[0])
|
||||
self._tags["ntyp"] = int(self._values[0])
|
||||
|
||||
def _set_lattice(self):
|
||||
"""Calculate and set lattice parameters.
|
||||
|
@ -276,35 +294,33 @@ class PwscfIn(object):
|
|||
Invoked by CELL_PARAMETERS tag_name.
|
||||
|
||||
"""
|
||||
|
||||
unit = self._values[0].lower()
|
||||
if unit == 'alat':
|
||||
if not self._tags['celldm(1)']:
|
||||
raise RuntimeError(
|
||||
"celldm(1) has to be specified when using alat.")
|
||||
if unit == "alat":
|
||||
if not self._tags["celldm(1)"]:
|
||||
raise RuntimeError("celldm(1) has to be specified when using alat.")
|
||||
else:
|
||||
factor = self._tags['celldm(1)'] # in Bohr
|
||||
elif 'angstrom' in unit:
|
||||
factor = self._tags["celldm(1)"] # in Bohr
|
||||
elif "angstrom" in unit:
|
||||
factor = 1.0 / Bohr
|
||||
elif 'bohr' in unit:
|
||||
elif "bohr" in unit:
|
||||
factor = 1.0
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"As a unit, alat, angstrom, and bohr can be only used.")
|
||||
raise RuntimeError("As a unit, alat, angstrom, and bohr can be only used.")
|
||||
|
||||
if len(self._values[1:]) < 9:
|
||||
raise RuntimeError("%s is wrongly set." % self._current_tag_name)
|
||||
|
||||
lattice = np.reshape([float(x) for x in self._values[1:10]], (3, 3))
|
||||
self._tags['cell_parameters'] = lattice * factor
|
||||
self._tags["cell_parameters"] = lattice * factor
|
||||
|
||||
def _set_positions(self):
|
||||
unit = self._values[0].lower()
|
||||
if 'crystal' not in unit:
|
||||
raise RuntimeError("Only ATOMIC_POSITIONS format with "
|
||||
"crystal coordinates is supported.")
|
||||
if "crystal" not in unit:
|
||||
raise RuntimeError(
|
||||
"Only ATOMIC_POSITIONS format with " "crystal coordinates is supported."
|
||||
)
|
||||
|
||||
natom = self._tags['nat']
|
||||
natom = self._tags["nat"]
|
||||
pos_vals = self._values[1:]
|
||||
if len(pos_vals) < natom * 4:
|
||||
raise RuntimeError("ATOMIC_POSITIONS is wrongly set.")
|
||||
|
@ -312,29 +328,34 @@ class PwscfIn(object):
|
|||
positions = []
|
||||
for i in range(natom):
|
||||
positions.append(
|
||||
[pos_vals[i * 4],
|
||||
[float(x) for x in pos_vals[i * 4 + 1:i * 4 + 4]]])
|
||||
[pos_vals[i * 4], [float(x) for x in pos_vals[i * 4 + 1 : i * 4 + 4]]]
|
||||
)
|
||||
|
||||
self._tags['atomic_positions'] = positions
|
||||
self._tags["atomic_positions"] = positions
|
||||
|
||||
def _set_atom_types(self):
|
||||
num_types = self._tags['ntyp']
|
||||
num_types = self._tags["ntyp"]
|
||||
if len(self._values) < num_types * 3:
|
||||
raise RuntimeError("%s is wrongly set." % self._current_tag_name)
|
||||
|
||||
species = []
|
||||
|
||||
for i in range(num_types):
|
||||
species.append([self._values[i * 3],
|
||||
species.append(
|
||||
[
|
||||
self._values[i * 3],
|
||||
float(self._values[i * 3 + 1]),
|
||||
self._values[i * 3 + 2]])
|
||||
self._values[i * 3 + 2],
|
||||
]
|
||||
)
|
||||
|
||||
self._tags['atomic_species'] = species
|
||||
self._tags["atomic_species"] = species
|
||||
|
||||
|
||||
class PH_Q2R(object):
|
||||
"""Parse QE/q2r output and create supercell force constants array
|
||||
that is readable by phonopy. A simple usage is as follows:
|
||||
class PH_Q2R:
|
||||
"""Parse QE/q2r output and create supercell force constants array.
|
||||
|
||||
A simple usage is as follows:
|
||||
|
||||
---------
|
||||
#!/usr/bin/env python
|
||||
|
@ -397,6 +418,7 @@ class PH_Q2R(object):
|
|||
"""
|
||||
|
||||
def __init__(self, filename, symprec=1e-5):
|
||||
"""Init method."""
|
||||
self.fc = None
|
||||
self.dimension = None
|
||||
self.epsilon = None
|
||||
|
@ -407,7 +429,7 @@ class PH_Q2R(object):
|
|||
self._filename = filename
|
||||
|
||||
def run(self, cell, is_full_fc=False, parse_fc=True):
|
||||
"""Make supercell force constants readable for phonopy
|
||||
"""Make supercell force constants readable for phonopy.
|
||||
|
||||
Note
|
||||
----
|
||||
|
@ -427,28 +449,26 @@ class PH_Q2R(object):
|
|||
False may be used when expected to parse only epsilon and born.
|
||||
|
||||
"""
|
||||
|
||||
with open(self._filename) as f:
|
||||
fc_dct = self._parse_q2r(f)
|
||||
self.dimension = fc_dct['dimension']
|
||||
self.epsilon = fc_dct['dielectric']
|
||||
self.borns = fc_dct['born']
|
||||
self.dimension = fc_dct["dimension"]
|
||||
self.epsilon = fc_dct["dielectric"]
|
||||
self.borns = fc_dct["born"]
|
||||
if parse_fc:
|
||||
(self.fc,
|
||||
self.primitive,
|
||||
self.supercell) = self._arrange_supercell_fc(
|
||||
cell, fc_dct['fc'], is_full_fc=is_full_fc)
|
||||
(self.fc, self.primitive, self.supercell) = self._arrange_supercell_fc(
|
||||
cell, fc_dct["fc"], is_full_fc=is_full_fc
|
||||
)
|
||||
|
||||
def write_force_constants(self, fc_format='hdf5'):
|
||||
def write_force_constants(self, fc_format="hdf5"):
|
||||
"""Write force constatns to file in hdf5."""
|
||||
if self.fc is not None:
|
||||
if fc_format == 'hdf5':
|
||||
p2s_map = self.primitive.get_primitive_to_supercell_map()
|
||||
write_force_constants_to_hdf5(self.fc, p2s_map=p2s_map)
|
||||
if fc_format == "hdf5":
|
||||
write_force_constants_to_hdf5(self.fc, p2s_map=self.primitive.p2s_map)
|
||||
else:
|
||||
write_FORCE_CONSTANTS(self.fc)
|
||||
|
||||
def _parse_q2r(self, f):
|
||||
"""Parse q2r output file
|
||||
"""Parse q2r output file.
|
||||
|
||||
The format of q2r output is described at the mailing list below:
|
||||
http://www.democritos.it/pipermail/pw_forum/2005-April/002408.html
|
||||
|
@ -457,12 +477,13 @@ class PH_Q2R(object):
|
|||
https://www.mail-archive.com/pw_forum@pwscf.org/msg24388.html
|
||||
|
||||
"""
|
||||
|
||||
natom, dim, epsilon, borns = self._parse_parameters(f)
|
||||
fc_dct = {'fc': self._parse_fc(f, natom, dim),
|
||||
'dimension': dim,
|
||||
'dielectric': epsilon,
|
||||
'born': borns}
|
||||
fc_dct = {
|
||||
"fc": self._parse_fc(f, natom, dim),
|
||||
"dimension": dim,
|
||||
"dielectric": epsilon,
|
||||
"born": borns,
|
||||
}
|
||||
return fc_dct
|
||||
|
||||
def _parse_parameters(self, f):
|
||||
|
@ -474,19 +495,19 @@ class PH_Q2R(object):
|
|||
for i in range(ntype + natom):
|
||||
line = f.readline()
|
||||
line = f.readline()
|
||||
if line.strip() == 'T':
|
||||
if line.strip() == "T":
|
||||
epsilon, borns = self._parse_born(f, natom)
|
||||
else:
|
||||
epsilon = None
|
||||
borns = None
|
||||
line = f.readline()
|
||||
dim = np.array([int(x) for x in line.split()], dtype='intc')
|
||||
dim = np.array([int(x) for x in line.split()], dtype="intc")
|
||||
|
||||
return natom, dim, epsilon, borns
|
||||
|
||||
def _parse_born(self, f, natom):
|
||||
epsilon = np.zeros((3, 3), dtype='double', order='C')
|
||||
borns = np.zeros((natom, 3, 3), dtype='double', order='C')
|
||||
epsilon = np.zeros((3, 3), dtype="double", order="C")
|
||||
borns = np.zeros((natom, 3, 3), dtype="double", order="C")
|
||||
for i in range(3):
|
||||
line = f.readline()
|
||||
epsilon[i, :] = [float(x) for x in line.split()]
|
||||
|
@ -498,14 +519,13 @@ class PH_Q2R(object):
|
|||
return epsilon, borns
|
||||
|
||||
def _parse_fc(self, f, natom, dim):
|
||||
"""Parse force constants part
|
||||
"""Parse force constants part.
|
||||
|
||||
Physical unit of force cosntants in the file is Ry/au^2.
|
||||
|
||||
"""
|
||||
|
||||
ndim = np.prod(dim)
|
||||
fc = np.zeros((natom, natom * ndim, 3, 3), dtype='double', order='C')
|
||||
fc = np.zeros((natom, natom * ndim, 3, 3), dtype="double", order="C")
|
||||
for k, l, i, j in np.ndindex((3, 3, natom, natom)):
|
||||
line = f.readline()
|
||||
for i_dim in range(ndim):
|
||||
|
@ -525,22 +545,20 @@ class PH_Q2R(object):
|
|||
assert (np.abs(diff) < 1e-8).all()
|
||||
assert scell.get_number_of_atoms() == len(q2r_spos)
|
||||
|
||||
site_map = self._get_site_mapping(scell.get_scaled_positions(),
|
||||
q2r_spos,
|
||||
scell.get_cell())
|
||||
site_map = self._get_site_mapping(
|
||||
scell.get_scaled_positions(), q2r_spos, scell.get_cell()
|
||||
)
|
||||
natom = pcell.get_number_of_atoms()
|
||||
ndim = np.prod(dim)
|
||||
natom_s = natom * ndim
|
||||
|
||||
if is_full_fc:
|
||||
fc = np.zeros((natom_s, natom_s, 3, 3), dtype='double', order='C')
|
||||
fc = np.zeros((natom_s, natom_s, 3, 3), dtype="double", order="C")
|
||||
p2s = pcell.get_primitive_to_supercell_map()
|
||||
fc[p2s, :] = q2r_fc[:, site_map]
|
||||
distribute_force_constants_by_translations(fc,
|
||||
pcell,
|
||||
scell)
|
||||
distribute_force_constants_by_translations(fc, pcell, scell)
|
||||
else:
|
||||
fc = np.zeros((natom, natom_s, 3, 3), dtype='double', order='C')
|
||||
fc = np.zeros((natom, natom_s, 3, 3), dtype="double", order="C")
|
||||
fc[:, :] = q2r_fc[:, site_map]
|
||||
|
||||
return fc, pcell, scell
|
||||
|
@ -549,10 +567,10 @@ class PH_Q2R(object):
|
|||
dim = self.dimension
|
||||
natom = cell.get_number_of_atoms()
|
||||
ndim = np.prod(dim)
|
||||
spos = np.zeros((natom * np.prod(dim), 3), dtype='double', order='C')
|
||||
spos = np.zeros((natom * np.prod(dim), 3), dtype="double", order="C")
|
||||
trans = [x[::-1] for x in np.ndindex(tuple(dim[::-1]))]
|
||||
for i, p in enumerate(cell.get_scaled_positions()):
|
||||
spos[i * ndim:(i + 1) * ndim] = (trans + p) / dim
|
||||
spos[i * ndim : (i + 1) * ndim] = (trans + p) / dim
|
||||
return spos
|
||||
|
||||
def _get_site_mapping(self, spos, q2r_spos, lattice):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SIESTA calculator interface."""
|
||||
# Copyright (C) 2015 Henrique Pereira Coutada Miranda
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,32 +33,32 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import numpy as np
|
||||
import re
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.file_IO import iter_collect_forces
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.units import Bohr
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.units import Bohr
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
hook = '' # Just for skipping the first line
|
||||
"""Parse forces from output files."""
|
||||
hook = "" # Just for skipping the first line
|
||||
is_parsed = True
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
siesta_forces = iter_collect_forces(filename,
|
||||
num_atoms,
|
||||
hook,
|
||||
[1, 2, 3],
|
||||
word='')
|
||||
siesta_forces = iter_collect_forces(
|
||||
filename, num_atoms, hook, [1, 2, 3], word=""
|
||||
)
|
||||
if check_forces(siesta_forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(siesta_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
siesta_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(siesta_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -69,6 +70,7 @@ def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
|||
|
||||
|
||||
def read_siesta(filename):
|
||||
"""Read crystal structure."""
|
||||
siesta_in = SiestaIn(open(filename).read())
|
||||
numbers = siesta_in._tags["atomicnumbers"]
|
||||
alat = siesta_in._tags["latticeconstant"]
|
||||
|
@ -87,32 +89,35 @@ def read_siesta(filename):
|
|||
elif coordformat == "notscaledcartesianbohr" or coordformat == "bohr":
|
||||
cell.set_positions(np.array(positions))
|
||||
else:
|
||||
print("The format %s for the AtomicCoordinatesFormat is not "
|
||||
"implemented." % coordformat)
|
||||
print(
|
||||
"The format %s for the AtomicCoordinatesFormat is not "
|
||||
"implemented." % coordformat
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
return cell, atypes
|
||||
|
||||
|
||||
def write_siesta(filename, cell, atypes):
|
||||
with open(filename, 'w') as w:
|
||||
"""Write cell to file."""
|
||||
with open(filename, "w") as w:
|
||||
w.write(get_siesta_structure(cell, atypes))
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
atypes,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
def write_supercells_with_displacements(
|
||||
supercell, cells_with_displacements, ids, atypes, pre_filename="supercell", width=3
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_siesta("%s.fdf" % pre_filename, supercell, atypes)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}.fdf".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_siesta(filename, cell, atypes)
|
||||
|
||||
|
||||
def get_siesta_structure(cell,atypes):
|
||||
def get_siesta_structure(cell, atypes):
|
||||
"""Return SIESTA structure in text."""
|
||||
lattice = cell.get_cell()
|
||||
positions = cell.get_scaled_positions()
|
||||
chemical_symbols = cell.get_chemical_symbols()
|
||||
|
@ -131,100 +136,119 @@ def get_siesta_structure(cell,atypes):
|
|||
|
||||
lines += "%block AtomicCoordinatesAndAtomicSpecies\n"
|
||||
for pos, i in zip(positions, chemical_symbols):
|
||||
lines += ("%21.16lf"*3+" %d\n") % tuple(pos.tolist()+[atypes[i]])
|
||||
lines += ("%21.16lf" * 3 + " %d\n") % tuple(pos.tolist() + [atypes[i]])
|
||||
lines += "%endblock AtomicCoordinatesAndAtomicSpecies\n"
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
class SiestaIn(object):
|
||||
_num_regex = '([+-]?\d+(?:\.\d*)?(?:[eE][-+]?\d+)?)'
|
||||
_tags = { "latticeconstant": 1.0,
|
||||
class SiestaIn:
|
||||
"""Class to create SIESTA input file."""
|
||||
|
||||
_num_regex = r"([+-]?\d+(?:\.\d*)?(?:[eE][-+]?\d+)?)"
|
||||
_tags = {
|
||||
"latticeconstant": 1.0,
|
||||
"latticeconstantunit": None,
|
||||
"chemicalspecieslabel": None,
|
||||
"atomiccoordinatesformat": None,
|
||||
"atomicnumbers": None,
|
||||
"atomicspecies": None,
|
||||
"atomiccoordinates": None }
|
||||
"atomiccoordinates": None,
|
||||
}
|
||||
|
||||
def __init__(self, lines):
|
||||
"""Init method."""
|
||||
self._collect(lines)
|
||||
|
||||
def _collect(self, lines):
|
||||
""" This routine reads the following from the Siesta file:
|
||||
"""Collect values.
|
||||
|
||||
This routine reads the following from the Siesta file:
|
||||
- atomic positions
|
||||
- cell_parameters
|
||||
- atomic_species
|
||||
|
||||
"""
|
||||
for tag, value, unit in re.findall(
|
||||
r'([\.A-Za-z]+)\s+%s\s+([A-Za-z]+)?' %
|
||||
self._num_regex, lines):
|
||||
r"([\.A-Za-z]+)\s+%s\s+([A-Za-z]+)?" % self._num_regex, lines
|
||||
):
|
||||
tag = tag.lower()
|
||||
unit = unit.lower()
|
||||
if tag == "latticeconstant":
|
||||
self._tags['latticeconstantunit'] = unit.capitalize()
|
||||
if unit == 'ang':
|
||||
self._tags["latticeconstantunit"] = unit.capitalize()
|
||||
if unit == "ang":
|
||||
self._tags[tag] = float(value) / Bohr
|
||||
elif unit == 'bohr':
|
||||
elif unit == "bohr":
|
||||
self._tags[tag] = float(value)
|
||||
else:
|
||||
raise ValueError(
|
||||
'Unknown LatticeConstant unit: {}'.format(unit))
|
||||
raise ValueError("Unknown LatticeConstant unit: {}".format(unit))
|
||||
|
||||
for tag, value in re.findall(r'([\.A-Za-z]+)[ \t]+([a-zA-Z]+)', lines):
|
||||
tag = tag.replace('_', '').lower()
|
||||
for tag, value in re.findall(r"([\.A-Za-z]+)[ \t]+([a-zA-Z]+)", lines):
|
||||
tag = tag.replace("_", "").lower()
|
||||
if tag == "atomiccoordinatesformat":
|
||||
self._tags[tag] = value.strip().lower()
|
||||
|
||||
# check if the necessary tags are present
|
||||
self.check_present('atomiccoordinatesformat')
|
||||
acell = self._tags['latticeconstant']
|
||||
self._check_present("atomiccoordinatesformat")
|
||||
acell = self._tags["latticeconstant"]
|
||||
|
||||
# capture the blocks
|
||||
blocks = re.findall(
|
||||
r'%block\s+([A-Za-z_]+)\s*\n((?:.+\n)+?(?=(?:\s+)?%endblock))',
|
||||
lines, re.MULTILINE)
|
||||
r"%block\s+([A-Za-z_]+)\s*\n((?:.+\n)+?(?=(?:\s+)?%endblock))",
|
||||
lines,
|
||||
re.MULTILINE,
|
||||
)
|
||||
for tag, block in blocks:
|
||||
tag = tag.replace('_','').lower()
|
||||
tag = tag.replace("_", "").lower()
|
||||
if tag == "chemicalspecieslabel":
|
||||
block_array = block.split('\n')[:-1]
|
||||
block_array = block.split("\n")[:-1]
|
||||
self._tags["atomicnumbers"] = dict(
|
||||
[map(int, species.split()[:2]) for species in block_array])
|
||||
[map(int, species.split()[:2]) for species in block_array]
|
||||
)
|
||||
self._tags[tag] = dict(
|
||||
[(lambda x: (x[2], int(x[0])))(species.split())
|
||||
for species in block_array])
|
||||
[
|
||||
(lambda x: (x[2], int(x[0])))(species.split())
|
||||
for species in block_array
|
||||
]
|
||||
)
|
||||
elif tag == "latticevectors":
|
||||
self._tags[tag] = [[float(v)*acell for v in vector.split()]
|
||||
for vector in block.split('\n')[:3]]
|
||||
self._tags[tag] = [
|
||||
[float(v) * acell for v in vector.split()]
|
||||
for vector in block.split("\n")[:3]
|
||||
]
|
||||
elif tag == "atomiccoordinatesandatomicspecies":
|
||||
block_array = block.split('\n')[:-1]
|
||||
block_array = block.split("\n")[:-1]
|
||||
self._tags["atomiccoordinates"] = [
|
||||
[float(x) for x in atom.split()[:3]] for atom in block_array]
|
||||
self._tags["atomicspecies"] = [int(atom.split()[3])
|
||||
for atom in block_array]
|
||||
[float(x) for x in atom.split()[:3]] for atom in block_array
|
||||
]
|
||||
self._tags["atomicspecies"] = [
|
||||
int(atom.split()[3]) for atom in block_array
|
||||
]
|
||||
|
||||
# check if the block are present
|
||||
self.check_present("atomicspecies")
|
||||
self.check_present("atomiccoordinates")
|
||||
self.check_present("latticevectors")
|
||||
self.check_present("chemicalspecieslabel")
|
||||
self._check_present("atomicspecies")
|
||||
self._check_present("atomiccoordinates")
|
||||
self._check_present("latticevectors")
|
||||
self._check_present("chemicalspecieslabel")
|
||||
|
||||
# translate the atomicspecies to atomic numbers
|
||||
self._tags["atomicnumbers"] = [
|
||||
self._tags["atomicnumbers"][atype]
|
||||
for atype in self._tags["atomicspecies"]]
|
||||
self._tags["atomicnumbers"][atype] for atype in self._tags["atomicspecies"]
|
||||
]
|
||||
|
||||
def check_present(self, tag):
|
||||
def _check_present(self, tag):
|
||||
if not self._tags[tag]:
|
||||
print("%s not present" % tag)
|
||||
sys.exit(1)
|
||||
|
||||
def __str__(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
|
||||
cell, atypes = read_siesta(sys.argv[1])
|
||||
symmetry = Symmetry(cell)
|
||||
print("# %s" % symmetry.get_international_table())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""CRYSTAL calculator interface."""
|
||||
# Copyright (C) 2019 Antti J. Karttunen (antti.j.karttunen@iki.fi)
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,36 +33,36 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
|
||||
|
||||
def parse_set_of_forces(num_atoms,
|
||||
forces_filenames,
|
||||
verbose=True):
|
||||
def parse_set_of_forces(num_atoms, forces_filenames, verbose=True):
|
||||
"""Parse forces from output files."""
|
||||
# Filenames = subdirectories supercell-001, supercell-002, ...
|
||||
force_sets = []
|
||||
for i, filename in enumerate(forces_filenames):
|
||||
if verbose:
|
||||
sys.stdout.write("%d. " % (i + 1))
|
||||
|
||||
f_gradient = open(os.path.join(filename, 'gradient'))
|
||||
f_gradient = open(os.path.join(filename, "gradient"))
|
||||
lines = f_gradient.readlines()
|
||||
f_gradient.close()
|
||||
# Structure of the gradient file:
|
||||
#$grad cartesian gradients
|
||||
# $grad cartesian gradients
|
||||
# cycle = 1 SCF energy = -578.5931883878 |dE/dxyz| = 0.000007
|
||||
# coordinates (num_atoms lines)
|
||||
# gradients (num_atoms lines)
|
||||
#$end
|
||||
# $end
|
||||
turbomole_forces = []
|
||||
for line in lines[2 + num_atoms : 2 + 2 * num_atoms]:
|
||||
# Replace D with E in double precision floats
|
||||
turbomole_forces.append([float(x.replace('D', 'E')) for x in line.split()])
|
||||
turbomole_forces.append([float(x.replace("D", "E")) for x in line.split()])
|
||||
|
||||
# Change from gradient to force by inverting the sign
|
||||
# Units: hartree / Bohr
|
||||
|
@ -69,9 +70,9 @@ def parse_set_of_forces(num_atoms,
|
|||
|
||||
if check_forces(turbomole_forces, num_atoms, filename, verbose=verbose):
|
||||
is_parsed = True
|
||||
drift_force = get_drift_forces(turbomole_forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(
|
||||
turbomole_forces, filename=filename, verbose=verbose
|
||||
)
|
||||
force_sets.append(np.array(turbomole_forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -83,138 +84,155 @@ def parse_set_of_forces(num_atoms,
|
|||
|
||||
|
||||
def read_turbomole(filename):
|
||||
"""Read crystal structure."""
|
||||
# filename is typically "control"
|
||||
f_turbomole = open(filename)
|
||||
turbomole_in = TurbomoleIn(f_turbomole.readlines())
|
||||
f_turbomole.close()
|
||||
tags = turbomole_in.get_tags()
|
||||
cell = Atoms(cell=tags['lattice_vectors'],
|
||||
symbols=tags['atomic_species'],
|
||||
positions=tags['coordinates'])
|
||||
cell = Atoms(
|
||||
cell=tags["lattice_vectors"],
|
||||
symbols=tags["atomic_species"],
|
||||
positions=tags["coordinates"],
|
||||
)
|
||||
|
||||
return cell
|
||||
|
||||
|
||||
def write_turbomole(filename, cell):
|
||||
"""Write cell to file."""
|
||||
# Write geometry in a new directory
|
||||
# Check if directory exists (directory supercell will already exist for phono3py)
|
||||
if not os.path.exists(filename):
|
||||
os.mkdir(filename)
|
||||
|
||||
# Create control file
|
||||
lines = '$title ' + filename + '\n'
|
||||
lines += '$symmetry c1\n'
|
||||
lines += '$coord file=coord\n'
|
||||
lines += '$periodic 3\n'
|
||||
lines += '$kpoints\n'
|
||||
lines += ' nkpoints KPOINTS_HERE\n'
|
||||
lines += '$scfconv 10\n'
|
||||
lines += '$lattice\n'
|
||||
lines = "$title " + filename + "\n"
|
||||
lines += "$symmetry c1\n"
|
||||
lines += "$coord file=coord\n"
|
||||
lines += "$periodic 3\n"
|
||||
lines += "$kpoints\n"
|
||||
lines += " nkpoints KPOINTS_HERE\n"
|
||||
lines += "$scfconv 10\n"
|
||||
lines += "$lattice\n"
|
||||
lattice = cell.get_cell()
|
||||
for lattvec in lattice:
|
||||
lines += ("%12.8f" * 3 + "\n") % tuple(lattvec)
|
||||
lines += '$end\n'
|
||||
f_control = open(os.path.join(filename, 'control'), 'w')
|
||||
lines += "$end\n"
|
||||
f_control = open(os.path.join(filename, "control"), "w")
|
||||
f_control.write(lines)
|
||||
f_control.close()
|
||||
|
||||
# Create coord file
|
||||
symbols = cell.get_chemical_symbols()
|
||||
positions = cell.get_positions()
|
||||
lines = '$coord\n'
|
||||
lines = "$coord\n"
|
||||
for atom, pos in zip(symbols, positions):
|
||||
lines += ("%16.12f"*3 + " %s\n") % (pos[0], pos[1], pos[2], atom.lower())
|
||||
lines += '$end\n'
|
||||
f_coord = open(os.path.join(filename, 'coord'), 'w')
|
||||
lines += ("%16.12f" * 3 + " %s\n") % (pos[0], pos[1], pos[2], atom.lower())
|
||||
lines += "$end\n"
|
||||
f_coord = open(os.path.join(filename, "coord"), "w")
|
||||
f_coord.write(lines)
|
||||
f_coord.close()
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
pre_filename="supercell",
|
||||
width=3):
|
||||
|
||||
def write_supercells_with_displacements(
|
||||
supercell, cells_with_displacements, ids, pre_filename="supercell", width=3
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
write_turbomole(pre_filename, supercell)
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
filename = "{pre_filename}-{0:0{width}}".format(
|
||||
i, pre_filename=pre_filename, width=width)
|
||||
i, pre_filename=pre_filename, width=width
|
||||
)
|
||||
write_turbomole(filename, cell)
|
||||
|
||||
|
||||
class TurbomoleIn:
|
||||
"""Class to create TURBOMOLE input file."""
|
||||
|
||||
def __init__(self, lines):
|
||||
self._tags = {'lattice_vectors': None,
|
||||
'atomic_species': None,
|
||||
'coordinates': None}
|
||||
"""Init method."""
|
||||
self._tags = {
|
||||
"lattice_vectors": None,
|
||||
"atomic_species": None,
|
||||
"coordinates": None,
|
||||
}
|
||||
|
||||
self._values = None
|
||||
self._collect(lines)
|
||||
|
||||
def get_tags(self):
|
||||
"""Return tags."""
|
||||
return self._tags
|
||||
|
||||
def _collect(self, lines):
|
||||
# Reads TURBOMOLE control and coord files (lattice vectors, cartesian atomic positions).
|
||||
l = 0
|
||||
# Reads TURBOMOLE control and coord files
|
||||
# (lattice vectors, cartesian atomic positions).
|
||||
ll = 0
|
||||
lattvecs = []
|
||||
aspecies = []
|
||||
coords = []
|
||||
while l < len(lines):
|
||||
line = lines[l]
|
||||
while ll < len(lines):
|
||||
line = lines[ll]
|
||||
# Look for lattice vectors
|
||||
# Only supports atomic units, $lattice angs not supported
|
||||
if '$lattice' in line:
|
||||
if "$lattice" in line:
|
||||
# 0.00000000000 5.17898186576 5.17898186576
|
||||
for lattvec in lines[l+1:l+4]:
|
||||
for lattvec in lines[ll + 1 : ll + 4]:
|
||||
lattvecs.append([float(x) for x in lattvec.split()])
|
||||
l += 4
|
||||
# Look for Cartesian coordinates. They can be in another file or embedded in control:
|
||||
#1) $coord file=coord
|
||||
#2) $coord
|
||||
ll += 4
|
||||
# Look for Cartesian coordinates.
|
||||
# They can be in another file or embedded in control:
|
||||
# 1) $coord file=coord
|
||||
# 2) $coord
|
||||
# 2.58949092075 2.58949092075 2.58949092075 si
|
||||
elif '$coord' in line:
|
||||
if line.strip() == '$coord':
|
||||
elif "$coord" in line:
|
||||
if line.strip() == "$coord":
|
||||
# Embdedded coordinates.
|
||||
l += 1
|
||||
while l < len(lines):
|
||||
atom = lines[l].split()
|
||||
ll += 1
|
||||
while ll < len(lines):
|
||||
atom = lines[ll].split()
|
||||
if len(atom) == 4:
|
||||
coords.append([float(x) for x in atom[0:3]])
|
||||
aspecies.append(atom[3].title()) # Convert si to Si, c to C, etc.
|
||||
l += 1
|
||||
aspecies.append(
|
||||
atom[3].title()
|
||||
) # Convert si to Si, c to C, etc.
|
||||
ll += 1
|
||||
else:
|
||||
# End of $coord, go back to the main while loop to interpret the current line
|
||||
# End of $coord, go back to the main while loop to interpret
|
||||
# the current line
|
||||
break
|
||||
elif line.find('file=') > 6:
|
||||
elif line.find("file=") > 6:
|
||||
# Cross-reference to another file
|
||||
coordfile = line.split('=')[1].strip()
|
||||
coordfile = line.split("=")[1].strip()
|
||||
f_coord = open(coordfile)
|
||||
for coordline in f_coord:
|
||||
# 2.58949092075 2.58949092075 2.58949092075 si
|
||||
atom = coordline.split()
|
||||
if len(atom) == 4:
|
||||
coords.append([float(x) for x in atom[0:3]])
|
||||
aspecies.append(atom[3].title()) # Convert si to Si, c to C, etc.
|
||||
aspecies.append(
|
||||
atom[3].title()
|
||||
) # Convert si to Si, c to C, etc.
|
||||
f_coord.close()
|
||||
l += 1
|
||||
ll += 1
|
||||
else:
|
||||
# $coordinateupdate or invalid $coord line
|
||||
l += 1
|
||||
ll += 1
|
||||
else:
|
||||
l += 1
|
||||
ll += 1
|
||||
|
||||
if len(lattvecs) == 3 and len(aspecies) > 0 and len(aspecies) == len(coords):
|
||||
self._tags['lattice_vectors'] = lattvecs
|
||||
self._tags['atomic_species'] = aspecies
|
||||
self._tags['coordinates'] = coords
|
||||
self._tags["lattice_vectors"] = lattvecs
|
||||
self._tags["atomic_species"] = aspecies
|
||||
self._tags["coordinates"] = coords
|
||||
else:
|
||||
print("TURBOMOLE-interface: Error parsing TURBOMOLE output file")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
|
||||
cell = read_turbomole(sys.argv[1])
|
||||
symmetry = Symmetry(cell)
|
||||
print("# %s" % symmetry.get_international_table())
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
|||
"""Wien2k calculator interface."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,20 +34,25 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from phonopy.interface.vasp import get_drift_forces, check_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
from phonopy.structure.cells import get_angles, get_cell_parameters
|
||||
|
||||
from phonopy.harmonic.force_constants import similarity_transformation
|
||||
from phonopy.interface.vasp import check_forces, get_drift_forces
|
||||
from phonopy.structure.atoms import PhonopyAtoms as Atoms
|
||||
from phonopy.structure.cells import get_angles, get_cell_parameters
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
|
||||
|
||||
def parse_set_of_forces(disps,
|
||||
def parse_set_of_forces(
|
||||
disps,
|
||||
forces_filenames,
|
||||
supercell,
|
||||
wien2k_P1_mode=False, # Only for the test
|
||||
symmetry_tolerance=None,
|
||||
verbose=True):
|
||||
verbose=True,
|
||||
):
|
||||
"""Parse forces from output files."""
|
||||
if symmetry_tolerance is None:
|
||||
symprec = 1e-5
|
||||
else:
|
||||
|
@ -65,11 +71,8 @@ def parse_set_of_forces(disps,
|
|||
wien2k_forces = _get_forces_wien2k(filename, lattice)
|
||||
if not wien2k_P1_mode:
|
||||
forces = _distribute_forces(
|
||||
supercell,
|
||||
disp,
|
||||
wien2k_forces,
|
||||
filename,
|
||||
symprec)
|
||||
supercell, disp, wien2k_forces, filename, symprec
|
||||
)
|
||||
else:
|
||||
if num_atoms != len(wien2k_forces):
|
||||
forces = []
|
||||
|
@ -77,9 +80,7 @@ def parse_set_of_forces(disps,
|
|||
forces = wien2k_forces
|
||||
|
||||
if check_forces(forces, num_atoms, filename, verbose=verbose):
|
||||
drift_force = get_drift_forces(forces,
|
||||
filename=filename,
|
||||
verbose=verbose)
|
||||
drift_force = get_drift_forces(forces, filename=filename, verbose=verbose)
|
||||
force_sets.append(np.array(forces) - drift_force)
|
||||
else:
|
||||
is_parsed = False
|
||||
|
@ -91,9 +92,10 @@ def parse_set_of_forces(disps,
|
|||
|
||||
|
||||
def parse_wien2k_struct(filename):
|
||||
"""Read crystal structure."""
|
||||
with open(filename) as f:
|
||||
# 1
|
||||
title = f.readline().rstrip()
|
||||
_ = f.readline().rstrip()
|
||||
|
||||
# 2
|
||||
num_site = int(f.readline()[27:30])
|
||||
|
@ -154,51 +156,50 @@ def parse_wien2k_struct(filename):
|
|||
for j in range(3):
|
||||
f.readline()
|
||||
|
||||
cell = Atoms(symbols=symbols,
|
||||
scaled_positions=positions,
|
||||
cell=lattice)
|
||||
cell = Atoms(symbols=symbols, scaled_positions=positions, cell=lattice)
|
||||
|
||||
return cell, npts, r0s, rmts
|
||||
|
||||
|
||||
def write_supercells_with_displacements(supercell,
|
||||
def write_supercells_with_displacements(
|
||||
supercell,
|
||||
cells_with_displacements,
|
||||
ids,
|
||||
npts, r0s, rmts,
|
||||
npts,
|
||||
r0s,
|
||||
rmts,
|
||||
num_unitcells_in_supercell,
|
||||
pre_filename="wien2k",
|
||||
width=3):
|
||||
width=3,
|
||||
):
|
||||
"""Write supercells with displacements to files."""
|
||||
npts_super = []
|
||||
r0s_super = []
|
||||
rmts_super = []
|
||||
for i, j, k in zip(npts, r0s, rmts):
|
||||
for l in range(num_unitcells_in_supercell):
|
||||
for _ in range(num_unitcells_in_supercell):
|
||||
npts_super.append(i)
|
||||
r0s_super.append(j)
|
||||
rmts_super.append(k)
|
||||
|
||||
_pre_filename = pre_filename.split('/')[-1] + "S"
|
||||
write_wein2k(_pre_filename,
|
||||
supercell,
|
||||
npts_super,
|
||||
r0s_super,
|
||||
rmts_super)
|
||||
_pre_filename = pre_filename.split("/")[-1] + "S"
|
||||
write_wein2k(_pre_filename, supercell, npts_super, r0s_super, rmts_super)
|
||||
|
||||
for i, cell in zip(ids, cells_with_displacements):
|
||||
symmetry = Symmetry(cell)
|
||||
filename = "{pre_filename}-{0:0{width}}.in".format(
|
||||
i, pre_filename=_pre_filename, width=width)
|
||||
print("Number of non-equivalent atoms in %s: %d" %
|
||||
(filename, len(symmetry.get_independent_atoms())))
|
||||
write_wein2k(filename,
|
||||
cell,
|
||||
npts_super,
|
||||
r0s_super,
|
||||
rmts_super)
|
||||
i, pre_filename=_pre_filename, width=width
|
||||
)
|
||||
print(
|
||||
"Number of non-equivalent atoms in %s: %d"
|
||||
% (filename, len(symmetry.get_independent_atoms()))
|
||||
)
|
||||
write_wein2k(filename, cell, npts_super, r0s_super, rmts_super)
|
||||
|
||||
|
||||
def write_wein2k(filename, cell, npts, r0s, rmts):
|
||||
with open(filename, 'w') as w:
|
||||
"""Write cell to file."""
|
||||
with open(filename, "w") as w:
|
||||
w.write(_get_wien2k_struct(cell, npts, r0s, rmts))
|
||||
|
||||
|
||||
|
@ -223,8 +224,7 @@ def _get_wien2k_struct(cell, npts, r0s, rmts):
|
|||
text += "%13s%4s\n" % ("MODE OF CALC=", "RELA")
|
||||
|
||||
# 4
|
||||
text += "%10.6f%10.6f%10.6f%10.6f%10.6f%10.6f\n" % (
|
||||
a, b, c, alpha, beta, gamma)
|
||||
text += "%10.6f%10.6f%10.6f%10.6f%10.6f%10.6f\n" % (a, b, c, alpha, beta, gamma)
|
||||
|
||||
for i, pos in enumerate(positions):
|
||||
for j in (0, 1, 2):
|
||||
|
@ -235,7 +235,15 @@ def _get_wien2k_struct(cell, npts, r0s, rmts):
|
|||
|
||||
# 5 format (4X,I4,4X,F10.8,3X,F10.8,3X,F10.8)
|
||||
text += "%4s%4d%4s%10.8f%3s%10.8f%3s%10.8f\n" % (
|
||||
"ATOM", -(i + 1), ": X=", pos[0], " Y=", pos[1], " Z=", pos[2])
|
||||
"ATOM",
|
||||
-(i + 1),
|
||||
": X=",
|
||||
pos[0],
|
||||
" Y=",
|
||||
pos[1],
|
||||
" Z=",
|
||||
pos[2],
|
||||
)
|
||||
|
||||
# 6 format (15X,I2,17X,I2)
|
||||
text += "%15s%2d%17s%2d\n" % ("MULT=", 1, "ISPLIT=", 8)
|
||||
|
@ -245,7 +253,16 @@ def _get_wien2k_struct(cell, npts, r0s, rmts):
|
|||
r0 = r0s[i]
|
||||
rmt = rmts[i]
|
||||
text += "%-10s%5s%5d%5s%10.8f%5s%10.5f%5s%5.1f\n" % (
|
||||
symbols[i], "NPT=", npt, "R0=", r0, "RMT=", rmt, "Z:", numbers[i])
|
||||
symbols[i],
|
||||
"NPT=",
|
||||
npt,
|
||||
"R0=",
|
||||
r0,
|
||||
"RMT=",
|
||||
rmt,
|
||||
"Z:",
|
||||
numbers[i],
|
||||
)
|
||||
|
||||
# 8 - 10 format (20X,3F10.7)
|
||||
text += "%-20s%10.7f%10.7f%10.7f\n" % ("LOCAL ROT MATRIX:", 1, 0, 0)
|
||||
|
@ -269,7 +286,7 @@ def _transform_axis(alpha, beta, gamma, a, b, c):
|
|||
|
||||
az = np.cos(beta) * a
|
||||
ay = (np.cos(gamma) - np.cos(beta) * np.cos(alpha)) / np.sin(alpha) * a
|
||||
ax = np.sqrt(a**2 - ay**2 - az**2)
|
||||
ax = np.sqrt(a ** 2 - ay ** 2 - az ** 2)
|
||||
|
||||
return [ax, ay, az], [0, by, bz], [0, 0, cz]
|
||||
|
||||
|
@ -280,7 +297,7 @@ def _parse_core_param(file):
|
|||
rmts = []
|
||||
|
||||
for line in file:
|
||||
if line.strip()[0] == '#':
|
||||
if line.strip()[0] == "#":
|
||||
continue
|
||||
|
||||
vals = line.strip().split()
|
||||
|
@ -300,7 +317,7 @@ def _get_forces_wien2k(filename, lattice):
|
|||
|
||||
num_atom = 0
|
||||
for line in open(filename):
|
||||
if line.count('total forces') > 0:
|
||||
if line.count("total forces") > 0:
|
||||
if line[:4] == ":FGL":
|
||||
fx = float(line[29:45])
|
||||
fy = float(line[45:61])
|
||||
|
@ -316,16 +333,13 @@ def _distribute_forces(supercell, disp, forces, filename, symprec):
|
|||
lattice = supercell.get_cell()
|
||||
symbols = supercell.get_chemical_symbols()
|
||||
positions = supercell.get_positions() + disp
|
||||
cell = Atoms(cell=lattice,
|
||||
positions=positions,
|
||||
symbols=symbols,
|
||||
pbc=True)
|
||||
cell = Atoms(cell=lattice, positions=positions, symbols=symbols, pbc=True)
|
||||
symmetry = Symmetry(cell, symprec)
|
||||
independent_atoms = symmetry.get_independent_atoms()
|
||||
|
||||
# Rotation matrices in Cartesian
|
||||
rotations = []
|
||||
for r in symmetry.get_symmetry_operations()['rotations']:
|
||||
for r in symmetry.get_symmetry_operations()["rotations"]:
|
||||
rotations.append(similarity_transformation(lattice.T, r))
|
||||
|
||||
map_operations = symmetry.get_map_operations()
|
||||
|
@ -335,18 +349,18 @@ def _distribute_forces(supercell, disp, forces, filename, symprec):
|
|||
|
||||
if len(forces) != len(atoms_in_dot_scf):
|
||||
print("%s does not contain necessary information." % filename)
|
||||
print("Plese check if there are \"FGL\" lines with")
|
||||
print("\"total forces\" are required.")
|
||||
print('Plese check if there are "FGL" lines with')
|
||||
print('"total forces" are required.')
|
||||
return False
|
||||
|
||||
if len(atoms_in_dot_scf) == natom:
|
||||
print("It is assumed that there is no symmetrically-equivalent "
|
||||
"atoms in ")
|
||||
print("\'%s\' at wien2k calculation." % filename)
|
||||
print("It is assumed that there is no symmetrically-equivalent " "atoms in ")
|
||||
print("'%s' at wien2k calculation." % filename)
|
||||
force_set = forces
|
||||
elif len(forces) != len(independent_atoms):
|
||||
print("Non-equivalent atoms of %s could not be recognized by phonopy."
|
||||
% filename)
|
||||
print(
|
||||
"Non-equivalent atoms of %s could not be recognized by phonopy." % filename
|
||||
)
|
||||
return False
|
||||
else:
|
||||
# 1. Transform wien2k forces to those on independent atoms
|
||||
|
@ -357,8 +371,7 @@ def _distribute_forces(supercell, disp, forces, filename, symprec):
|
|||
diff = pos_wien2k - pos
|
||||
diff -= np.rint(diff)
|
||||
if (abs(diff) < symprec).all():
|
||||
forces_remap.append(
|
||||
np.dot(rotations[map_operations[j]], forces[i]))
|
||||
forces_remap.append(np.dot(rotations[map_operations[j]], forces[i]))
|
||||
indep_atoms_to_wien2k.append(map_atoms[j])
|
||||
break
|
||||
|
||||
|
@ -372,8 +385,7 @@ def _distribute_forces(supercell, disp, forces, filename, symprec):
|
|||
force_set = []
|
||||
for i in range(natom):
|
||||
j = indep_atoms_to_wien2k.index(map_atoms[i])
|
||||
force_set.append(np.dot(
|
||||
rotations[map_operations[i]].T, forces_remap[j]))
|
||||
force_set.append(np.dot(rotations[map_operations[i]].T, forces_remap[j]))
|
||||
|
||||
return force_set
|
||||
|
||||
|
@ -396,11 +408,12 @@ def _get_independent_atoms_in_dot_scf(filename):
|
|||
return np.array(positions)[-num_atom:]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
from optparse import OptionParser
|
||||
from phonopy.interface.vasp import write_vasp, read_vasp
|
||||
|
||||
def clean_scaled_positions(cell):
|
||||
from phonopy.interface.vasp import read_vasp, write_vasp
|
||||
|
||||
def _clean_scaled_positions(cell):
|
||||
positions = cell.get_scaled_positions()
|
||||
for pos in positions:
|
||||
for i in (0, 1, 2):
|
||||
|
@ -411,12 +424,12 @@ if __name__ == '__main__':
|
|||
|
||||
parser = OptionParser()
|
||||
parser.set_defaults(w2v=False, v2w=False)
|
||||
parser.add_option("-w", dest="w2v",
|
||||
action="store_true",
|
||||
help="Convert WIEN2k to VASP")
|
||||
parser.add_option("-v", dest="v2w",
|
||||
action="store_true",
|
||||
help="Convert VASP to WIEN2k")
|
||||
parser.add_option(
|
||||
"-w", dest="w2v", action="store_true", help="Convert WIEN2k to VASP"
|
||||
)
|
||||
parser.add_option(
|
||||
"-v", dest="v2w", action="store_true", help="Convert VASP to WIEN2k"
|
||||
)
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
from phonopy.units import Bohr
|
||||
|
@ -435,14 +448,12 @@ if __name__ == '__main__':
|
|||
lattice = cell.get_cell() * Bohr
|
||||
cell.set_cell(lattice)
|
||||
cell.set_scaled_positions(positions)
|
||||
clean_scaled_positions(cell)
|
||||
_clean_scaled_positions(cell)
|
||||
write_vasp("POSCAR.wien2k", cell, direct=True)
|
||||
w = open("wien2k_core.dat", 'w')
|
||||
w = open("wien2k_core.dat", "w")
|
||||
|
||||
w.write("# symbol npt r0 rmt\n")
|
||||
for symbol, npt, r0, rmt in \
|
||||
zip(cell.get_chemical_symbols(), npts, r0s, rmts):
|
||||
w.write("%-10s %5d %10.8f %10.5f\n" %
|
||||
(symbol, npt, r0, rmt))
|
||||
for symbol, npt, r0, rmt in zip(cell.get_chemical_symbols(), npts, r0s, rmts):
|
||||
w.write("%-10s %5d %10.8f %10.5f\n" % (symbol, npt, r0, rmt))
|
||||
else:
|
||||
print("You need to set -r or -w option.")
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
"""Routines of phonon and post-phonon calculations."""
|
||||
# Copyright (C) 2021 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of phonopy.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# * Neither the name of the phonopy project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,3 +1,4 @@
|
|||
"""Convert phonon results to animation formats."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,88 +34,74 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import numpy as np
|
||||
from phonopy.structure.cells import (get_angles, get_cell_parameters,
|
||||
get_cell_matrix)
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
|
||||
from phonopy.interface.vasp import write_vasp
|
||||
from phonopy.structure.atoms import PhonopyAtoms
|
||||
from phonopy.structure.cells import get_angles, get_cell_matrix, get_cell_parameters
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
def write_animation(dynamical_matrix,
|
||||
def write_animation(
|
||||
dynamical_matrix,
|
||||
q_point=None,
|
||||
anime_type='v_sim',
|
||||
anime_type="v_sim",
|
||||
band_index=None,
|
||||
amplitude=None,
|
||||
num_div=None,
|
||||
shift=None,
|
||||
factor=None,
|
||||
filename=None):
|
||||
filename=None,
|
||||
):
|
||||
"""Write atomic modulations in animation format."""
|
||||
animation = Animation(q_point,
|
||||
dynamical_matrix,
|
||||
shift=shift)
|
||||
animation = Animation(q_point, dynamical_matrix, shift=shift)
|
||||
|
||||
if anime_type == 'v_sim':
|
||||
if anime_type == "v_sim":
|
||||
if filename:
|
||||
fname_out = animation.write_v_sim(amplitude=amplitude,
|
||||
factor=factor,
|
||||
filename=filename)
|
||||
fname_out = animation.write_v_sim(
|
||||
amplitude=amplitude, factor=factor, filename=filename
|
||||
)
|
||||
else:
|
||||
fname_out = animation.write_v_sim(amplitude=amplitude,
|
||||
factor=factor)
|
||||
fname_out = animation.write_v_sim(amplitude=amplitude, factor=factor)
|
||||
|
||||
elif anime_type == 'arc' or anime_type is None:
|
||||
elif anime_type == "arc" or anime_type is None:
|
||||
if filename:
|
||||
fname_out = animation.write_arc(band_index,
|
||||
amplitude,
|
||||
num_div,
|
||||
filename=filename)
|
||||
fname_out = animation.write_arc(
|
||||
band_index, amplitude, num_div, filename=filename
|
||||
)
|
||||
else:
|
||||
fname_out = animation.write_arc(band_index,
|
||||
amplitude,
|
||||
num_div)
|
||||
elif anime_type == 'xyz':
|
||||
fname_out = animation.write_arc(band_index, amplitude, num_div)
|
||||
elif anime_type == "xyz":
|
||||
if filename:
|
||||
fname_out = animation.write_xyz(band_index,
|
||||
amplitude,
|
||||
num_div,
|
||||
factor,
|
||||
filename=filename)
|
||||
fname_out = animation.write_xyz(
|
||||
band_index, amplitude, num_div, factor, filename=filename
|
||||
)
|
||||
else:
|
||||
fname_out = animation.write_xyz(band_index,
|
||||
amplitude,
|
||||
num_div,
|
||||
factor)
|
||||
elif anime_type == 'jmol':
|
||||
fname_out = animation.write_xyz(band_index, amplitude, num_div, factor)
|
||||
elif anime_type == "jmol":
|
||||
if filename:
|
||||
fname_out = animation.write_xyz_jmol(amplitude=amplitude,
|
||||
factor=factor,
|
||||
filename=filename)
|
||||
fname_out = animation.write_xyz_jmol(
|
||||
amplitude=amplitude, factor=factor, filename=filename
|
||||
)
|
||||
else:
|
||||
fname_out = animation.write_xyz_jmol(amplitude=amplitude,
|
||||
factor=factor)
|
||||
elif anime_type == 'poscar':
|
||||
fname_out = animation.write_xyz_jmol(amplitude=amplitude, factor=factor)
|
||||
elif anime_type == "poscar":
|
||||
if filename:
|
||||
fname_out = animation.write_POSCAR(band_index,
|
||||
amplitude,
|
||||
num_div,
|
||||
filename=filename)
|
||||
fname_out = animation.write_POSCAR(
|
||||
band_index, amplitude, num_div, filename=filename
|
||||
)
|
||||
else:
|
||||
fname_out = animation.write_POSCAR(band_index,
|
||||
amplitude,
|
||||
num_div)
|
||||
fname_out = animation.write_POSCAR(band_index, amplitude, num_div)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Animation format \'%s\' was not found." % anime_type)
|
||||
raise RuntimeError("Animation format '%s' was not found." % anime_type)
|
||||
|
||||
return fname_out
|
||||
|
||||
|
||||
class Animation(object):
|
||||
def __init__(self,
|
||||
qpoint,
|
||||
dynamical_matrix,
|
||||
shift=None):
|
||||
class Animation:
|
||||
"""Class to convert phonon results to animation formats."""
|
||||
|
||||
def __init__(self, qpoint, dynamical_matrix, shift=None):
|
||||
"""Init method."""
|
||||
if qpoint is None:
|
||||
_qpoint = [0, 0, 0]
|
||||
else:
|
||||
|
@ -138,14 +125,15 @@ class Animation(object):
|
|||
a, b, c = self._cell_params
|
||||
alpha, beta, gamma = self._angles
|
||||
self._lattice_oriented = get_cell_matrix(a, b, c, alpha, beta, gamma)
|
||||
self._positions_oriented = \
|
||||
self._get_oriented_displacements(np.dot(self._positions,
|
||||
self._lattice))
|
||||
self._positions_oriented = self._get_oriented_displacements(
|
||||
np.dot(self._positions, self._lattice)
|
||||
)
|
||||
|
||||
# For the orientation, see get_cell_matrix
|
||||
def _get_oriented_displacements(self, vec_cartesian):
|
||||
return np.dot(np.dot(vec_cartesian, np.linalg.inv(self._lattice)),
|
||||
self._lattice_oriented)
|
||||
return np.dot(
|
||||
np.dot(vec_cartesian, np.linalg.inv(self._lattice)), self._lattice_oriented
|
||||
)
|
||||
|
||||
def _set_displacements(self, band_index):
|
||||
u = []
|
||||
|
@ -154,18 +142,16 @@ class Animation(object):
|
|||
|
||||
self._displacements = np.array(u).reshape(-1, 3)
|
||||
|
||||
def write_v_sim(self,
|
||||
amplitude=None,
|
||||
factor=VaspToTHz,
|
||||
filename="anime.ascii"):
|
||||
def write_v_sim(self, amplitude=None, factor=VaspToTHz, filename="anime.ascii"):
|
||||
"""Write to file in v_sim format."""
|
||||
if amplitude is None:
|
||||
_amplitude = 1.0
|
||||
self._set_cell_oriented()
|
||||
lat = self._lattice_oriented
|
||||
q = self._qpoint
|
||||
text = "# Phonopy generated file for v_sim 3.6\n"
|
||||
text += "%15.9f%15.9f%15.9f\n" % (lat[0,0], lat[1,0], lat[1,1])
|
||||
text += "%15.9f%15.9f%15.9f\n" % (lat[2,0], lat[2,1], lat[2,2])
|
||||
text += "%15.9f%15.9f%15.9f\n" % (lat[0, 0], lat[1, 0], lat[1, 1])
|
||||
text += "%15.9f%15.9f%15.9f\n" % (lat[2, 0], lat[2, 1], lat[2, 2])
|
||||
for s, p in zip(self._symbols, self._positions_oriented):
|
||||
text += "%15.9f%15.9f%15.9f %2s\n" % (p[0], p[1], p[2], s)
|
||||
|
||||
|
@ -176,24 +162,29 @@ class Animation(object):
|
|||
omega = -np.sqrt(-val)
|
||||
self._set_displacements(i)
|
||||
text += "#metaData: qpt=[%f;%f;%f;%f \\\n" % (
|
||||
q[0], q[1], q[2], omega * factor)
|
||||
for u in (self._get_oriented_displacements(self._displacements) *
|
||||
_amplitude):
|
||||
q[0],
|
||||
q[1],
|
||||
q[2],
|
||||
omega * factor,
|
||||
)
|
||||
for u in self._get_oriented_displacements(self._displacements) * _amplitude:
|
||||
text += "#; %f; %f; %f; %f; %f; %f \\\n" % (
|
||||
u[0].real, u[1].real, u[2].real,
|
||||
u[0].imag, u[1].imag, u[2].imag)
|
||||
u[0].real,
|
||||
u[1].real,
|
||||
u[2].real,
|
||||
u[0].imag,
|
||||
u[1].imag,
|
||||
u[2].imag,
|
||||
)
|
||||
text += "# ]\n"
|
||||
w = open(filename, 'w')
|
||||
w = open(filename, "w")
|
||||
w.write(text)
|
||||
w.close()
|
||||
|
||||
return filename
|
||||
|
||||
def write_arc(self,
|
||||
band_index,
|
||||
amplitude=1,
|
||||
num_div=20,
|
||||
filename="anime.arc"):
|
||||
def write_arc(self, band_index, amplitude=1, num_div=20, filename="anime.arc"):
|
||||
"""Write to file in BIOSYM archive 3 format."""
|
||||
self._set_cell_oriented()
|
||||
self._set_displacements(band_index - 1)
|
||||
displacements = self._get_oriented_displacements(self._displacements)
|
||||
|
@ -206,32 +197,47 @@ class Animation(object):
|
|||
text += "PBC=ON\n"
|
||||
|
||||
for i in range(num_div):
|
||||
text += " 0.000000\n"
|
||||
text += " 0.000000\n" # noqa E501
|
||||
text += "!DATE\n"
|
||||
text += "%-4s%10.4f%10.4f%10.4f%10.4f%10.4f%10.4f\n" % (
|
||||
"PBC", a, b, c, alpha, beta, gamma)
|
||||
positions = (self._positions_oriented +
|
||||
(displacements *
|
||||
np.exp(2j * np.pi / num_div * i)).imag * amplitude)
|
||||
"PBC",
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
alpha,
|
||||
beta,
|
||||
gamma,
|
||||
)
|
||||
positions = (
|
||||
self._positions_oriented
|
||||
+ (displacements * np.exp(2j * np.pi / num_div * i)).imag * amplitude
|
||||
)
|
||||
for j, p in enumerate(positions):
|
||||
text += "%-5s%15.9f%15.9f%15.9f CORE" % (
|
||||
self._symbols[j], p[0], p[1], p[2])
|
||||
self._symbols[j],
|
||||
p[0],
|
||||
p[1],
|
||||
p[2],
|
||||
)
|
||||
text += "%5s%3s%3s%9.4f%5s\n" % (
|
||||
j + 1, self._symbols[j], self._symbols[j], 0.0, j + 1)
|
||||
j + 1,
|
||||
self._symbols[j],
|
||||
self._symbols[j],
|
||||
0.0,
|
||||
j + 1,
|
||||
)
|
||||
|
||||
text += "end\n"
|
||||
text += "end\n"
|
||||
|
||||
w = open(filename, 'w')
|
||||
w = open(filename, "w")
|
||||
w.write(text)
|
||||
w.close()
|
||||
|
||||
return filename
|
||||
|
||||
def write_xyz_jmol(self,
|
||||
amplitude=10,
|
||||
factor=VaspToTHz,
|
||||
filename="anime.xyz_jmol"):
|
||||
def write_xyz_jmol(self, amplitude=10, factor=VaspToTHz, filename="anime.xyz_jmol"):
|
||||
"""Write to file in jmol xyz format."""
|
||||
self._set_cell_oriented()
|
||||
text = ""
|
||||
for i, val in enumerate(self._eigenvalues):
|
||||
|
@ -240,27 +246,30 @@ class Animation(object):
|
|||
else:
|
||||
freq = -np.sqrt(-val)
|
||||
self._set_displacements(i)
|
||||
displacements = self._get_oriented_displacements(
|
||||
self._displacements) * amplitude
|
||||
displacements = (
|
||||
self._get_oriented_displacements(self._displacements) * amplitude
|
||||
)
|
||||
text += "%d\n" % len(self._symbols)
|
||||
text += "q %s , b %d , f %f " % (str(self._qpoint), i+1, freq * factor)
|
||||
text += "q %s , b %d , f %f " % (str(self._qpoint), i + 1, freq * factor)
|
||||
text += "(generated by Phonopy)\n"
|
||||
for s, p, u in zip(
|
||||
self._symbols, self._positions_oriented, displacements):
|
||||
for s, p, u in zip(self._symbols, self._positions_oriented, displacements):
|
||||
text += "%-3s %22.15f %22.15f %22.15f " % (s, p[0], p[1], p[2])
|
||||
text += "%15.9f %15.9f %15.9f\n" % (u[0].real, u[1].real, u[2].real)
|
||||
w = open(filename, 'w')
|
||||
w = open(filename, "w")
|
||||
w.write(text)
|
||||
w.close()
|
||||
|
||||
return filename
|
||||
|
||||
def write_xyz(self,
|
||||
def write_xyz(
|
||||
self,
|
||||
band_index,
|
||||
amplitude=1,
|
||||
num_div=20,
|
||||
factor=VaspToTHz,
|
||||
filename="anime.xyz"):
|
||||
filename="anime.xyz",
|
||||
):
|
||||
"""Write to file in xyz format."""
|
||||
self._set_cell_oriented()
|
||||
freq = self._eigenvalues[band_index - 1]
|
||||
self._set_displacements(band_index - 1)
|
||||
|
@ -269,36 +278,45 @@ class Animation(object):
|
|||
for i in range(num_div):
|
||||
text += "%d\n" % len(self._symbols)
|
||||
text += "q %s , b %d , f %f , " % (
|
||||
str(self._qpoint), band_index, freq * factor)
|
||||
str(self._qpoint),
|
||||
band_index,
|
||||
freq * factor,
|
||||
)
|
||||
text += "div %d / %d " % (i, num_div)
|
||||
text += "(generated by Phonopy)\n"
|
||||
positions = (self._positions_oriented +
|
||||
(displacements *
|
||||
np.exp(2j * np.pi / num_div * i)).imag * amplitude)
|
||||
positions = (
|
||||
self._positions_oriented
|
||||
+ (displacements * np.exp(2j * np.pi / num_div * i)).imag * amplitude
|
||||
)
|
||||
for j, p in enumerate(positions):
|
||||
text += "%-3s %22.15f %22.15f %22.15f\n" % (
|
||||
self._symbols[j], p[0], p[1], p[2])
|
||||
w = open(filename, 'w')
|
||||
self._symbols[j],
|
||||
p[0],
|
||||
p[1],
|
||||
p[2],
|
||||
)
|
||||
w = open(filename, "w")
|
||||
w.write(text)
|
||||
w.close()
|
||||
|
||||
return filename
|
||||
|
||||
def write_POSCAR(self,
|
||||
band_index,
|
||||
amplitude=1,
|
||||
num_div=20,
|
||||
filename="APOSCAR"):
|
||||
def write_POSCAR(self, band_index, amplitude=1, num_div=20, filename="APOSCAR"):
|
||||
"""Write snapshots to files in VASP POSCAR format."""
|
||||
self._set_displacements(band_index - 1)
|
||||
for i in range(num_div):
|
||||
positions = (np.dot(self._positions, self._lattice) +
|
||||
(self._displacements *
|
||||
np.exp(2j * np.pi / num_div * i)).imag * amplitude)
|
||||
atoms = PhonopyAtoms(cell=self._lattice,
|
||||
positions = (
|
||||
np.dot(self._positions, self._lattice)
|
||||
+ (self._displacements * np.exp(2j * np.pi / num_div * i)).imag
|
||||
* amplitude
|
||||
)
|
||||
atoms = PhonopyAtoms(
|
||||
cell=self._lattice,
|
||||
positions=positions,
|
||||
masses=self._masses,
|
||||
symbols=self._symbols,
|
||||
pbc=True)
|
||||
write_vasp((filename+"-%03d") % i, atoms, direct=True)
|
||||
pbc=True,
|
||||
)
|
||||
write_vasp((filename + "-%03d") % i, atoms, direct=True)
|
||||
|
||||
return filename
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
|||
"""Utility routines to handle degeneracy."""
|
||||
# Copyright (C) 2014 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,10 +33,39 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.harmonic.derivative_dynmat import DerivativeOfDynamicalMatrix
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
|
||||
|
||||
def degenerate_sets(freqs, cutoff=1e-4):
|
||||
"""Find degenerate bands from frequencies.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
freqs : ndarray
|
||||
A list of values.
|
||||
shape=(values,)
|
||||
cutoff : float, optional
|
||||
Equivalent of values is defined by this value, i.e.,
|
||||
abs(val1 - val2) < cutoff
|
||||
Default is 1e-4.
|
||||
|
||||
Returns
|
||||
-------
|
||||
indices : list of list
|
||||
Indices of equivalent values are grouped as a list and those groups are
|
||||
stored in a list.
|
||||
|
||||
Example
|
||||
-------
|
||||
In : degenerate_sets(np.array([1.5, 2.1, 2.1, 3.4, 8]))
|
||||
Out: [[0], [1, 2], [3], [4]]
|
||||
|
||||
"""
|
||||
indices = []
|
||||
done = []
|
||||
for i in range(len(freqs)):
|
||||
|
@ -53,12 +83,15 @@ def degenerate_sets(freqs, cutoff=1e-4):
|
|||
return indices
|
||||
|
||||
|
||||
def get_eigenvectors(q,
|
||||
dm,
|
||||
ddm,
|
||||
def get_eigenvectors(
|
||||
q,
|
||||
dm: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
ddm: DerivativeOfDynamicalMatrix,
|
||||
perturbation=None,
|
||||
derivative_order=None,
|
||||
nac_q_direction=None):
|
||||
nac_q_direction=None,
|
||||
):
|
||||
"""Return degenerated eigenvalues and rotated eigenvalues."""
|
||||
if nac_q_direction is not None and (np.abs(q) < 1e-5).all():
|
||||
dm.run(q, q_direction=nac_q_direction)
|
||||
else:
|
||||
|
@ -77,6 +110,20 @@ def get_eigenvectors(q,
|
|||
|
||||
|
||||
def rotate_eigenvectors(eigvals, eigvecs, dD):
|
||||
"""Rotate eigenvectors among degenerated band.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
eigvals :
|
||||
Eigenvalues.
|
||||
shape=(num_band, )
|
||||
eigvecs :
|
||||
Eigenvectors.
|
||||
shape=(num_atom * 3, num_band)
|
||||
dD :
|
||||
q-point derivative of dynamical matrix.
|
||||
|
||||
"""
|
||||
rot_eigvecs = np.zeros_like(eigvecs)
|
||||
eigvals_dD = np.zeros_like(eigvals)
|
||||
for deg in degenerate_sets(eigvals):
|
||||
|
@ -86,10 +133,17 @@ def rotate_eigenvectors(eigvals, eigvecs, dD):
|
|||
return rot_eigvecs, eigvals_dD
|
||||
|
||||
|
||||
def _get_dD(q, ddm, perturbation):
|
||||
def _get_dD(q, ddm: DerivativeOfDynamicalMatrix, perturbation):
|
||||
"""Return q-vector derivative of dynamical matrix.
|
||||
|
||||
Returns
|
||||
-------
|
||||
shape=(3, num_band, num_band).
|
||||
|
||||
"""
|
||||
ddm.run(q)
|
||||
ddm_vals = ddm.get_derivative_of_dynamical_matrix()
|
||||
dD = np.zeros(ddm_vals.shape[1:], dtype=ddm_vals.dtype, order='C')
|
||||
dD = np.zeros(ddm_vals.shape[1:], dtype=ddm_vals.dtype, order="C")
|
||||
if len(ddm_vals) == 3:
|
||||
for i in range(3):
|
||||
dD += perturbation[i] * ddm_vals[i]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Calculation of density of states."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,230 +34,93 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.phonon.mesh import Mesh
|
||||
from phonopy.phonon.tetrahedron_mesh import TetrahedronMesh
|
||||
from phonopy.structure.tetrahedron_method import TetrahedronMethod
|
||||
|
||||
|
||||
def get_pdos_indices(symmetry):
|
||||
mapping = symmetry.get_map_atoms()
|
||||
return [list(np.where(mapping == i)[0])
|
||||
for i in symmetry.get_independent_atoms()]
|
||||
class NormalDistribution:
|
||||
"""Class to represent normal distribution."""
|
||||
|
||||
|
||||
def write_total_dos(frequency_points,
|
||||
total_dos,
|
||||
comment=None,
|
||||
filename="total_dos.dat"):
|
||||
with open(filename, 'w') as fp:
|
||||
if comment is not None:
|
||||
fp.write("# %s\n" % comment)
|
||||
|
||||
for freq, dos in zip(frequency_points, total_dos):
|
||||
fp.write("%20.10f%20.10f\n" % (freq, dos))
|
||||
|
||||
|
||||
def write_partial_dos(frequency_points,
|
||||
partial_dos,
|
||||
comment=None,
|
||||
filename="partial_dos.dat"):
|
||||
with open(filename, 'w') as fp:
|
||||
if comment is not None:
|
||||
fp.write("# %s\n" % comment)
|
||||
|
||||
for freq, pdos in zip(frequency_points, partial_dos.T):
|
||||
fp.write("%20.10f" % freq)
|
||||
fp.write(("%20.10f" * len(pdos)) % tuple(pdos))
|
||||
fp.write("\n")
|
||||
|
||||
|
||||
def plot_total_dos(ax,
|
||||
frequency_points,
|
||||
total_dos,
|
||||
freq_Debye=None,
|
||||
Debye_fit_coef=None,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False):
|
||||
ax.xaxis.set_ticks_position('both')
|
||||
ax.yaxis.set_ticks_position('both')
|
||||
ax.xaxis.set_tick_params(which='both', direction='in')
|
||||
ax.yaxis.set_tick_params(which='both', direction='in')
|
||||
|
||||
if freq_Debye is not None:
|
||||
freq_pitch = frequency_points[1] - frequency_points[0]
|
||||
num_points = int(freq_Debye / freq_pitch)
|
||||
freqs = np.linspace(0, freq_Debye, num_points + 1)
|
||||
|
||||
if flip_xy:
|
||||
ax.plot(total_dos, frequency_points, 'r-', linewidth=1)
|
||||
if freq_Debye:
|
||||
ax.plot(np.append(Debye_fit_coef * freqs**2, 0),
|
||||
np.append(freqs, freq_Debye), 'b-', linewidth=1)
|
||||
else:
|
||||
ax.plot(frequency_points, total_dos, 'r-', linewidth=1)
|
||||
if freq_Debye:
|
||||
ax.plot(np.append(freqs, freq_Debye),
|
||||
np.append(Debye_fit_coef * freqs**2, 0), 'b-', linewidth=1)
|
||||
|
||||
if xlabel:
|
||||
ax.set_xlabel(xlabel)
|
||||
if ylabel:
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
ax.grid(draw_grid)
|
||||
|
||||
|
||||
def plot_partial_dos(ax,
|
||||
frequency_points,
|
||||
partial_dos,
|
||||
indices=None,
|
||||
legend=None,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False):
|
||||
ax.xaxis.set_ticks_position('both')
|
||||
ax.yaxis.set_ticks_position('both')
|
||||
ax.xaxis.set_tick_params(which='both', direction='in')
|
||||
ax.yaxis.set_tick_params(which='both', direction='in')
|
||||
|
||||
plots = []
|
||||
num_pdos = len(partial_dos)
|
||||
|
||||
if indices is None:
|
||||
indices = []
|
||||
for i in range(num_pdos):
|
||||
indices.append([i])
|
||||
|
||||
for set_for_sum in indices:
|
||||
pdos_sum = np.zeros_like(frequency_points)
|
||||
for i in set_for_sum:
|
||||
if i > num_pdos - 1:
|
||||
print("Index number \'%d\' is specified," % (i + 1))
|
||||
print("but it is not allowed to be larger than the number of "
|
||||
"atoms.")
|
||||
raise ValueError
|
||||
if i < 0:
|
||||
print("Index number \'%d\' is specified, but it must be "
|
||||
"positive." % (i + 1))
|
||||
raise ValueError
|
||||
pdos_sum += partial_dos[i]
|
||||
if flip_xy:
|
||||
plots.append(ax.plot(pdos_sum, frequency_points, linewidth=1))
|
||||
else:
|
||||
plots.append(ax.plot(frequency_points, pdos_sum, linewidth=1))
|
||||
|
||||
if legend is not None:
|
||||
ax.legend(legend)
|
||||
|
||||
if xlabel:
|
||||
ax.set_xlabel(xlabel)
|
||||
if ylabel:
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
ax.grid(draw_grid)
|
||||
|
||||
|
||||
class NormalDistribution(object):
|
||||
def __init__(self, sigma):
|
||||
"""Init method."""
|
||||
self._sigma = sigma
|
||||
|
||||
def calc(self, x):
|
||||
return 1.0 / np.sqrt(2 * np.pi) / self._sigma * \
|
||||
np.exp(-x**2 / 2.0 / self._sigma**2)
|
||||
"""Return normal distribution."""
|
||||
return (
|
||||
1.0
|
||||
/ np.sqrt(2 * np.pi)
|
||||
/ self._sigma
|
||||
* np.exp(-(x ** 2) / 2.0 / self._sigma ** 2)
|
||||
)
|
||||
|
||||
|
||||
class CauchyDistribution(object):
|
||||
class CauchyDistribution:
|
||||
"""Class to represent Cauchy distribution."""
|
||||
|
||||
def __init__(self, gamma):
|
||||
"""Init method."""
|
||||
self._gamma = gamma
|
||||
|
||||
def calc(self, x):
|
||||
return self._gamma / np.pi / (x**2 + self._gamma**2)
|
||||
"""Return Cauchy distribution."""
|
||||
return self._gamma / np.pi / (x ** 2 + self._gamma ** 2)
|
||||
|
||||
|
||||
def run_tetrahedron_method_dos(mesh,
|
||||
frequency_points,
|
||||
frequencies,
|
||||
grid_address,
|
||||
grid_mapping_table,
|
||||
relative_grid_address,
|
||||
coef=None): # for each grid point
|
||||
try:
|
||||
import phonopy._phonopy as phonoc
|
||||
except ImportError:
|
||||
import sys
|
||||
print("Phonopy C-extension has to be built properly.")
|
||||
sys.exit(1)
|
||||
class Dos:
|
||||
"""Base class to calculate density of states."""
|
||||
|
||||
if coef is None:
|
||||
_coef = np.ones((frequencies.shape[0], 1, frequencies.shape[1]),
|
||||
dtype='double')
|
||||
else:
|
||||
_coef = np.array(coef, dtype='double', order='C')
|
||||
arr_shape = frequencies.shape + (len(frequency_points), _coef.shape[1])
|
||||
dos = np.zeros(arr_shape, dtype='double')
|
||||
|
||||
phonoc.tetrahedron_method_dos(
|
||||
dos,
|
||||
np.array(mesh, dtype='int_'),
|
||||
frequency_points,
|
||||
frequencies,
|
||||
_coef,
|
||||
np.array(grid_address, dtype='int_', order='C'),
|
||||
np.array(grid_mapping_table, dtype='int_', order='C'),
|
||||
relative_grid_address)
|
||||
if coef is None:
|
||||
return dos[:, :, :, 0].sum(axis=0).sum(axis=0) / np.prod(mesh)
|
||||
else:
|
||||
return dos.sum(axis=0).sum(axis=0) / np.prod(mesh)
|
||||
|
||||
|
||||
class Dos(object):
|
||||
def __init__(self, mesh_object, sigma=None, use_tetrahedron_method=False):
|
||||
def __init__(self, mesh_object: Mesh, sigma=None, use_tetrahedron_method=False):
|
||||
"""Init method."""
|
||||
self._mesh_object = mesh_object
|
||||
self._frequencies = mesh_object.frequencies
|
||||
self._weights = mesh_object.weights
|
||||
self._tetrahedron_mesh = None
|
||||
if use_tetrahedron_method and sigma is None:
|
||||
self._tetrahedron_mesh = TetrahedronMesh(
|
||||
mesh_object.dynamical_matrix.primitive,
|
||||
self._frequencies,
|
||||
mesh_object.mesh_numbers,
|
||||
np.array(mesh_object.grid_address, dtype='int_'),
|
||||
np.array(mesh_object.grid_mapping_table, dtype='int_'),
|
||||
mesh_object.ir_grid_points)
|
||||
else:
|
||||
self._tetrahedron_mesh = None
|
||||
|
||||
np.array(mesh_object.grid_address, dtype="int_"),
|
||||
np.array(mesh_object.grid_mapping_table, dtype="int_"),
|
||||
mesh_object.ir_grid_points,
|
||||
)
|
||||
self._frequency_points = None
|
||||
self._sigma = sigma
|
||||
self.set_draw_area()
|
||||
self.set_smearing_function('Normal')
|
||||
self.set_smearing_function("Normal")
|
||||
|
||||
@property
|
||||
def frequency_points(self):
|
||||
"""Return frequency points."""
|
||||
return self._frequency_points
|
||||
|
||||
def set_smearing_function(self, function_name):
|
||||
"""
|
||||
function_name ==
|
||||
"""Set function form for smearing method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
function_name : str
|
||||
'Normal': smearing is done by normal distribution.
|
||||
'Cauchy': smearing is done by Cauchy distribution.
|
||||
|
||||
"""
|
||||
if function_name == 'Cauchy':
|
||||
if function_name == "Cauchy":
|
||||
self._smearing_function = CauchyDistribution(self._sigma)
|
||||
else:
|
||||
self._smearing_function = NormalDistribution(self._sigma)
|
||||
|
||||
def set_sigma(self, sigma):
|
||||
"""Set sigma."""
|
||||
self._sigma = sigma
|
||||
|
||||
def set_draw_area(self,
|
||||
freq_min=None,
|
||||
freq_max=None,
|
||||
freq_pitch=None):
|
||||
|
||||
def set_draw_area(self, freq_min=None, freq_max=None, freq_pitch=None):
|
||||
"""Set frequency points."""
|
||||
f_min = self._frequencies.min()
|
||||
f_max = self._frequencies.max()
|
||||
|
||||
|
@ -277,50 +141,67 @@ class Dos(object):
|
|||
f_delta = (f_max - f_min) / 200.0
|
||||
else:
|
||||
f_delta = freq_pitch
|
||||
self._frequency_points = np.arange(f_min,
|
||||
f_max + f_delta * 0.1,
|
||||
f_delta)
|
||||
self._frequency_points = np.arange(f_min, f_max + f_delta * 0.1, f_delta)
|
||||
|
||||
|
||||
class TotalDos(Dos):
|
||||
def __init__(self, mesh_object, sigma=None, use_tetrahedron_method=False):
|
||||
Dos.__init__(self,
|
||||
"""Class to calculate total DOS."""
|
||||
|
||||
def __init__(self, mesh_object: Mesh, sigma=None, use_tetrahedron_method=False):
|
||||
"""Init method."""
|
||||
super().__init__(
|
||||
mesh_object,
|
||||
sigma=sigma,
|
||||
use_tetrahedron_method=use_tetrahedron_method)
|
||||
use_tetrahedron_method=use_tetrahedron_method,
|
||||
)
|
||||
self._dos = None
|
||||
self._freq_Debye = None
|
||||
self._Debye_fit_coef = None
|
||||
self._openmp_thm = True
|
||||
|
||||
def run(self):
|
||||
"""Calculate total DOS."""
|
||||
if self._tetrahedron_mesh is None:
|
||||
self._dos = np.array([self._get_density_of_states_at_freq(f)
|
||||
for f in self._frequency_points])
|
||||
self._dos = np.array(
|
||||
[self._get_density_of_states_at_freq(f) for f in self._frequency_points]
|
||||
)
|
||||
else:
|
||||
if self._openmp_thm:
|
||||
self._run_tetrahedron_method_dos()
|
||||
else:
|
||||
self._dos = np.zeros_like(self._frequency_points)
|
||||
thm = self._tetrahedron_mesh
|
||||
thm.set(value='I', frequency_points=self._frequency_points)
|
||||
thm.set(value="I", frequency_points=self._frequency_points)
|
||||
for i, iw in enumerate(thm):
|
||||
self._dos += np.sum(iw * self._weights[i], axis=1)
|
||||
|
||||
@property
|
||||
def dos(self):
|
||||
"""Return total DOS."""
|
||||
return self._dos
|
||||
|
||||
def get_dos(self):
|
||||
"""Return frequency points and total DOS.
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple
|
||||
(frequency_points, total_dos)
|
||||
|
||||
"""
|
||||
Return freqs and total dos
|
||||
"""
|
||||
warnings.warn(
|
||||
"TotalDos.get_dos() is deprecated. "
|
||||
"Use frequency_points and dos attributes instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self._frequency_points, self._dos
|
||||
|
||||
def get_Debye_frequency(self):
|
||||
"""Return a kind of Debye frequency."""
|
||||
return self._freq_Debye
|
||||
|
||||
def set_Debye_frequency(self, num_atoms, freq_max_fit=None):
|
||||
"""Calculate a kind of Debye frequency."""
|
||||
try:
|
||||
from scipy.optimize import curve_fit
|
||||
except ImportError:
|
||||
|
@ -328,7 +209,7 @@ class TotalDos(Dos):
|
|||
sys.exit(1)
|
||||
|
||||
def Debye_dos(freq, a):
|
||||
return a * freq**2
|
||||
return a * freq ** 2
|
||||
|
||||
freq_min = self._frequency_points.min()
|
||||
freq_max = self._frequency_points.max()
|
||||
|
@ -336,34 +217,32 @@ class TotalDos(Dos):
|
|||
if freq_max_fit is None:
|
||||
N_fit = int(len(self._frequency_points) / 4.0) # Hard coded
|
||||
else:
|
||||
N_fit = int(freq_max_fit / (freq_max - freq_min) *
|
||||
len(self._frequency_points))
|
||||
popt, pcov = curve_fit(Debye_dos,
|
||||
self._frequency_points[0:N_fit],
|
||||
self._dos[0:N_fit])
|
||||
N_fit = int(
|
||||
freq_max_fit / (freq_max - freq_min) * len(self._frequency_points)
|
||||
)
|
||||
popt, pcov = curve_fit(
|
||||
Debye_dos, self._frequency_points[0:N_fit], self._dos[0:N_fit]
|
||||
)
|
||||
a2 = popt[0]
|
||||
self._freq_Debye = (3 * 3 * num_atoms / a2)**(1.0 / 3)
|
||||
self._freq_Debye = (3 * 3 * num_atoms / a2) ** (1.0 / 3)
|
||||
self._Debye_fit_coef = a2
|
||||
|
||||
def plot(self,
|
||||
ax,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False):
|
||||
def plot(self, ax, xlabel=None, ylabel=None, draw_grid=True, flip_xy=False):
|
||||
"""Plot total DOS."""
|
||||
if flip_xy:
|
||||
_xlabel = 'Density of states'
|
||||
_ylabel = 'Frequency'
|
||||
_xlabel = "Density of states"
|
||||
_ylabel = "Frequency"
|
||||
else:
|
||||
_xlabel = 'Frequency'
|
||||
_ylabel = 'Density of states'
|
||||
_xlabel = "Frequency"
|
||||
_ylabel = "Density of states"
|
||||
|
||||
if xlabel is not None:
|
||||
_xlabel = xlabel
|
||||
if ylabel is not None:
|
||||
_ylabel = ylabel
|
||||
|
||||
plot_total_dos(ax,
|
||||
plot_total_dos(
|
||||
ax,
|
||||
self._frequency_points,
|
||||
self._dos,
|
||||
freq_Debye=self._freq_Debye,
|
||||
|
@ -371,23 +250,24 @@ class TotalDos(Dos):
|
|||
xlabel=_xlabel,
|
||||
ylabel=_ylabel,
|
||||
draw_grid=draw_grid,
|
||||
flip_xy=flip_xy)
|
||||
flip_xy=flip_xy,
|
||||
)
|
||||
|
||||
def write(self, filename="total_dos.dat"):
|
||||
"""Write total DOS to total_dos.dat."""
|
||||
if self._tetrahedron_mesh is None:
|
||||
comment = "Sigma = %f" % self._sigma
|
||||
else:
|
||||
comment = "Tetrahedron method"
|
||||
|
||||
write_total_dos(self._frequency_points,
|
||||
self._dos,
|
||||
comment=comment,
|
||||
filename=filename)
|
||||
write_total_dos(
|
||||
self._frequency_points, self._dos, comment=comment, filename=filename
|
||||
)
|
||||
|
||||
def _run_tetrahedron_method_dos(self):
|
||||
mesh_numbers = self._mesh_object.mesh_numbers
|
||||
cell = self._mesh_object.dynamical_matrix.primitive
|
||||
reciprocal_lattice = np.linalg.inv(cell.get_cell())
|
||||
reciprocal_lattice = np.linalg.inv(cell.cell)
|
||||
tm = TetrahedronMethod(reciprocal_lattice, mesh=mesh_numbers)
|
||||
self._dos = run_tetrahedron_method_dos(
|
||||
mesh_numbers,
|
||||
|
@ -395,41 +275,48 @@ class TotalDos(Dos):
|
|||
self._frequencies,
|
||||
self._mesh_object.grid_address,
|
||||
self._mesh_object.grid_mapping_table,
|
||||
tm.get_tetrahedra())
|
||||
tm.get_tetrahedra(),
|
||||
)
|
||||
|
||||
def _get_density_of_states_at_freq(self, f):
|
||||
return np.sum(np.dot(
|
||||
self._weights, self._smearing_function.calc(self._frequencies - f))
|
||||
return np.sum(
|
||||
np.dot(self._weights, self._smearing_function.calc(self._frequencies - f))
|
||||
) / np.sum(self._weights)
|
||||
|
||||
|
||||
class PartialDos(Dos):
|
||||
def __init__(self,
|
||||
mesh_object,
|
||||
class ProjectedDos(Dos):
|
||||
"""Class to calculate projected DOS."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mesh_object: Mesh,
|
||||
sigma=None,
|
||||
use_tetrahedron_method=False,
|
||||
direction=None,
|
||||
xyz_projection=False):
|
||||
Dos.__init__(self,
|
||||
xyz_projection=False,
|
||||
):
|
||||
"""Init method."""
|
||||
super().__init__(
|
||||
mesh_object,
|
||||
sigma=sigma,
|
||||
use_tetrahedron_method=use_tetrahedron_method)
|
||||
use_tetrahedron_method=use_tetrahedron_method,
|
||||
)
|
||||
self._eigenvectors = self._mesh_object.eigenvectors
|
||||
self._partial_dos = None
|
||||
self._projected_dos = None
|
||||
|
||||
if xyz_projection:
|
||||
self._eigvecs2 = np.abs(self._eigenvectors) ** 2
|
||||
else:
|
||||
num_atom = self._frequencies.shape[1] // 3
|
||||
i_x = np.arange(num_atom, dtype='int') * 3
|
||||
i_y = np.arange(num_atom, dtype='int') * 3 + 1
|
||||
i_z = np.arange(num_atom, dtype='int') * 3 + 2
|
||||
i_x = np.arange(num_atom, dtype="int") * 3
|
||||
i_y = np.arange(num_atom, dtype="int") * 3 + 1
|
||||
i_z = np.arange(num_atom, dtype="int") * 3 + 2
|
||||
if direction is None:
|
||||
self._eigvecs2 = np.abs(self._eigenvectors[:, i_x, :]) ** 2
|
||||
self._eigvecs2 += np.abs(self._eigenvectors[:, i_y, :]) ** 2
|
||||
self._eigvecs2 += np.abs(self._eigenvectors[:, i_z, :]) ** 2
|
||||
else:
|
||||
d = np.array(direction, dtype='double')
|
||||
d = np.array(direction, dtype="double")
|
||||
d /= np.linalg.norm(direction)
|
||||
proj_eigvecs = self._eigenvectors[:, i_x, :] * d[0]
|
||||
proj_eigvecs += self._eigenvectors[:, i_y, :] * d[1]
|
||||
|
@ -440,13 +327,21 @@ class PartialDos(Dos):
|
|||
|
||||
@property
|
||||
def partial_dos(self):
|
||||
return self._partial_dos
|
||||
"""Return partial DOS."""
|
||||
warnings.warn(
|
||||
"PartialDos.partial_dos attribute is deprecated. "
|
||||
"Use projected_dos attribute instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self._projected_dos
|
||||
|
||||
@property
|
||||
def projected_dos(self):
|
||||
return self._partial_dos
|
||||
"""Return projected DOS."""
|
||||
return self._projected_dos
|
||||
|
||||
def run(self):
|
||||
"""Calculate projected DOS."""
|
||||
if self._tetrahedron_mesh is None:
|
||||
self._run_smearing_method()
|
||||
else:
|
||||
|
@ -456,79 +351,97 @@ class PartialDos(Dos):
|
|||
self._run_tetrahedron_method()
|
||||
|
||||
def get_partial_dos(self):
|
||||
"""
|
||||
frequency_points: Sampling frequencies
|
||||
partial_dos: [atom_index, frequency_points_index]
|
||||
"""
|
||||
return self._frequency_points, self._partial_dos
|
||||
"""Return partial DOS.
|
||||
|
||||
def plot(self,
|
||||
Returns
|
||||
-------
|
||||
tuple
|
||||
frequency_points: Sampling frequencies
|
||||
projected_dos: [atom_index, frequency_points_index]
|
||||
|
||||
"""
|
||||
warnings.warn(
|
||||
"ProjectedDos.get_partial_dos() is deprecated. "
|
||||
"Use frequency_points and projected_dos attributes instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self._frequency_points, self._projected_dos
|
||||
|
||||
def plot(
|
||||
self,
|
||||
ax,
|
||||
indices=None,
|
||||
legend=None,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False):
|
||||
|
||||
flip_xy=False,
|
||||
):
|
||||
"""Plot projected DOS."""
|
||||
if flip_xy:
|
||||
_xlabel = 'Partial density of states'
|
||||
_ylabel = 'Frequency'
|
||||
_xlabel = "Partial density of states"
|
||||
_ylabel = "Frequency"
|
||||
else:
|
||||
_xlabel = 'Frequency'
|
||||
_ylabel = 'Partial density of states'
|
||||
_xlabel = "Frequency"
|
||||
_ylabel = "Partial density of states"
|
||||
|
||||
if xlabel is not None:
|
||||
_xlabel = xlabel
|
||||
if ylabel is not None:
|
||||
_ylabel = ylabel
|
||||
|
||||
plot_partial_dos(ax,
|
||||
plot_projected_dos(
|
||||
ax,
|
||||
self._frequency_points,
|
||||
self._partial_dos,
|
||||
self._projected_dos,
|
||||
indices=indices,
|
||||
legend=legend,
|
||||
xlabel=_xlabel,
|
||||
ylabel=_ylabel,
|
||||
draw_grid=draw_grid,
|
||||
flip_xy=flip_xy)
|
||||
flip_xy=flip_xy,
|
||||
)
|
||||
|
||||
def write(self, filename="partial_dos.dat"):
|
||||
def write(self, filename="projected_dos.dat"):
|
||||
"""Write projected DOS to projected_dos.dat."""
|
||||
if self._tetrahedron_mesh is None:
|
||||
comment = "Sigma = %f" % self._sigma
|
||||
else:
|
||||
comment = "Tetrahedron method"
|
||||
|
||||
write_partial_dos(self._frequency_points,
|
||||
self._partial_dos,
|
||||
write_projected_dos(
|
||||
self._frequency_points,
|
||||
self._projected_dos,
|
||||
comment=comment,
|
||||
filename=filename)
|
||||
filename=filename,
|
||||
)
|
||||
|
||||
def _run_smearing_method(self):
|
||||
num_pdos = self._eigvecs2.shape[1]
|
||||
num_freqs = len(self._frequency_points)
|
||||
self._partial_dos = np.zeros((num_pdos, num_freqs), dtype='double')
|
||||
self._projected_dos = np.zeros((num_pdos, num_freqs), dtype="double")
|
||||
weights = self._weights / float(np.sum(self._weights))
|
||||
for i, freq in enumerate(self._frequency_points):
|
||||
amplitudes = self._smearing_function.calc(self._frequencies - freq)
|
||||
for j in range(self._partial_dos.shape[0]):
|
||||
self._partial_dos[j, i] = np.dot(
|
||||
weights, self._eigvecs2[:, j, :] * amplitudes).sum()
|
||||
for j in range(self._projected_dos.shape[0]):
|
||||
self._projected_dos[j, i] = np.dot(
|
||||
weights, self._eigvecs2[:, j, :] * amplitudes
|
||||
).sum()
|
||||
|
||||
def _run_tetrahedron_method(self):
|
||||
num_pdos = self._eigvecs2.shape[1]
|
||||
num_freqs = len(self._frequency_points)
|
||||
self._partial_dos = np.zeros((num_pdos, num_freqs), dtype='double')
|
||||
self._projected_dos = np.zeros((num_pdos, num_freqs), dtype="double")
|
||||
thm = self._tetrahedron_mesh
|
||||
thm.set(value='I', frequency_points=self._frequency_points)
|
||||
thm.set(value="I", frequency_points=self._frequency_points)
|
||||
for i, iw in enumerate(thm):
|
||||
w = self._weights[i]
|
||||
self._partial_dos += np.dot(iw * w, self._eigvecs2[i].T).T
|
||||
self._projected_dos += np.dot(iw * w, self._eigvecs2[i].T).T
|
||||
|
||||
def _run_tetrahedron_method_dos(self):
|
||||
mesh_numbers = self._mesh_object.mesh_numbers
|
||||
cell = self._mesh_object.dynamical_matrix.primitive
|
||||
reciprocal_lattice = np.linalg.inv(cell.get_cell())
|
||||
reciprocal_lattice = np.linalg.inv(cell.cell)
|
||||
tm = TetrahedronMethod(reciprocal_lattice, mesh=mesh_numbers)
|
||||
pdos = run_tetrahedron_method_dos(
|
||||
mesh_numbers,
|
||||
|
@ -537,5 +450,250 @@ class PartialDos(Dos):
|
|||
self._mesh_object.grid_address,
|
||||
self._mesh_object.grid_mapping_table,
|
||||
tm.get_tetrahedra(),
|
||||
coef=self._eigvecs2)
|
||||
self._partial_dos = pdos.T
|
||||
coef=self._eigvecs2,
|
||||
)
|
||||
self._projected_dos = pdos.T
|
||||
|
||||
|
||||
class PartialDos(ProjectedDos):
|
||||
"""Class to calculate partial DOS."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mesh_object: Mesh,
|
||||
sigma=None,
|
||||
use_tetrahedron_method=False,
|
||||
direction=None,
|
||||
xyz_projection=False,
|
||||
):
|
||||
"""Init method."""
|
||||
warnings.warn(
|
||||
"PartialDos class is deprecated. Use ProjectedDOS instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
super().__init__(
|
||||
mesh_object,
|
||||
sigma=sigma,
|
||||
use_tetrahedron_method=use_tetrahedron_method,
|
||||
direction=direction,
|
||||
xyz_projection=xyz_projection,
|
||||
)
|
||||
|
||||
|
||||
def get_pdos_indices(symmetry):
|
||||
"""Return atomic indieces grouped by symmetry."""
|
||||
mapping = symmetry.get_map_atoms()
|
||||
return [list(np.where(mapping == i)[0]) for i in symmetry.get_independent_atoms()]
|
||||
|
||||
|
||||
def write_total_dos(
|
||||
frequency_points, total_dos, comment=None, filename="total_dos.dat"
|
||||
):
|
||||
"""Write total_dos.dat."""
|
||||
with open(filename, "w") as fp:
|
||||
if comment is not None:
|
||||
fp.write("# %s\n" % comment)
|
||||
|
||||
for freq, dos in zip(frequency_points, total_dos):
|
||||
fp.write("%20.10f%20.10f\n" % (freq, dos))
|
||||
|
||||
|
||||
def write_partial_dos(
|
||||
frequency_points, partial_dos, comment=None, filename="partial_dos.dat"
|
||||
):
|
||||
"""Write partial_dos.dat."""
|
||||
warnings.warn(
|
||||
"write_partial_dos() is deprecated. Use write_projected_dos() instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
write_projected_dos(
|
||||
frequency_points, partial_dos, comment=comment, filename=filename
|
||||
)
|
||||
|
||||
|
||||
def write_projected_dos(
|
||||
frequency_points, projected_dos, comment=None, filename="projected_dos.dat"
|
||||
):
|
||||
"""Write projected_dos.dat."""
|
||||
with open(filename, "w") as fp:
|
||||
if comment is not None:
|
||||
fp.write("# %s\n" % comment)
|
||||
|
||||
for freq, pdos in zip(frequency_points, projected_dos.T):
|
||||
fp.write("%20.10f" % freq)
|
||||
fp.write(("%20.10f" * len(pdos)) % tuple(pdos))
|
||||
fp.write("\n")
|
||||
|
||||
|
||||
def plot_total_dos(
|
||||
ax,
|
||||
frequency_points,
|
||||
total_dos,
|
||||
freq_Debye=None,
|
||||
Debye_fit_coef=None,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False,
|
||||
):
|
||||
"""Plot total DOS."""
|
||||
ax.xaxis.set_ticks_position("both")
|
||||
ax.yaxis.set_ticks_position("both")
|
||||
ax.xaxis.set_tick_params(which="both", direction="in")
|
||||
ax.yaxis.set_tick_params(which="both", direction="in")
|
||||
|
||||
if freq_Debye is not None:
|
||||
freq_pitch = frequency_points[1] - frequency_points[0]
|
||||
num_points = int(freq_Debye / freq_pitch)
|
||||
freqs = np.linspace(0, freq_Debye, num_points + 1)
|
||||
|
||||
if flip_xy:
|
||||
ax.plot(total_dos, frequency_points, "r-", linewidth=1)
|
||||
if freq_Debye:
|
||||
ax.plot(
|
||||
np.append(Debye_fit_coef * freqs ** 2, 0),
|
||||
np.append(freqs, freq_Debye),
|
||||
"b-",
|
||||
linewidth=1,
|
||||
)
|
||||
else:
|
||||
ax.plot(frequency_points, total_dos, "r-", linewidth=1)
|
||||
if freq_Debye:
|
||||
ax.plot(
|
||||
np.append(freqs, freq_Debye),
|
||||
np.append(Debye_fit_coef * freqs ** 2, 0),
|
||||
"b-",
|
||||
linewidth=1,
|
||||
)
|
||||
|
||||
if xlabel:
|
||||
ax.set_xlabel(xlabel)
|
||||
if ylabel:
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
ax.grid(draw_grid)
|
||||
|
||||
|
||||
def plot_partial_dos(
|
||||
ax,
|
||||
frequency_points,
|
||||
partial_dos,
|
||||
indices=None,
|
||||
legend=None,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False,
|
||||
):
|
||||
"""Plot partial DOS."""
|
||||
warnings.warn(
|
||||
"plot_partial_dos() is deprecated. Use plot_projected_dos() instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
plot_projected_dos(
|
||||
ax,
|
||||
frequency_points,
|
||||
partial_dos,
|
||||
indices=indices,
|
||||
legend=legend,
|
||||
xlabel=xlabel,
|
||||
ylabel=ylabel,
|
||||
draw_grid=draw_grid,
|
||||
flip_xy=flip_xy,
|
||||
)
|
||||
|
||||
|
||||
def plot_projected_dos(
|
||||
ax,
|
||||
frequency_points,
|
||||
projected_dos,
|
||||
indices=None,
|
||||
legend=None,
|
||||
xlabel=None,
|
||||
ylabel=None,
|
||||
draw_grid=True,
|
||||
flip_xy=False,
|
||||
):
|
||||
"""Plot projected DOS."""
|
||||
ax.xaxis.set_ticks_position("both")
|
||||
ax.yaxis.set_ticks_position("both")
|
||||
ax.xaxis.set_tick_params(which="both", direction="in")
|
||||
ax.yaxis.set_tick_params(which="both", direction="in")
|
||||
|
||||
plots = []
|
||||
num_pdos = len(projected_dos)
|
||||
|
||||
if indices is None:
|
||||
indices = []
|
||||
for i in range(num_pdos):
|
||||
indices.append([i])
|
||||
|
||||
for set_for_sum in indices:
|
||||
pdos_sum = np.zeros_like(frequency_points)
|
||||
for i in set_for_sum:
|
||||
if i > num_pdos - 1:
|
||||
print("Index number '%d' is specified," % (i + 1))
|
||||
print("but it is not allowed to be larger than the number of " "atoms.")
|
||||
raise ValueError
|
||||
if i < 0:
|
||||
print(
|
||||
"Index number '%d' is specified, but it must be "
|
||||
"positive." % (i + 1)
|
||||
)
|
||||
raise ValueError
|
||||
pdos_sum += projected_dos[i]
|
||||
if flip_xy:
|
||||
plots.append(ax.plot(pdos_sum, frequency_points, linewidth=1))
|
||||
else:
|
||||
plots.append(ax.plot(frequency_points, pdos_sum, linewidth=1))
|
||||
|
||||
if legend is not None:
|
||||
ax.legend(legend)
|
||||
|
||||
if xlabel:
|
||||
ax.set_xlabel(xlabel)
|
||||
if ylabel:
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
ax.grid(draw_grid)
|
||||
|
||||
|
||||
def run_tetrahedron_method_dos(
|
||||
mesh,
|
||||
frequency_points,
|
||||
frequencies,
|
||||
grid_address,
|
||||
grid_mapping_table,
|
||||
relative_grid_address,
|
||||
coef=None,
|
||||
):
|
||||
"""Return (P)DOS calculated by tetrahedron method in C."""
|
||||
try:
|
||||
import phonopy._phonopy as phonoc
|
||||
except ImportError:
|
||||
import sys
|
||||
|
||||
print("Phonopy C-extension has to be built properly.")
|
||||
sys.exit(1)
|
||||
|
||||
if coef is None:
|
||||
_coef = np.ones((frequencies.shape[0], 1, frequencies.shape[1]), dtype="double")
|
||||
else:
|
||||
_coef = np.array(coef, dtype="double", order="C")
|
||||
arr_shape = frequencies.shape + (len(frequency_points), _coef.shape[1])
|
||||
dos = np.zeros(arr_shape, dtype="double")
|
||||
|
||||
phonoc.tetrahedron_method_dos(
|
||||
dos,
|
||||
np.array(mesh, dtype="int_"),
|
||||
frequency_points,
|
||||
frequencies,
|
||||
_coef,
|
||||
np.array(grid_address, dtype="int_", order="C"),
|
||||
np.array(grid_mapping_table, dtype="int_", order="C"),
|
||||
relative_grid_address,
|
||||
)
|
||||
if coef is None:
|
||||
return dos[:, :, :, 0].sum(axis=0).sum(axis=0) / np.prod(mesh)
|
||||
else:
|
||||
return dos.sum(axis=0).sum(axis=0) / np.prod(mesh)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Group velocity calculation."""
|
||||
# Copyright (C) 2013 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,40 +34,20 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
import textwrap
|
||||
from typing import Optional, Union
|
||||
|
||||
import numpy as np
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
from phonopy.harmonic.derivative_dynmat import DerivativeOfDynamicalMatrix
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
from phonopy.harmonic.force_constants import similarity_transformation
|
||||
from phonopy.phonon.degeneracy import degenerate_sets
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
def get_group_velocity(q, # q-point
|
||||
dynamical_matrix,
|
||||
q_length=None, # finite distance in q
|
||||
symmetry=None,
|
||||
frequency_factor_to_THz=VaspToTHz):
|
||||
"""Returns group velocity at a q-point."""
|
||||
gv = GroupVelocity(dynamical_matrix,
|
||||
q_length=q_length,
|
||||
symmetry=symmetry,
|
||||
frequency_factor_to_THz=frequency_factor_to_THz)
|
||||
gv.run([q])
|
||||
return gv.group_velocity[0]
|
||||
|
||||
|
||||
def delta_dynamical_matrix(q,
|
||||
delta_q,
|
||||
dynmat):
|
||||
dynmat.run(q - delta_q)
|
||||
dm1 = dynmat.dynamical_matrix
|
||||
dynmat.run(q + delta_q)
|
||||
dm2 = dynmat.dynamical_matrix
|
||||
return dm2 - dm1
|
||||
|
||||
|
||||
class GroupVelocity(object):
|
||||
r"""Class to calculate group velocities of phonons
|
||||
class GroupVelocity:
|
||||
r"""Class to calculate group velocities of phonons.
|
||||
|
||||
d omega ----
|
||||
------- = \ / omega
|
||||
|
@ -82,21 +63,24 @@ class GroupVelocity(object):
|
|||
----------
|
||||
group_velocity : ndarray
|
||||
Group velocities at q-points.
|
||||
shape=(q-points, 3), dtype='double', order='C'
|
||||
shape=(q-points, num_band, 3), dtype='double', order='C'
|
||||
q_length : float
|
||||
Distance in reciprocal space used to calculate finite difference of
|
||||
dynamcial matrix.
|
||||
|
||||
"""
|
||||
|
||||
Default_q_length = 1e-5
|
||||
|
||||
def __init__(self,
|
||||
dynamical_matrix,
|
||||
def __init__(
|
||||
self,
|
||||
dynamical_matrix: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
q_length=None,
|
||||
symmetry=None,
|
||||
symmetry: Optional[Symmetry] = None,
|
||||
frequency_factor_to_THz=VaspToTHz,
|
||||
cutoff_frequency=1e-4):
|
||||
"""
|
||||
cutoff_frequency=1e-4,
|
||||
):
|
||||
"""Init method.
|
||||
|
||||
dynamical_matrix : DynamicalMatrix or DynamicalMatrixNAC
|
||||
Dynamical matrix class instance.
|
||||
|
@ -118,21 +102,23 @@ class GroupVelocity(object):
|
|||
self._reciprocal_lattice_inv = primitive.cell
|
||||
self._reciprocal_lattice = np.linalg.inv(self._reciprocal_lattice_inv)
|
||||
self._q_length = q_length
|
||||
if self._dynmat.is_nac() and self._dynmat.nac_method == 'gonze':
|
||||
if self._dynmat.is_nac() and self._dynmat.nac_method == "gonze":
|
||||
if self._q_length is None:
|
||||
self._q_length = self.Default_q_length
|
||||
|
||||
self._ddm: Optional[DerivativeOfDynamicalMatrix]
|
||||
if self._q_length is None:
|
||||
self._ddm = DerivativeOfDynamicalMatrix(dynamical_matrix)
|
||||
else:
|
||||
self._ddm = None
|
||||
|
||||
self._symmetry = symmetry
|
||||
self._factor = frequency_factor_to_THz
|
||||
self._cutoff_frequency = cutoff_frequency
|
||||
|
||||
self._directions = np.array([[1, 2, 3],
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]], dtype='double')
|
||||
self._directions = np.array(
|
||||
[[1, 2, 3], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype="double"
|
||||
)
|
||||
self._directions[0] /= np.linalg.norm(self._directions[0])
|
||||
|
||||
self._q_points = None
|
||||
|
@ -152,43 +138,57 @@ class GroupVelocity(object):
|
|||
Direction in fractional coordinates of reciprocal space.
|
||||
|
||||
"""
|
||||
|
||||
self._q_points = q_points
|
||||
self._perturbation = perturbation
|
||||
if perturbation is None:
|
||||
# Give an random direction to break symmetry
|
||||
self._directions[0] = np.array([1, 2, 3])
|
||||
else:
|
||||
self._directions[0] = np.dot(
|
||||
self._reciprocal_lattice, perturbation)
|
||||
self._directions[0] = np.dot(self._reciprocal_lattice, perturbation)
|
||||
self._directions[0] /= np.linalg.norm(self._directions[0])
|
||||
|
||||
gv = [self._calculate_group_velocity_at_q(q) for q in self._q_points]
|
||||
self._group_velocities = np.array(gv, dtype='double', order='C')
|
||||
self._group_velocities = np.array(gv, dtype="double", order="C")
|
||||
|
||||
@property
|
||||
def q_length(self):
|
||||
"""Setter an getter of q_length."""
|
||||
return self._q_length
|
||||
|
||||
def get_q_length(self):
|
||||
return self.q_length
|
||||
|
||||
@q_length.setter
|
||||
def q_length(self, q_length):
|
||||
self._q_length = q_length
|
||||
|
||||
def get_q_length(self):
|
||||
"""Return q_length."""
|
||||
warnings.warn(
|
||||
"GroupVelocity.get_q_length() is deprecated. "
|
||||
"Use q_length attribute instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.q_length
|
||||
|
||||
def set_q_length(self, q_length):
|
||||
"""Set q_length."""
|
||||
warnings.warn(
|
||||
"GroupVelocity.set_q_length() is deprecated. "
|
||||
"Use q_length attribute instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
self.q_length = q_length
|
||||
|
||||
@property
|
||||
def group_velocities(self):
|
||||
"""Return group velocities."""
|
||||
return self._group_velocities
|
||||
|
||||
def get_group_velocity(self):
|
||||
"""Return group velocities."""
|
||||
warnings.warn(
|
||||
"GroupVelocity.get_group_velocity() is deprecated. "
|
||||
"Use the attribute group_velocities.",
|
||||
DeprecationWarning)
|
||||
"Use group_velocities attribute instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.group_velocities
|
||||
|
||||
def _calculate_group_velocity_at_q(self, q):
|
||||
|
@ -197,13 +197,13 @@ class GroupVelocity(object):
|
|||
eigvals, eigvecs = np.linalg.eigh(dm)
|
||||
eigvals = eigvals.real
|
||||
freqs = np.sqrt(abs(eigvals)) * np.sign(eigvals) * self._factor
|
||||
gv = np.zeros((len(freqs), 3), dtype='double', order='C')
|
||||
gv = np.zeros((len(freqs), 3), dtype="double", order="C")
|
||||
deg_sets = degenerate_sets(freqs)
|
||||
|
||||
ddms = self._get_dD(np.array(q))
|
||||
pos = 0
|
||||
for deg in deg_sets:
|
||||
gv[pos:pos+len(deg)] = self._perturb_D(ddms, eigvecs[:, deg])
|
||||
gv[pos : pos + len(deg)] = self._perturb_D(ddms, eigvecs[:, deg])
|
||||
pos += len(deg)
|
||||
|
||||
for i, f in enumerate(freqs):
|
||||
|
@ -222,7 +222,6 @@ class GroupVelocity(object):
|
|||
|
||||
def _symmetrize_group_velocity(self, gv, q):
|
||||
"""Symmetrize obtained group velocities using site symmetries."""
|
||||
|
||||
rotations = []
|
||||
for r in self._symmetry.reciprocal_operations:
|
||||
q_in_BZ = q - np.rint(q)
|
||||
|
@ -238,38 +237,35 @@ class GroupVelocity(object):
|
|||
return gv_sym / len(rotations)
|
||||
|
||||
def _get_dD(self, q):
|
||||
"""Compute derivative or finite difference of dynamcial matrices"""
|
||||
|
||||
"""Compute derivative or finite difference of dynamcial matrices."""
|
||||
if self._q_length is None:
|
||||
return self._get_dD_analytical(q)
|
||||
else:
|
||||
return self._get_dD_FD(q)
|
||||
|
||||
def _get_dD_FD(self, q):
|
||||
"""Compute finite difference of dynamcial matrices"""
|
||||
|
||||
"""Compute finite difference of dynamcial matrices."""
|
||||
ddm = []
|
||||
for dqc in self._directions * self._q_length:
|
||||
dq = np.dot(self._reciprocal_lattice_inv, dqc)
|
||||
ddm.append(delta_dynamical_matrix(q, dq, self._dynmat) /
|
||||
self._q_length / 2)
|
||||
ddm.append(
|
||||
_delta_dynamical_matrix(q, dq, self._dynmat) / self._q_length / 2
|
||||
)
|
||||
return np.array(ddm)
|
||||
|
||||
def _get_dD_analytical(self, q):
|
||||
"""Compute derivative of dynamcial matrices"""
|
||||
|
||||
"""Compute derivative of dynamcial matrices."""
|
||||
self._ddm.run(q)
|
||||
ddm = self._ddm.get_derivative_of_dynamical_matrix()
|
||||
dtype = "c%d" % (np.dtype('double').itemsize * 2)
|
||||
ddm_dirs = np.zeros((len(self._directions),) + ddm.shape[1:],
|
||||
dtype=dtype)
|
||||
dtype = "c%d" % (np.dtype("double").itemsize * 2)
|
||||
ddm_dirs = np.zeros((len(self._directions),) + ddm.shape[1:], dtype=dtype)
|
||||
for i, dq in enumerate(self._directions):
|
||||
for j in range(3):
|
||||
ddm_dirs[i] += dq[j] * ddm[j]
|
||||
return ddm_dirs
|
||||
|
||||
def _perturb_D(self, ddms, eigsets):
|
||||
"""Treat degeneracy
|
||||
"""Treat degeneracy.
|
||||
|
||||
Group velocities are calculated using analytical continuation using
|
||||
specified directions (self._directions) in reciprocal space.
|
||||
|
@ -281,15 +277,39 @@ class GroupVelocity(object):
|
|||
List of phonon eigenvectors of degenerate bands.
|
||||
|
||||
"""
|
||||
|
||||
eigvals, eigvecs = np.linalg.eigh(
|
||||
np.dot(eigsets.T.conj(), np.dot(ddms[0], eigsets)))
|
||||
_, eigvecs = np.linalg.eigh(np.dot(eigsets.T.conj(), np.dot(ddms[0], eigsets)))
|
||||
|
||||
gv = []
|
||||
rot_eigsets = np.dot(eigsets, eigvecs)
|
||||
for ddm in ddms[1:]:
|
||||
gv.append(
|
||||
np.diag(np.dot(rot_eigsets.T.conj(),
|
||||
np.dot(ddm, rot_eigsets))).real)
|
||||
np.diag(np.dot(rot_eigsets.T.conj(), np.dot(ddm, rot_eigsets))).real
|
||||
)
|
||||
|
||||
return np.transpose(gv)
|
||||
|
||||
|
||||
def get_group_velocity(
|
||||
q, # q-point
|
||||
dynamical_matrix,
|
||||
q_length=None, # finite distance in q
|
||||
symmetry=None,
|
||||
frequency_factor_to_THz=VaspToTHz,
|
||||
):
|
||||
"""Return group velocity at a q-point."""
|
||||
gv = GroupVelocity(
|
||||
dynamical_matrix,
|
||||
q_length=q_length,
|
||||
symmetry=symmetry,
|
||||
frequency_factor_to_THz=frequency_factor_to_THz,
|
||||
)
|
||||
gv.run([q])
|
||||
return gv.group_velocity[0]
|
||||
|
||||
|
||||
def _delta_dynamical_matrix(q, delta_q, dynmat):
|
||||
dynmat.run(q - delta_q)
|
||||
dm1 = dynmat.dynamical_matrix
|
||||
dynmat.run(q + delta_q)
|
||||
dm2 = dynmat.dynamical_matrix
|
||||
return dm2 - dm1
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Calculate irreducible representation from eigenvectors."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,31 +33,50 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
from phonopy.harmonic.force_constants import similarity_transformation
|
||||
from phonopy.phonon.degeneracy import degenerate_sets as get_degenerate_sets
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
from phonopy.harmonic.derivative_dynmat import DerivativeOfDynamicalMatrix
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
from phonopy.harmonic.force_constants import similarity_transformation
|
||||
from phonopy.phonon.character_table import character_table
|
||||
from phonopy.phonon.degeneracy import degenerate_sets as get_degenerate_sets
|
||||
from phonopy.structure.symmetry import Symmetry
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
class IrReps(object):
|
||||
def __init__(self,
|
||||
dynamical_matrix,
|
||||
class IrReps:
|
||||
"""Class to calculate irreducible representations from eigenvectors.
|
||||
|
||||
Methods and terminologies used in this class may be easily found
|
||||
in textbooks such as
|
||||
- Group theory with applications in chemical physics by Patrick Jacobs
|
||||
- Symmetry and condensed matter physics by M. El-Batanouny and F. Wooten
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dynamical_matrix: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
q,
|
||||
is_little_cogroup=False,
|
||||
nac_q_direction=None,
|
||||
factor=VaspToTHz,
|
||||
symprec=1e-5,
|
||||
degeneracy_tolerance=1e-5,
|
||||
log_level=0):
|
||||
degeneracy_tolerance=None,
|
||||
log_level=0,
|
||||
):
|
||||
"""Init method."""
|
||||
self._is_little_cogroup = is_little_cogroup
|
||||
self._nac_q_direction = nac_q_direction
|
||||
self._factor = factor
|
||||
self._log_level = log_level
|
||||
|
||||
self._q = np.array(q)
|
||||
if degeneracy_tolerance is None:
|
||||
self._degeneracy_tolerance = 1e-5
|
||||
else:
|
||||
self._degeneracy_tolerance = degeneracy_tolerance
|
||||
self._symprec = symprec
|
||||
self._primitive = dynamical_matrix.primitive
|
||||
|
@ -65,25 +85,30 @@ class IrReps(object):
|
|||
self._character_table = None
|
||||
|
||||
def run(self):
|
||||
"""Calculate irreps."""
|
||||
self._set_eigenvectors(self._dynamical_matrix)
|
||||
self._symmetry_dataset = Symmetry(self._primitive,
|
||||
symprec=self._symprec).get_dataset()
|
||||
self._symmetry_dataset = Symmetry(
|
||||
self._primitive, symprec=self._symprec
|
||||
).dataset
|
||||
|
||||
if not self._is_primitive_cell():
|
||||
print('')
|
||||
print("")
|
||||
print("Non-primitve cell is used.")
|
||||
print("Your unit cell may be transformed to a primitive cell "
|
||||
"by PRIMITIVE_AXIS tag.")
|
||||
print(
|
||||
"Your unit cell may be transformed to a primitive cell "
|
||||
"by PRIMITIVE_AXIS tag."
|
||||
)
|
||||
return False
|
||||
|
||||
(self._rotations_at_q,
|
||||
self._translations_at_q) = self._get_rotations_at_q()
|
||||
(self._rotations_at_q, self._translations_at_q) = self._get_rotations_at_q()
|
||||
|
||||
self._g = len(self._rotations_at_q)
|
||||
|
||||
(self._pointgroup_symbol,
|
||||
(
|
||||
self._pointgroup_symbol,
|
||||
self._transformation_matrix,
|
||||
self._conventional_rotations) = self._get_conventional_rotations()
|
||||
self._conventional_rotations,
|
||||
) = self._get_conventional_rotations()
|
||||
|
||||
self._ground_matrices = self._get_ground_matrix()
|
||||
self._degenerate_sets = self._get_degenerate_sets()
|
||||
|
@ -92,8 +117,10 @@ class IrReps(object):
|
|||
|
||||
self._ir_labels = None
|
||||
|
||||
if (self._pointgroup_symbol in character_table.keys() and
|
||||
character_table[self._pointgroup_symbol] is not None):
|
||||
if (
|
||||
self._pointgroup_symbol in character_table.keys()
|
||||
and character_table[self._pointgroup_symbol] is not None
|
||||
):
|
||||
self._rotation_symbols = self._get_rotation_symbols()
|
||||
if (abs(self._q) < self._symprec).all() and self._rotation_symbols:
|
||||
self._ir_labels = self._get_ir_labels()
|
||||
|
@ -109,47 +136,61 @@ class IrReps(object):
|
|||
return True
|
||||
|
||||
def _get_degenerate_sets(self):
|
||||
deg_sets = get_degenerate_sets(self._freqs,
|
||||
cutoff=self._degeneracy_tolerance)
|
||||
deg_sets = get_degenerate_sets(self._freqs, cutoff=self._degeneracy_tolerance)
|
||||
self._ddm.run(self._q)
|
||||
return deg_sets
|
||||
|
||||
def get_band_indices(self):
|
||||
"""Return band indices.
|
||||
|
||||
Returns
|
||||
-------
|
||||
See docstring of ``degenerate_sets``.
|
||||
|
||||
"""
|
||||
return self._degenerate_sets
|
||||
|
||||
def get_characters(self):
|
||||
"""Return characters of irreps."""
|
||||
return self._characters
|
||||
|
||||
def get_eigenvectors(self):
|
||||
"""Return eigenvectors."""
|
||||
return self._eigvecs
|
||||
|
||||
def get_irreps(self):
|
||||
"""Return irreps."""
|
||||
return self._irreps
|
||||
|
||||
def get_ground_matrices(self):
|
||||
"""Return ground matrices."""
|
||||
return self._ground_matrices
|
||||
|
||||
def get_rotation_symbols(self):
|
||||
"""Return symbols assigned to rotation matrices."""
|
||||
return self._rotation_symbols
|
||||
|
||||
def get_rotations(self):
|
||||
"""Return rotation matrices."""
|
||||
return self._conventional_rotations
|
||||
|
||||
def get_projection_operators(self, idx_irrep, i=None, j=None):
|
||||
"""Return projection operators."""
|
||||
if i is None or j is None:
|
||||
return self._get_character_projection_operators(idx_irrep)
|
||||
else:
|
||||
return self._get_projection_operators(idx_irrep, i, j)
|
||||
|
||||
def show(self, show_irreps=False):
|
||||
"""Show irreps."""
|
||||
self._show(show_irreps)
|
||||
|
||||
def write_yaml(self, show_irreps=False):
|
||||
"""Write irreps in yaml file."""
|
||||
self._write_yaml(show_irreps)
|
||||
|
||||
def _set_eigenvectors(self, dm):
|
||||
if (self._nac_q_direction is not None and
|
||||
(np.abs(self._q) < 1e-5).all()):
|
||||
if self._nac_q_direction is not None and (np.abs(self._q) < 1e-5).all():
|
||||
dm.run(self._q, q_direction=self._nac_q_direction)
|
||||
else:
|
||||
dm.run(self._q)
|
||||
|
@ -159,8 +200,9 @@ class IrReps(object):
|
|||
def _get_rotations_at_q(self):
|
||||
rotations_at_q = []
|
||||
trans_at_q = []
|
||||
for r, t in zip(self._symmetry_dataset['rotations'],
|
||||
self._symmetry_dataset['translations']):
|
||||
for r, t in zip(
|
||||
self._symmetry_dataset["rotations"], self._symmetry_dataset["translations"]
|
||||
):
|
||||
|
||||
# Using r is used instead of np.linalg.inv(r)
|
||||
diff = np.dot(self._q, r) - self._q
|
||||
|
@ -176,14 +218,13 @@ class IrReps(object):
|
|||
|
||||
def _get_conventional_rotations(self):
|
||||
rotations = self._rotations_at_q.copy()
|
||||
pointgroup_symbol = self._symmetry_dataset['pointgroup']
|
||||
transformation_matrix = self._symmetry_dataset['transformation_matrix']
|
||||
pointgroup_symbol = self._symmetry_dataset["pointgroup"]
|
||||
transformation_matrix = self._symmetry_dataset["transformation_matrix"]
|
||||
conventional_rotations = self._transform_rotations(
|
||||
transformation_matrix, rotations)
|
||||
transformation_matrix, rotations
|
||||
)
|
||||
|
||||
return (pointgroup_symbol,
|
||||
transformation_matrix,
|
||||
conventional_rotations)
|
||||
return (pointgroup_symbol, transformation_matrix, conventional_rotations)
|
||||
|
||||
def _transform_rotations(self, tmat, rotations):
|
||||
trans_rots = []
|
||||
|
@ -197,10 +238,9 @@ class IrReps(object):
|
|||
def _get_ground_matrix(self):
|
||||
matrices = []
|
||||
|
||||
for (r, t) in zip(self._rotations_at_q,
|
||||
self._translations_at_q):
|
||||
for (r, t) in zip(self._rotations_at_q, self._translations_at_q):
|
||||
|
||||
lat = self._primitive.get_cell().T
|
||||
lat = self._primitive.cell.T
|
||||
r_cart = similarity_transformation(lat, r)
|
||||
|
||||
perm_mat = self._get_modified_permutation_matrix(r, t)
|
||||
|
@ -217,8 +257,8 @@ class IrReps(object):
|
|||
return np.array(characters), np.array(irrep_dims)
|
||||
|
||||
def _get_modified_permutation_matrix(self, r, t):
|
||||
num_atom = self._primitive.get_number_of_atoms()
|
||||
pos = self._primitive.get_scaled_positions()
|
||||
num_atom = len(self._primitive)
|
||||
pos = self._primitive.scaled_positions
|
||||
matrix = np.zeros((num_atom, num_atom), dtype=complex)
|
||||
for i, p1 in enumerate(pos):
|
||||
p_rot = np.dot(r, p1) + t
|
||||
|
@ -232,8 +272,7 @@ class IrReps(object):
|
|||
# considering internal atomic positions, so
|
||||
# the phase factors of eigenvectors are shifted in
|
||||
# _get_irreps().
|
||||
phase_factor = np.dot(
|
||||
self._q, np.dot(np.linalg.inv(r), p2 - p_rot))
|
||||
phase_factor = np.dot(self._q, np.dot(np.linalg.inv(r), p2 - p_rot))
|
||||
|
||||
# This phase factor comes from non-pure-translation of
|
||||
# each symmetry opration.
|
||||
|
@ -247,8 +286,12 @@ class IrReps(object):
|
|||
def _get_irreps(self):
|
||||
eigvecs = []
|
||||
phases = np.kron(
|
||||
[np.exp(2j * np.pi * np.dot(self._q, pos))
|
||||
for pos in self._primitive.get_scaled_positions()], [1, 1, 1])
|
||||
[
|
||||
np.exp(2j * np.pi * np.dot(self._q, pos))
|
||||
for pos in self._primitive.scaled_positions
|
||||
],
|
||||
[1, 1, 1],
|
||||
)
|
||||
for vec in self._eigvecs.T:
|
||||
eigvecs.append(vec * phases)
|
||||
|
||||
|
@ -278,20 +321,33 @@ class IrReps(object):
|
|||
def _get_character_projection_operators(self, idx_irrep):
|
||||
dim = self._irrep_dims[idx_irrep]
|
||||
chars = self._characters[idx_irrep]
|
||||
return np.sum([mat * char.conj()
|
||||
for mat, char in zip(self._ground_matrices, chars)],
|
||||
axis=0) * dim / self._g
|
||||
return (
|
||||
np.sum(
|
||||
[mat * char.conj() for mat, char in zip(self._ground_matrices, chars)],
|
||||
axis=0,
|
||||
)
|
||||
* dim
|
||||
/ self._g
|
||||
)
|
||||
|
||||
def _get_projection_operators(self, idx_irrep, i, j):
|
||||
dim = self._irrep_dims[idx_irrep]
|
||||
return np.sum([mat * r[i, j].conj() for mat, r
|
||||
in zip(self._ground_matrices, self._irreps[idx_irrep])],
|
||||
axis=0) * dim / self._g
|
||||
return (
|
||||
np.sum(
|
||||
[
|
||||
mat * r[i, j].conj()
|
||||
for mat, r in zip(self._ground_matrices, self._irreps[idx_irrep])
|
||||
],
|
||||
axis=0,
|
||||
)
|
||||
* dim
|
||||
/ self._g
|
||||
)
|
||||
|
||||
def _get_rotation_symbols(self):
|
||||
ptg_symbol = self._pointgroup_symbol
|
||||
for ct in character_table[ptg_symbol]:
|
||||
mapping_table = ct['mapping_table']
|
||||
mapping_table = ct["mapping_table"]
|
||||
rotation_symbols = []
|
||||
for r in self._conventional_rotations:
|
||||
rotation_symbols.append(_get_rotation_symbol(r, mapping_table))
|
||||
|
@ -307,21 +363,21 @@ class IrReps(object):
|
|||
|
||||
def _get_ir_labels(self):
|
||||
ir_labels = []
|
||||
rot_list = self._character_table['rotation_list']
|
||||
char_table = self._character_table['character_table']
|
||||
rot_list = self._character_table["rotation_list"]
|
||||
char_table = self._character_table["character_table"]
|
||||
for chars, deg_set in zip(self._characters, self._degenerate_sets):
|
||||
chars_ordered = np.zeros(len(rot_list), dtype=complex)
|
||||
for rs, ch in zip(self._rotation_symbols, chars):
|
||||
chars_ordered[rot_list.index(rs)] += ch
|
||||
|
||||
for i, rl in enumerate(rot_list):
|
||||
chars_ordered[i] /= len(
|
||||
self._character_table['mapping_table'][rl])
|
||||
chars_ordered[i] /= len(self._character_table["mapping_table"][rl])
|
||||
|
||||
found = False
|
||||
for ct_label in char_table.keys():
|
||||
if (abs(chars_ordered - np.array(char_table[ct_label])) <
|
||||
self._symprec).all():
|
||||
if (
|
||||
abs(chars_ordered - np.array(char_table[ct_label])) < self._symprec
|
||||
).all():
|
||||
ir_labels.append(ct_label)
|
||||
found = True
|
||||
break
|
||||
|
@ -342,8 +398,8 @@ class IrReps(object):
|
|||
|
||||
def _is_primitive_cell(self):
|
||||
num_identity = 0
|
||||
for r in self._symmetry_dataset['rotations']:
|
||||
if (r - np.eye(3, dtype='intc') == 0).all():
|
||||
for r in self._symmetry_dataset["rotations"]:
|
||||
if (r - np.eye(3, dtype="intc") == 0).all():
|
||||
num_identity += 1
|
||||
if num_identity > 1:
|
||||
return False
|
||||
|
@ -351,39 +407,41 @@ class IrReps(object):
|
|||
return True
|
||||
|
||||
def _show(self, show_irreps):
|
||||
print('')
|
||||
print("")
|
||||
print("-------------------------------")
|
||||
print(" Irreducible representations")
|
||||
print("-------------------------------")
|
||||
print("q-point: %s" % self._q)
|
||||
print("Point group: %s" % self._pointgroup_symbol)
|
||||
print('')
|
||||
print("")
|
||||
|
||||
if (np.abs(self._q) < self._symprec).all():
|
||||
width = 6
|
||||
print("Original rotation matrices:")
|
||||
print('')
|
||||
print("")
|
||||
_print_rotations(self._rotations_at_q, width=width)
|
||||
else:
|
||||
width = 4
|
||||
print("Original symmetry operations:")
|
||||
print('')
|
||||
_print_rotations(self._rotations_at_q,
|
||||
translations=self._translations_at_q,
|
||||
width=width)
|
||||
print("")
|
||||
_print_rotations(
|
||||
self._rotations_at_q, translations=self._translations_at_q, width=width
|
||||
)
|
||||
|
||||
print("Transformation matrix:")
|
||||
print('')
|
||||
print("")
|
||||
for v in self._transformation_matrix:
|
||||
print("%6.3f %6.3f %6.3f" % tuple(v))
|
||||
print('')
|
||||
print("")
|
||||
print("Rotation matrices by transformation matrix:")
|
||||
print('')
|
||||
_print_rotations(self._conventional_rotations,
|
||||
print("")
|
||||
_print_rotations(
|
||||
self._conventional_rotations,
|
||||
rotation_symbols=self._rotation_symbols,
|
||||
width=width)
|
||||
width=width,
|
||||
)
|
||||
print("Character table:")
|
||||
print('')
|
||||
print("")
|
||||
for i, deg_set in enumerate(self._degenerate_sets):
|
||||
text = "%3d (%8.3f): " % (deg_set[0] + 1, self._freqs[deg_set[0]])
|
||||
if self._ir_labels is None:
|
||||
|
@ -394,23 +452,24 @@ class IrReps(object):
|
|||
else:
|
||||
print("%s%s" % (text, self._ir_labels[i]))
|
||||
_print_characters(self._characters[i])
|
||||
print('')
|
||||
print("")
|
||||
|
||||
if show_irreps:
|
||||
self._show_irreps()
|
||||
|
||||
def _show_irreps(self):
|
||||
print("IR representations:")
|
||||
print('')
|
||||
print("")
|
||||
|
||||
for i, (deg_set, irrep_Rs) in enumerate(zip(self._degenerate_sets,
|
||||
self._irreps)):
|
||||
for i, (deg_set, irrep_Rs) in enumerate(
|
||||
zip(self._degenerate_sets, self._irreps)
|
||||
):
|
||||
print("%3d (%8.3f):" % (deg_set[0] + 1, self._freqs[deg_set[0]]))
|
||||
print('')
|
||||
print("")
|
||||
for j, irrep_R in enumerate(irrep_Rs):
|
||||
for k, irrep_Rk in enumerate(irrep_R):
|
||||
text = " "
|
||||
for l, irrep_Rkl in enumerate(irrep_Rk):
|
||||
for ll, irrep_Rkl in enumerate(irrep_Rk):
|
||||
if irrep_Rkl.real > 0:
|
||||
sign_r = " "
|
||||
else:
|
||||
|
@ -426,21 +485,24 @@ class IrReps(object):
|
|||
else:
|
||||
str_index = " "
|
||||
|
||||
if l > 0:
|
||||
str_index = ''
|
||||
if ll > 0:
|
||||
str_index = ""
|
||||
|
||||
text += "%s (%s%5.3f %s%5.3fi) " % (
|
||||
str_index,
|
||||
sign_r, abs(irrep_Rkl.real),
|
||||
sign_i, abs(irrep_Rkl.imag))
|
||||
sign_r,
|
||||
abs(irrep_Rkl.real),
|
||||
sign_i,
|
||||
abs(irrep_Rkl.imag),
|
||||
)
|
||||
print(text)
|
||||
if len(irrep_R) > 1:
|
||||
print('')
|
||||
print("")
|
||||
if len(irrep_R) == 1:
|
||||
print('')
|
||||
print("")
|
||||
|
||||
def _write_yaml(self, show_irreps):
|
||||
w = open("irreps.yaml", 'w')
|
||||
w = open("irreps.yaml", "w")
|
||||
w.write("q-position: [ %12.7f, %12.7f, %12.7f ]\n" % tuple(self._q))
|
||||
w.write("point_group: %s\n" % self._pointgroup_symbol)
|
||||
w.write("transformation_matrix:\n")
|
||||
|
@ -487,30 +549,39 @@ class IrReps(object):
|
|||
w.write("\n")
|
||||
w.write("irreps:\n")
|
||||
for i, (deg_set, irrep_Rs) in enumerate(
|
||||
zip(self._degenerate_sets, self._irreps)):
|
||||
zip(self._degenerate_sets, self._irreps)
|
||||
):
|
||||
w.write("- # %d\n" % (i + 1))
|
||||
for j, irrep_R in enumerate(irrep_Rs):
|
||||
if self._rotation_symbols:
|
||||
symbol = self._rotation_symbols[j]
|
||||
else:
|
||||
symbol = ''
|
||||
symbol = ""
|
||||
if len(deg_set) > 1:
|
||||
w.write(" - # %d %s\n" % (j + 1, symbol))
|
||||
for k, v in enumerate(irrep_R):
|
||||
w.write(" - [ ")
|
||||
for x in v[:-1]:
|
||||
w.write("%10.7f, %10.7f, " % (x.real, x.imag))
|
||||
w.write("%10.7f, %10.7f ] # (" %
|
||||
(v[-1].real, v[-1].imag))
|
||||
w.write("%10.7f, %10.7f ] # (" % (v[-1].real, v[-1].imag))
|
||||
|
||||
w.write(("%5.0f" * len(v)) %
|
||||
tuple((np.angle(v) / np.pi * 180) % 360))
|
||||
w.write(
|
||||
("%5.0f" * len(v))
|
||||
% tuple((np.angle(v) / np.pi * 180) % 360)
|
||||
)
|
||||
w.write(")\n")
|
||||
else:
|
||||
x = irrep_R[0][0]
|
||||
w.write(" - [ [ %10.7f, %10.7f ] ] # (%3.0f) %d %s\n" %
|
||||
(x.real, x.imag,
|
||||
(np.angle(x) / np.pi * 180) % 360, j + 1, symbol))
|
||||
w.write(
|
||||
" - [ [ %10.7f, %10.7f ] ] # (%3.0f) %d %s\n"
|
||||
% (
|
||||
x.real,
|
||||
x.imag,
|
||||
(np.angle(x) / np.pi * 180) % 360,
|
||||
j + 1,
|
||||
symbol,
|
||||
)
|
||||
)
|
||||
|
||||
pass
|
||||
|
||||
|
@ -543,20 +614,19 @@ def _print_characters(characters, width=6):
|
|||
text = ""
|
||||
|
||||
|
||||
def _get_rotation_text(rotations,
|
||||
translations,
|
||||
rotation_symbols,
|
||||
width,
|
||||
num_rest,
|
||||
i):
|
||||
def _get_rotation_text(rotations, translations, rotation_symbols, width, num_rest, i):
|
||||
lines = []
|
||||
if rotation_symbols is None:
|
||||
if translations is None:
|
||||
lines.append((" %2d " * num_rest) %
|
||||
tuple(np.arange(i * width, i * width + num_rest) + 1))
|
||||
lines.append(
|
||||
(" %2d " * num_rest)
|
||||
% tuple(np.arange(i * width, i * width + num_rest) + 1)
|
||||
)
|
||||
else:
|
||||
lines.append((" %2d " * num_rest) %
|
||||
tuple(np.arange(i * width, i * width + num_rest) + 1))
|
||||
lines.append(
|
||||
(" %2d " * num_rest)
|
||||
% tuple(np.arange(i * width, i * width + num_rest) + 1)
|
||||
)
|
||||
else:
|
||||
text = ""
|
||||
for k in range(num_rest):
|
||||
|
@ -588,29 +658,24 @@ def _get_rotation_text(rotations,
|
|||
if translations is not None:
|
||||
text += "%5.2f " % translations[i * width + k][j]
|
||||
lines.append(text)
|
||||
lines.append('')
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _print_rotations(rotations,
|
||||
translations=None,
|
||||
rotation_symbols=None,
|
||||
width=6):
|
||||
def _print_rotations(rotations, translations=None, rotation_symbols=None, width=6):
|
||||
for i in range(len(rotations) // width):
|
||||
print(_get_rotation_text(rotations,
|
||||
translations,
|
||||
rotation_symbols,
|
||||
width,
|
||||
width,
|
||||
i))
|
||||
print(
|
||||
_get_rotation_text(
|
||||
rotations, translations, rotation_symbols, width, width, i
|
||||
)
|
||||
)
|
||||
|
||||
num_rest = len(rotations) % width
|
||||
if num_rest > 0:
|
||||
i = len(rotations) // width
|
||||
print(_get_rotation_text(rotations,
|
||||
translations,
|
||||
rotation_symbols,
|
||||
width,
|
||||
num_rest,
|
||||
i))
|
||||
print(
|
||||
_get_rotation_text(
|
||||
rotations, translations, rotation_symbols, width, num_rest, i
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Phonon calculation on sampling mesh."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,13 +33,17 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix
|
||||
from phonopy.structure.grid_points import GridPoints
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
class MeshBase(object):
|
||||
"""Base class of Mesh and IterMesh classes
|
||||
class MeshBase:
|
||||
"""Base class of Mesh and IterMesh classes.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
@ -71,8 +76,9 @@ class MeshBase(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
dynamical_matrix,
|
||||
def __init__(
|
||||
self,
|
||||
dynamical_matrix: DynamicalMatrix,
|
||||
mesh,
|
||||
shift=None,
|
||||
is_time_reversal=True,
|
||||
|
@ -80,21 +86,24 @@ class MeshBase(object):
|
|||
with_eigenvectors=False,
|
||||
is_gamma_center=False,
|
||||
rotations=None, # Point group operations in real space
|
||||
factor=VaspToTHz):
|
||||
self._mesh = np.array(mesh, dtype='intc')
|
||||
factor=VaspToTHz,
|
||||
):
|
||||
"""Init method."""
|
||||
self._mesh = np.array(mesh, dtype="intc")
|
||||
self._with_eigenvectors = with_eigenvectors
|
||||
self._factor = factor
|
||||
self._cell = dynamical_matrix.primitive
|
||||
self._dynamical_matrix = dynamical_matrix
|
||||
|
||||
self._gp = GridPoints(self._mesh,
|
||||
self._gp = GridPoints(
|
||||
self._mesh,
|
||||
np.linalg.inv(self._cell.cell),
|
||||
q_mesh_shift=shift,
|
||||
is_gamma_center=is_gamma_center,
|
||||
is_time_reversal=(is_time_reversal and
|
||||
is_mesh_symmetry),
|
||||
is_time_reversal=(is_time_reversal and is_mesh_symmetry),
|
||||
rotations=rotations,
|
||||
is_mesh_symmetry=is_mesh_symmetry)
|
||||
is_mesh_symmetry=is_mesh_symmetry,
|
||||
)
|
||||
|
||||
self._qpoints = self._gp.qpoints
|
||||
self._weights = self._gp.weights
|
||||
|
@ -106,60 +115,106 @@ class MeshBase(object):
|
|||
|
||||
@property
|
||||
def mesh_numbers(self):
|
||||
"""Return mesh numbers."""
|
||||
return self._mesh
|
||||
|
||||
def get_mesh_numbers(self):
|
||||
"""Return mesh numbers."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_mesh_numbers() is deprecated. Use mesh_numbers attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.mesh_numbers
|
||||
|
||||
@property
|
||||
def qpoints(self):
|
||||
"""Return (irreducible) q-points."""
|
||||
return self._qpoints
|
||||
|
||||
def get_qpoints(self):
|
||||
"""Return (irreducible) q-points."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_qpoints() is deprecated. Use qpoints attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.qpoints
|
||||
|
||||
@property
|
||||
def weights(self):
|
||||
"""Return (irreducible) weights of q-points."""
|
||||
return self._weights
|
||||
|
||||
def get_weights(self):
|
||||
"""Return (irreducible) weights of q-points."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_weights()) is deprecated. Use weights attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.weights
|
||||
|
||||
@property
|
||||
def grid_address(self):
|
||||
"""Return mesh grid addresses."""
|
||||
return self._gp.grid_address
|
||||
|
||||
def get_grid_address(self):
|
||||
"""Return mesh grid addresses."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_grid_address()) is deprecated. Use grid_address attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.grid_address
|
||||
|
||||
@property
|
||||
def ir_grid_points(self):
|
||||
"""Return irreducible grid indices."""
|
||||
return self._gp.ir_grid_points
|
||||
|
||||
def get_ir_grid_points(self):
|
||||
"""Return irreducible grid indices."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_ir_grid_points() is deprecated. "
|
||||
"Use ir_grid_points attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.ir_grid_points
|
||||
|
||||
@property
|
||||
def grid_mapping_table(self):
|
||||
"""Return grid index mapping table."""
|
||||
return self._gp.grid_mapping_table
|
||||
|
||||
def get_grid_mapping_table(self):
|
||||
"""Return grid index mapping table."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_grid_mapping_table() is deprecated. "
|
||||
"Use grid_mapping_table attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.grid_mapping_table
|
||||
|
||||
@property
|
||||
def dynamical_matrix(self):
|
||||
"""Return dynamical matrix class instance."""
|
||||
return self._dynamical_matrix
|
||||
|
||||
def get_dynamical_matrix(self):
|
||||
"""Return dynamical matrix class instance."""
|
||||
warnings.warn(
|
||||
"MeshBase.get_dynamical_matrix() is deprecated. "
|
||||
"Use dynamical_matrix attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.dynamical_matrix
|
||||
|
||||
@property
|
||||
def with_eigenvectors(self):
|
||||
"""Whether eigenvectors are calculated or not."""
|
||||
return self._with_eigenvectors
|
||||
|
||||
|
||||
class Mesh(MeshBase):
|
||||
"""Class for phonons on mesh grid
|
||||
"""Class for phonons on mesh grid.
|
||||
|
||||
Frequencies and eigenvectors can be also accessible by iterator
|
||||
representation to be compatible with IterMesh.
|
||||
|
@ -184,7 +239,9 @@ class Mesh(MeshBase):
|
|||
More attributes from MeshBase should be watched.
|
||||
|
||||
"""
|
||||
def __init__(self,
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dynamical_matrix,
|
||||
mesh,
|
||||
shift=None,
|
||||
|
@ -195,8 +252,10 @@ class Mesh(MeshBase):
|
|||
group_velocity=None,
|
||||
rotations=None, # Point group operations in real space
|
||||
factor=VaspToTHz,
|
||||
use_lapack_solver=False):
|
||||
MeshBase.__init__(self,
|
||||
use_lapack_solver=False,
|
||||
):
|
||||
"""Init method."""
|
||||
super().__init__(
|
||||
dynamical_matrix,
|
||||
mesh,
|
||||
shift=shift,
|
||||
|
@ -205,21 +264,27 @@ class Mesh(MeshBase):
|
|||
with_eigenvectors=with_eigenvectors,
|
||||
is_gamma_center=is_gamma_center,
|
||||
rotations=rotations,
|
||||
factor=factor)
|
||||
factor=factor,
|
||||
)
|
||||
|
||||
self._group_velocity = group_velocity
|
||||
self._group_velocities = None
|
||||
self._use_lapack_solver = use_lapack_solver
|
||||
|
||||
def __iter__(self):
|
||||
"""Define iterator over q-points.
|
||||
|
||||
Initially, all phonons are computed and stored in arrays.
|
||||
Then this is just used as an iterator to return exisiting results.
|
||||
The purpose of this iterator is compatible use of IterMesh.
|
||||
|
||||
"""
|
||||
if self._frequencies is None:
|
||||
self.run()
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __next__(self):
|
||||
"""Return phonon frequencies and eigenvectors at each q-point."""
|
||||
if self._q_count == len(self._qpoints):
|
||||
self._q_count = 0
|
||||
raise StopIteration
|
||||
|
@ -232,22 +297,30 @@ class Mesh(MeshBase):
|
|||
return self._frequencies[i], self._eigenvectors[i]
|
||||
|
||||
def run(self):
|
||||
"""Calculate phonons at all required q-points."""
|
||||
self._set_phonon()
|
||||
if self._group_velocity is not None:
|
||||
self._set_group_velocities(self._group_velocity)
|
||||
|
||||
@property
|
||||
def frequencies(self):
|
||||
"""Return phonon frequencies."""
|
||||
if self._frequencies is None:
|
||||
self.run()
|
||||
return self._frequencies
|
||||
|
||||
def get_frequencies(self):
|
||||
"""Return phonon frequencies."""
|
||||
warnings.warn(
|
||||
"Mesh.get_frequencies() is deprecated. " "Use frequencies attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.frequencies
|
||||
|
||||
@property
|
||||
def eigenvectors(self):
|
||||
"""
|
||||
"""Return eigenvectors.
|
||||
|
||||
Eigenvectors is a numpy array of three dimension.
|
||||
The first index runs through q-points.
|
||||
In the second and third indices, eigenvectors obtained
|
||||
|
@ -255,57 +328,70 @@ class Mesh(MeshBase):
|
|||
|
||||
The third index corresponds to the eigenvalue's index.
|
||||
The second index is for atoms [x1, y1, z1, x2, y2, z2, ...].
|
||||
|
||||
"""
|
||||
if self._frequencies is None:
|
||||
self.run()
|
||||
return self._eigenvectors
|
||||
|
||||
def get_eigenvectors(self):
|
||||
"""Return eigenvectors."""
|
||||
warnings.warn(
|
||||
"Mesh.get_eigenvectors() is deprecated. " "Use eigenvectors attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.eigenvectors
|
||||
|
||||
@property
|
||||
def group_velocities(self):
|
||||
"""Return group velocities."""
|
||||
if self._frequencies is None:
|
||||
self.run()
|
||||
return self._group_velocities
|
||||
|
||||
def get_group_velocities(self):
|
||||
"""Return group velocities."""
|
||||
warnings.warn(
|
||||
"Mesh.get_group_velocities() is deprecated. "
|
||||
"Use group_velocities attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.group_velocities
|
||||
|
||||
def write_hdf5(self):
|
||||
def write_hdf5(self, filename="mesh.hdf5"):
|
||||
"""Write results to hdf5 file."""
|
||||
import h5py
|
||||
with h5py.File('mesh.hdf5', 'w') as w:
|
||||
w.create_dataset('mesh', data=self._mesh)
|
||||
w.create_dataset('qpoint', data=self._qpoints)
|
||||
w.create_dataset('weight', data=self._weights)
|
||||
w.create_dataset('frequency', data=self._frequencies)
|
||||
if self._eigenvectors is not None:
|
||||
w.create_dataset('eigenvector', data=self._eigenvectors)
|
||||
if self._group_velocities is not None:
|
||||
w.create_dataset('group_velocity', data=self._group_velocities)
|
||||
|
||||
def write_yaml(self):
|
||||
natom = self._cell.get_number_of_atoms()
|
||||
rec_lattice = np.linalg.inv(self._cell.get_cell()) # column vectors
|
||||
distances = np.sqrt(
|
||||
np.sum(np.dot(self._qpoints, rec_lattice.T) ** 2, axis=1))
|
||||
with h5py.File(filename, "w") as w:
|
||||
w.create_dataset("mesh", data=self._mesh)
|
||||
w.create_dataset("qpoint", data=self._qpoints)
|
||||
w.create_dataset("weight", data=self._weights)
|
||||
w.create_dataset("frequency", data=self._frequencies)
|
||||
if self._eigenvectors is not None:
|
||||
w.create_dataset("eigenvector", data=self._eigenvectors)
|
||||
if self._group_velocities is not None:
|
||||
w.create_dataset("group_velocity", data=self._group_velocities)
|
||||
|
||||
def write_yaml(self, filename="mesh.yaml"):
|
||||
"""Write results to yaml file."""
|
||||
natom = len(self._cell)
|
||||
rec_lattice = np.linalg.inv(self._cell.cell) # column vectors
|
||||
distances = np.sqrt(np.sum(np.dot(self._qpoints, rec_lattice.T) ** 2, axis=1))
|
||||
|
||||
lines = []
|
||||
|
||||
lines.append("mesh: [ %5d, %5d, %5d ]" % tuple(self._mesh))
|
||||
lines.append("nqpoint: %-7d" % self._qpoints.shape[0])
|
||||
lines.append("reciprocal_lattice:")
|
||||
for vec, axis in zip(rec_lattice.T, ('a*', 'b*', 'c*')):
|
||||
lines.append("- [ %12.8f, %12.8f, %12.8f ] # %2s" %
|
||||
(tuple(vec) + (axis,)))
|
||||
for vec, axis in zip(rec_lattice.T, ("a*", "b*", "c*")):
|
||||
lines.append("- [ %12.8f, %12.8f, %12.8f ] # %2s" % (tuple(vec) + (axis,)))
|
||||
lines.append("natom: %-7d" % natom)
|
||||
lines.append(str(self._cell))
|
||||
lines.append("")
|
||||
lines.append("phonon:")
|
||||
|
||||
for i, (q, d) in enumerate(zip(self._qpoints, distances)):
|
||||
lines.append("- q-position: [ %12.7f, %12.7f, %12.7f ]"
|
||||
% tuple(q))
|
||||
lines.append("- q-position: [ %12.7f, %12.7f, %12.7f ]" % tuple(q))
|
||||
lines.append(" distance_from_gamma: %12.9f" % d)
|
||||
lines.append(" weight: %-5d" % self._weights[i])
|
||||
lines.append(" band:")
|
||||
|
@ -315,43 +401,58 @@ class Mesh(MeshBase):
|
|||
lines.append(" frequency: %15.10f" % freq)
|
||||
|
||||
if self._group_velocities is not None:
|
||||
lines.append(" group_velocity: "
|
||||
"[ %13.7f, %13.7f, %13.7f ]" %
|
||||
tuple(self._group_velocities[i, j]))
|
||||
lines.append(
|
||||
" group_velocity: "
|
||||
"[ %13.7f, %13.7f, %13.7f ]"
|
||||
% tuple(self._group_velocities[i, j])
|
||||
)
|
||||
|
||||
if self._with_eigenvectors:
|
||||
lines.append(" eigenvector:")
|
||||
for k in range(natom):
|
||||
lines.append(" - # atom %d" % (k+1))
|
||||
for l in (0, 1, 2):
|
||||
lines.append(" - # atom %d" % (k + 1))
|
||||
for ll in (0, 1, 2):
|
||||
lines.append(
|
||||
" - [ %17.14f, %17.14f ]"
|
||||
% (self._eigenvectors[i, k*3+l, j].real,
|
||||
self._eigenvectors[i, k*3+l, j].imag))
|
||||
% (
|
||||
self._eigenvectors[i, k * 3 + ll, j].real,
|
||||
self._eigenvectors[i, k * 3 + ll, j].imag,
|
||||
)
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
with open('mesh.yaml', 'w') as w:
|
||||
with open(filename, "w") as w:
|
||||
w.write("\n".join(lines))
|
||||
|
||||
def _set_phonon(self):
|
||||
num_band = self._cell.get_number_of_atoms() * 3
|
||||
num_band = len(self._cell) * 3
|
||||
num_qpoints = len(self._qpoints)
|
||||
|
||||
self._frequencies = np.zeros((num_qpoints, num_band), dtype='double')
|
||||
self._frequencies = np.zeros((num_qpoints, num_band), dtype="double")
|
||||
if self._with_eigenvectors or self._use_lapack_solver:
|
||||
dtype = "c%d" % (np.dtype('double').itemsize * 2)
|
||||
dtype = "c%d" % (np.dtype("double").itemsize * 2)
|
||||
self._eigenvectors = np.zeros(
|
||||
(num_qpoints, num_band, num_band,), dtype=dtype, order='C')
|
||||
(
|
||||
num_qpoints,
|
||||
num_band,
|
||||
num_band,
|
||||
),
|
||||
dtype=dtype,
|
||||
order="C",
|
||||
)
|
||||
|
||||
if self._use_lapack_solver:
|
||||
from phono3py.phonon.solver import get_phonons_at_qpoints
|
||||
get_phonons_at_qpoints(self._frequencies,
|
||||
|
||||
get_phonons_at_qpoints(
|
||||
self._frequencies,
|
||||
self._eigenvectors,
|
||||
self._dynamical_matrix,
|
||||
self._qpoints,
|
||||
self._factor,
|
||||
nac_q_direction=None,
|
||||
lapack_zheev_uplo='L')
|
||||
lapack_zheev_uplo="L",
|
||||
)
|
||||
else:
|
||||
for i, q in enumerate(self._qpoints):
|
||||
self._dynamical_matrix.run(q)
|
||||
|
@ -361,10 +462,14 @@ class Mesh(MeshBase):
|
|||
eigenvalues = eigvals.real
|
||||
else:
|
||||
eigenvalues = np.linalg.eigvalsh(dm).real
|
||||
self._frequencies[i] = np.array(np.sqrt(abs(eigenvalues)) *
|
||||
np.sign(eigenvalues),
|
||||
dtype='double',
|
||||
order='C') * self._factor
|
||||
self._frequencies[i] = (
|
||||
np.array(
|
||||
np.sqrt(abs(eigenvalues)) * np.sign(eigenvalues),
|
||||
dtype="double",
|
||||
order="C",
|
||||
)
|
||||
* self._factor
|
||||
)
|
||||
|
||||
def _set_group_velocities(self, group_velocity):
|
||||
group_velocity.run(self._qpoints)
|
||||
|
@ -372,7 +477,7 @@ class Mesh(MeshBase):
|
|||
|
||||
|
||||
class IterMesh(MeshBase):
|
||||
"""Generator class for phonons on mesh grid
|
||||
"""Generator class for phonons on mesh grid.
|
||||
|
||||
Not like as Mesh class, frequencies and eigenvectors are not
|
||||
stored, instead generated by iterator. This may be used for
|
||||
|
@ -383,7 +488,9 @@ class IterMesh(MeshBase):
|
|||
Attributes from MeshBase should be watched.
|
||||
|
||||
"""
|
||||
def __init__(self,
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dynamical_matrix,
|
||||
mesh,
|
||||
shift=None,
|
||||
|
@ -392,8 +499,10 @@ class IterMesh(MeshBase):
|
|||
with_eigenvectors=False,
|
||||
is_gamma_center=False,
|
||||
rotations=None, # Point group operations in real space
|
||||
factor=VaspToTHz):
|
||||
MeshBase.__init__(self,
|
||||
factor=VaspToTHz,
|
||||
):
|
||||
"""Init method."""
|
||||
super().__init__(
|
||||
dynamical_matrix,
|
||||
mesh,
|
||||
shift=shift,
|
||||
|
@ -402,15 +511,15 @@ class IterMesh(MeshBase):
|
|||
with_eigenvectors=with_eigenvectors,
|
||||
is_gamma_center=is_gamma_center,
|
||||
rotations=rotations,
|
||||
factor=factor)
|
||||
factor=factor,
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
"""Define iterator over q-points."""
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __next__(self):
|
||||
"""Calculate phonons at a q-point."""
|
||||
if self._q_count == len(self._qpoints):
|
||||
self._q_count = 0
|
||||
raise StopIteration
|
||||
|
@ -423,9 +532,13 @@ class IterMesh(MeshBase):
|
|||
eigenvalues = eigvals.real
|
||||
else:
|
||||
eigenvalues = np.linalg.eigvalsh(dm).real
|
||||
frequencies = np.array(np.sqrt(abs(eigenvalues)) *
|
||||
np.sign(eigenvalues),
|
||||
dtype='double',
|
||||
order='C') * self._factor
|
||||
frequencies = (
|
||||
np.array(
|
||||
np.sqrt(abs(eigenvalues)) * np.sign(eigenvalues),
|
||||
dtype="double",
|
||||
order="C",
|
||||
)
|
||||
* self._factor
|
||||
)
|
||||
self._q_count += 1
|
||||
return frequencies, eigenvectors
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Create atomic displacements."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,31 +33,34 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from phonopy.structure.cells import get_supercell
|
||||
from phonopy.interface.vasp import write_vasp
|
||||
from phonopy.units import VaspToTHz
|
||||
from phonopy.phonon.degeneracy import get_eigenvectors
|
||||
|
||||
from phonopy.harmonic.derivative_dynmat import DerivativeOfDynamicalMatrix
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
from phonopy.interface.vasp import write_vasp
|
||||
from phonopy.phonon.degeneracy import get_eigenvectors
|
||||
from phonopy.structure.cells import get_supercell
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
class Modulation(object):
|
||||
def __init__(self,
|
||||
dynamical_matrix,
|
||||
class Modulation:
|
||||
"""Class to create atomic displacements."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dynamical_matrix: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
dimension,
|
||||
phonon_modes,
|
||||
delta_q=None,
|
||||
derivative_order=None,
|
||||
nac_q_direction=None,
|
||||
factor=VaspToTHz):
|
||||
|
||||
"""Class describe atomic modulations
|
||||
|
||||
Atomic modulations corresponding to phonon modes are created.
|
||||
|
||||
"""
|
||||
factor=VaspToTHz,
|
||||
):
|
||||
"""Init method."""
|
||||
self._dm = dynamical_matrix
|
||||
self._primitive = dynamical_matrix.get_primitive()
|
||||
self._primitive = dynamical_matrix.primitive
|
||||
self._phonon_modes = phonon_modes
|
||||
self._dimension = np.array(dimension).ravel()
|
||||
self._delta_q = delta_q # 1st/2nd order perturbation direction
|
||||
|
@ -65,16 +69,22 @@ class Modulation(object):
|
|||
self._derivative_order = derivative_order
|
||||
|
||||
self._factor = factor
|
||||
self._u = []
|
||||
self._eigvecs = []
|
||||
self._eigvals = []
|
||||
self._supercell = None
|
||||
|
||||
dim = self._get_dimension_3x3()
|
||||
self._supercell = get_supercell(self._primitive, dim)
|
||||
complex_dtype = "c%d" % (np.dtype("double").itemsize * 2)
|
||||
self._u = np.zeros(
|
||||
(len(self._phonon_modes), len(self._supercell), 3),
|
||||
dtype=complex_dtype,
|
||||
order="C",
|
||||
)
|
||||
self._eigvals = np.zeros(len(self._phonon_modes), dtype="double")
|
||||
self._eigvecs = np.zeros(
|
||||
(len(self._phonon_modes), len(self._primitive) * 3), dtype=complex_dtype
|
||||
)
|
||||
|
||||
def run(self):
|
||||
for ph_mode in self._phonon_modes:
|
||||
"""Calculate modulations."""
|
||||
for i, ph_mode in enumerate(self._phonon_modes):
|
||||
q, band_index, amplitude, argument = ph_mode
|
||||
eigvals, eigvecs = get_eigenvectors(
|
||||
q,
|
||||
|
@ -82,29 +92,30 @@ class Modulation(object):
|
|||
self._ddm,
|
||||
perturbation=self._delta_q,
|
||||
derivative_order=self._derivative_order,
|
||||
nac_q_direction=self._nac_q_direction)
|
||||
u = self._get_displacements(eigvecs[:, band_index],
|
||||
q,
|
||||
amplitude,
|
||||
argument)
|
||||
self._u.append(u)
|
||||
self._eigvecs.append(eigvecs[:, band_index])
|
||||
self._eigvals.append(eigvals[band_index])
|
||||
nac_q_direction=self._nac_q_direction,
|
||||
)
|
||||
u = self._get_displacements(eigvecs[:, band_index], q, amplitude, argument)
|
||||
self._u[i] = u
|
||||
self._eigvecs[i] = eigvecs[:, band_index]
|
||||
self._eigvals[i] = eigvals[band_index]
|
||||
|
||||
def get_modulated_supercells(self):
|
||||
"""Return modulations."""
|
||||
modulations = []
|
||||
for u in self._u:
|
||||
modulations.append(self._get_cell_with_modulation(u))
|
||||
return modulations
|
||||
|
||||
def get_modulations_and_supercell(self):
|
||||
"""Return modulations and perfect supercell."""
|
||||
return self._u, self._supercell
|
||||
|
||||
def write(self, filename="MPOSCAR"):
|
||||
"""Write supercells with modulations to MPOSCARs."""
|
||||
deltas = []
|
||||
for i, u in enumerate(self._u):
|
||||
cell = self._get_cell_with_modulation(u)
|
||||
write_vasp((filename+"-%03d") % (i+1), cell, direct=True)
|
||||
write_vasp((filename + "-%03d") % (i + 1), cell, direct=True)
|
||||
deltas.append(u)
|
||||
|
||||
sum_of_deltas = np.sum(deltas, axis=0)
|
||||
|
@ -112,20 +123,21 @@ class Modulation(object):
|
|||
write_vasp(filename, cell, direct=True)
|
||||
no_modulations = np.zeros(sum_of_deltas.shape, dtype=complex)
|
||||
cell = self._get_cell_with_modulation(no_modulations)
|
||||
write_vasp(filename+"-orig", cell, direct=True)
|
||||
write_vasp(filename + "-orig", cell, direct=True)
|
||||
|
||||
def write_yaml(self):
|
||||
self._write_yaml()
|
||||
def write_yaml(self, filename="modulation.yaml"):
|
||||
"""Write modulations to file in yaml."""
|
||||
self._write_yaml(filename=filename)
|
||||
|
||||
def _get_cell_with_modulation(self, modulation):
|
||||
lattice = self._supercell.get_cell()
|
||||
positions = self._supercell.get_positions()
|
||||
lattice = self._supercell.cell
|
||||
positions = self._supercell.positions
|
||||
positions += modulation.real
|
||||
scaled_positions = np.dot(positions, np.linalg.inv(lattice))
|
||||
for p in scaled_positions:
|
||||
p -= np.floor(p)
|
||||
cell = self._supercell.copy()
|
||||
cell.set_scaled_positions(scaled_positions)
|
||||
cell.scaled_positions = scaled_positions
|
||||
|
||||
return cell
|
||||
|
||||
|
@ -137,26 +149,25 @@ class Modulation(object):
|
|||
else:
|
||||
dim = np.array(self._dimension)
|
||||
if dim.shape == (3, 3):
|
||||
dim = np.array(dim, dtype='intc')
|
||||
dim = np.array(dim, dtype="intc")
|
||||
else:
|
||||
print("Dimension is incorrectly set. Unit cell is used.")
|
||||
dim = np.eye(3, dtype='intc')
|
||||
dim = np.eye(3, dtype="intc")
|
||||
|
||||
return dim
|
||||
|
||||
def _get_displacements(self, eigvec, q, amplitude, argument):
|
||||
m = self._supercell.get_masses()
|
||||
s2u_map = self._supercell.get_supercell_to_unitcell_map()
|
||||
u2u_map = self._supercell.get_unitcell_to_unitcell_map()
|
||||
m = self._supercell.masses
|
||||
s2u_map = self._supercell.s2u_map
|
||||
u2u_map = self._supercell.u2u_map
|
||||
s2uu_map = [u2u_map[x] for x in s2u_map]
|
||||
spos = self._supercell.get_scaled_positions()
|
||||
dim = self._supercell.get_supercell_matrix()
|
||||
coefs = (np.exp(2j * np.pi * np.dot(np.dot(spos, dim.T), q))
|
||||
/ np.sqrt(m))
|
||||
spos = self._supercell.scaled_positions
|
||||
dim = self._supercell.supercell_matrix
|
||||
coefs = np.exp(2j * np.pi * np.dot(np.dot(spos, dim.T), q)) / np.sqrt(m)
|
||||
u = []
|
||||
for i, coef in enumerate(coefs):
|
||||
eig_index = s2uu_map[i] * 3
|
||||
u.append(eigvec[eig_index:eig_index + 3] * coef)
|
||||
u.append(eigvec[eig_index : eig_index + 3] * coef)
|
||||
|
||||
u = np.array(u) / np.sqrt(len(m))
|
||||
phase_factor = self._get_phase_factor(u, argument)
|
||||
|
@ -177,10 +188,10 @@ class Modulation(object):
|
|||
e = np.array(eigvals).real
|
||||
return np.sqrt(np.abs(e)) * np.sign(e) * self._factor
|
||||
|
||||
def _write_yaml(self):
|
||||
w = open('modulation.yaml', 'w')
|
||||
primitive = self._dm.get_primitive()
|
||||
num_atom = primitive.get_number_of_atoms()
|
||||
def _write_yaml(self, filename="modulation.yaml"):
|
||||
w = open(filename, "w")
|
||||
primitive = self._dm.primitive
|
||||
num_atom = len(primitive)
|
||||
|
||||
w.write("primitive_cell:\n")
|
||||
self._write_cell_yaml(primitive, w)
|
||||
|
@ -190,11 +201,10 @@ class Modulation(object):
|
|||
for v in dim:
|
||||
w.write(" - [ %d, %d, %d ]\n" % tuple(v))
|
||||
self._write_cell_yaml(self._supercell, w)
|
||||
inv_lattice = np.linalg.inv(self._supercell.get_cell().T)
|
||||
inv_lattice = np.linalg.inv(self._supercell.cell.T)
|
||||
|
||||
w.write("modulations:\n")
|
||||
for u, mode in zip(self._u,
|
||||
self._phonon_modes):
|
||||
for u, mode in zip(self._u, self._phonon_modes):
|
||||
q = mode[0]
|
||||
w.write("- q-position: [ %12.7f, %12.7f, %12.7f ]\n" % tuple(q))
|
||||
w.write(" band: %d\n" % (mode[1] + 1))
|
||||
|
@ -202,28 +212,34 @@ class Modulation(object):
|
|||
w.write(" phase: %f\n" % mode[3])
|
||||
w.write(" displacements:\n")
|
||||
for i, p in enumerate(u):
|
||||
w.write(" - [ %20.15f, %20.15f ] # %d x (%f)\n" %
|
||||
(p[0].real, p[0].imag, i + 1, abs(p[0])))
|
||||
w.write(" - [ %20.15f, %20.15f ] # %d y (%f)\n" %
|
||||
(p[1].real, p[1].imag, i + 1, abs(p[1])))
|
||||
w.write(" - [ %20.15f, %20.15f ] # %d z (%f)\n" %
|
||||
(p[2].real, p[2].imag, i + 1, abs(p[2])))
|
||||
w.write(
|
||||
" - [ %20.15f, %20.15f ] # %d x (%f)\n"
|
||||
% (p[0].real, p[0].imag, i + 1, abs(p[0]))
|
||||
)
|
||||
w.write(
|
||||
" - [ %20.15f, %20.15f ] # %d y (%f)\n"
|
||||
% (p[1].real, p[1].imag, i + 1, abs(p[1]))
|
||||
)
|
||||
w.write(
|
||||
" - [ %20.15f, %20.15f ] # %d z (%f)\n"
|
||||
% (p[2].real, p[2].imag, i + 1, abs(p[2]))
|
||||
)
|
||||
w.write(" fractional_displacements:\n")
|
||||
for i, p in enumerate(np.dot(u, inv_lattice.T)):
|
||||
w.write(" - [ %20.15f, %20.15f ] # %d a\n" %
|
||||
(p[0].real, p[0].imag, i + 1))
|
||||
w.write(" - [ %20.15f, %20.15f ] # %d b\n" %
|
||||
(p[1].real, p[1].imag, i + 1))
|
||||
w.write(" - [ %20.15f, %20.15f ] # %d c\n" %
|
||||
(p[2].real, p[2].imag, i + 1))
|
||||
w.write(
|
||||
" - [ %20.15f, %20.15f ] # %d a\n" % (p[0].real, p[0].imag, i + 1)
|
||||
)
|
||||
w.write(
|
||||
" - [ %20.15f, %20.15f ] # %d b\n" % (p[1].real, p[1].imag, i + 1)
|
||||
)
|
||||
w.write(
|
||||
" - [ %20.15f, %20.15f ] # %d c\n" % (p[2].real, p[2].imag, i + 1)
|
||||
)
|
||||
|
||||
w.write("phonon:\n")
|
||||
freqs = self._eigvals_to_frequencies(self._eigvals)
|
||||
for eigvec, freq, mode in zip(self._eigvecs,
|
||||
freqs,
|
||||
self._phonon_modes):
|
||||
w.write("- q-position: [ %12.7f, %12.7f, %12.7f ]\n"
|
||||
% tuple(mode[0]))
|
||||
for eigvec, freq, mode in zip(self._eigvecs, freqs, self._phonon_modes):
|
||||
w.write("- q-position: [ %12.7f, %12.7f, %12.7f ]\n" % tuple(mode[0]))
|
||||
w.write(" band: %d\n" % (mode[1] + 1))
|
||||
w.write(" amplitude: %f\n" % mode[2])
|
||||
w.write(" phase: %f\n" % mode[3])
|
||||
|
@ -233,8 +249,10 @@ class Modulation(object):
|
|||
w.write(" - # atom %d\n" % (j + 1))
|
||||
for k in (0, 1, 2):
|
||||
val = eigvec[j * 3 + k]
|
||||
w.write(" - [ %17.14f, %17.14f ] # %f\n" %
|
||||
(val.real, val.imag, np.angle(val, deg=True)))
|
||||
w.write(
|
||||
" - [ %17.14f, %17.14f ] # %f\n"
|
||||
% (val.real, val.imag, np.angle(val, deg=True))
|
||||
)
|
||||
|
||||
def _write_cell_yaml(self, cell, w):
|
||||
lattice = cell.get_cell()
|
||||
|
@ -246,9 +264,8 @@ class Modulation(object):
|
|||
w.write(" - { name: %2s, mass: %10.5f }\n" % (s, m))
|
||||
|
||||
w.write(" reciprocal_lattice:\n")
|
||||
for vec, axis in zip(np.linalg.inv(lattice), ('a*', 'b*', 'c*')):
|
||||
w.write(" - [ %12.8f, %12.8f, %12.8f ] # %2s\n" %
|
||||
(tuple(vec) + (axis,)))
|
||||
for vec, axis in zip(np.linalg.inv(lattice), ("a*", "b*", "c*")):
|
||||
w.write(" - [ %12.8f, %12.8f, %12.8f ] # %2s\n" % (tuple(vec) + (axis,)))
|
||||
w.write(" real_lattice:\n")
|
||||
w.write(" - [ %20.15f, %20.15f, %20.15f ]\n" % (tuple(lattice[0])))
|
||||
w.write(" - [ %20.15f, %20.15f, %20.15f ]\n" % (tuple(lattice[1])))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Calculate phonon state moments."""
|
||||
# Copyright (C) 2016 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -32,14 +33,24 @@
|
|||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class PhononMoment(object):
|
||||
def __init__(self,
|
||||
frequencies,
|
||||
weights,
|
||||
eigenvectors=None):
|
||||
class PhononMoment:
|
||||
"""Calculate phonon state moments.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
moment : float or ndarray
|
||||
Phonon state moment of specified order (float) or
|
||||
projected phonon state moment of specified order (ndarray).
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, frequencies, weights, eigenvectors=None):
|
||||
"""Init method."""
|
||||
self._frequencies = frequencies
|
||||
self._eigenvectors = eigenvectors
|
||||
self._weights = weights
|
||||
|
@ -50,15 +61,20 @@ class PhononMoment(object):
|
|||
|
||||
@property
|
||||
def moment(self):
|
||||
"""Return phonon state moment."""
|
||||
return self._moment
|
||||
|
||||
def get_moment(self):
|
||||
"""Return phonon state moment."""
|
||||
warnings.warn(
|
||||
"PhononMoment.get_moment() is deprecated. "
|
||||
"Use PhononMoment.moment attribute.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.moment
|
||||
|
||||
def set_frequency_range(self,
|
||||
freq_min=None,
|
||||
freq_max=None,
|
||||
tolerance=1e-8):
|
||||
def set_frequency_range(self, freq_min=None, freq_max=None, tolerance=1e-8):
|
||||
"""Set frequeny range where moment is computed."""
|
||||
if freq_min is None:
|
||||
self._fmin = tolerance
|
||||
else:
|
||||
|
@ -70,6 +86,7 @@ class PhononMoment(object):
|
|||
self._fmax = freq_max + tolerance
|
||||
|
||||
def run(self, order=1):
|
||||
"""Calculate phonon state moment of specified order."""
|
||||
if self._eigenvectors is None:
|
||||
self._get_moment(order)
|
||||
else:
|
||||
|
@ -80,20 +97,26 @@ class PhononMoment(object):
|
|||
norm0 = 0
|
||||
for i, w in enumerate(self._weights):
|
||||
for freq in self._frequencies[i]:
|
||||
if self._fmin < freq and freq < self._fmax :
|
||||
if self._fmin < freq and freq < self._fmax:
|
||||
norm0 += w
|
||||
moment += freq ** order * w
|
||||
self._moment = moment / norm0
|
||||
|
||||
def _get_projected_moment(self, order):
|
||||
moment = np.zeros(self._frequencies.shape[1], dtype='double')
|
||||
moment = np.zeros(self._frequencies.shape[1], dtype="double")
|
||||
norm0 = np.zeros_like(moment)
|
||||
for i, w in enumerate(self._weights):
|
||||
for freq, eigvec in zip(self._frequencies[i],
|
||||
self._eigenvectors[i].T):
|
||||
if self._fmin < freq and freq < self._fmax :
|
||||
for freq, eigvec in zip(self._frequencies[i], self._eigenvectors[i].T):
|
||||
if self._fmin < freq and freq < self._fmax:
|
||||
projection = np.abs(eigvec) ** 2
|
||||
norm0 += w * projection
|
||||
moment += freq ** order * w * projection
|
||||
self._moment = np.array([np.sum((moment / norm0)[i * 3:(i + 1) * 3])
|
||||
for i in range(len(moment) // 3)]) / 3
|
||||
self._moment = (
|
||||
np.array(
|
||||
[
|
||||
np.sum((moment / norm0)[i * 3 : (i + 1) * 3])
|
||||
for i in range(len(moment) // 3)
|
||||
]
|
||||
)
|
||||
/ 3
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Phonon calculation at specific q-points."""
|
||||
# Copyright (C) 2011 Atsushi Togo
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -33,12 +34,18 @@
|
|||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import warnings
|
||||
from typing import Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from phonopy.harmonic.dynamical_matrix import DynamicalMatrix, DynamicalMatrixNAC
|
||||
from phonopy.phonon.group_velocity import GroupVelocity
|
||||
from phonopy.structure.cells import Primitive
|
||||
from phonopy.units import VaspToTHz
|
||||
|
||||
|
||||
class QpointsPhonon(object):
|
||||
"""Calculate phonons at specified qpoints
|
||||
class QpointsPhonon:
|
||||
"""Calculate phonons at specified qpoints.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
@ -62,15 +69,18 @@ class QpointsPhonon(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
def __init__(
|
||||
self,
|
||||
qpoints,
|
||||
dynamical_matrix,
|
||||
dynamical_matrix: Union[DynamicalMatrix, DynamicalMatrixNAC],
|
||||
nac_q_direction=None,
|
||||
with_eigenvectors=False,
|
||||
group_velocity=None,
|
||||
with_dynamical_matrices=False,
|
||||
factor=VaspToTHz):
|
||||
primitive = dynamical_matrix.primitive
|
||||
factor=VaspToTHz,
|
||||
):
|
||||
"""Init method."""
|
||||
primitive: Primitive = dynamical_matrix.primitive
|
||||
self._natom = len(primitive)
|
||||
self._masses = primitive.masses
|
||||
self._symbols = primitive.symbols
|
||||
|
@ -81,7 +91,7 @@ class QpointsPhonon(object):
|
|||
self._dynamical_matrix = dynamical_matrix
|
||||
self._nac_q_direction = nac_q_direction
|
||||
self._with_eigenvectors = with_eigenvectors
|
||||
self._group_velocity = group_velocity
|
||||
self._gv_obj: Optional[GroupVelocity] = group_velocity
|
||||
self._with_dynamical_matrices = with_dynamical_matrices
|
||||
self._factor = factor
|
||||
|
||||
|
@ -95,58 +105,77 @@ class QpointsPhonon(object):
|
|||
|
||||
@property
|
||||
def frequencies(self):
|
||||
"""Return frequencies."""
|
||||
return self._frequencies
|
||||
|
||||
def get_frequencies(self):
|
||||
warnings.warn("Use attribute, frequencies.", DeprecationWarning)
|
||||
"""Return frequencies."""
|
||||
warnings.warn(
|
||||
"QpointsPhonon.get_frequencies() is deprecated. Use frequencies instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.frequencies
|
||||
|
||||
@property
|
||||
def eigenvalues(self):
|
||||
"""Return eigenvalues."""
|
||||
return self._eigenvalues
|
||||
|
||||
@property
|
||||
def eigenvectors(self):
|
||||
"""Return eigenvectors."""
|
||||
return self._eigenvectors
|
||||
|
||||
def get_eigenvectors(self):
|
||||
warnings.warn("Use attribute, eigenvectors.", DeprecationWarning)
|
||||
"""Return eigenvectors."""
|
||||
warnings.warn(
|
||||
"QpointsPhonon.get_eigenvectors() is deprecated. Use eigenvectors instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.eigenvectors
|
||||
|
||||
@property
|
||||
def group_velocities(self):
|
||||
"""Return group velocities."""
|
||||
return self._group_velocities
|
||||
|
||||
def get_group_velocities(self):
|
||||
warnings.warn("Use attribute, group_velocities.", DeprecationWarning)
|
||||
"""Return group velocities."""
|
||||
warnings.warn(
|
||||
"QpointsPhonon.get_group_velocities() is deprecated. "
|
||||
"Use group_velocities instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.group_velocities
|
||||
|
||||
@property
|
||||
def dynamical_matrices(self):
|
||||
"""Return DynamicalMatrix class instance."""
|
||||
return self._dynamical_matrices
|
||||
|
||||
def write_hdf5(self):
|
||||
def write_hdf5(self, filename="qpoints.hdf5"):
|
||||
"""Write results in hdf5."""
|
||||
import h5py
|
||||
with h5py.File('qpoints.hdf5', 'w') as w:
|
||||
w.create_dataset('qpoint', data=self._qpoints)
|
||||
w.create_dataset('frequency', data=self._frequencies)
|
||||
if self._with_eigenvectors:
|
||||
w.create_dataset('eigenvector', data=self._eigenvectors)
|
||||
if self._group_velocities is not None:
|
||||
w.create_dataset('group_velocity', data=self._group_velocities)
|
||||
if self._with_dynamical_matrices:
|
||||
w.create_dataset('dynamical_matrix',
|
||||
data=self._dynamical_matrices)
|
||||
|
||||
def write_yaml(self):
|
||||
w = open('qpoints.yaml', 'w')
|
||||
with h5py.File(filename, "w") as w:
|
||||
w.create_dataset("qpoint", data=self._qpoints)
|
||||
w.create_dataset("frequency", data=self._frequencies)
|
||||
if self._with_eigenvectors:
|
||||
w.create_dataset("eigenvector", data=self._eigenvectors)
|
||||
if self._group_velocities is not None:
|
||||
w.create_dataset("group_velocity", data=self._group_velocities)
|
||||
if self._with_dynamical_matrices:
|
||||
w.create_dataset("dynamical_matrix", data=self._dynamical_matrices)
|
||||
|
||||
def write_yaml(self, filename="qpoints.yaml"):
|
||||
"""Write results in yaml."""
|
||||
w = open(filename, "w")
|
||||
w.write("nqpoint: %-7d\n" % len(self._qpoints))
|
||||
w.write("natom: %-7d\n" % self._natom)
|
||||
rec_lattice = np.linalg.inv(self._lattice) # column vectors
|
||||
w.write("reciprocal_lattice:\n")
|
||||
for vec, axis in zip(rec_lattice.T, ('a*', 'b*', 'c*')):
|
||||
w.write("- [ %12.8f, %12.8f, %12.8f ] # %2s\n" %
|
||||
(tuple(vec) + (axis,)))
|
||||
for vec, axis in zip(rec_lattice.T, ("a*", "b*", "c*")):
|
||||
w.write("- [ %12.8f, %12.8f, %12.8f ] # %2s\n" % (tuple(vec) + (axis,)))
|
||||
w.write("phonon:\n")
|
||||
|
||||
for i, q in enumerate(self._qpoints):
|
||||
|
@ -168,24 +197,29 @@ class QpointsPhonon(object):
|
|||
w.write(" frequency: %15.10f\n" % freq)
|
||||
|
||||
if self._group_velocities is not None:
|
||||
w.write(" group_velocity: [ %13.7f, %13.7f, %13.7f ]\n"
|
||||
% tuple(self._group_velocities[i, j]))
|
||||
w.write(
|
||||
" group_velocity: [ %13.7f, %13.7f, %13.7f ]\n"
|
||||
% tuple(self._group_velocities[i, j])
|
||||
)
|
||||
|
||||
if self._with_eigenvectors:
|
||||
w.write(" eigenvector:\n")
|
||||
for k in range(self._natom):
|
||||
w.write(" - # atom %d\n" % (k + 1))
|
||||
for l in (0, 1, 2):
|
||||
w.write(" - [ %17.14f, %17.14f ]\n" %
|
||||
(self._eigenvectors[i][k * 3 + l, j].real,
|
||||
self._eigenvectors[i][k * 3 + l, j].imag))
|
||||
for ll in (0, 1, 2):
|
||||
w.write(
|
||||
" - [ %17.14f, %17.14f ]\n"
|
||||
% (
|
||||
self._eigenvectors[i][k * 3 + ll, j].real,
|
||||
self._eigenvectors[i][k * 3 + ll, j].imag,
|
||||
)
|
||||
)
|
||||
w.write("\n")
|
||||
|
||||
def _run(self):
|
||||
if self._group_velocity is not None:
|
||||
self._group_velocity.run(
|
||||
self._qpoints, perturbation=self._nac_q_direction)
|
||||
self._group_velocities = self._group_velocity.group_velocities
|
||||
if self._gv_obj is not None:
|
||||
self._gv_obj.run(self._qpoints, perturbation=self._nac_q_direction)
|
||||
self._group_velocities = self._gv_obj.group_velocities
|
||||
|
||||
if self._with_dynamical_matrices:
|
||||
dynamical_matrices = []
|
||||
|
@ -206,25 +240,27 @@ class QpointsPhonon(object):
|
|||
eigvals = np.linalg.eigvalsh(dm)
|
||||
eigvals = eigvals.real
|
||||
eigenvalues.append(eigvals)
|
||||
frequencies.append(np.sqrt(np.abs(eigvals)) *
|
||||
np.sign(eigvals) * self._factor)
|
||||
frequencies.append(
|
||||
np.sqrt(np.abs(eigvals)) * np.sign(eigvals) * self._factor
|
||||
)
|
||||
|
||||
self._eigenvalues = np.array(eigenvalues, dtype='double', order='C')
|
||||
self._frequencies = np.array(frequencies, dtype='double', order='C')
|
||||
dtype = "c%d" % (np.dtype('double').itemsize * 2)
|
||||
self._eigenvalues = np.array(eigenvalues, dtype="double", order="C")
|
||||
self._frequencies = np.array(frequencies, dtype="double", order="C")
|
||||
dtype = "c%d" % (np.dtype("double").itemsize * 2)
|
||||
if self._with_eigenvectors:
|
||||
self._eigenvectors = np.array(eigenvectors,
|
||||
dtype=dtype, order='C')
|
||||
self._eigenvectors = np.array(eigenvectors, dtype=dtype, order="C")
|
||||
if self._with_dynamical_matrices:
|
||||
self._dynamical_matrices = np.array(dynamical_matrices,
|
||||
dtype=dtype, order='C')
|
||||
self._dynamical_matrices = np.array(
|
||||
dynamical_matrices, dtype=dtype, order="C"
|
||||
)
|
||||
|
||||
def _get_dynamical_matrix(self, q):
|
||||
if (self._dynamical_matrix.is_nac() and
|
||||
self._nac_q_direction is not None and
|
||||
(np.abs(q) < 1e-5).all()):
|
||||
self._dynamical_matrix.run(
|
||||
q, q_direction=self._nac_q_direction)
|
||||
if (
|
||||
self._dynamical_matrix.is_nac()
|
||||
and self._nac_q_direction is not None
|
||||
and (np.abs(q) < 1e-5).all()
|
||||
):
|
||||
self._dynamical_matrix.run(q, q_direction=self._nac_q_direction)
|
||||
else:
|
||||
self._dynamical_matrix.run(q)
|
||||
return self._dynamical_matrix.dynamical_matrix
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue