Пример #1
0
    def get_from_string(cls, code_string):
        """
        Get a Computer object with given identifier string, that can either be
        the numeric ID (pk), or the label (if unique); the label can either
        be simply the label, or in the format label@machinename. See the note
        below for details on the string detection algorithm.

        .. note:: If a string that can be converted to an integer is given,
          the numeric ID is verified first (therefore, is a code A with a
          label equal to the ID of another code B is present, code A cannot
          be referenced by label). Similarly, the (leftmost) '@' symbol is
          always used to split code and computername. Therefore do not use
          '@' in the code name if you want to use this function
          ('@' in the computer name are instead valid).

        :param code_string: the code string identifying the code to load

        :raise NotExistent: if no code identified by the given string is found
        :raise MultipleObjectsError: if the string cannot identify uniquely
            a code
        """
        from aiida.common.exceptions import NotExistent, MultipleObjectsError
        from aiida.orm.utils import load_node

        try:
            code_int = int(code_string)
            try:
                return load_node(code_int, parent_class=cls)
            except NotExistent:
                raise ValueError()  # Jump to the following section
                # to check if a code with the given
                # label exists.
            except MultipleObjectsError:
                raise MultipleObjectsError("More than one code in the DB "
                                           "with pk='{}'!".format(code_string))
        except ValueError:
            # Before dying, try to see if the user passed a (unique) label.
            # split with the leftmost '@' symbol (i.e. code names cannot
            # contain '@' symbols, computer names can)
            codename, sep, computername = code_string.partition('@')
            if sep:
                codes = cls.query(label=codename, dbcomputer__name=computername)
            else:
                codes = cls.query(label=codename)

            if len(codes) == 0:
                raise NotExistent("'{}' is not a valid code "
                                  "ID or label.".format(code_string))
            elif len(codes) > 1:
                retstr = (
                "There are multiple codes with label '{}', having IDs: "
                "".format(code_string))
                retstr += ", ".join(sorted([str(c.pk) for c in codes])) + ".\n"
                retstr += ("Relabel them (using their ID), or refer to them "
                           "with their ID.")
                raise MultipleObjectsError(retstr)
            else:
                return codes[0]
Пример #2
0
def _get_calculation(node):
    """
    Gets the parent (immediate) calculation, attached as the input of
    the node.

    :param node: an instance of subclass of :py:class:`aiida.orm.node.Node`
    :return: an instance of subclass of
        :py:class:`aiida.orm.calculation.Calculation`
    :raises MultipleObjectsError: if the node has more than one calculation
        attached.
    """
    from aiida.common.exceptions import MultipleObjectsError
    from aiida.orm.calculation import Calculation
    from aiida.common.links import LinkType
    if len(node.get_inputs(node_type=Calculation,
                           link_type=LinkType.CREATE)) == 1:
        return node.get_inputs(node_type=Calculation,
                               link_type=LinkType.CREATE)[0]
    elif len(node.get_inputs(node_type=Calculation,
                             link_type=LinkType.CREATE)) == 0:
        return None
    else:
        raise MultipleObjectsError("Node {} seems to have more than one "
                                   "parent (immediate) calculation -- "
                                   "exporter does not know which one of "
                                   "them produced the node".format(node))
Пример #3
0
def _show_vmd(exec_name, structure_list):
    """
    Plugin for vmd
    """
    import tempfile
    import subprocess

    if len(structure_list) > 1:
        raise MultipleObjectsError("Visualization of multiple objects "
                                   "is not implemented")
    structure = structure_list[0]

    # pylint: disable=protected-access
    with tempfile.NamedTemporaryFile(suffix='.xsf') as tmpf:
        tmpf.write(structure._exportstring('xsf')[0])
        tmpf.flush()

        try:
            subprocess.check_output([exec_name, tmpf.name])
        except subprocess.CalledProcessError:
            # The program died: just print a message
            echo.echo_info(
                "the call to {} ended with an error.".format(exec_name))
        except OSError as err:
            if err.errno == 2:
                echo.echo_critical(
                    "No executable '{}' found. Add to the path, "
                    "or try with an absolute path.".format(exec_name))
            else:
                raise
Пример #4
0
    def load_entity(cls,
                    identifier,
                    identifier_type=None,
                    sub_classes=None,
                    query_with_dashes=True):
        """
        Load an entity that uniquely corresponds to the provided identifier of the identifier type.

        :param identifier: the identifier
        :param identifier_type: the type of the identifier
        :param sub_classes: an optional tuple of orm classes, that should each be strict sub classes of the
            base orm class of the loader, that will narrow the queryset
        :returns: the loaded entity
        :raises aiida.common.MultipleObjectsError: if the identifier maps onto multiple entities
        :raises aiida.common.NotExistent: if the identifier maps onto not a single entity
        """
        builder, query_parameters = cls.get_query_builder(
            identifier, identifier_type, sub_classes, query_with_dashes)
        builder.limit(2)

        classes = ' or '.join(
            [sub_class.__name__ for sub_class in query_parameters['classes']])
        identifier = query_parameters['identifier']
        identifier_type = query_parameters['identifier_type'].value

        try:
            entity = builder.one()[0]
        except MultipleObjectsError:
            error = f'multiple {classes} entries found with {identifier_type}<{identifier}>'
            raise MultipleObjectsError(error)
        except NotExistent as exception:
            error = f'no {classes} found with {identifier_type}<{identifier}>: {exception}'
            raise NotExistent(error)

        return entity
Пример #5
0
    def load_entity(cls,
                    identifier,
                    identifier_type=None,
                    sub_classes=None,
                    query_with_dashes=True):
        from aiida.orm import Workflow

        if identifier_type is None:
            identifier, identifier_type = cls.infer_identifier_type(identifier)

        if identifier_type == IdentifierType.ID:
            result = Workflow.query(pk=identifier)
        elif identifier_type == IdentifierType.UUID:
            result = Workflow.query(uuid=identifier)
        elif identifier_type == IdentifierType.LABEL:
            result = Workflow.query(label=identifier)

        result = [workflow for workflow in result]

        if len(result) > 1:
            error = 'multiple legacy workflows found with {} <{}>'.format(
                identifier_type, identifier)
            raise MultipleObjectsError(error)
        elif not result:
            error = 'no legacy workflow found with {} <{}>'.format(
                identifier_type, identifier)
            raise NotExistent(error)

        return result[0]
Пример #6
0
def get_pseudos_from_structure(structure, family_name):
    """
    Given a family name (a PsfFamily group in the DB) and a AiiDA
    structure, return a dictionary associating each kind name with its
    PsfData object.

    :raise MultipleObjectsError: if more than one PSF for the same element is
       found in the group.
    :raise NotExistent: if no PSF for an element in the group is
       found in the group.
    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError

    family_pseudos = {}
    family = PsfData.get_psf_group(family_name)
    for node in family.nodes:
        if isinstance(node, PsfData):
            if node.element in family_pseudos:
                raise MultipleObjectsError(
                    "More than one PSF for element {} found in "
                    "family {}".format(node.element, family_name))
            family_pseudos[node.element] = node

    pseudo_list = {}
    for kind in structure.kinds:
        symbol = kind.symbol
        try:
            pseudo_list[kind.name] = family_pseudos[symbol]
        except KeyError:
            raise NotExistent(
                "No PSF for element {} found in family {}".format(
                    symbol, family_name))

    return pseudo_list
Пример #7
0
def get_pseudos_from_structure(structure, family_name):
    """Given a family name (a Siesta pseudo group in the DB, possibly with
    mixed psf and psml pseudopotentials) and an AiiDA structure
    object, return a dictionary associating each 'kind' name in the
    structure with its object (PsfData or PsmlData).

    :raise MultipleObjectsError: if more than one pseudo for the same
       element is found in the group.

    :raise NotExistent: if no pseudo for an element in the group is
       found in the group.

    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError

    family_pseudos = {}
    family = Group.get(label=family_name)
    for node in family.nodes:
        if isinstance(node, (PsfData, PsmlData)):
            if node.element in family_pseudos:
                raise MultipleObjectsError(
                    "More than one pseudo for element {} found in "
                    "family {}".format(node.element, family_name)
                )
            family_pseudos[node.element] = node

    pseudo_list = {}
    for kind in structure.kinds:
        symbol = kind.symbol
        try:
            pseudo_list[kind.name] = family_pseudos[symbol]
        except KeyError:
            raise NotExistent("No pseudo for element {} found in family {}".format(symbol, family_name))

    return pseudo_list
Пример #8
0
def _show_xcrysden(exec_name, object_list, **kwargs):
    """
    Plugin for xcrysden
    """
    import tempfile
    import subprocess

    if len(object_list) > 1:
        raise MultipleObjectsError(
            'Visualization of multiple trajectories is not implemented')
    obj = object_list[0]

    # pylint: disable=protected-access
    with tempfile.NamedTemporaryFile(mode='w+b', suffix='.xsf') as tmpf:
        tmpf.write(obj._exportcontent('xsf', **kwargs)[0])
        tmpf.flush()

        try:
            subprocess.check_output([exec_name, '--xsf', tmpf.name])
        except subprocess.CalledProcessError:
            # The program died: just print a message
            echo.echo_info(
                'the call to {} ended with an error.'.format(exec_name))
        except OSError as err:
            if err.errno == 2:
                echo.echo_critical(
                    "No executable '{}' found. Add to the path, "
                    'or try with an absolute path.'.format(exec_name))
            else:
                raise
Пример #9
0
    def get_code_helper(cls, label, machinename=None):
        """
        :param label: the code label identifying the code to load
        :param machinename: the machine name where code is setup

        :raise aiida.common.NotExistent: if no code identified by the given string is found
        :raise aiida.common.MultipleObjectsError: if the string cannot identify uniquely
            a code
        """
        from aiida.common.exceptions import NotExistent, MultipleObjectsError
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.computers import Computer

        qb = QueryBuilder()
        qb.append(cls, filters={'label': {'==': label}}, project=['*'], tag='code')
        if machinename:
            qb.append(Computer, filters={'name': {'==': machinename}}, with_node='code')

        if qb.count() == 0:
            raise NotExistent("'{}' is not a valid code name.".format(label))
        elif qb.count() > 1:
            codes = qb.all(flat=True)
            retstr = ("There are multiple codes with label '{}', having IDs: ".format(label))
            retstr += ', '.join(sorted([str(c.pk) for c in codes])) + '.\n'
            retstr += ('Relabel them (using their ID), or refer to them with their ID.')
            raise MultipleObjectsError(retstr)
        else:
            return qb.first()[0]
Пример #10
0
    def get_from_string(cls, code_string):
        """
        Get a Computer object with given identifier string in the format
        label@machinename. See the note below for details on the string
        detection algorithm.

        .. note:: the (leftmost) '@' symbol is always used to split code
            and computername. Therefore do not use
            '@' in the code name if you want to use this function
            ('@' in the computer name are instead valid).

        :param code_string: the code string identifying the code to load

        :raise aiida.common.NotExistent: if no code identified by the given string is found
        :raise aiida.common.MultipleObjectsError: if the string cannot identify uniquely
            a code
        :raise aiida.common.InputValidationError: if code_string is not of string type

        """
        from aiida.common.exceptions import NotExistent, MultipleObjectsError, InputValidationError

        try:
            label, sep, machinename = code_string.partition('@')
        except AttributeError as exception:
            raise InputValidationError('the provided code_string is not of valid string type')

        try:
            return cls.get_code_helper(label, machinename)
        except NotExistent:
            raise NotExistent('{} could not be resolved to a valid code label'.format(code_string))
        except MultipleObjectsError:
            raise MultipleObjectsError('{} could not be uniquely resolved'.format(code_string))
Пример #11
0
    def get(cls, pk=None, label=None, machinename=None):
        """
        Get a Computer object with given identifier string, that can either be
        the numeric ID (pk), or the label (and computername) (if unique).

        :param pk: the numeric ID (pk) for code
        :param label: the code label identifying the code to load
        :param machinename: the machine name where code is setup

        :raise NotExistent: if no code identified by the given string is found
        :raise MultipleObjectsError: if the string cannot identify uniquely
            a code
        """
        from aiida.common.exceptions import (NotExistent, MultipleObjectsError,
                                             InputValidationError)

        # first check if code pk is provided
        if (pk):
            code_int = int(pk)
            try:
                return cls.get_subclass_from_pk(code_int)
            except NotExistent:
                raise ValueError("{} is not valid code pk".format(pk))
            except MultipleObjectsError:
                raise MultipleObjectsError("More than one code in the DB "
                                           "with pk='{}'!".format(pk))

        # check if label (and machinename) is provided
        elif (label != None):
            return cls.get_code_helper(label, machinename)

        else:
            raise InputValidationError(
                "Pass either pk or code label (and machinename)")
Пример #12
0
    def get_basis_group_map(cls, group_name):
        """Get a mapping of elements to basissets in a basis set family.

        Parameters
        ----------
        group_name : str
            the group name of the basis set

        Returns
        -------
        dict
            a mapping of element to basis set

        Raises
        ------
        aiida.common.exceptions.MultipleObjectsError
            if there is more than one element s

        """
        family_bases = {}
        family = cls.get_basis_group(group_name)
        for node in family.nodes:
            if isinstance(node, cls):
                if node.element in family_bases:
                    raise MultipleObjectsError(
                        "More than one BasisSetData for element {} found in "
                        "family {}".format(node.element, group_name)
                    )
                family_bases[node.element] = node
        return family_bases
Пример #13
0
def get_pseudos_from_structure(structure, family_name):
    """Return a dictionary mapping each kind name of the structure to corresponding `UpfData` from given family.

    :param structure: a `StructureData`
    :param family_name: the name of a UPF family group
    :return: dictionary mapping each structure kind name onto `UpfData` of corresponding element
    :raise aiida.common.MultipleObjectsError: if more than one UPF for the same element is found in the group.
    :raise aiida.common.NotExistent: if no UPF for an element in the group is found in the group.
    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError

    pseudo_list = {}
    family_pseudos = {}
    family = UpfData.get_upf_group(family_name)

    for node in family.nodes:
        if isinstance(node, UpfData):
            if node.element in family_pseudos:
                raise MultipleObjectsError(
                    'More than one UPF for element {} found in family {}'.
                    format(node.element, family_name))
            family_pseudos[node.element] = node

    for kind in structure.kinds:
        try:
            pseudo_list[kind.name] = family_pseudos[kind.symbol]
        except KeyError:
            raise NotExistent(
                'No UPF for element {} found in family {}'.format(
                    kind.symbol, family_name))

    return pseudo_list
Пример #14
0
    def __init__(self, **kwargs):

        # If no arguments are passed, then create a new DbComment
        if not kwargs:
            self.dbcomment = DbComment()

        # If a DbComment is passed as argument. Just use it and
        # wrap it with a Comment object
        elif 'dbcomment' in kwargs:
            # When a dbcomment is passed as argument, then no other arguments
            # should be passed.
            if len(kwargs) > 1:
                raise ValueError("When a DbComment is passed as argument, no"
                                 "further arguments are accepted.")
            dbcomment = kwargs.pop('dbcomment')
            if not isinstance(dbcomment, DbComment):
                raise ValueError("Expected a DbComment. Object of a different"
                                 "class was given as argument.")
            self.dbcomment = dbcomment

        else:
            id = kwargs.pop('id', None)
            if id is None:
                id = kwargs.pop('pk', None)
            user = kwargs.pop('user', None)
            dbnode = kwargs.pop('dbnode', None)

            # Constructing the default query
            import operator
            from django.db.models import Q
            query_list = []

            # If an id is specified then we add it to the query
            if id is not None:
                query_list.append(Q(pk=id))

            # If a user is specified then we add it to the query
            if user is not None:
                query_list.append(Q(user=user))

            # If a dbnode is specified then we add it to the query
            if dbnode is not None:
                query_list.append(Q(dbnode=dbnode))

            res = DbComment.objects.filter(reduce(operator.and_, query_list))
            ccount = len(res)
            if ccount > 1:
                raise MultipleObjectsError(
                    "The arguments that you specified were too vague. More "
                    "than one comments with this data were found in the "
                    "database")
            elif ccount == 0:
                raise NotExistent("No comments were found with the given "
                                  "arguments")

            self.dbcomment = res[0]
Пример #15
0
    def get(cls, *args, **kwargs):
        queryresults = cls.query(*args, **kwargs)

        if len(queryresults) == 1:
            return queryresults[0]
        elif len(queryresults) == 0:
            raise NotExistent("No Group matching the query found")
        else:
            raise MultipleObjectsError("More than one Group found -- "
                                       "I found {}".format(len(queryresults)))
Пример #16
0
    def get(cls,
            element,
            name=None,
            version="latest",
            match_aliases=True,
            group_label=None,
            n_el=None):
        from aiida.orm.querybuilder import QueryBuilder

        query = QueryBuilder()

        params = {}

        if group_label:
            query.append(Group, filters={"label": group_label}, tag="group")
            params["with_group"] = "group"

        query.append(BasisSet, **params)

        filters = {"attributes.element": {"==": element}}

        if version != "latest":
            filters["attributes.version"] = {"==": version}

        if name:
            if match_aliases:
                filters["attributes.aliases"] = {"contains": [name]}
            else:
                filters["attributes.name"] = {"==": name}

        if n_el:
            filters["attributes.n_el"] = {"==": n_el}

        query.add_filter(BasisSet, filters)

        # SQLA ORM only solution:
        # query.order_by({BasisSet: [{"attributes.version": {"cast": "i", "order": "desc"}}]})
        # items = query.first()

        items = sorted(query.iterall(),
                       key=lambda b: b[0].version,
                       reverse=True)

        if not items:
            raise NotExistent(
                f"No Gaussian Basis Set found for element={element}, name={name}, version={version}"
            )

        # if we get different names there is no well ordering, sorting by version only works if they have the same name
        if len(set(b[0].name for b in items)) > 1:
            raise MultipleObjectsError(
                f"Multiple Gaussian Basis Set found for element={element}, name={name}, version={version}"
            )

        return items[0][0]
Пример #17
0
def load_node(node_id=None,
              pk=None,
              uuid=None,
              parent_class=None,
              query_with_dashes=True):
    """
    Returns an AiiDA node given its PK or UUID.

    :param node_id: PK (integer) or UUID (string) or a node
    :param pk: PK of a node
    :param uuid: UUID of a node, or the beginning of the uuid
    :param parent_class: if specified, checks whether the node loaded is a
        subclass of parent_class
    :param bool query_with_dashes: Specific if uuid is passed, allows to put the uuid in the correct form.
        Default=True
    :param bool return_node: lets the function return the AiiDA node referred by the input.
        Default=False
    :return: the required AiiDA node if existing, unique, and (sub)instance
        of parent_class
    :raise InputValidationError: if none or more than one of parameters is supplied
    :raise TypeError: I the wrong types are provided
    :raise NotExistent: if no matching Node is found.
    :raise MultipleObjectsError: If more than one Node was found

    """
    from aiida.common.exceptions import MultipleObjectsError, NotExistent
    from aiida.orm.implementation import Node

    # I can use this functions to load only nodes, i.e. not users, groups etc ...
    # If nothing is specified I assume the big granpa: Node!
    class_ = parent_class or Node
    if not issubclass(class_, Node):
        raise TypeError("{} is not a subclass of {}".format(class_, Node))

    # The logic is to use the function is_node_existing with return_node=True
    kwargs = dict(node_id=node_id,
                  pk=pk,
                  uuid=uuid,
                  parent_class=parent_class,
                  query_with_dashes=query_with_dashes)

    qb = create_node_id_qb(**kwargs)
    qb.add_projection('node', '*')

    # For efficiency I do not go further than 2 results.
    qb.limit(2)

    # FInally, I check the existence and unicity of the node
    try:
        return qb.one()[0]
    except MultipleObjectsError:
        raise MultipleObjectsError("More than one node found. Provide longer "
                                   "starting pattern for uuid.")
    except NotExistent:
        raise NotExistent("No node was found")
Пример #18
0
    def __init__(self, **kwargs):

        # If no arguments are passed, then create a new DbComment
        if not kwargs:
            self.dbcomment = DbComment()

        # If a DbComment is passed as argument. Just use it and
        # wrap it with a Comment object
        elif 'dbcomment' in kwargs:
            # When a dbcomment is passed as argument, then no other arguments
            # should be passed.
            if len(kwargs) > 1:
                raise ValueError("When a DbComment is passed as argument, no"
                                 "further arguments are accepted.")
            dbcomment = kwargs.pop('dbcomment')
            if not isinstance(dbcomment, DbComment):
                raise ValueError("Expected a DbComment. Object of a different"
                                 "class was given as argument.")
            self.dbcomment = dbcomment

        else:
            id = kwargs.pop('id', None)
            if id is None:
                id = kwargs.pop('pk', None)
            user = kwargs.pop('user', None)
            dbnode = kwargs.pop('dbnode', None)

            # Constructing the default query
            dbcomment_query = DbComment.query

            # If an id is specified then we add it to the query
            if id is not None:
                dbcomment_query = dbcomment_query.filter_by(id=id)

            # If a user is specified then we add it to the query
            if user is not None:
                dbcomment_query = dbcomment_query.filter_by(user=user)

            # If a dbnode is specified then we add it to the query
            if dbnode is not None:
                dbcomment_query = dbcomment_query.filter_by(dbnode=dbnode)

            ccount = dbcomment_query.count()
            if ccount > 1:
                raise MultipleObjectsError(
                    "The arguments that you specified were too vague. More "
                    "than one comments with this data were found in the "
                    "database")
            elif ccount == 0:
                raise NotExistent("No comments were found with the given "
                                  "arguments")

            self.dbcomment = dbcomment_query.first()
Пример #19
0
 def get_basis_group_map(cls, group_name):
     """
     Return an {element: basis} map for the BasisFamily group with the given name.
     """
     from aiida.common.exceptions import MultipleObjectsError
     family_bases = {}
     family = cls.get_basis_group(group_name)
     for node in family.nodes:
         if isinstance(node, cls):
             if node.element in family_bases:
                 raise MultipleObjectsError(
                     "More than one BasisSetData for element {} found in "
                     "family {}".format(node.element, group_name))
             family_bases[node.element] = node
     return family_bases
Пример #20
0
    def get_potcars_dict(cls, elements, family_name, mapping=None):
        """
        Get a dictionary {element: POTCAR} for all given symbols.

        :param elements: The list of symbols to find POTCARs for
        :param family_name: The POTCAR family to be used
        :param mapping: A mapping[element] -> full_name
        """
        if not mapping:
            mapping = {element: element for element in elements}
        group_filters = {
            'name': {
                '==': family_name
            },
            'type': {
                '==': cls.potcar_family_type_string
            }
        }
        element_filters = {
            'attributes.full_name': {
                'in': [mapping[element] for element in elements]
            }
        }
        query = QueryBuilder()
        query.append(Group, tag='family', filters=group_filters)
        query.append(cls,
                     tag='potcar',
                     member_of='family',
                     filters=element_filters)

        result_potcars = {}
        for element in elements:
            full_name = mapping[element]
            potcars_of_kind = [
                potcar[0] for potcar in query.all()
                if potcar[0].full_name == full_name
            ]
            if not potcars_of_kind:
                raise NotExistent(
                    'No POTCAR found for full name {} in family {}'.format(
                        full_name, family_name))
            elif len(potcars_of_kind) > 1:
                raise MultipleObjectsError(
                    'More than one POTCAR for full name {} found in family {}'.
                    format(full_name, family_name))
            result_potcars[element] = potcars_of_kind[0]

        return result_potcars
Пример #21
0
    def get_or_create(cls, *args, **kwargs):
        """
        Try to retrieve a group from the DB with the given arguments;
        create (and store) a new group if such a group was not present yet.

        :return: (group, created) where group is the group (new or existing,
          in any case already stored) and created is a boolean saying
        """
        res = cls.query(name=kwargs.get("name"),
                        type_string=kwargs.get("type_string"))

        if res is None or len(res) == 0:
            return cls.create(*args, **kwargs), True
        elif len(res) > 1:
            raise MultipleObjectsError("More than one groups found in the "
                                       "database")
        else:
            return res[0], False
Пример #22
0
def get_pseudos_from_structure(structure, family_name):
    """
    Given a family name (a PsmlFamily group in the DB) and a AiiDA
    structure, return a dictionary associating each kind name with its
    PsmlData object.

    :raise MultipleObjectsError: if more than one PSML for the same element is
       found in the group.
    :raise NotExistent: if no PSML for an element in the group is
       found in the group.
    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError

    message = (  #pylint: disable=invalid-name
        'This function has been deprecated and will be removed in `v2.0.0`. ' +
        '`get_pseudos_from_structure` is substitued by `fam.get_pseudos(structure=structure)` '
        +
        'where `fam` is an instance of the families classes in `aiida_pseudo.groups.family`.'
    )

    warnings.warn(message, AiidaSiestaDeprecationWarning)

    family_pseudos = {}
    family = PsmlData.get_psml_group(family_name)
    for node in family.nodes:
        if isinstance(node, PsmlData):
            if node.element in family_pseudos:
                raise MultipleObjectsError(
                    "More than one PSML for element {} found in "
                    "family {}".format(node.element, family_name))
            family_pseudos[node.element] = node

    pseudo_list = {}
    for kind in structure.kinds:
        symbol = kind.symbol
        try:
            pseudo_list[kind.name] = family_pseudos[symbol]
        except KeyError:
            raise NotExistent(
                "No PSML for element {} found in family {}".format(
                    symbol, family_name))

    return pseudo_list
Пример #23
0
def get_pseudos_from_structure_and_path(structure, path='./'):
    """
    Given a structure and a path where UPF files are supposed to be located, load
    upf files and return those contained in the structure


    :raise aiida.common.MultipleObjectsError: if more than one UPF for the same element is
       found in the group.
    :raise aiida.common.NotExistent: if no UPF for an element  is
       found in the group.
    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError

    # load all pseudos
    pseudo_files = (set(glob.glob(os.path.join(path, '*UPF')))
                    | set(glob.glob(os.path.join(path, '*upf')))
                    | set(glob.glob(os.path.join(path, '*Upf'))))
    pseudos = {}
    for pp_file in pseudo_files:
        upf = UpfData(file=os.path.abspath(pp_file))
        if upf.element in pseudos:
            raise MultipleObjectsError(
                "More than one UPF for element {} found in "
                "{}".format(upf.element, path))

        pseudos[upf.element] = upf

    pseudo_list = {}
    for kind in structure.kinds:
        symbol = kind.symbol
        try:
            pseudo_list[kind.name] = pseudos[kind.name]
        except KeyError:
            raise NotExistent("No UPF for element {} found in path {}".format(
                symbol, path))

    return pseudo_list
Пример #24
0
def load_group(group_id=None, pk=None, uuid=None, query_with_dashes=True):
    """
    Load a group by its pk or uuid

    :param group_id: pk (integer) or uuid (string) of a group
    :param pk: pk of a group
    :param uuid: uuid of a group, or the beginning of the uuid
    :param bool query_with_dashes: allow to query for a uuid with dashes (default=True)
    :returns: the requested group if existing and unique
    :raise InputValidationError: if none or more than one of the arguments are supplied
    :raise TypeError: if the wrong types are provided
    :raise NotExistent: if no matching Node is found.
    :raise MultipleObjectsError: if more than one Node was found
    """
    from aiida.orm import Group

    kwargs = {
        'node_id': group_id,
        'pk': pk,
        'uuid': uuid,
        'parent_class': Group,
        'query_with_dashes': query_with_dashes
    }

    qb = create_node_id_qb(**kwargs)
    qb.add_projection('node', '*')
    qb.limit(2)

    try:
        return qb.one()[0]
    except MultipleObjectsError:
        raise MultipleObjectsError(
            'More than one group found. Provide longer starting pattern for uuid.'
        )
    except NotExistent:
        raise NotExistent('No group was found')
Пример #25
0
    def get(cls,
            element,
            name=None,
            version="latest",
            match_aliases=True,
            group_label=None,
            n_el=None):
        """
        Get the first matching Pseudopotential for the given parameters.

        :param element: The atomic symbol
        :param name: The name of the pseudo
        :param version: A specific version (if more than one in the database and not the highest/latest)
        :param match_aliases: Whether to look in the list of of aliases for a matching name
        """
        from aiida.orm.querybuilder import QueryBuilder

        query = QueryBuilder()

        params = {}

        if group_label:
            query.append(Group, filters={"label": group_label}, tag="group")
            params["with_group"] = "group"

        query.append(Pseudopotential, **params)

        filters = {"attributes.element": {"==": element}}

        if version != "latest":
            filters["attributes.version"] = {"==": version}

        if name:
            if match_aliases:
                filters["attributes.aliases"] = {"contains": [name]}
            else:
                filters["attributes.name"] = {"==": name}

        query.add_filter(Pseudopotential, filters)

        # SQLA ORM only solution:
        # query.order_by({Pseudopotential: [{"attributes.version": {"cast": "i", "order": "desc"}}]})
        # items = query.first()

        all_iter = query.iterall()

        if n_el:
            all_iter = filter(lambda p: sum(p[0].n_el) == n_el, all_iter)

        items = sorted(all_iter, key=lambda p: p[0].version, reverse=True)

        if not items:
            raise NotExistent(
                f"No Gaussian Pseudopotential found for element={element}, name={name}, version={version}"
            )

        # if we get different names there is no well ordering, sorting by version only works if they have the same name
        if len(set(p[0].name for p in items)) > 1:
            raise MultipleObjectsError(
                f"Multiple Gaussian Pseudopotentials found for element={element}, name={name}, version={version}"
            )

        return items[0][0]
Пример #26
0
def load_node(node_id=None,
              pk=None,
              uuid=None,
              parent_class=None,
              query_with_dashes=True):
    """
    Return an AiiDA node given PK or UUID.

    :param node_id: PK (integer) or UUID (string) or a node
    :param pk: PK of a node
    :param uuid: UUID of a node, or the beginning of the uuid
    :param parent_class: if specified, checks whether the node loaded is a
        subclass of parent_class
    :param bool query_with_dashes: Specific if uuid is passed, allows to put the uuid in the correct form.
        Default=True
    :return: an AiiDA node
    :raise InputValidationError: if none or more than one of parameters is supplied
    :raise TypeError: I the wrong types are provided
    :raise NotExistent: if no matching Node is found.
    :raise MultipleObjectsError: If more than one Node was fouuund
    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError, NotExistent, InputValidationError
    # This must be done inside here, because at import time the profile
    # must have been already loaded. If you put it at the module level,
    # the implementation is frozen to the default one at import time.
    from aiida.orm.implementation import Node
    from aiida.orm.querybuilder import QueryBuilder

    # First checking if the inputs are valid:
    inputs_provided = [val is not None
                       for val in (node_id, pk, uuid)].count(True)
    if inputs_provided == 0:
        raise InputValidationError(
            "one of the parameters 'node_id', 'pk' and 'uuid' "
            "has to be supplied")
    elif inputs_provided > 1:
        raise InputValidationError(
            "only one of parameters 'node_id', 'pk' and 'uuid' "
            "has to be supplied")

    class_ = parent_class or Node
    if not issubclass(class_, Node):
        raise TypeError("{} is not a subclass of {}".format(class_, Node))
    # The logic is as follows: If pk is specified I will look for the pk
    # if UUID is specified for the uuid.
    # node_id can either be string -> uuid or an integer -> pk
    # Checking first if node_id specified
    if node_id is not None:
        if isinstance(node_id, (str, unicode)):
            uuid = node_id
        elif isinstance(node_id, int):
            pk = node_id
        else:
            raise TypeError("'node_id' has to be either string, unicode or "
                            "integer, {} given".format(type(node_id)))

            #I am checking whether uuid, if supplied,  is a string
    if uuid is not None:
        if not isinstance(uuid, (str, unicode)):
            raise TypeError("'uuid' has to be string or unicode")
        # or whether the pk, if provided, is an integer
    elif pk is not None:
        if not isinstance(pk, int):
            raise TypeError("'pk' has to be an integer")
    else:
        # I really shouldn't get here
        assert True, "Neither pk  nor uuid was provided"

    qb = QueryBuilder()
    qb.append(class_, tag='node', project='*')

    if pk:
        qb.add_filter('node', {'id': pk})
    elif uuid:
        # Put back dashes in the right place
        start_uuid = uuid.replace('-', '')
        if query_with_dashes:
            # Essential that this is ordered from largest to smallest!
            for dash_pos in [20, 16, 12, 8]:
                if len(start_uuid) > dash_pos:
                    start_uuid = "{}-{}".format(start_uuid[:dash_pos],
                                                start_uuid[dash_pos:])

        qb.add_filter('node', {'uuid': {'like': "{}%".format(start_uuid)}})
    try:
        return qb.one()[0]
    except MultipleObjectsError:
        raise MultipleObjectsError("More than one node found")
    except NotExistent:
        raise NotExistent("No node was found")
Пример #27
0
def _collect_tags(
        node,
        calc,
        parameters=None,
        dump_aiida_database=default_options['dump_aiida_database'],
        exclude_external_contents=default_options['exclude_external_contents'],
        gzip=default_options['gzip'],
        gzip_threshold=default_options['gzip_threshold']):
    """
    Retrieve metadata from attached calculation and pseudopotentials
    and prepare it to be saved in TCOD CIF.
    """
    from aiida.common.links import LinkType
    import os, json
    import aiida
    tags = {
        '_audit_creation_method': "AiiDA version {}".format(aiida.__version__)
    }

    # Recording the dictionaries (if any)

    if len(conforming_dictionaries):
        for postfix in ['name', 'version', 'location']:
            key = '_audit_conform_dict_{}'.format(postfix)
            if key not in tags:
                tags[key] = []

    for dictionary in conforming_dictionaries:
        tags['_audit_conform_dict_name'].append(dictionary['name'])
        tags['_audit_conform_dict_version'].append(dictionary['version'])
        tags['_audit_conform_dict_location'].append(dictionary['url'])

    # Collecting metadata from input files:

    calc_data = []
    if calc is not None:
        calc_data = _collect_calculation_data(calc)

    for tag in tcod_loops['_tcod_computation'] + tcod_loops['_tcod_file']:
        tags[tag] = []

    export_files = []

    sn = 1
    for step in calc_data:
        tags['_tcod_computation_step'].append(sn)
        tags['_tcod_computation_command'].append('cd {}; ./{}'.format(
            sn, aiida_executable_name))
        tags['_tcod_computation_reference_uuid'].append(step['uuid'])
        if 'env' in step:
            tags['_tcod_computation_environment'].append("\n".join(
                ["%s=%s" % (key, step['env'][key]) for key in step['env']]))
        else:
            tags['_tcod_computation_environment'].append('')
        if 'stdout' in step and step['stdout'] is not None:
            tags['_tcod_computation_stdout'].append(step['stdout'])
        else:
            tags['_tcod_computation_stdout'].append('')
        if 'stderr' in step and step['stderr'] is not None:
            tags['_tcod_computation_stderr'].append(step['stderr'])
        else:
            tags['_tcod_computation_stderr'].append('')

        export_files.append({
            'name': "{}{}".format(sn, os.sep),
            'type': 'folder'
        })

        for f in step['files']:
            f['name'] = os.path.join(str(sn), f['name'])
        export_files.extend(step['files'])

        sn = sn + 1

    # Creating importable AiiDA database dump in CIF tags

    if dump_aiida_database and node.is_stored:
        import json
        from aiida.common.exceptions import LicensingException
        from aiida.common.folders import SandboxFolder
        from aiida.orm.importexport import export_tree

        with SandboxFolder() as folder:
            try:
                export_tree([node.dbnode],
                            folder=folder,
                            silent=True,
                            allowed_licenses=['CC0'])
            except LicensingException as e:
                raise LicensingException(e.message + \
                                         ". Only CC0 license is accepted.")

            files = _collect_files(folder.abspath)
            with open(folder.get_abs_path('data.json')) as f:
                data = json.loads(f.read())
            md5_to_url = {}
            if exclude_external_contents:
                for pk in data['node_attributes']:
                    n = data['node_attributes'][pk]
                    if 'md5' in n.keys() and 'source' in n.keys() and \
                      'uri' in n['source'].keys():
                        md5_to_url[n['md5']] = n['source']['uri']

            for f in files:
                f['name'] = os.path.join('aiida', f['name'])
                if f['type'] == 'file' and f['md5'] in md5_to_url.keys():
                    f['uri'] = md5_to_url[f['md5']]

            export_files.extend(files)

    # Describing seen files in _tcod_file_* loop

    encodings = list()

    fn = 0
    for f in export_files:
        # ID and name
        tags['_tcod_file_id'].append(fn)
        tags['_tcod_file_name'].append(f['name'])

        # Checksums
        md5sum = None
        sha1sum = None
        if f['type'] == 'file':
            md5sum = f['md5']
            sha1sum = f['sha1']
        else:
            md5sum = '.'
            sha1sum = '.'
        tags['_tcod_file_md5sum'].append(md5sum)
        tags['_tcod_file_sha1sum'].append(sha1sum)

        # Content, encoding and URI
        contents = '?'
        encoding = None
        if 'uri' in f.keys():
            contents = '.'
            tags['_tcod_file_URI'].append(f['uri'])
        else:
            tags['_tcod_file_URI'].append('?')
            if f['type'] == 'file':
                contents,encoding = \
                    cif_encode_contents(f['contents'],
                                        gzip=gzip,
                                        gzip_threshold=gzip_threshold)
            else:
                contents = '.'

        if encoding is None:
            encoding = '.'
        elif encoding not in encodings:
            encodings.append(encoding)
        tags['_tcod_file_contents'].append(contents)
        tags['_tcod_file_content_encoding'].append(encoding)

        # Role
        role = '?'
        if 'role' in f.keys():
            role = f['role']
        tags['_tcod_file_role'].append(role)

        fn = fn + 1

    # Describing the encodings

    if encodings:
        for tag in tcod_loops['_tcod_content_encoding']:
            tags[tag] = []
    for encoding in encodings:
        layers = encoding.split('+')
        for i in range(0, len(layers)):
            tags['_tcod_content_encoding_id'].append(encoding)
            tags['_tcod_content_encoding_layer_id'].append(i + 1)
            tags['_tcod_content_encoding_layer_type'].append(layers[i])

    # Describing Brillouin zone (if used)

    if calc is not None:
        from aiida.orm.data.array.kpoints import KpointsData
        kpoints_list = calc.get_inputs(KpointsData, link_type=LinkType.INPUT)
        # TODO: stop if more than one KpointsData is used?
        if len(kpoints_list) == 1:
            kpoints = kpoints_list[0]
            density, shift = kpoints.get_kpoints_mesh()
            tags['_dft_BZ_integration_grid_X'] = density[0]
            tags['_dft_BZ_integration_grid_Y'] = density[1]
            tags['_dft_BZ_integration_grid_Z'] = density[2]
            tags['_dft_BZ_integration_grid_shift_X'] = shift[0]
            tags['_dft_BZ_integration_grid_shift_Y'] = shift[1]
            tags['_dft_BZ_integration_grid_shift_Z'] = shift[2]

    from aiida.common.exceptions import MultipleObjectsError
    from aiida.common.pluginloader import all_plugins, get_plugin

    category = 'tools.dbexporters.tcod_plugins'
    plugins = list()

    if calc is not None:
        for entry_point in all_plugins(category):
            plugin = get_plugin(category, entry_point)
            if calc._plugin_type_string.endswith(plugin._plugin_type_string +
                                                 '.'):
                plugins.append(plugin)

    if len(plugins) > 1:
        raise MultipleObjectsError('more than one plugin found for {}'.format(
            calc._plugin_type_string))

    if len(plugins) == 1:
        plugin = plugins[0]
        translated_tags = translate_calculation_specific_values(calc, plugin)
        tags.update(translated_tags)

    return tags
Пример #28
0
def export_cifnode(what,
                   parameters=None,
                   trajectory_index=None,
                   store=False,
                   reduce_symmetry=default_options['reduce_symmetry'],
                   **kwargs):
    """
    The main exporter function. Exports given coordinate-containing \*Data
    node to :py:class:`aiida.orm.data.cif.CifData` node, ready to be
    exported to TCOD. All \*Data types, having method ``_get_cif()``, are
    supported in addition to :py:class:`aiida.orm.data.cif.CifData`.

    :param what: data node to be exported.
    :param parameters: a :py:class:`aiida.orm.data.parameter.ParameterData`
        instance, produced by the same calculation as the original exported
        node.
    :param trajectory_index: a step to be converted and exported in case a
        :py:class:`aiida.orm.data.array.trajectory.TrajectoryData` is
        exported.
    :param store: boolean indicating whether to store intermediate nodes or
        not. Default False.
    :param dump_aiida_database: boolean indicating whether to include the
        dump of AiiDA database (containing only transitive closure of the
        exported node). Default True.
    :param exclude_external_contents: boolean indicating whether to exclude
        nodes from AiiDA database dump, that are taken from external
        repositores and have a URL link allowing to refetch their contents.
        Default False.
    :param gzip: boolean indicating whether to Gzip large CIF text fields.
        Default False.
    :param gzip_threshold: integer indicating the maximum size (in bytes) of
        uncompressed CIF text fields when the **gzip** option is in action.
        Default 1024.
    :return: a :py:class:`aiida.orm.data.cif.CifData` node.
    """
    from aiida.common.links import LinkType
    from aiida.common.exceptions import MultipleObjectsError
    from aiida.orm.calculation.inline import make_inline
    CifData = DataFactory('cif')
    StructureData = DataFactory('structure')
    TrajectoryData = DataFactory('array.trajectory')
    ParameterData = DataFactory('parameter')

    calc = _get_calculation(what)

    if parameters is not None:
        if not isinstance(parameters, ParameterData):
            raise ValueError("Supplied parameters are not an "
                             "instance of ParameterData")
    elif calc is not None:
        params = calc.get_outputs(type=ParameterData,
                                  link_type=LinkType.CREATE)
        if len(params) == 1:
            parameters = params[0]
        elif len(params) > 0:
            raise MultipleObjectsError("Calculation {} has more than "
                                       "one ParameterData output, please "
                                       "specify which one to use with "
                                       "an option parameters='' when "
                                       "calling export_cif()".format(calc))

    if parameters is not None:
        _assert_same_parents(what, parameters)

    node = what

    # Convert node to CifData (if required)

    if not isinstance(node, CifData) and getattr(node, '_get_cif'):
        function_args = {'store': store}
        if trajectory_index is not None:
            function_args['index'] = trajectory_index
        node = node._get_cif(**function_args)

    if not isinstance(node, CifData):
        raise NotImplementedError("Exporter does not know how to "
                                  "export {}".format(type(node)))

    # Reduction of the symmetry

    if reduce_symmetry:
        from aiida.orm.data.cif import refine_inline
        ret_dict = refine_inline(node=node, store=store)
        node = ret_dict['cif']

    # Addition of the metadata

    args = ParameterData(dict=kwargs)
    function_args = {'what': what, 'args': args, 'store': store}
    if node != what:
        function_args['node'] = node
    if parameters is not None:
        function_args['parameters'] = parameters
    ret_dict = add_metadata_inline(**function_args)

    return ret_dict['cif']