diff --git a/aiida_common_workflows/workflows/bands/fleur/__init__.py b/aiida_common_workflows/workflows/bands/fleur/__init__.py new file mode 100644 index 00000000..c725bf86 --- /dev/null +++ b/aiida_common_workflows/workflows/bands/fleur/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# pylint: disable=undefined-variable +"""Module with the implementations of the common bands workchain for Siesta.""" +from .generator import * +from .workchain import * + +__all__ = (generator.__all__ + workchain.__all__) diff --git a/aiida_common_workflows/workflows/bands/fleur/generator.py b/aiida_common_workflows/workflows/bands/fleur/generator.py new file mode 100644 index 00000000..a477ea78 --- /dev/null +++ b/aiida_common_workflows/workflows/bands/fleur/generator.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +"""Implementation of `aiida_common_workflows.common.bands.generator.CommonBandsInputGenerator` for Fleur.""" + +from aiida import engine, orm + +from aiida_common_workflows.generators import CodeType + +from ..generator import CommonBandsInputGenerator + +__all__ = ('FleurCommonBandsInputGenerator',) + + +class FleurCommonBandsInputGenerator(CommonBandsInputGenerator): + """Generator of inputs for the FleurCommonBandsWorkChain""" + + @classmethod + def define(cls, spec): + """Define the specification of the input generator. + + The ports defined on the specification are the inputs that will be accepted by the ``get_builder`` method. + """ + super().define(spec) + spec.inputs['engines']['bands']['code'].valid_type = CodeType('fleur.fleur') + + def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: + """Construct a process builder based on the provided keyword arguments. + + The keyword arguments will have been validated against the input generator specification. + """ + # pylint: disable=too-many-branches,too-many-statements,too-many-locals + engines = kwargs.get('engines', None) + parent_folder = kwargs['parent_folder'] + bands_kpoints = kwargs['bands_kpoints'] + + # From the parent folder, we retrieve the calculation that created it. + parent_calc = parent_folder.creator + if parent_calc.process_type != 'aiida.calculations:fleur.fleur': + raise ValueError('The `parent_folder` has not been created by a FleurCalculation') + builder_parent = parent_folder.creator.get_builder_restart() + + #Transfer inputs from the parent calculation + builder = self.process_class.get_builder() + builder.options = orm.Dict(dict=builder_parent.metadata.options) + builder.fleur = builder_parent.code + + wf_parameters = {'kpath': 'skip'} + + builder.wf_parameters = orm.Dict(dict=wf_parameters) + builder.kpoints = bands_kpoints + builder.remote = parent_folder + + #Add inputs from engines if given + if engines: + try: + band_engines = engines['bands'] + except KeyError: + raise ValueError('The `engines` dictionary must contain `bands` as a top-level key') + if 'code' in band_engines: + builder.fleur = band_engines['code'] + if 'options' in band_engines: + builder.options = orm.Dict(dict=band_engines['options']) + + return builder diff --git a/aiida_common_workflows/workflows/bands/fleur/workchain.py b/aiida_common_workflows/workflows/bands/fleur/workchain.py new file mode 100644 index 00000000..d4ec938c --- /dev/null +++ b/aiida_common_workflows/workflows/bands/fleur/workchain.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +"""Implementation of `aiida_common_workflows.common.relax.workchain.CommonRelaxWorkChain` for Fleur.""" +from aiida.orm import Float +from aiida.plugins import WorkflowFactory + +from ..workchain import CommonBandsWorkChain +from .generator import FleurCommonBandsInputGenerator + +__all__ = ('FleurCommonBandsWorkChain',) + + +class FleurCommonBandsWorkChain(CommonBandsWorkChain): + """Implementation of `aiida_common_workflows.common.bands.workchain.CommonBandsWorkChain` for Fleur.""" + + _process_class = WorkflowFactory('fleur.banddos') + _generator_class = FleurCommonBandsInputGenerator + + def convert_outputs(self): + """Convert the outputs of the sub workchain to the common output specification.""" + self.report('Bands calculation concluded sucessfully, converting outputs') + if 'output_banddos_wc_bands' not in self.ctx.workchain.outputs: + self.report('FleurBandDOSWorkChain concluded without returning bands!') + return self.exit_codes.ERROR_SUB_PROCESS_FAILED + + #The bands output of fleur is shifted to have the fermi energy at zero + self.out('fermi_energy', Float(0.0)) + + self.out('bands', self.ctx.workchain.outputs['output_banddos_wc_bands']) diff --git a/setup.json b/setup.json index 2f5fa85a..e0bfe878 100644 --- a/setup.json +++ b/setup.json @@ -74,6 +74,7 @@ "common_workflows.relax.quantum_espresso = aiida_common_workflows.workflows.relax.quantum_espresso.workchain:QuantumEspressoCommonRelaxWorkChain", "common_workflows.relax.siesta = aiida_common_workflows.workflows.relax.siesta.workchain:SiestaCommonRelaxWorkChain", "common_workflows.relax.vasp = aiida_common_workflows.workflows.relax.vasp.workchain:VaspCommonRelaxWorkChain", + "common_workflows.bands.fleur = aiida_common_workflows.workflows.bands.fleur.workchain:FleurCommonBandsWorkChain", "common_workflows.bands.siesta = aiida_common_workflows.workflows.bands.siesta.workchain:SiestaCommonBandsWorkChain" ] },