Esempio n. 1
0
    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
Esempio n. 2
0
    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
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
0
    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):  # 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
Esempio n. 8
0
 def _prepare_codeinfo(self):
     # Prepare CodeInfo object for aiida
     codeinfo = CodeInfo()
     codeinfo.code_uuid = self.inputs.code.uuid
     codeinfo.stdin_name = self.inputs.metadata.options.input_filename
     return codeinfo