Пример #1
0
    def _prepare_for_submission(self, tempfolder, inputdict):
        """
        This is the routine to be called when you want to create
        the input files and related stuff with a plugin.
        
        :param tempfolder: a aiida.common.folders.Folder subclass where
                           the plugin should put all its files.
        :param inputdict: a dictionary with the input nodes, as they would
                be returned by get_inputdata_dict (without the Code!)
        """
        import numpy as np

        local_copy_list = []
        remote_copy_list = []
        remote_symlink_list = []

        try:
            code = inputdict.pop(self.get_linkname('code'))
        except KeyError:
            raise InputValidationError(
                "No code specified for this calculation")

        try:
            pw_parameters = inputdict.pop(self.get_linkname('pw_parameters'))
        except KeyError:
            raise InputValidationError(
                "No PW parameters specified for this calculation")
        if not isinstance(pw_parameters, ParameterData):
            raise InputValidationError(
                "PW parameters is not of type ParameterData")

        try:
            neb_parameters = inputdict.pop(self.get_linkname('neb_parameters'))
        except KeyError:
            raise InputValidationError(
                "No NEB parameters specified for this calculation")
        if not isinstance(neb_parameters, ParameterData):
            raise InputValidationError(
                "NEB parameters is not of type ParameterData")

        try:
            first_structure = inputdict.pop(
                self.get_linkname('first_structure'))
        except KeyError:
            raise InputValidationError(
                "No initial structure specified for this calculation")
        if not isinstance(first_structure, StructureData):
            raise InputValidationError(
                "Initial structure is not of type StructureData")

        try:
            last_structure = inputdict.pop(self.get_linkname('last_structure'))
        except KeyError:
            raise InputValidationError(
                "No final structure specified for this calculation")
        if not isinstance(last_structure, StructureData):
            raise InputValidationError(
                "Final structure is not of type StructureData")

        try:
            kpoints = inputdict.pop(self.get_linkname('kpoints'))
        except KeyError:
            raise InputValidationError(
                "No kpoints specified for this calculation")
        if not isinstance(kpoints, KpointsData):
            raise InputValidationError("kpoints is not of type KpointsData")

        # Settings can be undefined, and defaults to an empty dictionary
        settings = inputdict.pop(self.get_linkname('settings'), None)
        if settings is None:
            settings_dict = {}
        else:
            if not isinstance(settings, ParameterData):
                raise InputValidationError(
                    "settings, if specified, must be of "
                    "type ParameterData")
            # Settings converted to uppercase
            settings_dict = _uppercase_dict(settings.get_dict(),
                                            dict_name='settings')

        pseudos = {}
        # I create here a dictionary that associates each kind name to a pseudo
        for link in inputdict.keys():
            if link.startswith(self._get_linkname_pseudo_prefix()):
                kindstring = link[len(self._get_linkname_pseudo_prefix()):]
                kinds = kindstring.split('_')
                the_pseudo = inputdict.pop(link)
                if not isinstance(the_pseudo, UpfData):
                    raise InputValidationError(
                        "Pseudo for kind(s) {} is not of "
                        "type UpfData".format(",".join(kinds)))
                for kind in kinds:
                    if kind in pseudos:
                        raise InputValidationError(
                            "Pseudo for kind {} passed "
                            "more than one time".format(kind))
                    pseudos[kind] = the_pseudo

        parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'),
                                           None)
        if parent_calc_folder is not None:
            if not isinstance(parent_calc_folder, RemoteData):
                raise InputValidationError("parent_calc_folder, if specified, "
                                           "must be of type RemoteData")

        vdw_table = inputdict.pop(self.get_linkname('vdw_table'), None)
        if vdw_table is not None:
            if not isinstance(vdw_table, SinglefileData):
                raise InputValidationError("vdw_table, if specified, "
                                           "must be of type SinglefileData")

        # Here, there should be no more parameters...
        if inputdict:
            raise InputValidationError("The following input data nodes are "
                                       "unrecognized: {}".format(
                                           inputdict.keys()))

        # 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 not first_structure.get_site_kindnames(
        ) == last_structure.get_site_kindnames():
            raise InputValidationError(
                "Mismatch between the kind names and/or oder between "
                "the first and final image")

        # Check structure, get species, check peudos
        kindnames = [k.name for k in first_structure.kinds]
        if set(kindnames) != set(pseudos.keys()):
            err_msg = ("Mismatch between the defined pseudos and the list of "
                       "kinds of the structure. Pseudos: {}; kinds: {}".format(
                           ",".join(pseudos.keys()),
                           ",".join(list(kindnames))))
            raise InputValidationError(err_msg)

        ##############################
        # END OF INITIAL INPUT CHECK #
        ##############################
        # 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 (sometimes Quantum
        # Espresso codes crash if an empty folder is not already there
        tempfolder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True)

        # We first prepare the NEB-specific input file
        input_filecontent = self._generate_NEBinputdata(
            neb_parameters, settings_dict)

        input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME)
        with open(input_filename, 'w') as infile:
            infile.write(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)
            input_filecontent, this_local_copy_pseudo_list = self._generate_PWCPinputdata(
                pw_parameters, this_settings_dict, pseudos, structure, kpoints)
            local_copy_pseudo_list += this_local_copy_pseudo_list

            input_filename = tempfolder.get_abs_path('pw_{}.in'.format(i + 1))
            with open(input_filename, 'w') as infile:
                infile.write(input_filecontent)

        # We need to pop the settings that were used in the PW calculations
        for key in settings_dict.keys():
            if key not in 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(
                set([
                    pseudoname
                    for local_path, pseudoname 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 QE.
        if vdw_table:
            local_copy_list.append(
                (vdw_table.get_file_abs_path(),
                 os.path.join(self._PSEUDO_SUBFOLDER,
                              os.path.split(
                                  vdw_table.get_file_abs_path())[1])))

        # operations for restart
        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.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                                  self._OUTPUT_SUBFOLDER,
                                  '*'), self._OUTPUT_SUBFOLDER))
                # and to the old parent prefix.path
                remote_symlink_list.append(
                    (parent_calc_folder.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                                  '{}.path'.format(self._PREFIX)),
                     '{}.path'.format(self._PREFIX)))
        else:
            # copy remote output dir and .path file, if specified
            if parent_calc_folder is not None:
                remote_copy_list.append(
                    (parent_calc_folder.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                                  self._OUTPUT_SUBFOLDER,
                                  '*'), self._OUTPUT_SUBFOLDER))
                # and to the old parent prefix.path
                remote_copy_list.append(
                    (parent_calc_folder.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                                  '{}.path'.format(self._PREFIX)),
                     '{}.path'.format(self._PREFIX)))

        # here we may create an aiida.EXIT file
        create_exit_file = settings_dict.pop('ONLY_INITIALIZATION', False)
        if create_exit_file:
            exit_filename = tempfolder.get_abs_path('{}.EXIT'.format(
                self._PREFIX))
            with open(exit_filename, 'w') as f:
                f.write('\n')

        calcinfo = CalcInfo()
        codeinfo = CodeInfo()

        calcinfo.uuid = self.uuid
        # Empty command line by default
        cmdline_params = settings_dict.pop('CMDLINE', [])
        # For the time-being we only have the initial and final image
        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._OUTPUT_FILE_NAME
        codeinfo.code_uuid = code.uuid
        calcinfo.codes_info = [codeinfo]

        # Retrieve by default the output file and ...
        calcinfo.retrieve_list = []
        calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME)
        calcinfo.retrieve_list.append([
            os.path.join(self._OUTPUT_SUBFOLDER, self._PREFIX + '_*[0-9]',
                         'PW.out'), '.', 2
        ])
        calcinfo.retrieve_list.append([
            os.path.join(self._OUTPUT_SUBFOLDER, self._PREFIX + '_*[0-9]',
                         self._PREFIX + '.save', self._DATAFILE_XML_BASENAME),
            '.', 3
        ])
        settings_retrieve_list = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST',
                                                   [])
        calcinfo.retrieve_list += settings_retrieve_list
        calcinfo.retrieve_list += self._internal_retrieve_list

        if settings_dict:
            try:
                Parserclass = self.get_parserclass()
                parser = Parserclass(self)
                parser_opts = parser.get_parser_settings_key()
                settings_dict.pop(parser_opts)
            except (KeyError, AttributeError
                    ):  # the key parser_opts isn't inside the dictionary
                raise InputValidationError(
                    "The following keys have been found in "
                    "the settings input node, but were not understood: {}".
                    format(",".join(settings_dict.keys())))

        return calcinfo
Пример #2
0
    def _generate_NEBinputdata(self, neb_parameters, settings_dict):
        """ 
        This methods generate the input data for the NEB part of the calculation
        """
        # I put the first-level keys as uppercase (i.e., namelist and card names)
        # and the second-level keys as lowercase
        # (deeper levels are unchanged)
        input_params = _uppercase_dict(neb_parameters.get_dict(),
                                       dict_name='parameters')
        input_params = {
            k: _lowercase_dict(v, dict_name=k)
            for k, v in input_params.iteritems()
        }

        # For the neb input there is no blocked keyword

        # Create an empty dictionary for the compulsory namelist 'PATH'
        # if not present
        if 'PATH' not in input_params:
            input_params['PATH'] = {}

        # In case of climbing image, we need the corresponding card
        climbing_image = False
        if input_params['PATH'].get('ci_scheme',
                                    'no-ci').lower() in ['manual']:
            climbing_image = True
            try:
                climbing_image_list = settings_dict.pop("CLIMBING_IMAGES")
            except KeyError:
                raise InputValidationError(
                    "No climbing image specified for this calculation")
            if not isinstance(climbing_image_list, list):
                raise InputValidationError(
                    "Climbing images should be provided as a list")
            if [
                    i for i in climbing_image_list if i < 2
                    or i >= input_params['PATH'].get('num_of_images', 2)
            ]:
                raise InputValidationError(
                    "The climbing images should be in the range between the first "
                    "and the last image")

            climbing_image_card = "CLIMBING_IMAGES\n"
            climbing_image_card += ", ".join(
                [str(_) for _ in climbing_image_list]) + "\n"

        inputfile = ""
        inputfile += "&PATH\n"
        # namelist content; set to {} if not present, so that we leave an
        # empty namelist
        namelist = input_params.pop('PATH', {})
        for k, v in sorted(namelist.iteritems()):
            inputfile += get_input_data_text(k, v)
        inputfile += "/\n"

        # Write cards now
        if climbing_image:
            inputfile += climbing_image_card

        if input_params:
            raise InputValidationError(
                "The following namelists are specified in input_params, but are "
                "not valid namelists for the current type of calculation: "
                "{}".format(",".join(input_params.keys())))

        return inputfile
Пример #3
0
    def _prepare_for_submission(self, tempfolder, inputdict):
        """
        Routine, which creates the input and prepares for submission

        :param tempfolder: a aiida.common.folders.Folder subclass where
                           the plugin should put all its files.
        :param inputdict: a dictionary with the input nodes, as they would
                be returned by get_inputdata_dict (without the Code!)
        """

        local_copy_list = []
        remote_copy_list = []
        remote_symlink_list = []

        try:
            code = inputdict.pop(self.get_linkname('code'))
        except KeyError:
            raise InputValidationError(
                "No code specified for this calculation")

        try:
            parameters = inputdict.pop(self.get_linkname('parameters'))
        except KeyError:
            raise InputValidationError(
                "No parameters specified for this calculation")
        if not isinstance(parameters, ParameterData):
            raise InputValidationError(
                "parameters is not of type ParameterData")

        # Settings can be undefined, and defaults to an empty dictionary
        settings = inputdict.pop(self.get_linkname('settings'), None)
        if settings is None:
            settings_dict = {}
        else:
            if not isinstance(settings, ParameterData):
                raise InputValidationError(
                    "settings, if specified, must be of "
                    "type ParameterData")
            # Settings converted to uppercase
            settings_dict = _uppercase_dict(settings.get_dict(),
                                            dict_name='settings')

        parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'),
                                           None)

        if parent_calc_folder is not None:
            if not isinstance(parent_calc_folder, self._parent_folder_type):
                if not isinstance(self._parent_folder_type, tuple):
                    possible_types = [self._parent_folder_type.__name__]
                else:
                    possible_types = [
                        t.__name__ for t in self._parent_folder_type
                    ]
                raise InputValidationError("parent_calc_folder, if specified,"
                                           "must be of type {}".format(
                                               " or ".join(possible_types)))

        following_text = self._get_following_text(inputdict, settings)

        # Here, there should be no more parameters...
        if inputdict:
            raise InputValidationError("The following input data nodes are "
                                       "unrecognized: {}".format(
                                           inputdict.keys()))

        # I put the first-level keys as uppercase (i.e., namelist and card names)
        # and the second-level keys as lowercase
        # (deeper levels are unchanged)
        input_params = _uppercase_dict(parameters.get_dict(),
                                       dict_name='parameters')
        input_params = {
            k: _lowercase_dict(v, dict_name=k)
            for k, v in input_params.iteritems()
        }

        # set default values. NOTE: this is different from PW/CP
        for blocked in self._blocked_keywords:
            namelist = blocked[0].upper()
            key = blocked[1].lower()
            value = blocked[2]

            if namelist in input_params:
                if key in input_params[namelist]:
                    raise InputValidationError(
                        "You cannot specify explicitly the '{}' key in the '{}' "
                        "namelist.".format(key, namelist))

            # set to a default
            if not input_params[namelist]:
                input_params[namelist] = {}
            input_params[namelist][key] = value

        # =================== NAMELISTS AND CARDS ========================
        try:
            namelists_toprint = settings_dict.pop('NAMELISTS')
            if not isinstance(namelists_toprint, list):
                raise InputValidationError(
                    "The 'NAMELISTS' value, if specified in the settings input "
                    "node, must be a list of strings")
        except KeyError:  # list of namelists not specified; do automatic detection
            namelists_toprint = self._default_namelists

        input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME)

        with open(input_filename, 'w') as infile:
            for namelist_name in namelists_toprint:
                infile.write("&{0}\n".format(namelist_name))
                # namelist content; set to {} if not present, so that we leave an
                # empty namelist
                namelist = input_params.pop(namelist_name, {})
                for k, v in sorted(namelist.iteritems()):
                    infile.write(get_input_data_text(k, v))
                infile.write("/\n")

            # Write remaning text now, if any
            infile.write(following_text)

        # Check for specified namelists that are not expected
        if input_params:
            raise InputValidationError(
                "The following namelists are specified in input_params, but are "
                "not valid namelists for the current type of calculation: "
                "{}".format(",".join(input_params.keys())))

        # copy remote output dir, if specified
        if parent_calc_folder is not None:
            if isinstance(parent_calc_folder, RemoteData):
                parent_calc_out_subfolder = settings_dict.pop(
                    'PARENT_CALC_OUT_SUBFOLDER', self._INPUT_SUBFOLDER)
                remote_symlink_list.append(
                    (parent_calc_folder.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                                  parent_calc_out_subfolder),
                     self._OUTPUT_SUBFOLDER))

#             elif isinstance(parent_calc_folder,FolderData):
#                 local_copy_list.append(
#                     (parent_calc_folder.get_abs_path(self._INPUT_SUBFOLDER),
#                         self._OUTPUT_SUBFOLDER)
#                     )
#             elif isinstance(parent_calc_folder,SinglefileData):
#                 filename =parent_calc_folder.get_file_abs_path()
#                 local_copy_list.append(
#                     (filename, os.path.basename(filename))
#                  )

        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

        # Add any custom copy/sym links
        remote_symlink_list += settings_dict.pop("ADDITIONAL_SYMLINK_LIST", [])
        calcinfo.remote_symlink_list = remote_symlink_list

        codeinfo = CodeInfo()
        codeinfo.cmdline_params = settings_dict.pop('CMDLINE', [])
        codeinfo.stdin_name = self._INPUT_FILE_NAME
        codeinfo.stdout_name = self._OUTPUT_FILE_NAME
        codeinfo.code_uuid = code.uuid
        calcinfo.codes_info = [codeinfo]

        # Retrieve by default the output file and the .dat files with the dielectric function
        # (all the optical properties can be reconstructed starting from that)
        calcinfo.retrieve_list = []
        calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME)
        calcinfo.retrieve_list.append(self._OUTPUT_DIELECTRIC_IM)
        calcinfo.retrieve_list.append(self._OUTPUT_DIELECTRIC_RE)
        settings_retrieve_list = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST',
                                                   [])
        calcinfo.retrieve_list += settings_retrieve_list
        calcinfo.retrieve_list += self._internal_retrieve_list
        calcinfo.retrieve_singlefile_list = self._retrieve_singlefile_list

        if settings_dict:
            try:
                Parserclass = self.get_parserclass()
                parser = Parserclass(self)
                parser_opts = parser.get_parser_settings_key()
                settings_dict.pop(parser_opts)
            except (
                    KeyError, AttributeError
            ):  # the key parser_opts isn't inside the dictionary, or it is set to None
                raise InputValidationError(
                    "The following keys have been found in "
                    "the settings input node, but were not understood: {}".
                    format(",".join(settings_dict.keys())))

        return calcinfo
Пример #4
0
    def _prepare_for_submission(self,tempfolder,inputdict):        
        """
        This is the routine to be called when you want to create
        the input files and related stuff with a plugin.
        
        :param tempfolder: a aiida.common.folders.Folder subclass where
                           the plugin should put all its files.
        :param inputdict: a dictionary with the input nodes, as they would
                be returned by get_inputdata_dict (without the Code!)
        """
        try:
            code = inputdict.pop(self.get_linkname('code'))
        except KeyError:
            raise InputValidationError("No code specified for this calculation")

        local_copy_list = []
        remote_copy_list = []
        remote_symlink_list = []
        
        try:
            parameters = inputdict.pop(self.get_linkname('parameters'))
        except KeyError:
            raise InputValidationError("No parameters specified for this calculation")
        if not isinstance(parameters, ParameterData):
            raise InputValidationError("parameters is not of type ParameterData")
        
        try:
            qpoints = inputdict.pop(self.get_linkname('qpoints'))
        except KeyError:
            raise InputValidationError("No qpoints specified for this calculation")
        if not isinstance(qpoints, KpointsData):
            raise InputValidationError("qpoints is not of type KpointsData")

        # Settings can be undefined, and defaults to an empty dictionary.
        # They will be used for any input that doen't fit elsewhere.
        settings = inputdict.pop(self.get_linkname('settings'),None)
        if settings is None:
            settings_dict = {}
        else:
            if not isinstance(settings,  ParameterData):
                raise InputValidationError("settings, if specified, must be of "
                                           "type ParameterData")
            # Settings converted to uppercase
            settings_dict = _uppercase_dict(settings.get_dict(),
                                            dict_name='settings')

        parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'),None)
        if parent_calc_folder is None:
            raise InputValidationError("No parent calculation found, needed to "
                                       "compute phonons")
        # TODO: to be a PwCalculation is not sufficient: it could also be a nscf
        # calculation that is invalid for phonons
        
        if not isinstance(parent_calc_folder, RemoteData):
            raise InputValidationError("parent_calc_folder, if specified,"
                                       "must be of type RemoteData")

        restart_flag = False
        # extract parent calculation
        parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation)
        n_parents = len(parent_calcs)
        if n_parents != 1:
            raise UniquenessError("Input RemoteData is child of {} "
                                  "calculation{}, while it should have "
                                  "a single parent".format(n_parents,
                                                "" if n_parents==0 else "s"))
        parent_calc = parent_calcs[0]
        # check that it is a valid parent
        self._check_valid_parent(parent_calc)
        
        if not isinstance(parent_calc, PwCalculation):
            restart_flag = True

        # Also, the parent calculation must be on the same computer
        new_comp = self.get_computer()
        old_comp = parent_calc.get_computer()
        if ( not new_comp.uuid == old_comp.uuid ):
            raise InputValidationError("PhCalculation must be launched on the same computer"
                          " of the parent: {}".format(old_comp.get_name()))

        # put by default, default_parent_output_folder = ./out
        try:
            default_parent_output_folder = parent_calc._OUTPUT_SUBFOLDER
        except AttributeError:
            try:
                default_parent_output_folder = parent_calc._get_output_folder()
            except AttributeError:
                raise InputValidationError("Parent of PhCalculation  does not "
                                           "have a default output subfolder")
        #os.path.join(
        #                   parent_calc.OUTPUT_SUBFOLDER, 
        #                  '{}.save'.format(parent_calc.PREFIX))
        parent_calc_out_subfolder = settings_dict.pop('PARENT_CALC_OUT_SUBFOLDER',
                                                      default_parent_output_folder)      

        # Here, there should be no other inputs
        if inputdict:
            raise InputValidationError("The following input data nodes are "
                "unrecognized: {}".format(inputdict.keys()))

        ##############################
        # END OF INITIAL INPUT CHECK #
        ##############################

        # I put the first-level keys as uppercase (i.e., namelist and card names)
        # and the second-level keys as lowercase
        # (deeper levels are unchanged)
        input_params = _uppercase_dict(parameters.get_dict(),
                                       dict_name='parameters')
        input_params = {k: _lowercase_dict(v, dict_name=k) 
                        for k, v in input_params.iteritems()}

        prepare_for_d3 = settings_dict.pop('PREPARE_FOR_D3',False)
        if prepare_for_d3:
            self._blocked_keywords += [('INPUTPH', 'fildrho'),
                                       ('INPUTPH', 'drho_star%open'),
                                       ('INPUTPH', 'drho_star%ext'),
                                       ('INPUTPH', 'drho_star%dir')]
        
        # I remove unwanted elements (for the moment, instead, I stop; to change when
        # we setup a reasonable logging)
        for nl, flag in self._blocked_keywords:
            if nl in input_params:
                if flag in input_params[nl]:
                    raise InputValidationError(
                        "You cannot specify explicitly the '{}' flag in the '{}' "
                        "namelist or card.".format(flag, nl))
        
        # Set some variables (look out at the case! NAMELISTS should be uppercase,
        # internal flag names must be lowercase)
        if 'INPUTPH' not in input_params:
            raise InputValidationError("No namelist INPUTPH found in input") # I cannot decide what to do in the calculation
        input_params['INPUTPH']['outdir'] = self._OUTPUT_SUBFOLDER
        input_params['INPUTPH']['iverbosity'] = 1 # in human language 1=high
        input_params['INPUTPH']['prefix'] = self._PREFIX
        input_params['INPUTPH']['fildyn'] = self._OUTPUT_DYNAMICAL_MATRIX_PREFIX
        if prepare_for_d3:
            input_params['INPUTPH']['fildrho'] = self._DRHO_PREFIX
            input_params['INPUTPH']['drho_star%open'] = True
            input_params['INPUTPH']['drho_star%ext'] = self._DRHO_STAR_EXT
            input_params['INPUTPH']['drho_star%dir'] = self._FOLDER_DRHO
        
        # qpoints part
        try:
            mesh,offset = qpoints.get_kpoints_mesh()
            
            if any([i!=0. for i in offset]):
                raise NotImplementedError("Computation of phonons on a mesh with"
                    " non zero offset is not implemented, at the level of ph.x")
            
            input_params["INPUTPH"]["ldisp"] = True
            input_params["INPUTPH"]["nq1"] = mesh[0]
            input_params["INPUTPH"]["nq2"] = mesh[1]
            input_params["INPUTPH"]["nq3"] = mesh[2]
            
            postpend_text = None
            
        except AttributeError:
            # this is the case where no mesh was set. Maybe it's a list
            try:
                list_of_points = qpoints.get_kpoints(cartesian=True)
            except AttributeError as e:
                # In this case, there are no info on the qpoints at all
                raise InputValidationError("Neither a qpoints mesh or a valid "
                                           "list of qpoints was found in input",
                                           e.message)
            # change to 2pi/a coordinates
            lattice_parameter = numpy.linalg.norm(qpoints.cell[0])
            list_of_points *= lattice_parameter / (2.*numpy.pi)
            # add here the list of point coordinates            
            if len(list_of_points)>1:
                input_params["INPUTPH"]["qplot"] = True
                input_params["INPUTPH"]["ldisp"] = True
                postpend_text = "{}\n".format(len(list_of_points))
                for points in list_of_points:
                    postpend_text += "{}  {}  {}  1\n".format(*points)
                # Note: the weight is fixed to 1, because ph.x calls these 
                # things weights but they are not such. If they are going to 
                # exist with the meaning of weights, they will be supported
            else:
                input_params["INPUTPH"]["ldisp"] = False
                postpend_text = ""
                for points in list_of_points:
                    postpend_text += "{}  {}  {}\n".format(*points)
            
        
        # =================== NAMELISTS ========================
        
        # customized namelists, otherwise not present in the distributed ph code
        try:
            namelists_toprint = settings_dict.pop('NAMELISTS')
            if not isinstance(namelists_toprint, list):
                raise InputValidationError(
                    "The 'NAMELISTS' value, if specified in the settings input "
                    "node, must be a list of strings")
        except KeyError: # list of namelists not specified in the settings; do automatic detection
            namelists_toprint = self._compulsory_namelists
        
        input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME)

        # create a folder for the dynamical matrices
        if not restart_flag: # if it is a restart, it will be copied over
            tempfolder.get_subfolder(self._FOLDER_DYNAMICAL_MATRIX,
                                     create=True)
        
        with open(input_filename,'w') as infile:
            infile.write('AiiDA calculation\n')
            for namelist_name in namelists_toprint:
                infile.write("&{0}\n".format(namelist_name))
                # namelist content; set to {} if not present, so that we leave an 
                # empty namelist
                namelist = input_params.pop(namelist_name,{})
                for k, v in sorted(namelist.iteritems()):
                    infile.write(get_input_data_text(k,v))
                infile.write("/\n")
            
            # add list of qpoints if required
            if postpend_text is not None:
                infile.write(postpend_text)
            
            #TODO: write nat_todo
            
        if input_params:
            raise InputValidationError(
                "The following namelists are specified in input_params, but are "
                "not valid namelists for the current type of calculation: "
                "{}".format(",".join(input_params.keys())))
        
        # copy the parent scratch
        symlink = settings_dict.pop('PARENT_FOLDER_SYMLINK',
                                    _default_symlink_usage) # a boolean
        if symlink:
            # I create a symlink to each file/folder in the parent ./out
            tempfolder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True)
            
            remote_symlink_list.append( (parent_calc_folder.get_computer().uuid,
                                         os.path.join(parent_calc_folder.get_remote_path(),
                                                      parent_calc_out_subfolder,
                                                      "*"),
                                         self._OUTPUT_SUBFOLDER
                                         ) )
            
            # I also create a symlink for the ./pseudo folder
            # TODO: suppress this when the recover option of QE will be fixed 
            # (bug when trying to find pseudo file) 
            remote_symlink_list.append((parent_calc_folder.get_computer().uuid,
                                        os.path.join(parent_calc_folder.get_remote_path(),
                                                     self._get_pseudo_folder()),
                                        self._get_pseudo_folder()
                                        ))
            #pass
        else:
            # here I copy the whole folder ./out
            remote_copy_list.append(
                (parent_calc_folder.get_computer().uuid,
                 os.path.join(parent_calc_folder.get_remote_path(),
                              parent_calc_out_subfolder),
                 self._OUTPUT_SUBFOLDER))
            # I also copy the ./pseudo folder
            # TODO: suppress this when the recover option of QE will be fixed 
            # (bug when trying to find pseudo file) 
            remote_copy_list.append(
                (parent_calc_folder.get_computer().uuid,
                 os.path.join(parent_calc_folder.get_remote_path(),
                              self._get_pseudo_folder()),
                        self._get_pseudo_folder()))
            
        
        if restart_flag: # in this case, copy in addition also the dynamical matrices
            if symlink:
                remote_symlink_list.append(
                    (parent_calc_folder.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                              self._FOLDER_DYNAMICAL_MATRIX),
                     self._FOLDER_DYNAMICAL_MATRIX))

            else:
                # copy the dynamical matrices
                remote_copy_list.append(
                    (parent_calc_folder.get_computer().uuid,
                     os.path.join(parent_calc_folder.get_remote_path(),
                              self._FOLDER_DYNAMICAL_MATRIX),
                     '.'))
                # no need to copy the _ph0, since I copied already the whole ./out folder
        
        # here we may create an aiida.EXIT file
        create_exit_file = settings_dict.pop('ONLY_INITIALIZATION',False)
        if create_exit_file:
            exit_filename = tempfolder.get_abs_path(
                             '{}.EXIT'.format(self._PREFIX))
            with open(exit_filename,'w') as f:
                f.write('\n')

        calcinfo = CalcInfo()
        
        calcinfo.uuid = self.uuid
        # Empty command line by default
        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
        
        codeinfo = CodeInfo()
        codeinfo.cmdline_params = (list(cmdline_params)
                                   + ["-in", self._INPUT_FILE_NAME])
        codeinfo.stdout_name = self._OUTPUT_FILE_NAME
        codeinfo.code_uuid = code.uuid
        calcinfo.codes_info = [codeinfo]
        
        # Retrieve by default the output file and the xml file
        calcinfo.retrieve_list = []
        calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME)
        calcinfo.retrieve_list.append(self._FOLDER_DYNAMICAL_MATRIX)
        calcinfo.retrieve_list.append(  
                os.path.join(self._OUTPUT_SUBFOLDER,
                             '_ph0',
                             '{}.phsave'.format(self._PREFIX),
                             self._OUTPUT_XML_TENSOR_FILE_NAME))
        
        extra_retrieved = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', [])
        for extra in extra_retrieved:
            calcinfo.retrieve_list.append( extra )
        
        if settings_dict:
            raise InputValidationError("The following keys have been found in "
                "the settings input node, but were not understood: {}".format(
                ",".join(settings_dict.keys())))
        
        return calcinfo
Пример #5
0
def parse_namelists(txt):
    """
    Parse txt to extract a dictionary of the namelist info.
    
    :param txt: A single string containing the QE input text to be parsed.
    :type txt: str

    :returns:
        A nested dictionary of the namelists and their key-value pairs. The
        namelists will always be upper-case keys, while the parameter keys will
        always be lower-case.

        For example::

            {"CONTROL": {"calculation": "bands",
                         "prefix": "al",
                         "pseudo_dir": "./pseudo",
                         "outdir": "./out"},
             "ELECTRONS": {"diagonalization": "cg"},
             "SYSTEM": {"nbnd": 8,
                        "ecutwfc": 15.0,
                        "celldm(1)": 7.5,
                        "ibrav": 2,
                        "nat": 1,
                        "ntyp": 1}
            }

    :raises aiida.common.exceptions.ParsingError: if there are issues
        parsing the input.
    """
    # TODO: Incorporate support for algebraic expressions?
    # Define the re to match a namelist and extract the info from it.
    namelist_re = re.compile(
        r"""
        ^ [ \t]* &(\S+) [ \t]* $\n  # match line w/ nmlst tag; save nmlst name
        (
         [\S\s]*?                # match any line non-greedily
        )                        # save the group of text between nmlst
        ^ [ \t]* / [ \t]* $\n    # match line w/ "/" as only non-whitespace char
        """, re.M | re.X)
    # Define the re to match and extract all of the key = val pairs inside
    # a block of namelist text.
    key_value_re = re.compile(
        r"""
        [ \t]* (\S+?) [ \t]*  # match and store key
        =               # equals sign separates key and value
        [ \t]* (\S+?) [ \t]*  # match and store value
        [\n,]           # return or comma separates "key = value" pairs
        """, re.M | re.X)
    # Scan through the namelists...
    params_dict = {}
    for nmlst, blockstr in namelist_re.findall(txt):
        # ...extract the key value pairs, storing them each in nmlst_dict,...
        nmlst_dict = {}
        for key, valstr in key_value_re.findall(blockstr):
            nmlst_dict[key.lower()] = str2val(valstr)
        # ...and, store nmlst_dict as a value in params_dict with the namelist
        # as the key.
        if len(nmlst_dict.keys()) > 0:
            params_dict[nmlst.upper()] = nmlst_dict
    if len(params_dict) == 0:
        raise ParsingError(
            'No data was found while parsing the namelist in the following '
            'text\n' + txt)
    return _uppercase_dict(params_dict, "params_dict")