void CanonicalSmilesSaver::saveMolecule (Molecule &mol_) const { if (mol_.vertexCount() < 1) return; QS_DEF(Array<int>, ignored); QS_DEF(Array<int>, order); QS_DEF(Array<int>, ranks); QS_DEF(Molecule, mol); int i; if (mol_.repeating_units.size() > 0) throw Error("can not canonicalize a polymer"); // Detect hydrogens configuration if aromatic but not ambiguous bool found_invalid_h = false; for (i = mol_.vertexBegin(); i != mol_.vertexEnd(); i = mol_.vertexNext(i)) { if (mol_.isRSite(i) || mol_.isPseudoAtom(i)) continue; if (mol_.getImplicitH_NoThrow(i, -1) == -1) found_invalid_h = true; } if (found_invalid_h) { AromaticityOptions options; options.method = AromaticityOptions::GENERIC; options.unique_dearomatization = true; MoleculeDearomatizer::restoreHydrogens(mol_, options); } mol.clone(mol_, 0, 0); // TODO: canonicalize allenes properly mol.allene_stereo.clear(); ignored.clear_resize(mol.vertexEnd()); ignored.zerofill(); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.convertableToImplicitHydrogen(i)) ignored[i] = 1; for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.getBondTopology(i) == TOPOLOGY_RING && mol.cis_trans.getParity(i) != 0) { // we save cis/trans ring bonds into SMILES, but only those who // do not participate in bigger ring systems const Edge &edge = mol.getEdge(i); if (mol.getAtomRingBondsCount(edge.beg) != 2 || mol.getAtomRingBondsCount(edge.end) != 2) { mol.cis_trans.setParity(i, 0); continue; } // also, discard the cis-trans bonds that have been converted to aromatic const Vertex &beg = mol.getVertex(edge.beg); const Vertex &end = mol.getVertex(edge.end); bool have_singlebond_beg = false; bool have_singlebond_end = false; int j; for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) if (mol.getBondOrder(beg.neiEdge(j)) == BOND_SINGLE) have_singlebond_beg = true; for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) if (mol.getBondOrder(end.neiEdge(j)) == BOND_SINGLE) have_singlebond_end = true; if (!have_singlebond_beg || !have_singlebond_end) { mol.cis_trans.setParity(i, 0); continue; } } MoleculeAutomorphismSearch of; of.detect_invalid_cistrans_bonds = find_invalid_stereo; of.detect_invalid_stereocenters = find_invalid_stereo; of.find_canonical_ordering = true; of.ignored_vertices = ignored.ptr(); of.process(mol); of.getCanonicalNumbering(order); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.cis_trans.getParity(i) != 0 && of.invalidCisTransBond(i)) mol.cis_trans.setParity(i, 0); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY && of.invalidStereocenter(i)) mol.stereocenters.remove(i); ranks.clear_resize(mol.vertexEnd()); for (i = 0; i < order.size(); i++) ranks[order[i]] = i; SmilesSaver saver(_output); saver.ignore_invalid_hcount = false; saver.vertex_ranks = ranks.ptr(); saver.ignore_hydrogens = true; saver.canonize_chiralities = true; saver.saveMolecule(mol); }
void CanonicalSmilesSaver::saveMolecule (Molecule &mol_) { if (mol_.vertexCount() < 1) return; QS_DEF(Array<int>, ignored); QS_DEF(Array<int>, order); QS_DEF(Array<int>, ranks); QS_DEF(Molecule, mol); int i; if (mol_.sgroups.isPolimer()) throw Error("can not canonicalize a polymer"); // Detect hydrogens configuration if aromatic but not ambiguous // We can store this infromation in the original structure mol_. mol_.restoreAromaticHydrogens(); mol.clone(mol_, 0, 0); // TODO: canonicalize allenes properly mol.allene_stereo.clear(); ignored.clear_resize(mol.vertexEnd()); ignored.zerofill(); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.convertableToImplicitHydrogen(i)) ignored[i] = 1; // Try to save into ordinary smiles and find what cis-trans bonds were used NullOutput null_output; SmilesSaver saver_cistrans(null_output); saver_cistrans.ignore_hydrogens = true; saver_cistrans.saveMolecule(mol); // Then reset cis-trans infromation that is not saved into SMILES const Array<int>& parities = saver_cistrans.getSavedCisTransParities(); for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { if (mol.cis_trans.getParity(i) != 0 && parities[i] == 0) mol.cis_trans.setParity(i, 0); } MoleculeAutomorphismSearch of; of.detect_invalid_cistrans_bonds = find_invalid_stereo; of.detect_invalid_stereocenters = find_invalid_stereo; of.find_canonical_ordering = true; of.ignored_vertices = ignored.ptr(); of.process(mol); of.getCanonicalNumbering(order); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.cis_trans.getParity(i) != 0 && of.invalidCisTransBond(i)) mol.cis_trans.setParity(i, 0); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY && of.invalidStereocenter(i)) mol.stereocenters.remove(i); ranks.clear_resize(mol.vertexEnd()); for (i = 0; i < order.size(); i++) ranks[order[i]] = i; vertex_ranks = ranks.ptr(); _actual_atom_atom_mapping.clear_resize(mol.vertexCount()); _actual_atom_atom_mapping.zerofill(); for (int i = 0; i < order.size(); ++i) { int aam = mol.reaction_atom_mapping[order[i]]; if (aam) { if (!_initial_to_actual.find(aam)) { _initial_to_actual.insert(aam, ++_aam_counter); _actual_atom_atom_mapping[order[i]] = _aam_counter; } else { _actual_atom_atom_mapping[order[i]] = _initial_to_actual.at(aam); } } } mol.reaction_atom_mapping.copy(_actual_atom_atom_mapping); SmilesSaver::saveMolecule(mol); }