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

dockonsurf / modules / screening.py @ b4aef3d7

Historique | Voir | Annoter | Télécharger (8,18 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 12a12bdd Carles Marti
    @param vects: np.ndarray with shape (num_vectors, length_vector).
28 a5cc42ff Carles Marti
    @return: vector average computed doing the element-wise mean.
29 12a12bdd Carles Marti
    """
30 12a12bdd Carles Marti
    from utilities import try_command
31 12a12bdd Carles Marti
    err = "vect_avg parameter vects must be a list-like, able to be converted" \
32 12a12bdd Carles Marti
          " np.array"
33 12a12bdd Carles Marti
    vects = try_command(np.array, [(ValueError, err)], vects)
34 12a12bdd Carles Marti
    if len(vects.shape) == 1:
35 12a12bdd Carles Marti
        return vects
36 12a12bdd Carles Marti
    else:
37 12a12bdd Carles Marti
        num_vects = vects.shape[1]
38 12a12bdd Carles Marti
        return np.array([np.average(vects[:, i]) for i in range(num_vects)])
39 12a12bdd Carles Marti
40 12a12bdd Carles Marti
41 f3d1e601 Carles Marti
def get_atom_coords(atoms: ase.Atoms, ctrs_list):
42 a5cc42ff Carles Marti
    """Gets the coordinates of the specified atoms from a ase.Atoms object.
43 a5cc42ff Carles Marti

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

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

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

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