Пример #1
0
    def upload_potcar_family(cls,
                             source,
                             group_name,
                             group_description=None,
                             stop_if_existing=True,
                             dry_run=False):
        """
        Upload a set of POTCAR potentials as a family.

        :param folder: a path containing all POTCAR files to be added.
        :param group_name: the name of the group to create. If it exists and is
            non-empty, a UniquenessError is raised.
        :param group_description: a string to be set as the group description.
            Overwrites previous descriptions, if the group was existing.
        :param stop_if_existing: if True, check for the md5 of the files and,
            if the file already exists in the DB, raises a MultipleObjectsError.
            If False, simply adds the existing UPFData node to the group.
        :param dry_run: If True, do not change the database.
        """
        group = cls._prepare_group_for_upload(group_name,
                                              group_description,
                                              dry_run=dry_run)

        potcar_finder = PotcarWalker(source)
        potcar_finder.walk()
        num_files = len(potcar_finder.potcars)
        family_nodes_uuid = [node.uuid
                             for node in group.nodes] if not dry_run else []
        potcars_tried_upload = cls._try_upload_potcars(
            potcar_finder.potcars,
            stop_if_existing=stop_if_existing,
            dry_run=dry_run)
        new_potcars_added = [
            (potcar, created, file_path)
            for potcar, created, file_path in potcars_tried_upload
            if potcar.uuid not in family_nodes_uuid
        ]

        for potcar, created, file_path in new_potcars_added:
            if created:
                aiidalogger.debug(
                    'New PotcarData node %s created while uploading file %s for family %s',
                    potcar.uuid, file_path, group_name)
            else:
                aiidalogger.debug(
                    'PotcarData node %s used instead of uploading file %s to family %s',
                    potcar.uuid, file_path, group_name)

        if not dry_run:
            group.add_nodes(
                [potcar for potcar, created, file_path in new_potcars_added])

        num_added = len(new_potcars_added)
        num_uploaded = len([item for item in new_potcars_added
                            if item[1]])  # item[1] refers to 'created'

        return num_files, num_added, num_uploaded
Пример #2
0
    def import_family(cls,
                      folder,
                      familyname=None,
                      family_desc=None,
                      store=True,
                      stop_if_existing=False):
        """Import a family from a folder like the ones distributed with VASP,
        usually named potpaw_XXX"""
        from aiida.common import aiidalogger

        ffound = []
        fupl = []
        family_path = os.path.abspath(folder)
        # ~ ffname = os.path.basename(
        # ~ os.path.dirname(folder)).replace('potpaw_', '')
        # ~ famname = familyname or ffname

        group, group_created = cls.get_or_create_famgroup(familyname)

        # Always update description, even if the group already existed
        group.description = family_desc

        paw_list = cls._find_paws(family_path, ffound, group, group_created)

        if stop_if_existing:
            for pawinfo in paw_list:
                if not pawinfo[1]:
                    raise ValueError("A PAW with identical MD5 to "
                                     '' + pawinfo[2] + " cannot be added with "
                                     "stop_if_existing")

        for pawinfo in paw_list:
            paw = pawinfo[0]
            created = pawinfo[1]
            path = pawinfo[2]
            if store:
                if created:
                    paw.store_all()
                    aiidalogger.debug("New node %s created for file %s",
                                      paw.uuid, path)
                    fupl.append(path)
                else:
                    aiidalogger.debug("Reusing node %s for file %s", paw.uuid,
                                      path)

        if store:
            if group_created:
                group.store()
                aiidalogger.debug("New PAW family goup %s created", group.uuid)
            group.add_nodes(i[0] for i in paw_list)
        else:
            print map(repr, [i[0] for i in paw_list])

        return ffound, fupl
Пример #3
0
    def import_family(cls, folder, familyname=None,
                      family_desc=None, store=True, stop_if_existing=False):
        '''Import a family from a folder like the ones distributed with VASP,
        usually named potpaw_XXX'''
        from aiida.common import aiidalogger

        ffound = []
        fupl = []
        paw_list = []
        fp = os.path.abspath(folder)
        # ~ ffname = os.path.basename(
            # ~ os.path.dirname(folder)).replace('potpaw_', '')
        # ~ famname = familyname or ffname

        group, group_created = cls.get_or_create_famgroup(familyname)

        # Always update description, even if the group already existed
        group.description = family_desc

        for pawf in os.listdir(fp):
            try:
                ap = os.path.join(fp, pawf)
                pp = os.path.join(ap, 'POTCAR')
                if os.path.isdir(ap) and os.path.exists(pp):
                    ffound.append(pawf)
                    paw, paw_created = cls.get_or_create(ap)
                    # ~ paw._set_attr('family', famname)
                    # ~ upload = paw_created
                    # enforce group-wise uniqueness of symbols
                    in_group = False
                    if not group_created:
                        in_group = bool(cls.load_paw(group=group,
                                        symbol=paw.symbol, silent=True))
                    if not in_group:
                        paw_list.append((paw, paw_created, pawf))
            except:
                import sys
                e = sys.exc_info()[1]
                print 'WARNING: skipping ' + os.path.abspath(
                    os.path.join(fp, pawf))
                print '  ' + e.__class__.__name__ + ': ' + e.message

        if stop_if_existing:
            for pawinfo in paw_list:
                if not pawinfo[1]:
                    raise ValueError("A PAW with identical MD5 to "
                                     '' + pawinfo[2] + " cannot be added with "
                                     "stop_if_existing")

        for pawinfo in paw_list:
            paw = pawinfo[0]
            created = pawinfo[1]
            path = pawinfo[2]
            if store:
                if created:
                    paw.store_all()
                    aiidalogger.debug("New node {} created for file {}".format(
                        paw.uuid, path))
                    fupl.append(path)
                else:
                    aiidalogger.debug("Reusing node {} for file {}".format(
                        paw.uuid, path))

        if store:
            if group_created:
                group.store()
                aiidalogger.debug("New PAW family goup {} created".format(
                    group.uuid))
            group.add_nodes(i[0] for i in paw_list)
        else:
            print map(repr, [i[0] for i in paw_list])

        return ffound, fupl
Пример #4
0
    def upload_basisset_family(cls,
                               folder,
                               group_name,
                               group_description,
                               stop_if_existing=True,
                               extension=".basis",
                               dry_run=False):
        """
        Upload a set of Basis Set files in a given group.

        :param folder: a path containing all Basis Set files to be added.
            Only files ending in the set extension (case-insensitive) are considered.
        :param group_name: the name of the group to create. If it exists and is
            non-empty, a UniquenessError is raised.
        :param group_description: a string to be set as the group description.
            Overwrites previous descriptions, if the group was existing.
        :param stop_if_existing: if True, check for the md5 of the files and,
            if the file already exists in the DB, raises a MultipleObjectsError.
            If False, simply adds the existing BasisSetData node to the group.
        :param extension: the filename extension to look for
        :param dry_run: If True, do not change the database.
        """
        from aiida.common import aiidalogger
        from aiida.orm import Group
        from aiida.common.exceptions import UniquenessError, NotExistent
        from aiida_crystal17.aiida_compatability import get_automatic_user

        automatic_user = get_automatic_user()

        if not os.path.isdir(folder):
            raise ValueError("folder must be a directory")

        # only files, and only those ending with specified exension;
        # go to the real file if it is a symlink
        files = [
            os.path.realpath(os.path.join(folder, i))
            for i in os.listdir(folder)
            if os.path.isfile(os.path.join(folder, i))
            and i.lower().endswith(extension)
        ]

        nfiles = len(files)

        try:
            group = Group.get(name=group_name, type_string=BASISGROUP_TYPE)
            group_created = False
        except NotExistent:
            group = Group(name=group_name,
                          type_string=BASISGROUP_TYPE,
                          user=automatic_user)
            group_created = True

        if group.user.email != automatic_user.email:
            raise UniquenessError(
                "There is already a BasisFamily group with name {}"
                ", but it belongs to user {}, therefore you "
                "cannot modify it".format(group_name, group.user.email))

        # Always update description, even if the group already existed
        group.description = group_description

        # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY

        basis_and_created = _retrieve_basis_sets(files, stop_if_existing)
        # check whether basisset are unique per element
        elements = [(i[0].element, i[0].md5sum) for i in basis_and_created]
        # If group already exists, check also that I am not inserting more than
        # once the same element
        if not group_created:
            for aiida_n in group.nodes:
                # Skip non-basis sets
                if not isinstance(aiida_n, BasisSetData):
                    continue
                elements.append((aiida_n.element, aiida_n.md5sum))

        elements = set(
            elements)  # Discard elements with the same MD5, that would
        # not be stored twice
        elements_names = [e[0] for e in elements]

        if not len(elements_names) == len(set(elements_names)):
            duplicates = set(
                [x for x in elements_names if elements_names.count(x) > 1])
            duplicates_string = ", ".join(i for i in duplicates)
            raise UniquenessError(
                "More than one Basis found for the elements: " +
                duplicates_string + ".")

        # At this point, save the group, if still unstored
        if group_created and not dry_run:
            group.store()

        # save the basis set in the database, and add them to group
        for basisset, created in basis_and_created:
            if created:
                if not dry_run:
                    basisset.store()

                aiidalogger.debug("New node {0} created for file {1}".format(  # pylint: disable=logging-format-interpolation
                    basisset.uuid, basisset.filename))
            else:
                aiidalogger.debug("Reusing node {0} for file {1}".format(  # pylint: disable=logging-format-interpolation
                    basisset.uuid, basisset.filename))

        # Add elements to the group all together
        if not dry_run:
            group.add_nodes(basis for basis, created in basis_and_created)

        nuploaded = len([_ for _, created in basis_and_created if created])

        return nfiles, nuploaded
Пример #5
0
def upload_psf_family(folder,
                      group_name,
                      group_description,
                      stop_if_existing=True):
    """
    Upload a set of PSF files in a given group.

    :param folder: a path containing all PSF files to be added.
        Only files ending in .PSF (case-insensitive) are considered.
    :param group_name: the name of the group to create. If it exists and is
        non-empty, a UniquenessError is raised.
    :param group_description: a string to be set as the group description.
        Overwrites previous descriptions, if the group was existing.
    :param stop_if_existing: if True, check for the md5 of the files and,
        if the file already exists in the DB, raises a MultipleObjectsError.
        If False, simply adds the existing PsfData node to the group.
    """
    import os
    import aiida.common
    from aiida.common import aiidalogger
    from aiida.orm import Group
    from aiida.common.exceptions import UniquenessError, NotExistent
    from aiida.backends.utils import get_automatic_user
    from aiida.orm.querybuilder import QueryBuilder
    if not os.path.isdir(folder):
        raise ValueError("folder must be a directory")

    # only files, and only those ending with .psf or .PSF;
    # go to the real file if it is a symlink
    files = [
        os.path.realpath(os.path.join(folder, i)) for i in os.listdir(folder)
        if os.path.isfile(os.path.join(folder, i))
        and i.lower().endswith('.psf')
    ]

    nfiles = len(files)

    try:
        group = Group.get(name=group_name, type_string=PSFGROUP_TYPE)
        group_created = False
    except NotExistent:
        group = Group(name=group_name,
                      type_string=PSFGROUP_TYPE,
                      user=get_automatic_user())
        group_created = True

    if group.user != get_automatic_user():
        raise UniquenessError("There is already a PsfFamily group with name {}"
                              ", but it belongs to user {}, therefore you "
                              "cannot modify it".format(
                                  group_name, group.user.email))

    # Always update description, even if the group already existed
    group.description = group_description

    # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY

    pseudo_and_created = []

    for f in files:
        md5sum = aiida.common.utils.md5_file(f)
        qb = QueryBuilder()
        qb.append(PsfData, filters={'attributes.md5': {'==': md5sum}})
        existing_psf = qb.first()

        #existing_psf = PsfData.query(dbattributes__key="md5",
        #                            dbattributes__tval = md5sum)

        if existing_psf is None:
            # return the psfdata instances, not stored
            pseudo, created = PsfData.get_or_create(f,
                                                    use_first=True,
                                                    store_psf=False)
            # to check whether only one psf per element exists
            # NOTE: actually, created has the meaning of "to_be_created"
            pseudo_and_created.append((pseudo, created))
        else:
            if stop_if_existing:
                raise ValueError("A PSF with identical MD5 to "
                                 " {} cannot be added with stop_if_existing"
                                 "".format(f))
            existing_psf = existing_psf[0]
            pseudo_and_created.append((existing_psf, False))

    # check whether pseudo are unique per element
    elements = [(i[0].element, i[0].md5sum) for i in pseudo_and_created]
    # If group already exists, check also that I am not inserting more than
    # once the same element
    if not group_created:
        for aiida_n in group.nodes:
            # Skip non-pseudos
            if not isinstance(aiida_n, PsfData):
                continue
            elements.append((aiida_n.element, aiida_n.md5sum))

    elements = set(elements)  # Discard elements with the same MD5, that would
    # not be stored twice
    elements_names = [e[0] for e in elements]

    if not len(elements_names) == len(set(elements_names)):
        duplicates = set(
            [x for x in elements_names if elements_names.count(x) > 1])
        duplicates_string = ", ".join(i for i in duplicates)
        raise UniquenessError("More than one PSF found for the elements: " +
                              duplicates_string + ".")

    # At this point, save the group, if still unstored
    if group_created:
        group.store()

    # save the psf in the database, and add them to group
    for pseudo, created in pseudo_and_created:
        if created:
            pseudo.store()

            aiidalogger.debug("New node {} created for file {}".format(
                pseudo.uuid, pseudo.filename))
        else:
            aiidalogger.debug("Reusing node {} for file {}".format(
                pseudo.uuid, pseudo.filename))

    # Add elements to the group all togetehr
    group.add_nodes(pseudo for pseudo, created in pseudo_and_created)

    nuploaded = len([_ for _, created in pseudo_and_created if created])

    return nfiles, nuploaded
Пример #6
0
def parse_upf(fname, check_filename=True):
    """
    Try to get relevant information from the UPF. For the moment, only the
    element name. Note that even UPF v.2 cannot be parsed with the XML minidom!
    (e.g. due to the & characters in the human-readable section).

    If check_filename is True, raise a ParsingError exception if the filename
    does not start with the element name.
    """
    import os

    from aiida.common.exceptions import ParsingError
    from aiida.common import aiidalogger
    # TODO: move these data in a 'chemistry' module
    from aiida.orm.data.structure import _valid_symbols

    parsed_data = {}

    with open(fname) as f:
        first_line = f.readline().strip()
        match = _upfversion_regexp.match(first_line)
        if match:
            version = match.group('version')
            aiidalogger.debug("Version found: {} for file {}".format(
                version, fname))
        else:
            aiidalogger.debug("Assuming version 1 for file {}".format(fname))
            version = "1"

        parsed_data['version'] = version
        try:
            version_major = int(version.partition('.')[0])
        except ValueError:
            # If the version string does not start with a dot, fallback
            # to version 1
            aiidalogger.debug("Falling back to version 1 for file {}, "
                              "version string '{}' unrecognized".format(
                                  fname, version))
            version_major = 1

        element = None
        if version_major == 1:
            for l in f:
                match = _element_v1_regexp.match(l.strip())
                if match:
                    element = match.group('element_name')
                    break
        else:  # all versions > 1
            for l in f:
                match = _element_v2_regexp.match(l.strip())
                if match:
                    element = match.group('element_name')
                    break

        if element is None:
            raise ParsingError(
                "Unable to find the element of UPF {}".format(fname))
        element = element.capitalize()
        if element not in _valid_symbols:
            raise ParsingError("Unknown element symbol {} for file {}".format(
                element, fname))
        if check_filename:
            if not os.path.basename(fname).lower().startswith(element.lower()):
                raise ParsingError("Filename {0} was recognized for element "
                                   "{1}, but the filename does not start "
                                   "with {1}".format(fname, element))

        parsed_data['element'] = element

    return parsed_data