Building things

Operate adsorbate

acat.build.action.add_adsorbate(atoms, adsorbate, site=None, surface=None, morphology=None, indices=None, height=None, composition=None, orientation=None, tilt_angle=0.0, subsurf_element=None, all_sites=None)[source]

A general function for adding one adsorbate to the surface. Note that this function adds one adsorbate to a random site that meets the specified condition regardless of it is already occupied or not. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • atoms (ase.Atoms object) – Accept any ase.Atoms object. No need to be built-in.

  • adsorbate (str or ase.Atom object or ase.Atoms object) – The adsorbate species to be added onto the surface.

  • site (str, default None) – The site type that the adsorbate should be added to.

  • surface (str, default None) – The surface type (crystal structure + Miller indices) If the structure is a periodic surface slab, this is required. If the structure is a nanoparticle, the function enumerates only the sites on the specified surface.

  • morphology (str, default None) – The morphology type that the adsorbate should be added to. Only available for surface slabs.

  • indices (list or tuple) – The indices of the atoms that contribute to the site that you want to add adsorbate to. This has the highest priority.

  • height (float, default None) – The height of the added adsorbate from the surface. Use the default settings if not specified.

  • composition (str, default None) – The elemental of the site that should be added to.

  • orientation (list or numpy.array, default None) – The vector that the multidentate adsorbate is aligned to.

  • tilt_angle (float, default 0.) – Tilt the adsorbate with an angle (in degress) relative to the surface normal.

  • subsurf_element (str, default None) – The subsurface element of the hcp or 4fold hollow site that should be added to.

  • all_sites (list of dicts, default None) – The list of all sites. Provide this to make the function much faster. Useful when the function is called many times.

Example

To add a NO molecule to a bridge site consists of one Pt and one Ni on the fcc111 surface of a truncated octahedron:

>>> from acat.build.action import add_adsorbate
>>> from ase.cluster import Octahedron
>>> from ase.visualize import view
>>> atoms = Octahedron('Ni', length=7, cutoff=2)
>>> for atom in atoms:
...     if atom.index % 2 == 0:
...         atom.symbol = 'Pt'
>>> add_adsorbate(atoms, adsorbate='NO', site='bridge',
...               surface='fcc111', composition='NiPt')
>>> view(atoms)

Output:

_images/add_adsorbate.png
acat.build.action.add_adsorbate_to_site(atoms, adsorbate, site, height=None, orientation=None, tilt_angle=0.0)[source]

The base function for adding one adsorbate to a site. Site must include information of ‘normal’ and ‘position’. Useful for adding adsorbate to multiple sites or adding multidentate adsorbates.

Parameters
  • atoms (ase.Atoms object) – Accept any ase.Atoms object. No need to be built-in.

  • adsorbate (str or ase.Atom object or ase.Atoms object) – The adsorbate species to be added onto the surface.

  • site (dict) – The site that the adsorbate should be added to. Must contain information of the position and the normal vector of the site.

  • height (float, default None) – The height of the added adsorbate from the surface. Use the default settings if not specified.

  • orientation (list or numpy.array, default None) – The vector that the multidentate adsorbate is aligned to.

  • tilt_angle (float, default None) – Tilt the adsorbate with an angle (in degress) relative to the surface normal.

Example

To add CO to all fcc sites of an icosahedral nanoparticle:

>>> from acat.adsorption_sites import ClusterAdsorptionSites
>>> from acat.build.action import add_adsorbate_to_site
>>> from ase.cluster import Icosahedron
>>> from ase.visualize import view
>>> atoms = Icosahedron('Pt', noshells=5)
>>> atoms.center(vacuum=5.)
>>> cas = ClusterAdsorptionSites(atoms)
>>> fcc_sites = cas.get_sites(site='fcc')
>>> for site in fcc_sites:
...     add_adsorbate_to_site(atoms, adsorbate='CO', site=site)
>>> view(atoms)

Output:

_images/add_adsorbate_to_site_1.png

To add a bidentate CH3OH to the (54, 57, 58) site on a Pt fcc111 surface slab and rotate the orientation to a neighbor site:

>>> from acat.adsorption_sites import SlabAdsorptionSites
>>> from acat.adsorption_sites import get_adsorption_site
>>> from acat.build.action import add_adsorbate_to_site
>>> from acat.utilities import get_mic
>>> from ase.build import fcc111
>>> from ase.visualize import view
>>> atoms = fcc111('Pt', (4, 4, 4), vacuum=5.)
>>> i, site = get_adsorption_site(atoms, indices=(54, 57, 58),
...                               surface='fcc111',
...                               return_index=True)
>>> sas = SlabAdsorptionSites(atoms, surface='fcc111')
>>> sites = sas.get_sites()
>>> nbsites = sas.get_neighbor_site_list(neighbor_number=1)
>>> nbsite = sites[nbsites[i][0]] # Choose the first neighbor site
>>> ori = get_mic(site['position'], nbsite['position'], atoms.cell)
>>> add_adsorbate_to_site(atoms, adsorbate='CH3OH', site=site,
...                       orientation=ori)
>>> view(atoms)

Output:

_images/add_adsorbate_to_site_2.png
acat.build.action.add_adsorbate_to_label(atoms, adsorbate, label, surface=None, height=None, orientation=None, tilt_angle=0.0, composition_effect=False, all_sites=None)[source]

Same as add_adsorbate function, except that the site type is represented by a numerical label. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • atoms (ase.Atoms object) – Accept any ase.Atoms object. No need to be built-in.

  • adsorbate (str or ase.Atom object or ase.Atoms object) – The adsorbate species to be added onto the surface.

  • label (int or str) – The label of the site that the adsorbate should be added to.

  • surface (str, default None) – The surface type (crystal structure + Miller indices) If the structure is a periodic surface slab, this is required. If the structure is a nanoparticle, the function enumerates only the sites on the specified surface.

  • height (float, default None) – The height of the added adsorbate from the surface. Use the default settings if not specified.

  • orientation (list or numpy.array, default None) – The vector that the multidentate adsorbate is aligned to.

  • tilt_angle (float, default 0.) – Tilt the adsorbate with an angle (in degress) relative to the surface normal.

  • composition_effect (bool, default False) – Whether the label is defined in bimetallic labels or not.

  • all_sites (list of dicts, default None) – The list of all sites. Provide this to make the function much faster. Useful when the function is called many times.

Example

To add a NH molecule to a site with bimetallic label 14 (an hcp CuCuAu site) on a fcc110 surface slab:

>>> from acat.build.action import add_adsorbate_to_label
>>> from ase.build import fcc110
>>> from ase.visualize import view
>>> atoms = fcc110('Cu', (3, 3, 8), vacuum=5.)
>>> for atom in atoms:
...     if atom.index % 2 == 0:
...         atom.symbol = 'Au'
... atoms.center()
>>> add_adsorbate_to_label(atoms, adsorbate='NH', label=14,
...                        surface='fcc110', composition_effect=True)
>>> view(atoms)

Output:

_images/add_adsorbate_to_label.png
acat.build.action.remove_adsorbate_from_site(atoms, site, remove_fragment=False)[source]

The base function for removing one adsorbate from an occupied site. The site must include information of ‘adsorbate_indices’ or ‘fragment_indices’. Note that if you want to remove adsorbates from multiple sites, call this function multiple times will return the wrong result. Please use remove_adsorbates_from_sites instead.

Parameters
  • atoms (ase.Atoms object) – Accept any ase.Atoms object. No need to be built-in.

  • site (dict) – The site that the adsorbate should be removed from. Must contain information of the adsorbate indices.

  • remove_fragment (bool, default False) – Remove the fragment of a multidentate adsorbate instead of the whole adsorbate.

Example

To remove a CO molecule from a fcc111 surface slab with one CO and one OH:

>>> from acat.adsorption_sites import SlabAdsorptionSites
>>> from acat.adsorbate_coverage import SlabAdsorbateCoverage
>>> from acat.build.action import add_adsorbate_to_site
>>> from acat.build.action import remove_adsorbate_from_site
>>> from ase.build import fcc111
>>> from ase.visualize import view
>>> atoms = fcc111('Pt', (6, 6, 4), 4, vacuum=5.)
>>> atoms.center()
>>> sas = SlabAdsorptionSites(atoms, surface='fcc111')
>>> sites = sas.get_sites()
>>> add_adsorbate_to_site(atoms, adsorbate='OH', site=sites[0])
>>> add_adsorbate_to_site(atoms, adsorbate='CO', site=sites[-1])
>>> sac = SlabAdsorbateCoverage(atoms, sas)
>>> occupied_sites = sac.get_sites(occupied_only=True)
>>> CO_site = next((s for s in occupied_sites if s['adsorbate'] == 'CO'))
>>> remove_adsorbate_from_site(atoms, site=CO_site)
>>> view(atoms)
_images/remove_adsorbate_from_site.png
acat.build.action.remove_adsorbates_from_sites(atoms, sites, remove_fragments=False)[source]

The base function for removing multiple adsorbates from an occupied site. The sites must include information of ‘adsorbate_indices’ or ‘fragment_indices’.

Parameters
  • atoms (ase.Atoms object) – Accept any ase.Atoms object. No need to be built-in.

  • sites (list of dicts) – The site that the adsorbate should be removed from. Must contain information of the adsorbate indices.

  • remove_fragments (bool, default False) – Remove the fragment of a multidentate adsorbate instead of the whole adsorbate.

Example

To remove all CO species from a fcc111 surface slab covered with both CO and OH:

>>> from acat.adsorption_sites import SlabAdsorptionSites
>>> from acat.adsorbate_coverage import SlabAdsorbateCoverage
>>> from acat.build.pattern import random_coverage_pattern
>>> from acat.build.action import remove_adsorbates_from_sites
>>> from ase.build import fcc111
>>> from ase.visualize import view
>>> slab = fcc111('Pt', (6, 6, 4), 4, vacuum=5.)
>>> slab.center()
>>> atoms = random_coverage_pattern(slab, adsorbate_species=['OH','CO'],
...                                 surface='fcc111',
...                                 min_adsorbate_distance=5.)
>>> sas = SlabAdsorptionSites(atoms, surface='fcc111')
>>> sac = SlabAdsorbateCoverage(atoms, sas)
>>> occupied_sites = sac.get_sites(occupied_only=True)
>>> CO_sites = [s for s in occupied_sites if s['adsorbate'] == 'CO']
>>> remove_adsorbates_from_sites(atoms, sites=CO_sites)
>>> view(atoms)

Output:

_images/remove_adsorbates_from_sites.png
acat.build.action.remove_adsorbates_too_close(atoms, adsorbate_coverage=None, surface=None, min_adsorbate_distance=0.5)[source]

Find adsorbates that are too close, remove one set of them. The function is intended to remove atoms that are unphysically close. Please do not use a min_adsorbate_distace larger than 2. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • atoms (ase.Atoms object) – The nanoparticle or surface slab onto which the adsorbates are added. Accept any ase.Atoms object. No need to be built-in.

  • adsorbate_coverage (acat.adsorbate_coverage.ClusterAdsorbateCoverage object or acat.adsorbate_coverage.SlabAdsorbateCoverage object, default None) – The built-in adsorbate coverage class.

  • surface (str, default None) – The surface type (crystal structure + Miller indices). If the structure is a periodic surface slab, this is required. If the structure is a nanoparticle, the function only add adsorbates to the sites on the specified surface.

  • min_adsorbate_distance (float, default 0.) – The minimum distance between two atoms that is not considered to be to close. This distance has to be small.

Example

To remove unphysically close adsorbates on the edges of a Marks decahedron with 0.75 ML symmetric CO coverage:

>>> from acat.build.pattern import symmetric_coverage_pattern
>>> from acat.build.action import remove_adsorbates_too_close
>>> from ase.cluster import Decahedron
>>> from ase.visualize import view
>>> atoms = Decahedron('Pt', p=4, q=3, r=1)
>>> atoms.center(vacuum=5.)
>>> pattern = symmetric_coverage_pattern(atoms, adsorbate='CO',
...                                      coverage=0.75)
>>> remove_adsorbates_too_close(pattern, min_adsorbate_distance=1.)
>>> view(pattern)

Output:

_images/remove_adsorbates_too_close.png

Generate adsorbate coverage patterns

class acat.build.pattern.StochasticPatternGenerator(images, adsorbate_species, image_probabilities=None, species_probabilities=None, min_adsorbate_distance=1.5, adsorption_sites=None, surface=None, heights={'3fold': 1.3, '4fold': 1.3, '5fold': 1.5, '6fold': 0.0, 'bridge': 1.5, 'fcc': 1.3, 'hcp': 1.3, 'longbridge': 1.5, 'ontop': 1.8, 'shortbridge': 1.5}, allow_6fold=False, composition_effect=True, dmax=2.5, species_forbidden_sites=None, species_forbidden_labels=None, fragmentation=True, trajectory='patterns.traj', append_trajectory=False, logfile='patterns.log')[source]

StochasticPatternGenerator is a class for generating adsorbate coverage patterns stochastically. Graph isomorphism is implemented to identify identical coverage patterns. 4 adsorbate actions are supported: add, remove, move, replace. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • images (ase.Atoms object or list of ase.Atoms objects) – The structure to perform the adsorbate actions on. If a list of structures is provided, perform one adsorbate action on one of the structures in each step. Accept any ase.Atoms object. No need to be built-in.

  • adsorbate_species (str or list of strs) – A list of adsorbate species to be randomly added to the surface.

  • image_probabilities (listt, default None) – A list of the probabilities of selecting each structure. Selecting structure with equal probability if not specified.

  • species_probabilities (dict, default None) – A dictionary that contains keys of each adsorbate species and values of their probabilities of adding onto the surface. Adding adsorbate species with equal probability if not specified.

  • min_adsorbate_distance (float, default 1.5) – The minimum distance constraint between two atoms that belongs to two adsorbates.

  • adsorption_sites (acat.adsorption_sites.ClusterAdsorptionSites object or acat.adsorption_sites.SlabAdsorptionSites object, default None) – Provide the built-in adsorption sites class to accelerate the pattern generation. Make sure all the structures have the same periodicity and atom indexing. If composition_effect=True, you should only provide adsorption_sites when the surface composition is fixed.

  • surface (str, default None) – The surface type (crystal structure + Miller indices). Only required if the structure is a periodic surface slab.

  • heights (dict, default acat.settings.site_heights) – A dictionary that contains the adsorbate height for each site type. Use the default height settings if the height for a site type is not specified.

  • allow_6fold (bool, default False) – Whether to allow the adsorption on 6-fold subsurf sites underneath fcc hollow sites.

  • composition_effect (bool, default False) – Whether to consider sites with different elemental compositions as different sites. It is recommended to set composition=False for monometallics.

  • dmax (float, default 2.5) – The maximum bond length (in Angstrom) between the site and the bonding atom that should be considered as an adsorbate.

  • species_forbidden_sites (dict, default None) – A dictionary that contains keys of each adsorbate species and values of the site (can be one or multiple site types) that the speices is not allowed to add to. All sites are availabe for a species if not specified. Note that this does not differentiate sites with different compositions.

  • species_forbidden_labels (dict, default None) – Same as species_forbidden_sites except that the adsorption sites are written as numerical labels according to acat.labels. Useful when you need to differentiate sites with different compositions.

  • fragmentation (bool, default True) – Whether to cut multidentate species into fragments. This ensures that multidentate species with different orientations are considered as different coverage patterns.

  • trajectory (str, default 'patterns.traj') – The name of the output ase trajectory file.

  • append_trajectory (bool, default False) –

    Whether to append structures to the existing trajectory. If only unique patterns are accepted, the code will also check graph isomorphism for the existing structures in the trajectory. This is also useful when you want to generate coverage patterns stochastically but for all images systematically, e.g. generating 10 stochastic coverage patterns for each image:

    >>> from acat.build.patterns import StochasticPatternGenerator as SPG
    >>> for atoms in images:
    ...    spg = SPG(atoms, ..., append_trajectory=True)
    ...    spg.run(ngen = 10)
    

  • logfile (str, default 'patterns.log') – The name of the log file.

run(num_gen, action='add', action_probabilities=None, unique=True)[source]

Run the pattern generator.

Parameters
  • num_gen (int) – Number of patterns to generate.

  • action (str or list of strs, default 'add') – Action(s) to perform. If a list of actions is provided, select actions from the list randomly or with probabilities.

  • action_probabilities (dict, default None) – A dictionary that contains keys of each action and values of the corresponding probabilities. Select actions with equal probability if not specified.

  • unique (bool, default True) – Whether to discard duplicate patterns based on isomorphism.

Example

The following example illustrates how to generate 100 stochastic adsorbate coverage patterns with CO, OH, CH3 and CHO, based on 10 Pt fcc111 surface slabs with random C and O coverages, where CH3 is forbidden to be added to ontop and bridge sites:

>>> from acat.build.pattern import StochasticPatternGenerator as SPG
>>> from acat.build.pattern import random_coverage_pattern
>>> from ase.build import fcc111
>>> from ase.io import read
>>> from ase.visualize import view
>>> slab = fcc111('Pt', (6, 6, 4), 4, vacuum=5.)
>>> slab.center()
>>> images = []
>>> for _ in range(10):
...     atoms = slab.copy()
...     image = random_coverage_pattern(atoms, adsorbate_species=['C','O'],
...                                     surface='fcc111',
...                                     min_adsorbate_distance=5.)
...     images.append(image)
>>> spg = SPG(images, adsorbate_species=['CO','OH','CH3','CHO'],
...           species_probabilities={'CO':0.3, 'OH': 0.3,
...                                  'CH3': 0.2, 'CHO': 0.2},
...           min_adsorbate_distance=1.5,
...           surface='fcc111',
...           composition_effect=False,
...           species_forbidden_sites={'CH3': ['ontop','bridge']})
>>> spg.run(num_gen=100, action='add')
>>> images = read('patterns.traj', index=':')
>>> view(images)

Output:

_images/StochasticPatternGenerator.gif
class acat.build.pattern.SystematicPatternGenerator(images, adsorbate_species, min_adsorbate_distance=1.5, adsorption_sites=None, surface=None, heights={'3fold': 1.3, '4fold': 1.3, '5fold': 1.5, '6fold': 0.0, 'bridge': 1.5, 'fcc': 1.3, 'hcp': 1.3, 'longbridge': 1.5, 'ontop': 1.8, 'shortbridge': 1.5}, allow_6fold=False, composition_effect=True, dmax=2.5, species_forbidden_sites=None, species_forbidden_labels=None, enumerate_orientations=True, trajectory='patterns.traj', append_trajectory=False, logfile='patterns.log')[source]

SystematicPatternGenerator is a class for generating adsorbate coverage patterns systematically. This is useful to enumerate all unique patterns at low coverage, but explodes at higher coverages. Graph isomorphism is implemented to identify identical coverage patterns. 4 adsorbate actions are supported: add, remove, move, replace. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • images (ase.Atoms object or list of ase.Atoms objects) – The structure to perform the adsorbate actions on. If a list of structures is provided, perform one adsorbate action on one of the structures in each step. Accept any ase.Atoms object. No need to be built-in.

  • adsorbate_species (str or list of strs) – A list of adsorbate species to be randomly added to the surface.

  • min_adsorbate_distance (float, default 1.5) – The minimum distance constraint between two atoms that belongs to two adsorbates.

  • adsorption_sites (acat.adsorption_sites.ClusterAdsorptionSites object or acat.adsorption_sites.SlabAdsorptionSites object, default None) – Provide the built-in adsorption sites class to accelerate the pattern generation. Make sure all the structures have the same periodicity and atom indexing. If composition_effect=True, you should only provide adsorption_sites when the surface composition is fixed.

  • surface (str, default None) – The surface type (crystal structure + Miller indices). Only required if the structure is a periodic surface slab.

  • heights (dict, default acat.settings.site_heights) – A dictionary that contains the adsorbate height for each site type. Use the default height settings if the height for a site type is not specified.

  • allow_6fold (bool, default False) – Whether to allow the adsorption on 6-fold subsurf sites underneath fcc hollow sites.

  • composition_effect (bool, default False) – Whether to consider sites with different elemental compositions as different sites. It is recommended to set composition=False for monometallics.

  • dmax (float, default 2.5) – The maximum bond length (in Angstrom) between the site and the bonding atom that should be considered as an adsorbate.

  • species_forbidden_sites (dict, default None) – A dictionary that contains keys of each adsorbate species and values of the site (can be one or multiple site types) that the speices is not allowed to add to. All sites are availabe for a species if not specified. Note that this does not differentiate sites with different compositions.

  • species_forbidden_labels (dict, default None) – Same as species_forbidden_sites except that the adsorption sites are written as numerical labels according to acat.labels. Useful when you need to differentiate sites with different compositions.

  • enumerate_orientations (bool, default True) – Whether to enumerate all orientations of multidentate species. This ensures that multidentate species with different orientations are all enumerated.

  • trajectory (str, default 'patterns.traj') – The name of the output ase trajectory file.

  • append_trajectory (bool, default False) – Whether to append structures to the existing trajectory. If only unique patterns are accepted, the code will also check graph isomorphism for the existing structures in the trajectory.

  • logfile (str, default 'patterns.log') – The name of the log file.

run(max_gen_per_image=None, action='add', unique=True)[source]

Run the pattern generator.

Parameters
  • max_gen_per_image (int, default None) – Maximum number of patterns to generate for each image. Enumerate all possible patterns if not specified.

  • action (str, defualt 'add') – Action to perform.

  • unique (bool, default True) – Whether to discard duplicate patterns based on isomorphism.

Example

The following example illustrates how to add CO to all unique sites on a cuboctahedral bimetallic nanoparticle:

>>> from acat.adsorption_sites import ClusterAdsorptionSites
>>> from acat.build.pattern import SystematicPatternGenerator as SPG
>>> from ase.cluster import Octahedron
>>> from ase.io import read
>>> from ase.visualize import view
>>> atoms = Octahedron('Cu', length=7, cutoff=3)
>>> for atom in atoms:
...     if atom.index % 2 == 0:
...         atom.symbol = 'Au'
>>> atoms.center(vacuum=5.)
>>> cas = ClusterAdsorptionSites(atoms, composition_effect=True)
>>> spg = SPG(atoms, adsorbate_species='CO',
...           min_adsorbate_distance=2.,
...           adsorption_sites=cas,
...           composition_effect=True)
>>> spg.run(action='add')
>>> images = read('patterns.traj', index=':')
>>> view(images)

Output:

_images/SystematicPatternGenerator.gif
acat.build.pattern.symmetric_coverage_pattern(atoms, adsorbate, coverage=1.0, surface=None, height=None, min_adsorbate_distance=0.0)[source]

A function for generating representative symmetric adsorbate coverage patterns. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • atoms (ase.Atoms object) – The nanoparticle or surface slab onto which the adsorbates are added. Accept any ase.Atoms object. No need to be built-in.

  • adsorbate (str or ase.Atom object or ase.Atoms object) – The adsorbate species to be added onto the surface. For now only support adding one type of adsorbate species.

  • coverage (float, default 1.) – The coverage (ML) of the adsorbate (N_adsorbate / N_surf_atoms). Support 4 coverage patterns (0.25 for p(2x2) pattern; 0.5 for c(2x2) pattern on fcc100 or honeycomb pattern on fcc111; 0.75 for (2x2) pattern on fcc100 or Kagome pattern on fcc111; 1 for p(1x1) pattern. Note that for small nanoparticles, the function might give results that do not correspond to the coverage. This is normal since the surface area can be too small to encompass the coverage pattern properly. We expect this function to work well on large nanoparticles and surface slabs.

  • surface (str, default None) – The surface type (crystal structure + Miller indices). For now only support 2 common surfaces: fcc100 and fcc111. If the structure is a periodic surface slab, this is required. If the structure is a nanoparticle, the function only add adsorbates to the sites on the specified surface.

  • height (float, default None) – The height of the added adsorbate from the surface. Use the default settings if not specified.

  • min_adsorbate_distance (float, default 0.) – The minimum distance between two atoms that belongs to two adsorbates.

Example

To add a 0.5 ML CO coverage pattern on a cuboctahedron:

>>> from acat.build.pattern import symmetric_coverage_pattern
>>> from ase.cluster import Octahedron
>>> from ase.visualize import view
>>> atoms = Octahedron('Au', length=9, cutoff=4)
>>> atoms.center(vacuum=5.)
>>> pattern = symmetric_coverage_pattern(atoms, adsorbate='CO',
...                                      coverage=0.5)
>>> view(pattern)

Output:

_images/symmetric_coverage_pattern_1.png

To add a 0.75 ML CO coverage pattern on a fcc111 surface slab:

>>> from acat.build.pattern import symmetric_coverage_pattern
>>> from ase.build import fcc111
>>> from ase.visualize import view
>>> atoms = fcc111('Cu', (8, 8, 4), vacuum=5.)
>>> atoms.center()
>>> pattern = symmetric_coverage_pattern(atoms, adsorbate='CO',
...                                      coverage=0.5,
...                                      surface='fcc111')
>>> view(pattern)

Output:

_images/symmetric_coverage_pattern_2.png
acat.build.pattern.full_coverage_pattern(atoms, adsorbate, site, surface=None, height=None, min_adsorbate_distance=0.0)[source]

A function for generating adsorbate coverage patterns on sites that are of a same type. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • atoms (ase.Atoms object) – The nanoparticle or surface slab onto which the adsorbates are added. Accept any ase.Atoms object. No need to be built-in.

  • adsorbate (str or ase.Atom object or ase.Atoms object) – The adsorbate species to be added onto the surface. For now only support adding one type of adsorbate species.

  • site (str) – The site type that the adsorbates should be added to.

  • surface (str, default None) – The surface type (crystal structure + Miller indices). If the structure is a periodic surface slab, this is required. If the structure is a nanoparticle, the function only add adsorbates to the sites on the specified surface.

  • height (float, default None) – The height of the added adsorbate from the surface. Use the default settings if not specified.

  • min_adsorbate_distance (float, default 0.) – The minimum distance between two atoms that belongs to two adsorbates.

Example

To add CO to all hcp sites on a icosahedron:

>>> from acat.build.pattern import full_coverage_pattern
>>> from ase.cluster import Icosahedron
>>> from ase.visualize import view
>>> atoms = Icosahedron('Au', noshells=5)
>>> atoms.center(vacuum=5.)
>>> pattern = full_coverage_pattern(atoms, adsorbate='CO', site='hcp')
>>> view(pattern)

Output:

_images/full_coverage_pattern_1.png

To add CO to all 3fold sites on a bcc110 surface slab:

>>> from acat.build.pattern import full_coverage_pattern
>>> from ase.build import bcc110
>>> from ase.visualize import view
>>> atoms = bcc110('Mo', (8, 8, 4), vacuum=5.)
>>> atoms.center()
>>> pattern = full_coverage_pattern(atoms, adsorbate='CO',
...                                 surface='bcc110', site='3fold')
>>> view(pattern)

Output:

_images/full_coverage_pattern_2.png
acat.build.pattern.random_coverage_pattern(atoms, adsorbate_species, species_probabilities=None, surface=None, min_adsorbate_distance=1.5, heights={'3fold': 1.3, '4fold': 1.3, '5fold': 1.5, '6fold': 0.0, 'bridge': 1.5, 'fcc': 1.3, 'hcp': 1.3, 'longbridge': 1.5, 'ontop': 1.8, 'shortbridge': 1.5}, allow_6fold=False)[source]

A function for generating random coverage patterns with a minimum distance constraint. The function is generalized for both periodic and non-periodic systems (distinguished by atoms.pbc).

Parameters
  • atoms (ase.Atoms object) – The nanoparticle or surface slab onto which the adsorbates are added. Accept any ase.Atoms object. No need to be built-in.

  • adsorbate_species (str or list of strs) – A list of adsorbate species to be randomly added to the surface.

  • species_probabilities (dict, default None) – A dictionary that contains keys of each adsorbate species and values of their probabilities of adding onto the surface.

  • surface (str, default None) – The surface type (crystal structure + Miller indices). If the structure is a periodic surface slab, this is required. If the structure is a nanoparticle, the function only add adsorbates to the sites on the specified surface.

  • heights (dict, default acat.settings.site_heights) – A dictionary that contains the adsorbate height for each site type. Use the default height settings if the height for a site type is not specified.

  • min_adsorbate_distance (float, default 1.5) – The minimum distance constraint between two atoms that belongs to two adsorbates.

  • allow_6fold (bool, default False) – Whether to allow the adsorption on 6-fold subsurf sites underneath fcc hollow sites.

Example

To add CO randomly onto a cuboctahedron with a minimum adsorbate distance of 5 Angstrom:

>>> from acat.build.pattern import random_coverage_pattern
>>> from ase.cluster import Octahedron
>>> from ase.visualize import view
>>> atoms = Octahedron('Au', length=9, cutoff=4)
>>> atoms.center(vacuum=5.)
>>> pattern = random_coverage_pattern(atoms, adsorbate_species='CO',
...                                   min_adsorbate_distance=5.)
>>> view(pattern)

Output:

_images/random_coverage_pattern_1.png

To add C, N, O randomly onto a hcp0001 surface slab with probabilities of 0.25, 0.25, 0.5, respectively, and a minimum adsorbate distance of 2 Angstrom:

>>> from acat.build.pattern import random_coverage_pattern
>>> from ase.build import hcp0001
>>> from ase.visualize import view
>>> atoms = hcp0001('Ru', (8, 8, 4), vacuum=5.)
>>> atoms.center()
>>> pattern = random_coverage_pattern(atoms, adsorbate_species=['C','N','O'],
...                                   species_probabilities={'C': 0.25,
...                                                          'N': 0.25,
...                                                          'O': 0.5},
...                                   surface='hcp0001',
...                                   min_adsorbate_distance=2.)
>>> view(pattern)

Output:

_images/random_coverage_pattern_2.png

Generate chemical orderings

class acat.build.ordering.SymmetricOrderingGenerator(atoms, elements, symmetry='spherical', cutoff=1.0, secondary_symmetry=None, secondary_cutoff=1.0, composition=None, shell_threshold=20, trajectory='orderings.traj', append_trajectory=False)[source]

SymmetricOrderingGenerator is a class for generating symmetric chemical orderings for a nanoalloy. As for now only support clusters without fixing the composition, but there is no limitation of the number of metal components. Please align the z direction to the symmetry axis of the cluster.

Parameters
  • atoms (ase.Atoms object) – The nanoparticle to use as a template to generate symmetric chemical orderings. Accept any ase.Atoms object. No need to be built-in.

  • elements (list of strs) – The metal elements of the nanoalloy.

  • symmetry (str, default 'spherical') –

    Support 9 symmetries:

    ’spherical’: centrosymmetry (shells defined by the distances to the geometric center);

    ’cylindrical’: cylindrical symmetry around z axis (shells defined by the distances to the z axis);

    ’planar’: planar symmetry around z axis (shells defined by the z coordinates), common for phase-separated nanoalloys;

    ’mirror_planar’: mirror planar symmetry around both z and xy plane (shells defined by the absolute z coordinate), high symmetry subset of ‘planar’; ‘circular’ = ring symmetry around z axis (shells defined by both z coordinate and distance to z axis);

    ’mirror_circular’: mirror ring symmetry around both z and xy plane (shells defined by both absolute z coordinate and distance to z axis);

    ’chemical’: symmetry w.r.t chemical environment (shells defined by the atomic energies given by a Gupta potential)

    ’geometrical’: symmetry w.r.t geometrical environment (shells defined by vertex / edge / fcc111 / fcc100 / bulk identified by CNA analysis);

    ’conventional’: conventional definition of shells (surface / subsurface, subsubsurface, …, core).

  • cutoff (float, default 1.0) – Maximum thickness (in Angstrom) of a single shell. The thickness is calculated as the difference between the “distances” of the closest-to-center atoms in two neighbor shells. Note that the criterion of “distance” depends on the symmetry. This parameter works differently if the symmetry is ‘chemical’, ‘geometrical’ or ‘conventional’. For ‘chemical’ it is defined as the maximum atomic energy difference (in eV) of a single shell predicted by a Gupta potential. For ‘geometrical’ and ‘conventional’ it is defined as the cutoff radius (in Angstrom) for CNA, and a reasonable cutoff based on the lattice constant of the material will automatically be used if cutoff <= 1. Use a larger cutoff if the structure is distorted.

  • secondary_symmetry (str, default None) – Add a secondary symmetry check to define shells hierarchically. For example, even if two atoms are classifed in the same shell defined by the primary symmetry, they can still end up in different shells if they fall into two different shells defined by the secondary symmetry. Support 7 symmetries: ‘spherical’, ‘cylindrical’, ‘planar’, ‘mirror_planar’, ‘chemical’, ‘geometrical’ and ‘conventional’. Note that secondary symmetry has the same importance as the primary symmetry, so you can set either of the two symmetries of interest as the secondary symmetry. Useful for combining symmetries of different types (e.g. circular + chemical) or combining symmetries with different cutoffs.

  • secondary_cutoff (float, default 1.0) – Same as cutoff, except that it is for the secondary symmetry.

  • composition (dict, None) – Generate symmetric orderings only at a certain composition. The dictionary contains the metal elements as keys and their concentrations as values. Generate orderings at all compositions if not specified. Note that the computational cost scales badly with the number of shells for a fixed-composition search.

  • shell_threshold (int, default 20) – Number of shells to switch to stochastic mode automatically.

  • trajectory (str, default 'orderings.traj') – The name of the output ase trajectory file.

  • append_trajectory (bool, default False) – Whether to append structures to the existing trajectory.

get_sorted_indices(symmetry)[source]

Returns the indices sorted by the metric that defines different shells, together with the corresponding vlues, given a specific symmetry. Returns the indices sorted by geometrical environment if symmetry=’geometrical’. Returns the indices sorted by surface, subsurface, subsubsurface, …, core if symmetry=’conventional’.

Parameters

symmetry (str) – Support 7 symmetries: spherical, cylindrical, planar, mirror_planar, chemical, geometrical, conventional.

get_shells()[source]

Get the shells (a list of lists of atom indices) that divided by the symmetry.

run(max_gen=None, mode='systematic', verbose=False)[source]

Run the chemical ordering generator.

Parameters
  • max_gen (int, default None) – Maximum number of chemical orderings to generate. Enumerate all symetric patterns if not specified.

  • mode (str, default 'systematic') – Mode ‘systematic’ = enumerate all possible chemical orderings. Mode ‘stochastic’ = sample chemical orderings stochastically. Stocahstic mode is recommended when there are many shells.

  • verbose (bool, default False) – Whether to print out information about number of shells and number of generated structures.

Example

To generate 100 symmetric chemical orderings of a truncated octahedral NiPt nanoalloy with spherical symmetry:

>>> from acat.build.ordering import SymmetricOrderingGenerator as SOG
>>> from ase.cluster import Octahedron
>>> from ase.io import read
>>> from ase.visualize import view
>>> atoms = Octahedron('Ni', length=8, cutoff=3)
>>> sog = SOG(atoms, elements=['Ni', 'Pt'], symmetry='spherical')
>>> sog.run(max_gen=100, verbose=True)
>>> images = read('orderings.traj', index=':')
>>> view(images)

Output:

10 layers classified
100 symmetric chemical orderings generated
_images/SymmetricOrderingGenerator1.gif

To generate 50 symmetric chemical orderings of a quaternary truncated octahedral Ni0.4Cu0.3Pt0.2Au0.1 nanoalloy with mirror circular symmetry:

>>> from acat.build.ordering import SymmetricOrderingGenerator as SOG
>>> from ase.cluster import Octahedron
>>> from ase.io import read
>>> from ase.visualize import view
>>> atoms = Octahedron('Ni', 7, 2)
>>> sog = SOG(atoms, elements=['Ni', 'Cu', 'Pt', 'Au'],
...           symmetry='mirror_circular',
...           composition={'Ni': 0.4, 'Cu': 0.3, 'Pt': 0.2, 'Au': 0.1})
>>> sog.run(max_gen=50, verbose=True)
>>> images = read('orderings.traj', index=':')
>>> view(images)

Output:

25 layers classified
50 symmetric chemical orderings generated
_images/SymmetricOrderingGenerator2.gif

Sometimes it is also useful to get the structure of each shell. For instance, to visualize the conventional shells of a truncated octahedral nanoparticle:

>>> from acat.build.ordering import SymmetricOrderingGenerator as SOG
>>> from ase.cluster import Octahedron
>>> from ase.visualize import view
>>> atoms = Octahedron('Ni', 12, 5)
>>> sog = SOG(atoms, elements=['Ni', 'Pt'], symmetry='conventional')
>>> shells = sog.get_shells()
>>> images = [atoms[s] for s in shells]
>>> view(images)

Output:

_images/SymmetricOrderingGenerator3.gif
class acat.build.ordering.RandomOrderingGenerator(atoms, elements, composition=None, trajectory='orderings.traj', append_trajectory=False)[source]

RandomOrderingGenerator is a class for generating random chemical orderings for an alloy catalyst. The function is generalized for both periodic and non-periodic systems, and there is no limitation of the number of metal components.

Parameters
  • atoms (ase.Atoms object) – The nanoparticle or surface slab to use as a template to generate random chemical orderings. Accept any ase.Atoms object. No need to be built-in.

  • elements (list of strs) – The metal elements of the alloy catalyst.

  • composition (dict, None) – Generate random orderings only at a certain composition. The dictionary contains the metal elements as keys and their concentrations as values. Generate orderings at all compositions if not specified.

  • trajectory (str, default 'patterns.traj') – The name of the output ase trajectory file.

  • append_trajectory (bool, default False) – Whether to append structures to the existing trajectory.

randint_with_sum()[source]

Return a randomly chosen list of N positive integers i summing to the number of atoms. N is the number of elements. Each such list is equally likely to occur.

random_split_indices()[source]

Generate random chunks of indices given sizes of each chunk.

run(num_gen)[source]

Run the chemical ordering generator.

Parameters

num_gen (int) – Number of chemical orderings to generate.

Example

To generate 50 random chemical orderings of a icosahedral Ni0.5Pt0.2Au0.3 nanoalloy:

>>> from acat.build.ordering import RandomOrderingGenerator as ROG
>>> from ase.cluster import Icosahedron
>>> from ase.io import read
>>> from ase.visualize import view
>>> atoms = Icosahedron('Ni', noshells=4)
>>> rog = ROG(atoms, elements=['Ni', 'Pt', 'Au'],
...           composition={'Ni': 0.5, 'Pt': 0.2, 'Au': 0.3})
>>> rog.run(num_gen=50)
>>> images = read('orderings.traj', index=':')
>>> view(images)

Output:

_images/RandomOrderingGenerator.gif