def test_port_names_overlapping_mutable_mapping_methods(self): # pylint: disable=invalid-name """Check that port names take precedence over `collections.MutableMapping` methods. The `ProcessBuilderNamespace` is a `collections.MutableMapping` but since the port names are made accessible as attributes, they can overlap with some of the mappings builtin methods, e.g. `values()`, `items()` etc. The port names should take precendence in this case and if one wants to access the mapping methods one needs to cast the builder to a dictionary first.""" builder = ExampleWorkChain.get_builder() # The `values` method is obscured by a port that also happens to be called `values`, so calling it should raise with self.assertRaises(TypeError): builder.values() # pylint: disable=not-callable # However, we can assign a node to it builder.values = orm.Int(2) # Calling the attribute `values` will then actually try to call the node, which should raise with self.assertRaises(TypeError): builder.values() # pylint: disable=not-callable # Casting the builder to a dict, *should* then make `values` callable again self.assertIn(orm.Int(2), dict(builder).values()) # The mapping methods should not be auto-completed, i.e. not in the values returned by calling `dir` for method in [method for method in dir(MutableMapping) if method != 'values']: self.assertNotIn(method, dir(builder)) # On the other hand, all the port names *should* be present for port_name in ExampleWorkChain.spec().inputs.keys(): self.assertIn(port_name, dir(builder)) # The `update` method is implemented, but prefixed with an underscore to not block the name for a port builder.update({'boolean': orm.Bool(False)}) self.assertEqual(builder.boolean, orm.Bool(False))
def prepare_kgrids(self): """Generate the complementary kpoints grids (FULL - CROP) & CROP""" inputs = { 'structure': self.ctx.current_structure, # 'distance': self.inputs.kpoints_distance, 'force_parity': orm.Bool(False), 'metadata': {'call_link_label': 'create_kpoints_from_distance'} } inputs['distance'] = self.inputs.nscf_full.kpoints_distance k_full = create_kpoints_from_distance(**inputs) inputs['distance'] = self.inputs.nscf_crop.kpoints_distance k_crop = create_kpoints_from_distance(**inputs) nk_full = len(k_full.get_kpoints_mesh(print_list=True)) nk_crop = len(k_crop.get_kpoints_mesh(print_list=True)) self.ctx.kpoint_full = kpt_crop(k_full, self.inputs.crop_centers, self.inputs.crop_radii, orm.Bool(True)) self.ctx.kpoint_crop = kpt_crop(k_crop, self.inputs.crop_centers, self.inputs.crop_radii, orm.Bool(False)) self.ctx.kpoint_full_weight = self.ctx.kpoint_full self.ctx.kpoint_crop_weight = self.ctx.kpoint_crop nka_full = len(self.ctx.kpoint_full.get_kpoints()) nka_crop = len(self.ctx.kpoint_crop.get_kpoints()) nka_full_weight = self.ctx.kpoint_full.get_array('weights').sum() nka_crop_weight = self.ctx.kpoint_crop.get_array('weights').sum() self.report('{}/{} k-points anti-cropped from FULL grid. tot_weight={}'.format(nka_full, nk_full, nka_full_weight)) self.report('{}/{} k-points cropped from CROP grid. tot_weight={}'.format(nka_crop, nk_crop, nka_crop_weight)) self.out('kpoints_full', self.ctx.kpoint_full) self.out('kpoints_crop', self.ctx.kpoint_crop)
def define(cls, spec): """Define the process specification.""" # yapf: disable super().define(spec) # spec.expose_inputs(PwRelaxWorkChain, namespace='relax', exclude=('clean_workdir', 'structure'), # namespace_options={'required': False, 'populate_defaults': False, # 'help': 'Inputs for the `PwRelaxWorkChain`, if not specified at all, the relaxation step is skipped.'}) spec.expose_inputs(PwBaseWorkChain, namespace='scf', exclude=('clean_workdir', 'pw.structure'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the SCF calculation.'}) spec.expose_inputs(PwBaseWorkChain, namespace='nscf', exclude=('clean_workdir', 'pw.structure'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the NSCF calculation.'}) spec.expose_inputs(Pw2gwCalc, namespace='pw2gw', exclude=('parent_folder', ), namespace_options={'help': 'Inputs for the `DosCalculation` for the DOS calculation.'}) spec.input('parent_folder', valid_type=orm.RemoteData, required=False) spec.input('structure', valid_type=orm.StructureData, help='The inputs structure.') # spec.input('kpoints_distance', valid_type=orm.Float, required=False, # help='The minimum desired distance in 1/Å between k-points in reciprocal space. The explicit k-points will ' # 'be generated automatically by a calculation function based on the input structure.') spec.input('nbands_factor', valid_type=orm.Float, default=lambda: orm.Float(1.5), help='The number of bands for the BANDS calculation is that used for the SCF multiplied by this factor.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') spec.outline( cls.setup, if_(cls.should_do_scf)( cls.run_scf, cls.inspect_scf, ), cls.run_nscf, cls.inspect_nscf, cls.run_pw2gw, cls.inspect_pw2gw, cls.results, ) spec.exit_code(201, 'ERROR_INVALID_INPUT_NUMBER_OF_BANDS', message='Cannot specify both `nbands_factor` and `bands.pw.parameters.SYSTEM.nbnd`.') spec.exit_code(202, 'ERROR_INVALID_INPUT_KPOINTS', message='Cannot specify both `bands_kpoints` and `bands_kpoints_distance`.') spec.exit_code(401, 'ERROR_SUB_PROCESS_FAILED_RELAX', message='The PwRelaxWorkChain sub process failed') spec.exit_code(402, 'ERROR_SUB_PROCESS_FAILED_SCF', message='The scf PwBasexWorkChain sub process failed') spec.exit_code(403, 'ERROR_SUB_PROCESS_FAILED_NSCF', message='The bands PwBasexWorkChain sub process failed') spec.exit_code(405, 'ERROR_SUB_PROCESS_FAILED_PW2GW', message='The pw2gw Pw2gwCalculation sub process failed') spec.output('scf_remote_folder', valid_type=orm.RemoteData, required=False) spec.output('nscf_remote_folder', valid_type=orm.RemoteData) spec.output('scf_parameters', valid_type=orm.Dict, required=False, help='The output parameters of the SCF `PwBaseWorkChain`.') spec.output('nscf_parameters', valid_type=orm.Dict, help='The output parameters of the NSCF `PwBaseWorkChain`.') spec.output('pw2gw_parameters', valid_type=orm.Dict, help='The output parameters of the PW2GW calculation.') spec.output('eps', valid_type=orm.ArrayData)
def define(cls, spec): super(BigDFTRelaxWorkChain, cls).define(spec) spec.expose_inputs(BigDFTBaseWorkChain, exclude=['parameters', 'extra_retrieved_files']) spec.input('parameters', valid_type=BigDFTParameters, required=False, default=lambda: orm.Dict(), help='param dictionary') spec.input('extra_retrieved_files', valid_type=List, required=False, help='', default=lambda: List()) spec.input('relax.perform', valid_type=orm.Bool, required=False, default=lambda: orm.Bool(True), help='perform relaxation') spec.input('relax.algo', valid_type=orm.Str, default=lambda: orm.Str('FIRE'), help='algorithm to use during relaxation') spec.input('relax.threshold_forces', valid_type=orm.Float, required=False, default=lambda: orm.Float(0.0), help='energy cutoff value, in ev/Ang') spec.input('relax.steps', valid_type=orm.Int, required=False, default=lambda: orm.Int(50), help='number of relaxation steps to perform.') spec.outline( cls.relax, cls.results, ) spec.expose_outputs(BigDFTBaseWorkChain) spec.output('relaxed_structure', valid_type=StructureData, required=False) spec.output('forces', valid_type=ArrayData, required=False) spec.output('total_energy', valid_type=orm.Float, required=False) spec.exit_code(101, 'ERROR_FAILED_RELAX', 'Subprocess failed for relaxation')
def test_output_validation_error(self): """Test that a process is marked as failed if its output namespace validation fails.""" class TestProcess1(Process): """Defining a new TestProcess class for testing.""" _node_class = orm.WorkflowNode @classmethod def define(cls, spec): super().define(spec) spec.input('add_outputs', valid_type=orm.Bool, default=lambda: orm.Bool(False)) spec.output_namespace('integer.namespace', valid_type=orm.Int, dynamic=True) spec.output('required_string', valid_type=orm.Str, required=True) def run(self): if self.inputs.add_outputs: self.out('required_string', orm.Str('testing').store()) self.out('integer.namespace.two', orm.Int(2).store()) _, node = run_get_node(TestProcess1) # For default inputs, no outputs will be attached, causing the validation to fail at the end so an internal # exit status will be set, which is a negative integer self.assertTrue(node.is_finished) self.assertFalse(node.is_finished_ok) self.assertEqual(node.exit_status, TestProcess1.exit_codes.ERROR_MISSING_OUTPUT.status) self.assertEqual(node.exit_message, TestProcess1.exit_codes.ERROR_MISSING_OUTPUT.message) # When settings `add_outputs` to True, the outputs should be added and validation should pass _, node = run_get_node(TestProcess1, add_outputs=orm.Bool(True)) self.assertTrue(node.is_finished) self.assertTrue(node.is_finished_ok) self.assertEqual(node.exit_status, 0)
def validate_kpoints(self): """Validate the inputs related to k-points. Either an explicit `KpointsData` with given mesh/path, or a desired k-points distance should be specified. In the case of the latter, the `KpointsData` will be constructed for the input `StructureData` using the `create_kpoints_from_distance` calculation function. """ if all([ key not in self.inputs for key in ['kpoints', 'kpoints_distance'] ]): return self.exit_codes.ERROR_INVALID_INPUT_KPOINTS try: kpoints = self.inputs.kpoints except AttributeError: inputs = { 'structure': self.inputs.pw.structure, 'distance': self.inputs.kpoints_distance, 'force_parity': self.inputs.get('kpoints_force_parity', orm.Bool(False)), 'metadata': { 'call_link_label': 'create_kpoints_from_distance' } } kpoints = create_kpoints_from_distance(**inputs) # pylint: disable=unexpected-keyword-arg self.ctx.inputs.kpoints = kpoints
def define(cls, spec): """Define the process specification.""" # yapf: disable super().define(spec) spec.expose_inputs(PwRelaxWorkChain, namespace='relax', exclude=('clean_workdir', 'structure'), namespace_options={'required': False, 'populate_defaults': False, 'help': 'Inputs for the `PwRelaxWorkChain`, if not specified at all, the relaxation step is skipped.'}) spec.expose_inputs(PwBaseWorkChain, namespace='scf', exclude=('clean_workdir', 'pw.structure'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the SCF calculation.'}) spec.expose_inputs(PwBaseWorkChain, namespace='bands', exclude=('clean_workdir', 'pw.structure', 'pw.kpoints', 'pw.kpoints_distance'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the BANDS calculation.'}) spec.input('structure', valid_type=orm.StructureData, help='The inputs structure.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') spec.input('nbands_factor', valid_type=orm.Float, required=False, help='The number of bands for the BANDS calculation is that used for the SCF multiplied by this factor.') spec.input('bands_kpoints', valid_type=orm.KpointsData, required=False, help='Explicit kpoints to use for the BANDS calculation. Specify either this or `bands_kpoints_distance`.') spec.input('bands_kpoints_distance', valid_type=orm.Float, required=False, help='Minimum kpoints distance for the BANDS calculation. Specify either this or `bands_kpoints`.') spec.inputs.validator = validate_inputs spec.outline( cls.setup, if_(cls.should_run_relax)( cls.run_relax, cls.inspect_relax, ), if_(cls.should_run_seekpath)( cls.run_seekpath, ), cls.run_scf, cls.inspect_scf, cls.run_bands, cls.inspect_bands, cls.results, ) spec.exit_code(201, 'ERROR_INVALID_INPUT_NUMBER_OF_BANDS', message='Cannot specify both `nbands_factor` and `bands.pw.parameters.SYSTEM.nbnd`.') spec.exit_code(202, 'ERROR_INVALID_INPUT_KPOINTS', message='Cannot specify both `bands_kpoints` and `bands_kpoints_distance`.') spec.exit_code(401, 'ERROR_SUB_PROCESS_FAILED_RELAX', message='The PwRelaxWorkChain sub process failed') spec.exit_code(402, 'ERROR_SUB_PROCESS_FAILED_SCF', message='The scf PwBasexWorkChain sub process failed') spec.exit_code(403, 'ERROR_SUB_PROCESS_FAILED_BANDS', message='The bands PwBasexWorkChain sub process failed') spec.output('primitive_structure', valid_type=orm.StructureData, required=False, help='The normalized and primitivized structure for which the bands are computed.') spec.output('seekpath_parameters', valid_type=orm.Dict, required=False, help='The parameters used in the SeeKpath call to normalize the input or relaxed structure.') spec.output('scf_parameters', valid_type=orm.Dict, help='The output parameters of the SCF `PwBaseWorkChain`.') spec.output('band_parameters', valid_type=orm.Dict, help='The output parameters of the BANDS `PwBaseWorkChain`.') spec.output('band_structure', valid_type=orm.BandsData, help='The computed band structure.')
def define(cls, spec): """Define the process specification.""" # yapf: disable super(PwBandStructureWorkChain, cls).define(spec) spec.input('code', valid_type=orm.Code, help='The `pw.x` code to use for the `PwCalculations`.') spec.input('structure', valid_type=orm.StructureData, help='The input structure.') spec.input('options', valid_type=orm.Dict, required=False, help='Optional `options` to use for the `PwCalculations`.') spec.input('protocol', valid_type=orm.Dict, default=lambda: orm.Dict(dict={'name': 'theos-ht-1.0'}), help='The protocol to use for the workchain.', validator=validate_protocol) # MODIFIED spec.input('pseudo_family', valid_type=orm.Str, required=False, help='[Deprecated: use `pw.pseudos` instead] An alternative to specifying the pseudo potentials manually in' ' `pseudos`: one can specify the name of an existing pseudo potential family and the work chain will ' 'generate the pseudos automatically based on the input structure.') spec.input('set_2d_mesh', valid_type=orm.Bool, default=lambda: orm.Bool( False), help='Set the mesh to [x,x,1]') spec.expose_outputs(PwBandsWorkChain) spec.outline( cls.setup_protocol, cls.setup_parameters, cls.run_bands, cls.results, ) spec.exit_code(201, 'ERROR_INVALID_INPUT_UNRECOGNIZED_KIND', message='Input `StructureData` contains an unsupported kind.') spec.exit_code(401, 'ERROR_SUB_PROCESS_FAILED_BANDS', message='The `PwBandsWorkChain` sub process failed.') spec.output('primitive_structure', valid_type=orm.StructureData) spec.output('seekpath_parameters', valid_type=orm.Dict) spec.output('scf_parameters', valid_type=orm.Dict) spec.output('band_parameters', valid_type=orm.Dict) spec.output('band_structure', valid_type=orm.BandsData)
def generate_inputs_base(protocol: Dict, code: orm.Code, structure: StructureData, sssp_family: SsspFamily, override: Dict[str, Any] = None) -> Dict[str, Any]: """Generate the inputs for the `PwBaseWorkChain` for a given code, structure and pseudo potential family. :param protocol: the dictionary with protocol inputs. :param code: the code to use. :param structure: the input structure. :param sssp_family: the pseudo potential family. :param override: a dictionary to override specific inputs. :return: the fully defined input dictionary. """ protocol['pw'] = generate_inputs_calculation(protocol['pw'], code, structure, sssp_family, override.get('pw', {})) merged = recursive_merge(protocol, override or {}) dictionary = { 'pw': merged['pw'], 'kpoints_distance': orm.Float(merged['kpoints_distance']), 'kpoints_force_parity': orm.Bool(merged['kpoints_force_parity']), } return dictionary
def parse(self, **kwargs): """Parse the contents of the output files stored in the `retrieved` output node.""" try: with self.retrieved.open(self.node.get_option("output_filename"), "r") as handle: result = handle.read().strip() except OSError: return self.exit_codes.ERROR_READING_OUTPUT_FILE try: with self.retrieved.open(self.node.get_option("payload_filename"), "r") as handle: pass except OSError: return self.exit_codes.ERROR_READING_PAYLOAD_FILE self.out("result", orm.Bool(result == "success")) self.out( "out_dict", orm.Dict( dict={ f"output_key_{i}": f"value_{i}" for i in range(self.node.get_option("output_dict_size")) }), ) array = orm.ArrayData() array.set_array("example", np.ones(self.node.get_option("output_array_size"))) self.out("out_array", array) if not result == "success": return self.exit_codes.ERROR_FAILED_OUTPUT
def test_sequential_not_conv(aiida_profile, generate_workchain_seq_converger, generate_wc_job_node, fixture_localhost): """ We test here the SiestaSequentialConverger, in the case a Converger fails to converge. """ from aiida.common.extendeddicts import AttributeDict process = generate_workchain_seq_converger() process.initialize() assert process.ctx.iteration_keys == ('iterate_over', ) inputs = {"iterate_over": orm.Dict(dict={"s": [2, 2]})} convergerwc = generate_wc_job_node("siesta.converger", fixture_localhost, inputs) convergerwc.set_process_state(ProcessState.FINISHED) convergerwc.set_exit_status(ExitCode(0).status) out_conv = orm.Bool(False) out_conv.store() out_conv.add_incoming(convergerwc, link_type=LinkType.RETURN, link_label='converged') process.ctx.last_inputs = AttributeDict({"parameters": {"yo": "yo"}}) process._analyze_process(convergerwc) assert process.ctx.already_converged == {} assert "parameters" in process.ctx.last_inputs
def test_valid_cache_hook(self): """ Test that the is_valid_cache behavior can be specified from the method in the Process sub-class. """ # Sanity check that caching works when the hook returns True. with enable_caching(): _, node1 = run_get_node(test_processes.IsValidCacheHook) _, node2 = run_get_node(test_processes.IsValidCacheHook) self.assertEqual(node1.get_extra('_aiida_hash'), node2.get_extra('_aiida_hash')) self.assertIn('_aiida_cached_from', node2.extras) with enable_caching(): _, node3 = run_get_node(test_processes.IsValidCacheHook, not_valid_cache=orm.Bool(True)) _, node4 = run_get_node(test_processes.IsValidCacheHook, not_valid_cache=orm.Bool(True)) self.assertEqual(node3.get_extra('_aiida_hash'), node4.get_extra('_aiida_hash')) self.assertNotIn('_aiida_cached_from', node4.extras)
def define(cls, spec): """Define the process specification.""" # yapf: disable super().define(spec) spec.expose_inputs(PwBaseWorkChain, namespace='base', exclude=('clean_workdir', 'pw.structure', 'pw.parent_folder'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the main relax loop.'}) spec.expose_inputs(PwBaseWorkChain, namespace='base_final_scf', exclude=('clean_workdir', 'pw.structure', 'pw.parent_folder'), namespace_options={'required': False, 'populate_defaults': False, 'help': 'Inputs for the `PwBaseWorkChain` for the final scf.'}) spec.input('structure', valid_type=orm.StructureData, help='The inputs structure.') spec.input('final_scf', valid_type=orm.Bool, default=lambda: orm.Bool(False), validator=validate_final_scf, help='If `True`, a final SCF calculation will be performed on the successfully relaxed structure.') spec.input('relaxation_scheme', valid_type=orm.Str, required=False, validator=validate_relaxation_scheme, help='The relaxation scheme to use: choose either `relax` or `vc-relax` for variable cell relax.') spec.input('relax_type', valid_type=orm.Str, default=lambda: orm.Str(RelaxType.ATOMS_CELL.value), validator=validate_relax_type, help='The relax type to use: should be a value of the enum ``common.types.RelaxType``.') spec.input('meta_convergence', valid_type=orm.Bool, default=lambda: orm.Bool(True), help='If `True` the workchain will perform a meta-convergence on the cell volume.') spec.input('max_meta_convergence_iterations', valid_type=orm.Int, default=lambda: orm.Int(5), help='The maximum number of variable cell relax iterations in the meta convergence cycle.') spec.input('volume_convergence', valid_type=orm.Float, default=lambda: orm.Float(0.01), help='The volume difference threshold between two consecutive meta convergence iterations.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') spec.outline( cls.setup, while_(cls.should_run_relax)( cls.run_relax, cls.inspect_relax, ), if_(cls.should_run_final_scf)( cls.run_final_scf, cls.inspect_final_scf, ), cls.results, ) spec.exit_code(401, 'ERROR_SUB_PROCESS_FAILED_RELAX', message='the relax PwBaseWorkChain sub process failed') spec.exit_code(402, 'ERROR_SUB_PROCESS_FAILED_FINAL_SCF', message='the final scf PwBaseWorkChain sub process failed') spec.expose_outputs(PwBaseWorkChain, exclude=('output_structure',)) spec.output('output_structure', valid_type=orm.StructureData, required=False, help='The successfully relaxed structure, unless `relax_type is RelaxType.NONE`.')
def submit_workchain(xsf_file, protocol, only_valence, do_disentanglement, do_mlwf, retrieve_hamiltonian, group_name): codes = check_codes() group_name = update_group_name(group_name, only_valence, do_disentanglement, do_mlwf) if isinstance(xsf_file, orm.StructureData): structure = xsf_file else: structure = read_structure(xsf_file) controls = { 'retrieve_hamiltonian': orm.Bool(retrieve_hamiltonian), 'only_valence': orm.Bool(only_valence), 'do_disentanglement': orm.Bool(do_disentanglement), 'do_mlwf': orm.Bool(do_mlwf) } if only_valence: print("Running only_valence/insulating for {}".format( structure.get_formula())) else: print("Running with conduction bands for {}".format( structure.get_formula())) wannier90_workchain_parameters = { "code": { 'pw': codes['pw_code'], 'pw2wannier90': codes['pw2wannier90_code'], 'projwfc': codes['projwfc_code'], 'wannier90': codes['wannier90_code'] }, "protocol": orm.Dict(dict={'name': protocol}), "structure": structure, "controls": controls } workchain = submit(Wannier90BandsWorkChain, **wannier90_workchain_parameters) add_to_group(workchain, group_name) print_help(workchain, structure) return workchain.pk
def submit_workchain(structure, daemon, protocol, parameters, pseudo_family, num_machines, num_mpiprocs_per_machine=4, set_2d_mesh=False): print("running dft band structure calculation for {}".format( structure.get_formula())) # Set custom pseudo modifiers = {'parameters': parameters} """ if pseudo_family is not None: from aiida_quantumespresso.utils.protocols.pw import _load_pseudo_metadata pseudo_data = _load_pseudo_metadata(pseudo_family) modifiers.update({'pseudo': 'custom', 'pseudo_data': pseudo_data}) """ # if pseudo_family is not None: # from aiida_quantumespresso.utils.pseudopotential import get_pseudos_from_structure # pseudo_data = get_pseudos_from_structure(structure, pseudo_family) # modifiers.update({'pseudo': 'custom', 'pseudo_data': pseudo_data}) # Submit the DFT bands workchain pwbands_workchain_parameters = { 'code': code, 'structure': structure, 'protocol': orm.Dict(dict={ 'name': protocol, 'modifiers': modifiers }), 'options': orm.Dict( dict={ 'resources': { 'num_machines': num_machines, 'num_mpiprocs_per_machine': num_mpiprocs_per_machine }, 'max_wallclock_seconds': 3600 * 5, 'withmpi': True, }), 'set_2d_mesh': orm.Bool(set_2d_mesh) } if pseudo_family is not None: pwbands_workchain_parameters['pseudo_family'] = orm.Str(pseudo_family) if daemon: dft_workchain = submit(PwBandStructureWorkChain, **pwbands_workchain_parameters) else: from aiida.engine import run_get_pk dft_workchain = run_get_pk(PwBandStructureWorkChain, **pwbands_workchain_parameters) return dft_workchain
def get_builder_from_protocol(cls, code, structure, protocol=None, overrides=None, **kwargs): """Return a builder prepopulated with inputs selected according to the chosen protocol. :param code: the ``Code`` instance configured for the ``quantumespresso.pw`` plugin. :param structure: the ``StructureData`` instance to use. :param protocol: protocol to use, if not specified, the default will be used. :param overrides: optional dictionary of inputs to override the defaults of the protocol. :param kwargs: additional keyword arguments that will be passed to the ``get_builder_from_protocol`` of all the sub processes that are called by this workchain. :return: a process builder instance with all inputs defined ready for launch. """ args = (code, structure, protocol) inputs = cls.get_protocol_inputs(protocol, overrides) builder = cls.get_builder() base = PwBaseWorkChain.get_builder_from_protocol(*args, overrides=inputs.get( 'base', None), **kwargs) base_final_scf = PwBaseWorkChain.get_builder_from_protocol( *args, overrides=inputs.get('base_final_scf', None), **kwargs) base['pw'].pop('structure', None) base.pop('clean_workdir', None) base_final_scf['pw'].pop('structure', None) base_final_scf.pop('clean_workdir', None) builder.base = base builder.base_final_scf = base_final_scf builder.structure = structure builder.clean_workdir = orm.Bool(inputs['clean_workdir']) builder.max_meta_convergence_iterations = orm.Int( inputs['max_meta_convergence_iterations']) builder.meta_convergence = orm.Bool(inputs['meta_convergence']) builder.relax_type = orm.Str(inputs['relax_type']) builder.volume_convergence = orm.Float(inputs['volume_convergence']) return builder
def get_inputs_and_processclass_from_extras(self, extras_values: ty.Tuple[str]): """Construct the inputs and get the process class from the values of the uniquely identifying extras.""" structure = self._get_structure_from_extras(extras_values) pseudos = self._pseudo_family.get_pseudos(structure=structure) ecutwfc, ecutrho = self._pseudo_family.get_recommended_cutoffs( structure=structure) metadata = { 'options': { 'resources': { 'num_machines': 1, 'num_mpiprocs_per_machine': 1 }, 'max_wallclock_seconds': 2 * 60, 'withmpi': True } } inputs = { 'clean_workdir': orm.Bool(True), 'kpoints_distance': orm.Float(0.25), 'pw': { 'structure': structure, 'metadata': metadata, 'code': self._code, 'pseudos': pseudos, 'parameters': orm.Dict( dict={ 'CONTROL': { 'calculation': 'scf', 'verbosity': 'low' }, 'SYSTEM': { 'ecutwfc': ecutwfc, 'ecutrho': ecutrho, 'nosym': False, 'occupations': 'smearing', 'smearing': 'gaussian', 'degauss': 0.5 / CONSTANTS.ry_to_ev }, 'ELECTRONS': { 'conv_thr': 1e-8, 'mixing_beta': 4e-1, 'electron_maxstep': 80, } }) } } return inputs, self._process_class
def define(cls, spec): super().define(spec) spec.input('add_outputs', valid_type=orm.Bool, default=lambda: orm.Bool(False)) spec.output_namespace('integer.namespace', valid_type=orm.Int, dynamic=True) spec.output('required_string', valid_type=orm.Str, required=True)
def crop_kpoints(structure, kpt_data, centers, radius): """Crop a given set of k-points `kpt_data` that are within a spherical radius `r` from a set of centers `centers`. :param structure: aiida.orm.StructureData used to get the cell of the material. :param kpt_data: aiida.orm.KpointsData to crop. :param centers: aiida.orm.ArrayData containing an array named `centers`. Each element of `centers` is used as the center of a spherical cropping. :param radius: radius of the sphere cropping. :return: aiida.orm.KpointsData node containing the cropped kpoints """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(kpt_data, orm.KpointsData): raise InputValidationError( 'Invalide type {} for parameter `kpt_data`'.format(type(kpt_data))) if not isinstance(centers, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `centers`'.format(type(centers))) if not isinstance(radius, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `radius`'.format(type(radius))) centers = centers.get_array('centers') if len(centers.shape) != 2 or centers.shape[1] != 3: raise InputValidationError( 'Invalide shape {} for array `centers`. Expected (*,3)'.format( centers.shape)) r = radius.value cell = np.array(structure.cell) recipr = recipr_base(cell) try: kpt_cryst = np.array(kpt_data.get_kpoints_mesh(print_list=True)) except MemoryError: return orm.Bool(False) kpt_cart = np.dot(kpt_cryst, recipr) c_cryst = centers c_cart = np.dot(c_cryst, recipr) kpt_cart = KDTree(kpt_cart) centers = KDTree(c_cart) query = kpt_cart.query_ball_tree(centers, r=r) where = [n for n, l in enumerate(query) if len(l)] new = orm.KpointsData() new.set_kpoints(kpt_cryst[where]) return new
def define(cls, spec): # yapf: disable super().define(spec) spec.input('max_iterations', valid_type=orm.Int, default=lambda: orm.Int(3), help='Maximum number of iterations the work chain will restart the calculation to finish successfully.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') spec.exit_code(101, 'ERROR_MAXIMUM_ITERATIONS_EXCEEDED', message='The maximum number of iterations was exceeded.') spec.exit_code(102, 'ERROR_SECOND_CONSECUTIVE_UNHANDLED_FAILURE', message='The calculation failed for an unknown reason, twice in a row.')
def run_bands(self): """Run the `PwBandsWorkChain` to compute the band structure.""" def get_common_inputs(): """Return the dictionary of inputs to be used as the basis for each `PwBaseWorkChain`.""" protocol, protocol_modifiers = self._get_protocol() checked_pseudos = protocol.check_pseudos( modifier_name=protocol_modifiers.get('pseudo', None), pseudo_data=protocol_modifiers.get('pseudo_data', None) ) known_pseudos = checked_pseudos['found'] inputs = AttributeDict({ 'pw': { 'code': self.inputs.code, 'pseudos': get_pseudos_from_dict(self.inputs.structure, known_pseudos), 'parameters': self.ctx.parameters, 'metadata': {}, } }) if 'options' in self.inputs: inputs.pw.metadata.options = self.inputs.options.get_dict() else: inputs.pw.metadata.options = get_default_options(with_mpi=True) return inputs inputs = AttributeDict({ 'structure': self.inputs.structure, 'relax': { 'base': get_common_inputs(), 'relaxation_scheme': orm.Str('vc-relax'), 'meta_convergence': orm.Bool(self.ctx.protocol['meta_convergence']), 'volume_convergence': orm.Float(self.ctx.protocol['volume_convergence']), }, 'scf': get_common_inputs(), 'bands': get_common_inputs(), }) inputs.relax.base.kpoints_distance = orm.Float(self.ctx.protocol['kpoints_mesh_density']) inputs.scf.kpoints_distance = orm.Float(self.ctx.protocol['kpoints_mesh_density']) inputs.bands.kpoints_distance = orm.Float(self.ctx.protocol['kpoints_distance_for_bands']) num_bands_factor = self.ctx.protocol.get('num_bands_factor', None) if num_bands_factor is not None: inputs.nbands_factor = orm.Float(num_bands_factor) running = self.submit(PwBandsWorkChain, **inputs) self.report(f'launching PwBandsWorkChain<{running.pk}>') return ToContext(workchain_bands=running)
def define(cls, spec): """Define the process specification.""" # yapf: disable super().define(spec) spec.expose_inputs(PwBaseWorkChain, namespace='scf', exclude=('clean_workdir', 'pw.structure'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the SCF calculation.'}) spec.expose_inputs(PwBaseWorkChain, namespace='nscf', exclude=('clean_workdir', 'pw.structure'), namespace_options={'help': 'Inputs for the `PwBaseWorkChain` for the NSCF calculation.'}) spec.expose_inputs(PpCalculation, namespace='pp', exclude=('parent_folder', ), namespace_options={'help': 'Inputs for the `PpCalculation` for the PP calculation.'}) spec.input('structure', valid_type=orm.StructureData, help='The inputs structure.') spec.input('kpoints', valid_type=orm.KpointsData, help='Explicit kpoints to use for the NSCF calculation.') spec.input('wavefunction_min', valid_type=orm.Int, required=False, help='Smallest index of wavefunctions to compute.') spec.input('wavefunction_max', valid_type=orm.Int, required=False, help='Largest index of wavefunctions to compute.') spec.input('wavefunction_ef_min', valid_type=orm.Int, required=False, help='Smallest index, with respoect to the fermi level, of wavefunctions to compute.') spec.input('wavefunction_ef_max', valid_type=orm.Int, required=False, help='Largest index, with respoect to the fermi level, of wavefunctions to compute.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') # spec.inputs.validator = validate_inputs spec.outline( cls.setup, cls.run_scf, cls.inspect_scf, cls.run_nscf, cls.inspect_nscf, cls.run_pp, cls.inspect_pp, cls.results, ) spec.exit_code(402, 'ERROR_SUB_PROCESS_FAILED_SCF', message='The scf PwBasexWorkChain sub process failed') spec.exit_code(403, 'ERROR_SUB_PROCESS_FAILED_NSCF', message='The nscf PwBasexWorkChain sub process failed') spec.exit_code(404, 'ERROR_SUB_PROCESS_FAILED_PP', message='The PpCalculation sub process failed') spec.output('scf_parameters', valid_type=orm.Dict, help='The output parameters of the SCF `PwBaseWorkChain`.') spec.output('nscf_parameters', valid_type=orm.Dict, help='The output parameters of the NSCF `PwBaseWorkChain`.') spec.output('pp_parameters', valid_type=orm.Dict, help='The output parameters of the `PpCalculation`.') spec.output_namespace('wfc_data', valid_type=orm.ArrayData, dynamic=True)
def __init__(self, base, base_final_scf, structure, relaxation_scheme, relax_type, meta_convergence=True, max_meta_convergence_iterations=5, volume_convergence=0.01, clean_workdir=True): self.base = base self.base_final_scf = base_final_scf self.structure = structure # self.final_scf = orm.Bool(final_scf) self.relaxation_scheme = orm.Str(relaxation_scheme) self.relax_type = orm.Str(relax_type) self.meta_convergence = orm.Bool(meta_convergence) self.max_meta_convergence_iterations = orm.Int( max_meta_convergence_iterations) self.volume_convergence = orm.Float(volume_convergence) self.clean_workdir = orm.Bool(clean_workdir)
def get_builder_from_protocol(cls, code, structure, protocol=None, overrides=None, **kwargs): """Return a builder prepopulated with inputs selected according to the chosen protocol. :param code: the ``Code`` instance configured for the ``quantumespresso.pw`` plugin. :param structure: the ``StructureData`` instance to use. :param protocol: protocol to use, if not specified, the default will be used. :param overrides: optional dictionary of inputs to override the defaults of the protocol. :param kwargs: additional keyword arguments that will be passed to the ``get_builder_from_protocol`` of all the sub processes that are called by this workchain. :return: a process builder instance with all inputs defined ready for launch. """ args = (code, structure, protocol) inputs = cls.get_protocol_inputs(protocol, overrides) builder = cls.get_builder() relax = PwRelaxWorkChain.get_builder_from_protocol( *args, overrides=inputs.get('relax', None), **kwargs) scf = PwBaseWorkChain.get_builder_from_protocol(*args, overrides=inputs.get( 'scf', None), **kwargs) bands = PwBaseWorkChain.get_builder_from_protocol(*args, overrides=inputs.get( 'bands', None), **kwargs) relax.pop('structure', None) relax.pop('clean_workdir', None) relax.pop('base_final_scf', None) scf['pw'].pop('structure', None) scf.pop('clean_workdir', None) bands['pw'].pop('structure', None) bands.pop('clean_workdir', None) bands.pop('kpoints_distance', None) bands.pop('kpoints_force_parity', None) builder.structure = structure builder.relax = relax builder.scf = scf builder.bands = bands builder.clean_workdir = orm.Bool(inputs['clean_workdir']) builder.nbands_factor = orm.Float(inputs['nbands_factor']) builder.bands_kpoints_distance = orm.Float( inputs['bands_kpoints_distance']) return builder
def define(cls, spec): """Define the process specification""" # yapf: disable super(DpEvaluateBaseWorkChain, cls).define(spec) spec.input('code', valid_type=orm.Code, help='The `pw.x` code to use for the `PwCalculations`.') spec.input('structure', valid_type=orm.StructureData, help='The input structure.') spec.input('options', valid_type=orm.Dict, required=False, help='Optional `options` to use for the `PwCalculations`.') spec.input('protocol', valid_type=orm.Dict, default=orm.Dict(dict={'name': 'theos-ht-1.0', 'modifiers': {'parameters': 'default', 'pseudo': 'SSSP-efficiency-1.1'}}), help='The protocol to use for the workchain.', validator=validate_protocol) spec.input('do_relax', valid_type=orm.Bool, default=orm.Bool(True), help='If `True`, running the relax and then scf') spec.input('clean_workdir', valid_type=orm.Bool, default=orm.Bool(True), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') spec.outline( cls.setup_protocol, cls.setup_parameters, if_(cls.should_do_relax)( cls.run_relax, cls.inspect_relax, ), cls.run_scf, cls.inspect_scf, cls.results, ) spec.exit_code(201, 'ERROR_INVALID_INPUT_UNRECOGNIZED_KIND', message='Input `StructureData` contains an unsupported kind.') spec.exit_code(401, 'ERROR_SUB_PROCESS_FAILED_RELAX', message='the PwRelaxWorkChain sub process failed') spec.exit_code(402, 'ERROR_SUB_PROCESS_FAILED_SCF', message='the scf PwBasexWorkChain sub process failed') spec.output('primitive_structure', valid_type=orm.StructureData, help='The normalized and primitivized structure for which the scf step are computed.') spec.output('scf_parameters', valid_type=orm.Dict, help='The output parameters of the SCF `PwBaseWorkChain`.')
def generate_inputs_relax(protocol: Dict, code: orm.Code, structure: StructureData, sssp_family: SsspFamily, override: Dict[str, Any] = None) -> Dict[str, Any]: """Generate the inputs for the `PwRelaxWorkChain` for a given code, structure and pseudo potential family. :param protocol: the dictionary with protocol inputs. :param code: the code to use. :param structure: the input structure. :param sssp_family: the pseudo potential family. :param override: a dictionary to override specific inputs. :return: the fully defined input dictionary. """ protocol['base'] = generate_inputs_base(protocol['base'], code, structure, sssp_family, override.get('base', {})) merged = recursive_merge(protocol, override) # Remove inputs that should not be passed top-level merged['base']['pw'].pop('structure', None) dictionary = { 'base': merged['base'], 'structure': structure, 'final_scf': orm.Bool(merged['final_scf']), 'max_meta_convergence_iterations': orm.Int(merged['max_meta_convergence_iterations']), 'meta_convergence': orm.Bool(merged['meta_convergence']), 'volume_convergence': orm.Float(merged['volume_convergence']), } return dictionary
def test_exit_codes_invalidate_cache(self): """ Test that returning an exit code with 'invalidates_cache' set to ``True`` indeed means that the ProcessNode will not be cached from. """ # Sanity check that caching works when the exit code is not returned. with enable_caching(): _, node1 = run_get_node(test_processes.InvalidateCaching, return_exit_code=orm.Bool(False)) _, node2 = run_get_node(test_processes.InvalidateCaching, return_exit_code=orm.Bool(False)) self.assertEqual(node1.get_extra('_aiida_hash'), node2.get_extra('_aiida_hash')) self.assertIn('_aiida_cached_from', node2.extras) with enable_caching(): _, node3 = run_get_node(test_processes.InvalidateCaching, return_exit_code=orm.Bool(True)) _, node4 = run_get_node(test_processes.InvalidateCaching, return_exit_code=orm.Bool(True)) self.assertEqual(node3.get_extra('_aiida_hash'), node4.get_extra('_aiida_hash')) self.assertNotIn('_aiida_cached_from', node4.extras)
def define(cls, spec): """Define the process specification.""" # yapf: disable super().define(spec) spec.input('max_iterations', valid_type=orm.Int, default=lambda: orm.Int(5), help='Maximum number of iterations the work chain will restart the calculation to finish successfully.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), help='If `True`, work directories of all called calculation will be cleaned at the end of execution.') spec.input_namespace('fixers', valid_type=tuple, required=False, help="Fixers you want to apply to the outputs of every calculation.", dynamic=True) spec.exit_code(101, 'ERROR_MAXIMUM_ITERATIONS_EXCEEDED', message='The maximum number of iterations was exceeded.') spec.exit_code(102, 'ERROR_SECOND_CONSECUTIVE_UNHANDLED_FAILURE', message='The calculation failed for an unknown reason, twice in a row.')
def run_relax(self): """Run the PwRelaxWorkChain to run a relax PwCalculation.""" inputs = AttributeDict({ 'structure': self.inputs.structure, 'base': self._get_common_inputs(), 'relaxation_scheme': orm.Str('vc-relax'), 'meta_convergence': orm.Bool(self.ctx.protocol['meta_convergence']), 'volume_convergence': orm.Float(self.ctx.protocol['volume_convergence']), }) inputs.base.kpoints_distance = orm.Float(self.ctx.protocol['kpoints_mesh_density']) running = self.submit(PwRelaxWorkChain, **inputs) self.report('launching PwRelaxWorkChain<{}>'.format(running.pk)) return ToContext(workchain_relax=running)
def test_sequential(aiida_profile, generate_workchain_seq_converger, generate_wc_job_node, fixture_localhost): """ We test here the SiestaSequentialConverger, just the main two methods that distinguish it from the BaseIterator, meaning the `initialize` and the `_analyze_process` """ from aiida.common.extendeddicts import AttributeDict process = generate_workchain_seq_converger() process.initialize() assert process.ctx.iteration_keys == ('iterate_over', ) convergerwc = generate_wc_job_node("siesta.converger", fixture_localhost) convergerwc.set_process_state(ProcessState.FINISHED) convergerwc.set_exit_status(ExitCode(0).status) out_par = orm.Dict(dict={"test_par_1": 1111, "test_par_2": "eV"}) out_par.store() out_conv = orm.Bool(True) out_conv.store() val_conv = orm.Float(1) val_conv.store() out_par.add_incoming(convergerwc, link_type=LinkType.RETURN, link_label='converged_parameters') out_conv.add_incoming(convergerwc, link_type=LinkType.RETURN, link_label='converged') val_conv.add_incoming(convergerwc, link_type=LinkType.RETURN, link_label='converged_target_value') process.ctx.last_inputs = AttributeDict({}) process._analyze_process(convergerwc) assert process.ctx.already_converged == { "test_par_1": 1111, "test_par_2": "eV" } assert process.ctx.last_target_value == val_conv assert "parameters" in process.ctx.last_inputs assert "test_par_1" in process.ctx.last_inputs.parameters.attributes assert process.ctx.last_inputs.parameters.attributes["test_par_2"] == "eV"