Statistiques
| Branche: | Tag: | Révision :

dockonsurf / modules / screening.py @ 1d22a086

Historique | Voir | Annoter | Télécharger (7,65 ko)

1 e07c09eb Carles
import logging
2 f3d1e601 Carles Marti
import numpy as np
3 f3d1e601 Carles Marti
import ase
4 e07c09eb Carles
5 e07c09eb Carles
logger = logging.getLogger('DockOnSurf')
6 e07c09eb Carles
7 e07c09eb Carles
8 12a12bdd Carles Marti
def vect_avg(vects):
9 a5cc42ff Carles Marti
    """Computes the element-wise mean of a set of vectors.
10 12a12bdd Carles Marti

11 12a12bdd Carles Marti
    @param vects: np.ndarray with shape (num_vectors, length_vector).
12 a5cc42ff Carles Marti
    @return: vector average computed doing the element-wise mean.
13 12a12bdd Carles Marti
    """
14 12a12bdd Carles Marti
    from utilities import try_command
15 12a12bdd Carles Marti
    err = "vect_avg parameter vects must be a list-like, able to be converted" \
16 12a12bdd Carles Marti
          " np.array"
17 12a12bdd Carles Marti
    vects = try_command(np.array, [(ValueError, err)], vects)
18 12a12bdd Carles Marti
    if len(vects.shape) == 1:
19 12a12bdd Carles Marti
        return vects
20 12a12bdd Carles Marti
    else:
21 12a12bdd Carles Marti
        num_vects = vects.shape[1]
22 12a12bdd Carles Marti
        return np.array([np.average(vects[:, i]) for i in range(num_vects)])
23 12a12bdd Carles Marti
24 12a12bdd Carles Marti
25 f3d1e601 Carles Marti
def get_atom_coords(atoms: ase.Atoms, ctrs_list):
26 a5cc42ff Carles Marti
    """Gets the coordinates of the specified atoms from a ase.Atoms object.
27 a5cc42ff Carles Marti

28 a5cc42ff Carles Marti
    Given an ase.Atoms object and a list of atom indices specified in ctrs_list
29 a5cc42ff Carles Marti
    it gets the coordinates of the specified atoms. If the element in the
30 a5cc42ff Carles Marti
    ctrs_list is not an index but yet a list of indices, it computes the
31 a5cc42ff Carles Marti
    element-wise mean of the coordinates of the atoms specified in the inner
32 a5cc42ff Carles Marti
    list.
33 a5cc42ff Carles Marti
    @param atoms: ase.Atoms object for which to obtain the coordinates of.
34 a5cc42ff Carles Marti
    @param ctrs_list: list of (indices/list of indices) of the atoms for which
35 a5cc42ff Carles Marti
                      the coordinates should be extracted.
36 a5cc42ff Carles Marti
    @return: np.ndarray of atomic coordinates.
37 a5cc42ff Carles Marti
    """
38 12a12bdd Carles Marti
    coords = []
39 12a12bdd Carles Marti
    for i, elem in enumerate(ctrs_list):
40 12a12bdd Carles Marti
        if isinstance(elem, list):
41 12a12bdd Carles Marti
            coords.append(vect_avg(np.array([atoms[c].position for c in elem])))
42 12a12bdd Carles Marti
        elif isinstance(elem, int):
43 12a12bdd Carles Marti
            coords.append(atoms[elem].position)
44 12a12bdd Carles Marti
        else:
45 12a12bdd Carles Marti
            err = f"'ctrs_list must be a list of ints or lists, {type(elem)} " \
46 12a12bdd Carles Marti
                  "found."
47 12a12bdd Carles Marti
            logger.error(err)
48 12a12bdd Carles Marti
            raise ValueError
49 12a12bdd Carles Marti
    return np.array(coords)
50 f3d1e601 Carles Marti
51 f3d1e601 Carles Marti
52 1d22a086 Carles Marti
def add_adsorbate(slab, adsorbate, surf_pos, mol_pos, height, offset=None,
53 1d22a086 Carles Marti
                  norm_vect=(0, 0, 1)):
54 1d22a086 Carles Marti
    """Add an adsorbate to a surface.
55 1d22a086 Carles Marti

56 1d22a086 Carles Marti
    This function extends the functionality of ase.build.add_adsorbate
57 1d22a086 Carles Marti
    (https://wiki.fysik.dtu.dk/ase/ase/build/surface.html#ase.build.add_adsorbate)
58 1d22a086 Carles Marti
    by enabling to change the z coordinate and the axis perpendicular to the
59 1d22a086 Carles Marti
    surface.
60 1d22a086 Carles Marti
    @param slab: ase.Atoms object containing the coordinates of the surface
61 1d22a086 Carles Marti
    @param adsorbate: ase.Atoms object containing the coordinates of the
62 1d22a086 Carles Marti
        adsorbate.
63 1d22a086 Carles Marti
    @param surf_pos: The coordinates of the adsorption site on the surface.
64 1d22a086 Carles Marti
    @param mol_pos: The coordinates of the adsorption center in the molecule.
65 1d22a086 Carles Marti
    @param height: The height above the surface
66 1d22a086 Carles Marti
    @param offset: Offsets the adsorbate by a number of unit cells. Mostly
67 1d22a086 Carles Marti
        useful when adding more than one adsorbate.
68 1d22a086 Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
69 1d22a086 Carles Marti
    """
70 1d22a086 Carles Marti
    info = slab.info.get('adsorbate_info', {})
71 1d22a086 Carles Marti
    pos = np.array([0.0, 0.0, 0.0])  # (x, y, z) part
72 1d22a086 Carles Marti
    spos = np.array([0.0, 0.0, 0.0])  # part relative to unit cell
73 1d22a086 Carles Marti
    norm_vect = np.array(norm_vect) / np.linalg.norm(norm_vect)
74 1d22a086 Carles Marti
    if offset is not None:
75 1d22a086 Carles Marti
        spos += np.asarray(offset, float)
76 1d22a086 Carles Marti
    if isinstance(surf_pos, str):
77 1d22a086 Carles Marti
        # A site-name:
78 1d22a086 Carles Marti
        if 'sites' not in info:
79 1d22a086 Carles Marti
            raise TypeError('If the atoms are not made by an ase.build '
80 1d22a086 Carles Marti
                            'function, position cannot be a name.')
81 1d22a086 Carles Marti
        if surf_pos not in info['sites']:
82 1d22a086 Carles Marti
            raise TypeError('Adsorption site %s not supported.' % surf_pos)
83 1d22a086 Carles Marti
        spos += info['sites'][surf_pos]
84 1d22a086 Carles Marti
    else:
85 1d22a086 Carles Marti
        pos += surf_pos
86 1d22a086 Carles Marti
    if 'cell' in info:
87 1d22a086 Carles Marti
        cell = info['cell']
88 1d22a086 Carles Marti
    else:
89 1d22a086 Carles Marti
        cell = slab.get_cell()
90 1d22a086 Carles Marti
    pos += np.dot(spos, cell)
91 1d22a086 Carles Marti
    # Convert the adsorbate to an Atoms object
92 1d22a086 Carles Marti
    if isinstance(adsorbate, ase.Atoms):
93 1d22a086 Carles Marti
        ads = adsorbate
94 1d22a086 Carles Marti
    elif isinstance(adsorbate, ase.Atom):
95 1d22a086 Carles Marti
        ads = ase.Atoms([adsorbate])
96 1d22a086 Carles Marti
    else:
97 1d22a086 Carles Marti
        # Assume it is a string representing a single Atom
98 1d22a086 Carles Marti
        ads = ase.Atoms([ase.Atom(adsorbate)])
99 1d22a086 Carles Marti
    pos += height * norm_vect
100 1d22a086 Carles Marti
    # Move adsorbate into position
101 1d22a086 Carles Marti
    ads.translate(pos - mol_pos)
102 1d22a086 Carles Marti
    # Attach the adsorbate
103 1d22a086 Carles Marti
    slab.extend(ads)
104 1d22a086 Carles Marti
105 1d22a086 Carles Marti
106 1d22a086 Carles Marti
def ads_euler(molec, surf, site, ctr, pts_angle, neigh_ctr, norm_vect):
107 1d22a086 Carles Marti
    from random import random
108 1d22a086 Carles Marti
    plane_vect = np.cross(norm_vect, [random() for i in range(3)])
109 1d22a086 Carles Marti
    add_adsorbate()
110 1d22a086 Carles Marti
111 1d22a086 Carles Marti
    return
112 f3d1e601 Carles Marti
113 f3d1e601 Carles Marti
114 f3d1e601 Carles Marti
def ads_chemcat(site, ctr, pts_angle):
115 a5cc42ff Carles Marti
    return "TO IMPLEMENT"
116 f3d1e601 Carles Marti
117 f3d1e601 Carles Marti
118 1d22a086 Carles Marti
def adsorb_confs(conf_list, surf, ads_ctrs, sites, algo, num_pts, neigh_ctrs,
119 1d22a086 Carles Marti
                 norm_vect):
120 a5cc42ff Carles Marti
    """Generates a number of adsorbate-surface structure coordinates.
121 a5cc42ff Carles Marti

122 a5cc42ff Carles Marti
    Given a list of conformers, a surface, a list of atom indices (or list of
123 a5cc42ff Carles Marti
    list of indices) of both the surface and the adsorbate, it generates a
124 a5cc42ff Carles Marti
    number of adsorbate-surface structures for every possible combination of
125 a5cc42ff Carles Marti
    them at different orientations.
126 a5cc42ff Carles Marti
    @param conf_list: list of ase.Atoms of the different conformers
127 a5cc42ff Carles Marti
    @param surf: the ase.Atoms object of the surface
128 a5cc42ff Carles Marti
    @param ads_ctrs: the list atom indices of the adsorbate.
129 a5cc42ff Carles Marti
    @param sites: the list of atom indices of the surface.
130 a5cc42ff Carles Marti
    @param algo: the algorithm to use for the generation of adsorbates.
131 a5cc42ff Carles Marti
    @param num_pts: the number of points per angle orientation to sample
132 a5cc42ff Carles Marti
    @param neigh_ctrs: the indices of the neighboring atoms to the adsorption
133 a5cc42ff Carles Marti
    atoms.
134 1d22a086 Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
135 a5cc42ff Carles Marti
    @return: list of ase.Atoms for the adsorbate-surface structures
136 a5cc42ff Carles Marti
    """
137 f3d1e601 Carles Marti
    surf_ads_list = []
138 f3d1e601 Carles Marti
    sites_coords = get_atom_coords(surf, sites)
139 f3d1e601 Carles Marti
    for conf in conf_list:
140 f3d1e601 Carles Marti
        molec_ctr_coords = get_atom_coords(conf, ads_ctrs)
141 f3d1e601 Carles Marti
        molec_neigh_coords = get_atom_coords(conf, neigh_ctrs)
142 f3d1e601 Carles Marti
        for site in sites_coords:
143 f3d1e601 Carles Marti
            for i, molec_ctr in enumerate(molec_ctr_coords):
144 f3d1e601 Carles Marti
                if algo == 'euler':
145 1d22a086 Carles Marti
                    surf_ads_list.append(ads_euler(conf, surf, site, molec_ctr,
146 1d22a086 Carles Marti
                                                   num_pts, norm_vect,
147 f3d1e601 Carles Marti
                                                   molec_neigh_coords[i]))
148 f3d1e601 Carles Marti
                elif algo == 'chemcat':
149 f3d1e601 Carles Marti
                    surf_ads_list.append(ads_chemcat(site, molec_ctr, num_pts))
150 f3d1e601 Carles Marti
    return surf_ads_list
151 f3d1e601 Carles Marti
152 f3d1e601 Carles Marti
153 4614bb6a Carles
def run_screening(inp_vars):
154 e07c09eb Carles
    """Carry out the screening of adsorbate coordinates on a surface
155 e07c09eb Carles

156 e07c09eb Carles
    @param inp_vars: Calculation parameters from input file.
157 e07c09eb Carles
    """
158 e07c09eb Carles
    import os
159 f3d1e601 Carles Marti
    from modules.formats import read_coords, read_energies, \
160 f3d1e601 Carles Marti
        rdkit_mol_to_ase_atoms, adapt_format
161 af3e2441 Carles Marti
    from modules.clustering import get_rmsd, clustering
162 a5cc42ff Carles Marti
    from modules.isolated import get_moments_of_inertia
163 f3d1e601 Carles Marti
    from modules.calculation import run_calc
164 e07c09eb Carles
165 e07c09eb Carles
    if not os.path.isdir("isolated"):
166 e07c09eb Carles
        err = "'isolated' directory not found. It is needed in order to carry "
167 e07c09eb Carles
        "out the screening of structures to be adsorbed"
168 e07c09eb Carles
        logger.error(err)
169 e07c09eb Carles
        raise ValueError(err)
170 e07c09eb Carles
171 f3d1e601 Carles Marti
    conf_list = read_coords(inp_vars['code'], 'isolated', 'rdkit')
172 f3d1e601 Carles Marti
    # TODO Implement neighbors algorithm
173 f3d1e601 Carles Marti
    # neigh_list = get_neighbors(conf_list[0], inp_vars['molec_ads_ctrs'])
174 f3d1e601 Carles Marti
    conf_enrgs = read_energies(inp_vars['code'], 'isolated')
175 f3d1e601 Carles Marti
    mois = np.array([get_moments_of_inertia(conf)[0] for conf in conf_list])
176 f3d1e601 Carles Marti
    rmsd_mtx = get_rmsd(conf_list)
177 05464650 Carles Marti
    exemplars = clustering(rmsd_mtx)
178 f3d1e601 Carles Marti
    conf_list = [conf_list[idx] for idx in exemplars]
179 f3d1e601 Carles Marti
    conf_list = [rdkit_mol_to_ase_atoms(conf) for conf in conf_list]
180 f3d1e601 Carles Marti
    surf = adapt_format('ase', inp_vars['surf_file'])
181 f3d1e601 Carles Marti
    surf_ads_list = adsorb_confs(conf_list, surf, inp_vars['molec_ads_ctrs'],
182 f3d1e601 Carles Marti
                                 inp_vars['sites'], inp_vars['ads_algo'],
183 f3d1e601 Carles Marti
                                 inp_vars['sample_points_per_angle'],
184 1d22a086 Carles Marti
                                 inp_vars['molec_neigh_ctrs'],
185 1d22a086 Carles Marti
                                 inp_vars['surf_norm_vect'])
186 f3d1e601 Carles Marti
    run_calc('screening', inp_vars, surf_ads_list)