Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Support QuantumESPRESSO v7.2 #32

Merged
merged 13 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 'v4.2.0'
rev: v4.1.0
hooks:
- id: double-quote-string-fixer
- id: end-of-file-fixer
Expand All @@ -14,19 +14,25 @@ repos:
- id: flynt

- repo: https://github.com/pycqa/isort
rev: '5.10.1'
rev: '5.12.0'
hooks:
- id: isort

- repo: https://github.com/pre-commit/mirrors-yapf
rev: 'v0.32.0'
rev: v0.32.0
hooks:
- id: yapf
name: yapf
types: [python]
args: ['-i']
additional_dependencies: ['toml']

- repo: https://github.com/PyCQA/pylint
rev: v2.12.2
hooks:
- id: pylint
language: system

- repo: https://github.com/PyCQA/pydocstyle
rev: '6.1.1'
hooks:
Expand All @@ -36,11 +42,3 @@ repos:
src/aiida_quantumespresso_hp/workflows/hubbard.py|
)$
additional_dependencies: ['toml']

- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
types: [python]
language: system
30 changes: 23 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ build-backend = 'flit_core.buildapi'
name = 'aiida-quantumespresso-hp'
dynamic = ['description', 'version']
authors = [
{name = 'Sebastiaan P. Huber', email = 'mail@sphuber.net'}
{name = 'Sebastiaan P. Huber', email = 'mail@sphuber.net'},
{name = 'Lorenzo Bastonero', email = 'lbastone@uni-bremen.de'}
]
readme = 'README.md'
license = {file = 'LICENSE.txt'}
Expand All @@ -19,27 +20,41 @@ classifiers = [
'Programming Language :: Python',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
]
keywords = ['aiida', 'workflows']
requires-python = '>=3.8'
dependencies = [
'aiida-core~=2.0',
'aiida-quantumespresso~=4.0',
'aiida-core~=2.2',
'aiida-quantumespresso~=4.3',
]

[project.urls]
Home = 'https://github.com/aiidateam/aiida-quantumespresso-hp'
Source = 'https://github.com/aiidateam/aiida-quantumespresso-hp'
Documentation = 'https://aiida-quantumespresso-hp.readthedocs.io'

[project.optional-dependencies]
docs = [
'sphinx~=4.1',
'sphinx-copybutton~=0.5.0',
'sphinx-book-theme~=0.3.2',
'sphinx-click~=4.0',
'sphinx-design~=0.0.13',
'sphinxcontrib-details-directive~=0.1.0',
'sphinx-autoapi'
]
pre-commit = [
'pre-commit~=2.17',
'pylint==2.13.7',
'pylint~=2.12.2',
'pylint-aiida~=0.1.1',
'toml'
]
tests = [
'pgtest~=1.3',
'pytest~=6.2',
'pytest-regressions~=1.0',
'pytest~=6.0',
'pytest-regressions~=2.3'
]

[project.scripts]
Expand All @@ -54,6 +69,7 @@ aiida-quantumespresso-hp = 'aiida_quantumespresso_hp.cli:cmd_root'
[project.entry-points.'aiida.workflows']
'quantumespresso.hp.main' = 'aiida_quantumespresso_hp.workflows.hp.main:HpWorkChain'
'quantumespresso.hp.parallelize_atoms' = 'aiida_quantumespresso_hp.workflows.hp.parallelize_atoms:HpParallelizeAtomsWorkChain'
'quantumespresso.hp.parallelize_qpoints' = 'aiida_quantumespresso_hp.workflows.hp.parallelize_qpoints:HpParallelizeQpointsWorkChain'
'quantumespresso.hp.base' = 'aiida_quantumespresso_hp.workflows.hp.base:HpBaseWorkChain'
'quantumespresso.hp.hubbard' = 'aiida_quantumespresso_hp.workflows.hubbard:SelfConsistentHubbardWorkChain'

Expand Down Expand Up @@ -93,7 +109,6 @@ max-line-length = 120

[tool.pylint.messages_control]
disable = [
'bad-continuation',
'duplicate-code',
'import-outside-toplevel',
'inconsistent-return-statements',
Expand All @@ -102,6 +117,7 @@ disable = [
'too-many-arguments',
'too-many-branches',
'too-many-locals',
'too-many-public-methods',
]

[tool.pytest.ini_options]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,91 @@
# -*- coding: utf-8 -*-
"""Calculation function to reorder the kinds of a structure with the Hubbard sites first."""
import re
"""Calculation function to relabel the kinds of a Hubbard structure."""
from __future__ import annotations

from copy import deepcopy

from aiida.engine import calcfunction
from aiida.orm import Dict
from aiida_quantumespresso.data.hubbard_structure import HubbardStructureData


@calcfunction
def structure_relabel_kinds(structure, hubbard):
def structure_relabel_kinds(
hubbard_structure: HubbardStructureData, hubbard: Dict, magnetization: Dict | None = None
) -> Dict:
"""Create a clone of the given structure but with new kinds, based on the new hubbard sites.

:param structure: ``StructureData`` node.
:param hubbard: the ``hubbard`` output node of a ``HpCalculation``.
:param hubbard_structure: ``HubbardStructureData`` instance.
:param hubbard: the ``hubbard`` output Dict node of a ``HpCalculation``.
:param magnetization: Dict instance containing the `starting_magnetization` QuantumESPRESSO inputs.
:returns: dict with keys:
* ``hubbard_structure``: relabelled ``HubbardStructureData``
* ``starting_magnetization``: updated magnetization as :class:`aiida.orm.Dict` (if provided in inputs)
"""
relabeled = structure.clone()
relabeled = hubbard_structure.clone()
relabeled.clear_kinds()
relabeled.clear_sites()

kind_suffix = -1
hubbard_u = {}
type_to_kind = {}
sites = structure.sites
sites = hubbard_structure.sites

# First do the Hubbard sites, upping the kind name suffix each time a new type is encountered. We do the suffix
# generation ourselves, because the indexing done by hp.x contains gaps in the sequence.
for index, site in enumerate(hubbard.base.attributes.get('sites')):
if magnetization:
old_magnetization = magnetization.get_dict()
new_magnetization = deepcopy(old_magnetization)
# Removing old Hubbard spin-polarized atom label.
for site in hubbard['sites']:
new_magnetization.pop(site['kind'], None)

symbol = re.search(r'^([A-za-z]+)[0-9]*$', site['kind']).group(1)
kind_set = hubbard_structure.get_site_kindnames()
symbol_set = [hubbard_structure.get_kind(kind_name).symbol for kind_name in kind_set]
symbol_counter = {key: 0 for key in hubbard_structure.get_symbols_set()}

# First do the Hubbard sites, popping the kind name suffix each time a new type is encountered. We do the suffix
# generation ourselves, because the indexing done by hp.x contains gaps in the sequence.
for index, site in enumerate(hubbard['sites']):
symbol = symbol_set[index]

try:
kind_name = type_to_kind[site['new_type']]
# We define a `spin_type`, since ``hp.x`` does not distinguish new types according to spin
spin_type = str(int(site['new_type']) * int(site['spin']))
kind_name = type_to_kind[spin_type]
except KeyError:
kind_suffix += 1
kind_name = f'{symbol}{kind_suffix}'
hubbard_u[kind_name] = float(site['value'])
type_to_kind[site['new_type']] = kind_name
kind_name = get_relabelled_symbol(symbol, symbol_counter[symbol])
type_to_kind[spin_type] = kind_name
symbol_counter[symbol] += 1

if magnetization:
# filling 'starting magnetization' with input value but new label;
# if does not present a starting value, pass.
if site['kind'] in old_magnetization:
new_magnetization[kind_name] = old_magnetization[site['kind']]

site = sites[index]
relabeled.append_atom(position=site.position, symbols=symbol, name=kind_name)

# Now add the non-Hubbard sites
for site in sites[len(relabeled.sites):]:
relabeled.append_atom(position=site.position, symbols=structure.get_kind(site.kind_name).symbols)
symbols = hubbard_structure.get_kind(site.kind_name).symbols
names = hubbard_structure.get_kind(site.kind_name).name
relabeled.append_atom(position=site.position, symbols=symbols, name=names)

return {'structure': relabeled, 'hubbard_u': Dict(hubbard_u)}
outputs = {'hubbard_structure': relabeled}
if magnetization:
outputs.update({'starting_magnetization': Dict(new_magnetization)})

return outputs


def get_relabelled_symbol(symbol: str, counter: int) -> str:
"""Return a relabelled symbol.

.. warning:: this function produces up to 36 different chemical symbols.

:param symbol: a chemical symbol, NOT a kind name
:param counter: a integer to assing the new label. Up to 9 an interger
is appended, while an *ascii uppercase letter* is used. Lower cases
are discarded to avoid possible misleading names
returns: a 3 digit length symbol (QuantumESPRESSO allows only up to 3)
"""
from string import ascii_uppercase, digits
suffix = (digits + ascii_uppercase)[counter]
return f'{symbol}{suffix}'
Original file line number Diff line number Diff line change
@@ -1,52 +1,26 @@
# -*- coding: utf-8 -*-
"""Calculation function to reorder the kinds of a structure with the Hubbard sites first."""
from copy import deepcopy

from aiida.engine import calcfunction
from aiida_quantumespresso.data.hubbard_structure import HubbardStructureData
from aiida_quantumespresso.utils.hubbard import HubbardUtils


@calcfunction
def structure_reorder_kinds(structure, hubbard_u):
"""Create a copy of the structure but with the kinds in the right order necessary for an hp.x calculation.
def structure_reorder_kinds(hubbard_structure: HubbardStructureData) -> HubbardStructureData:
"""Create a copy of the structure but with the kinds in the right order necessary for an ``hp.x`` calculation.

An HpCalculation which restarts from a completed PwCalculation, requires that the all Hubbard atoms appear first in
the atomic positions card of the PwCalculation input file. This order is based on the order of the kinds in the
structure. So a correct structure has all Hubbard kinds in the begining of kinds list.
An ``HpCalculation`` which restarts from a completed ``PwCalculation``,
requires that the all Hubbard atoms appear first in
the atomic positions card of the PwCalculation input file.
This order is based on the order of the kinds in the
structure. So a correct structure has all Hubbard kinds
in the begining of kinds list.

:param structure: StructureData node
:param hubbard_u: a Dict node with the Hubbard U kinds and their values
:param hubbard_structure: reordered :class:`aiida_quantumespresso.data.hubbard.HubbardStructureData` node
"""
reordered = deepcopy(structure)
reordered.clear_kinds()

sites = structure.sites
hubbard_kinds = list(hubbard_u.get_dict().keys())
hubbard_kinds.sort(reverse=True)

ordered_sites = []

while hubbard_kinds:

hubbard_kind = hubbard_kinds.pop()

hubbard_sites = []
remaining_sites = []

hubbard_sites = [s for s in sites if s.kind_name == hubbard_kind]
remaining_sites = [s for s in sites if not s.kind_name == hubbard_kind]

ordered_sites.extend(hubbard_sites)
sites = remaining_sites

# Extend the current site list with the remaining non-hubbard sites
ordered_sites.extend(sites)

for site in ordered_sites:

if site.kind_name not in reordered.get_kind_names():
kind = structure.get_kind(site.kind_name)
reordered.append_kind(kind)
reordered = hubbard_structure.clone()

reordered.append_site(site)
hubbard_utils = HubbardUtils(reordered)
hubbard_utils.reorder_atoms()

return reordered
return hubbard_utils.hubbard_structure
Loading