void IndigoInchi::saveMoleculeIntoInchi (Molecule &mol, Array<char> &inchi) { inchi_Input input; QS_DEF(Array<inchi_Atom>, atoms); QS_DEF(Array<inchi_Stereo0D>, stereo); // Check if structure has aromatic bonds bool has_aromatic = false; for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) if (mol.getBondOrder(e) == BOND_AROMATIC) { has_aromatic = true; break; } Molecule *target = &mol; Obj<Molecule> dearom; if (has_aromatic) { dearom.create(); dearom->clone(mol, 0, 0); try { dearom->dearomatize(); } catch (DearomatizationsGroups::Error &) { } target = dearom.get(); } generateInchiInput(*target, input, atoms, stereo); inchi_Output output; int ret = GetINCHI(&input, &output); if (output.szMessage) warning.readString(output.szMessage, true); if (output.szLog) log.readString(output.szLog, true); if (output.szAuxInfo) auxInfo.readString(output.szAuxInfo, true); if (ret != inchi_Ret_OKAY && ret != inchi_Ret_WARNING) { // Construct error before dispoing inchi output to preserve error message IndigoError error("Indigo-InChI: InChI generation failed: %s. Code: %d.", output.szMessage, ret); FreeINCHI(&output); throw error; } inchi.readString(output.szInChI, true); FreeINCHI(&output); }
bool AromaticityMatcher::match (int *core_sub, int *core_super) { // Check if detailed checking is necessary bool needCheck = false; for (int i = _query.edgeBegin(); i != _query.edgeEnd(); i = _query.edgeNext(i)) { if (!_query.getBond(i).hasConstraint(QueryMolecule::BOND_ORDER)) continue; if (_matching_edges_state[i] == AROMATIC && _query.getBondOrder(i) != BOND_AROMATIC) { needCheck = true; break; } } if (!needCheck) return true; // By our rules submolecule in the query, that maps on aromatic bonds in // the target, must have aromatic bond configuration to match the target. // To check this such submolecule from query molecule is extracted, then // all bonds are marked as aromatic, and then dearomatizer tries to find // aromatic bonds configuration with partially fixed bonds. // 1. Extract query submolecule that maps on aromatic bonds. It is the same as in target. // Set skip all additional informatio during copying QS_DEF(Array<int>, mapping); mapping.clear(); for (int v_idx = _query.vertexBegin(); v_idx < _query.vertexEnd(); v_idx = _query.vertexNext(v_idx)) { int target_idx = core_sub[v_idx]; if (target_idx < 0) continue; mapping.push(target_idx); } QS_DEF(Array<int>, edges); QS_DEF(Array<int>, base_edges_mask); edges.clear(); base_edges_mask.clear_resize(_base.edgeEnd()); base_edges_mask.zerofill(); for (int e_idx = _query.edgeBegin(); e_idx < _query.edgeEnd(); e_idx = _query.edgeNext(e_idx)) { const Edge &e = _query.getEdge(e_idx); if (core_sub[e.beg] < 0 || core_sub[e.end] < 0) continue; int target_idx = _base.findEdgeIndex(core_sub[e.beg], core_sub[e.end]); if (target_idx == -1) throw Error("(AromaticityMatcher::match) target edge wasn't found"); edges.push(target_idx); base_edges_mask[target_idx] = 1; } QS_DEF(Array<int>, inv_mapping); _submolecule->makeEdgeSubmolecule(_base, mapping, edges, &inv_mapping, SKIP_ALL); QS_DEF(Array<int>, external_conn); external_conn.resize(_submolecule->vertexEnd()); external_conn.zerofill(); // Calculate external connectivity for (int i = 0; i < mapping.size(); i++) { int base_idx = mapping[i]; const Vertex &v = _base.getVertex(base_idx); int cur_external_conn = 0; for (int ni = v.neiBegin(); ni != v.neiEnd(); ni = v.neiNext(ni)) { int ni_edge = v.neiEdge(ni); if (!base_edges_mask[ni_edge]) { int bond_order_diff = _base.getBondOrder(ni_edge); if (bond_order_diff == BOND_AROMATIC) bond_order_diff = 1; cur_external_conn += bond_order_diff; } } external_conn[i] = cur_external_conn; } // 1b. Find bonds in aromatic rings in query and skip aromatic // bonds that are not in cycles QS_DEF(Array<int>, is_edge_in_aromatic_cycle); is_edge_in_aromatic_cycle.clear_resize(_submolecule->edgeEnd()); is_edge_in_aromatic_cycle.zerofill(); // At first just mark aromatic bonds for (int e_idx = _submolecule->edgeBegin(); e_idx < _submolecule->edgeEnd(); e_idx = _submolecule->edgeNext(e_idx)) { if (_submolecule->getBondOrder(e_idx) == BOND_AROMATIC) is_edge_in_aromatic_cycle[e_idx] = 1; } Filter aromatic_edge_filter(is_edge_in_aromatic_cycle.ptr(), Filter::EQ, 1); SpanningTree arom_edges_st(_submolecule.ref(), 0, &aromatic_edge_filter); // Store in is_edge_in_aromatic_cycle marks about such bonds is_edge_in_aromatic_cycle.zerofill(); arom_edges_st.markAllEdgesInCycles(is_edge_in_aromatic_cycle.ptr(), 1); enum { AROMATIC_BOND_NOT_IN_AROMATIC_CYCLE = 2 }; for (int e_idx = _submolecule->edgeBegin(); e_idx < _submolecule->edgeEnd(); e_idx = _submolecule->edgeNext(e_idx)) { if (_submolecule->getBondOrder(e_idx) == BOND_AROMATIC && is_edge_in_aromatic_cycle[e_idx] == 0) { // Such bond is marked as aromatic but it isn't in aromatic ring // Here just change bond order to nonaromatic (single) and later // check if such query bond can be aromatic (not by aromatizer) if (_submolecule->isQueryMolecule()) { QueryMolecule &qmol = _submolecule->asQueryMolecule(); AutoPtr<QueryMolecule::Bond> bond(qmol.releaseBond(e_idx)); bond->removeConstraints(QueryMolecule::BOND_ORDER); AutoPtr<QueryMolecule::Bond> arom_bond( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)); qmol.resetBond(e_idx, QueryMolecule::Bond::und(bond.release(), arom_bond.release())); } else _submolecule->asMolecule().setBondOrder(e_idx, BOND_SINGLE, false); is_edge_in_aromatic_cycle[e_idx] = AROMATIC_BOND_NOT_IN_AROMATIC_CYCLE; } } // 2. Try to find suitable dearomatization QS_DEF(DearomatizationsStorage, dearomatizations); // Dearomatizer and DearomatizationMatcher will be created on demand Obj<Dearomatizer> dearomatizer; Obj<DearomatizationMatcher> dearomatizationMatcher; // Check edges for (int e = _submolecule->edgeBegin(); e != _submolecule->edgeEnd(); e = _submolecule->edgeNext(e)) { const Edge &edge = _submolecule->getEdge(e); int target_edge_index = _base.findEdgeIndex(mapping[edge.beg], mapping[edge.end]); const Edge &target_edge = _base.getEdge(target_edge_index); int query_edge_index = _query.findEdgeIndex(core_super[target_edge.beg], core_super[target_edge.end]); if (query_edge_index == -1) throw Error("(AromaticityMatcher::match) query edge wasn't found"); if (_matching_edges_state[query_edge_index] != AROMATIC) // Such target bond isn't aromatic. So we don't need to fix such bond continue; QueryMolecule::Bond &qbond = _query.getBond(query_edge_index); bool can_have_arom_order = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_AROMATIC, QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); if (can_have_arom_order) // This query bond is aromatic. continue; if (is_edge_in_aromatic_cycle[e] == AROMATIC_BOND_NOT_IN_AROMATIC_CYCLE) { // Such bond cannot be aromatic without aromatic // cycle because 'can_have_arom_order' is false return false; } bool has_other = !qbond.hasNoConstraintExcept(QueryMolecule::BOND_ORDER, QueryMolecule::BOND_TOPOLOGY); if (has_other) throw Error("Only bond with order and topology constraints are supported"); bool can_have_single = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_SINGLE, QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); bool can_have_double = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_DOUBLE, QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); // Check if query bond can be only single or only double if (can_have_single && can_have_double) // Don't fix such bond. It can be single/double, single/aromatic and etc. continue; if (!can_have_single && !can_have_double) // Bond without and specification and etc. continue; // Find dearomatization if (dearomatizer.get() == NULL) { dearomatizer.create(_submolecule.ref(), external_conn.ptr(), _arom_options); dearomatizer->enumerateDearomatizations(dearomatizations); dearomatizationMatcher.create(dearomatizations, _submolecule.ref(), external_conn.ptr()); } // Fix bond int type = can_have_single ? BOND_SINGLE : BOND_DOUBLE; if (!dearomatizationMatcher->isAbleToFixBond(e, type)) return false; dearomatizationMatcher->fixBond(e, type); } return true; }
void CrfSaver::_writeMolecule (Molecule &molecule) { Obj<CmfSaver> saver; int i; if (_encoder.get() != 0) saver.create(_encoder.ref()); else saver.create(_output); QS_DEF(Array<int>, atom_flags); QS_DEF(Array<int>, bond_flags); if (_atom_stereo_flags != 0) { atom_flags.clear_resize(molecule.vertexEnd()); atom_flags.zerofill(); for (i = molecule.vertexBegin(); i != molecule.vertexEnd(); i = molecule.vertexNext(i)) if (_atom_stereo_flags[i] == STEREO_RETAINS) atom_flags[i] = 1; else if (_atom_stereo_flags[i] == STEREO_INVERTS) atom_flags[i] = 2; saver->atom_flags = atom_flags.ptr(); } if (_bond_rc_flags != 0) { bond_flags.clear_resize(molecule.edgeEnd()); bond_flags.zerofill(); for (i = molecule.edgeBegin(); i != molecule.edgeEnd(); i = molecule.edgeNext(i)) { if (_bond_rc_flags[i] & RC_UNCHANGED) bond_flags[i] |= 1; if (_bond_rc_flags[i] & RC_MADE_OR_BROKEN) bond_flags[i] |= 2; if (_bond_rc_flags[i] & RC_ORDER_CHANGED) bond_flags[i] |= 4; } saver->bond_flags = bond_flags.ptr(); } saver->save_bond_dirs = save_bond_dirs; saver->save_highlighting = save_highlighting; saver->save_mapping = save_mapping; saver->saveMolecule(molecule); if (_aam != 0) _writeAam(_aam, saver->getAtomSequence()); if (xyz_output != 0) { if (xyz_output == &_output && _encoder.get() != 0) _encoder->finish(); saver->saveXyz(*xyz_output); if (xyz_output == &_output && _encoder.get() != 0) _encoder->start(); } }
void CrfLoader::_loadMolecule (Molecule &molecule) { Obj<CmfLoader> loader; int i; if (_decoder.get() != 0) loader.create(_decoder.ref()); else loader.create(_scanner); QS_DEF(Array<int>, atom_flags); QS_DEF(Array<int>, bond_flags); loader->atom_flags = &atom_flags; loader->bond_flags = &bond_flags; loader->version = version; loader->loadMolecule(molecule); bool has_mapping = loader->has_mapping; if (_atom_stereo_flags != 0) { _atom_stereo_flags->clear_resize(molecule.vertexCount()); _atom_stereo_flags->zerofill(); for (i = 0; i < molecule.vertexCount(); i++) { int idx = i; if (has_mapping) idx = loader->inv_atom_mapping_to_restore[i]; if (atom_flags[i] & 1) _atom_stereo_flags->at(idx) |= STEREO_RETAINS; if (atom_flags[i] & 2) _atom_stereo_flags->at(idx) |= STEREO_INVERTS; } } if (_bond_rc_flags != 0) { _bond_rc_flags->clear_resize(molecule.edgeCount()); _bond_rc_flags->zerofill(); for (i = 0; i < molecule.edgeCount(); i++) { int idx = i; if (has_mapping) idx = loader->inv_bond_mapping_to_restore[i]; if (bond_flags[i] & 1) _bond_rc_flags->at(idx) |= RC_UNCHANGED; if (bond_flags[i] & 2) _bond_rc_flags->at(idx) |= RC_MADE_OR_BROKEN; if (bond_flags[i] & 4) _bond_rc_flags->at(idx) |= RC_ORDER_CHANGED; } } if (_aam != 0) { _aam->clear_resize(molecule.vertexCount()); _aam->zerofill(); for (i = 0; i < molecule.vertexCount(); i++) { int value; if (_decoder.get() != 0) value = _decoder->get(); else value = _scanner.readByte(); int idx = i; if (has_mapping) idx = loader->inv_atom_mapping_to_restore[i]; _aam->at(idx) = value - 1; } } if (xyz_scanner != 0) loader->loadXyz(*xyz_scanner); }