220 |
220 |
return False
|
221 |
221 |
else:
|
222 |
222 |
slab_molec_cutoffs = natural_cutoffs(slab_molec, mult=coll_coeff)
|
223 |
|
slab_molec_nghbs = len(neighbor_list("i", slab_molec, slab_molec_cutoffs))
|
|
223 |
slab_molec_nghbs = len(
|
|
224 |
neighbor_list("i", slab_molec, slab_molec_cutoffs))
|
224 |
225 |
if slab_molec_nghbs > nn_slab + nn_molec:
|
225 |
226 |
return True
|
226 |
227 |
else:
|
227 |
228 |
return False
|
228 |
229 |
|
229 |
230 |
|
|
231 |
def dissociate_h(slab_molec_orig, h_idx, num_atoms_slab, neigh_cutoff=1):
|
|
232 |
# TODO rethink
|
|
233 |
"""Moves a H atom from a molecule to the surface if the distance is short
|
|
234 |
enough.
|
|
235 |
|
|
236 |
@param slab_molec_orig: The ase.Atoms object of the system adsorbate-slab.
|
|
237 |
@param h_idx: The index of the hydrogen atom to carry out adsorption of.
|
|
238 |
@param num_atoms_slab: The number of atoms of the slab without adsorbate.
|
|
239 |
@param neigh_cutoff: half the maximum distance between the surface and the
|
|
240 |
H for it to carry out dissociation.
|
|
241 |
@return: An ase.Atoms object of the system adsorbate-surface with H
|
|
242 |
"""
|
|
243 |
from copy import deepcopy
|
|
244 |
from ase.neighborlist import NeighborList
|
|
245 |
slab_molec = deepcopy(slab_molec_orig)
|
|
246 |
cutoffs = len(slab_molec) * [neigh_cutoff]
|
|
247 |
nl = NeighborList(cutoffs, self_interaction=False, bothways=True)
|
|
248 |
nl.update(slab_molec)
|
|
249 |
surf_h_vect = np.array([np.infty] * 3)
|
|
250 |
for neigh_idx in nl.get_neighbors(h_idx)[0]:
|
|
251 |
if neigh_idx < num_atoms_slab:
|
|
252 |
dist = np.linalg.norm(slab_molec[neigh_idx].position -
|
|
253 |
slab_molec[h_idx].position)
|
|
254 |
if dist < np.linalg.norm(surf_h_vect):
|
|
255 |
surf_h_vect = slab_molec[neigh_idx].position \
|
|
256 |
- slab_molec[h_idx].position
|
|
257 |
if np.linalg.norm(surf_h_vect) != np.infty:
|
|
258 |
trans_vect = surf_h_vect - surf_h_vect / np.linalg.norm(surf_h_vect)
|
|
259 |
slab_molec[h_idx].position = slab_molec[h_idx].position + trans_vect
|
|
260 |
return slab_molec
|
|
261 |
|
|
262 |
|
|
263 |
def dissociation(slab_molec, disso_atoms, num_atoms_slab):
|
|
264 |
# TODO rethink
|
|
265 |
# TODO multiple dissociation
|
|
266 |
"""Decides which H atoms to dissociate according to a list of atoms.
|
|
267 |
|
|
268 |
Given a list of chemical symbols or atom indices it checks for every atom
|
|
269 |
or any of its neighbor if it's a H and calls dissociate_h to try to carry
|
|
270 |
out dissociation of that H. For atom indices, it checks both whether
|
|
271 |
the atom index or its neighbors are H, for chemical symbols, it only checks
|
|
272 |
if there is a neighbor H.
|
|
273 |
@param slab_molec: The ase.Atoms object of the system adsorbate-slab.
|
|
274 |
@param disso_atoms: The indices or chemical symbols of the atoms
|
|
275 |
@param num_atoms_slab:
|
|
276 |
@return:
|
|
277 |
"""
|
|
278 |
from ase.neighborlist import natural_cutoffs, NeighborList
|
|
279 |
molec = slab_molec[num_atoms_slab:]
|
|
280 |
cutoffs = natural_cutoffs(molec)
|
|
281 |
nl = NeighborList(cutoffs, self_interaction=False, bothways=True)
|
|
282 |
nl.update(molec)
|
|
283 |
disso_structs = []
|
|
284 |
for el in disso_atoms:
|
|
285 |
if isinstance(el, int):
|
|
286 |
if molec[el].symbol == 'H':
|
|
287 |
disso_struct = dissociate_h(slab_molec, el + num_atoms_slab,
|
|
288 |
num_atoms_slab)
|
|
289 |
if disso_struct is not None:
|
|
290 |
disso_structs.append(disso_struct)
|
|
291 |
else:
|
|
292 |
for neigh_idx in nl.get_neighbors(el)[0]:
|
|
293 |
if molec[neigh_idx].symbol == 'H':
|
|
294 |
disso_struct = dissociate_h(slab_molec, neigh_idx +
|
|
295 |
num_atoms_slab,
|
|
296 |
num_atoms_slab)
|
|
297 |
if disso_struct is not None:
|
|
298 |
disso_structs.append(disso_struct)
|
|
299 |
else:
|
|
300 |
for atom in molec:
|
|
301 |
if atom.symbol.lower() == el.lower():
|
|
302 |
for neigh_idx in nl.get_neighbors(atom.index)[0]:
|
|
303 |
if molec[neigh_idx].symbol == 'H':
|
|
304 |
disso_struct = dissociate_h(slab_molec, neigh_idx \
|
|
305 |
+ num_atoms_slab,
|
|
306 |
num_atoms_slab)
|
|
307 |
if disso_struct is not None:
|
|
308 |
disso_structs.append(disso_struct)
|
|
309 |
return disso_structs
|
|
310 |
|
|
311 |
|
230 |
312 |
def correct_coll(molec, slab, ctr_coord, site_coord, num_pts,
|
231 |
313 |
min_coll_height, norm_vect, slab_nghbs, molec_nghbs,
|
232 |
314 |
coll_coeff):
|
... | ... | |
273 |
355 |
|
274 |
356 |
|
275 |
357 |
def ads_euler(orig_molec, slab, ctr_coord, site_coord, num_pts,
|
276 |
|
min_coll_height, coll_coeff, norm_vect, slab_nghbs, molec_nghbs):
|
|
358 |
min_coll_height, coll_coeff, norm_vect, slab_nghbs, molec_nghbs,
|
|
359 |
disso_atoms):
|
277 |
360 |
"""Generates adsorbate-surface structures by sampling over Euler angles.
|
278 |
361 |
|
279 |
362 |
This function generates a number of adsorbate-surface structures at
|
... | ... | |
293 |
376 |
@param norm_vect: The vector perpendicular to the surface.
|
294 |
377 |
@param slab_nghbs: Number of neigbors in the surface.
|
295 |
378 |
@param molec_nghbs: Number of neighbors in the molecule.
|
|
379 |
@param disso_atoms: List of atom types or atom numbers to try to dissociate.
|
296 |
380 |
@return: list of ase.Atoms object conatining all the orientations of a given
|
297 |
381 |
conformer
|
298 |
382 |
"""
|
... | ... | |
314 |
398 |
coll_coeff)
|
315 |
399 |
if not collision:
|
316 |
400 |
slab_ads_list.append(slab_molec)
|
|
401 |
slab_ads_list.extend(dissociation(slab_molec, disso_atoms,
|
|
402 |
len(slab)))
|
317 |
403 |
|
318 |
404 |
return slab_ads_list
|
319 |
405 |
|
... | ... | |
323 |
409 |
|
324 |
410 |
|
325 |
411 |
def adsorb_confs(conf_list, surf, molec_ctrs, sites, algo, num_pts, neigh_ctrs,
|
326 |
|
norm_vect, min_coll_height, coll_coeff):
|
|
412 |
norm_vect, min_coll_height, coll_coeff, disso_atoms):
|
327 |
413 |
"""Generates a number of adsorbate-surface structure coordinates.
|
328 |
414 |
|
329 |
415 |
Given a list of conformers, a surface, a list of atom indices (or list of
|
... | ... | |
343 |
429 |
@param coll_coeff: The coefficient that multiplies the covalent radius of
|
344 |
430 |
atoms resulting in a distance that two atoms being closer to that is
|
345 |
431 |
considered as atomic collision.
|
|
432 |
@param disso_atoms: List of atom types or atom numbers to try to dissociate.
|
346 |
433 |
@return: list of ase.Atoms for the adsorbate-surface structures
|
347 |
434 |
"""
|
348 |
435 |
from ase.neighborlist import natural_cutoffs, neighbor_list
|
... | ... | |
367 |
454 |
surf_ads_list.extend(ads_euler(conf, surf, ctr, site,
|
368 |
455 |
num_pts, min_coll_height,
|
369 |
456 |
coll_coeff, norm_vect,
|
370 |
|
surf_nghbs, molec_nghbs))
|
|
457 |
surf_nghbs, molec_nghbs,
|
|
458 |
disso_atoms))
|
371 |
459 |
elif algo == 'chemcat':
|
372 |
460 |
surf_ads_list.extend(ads_chemcat(site, ctr, num_pts))
|
373 |
461 |
return surf_ads_list
|
... | ... | |
401 |
489 |
inp_vars['molec_neigh_ctrs'],
|
402 |
490 |
inp_vars['surf_norm_vect'],
|
403 |
491 |
inp_vars['min_coll_height'],
|
404 |
|
inp_vars['collision_threshold'])
|
|
492 |
inp_vars['collision_threshold'],
|
|
493 |
inp_vars['disso_atoms'])
|
405 |
494 |
logger.info(f'Generated {len(surf_ads_list)} adsorbate-surface atomic '
|
406 |
495 |
f'configurations, to carry out a calculation of.')
|
407 |
496 |
run_calc('screening', inp_vars, surf_ads_list)
|