From 1a90ef617719c1ab53152eeacd01635c86aa6cef Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Thu, 4 Nov 2021 20:05:26 +0000 Subject: [PATCH 01/17] Added the CASTEP oxide-validation protocol --- .../workflows/relax/castep/protocol.yml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index b08a6b47..bdc85511 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -83,3 +83,41 @@ fast: geom_force_tol: 0.05 geom_energy_tol: 2.0e-5 geom_stress_tol: 0.1 + + +oxides-validation: + name: 'oxides-validation' + description: 'Protocol for the oxides validation study.' + relax: + relax_options: + max_meta_iterations: 5 + + base: + pseudos_family: 'C19' + max_iterations: 5 + kpoints_spacing: 0.04 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... + ensure_gamma_centering: True # Ensure that the kpoint grid is Gamma-centered + calc: + parameters: + task: geometryoptimisation + xc_functional: pbe + symmetry_generate: true + snap_to_symmetry: true + calculate_stress: true + basis_precision: precise + write_otfg: false + opt_strategy: speed + write_bib: false + max_scf_cycles: 100 + elec_energy_tol: 1e-8 + geom_force_tol: 0.03 + geom_energy_tol: 1.0e-5 + geom_stress_tol: 0.05 + grid_scale: 2 + fine_grid_scale : 3 + # Revised smearing scheme for the oxide validation project + smearing_width: 0.06122561905370023 # Equivalent to 0.0045 Ry + smearing_scheme: FERMIDIRAC + perc_extra_bands: 0.3 # More bands by default + settings: + ADDITIONAL_RETRIEVE_TEMPORARY_LIST: ["aiida.castep_bin"] From a47cfc870fc403cd33d243bad91d79d37ce30954 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Fri, 5 Nov 2021 07:55:08 +0000 Subject: [PATCH 02/17] Update for CASTEP --- aiida_common_workflows/workflows/eos.py | 16 +++++++++++----- .../workflows/relax/castep/generator.py | 3 ++- .../workflows/relax/castep/protocol.yml | 4 ++-- .../workflows/relax/generator.py | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/aiida_common_workflows/workflows/eos.py b/aiida_common_workflows/workflows/eos.py index ae2c511b..d25a993e 100644 --- a/aiida_common_workflows/workflows/eos.py +++ b/aiida_common_workflows/workflows/eos.py @@ -140,11 +140,17 @@ def get_sub_workchain_builder(self, scale_factor, reference_workchain=None): structure = scale_structure(self.inputs.structure, scale_factor) process_class = WorkflowFactory(self.inputs.sub_process_class) - builder = process_class.get_input_generator().get_builder( - structure=structure, - reference_workchain=reference_workchain, - **self.inputs.generator_inputs - ) + if reference_workchain is None: + builder = process_class.get_input_generator().get_builder( + structure=structure, + **self.inputs.generator_inputs + ) + else: + builder = process_class.get_input_generator().get_builder( + structure=structure, + reference_workchain=reference_workchain, + **self.inputs.generator_inputs + ) builder._update(**self.inputs.get('sub_process', {})) # pylint: disable=protected-access return builder, structure diff --git a/aiida_common_workflows/workflows/relax/castep/generator.py b/aiida_common_workflows/workflows/relax/castep/generator.py index 3633c63e..868a15a4 100644 --- a/aiida_common_workflows/workflows/relax/castep/generator.py +++ b/aiida_common_workflows/workflows/relax/castep/generator.py @@ -348,7 +348,8 @@ def generate_inputs_base( 'kpoints_spacing': orm.Float(merged['kpoints_spacing'] / 2 / pi), 'max_iterations': orm.Int(merged['max_iterations']), 'pseudos_family': orm.Str(otfg_family.label), - 'calc': calc_dictionary + 'calc': calc_dictionary, + 'ensure_gamma_centering': orm.Bool(merged.get('ensure_gamma_centering', False)), } return dictionary diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index bdc85511..4d717ae9 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -85,7 +85,7 @@ fast: geom_stress_tol: 0.1 -oxides-validation: +oxides_validation: name: 'oxides-validation' description: 'Protocol for the oxides validation study.' relax: @@ -118,6 +118,6 @@ oxides-validation: # Revised smearing scheme for the oxide validation project smearing_width: 0.06122561905370023 # Equivalent to 0.0045 Ry smearing_scheme: FERMIDIRAC - perc_extra_bands: 0.3 # More bands by default + perc_extra_bands: 30 # More bands by default settings: ADDITIONAL_RETRIEVE_TEMPORARY_LIST: ["aiida.castep_bin"] diff --git a/aiida_common_workflows/workflows/relax/generator.py b/aiida_common_workflows/workflows/relax/generator.py index 2ad8a0d3..c419edfa 100644 --- a/aiida_common_workflows/workflows/relax/generator.py +++ b/aiida_common_workflows/workflows/relax/generator.py @@ -32,7 +32,7 @@ def define(cls, spec): ) spec.input( 'protocol', - valid_type=ChoiceType(('fast', 'moderate', 'precise')), + valid_type=ChoiceType(('fast', 'moderate', 'precise', 'oxides_validation')), default='moderate', help='The protocol to use for the automated input generation. This value indicates the level of precision ' 'of the results and computational cost that the input parameters will be selected for.', From 34ff5336ed4c3850f967ba448e432c30031d6659 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Wed, 10 Nov 2021 12:46:13 +0000 Subject: [PATCH 03/17] Do not write checkpoint - they are huge ! --- aiida_common_workflows/workflows/relax/castep/protocol.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index 4d717ae9..7eb28668 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -108,6 +108,7 @@ oxides_validation: write_otfg: false opt_strategy: speed write_bib: false + write_checkpoint: minimal max_scf_cycles: 100 elec_energy_tol: 1e-8 geom_force_tol: 0.03 From 5ff96f835ec1a4e57be9ea75fe29fb327c466f33 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Sat, 13 Nov 2021 22:32:26 +0000 Subject: [PATCH 04/17] Commit changes of the protocol.yml --- aiida_common_workflows/workflows/relax/castep/protocol.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index 7eb28668..16ee54fb 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -109,6 +109,7 @@ oxides_validation: opt_strategy: speed write_bib: false write_checkpoint: minimal + finite_basis_corr: none # No finite basis corr as we are NOT moving any atoms.... max_scf_cycles: 100 elec_energy_tol: 1e-8 geom_force_tol: 0.03 @@ -119,6 +120,6 @@ oxides_validation: # Revised smearing scheme for the oxide validation project smearing_width: 0.06122561905370023 # Equivalent to 0.0045 Ry smearing_scheme: FERMIDIRAC - perc_extra_bands: 30 # More bands by default + perc_extra_bands: 80 settings: ADDITIONAL_RETRIEVE_TEMPORARY_LIST: ["aiida.castep_bin"] From ccfbd43dafc328a0a8bf1e86aee40a20b4873e53 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Thu, 18 Nov 2021 08:38:28 +0000 Subject: [PATCH 05/17] Added set with increased kspacing --- .../workflows/relax/castep/protocol.yml | 43 ++++++++++++++++++- .../workflows/relax/generator.py | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index 16ee54fb..eebf3fc1 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -95,7 +95,48 @@ oxides_validation: base: pseudos_family: 'C19' max_iterations: 5 - kpoints_spacing: 0.04 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... + #kpoints_spacing: 0.04 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... + kpoints_spacing: 0.10 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... + ensure_gamma_centering: True # Ensure that the kpoint grid is Gamma-centered + calc: + parameters: + task: geometryoptimisation + xc_functional: pbe + symmetry_generate: true + snap_to_symmetry: true + calculate_stress: true + basis_precision: precise + write_otfg: false + opt_strategy: speed + write_bib: false + write_checkpoint: minimal + finite_basis_corr: none # No finite basis corr as we are NOT moving any atoms.... + max_scf_cycles: 100 + elec_energy_tol: 1e-8 + geom_force_tol: 0.03 + geom_energy_tol: 1.0e-5 + geom_stress_tol: 0.05 + grid_scale: 2 + fine_grid_scale : 3 + # Revised smearing scheme for the oxide validation project + smearing_width: 0.06122561905370023 # Equivalent to 0.0045 Ry + smearing_scheme: FERMIDIRAC + perc_extra_bands: 80 + settings: + ADDITIONAL_RETRIEVE_TEMPORARY_LIST: ["aiida.castep_bin"] + +oxides_validation_k_015: + name: 'oxides-validation-kspacing-0.15' + description: 'Protocol for the oxides validation study.' + relax: + relax_options: + max_meta_iterations: 5 + + base: + pseudos_family: 'C19' + max_iterations: 5 + #kpoints_spacing: 0.04 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... + kpoints_spacing: 0.15 # Increased kspacing for checks ensure_gamma_centering: True # Ensure that the kpoint grid is Gamma-centered calc: parameters: diff --git a/aiida_common_workflows/workflows/relax/generator.py b/aiida_common_workflows/workflows/relax/generator.py index c419edfa..728d5817 100644 --- a/aiida_common_workflows/workflows/relax/generator.py +++ b/aiida_common_workflows/workflows/relax/generator.py @@ -32,7 +32,7 @@ def define(cls, spec): ) spec.input( 'protocol', - valid_type=ChoiceType(('fast', 'moderate', 'precise', 'oxides_validation')), + valid_type=ChoiceType(('fast', 'moderate', 'precise', 'oxides_validation', 'oxides_validation_k_015')), default='moderate', help='The protocol to use for the automated input generation. This value indicates the level of precision ' 'of the results and computational cost that the input parameters will be selected for.', From c0f8b62aa61a3c7db0195606724279aced31ae05 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Thu, 13 Jan 2022 15:02:15 +0000 Subject: [PATCH 06/17] Initial commit for the CASTEP common workchain --- .../workflows/bands/castep/__init__.py | 0 .../workflows/bands/castep/generator.py | 65 +++++++++++++++++++ .../workflows/bands/castep/workchain.py | 40 ++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 aiida_common_workflows/workflows/bands/castep/__init__.py create mode 100644 aiida_common_workflows/workflows/bands/castep/generator.py create mode 100644 aiida_common_workflows/workflows/bands/castep/workchain.py diff --git a/aiida_common_workflows/workflows/bands/castep/__init__.py b/aiida_common_workflows/workflows/bands/castep/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aiida_common_workflows/workflows/bands/castep/generator.py b/aiida_common_workflows/workflows/bands/castep/generator.py new file mode 100644 index 00000000..066f46ba --- /dev/null +++ b/aiida_common_workflows/workflows/bands/castep/generator.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +"""Implementation of `aiida_common_workflows.common.bands.generator.CommonBandsInputGenerator` for CASTEP.""" +from aiida import engine, orm + +from aiida_common_workflows.generators import CodeType + +from ..generator import CommonBandsInputGenerator + +__all__ = ('CastepCommonBandsInputGenerator',) + + +class CastepCommonBandsInputGenerator(CommonBandsInputGenerator): + """Generator of inputs for the CastepCommonBandsWorkChain""" + + @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('castep.castep') + + 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. Note + # that we are sure it exists (it wouldn't be the same for WorkChains). We then check + # that it is a CastepCalculation and create the builder. + parent_castep_calc = parent_folder.creator + if parent_castep_calc.process_type != 'aiida.calculations:castep.castep': + raise ValueError('The `parent_folder` has not been created by a CastepCalculation') + builder_castep_calc = parent_castep_calc.get_builder_restart() + + # Construct the builder of the `common_bands_wc` from the builder of a CastepCalculation. + builder_common_bands_wc = self.process_class.get_builder() + builder_common_bands_wc.scf.calc_options = orm.Dict(dict=dict(builder_castep_calc.metadata.options)) + builder_castep_calc.metadata = {} + # Attach inputs of the calculation + for key, value in builder_castep_calc.items(): + if value and key not in ['metadata', 'structure']: + builder_common_bands_wc.scf.calc[key] = value + + # Updated the structure (in case we have one in output) + if 'output_structure' in parent_castep_calc.outputs: + builder_common_bands_wc.structure = parent_castep_calc.outputs.output_structure + + engb = engines['bands'] + builder_common_bands_wc.code = engines['bands']['code'] + if 'options' in engb: + builder_common_bands_wc.options = orm.Dict(dict=engines['bands']['options']) + + # Set the `bandskpoints` and the `parent_calc_folder` for restart + builder_common_bands_wc.bands_kpoints = bands_kpoints + builder_common_bands_wc.scf.reuse_folder = parent_folder + builder_common_bands_wc.run_separate_scf = orm.Bool(False) + + return builder_common_bands_wc diff --git a/aiida_common_workflows/workflows/bands/castep/workchain.py b/aiida_common_workflows/workflows/bands/castep/workchain.py new file mode 100644 index 00000000..450f9dff --- /dev/null +++ b/aiida_common_workflows/workflows/bands/castep/workchain.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +"""Implementation of `aiida_common_workflows.common.relax.workchain.CommonRelaxWorkChain` for CASTEP.""" +from aiida.engine import calcfunction +from aiida.orm import Float +from aiida.plugins import WorkflowFactory + +from ..workchain import CommonBandsWorkChain +from .generator import CastepCommonBandsInputGenerator + +from logging import getLogger +logger = getLogger(__name__) + +__all__ = ('CastepCommonBandsWorkChain',) + + +@calcfunction +def get_fermi_energy(bands): + """Extract the Fermi energy from the BandsData output""" + efermi = bands.get_attribute("efermi") + if isinstance(efermi, list): + efermi = efermi[0] + logger.warn("Spin polarised calculation - using the efermi energy of the first spin channel.") + return Float(efermi) + + +class CastepCommonBandsWorkChain(CommonBandsWorkChain): + """Implementation of `aiida_common_workflows.common.bands.workchain.CommonBandsWorkChain` for SIESTA.""" + + _process_class = WorkflowFactory('castep.bands') + _generator_class = CastepCommonBandsInputGenerator + + def convert_outputs(self): + """Convert the outputs of the sub workchain to the common output specification.""" + if 'band_structure' not in self.ctx.workchain.outputs: + self.report('CastepBandsWorkChain concluded without returning bands!') + return self.exit_codes.ERROR_SUB_PROCESS_FAILED + + self.out('fermi_energy', get_fermi_energy(self.ctx.workchain.outputs['band_structure'])) + + self.out('bands', self.ctx.workchain.outputs['band_structure']) From dc4d6638be7346ec7e06d56c1a7e6f9a587f9f60 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Thu, 13 Jan 2022 18:01:23 +0000 Subject: [PATCH 07/17] Updated the generation for CASTEP --- .../workflows/bands/castep/generator.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/aiida_common_workflows/workflows/bands/castep/generator.py b/aiida_common_workflows/workflows/bands/castep/generator.py index 066f46ba..07c12498 100644 --- a/aiida_common_workflows/workflows/bands/castep/generator.py +++ b/aiida_common_workflows/workflows/bands/castep/generator.py @@ -42,7 +42,11 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: # Construct the builder of the `common_bands_wc` from the builder of a CastepCalculation. builder_common_bands_wc = self.process_class.get_builder() builder_common_bands_wc.scf.calc_options = orm.Dict(dict=dict(builder_castep_calc.metadata.options)) + # Ensure we use castep_bin for restart, instead of the check file + #builder_common_bands_wc.scf.options = orm.Dict(dict={'use_castep_bin': True}) + builder_castep_calc.metadata = {} + # Attach inputs of the calculation for key, value in builder_castep_calc.items(): if value and key not in ['metadata', 'structure']: @@ -51,15 +55,17 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: # Updated the structure (in case we have one in output) if 'output_structure' in parent_castep_calc.outputs: builder_common_bands_wc.structure = parent_castep_calc.outputs.output_structure + else: + builder_common_bands_wc.structure = parent_castep_calc.inputs.structure engb = engines['bands'] - builder_common_bands_wc.code = engines['bands']['code'] + builder_common_bands_wc.scf.calc.code = engines['bands']['code'] if 'options' in engb: - builder_common_bands_wc.options = orm.Dict(dict=engines['bands']['options']) + builder_common_bands_wc.scf.calc_options = orm.Dict(dict=engines['bands']['options']) # Set the `bandskpoints` and the `parent_calc_folder` for restart builder_common_bands_wc.bands_kpoints = bands_kpoints - builder_common_bands_wc.scf.reuse_folder = parent_folder + builder_common_bands_wc.scf.continuation_folder = parent_folder builder_common_bands_wc.run_separate_scf = orm.Bool(False) return builder_common_bands_wc From 8d01573d9ca2a85d9307a794a553c32bf7adf30d Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Thu, 13 Jan 2022 18:03:07 +0000 Subject: [PATCH 08/17] Fix typo --- .../workflows/bands/castep/workchain.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/aiida_common_workflows/workflows/bands/castep/workchain.py b/aiida_common_workflows/workflows/bands/castep/workchain.py index 450f9dff..3f4c4f16 100644 --- a/aiida_common_workflows/workflows/bands/castep/workchain.py +++ b/aiida_common_workflows/workflows/bands/castep/workchain.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- """Implementation of `aiida_common_workflows.common.relax.workchain.CommonRelaxWorkChain` for CASTEP.""" +from logging import getLogger + from aiida.engine import calcfunction from aiida.orm import Float from aiida.plugins import WorkflowFactory @@ -7,24 +9,22 @@ from ..workchain import CommonBandsWorkChain from .generator import CastepCommonBandsInputGenerator -from logging import getLogger -logger = getLogger(__name__) - __all__ = ('CastepCommonBandsWorkChain',) @calcfunction def get_fermi_energy(bands): """Extract the Fermi energy from the BandsData output""" - efermi = bands.get_attribute("efermi") + efermi = bands.get_attribute('efermi') if isinstance(efermi, list): efermi = efermi[0] - logger.warn("Spin polarised calculation - using the efermi energy of the first spin channel.") + logger = getLogger(__name__) + logger.warning('Spin polarised calculation - using the efermi energy of the first spin channel.') return Float(efermi) class CastepCommonBandsWorkChain(CommonBandsWorkChain): - """Implementation of `aiida_common_workflows.common.bands.workchain.CommonBandsWorkChain` for SIESTA.""" + """Implementation of `aiida_common_workflows.common.bands.workchain.CommonBandsWorkChain` for CASTEP.""" _process_class = WorkflowFactory('castep.bands') _generator_class = CastepCommonBandsInputGenerator @@ -38,3 +38,4 @@ def convert_outputs(self): self.out('fermi_energy', get_fermi_energy(self.ctx.workchain.outputs['band_structure'])) self.out('bands', self.ctx.workchain.outputs['band_structure']) + return None From b1c7020f1e1a2f51dd7ebc92ec1603a9b12060ce Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Thu, 13 Jan 2022 18:04:56 +0000 Subject: [PATCH 09/17] Remove the limitor of the protocols --- aiida_common_workflows/workflows/relax/generator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aiida_common_workflows/workflows/relax/generator.py b/aiida_common_workflows/workflows/relax/generator.py index 90feda29..e37c46bd 100644 --- a/aiida_common_workflows/workflows/relax/generator.py +++ b/aiida_common_workflows/workflows/relax/generator.py @@ -32,7 +32,6 @@ def define(cls, spec): ) spec.input( 'protocol', - valid_type=ChoiceType(('fast', 'moderate', 'precise', 'oxides_validation', 'oxides_validation_k_015')), default='moderate', help='The protocol to use for the automated input generation. This value indicates the level of precision ' 'of the results and computational cost that the input parameters will be selected for.', From 34060a3239987c999c161c1aec0cc930d1601231 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Fri, 21 Jan 2022 14:33:17 +0000 Subject: [PATCH 10/17] Update the generator codes --- .../workflows/bands/castep/generator.py | 18 ++++++++++++++---- .../workflows/relax/castep/generator.py | 7 +++++++ .../workflows/relax/generator.py | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/aiida_common_workflows/workflows/bands/castep/generator.py b/aiida_common_workflows/workflows/bands/castep/generator.py index 07c12498..88a57f22 100644 --- a/aiida_common_workflows/workflows/bands/castep/generator.py +++ b/aiida_common_workflows/workflows/bands/castep/generator.py @@ -27,7 +27,12 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: 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) + + required_keys = ('engines', 'parent_folder', 'bands_kpoints') + for key in required_keys: + if key not in kwargs: + raise ValueError(f'Required key `{key}` is missing in the function argument.') + engines = kwargs['engines'] parent_folder = kwargs['parent_folder'] bands_kpoints = kwargs['bands_kpoints'] @@ -58,10 +63,15 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: else: builder_common_bands_wc.structure = parent_castep_calc.inputs.structure - engb = engines['bands'] - builder_common_bands_wc.scf.calc.code = engines['bands']['code'] + try: + engb = engines['bands'] + except KeyError: + raise ValueError('The engines dictionary passed must contains a key named `bands`.') + + builder_common_bands_wc.scf.calc.code = engb['code'] + if 'options' in engb: - builder_common_bands_wc.scf.calc_options = orm.Dict(dict=engines['bands']['options']) + builder_common_bands_wc.scf.calc.metadata.options = engb['options'] # Set the `bandskpoints` and the `parent_calc_folder` for restart builder_common_bands_wc.bands_kpoints = bands_kpoints diff --git a/aiida_common_workflows/workflows/relax/castep/generator.py b/aiida_common_workflows/workflows/relax/castep/generator.py index 7ba9172b..3c80fb09 100644 --- a/aiida_common_workflows/workflows/relax/castep/generator.py +++ b/aiida_common_workflows/workflows/relax/castep/generator.py @@ -46,6 +46,13 @@ def define(cls, spec): The ports defined on the specification are the inputs that will be accepted by the ``get_builder`` method. """ super().define(spec) + spec.input( + 'protocol', + valid_type=ChoiceType(('fast', 'moderate', 'precise', 'oxide_validation')), + default='moderate', + help='The protocol to use for the automated input generation. This value indicates the level of precision ' + 'of the results and computational cost that the input parameters will be selected for.', + ) spec.inputs['spin_type'].valid_type = ChoiceType((SpinType.NONE, SpinType.COLLINEAR, SpinType.NON_COLLINEAR)) spec.inputs['relax_type'].valid_type = ChoiceType(tuple(RelaxType)) spec.inputs['electronic_type'].valid_type = ChoiceType((ElectronicType.METAL, ElectronicType.INSULATOR)) diff --git a/aiida_common_workflows/workflows/relax/generator.py b/aiida_common_workflows/workflows/relax/generator.py index e37c46bd..d50f7777 100644 --- a/aiida_common_workflows/workflows/relax/generator.py +++ b/aiida_common_workflows/workflows/relax/generator.py @@ -6,12 +6,11 @@ from aiida_common_workflows.common import ElectronicType, RelaxType, SpinType from aiida_common_workflows.generators import ChoiceType, InputGenerator -from aiida_common_workflows.protocol import ProtocolRegistry __all__ = ('CommonRelaxInputGenerator',) -class CommonRelaxInputGenerator(InputGenerator, ProtocolRegistry, metaclass=abc.ABCMeta): +class CommonRelaxInputGenerator(InputGenerator, metaclass=abc.ABCMeta): """Input generator for the common relax workflow. This class should be subclassed by implementations for specific quantum engines. After calling the super, they can @@ -32,6 +31,7 @@ def define(cls, spec): ) spec.input( 'protocol', + valid_type=ChoiceType(('fast', 'moderate', 'precise')), default='moderate', help='The protocol to use for the automated input generation. This value indicates the level of precision ' 'of the results and computational cost that the input parameters will be selected for.', From 82e96b5a7b3aac30003040382d9987379820a2ed Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Fri, 11 Mar 2022 17:27:28 +0000 Subject: [PATCH 11/17] Rename the protocol to verification-PBE-v1 --- .../workflows/relax/castep/protocol.yml | 44 +------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index eebf3fc1..fa537ee2 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -85,7 +85,7 @@ fast: geom_stress_tol: 0.1 -oxides_validation: +verification-PBE-v1: name: 'oxides-validation' description: 'Protocol for the oxides validation study.' relax: @@ -105,47 +105,7 @@ oxides_validation: symmetry_generate: true snap_to_symmetry: true calculate_stress: true - basis_precision: precise - write_otfg: false - opt_strategy: speed - write_bib: false - write_checkpoint: minimal - finite_basis_corr: none # No finite basis corr as we are NOT moving any atoms.... - max_scf_cycles: 100 - elec_energy_tol: 1e-8 - geom_force_tol: 0.03 - geom_energy_tol: 1.0e-5 - geom_stress_tol: 0.05 - grid_scale: 2 - fine_grid_scale : 3 - # Revised smearing scheme for the oxide validation project - smearing_width: 0.06122561905370023 # Equivalent to 0.0045 Ry - smearing_scheme: FERMIDIRAC - perc_extra_bands: 80 - settings: - ADDITIONAL_RETRIEVE_TEMPORARY_LIST: ["aiida.castep_bin"] - -oxides_validation_k_015: - name: 'oxides-validation-kspacing-0.15' - description: 'Protocol for the oxides validation study.' - relax: - relax_options: - max_meta_iterations: 5 - - base: - pseudos_family: 'C19' - max_iterations: 5 - #kpoints_spacing: 0.04 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... - kpoints_spacing: 0.15 # Increased kspacing for checks - ensure_gamma_centering: True # Ensure that the kpoint grid is Gamma-centered - calc: - parameters: - task: geometryoptimisation - xc_functional: pbe - symmetry_generate: true - snap_to_symmetry: true - calculate_stress: true - basis_precision: precise + cut_off_energy: 800 # Fixed cut off energy for handling both unaries and oxides write_otfg: false opt_strategy: speed write_bib: false From f27903150967f64f693761dac603cf1be0d28af6 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Fri, 11 Mar 2022 17:31:08 +0000 Subject: [PATCH 12/17] Update the protocol --- aiida_common_workflows/workflows/relax/castep/protocol.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index fa537ee2..ff8b8e28 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -86,7 +86,7 @@ fast: verification-PBE-v1: - name: 'oxides-validation' + name: 'verification-PBE-v1' description: 'Protocol for the oxides validation study.' relax: relax_options: @@ -95,8 +95,7 @@ verification-PBE-v1: base: pseudos_family: 'C19' max_iterations: 5 - #kpoints_spacing: 0.04 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... - kpoints_spacing: 0.10 # Equivalent to `kpoints_mp_spacing : 0.00636` - very fine... + kpoints_spacing: 0.06 # K point spacing in A^-1, not the A^-1 2Pi that CASTEP defaults to ensure_gamma_centering: True # Ensure that the kpoint grid is Gamma-centered calc: parameters: From ecc443244449a49953f29442f12c9111b51e36e0 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Sat, 12 Mar 2022 22:31:39 +0000 Subject: [PATCH 13/17] Fix the handling of soft elements Only apply a minimum cut off if cut off energy is not explicitly set already. --- .../workflows/relax/castep/generator.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/aiida_common_workflows/workflows/relax/castep/generator.py b/aiida_common_workflows/workflows/relax/castep/generator.py index 3c80fb09..9e6f3621 100644 --- a/aiida_common_workflows/workflows/relax/castep/generator.py +++ b/aiida_common_workflows/workflows/relax/castep/generator.py @@ -48,7 +48,7 @@ def define(cls, spec): super().define(spec) spec.input( 'protocol', - valid_type=ChoiceType(('fast', 'moderate', 'precise', 'oxide_validation')), + valid_type=ChoiceType(('fast', 'moderate', 'precise', 'verification-PBE-v1')), default='moderate', help='The protocol to use for the automated input generation. This value indicates the level of precision ' 'of the results and computational cost that the input parameters will be selected for.', @@ -80,7 +80,7 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: # Because the subsequent generators may modify this dictionary and convert things # to AiiDA types, here we make a full copy of the original protocol - protocol = copy.deepcopy(self.get_protocol(protocol)) + protocol = copy.deepcopy(self._protocols[protocol]) code = engines['relax']['code'] override = {'base': {'calc': {'metadata': {'options': engines['relax']['options']}}}} @@ -168,11 +168,13 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: # Raise the cut off energy for very soft pseudopotentials # this is because the small basis set will give rise to errors in EOS / variable volume # relaxation even with the "fine" option - with open(str(pathlib.Path(__file__).parent / 'soft_elements.yml')) as fhandle: - soft_elements = yaml.safe_load(fhandle) - symbols = [kind.symbol for kind in structure.kinds] - if all(sym in soft_elements for sym in symbols): - param['cut_off_energy'] = 326 # eV, approximately 12 Ha + if 'cut_off_energy' not in param: + with open(str(pathlib.Path(__file__).parent / 'soft_elements.yml')) as fhandle: + soft_elements = yaml.safe_load(fhandle) + symbols = [kind.symbol for kind in structure.kinds] + if all(sym in soft_elements for sym in symbols): + param['cut_off_energy'] = 326 # eV, approximately 12 Ha + param.pop('basis_precision', None) # Apply the overrides if param: From d48a79b2762244d957e3852732b5a239a3c6ed6d Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Sat, 12 Mar 2022 22:49:58 +0000 Subject: [PATCH 14/17] Fix the handling --- aiida_common_workflows/workflows/relax/castep/generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiida_common_workflows/workflows/relax/castep/generator.py b/aiida_common_workflows/workflows/relax/castep/generator.py index 9e6f3621..6dbb4224 100644 --- a/aiida_common_workflows/workflows/relax/castep/generator.py +++ b/aiida_common_workflows/workflows/relax/castep/generator.py @@ -168,7 +168,7 @@ def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: # Raise the cut off energy for very soft pseudopotentials # this is because the small basis set will give rise to errors in EOS / variable volume # relaxation even with the "fine" option - if 'cut_off_energy' not in param: + if 'cut_off_energy' not in protocol['relax']['base']['calc']['parameters']: with open(str(pathlib.Path(__file__).parent / 'soft_elements.yml')) as fhandle: soft_elements = yaml.safe_load(fhandle) symbols = [kind.symbol for kind in structure.kinds] From a581750c21be07a9e6ffd0268c3692287082aec8 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Sun, 13 Mar 2022 10:09:48 +0000 Subject: [PATCH 15/17] Fix the soft_elements.yml The Y and No elements expanded into Y and No.... --- .../workflows/relax/castep/soft_elements.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiida_common_workflows/workflows/relax/castep/soft_elements.yml b/aiida_common_workflows/workflows/relax/castep/soft_elements.yml index 3fcd2c1b..a97da9b5 100644 --- a/aiida_common_workflows/workflows/relax/castep/soft_elements.yml +++ b/aiida_common_workflows/workflows/relax/castep/soft_elements.yml @@ -16,7 +16,7 @@ - Kr - Rb - Sr -- Y +- 'Y' - Zr - In - Sn @@ -49,7 +49,7 @@ - Ac - Th - Pa -- No +- 'No' - Lr - Rf - Db From e7c2dba4c2c31d7bf6da4606cc45517c378bd898 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Tue, 12 Apr 2022 15:53:24 +0100 Subject: [PATCH 16/17] Updated CASTEP protcol Added modified pseudopotentials for lanthides and actinides. --- .../relax/castep/additional_otfg_families.yml | 32 +++++++++++++++++ .../workflows/relax/castep/generator.py | 35 ++++++++++++++++--- .../workflows/relax/castep/protocol.yml | 2 +- tests/workflows/relax/test_castep.py | 25 +++++++++++++ 4 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 aiida_common_workflows/workflows/relax/castep/additional_otfg_families.yml diff --git a/aiida_common_workflows/workflows/relax/castep/additional_otfg_families.yml b/aiida_common_workflows/workflows/relax/castep/additional_otfg_families.yml new file mode 100644 index 00000000..d7326774 --- /dev/null +++ b/aiida_common_workflows/workflows/relax/castep/additional_otfg_families.yml @@ -0,0 +1,32 @@ +# Family based on C19 with updated potentials for lanthanide and actinides +C19V2: + - "C19" + - "La 2|2.3|5|6|7|50U:60:51:52:43{4f0.1}(qc=4.5)" + - "Ce 2|2.2|8|9|10|50U:60:51:52:43{5d0.1}(qc=4.5)" + - "Pr 2|2.1|10|12|13|50U:60:51:52:43{5d0.1}(qc=5)" + - "Nd 2|2.1|10|12|13|50U:60:51:52:43{5d0.1}(qc=5)" + - "Pm 2|2.1|8|9|11|50U:60:51:52:43{5d0.1,4f4}(qc=5.5)" + - "Sm 2|2.1|9|10|12|50U:60:51:52:43{5d0.1,4f5}(qc=5.5)" + - "Eu 2|2.1|9|10|12|50U:60:51:52:43{5d0.1,4f6}(qc=5.5)" + - "Gd 3|2.1|9|10|12|50U:60:51:52:43(qc=5.5)" + - "Tb 2|2.2|12|13|15|50U:60:51:52:43{5d0.1}(qc=5)" + - "Dy 2|2.0|12|13|15|50U:60:51:52:43{5d0.1}(qc=6.5)" + - "Ho 2|2.0|12|13|15|50U:60:51:52:43{5d0.1}(qc=6.5)" + - "Er 2|2.1|10|12|13|50U:60:51:52:43{6s0.1,5d0.1}(qc=6)" + - "Tm 2|2.1|10|12|13|50U:60:51:52:43{5d0.1,4f12}(qc=6)" + - "Yb 2|2.1|10|12|13|50U:60:51:52:43{5d0.1,4f13}(qc=6)" + - "Ac 2|2.4|7|7|9|60U:70U2U2:61:62:53{6d0.1,5f0.1}(qc=5)" + - "Th 2|2.2|7|7|9|60U:70U2U2:61:62:53{5f0.1}(qc=5)" + - "Pa 2|2.2|8|9|10|60U:70U2U2:61:62:53(qc=5)" + - "U 2|2.2|8|9|10|60U:70U2U2:61:62:53(qc=5)" + - "Np 2|2.2|9|10|12|60U:70U2U2:61:62:53(qc=5)" + - "Pu 2|2.2|9|10|12|60U:70U2U2:61:62:53{6d0.1}(qc=5.5)" + - "Am 2|2.2|9|10|12|60U:70U2U2:61:62:53{6d0.1}(qc=5.5)" + - "Cm 2|2.2|9|10|12|60U:70U2U2:61:62:53(qc=5.5)" + # Elements below in this family are not tested again all-electron codes - use with CAUTION + - "Bk 2|2.2|9|10|12|60U:70U2U2:61:62:53{6d0.1}(qc=5.5)" + - "Cf 2|2.2|9|10|12|60U:70U2U2:61:62:53{6d0.1}(qc=5.5)" + - "Es 2|2.1|10|12|13|60U:70U2U2:61:62:53{6d0.1}(qc=6)" + - "Fm 2|2.1|10|12|13|60U:70U2U2:61:62:53{6d0.1}(qc=6)" + - "Md 2|2.0|10|12|13|60U:70U2U2:61:62:53{6d0.1,5f12}(qc=6)" + - "No 2|2.0|10|12|13|60U:70U2U2:61:62:53{6d0.1,5f13}(qc=6)" diff --git a/aiida_common_workflows/workflows/relax/castep/generator.py b/aiida_common_workflows/workflows/relax/castep/generator.py index 6dbb4224..b7949cb4 100644 --- a/aiida_common_workflows/workflows/relax/castep/generator.py +++ b/aiida_common_workflows/workflows/relax/castep/generator.py @@ -18,6 +18,7 @@ from ..generator import CommonRelaxInputGenerator # pylint: disable=import-outside-toplevel, too-many-branches, too-many-statements +KNOWN_BUILTIN_FAMILIES = ('C19', 'NCP19', 'QC5', 'C17', 'C9') __all__ = ('CastepCommonRelaxInputGenerator',) @@ -411,8 +412,12 @@ def generate_inputs_calculation( return dictionary -def ensure_otfg_family(family_name): - """Add common OTFG families if they do not exist""" +def ensure_otfg_family(family_name, force_update=False): + """ + Add common OTFG families if they do not exist + NOTE: CASTEP also supports UPF families, but it is not enabled here, since no UPS based protocol + has been implemented. + """ from aiida.common import NotExistent from aiida_castep.data.otfg import upload_otfg_family @@ -420,9 +425,29 @@ def ensure_otfg_family(family_name): # Ensure family name is a str if isinstance(family_name, orm.Str): family_name = family_name.value - try: OTFGGroup.objects.get(label=family_name) except NotExistent: - description = f"CASTEP built-in on-the-fly generated pseudos libraray '{family_name}'" - upload_otfg_family([family_name], family_name, description, stop_if_existing=True) + has_family = False + else: + has_family = True + + # Check if it is builtin family + if family_name in KNOWN_BUILTIN_FAMILIES: + if not has_family: + description = f"CASTEP built-in on-the-fly generated pseudos libraray '{family_name}'" + upload_otfg_family([family_name], family_name, description, stop_if_existing=True) + return + + # Not an known family - check if it in the additional settings list + # Load configuration from the settings + with open(str(pathlib.Path(__file__).parent / 'additional_otfg_families.yml')) as handle: + additional = yaml.safe_load(handle) + + if family_name in additional: + if not has_family or force_update: + description = f"Modified CASTEP built-in on-the-fly generated pseudos libraray '{family_name}'" + upload_otfg_family(additional[family_name], family_name, description, stop_if_existing=False) + elif not has_family: + # No family found - and it is not recognized + raise RuntimeError(f"Family name '{family_name}' is not recognized!") diff --git a/aiida_common_workflows/workflows/relax/castep/protocol.yml b/aiida_common_workflows/workflows/relax/castep/protocol.yml index ff8b8e28..8a70749e 100644 --- a/aiida_common_workflows/workflows/relax/castep/protocol.yml +++ b/aiida_common_workflows/workflows/relax/castep/protocol.yml @@ -93,7 +93,7 @@ verification-PBE-v1: max_meta_iterations: 5 base: - pseudos_family: 'C19' + pseudos_family: 'C19V2' max_iterations: 5 kpoints_spacing: 0.06 # K point spacing in A^-1, not the A^-1 2Pi that CASTEP defaults to ensure_gamma_centering: True # Ensure that the kpoint grid is Gamma-centered diff --git a/tests/workflows/relax/test_castep.py b/tests/workflows/relax/test_castep.py index e27d6a1f..f3035de3 100644 --- a/tests/workflows/relax/test_castep.py +++ b/tests/workflows/relax/test_castep.py @@ -240,3 +240,28 @@ def test_input_generator(castep_code, nacl, si): # pylint: disable=invalid-name ) assert builder.calc.settings is None assert builder.base.kpoints_spacing == pytest.approx(0.023873, abs=1e-6) + + +def test_otfg_upload(with_otfg): + """ + Test uploading customized OTFG family + """ + + # Initial upload + ensure_otfg_family('C19V2') + assert OTFGGroup.objects.get(label='C19V2') + + # Second call should not error + ensure_otfg_family('C19V2') + assert OTFGGroup.objects.get(label='C19V2') + + # Second call with forced update + ensure_otfg_family('C19V2', force_update=True) + + group = OTFGGroup.objects.get(label='C19V2') + found = False + for node in group.nodes: + if node.element == 'La': + assert node.entry == 'La 2|2.3|5|6|7|50U:60:51:52:43{4f0.1}(qc=4.5)' + found = True + assert found From 82bf2f524f6fa5c4e755d3969402b873206487e5 Mon Sep 17 00:00:00 2001 From: Bonan Zhu Date: Wed, 27 Apr 2022 09:50:01 +0100 Subject: [PATCH 17/17] Added extractor of TS term for CASTEP --- .../workflows/relax/castep/extractors.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 aiida_common_workflows/workflows/relax/castep/extractors.py diff --git a/aiida_common_workflows/workflows/relax/castep/extractors.py b/aiida_common_workflows/workflows/relax/castep/extractors.py new file mode 100644 index 00000000..211b1f05 --- /dev/null +++ b/aiida_common_workflows/workflows/relax/castep/extractors.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +""" +Collects some functions to postprocess a `CastepCommonRelaxWorkChain`. +""" +from aiida.common import LinkType +from aiida.orm import WorkChainNode +from aiida.plugins import WorkflowFactory + +CastepCommonRelaxWorkChain = WorkflowFactory('common_workflows.relax.castep') + + +def get_ts_energy(common_relax_workchain): + """ + Return the TS value of a concluded CastepCommonRelaxWorkChain. + + CASTEP reports three quantities related to the energy: + + - "free energy": ``Final free energy`` - the total energy minus the TS term. + This is the energy that gets minimised and is consistent with the forces calculated. + - "total energy": ``Final energy`` - the total Khon-Sham energy. + - "extrapolated 0K energy": ``NB est. 0K energy`` - the result of E-0.5TS to give better convergence. + - "enthalpy": Is the free energy minus to PV term under finite temperature (for geometry optimisation). + + The TS term can extrapolated by subtracting the free energy from the total energy. + """ + if not isinstance(common_relax_workchain, WorkChainNode): + return ValueError('The input is not a workchain (instance of `WorkChainNode`)') + if common_relax_workchain.process_class != CastepCommonRelaxWorkChain: + return ValueError('The input workchain is not a `CastepCommonRelaxWorkChain`') + + castep_base_wc = common_relax_workchain.get_outgoing(link_type=LinkType.CALL_WORK).one().node + e_ks = castep_base_wc.outputs.output_parameters['total energy'] + free_e = castep_base_wc.outputs.output_parameters['free energy'] + + ts = e_ks - free_e #pylint: disable=invalid-name + + return ts