Ejemplo n.º 1
0
def get_input_data_text(key, val, mapping=None):
    """
    Given a key and a value, return a string (possibly multiline for arrays)
    with the text to be added to the input file.
    
    :param key: the flag name
    :param val: the flag value. If it is an array, a line for each element
            is produced, with variable indexing starting from 1.
            Each value is formatted using the conv_to_fortran function.
    :param mapping: Optional parameter, must be provided if val is a dictionary.
            It maps each key of the 'val' dictionary to the corresponding 
            list index. For instance, if ``key='magn'``, 
            ``val = {'Fe': 0.1, 'O': 0.2}`` and ``mapping = {'Fe': 2, 'O': 1}``,
            this function will return the two lines ``magn(1) = 0.2`` and
            ``magn(2) = 0.1``. This parameter is ignored if 'val' 
            is not a dictionary. 
    """
    from aiida.common.utils import conv_to_fortran
    # I check first the dictionary, because it would also match
    # hasattr(__iter__)
    if isinstance(val, dict):
        if mapping is None:
            raise ValueError("If 'val' is a dictionary, you must provide also "
                             "the 'mapping' parameter")

        list_of_strings = []
        for elemk, itemval in val.iteritems():
            try:
                idx = mapping[elemk]
            except KeyError:
                raise ValueError("Unable to find the key '{}' in the mapping "
                                 "dictionary".format(elemk))

            list_of_strings.append(
                (idx, "  {0}({2}) = {1}\n".format(key,
                                                  conv_to_fortran(itemval),
                                                  idx)))

        # I first have to resort, then to remove the index from the first
        # column, finally to join the strings
        list_of_strings = zip(*sorted(list_of_strings))[1]
        return "".join(list_of_strings)
    elif hasattr(val, '__iter__'):
        # a list/array/tuple of values
        list_of_strings = [
            "{0}({2})  {1}\n".format(key, conv_to_fortran(itemval), idx + 1)
            for idx, itemval in enumerate(val)
        ]
        return "".join(list_of_strings)
    else:
        # single value
        if key[:6] == '%block':
            bname = key.split()[1]
            b1 = "{0}  {1}".format(key, my_conv_to_fortran(val))
            return b1 + "\n%endblock " + bname + "\n"
        else:
            return "{0}  {1}\n".format(key, my_conv_to_fortran(val))
Ejemplo n.º 2
0
def get_input_data_text(
        key,
        val,
        value_only,
        mapping=None):  #TODO rewrite for SPEX delete unnessesariy parts
    """
    Given a key and a value, return a string (possibly multiline for arrays)
    with the text to be added to the input file.

    :param key: the flag name
    :param val: the flag value. If it is an array, a line for each element
            is produced, with variable indexing starting from 1.
            Each value is formatted using the conv_to_fortran function.
    :param mapping: Optional parameter, must be provided if val is a dictionary.
            It maps each key of the 'val' dictionary to the corresponding
            list index. For instance, if ``key='magn'``,
            ``val = {'Fe': 0.1, 'O': 0.2}`` and ``mapping = {'Fe': 2, 'O': 1}``,
            this function will return the two lines ``magn(1) = 0.2`` and
            ``magn(2) = 0.1``. This parameter is ignored if 'val'
            is not a dictionary.
    """
    from aiida.common.utils import conv_to_fortran
    # I don't try to do iterator=iter(val) and catch TypeError because
    # it would also match strings
    # I check first the dictionary, because it would also matc
    # hasattr(__iter__)
    if isinstance(val, dict):
        if mapping is None:
            raise ValueError("If 'val' is a dictionary, you must provide also "
                             "the 'mapping' parameter")

        # At difference with the case of a list, at the beginning
        # list_of_strings
        # is a list of 2-tuples where the first element is the idx, and the
        # second is the actual line. This is used at the end to
        # resort everything.
        list_of_strings = []
        for elemk, itemval in val.iteritems():
            try:
                idx = mapping[elemk]
            except KeyError:
                raise ValueError("Unable to find the key '{}' in the mapping "
                                 "dictionary".format(elemk))

            list_of_strings.append(
                (idx, "  {0}({2})={1} ".format(key, conv_to_fortran(itemval),
                                               idx)))
            #changed {0}({2}) = {1}\n".format

        # I first have to resort, then to remove the index from the first
        # column, finally to join the strings
        list_of_strings = zip(*sorted(list_of_strings))[1]
        return "".join(list_of_strings)
    elif hasattr(val, '__iter__'):
        if value_only:
            list_of_strings = [
                "  ({1}){0} ".format(conv_to_fortran(itemval), idx + 1)
                for idx, itemval in enumerate(val)
            ]
        else:
            # a list/array/tuple of values
            list_of_strings = [
                "  {0}({2})={1} ".format(key, conv_to_fortran(itemval),
                                         idx + 1)
                for idx, itemval in enumerate(val)
            ]
        return "".join(list_of_strings)
    else:
        # single value
        #return "  {0}={1} ".format(key, conv_to_fortran(val))
        if value_only:
            return " {0} ".format(val)
        else:
            return "  {0}={1} ".format(key, val)
Ejemplo n.º 3
0
def convert_input_to_namelist_entry(key, val, mapping=None):
    """
    Given a key and a value, from an input parameters dictionary for a namelist calculation, map it to
    the appropriate string format for the namelist input file. For single values it will return a single
    string, but for values that are a dictionary, list or tuple, the returned string may be multiline.

    :param key: the namelist keyword name
    :param val: the namelist keyword value
        The value can be either a single value, list/tuple, a double nested list or a dictionary.
        Depending on the type of the value the resulting string differs vastly

        * single list:
            A list of keywords will be generated, where the index of the value in the list will be
            used as the index in the keyword and the value itself will be converted using conv_to_fortran.
            For example::

                'efield': [4, 5, 6]

            will result in::

                efield(1) = 4
                efield(1) = 5
                efield(1) = 6

        * double nested list:
            This format can be used for keywords that require one or more indices that do not necessarily
            follow a sequential number, but take specific values that need to be defined by the user.
            For example::

                'starting_ns_eigenvalue': [
                    [1, 1, 3, 3.5],
                    [2, 1, 1, 2.8]
                ]

            will be formatted as::

                starting_ns_eigenvalue(1,1,3) = 3.5
                starting_ns_eigenvalue(2,1,1) = 2.8

            Note that if the mapping argument is provided in the input, any value in sub lists that matches
            a key in the mapping dictionary (that is to say it is a string that matches one of the kinds), it
            will be replaced with the index of the corresponding atomic species. For example::

                hubbard_j: [
                    [2, 'Ni', 3.5],
                    [2, 'Fe', 7.4],
                ]

            would be formatted as::

                hubbard_j(2, 1) = 3.5
                hubbard_j(2, 3) = 7.4

            Assuming the mapping dictionary contained the kinds 'Ni' and 'Fe', with the indices 1 and 3, respectively

        * dictionary:
            The keys of this dictionary should correspond to keys in the mapping input argument and will be replaced
            with the corresponding value. This can be used for keywords that take a single index that needs to conform
            to the index of the atomic species to which the keyword value should apply. For example::

                hubbard_u: {
                    'Co': 3.5,
                    'O': 7.4,
                }

            will be formatted as::

                hubbard_u(1) = 3.5
                hubbard_u(3) = 7.4

            assuming that the kinds 'Co' and 'O' would have atomic species indices 1 and 3, respectively.
            This mapping from kind name to atomic species index should be defined by the `mapping` argument.

    :param mapping: optional parameter, that must be provided if val is a dictionary or a double nested list
        where the sub lists contain string values. The keys of the mapping dictionary should be the atomic species
        names that will be encountered in the value, and the corresponding value should be the index of that
        atomic species. Example::

            mapping = {
                'Fe': 1,
                'O': 2,
            }

        This will map every occurrence of 'Fe' and 'O' in the values to the corresponding integer.
    """
    from aiida.common.utils import conv_to_fortran

    # I don't try to do iterator=iter(val) and catch TypeError because it would also match strings
    # I check first the dictionary, because it would also match hasattr(__iter__)
    if isinstance(val, dict):

        if mapping is None:
            raise ValueError(
                "If 'val' is a dictionary, you must provide also the 'mapping' parameter"
            )

        # At difference with the case of a list, at the beginning list_of_strings
        # is a list of 2-tuples where the first element is the idx, and the
        # second is the actual line. This is used at the end to resort everything.
        list_of_strings = []

        for elemk, itemval in val.iteritems():
            try:
                idx = mapping[elemk]
            except KeyError:
                raise ValueError(
                    "Unable to find the key '{}' in the mapping dictionary".
                    format(elemk))

            list_of_strings.append(
                (idx, '  {0}({2}) = {1}\n'.format(key,
                                                  conv_to_fortran(itemval),
                                                  idx)))

        # I first have to resort, then to remove the index from the first column, finally to join the strings
        list_of_strings = zip(*sorted(list_of_strings))[1]
        return ''.join(list_of_strings)

    # A list/array/tuple of values
    elif hasattr(val, '__iter__'):

        list_of_strings = []

        for idx, itemval in enumerate(val):

            if isinstance(itemval, (list, tuple)):

                values = []

                for value in itemval[:-1]:

                    if not isinstance(value, (int, basestring)):
                        raise ValueError(
                            'values of double nested lists should be either integers or strings'
                        )

                    if isinstance(value, basestring):
                        if mapping is None:
                            raise ValueError(
                                'cannot map the string value because no mapping was defined'
                            )

                        if value not in mapping:
                            raise ValueError(
                                'the nested list contained string {} but this is not a key in the mapping'
                                .format(value))
                        else:
                            values.append(str(mapping[value]))
                    else:
                        values.append(str(value))

                idx_string = ','.join(values)
                itemval = itemval.pop()
            else:
                idx_string = '{}'.format(idx + 1)

            list_of_strings.append('  {0}({2}) = {1}\n'.format(
                key, conv_to_fortran(itemval), idx_string))

        return ''.join(list_of_strings)

    # Single value
    else:
        return '  {0} = {1}\n'.format(key, conv_to_fortran(val))