def prepare_for_submission(self, folder): """ This is the routine to be called when you want to create the input files and related stuff with a plugin. :param folder: a aiida.common.folders.Folder subclass where the plugin should put all its files. """ if 'structure' in self.inputs: pmg_structure = self.inputs.structure.get_pymatgen_molecule() else: # If structure is not specified, it is read from the chk file pmg_structure = None # Generate the input file input_string = GaussianCalculation._render_input_string_from_params( self.inputs.parameters.get_dict(), pmg_structure) with open(folder.get_abs_path(self.INPUT_FILE), "w") as out_file: out_file.write(input_string) settings = self.inputs.settings.get_dict( ) if "settings" in self.inputs else {} # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop("cmdline", []) codeinfo.code_uuid = self.inputs.code.uuid codeinfo.stdin_name = self.INPUT_FILE codeinfo.stdout_name = self.OUTPUT_FILE codeinfo.withmpi = self.inputs.metadata.options.withmpi # create calculation info calcinfo = CalcInfo() calcinfo.remote_copy_list = [] calcinfo.local_copy_list = [] calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.stdin_name = self.INPUT_FILE calcinfo.stdout_name = self.OUTPUT_FILE calcinfo.codes_info = [codeinfo] calcinfo.retrieve_list = [self.OUTPUT_FILE] # symlink or copy to parent calculation calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] if "parent_calc_folder" in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, self.PARENT_FOLDER_NAME) if (self.inputs.code.computer.uuid == comp_uuid): # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo
def prepare_for_submission(self, folder): # create code info codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid codeinfo.cmdline_params = [ self.PARENT_FOLDER_NAME + "/" + self.inputs.chk_name.value, self.DEFAULT_OUTPUT_FILE ] codeinfo.withmpi = self.inputs.metadata.options.withmpi # create calculation info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [codeinfo] calcinfo.retrieve_list = [] if self.inputs.retrieve_fchk: calcinfo.retrieve_list.append(self.DEFAULT_OUTPUT_FILE) # symlink or copy to parent calculation calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, self.PARENT_FOLDER_NAME) if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo
def _prepare_calcinfo(self, codeinfo): # Prepare CalcInfo object for aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [codeinfo] calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ settings = self.inputs.settings.get_dict( ) if 'settings' in self.inputs else {} param_dict = self.inputs.parameters.get_dict() # --------------------------------------------------- # Write params.ini file params_fn = folder.get_abs_path("params.ini") with open(params_fn, 'w') as f: for key, val in param_dict.items(): line = str(key) + " " if isinstance(val, list): line += " ".join(str(v) for v in val) else: line += str(val) f.write(line + '\n') # --------------------------------------------------- # create code info codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid codeinfo.withmpi = False # create calc info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [codeinfo] # file lists calcinfo.remote_symlink_list = [] calcinfo.local_copy_list = [ (self.inputs.atomtypes.uuid, self.inputs.atomtypes.filename, 'atomtypes.ini') ] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = ["*/*/*.npy"] # symlinks if 'parent_calc_folder' in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, 'parent_calc_folder/') if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo # EOF
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # create code info codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid param_dict = self.inputs.parameters.get_dict() cmdline = [] for key in param_dict: cmdline += [key] if param_dict[key] != '': if isinstance(param_dict[key], list): cmdline += param_dict[key] else: cmdline += [param_dict[key]] codeinfo.cmdline_params = cmdline # create calc info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.codes_info = [codeinfo] # file lists calcinfo.remote_symlink_list = [] calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = ["*.npy", "*.npz"] # symlinks if 'parent_calc_folder' in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, 'parent_calc_folder/') if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) if 'ppm_calc_folder' in self.inputs: comp_uuid = self.inputs.ppm_calc_folder.computer.uuid remote_path = self.inputs.ppm_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, 'ppm_calc_folder/') if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo
def prepare_for_submission(self, tempfolder): # pylint: disable=unused-argument,arguments-differ calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.remote_copy_list = [] codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid calcinfo.codes_info = [codeinfo] return calcinfo, codeinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ self.logger.info("prepare_for_submission") # These three lists are updated in self._create_additional_files(folder) self._internal_retrieve_list = [] self._additional_cmd_params = [] self._calculation_cmd = [] self._create_additional_files(folder) # ================= prepare the python input files ================= # BORN if (not self.inputs.fc_only and 'nac_params' in self.inputs and 'primitive' in self.inputs and 'symmetry_tolerance' in self.inputs.settings.attributes): born_txt = get_BORN_txt(self.inputs.nac_params, self.inputs.primitive, self.inputs.settings['symmetry_tolerance']) with folder.open(self._INPUT_NAC, 'w', encoding='utf8') as handle: handle.write(born_txt) for params in self._additional_cmd_params: params.append('--nac') # ============================ calcinfo =============================== local_copy_list = [] remote_copy_list = [] calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.retrieve_list = self._internal_retrieve_list calcinfo.codes_info = [] for i, (default_params, additional_params) in enumerate( zip(self._calculation_cmd, self._additional_cmd_params)): codeinfo = CodeInfo() codeinfo.cmdline_params = default_params + additional_params codeinfo.code_uuid = self.inputs.code.uuid codeinfo.stdout_name = "%s%d" % (self.options.output_filename, i + 1) codeinfo.withmpi = False calcinfo.codes_info.append(codeinfo) return calcinfo
def prepare_for_submission(self, folder): # create calculation info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [] calcinfo.retrieve_list = [] for key, params in self.inputs.parameters.get_dict().items(): cube_name = key+".cube" kind_str = params["kind"] npts = params["npts"] # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.cmdline_params.append(str(self.inputs.metadata.options.resources['tot_num_mpiprocs'])) codeinfo.cmdline_params.append(kind_str) codeinfo.cmdline_params.append(self._PARENT_FOLDER_NAME + "/" + self._DEFAULT_INPUT_FILE) codeinfo.cmdline_params.append(cube_name) codeinfo.cmdline_params.append(str(npts)) codeinfo.code_uuid = self.inputs.code.uuid codeinfo.withmpi = self.inputs.metadata.options.withmpi calcinfo.codes_info.append(codeinfo) if self.inputs.retrieve_cubes.value: calcinfo.retrieve_list.append(cube_name) extra_prepend = "\nexport GAUSS_MEMDEF=%dMB\n" % self.inputs.gauss_memdef if not hasattr(calcinfo, 'prepend_text') or not calcinfo.prepend_text: calcinfo.prepend_text = extra_prepend else: calcinfo.prepend_text += extra_prepend # symlink or copy to parent calculation calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, self._PARENT_FOLDER_NAME) if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo
def prepare_for_submission(self, folder): """ This is the routine to be called when you want to create the input files and related stuff with a plugin. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ code = self.inputs.code # an aiida Code parameters = self.inputs.parameters # an aiida Dict input_dict = parameters.get_dict() # a python dict if 'x1' not in input_dict or 'x2' not in input_dict: raise InputValidationError( 'The input parameters node should contain both keys "x1" and "x2", ' 'but it doesn\'t.') ############################## # END OF INITIAL INPUT CHECK # ############################## input_filename = self.inputs.metadata.options.input_filename output_filename = self.inputs.metadata.options.output_filename # write all the input to a file with folder.open(input_filename, 'w') as infile: json.dump(input_dict, infile) # ============================ calcinfo ================================ calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [output_filename] calcinfo.retrieve_temporary_list = [[ 'path/hugefiles*[0-9].xml', '.', '1' ]] codeinfo = CodeInfo() codeinfo.cmdline_params = [input_filename, output_filename] codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def prepare_for_submission(self, tempfolder): # pylint: disable=arguments-differ ev1_filename = 'eigenvals1.hdf5' ev2_filename = 'eigenvals2.hdf5' eigenval_file_1 = tempfolder.get_abs_path(ev1_filename) write_bands(self.inputs.bands1, eigenval_file_1) eigenval_file_2 = tempfolder.get_abs_path(ev2_filename) write_bands(self.inputs.bands2, eigenval_file_2) calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [self._OUTPUT_FILE_NAME] codeinfo = CodeInfo() codeinfo.cmdline_params = ['plot-bands', ev1_filename, ev2_filename] codeinfo.code_uuid = self.inputs.code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def prepare_for_submission(self, tempfolder): # pylint: disable=arguments-differ """ Writes the four minimum output files, INCAR, POSCAR, POTCAR, KPOINTS. Delegates the construction and writing / copying to write_<file> methods. That way, subclasses can use any form of input nodes and just have to implement the write_xxx method accordingly. Subclasses can extend by calling the super method and if neccessary modifying it's output CalcInfo before returning it. """ # write input files incar = tempfolder.get_abs_path('INCAR') structure = tempfolder.get_abs_path('POSCAR') potentials = tempfolder.get_abs_path('POTCAR') kpoints = tempfolder.get_abs_path('KPOINTS') remote_copy_list = [] self.verify_inputs() if self._is_restart(): remote_copy_list.extend(self.remote_copy_restart_folder()) self.write_incar(incar) self.write_poscar(structure) self.write_potcar(potentials) self.write_kpoints(kpoints) # calcinfo calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.retrieve_list = self.max_retrieve_list() codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid codeinfo.code_pk = self.inputs.code.pk calcinfo.codes_info = [codeinfo] calcinfo.remote_copy_list = remote_copy_list # here we need to do the charge density and wave function copy # as we need access to the calcinfo calcinfo.local_copy_list = [] self.write_additional(tempfolder, calcinfo) return calcinfo
def prepare_for_submission(self, folder): """ Create input files. :param folder: aiida.common.folders.Folder subclass where the plugin should put all its files. """ # create input files: d3 structure = self.inputs.get('structure', None) try: d3_content = D3(self.inputs.parameters.get_dict(), structure) except (ValueError, NotImplementedError) as err: raise InputValidationError( "an input file could not be created from the parameters: {}". format(err)) with folder.open(self._INPUT_FILE_NAME, "w") as f: d3_content.write(f) # create input files: fort.9 with self.inputs.wavefunction.open(mode="rb") as f: folder.create_file_from_filelike(f, self._WAVEFUNCTION_FILE_NAME, mode="wb") # Prepare CodeInfo object for aiida codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid codeinfo.stdin_name = self._INPUT_FILE_NAME codeinfo.stdout_name = self._OUTPUT_FILE_NAME codeinfo.withmpi = False # Prepare CalcInfo object for aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [codeinfo] calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [self._PROPERTIES_FILE_NAME] calcinfo.local_copy_list = [] return calcinfo
def prepare_for_submission(self, folder): """ Create input files. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid # network infers file format from file extension structure = self.inputs.structure structure_filename = self.inputs.parameters.get_structure_file_name( structure) calcinfo.local_copy_list = [(structure.uuid, structure.filename, structure_filename)] if 'atomic_radii' in self.inputs: atomic_radii = self.inputs.atomic_radii radii_file_name = atomic_radii.filename calcinfo.local_copy_list.append( (atomic_radii.uuid, atomic_radii.filename, atomic_radii.filename)) else: radii_file_name = None calcinfo.remote_copy_list = [] calcinfo.retrieve_list = self.inputs.parameters.output_files codeinfo = CodeInfo() codeinfo.cmdline_params = self.inputs.parameters.cmdline_params( structure_file_name=structure_filename, radii_file_name=radii_file_name) codeinfo.code_uuid = self.inputs.code.uuid codeinfo.withmpi = False calcinfo.codes_info = [codeinfo] return calcinfo
def _prepare_for_submission(self, tempfolder, input_nodes_raw): """ This method is called prior to job submission with a set of calculation input nodes. The inputs will be validated and sanitized, after which the necessary input files will be written to disk in a temporary folder. A CalcInfo instance will be returned that contains lists of files that need to be copied to the remote machine before job submission, as well as file lists that are to be retrieved after job completion. :param tempfolder: an aiida.common.folders.Folder to temporarily write files on disk :param input_nodes_raw: a dictionary with the raw input nodes :returns: CalcInfo instance """ input_nodes = self.validate_input_nodes(input_nodes_raw) input_parent_folder = self.validate_input_parent_folder(input_nodes) input_parameters = self.validate_input_parameters(input_nodes) input_settings = input_nodes[self.get_linkname('settings')].get_dict() input_code = input_nodes[self.get_linkname('code')] self.write_input_files(tempfolder, input_parameters) retrieve_list = self.get_retrieve_list(input_nodes) local_copy_list = self.get_local_copy_list(input_nodes) remote_copy_list = self.get_remote_copy_list(input_nodes) # Empty command line by default cmdline_params = input_settings.pop('CMDLINE', []) codeinfo = CodeInfo() codeinfo.cmdline_params = (list(cmdline_params) + ['-in', self.input_file_name]) codeinfo.stdout_name = self.output_file_name codeinfo.code_uuid = input_code.uuid calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [codeinfo] calcinfo.retrieve_list = retrieve_list calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # Write input to file input_filename = folder.get_abs_path(self._DEFAULT_INPUT_FILE) with open(input_filename, 'w') as infile: infile.write(input_render(self.inputs.parameters.get_dict())) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.remote_symlink_list = [] calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, [self._DEFAULT_ADDITIONAL_RETRIEVE_LIST, '.', 0], ] # Charge-density remotefolder (now working only for CP2K) if 'charge_density_folder' in self.inputs: charge_density_folder = self.inputs.charge_density_folder comp_uuid = charge_density_folder.computer.uuid remote_path = os.path.join( charge_density_folder.get_remote_path(), 'aiida-ELECTRON_DENSITY-1_0.cube', ) symlink = (comp_uuid, remote_path, 'valence_density.cube') calcinfo.remote_symlink_list.append(symlink) codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.code_uuid = self.inputs.code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def prepare_for_submission(self, folder): """ The baseclass will only setup the basic calcinfo arguments but will not write **any** files which has to be implemented in the subclassed prepare_for_submission() method """ # if no custodian code is defined directly run the VASP calculation, # i.e. initialize the CodeInfo for the passed VASP code if not self.inputs.custodian.get('code', False): codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid codeinfo.stdout_name = self._default_output_file codeinfo.stderr_name = self._default_error_file # otherwise wrap in Custodian calculation and initialize CodeInfo for # the passed Custodian code (This is sufficient as AiiDA will scan all # Code-inputs to generate the required prepend / append lines) else: codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.custodian.code.uuid # define custodian-exe command line arguments codeinfo.cmdline_params = ['run', PluginDefaults.CSTDN_SPEC_FNAME] # never add the MPI command to custodian since it will call # VASP using MPI itself codeinfo.withmpi = False calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [codeinfo] # those list are set defined in the inherited classes calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.remote_symlink_list = [] # retrieve lists are defined on the base class calcinfo.retrieve_temporary_list = self.retrieve_temporary_list() calcinfo.retrieve_list = self.retrieve_permanent_list() # need to set run mode since presubmit() takes all code inputs into # account and would complain if both vasp and custodian codes are set calcinfo.codes_run_mode = CodeRunMode.SERIAL # finally write the neccessary calculation inputs to the calculation's # input folder calcinfo = self.create_calculation_inputs(folder, calcinfo) return calcinfo
def prepare_for_submission(self, folder): """ Get ready. """ # We wont write anything to the `folder` since the program takes # input as commandline parameters code_info = CodeInfo() code_info.cmdline_params = [str(self.inputs.x.value), str(self.inputs.y.value)] code_info.stdout_name = self.options.output_filename code_info.code_uuid = self.inputs.code.uuid calc_info = CalcInfo() calc_info.uuid = str(self.node.uuid) # ? calc_info.codes_info = [code_info] calc_info.retrieve_list = [self.options.output_filename] calc_info.local_copy_list = [] # Anything that we have localy and could use calc_info.remote_copy_list = [] # Anything that we have remotely and could use return calc_info
def prepare_for_submission(self, folder): """ This is the routine to be called when you want to create the input files and related stuff with a plugin. :param folder: a aiida.common.folders.Folder subclass where the plugin should put all its files. """ # initialize input parameters inp = RaspaInput(self.inputs.parameters.get_dict()) # keep order of systems in the extras self.node.set_extra('system_order', inp.system_order) # handle framework(s) and/or box(es) if "System" in inp.params: self._handle_system_section(inp.params["System"], folder) # handle restart if 'retrieved_parent_folder' in self.inputs: self._handle_retrieved_parent_folder(inp, folder) inp.params['GeneralSettings']['RestartFile'] = True # handle binary restart remote_copy_list = [] if 'parent_folder' in self.inputs: self._handle_parent_folder(remote_copy_list) inp.params['GeneralSettings']['ContinueAfterCrash'] = True # get settings if 'settings' in self.inputs: settings = self.inputs.settings.get_dict() else: settings = {} # write raspa input file with open(folder.get_abs_path(self.INPUT_FILE), "w") as fobj: fobj.write(inp.render()) # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop('cmdline', []) + [self.INPUT_FILE] codeinfo.code_uuid = self.inputs.code.uuid # create calc info calcinfo = CalcInfo() calcinfo.stdin_name = self.INPUT_FILE calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.stdin_name = self.INPUT_FILE #calcinfo.stdout_name = self.OUTPUT_FILE calcinfo.codes_info = [codeinfo] # file lists calcinfo.remote_symlink_list = [] calcinfo.local_copy_list = [] if 'file' in self.inputs: for fobj in self.inputs.file.values(): calcinfo.local_copy_list.append( (fobj.uuid, fobj.filename, fobj.filename)) # block pockets if 'block_pocket' in self.inputs: for name, fobj in self.inputs.block_pocket.items(): calcinfo.local_copy_list.append( (fobj.uuid, fobj.filename, name + '.block')) # continue the previous calculation starting from the binary restart calcinfo.remote_copy_list = remote_copy_list calcinfo.retrieve_list = [self.OUTPUT_FOLDER, self.RESTART_FOLDER] calcinfo.retrieve_list += settings.pop('additional_retrieve_list', []) # check for left over settings if settings: raise InputValidationError( "The following keys have been found " + "in the settings input node {}, ".format(self.pk) + "but were not understood: " + ",".join(list(settings.keys()))) return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ from aiida_cp2k.utils import Cp2kInput # create cp2k input file inp = Cp2kInput(self.inputs.parameters.get_dict()) inp.add_keyword("GLOBAL/PROJECT", self._DEFAULT_PROJECT_NAME) # create input structure(s) if 'structure' in self.inputs: # As far as I understand self.inputs.structure can't deal with tags # self.inputs.structure.export(folder.get_abs_path(self._DEFAULT_COORDS_FILE_NAME), fileformat="xyz") self._write_structure(self.inputs.structure, folder, self._DEFAULT_COORDS_FILE_NAME) # modify the input dictionary accordingly for i, letter in enumerate('ABC'): inp.add_keyword('FORCE_EVAL/SUBSYS/CELL/' + letter, '{:<15} {:<15} {:<15}'.format( *self.inputs.structure.cell[i]), override=False, conflicting_keys=[ 'ABC', 'ALPHA_BETA_GAMMA', 'CELL_FILE_NAME' ]) topo = "FORCE_EVAL/SUBSYS/TOPOLOGY" inp.add_keyword(topo + "/COORD_FILE_NAME", self._DEFAULT_COORDS_FILE_NAME, override=False) inp.add_keyword(topo + "/COORD_FILE_FORMAT", "XYZ", override=False, conflicting_keys=['COORDINATE']) with io.open(folder.get_abs_path(self._DEFAULT_INPUT_FILE), mode="w", encoding="utf-8") as fobj: try: fobj.write(inp.render()) except ValueError as exc: six.raise_from( InputValidationError( "invalid keys or values in input parameters found"), exc) settings = self.inputs.settings.get_dict( ) if 'settings' in self.inputs else {} # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop( 'cmdline', []) + ["-i", self._DEFAULT_INPUT_FILE] codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE codeinfo.join_files = True codeinfo.code_uuid = self.inputs.code.uuid # create calc info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.stdin_name = self._DEFAULT_INPUT_FILE calcinfo.stdout_name = self._DEFAULT_OUTPUT_FILE calcinfo.codes_info = [codeinfo] # files or additional structures if 'file' in self.inputs: calcinfo.local_copy_list = [] for name, obj in self.inputs.file.items(): if isinstance(obj, SinglefileData): calcinfo.local_copy_list.append( (obj.uuid, obj.filename, obj.filename)) elif isinstance(obj, StructureData): self._write_structure(obj, folder, name + '.xyz') calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, self._DEFAULT_RESTART_FILE_NAME, self._DEFAULT_TRAJECT_FILE_NAME ] calcinfo.retrieve_list += settings.pop('additional_retrieve_list', []) # symlinks calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] if 'parent_calc_folder' in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, self._DEFAULT_PARENT_CALC_FLDR_NAME) if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) # check for left over settings if settings: raise InputValidationError( "The following keys have been found " + "in the settings input node {}, ".format(self.pk) + "but were not understood: " + ",".join(settings.keys())) return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ settings = self.inputs.settings.get_dict( ) if 'settings' in self.inputs else {} # create code info codeinfo = CodeInfo() codeinfo.code_uuid = self.inputs.code.uuid param_dict = self.inputs.parameters.get_dict() cmdline = [] for key in param_dict: cmdline += [key] if param_dict[key] != '': if isinstance(param_dict[key], list): cmdline += param_dict[key] else: cmdline += [param_dict[key]] codeinfo.cmdline_params = cmdline # create calc info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.codes_info = [codeinfo] # file lists calcinfo.remote_symlink_list = [] calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = settings.pop('additional_retrieve_list', []) # symlinks if 'parent_calc_folder' in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, 'parent_calc_folder/') if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) # check for left over settings if settings: raise InputValidationError( "The following keys have been found " + "in the settings input node {}, ".format(self.pk) + "but were not understood: " + ",".join(settings.keys())) return calcinfo # EOF
def prepare_for_submission(self, folder): """Prepare the calculation job for submission by transforming input nodes into input files. In addition to the input files being written to the sandbox folder, a `CalcInfo` instance will be returned that contains lists of files that need to be copied to the remote machine before job submission, as well as file lists that are to be retrieved after job completion. :param folder: a sandbox folder to temporarily write files on disk. :return: :py:`~aiida.common.datastructures.CalcInfo` instance. """ # pylint: disable=too-many-branches,too-many-statements import numpy as np local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] # Convert settings dictionary to have uppercase keys, or create an empty one if none was given. if 'settings' in self.inputs: settings_dict = _uppercase_dict(self.inputs.settings.get_dict(), dict_name='settings') else: settings_dict = {} first_structure = self.inputs.first_structure last_structure = self.inputs.last_structure # Check that the first and last image have the same cell if abs(np.array(first_structure.cell) - np.array(last_structure.cell)).max() > 1.e-4: raise InputValidationError('Different cell in the fist and last image') # Check that the first and last image have the same number of sites if len(first_structure.sites) != len(last_structure.sites): raise InputValidationError('Different number of sites in the fist and last image') # Check that sites in the initial and final structure have the same kinds if first_structure.get_site_kindnames() != last_structure.get_site_kindnames(): raise InputValidationError( 'Mismatch between the kind names and/or order between ' 'the first and final image' ) # Check that a pseudo potential was specified for each kind present in the `StructureData` # self.inputs.pw.pseudos is a plumpy.utils.AttributesFrozendict kindnames = [kind.name for kind in first_structure.kinds] if set(kindnames) != set(self.inputs.pw.pseudos.keys()): raise InputValidationError( 'Mismatch between the defined pseudos and the list of kinds of the structure.\nPseudos: {};\n' 'Kinds: {}'.format(', '.join(list(self.inputs.pw.pseudos.keys())), ', '.join(list(kindnames))) ) ############################## # END OF INITIAL INPUT CHECK # ############################## # Create the subfolder that will contain the pseudopotentials folder.get_subfolder(self._PSEUDO_SUBFOLDER, create=True) # Create the subfolder for the output data (sometimes Quantum ESPRESSO codes crash if the folder does not exist) folder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True) # We first prepare the NEB-specific input file. neb_input_filecontent = self._generate_input_files(self.inputs.parameters, settings_dict) with folder.open(self.inputs.metadata.options.input_filename, 'w') as handle: handle.write(neb_input_filecontent) # We now generate the PW input files for each input structure local_copy_pseudo_list = [] for i, structure in enumerate([first_structure, last_structure]): # We need to a pass a copy of the settings_dict for each structure this_settings_dict = copy.deepcopy(settings_dict) pw_input_filecontent, this_local_copy_pseudo_list = PwCalculation._generate_PWCPinputdata( # pylint: disable=protected-access self.inputs.pw.parameters, this_settings_dict, self.inputs.pw.pseudos, structure, self.inputs.pw.kpoints ) local_copy_pseudo_list += this_local_copy_pseudo_list with folder.open(f'pw_{i + 1}.in', 'w') as handle: handle.write(pw_input_filecontent) # We need to pop the settings that were used in the PW calculations for key in list(settings_dict.keys()): if key not in list(this_settings_dict.keys()): settings_dict.pop(key) # We avoid to copy twice the same pseudopotential to the same filename local_copy_pseudo_list = set(local_copy_pseudo_list) # We check that two different pseudopotentials are not copied # with the same name (otherwise the first is overwritten) if len({filename for (uuid, filename, local_path) in local_copy_pseudo_list}) < len(local_copy_pseudo_list): raise InputValidationError('Same filename for two different pseudopotentials') local_copy_list += local_copy_pseudo_list # If present, add also the Van der Waals table to the pseudo dir. Note that the name of the table is not checked # but should be the one expected by Quantum ESPRESSO. vdw_table = self.inputs.get('pw.vdw_table', None) if vdw_table: local_copy_list.append( (vdw_table.uuid, vdw_table.filename, os.path.join(self._PSEUDO_SUBFOLDER, vdw_table.filename)) ) # operations for restart parent_calc_folder = self.inputs.get('parent_folder', None) symlink = settings_dict.pop('PARENT_FOLDER_SYMLINK', self._default_symlink_usage) # a boolean if symlink: if parent_calc_folder is not None: # I put the symlink to the old parent ./out folder remote_symlink_list.append(( parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), self._OUTPUT_SUBFOLDER, '*'), # asterisk: make individual symlinks for each file self._OUTPUT_SUBFOLDER )) # and to the old parent prefix.path remote_symlink_list.append(( parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), f'{self._PREFIX}.path'), f'{self._PREFIX}.path' )) else: # copy remote output dir and .path file, if specified if parent_calc_folder is not None: remote_copy_list.append(( parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), self._OUTPUT_SUBFOLDER, '*'), self._OUTPUT_SUBFOLDER )) # and copy the old parent prefix.path remote_copy_list.append(( parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), f'{self._PREFIX}.path'), f'{self._PREFIX}.path' )) # here we may create an aiida.EXIT file create_exit_file = settings_dict.pop('ONLY_INITIALIZATION', False) if create_exit_file: exit_filename = f'{self._PREFIX}.EXIT' with folder.open(exit_filename, 'w') as handle: handle.write('\n') calcinfo = CalcInfo() codeinfo = CodeInfo() calcinfo.uuid = self.uuid cmdline_params = settings_dict.pop('CMDLINE', []) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.remote_symlink_list = remote_symlink_list # In neb calculations there is no input read from standard input!! codeinfo.cmdline_params = (['-input_images', '2'] + list(cmdline_params)) codeinfo.stdout_name = self.inputs.metadata.options.output_filename codeinfo.code_uuid = self.inputs.code.uuid calcinfo.codes_info = [codeinfo] # Retrieve the output files and the xml files calcinfo.retrieve_list = [] calcinfo.retrieve_list.append(self.inputs.metadata.options.output_filename) calcinfo.retrieve_list.append(( os.path.join(self._OUTPUT_SUBFOLDER, self._PREFIX + '_*[0-9]', 'PW.out'), # source relative path (globbing) '.', # destination relative path 2 # depth to preserve )) for xml_filepath in self.xml_filepaths: # pylint: disable=not-an-iterable calcinfo.retrieve_list.append([xml_filepath, '.', 3]) calcinfo.retrieve_list += settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', []) calcinfo.retrieve_list += self._internal_retrieve_list # We might still have parser options in the settings dictionary: pop them. _pop_parser_options(self, settings_dict) if settings_dict: unknown_keys = ', '.join(list(settings_dict.keys())) raise InputValidationError(f'`settings` contained unexpected keys: {unknown_keys}') return calcinfo
def prepare_for_submission( self, folder): # noqa: MC0001 - is mccabe too complex funct - """ Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # ============================ Initializations ============================= # All input ports are validated, here asses their presence in case optional. code = self.inputs.code # self.initialize preprocess structure and basis. Decides whether use ions or pseudos structure, basis_dict, floating_species_names, ion_or_pseudo_str = self.initialize( ) ion_or_pseudo = self.inputs[ion_or_pseudo_str] parameters = self.inputs.parameters if 'kpoints' in self.inputs: kpoints = self.inputs.kpoints else: kpoints = None # As internal convention, the keys of the settings dict are uppercase if 'settings' in self.inputs: settings = self.inputs.settings.get_dict() settings_dict = {str(k).upper(): v for (k, v) in settings.items()} else: settings_dict = {} if 'bandskpoints' in self.inputs: bandskpoints = self.inputs.bandskpoints else: bandskpoints = None if 'parent_calc_folder' in self.inputs: parent_calc_folder = self.inputs.parent_calc_folder else: parent_calc_folder = None lua_inputs = self.inputs.lua if 'script' in lua_inputs: lua_script = lua_inputs.script else: lua_script = None if 'parameters' in lua_inputs: lua_parameters = lua_inputs.parameters else: lua_parameters = None if 'input_files' in lua_inputs: lua_input_files = lua_inputs.input_files else: lua_input_files = None if 'retrieve_list' in lua_inputs: lua_retrieve_list = lua_inputs.retrieve_list else: lua_retrieve_list = None # List of files to copy in the folder where the calculation runs, e.g. pseudo files local_copy_list = [] # List of files for restart remote_copy_list = [] # ================ Preprocess of input parameters ================= input_params = FDFDict(parameters.get_dict()) input_params.update( {'system-name': self.inputs.metadata.options.prefix}) input_params.update( {'system-label': self.inputs.metadata.options.prefix}) input_params.update({'use-tree-timer': 'T'}) input_params.update({'xml-write': 'T'}) input_params.update({'number-of-species': len(structure.kinds)}) input_params.update({'number-of-atoms': len(structure.sites)}) input_params.update({'geometry-must-converge': 'T'}) input_params.update({'lattice-constant': '1.0 Ang'}) input_params.update({'atomic-coordinates-format': 'Ang'}) if lua_script is not None: input_params.update({'md-type-of-run': 'Lua'}) input_params.update({'lua-script': lua_script.filename}) local_copy_list.append( (lua_script.uuid, lua_script.filename, lua_script.filename)) if lua_input_files is not None: # Copy the whole contents of the FolderData object for file in lua_input_files.list_object_names(): local_copy_list.append((lua_input_files.uuid, file, file)) if ion_or_pseudo_str == "ions": input_params.update({'user-basis': 'T'}) # NOTES: # 1) The lattice-constant parameter must be 1.0 Ang to impose the units and consider # that the dimenstions of the lattice vectors are already correct with no need of alat. # This breaks the band-k-points "pi/a" option. The use of this option is banned. # 2) The implicit coordinate convention of the StructureData class corresponds to the "Ang" # convention in Siesta. That is why "atomic-coordinates-format" is blocked and reset. # 3) The Siesta code doesn't raise any warining if the geometry is not converged, unless # the keyword geometry-must-converge is set. That's why it is always added. # ============================ Preparation of input data ================================= # -------------------------------- CELL_PARAMETERS --------------------------------------- cell_parameters_card = "%block lattice-vectors\n" for vector in structure.cell: cell_parameters_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(*vector)) cell_parameters_card += "%endblock lattice-vectors\n" # ----------------------------ATOMIC_SPECIES & PSEUDOS/IONS------------------------------- atomic_species_card_list = [] # Dictionary to get the atomic number of a given element datmn = {v['symbol']: k for k, v in elements.items()} spind = {} spcount = 0 for kind in structure.kinds: spcount += 1 # species count spind[kind.name] = spcount atomic_number = datmn[kind.symbol] # Siesta expects negative atomic numbers for floating species if kind.name in floating_species_names: atomic_number = -atomic_number #Create the core of the chemicalspecieslabel block atomic_species_card_list.append("{0:5} {1:5} {2:5}\n".format( spind[kind.name], atomic_number, kind.name.rjust(6))) psp_or_ion = ion_or_pseudo[kind.name] # Add pseudo (ion) file to the list of files to copy (create), with the appropiate name. # In the case of sub-species (different kind.name but same kind.symbol, e.g., 'C_surf', # sharing the same pseudo with 'C'), we copy the file ('C.psf') twice, once as 'C.psf', # and once as 'C_surf.psf'. This is required by Siesta. # It is passed as list of tuples with format ('node_uuid', 'filename', 'relativedestpath'). # Since no subfolder is present in Siesta for pseudos, filename == relativedestpath. if isinstance(psp_or_ion, IonData): file_name = kind.name + ".ion" with folder.open(file_name, 'w', encoding='utf8') as handle: handle.write(psp_or_ion.get_content_ascii_format()) if isinstance(psp_or_ion, (PsfData, DeprecatedPsfData)): local_copy_list.append( (psp_or_ion.uuid, psp_or_ion.filename, kind.name + ".psf")) if isinstance(psp_or_ion, (PsmlData, DeprecatedPsmlData)): local_copy_list.append((psp_or_ion.uuid, psp_or_ion.filename, kind.name + ".psml")) atomic_species_card_list = (["%block chemicalspecieslabel\n"] + list(atomic_species_card_list)) atomic_species_card = "".join(atomic_species_card_list) atomic_species_card += "%endblock chemicalspecieslabel\n" # Free memory del atomic_species_card_list # -------------------------------------- ATOMIC_POSITIONS ----------------------------------- atomic_positions_card_list = [ "%block atomiccoordinatesandatomicspecies\n" ] countatm = 0 for site in structure.sites: countatm += 1 atomic_positions_card_list.append( "{0:18.10f} {1:18.10f} {2:18.10f} {3:4} {4:6} {5:6}\n".format( site.position[0], site.position[1], site.position[2], spind[site.kind_name], site.kind_name.rjust(6), countatm)) atomic_positions_card = "".join(atomic_positions_card_list) del atomic_positions_card_list # Free memory atomic_positions_card += "%endblock atomiccoordinatesandatomicspecies\n" # --------------------------------------- K-POINTS ---------------------------------------- # It is optional, if not specified, gamma point only is performed (default of siesta) if kpoints is not None: mesh, offset = kpoints.get_kpoints_mesh() kpoints_card_list = ["%block kgrid_monkhorst_pack\n"] kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( mesh[0], 0, 0, offset[0])) kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( 0, mesh[1], 0, offset[1])) kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( 0, 0, mesh[2], offset[2])) kpoints_card = "".join(kpoints_card_list) kpoints_card += "%endblock kgrid_monkhorst_pack\n" del kpoints_card_list # ------------------------------------ K-POINTS-FOR-BANDS ---------------------------------- # Two possibility are supported in Siesta: BandLines ad BandPoints. # User can't choose directly one of the two options, BandsLine is set automatically # if bandskpoints has labels, BandsPoints if bandskpoints has no labels. # BandLinesScale=pi/a not supported because a=1 always. BandLinesScale ReciprocalLatticeVectors # always set. if bandskpoints is not None: #the band line scale bandskpoints_card_list = [ "BandLinesScale ReciprocalLatticeVectors\n" ] #set the BandPoints if bandskpoints.labels is None: bandskpoints_card_list.append("%block BandPoints\n") for kpo in bandskpoints.get_kpoints(): bandskpoints_card_list.append( "{0:8.3f} {1:8.3f} {2:8.3f} \n".format( kpo[0], kpo[1], kpo[2])) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandPoints\n" #set the BandLines else: bandskpoints_card_list.append("%block BandLines\n") savindx = [] listforbands = bandskpoints.get_kpoints() for indx, label in bandskpoints.labels: savindx.append(indx) rawindex = 0 for indx, label in bandskpoints.labels: rawindex = rawindex + 1 x, y, z = listforbands[indx] if rawindex == 1: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( 1, x, y, z, label)) else: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( indx - savindx[rawindex - 2], x, y, z, label)) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandLines\n" del bandskpoints_card_list # ================================= Operations for restart ================================= # The presence of a 'parent_calc_folder' input node signals that we want to # get something from there, as indicated in the self._restart_copy_from attribute. # In Siesta's case, for now, just the density-matrix file is copied # to the current calculation's working folder. # ISSUE: Is this mechanism flexible enough? An alternative would be to # pass the information about which file(s) to copy in the metadata.options dictionary if parent_calc_folder is not None: remote_copy_list.append( (parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), self._restart_copy_from), self._restart_copy_to)) input_params.update({'dm-use-save-dm': "T"}) # ===================================== FDF file creation ==================================== # To have easy access to inputs metadata options metadataoption = self.inputs.metadata.options # input_filename = self.inputs.metadata.options.input_filename input_filename = folder.get_abs_path(metadataoption.input_filename) # Print to file with open(input_filename, 'w') as infile: # Parameters for k, v in sorted(input_params.get_filtered_items()): infile.write("%s %s\n" % (k, v)) # Basis set info is processed just like the general parameters section. if basis_dict: #It migh also be empty dict. In such case we do not write. infile.write("#\n# -- Basis Set Info follows\n#\n") for k, v in basis_dict.items(): infile.write("%s %s\n" % (k, v)) # Write previously generated cards now infile.write("#\n# -- Structural Info follows\n#\n") infile.write(atomic_species_card) infile.write(cell_parameters_card) infile.write(atomic_positions_card) if kpoints is not None: infile.write("#\n# -- K-points Info follows\n#\n") infile.write(kpoints_card) if bandskpoints is not None: infile.write("#\n# -- Bandlines/Bandpoints Info follows\n#\n") infile.write(fbkpoints_card) # Write max wall-clock time # This should prevent SiestaCalculation from being terminated by scheduler, however the # strategy is not 100% effective since SIESTA checks the simulation time versus max-walltime # only at the end of each SCF and geometry step. The scheduler might kill the process in between. infile.write("#\n# -- Max wall-clock time block\n#\n") infile.write( f"maxwalltime {metadataoption.max_wallclock_seconds}\n") # ================================== Lua parameters file =================================== if lua_parameters is not None: lua_config_filename = folder.get_abs_path("config.lua") # Generate a 'config.lua' file with Lua syntax with open(lua_config_filename, 'w') as f_lua: f_lua.write("--- Lua script parameters \n") for k, v in lua_parameters.get_dict().items(): if isinstance(v, str): f_lua.write('%s = "%s"\n' % (k, v)) else: f_lua.write("%s = %s\n" % (k, v)) # ============================= Code and Calc info ========================================= # Code information object and Calc information object are now # only used to set up the CMDLINE (the bash line that launches siesta) # and to set up the list of files to retrieve. cmdline_params = settings_dict.pop('CMDLINE', []) codeinfo = CodeInfo() codeinfo.cmdline_params = list(cmdline_params) codeinfo.stdin_name = metadataoption.input_filename codeinfo.stdout_name = metadataoption.output_filename codeinfo.code_uuid = code.uuid calcinfo = CalcInfo() calcinfo.uuid = str(self.uuid) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.codes_info = [codeinfo] # Retrieve by default: the output file, the xml file, the messages file, and the json timing file. # If bandskpoints, also the bands file is added to the retrieve list. calcinfo.retrieve_list = [] xml_file = str(metadataoption.prefix) + ".xml" bands_file = str(metadataoption.prefix) + ".bands" calcinfo.retrieve_list.append(metadataoption.output_filename) calcinfo.retrieve_list.append(xml_file) calcinfo.retrieve_list.append(self._JSON_FILE) calcinfo.retrieve_list.append(self._MESSAGES_FILE) calcinfo.retrieve_list.append(self._BASIS_ENTHALPY_FILE) calcinfo.retrieve_list.append("*.ion.xml") if bandskpoints is not None: calcinfo.retrieve_list.append(bands_file) if lua_retrieve_list is not None: calcinfo.retrieve_list += lua_retrieve_list.get_list() # If we ever want to avoid having the config.lua file in the repository, # since the information is already in the lua_parameters dictionary: # if lua_parameters is not None: # calcinfo.provenance_exclude_list = ['config.lua'] # Any other files specified in the settings dictionary settings_retrieve_list = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', []) calcinfo.retrieve_list += settings_retrieve_list return calcinfo
def prepare_for_submission(self, folder): """ This is the routine to be called when you want to create the input files and related stuff with a plugin. :param folder: a aiida.common.folders.Folder subclass where the plugin should put all its files. """ # create calc info calcinfo = CalcInfo() calcinfo.remote_copy_list = [] calcinfo.local_copy_list = [] # The main input try: input_string = GaussianCalculation._render_input_string_from_params( self.inputs.parameters.get_dict(), self.inputs.structure ) # If structure is not specified the user might want to restart from a chk except AttributeError: input_string = GaussianCalculation._render_input_string_from_params( self.inputs.parameters.get_dict(), None ) # Parse additional link1 sections if "extra_link1_sections" in self.inputs: for l1_name, l1_params in self.inputs.extra_link1_sections.items(): input_string += "--Link1--\n" # The link1 secions don't support their own geometries. input_string += GaussianCalculation._render_input_string_from_params( l1_params.get_dict(), None ) with open(folder.get_abs_path(self.INPUT_FILE), "w") as out_file: out_file.write(input_string) settings = self.inputs.settings.get_dict() if "settings" in self.inputs else {} # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop("cmdline", []) codeinfo.code_uuid = self.inputs.code.uuid codeinfo.stdin_name = self.INPUT_FILE codeinfo.stdout_name = self.OUTPUT_FILE codeinfo.withmpi = self.inputs.metadata.options.withmpi # create calculation info calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.stdin_name = self.INPUT_FILE calcinfo.stdout_name = self.OUTPUT_FILE calcinfo.codes_info = [codeinfo] calcinfo.retrieve_list = [self.OUTPUT_FILE] # symlink or copy to parent calculation calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] if "parent_calc_folder" in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, "parent_calc") if ( self.inputs.code.computer.uuid == comp_uuid ): # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo
def prepare_for_submission(self, folder): # create calculation info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.codes_info = [] calcinfo.retrieve_list = [] calcinfo.retrieve_temporary_list = [] calcinfo.prepend_text = "export GAUSS_MEMDEF=%dMB\n" % self.inputs.gauss_memdef calcinfo.local_copy_list = [] if "stencil" in self.inputs: calcinfo.local_copy_list.append( (self.inputs.stencil.uuid, self.inputs.stencil.filename, 'stencil.txt')) for key, params in self.inputs.parameters.get_dict().items(): cube_name = key + ".cube" kind_str = params["kind"] npts = params["npts"] # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.cmdline_params.append( str(self.inputs.metadata.options.resources['tot_num_mpiprocs']) ) codeinfo.cmdline_params.append(kind_str) codeinfo.cmdline_params.append(self.PARENT_FOLDER_NAME + "/" + self.DEFAULT_INPUT_FILE) codeinfo.cmdline_params.append(cube_name) if npts == -1: if 'stencil' not in self.inputs: self.report( "Warning: npts: -1 set but no stencil provided, using -2" ) codeinfo.cmdline_params.append("-2") else: codeinfo.cmdline_params.append(str(npts)) codeinfo.stdin_name = "stencil.txt" else: codeinfo.cmdline_params.append(str(npts)) codeinfo.code_uuid = self.inputs.code.uuid codeinfo.withmpi = self.inputs.metadata.options.withmpi calcinfo.codes_info.append(codeinfo) if self.inputs.retrieve_cubes.value: calcinfo.retrieve_list.append(cube_name) else: calcinfo.retrieve_temporary_list.append(cube_name) # symlink or copy to parent calculation calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, self.PARENT_FOLDER_NAME) if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink # if not - copy the folder calcinfo.remote_symlink_list.append(copy_info) else: calcinfo.remote_copy_list.append(copy_info) return calcinfo
def prepare_for_submission(self, tempfolder): """ Create the input files from the input nodes passed to this instance of the `CalcJob`. :param tempfolder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ ##################################################### # BEGINNING OF INITIAL INPUT CHECK # # All input ports that are defined via spec.input # # are checked by default, only need to asses their # # presence in case they are optional # ##################################################### code = self.inputs.code structure = self.inputs.structure parameters = self.inputs.parameters if 'kpoints' in self.inputs: kpoints = self.inputs.kpoints else: kpoints = None if 'basis' in self.inputs: basis = self.inputs.basis else: basis = None if 'settings' in self.inputs: settings = self.inputs.settings.get_dict() settings_dict = _uppercase_dict(settings, dict_name='settings') else: settings_dict = {} if 'bandskpoints' in self.inputs: bandskpoints = self.inputs.bandskpoints else: bandskpoints = None if 'parent_calc_folder' in self.inputs: parent_calc_folder = self.inputs.parent_calc_folder else: parent_calc_folder = None pseudos = self.inputs.pseudos kinds = [kind.name for kind in structure.kinds] if set(kinds) != set(pseudos.keys()): raise ValueError( 'Mismatch between the defined pseudos and the list of kinds of the structure.\n', 'Pseudos: {} \n'.format(', '.join(list(pseudos.keys()))), 'Kinds: {}'.format(', '.join(list(kinds))), ) # List of the file to copy in the folder where the calculation # runs, for instance pseudo files local_copy_list = [] # List of files for restart remote_copy_list = [] ############################## # END OF INITIAL INPUT CHECK # ############################## # ============== Preprocess of input parameters =============== # There should be a warning for duplicated (canonicalized) keys # in the original dictionary in the script input_params = FDFDict(parameters.get_dict()) # Look for blocked keywords and # add the proper values to the dictionary for blocked_key in self._aiida_blocked_keywords: canonical_blocked = FDFDict.translate_key(blocked_key) for key in input_params: if key == canonical_blocked: raise InputValidationError( "You cannot specify explicitly the '{}' flag in the " "input parameters".format( input_params.get_last_key(key))) input_params.update({'system-name': self._PREFIX}) input_params.update({'system-label': self._PREFIX}) input_params.update({'use-tree-timer': 'T'}) input_params.update({'xml-write': 'T'}) input_params.update({'number-of-species': len(structure.kinds)}) input_params.update({'number-of-atoms': len(structure.sites)}) # Regarding the lattice-constant parameter: # -- The variable "alat" is not typically kept anywhere, and # has already been used to define the vectors. # We need to specify that the units of these vectors are Ang... input_params.update({'lattice-constant': '1.0 Ang'}) # Note that this will break havoc with the band-k-points "pi/a" # option. The use of this option should be banned. # Note that the implicit coordinate convention of the Structure # class corresponds to the "Ang" convention in Siesta. # That is why the "atomic-coordinates-format" keyword is blocked # and reset. input_params.update({'atomic-coordinates-format': 'Ang'}) # ============== Preparation of input data =============== # ---------------- CELL_PARAMETERS ------------------------ cell_parameters_card = "%block lattice-vectors\n" for vector in structure.cell: cell_parameters_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(*vector)) cell_parameters_card += "%endblock lattice-vectors\n" # --------------ATOMIC_SPECIES & PSEUDOS------------------- # I create the subfolder that will contain the pseudopotentials tempfolder.get_subfolder(self._PSEUDO_SUBFOLDER, create=True) # I create the subfolder with the output data tempfolder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True) atomic_species_card_list = [] # Dictionary to get the atomic number of a given element datmn = dict([(v['symbol'], k) for k, v in six.iteritems(elements)]) spind = {} spcount = 0 for kind in structure.kinds: spcount += 1 # species count spind[kind.name] = spcount atomic_species_card_list.append("{0:5} {1:5} {2:5}\n".format( spind[kind.name], datmn[kind.symbol], kind.name.rjust(6))) ps = pseudos[kind.name] # Add this pseudo file to the list of files to copy, with # the appropiate name. In the case of sub-species # (different kind.name but same kind.symbol, e.g., # 'C_surf', sharing the same pseudo with 'C'), we will # copy the file ('C.psf') twice, once as 'C.psf', and once # as 'C_surf.psf'. This is required by Siesta. # ... list of tuples with format ('node_uuid', 'filename', relativedestpath') # We probably should be pre-pending 'self._PSEUDO_SUBFOLDER' in the # last slot, for generality... if isinstance(ps, PsfData): local_copy_list.append((ps.uuid, ps.filename, kind.name + ".psf")) elif isinstance(ps, PsmlData): local_copy_list.append((ps.uuid, ps.filename, kind.name + ".psml")) else: pass atomic_species_card_list = (["%block chemicalspecieslabel\n"] + list(atomic_species_card_list)) atomic_species_card = "".join(atomic_species_card_list) atomic_species_card += "%endblock chemicalspecieslabel\n" # Free memory del atomic_species_card_list # --------------------- ATOMIC_POSITIONS ----------------------- atomic_positions_card_list = [ "%block atomiccoordinatesandatomicspecies\n" ] countatm = 0 for site in structure.sites: countatm += 1 atomic_positions_card_list.append( "{0:18.10f} {1:18.10f} {2:18.10f} {3:4} {4:6} {5:6}\n".format( site.position[0], site.position[1], site.position[2], spind[site.kind_name], site.kind_name.rjust(6), countatm)) atomic_positions_card = "".join(atomic_positions_card_list) del atomic_positions_card_list # Free memory atomic_positions_card += "%endblock atomiccoordinatesandatomicspecies\n" # -------------------- K-POINTS ---------------------------- # It is optional, if not specified, gamma point only is performed, # this is default of siesta if kpoints is not None: # # Get a mesh for sampling # NOTE that there is not yet support for the 'kgrid-cutoff' # option in Siesta. # try: mesh, offset = kpoints.get_kpoints_mesh() has_mesh = True except AttributeError: raise InputValidationError("K-point sampling for scf " "must be given in mesh form") kpoints_card_list = ["%block kgrid_monkhorst_pack\n"] # # This will fail if has_mesh is False (for the case of a list), # since in that case 'offset' is undefined. # kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( mesh[0], 0, 0, offset[0])) kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( 0, mesh[1], 0, offset[1])) kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( 0, 0, mesh[2], offset[2])) kpoints_card = "".join(kpoints_card_list) kpoints_card += "%endblock kgrid_monkhorst_pack\n" del kpoints_card_list # ----------------- K-POINTS-FOR-BANDS ---------------------- #Two possibility are supported in Siesta: BandLines ad BandPoints #At the moment the user can't choose directly one of the two options #BandsLine is set automatically if bandskpoints has labels, #BandsPoints if bandskpoints has no labels #BandLinesScale =pi/a is not supported at the moment because currently #a=1 always. BandLinesScale ReciprocalLatticeVectors is always set if bandskpoints is not None: bandskpoints_card_list = [ "BandLinesScale ReciprocalLatticeVectors\n" ] if bandskpoints.labels == None: bandskpoints_card_list.append("%block BandPoints\n") for s in bandskpoints.get_kpoints(): bandskpoints_card_list.append( "{0:8.3f} {1:8.3f} {2:8.3f} \n".format( s[0], s[1], s[2])) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandPoints\n" else: bandskpoints_card_list.append("%block BandLines\n") savs = [] listforbands = bandskpoints.get_kpoints() for s, m in bandskpoints.labels: savs.append(s) rawindex = 0 for s, m in bandskpoints.labels: rawindex = rawindex + 1 x, y, z = listforbands[s] if rawindex == 1: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( 1, x, y, z, m)) else: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( s - savs[rawindex - 2], x, y, z, m)) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandLines\n" del bandskpoints_card_list # ================ Operations for restart ======================= # The presence of a 'parent_calc_folder' input node signals # that we want to get something from there, as indicated in the # self._restart_copy_from attribute. # In Siesta's case, for now, it is just the density-matrix file # # It will be copied to the current calculation's working folder. # NOTE: This mechanism is not flexible enough. # Maybe we should pass the information about which file(s) to # copy in the metadata 'options' dictionary if parent_calc_folder is not None: remote_copy_list.append( (parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), self._restart_copy_from), self._restart_copy_to)) input_params.update({'dm-use-save-dm': "T"}) # ====================== FDF file creation ======================== # To have easy access to inputs metadata options metadataoption = self.inputs.metadata.options # input_filename = self.inputs.metadata.options.input_filename input_filename = tempfolder.get_abs_path(metadataoption.input_filename) with open(input_filename, 'w') as infile: # here print keys and values tp file # for k, v in sorted(six.iteritems(input_params)): for k, v in sorted(input_params.get_filtered_items()): infile.write("%s %s\n" % (k, v)) # Basis set info is processed just like the general # parameters section. Some discipline is needed to # put any basis-related parameters (including blocks) # in the basis dictionary in the input script. # if basis is not None: infile.write("#\n# -- Basis Set Info follows\n#\n") for k, v in six.iteritems(basis.get_dict()): infile.write("%s %s\n" % (k, v)) # Write previously generated cards now infile.write("#\n# -- Structural Info follows\n#\n") infile.write(atomic_species_card) infile.write(cell_parameters_card) infile.write(atomic_positions_card) if kpoints is not None: infile.write("#\n# -- K-points Info follows\n#\n") infile.write(kpoints_card) if bandskpoints is not None: infile.write("#\n# -- Bandlines/Bandpoints Info follows\n#\n") infile.write(fbkpoints_card) # Write max wall-clock time infile.write("#\n# -- Max wall-clock time block\n#\n") infile.write("max.walltime {}".format( metadataoption.max_wallclock_seconds)) # ====================== Code and Calc info ======================== # Code information object and Calc information object are now # only used to set up the CMDLINE (the bash line that launches siesta) # and to set up the list of files to retrieve. cmdline_params = settings_dict.pop('CMDLINE', []) codeinfo = CodeInfo() codeinfo.cmdline_params = list(cmdline_params) codeinfo.stdin_name = metadataoption.input_filename codeinfo.stdout_name = metadataoption.output_filename codeinfo.code_uuid = code.uuid calcinfo = CalcInfo() calcinfo.uuid = str(self.uuid) if cmdline_params: calcinfo.cmdline_params = list(cmdline_params) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.stdin_name = metadataoption.input_filename calcinfo.stdout_name = metadataoption.output_filename calcinfo.codes_info = [codeinfo] # Retrieve by default: the output file, the xml file, the # messages file, and the json timing file. # If bandskpoints, also the bands file is added to the retrieve list. calcinfo.retrieve_list = [] calcinfo.retrieve_list.append(metadataoption.output_filename) calcinfo.retrieve_list.append(self._DEFAULT_XML_FILE) calcinfo.retrieve_list.append(self._DEFAULT_JSON_FILE) calcinfo.retrieve_list.append(self._DEFAULT_MESSAGES_FILE) if bandskpoints is not None: calcinfo.retrieve_list.append(self._DEFAULT_BANDS_FILE) # Any other files specified in the settings dictionary settings_retrieve_list = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', []) calcinfo.retrieve_list += settings_retrieve_list return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # pylint: disable=too-many-statements,too-many-branches # Create cp2k input file. inp = Cp2kInput(self.inputs.parameters.get_dict()) inp.add_keyword("GLOBAL/PROJECT", self._DEFAULT_PROJECT_NAME) # Create input structure(s). if 'structure' in self.inputs: # As far as I understand self.inputs.structure can't deal with tags # self.inputs.structure.export(folder.get_abs_path(self._DEFAULT_COORDS_FILE_NAME), fileformat="xyz") self._write_structure(self.inputs.structure, folder, self._DEFAULT_COORDS_FILE_NAME) # modify the input dictionary accordingly for i, letter in enumerate('ABC'): inp.add_keyword('FORCE_EVAL/SUBSYS/CELL/' + letter, '{:<15} {:<15} {:<15}'.format(*self.inputs.structure.cell[i]), override=False, conflicting_keys=['ABC', 'ALPHA_BETA_GAMMA', 'CELL_FILE_NAME']) topo = "FORCE_EVAL/SUBSYS/TOPOLOGY" inp.add_keyword(topo + "/COORD_FILE_NAME", self._DEFAULT_COORDS_FILE_NAME, override=False) inp.add_keyword(topo + "/COORD_FILE_FORMAT", "XYZ", override=False, conflicting_keys=['COORDINATE']) if 'basissets' in self.inputs: validate_basissets(inp, self.inputs.basissets, self.inputs.structure if 'structure' in self.inputs else None) write_basissets(inp, self.inputs.basissets, folder) if 'pseudos' in self.inputs: validate_pseudos(inp, self.inputs.pseudos, self.inputs.structure if 'structure' in self.inputs else None) write_pseudos(inp, self.inputs.pseudos, folder) # Kpoints. if 'kpoints' in self.inputs: try: mesh, _ = self.inputs.kpoints.get_kpoints_mesh() except AttributeError: raise InputValidationError("K-point sampling for SCF must be given in mesh form.") inp.add_keyword('FORCE_EVAL/DFT/KPOINTS', {'WAVEFUNCTIONS': ' COMPLEX', 'FULL_GRID': '.TRUE.'}) inp.add_keyword('FORCE_EVAL/DFT/KPOINTS/SCHEME MONKHORST-PACK', f'{mesh[0]} {mesh[1]} {mesh[2]}') with io.open(folder.get_abs_path(self._DEFAULT_INPUT_FILE), mode="w", encoding="utf-8") as fobj: try: fobj.write(inp.render()) except ValueError as exc: raise InputValidationError("Invalid keys or values in input parameters found") from exc settings = self.inputs.settings.get_dict() if 'settings' in self.inputs else {} # Create code info. codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop('cmdline', []) + ["-i", self._DEFAULT_INPUT_FILE] codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE codeinfo.join_files = True codeinfo.code_uuid = self.inputs.code.uuid # Create calc info. calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.stdin_name = self._DEFAULT_INPUT_FILE calcinfo.stdout_name = self._DEFAULT_OUTPUT_FILE calcinfo.codes_info = [codeinfo] # Files or additional structures. if 'file' in self.inputs: calcinfo.local_copy_list = [] for name, obj in self.inputs.file.items(): if isinstance(obj, SinglefileData): calcinfo.local_copy_list.append((obj.uuid, obj.filename, obj.filename)) elif isinstance(obj, StructureData): self._write_structure(obj, folder, name + '.xyz') calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, self._DEFAULT_RESTART_FILE_NAME, self._DEFAULT_TRAJECT_FILE_NAME ] calcinfo.retrieve_list += settings.pop('additional_retrieve_list', []) # Symlinks. calcinfo.remote_symlink_list = [] calcinfo.remote_copy_list = [] if 'parent_calc_folder' in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() copy_info = (comp_uuid, remote_path, self._DEFAULT_PARENT_CALC_FLDR_NAME) # If running on the same computer - make a symlink. if self.inputs.code.computer.uuid == comp_uuid: calcinfo.remote_symlink_list.append(copy_info) # If not - copy the folder. else: calcinfo.remote_copy_list.append(copy_info) # Check for left over settings. if settings: raise InputValidationError( f"The following keys have been found in the settings input node {self.pk}, but were not understood: " + ",".join(settings.keys())) return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ self.logger.info("prepare_for_submission") self._internal_retrieve_list = [] self._additional_cmd_params = [] self._calculation_cmd = [] settings = self.inputs.settings structure = self.inputs.structure code = self.inputs.code ############################## # END OF INITIAL INPUT CHECK # ############################## # ================= prepare the python input files ================= self._create_additional_files(folder) cell_txt = get_poscar_txt(structure) input_txt = get_phonopy_conf_file_txt(settings) input_filename = folder.get_abs_path( self.inputs.metadata.options.input_filename) with open(input_filename, 'w') as infile: infile.write(input_txt) cell_filename = folder.get_abs_path(self._INPUT_CELL) with open(cell_filename, 'w') as infile: infile.write(cell_txt) if ('nac_params' in self.inputs and 'primitive' in self.inputs): born_txt = get_BORN_txt(self.inputs.nac_params, self.inputs.primitive, settings['symmetry_tolerance']) nac_filename = folder.get_abs_path(self._INPUT_NAC) with open(nac_filename, 'w') as infile: infile.write(born_txt) for params in self._additional_cmd_params: params.append('--nac') # ============================ calcinfo =============================== local_copy_list = [] remote_copy_list = [] calcinfo = CalcInfo() calcinfo.uuid = self.uuid # Empty command line by default calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list # Retrieve files calcinfo.retrieve_list = self._internal_retrieve_list calcinfo.codes_info = [] for default_params, additional_params in zip( self._calculation_cmd, self._additional_cmd_params): codeinfo = CodeInfo() codeinfo.cmdline_params = ([ self.inputs.metadata.options.input_filename, ] + default_params + additional_params) codeinfo.code_uuid = code.uuid codeinfo.stdout_name = self.inputs.metadata.options.output_filename codeinfo.withmpi = False calcinfo.codes_info.append(codeinfo) return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # Determine atomic densities directory pm_dict = self.inputs.parameters.get_dict() if DENSITY_DIR_KEY not in pm_dict: pm_dict[DENSITY_DIR_KEY] = self.inputs.code.extras.get( DENSITY_DIR_EXTRA) if not pm_dict[DENSITY_DIR_KEY]: raise ValueError( f"Please provide '{DENSITY_DIR_KEY}' in the input parameters or set the " f"{DENSITY_DIR_EXTRA} extra on the ddec code.") # The directory must end with a slash or chargemol crashes if not pm_dict[DENSITY_DIR_KEY].endswith('/'): pm_dict[DENSITY_DIR_KEY] += '/' if not os.path.isabs(pm_dict[DENSITY_DIR_KEY]): raise ValueError( f"Path to atomic densities directory '{pm_dict[DENSITY_DIR_KEY]}' is not absolute." ) # Create symlink to atomic densities directory density_dir_symlink = (self.inputs.code.computer.uuid, pm_dict[DENSITY_DIR_KEY], DENSITY_DIR_SYMLINK) pm_dict[DENSITY_DIR_KEY] = DENSITY_DIR_SYMLINK # Write input to file input_filename = folder.get_abs_path(self._DEFAULT_INPUT_FILE) with open(input_filename, 'w') as infile: infile.write(input_render(pm_dict)) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.remote_symlink_list = [density_dir_symlink] calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, [self._DEFAULT_ADDITIONAL_RETRIEVE_LIST, '.', 0], ] # Charge-density remote folder (now working only for CP2K) if 'charge_density_folder' in self.inputs: charge_density_folder = self.inputs.charge_density_folder comp_uuid = charge_density_folder.computer.uuid remote_path = os.path.join( charge_density_folder.get_remote_path(), 'aiida-ELECTRON_DENSITY-1_0.cube', ) copy_info = (comp_uuid, remote_path, 'valence_density.cube') if self.inputs.code.computer.uuid == comp_uuid: # if running on the same computer - make a symlink calcinfo.remote_symlink_list.append(copy_info) else: # if not - copy the folder self.report( f"Warning: Transferring cube file {charge_density_folder.get_remote_path()} from " + f"computer {charge_density_folder.computer.label} to computer {self.inputs.code.computer.label}. " + "This may put strain on your network.") calcinfo.remote_copy_list.append(copy_info) codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.code_uuid = self.inputs.code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def prepare_for_submission( self, folder): # noqa: MC0001 - is mccabe too complex funct - """ Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ code = self.inputs.code ldos_folder = self.inputs.ldos_folder value = self.inputs.value spin_option = self.inputs.spin_option mode = self.inputs.mode # As internal convention, the keys of the settings dict are uppercase if 'settings' in self.inputs: settings = self.inputs.settings.get_dict() settings_dict = {str(k).upper(): v for (k, v) in settings.items()} else: settings_dict = {} # List of files for restart remote_copy_list = [] # ======================== Creation of input file ========================= # Input file is only necessary for the old versions of plstm. # For the new versions, all is done through command line (next section). # To have easy access to inputs metadata options metadataoption = self.inputs.metadata.options # input_filename access input_filename = folder.get_abs_path(metadataoption.input_filename) # Getting the prefix from ldos_folder if "prefix" in ldos_folder.creator.attributes: prefix = str(ldos_folder.creator.get_attribute("prefix")) else: self.report( "No prefix detected from the remote folder, set 'aiida' as prefix" ) prefix = "aiida" ldosfile = prefix + ".LDOS" # Convert height to bohr... if mode.value == "constant-height": vvalue = value.value / 0.529177 else: vvalue = value.value with open(input_filename, 'w') as infile: infile.write(f"{prefix}\n") infile.write("ldos\n") infile.write(f"{mode.value}\n") infile.write(f"{vvalue:.5f}\n") infile.write("unformatted\n") # ============================== Code and Calc info =============================== # Code information object is used to set up the the bash line that launches siesta # (CMDLINE and input output files). # Calc information object is to set up thee list of files to retrieve. # The presence of a 'ldos_folder' is mandatory, to get the LDOS file as indicated in # the self._restart_copy_from attribute. (this is not technically a restart, though) # It will be copied to the current calculation's working folder. remote_copy_list.append( (ldos_folder.computer.uuid, os.path.join(ldos_folder.get_remote_path(), self._restart_copy_from), self._restart_copy_to)) # Empty command line by default. Why use 'pop' ? cmdline_params = settings_dict.pop('CMDLINE', []) # Code information object. Sets the command line codeinfo = CodeInfo() if mode.value == "constant-height": cmdline_params = (list(cmdline_params) + ['-z', '{0:.5f}'.format(vvalue)]) else: cmdline_params = (list(cmdline_params) + ['-i', '{0:.5f}'.format(vvalue)]) if spin_option.value != "q": cmdline_params = (list(cmdline_params) + ['-s', str(spin_option.value), ldosfile]) else: cmdline_params = (list(cmdline_params) + [ldosfile]) codeinfo.cmdline_params = list(cmdline_params) codeinfo.stdin_name = metadataoption.input_filename codeinfo.stdout_name = metadataoption.output_filename codeinfo.code_uuid = code.uuid # Calc information object. Important for files to copy, retrieve, etc calcinfo = CalcInfo() calcinfo.uuid = str(self.uuid) calcinfo.local_copy_list = [ ] #No local files to copy (no pseudo for instance) calcinfo.remote_copy_list = remote_copy_list calcinfo.codes_info = [codeinfo] # Retrieve by default: the output file and the plot file. Some logic to understand which # is the plot file will be in parser, here we put to retrieve every file ending in *.STM calcinfo.retrieve_list = [] calcinfo.retrieve_list.append(metadataoption.output_filename) calcinfo.retrieve_list.append("*.STM") # Any other files specified in the settings dictionary settings_retrieve_list = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', []) calcinfo.retrieve_list += settings_retrieve_list return calcinfo
def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ from .utils import Cp2kInput # create input structure if "structure" in self.inputs: self.inputs.structure.export(folder.get_abs_path( self._DEFAULT_COORDS_FILE_NAME), fileformat="xyz") # create cp2k input file inp = Cp2kInput(self.inputs.parameters.get_dict()) inp.add_keyword("GLOBAL/PROJECT", self._DEFAULT_PROJECT_NAME) if "structure" in self.inputs: for i, letter in enumerate("ABC"): inp.add_keyword( "FORCE_EVAL/SUBSYS/CELL/" + letter, "{:<15} {:<15} {:<15}".format( *self.inputs.structure.cell[i]), ) topo = "FORCE_EVAL/SUBSYS/TOPOLOGY" inp.add_keyword(topo + "/COORD_FILE_NAME", self._DEFAULT_COORDS_FILE_NAME) inp.add_keyword(topo + "/COORD_FILE_FORMAT", "XYZ") with io.open(folder.get_abs_path(self._DEFAULT_INPUT_FILE), mode="w", encoding="utf-8") as fobj: try: inp.to_file(fobj) except ValueError as exc: six.raise_from( InputValidationError( "invalid keys or values in input parameters found"), exc, ) if "settings" in self.inputs: settings = self.inputs.settings.get_dict() else: settings = {} # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop("cmdline", []) + [ "-i", self._DEFAULT_INPUT_FILE, ] codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE codeinfo.join_files = True codeinfo.code_uuid = self.inputs.code.uuid # create calc info calcinfo = CalcInfo() calcinfo.stdin_name = self._DEFAULT_INPUT_FILE calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.stdin_name = self._DEFAULT_INPUT_FILE calcinfo.stdout_name = self._DEFAULT_OUTPUT_FILE calcinfo.codes_info = [codeinfo] # file lists calcinfo.remote_symlink_list = [] if "file" in self.inputs: calcinfo.local_copy_list = [] for fobj in self.inputs.file.values(): calcinfo.local_copy_list.append( (fobj.uuid, fobj.filename, fobj.filename)) calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, self._DEFAULT_RESTART_FILE_NAME, ] calcinfo.retrieve_list += settings.pop("additional_retrieve_list", []) # symlinks if "parent_calc_folder" in self.inputs: comp_uuid = self.inputs.parent_calc_folder.computer.uuid remote_path = self.inputs.parent_calc_folder.get_remote_path() symlink = (comp_uuid, remote_path, self._DEFAULT_PARENT_CALC_FLDR_NAME) calcinfo.remote_symlink_list.append(symlink) # check for left over settings if settings: raise InputValidationError( "The following keys have been found " + "in the settings input node {}, ".format(self.pk) + "but were not understood: " + ",".join(settings.keys())) return calcinfo