Skip to content

Commit

Permalink
Move calcfunctions to aiida_quantumespresso.calculations.functions
Browse files Browse the repository at this point in the history
Since they are calculation functions they do not belong in the workflow
module. To not break backwards-compatibility the original modules import
the functions from their new location and emit a `Warning`. Note that we
explicitly do not use a `DeprecationWarning` since those are
automatically filtered in Python 3.
  • Loading branch information
sphuber committed May 7, 2020
1 parent 1c9e55d commit fd725da
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 77 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
"""Calculation function to compute a k-point mesh for a structure with a guaranteed minimum k-point distance."""
from aiida.engine import calcfunction


@calcfunction
def create_kpoints_from_distance(structure, distance, force_parity):
"""Generate a uniformly spaced kpoint mesh for a given structure.
The spacing between kpoints in reciprocal space is guaranteed to be at least the defined distance.
:param structure: the StructureData to which the mesh should apply
:param distance: a Float with the desired distance between kpoints in reciprocal space
:param force_parity: a Bool to specify whether the generated mesh should maintain parity
:returns: a KpointsData with the generated mesh
"""
from numpy import linalg
from aiida.orm import KpointsData

epsilon = 1E-5

kpoints = KpointsData()
kpoints.set_cell_from_structure(structure)
kpoints.set_kpoints_mesh_from_density(distance.value, force_parity=force_parity.value)

lengths_vector = [linalg.norm(vector) for vector in structure.cell]
lengths_kpoint = kpoints.get_kpoints_mesh()[0]

is_symmetric_cell = all(abs(length - lengths_vector[0]) < epsilon for length in lengths_vector)
is_symmetric_mesh = all(length == lengths_kpoint[0] for length in lengths_kpoint)

# If the vectors of the cell all have the same length, the kpoint mesh should be isotropic as well
if is_symmetric_cell and not is_symmetric_mesh:
nkpoints = max(lengths_kpoint)
kpoints.set_kpoints_mesh([nkpoints, nkpoints, nkpoints])

return kpoints
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
"""Calcfunction to primitivize a structure and return high symmetry k-point path through its Brillouin zone."""
from aiida.engine import calcfunction
from aiida.orm import Data


@calcfunction
def seekpath_structure_analysis(structure, **kwargs):
"""Primitivize the structure with SeeKpath and generate the high symmetry k-point path through its Brillouin zone.
This calcfunction will take a structure and pass it through SeeKpath to get the normalized primitive cell and the
path of high symmetry k-points through its Brillouin zone. Note that the returned primitive cell may differ from the
original structure in which case the k-points are only congruent with the primitive cell.
The keyword arguments can be used to specify various Seekpath parameters, such as:
with_time_reversal: True
reference_distance: 0.025
recipe: 'hpkot'
threshold: 1e-07
symprec: 1e-05
angle_tolerance: -1.0
Note that exact parameters that are available and their defaults will depend on your Seekpath version.
"""
from aiida.tools import get_explicit_kpoints_path

# All keyword arugments should be `Data` node instances of base type and so should have the `.value` attribute
unwrapped_kwargs = {key: node.value for key, node in kwargs.items() if isinstance(node, Data)}

return get_explicit_kpoints_path(structure, **unwrapped_kwargs)
Original file line number Diff line number Diff line change
@@ -1,37 +1,12 @@
# -*- coding: utf-8 -*-
"""Calculation function to compute a k-point mesh for a structure with a guaranteed minimum k-point distance."""
from aiida.engine import calcfunction


@calcfunction
def create_kpoints_from_distance(structure, distance, force_parity):
"""Generate a uniformly spaced kpoint mesh for a given structure.
The spacing between kpoints in reciprocal space is guaranteed to be at least the defined distance.
:param structure: the StructureData to which the mesh should apply
:param distance: a Float with the desired distance between kpoints in reciprocal space
:param force_parity: a Bool to specify whether the generated mesh should maintain parity
:returns: a KpointsData with the generated mesh
"""
from numpy import linalg
from aiida.orm import KpointsData

epsilon = 1E-5

kpoints = KpointsData()
kpoints.set_cell_from_structure(structure)
kpoints.set_kpoints_mesh_from_density(distance.value, force_parity=force_parity.value)

lengths_vector = [linalg.norm(vector) for vector in structure.cell]
lengths_kpoint = kpoints.get_kpoints_mesh()[0]

is_symmetric_cell = all(abs(length - lengths_vector[0]) < epsilon for length in lengths_vector)
is_symmetric_mesh = all(length == lengths_kpoint[0] for length in lengths_kpoint)

# If the vectors of the cell all have the same length, the kpoint mesh should be isotropic as well
if is_symmetric_cell and not is_symmetric_mesh:
nkpoints = max(lengths_kpoint)
kpoints.set_kpoints_mesh([nkpoints, nkpoints, nkpoints])

return kpoints
# pylint: disable=unused-import
import warnings
from aiida_quantumespresso.calculations.functions.create_kpoints_from_distance import create_kpoints_from_distance

warnings.warn(
'This module is deprecated and will be removed soon.\nPlease use instead the new module:\n'
'from aiida_quantumespresso.calculations.functions.create_kpoints_from_distance import create_kpoints_from_distance'
"\nOr use the entry point with the factory: CalculationFactory('quantumespresso.create_kpoints_from_distance')",
FutureWarning
)
Original file line number Diff line number Diff line change
@@ -1,31 +1,12 @@
# -*- coding: utf-8 -*-
"""Calcfunction to primitivize a structure and return high symmetry k-point path through its Brillouin zone."""
from aiida.engine import calcfunction
from aiida.orm import Data


@calcfunction
def seekpath_structure_analysis(structure, **kwargs):
"""Primitivize the structure with SeeKpath and generate the high symmetry k-point path through its Brillouin zone.
This calcfunction will take a structure and pass it through SeeKpath to get the normalized primitive cell and the
path of high symmetry k-points through its Brillouin zone. Note that the returned primitive cell may differ from the
original structure in which case the k-points are only congruent with the primitive cell.
The keyword arguments can be used to specify various Seekpath parameters, such as:
with_time_reversal: True
reference_distance: 0.025
recipe: 'hpkot'
threshold: 1e-07
symprec: 1e-05
angle_tolerance: -1.0
Note that exact parameters that are available and their defaults will depend on your Seekpath version.
"""
from aiida.tools import get_explicit_kpoints_path

# All keyword arugments should be `Data` node instances of base type and so should have the `.value` attribute
unwrapped_kwargs = {key: node.value for key, node in kwargs.items() if isinstance(node, Data)}

return get_explicit_kpoints_path(structure, **unwrapped_kwargs)
# pylint: disable=unused-import
import warnings
from aiida_quantumespresso.calculations.functions.seekpath_structure_analysis import seekpath_structure_analysis

warnings.warn(
'This module is deprecated and will be removed soon.\nPlease use instead the new module:\n'
'from aiida_quantumespresso.calculations.functions.seekpath_structure_analysis import seekpath_structure_analysis'
"\nOr use the entry point with the factory: CalculationFactory('quantumespresso.seekpath_structure_analysis')",
FutureWarning
)
2 changes: 1 addition & 1 deletion aiida_quantumespresso/workflows/pw/bands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from aiida.plugins import WorkflowFactory
from aiida.engine import WorkChain, ToContext, if_

from aiida_quantumespresso.calculations.functions.seekpath_structure_analysis import seekpath_structure_analysis
from aiida_quantumespresso.utils.mapping import prepare_process_inputs
from aiida_quantumespresso.workflows.functions.seekpath_structure_analysis import seekpath_structure_analysis

PwBaseWorkChain = WorkflowFactory('quantumespresso.pw.base')
PwRelaxWorkChain = WorkflowFactory('quantumespresso.pw.relax')
Expand Down
2 changes: 1 addition & 1 deletion aiida_quantumespresso/workflows/pw/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from aiida.engine import ToContext, if_, while_
from aiida.plugins import CalculationFactory

from aiida_quantumespresso.calculations.functions.create_kpoints_from_distance import create_kpoints_from_distance
from aiida_quantumespresso.common.workchain.utils import register_error_handler, ErrorHandlerReport
from aiida_quantumespresso.common.workchain.base.restart import BaseRestartWorkChain
from aiida_quantumespresso.utils.defaults.calculation import pw as qe_defaults
from aiida_quantumespresso.utils.mapping import update_mapping, prepare_process_inputs
from aiida_quantumespresso.utils.pseudopotential import validate_and_prepare_pseudos_inputs
from aiida_quantumespresso.utils.resources import get_default_options, get_pw_parallelization_parameters
from aiida_quantumespresso.utils.resources import cmdline_remove_npools, create_scheduler_resources
from aiida_quantumespresso.workflows.functions.create_kpoints_from_distance import create_kpoints_from_distance

PwCalculation = CalculationFactory('quantumespresso.pw')

Expand Down
8 changes: 5 additions & 3 deletions setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@
"entry_points": {
"aiida.calculations": [
"quantumespresso.cp = aiida_quantumespresso.calculations.cp:CpCalculation",
"quantumespresso.create_kpoints_from_distance = aiida_quantumespresso.calculations.functions.create_kpoints_from_distance:create_kpoints_from_distance",
"quantumespresso.dos = aiida_quantumespresso.calculations.dos:DosCalculation",
"quantumespresso.epw = aiida_quantumespresso.calculations.epw:EpwCalculation",
"quantumespresso.matdyn = aiida_quantumespresso.calculations.matdyn:MatdynCalculation",
"quantumespresso.namelists = aiida_quantumespresso.calculations.namelists:NamelistsCalculation",
"quantumespresso.neb = aiida_quantumespresso.calculations.neb:NebCalculation",
"quantumespresso.ph = aiida_quantumespresso.calculations.ph:PhCalculation",
"quantumespresso.pp = aiida_quantumespresso.calculations.pp:PpCalculation",
"quantumespresso.projwfc = aiida_quantumespresso.calculations.projwfc:ProjwfcCalculation",
"quantumespresso.pw = aiida_quantumespresso.calculations.pw:PwCalculation",
"quantumespresso.epw = aiida_quantumespresso.calculations.epw:EpwCalculation",
"quantumespresso.pw2gw = aiida_quantumespresso.calculations.pw2gw:Pw2gwCalculation",
"quantumespresso.projwfc = aiida_quantumespresso.calculations.projwfc:ProjwfcCalculation",
"quantumespresso.pw2wannier90 = aiida_quantumespresso.calculations.pw2wannier90:Pw2wannier90Calculation",
"quantumespresso.pwimmigrant = aiida_quantumespresso.calculations.pwimmigrant:PwimmigrantCalculation",
"quantumespresso.q2r = aiida_quantumespresso.calculations.q2r:Q2rCalculation",
"quantumespresso.pwimmigrant = aiida_quantumespresso.calculations.pwimmigrant:PwimmigrantCalculation"
"quantumespresso.seekpath_structure_analysis = aiida_quantumespresso.calculations.functions.seekpath_structure_analysis:seekpath_structure_analysis"
],
"aiida.data": [
"quantumespresso.force_constants = aiida_quantumespresso.data.force_constants:ForceConstantsData"
Expand Down
17 changes: 9 additions & 8 deletions tests/calculations/test_autoinvalidate_cache.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
# -*- coding: utf-8 -*-
"""Test the automatic 'invalidates_cache' attribute for exit codes."""
from distutils.version import StrictVersion # pylint: disable=import-error,no-name-in-module

import inspect
import pytest

import aiida
from aiida.engine import CalcJob
from aiida.plugins import CalculationFactory
from aiida.plugins.entry_point import get_entry_point_names

if StrictVersion(aiida.__version__) < StrictVersion('1.1.0'):
pytest.skip("The 'invalidates_cache' feature is only available on AiiDA 1.1+", allow_module_level=True)

QE_CALC_ENTRY_POINT_NAMES = [
ep_name for ep_name in get_entry_point_names(group='aiida.calculations') if ep_name.startswith('quantumespresso')
]
Expand All @@ -28,9 +24,14 @@ def test_exit_code_invalidates_cache(entry_point_name):
Test that the 'invalidates_cache' attribute of exit codes is automatically
set according to the status integer.
"""
calc_class = CalculationFactory(entry_point_name)
entry_point = CalculationFactory(entry_point_name)

if not inspect.isclass(entry_point) or not issubclass(entry_point, CalcJob):
return

overrides = EXPLICIT_OVERRIDES.get(entry_point_name, [])
for exit_code in calc_class.exit_codes.values():

for exit_code in entry_point.exit_codes.values():
if exit_code.status not in overrides:
if exit_code.status < 400:
assert exit_code.invalidates_cache
Expand Down

0 comments on commit fd725da

Please sign in to comment.