acat.ga.adsorbate_comparators — ACAT 1.0.0 documentation

# Source code for acat.ga.adsorbate_comparators

    """Comparator objects relevant to particles with adsorbates."""
    from ase import Atoms

    def count_ads(atoms, adsorbate):
        """Very naive implementation only taking into account
        the symbols. atoms and adsorbate should both be supplied
        as Atoms objects."""
        syms = atoms.get_chemical_symbols()
        try:
            ads_syms = adsorbate.get_chemical_symbols()
        except AttributeError:
            # It is hopefully a string
            ads_syms = Atoms(adsorbate).get_chemical_symbols()

        counts = []
        for c in ads_syms:
            counts.append(syms.count(c))
            if len(set(counts)) == 1:
                return counts[0]
            else:
                raise NotImplementedError

    class AdsorbateCountComparator(object):
        """Compares the number of adsorbates on the particles and
        returns True if the numbers are the same, False otherwise.

        Parameters:

        adsorbate: list or string
        a supplied list of adsorbates or a string if only one adsorbate
        is possible
        """

        def __init__(self, adsorbate):
            try:
                adsorbate + ''
                # It is a string (or similar) type
                self.adsorbate = [adsorbate]
            except TypeError:
                self.adsorbate = adsorbate

        def looks_like(self, a1, a2):
            """Does the actual comparison."""
            for ads in self.adsorbate:
                ads = Atoms(ads)
                if count_ads(a1, ads) != count_ads(a2, ads):
                    return False
            return True

    class AdsorptionSitesComparator(object):
        """Compares the metal atoms in the adsorption sites and returns True
        if less than min_diff_adsorption_sites of the sites with adsorbates
        consist of different atoms.

        Ex:
        a1.info['data']['adsorbates_site_atoms'] =
        [('Cu','Ni'),('Cu','Ni'),('Ni'),('Ni')]

        a2.info['data']['adsorbates_site_atoms'] =
        [('Cu','Ni'),('Ni','Ni', 'Ni'),('Ni'),('Ni')]

        will have a difference of 2:
        (2*('Cu','Ni')-1*('Cu','Ni')=1, 1*('Ni','Ni','Ni')=1, 2*('Ni')-2*('Ni')=0)

        """

        def __init__(self, min_diff_adsorption_sites=2):
            self.min_diff_adsorption_sites = min_diff_adsorption_sites

        def looks_like(self, a1, a2):
            s = 'adsorbates_site_atoms'
            if not all([(s in a.info['data'] and
                         a.info['data'][s] != [])
                        for a in [a1, a2]]):
                return False

            counter = {}
            for asa in a1.info['data'][s]:
                t_asa = tuple(sorted(asa))
                if t_asa not in counter.keys():
                    counter[t_asa] = 1
                else:
                    counter[t_asa] += 1

            for asa in a2.info['data'][s]:
                t_asa = tuple(sorted(asa))
                if t_asa not in counter.keys():
                    counter[t_asa] = -1
                else:
                    counter[t_asa] -= 1

            # diffs = len([k for k, v in counter.items() if v != 0])
            sumdiffs = sum([abs(v) for k, v in counter.items()])

            if sumdiffs < self.min_diff_adsorption_sites:
                return True

            return False

    class AdsorptionMetalsComparator(object):
        """Compares the number of adsorbate-metal bonds and returns True if the
        number for a1 and a2 differs by less than the supplied parameter
        ``same_adsorption_number``

        Ex:
        a1.info['data']['adsorbates_bound_to'] = {'Cu':1, 'Ni':3}
        a2.info['data']['adsorbates_bound_to'] = {'Cu':.5, 'Ni':3.5}
        will have a difference of .5 in both elements:
        """

        def __init__(self, same_adsorption_number):
            self.same_adsorption_number = same_adsorption_number

        def looks_like(self, a1, a2):
            s = 'adsorbates_bound_to'
            if not all([(s in a.info['data'] and
                         any(a.info['data'][s].values()))
                        for a in [a1, a2]]):
                return False

            diffs = [a1.info['data'][s][k] - a2.info['data'][s][k]
                     for k in a1.info['data'][s].keys()]
            for d in diffs:
                if abs(d) < self.same_adsorption_number:
                    return True
            return False