Beispiel #1
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
Beispiel #2
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
    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!)
        """
        local_copy_list = []
        remote_copy_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()))

        ##############################
        # 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()
        }

        # 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_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))
            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

        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 xml file
        calcinfo.retrieve_list = []
        calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME)
        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