diff --git a/aiida_quantumespresso/calculations/functions/__init__.py b/aiida_quantumespresso/calculations/functions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/aiida_quantumespresso/calculations/functions/create_kpoints_from_distance.py b/aiida_quantumespresso/calculations/functions/create_kpoints_from_distance.py new file mode 100644 index 000000000..67b834581 --- /dev/null +++ b/aiida_quantumespresso/calculations/functions/create_kpoints_from_distance.py @@ -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 diff --git a/aiida_quantumespresso/calculations/functions/seekpath_structure_analysis.py b/aiida_quantumespresso/calculations/functions/seekpath_structure_analysis.py new file mode 100644 index 000000000..4237342be --- /dev/null +++ b/aiida_quantumespresso/calculations/functions/seekpath_structure_analysis.py @@ -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) diff --git a/aiida_quantumespresso/workflows/functions/create_kpoints_from_distance.py b/aiida_quantumespresso/workflows/functions/create_kpoints_from_distance.py index 67b834581..c6ad7837e 100644 --- a/aiida_quantumespresso/workflows/functions/create_kpoints_from_distance.py +++ b/aiida_quantumespresso/workflows/functions/create_kpoints_from_distance.py @@ -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 +) diff --git a/aiida_quantumespresso/workflows/functions/seekpath_structure_analysis.py b/aiida_quantumespresso/workflows/functions/seekpath_structure_analysis.py index 4237342be..136a39af6 100644 --- a/aiida_quantumespresso/workflows/functions/seekpath_structure_analysis.py +++ b/aiida_quantumespresso/workflows/functions/seekpath_structure_analysis.py @@ -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 +) diff --git a/aiida_quantumespresso/workflows/pw/bands.py b/aiida_quantumespresso/workflows/pw/bands.py index 988f20eff..6e0463576 100644 --- a/aiida_quantumespresso/workflows/pw/bands.py +++ b/aiida_quantumespresso/workflows/pw/bands.py @@ -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') diff --git a/aiida_quantumespresso/workflows/pw/base.py b/aiida_quantumespresso/workflows/pw/base.py index 1202054c1..be5b97f0b 100644 --- a/aiida_quantumespresso/workflows/pw/base.py +++ b/aiida_quantumespresso/workflows/pw/base.py @@ -5,6 +5,7 @@ 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 @@ -12,7 +13,6 @@ 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') diff --git a/setup.json b/setup.json index eec0ad389..7fad8aacc 100644 --- a/setup.json +++ b/setup.json @@ -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" diff --git a/tests/calculations/test_autoinvalidate_cache.py b/tests/calculations/test_autoinvalidate_cache.py index a65a3c5a2..86dbeb830 100644 --- a/tests/calculations/test_autoinvalidate_cache.py +++ b/tests/calculations/test_autoinvalidate_cache.py @@ -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') ] @@ -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