def recollect_atomic_calculations(**kwargs): """ Collect dynamical matrix files into a single folder, putting a different number at the end of each final dynamical matrix file, obtained from the input link, which corresponds to its place in the list of q-points originally generated by distribute_qpoints. :param kwargs: keys are the string representation of the hubbard atom index and the value is the corresponding retrieved folder object. :return: FolderData object containing the perturbation files of the computed HpBaseWorkChain """ import os import errno output_folder_sub = HpCalculation._OUTPUT_SUBFOLDER output_folder_raw = HpCalculation._FOLDER_RAW output_prefix = HpCalculation()._PREFIX # Initialize the merged folder, by creating the subdirectory for the perturbation files merged_folder = FolderData() folder_path = os.path.normpath(merged_folder.get_abs_path('.')) output_path = os.path.join(folder_path, output_folder_raw) try: os.makedirs(output_path) except OSError as error: if error.errno == errno.EEXIST and os.path.isdir(output_path): pass else: raise for atomic_site_index, retrieved_folder in kwargs.iteritems(): filepath = os.path.join( output_folder_raw, '{}.chi.pert_{}.dat'.format(output_prefix, atomic_site_index)) filepath_src = retrieved_folder.get_abs_path(filepath) filepath_dst = filepath merged_folder.add_path(filepath_src, filepath_dst) # TODO: currently the Hp code requires the .save folder that is written by the original # PwCalculation, for the final post-processing matrix collection step. It doesn't really need all # the information contained in that folder, and requiring it means, copying it from remote to a # local folder and then reuploading it to remote folder. This is unnecessarily heavy retrieved_folder = kwargs.values()[0] dirpath = os.path.join(output_folder_sub, output_prefix + '.save') dirpath_src = retrieved_folder.get_abs_path(dirpath) dirpath_dst = dirpath merged_folder.add_path(dirpath_src, dirpath_dst) retrieved_folder = kwargs.values()[0] filepath = os.path.join(output_folder_sub, output_prefix + '.occup') filepath_src = retrieved_folder.get_abs_path(filepath) filepath_dst = filepath merged_folder.add_path(filepath_src, filepath_dst) return merged_folder
def migrate_as_main(work_dir, input_rel_path, output_rel_path, resources=None, input_links=None): """ migrate existing CRYSTAL17 calculation as a WorkCalculation, which imitates a ``crystal17.main`` calculation :param work_dir: the absolute path to the directory to holding the files :param input_rel_path: relative path (from work_dir) to .d12 file :param output_rel_path: relative path (from work_dir) to .out file :param resources: a dict of of job resource parameters (not yet implemented) :param input_links: a dict of existing nodes to link inputs to (allowed keys: 'structure', 'settings', 'parameters') Example of input_links={'structure': {"cif_file": CifNode}}, will create a link (via a workcalculation) from the CifNode to the input StructureData :raise IOError: if the work_dir or files do not exist :raises aiida.common.exceptions.ParsingError: if the input parsing fails :raises aiida.parsers.exceptions.OutputParsingError: if the output parsing fails :return: the calculation node :rtype: aiida.orm.WorkCalculation """ from aiida.orm.data.folder import FolderData from aiida_crystal17.calculations.cry_main import CryMainCalculation from aiida_crystal17.parsers.cry_basic import CryBasicParser calc = CryMainCalculation() parser_cls = CryBasicParser # TODO optionally use transport to remote work directory if not os.path.exists(work_dir): raise IOError("work_dir doesn't exist: {}".format(work_dir)) input_path = os.path.join(work_dir, input_rel_path) if not os.path.exists(input_path): raise IOError("input_path doesn't exist: {}".format(input_path)) output_path = os.path.join(work_dir, output_rel_path) if not os.path.exists(output_path): raise IOError("output_path doesn't exist: {}".format(output_path)) if resources: raise NotImplementedError("saving resources to ImmigrantCalculation") # resources = {} if resources is None else resources inputs = create_inputs(input_path, output_path) psuccess, output_nodes = parse_mainout(output_path, parser_class=parser_cls.__name__, init_struct=inputs['structure'], init_settings=inputs['settings']) outparams = output_nodes.pop("parameters") perrors = outparams.get_attr("errors") + outparams.get_attr( "parser_warnings") if perrors or not psuccess: raise ParsingError( "the parser failed, raising the following errors:\n{}".format( "\n\t".join(perrors))) folder = FolderData() folder.add_path(input_path, calc._DEFAULT_INPUT_FILE) # pylint: disable=protected-access folder.add_path(output_path, calc._DEFAULT_OUTPUT_FILE) # pylint: disable=protected-access # create links from existing nodes to inputs input_links = {} if not input_links else input_links for key, nodes_dict in input_links.items(): _run_dummy_workchain( nodes_dict, {key: inputs[key]}, ) # assign linknames inputs_dict = { calc.get_linkname("parameters"): inputs['parameters'], calc.get_linkname("structure"): inputs['structure'], calc.get_linkname("settings"): inputs['settings'] } for el, basis in inputs["basis"].items(): inputs_dict[calc.get_linkname_basisset(el)] = basis outputs_dict = {parser_cls.get_linkname_outparams(): outparams} if "settings" in output_nodes: outputs_dict[parser_cls.get_linkname_outsettings()] = output_nodes.pop( "settings") if "structure" in output_nodes: outputs_dict[parser_cls.get_linkname_outstructure( )] = output_nodes.pop("structure") if output_nodes: raise ParsingError("unknown key(s) in output_nodes: {}".format( list(output_nodes.keys()))) outputs_dict["retrieved"] = folder calcnode = _run_dummy_workchain(inputs_dict, outputs_dict, CryMainImmigrant) calcnode.label = "CryMainImmigrant" calcnode.description = "an immigrated CRYSTAL17 calculation into the {} format".format( calc.__class__) return calcnode