|
49 | 49 | import math |
50 | 50 | import os.path |
51 | 51 | import re |
| 52 | +import itertools |
52 | 53 |
|
53 | 54 | try: |
54 | 55 | import cairocffi as cairo |
@@ -97,6 +98,12 @@ def create_new_surface(file_format, target=None, width=1024, height=768): |
97 | 98 |
|
98 | 99 | ################################################################################ |
99 | 100 |
|
| 101 | +class AdsorbateDrawingError(Exception): |
| 102 | + """ |
| 103 | + When something goes wrong trying to draw an adsorbate. |
| 104 | + """ |
| 105 | + pass |
| 106 | + |
100 | 107 | class MoleculeDrawer(object): |
101 | 108 | """ |
102 | 109 | This class provides functionality for drawing the skeletal formula of |
@@ -209,9 +216,13 @@ def draw(self, molecule, file_format, target=None): |
209 | 216 | # bugs with RDKit |
210 | 217 | old_bond_dictionary = self._make_single_bonds() |
211 | 218 | if molecule.contains_surface_site(): |
212 | | - self._connect_surface_sites() |
213 | | - self._generate_coordinates() |
214 | | - self._disconnect_surface_sites() |
| 219 | + try: |
| 220 | + self._connect_surface_sites() |
| 221 | + self._generate_coordinates() |
| 222 | + self._disconnect_surface_sites() |
| 223 | + except AdsorbateDrawingError as e: |
| 224 | + self._disconnect_surface_sites() |
| 225 | + self._generate_coordinates(fix_surface_sites=False) |
215 | 226 | else: |
216 | 227 | self._generate_coordinates() |
217 | 228 | self._replace_bonds(old_bond_dictionary) |
@@ -329,11 +340,13 @@ def _find_ring_groups(self): |
329 | 340 | if not found: |
330 | 341 | self.ringSystems.append([cycle]) |
331 | 342 |
|
332 | | - def _generate_coordinates(self): |
| 343 | + def _generate_coordinates(self, fix_surface_sites=True): |
333 | 344 | """ |
334 | 345 | Generate the 2D coordinates to be used when drawing the current |
335 | 346 | molecule. The function uses rdKits 2D coordinate generation. |
336 | 347 | Updates the self.coordinates Array in place. |
| 348 | + If `fix_surface_sites` is True, then the surface sites are placed |
| 349 | + at the bottom of the molecule. |
337 | 350 | """ |
338 | 351 | atoms = self.molecule.atoms |
339 | 352 | natoms = len(atoms) |
@@ -464,7 +477,7 @@ def _generate_coordinates(self): |
464 | 477 | coordinates[:, 1] = temp[:, 0] |
465 | 478 |
|
466 | 479 | # For surface species |
467 | | - if self.molecule.contains_surface_site(): |
| 480 | + if fix_surface_sites and self.molecule.contains_surface_site(): |
468 | 481 | if len(self.molecule.atoms) == 1: |
469 | 482 | return coordinates |
470 | 483 | sites = [atom for atom in self.molecule.atoms if atom.is_surface_site()] |
@@ -503,10 +516,16 @@ def _generate_coordinates(self): |
503 | 516 | x = coordinates[adatom_indices, 0] |
504 | 517 | y = coordinates[adatom_indices, 1] |
505 | 518 | site_y_pos = min(min(y) - 0.8, min(coordinates[not_site_indices, 1]) - 0.5) |
| 519 | + if max(y) - site_y_pos > 1.5: |
| 520 | + raise AdsorbateDrawingError("Adsorbate bond too long") |
| 521 | + for x1, x2 in itertools.combinations(x, 2): |
| 522 | + if abs(x1 - x2) < 0.2: |
| 523 | + raise AdsorbateDrawingError("Sites overlapping") |
506 | 524 | for site, x_pos in zip(sites, x): |
507 | 525 | index = atoms.index(site) |
508 | 526 | coordinates[index, 1] = site_y_pos |
509 | 527 | coordinates[index, 0] = x_pos |
| 528 | + |
510 | 529 | else: |
511 | 530 | # more than 4 surface sites? leave them alone |
512 | 531 | pass |
|
0 commit comments