1 #define BOOST_TEST_MODULE Test_Coordgen
2
3 #include <boost/filesystem.hpp>
4 #include <boost/test/unit_test.hpp>
5 #include <unordered_set>
6
7 #include "../CoordgenFragmenter.h"
8 #include "../sketcherMinimizer.h"
9 #include "../sketcherMinimizerMaths.h"
10 #include "../sketcherMinimizerStretchInteraction.h"
11 #include "../sketcherMinimizerBendInteraction.h"
12 #include "../sketcherMaeReading.h"
13 #include "coordgenBasicSMILES.h"
14
15 #include "maeparser/MaeConstants.hpp"
16 #include "maeparser/Reader.hpp"
17
18 using std::unordered_set;
19 using namespace schrodinger;
20
21 const boost::filesystem::path test_samples_path(TEST_SAMPLES_PATH);
22
23 namespace
24 {
25 std::map<sketcherMinimizerAtom*, int>
getReportingIndices(sketcherMinimizerMolecule & mol)26 getReportingIndices(sketcherMinimizerMolecule& mol)
27 {
28 std::map<sketcherMinimizerAtom*, int> fakeIndices;
29 int index = 0;
30 for (auto& atom : mol.getAtoms()) {
31 fakeIndices.emplace(atom, ++index);
32 }
33 return fakeIndices;
34 }
35
36 bool areBondsNearIdeal(sketcherMinimizerMolecule& mol,
37 std::map<sketcherMinimizerAtom*, int>& indices,
38 std::set<std::pair <int, int> > skip = std::set<std::pair <int, int> > ())
39 {
40 const float targetBondLength = BONDLENGTH * BONDLENGTH;
41 const auto tolerance = static_cast<float>(targetBondLength * 0.1);
42
43 bool passed = true;
44 for (auto& bond : mol.getBonds()) {
45 auto bondPair = std::pair<int, int> (indices[bond->getStartAtom()], indices[bond->getEndAtom()]);
46 if (skip.find(bondPair) != skip.end()) {
47 continue;
48 }
49 auto& startCoordinates = bond->getStartAtom()->getCoordinates();
50 auto& endCoordinates = bond->getEndAtom()->getCoordinates();
51
52 const auto sq_distance = sketcherMinimizerMaths::squaredDistance(
53 startCoordinates, endCoordinates);
54 const auto deviation = sq_distance - targetBondLength;
55 if (deviation < -tolerance || deviation > tolerance) {
56 std::cerr << "Bond" << indices[bond->getStartAtom()] << '-'
57 << indices[bond->getEndAtom()] << " has length "
58 << sq_distance << " (" << targetBondLength << ")\n";
59 passed = false;
60 }
61 }
62 return passed;
63 }
64
noCrossingBonds(sketcherMinimizerMolecule & mol,std::map<sketcherMinimizerAtom *,int> & indices)65 bool noCrossingBonds(sketcherMinimizerMolecule& mol,
66 std::map<sketcherMinimizerAtom*, int>& indices)
67 {
68 bool passed = true;
69 for (auto& bond : mol.getBonds()) {
70 for (auto& bond2 : mol.getBonds()) {
71 if (bond == bond2) continue;
72 if (bond->getStartAtom() == bond2->getStartAtom()) continue;
73 if (bond->getStartAtom() == bond2->getEndAtom()) continue;
74 if (bond->getEndAtom() == bond2->getStartAtom()) continue;
75 if (bond->getEndAtom() == bond2->getEndAtom()) continue;
76
77 auto& startCoordinates1 = bond->getStartAtom()->getCoordinates();
78 auto& endCoordinates1 = bond->getEndAtom()->getCoordinates();
79 auto& startCoordinates2 = bond2->getStartAtom()->getCoordinates();
80 auto& endCoordinates2 = bond2->getEndAtom()->getCoordinates();
81
82 if (sketcherMinimizerMaths::intersectionOfSegments(startCoordinates1,
83 endCoordinates1,
84 startCoordinates2,
85 endCoordinates2)) {
86 std::cerr << "Bond" << indices[bond->getStartAtom()] << '-'
87 << indices[bond->getEndAtom()] << " intersects bond "
88 << indices[bond2->getStartAtom()]<< '-'
89 << indices[bond2->getEndAtom()]<<")\n";
90 passed = false;
91 }
92 }
93 }
94 return passed;
95 }
96
97 } // namespace
98
99
operator ""_smiles(const char * smiles,size_t len)100 static sketcherMinimizerMolecule* operator"" _smiles(const char * smiles, size_t len)
101 {
102 return approxSmilesParse({smiles, len});
103 }
104
BOOST_AUTO_TEST_CASE(SampleTest)105 BOOST_AUTO_TEST_CASE(SampleTest)
106 {
107 // A small sample test showing how to import a molecule from a .mae file and
108 // initialize the minimizer with it.
109
110 const std::string testfile = (test_samples_path / "test.mae").string();
111
112 mae::Reader r(testfile);
113 auto block = r.next(mae::CT_BLOCK);
114 BOOST_REQUIRE(block != nullptr);
115
116 auto* mol = mol_from_mae_block(*block);
117 BOOST_REQUIRE(mol != nullptr);
118 BOOST_REQUIRE_EQUAL(mol->getAtoms().size(), 26);
119 BOOST_REQUIRE_EQUAL(mol->getBonds().size(), 26);
120
121 sketcherMinimizer minimizer;
122 minimizer.initialize(mol); // minimizer takes ownership of mol
123 minimizer.runGenerateCoordinates();
124
125 for (auto& atom : mol->getAtoms()) {
126 auto c = atom->getCoordinates();
127
128 // This is best we can do with checking: there are precision and
129 // rounding issues depending on platform and environment.
130 BOOST_CHECK(c.x() != 0 || c.y() != 0);
131 }
132
133 auto indices = getReportingIndices(*mol);
134 BOOST_CHECK(areBondsNearIdeal(*mol, indices));
135 }
136
137
BOOST_AUTO_TEST_CASE(TemplateTest)138 BOOST_AUTO_TEST_CASE(TemplateTest)
139 {
140 ///
141 // Do the structures in the templates file get the same coordinates that
142 // were supplied in the templates file?
143
144 const boost::filesystem::path source_dir(SOURCE_DIR);
145 const std::string templates_file = (source_dir / "templates.mae").string();
146
147 // Known issues. See issue #52
148 const unordered_set<size_t> no_match = {1, 8, 19, 20, 22, 32, 43, 53, 65, 66, 67};
149 // 32 is odd. minimization removes atoms?? But it matches??
150 const unordered_set<size_t> match_incorrectly = {18, 27};
151
152 mae::Reader r(templates_file);
153 std::shared_ptr<mae::Block> b;
154 size_t template_index = 0;
155 while ((b = r.next(mae::CT_BLOCK)) != nullptr) {
156 if (no_match.count(template_index) > 0) {
157 ++template_index;
158 continue;
159 }
160
161 auto* mol = mol_from_mae_block(*b);
162 BOOST_REQUIRE(mol != nullptr);
163 const auto original_atom_count = mol->getAtoms().size();
164
165 sketcherMinimizer minimizer;
166 minimizer.setTemplateFileDir(source_dir.string());
167
168 minimizer.initialize(mol); // minimizer takes ownership of mol
169 minimizer.runGenerateCoordinates();
170
171 BOOST_CHECK_EQUAL(original_atom_count, mol->getAtoms().size());
172
173 bool any_rigid = false;
174 bool all_rigid = true;
175 for (auto a: mol->getAtoms()) {
176 if (a->rigid) {
177 any_rigid = true;
178 } else {
179 all_rigid = false;
180 }
181 }
182 const bool matches_incorrectly = match_incorrectly.count(template_index) > 0;
183 if (!any_rigid) {
184 BOOST_CHECK_MESSAGE(any_rigid, "No template found for " << template_index);
185 } else if (!matches_incorrectly) {
186 BOOST_CHECK_MESSAGE(all_rigid, "Not all atoms templated for " << template_index);
187 }
188
189 ++template_index;
190 }
191 }
192
BOOST_AUTO_TEST_CASE(ClearWedgesTest)193 BOOST_AUTO_TEST_CASE(ClearWedgesTest)
194 {
195 // test that when writing stereochemistry we first clear the existing one
196
197 const std::string testfile = (test_samples_path / "testChirality.mae").string();
198
199 mae::Reader r(testfile);
200 auto block = r.next(mae::CT_BLOCK);
201 BOOST_REQUIRE(block != nullptr);
202
203 auto* mol = mol_from_mae_block(*block);
204 BOOST_REQUIRE(mol != nullptr);
205
206 /*set wedges on all bonds*/
207 mol->getBonds().at(0)->hasStereochemistryDisplay = true;
208 mol->getBonds().at(1)->hasStereochemistryDisplay = true;
209 mol->getBonds().at(2)->hasStereochemistryDisplay = true;
210 mol->getBonds().at(3)->hasStereochemistryDisplay = true;
211
212 /*set chirality on the atom*/
213 auto carbon = mol->getAtoms().at(0);
214 BOOST_REQUIRE_EQUAL(carbon->atomicNumber, 6);
215 carbon->hasStereochemistrySet = true;
216 carbon->isR = true;
217
218 /*run minimization*/
219 sketcherMinimizer minimizer;
220 minimizer.initialize(mol); // minimizer takes ownership of mol
221 minimizer.runGenerateCoordinates();
222
223 /*make sure that previous wedges are reset and only 2 are now used*/
224 BOOST_REQUIRE_EQUAL(mol->getBonds().at(0)->hasStereochemistryDisplay, false);
225 BOOST_REQUIRE_EQUAL(mol->getBonds().at(1)->hasStereochemistryDisplay, true);
226 BOOST_REQUIRE_EQUAL(mol->getBonds().at(2)->hasStereochemistryDisplay, false);
227 BOOST_REQUIRE_EQUAL(mol->getBonds().at(3)->hasStereochemistryDisplay, true);
228 }
229
230
BOOST_AUTO_TEST_CASE(DisableMetalZOBs)231 BOOST_AUTO_TEST_CASE(DisableMetalZOBs)
232 {
233 // Load a molecule with a single bond to a metal. Make sure that disabling the automatic conversion
234 // to zob behaves as expected
235
236 const std::string testfile = (test_samples_path / "nonterminalMetalZobs.mae").string();
237
238 mae::Reader r(testfile);
239 auto block = r.next(mae::CT_BLOCK);
240 BOOST_REQUIRE(block != nullptr);
241
242 auto* mol = mol_from_mae_block(*block);
243 BOOST_REQUIRE(mol != nullptr);
244
245 sketcherMinimizer minimizer;
246 minimizer.setTreatNonterminalBondsToMetalAsZOBs(false);
247 auto Al = mol->getAtoms().at(0);
248 auto N = mol->getAtoms().at(1);
249 //make sure we got the right atoms
250 BOOST_REQUIRE_EQUAL(Al->atomicNumber, 13);
251 BOOST_REQUIRE_EQUAL(N->atomicNumber, 7);
252
253 minimizer.initialize(mol); // minimizer takes ownership of mol
254 minimizer.runGenerateCoordinates();
255 auto bondLength = (Al->coordinates - N->coordinates).length();
256 auto expectedLength = 50.f;
257 auto tolerance = 2.f;
258 BOOST_REQUIRE ((bondLength > expectedLength-tolerance) && (bondLength < expectedLength+tolerance));
259 auto indices = getReportingIndices(*mol);
260 BOOST_CHECK(areBondsNearIdeal(*mol, indices));
261 }
262
263
BOOST_AUTO_TEST_CASE(terminalMetalZOBs)264 BOOST_AUTO_TEST_CASE(terminalMetalZOBs)
265 {
266 // Load a molecule with a single bond to a terminal metal. Make sure the bondlength is consistant with a terminal bond
267
268 const std::string testfile = (test_samples_path / "metalZobs.mae").string();
269
270 mae::Reader r(testfile);
271 auto block = r.next(mae::CT_BLOCK);
272 BOOST_REQUIRE(block != nullptr);
273
274 auto* mol = mol_from_mae_block(*block);
275 BOOST_REQUIRE(mol != nullptr);
276
277 sketcherMinimizer minimizer;
278 auto Al = mol->getAtoms().at(0);
279 auto N = mol->getAtoms().at(1);
280 //make sure we got the right atoms
281 BOOST_REQUIRE_EQUAL(Al->atomicNumber, 13);
282 BOOST_REQUIRE_EQUAL(N->atomicNumber, 7);
283
284 minimizer.initialize(mol); // minimizer takes ownership of mol
285 minimizer.runGenerateCoordinates();
286 auto bondLength = (Al->coordinates - N->coordinates).length();
287 auto expectedLength = 50.f;
288 auto tolerance = 2.f;
289 BOOST_REQUIRE ((bondLength > expectedLength-tolerance) && (bondLength < expectedLength+tolerance));
290 auto indices = getReportingIndices(*mol);
291 BOOST_CHECK(areBondsNearIdeal(*mol, indices));
292 }
293
BOOST_AUTO_TEST_CASE(testMinimizedRingsShape)294 BOOST_AUTO_TEST_CASE(testMinimizedRingsShape)
295 {
296 // minimize a complex macrocycle. Make sure rings have the shape of regular polygons
297
298 const std::string testfile = (test_samples_path / "macrocycle.mae").string();
299
300 mae::Reader r(testfile);
301 auto block = r.next(mae::CT_BLOCK);
302 BOOST_REQUIRE(block != nullptr);
303
304 auto* mol = mol_from_mae_block(*block);
305 BOOST_REQUIRE(mol != nullptr);
306
307 sketcherMinimizer minimizer;
308
309
310 minimizer.initialize(mol); // minimizer takes ownership of mol
311 minimizer.runGenerateCoordinates();
312 //check the length of every non macrocycle-ring bond
313 int bondsN = 0;
314 for (auto interaction : minimizer.getStretchInteractions()) {
315 auto ring = sketcherMinimizer::sameRing(interaction->atom1, interaction->atom2);
316 if (ring && !ring->isMacrocycle()) {
317 auto expectedLength = 50.f;
318 auto tolerance = 2.f;
319 auto bondLength = (interaction->atom1->coordinates - interaction->atom2->coordinates).length();
320 BOOST_REQUIRE ((bondLength > expectedLength-tolerance) && (bondLength < expectedLength+tolerance));
321 bondsN++;
322 }
323 }
324 // check the angles
325 int anglesN = 0;
326 for (auto interaction : minimizer.getBendInteractions()) {
327 if (interaction->isRing) {
328 auto ring = sketcherMinimizer::sameRing(interaction->atom1, interaction->atom2, interaction->atom3);
329 BOOST_REQUIRE (ring != nullptr);
330 BOOST_REQUIRE (!ring->isMacrocycle());
331 auto expectedValue = interaction->restV;
332 auto tolerance = 2.f;
333 auto angle = interaction->angle();
334 BOOST_REQUIRE ((angle > expectedValue-tolerance) && (angle < expectedValue+tolerance));
335 anglesN++;
336 }
337 }
338 BOOST_REQUIRE (anglesN == 32);
339 }
340
341
BOOST_AUTO_TEST_CASE(testPolyominoCoordinatesOfSubstituent)342 BOOST_AUTO_TEST_CASE(testPolyominoCoordinatesOfSubstituent)
343 {
344 Polyomino p;
345 p.addHex(hexCoords(0, 0));
346 auto substCoords =
347 p.coordinatesOfSubstituent(vertexCoords(1, 0, 0));
348 BOOST_REQUIRE(substCoords == vertexCoords(1, -1, -1));
349
350 p.addHex(hexCoords(1, 0));
351 substCoords = p.coordinatesOfSubstituent(vertexCoords(1, 0, 0));
352 BOOST_REQUIRE(substCoords == vertexCoords(0, 0, -1));
353 }
354
BOOST_AUTO_TEST_CASE(testPolyominoSameAs)355 BOOST_AUTO_TEST_CASE(testPolyominoSameAs)
356 {
357 // identity
358 Polyomino p1;
359 p1.addHex(hexCoords(0, 0));
360 p1.addHex(hexCoords(1, 0));
361 p1.addHex(hexCoords(2, 0));
362 p1.addHex(hexCoords(0, 1));
363 BOOST_REQUIRE(p1.isTheSameAs(p1));
364
365 // order-independence
366 Polyomino p2;
367 p2.addHex(hexCoords(2, 0));
368 p2.addHex(hexCoords(0, 0));
369 p2.addHex(hexCoords(0, 1));
370 p2.addHex(hexCoords(1, 0));
371 BOOST_REQUIRE(p1.isTheSameAs(p2));
372 BOOST_REQUIRE(p2.isTheSameAs(p1));
373
374 // translation
375 Polyomino p3;
376 p3.addHex(hexCoords(4, 2));
377 p3.addHex(hexCoords(5, 2));
378 p3.addHex(hexCoords(6, 2));
379 p3.addHex(hexCoords(4, 3));
380 BOOST_REQUIRE(p1.isTheSameAs(p3));
381 BOOST_REQUIRE(p3.isTheSameAs(p1));
382
383 // rotation
384 Polyomino p4;
385 p4.addHex(hexCoords(0, 0));
386 p4.addHex(hexCoords(-1, 0));
387 p4.addHex(hexCoords(-2, 0));
388 p4.addHex(hexCoords(0, -1));
389 BOOST_REQUIRE(p1.isTheSameAs(p4));
390 BOOST_REQUIRE(p4.isTheSameAs(p1));
391
392 // symmetry
393 Polyomino p5;
394 p5.addHex(hexCoords(0, 0));
395 p5.addHex(hexCoords(0, 1));
396 p5.addHex(hexCoords(0, 2));
397 p5.addHex(hexCoords(1, 0));
398 BOOST_REQUIRE(!p1.isTheSameAs(p5));
399 BOOST_REQUIRE(!p5.isTheSameAs(p1));
400
401 // different number of points
402 Polyomino p6;
403 p6.addHex(hexCoords(1, 0));
404 p6.addHex(hexCoords(2, 0));
405 p6.addHex(hexCoords(0, 1));
406 BOOST_REQUIRE(!p1.isTheSameAs(p6));
407 BOOST_REQUIRE(!p6.isTheSameAs(p1));
408 }
409
BOOST_AUTO_TEST_CASE(testGetDoubleBondConstraints)410 BOOST_AUTO_TEST_CASE(testGetDoubleBondConstraints)
411 {
412 /*
413 test that getDoubleBondConstraints behaves as expected:
414 notably don't set up interactions for double bonds that are also part of
415 a small ring.
416 CRDGEN-160
417 */
418 sketcherMinimizer min;
419 CoordgenFragmentBuilder fragmentBuilder;
420 CoordgenMacrocycleBuilder macrocycleBuilder;
421
422 const std::string testfile = (test_samples_path / "test_mol.mae").string();
423 mae::Reader r(testfile);
424 auto block = r.next(mae::CT_BLOCK);
425 BOOST_REQUIRE(block != nullptr);
426
427 auto* mol = mol_from_mae_block(*block);
428 BOOST_REQUIRE(mol != nullptr);
429
430 min.initialize(mol); // minimizer takes ownership of mol
431 for (auto molecule : min.getMolecules()) {
432 for (auto ring : molecule->getRings()) {
433 std::vector<sketcherMinimizerAtom*> atoms =
434 fragmentBuilder.orderRingAtoms(ring);
435 std::vector<doubleBondConstraint> constraints =
436 macrocycleBuilder.getDoubleBondConstraints(atoms);
437 BOOST_REQUIRE(constraints.size() == 0);
438 }
439 }
440 }
441
BOOST_AUTO_TEST_CASE(testClockwiseOrderedSubstituents)442 BOOST_AUTO_TEST_CASE(testClockwiseOrderedSubstituents)
443 {
444 auto mol = "CN(C)C"_smiles;
445
446 sketcherMinimizer minimizer;
447 minimizer.initialize(mol); // minimizer takes ownership of mol
448 minimizer.runGenerateCoordinates();
449
450 const auto& atoms = minimizer.getAtoms();
451 sketcherMinimizerAtom* center = atoms.at(0);
452 sketcherMinimizerAtom* neigh1 = atoms.at(1);
453 sketcherMinimizerAtom* neigh2 = atoms.at(2);
454 sketcherMinimizerAtom* neigh3 = atoms.at(3);
455 BOOST_REQUIRE_EQUAL(center->getAtomicNumber(), 7);
456
457 sketcherMinimizerPointF origin(0, 0);
458 sketcherMinimizerPointF above(0, 50);
459 sketcherMinimizerPointF left(-50, 0);
460 sketcherMinimizerPointF right(50, 0);
461
462 center->coordinates = origin;
463 neigh1->coordinates = above;
464 neigh2->coordinates = left;
465 neigh3->coordinates = right;
466
467 auto orderedNeighbors =
468 center->clockwiseOrderedNeighbors();
469
470 BOOST_REQUIRE((orderedNeighbors[0]->coordinates - above).length() == 0);
471 BOOST_REQUIRE((orderedNeighbors[1]->coordinates - left).length() == 0);
472 BOOST_REQUIRE((orderedNeighbors[2]->coordinates - right).length() == 0);
473
474 BOOST_REQUIRE_EQUAL(orderedNeighbors[0], neigh1);
475 BOOST_REQUIRE_EQUAL(orderedNeighbors[1], neigh2);
476 BOOST_REQUIRE_EQUAL(orderedNeighbors[2], neigh3);
477
478 neigh1->coordinates = above;
479 neigh3->coordinates = left;
480 neigh2->coordinates = right;
481
482 orderedNeighbors = center->clockwiseOrderedNeighbors();
483
484 BOOST_REQUIRE((orderedNeighbors[0]->coordinates - above).length() == 0);
485 BOOST_REQUIRE((orderedNeighbors[1]->coordinates - left).length() == 0);
486 BOOST_REQUIRE((orderedNeighbors[2]->coordinates - right).length() == 0);
487
488 BOOST_REQUIRE_EQUAL(orderedNeighbors[0], neigh1);
489 BOOST_REQUIRE_EQUAL(orderedNeighbors[1], neigh3);
490 BOOST_REQUIRE_EQUAL(orderedNeighbors[2], neigh2);
491 }
492
BOOST_AUTO_TEST_CASE(testbicyclopentane)493 BOOST_AUTO_TEST_CASE(testbicyclopentane)
494 {
495 /*
496 test that bicyclo(1,1,1)pentane is rendered without clashes between the bridge carbons
497 CRDGEN-270
498 */
499
500 auto mol = "C1C2CC1C2"_smiles;
501 sketcherMinimizer minimizer;
502 minimizer.initialize(mol); // minimizer takes ownership of mol
503 minimizer.runGenerateCoordinates();
504
505 const auto& atoms = minimizer.getAtoms();
506 auto bridgeAtom1 = atoms.at(1);
507 auto bridgeAtom2 = atoms.at(2);
508 auto bridgeAtom3 = atoms.at(3);
509 BOOST_TEST(bridgeAtom1->neighbors.size() == 2);
510 BOOST_TEST(bridgeAtom2->neighbors.size() == 2);
511 BOOST_TEST(bridgeAtom3->neighbors.size() == 2);
512 auto distance1 = (bridgeAtom1->getCoordinates() - bridgeAtom2->getCoordinates()).length();
513 auto distance2 = (bridgeAtom1->getCoordinates() - bridgeAtom3->getCoordinates()).length();
514 auto distance3 = (bridgeAtom2->getCoordinates() - bridgeAtom3->getCoordinates()).length();
515 auto minimumDistance = 15.f;
516 BOOST_TEST(distance1 > minimumDistance);
517 BOOST_TEST(distance2 > minimumDistance);
518 BOOST_TEST(distance3 > minimumDistance);
519 }
520
BOOST_AUTO_TEST_CASE(testFusedRings)521 BOOST_AUTO_TEST_CASE(testFusedRings)
522 {
523 /*
524 CRDGEN271, CRDGEN-272
525 */
526
527 std::vector<std::string> smiles {"C1CCC23CCCCC2CC3C1",
528 "C1=CC2C3CC4C(CC3NC3CCCC(C32)N1)NC1CCCC2C1C4CCN2"};
529 for (auto smile : smiles) {
530 auto mol = approxSmilesParse(smile);
531 sketcherMinimizer minimizer;
532 minimizer.initialize(mol); // minimizer takes ownership of mol
533 minimizer.runGenerateCoordinates();
534 auto indices = getReportingIndices(*mol);
535 BOOST_CHECK(areBondsNearIdeal(*mol, indices));
536 BOOST_CHECK(noCrossingBonds(*mol, indices));
537 }
538 }
539
540
BOOST_AUTO_TEST_CASE(testTemplates)541 BOOST_AUTO_TEST_CASE(testTemplates)
542 {
543 auto mol = "C12CC3CC(CC3C1)C2"_smiles;
544 sketcherMinimizer minimizer;
545 minimizer.initialize(mol); // minimizer takes ownership of mol
546 minimizer.runGenerateCoordinates();
547 auto indices = getReportingIndices(*mol);
548 //template has two stretched bonds
549 std::set<std::pair<int, int> > skip;
550 skip.insert(std::pair<int, int> (2, 5));
551 skip.insert(std::pair<int, int> (6, 2));
552 BOOST_CHECK(areBondsNearIdeal(*mol, indices, skip));
553 }
554
555
BOOST_AUTO_TEST_CASE(testRingComplex)556 BOOST_AUTO_TEST_CASE(testRingComplex)
557 {
558 auto mol = "CC1CC2CCCC(C3CC4CCC(C3)C4C)C2O1"_smiles;
559 sketcherMinimizer minimizer;
560 minimizer.initialize(mol); // minimizer takes ownership of mol
561 minimizer.runGenerateCoordinates();
562 auto indices = getReportingIndices(*mol);
563 BOOST_CHECK(noCrossingBonds(*mol, indices));
564 }
565
BOOST_AUTO_TEST_CASE(testCoordgenFragmenter)566 BOOST_AUTO_TEST_CASE(testCoordgenFragmenter)
567 {
568 /*
569 Test that a molecule is fragmented as expected and fragments are given the
570 correct flags. Atoms 3-8 and 11 are constrained.
571 6
572 / \
573 1 -- 2 -- 3 -- 4 -- 5 7 -- 8 -- 9 -- 10
574 \ /
575 11
576 */
577 auto mol = "CCCCC1CC(CCC)C1"_smiles;
578 auto atoms = mol->getAtoms();
579 for (int i = 3; i <= 8; ++i) {
580 atoms[i-1]->constrained = true;
581 }
582 atoms[10]->constrained = true;
583 auto atom_map = getReportingIndices(*mol);
584
585 sketcherMinimizer minimizer;
586 minimizer.initialize(mol);
587 CoordgenFragmenter::splitIntoFragments(mol);
588
589 std::vector<std::set<int>> expected_fragments = {{1, 2}, {3}, {4}, {5, 6, 7, 11}, {8}, {9, 10}};
590 std::vector<std::set<int>> actual_fragments;
591 for (auto fragment : mol->_fragments) {
592 std::set<int> fragment_idices;
593 for (auto at : fragment->getAtoms()) {
594 fragment_idices.insert(atom_map[at]);
595 }
596 actual_fragments.push_back(fragment_idices);
597 }
598 std::sort(actual_fragments.begin(), actual_fragments.end());
599
600 // Check that the fragmenting is correct
601 BOOST_REQUIRE_EQUAL(actual_fragments.size(), expected_fragments.size());
602 for (size_t i = 0; i < actual_fragments.size(); ++i) {
603 BOOST_CHECK_EQUAL_COLLECTIONS(expected_fragments[i].begin(), expected_fragments[i].end(), actual_fragments[i].begin(), actual_fragments[i].end());
604 }
605
606 // Fragment containing atoms (1, 2)
607 BOOST_TEST(atoms[0]->fragment->constrained == false);
608 BOOST_TEST(atoms[0]->fragment->constrainedFlip == false);
609
610 // Fragment containing atom 3. Flip should not be constrained since
611 // the fragment does not have any constrained child fragments
612 BOOST_TEST(atoms[2]->fragment->constrained == true);
613 BOOST_TEST(atoms[2]->fragment->constrainedFlip == false);
614
615 // Fragment containing atom 4. Flip should be constrained since fragment
616 // has a child fragment that is constrained
617 BOOST_TEST(atoms[3]->fragment->constrained == true);
618 BOOST_TEST(atoms[3]->fragment->constrainedFlip == true);
619
620 // Fragment containing atoms (5, 6, 7, 11)
621 BOOST_TEST(atoms[4]->fragment->constrained == true);
622 BOOST_TEST(atoms[4]->fragment->constrainedFlip == true);
623
624 // Fragment containing atom 8
625 BOOST_TEST(atoms[7]->fragment->constrained == true);
626 BOOST_TEST(atoms[7]->fragment->constrainedFlip == false);
627
628 // Fragment containing atoms (9, 10)
629 BOOST_TEST(atoms[8]->fragment->constrained == false);
630 BOOST_TEST(atoms[8]->fragment->constrainedFlip == false);
631 }
632