diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b57d8e5a..fab8b699 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - id: flynt - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 23.3.0 hooks: - id: black language_version: python3 # Should be a command that runs python3.6+ @@ -33,8 +33,6 @@ repos: aiida_sssp_workflow/workflows/measure/bands.py | aiida_sssp_workflow/workflows/convergence/_base.py )$ - - id: black-jupyter - exclude: *exclude_pre_commit_black_hooks - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 diff --git a/README.md b/README.md index 7843e105..2d08406c 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,12 @@ This what we call it residual volume is therefore a stiffness-agnostic value tha In convergence delta factor calculation, the GS configurations from Cottiner's paper are used. To have a uniform sence of precision through looking at delta factor, the value is defined as delta factor per atom. However, since it is hard to define delta per/atom for oxides, ACWF does not use per/atom value to represent results. -To compatible with it, the precision verification also use the structure delta. +It uses the nu factor which was defined in the ACWF paper. + +For the oxides, it needs oxygen pseudopotential first, same as nitrides. +The verification needs to run for all know oxygen/nitrigen pseudopotentials filst. However, the cutoffs of them are not known and the cutoffs are input for the precision measure. +So the very first run is the convergence test on all oxygen/nitrigen pseudopotentials. +Once the recommended cutoffs are known, the precision measure can be run for all oxygen/nitrigen elements. In the convergence verification, there are many options to choose from for what configuration to use. But it is redundant to use all of them, we what the configuration that gives the largest recommended cutoff energy for the pseudopotential. After tests (show test results, which in `sssp-verify-scripts`), we found that the for non-magnetic elements, the Diamond configuration that gives the largest cutoff energy. For magnetic elements, the GS configuration calculation with magnetization turned on for the bulk (in cohesive energy which usually give the largest recommended cutoff, where the atomic calculation still don't have magnetization turned on and it is not needed because the pseudopotential is generated in the close shell approximiation (??)) that gives the largest cutoff energy. @@ -190,4 +195,4 @@ MIT ## Contact -📧 email: jusong.yu@epfl.ch +📧 email: jusong.yu@psi.ch diff --git a/aiida_sssp_workflow/calculations/calculate_bands_distance.py b/aiida_sssp_workflow/calculations/calculate_bands_distance.py index 4fb4fdb9..b5eb0241 100644 --- a/aiida_sssp_workflow/calculations/calculate_bands_distance.py +++ b/aiida_sssp_workflow/calculations/calculate_bands_distance.py @@ -171,7 +171,7 @@ def fun_shift(occ, bands_diff, shift): def get_bands_distance( bandsdata_a: dict, bandsdata_b: dict, - smearing: float, + smearing: float, # from degauss fermi_shift: float, do_smearing: bool, spin: bool, @@ -189,6 +189,9 @@ def get_bands_distance( First aligh the number of two bands, e.g tranctrate the overceed nubmer of bands """ + _RY_TO_EV = 13.6056980659 + smearing = smearing * _RY_TO_EV + # post process to deserial list to numpy arrar for key in ["bands", "kpoints", "weights"]: bandsdata_a[key] = np.asarray(bandsdata_a[key]) diff --git a/aiida_sssp_workflow/cli/run.py b/aiida_sssp_workflow/cli/run.py index cc8534de..60ba180a 100644 --- a/aiida_sssp_workflow/cli/run.py +++ b/aiida_sssp_workflow/cli/run.py @@ -9,6 +9,7 @@ import click from aiida import orm from aiida.cmdline.params import options, types +from aiida.cmdline.utils import echo from aiida.engine import run_get_node, submit from aiida.plugins import DataFactory, WorkflowFactory @@ -27,12 +28,13 @@ @cmd_root.command("launch") +@click.argument("pseudo", type=click.Path(exists=True)) @options.OverridableOption( "--pw-code", "pw_code", type=types.CodeParamType(entry_point="quantumespresso.pw") )(required=True) @options.OverridableOption( "--ph-code", "ph_code", type=types.CodeParamType(entry_point="quantumespresso.ph") -)(required=True) +)(required=False) @click.option( "--property", multiple=True, @@ -42,22 +44,8 @@ @click.option( "protocol", "--protocol", - default="standard", - help="Protocol to use for the verification.", -) -@click.option( - "cutoff_control", - "--cutoff-control", - default="standard", - help="Control of convergence.", -) -@click.option( - "criteria", "--criteria", default="efficiency", help="Criteria of convergence." -) -@click.option( - "configuration", - "--configuration", - help="(convergence only) Configuration of structure, can be: SC, FCC, BCC, Diamond, XO, XO2, XO3, X2O, X2O3, X2O5, GS.", + default="acwf", + help="Protocol to use for the verification, (acwf, test).", ) @click.option("withmpi", "--withmpi", default=True, help="Run with mpi.") @click.option("npool", "--npool", default=1, help="Number of pool.") @@ -78,12 +66,39 @@ "--comment", help="Add a comment word as the prefix the workchain description.", ) -@click.argument("pseudo", type=click.Path(exists=True)) +# ecutwfc and ecutrho are for measure workflows only +@click.option( + "ecutwfc", + "--ecutwfc", + help="Cutoff energy for wavefunctions in Rydberg.", +) +@click.option( + "ecutrho", + "--ecutrho", + help="Cutoff energy for charge density in Rydberg.", +) +# cutoff_control, criteria, configuration is for convergence workflows only +@click.option( + "cutoff_control", + "--cutoff-control", + help="Cutoff control for convergence workflow, (standard, quick, opsp).", +) +@click.option( + "criteria", "--criteria", help="Criteria for convergence (efficiency, precision)." +) +# configuration is hard coded for convergence workflow, but here is an interface for experiment purpose +@click.option( + "configuration", + "--configuration", + help="(convergence test only) Configuration of structure, can be: SC, FCC, BCC, Diamond, XO, XO2, XO3, X2O, X2O3, X2O5, GS, RE", +) def launch( pw_code, ph_code, property, protocol, + ecutwfc, + ecutrho, cutoff_control, criteria, configuration, @@ -112,8 +127,41 @@ def launch( properties_list = list(property) extra_desc = f"{properties_list}" + # validate the options are all provide for the property + is_convergence = False + is_measure = False + is_ph = False + for prop in properties_list: + if prop.startswith("convergence"): + is_convergence = True + if prop.startswith("measure"): + is_measure = True + if "phonon_frequencies" in prop: + is_ph = True + + # raise error if the options are not provided + if is_convergence and not (cutoff_control and criteria): + echo.echo_critical( + "cutoff_control, criteria must be provided for convergence workflow." + ) + + if is_measure and not (ecutwfc and ecutrho): + echo.echo_critical("ecutwfc and ecutrho must be provided for measure workflow.") + + if is_ph and not ph_code: + echo.echo_critical("ph_code must be provided for phonon frequencies.") + + # raise warning if the options are over provided, e.g. cutoff_control is provided for measure workflow + if is_measure and (cutoff_control or criteria or configuration): + echo.echo_warning( + "cutoff_control, criteria, configuration are not used for measure workflow." + ) + + if is_convergence and (ecutwfc or ecutrho): + echo.echo_warning("ecutwfc and ecutrho are not used for convergence workflow.") + _profile = aiida.load_profile() - click.echo(f"Current profile: {_profile.name}") + echo.echo_info(f"Current profile: {_profile.name}") basename = os.path.basename(pseudo) @@ -130,7 +178,8 @@ def launch( inputs = { "measure": { "protocol": orm.Str(protocol), - "cutoff_control": orm.Str(cutoff_control), + "wavefunction_cutoff": orm.Float(ecutwfc), + "charge_density_cutoff": orm.Float(ecutrho), }, "convergence": { "protocol": orm.Str(protocol), @@ -138,7 +187,6 @@ def launch( "criteria": orm.Str(criteria), }, "pw_code": pw_code, - "ph_code": ph_code, "pseudo": pseudo, "label": label, "properties_list": orm.List(properties_list), @@ -156,6 +204,9 @@ def launch( "clean_workdir": orm.Bool(clean_workdir), } + if is_ph: + inputs["ph_code"] = ph_code + if configuration is not None: inputs["convergence"]["configuration"] = orm.Str(configuration) @@ -167,8 +218,8 @@ def launch( description = f"{label.value} ({extra_desc})" node.description = f"({comment}) {description}" if comment else description - click.echo(node) - click.echo(f"calculated on property: {'/'.join(properties_list)}") + echo.echo_info(node) + echo.echo_success(f"calculated on property: {'/'.join(properties_list)}") if __name__ == "__main__": diff --git a/aiida_sssp_workflow/protocol/control.yml b/aiida_sssp_workflow/protocol/control.yml index 3f236577..888bbbb8 100644 --- a/aiida_sssp_workflow/protocol/control.yml +++ b/aiida_sssp_workflow/protocol/control.yml @@ -2,7 +2,6 @@ precheck: description: run on 150 v.s 200 v.s 300 Ry wavefunction cutoff with fix dual to give sense whether 200 Ry enough - # max_wfc: 0 # Not used if run precision measurement veri with this protocol, just raise exception. wfc_scan: [150, 200, 300] # at fix dual nc_dual_scan: [] # set empty so rho test not run nonnc_dual_scan: [] @@ -11,7 +10,6 @@ precheck: standard: description: high wavefunction cutoff set and cutoffs dense interval therefore time consuming - max_wfc: 200 # The max wfc cut for precision measurement wfc_scan: [30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 90, 100, 120, 150, 200] # at fix dual nc_dual_scan: [2.0, 2.5, 3.0, 3.5, 4.0] # at fix wfc nonnc_dual_scan: [6.0, 6.5, 7.0, 7.5, 8.0] @@ -20,7 +18,6 @@ standard: quick: description: low wavefunction cutoff set and cutoffs sparse interval therefore can run on local - max_wfc: 200 # The max wfc cut for precision measurement wfc_scan: [30, 40, 50, 60, 75, 100, 150, 200] # at fix dual nc_dual_scan: [3.0, 3.5, 4.0] # at fix wfc nonnc_dual_scan: [6.0, 7.0, 8.0] @@ -29,7 +26,6 @@ quick: test: description: test only - max_wfc: 35 # The max wfc cut for precision measurement wfc_scan: [30, 35] # at fix dual nc_dual_scan: [] # at fix wfc nonnc_dual_scan: [] # at fix rho @@ -38,7 +34,6 @@ test: opsp: description: To running opsp verification, make the calculations finished fast. - max_wfc: 40 # The max wfc cut for precision measurement wfc_scan: [30, 35] # at fix dual nc_dual_scan: [2.0, 4.0] # at fix wfc nonnc_dual_scan: [6.0, 8.0] diff --git a/aiida_sssp_workflow/protocol/converge.yml b/aiida_sssp_workflow/protocol/converge.yml index 89550ba2..52446c61 100644 --- a/aiida_sssp_workflow/protocol/converge.yml +++ b/aiida_sssp_workflow/protocol/converge.yml @@ -37,42 +37,42 @@ acwf: mixing_beta: 0.4 -moderate: - name: moderate - description: The protocol where input parameters bring from aiidaqe moderate protocol. Only for QE >= 6.8 - - base: # base parameters is inherit by other process - occupations: smearing - degauss: 0.01 - smearing: cold - conv_thr_per_atom: 1.0e-10 - kpoints_distance: 0.15 - - cohesive_energy: - atom_smearing: gaussian - vacuum_length: 12.0 - - phonon_frequencies: - qpoints_list: - - [0.5, 0.5, 0.5] - epsilon: false - tr2_ph: 1.0e-16 - - pressure: - scale_count: 7 - scale_increment: 0.02 - mixing_beta: 0.4 - - bands: - init_nbands_factor: 3.0 - fermi_shift: 10.0 - kpoints_distance_scf: 0.15 - kpoints_distance_bands: 0.15 - - delta: - scale_count: 7 - scale_increment: 0.02 - mixing_beta: 0.4 +#moderate: +# name: moderate +# description: The protocol where input parameters bring from aiidaqe moderate protocol. Only for QE >= 6.8 +# +# base: # base parameters is inherit by other process +# occupations: smearing +# degauss: 0.01 +# smearing: cold +# conv_thr_per_atom: 1.0e-10 +# kpoints_distance: 0.15 +# +# cohesive_energy: +# atom_smearing: gaussian +# vacuum_length: 12.0 +# +# phonon_frequencies: +# qpoints_list: +# - [0.5, 0.5, 0.5] +# epsilon: false +# tr2_ph: 1.0e-16 +# +# pressure: +# scale_count: 7 +# scale_increment: 0.02 +# mixing_beta: 0.4 +# +# bands: +# init_nbands_factor: 3.0 +# fermi_shift: 10.0 +# kpoints_distance_scf: 0.15 +# kpoints_distance_bands: 0.15 +# +# delta: +# scale_count: 7 +# scale_increment: 0.02 +# mixing_beta: 0.4 test: name: test-only diff --git a/aiida_sssp_workflow/workflows/convergence/bands.py b/aiida_sssp_workflow/workflows/convergence/bands.py index 9e9e71f0..725892c4 100644 --- a/aiida_sssp_workflow/workflows/convergence/bands.py +++ b/aiida_sssp_workflow/workflows/convergence/bands.py @@ -74,8 +74,6 @@ class ConvergenceBandsWorkChain(_BaseConvergenceWorkChain): # pylint: disable=too-many-instance-attributes - _RY_TO_EV = 13.6056980659 - _PROPERTY_NAME = "bands" _EVALUATE_WORKCHAIN = BandsWorkChain _MEASURE_OUT_PROPERTY = "eta_c" @@ -186,7 +184,7 @@ def helper_compare_result_extract_fun(self, sample_node, reference_node, **kwarg sample_band_parameters, reference_band_structure, reference_band_parameters, - smearing=orm.Float(self.ctx.degauss * self._RY_TO_EV), + smearing=orm.Float(self.ctx.degauss), fermi_shift=orm.Float(self.ctx.fermi_shift), do_smearing=orm.Bool(True), spin=orm.Bool(spin), diff --git a/aiida_sssp_workflow/workflows/evaluate/_cohesive_energy.py b/aiida_sssp_workflow/workflows/evaluate/_cohesive_energy.py index 8d77f673..70254c9c 100644 --- a/aiida_sssp_workflow/workflows/evaluate/_cohesive_energy.py +++ b/aiida_sssp_workflow/workflows/evaluate/_cohesive_energy.py @@ -122,7 +122,6 @@ def run_energy(self): self.to_context(workchain_bulk_energy=running_bulk_energy) for element, structure in self.ctx.d_element_structure.items(): - atom_inputs = self.exposed_inputs(PwBaseWorkflow, namespace="atom") atom_inputs["pw"]["structure"] = structure atom_inputs["pw"]["pseudos"] = { diff --git a/aiida_sssp_workflow/workflows/measure/__init__.py b/aiida_sssp_workflow/workflows/measure/__init__.py index f992e677..6f544beb 100644 --- a/aiida_sssp_workflow/workflows/measure/__init__.py +++ b/aiida_sssp_workflow/workflows/measure/__init__.py @@ -11,6 +11,16 @@ class _BaseMeasureWorkChain(SelfCleanWorkChain): + """Base Measure Workchain since bands measure and precision measure share same input ports""" + + # ECUT for oxygen, remember to update this if the oxygen pseudo is changed + _O_ECUTWFC = 75.0 + _O_ECUTRHO = 600.0 + + # ECUT for nitrogen, remember to update this if the nitrogen pseudo is changed + _N_ECUTWFC = 80.0 + _N_ECUTRHO = 320.0 + @classmethod def define(cls, spec): """Define the process specification.""" @@ -22,9 +32,26 @@ def define(cls, spec): help='Pseudopotential to be verified') spec.input('protocol', valid_type=orm.Str, required=True, help='The protocol which define input calculation parameters.') - spec.input('cutoff_control', valid_type=orm.Str, default=lambda: orm.Str('standard'), - help='The control protocol where define max_wfc.') + spec.input('wavefunction_cutoff', valid_type=orm.Float, required=True, help='The wavefunction cutoff.') + spec.input('charge_density_cutoff', valid_type=orm.Float, required=True, help='The charge density cutoff.') spec.input('options', valid_type=orm.Dict, required=True, help='Optional `options` to use for the `PwCalculations`.') spec.input('parallelization', valid_type=orm.Dict, required=True, help='Parallelization options for the `PwCalculations`.') + + def _get_pw_cutoff( + self, structure: orm.StructureData, ecutwfc: float, ecutrho: float + ): + """Get cutoff pair, if strcture contains oxygen or nitrogen, need + to use the max between pseudo cutoff and the O/N cutoff. + """ + elements = set(structure.get_symbols_set()) + if "O" in elements: + ecutwfc = max(ecutwfc, self._O_ECUTWFC) + ecutrho = max(ecutrho, self._O_ECUTRHO) + + if "N" in elements: + ecutwfc = max(ecutwfc, self._N_ECUTWFC) + ecutrho = max(ecutrho, self._N_ECUTRHO) + + return ecutwfc, ecutrho diff --git a/aiida_sssp_workflow/workflows/measure/bands.py b/aiida_sssp_workflow/workflows/measure/bands.py index 824c79f8..d918ddf9 100644 --- a/aiida_sssp_workflow/workflows/measure/bands.py +++ b/aiida_sssp_workflow/workflows/measure/bands.py @@ -8,7 +8,6 @@ from aiida.plugins import DataFactory from aiida_sssp_workflow.utils import ( - HIGH_DUAL_ELEMENTS, LANTHANIDE_ELEMENTS, MAGNETIC_ELEMENTS, NONMETAL_ELEMENTS, @@ -20,7 +19,6 @@ ) from aiida_sssp_workflow.workflows.common import ( get_extra_parameters_for_lanthanides, - get_pseudo_element_and_type, get_pseudo_N, ) from aiida_sssp_workflow.workflows.evaluate._bands import BandsWorkChain @@ -42,7 +40,6 @@ class BandsMeasureWorkChain(_BaseMeasureWorkChain): WorkChain to run bands measure, run without sym for distance compare and band structure along the path """ - _RY_TO_EV = 13.6056980659 @classmethod def define(cls, spec): @@ -51,7 +48,7 @@ def define(cls, spec): super().define(spec) spec.outline( - cls.init_setup, + cls.setup, if_(cls.is_magnetic_element)( cls.extra_setup_for_magnetic_element, ), @@ -64,7 +61,7 @@ def define(cls, spec): spec.expose_outputs(BandsWorkChain) - def init_setup(self): + def setup(self): """ This step contains all preparation before actaul setup, e.g. set the context of element, base_structure, base pw_parameters and pseudos. @@ -83,6 +80,10 @@ def init_setup(self): # extra setting for bands convergence self.ctx.is_metal = element not in NONMETAL_ELEMENTS + # set up the ecutwfc and ecutrho + self.ctx.ecutwfc = self.inputs.wavefunction_cutoff.value + self.ctx.ecutrho = self.inputs.charge_density_cutoff.value + def is_magnetic_element(self): """Check if the element is magnetic""" return self.ctx.element in MAGNETIC_ELEMENTS @@ -126,12 +127,6 @@ def setup_pw_parameters_from_protocol(self): self._INIT_NBANDS_FACTOR = protocol['init_nbands_factor'] self._FERMI_SHIFT = protocol['fermi_shift'] - cutoff_control = get_protocol( - category="control", name=self.inputs.cutoff_control.value - ) - self._ECUTWFC = cutoff_control["max_wfc"] - - self.ctx.ecutwfc = self._ECUTWFC self.ctx.init_nbands_factor = self._INIT_NBANDS_FACTOR self.ctx.fermi_shift = self._FERMI_SHIFT @@ -154,8 +149,6 @@ def setup_pw_parameters_from_protocol(self): }, } - self.ctx.ecutwfc = self._ECUTWFC - self.ctx.pw_parameters = update_dict(self.ctx.pw_parameters, parameters) self.logger.info( @@ -166,24 +159,12 @@ def _get_inputs(self, pseudos): """ get inputs for the bands evaluation with given pseudo """ - element, pseudo_type = get_pseudo_element_and_type(self.inputs.pseudo) - if pseudo_type in ['nc', 'sl']: - ecutrho = self.ctx.ecutwfc * 4 - else: - ecutrho = self.ctx.ecutwfc * 8 - - if element in HIGH_DUAL_ELEMENTS and pseudo_type not in ['nc', 'sl']: - ecutrho = self.ctx.ecutwfc * 18 - - if element in LANTHANIDE_ELEMENTS: - # since nitrides is used, the pseudo of N is non-NC - # The N.us.z_5.ld1.theose.v0 is used so set dual equal to 8 - ecutrho = self.ctx.ecutwfc * 8 + ecutwfc, ecutrho = self._get_pw_cutoff(self.ctx.structure, self.ctx.ecutwfc, self.ctx.ecutrho) parameters = { "SYSTEM": { - "ecutwfc": round(self.ctx.ecutwfc), - "ecutrho": round(ecutrho), + "ecutwfc": round(ecutwfc, 1), + "ecutrho": round(ecutrho, 1), }, } parameters = update_dict(parameters, self.ctx.pw_parameters) diff --git a/aiida_sssp_workflow/workflows/measure/precision.py b/aiida_sssp_workflow/workflows/measure/precision.py index 1a7babc4..9992ef94 100644 --- a/aiida_sssp_workflow/workflows/measure/precision.py +++ b/aiida_sssp_workflow/workflows/measure/precision.py @@ -135,6 +135,10 @@ def setup(self): self._setup_pseudo_and_configuration() + # set up the ecutwfc and ecutrho + self.ctx.ecutwfc = self.inputs.wavefunction_cutoff.value + self.ctx.ecutrho = self.inputs.charge_density_cutoff.value + def setup_pw_parameters_from_protocol(self): """Input validation""" # pylint: disable=invalid-name, attribute-defined-outside-init @@ -157,11 +161,6 @@ def setup_pw_parameters_from_protocol(self): if key not in clist: self.ctx.structures.pop(key) - cutoff_control = get_protocol( - category="control", name=self.inputs.cutoff_control.value - ) - self._ECUTWFC = cutoff_control["max_wfc"] - parameters = { "CONTROL": { "calculation": "scf", @@ -178,8 +177,6 @@ def setup_pw_parameters_from_protocol(self): }, } - self.ctx.ecutwfc = self._ECUTWFC - self.ctx.pw_parameters = update_dict(self.ctx.pw_parameters, parameters) self.logger.info(f"The pw parameters for EOS step is: {self.ctx.pw_parameters}") @@ -214,17 +211,13 @@ def _compute_nelectrons_of_oxide(configuration, z_O, z_X): ) def _get_inputs(self, structure, configuration): - element, pseudo_type = get_pseudo_element_and_type(self.inputs.pseudo) - + """Set pw parameters and pseudos based on configuration and structure""" if configuration in self._OXIDE_CONFIGURATIONS: # pseudos for oxides pseudos = self.ctx.pseudos_oxide pw_parameters = self.ctx.pw_parameters kpoints_distance = self.ctx.kpoints_distance - # Since non-NC oxygen pseudo is used - ecutrho = self.ctx.ecutwfc * 8 - # need also increase nbands for Rare-earth oxides. # See https://github.com/aiidateam/aiida-sssp-workflow/issues/161 # This is not easy to be set in the rare-earth step since it will @@ -249,10 +242,6 @@ def _get_inputs(self, structure, configuration): pseudos = self.ctx.pseudos_unary pw_parameters = self.ctx.pw_parameters kpoints_distance = self.ctx.kpoints_distance - if pseudo_type in ["nc", "sl"]: - ecutrho = self.ctx.ecutwfc * 4 - else: - ecutrho = self.ctx.ecutwfc * 8 if configuration == "GS" and self.ctx.element in MAGNETIC_ELEMENTS: # specific setting for magnetic elements gs since mag on @@ -272,11 +261,6 @@ def _get_inputs(self, structure, configuration): pseudos = self.ctx.pseudos_magnetic pw_parameters = update_dict(self.ctx.pw_parameters, pw_magnetic_parameters) - if pseudo_type in ["nc", "sl"]: - ecutrho = self.ctx.ecutwfc * 4 - else: - ecutrho = self.ctx.ecutwfc * 8 - if configuration == "RE": # pseudos for nitrides pseudo_N = get_pseudo_N() @@ -302,16 +286,13 @@ def _get_inputs(self, structure, configuration): # sparse kpoints, we use tetrahedra occupation kpoints_distance = self.ctx.kpoints_distance + 0.1 - # Since non-NC nitrogen pseudo is used - ecutrho = self.ctx.ecutwfc * 8 - - if element in HIGH_DUAL_ELEMENTS and pseudo_type not in ["nc", "sl"]: - ecutrho = self.ctx.ecutwfc * 18 - + ecutwfc, ecutrho = self._get_pw_cutoff( + structure, self.ctx.ecutwfc, self.ctx.ecutrho + ) parameters = { "SYSTEM": { - "ecutwfc": round(self.ctx.ecutwfc), - "ecutrho": round(ecutrho), + "ecutwfc": round(ecutwfc, 1), + "ecutrho": round(ecutrho, 1), }, } parameters = update_dict(parameters, pw_parameters) diff --git a/aiida_sssp_workflow/workflows/verifications.py b/aiida_sssp_workflow/workflows/verifications.py index 22f79992..cd5a4ec5 100644 --- a/aiida_sssp_workflow/workflows/verifications.py +++ b/aiida_sssp_workflow/workflows/verifications.py @@ -3,9 +3,6 @@ All in one verification workchain """ # pylint: disable=cyclic-import - -from typing import Optional - from aiida import orm from aiida.engine import ToContext, if_ from aiida.engine.processes.exit_code import ExitCode @@ -13,7 +10,6 @@ from aiida.plugins import DataFactory, WorkflowFactory from aiida_sssp_workflow.workflows import SelfCleanWorkChain -from aiida_sssp_workflow.workflows.common import invalid_cache, operate_calcjobs from aiida_sssp_workflow.workflows.convergence import _BaseConvergenceWorkChain from aiida_sssp_workflow.workflows.convergence.caching import ( _CachingConvergenceWorkChain, @@ -89,10 +85,14 @@ def define(cls, spec): exclude=['code', 'pseudo', 'options', 'parallelization', 'clean_workdir']) spec.input('pw_code', valid_type=orm.AbstractCode, help='The `pw.x` code use for the `PwCalculation`.') - spec.input('ph_code', valid_type=orm.AbstractCode, + spec.input('ph_code', valid_type=orm.AbstractCode, required=False, help='The `ph.x` code use for the `PhCalculation`.') spec.input('pseudo', valid_type=UpfData, required=True, help='Pseudopotential to be verified') + spec.input('wavefunction_cutoff', valid_type=orm.Float, required=False, default=lambda: orm.Float(100.0), + help='The wavefunction cutoff for the Measure properties.') + spec.input('charge_density_cutoff', valid_type=orm.Float, required=False, default=lambda: orm.Float(800.0), + help='The charge density cutoff for the Measure properties.') spec.input('label', valid_type=orm.Str, required=False, help='label store for display as extra attributes.') spec.input('properties_list', valid_type=orm.List, @@ -209,6 +209,8 @@ def init_setup(self): # 3. pressue # 4. delta # 5. bands distance + self.ctx.convergence_inputs = dict() + convergence_inputs = self.exposed_inputs( _BaseConvergenceWorkChain, namespace="convergence" ) @@ -219,28 +221,32 @@ def init_setup(self): convergence_inputs["clean_workdir"] = self.inputs.clean_workdir + for prop in ["cohesive_energy", "delta", "pressure"]: + self.ctx.convergence_inputs[prop] = convergence_inputs.copy() + # Here, the shallow copy can be used since the type of convergence_inputs # is AttributesDict. # The deepcopy can't be used, since it will create new data node. - inputs_phonon_frequencies = convergence_inputs.copy() - inputs_phonon_frequencies.pop("code", None) - inputs_phonon_frequencies["pw_code"] = self.inputs.pw_code - inputs_phonon_frequencies["ph_code"] = self.inputs.ph_code - - # For phonon frequencies and bands convergence workflow, the clean dir is taken care by the the finalize step - # of the verification workflow. - inputs_bands = convergence_inputs.copy() - - inputs_phonon_frequencies["clean_workdir"] = orm.Bool(False) - inputs_bands["clean_workdir"] = orm.Bool(False) - - self.ctx.convergence_inputs = { - "cohesive_energy": convergence_inputs.copy(), - "phonon_frequencies": inputs_phonon_frequencies, - "pressure": convergence_inputs.copy(), - "delta": convergence_inputs.copy(), - "bands": convergence_inputs.copy(), - } + if "convergence.phonon_frequencies" in self.ctx.properties_list: + inputs_phonon_frequencies = convergence_inputs.copy() + inputs_phonon_frequencies.pop("code", None) + inputs_phonon_frequencies["pw_code"] = self.inputs.pw_code + inputs_phonon_frequencies["ph_code"] = self.inputs.ph_code + inputs_phonon_frequencies["clean_workdir"] = orm.Bool( + False + ) # For phonon frequencies convergence workflow, the clean dir is taken care by the the finalize step of the verification workflow. + + self.ctx.convergence_inputs[ + "phonon_frequencies" + ] = inputs_phonon_frequencies + + if "convergence.bands" in self.ctx.properties_list: + inputs_bands = convergence_inputs.copy() + inputs_bands["clean_workdir"] = orm.Bool( + False + ) # For bands convergence workflow, the clean dir is taken care by the the finalize step of the verification workflow. + + self.ctx.convergence_inputs["bands"] = inputs_bands # Caching inputs setting # The running strategy of caching is: @@ -255,10 +261,10 @@ def init_setup(self): ) # shouldn't clean until last, default of _caching but do it here explicitly # to collect workchains in a dict - self.ctx.workchains = {} + self.ctx.workchains = dict() # For store the finished_ok workflow - self.ctx.finished_ok_wf = {} + self.ctx.finished_ok_wf = dict() def inspect_measure(self): """Inspect delta measure results""" diff --git a/pseudo_parser/upf_parser/__init__.py b/pseudo_parser/upf_parser/__init__.py index 07e1f25c..ac83c2b2 100644 --- a/pseudo_parser/upf_parser/__init__.py +++ b/pseudo_parser/upf_parser/__init__.py @@ -37,7 +37,6 @@ def parse_element(content: str): :return: the symbol of the element following the IUPAC naming standard. """ for regex in [REGEX_ELEMENT_V2, REGEX_ELEMENT_V1]: - match = regex.search(content) if match: @@ -52,7 +51,6 @@ def parse_z_valence(content: str) -> int: :return: the Z valence. """ for regex in [REGEX_Z_VALENCE_V2, REGEX_Z_VALENCE_V1]: - match = regex.search(content) if match: @@ -82,7 +80,6 @@ def parse_pseudo_type(content: str) -> str: :return: the pseudo type str. """ for regex in [REGEX_PSEUDO_TYPE_V2, REGEX_PSEUDO_TYPE_V1]: - match = regex.search(content) if match: