jibsn commited on
Commit
009ae32
·
verified ·
1 Parent(s): 7d64d36

Upload utils.py

Browse files
Files changed (1) hide show
  1. utils.py +527 -0
utils.py ADDED
@@ -0,0 +1,527 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import json
3
+ import math
4
+ import numpy as np
5
+ import pandas as pd
6
+ import torch
7
+ from scipy.spatial import cKDTree
8
+ from rdkit import Chem
9
+ from rdkit.Chem import RWMol
10
+ from rdkit.Chem import Draw, AllChem
11
+ from rdkit.Chem import rdDepictor
12
+ import matplotlib.pyplot as plt
13
+ import re
14
+
15
+ def output_to_smiles(output,idx_to_labels,bond_labels,result):
16
+
17
+ x_center = (output["boxes"][:, 0] + output["boxes"][:, 2]) / 2
18
+ y_center = (output["boxes"][:, 1] + output["boxes"][:, 3]) / 2
19
+
20
+ center_coords = torch.stack((x_center, y_center), dim=1)
21
+
22
+ output = {'bbox': output["boxes"].to("cpu").numpy(),
23
+ 'bbox_centers': center_coords.to("cpu").numpy(),
24
+ 'scores': output["scores"].to("cpu").numpy(),
25
+ 'pred_classes': output["labels"].to("cpu").numpy()}
26
+
27
+
28
+ atoms_list, bonds_list = bbox_to_graph_with_charge(output,
29
+ idx_to_labels=idx_to_labels,
30
+ bond_labels=bond_labels,
31
+ result=result)
32
+ #NOTE print
33
+ return mol_from_graph_with_chiral(atoms_list, bonds_list)
34
+
35
+
36
+ def bbox_to_graph(output, idx_to_labels, bond_labels,result):
37
+
38
+ # calculate atoms mask (pred classes that are atoms/bonds)
39
+ atoms_mask = np.array([True if ins not in bond_labels else False for ins in output['pred_classes']])
40
+
41
+ # get atom list
42
+ atoms_list = [idx_to_labels[a] for a in output['pred_classes'][atoms_mask]]
43
+
44
+ # if len(result) !=0 and 'other' in atoms_list:
45
+ # new_list = []
46
+ # replace_index = 0
47
+ # for item in atoms_list:
48
+ # if item == 'other':
49
+ # new_list.append(result[replace_index % len(result)])
50
+ # replace_index += 1
51
+ # else:
52
+ # new_list.append(item)
53
+ # atoms_list = new_list
54
+
55
+ atoms_list = pd.DataFrame({'atom': atoms_list,
56
+ 'x': output['bbox_centers'][atoms_mask, 0],
57
+ 'y': output['bbox_centers'][atoms_mask, 1]})
58
+
59
+ # in case atoms with sign gets detected two times, keep only the signed one
60
+ for idx, row in atoms_list.iterrows():
61
+ if row.atom[-1] != '0':
62
+ if row.atom[-2] != '-':#assume charge value -9~9
63
+ overlapping = atoms_list[atoms_list.atom.str.startswith(row.atom[:-1])]
64
+ else:
65
+ overlapping = atoms_list[atoms_list.atom.str.startswith(row.atom[:-2])]
66
+
67
+ kdt = cKDTree(overlapping[['x', 'y']])
68
+ dists, neighbours = kdt.query([row.x, row.y], k=2)
69
+ if dists[1] < 7:
70
+ atoms_list.drop(overlapping.index[neighbours[1]], axis=0, inplace=True)
71
+
72
+ bonds_list = []
73
+
74
+ # get bonds
75
+ for bbox, bond_type, score in zip(output['bbox'][np.logical_not(atoms_mask)],
76
+ output['pred_classes'][np.logical_not(atoms_mask)],
77
+ output['scores'][np.logical_not(atoms_mask)]):
78
+
79
+ # if idx_to_labels[bond_type] == 'SINGLE':
80
+ if idx_to_labels[bond_type] in ['-','SINGLE', 'NONE', 'ENDUPRIGHT', 'BEGINWEDGE', 'BEGINDASH', 'ENDDOWNRIGHT']:
81
+ _margin = 5
82
+ else:
83
+ _margin = 8
84
+
85
+ # anchor positions are _margin distances away from the corners of the bbox.
86
+ anchor_positions = (bbox + [_margin, _margin, -_margin, -_margin]).reshape([2, -1])
87
+ oposite_anchor_positions = anchor_positions.copy()
88
+ oposite_anchor_positions[:, 1] = oposite_anchor_positions[:, 1][::-1]
89
+
90
+ # Upper left, lower right, lower left, upper right
91
+ # 0 - 1, 2 - 3
92
+ anchor_positions = np.concatenate([anchor_positions, oposite_anchor_positions])
93
+
94
+ # get the closest point to every corner
95
+ atoms_pos = atoms_list[['x', 'y']].values
96
+ kdt = cKDTree(atoms_pos)
97
+ dists, neighbours = kdt.query(anchor_positions, k=1)
98
+
99
+ # check corner with the smallest total distance to closest atoms
100
+ if np.argmin((dists[0] + dists[1], dists[2] + dists[3])) == 0:
101
+ # visualize setup
102
+ begin_idx, end_idx = neighbours[:2]
103
+ else:
104
+ # visualize setup
105
+ begin_idx, end_idx = neighbours[2:]
106
+
107
+ #NOTE this proces may lead self-bonding for one atom
108
+ if begin_idx != end_idx:# avoid self-bond
109
+ bonds_list.append((begin_idx, end_idx, idx_to_labels[bond_type], idx_to_labels[bond_type], score))
110
+ else:
111
+ continue
112
+ # return atoms_list.atom.values.tolist(), bonds_list
113
+ return atoms_list, bonds_list
114
+
115
+
116
+ def calculate_distance(coord1, coord2):
117
+ # Calculate Euclidean distance between two coordinates
118
+ return math.sqrt((coord1[0] - coord2[0])**2 + (coord1[1] - coord2[1])**2)
119
+
120
+ def assemble_atoms_with_charges(atom_list, charge_list):
121
+ used_charge_indices=set()
122
+ atom_list['atom'] = atom_list['atom'] + '0'
123
+ kdt = cKDTree(atom_list[['x','y']])
124
+ for i, charge in charge_list.iterrows():
125
+ if i in used_charge_indices:
126
+ continue
127
+ charge_=charge['charge']
128
+ if charge_=='1':charge_='+'
129
+ dist, idx_atom=kdt.query([charge_list.x[i],charge_list.y[i]], k=1)
130
+ atom_str=atom_list.loc[idx_atom,'atom']
131
+ atom_ = re.findall(r'[A-Za-z]+', atom_str)[0] + charge_
132
+ atom_list.loc[idx_atom,'atom']=atom_
133
+
134
+ return atom_list
135
+
136
+
137
+
138
+ def assemble_atoms_with_charges2(atom_list, charge_list, max_distance=10):
139
+ used_charge_indices = set()
140
+
141
+ for idx, atom in atom_list.iterrows():
142
+ atom_coord = atom['x'],atom['y']
143
+ atom_label = atom['atom']
144
+ closest_charge = None
145
+ min_distance = float('inf')
146
+
147
+ for i, charge in charge_list.iterrows():
148
+ if i in used_charge_indices:
149
+ continue
150
+
151
+ charge_coord = charge['x'],charge['y']
152
+ charge_label = charge['charge']
153
+
154
+ distance = calculate_distance(atom_coord, charge_coord)
155
+ #NOTE how t determin this max_distance, dependent on image size??
156
+ if distance <= max_distance and distance < min_distance:
157
+ closest_charge = charge
158
+ min_distance = distance
159
+
160
+
161
+ if closest_charge is not None:
162
+ if closest_charge['charge'] == '1':
163
+ charge_ = '+'
164
+ else:
165
+ charge_ = closest_charge['charge']
166
+ atom_ = atom['atom'] + charge_
167
+
168
+ # atom['atom'] = atom_
169
+ atom_list.loc[idx,'atom'] = atom_
170
+ used_charge_indices.add(tuple(charge))
171
+
172
+ else:
173
+ # atom['atom'] = atom['atom'] + '0'
174
+ atom_list.loc[idx,'atom'] = atom['atom'] + '0'
175
+
176
+ return atom_list
177
+
178
+
179
+
180
+ def bbox_to_graph_with_charge(output, idx_to_labels, bond_labels,result):
181
+
182
+ bond_labels_pre=bond_labels
183
+ charge_labels = [18,19,20,21,22]#make influence
184
+
185
+ atoms_mask = np.array([True if ins not in bond_labels and ins not in charge_labels else False for ins in output['pred_classes']])
186
+ atoms_list = [idx_to_labels[a] for a in output['pred_classes'][atoms_mask]]
187
+ atoms_list = pd.DataFrame({'atom': atoms_list,
188
+ 'x': output['bbox_centers'][atoms_mask, 0],
189
+ 'y': output['bbox_centers'][atoms_mask, 1],
190
+ 'bbox': output['bbox'][atoms_mask].tolist() ,#need this for */other converting
191
+ })
192
+
193
+ charge_mask = np.array([True if ins in charge_labels else False for ins in output['pred_classes']])
194
+ charge_list = [idx_to_labels[a] for a in output['pred_classes'][charge_mask]]
195
+ charge_list = pd.DataFrame({'charge': charge_list,
196
+ 'x': output['bbox_centers'][charge_mask, 0],
197
+ 'y': output['bbox_centers'][charge_mask, 1]})
198
+
199
+ # print(charge_list,'\n@bbox_to_graph_with_charge')
200
+ if len(charge_list) > 0:
201
+ atoms_list = assemble_atoms_with_charges(atoms_list,charge_list)
202
+ else:#Note Most mols are not formal charged
203
+ atoms_list['atom'] = atoms_list['atom']+'0'
204
+ # print(atoms_list,"after @@assemble_atoms_with_charges ")
205
+ # in case atoms with sign gets detected two times, keep only the signed one
206
+ for idx, row in atoms_list.iterrows():
207
+ if row.atom[-1] != '0':
208
+ try:
209
+ if row.atom[-2] != '-':#assume charge value -9~9
210
+ overlapping = atoms_list[atoms_list.atom.str.startswith(row.atom[:-1])]
211
+ except Exception as e:
212
+ print(row.atom,"@row.atom")
213
+ print(e)
214
+ else:
215
+ overlapping = atoms_list[atoms_list.atom.str.startswith(row.atom[:-2])]
216
+
217
+ kdt = cKDTree(overlapping[['x', 'y']])
218
+ dists, neighbours = kdt.query([row.x, row.y], k=2)
219
+ if dists[1] < 7:
220
+ atoms_list.drop(overlapping.index[neighbours[1]], axis=0, inplace=True)
221
+
222
+ bonds_list = []
223
+ # get bonds
224
+ # bond_mask=np.logical_not(np.logical_not(atoms_mask) | np.logical_not(charge_mask))
225
+ bond_mask=np.logical_not(atoms_mask) & np.logical_not(charge_mask)
226
+ for bbox, bond_type, score in zip(output['bbox'][bond_mask], #NOTE also including the charge part
227
+ output['pred_classes'][bond_mask],
228
+ output['scores'][bond_mask]):
229
+
230
+ # if idx_to_labels[bond_type] == 'SINGLE':
231
+ if idx_to_labels[bond_type] in ['-','SINGLE', 'NONE', 'ENDUPRIGHT', 'BEGINWEDGE', 'BEGINDASH', 'ENDDOWNRIGHT']:
232
+ _margin = 5
233
+ else:
234
+ _margin = 8
235
+
236
+ # anchor positions are _margin distances away from the corners of the bbox.
237
+ anchor_positions = (bbox + [_margin, _margin, -_margin, -_margin]).reshape([2, -1])
238
+ oposite_anchor_positions = anchor_positions.copy()
239
+ oposite_anchor_positions[:, 1] = oposite_anchor_positions[:, 1][::-1]
240
+
241
+ # Upper left, lower right, lower left, upper right
242
+ # 0 - 1, 2 - 3
243
+ anchor_positions = np.concatenate([anchor_positions, oposite_anchor_positions])
244
+
245
+ # get the closest point to every corner
246
+ atoms_pos = atoms_list[['x', 'y']].values
247
+ kdt = cKDTree(atoms_pos)
248
+ dists, neighbours = kdt.query(anchor_positions, k=1)
249
+
250
+ # check corner with the smallest total distance to closest atoms
251
+ if np.argmin((dists[0] + dists[1], dists[2] + dists[3])) == 0:
252
+ # visualize setup
253
+ begin_idx, end_idx = neighbours[:2]
254
+ else:
255
+ # visualize setup
256
+ begin_idx, end_idx = neighbours[2:]
257
+
258
+ #NOTE this proces may lead self-bonding for one atom
259
+ if begin_idx != end_idx:
260
+ if bond_type in bond_labels:# avoid self-bond
261
+ bonds_list.append((begin_idx, end_idx, idx_to_labels[bond_type], idx_to_labels[bond_type], score))
262
+ else:
263
+ print(f'this box may be charges box not bonds {[bbox, bond_type, score ]}')
264
+ else:
265
+ continue
266
+ # return atoms_list.atom.values.tolist(), bonds_list
267
+ # print(f"@box2graph: atom,bond nums:: {len(atoms_list)}, {len(bonds_list)}")
268
+ return atoms_list, bonds_list#dataframe, list
269
+
270
+
271
+
272
+ def mol_from_graph_with_chiral(atoms_list, bonds):
273
+
274
+ mol = RWMol()
275
+ nodes_idx = {}
276
+ atoms = atoms_list.atom.values.tolist()
277
+ coords = [(row['x'], 300-row['y'], 0) for index, row in atoms_list.iterrows()]
278
+ coords = tuple(coords)
279
+ coords = tuple(tuple(num / 100 for num in sub_tuple) for sub_tuple in coords)
280
+
281
+ # points = [(row['x'], 300-row['y']) for index, row in atoms_list.iterrows()]
282
+ # plt.figure(figsize=(6, 6))
283
+ # for point in points:
284
+ # plt.scatter(point[0], point[1], color='blue')
285
+ # plt.xlim(0, 300)
286
+ # plt.ylim(300, 0)
287
+ # plt.gca().set_aspect('equal', adjustable='box')
288
+ # plt.savefig('/home/jovyan/rt-detr/output/test/plot.png')
289
+
290
+
291
+ for i in range(len(bonds)):
292
+ idx_1, idx_2, bond_type, bond_dir, score = bonds[i]
293
+ if bond_type in ['-', 'NONE', 'ENDUPRIGHT', 'BEGINWEDGE', 'BEGINDASH', 'ENDDOWNRIGHT']:
294
+ bonds[i] = (idx_1, idx_2, 'SINGLE', bond_dir, score)
295
+ elif bond_type == '=':
296
+ bonds[i] = (idx_1, idx_2, 'DOUBLE', bond_dir, score)
297
+ elif bond_type == '#':
298
+ bonds[i] = (idx_1, idx_2, 'TRIPLE', bond_dir, score)
299
+
300
+
301
+
302
+ bond_types = {'SINGLE': Chem.rdchem.BondType.SINGLE,
303
+ 'DOUBLE': Chem.rdchem.BondType.DOUBLE,
304
+ 'TRIPLE': Chem.rdchem.BondType.TRIPLE,
305
+ 'AROMATIC': Chem.rdchem.BondType.AROMATIC}
306
+
307
+ bond_dirs = {'NONE': Chem.rdchem.BondDir.NONE,
308
+ 'ENDUPRIGHT': Chem.rdchem.BondDir.ENDUPRIGHT,
309
+ 'BEGINWEDGE': Chem.rdchem.BondDir.BEGINWEDGE,
310
+ 'BEGINDASH': Chem.rdchem.BondDir.BEGINDASH,
311
+ 'ENDDOWNRIGHT': Chem.rdchem.BondDir.ENDDOWNRIGHT,}
312
+
313
+
314
+
315
+ try:
316
+ # add nodes
317
+ s10=[str(x) for x in range(10)]
318
+ for idx, node in enumerate(atoms):#NOTE no formal charge will be X0 here
319
+ # node=node.split(' ')
320
+ # if ('0' in node) or ('1' in node):
321
+ if 'other' in node:
322
+ a='*'
323
+ if '-' in node or '+' in node:
324
+ fc = int(node[-2:])
325
+ else:
326
+ fc = int(node[-1])
327
+ elif node[-1] in s10:
328
+ if '-' in node or '+' in node:
329
+ a = node[:-2]
330
+ fc = int(node[-2:])
331
+ else:
332
+ a = node[:-1]
333
+ fc = int(node[-1])
334
+ elif node[-1]=='+':
335
+ a = node[:-1]
336
+ fc = 1
337
+ elif node[-1]=='-':
338
+ a = node[:-1]
339
+ fc = -1
340
+
341
+ # elif ('-1' in node) or ('-' in node):
342
+ # a = node[:-2]
343
+ # fc = int(node[-2])
344
+ else:
345
+ a = node
346
+ fc = 0
347
+
348
+ ad = Chem.Atom(a)
349
+ ad.SetFormalCharge(fc)
350
+
351
+ atom_idx = mol.AddAtom(ad)
352
+ nodes_idx[idx] = atom_idx
353
+
354
+ # add bonds
355
+ existing_bonds = set()
356
+ for idx_1, idx_2, bond_type, bond_dir, score in bonds:
357
+ if (idx_1 in nodes_idx) and (idx_2 in nodes_idx):
358
+ if (idx_1, idx_2) not in existing_bonds and (idx_2, idx_1) not in existing_bonds:
359
+ try:
360
+ mol.AddBond(nodes_idx[idx_1], nodes_idx[idx_2], bond_types[bond_type])
361
+ except Exception as e:
362
+ print([idx_1, idx_2, bond_type, bond_dir, score],f"erro @add bonds ")
363
+ print(f"erro@add existing_bonds: {e}\n{bonds}")
364
+ continue
365
+ existing_bonds.add((idx_1, idx_2))
366
+
367
+ if Chem.MolFromSmiles(Chem.MolToSmiles(mol.GetMol())):
368
+ prev_mol = copy.deepcopy(mol)
369
+ else:
370
+ mol = copy.deepcopy(prev_mol)
371
+
372
+
373
+ chiral_centers = Chem.FindMolChiralCenters(
374
+ mol, includeUnassigned=True, includeCIP=False, useLegacyImplementation=False)
375
+ chiral_center_ids = [idx for idx, _ in chiral_centers]
376
+
377
+ for id in chiral_center_ids:
378
+ for index, tup in enumerate(bonds):
379
+ if id == tup[1]:
380
+ new_tup = tuple([tup[1], tup[0], tup[2], tup[3], tup[4]])#idx_1, idx_2, bond_type, bond_dir, score
381
+ bonds[index] = new_tup
382
+ mol.RemoveBond(int(tup[0]), int(tup[1]))
383
+ try:
384
+ mol.AddBond(int(tup[1]), int(tup[0]), bond_types[tup[2]])
385
+ except Exception as e:
386
+ print( index, tup, id)
387
+ print(f"bonds: {bonds}")
388
+ print(f"erro@chiral_center_ids: {e}")
389
+ mol = mol.GetMol()
390
+
391
+ # if 'S0' in atoms:
392
+ # bonds_ = [[row[0], row[1], row[3]] for row in bonds]
393
+
394
+ # n_atoms=len(atoms)
395
+ # for i in chiral_center_ids:
396
+ # for j in range(n_atoms):
397
+
398
+ # if [i,j,'BEGINWEDGE'] in bonds_:
399
+ # mol.GetBondBetweenAtoms(i, j).SetBondDir(bond_dirs['BEGINWEDGE'])
400
+ # elif [i,j,'BEGINDASH'] in bonds_:
401
+ # mol.GetBondBetweenAtoms(i, j).SetBondDir(bond_dirs['BEGINDASH'])
402
+
403
+ # Chem.SanitizeMol(mol)
404
+ # AllChem.Compute2DCoords(mol)
405
+ # Chem.AssignChiralTypesFromBondDirs(mol)
406
+ # Chem.AssignStereochemistry(mol, force=True, cleanIt=True)
407
+
408
+ # else:
409
+
410
+ mol.RemoveAllConformers()
411
+ conf = Chem.Conformer(mol.GetNumAtoms())
412
+ conf.Set3D(True)
413
+ for i, (x, y, z) in enumerate(coords):
414
+ conf.SetAtomPosition(i, (x, y, z))
415
+ mol.AddConformer(conf)
416
+ # Chem.SanitizeMol(mol)
417
+ Chem.AssignStereochemistryFrom3D(mol)
418
+
419
+ bonds_ = [[row[0], row[1], row[3]] for row in bonds]
420
+
421
+ n_atoms=len(atoms)
422
+ for i in chiral_center_ids:
423
+ for j in range(n_atoms):
424
+ if [i,j,'BEGINWEDGE'] in bonds_:
425
+ mol.GetBondBetweenAtoms(i, j).SetBondDir(bond_dirs['BEGINWEDGE'])
426
+ elif [i,j,'BEGINDASH'] in bonds_:
427
+ mol.GetBondBetweenAtoms(i, j).SetBondDir(bond_dirs['BEGINDASH'])
428
+
429
+ Chem.SanitizeMol(mol)
430
+ Chem.DetectBondStereochemistry(mol)
431
+ Chem.AssignChiralTypesFromBondDirs(mol)
432
+ Chem.AssignStereochemistry(mol)
433
+
434
+ # mol.Debug()
435
+ # print('debuged')
436
+
437
+ # drawing out
438
+ # opts = Draw.MolDrawOptions()
439
+ # opts.addAtomIndices = False
440
+ # opts.addStereoAnnotation = False
441
+ # img = Draw.MolToImage(mol, options=opts,size=(1000, 1000))
442
+ # img.save('tttttttttttttafter.png')
443
+ # Chem.Draw.MolToImageFile(mol, 'tttttttttttttbefore.png')
444
+ # img.save('/home/jovyan/rt-detr/output/test/after.png')
445
+ # Chem.Draw.MolToImageFile(mol, '/home/jovyan/rt-detr/output/test/before.png')
446
+
447
+ smiles=Chem.MolToSmiles(mol)
448
+ return smiles,mol
449
+
450
+
451
+ except Chem.rdchem.AtomValenceException as e:
452
+ print(f"捕获到 AtomValenceException 异常@@{e}")
453
+
454
+ # except Chem.rdchem.AtomValenceException as e:
455
+ # print(f"捕获到 AtomValenceException 异常@@{e}")
456
+
457
+ except Exception as e:
458
+ print(f"捕获到 异常@@{e}")
459
+ print(f"Error@@node {node} atom@@ {a} \n")
460
+ print(atoms,idx,atoms[idx])
461
+
462
+
463
+
464
+
465
+ def mol_from_graph_without_chiral(atoms, bonds):
466
+
467
+ mol = RWMol()
468
+ nodes_idx = {}
469
+
470
+ for i in range(len(bonds)):
471
+ idx_1, idx_2, bond_type, bond_dir, score = bonds[i]
472
+ if bond_type in ['-', 'NONE', 'ENDUPRIGHT', 'BEGINWEDGE', 'BEGINDASH', 'ENDDOWNRIGHT']:
473
+ bonds[i] = (idx_1, idx_2, 'SINGLE', bond_dir, score)
474
+ elif bond_type == '=':
475
+ bonds[i] = (idx_1, idx_2, 'DOUBLE', bond_dir, score)
476
+ elif bond_type == '#':
477
+ bonds[i] = (idx_1, idx_2, 'TRIPLE', bond_dir, score)
478
+
479
+
480
+ bond_types = {'SINGLE': Chem.rdchem.BondType.SINGLE,
481
+ 'DOUBLE': Chem.rdchem.BondType.DOUBLE,
482
+ 'TRIPLE': Chem.rdchem.BondType.TRIPLE,
483
+ 'AROMATIC': Chem.rdchem.BondType.AROMATIC}
484
+
485
+
486
+ try:
487
+ # add nodes
488
+ for idx, node in enumerate(atoms):
489
+ if ('0' in node) or ('1' in node):
490
+ a = node[:-1]
491
+ fc = int(node[-1])
492
+ if '-1' in node:
493
+ a = node[:-2]
494
+ fc = -1
495
+
496
+ a = Chem.Atom(a)
497
+ a.SetFormalCharge(fc)
498
+
499
+ atom_idx = mol.AddAtom(a)
500
+ nodes_idx[idx] = atom_idx
501
+
502
+ # add bonds
503
+ existing_bonds = set()
504
+ for idx_1, idx_2, bond_type, bond_dir, score in bonds:
505
+ if (idx_1 in nodes_idx) and (idx_2 in nodes_idx):
506
+ if (idx_1, idx_2) not in existing_bonds and (idx_2, idx_1) not in existing_bonds:
507
+ try:
508
+ mol.AddBond(nodes_idx[idx_1], nodes_idx[idx_2], bond_types[bond_type])
509
+ except:
510
+ continue
511
+ existing_bonds.add((idx_1, idx_2))
512
+ if Chem.MolFromSmiles(Chem.MolToSmiles(mol.GetMol())):
513
+ prev_mol = copy.deepcopy(mol)
514
+ else:
515
+ mol = copy.deepcopy(prev_mol)
516
+
517
+ mol = mol.GetMol()
518
+ mol = Chem.MolFromSmiles(Chem.MolToSmiles(mol))
519
+ return Chem.MolToSmiles(mol)
520
+
521
+ except Chem.rdchem.AtomValenceException as e:
522
+ print("捕获到 AtomValenceException 异常")
523
+
524
+
525
+
526
+
527
+