Esempio n. 1
0
def parse_state_card(corestateNode, iteration_node, parser_info=None):
    """
    Parses the ONE core state card

    :params corestateNode: an etree element (node), of a fleur output corestate card
    :params iteration_node: an etree element, iteration node
    :params jspin: integer 1 or 2

    :returns: a pythondict of type:

    .. code-block:: python

            {'eigenvalue_sum' : eigenvalueSum,
             'corestates': states,
             'spin' : spin,
             'kin_energy' : kinEnergy,
             'atomtype' : atomtype}

    """
    ##### all xpath of density convergence card (maintain) ########
    coreStates_xpath = 'coreStates'
    state_xpath = 'state'

    units_name = 'units'
    value_name = 'value'
    distance_name = 'distance'

    n_name = 'n'
    j_name = 'j'
    l_name = 'l'
    energy_name = 'energy'
    weight_name = 'weight'
    spin_name = 'spin'
    kinEnergy_name = 'kinEnergy'
    eigenvalueSum_name = 'eigValSum'
    lostElectrons_name = 'lostElectrons'
    atomtype_name = 'atomType'
    #######
    if parser_info is None:
        parser_info = {'parser_warnings': []}

    atomtype = get_xml_attribute(corestateNode, atomtype_name, parser_info)

    kinEnergy = get_xml_attribute(corestateNode, kinEnergy_name, parser_info)
    vE2, suc = convert_to_float(kinEnergy, parser_info)
    eigenvalueSum = get_xml_attribute(corestateNode, eigenvalueSum_name,
                                      parser_info)
    vE2, suc = convert_to_float(eigenvalueSum, parser_info)

    spin = get_xml_attribute(corestateNode, spin_name, parser_info)
    #print('spin {}'.format(spin))
    #states = corestateNode.xpath(
    #for state in states:

    # get all corestate tags, (atomtypes * spin)
    #corestateNodes = eval_xpath(iteration_node, coreStates_xpath, parser_info)
    # for every corestate tag parse the attributes

    # some only the first interation, then get all state tags of the corestate tag (atom depended)
    # parse each core state #Attention to spin
    states = []
    corestates = eval_xpath2(corestateNode, state_xpath)  #, parser_info)

    for corestate in corestates:  # be careful that corestates is a list
        state_dict = {}
        n_state = get_xml_attribute(corestate, n_name, parser_info)
        l_state = get_xml_attribute(corestate, l_name, parser_info)
        j_state = get_xml_attribute(corestate, j_name, parser_info)
        energy, suc = convert_to_float(
            get_xml_attribute(corestate, energy_name, parser_info),
            parser_info)
        weight, suc = convert_to_float(
            get_xml_attribute(corestate, weight_name, parser_info),
            parser_info)
        state_dict = {
            'n': n_state,
            'l': l_state,
            'j': j_state,
            'energy': energy,
            'weight': weight
        }
        states.append(state_dict)

    core_states = {
        'eigenvalue_sum': eigenvalueSum,
        'corestates': states,
        'spin': spin,
        'kin_energy': kinEnergy,
        'atomtype': atomtype
    }
    return core_states
Esempio n. 2
0
    def get_res(self):
        """
        Check how the last Fleur calculation went
        Parse some results.
        """
        self.report('INFO: get results FLEUR')

        xpath_energy = '/fleurOutput/scfLoop/iteration/totalEnergy/@value'
        xpath_iter = '/fleurOutput/scfLoop/iteration'
        xpath_force = 'totalForcesOnRepresentativeAtoms/forceTotal'

        # be aware of magnetism
        xpath_distance = '/fleurOutput/scfLoop/iteration/densityConvergence/chargeDensity/@distance'
        overallchargedensity_xpath = ('/fleurOutput/scfLoop/iteration/densityConvergence/'
                                      'overallChargeDensity/@distance')

        mode = self.ctx.wf_dict.get('mode')
        if self.ctx.parse_last:
            last_base_wc = self.ctx.last_base_wc
            fleur_calcjob = load_node(find_last_submitted_calcjob(last_base_wc))
            walltime = last_base_wc.outputs.output_parameters.dict.walltime
            if isinstance(walltime, int):
                self.ctx.total_wall_time = self.ctx.total_wall_time + walltime
            with fleur_calcjob.outputs.retrieved.open(fleur_calcjob.process_class._OUTXML_FILE_NAME,
                                                      'r') as outxmlfile_opened:
                tree = etree.parse(outxmlfile_opened)
            root = tree.getroot()

            energies = eval_xpath2(root, xpath_energy)
            for energy in energies:
                self.ctx.total_energy.append(float(energy))

            overall_distances = eval_xpath2(root, overallchargedensity_xpath)
            if not overall_distances:
                distances = eval_xpath2(root, xpath_distance)
                for distance in distances:
                    self.ctx.distance.append(float(distance))
            else:
                for distance in overall_distances:
                    self.ctx.distance.append(float(distance))

            if mode == 'force':
                iter_all = eval_xpath2(root, xpath_iter)
                for iteration in iter_all:
                    forces = eval_xpath2(iteration, xpath_force)
                    forces_in_iter = []
                    for force in forces:
                        force_x = float(get_xml_attribute(force, 'F_x'))
                        force_y = float(get_xml_attribute(force, 'F_y'))
                        force_z = float(get_xml_attribute(force, 'F_z'))

                        forces_in_iter.append(force_x)
                        forces_in_iter.append(force_y)
                        forces_in_iter.append(force_z)

                    self.ctx.all_forces.append(forces_in_iter)
        else:
            errormsg = 'ERROR: scf wc was not successful, check log for details'
            self.control_end_wc(errormsg)
            return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED

        if not self.ctx.distance:
            # if fleur relaxes an already converged crystal it stops directly
            if mode == 'force':
                self.report('INFO: System already force converged, could not extract distance.')
                self.ctx.last_charge_density = None
            else:
                errormsg = 'ERROR: did not manage to extract charge density from the calculation'
                self.control_end_wc(errormsg)
                return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED
        else:
            self.ctx.last_charge_density = self.ctx.distance[-1]
Esempio n. 3
0
def validate_nmmpmat(fleurinp_tree, nmmp_lines):
    """
    Checks that the given nmmp_lines is valid with the given fleurinp_tree

    Checks that the number of blocks is as expected from the inp.xml and each
    block does not contain non-zero elements outside their size given by the
    orbital quantum number in the inp.xml. Additionally the occupations, i.e.
    diagonal elements are checked that they are in between 0 and the maximum
    possible occupation

    :param fleurinp_tree_copy: an xmltree that represents inp.xml
    :param nmmp_lines_copy: list of lines in the n_mmp_mat file

    :raises ValueError: if any of the above checks are violated.
    """

    #First check the number of ldau procedures
    ldau_xpath = '/fleurInput/atomSpecies/species/ldaU'
    magnetism_xpath = '/fleurInput/calculationSetup/magnetism'

    #Get number of spins (TODO for develop version also read l_mtnocoPot)
    mag_elem = eval_xpath(fleurinp_tree, magnetism_xpath)
    nspins = convert_to_int(get_xml_attribute(mag_elem, 'jspins'), suc_return=False)

    all_ldau = eval_xpath2(fleurinp_tree, ldau_xpath)
    numRows = nspins * 14 * len(all_ldau)

    tol = 0.01
    if nspins > 1:
        maxOcc = 1.0
    else:
        maxOcc = 2.0

    #Check that numRows matches the number of lines in nmmp_lines
    if nmmp_lines is not None:
        #Remove blank lines
        while '' in nmmp_lines:
            nmmp_lines.remove('')
        if numRows != len(nmmp_lines):
            raise ValueError('The number of lines in n_mmp_mat does not match the number expected from '+\
                             'the inp.xml file.')
    else:
        return

    #Now check for each block if the numbers make sense
    #(no numbers outside the valid area and no nonsensical occupations)
    for ldau_index, ldau in enumerate(all_ldau):

        orbital = convert_to_int(get_xml_attribute(ldau, 'l'), suc_return=False)
        species_name = get_xml_attribute(ldau.getparent(), 'name')

        for spin in range(nspins):
            startRow = (spin * len(all_ldau) + ldau_index) * 14

            for index in range(startRow, startRow + 14):
                currentLine = index - startRow
                currentRow = currentLine // 2

                line = nmmp_lines[index].split('    ')
                while '' in line:
                    line.remove('')
                nmmp = np.array([float(x) for x in line])

                outside_val = False
                if abs(currentRow - 3) > orbital:
                    if any(np.abs(nmmp) > 1e-12):
                        outside_val = True

                if currentLine % 2 == 0:
                    #m=-3 to m=0 real part
                    if any(np.abs(nmmp[:(3 - orbital) * 2]) > 1e-12):
                        outside_val = True

                else:
                    #m=0 imag part to m=3
                    if any(np.abs(nmmp[orbital * 2 + 1:]) > 1e-12):
                        outside_val = True

                if outside_val:
                    raise ValueError(f'Found value outside of valid range in for species {species_name}, spin {spin+1}'
                                     f' and l={orbital}')

                invalid_diag = False
                if spin < 2:
                    if currentRow - 3 <= 0 and currentLine % 2 == 0:
                        if nmmp[currentRow * 2] < -tol or nmmp[currentRow * 2] > maxOcc + tol:
                            invalid_diag = True
                    else:
                        if nmmp[(currentRow - 3) * 2 - 1] < -tol or nmmp[(currentRow - 3) * 2 - 1] > maxOcc + tol:
                            invalid_diag = True

                if invalid_diag:
                    raise ValueError(f'Found invalid diagonal element for species {species_name}, spin {spin+1}'
                                     f' and l={orbital}')
Esempio n. 4
0
def set_nmmpmat(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital, spin,\
                occStates=None, denmat=None, phi=None, theta=None):
    """Routine sets the block in the n_mmp_mat file specified by species_name, orbital and spin
    to the desired density matrix

    :param fleurinp_tree_copy: an xmltree that represents inp.xml
    :param nmmp_lines_copy: list of lines in the n_mmp_mat file
    :param species_name: string, name of the species you want to change
    :param orbital: integer, orbital quantum number of the LDA+U procedure to be modified
    :param spin: integer, specifies which spin block should be modified
    :param occStates: list, sets the diagonal elements of the density matrix and everything
                      else to zero
    :param denmat: matrix, specify the density matrix explicitely
    :param phi: float, optional angle (radian), by which to rotate the density matrix before writing it
    :param theta: float, optional angle (radian), by which to rotate the density matrix before writing it

    :raises ValueError: If something in the input is wrong
    :raises KeyError: If no LDA+U procedure is found on a species
    """

    #All lda+U procedures have to be considered since we need to keep the order
    ldau_xpath = '/fleurInput/atomSpecies/species/ldaU'
    magnetism_xpath = '/fleurInput/calculationSetup/magnetism'

    if species_name == 'all':
        species_xpath = '/fleurInput/atomSpecies/species'
    elif species_name[:4] == 'all-':  #format all-<string>
        species_xpath = '/fleurInput/atomSpecies/species[contains(@name,"{}")]'.format(species_name[4:])
    else:
        species_xpath = '/fleurInput/atomSpecies/species[@name = "{}"]'.format(species_name)

    all_species = eval_xpath2(fleurinp_tree_copy, species_xpath)

    #Get number of spins (TODO for develop version also read l_mtnocoPot)
    mag_elem = eval_xpath(fleurinp_tree_copy, magnetism_xpath)
    nspins = convert_to_int(get_xml_attribute(mag_elem, 'jspins'), suc_return=False)

    if spin > nspins:
        raise ValueError(f'Invalid input: spin {spin} requested, but input has only {nspins} spins')

    all_ldau = eval_xpath2(fleurinp_tree_copy, ldau_xpath)
    numRows = nspins * 14 * len(all_ldau)

    #Check that numRows matches the number of lines in nmmp_lines_copy
    #If not either there was an n_mmp_mat file present in Fleurinp before and a lda+u calculation
    #was added or removed or the n_mmp_mat file was initialized and after the fact lda+u procedures were added
    #or removed. In both cases the resolution of this modification is very involved so we throw an error
    if nmmp_lines_copy is not None:
        #Remove blank lines
        while '' in nmmp_lines_copy:
            nmmp_lines_copy.remove('')
        if numRows != len(nmmp_lines_copy):
            raise ValueError('The number of lines in n_mmp_mat does not match the number expected from '+\
                             'the inp.xml file. Either remove the existing file before making modifications '+\
                             'and only use set_nmmpmat after all modifications to the inp.xml')

    if phi is not None or theta is not None:
        if phi is None:
            phi = 0.0
        if theta is None:
            theta = 0.0
        d_wigner = get_wigner_matrix(orbital, phi, theta)

    for species in all_species:
        current_name = get_xml_attribute(species, 'name')

        #Determine the place at which the given U procedure occurs
        ldau_index = None
        for index, ldau in enumerate(all_ldau):
            ldau_species = get_xml_attribute(ldau.getparent(), 'name')
            ldau_orbital = convert_to_int(get_xml_attribute(ldau, 'l'), suc_return=False)
            if current_name == ldau_species and ldau_orbital == orbital:
                ldau_index = index

        if ldau_index is None:
            raise KeyError(f'No LDA+U procedure found on species {current_name} with l={orbital}')

        if occStates is not None:
            #diagonal density matrix
            denmatpad = np.zeros((7, 7), dtype=complex)

            #Fill out the outer states with zero
            occStatespad = np.zeros(7, dtype=complex)
            occStatespad[3 - orbital:4 + orbital] = occStates[:]

            for i, occ in enumerate(occStatespad):
                denmatpad[i, i] = occ
        elif denmat is not None:
            #density matrix is completely specified
            denmatpad = np.zeros((7, 7), dtype=complex)
            denmatpad[3 - orbital:4 + orbital, 3 - orbital:4 + orbital] = denmat
        else:
            raise ValueError('Invalid definition of density matrix. Provide either occStates or denmat')

        if phi is not None and theta is not None:
            #Rotate the density matrix
            denmatpad = d_wigner.T.conj().dot(denmatpad.dot(d_wigner))

        #check if fleurinp has a specified n_mmp_mat file if not initialize it with 0
        if nmmp_lines_copy is None:
            nmmp_lines_copy = []
            for index in range(numRows):
                nmmp_lines_copy.append(''.join(map(str, [f'{0.0:20.13f}' for x in range(7)])))

        #Select the right block from n_mmp_mat and overwrite it with denmatpad
        startRow = ((spin - 1) * len(all_ldau) + ldau_index) * 14
        for index in range(startRow, startRow + 14):
            currentLine = index - startRow
            currentRow = currentLine // 2
            if currentLine % 2 == 0:
                #Line ends with a real part
                nmmp_lines_copy[index] = ''.join(map(str, [f'{x.real:20.13f}{x.imag:20.13f}'\
                                                           for x in denmatpad[currentRow, :3]])) +\
                                         f'{denmatpad[currentRow, 3].real:20.13f}'
            else:
                #Line begins with a imaginary part
                nmmp_lines_copy[index] = f'{denmatpad[currentRow, 3].imag:20.13f}' +\
                                         ''.join(map(str, [f'{x.real:20.13f}{x.imag:20.13f}'\
                                                           for x in denmatpad[currentRow, 4:]]))

    return nmmp_lines_copy
Esempio n. 5
0
def create_corehole_fleurinp(fleurinp, species, stateocc, pos=[], coreconfig='same', valenceconfig='same'):
    """
    Removes an electron from the core and adds it to the valence band of the kind
    given econfig as in inp.xml [Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2)
    if position(pos) is given the electronConfig for the specifed position will be set.
    (or todo? econfig, either [Kr] 5s2 4d10 4f13 | 5p6 5d4 6s2 or
    [Kr] 2(5s1/2) 4(4d3/2) 6(4d5/2) 6(4f5/2) 8(4f7/2) |2(5p1/2) 4(5p3/2) 2(6s1/2) 2(5d3/2) 2(5d5/2))
    occ tags already there will be untouched, unless the state is the same as given

    :param fleurinp:, an unstored! changes are done on this fleurinp fleurinpdata object # TODO alternatively stored?
    :param species:, string with species name
    :param stateocc: dict state tuples (spinup, spindown), exp: {'(5d3/2)' : (2.5, 0.0), '(4f7/2)' : (3.5 , 4.0)}
    :param pos: list of tuples of 3, pos=[(0.0, 0.0, 0.0), ...]
    :param coreconfig: string, e.g: [Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2), default='same' (same as current in inp.xml)
    :param valenceconfig, string, e.g.: (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2)

    :return: the changes fleurinpData object
    """
    '''
         <electronConfig>
            <coreConfig>[Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2)</coreConfig>
            <valenceConfig>(5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2)</valenceConfig>
            <stateOccupation state="(5d3/2)" spinUp="2.00000000" spinDown=".00000000"/>
            <stateOccupation state="(5d5/2)" spinUp="2.00000000" spinDown=".00000000"/>
         </electronConfig>
    '''
    from aiida_fleur.tools.xml_util import eval_xpath2, get_xml_attribute
    #from aiida_fleur.data.fleurinpmodifier import FleurinpModifier
    # or from fleurinp?

    FleurinpData = DataFactory('fleur.fleurinp')
    ########### all xpath maintain ########### ? needed?
    electronConfig_xpath =  '/fleurInput/atomSpecies/species/electronConfig'
    species_xpath = '/fleurInput/atomSpecies/species'
    #####################

    # test input, if something wrong return/ throw error
    # get electronConfig tag from fleurinp
    # create new tag from old tag and method input
    # return new fleurinp data
    # Best use fleurinp functions
    # Do it with xml or rather

    new_core = False
    new_valence = False

    # test input.
    if not isinstance(fleurinp, FleurinpData):
        print 'No fleurinp Data given to "create_valence_corehole"'
        return None # TODO throw error?

    if coreconfig != 'same':
        new_core = True

    if valenceconfig != 'same':
        new_valence = True
    #print stateocc

    species_tags = fleurinp.get_tag(species_xpath)
    #print species_tags
    for speci in species_tags:
        if get_xml_attribute(speci, 'name') == species:
            # change
            econfig = eval_xpath2(speci, 'electronConfig')[0]
            coreconfig = eval_xpath2(econfig, 'coreConfig')
            valenceconfig = eval_xpath2(econfig, 'valenceConfig')
            occupations = eval_xpath2(econfig, 'stateOccupation')

            for key, val in stateocc.iteritems():
                #print key
                added = False
                for occ in occupations:
                    name = get_xml_attribute(occ, 'state')
                    #print name
                    if name == key:
                        added = True
                        # override and break (can occur only once)
                        occ.set('spinUp', str(val[0]))
                        occ.set('spinDown', str(val[0]))
                        break
                if not added:
                    pass


    #st_inpchanges(change_dict)


    #alternative
    change_dict = {'coreConfig' : '', 'valenceConfig': ''}
    change_dict2 = {'state' : '', 'spinUp' : '', 'spinDown' : ''}
    change_dict3 = {'valenceElectrons' : ''}




    return fleurinp