示例#1
0
    def list_available_sbu(self,
                           topology_name=None,
                           from_list=[],
                           coercion=False):
        """Return the dictionary of compatible SBU.

        Filters the existing SBU by shape until only
        those compatible with a slot within the topology are left.
        topology -- name of the topology in the database
        from_list -- only consider SBU from this list
        """
        av_sbu = defaultdict(list)
        if from_list:
            sbu_names = from_list
        else:
            sbu_names = list(self.sbu.keys())
        if topology_name is not None or self.topology is not None:
            if topology_name is not None:
                topology = Topology(name=topology_name,
                                    atoms=self.topologies[topology_name])
            else:
                topology = self.topology
            logger.info("List of compatible SBU with topology {t}:".format(
                t=topology.name))
            sbu_list = []
            logger.info("\tShape analysis of {} available SBU...".format(
                len(self.sbu)))
            for sbuk in sbu_names:
                sbuv = self.sbu[sbuk]
                try:
                    sbu = SBU(name=sbuk, atoms=sbuv)
                except Exception as exc:
                    logger.debug("SBU {k} not loaded: {exc}".format(k=sbuk,
                                                                    exc=exc))
                    continue
                sbu_list.append(sbu)
            for sites in topology.equivalent_sites:
                logger.info("\tSites considered : {}".format(", ".join(
                    [str(s) for s in sites])))
                shape = topology.shapes[sites[0]]
                for sbu in sbu_list:
                    is_compatible = sbu.is_compatible(shape, coercion=coercion)
                    if is_compatible:
                        logger.info("\t\t|--> {k}".format(k=sbu.name))
                        av_sbu[tuple(sites)].append(sbu.name)
        else:
            logger.info("Listing full database of SBU.")
            av_sbu = list(self.sbu.keys())
        return dict(av_sbu)
示例#2
0
    def get_sbu_dict(self, sbu_names, coercion=False):
        """Return a dictionary of SBU by corresponding fragment.

        This stage get a one to one correspondance between
        each topology slot and an available SBU from the list of names.

        Parameters
        ----------
        topology: autografs.utils.topology.Topology
            the topology to use as map for creation
            of the framework.
        sbu_names: [str,...] or [(str,float),...]
            the list of SBU names as strings. accepts probabilistic
            SBU scheduling.
        coercion: bool, optional
            If True, force compatibility by coordination alone

        Returns
        -------
        dict
            {slot index: autografs.utils.sbu.SBU, ...}
            the dictionary is the map that will generate
            the final Framework object
        """
        assert self.topology is not None
        weights = defaultdict(list)
        by_shape = defaultdict(list)
        for name in sbu_names:
            # check if probabilities included
            if isinstance(name, tuple):
                name, p = name
                p = float(p)
                name = str(name)
            else:
                p = 1.0
            # create the SBU object
            sbu = SBU(name=name, atoms=self.sbu[name])
            slots = self.topology.has_compatible_slots(sbu=sbu,
                                                       coercion=coercion)
            if not slots:
                continue
            for slot in slots:
                weights[slot].append(p)
                by_shape[slot].append(sbu)
        # now fill the choices
        sbu_dict = {}
        for index, shape in self.topology.shapes.items():
            # here, should accept weights also
            shape = tuple(shape)
            if shape not in by_shape.keys():
                logger.info("Unfilled slot at index {idx}".format(idx=index))
            p = weights[shape]
            # no weights means same proba
            p /= numpy.sum(p)
            sbu_chosen = numpy.random.choice(by_shape[shape], p=p).copy()
            sbu_dict[index] = sbu_chosen
        return sbu_dict
示例#3
0
    def get_sbu_dict(self, sbu_names, coercion=False):
        """Return a dictionary of SBU by corresponding fragment.

        This stage get a one to one correspondance between
        each topology slot and an available SBU from the list of names.
        topology  -- the Topology object
        sbu_names -- the list of SBU names as strings
        coercion -- wether to force compatibility by coordination alone
        """
        logger.debug("Generating slot to SBU map.")
        assert self.topology is not None
        weights = defaultdict(list)
        by_shape = defaultdict(list)
        for name in sbu_names:
            # check if probabilities included
            if isinstance(name, tuple):
                name, p = name
                p = float(p)
                name = str(name)
            else:
                p = 1.0
            # create the SBU object
            sbu = SBU(name=name, atoms=self.sbu[name])
            slots = self.topology.has_compatible_slots(sbu=sbu,
                                                       coercion=coercion)
            if not slots:
                logger.debug(
                    "SBU {s} has no compatible slot in topology {t}".format(
                        s=name, t=self.topology.name))
                continue
            for slot in slots:
                weights[slot].append(p)
                by_shape[slot].append(sbu)
        # now fill the choices
        sbu_dict = {}
        for index, shape in self.topology.shapes.items():
            # here, should accept weights also
            shape = tuple(shape)
            if shape not in by_shape.keys():
                logger.info("Unfilled slot at index {idx}".format(idx=index))
            p = weights[shape]
            # no weights means same proba
            p /= numpy.sum(p)
            sbu_chosen = numpy.random.choice(by_shape[shape], p=p).copy()
            logger.debug("Slot {sl}: {sb} chosen with p={p}.".format(
                sl=index, sb=sbu_chosen.name, p=p))
            sbu_dict[index] = sbu_chosen
        return sbu_dict
示例#4
0
    def list_available_sbu(self,
                           topology_name=None,
                           from_list=[],
                           coercion=False):
        """Return the dictionary of compatible SBU.

        Filters the existing SBU by shape until only
        those compatible with a slot within the topology are left.

        Parameters
        ----------
        topology_name: str
            Name of the topology to analyse
        from_list: [str, ...]
            subset of SBU to consider
        coercion: bool, optional
            If True, force compatibility by coordination alone

        Returns
        -------
        list
            list of SBU names
        """
        av_sbu = defaultdict(list)
        if from_list:
            sbu_names = from_list
        else:
            sbu_names = list(self.sbu.keys())
        sbu_names = sorted(sbu_names)
        if topology_name is not None or self.topology is not None:
            if topology_name is not None:
                topology = Topology(name=topology_name,
                                    atoms=self.topologies[topology_name])
            else:
                topology = self.topology
            logger.info(("List of compatible SBU"
                         " with topology {t}:").format(t=topology.name))
            sbu_list = []
            logger.info(("\tShape analysis of"
                         " {le} available SBU...").format(le=len(self.sbu)))
            for sbuk in sbu_names:
                sbuv = self.sbu[sbuk]
                sbu = SBU(name=sbuk, atoms=sbuv)
                if sbu is None:
                    continue
                sbu_list.append(sbu)
            for sites in topology.equivalent_sites:
                logger.info(("\tSites considered"
                             " : {s}").format(s=", ".join(map(str, sites))))
                shape = topology.shapes[sites[0]]
                for sbu in sbu_list:
                    is_compatible = sbu.is_compatible(shape, coercion=coercion)
                    if is_compatible:
                        logger.info("\t\t|--> {k}".format(k=sbu.name))
                        av_sbu[tuple(sites)].append(sbu.name)
            return dict(av_sbu)
        else:
            logger.info("Listing full database of SBU.")
            av_sbu = list(self.sbu.keys())
            av_sbu = sorted(av_sbu)
            return av_sbu
示例#5
0
    def list_available_topologies(self,
                                  sbu_names=[],
                                  full=True,
                                  max_size=100,
                                  from_list=[],
                                  pbc="all",
                                  coercion=False):
        """Return a list of topologies compatible with the SBUs

        For each sbu in the list given in input, refines first by coordination
        then by shapes within the topology. Thus, we do not need to analyze
        every topology.

        Parameters
        ----------
        sbu_names: [str,...]
            the list of SBU names as strings
        full: bool, optional
            if True, only list topologies fully
            filled with the given SBU names
        max_size: int
            maximum size of topologies to consider, in number of
            building units
        from_list: [str, ...]
            subset of topologies to consider
        coercion: bool, optional
            If True, force compatibility by coordination alone
        pbc: str, optional
            can be '2D', '3D' or 'all'. Restrits the
            search to topologies of the given periodicity

        Returns
        -------
        list
            list of topology names
        """
        these_topologies_names = self.topologies.keys()
        if max_size is None:
            max_size = 999999
        if from_list:
            these_topologies_names = from_list
        if pbc == "2D":
            logger.info("only considering 2D periodic topologies.")
            these_topologies_names = [
                tk for tk, tv in self.topologies.items() if sum(tv.pbc) == 2
            ]
        elif pbc == "3D":
            logger.info("only considering 3D periodic topologies.")
            these_topologies_names = [
                tk for tk, tv in self.topologies.items() if sum(tv.pbc) == 3
            ]
        elif pbc != "all":
            logger.info(("pbc keyword has to be '2D','3D'"
                         " or 'all'. Assumed 'all'."))
        these_topologies_names = sorted(these_topologies_names)
        if sbu_names:
            logger.info("Checking topology compatibility.")
            topologies = []
            sbu = [SBU(name=n, atoms=self.sbu[n]) for n in sbu_names]
            for tk in these_topologies_names:
                tv = self.topologies[tk]
                if max_size is None or len(tv) > max_size:
                    continue
                topology = Topology(name=tk, atoms=tv)
                if topology is None:
                    continue
                # For now, no shape compatibilities
                filled = {
                    shape: False
                    for shape in topology.get_unique_shapes()
                }
                fslots = [
                    topology.has_compatible_slots(s, coercion=coercion)
                    for s in sbu
                ]
                for slots in fslots:
                    for slot in slots:
                        filled[slot] = True
                if all(filled.values()):
                    logger.info(("\tTopology {tk}"
                                 " fully available.").format(tk=tk))
                    topologies.append(tk)
                elif any(filled.values()) and not full:
                    logger.info(("\tTopology {tk}"
                                 " partially available.").format(tk=tk))
                    topologies.append(tk)
        else:
            logger.info("Listing full database of topologies.")
            topologies = list(self.topologies.keys())
            topologies = sorted(topologies)
        return topologies
示例#6
0
    def make(self,
             topology_name=None,
             sbu_names=None,
             sbu_dict=None,
             supercell=(1, 1, 1),
             coercion=False):
        """Create a framework using given topology and sbu.

        Main funtion of Autografs. The sbu names and topology's
        are to be taken from the compiled databases. The sbu_dict
        can also be passed for multiple components frameworks.
        If the sbu_names is a list of tuples in the shape
        (name,n), the number n will be used as a drawing probability
        when multiple options are available for the same shape.

        Parameters
        ----------
        topology_name: str, optional
            name of the topology to use. If not given,
            Autografs will try to use its stored
            topology attribute.
        sbu_names: [str,...], optional
            list of names of the sbu to use. The names are the
            keys used to search the SBU database. If not used,
            the sbu_dict option has to be given.
        sbu_dict: {int:str,...}, optional
            slot index to sbu name mapping. ASE Atoms can
            also be given. If used, this argument will take
            precedence over sbu_names.
        supercell: int or (int, int, int), optional
            multiplicator for generation of a supercell
            of the topology, done before any framework
            generation. Useful for statistical defect
            introduction.
        coercion: bool, optional
            force the compatibility detection to only consider
            the multiplicity of SBU: any 4 connected SBU
            can fit any 4-connected slot.

        Returns
        -------
        autografs.framework.Framework
            the scaled, aligned version of the framework
            built using the defined options.
        """
        logger.info("{0:-^50}".format(" Starting Framework Generation "))
        logger.info("")
        self.sbudict = None
        # only set the topology if not already done
        if topology_name is not None:
            self.set_topology(topology_name=topology_name, supercell=supercell)
        # container for the aligned SBUs
        aligned = Framework()
        aligned.set_topology(self.topology)
        # identify the corresponding SBU
        if sbu_dict is None and sbu_names is not None:
            logger.info("Scheduling the SBU to slot alignment.")
            self.sbu_dict = self.get_sbu_dict(sbu_names=sbu_names,
                                              coercion=coercion)
        elif sbu_dict is not None:
            logger.info("SBU to slot alignment is user defined.")
            # the sbu_dict has been passed. if not SBU object, create them
            for k, v in sbu_dict.items():
                if not isinstance(v, SBU):
                    if not isinstance(v, ase.Atoms):
                        name = str(v)
                        v = self.sbu[name].copy()
                    elif "name" in v.info.keys():
                        name = v.info["name"]
                    else:
                        name = str(k)
                    sbu_dict[k] = SBU(name=name, atoms=v)
            self.sbu_dict = sbu_dict
        else:
            raise ValueError("Either supply sbu_names or sbu_dict.")
        # some logging for pretty information
        for idx, sbu in self.sbu_dict.items():
            logging.info("\tSlot {sl}".format(sl=idx))
            logging.info("\t   |--> SBU {sbn}".format(sbn=sbu.name))
        # carry on
        alpha = 0.0
        # should be parrallelized one of these days
        for idx, sbu in self.sbu_dict.items():
            # now align and get the scaling factor
            sbu, f = self.align(fragment=self.topology.fragments[idx], sbu=sbu)
            alpha += f
            aligned.append(index=idx, sbu=sbu)
        logger.info("")
        # refine the cell of the aligned object
        aligned.refine(alpha0=alpha)
        # inform user of finished generation
        logger.info("")
        logger.info("Finished framework generation.")
        logger.info("")
        logger.info("{0:-^50}".format(" Post-treatment "))
        logger.info("")
        return aligned
示例#7
0
    def make(self,
             topology_name=None,
             sbu_names=None,
             sbu_dict=None,
             supercell=(1, 1, 1),
             coercion=False):
        """Create a framework using given topology and sbu.

        Main funtion of Autografs. The sbu names and topology's
        are to be taken from the compiled databases. The sbu_dict
        can also be passed for multiple components frameworks.
        If the sbu_names is a list of tuples in the shape
        (name,n), the number n will be used as a drawing probability
        when multiple options are available for the same shape.
        topology_name -- name of the topology to use
        sbu_names     -- list of names of the sbu to use
        sbu_dict -- (optional) one to one sbu to slot correspondance
                    in the shape {index of slot : 'name of sbu'}
        supercell -- (optional) creates a supercell pre-treatment
        coercion -- (optional) force the compatibility to only consider
                    the multiplicity of SBU
        """
        logger.info("{0:*^80}".format(" STARTING THE MOF GENERATION "))
        self.sbudict = None
        # only set the topology if not already done
        if topology_name is not None:
            self.set_topology(topology_name=topology_name, supercell=supercell)
        # container for the aligned SBUs
        aligned = Framework()
        aligned.set_topology(self.topology)
        # identify the corresponding SBU
        try:
            if sbu_dict is None and sbu_names is not None:
                logger.info("Scheduling the SBU to slot alignment.")
                self.sbu_dict = self.get_sbu_dict(sbu_names=sbu_names,
                                                  coercion=coercion)
            elif sbu_dict is not None:
                logger.info("SBU to slot alignment is user defined.")
                # the sbu_dict has been passed. if not SBU object, create them
                for k, v in sbu_dict.items():
                    if not isinstance(v, SBU):
                        if not isinstance(v, ase.Atoms):
                            name = str(v)
                            v = self.sbu[name].copy()
                        elif "name" in v.info.keys():
                            name = v.info["name"]
                        else:
                            name = str(k)
                        sbu_dict[k] = SBU(name=name, atoms=v)
                self.sbu_dict = sbu_dict
            else:
                raise RuntimeError("Either supply sbu_names or sbu_dict.")
        except RuntimeError as exc:
            logger.error("Slot to SBU mappping interrupted.")
            logger.error("{exc}".format(exc=exc))
            logger.info(
                "No valid framework was generated. Please check your input.")
            logger.info(
                "You can coerce sbu assignment by directly passing a slot to sbu dictionary."
            )
            return
        # some logging
        self.log_sbu_dict(sbu_dict=self.sbu_dict, topology=self.topology)
        # carry on
        alpha = 0.0
        for idx, sbu in self.sbu_dict.items():
            logger.debug("Treating slot number {idx}".format(idx=idx))
            logger.debug("\t|--> Aligning SBU {name}".format(name=sbu.name))
            # now align and get the scaling factor
            sbu, f = self.align(fragment=self.topology.fragments[idx], sbu=sbu)
            alpha += f
            aligned.append(index=idx, sbu=sbu)
        aligned.refine(alpha0=alpha)
        return aligned
示例#8
0
    def list_available_topologies(self,
                                  sbu_names=[],
                                  full=True,
                                  max_size=100,
                                  from_list=[],
                                  pbc="all",
                                  coercion=False):
        """Return a list of topologies compatible with the SBUs

        For each sbu in the list given in input, refines first by coordination
        then by shapes within the topology. Thus, we do not need to analyze
        every topology.
        sbu  -- list of sbu names
        full -- wether the topology is entirely represented by the sbu
        max_size -- maximum size of in SBU numbers of topologies to consider
        from_list -- only consider topologies from this list
        """
        these_topologies_names = self.topologies.keys()
        if max_size is None:
            max_size = 999999
        if from_list:
            these_topologies_names = from_list
        if pbc == "2D":
            logger.info("only considering 2D periodic topologies.")
            these_topologies_names = [
                tk for tk, tv in self.topologies.items() if sum(tv.pbc) == 2
            ]
        elif pbc == "3D":
            logger.info("only considering 3D periodic topologies.")
            these_topologies_names = [
                tk for tk, tv in self.topologies.items() if sum(tv.pbc) == 3
            ]
        elif pbc != "all":
            logger.info(
                "pbc keyword has to be '2D','3D' or 'all'. Assumed 'all'.")
        if sbu_names:
            logger.info("Checking topology compatibility.")
            topologies = []
            sbu = [SBU(name=n, atoms=self.sbu[n]) for n in sbu_names]
            for tk in these_topologies_names:
                tv = self.topologies[tk]
                if max_size is None or len(tv) > max_size:
                    logger.debug("\tTopology {tk} to big : size = {s}.".format(
                        tk=tk, s=len(tv)))
                    continue
                try:
                    topology = Topology(name=tk, atoms=tv)
                except Exception as exc:
                    logger.debug("Topology {tk} not loaded: {exc}".format(
                        tk=tk, exc=exc))
                    continue
                filled = {
                    shape: False
                    for shape in topology.get_unique_shapes()
                }
                slots_full = [
                    topology.has_compatible_slots(s, coercion=coercion)
                    for s in sbu
                ]
                for slots in slots_full:
                    for slot in slots:
                        filled[slot] = True
                if all(filled.values()):
                    logger.info(
                        "\tTopology {tk} fully available.".format(tk=tk))
                    topologies.append(tk)
                elif any(filled.values()) and not full:
                    logger.info(
                        "\tTopology {tk} partially available.".format(tk=tk))
                    topologies.append(tk)
                else:
                    logger.debug(
                        "\tTopology {tk} not available.".format(tk=tk))
        else:
            logger.info("Listing full database of topologies.")
            topologies = list(self.topologies.keys())
        return topologies