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

dockonsurf / modules / screening.py @ dadc6016

Historique | Voir | Annoter | Télécharger (8,54 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 b4aef3d7 Carles Marti
def get_vect_angle(v1, v2, degrees=False):
9 b4aef3d7 Carles Marti
    """Computes the angle between two vectors.
10 b4aef3d7 Carles Marti

11 b4aef3d7 Carles Marti
    @param v1: The first vector.
12 b4aef3d7 Carles Marti
    @param v2: The second vector.
13 b4aef3d7 Carles Marti
    @param degrees: Whether the result should be in radians (True) or in
14 b4aef3d7 Carles Marti
        degrees (False).
15 b4aef3d7 Carles Marti
    @return: The angle in radians if degrees = False, or in degrees if
16 b4aef3d7 Carles Marti
        degrees =True
17 b4aef3d7 Carles Marti
    """
18 b4aef3d7 Carles Marti
    v1_u = v1 / np.linalg.norm(v1)
19 b4aef3d7 Carles Marti
    v2_u = v2 / np.linalg.norm(v2)
20 b4aef3d7 Carles Marti
    angle = np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
21 b4aef3d7 Carles Marti
    return angle if not degrees else angle * 180 / np.pi
22 b4aef3d7 Carles Marti
23 b4aef3d7 Carles Marti
24 12a12bdd Carles Marti
def vect_avg(vects):
25 a5cc42ff Carles Marti
    """Computes the element-wise mean of a set of vectors.
26 12a12bdd Carles Marti

27 dadc6016 Carles Marti
    @param vects: list of lists-like: containing the vectors (num_vectors,
28 dadc6016 Carles Marti
        length_vector).
29 a5cc42ff Carles Marti
    @return: vector average computed doing the element-wise mean.
30 12a12bdd Carles Marti
    """
31 12a12bdd Carles Marti
    from utilities import try_command
32 12a12bdd Carles Marti
    err = "vect_avg parameter vects must be a list-like, able to be converted" \
33 12a12bdd Carles Marti
          " np.array"
34 dadc6016 Carles Marti
    array = try_command(np.array, [(ValueError, err)], vects)
35 dadc6016 Carles Marti
    if len(array.shape) == 1:
36 dadc6016 Carles Marti
        return array
37 12a12bdd Carles Marti
    else:
38 dadc6016 Carles Marti
        num_vects = array.shape[1]
39 dadc6016 Carles Marti
        return np.array([np.average(array[:, i]) for i in range(num_vects)])
40 12a12bdd Carles Marti
41 12a12bdd Carles Marti
42 f3d1e601 Carles Marti
def get_atom_coords(atoms: ase.Atoms, ctrs_list):
43 a5cc42ff Carles Marti
    """Gets the coordinates of the specified atoms from a ase.Atoms object.
44 a5cc42ff Carles Marti

45 a5cc42ff Carles Marti
    Given an ase.Atoms object and a list of atom indices specified in ctrs_list
46 a5cc42ff Carles Marti
    it gets the coordinates of the specified atoms. If the element in the
47 a5cc42ff Carles Marti
    ctrs_list is not an index but yet a list of indices, it computes the
48 a5cc42ff Carles Marti
    element-wise mean of the coordinates of the atoms specified in the inner
49 a5cc42ff Carles Marti
    list.
50 a5cc42ff Carles Marti
    @param atoms: ase.Atoms object for which to obtain the coordinates of.
51 a5cc42ff Carles Marti
    @param ctrs_list: list of (indices/list of indices) of the atoms for which
52 a5cc42ff Carles Marti
                      the coordinates should be extracted.
53 a5cc42ff Carles Marti
    @return: np.ndarray of atomic coordinates.
54 a5cc42ff Carles Marti
    """
55 12a12bdd Carles Marti
    coords = []
56 12a12bdd Carles Marti
    for i, elem in enumerate(ctrs_list):
57 12a12bdd Carles Marti
        if isinstance(elem, list):
58 12a12bdd Carles Marti
            coords.append(vect_avg(np.array([atoms[c].position for c in elem])))
59 12a12bdd Carles Marti
        elif isinstance(elem, int):
60 12a12bdd Carles Marti
            coords.append(atoms[elem].position)
61 12a12bdd Carles Marti
        else:
62 12a12bdd Carles Marti
            err = f"'ctrs_list must be a list of ints or lists, {type(elem)} " \
63 12a12bdd Carles Marti
                  "found."
64 12a12bdd Carles Marti
            logger.error(err)
65 12a12bdd Carles Marti
            raise ValueError
66 12a12bdd Carles Marti
    return np.array(coords)
67 f3d1e601 Carles Marti
68 f3d1e601 Carles Marti
69 dadc6016 Carles Marti
def add_adsorbate(slab, adsorbate, site_coord, ctr_coord, height, offset=None,
70 1d22a086 Carles Marti
                  norm_vect=(0, 0, 1)):
71 1d22a086 Carles Marti
    """Add an adsorbate to a surface.
72 1d22a086 Carles Marti

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

139 a5cc42ff Carles Marti
    Given a list of conformers, a surface, a list of atom indices (or list of
140 a5cc42ff Carles Marti
    list of indices) of both the surface and the adsorbate, it generates a
141 a5cc42ff Carles Marti
    number of adsorbate-surface structures for every possible combination of
142 a5cc42ff Carles Marti
    them at different orientations.
143 a5cc42ff Carles Marti
    @param conf_list: list of ase.Atoms of the different conformers
144 a5cc42ff Carles Marti
    @param surf: the ase.Atoms object of the surface
145 a5cc42ff Carles Marti
    @param ads_ctrs: the list atom indices of the adsorbate.
146 a5cc42ff Carles Marti
    @param sites: the list of atom indices of the surface.
147 a5cc42ff Carles Marti
    @param algo: the algorithm to use for the generation of adsorbates.
148 a5cc42ff Carles Marti
    @param num_pts: the number of points per angle orientation to sample
149 a5cc42ff Carles Marti
    @param neigh_ctrs: the indices of the neighboring atoms to the adsorption
150 a5cc42ff Carles Marti
    atoms.
151 1d22a086 Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
152 dadc6016 Carles Marti
    @param coll_bttm: The lowermost height for which to detect a collision
153 a5cc42ff Carles Marti
    @return: list of ase.Atoms for the adsorbate-surface structures
154 a5cc42ff Carles Marti
    """
155 f3d1e601 Carles Marti
    surf_ads_list = []
156 f3d1e601 Carles Marti
    sites_coords = get_atom_coords(surf, sites)
157 f3d1e601 Carles Marti
    for conf in conf_list:
158 f3d1e601 Carles Marti
        molec_ctr_coords = get_atom_coords(conf, ads_ctrs)
159 f3d1e601 Carles Marti
        molec_neigh_coords = get_atom_coords(conf, neigh_ctrs)
160 f3d1e601 Carles Marti
        for site in sites_coords:
161 f3d1e601 Carles Marti
            for i, molec_ctr in enumerate(molec_ctr_coords):
162 f3d1e601 Carles Marti
                if algo == 'euler':
163 dadc6016 Carles Marti
                    surf_ads_list.extend(ads_euler(conf, surf, molec_ctr, site,
164 1d22a086 Carles Marti
                                                   num_pts, norm_vect,
165 dadc6016 Carles Marti
                                                   coll_bttm,
166 f3d1e601 Carles Marti
                                                   molec_neigh_coords[i]))
167 f3d1e601 Carles Marti
                elif algo == 'chemcat':
168 dadc6016 Carles Marti
                    surf_ads_list.extend(ads_chemcat(site, molec_ctr, num_pts))
169 f3d1e601 Carles Marti
    return surf_ads_list
170 f3d1e601 Carles Marti
171 f3d1e601 Carles Marti
172 4614bb6a Carles
def run_screening(inp_vars):
173 e07c09eb Carles
    """Carry out the screening of adsorbate coordinates on a surface
174 e07c09eb Carles

175 e07c09eb Carles
    @param inp_vars: Calculation parameters from input file.
176 e07c09eb Carles
    """
177 e07c09eb Carles
    import os
178 f3d1e601 Carles Marti
    from modules.formats import read_coords, read_energies, \
179 f3d1e601 Carles Marti
        rdkit_mol_to_ase_atoms, adapt_format
180 af3e2441 Carles Marti
    from modules.clustering import get_rmsd, clustering
181 a5cc42ff Carles Marti
    from modules.isolated import get_moments_of_inertia
182 f3d1e601 Carles Marti
    from modules.calculation import run_calc
183 e07c09eb Carles
184 e07c09eb Carles
    if not os.path.isdir("isolated"):
185 e07c09eb Carles
        err = "'isolated' directory not found. It is needed in order to carry "
186 e07c09eb Carles
        "out the screening of structures to be adsorbed"
187 e07c09eb Carles
        logger.error(err)
188 e07c09eb Carles
        raise ValueError(err)
189 e07c09eb Carles
190 f3d1e601 Carles Marti
    conf_list = read_coords(inp_vars['code'], 'isolated', 'rdkit')
191 dadc6016 Carles Marti
    # TODO Implement neighbors algorithm / align molecule to surface
192 f3d1e601 Carles Marti
    # neigh_list = get_neighbors(conf_list[0], inp_vars['molec_ads_ctrs'])
193 f3d1e601 Carles Marti
    conf_enrgs = read_energies(inp_vars['code'], 'isolated')
194 f3d1e601 Carles Marti
    mois = np.array([get_moments_of_inertia(conf)[0] for conf in conf_list])
195 f3d1e601 Carles Marti
    rmsd_mtx = get_rmsd(conf_list)
196 dadc6016 Carles Marti
    exemplars = clustering(rmsd_mtx)  # TODO Kind of Clustering
197 dadc6016 Carles Marti
    clust_conf_list = [conf_list[idx] for idx in exemplars]
198 dadc6016 Carles Marti
    conf_list = [rdkit_mol_to_ase_atoms(conf) for conf in clust_conf_list]
199 f3d1e601 Carles Marti
    surf = adapt_format('ase', inp_vars['surf_file'])
200 f3d1e601 Carles Marti
    surf_ads_list = adsorb_confs(conf_list, surf, inp_vars['molec_ads_ctrs'],
201 f3d1e601 Carles Marti
                                 inp_vars['sites'], inp_vars['ads_algo'],
202 f3d1e601 Carles Marti
                                 inp_vars['sample_points_per_angle'],
203 1d22a086 Carles Marti
                                 inp_vars['molec_neigh_ctrs'],
204 dadc6016 Carles Marti
                                 inp_vars['surf_norm_vect'],
205 dadc6016 Carles Marti
                                 inp_vars['collision_bottom'])
206 f3d1e601 Carles Marti
    run_calc('screening', inp_vars, surf_ads_list)