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

dockonsurf / modules / screening.py @ 695dcff8

Historique | Voir | Annoter | Télécharger (17,55 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 bfe93f0d Carles Marti
def assign_prop(atoms: ase.Atoms, prop_name: str, prop_val):  # TODO Needed?
9 bfe93f0d Carles Marti
    atoms.info[prop_name] = prop_val
10 bfe93f0d Carles Marti
11 bfe93f0d Carles Marti
12 bfe93f0d Carles Marti
def select_confs(orig_conf_list: list, magns: list, num_sel: int, code: str):
13 bfe93f0d Carles Marti
    """Takes a list ase.Atoms and selects the most different magnitude-wise.
14 bfe93f0d Carles Marti

15 bfe93f0d Carles Marti
    Given a list of ase.Atoms objects and a list of magnitudes, it selects a
16 bfe93f0d Carles Marti
    number of the most different conformers according to every magnitude
17 bfe93f0d Carles Marti
    specified.
18 bfe93f0d Carles Marti
    @param orig_conf_list: list of ase.Atoms objects to select among.
19 bfe93f0d Carles Marti
    @param magns: list of str with the names of the magnitudes to use for the
20 bfe93f0d Carles Marti
        conformer selection.
21 bfe93f0d Carles Marti
        Supported magnitudes: 'energy', 'moi'.
22 bfe93f0d Carles Marti
    @param num_sel: number of conformers to select for every of the magnitudes.
23 bfe93f0d Carles Marti
    @param code: The code that generated the magnitude information.
24 bfe93f0d Carles Marti
         Supported codes: See formats.py
25 bfe93f0d Carles Marti
    @return: list of the selected ase.Atoms objects.
26 bfe93f0d Carles Marti
    """
27 bfe93f0d Carles Marti
    from copy import deepcopy
28 bfe93f0d Carles Marti
    from modules.formats import read_energies
29 bfe93f0d Carles Marti
30 bfe93f0d Carles Marti
    conf_list = deepcopy(orig_conf_list)
31 bfe93f0d Carles Marti
    selected_ids = []
32 bfe93f0d Carles Marti
    if num_sel >= len(conf_list):
33 bfe93f0d Carles Marti
        logger.warning('Number of conformers per magnitude is equal or larger '
34 bfe93f0d Carles Marti
                       'than the total number of conformers. Using all '
35 695dcff8 Carles Marti
                       f'available conformers: {len(conf_list)}.')
36 bfe93f0d Carles Marti
        return conf_list
37 bfe93f0d Carles Marti
38 bfe93f0d Carles Marti
    # Read properties
39 bfe93f0d Carles Marti
    if 'energy' in magns:
40 bfe93f0d Carles Marti
        conf_enrgs = read_energies(code, 'isolated')
41 bfe93f0d Carles Marti
    if 'moi' in magns:
42 bfe93f0d Carles Marti
        mois = np.array([conf.get_moments_of_inertia() for conf in conf_list])
43 bfe93f0d Carles Marti
44 bfe93f0d Carles Marti
    # Assign values
45 bfe93f0d Carles Marti
    for i, conf in enumerate(conf_list):
46 bfe93f0d Carles Marti
        assign_prop(conf, 'idx', i)
47 bfe93f0d Carles Marti
        if 'energy' in magns:
48 bfe93f0d Carles Marti
            assign_prop(conf, 'energy', conf_enrgs[i])
49 bfe93f0d Carles Marti
        if 'moi' in magns:
50 bfe93f0d Carles Marti
            assign_prop(conf, 'moi', mois[i, 2])
51 bfe93f0d Carles Marti
52 bfe93f0d Carles Marti
    # pick ids
53 bfe93f0d Carles Marti
    for magn in magns:
54 bfe93f0d Carles Marti
        sorted_list = sorted(conf_list, key=lambda conf: abs(conf.info[magn]))
55 bfe93f0d Carles Marti
        if sorted_list[-1].info['idx'] not in selected_ids:
56 bfe93f0d Carles Marti
            selected_ids.append(sorted_list[-1].info['idx'])
57 bfe93f0d Carles Marti
        if num_sel > 1:
58 bfe93f0d Carles Marti
            for i in range(0, len(sorted_list) - 1,
59 bfe93f0d Carles Marti
                           len(conf_list) // (num_sel - 1)):
60 bfe93f0d Carles Marti
                if sorted_list[i].info['idx'] not in selected_ids:
61 bfe93f0d Carles Marti
                    selected_ids.append(sorted_list[i].info['idx'])
62 bfe93f0d Carles Marti
63 695dcff8 Carles Marti
    logger.info(f'Selected {len(selected_ids)} conformers for adsorption.')
64 bfe93f0d Carles Marti
    return [conf_list[idx] for idx in selected_ids]
65 bfe93f0d Carles Marti
66 bfe93f0d Carles Marti
67 b4aef3d7 Carles Marti
def get_vect_angle(v1, v2, degrees=False):
68 b4aef3d7 Carles Marti
    """Computes the angle between two vectors.
69 b4aef3d7 Carles Marti

70 b4aef3d7 Carles Marti
    @param v1: The first vector.
71 b4aef3d7 Carles Marti
    @param v2: The second vector.
72 b4aef3d7 Carles Marti
    @param degrees: Whether the result should be in radians (True) or in
73 b4aef3d7 Carles Marti
        degrees (False).
74 b4aef3d7 Carles Marti
    @return: The angle in radians if degrees = False, or in degrees if
75 b4aef3d7 Carles Marti
        degrees =True
76 b4aef3d7 Carles Marti
    """
77 b4aef3d7 Carles Marti
    v1_u = v1 / np.linalg.norm(v1)
78 b4aef3d7 Carles Marti
    v2_u = v2 / np.linalg.norm(v2)
79 b4aef3d7 Carles Marti
    angle = np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
80 b4aef3d7 Carles Marti
    return angle if not degrees else angle * 180 / np.pi
81 b4aef3d7 Carles Marti
82 b4aef3d7 Carles Marti
83 12a12bdd Carles Marti
def vect_avg(vects):
84 a5cc42ff Carles Marti
    """Computes the element-wise mean of a set of vectors.
85 12a12bdd Carles Marti

86 dadc6016 Carles Marti
    @param vects: list of lists-like: containing the vectors (num_vectors,
87 dadc6016 Carles Marti
        length_vector).
88 a5cc42ff Carles Marti
    @return: vector average computed doing the element-wise mean.
89 12a12bdd Carles Marti
    """
90 12a12bdd Carles Marti
    from utilities import try_command
91 12a12bdd Carles Marti
    err = "vect_avg parameter vects must be a list-like, able to be converted" \
92 12a12bdd Carles Marti
          " np.array"
93 dadc6016 Carles Marti
    array = try_command(np.array, [(ValueError, err)], vects)
94 dadc6016 Carles Marti
    if len(array.shape) == 1:
95 dadc6016 Carles Marti
        return array
96 12a12bdd Carles Marti
    else:
97 dadc6016 Carles Marti
        num_vects = array.shape[1]
98 dadc6016 Carles Marti
        return np.array([np.average(array[:, i]) for i in range(num_vects)])
99 12a12bdd Carles Marti
100 12a12bdd Carles Marti
101 8fdff302 Carles Marti
def get_atom_coords(atoms: ase.Atoms, ctrs_list=None):
102 8fdff302 Carles Marti
    """Gets the coordinates of the specified indices from a ase.Atoms object.
103 a5cc42ff Carles Marti

104 a5cc42ff Carles Marti
    Given an ase.Atoms object and a list of atom indices specified in ctrs_list
105 a5cc42ff Carles Marti
    it gets the coordinates of the specified atoms. If the element in the
106 a5cc42ff Carles Marti
    ctrs_list is not an index but yet a list of indices, it computes the
107 a5cc42ff Carles Marti
    element-wise mean of the coordinates of the atoms specified in the inner
108 a5cc42ff Carles Marti
    list.
109 a5cc42ff Carles Marti
    @param atoms: ase.Atoms object for which to obtain the coordinates of.
110 a5cc42ff Carles Marti
    @param ctrs_list: list of (indices/list of indices) of the atoms for which
111 a5cc42ff Carles Marti
                      the coordinates should be extracted.
112 a5cc42ff Carles Marti
    @return: np.ndarray of atomic coordinates.
113 a5cc42ff Carles Marti
    """
114 12a12bdd Carles Marti
    coords = []
115 8fdff302 Carles Marti
    err = "'ctrs_list' argument must be an integer, a list of integers or a " \
116 8fdff302 Carles Marti
          "list of lists of integers. Every integer must be in the range " \
117 8fdff302 Carles Marti
          "[0, num_atoms)"
118 8fdff302 Carles Marti
    if ctrs_list is None:
119 8fdff302 Carles Marti
        ctrs_list = range(len(atoms))
120 8fdff302 Carles Marti
    elif isinstance(ctrs_list, int):
121 8fdff302 Carles Marti
        if ctrs_list not in range(len(atoms)):
122 8fdff302 Carles Marti
            logger.error(err)
123 8fdff302 Carles Marti
            raise ValueError(err)
124 8fdff302 Carles Marti
        return atoms[ctrs_list].position
125 8fdff302 Carles Marti
    for elem in ctrs_list:
126 12a12bdd Carles Marti
        if isinstance(elem, list):
127 8fdff302 Carles Marti
            coords.append(vect_avg([atoms[c].position for c in elem]))
128 12a12bdd Carles Marti
        elif isinstance(elem, int):
129 12a12bdd Carles Marti
            coords.append(atoms[elem].position)
130 12a12bdd Carles Marti
        else:
131 8fdff302 Carles Marti
132 12a12bdd Carles Marti
            logger.error(err)
133 12a12bdd Carles Marti
            raise ValueError
134 12a12bdd Carles Marti
    return np.array(coords)
135 f3d1e601 Carles Marti
136 f3d1e601 Carles Marti
137 dadc6016 Carles Marti
def add_adsorbate(slab, adsorbate, site_coord, ctr_coord, height, offset=None,
138 1d22a086 Carles Marti
                  norm_vect=(0, 0, 1)):
139 1d22a086 Carles Marti
    """Add an adsorbate to a surface.
140 1d22a086 Carles Marti

141 1d22a086 Carles Marti
    This function extends the functionality of ase.build.add_adsorbate
142 1d22a086 Carles Marti
    (https://wiki.fysik.dtu.dk/ase/ase/build/surface.html#ase.build.add_adsorbate)
143 1d22a086 Carles Marti
    by enabling to change the z coordinate and the axis perpendicular to the
144 1d22a086 Carles Marti
    surface.
145 1d22a086 Carles Marti
    @param slab: ase.Atoms object containing the coordinates of the surface
146 1d22a086 Carles Marti
    @param adsorbate: ase.Atoms object containing the coordinates of the
147 1d22a086 Carles Marti
        adsorbate.
148 dadc6016 Carles Marti
    @param site_coord: The coordinates of the adsorption site on the surface.
149 dadc6016 Carles Marti
    @param ctr_coord: The coordinates of the adsorption center in the molecule.
150 dadc6016 Carles Marti
    @param height: The height above the surface where to adsorb.
151 1d22a086 Carles Marti
    @param offset: Offsets the adsorbate by a number of unit cells. Mostly
152 1d22a086 Carles Marti
        useful when adding more than one adsorbate.
153 1d22a086 Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
154 1d22a086 Carles Marti
    """
155 36d92f4f Carles Marti
    from copy import deepcopy
156 1d22a086 Carles Marti
    info = slab.info.get('adsorbate_info', {})
157 dadc6016 Carles Marti
    pos = np.array([0.0, 0.0, 0.0])  # part of absolute coordinates
158 1d22a086 Carles Marti
    spos = np.array([0.0, 0.0, 0.0])  # part relative to unit cell
159 dadc6016 Carles Marti
    norm_vect_u = np.array(norm_vect) / np.linalg.norm(norm_vect)
160 1d22a086 Carles Marti
    if offset is not None:
161 1d22a086 Carles Marti
        spos += np.asarray(offset, float)
162 dadc6016 Carles Marti
    if isinstance(site_coord, str):
163 1d22a086 Carles Marti
        # A site-name:
164 1d22a086 Carles Marti
        if 'sites' not in info:
165 1d22a086 Carles Marti
            raise TypeError('If the atoms are not made by an ase.build '
166 1d22a086 Carles Marti
                            'function, position cannot be a name.')
167 dadc6016 Carles Marti
        if site_coord not in info['sites']:
168 dadc6016 Carles Marti
            raise TypeError('Adsorption site %s not supported.' % site_coord)
169 dadc6016 Carles Marti
        spos += info['sites'][site_coord]
170 1d22a086 Carles Marti
    else:
171 dadc6016 Carles Marti
        pos += site_coord
172 1d22a086 Carles Marti
    if 'cell' in info:
173 1d22a086 Carles Marti
        cell = info['cell']
174 1d22a086 Carles Marti
    else:
175 1d22a086 Carles Marti
        cell = slab.get_cell()
176 1d22a086 Carles Marti
    pos += np.dot(spos, cell)
177 1d22a086 Carles Marti
    # Convert the adsorbate to an Atoms object
178 1d22a086 Carles Marti
    if isinstance(adsorbate, ase.Atoms):
179 36d92f4f Carles Marti
        ads = deepcopy(adsorbate)
180 1d22a086 Carles Marti
    elif isinstance(adsorbate, ase.Atom):
181 1d22a086 Carles Marti
        ads = ase.Atoms([adsorbate])
182 1d22a086 Carles Marti
    else:
183 1d22a086 Carles Marti
        # Assume it is a string representing a single Atom
184 1d22a086 Carles Marti
        ads = ase.Atoms([ase.Atom(adsorbate)])
185 dadc6016 Carles Marti
    pos += height * norm_vect_u
186 1d22a086 Carles Marti
    # Move adsorbate into position
187 dadc6016 Carles Marti
    ads.translate(pos - ctr_coord)
188 1d22a086 Carles Marti
    # Attach the adsorbate
189 1d22a086 Carles Marti
    slab.extend(ads)
190 1d22a086 Carles Marti
191 1d22a086 Carles Marti
192 a4b57124 Carles Marti
def check_collision(slab_molec, slab_num_atoms, min_height, vect, nn_slab=0,
193 a4b57124 Carles Marti
                    nn_molec=0, coll_coeff=0.9):
194 5f3f4b69 Carles Marti
    """Checks whether a slab and a molecule collide or not.
195 5f3f4b69 Carles Marti

196 5f3f4b69 Carles Marti
    @param slab_molec: The system of adsorbate-slab for which to detect if there
197 5f3f4b69 Carles Marti
        are collisions.
198 5f3f4b69 Carles Marti
    @param nn_slab: Number of neigbors in the surface.
199 5f3f4b69 Carles Marti
    @param nn_molec: Number of neighbors in the molecule.
200 5f3f4b69 Carles Marti
    @param coll_coeff: The coefficient that multiplies the covalent radius of
201 5f3f4b69 Carles Marti
        atoms resulting in a distance that two atoms being closer to that is
202 5f3f4b69 Carles Marti
        considered as atomic collision.
203 5f3f4b69 Carles Marti
    @param slab_num_atoms: Number of atoms of the bare slab.
204 5f3f4b69 Carles Marti
    @param min_height: The minimum height atoms can have to not be considered as
205 5f3f4b69 Carles Marti
        colliding.
206 5f3f4b69 Carles Marti
    @param vect: The vector perpendicular to the slab.
207 5f3f4b69 Carles Marti
    @return: bool, whether the surface and the molecule collide.
208 5f3f4b69 Carles Marti
    """
209 5f3f4b69 Carles Marti
    from ase.neighborlist import natural_cutoffs, neighbor_list
210 5fb01677 Carles Marti
    if min_height is not False:
211 a4b57124 Carles Marti
        cart_axes = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
212 a4b57124 Carles Marti
                     [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]
213 a4b57124 Carles Marti
        if vect.tolist() in cart_axes:
214 5fb01677 Carles Marti
            for atom in slab_molec[slab_num_atoms:]:
215 a4b57124 Carles Marti
                for i, coord in enumerate(vect):
216 a4b57124 Carles Marti
                    if coord == 0:
217 a4b57124 Carles Marti
                        continue
218 a4b57124 Carles Marti
                    if atom.position[i] * coord < min_height:
219 a4b57124 Carles Marti
                        return True
220 a4b57124 Carles Marti
            return False
221 5f3f4b69 Carles Marti
    else:
222 5fb01677 Carles Marti
        slab_molec_cutoffs = natural_cutoffs(slab_molec, mult=coll_coeff)
223 5fb01677 Carles Marti
        slab_molec_nghbs = len(neighbor_list("i", slab_molec, slab_molec_cutoffs))
224 5fb01677 Carles Marti
        if slab_molec_nghbs > nn_slab + nn_molec:
225 5fb01677 Carles Marti
            return True
226 5fb01677 Carles Marti
        else:
227 5fb01677 Carles Marti
            return False
228 5f3f4b69 Carles Marti
229 5f3f4b69 Carles Marti
230 a260d04d Carles Marti
def correct_coll(molec, slab, ctr_coord, site_coord, num_pts,
231 5fb01677 Carles Marti
                 min_coll_height, norm_vect, slab_nghbs, molec_nghbs,
232 5fb01677 Carles Marti
                 coll_coeff):
233 a260d04d Carles Marti
    # TODO Rethink this function
234 a260d04d Carles Marti
    """Tries to adsorb a molecule on a slab trying to avoid collisions by doing
235 a260d04d Carles Marti
    small rotations.
236 a260d04d Carles Marti

237 a260d04d Carles Marti
    @param molec: ase.Atoms object of the molecule to adsorb
238 a260d04d Carles Marti
    @param slab: ase.Atoms object of the surface on which to adsorb the
239 a260d04d Carles Marti
        molecule
240 a260d04d Carles Marti
    @param ctr_coord: The coordinates of the molecule to use as adsorption
241 a260d04d Carles Marti
        center.
242 a260d04d Carles Marti
    @param site_coord: The coordinates of the surface on which to adsorb the
243 a260d04d Carles Marti
        molecule
244 a260d04d Carles Marti
    @param num_pts: Number on which to sample Euler angles.
245 5fb01677 Carles Marti
    @param min_coll_height: The lowermost height for which to detect a collision
246 a260d04d Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
247 a260d04d Carles Marti
    @param slab_nghbs: Number of neigbors in the surface.
248 a260d04d Carles Marti
    @param molec_nghbs: Number of neighbors in the molecule.
249 a260d04d Carles Marti
    @param coll_coeff: The coefficient that multiplies the covalent radius of
250 a260d04d Carles Marti
        atoms resulting in a distance that two atoms being closer to that is
251 a260d04d Carles Marti
        considered as atomic collision.
252 a260d04d Carles Marti
    @return collision: bool, whether the structure generated has collisions
253 a260d04d Carles Marti
        between slab and adsorbate.
254 a260d04d Carles Marti
    """
255 a260d04d Carles Marti
    from copy import deepcopy
256 a260d04d Carles Marti
    slab_num_atoms = len(slab)
257 a260d04d Carles Marti
    collision = True
258 a260d04d Carles Marti
    max_corr = 6  # Should be an even number
259 a260d04d Carles Marti
    d_angle = 180 / ((max_corr / 2.0) * num_pts)
260 a260d04d Carles Marti
    num_corr = 0
261 a260d04d Carles Marti
    while collision and num_corr <= max_corr:
262 a260d04d Carles Marti
        k = num_corr * (-1) ** num_corr
263 a260d04d Carles Marti
        slab_molec = deepcopy(slab)
264 a260d04d Carles Marti
        molec.euler_rotate(k * d_angle, k * d_angle / 2, k * d_angle,
265 a260d04d Carles Marti
                           center=ctr_coord)
266 a260d04d Carles Marti
        add_adsorbate(slab_molec, molec, site_coord, ctr_coord, 2.5,
267 a260d04d Carles Marti
                      norm_vect=norm_vect)
268 5fb01677 Carles Marti
        collision = check_collision(slab_molec, slab_num_atoms, min_coll_height,
269 a260d04d Carles Marti
                                    norm_vect, slab_nghbs, molec_nghbs,
270 a260d04d Carles Marti
                                    coll_coeff)
271 a260d04d Carles Marti
        num_corr += 1
272 a260d04d Carles Marti
    return slab_molec, collision
273 a260d04d Carles Marti
274 1d22a086 Carles Marti
275 3ab0865c Carles Marti
def ads_euler(orig_molec, slab, ctr_coord, site_coord, num_pts,
276 5fb01677 Carles Marti
              min_coll_height, coll_coeff, norm_vect, slab_nghbs, molec_nghbs):
277 3ab0865c Carles Marti
    """Generates adsorbate-surface structures by sampling over Euler angles.
278 3ab0865c Carles Marti

279 3ab0865c Carles Marti
    This function generates a number of adsorbate-surface structures at
280 3ab0865c Carles Marti
    different orientations of the adsorbate sampled at multiple Euler (zxz)
281 3ab0865c Carles Marti
    angles.
282 3ab0865c Carles Marti
    @param orig_molec: ase.Atoms object of the molecule to adsorb
283 3ab0865c Carles Marti
    @param slab: ase.Atoms object of the surface on which to adsorb the molecule
284 3ab0865c Carles Marti
    @param ctr_coord: The coordinates of the molecule to use as adsorption
285 3ab0865c Carles Marti
        center.
286 3ab0865c Carles Marti
    @param site_coord: The coordinates of the surface on which to adsorb the
287 3ab0865c Carles Marti
        molecule
288 3ab0865c Carles Marti
    @param num_pts: Number on which to sample Euler angles.
289 5fb01677 Carles Marti
    @param min_coll_height: The lowermost height for which to detect a collision
290 3ab0865c Carles Marti
    @param coll_coeff: The coefficient that multiplies the covalent radius of
291 3ab0865c Carles Marti
        atoms resulting in a distance that two atoms being closer to that is
292 3ab0865c Carles Marti
        considered as atomic collision.
293 3ab0865c Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
294 3ab0865c Carles Marti
    @param slab_nghbs: Number of neigbors in the surface.
295 3ab0865c Carles Marti
    @param molec_nghbs: Number of neighbors in the molecule.
296 3ab0865c Carles Marti
    @return: list of ase.Atoms object conatining all the orientations of a given
297 3ab0865c Carles Marti
        conformer
298 3ab0865c Carles Marti
    """
299 3ab0865c Carles Marti
    from copy import deepcopy
300 3ab0865c Carles Marti
    slab_ads_list = []
301 3ab0865c Carles Marti
    # rotation around z
302 3ab0865c Carles Marti
    for alpha in np.arange(0, 360, 360 / num_pts):
303 3ab0865c Carles Marti
        # rotation around x'
304 3ab0865c Carles Marti
        for beta in np.arange(0, 180, 180 / num_pts):
305 3ab0865c Carles Marti
            # rotation around z'
306 3ab0865c Carles Marti
            for gamma in np.arange(0, 360, 360 / num_pts):
307 3ab0865c Carles Marti
                molec = deepcopy(orig_molec)
308 3ab0865c Carles Marti
                molec.euler_rotate(alpha, beta, gamma, center=ctr_coord)
309 3ab0865c Carles Marti
                slab_molec, collision = correct_coll(molec, slab,
310 5fb01677 Carles Marti
                                                     ctr_coord, site_coord,
311 5fb01677 Carles Marti
                                                     num_pts, min_coll_height,
312 5fb01677 Carles Marti
                                                     norm_vect,
313 5fb01677 Carles Marti
                                                     slab_nghbs, molec_nghbs,
314 5fb01677 Carles Marti
                                                     coll_coeff)
315 3ab0865c Carles Marti
                if not collision:
316 3ab0865c Carles Marti
                    slab_ads_list.append(slab_molec)
317 3ab0865c Carles Marti
318 3ab0865c Carles Marti
    return slab_ads_list
319 f3d1e601 Carles Marti
320 f3d1e601 Carles Marti
321 f3d1e601 Carles Marti
def ads_chemcat(site, ctr, pts_angle):
322 a5cc42ff Carles Marti
    return "TO IMPLEMENT"
323 f3d1e601 Carles Marti
324 f3d1e601 Carles Marti
325 bb55f47c Carles Marti
def adsorb_confs(conf_list, surf, molec_ctrs, sites, algo, num_pts, neigh_ctrs,
326 5fb01677 Carles Marti
                 norm_vect, min_coll_height, coll_coeff):
327 a5cc42ff Carles Marti
    """Generates a number of adsorbate-surface structure coordinates.
328 a5cc42ff Carles Marti

329 a5cc42ff Carles Marti
    Given a list of conformers, a surface, a list of atom indices (or list of
330 a5cc42ff Carles Marti
    list of indices) of both the surface and the adsorbate, it generates a
331 a5cc42ff Carles Marti
    number of adsorbate-surface structures for every possible combination of
332 a5cc42ff Carles Marti
    them at different orientations.
333 a5cc42ff Carles Marti
    @param conf_list: list of ase.Atoms of the different conformers
334 a5cc42ff Carles Marti
    @param surf: the ase.Atoms object of the surface
335 bb55f47c Carles Marti
    @param molec_ctrs: the list atom indices of the adsorbate.
336 a5cc42ff Carles Marti
    @param sites: the list of atom indices of the surface.
337 a5cc42ff Carles Marti
    @param algo: the algorithm to use for the generation of adsorbates.
338 a5cc42ff Carles Marti
    @param num_pts: the number of points per angle orientation to sample
339 a5cc42ff Carles Marti
    @param neigh_ctrs: the indices of the neighboring atoms to the adsorption
340 bb55f47c Carles Marti
        atoms.
341 1d22a086 Carles Marti
    @param norm_vect: The vector perpendicular to the surface.
342 5fb01677 Carles Marti
    @param min_coll_height: The lowermost height for which to detect a collision
343 bb55f47c Carles Marti
    @param coll_coeff: The coefficient that multiplies the covalent radius of
344 bb55f47c Carles Marti
        atoms resulting in a distance that two atoms being closer to that is
345 bb55f47c Carles Marti
        considered as atomic collision.
346 a5cc42ff Carles Marti
    @return: list of ase.Atoms for the adsorbate-surface structures
347 a5cc42ff Carles Marti
    """
348 bb55f47c Carles Marti
    from ase.neighborlist import natural_cutoffs, neighbor_list
349 f3d1e601 Carles Marti
    surf_ads_list = []
350 f3d1e601 Carles Marti
    sites_coords = get_atom_coords(surf, sites)
351 5fb01677 Carles Marti
    if min_coll_height is not False:
352 5fb01677 Carles Marti
        surf_cutoffs = natural_cutoffs(surf, mult=coll_coeff)
353 5fb01677 Carles Marti
        surf_nghbs = len(neighbor_list("i", surf, surf_cutoffs))
354 5fb01677 Carles Marti
    else:
355 5fb01677 Carles Marti
        surf_nghbs = 0
356 f3d1e601 Carles Marti
    for conf in conf_list:
357 bb55f47c Carles Marti
        molec_ctr_coords = get_atom_coords(conf, molec_ctrs)
358 f3d1e601 Carles Marti
        molec_neigh_coords = get_atom_coords(conf, neigh_ctrs)
359 5fb01677 Carles Marti
        if min_coll_height is not False:
360 5fb01677 Carles Marti
            conf_cutoffs = natural_cutoffs(conf, mult=coll_coeff)
361 5fb01677 Carles Marti
            molec_nghbs = len(neighbor_list("i", conf, conf_cutoffs))
362 5fb01677 Carles Marti
        else:
363 5fb01677 Carles Marti
            molec_nghbs = 0
364 f3d1e601 Carles Marti
        for site in sites_coords:
365 bb55f47c Carles Marti
            for ctr in molec_ctr_coords:
366 f3d1e601 Carles Marti
                if algo == 'euler':
367 bb55f47c Carles Marti
                    surf_ads_list.extend(ads_euler(conf, surf, ctr, site,
368 5fb01677 Carles Marti
                                                   num_pts, min_coll_height,
369 bb55f47c Carles Marti
                                                   coll_coeff, norm_vect,
370 bb55f47c Carles Marti
                                                   surf_nghbs, molec_nghbs))
371 f3d1e601 Carles Marti
                elif algo == 'chemcat':
372 bb55f47c Carles Marti
                    surf_ads_list.extend(ads_chemcat(site, ctr, num_pts))
373 f3d1e601 Carles Marti
    return surf_ads_list
374 f3d1e601 Carles Marti
375 f3d1e601 Carles Marti
376 4614bb6a Carles
def run_screening(inp_vars):
377 e07c09eb Carles
    """Carry out the screening of adsorbate coordinates on a surface
378 e07c09eb Carles

379 e07c09eb Carles
    @param inp_vars: Calculation parameters from input file.
380 e07c09eb Carles
    """
381 e07c09eb Carles
    import os
382 bfe93f0d Carles Marti
    from modules.formats import read_coords, adapt_format
383 f3d1e601 Carles Marti
    from modules.calculation import run_calc
384 e07c09eb Carles
385 e07c09eb Carles
    if not os.path.isdir("isolated"):
386 e07c09eb Carles
        err = "'isolated' directory not found. It is needed in order to carry "
387 e07c09eb Carles
        "out the screening of structures to be adsorbed"
388 e07c09eb Carles
        logger.error(err)
389 e07c09eb Carles
        raise ValueError(err)
390 e07c09eb Carles
391 bfe93f0d Carles Marti
    conf_list = read_coords(inp_vars['code'], 'isolated', 'ase')
392 695dcff8 Carles Marti
    logger.info(f"Found {len(conf_list)} structures of isolated conformers.")
393 bfe93f0d Carles Marti
    selected_confs = select_confs(conf_list, inp_vars['select_magns'],
394 bfe93f0d Carles Marti
                                  inp_vars['confs_per_magn'],
395 bfe93f0d Carles Marti
                                  inp_vars['code'])
396 f3d1e601 Carles Marti
    surf = adapt_format('ase', inp_vars['surf_file'])
397 bfe93f0d Carles Marti
    surf_ads_list = adsorb_confs(selected_confs, surf,
398 bfe93f0d Carles Marti
                                 inp_vars['molec_ads_ctrs'], inp_vars['sites'],
399 bfe93f0d Carles Marti
                                 inp_vars['ads_algo'],
400 f3d1e601 Carles Marti
                                 inp_vars['sample_points_per_angle'],
401 1d22a086 Carles Marti
                                 inp_vars['molec_neigh_ctrs'],
402 dadc6016 Carles Marti
                                 inp_vars['surf_norm_vect'],
403 5fb01677 Carles Marti
                                 inp_vars['min_coll_height'],
404 bb55f47c Carles Marti
                                 inp_vars['collision_threshold'])
405 bfe93f0d Carles Marti
    logger.info(f'Generated {len(surf_ads_list)} adsorbate-surface atomic '
406 695dcff8 Carles Marti
                f'configurations, to carry out a calculation of.')
407 f3d1e601 Carles Marti
    run_calc('screening', inp_vars, surf_ads_list)