1 //
2 //  Copyright (C) 2017-2020 Greg Landrum
3 //
4 //   @@ All Rights Reserved @@
5 //  This file is part of the RDKit.
6 //  The contents are covered by the terms of the BSD license
7 //  which is included in the file license.txt, found at the root
8 //  of the RDKit source tree.
9 //
10 #include <iostream>
11 
12 #include <fstream>
13 #include <GraphMol/RDKitBase.h>
14 #include <GraphMol/SmilesParse/SmilesParse.h>
15 #include <GraphMol/FileParsers/FileParsers.h>
16 #include <GraphMol/Substruct/SubstructMatch.h>
17 #include <GraphMol/MolAlign/AlignMolecules.h>
18 #include <GraphMol/MolTransforms/MolTransforms.h>
19 #include <GraphMol/ChemTransforms/ChemTransforms.h>
20 
21 #include <RDGeneral/RDLog.h>
22 
23 #include "coordgen/sketcherMinimizer.h"
24 #include <CoordGen/CoordGen.h>
25 
26 using namespace RDKit;
27 
test1()28 void test1() {
29   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
30   BOOST_LOG(rdInfoLog) << "test1: basics" << std::endl;
31 #if 1
32   {
33     ROMol* m = SmilesToMol("c1cc(CC)cnc1CC(=O)O");
34     TEST_ASSERT(m);
35     m->setProp("_Name", "test1");
36 
37     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
38     TEST_ASSERT(m->getNumConformers() == 1);
39     auto mb = MolToMolBlock(*m);
40     std::cerr << mb << std::endl;
41     delete m;
42   }
43   {
44     // ROMol* m = SmilesToMol("c1ccncc1");
45 
46     ROMol* m = SmilesToMol("ClC(O)(F)C");
47     TEST_ASSERT(m);
48     m->setProp("_Name", "test2");
49 
50     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
51     TEST_ASSERT(m->getNumConformers() == 1);
52     auto mb = MolToMolBlock(*m);
53     std::cerr << mb << std::endl;
54     delete m;
55   }
56 
57   {
58     ROMol* m = SmilesToMol(
59         "CC[C@H]1C(=O)N(CC(=O)N([C@H](C(=O)N[C@H](C(=O)N([C@H](C(=O)N[C@H](C(="
60         "O)N[C@@H](C(=O)N([C@H](C(=O)N([C@H](C(=O)N([C@H](C(=O)N([C@H](C(=O)N1)"
61         "[C@@H]([C@H](C)C/C=C/"
62         "C)O)C)C(C)C)C)CC(C)C)C)CC(C)C)C)C)C)CC(C)C)C)C(C)C)CC(C)C)C)C");
63     TEST_ASSERT(m);
64     m->setProp("_Name", "cyclosporine a");
65 
66     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
67     TEST_ASSERT(m->getNumConformers() == 1);
68     auto mb = MolToMolBlock(*m);
69     std::cerr << mb << std::endl;
70     delete m;
71   }
72 
73   {
74     // ROMol* m = SmilesToMol("c1ccncc1");
75 
76     ROMol* m = SmilesToMol("CCCNC=CNCOC=CC=CC=COC");
77     TEST_ASSERT(m);
78     m->setProp("_Name", "single-double");
79 
80     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
81     TEST_ASSERT(m->getNumConformers() == 1);
82     auto mb = MolToMolBlock(*m);
83     std::cerr << mb << std::endl;
84     delete m;
85   }
86 #endif
87 
88   {
89     ROMol* m = SmilesToMol("O/C=C/C=C/C=C\\C=C/N");
90     TEST_ASSERT(m);
91     m->setProp("_Name", "cis-trans");
92 
93     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
94     TEST_ASSERT(m->getNumConformers() == 1);
95     auto mb = MolToMolBlock(*m);
96     std::cerr << mb << std::endl;
97     delete m;
98   }
99 
100   {
101     ROMol* m = SmilesToMol("C1C3CC2CC(CC1C2)C3");
102     TEST_ASSERT(m);
103     m->setProp("_Name", "admntn");
104 
105     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
106     TEST_ASSERT(m->getNumConformers() == 1);
107     auto mb = MolToMolBlock(*m);
108     std::cerr << mb << std::endl;
109     delete m;
110   }
111 
112   BOOST_LOG(rdInfoLog) << "done" << std::endl;
113 }
114 
115 namespace {
compareConfs(const ROMol * m,ROMol * templ,const MatchVectType & mv,bool alignFirst=false,int molConfId=-1,int templateConfId=-1,double postol=1e-2,double rmstol=0.1)116 bool compareConfs(const ROMol* m, ROMol* templ, const MatchVectType& mv,
117                   bool alignFirst = false, int molConfId = -1,
118                   int templateConfId = -1, double postol = 1e-2,
119                   double rmstol = 0.1) {
120   PRECONDITION(m, "bad pointer");
121   PRECONDITION(templ, "bad pointer");
122   TEST_ASSERT(m->getNumAtoms() >= templ->getNumAtoms());
123 
124   if (alignFirst) {
125     double rmsd =
126         MolAlign::alignMol(*templ, *m, molConfId, templateConfId, &mv);
127     if (rmsd > rmstol) {
128       return false;
129     }
130   }
131 
132   const Conformer& conf1 = m->getConformer(molConfId);
133   const Conformer& conf2 = templ->getConformer(templateConfId);
134   for (unsigned int i = 0; i < templ->getNumAtoms(); i++) {
135     TEST_ASSERT(m->getAtomWithIdx(mv[i].second)->getAtomicNum() ==
136                 templ->getAtomWithIdx(mv[i].first)->getAtomicNum());
137 
138     RDGeom::Point3D pt1i = conf1.getAtomPos(mv[i].second);
139     RDGeom::Point3D pt2i = conf2.getAtomPos(mv[i].first);
140     if ((pt1i - pt2i).length() >= postol) {
141       return false;
142     }
143   }
144   return true;
145 }
146 }  // namespace
147 
test2()148 void test2() {
149   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
150   BOOST_LOG(rdInfoLog) << "test2: using templates" << std::endl;
151 
152   {
153     ROMol* core = SmilesToMol("C1CON1");
154     TEST_ASSERT(core);
155     core->setProp("_Name", "core");
156 
157     TEST_ASSERT(CoordGen::addCoords(*core) == 0);
158     TEST_ASSERT(core->getNumConformers() == 1);
159     auto mb = MolToMolBlock(*core);
160     std::cerr << mb << std::endl;
161 
162     ROMol* m = SmilesToMol("C1C(CCC)ON1");
163     TEST_ASSERT(m);
164     m->setProp("_Name", "core+sidechain");
165 
166     MatchVectType mv;
167     SubstructMatch(*m, *core, mv);
168 
169     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
170     TEST_ASSERT(m->getNumConformers() == 1);
171     mb = MolToMolBlock(*m);
172     std::cerr << mb << std::endl;
173     TEST_ASSERT(!compareConfs(m, core, mv));
174 
175     {
176       auto coreConf = core->getConformer();
177       RDGeom::INT_POINT2D_MAP coordMap;
178       for (auto& i : mv) {
179         coordMap[i.second] = RDGeom::Point2D(coreConf.getAtomPos(i.first).x,
180                                              coreConf.getAtomPos(i.first).y);
181       }
182       CoordGen::CoordGenParams params;
183       params.coordMap = coordMap;
184       params.dbg_useFixed = true;
185       TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
186       TEST_ASSERT(m->getNumConformers() == 1);
187       // m->setProp("_Name", "templated");
188       // mb = MolToMolBlock(*m);
189       // std::cerr << mb << std::endl;
190       TEST_ASSERT(compareConfs(m, core, mv));
191     }
192     {
193       CoordGen::CoordGenParams params;
194       params.templateMol = core;
195       params.dbg_useFixed = true;
196       TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
197       TEST_ASSERT(m->getNumConformers() == 1);
198       m->setProp("_Name", "templated");
199       mb = MolToMolBlock(*m);
200       std::cerr << mb << std::endl;
201       TEST_ASSERT(compareConfs(m, core, mv));
202     }
203     delete m;
204     delete core;
205   }
206 
207   {
208     ROMol* core = SmilesToMol("C1CCCCCONCN1");
209     TEST_ASSERT(core);
210     core->setProp("_Name", "core");
211 
212     TEST_ASSERT(CoordGen::addCoords(*core) == 0);
213     TEST_ASSERT(core->getNumConformers() == 1);
214     auto mb = MolToMolBlock(*core);
215     std::cerr << mb << std::endl;
216 
217     ROMol* m = SmilesToMol("C1CCCCONC(CC)NC1");
218     TEST_ASSERT(m);
219     m->setProp("_Name", "core+sidechain");
220 
221     MatchVectType mv;
222     SubstructMatch(*m, *core, mv);
223 
224     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
225     TEST_ASSERT(m->getNumConformers() == 1);
226     mb = MolToMolBlock(*m);
227     std::cerr << mb << std::endl;
228     TEST_ASSERT(!compareConfs(m, core, mv));
229 
230     {
231       auto coreConf = core->getConformer();
232       RDGeom::INT_POINT2D_MAP coordMap;
233       for (auto& i : mv) {
234         coordMap[i.second] = RDGeom::Point2D(coreConf.getAtomPos(i.first).x,
235                                              coreConf.getAtomPos(i.first).y);
236       }
237       CoordGen::CoordGenParams params;
238       params.coordMap = coordMap;
239       params.dbg_useFixed = true;
240       TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
241       TEST_ASSERT(m->getNumConformers() == 1);
242       // m->setProp("_Name", "templated");
243       // mb = MolToMolBlock(*m);
244       // std::cerr << mb << std::endl;
245       TEST_ASSERT(compareConfs(m, core, mv));
246     }
247     {
248       CoordGen::CoordGenParams params;
249       params.templateMol = core;
250       params.dbg_useFixed = true;
251       TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
252       TEST_ASSERT(m->getNumConformers() == 1);
253       m->setProp("_Name", "templated");
254       mb = MolToMolBlock(*m);
255       std::cerr << mb << std::endl;
256       TEST_ASSERT(compareConfs(m, core, mv));
257     }
258 
259     delete m;
260     delete core;
261   }
262 
263   {
264     ROMol* core = SmilesToMol("C1CCCCCONCN1");
265     TEST_ASSERT(core);
266     core->setProp("_Name", "core");
267 
268     TEST_ASSERT(CoordGen::addCoords(*core) == 0);
269     TEST_ASSERT(core->getNumConformers() == 1);
270     auto mb = MolToMolBlock(*core);
271     std::cerr << mb << std::endl;
272 
273     ROMol* m = SmilesToMol("C1CCCCONC(CCCCCC)NC1");
274     TEST_ASSERT(m);
275     m->setProp("_Name", "core+sidechain");
276 
277     MatchVectType mv;
278     SubstructMatch(*m, *core, mv);
279 
280     TEST_ASSERT(CoordGen::addCoords(*m) == 0);
281     TEST_ASSERT(m->getNumConformers() == 1);
282     mb = MolToMolBlock(*m);
283     std::cerr << mb << std::endl;
284     TEST_ASSERT(!compareConfs(m, core, mv));
285 
286     {
287       auto coreConf = core->getConformer();
288       RDGeom::INT_POINT2D_MAP coordMap;
289       for (auto& i : mv) {
290         coordMap[i.second] = RDGeom::Point2D(coreConf.getAtomPos(i.first).x,
291                                              coreConf.getAtomPos(i.first).y);
292       }
293 
294       CoordGen::CoordGenParams params;
295       params.coordMap = coordMap;
296       params.dbg_useFixed = true;
297       TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
298       TEST_ASSERT(m->getNumConformers() == 1);
299       // m->setProp("_Name", "templated");
300       // mb = MolToMolBlock(*m);
301       // std::cerr << mb << std::endl;
302       TEST_ASSERT(compareConfs(m, core, mv));
303     }
304     {
305       CoordGen::CoordGenParams params;
306       params.templateMol = core;
307       params.dbg_useFixed = true;
308       TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
309       TEST_ASSERT(m->getNumConformers() == 1);
310       m->setProp("_Name", "templated");
311       mb = MolToMolBlock(*m);
312       std::cerr << mb << std::endl;
313       TEST_ASSERT(compareConfs(m, core, mv));
314     }
315     delete m;
316     delete core;
317   }
318 
319   {
320     ROMol* core = SmilesToMol("C1CCCC2C1NCC2");
321     TEST_ASSERT(core);
322     core->setProp("_Name", "core");
323 
324     CoordGen::addCoords(*core);
325     TEST_ASSERT(core->getNumConformers() == 1);
326     auto mb = MolToMolBlock(*core);
327     std::cerr << mb << std::endl;
328 
329     ROMol* m = SmilesToMol("C1C(CCC)CC(CC3CC3)C2C1N(C(C)C)CC2");
330     TEST_ASSERT(m);
331     m->setProp("_Name", "core+sidechain");
332 
333     MatchVectType mv;
334     SubstructMatch(*m, *core, mv);
335 
336     CoordGen::addCoords(*m);
337     TEST_ASSERT(m->getNumConformers() == 1);
338     mb = MolToMolBlock(*m);
339     std::cerr << mb << std::endl;
340     // This is a rigid core: if we provide the matching substructure,
341     // and do alignment, the conformations will still match.
342     TEST_ASSERT(!compareConfs(m, core, mv, false));
343 
344     {
345       CoordGen::CoordGenParams params;
346       params.templateMol = core;
347       CoordGen::addCoords(*m, &params);
348       TEST_ASSERT(m->getNumConformers() == 1);
349       m->setProp("_Name", "templated");
350       mb = MolToMolBlock(*m);
351       std::cerr << mb << std::endl;
352       TEST_ASSERT(compareConfs(m, core, mv, true, -1, -1, 0.3));
353     }
354     delete m;
355     delete core;
356   }
357 
358   {
359     ROMol* core = SmilesToMol("CC(N)CC");
360     TEST_ASSERT(core);
361     core->setProp("_Name", "core");
362 
363     CoordGen::addCoords(*core);
364     TEST_ASSERT(core->getNumConformers() == 1);
365     auto mb = MolToMolBlock(*core);
366     std::cerr << mb << std::endl;
367 
368     ROMol* m = SmilesToMol("CC(N)CC(O)C");
369     TEST_ASSERT(m);
370     m->setProp("_Name", "core+sidechain");
371 
372     MatchVectType mv;
373     SubstructMatch(*m, *core, mv);
374 
375     CoordGen::addCoords(*m);
376     TEST_ASSERT(m->getNumConformers() == 1);
377     mb = MolToMolBlock(*m);
378     std::cerr << mb << std::endl;
379     // This mol is just slightly bigger than the core, providing
380     // the matching substructure and doing alignment will cause
381     // the conformations to match.
382     TEST_ASSERT(!compareConfs(m, core, mv, false));
383 
384     {
385       CoordGen::CoordGenParams params;
386       params.templateMol = core;
387       CoordGen::addCoords(*m, &params);
388       TEST_ASSERT(m->getNumConformers() == 1);
389       m->setProp("_Name", "templated");
390       mb = MolToMolBlock(*m);
391       std::cerr << mb << std::endl;
392       TEST_ASSERT(compareConfs(m, core, mv, true, -1, -1, 0.05));
393     }
394     delete m;
395     delete core;
396   }
397 
398   BOOST_LOG(rdInfoLog) << "done" << std::endl;
399 }
400 
testGithub1929()401 void testGithub1929() {
402   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
403   BOOST_LOG(rdInfoLog)
404       << "testing github1929: make sure coordgen works with bogus file names"
405       << std::endl;
406   {
407     ROMol* m = SmilesToMol("c1cc(CC)cnc1CC(=O)O");
408     TEST_ASSERT(m);
409     m->setProp("_Name", "test1");
410     CoordGen::CoordGenParams params;
411     params.templateFileDir = "I_do_not_exist";
412 
413     TEST_ASSERT(CoordGen::addCoords(*m, &params) == 0);
414     TEST_ASSERT(m->getNumConformers() == 1);
415     delete m;
416   }
417 
418   BOOST_LOG(rdInfoLog) << "done" << std::endl;
419 }
420 
testGithub3131()421 void testGithub3131() {
422   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
423   BOOST_LOG(rdInfoLog)
424       << "testing github3131: results from coordgen are sometimes not centered"
425       << std::endl;
426   {
427     auto m1 =
428         "CC1=C(C=C(C=C1)NC(=O)C2=CC=C(C=C2)CN3CCN(CC3)C)NC4=NC=CC(=N4)C5=CN=CC="
429         "C5"_smiles;
430     TEST_ASSERT(m1);
431     TEST_ASSERT(CoordGen::addCoords(*m1) == 0);
432     TEST_ASSERT(m1->getNumConformers() == 1);
433     auto center = MolTransforms::computeCentroid(m1->getConformer());
434     TEST_ASSERT(feq(center.x, 0.0));
435     TEST_ASSERT(feq(center.y, 0.0));
436   }
437 
438   {
439     auto m1 =
440         "CCC1=C2N=C(C=C(N2N=C1)NCC3=C[N+](=CC=C3)[O-])N4CCCC[C@H]4CCO"_smiles;
441     TEST_ASSERT(m1);
442     TEST_ASSERT(CoordGen::addCoords(*m1) == 0);
443     TEST_ASSERT(m1->getNumConformers() == 1);
444     auto center = MolTransforms::computeCentroid(m1->getConformer());
445     TEST_ASSERT(feq(center.x, 0.0));
446     TEST_ASSERT(feq(center.y, 0.0));
447   }
448 
449   {
450     // make sure that it's not recentered if we provide a coordmap:
451     auto m1 =
452         "CCC1=C2N=C(C=C(N2N=C1)NCC3=C[N+](=CC=C3)[O-])N4CCCC[C@H]4CCO"_smiles;
453     TEST_ASSERT(m1);
454     CoordGen::CoordGenParams params;
455     params.coordMap[0] = {10.0, 10.0};
456     params.coordMap[1] = {11.0, 10.0};
457     TEST_ASSERT(CoordGen::addCoords(*m1, &params) == 0);
458     TEST_ASSERT(m1->getNumConformers() == 1);
459     auto center = MolTransforms::computeCentroid(m1->getConformer());
460     TEST_ASSERT(!feq(center.x, 0.0));
461     TEST_ASSERT(!feq(center.y, 0.0));
462   }
463 
464   {
465     // make sure that it's not recentered if we provide a template:
466     auto templateMol =
467         "C1=C2N=C(C=C(N2N=C1)NCC3=C[N+](=CC=C3))N4CCCC[C@H]4"_smiles;
468     TEST_ASSERT(templateMol);
469     TEST_ASSERT(CoordGen::addCoords(*templateMol) == 0);
470     TEST_ASSERT(templateMol->getNumConformers() == 1);
471 
472     auto center = MolTransforms::computeCentroid(templateMol->getConformer());
473     TEST_ASSERT(feq(center.x, 0.0));
474     TEST_ASSERT(feq(center.y, 0.0));
475 
476     auto m1 =
477         "CCC1=C2N=C(C=C(N2N=C1)NCC3=C[N+](=CC=C3)[O-])N4CCCC[C@H]4CCO"_smiles;
478     TEST_ASSERT(m1);
479     CoordGen::CoordGenParams params;
480     params.templateMol = templateMol.get();
481     TEST_ASSERT(CoordGen::addCoords(*m1, &params) == 0);
482     TEST_ASSERT(m1->getNumConformers() == 1);
483     center = MolTransforms::computeCentroid(m1->getConformer());
484     TEST_ASSERT(!feq(center.x, 0.0));
485     TEST_ASSERT(!feq(center.y, 0.0));
486   }
487 
488   BOOST_LOG(rdInfoLog) << "done" << std::endl;
489 }
490 
testCoordgenMinimize()491 void testCoordgenMinimize() {
492   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
493   BOOST_LOG(rdInfoLog) << "testing coordgen minimize" << std::endl;
494   {
495     auto m1 =
496         R"CTAB(
497   Mrv2014 08052005142D
498 
499   0  0  0     0  0            999 V3000
500 M  V30 BEGIN CTAB
501 M  V30 COUNTS 8 8 0 0 0
502 M  V30 BEGIN ATOM
503 M  V30 1 C -2.1121 -0.3399 0 0
504 M  V30 2 C -3.3708 0.5474 0 0
505 M  V30 3 C -1.9835 0.9311 0 0
506 M  V30 4 C -0.7248 0.0437 0 0
507 M  V30 5 C 0.7926 0.3064 0 0
508 M  V30 6 O 1.3239 1.7518 0 0
509 M  V30 7 O 0.612 -1.2514 0 0
510 M  V30 8 C 1.3429 -0.2989 0 0
511 M  V30 END ATOM
512 M  V30 BEGIN BOND
513 M  V30 1 1 1 2
514 M  V30 2 1 2 3
515 M  V30 3 1 3 4
516 M  V30 4 1 4 5
517 M  V30 5 2 5 6
518 M  V30 6 1 5 7
519 M  V30 7 1 7 8
520 M  V30 8 1 4 1
521 M  V30 END BOND
522 M  V30 END CTAB
523 M  END
524 )CTAB"_ctab;
525     TEST_ASSERT(m1);
526     TEST_ASSERT(m1->getNumConformers() == 1);
527     auto ref = R"CTAB(
528      RDKit          2D
529 
530   8  8  0  0  0  0  0  0  0  0999 V2000
531    -1.5738   -1.1409    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
532    -2.7946   -0.3071    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
533    -1.9122    0.8790    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
534    -0.6914    0.0452    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
535     0.7517    0.2885    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
536     1.2665    1.6721    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
537     1.6941   -0.8483    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
538     3.1500   -0.6002    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
539   1  2  1  0
540   2  3  1  0
541   3  4  1  0
542   4  5  1  0
543   5  6  2  0
544   5  7  1  0
545   7  8  1  0
546   4  1  1  0
547 M  END
548 )CTAB"_ctab;
549     TEST_ASSERT(ref);
550     TEST_ASSERT(ref->getNumConformers() == 1);
551 
552     CoordGen::CoordGenParams ps;
553     ps.minimizeOnly = true;
554     CoordGen::addCoords(*m1, &ps);
555     ROMol m2(*m1);
556     double rmsd = MolAlign::alignMol(m2, *ref);
557     TEST_ASSERT(rmsd < 0.1);
558   }
559   BOOST_LOG(rdInfoLog) << "done" << std::endl;
560 }
561 
testZOBs()562 void testZOBs() {
563   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
564   BOOST_LOG(rdInfoLog) << "testing the zero-order bond setting with coordgen"
565                        << std::endl;
566   {
567     auto m1 =
568         R"CTAB(
569      RDKit          2D
570 
571   0  0  0  0  0  0  0  0  0  0999 V3000
572 M  V30 BEGIN CTAB
573 M  V30 COUNTS 8 7 0 0 0
574 M  V30 BEGIN ATOM
575 M  V30 1 Al 0.108450 -0.062175 0.000000 0 VAL=3
576 M  V30 2 C 0.976250 -0.558975 0.000000 0
577 M  V30 3 C 0.104850 0.937825 0.000000 0
578 M  V30 4 C -0.755750 -0.565175 0.000000 0
579 M  V30 5 C 1.840650 -0.055775 0.000000 0
580 M  V30 6 C -1.623550 -0.068375 0.000000 0
581 M  V30 7 C -2.487750 -0.571375 0.000000 0
582 M  V30 8 C 1.836850 0.944025 0.000000 0
583 M  V30 END ATOM
584 M  V30 BEGIN BOND
585 M  V30 1 1 1 2
586 M  V30 2 1 1 3
587 M  V30 3 1 1 4
588 M  V30 4 2 2 5
589 M  V30 5 2 4 6
590 M  V30 6 1 6 7
591 M  V30 7 1 5 8
592 M  V30 END BOND
593 M  V30 END CTAB
594 M  END
595 )CTAB"_ctab;
596     TEST_ASSERT(m1);
597     TEST_ASSERT(m1->getNumConformers() == 1);
598     CoordGen::addCoords(*m1);
599     TEST_ASSERT(m1->getNumConformers() == 1);
600     {
601       std::unique_ptr<ROMol> nm{MolBlockToMol(MolToMolBlock(*m1))};
602       TEST_ASSERT(nm);
603       TEST_ASSERT(nm->getBondWithIdx(3)->getStereo() ==
604                   m1->getBondWithIdx(3)->getStereo());
605       TEST_ASSERT(nm->getBondWithIdx(4)->getStereo() ==
606                   m1->getBondWithIdx(4)->getStereo());
607     }
608 
609     CoordGen::CoordGenParams ps;
610     ps.treatNonterminalBondsToMetalAsZeroOrder = true;
611     CoordGen::addCoords(*m1, &ps);
612     {
613       // the ZOB behavior screws up the double bond stereo here... detect that
614       std::unique_ptr<ROMol> nm{MolBlockToMol(MolToMolBlock(*m1))};
615       TEST_ASSERT(nm);
616       TEST_ASSERT(nm->getBondWithIdx(3)->getStereo() !=
617                   m1->getBondWithIdx(3)->getStereo());
618       TEST_ASSERT(nm->getBondWithIdx(4)->getStereo() !=
619                   m1->getBondWithIdx(4)->getStereo());
620     }
621   }
622   BOOST_LOG(rdInfoLog) << "done" << std::endl;
623 }
624 
main(int argc,char * argv[])625 int main(int argc, char* argv[]) {
626   (void)argc;
627   (void)argv;
628   RDLog::InitLogs();
629 #if 1
630   test2();
631   test1();
632   testGithub1929();
633   testGithub3131();
634 #endif
635   testCoordgenMinimize();
636   testZOBs();
637 }
638