示例#1
0
def cmd_export(
    group, max_atoms, max_atomic_number, number_species, partial_occupancies, include_duplicates, no_cod_hydrogen,
    sssp_only, filename
):
    """Pass."""
    from aiida import orm
    from aiida.common.constants import elements
    from aiida.tools.importexport import export

    filters_elements = set()
    filters_structures = {'and': []}

    if no_cod_hydrogen:
        filters_structures['and'].append({'id': {'!in': get_cod_hydrogen_structure_ids()}})

    if max_atoms is not None:
        filters_structures['and'].append({'attributes.sites': {'shorter': max_atoms + 1}})

    if max_atomic_number:
        filters_elements = filters_elements.union({e['symbol'] for z, e in elements.items() if z > max_atomic_number})

    if sssp_only:
        # All elements with atomic number of Radon or lower, with the exception of Astatine
        filters_elements = filters_elements.union({e['symbol'] for z, e in elements.items() if z > 86 or z == 85})

    builder = orm.QueryBuilder().append(
        orm.Group, filters={'id': group.pk}, tag='group').append(
        orm.StructureData, with_group='group', filters=filters_structures)

    duplicates = []

    if max_atomic_number or sssp_only:
        structures = []
        for structure, in builder.iterall():
            if all([element not in filters_elements for element in structure.get_symbols_set()]):
                structures.append(structure)
    else:
        structures = builder.all(flat=True)

    if include_duplicates:
        for structure in structures:
            dupes = []
            structure_duplicates = structure.get_extra('duplicates')
            for database, uuids in structure_duplicates.items():
                dupes.extend(uuids)
            for duplicate in dupes:
                if duplicate != structure.uuid:
                    duplicates.append(orm.load_node(duplicate))

    export(structures + duplicates, outfile=filename, create_backward=False, return_backward=False)
示例#2
0
def change_struc_imp_aux_wf(struc, imp_info): # Note: works for single imp at center only!
    from aiida.common.constants import elements as PeriodicTableElements
    _atomic_numbers = {data['symbol']: num for num, data in PeriodicTableElements.items()}

    new_struc = StructureData(cell=struc.cell)
    new_struc.pbc = struc.pbc # take also pbc values from parent struc
    isite = 0
    for site in struc.sites:
        sname = site.kind_name
        kind = struc.get_kind(sname)
        pos = site.position
        # intermediate fix to avoid crash for old structures with vacuum:'{H0.00X1.00}'
        # use atom kind='X' in the future for new structures
        if kind.get_symbols_string()=='{H0.00X1.00}':
            zatom = 0
        else:
            zatom = _atomic_numbers[kind.get_symbols_string()]
        if isite == imp_info.get_dict().get('ilayer_center'):
            zatom = imp_info.get_dict().get('Zimp')
            if type(zatom)==list:
              zatom = zatom[0] # here this works for single impurity only!
        symbol = PeriodicTableElements.get(zatom).get('symbol')
        new_struc.append_atom(position=pos, symbols=symbol)
        isite += 1

    return new_struc
示例#3
0
def structure_to_spglib_tuple(structure):
    """
    Convert an AiiDA structure to a tuple of the format (cell, scaled_positions, element_numbers).

    :param structure: the AiiDA structure
    :return: (structure_tuple, kind_info, kinds) where structure_tuple
        is a tuple of format (cell, scaled_positions, element_numbers);
        kind_info is a dictionary mapping the kind_names to
        the numbers used in element_numbers. When possible, it uses
        the Z number of the element, otherwise it uses numbers > 1000;
        kinds is a list of the kinds of the structure.
    """
    def get_new_number(the_list, start_from):
        """
        Get the first integer >= start_from not yet in the list
        """
        retval = start_from
        comp_list = sorted(_ for _ in the_list if _ >= start_from)

        current_pos = 0
        found = False
        while not found:
            if len(comp_list) <= current_pos:
                return retval
            if retval == comp_list[current_pos]:
                current_pos += 1
                retval += 1
            else:
                found = True
                return retval

    z_numbers = {v['symbol']: k for k, v in elements.items()}

    cell = np.array(structure.cell)
    abs_pos = np.array([_.position for _ in structure.sites])
    rel_pos = np.dot(abs_pos, np.linalg.inv(cell))
    # kinds = {k.name: k for k in structure.kinds}

    kind_numbers = {}
    for kind in structure.kinds:
        if len(kind.symbols) == 1:
            realnumber = z_numbers[kind.symbols[0]]
            if realnumber in kind_numbers.values():
                number = get_new_number(list(kind_numbers.values()),
                                        start_from=realnumber * 1000)
            else:
                number = realnumber
            kind_numbers[kind.name] = number
        else:
            number = get_new_number(list(kind_numbers.values()),
                                    start_from=200000)
            kind_numbers[kind.name] = number

    numbers = [kind_numbers[s.kind_name] for s in structure.sites]

    return ((cell, rel_pos, numbers), kind_numbers, list(structure.kinds))
示例#4
0
def get_structure_data(structure):
    """
    Function to take data from AiiDA's StructureData type and store it into a single numpy array of the following form:
    a = [[x-Position 1st atom, y-Position 1st atom, z-Position 1st atom, index 1st atom, charge 1st atom, 0.],
         [x-Position 2nd atom, y-Position 2nd atom, z-Position 2nd atom, index 2nd atom, charge 1st atom, 0.],
         [..., ..., ..., ..., ..., ...],
         ...
         ]

    :param structure: input structure of the type StructureData

    :return: numpy array a[# of atoms in the unit cell][5] containing the structure related data (positions in units
             of the unit cell length)

    :note:
    """

    #import packages
    from aiida.common.constants import elements as PeriodicTableElements
    import numpy as np

    #get the connection between coordination number and element symbol
    _atomic_numbers = {
        data['symbol']: num
        for num, data in PeriodicTableElements.items()
    }

    #initialize the array that will be returned later (it will be a (# of atoms in the cell) x 6-matrix)
    a = np.zeros((len(structure.sites), 6))
    k = 0  #running index for filling up the array with data correctly
    charges = [
    ]  #will be needed to return the charge number for the different atoms later

    #loop to fill up the a-array with positions, index, charge and a 0. for every atom in the cell
    sites = structure.sites
    n = len(structure.sites) + 1  #needed to do the indexing of atoms
    m = len(structure.sites)  #needed to do the indexing of atoms
    for site in sites:
        for j in range(3):
            a[k][j] = site.position[j]
        sitekind = structure.get_kind(site.kind_name)
        naez = n - m
        m = m - 1
        #convert the element symbol from StructureData to the charge number
        for ikind in range(len(sitekind.symbols)):
            site_symbol = sitekind.symbols[ikind]
            charges.append(_atomic_numbers[site_symbol])
        i = len(charges) - 1
        a[k][3] = int(naez)
        a[k][4] = float(charges[i])
        k = k + 1

    return a
示例#5
0
    def _prepare_xsf(self, index=None, main_file_name=''):  # pylint: disable=unused-argument
        """
        Write the given trajectory to a string of format XSF (for XCrySDen).
        """
        from aiida.common.constants import elements
        _atomic_numbers = {
            data['symbol']: num
            for num, data in elements.items()
        }

        indices = list(range(self.numsteps))
        if index is not None:
            indices = [index]
        return_string = 'ANIMSTEPS {}\nCRYSTAL\n'.format(len(indices))
        # Do the checks once and for all here:
        structure = self.get_step_structure(index=0)
        if structure.is_alloy or structure.has_vacancies:
            raise NotImplementedError(
                'XSF for alloys or systems with vacancies not implemented.')
        cells = self.get_cells()
        if cells is None:
            raise ValueError(
                'No cell parameters have been supplied for TrajectoryData')
        positions = self.get_positions()
        symbols = self.symbols
        atomic_numbers_list = [_atomic_numbers[s] for s in symbols]
        nat = len(symbols)

        for idx in indices:
            return_string += 'PRIMVEC {}\n'.format(idx + 1)
            for cell_vector in cells[idx]:
                return_string += ' '.join(
                    ['{:18.5f}'.format(i) for i in cell_vector])
                return_string += '\n'
            return_string += 'PRIMCOORD {}\n'.format(idx + 1)
            return_string += '{} 1\n'.format(nat)
            for atn, pos in zip(atomic_numbers_list, positions[idx]):
                try:
                    return_string += '{} {:18.10f} {:18.10f} {:18.10f}\n'.format(
                        atn, pos[0], pos[1], pos[2])
                except:
                    print(atn, pos)
                    raise
        return return_string.encode('utf-8'), {}
示例#6
0
def _tuple_to_aiida(structure_tuple, kind_info=None, kinds=None):
    """
    Convert an tuple of the format
    (cell, scaled_positions, element_numbers) to an AiiDA structure.

    Unless the element_numbers are identical to the Z number of the atoms,
    you should pass both kind_info and kinds, with the same format as returned
    by get_tuple_from_aiida_structure.

    .. deprecated:: 1.8
      Use the methods in AiiDA instead.

    :param structure_tuple: the structure in format (structure_tuple, kind_info)
    :param kind_info: a dictionary mapping the kind_names to
       the numbers used in element_numbers. If not provided, assumes {element_name: element_Z}
    :param kinds: a list of the kinds of the structure.
    """
    import warnings
    warnings.warn(
        'this method has been deprecated and moved to AiiDA, see {}'.format(
            DEPRECATION_DOCS_URL), DeprecationWarning)

    from aiida.common.constants import elements
    from aiida.orm.data.structure import Kind, Site, StructureData
    import numpy as np
    import copy

    if kind_info is None and kinds is not None:
        raise ValueError("If you pass kind_info, you should also pass kinds")
    if kinds is None and kind_info is not None:
        raise ValueError("If you pass kinds, you should also pass kind_info")

    Z = {v['symbol']: k for k, v in elements.items()}
    cell, rel_pos, numbers = structure_tuple
    if kind_info:
        _kind_info = copy.copy(kind_info)
        _kinds = copy.copy(kinds)
    else:
        try:
            # For each site
            symbols = [elements[num]['symbol'] for num in numbers]
        except KeyError as e:
            raise ValueError(
                "You did not pass kind_info, but at least one number "
                "is not a valid Z number: {}".format(e.message))

        _kind_info = {elements[num]['symbol']: num for num in set(numbers)}
        # Get the default kinds
        _kinds = [Kind(symbols=sym) for sym in set(symbols)]

    _kinds_dict = {k.name: k for k in _kinds}
    # Now I will use in any case _kinds and _kind_info
    if len(_kind_info.values()) != len(set(_kind_info.values())):
        raise ValueError(
            "There is at least a number repeated twice in kind_info!")
    # Invert the mapping
    mapping_num_kindname = {v: k for k, v in _kind_info.items()}
    # Create the actual mapping
    try:
        mapping_to_kinds = {
            num: _kinds_dict[kindname]
            for num, kindname in mapping_num_kindname.items()
        }
    except KeyError as e:
        raise ValueError("Unable to find '{}' in the kinds list".format(
            e.message))

    try:
        site_kinds = [mapping_to_kinds[num] for num in numbers]
    except KeyError as e:
        raise ValueError(
            "Unable to find kind in kind_info for number {}".format(e.message))

    out_structure = StructureData(cell=cell)
    for k in _kinds:
        out_structure.append_kind(k)
    abs_pos = np.dot(rel_pos, cell)
    if len(abs_pos) != len(site_kinds):
        raise ValueError(
            "The length of the positions array is different from the "
            "length of the element numbers")

    for kind, pos in zip(site_kinds, abs_pos):
        out_structure.append_site(Site(kind_name=kind.name, position=pos))

    return out_structure
示例#7
0
def _aiida_to_tuple(aiida_structure):
    """
    Convert an AiiDA structure to a tuple of the format
    (cell, scaled_positions, element_numbers).

    .. deprecated:: 1.8
      Use the methods in AiiDA instead.

    :param aiida_structure: the AiiDA structure
    :return: (structure_tuple, kind_info, kinds) where structure_tuple
       is a tuple of format (cell, scaled_positions, element_numbers);
       kind_info is a dictionary mapping the kind_names to
       the numbers used in element_numbers. When possible, it uses
       the Z number of the element, otherwise it uses numbers > 1000;
       kinds is a list of the kinds of the structure.
    """
    import warnings
    warnings.warn(
        'this method has been deprecated and moved to AiiDA, see {}'.format(
            DEPRECATION_DOCS_URL), DeprecationWarning)

    import numpy as np
    from aiida.common.constants import elements

    def get_new_number(the_list, start_from):
        """
        Get the first integer >= start_from not yet in the list
        """
        retval = start_from
        comp_list = sorted(_ for _ in the_list if _ >= start_from)

        current_pos = 0
        found = False
        while not found:
            if len(comp_list) <= current_pos:
                return retval
            if retval == comp_list[current_pos]:
                current_pos += 1
                retval += 1
            else:
                found = True
                return retval

    Z = {v['symbol']: k for k, v in elements.items()}

    cell = np.array(aiida_structure.cell)
    abs_pos = np.array([_.position for _ in aiida_structure.sites])
    rel_pos = np.dot(abs_pos, np.linalg.inv(cell))
    kinds = {k.name: k for k in aiida_structure.kinds}

    kind_numbers = {}
    for kind in aiida_structure.kinds:
        if len(kind.symbols) == 1:
            realnumber = Z[kind.symbols[0]]
            if realnumber in list(kind_numbers.values()):
                number = get_new_number(list(kind_numbers.values()),
                                        start_from=realnumber * 1000)
            else:
                number = realnumber
            kind_numbers[kind.name] = number
        else:
            number = get_new_number(list(kind_numbers.values()),
                                    start_from=200000)
            kind_numbers[kind.name] = number

    numbers = [kind_numbers[s.kind_name] for s in aiida_structure.sites]

    return ((cell, rel_pos, numbers), kind_numbers,
            list(aiida_structure.kinds))
def generate_inputcard_from_structure(parameters,
                                      structure,
                                      input_filename,
                                      parent_calc=None,
                                      shapes=None,
                                      isvoronoi=False,
                                      use_input_alat=False,
                                      vca_structure=False):
    """
    Takes information from parameter and structure data and writes input file 'input_filename'

    :param parameters: input parameters node containing KKR-related input parameter
    :param structure: input structure node containing lattice information
    :param input_filename: input filename, typically called 'inputcard'

    optional arguments
    :param parent_calc: input parent calculation node used to determine if EMIN
                        parameter is automatically overwritten (from voronoi output)
                        or not
    :param shapes: input shapes array (set automatically by
                   aiida_kkr.calculations.Kkrcaluation and shall not be overwritten)
    :param isvoronoi: tell whether or not the parameter set is for a voronoi calculation or kkr calculation (have different lists of mandatory keys)
    :param use_input_alat: True/False, determines whether the input alat value is taken or the new alat is computed from the Bravais vectors


    :note: assumes valid structure and parameters, i.e. for 2D case all necessary
           information has to be given. This is checked with function
           'check_2D_input' called in aiida_kkr.calculations.Kkrcaluation
    """

    from aiida.common.constants import elements as PeriodicTableElements
    from numpy import array
    from masci_tools.io.kkr_params import kkrparams
    from masci_tools.io.common_functions import get_Ang2aBohr, get_alat_from_bravais
    from aiida_kkr.calculations.voro import VoronoiCalculation

    # initialize list of warnings
    warnings = []

    #list of globally used constants
    a_to_bohr = get_Ang2aBohr()

    # Get the connection between coordination number and element symbol
    # maybe do in a differnt way

    _atomic_numbers = {
        data['symbol']: num
        for num, data in PeriodicTableElements.items()
    }

    # KKR wants units in bohr
    bravais = array(structure.cell) * a_to_bohr
    alat_input = parameters.get_dict().get('ALATBASIS')
    if use_input_alat and alat_input is not None:
        alat = alat_input
        wmess = 'found alat in input parameters, this will trigger scaling of RMAX, GMAX and RCLUSTZ!'
        print('WARNING: ' + wmess)
        warnings.append(wmess)
    else:
        alat = get_alat_from_bravais(bravais, is3D=structure.pbc[2])
    bravais = bravais / alat

    sites = structure.sites
    naez = len(sites)
    positions = []
    charges = []
    weights = []  # for CPA
    isitelist = []  # counter sites array for CPA
    isite = 0
    for site in sites:
        pos = site.position
        #TODO maybe convert to rel pos and make sure that type is right for script (array or tuple)
        abspos = array(pos) * a_to_bohr / alat  # also in units of alat
        positions.append(abspos)
        isite += 1
        sitekind = structure.get_kind(site.kind_name)
        for ikind in range(len(sitekind.symbols)):
            site_symbol = sitekind.symbols[ikind]
            if sitekind.is_alloy:
                wght = sitekind.weights[ikind]
            else:
                wght = 1.
            if not sitekind.has_vacancies:
                zatom_tmp = _atomic_numbers[site_symbol]
            else:
                zatom_tmp = 0.0
            if vca_structure and ikind > 0 and not isvoronoi:
                # for VCA case take weighted average (only for KKR code, voronoi code uses zatom of first site for dummy calculation)
                zatom = zatom * wght_last + zatom_tmp * wght
                # also reset weight to 1
                wght = 1.
            else:
                zatom = zatom_tmp
                if vca_structure and isvoronoi:
                    wght = 1.

            wght_last = wght  # for VCA mode

            # make sure that for VCA only averaged position is written (or first for voronoi code)
            if ((vca_structure and ((len(sitekind.symbols) == 1) or
                                    (not isvoronoi and ikind == 1) or
                                    (isvoronoi and ikind == 0)))
                    or (not vca_structure)):
                charges.append(zatom)
                weights.append(wght)
                isitelist.append(isite)

    weights = array(weights)
    isitelist = array(isitelist)
    charges = array(charges)
    positions = array(positions)

    # workaround for voronoi calculation with Zatom=83 (Bi potential not there!)
    if isvoronoi:
        from numpy import where
        mask_replace_Bi_Pb = where(charges == 83)
        if len(mask_replace_Bi_Pb[0]) > 0:
            charges[mask_replace_Bi_Pb] = 82
            wmess = 'Bi potential not available, using Pb instead!!!'
            print('WARNING: ' + wmess)
            warnings.append(wmess)

    ######################################
    # Prepare keywords for kkr from input structure

    # get parameter dictionary
    input_dict = parameters.get_dict()

    # remove special keys that are used for special cases but are not part of the KKR parameter set
    for key in _ignored_keys:
        if input_dict.get(key) is not None:
            wmess = 'automatically removing value of key {}'.format(key)
            print('WARNING: ' + wmess)
            warnings.append(wmess)
            input_dict.pop(key)

    # get rid of structure related inputs that are overwritten from structure input
    for key in [
            'BRAVAIS', 'ALATBASIS', 'NAEZ', '<ZATOM>', '<RBASIS>', 'CARTESIAN'
    ]:
        if input_dict.get(key) is not None:
            wmess = 'automatically removing value of key {}'.format(key)
            print('WARNING: ' + wmess)
            warnings.append(wmess)
            input_dict.pop(key)

    # automatically rescale RMAX, GMAX, RCLUSTZ, RCLUSTXY which are scaled with the lattice constant
    if alat_input is not None:
        if input_dict.get('RMAX') is not None:
            wmess = 'rescale RMAX: {}'.format(alat_input / alat)
            print('WARNING: ' + wmess)
            warnings.append(wmess)
            input_dict['RMAX'] = input_dict['RMAX'] * alat_input / alat
        if input_dict.get('GMAX') is not None:
            wmess = 'rescale GMAX: {}'.format(1 / (alat_input / alat))
            print('WARNING: ' + wmess)
            warnings.append(wmess)
            input_dict['GMAX'] = input_dict['GMAX'] * 1 / (alat_input / alat)
        if input_dict.get('RCLUSTZ') is not None:
            wmess = 'rescale RCLUSTZ: {}'.format(alat_input / alat)
            print('WARNING: ' + wmess)
            warnings.append(wmess)
            input_dict['RCLUSTZ'] = input_dict['RCLUSTZ'] * alat_input / alat
        if input_dict.get('RCLUSTXY') is not None:
            wmess = 'rescale RCLUSTXY: {}'.format(alat_input / alat)
            print('WARNING: ' + wmess)
            warnings.append(wmess)
            input_dict['RCLUSTXY'] = input_dict['RCLUSTXY'] * alat_input / alat

    # empty kkrparams instance (contains formatting info etc.)
    if not isvoronoi:
        params = kkrparams()
    else:
        params = kkrparams(params_type='voronoi')

    # for KKR calculation set EMIN automatically from parent_calc (always in res.emin of voronoi and kkr) if not provided in input node
    if ('EMIN' not in list(input_dict.keys())
            or input_dict['EMIN'] is None) and parent_calc is not None:
        wmess = 'Overwriting EMIN with value from parent calculation {}'.format(
            parent_calc)
        print('WARNING: ' + wmess)
        warnings.append(wmess)
        if parent_calc.process_class == VoronoiCalculation:
            emin = parent_calc.outputs.output_parameters.get_dict().get('emin')
        else:
            emin = parent_calc.outputs.output_parameters.get_dict().get(
                'energy_contour_group').get('emin')
        print('Setting emin:', emin, 'is emin None?', emin is None)
        params.set_value('EMIN', emin)

    # overwrite keywords with input parameter
    for key in list(input_dict.keys()):
        params.set_value(key, input_dict[key], silent=True)

    # Write input to file (the parameters that are set here are not allowed to be modfied externally)
    params.set_multiple_values(BRAVAIS=bravais,
                               ALATBASIS=alat,
                               NAEZ=naez,
                               ZATOM=charges,
                               RBASIS=positions,
                               CARTESIAN=True)
    # for CPA case:
    if len(weights) > naez:
        natyp = len(weights)
        params.set_value('NATYP', natyp)
        params.set_value('<CPA-CONC>', weights)
        params.set_value('<SITE>', isitelist)
    else:
        natyp = naez

    # write shapes (extracted from voronoi parent automatically in kkr calculation plugin)
    if shapes is not None:
        params.set_value('<SHAPE>', shapes)

    # change input values of 2D input to new alat:
    rbl = params.get_value('<RBLEFT>')
    rbr = params.get_value('<RBRIGHT>')
    zper_l = params.get_value('ZPERIODL')
    zper_r = params.get_value('ZPERIODR')
    if rbl is not None:
        params.set_value('<RBLEFT>', array(rbl) * a_to_bohr / alat)
    if rbr is not None:
        params.set_value('<RBRIGHT>', array(rbr) * a_to_bohr / alat)
    if zper_l is not None:
        params.set_value('ZPERIODL', array(zper_l) * a_to_bohr / alat)
    if zper_r is not None:
        params.set_value('ZPERIODR', array(zper_r) * a_to_bohr / alat)

    # write inputfile
    params.fill_keywords_to_inputfile(output=input_filename)

    nspin = params.get_value('NSPIN')

    newsosol = False
    if 'NEWSOSOL' in params.get_value('RUNOPT'):
        newsosol = True

    return natyp, nspin, newsosol, warnings
def prepare_calc_parameters(parameters, spin_type, magnetization_per_site,
                            structure, kmax):
    """Prepare a calc_parameter node for a inpgen jobcalc

    Depending on the imports merge information

    :param parameters: given calc_parameter orm.Dict node to merge with
    :param spin_type: type of magnetic calculation
    :param magnetization_per_site: list
    :param kmax: int, basis cutoff for the simulations
    :return: orm.Dict
    """
    # pylint: disable=too-many-locals
    #parameters_b = None

    # Spin type options
    if spin_type == SpinType.NONE:
        jspins = 1
    else:
        jspins = 2
    add_parameter_dict = {'comp': {'jspins': jspins}}

    # electronic Structure options
    # None. Gff we want to increase the smearing and kpoint density in case of metal

    if kmax is not None:  # add kmax from protocol
        add_parameter_dict = recursive_merge(add_parameter_dict,
                                             {'comp': {
                                                 'kmax': kmax
                                             }})
    if parameters is not None:
        add_parameter_dict = recursive_merge(add_parameter_dict,
                                             parameters.get_dict())
        # In general better use aiida-fleur merge methods for calc parameters...

    if magnetization_per_site is not None:
        # Do for now sake we have this. If the structure is not rightly prepared it will run, but
        # the set magnetization will be wrong
        atomic_numbers = {
            data['symbol']: num
            for num, data in PeriodicTableElements.items()
        }

        if spin_type == SpinType.NONE:
            import warnings
            warnings.warn(
                '`magnetization_per_site` will be ignored as `spin_type` is set to SpinType.NONE'
            )
        if spin_type == SpinType.COLLINEAR:
            # add atom lists for each kind and set bmu in muBohr
            # this will break symmetry and changes the structure, if needed
            # be careful here because this may override things the plugin is during already.
            # This is very fragile and not robust, it will be wrong if input structure is wrong,
            # i.e does not have enough kinds but we do not change the input structure that way.
            # In the end this should be implemented aiida-fleur and imported
            mag_dict = {}
            sites = list(structure.sites)
            for i, val in enumerate(magnetization_per_site):
                kind_name = sites[i].kind_name
                kind = structure.get_kind(kind_name)
                site_symbol = kind.symbols[0]  # assume atoms
                atomic_number = atomic_numbers[site_symbol]

                if kind_name != site_symbol:
                    head = kind_name.rstrip('0123456789')
                    try:
                        kind_namet = int(kind_name[len(head):])
                    except ValueError:
                        kind_namet = 0
                    kind_id = f'{atomic_number}.{kind_namet}'
                else:
                    kind_id = f'{atomic_number}'
                mag_dict[f'atom{i}'] = {
                    'z': atomic_number,
                    'id': kind_id,
                    'bmu': val
                }
            # Better would be a valid parameter data merge from aiida-fleur, to merge atom lists
            # right
            add_parameter_dict = recursive_merge(add_parameter_dict, mag_dict)
            #structure, parameters_b = break_symmetry(structure, parameterdata=orm.Dict(dict=add_parameter_dict))

    new_parameters = orm.Dict(dict=add_parameter_dict)

    return new_parameters, structure
    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
示例#11
0
def create_corehole_para(structure,
                         kind,
                         econfig,
                         species_name='corehole',
                         parameterdata=None):
    """
    This methods sets of electron configurations for a kind
    or position given, make sure to break the symmetry for this position/kind
    beforehand, otherwise you will create several coreholes.

    :param structure: StructureData
    :param kind: a string with the kind_name (TODO: alternative the kind object)
    :param econfig: string, e.g. econfig = "[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2" to set, i.e. the corehole

    :return: a Dict node
    """
    # TODO: Since fleur MaXR5 there is a default econfig file and the order behavior
    # has changed. now to atom lists only change the default if they have an id.
    from aiida.common.constants import elements as PeriodicTableElements
    from aiida import orm
    _atomic_numbers = {
        data['symbol']: num
        for num, data in PeriodicTableElements.items()
    }
    #from aiida_fleur.tools.merge_parameter import merge_parameter

    kindo = structure.get_kind(kind)
    symbol = kindo.symbol
    head = kindo.name.rstrip('01223456789')
    charge = _atomic_numbers[kindo.symbol]
    a_id = float('{}.{}'.format(charge, kindo.name[len(head):]))

    # get kind symbol, get kind name,
    #&atom element="W" jri=921 lmax=8 rmt=2.52 dx=0.014 lo="5p" econfig="[Kr] 5s2 4d10 4f13 | 5p6 5d4 6s2" /
    #count = 0
    if parameterdata:
        new_parameterd = parameterdata.get_dict(
        )  # dict()otherwise parameterdata is changed
        for key, val in new_parameterd.items():
            if 'atom' in key:
                if val.get('element', None) == symbol:
                    # remember atomic id is atomic number.some int
                    if (a_id and float(a_id) == float(val.get('id', -1))):
                        val.update({'econfig': econfig})
                        break
                    if not a_id:
                        val.update({'econfig': econfig})
                    else:
                        pass
    else:
        if a_id:
            if species_name:
                new_parameterd = {
                    'atom': {
                        'element': symbol,
                        'econfig': econfig,
                        'id': a_id,
                        'name': species_name
                    }
                }
            else:
                new_parameterd = {
                    'atom': {
                        'element': symbol,
                        'econfig': econfig,
                        'id': a_id
                    }
                }
        else:
            new_parameterd = {'atom': {'element': symbol, 'econfig': econfig}}

    new_parameter = orm.Dict(dict=new_parameterd)
    #if parameterdata:
    #    new_parameter = merge_parameter(parameterdata, new_parameter)
    return new_parameter  #structure