void IndigoInchi::neutralizeV5Nitrogen (Molecule &mol) { // Initial structure C[C@H](O)[C@H](COC)/N=[N+](\[O-])/C=CCCCCCC // is loaded via InChI as CCCCCCC=CN(=O)=N[C@@H](COC)[C@H](C)O // and we cannot restore cis-trans configuration for O=N=N-C bond for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) if (mol.isNitrogenV5(v)) { const Vertex &vertex = mol.getVertex(v); for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int nei_edge = vertex.neiEdge(nei); if (mol.getBondOrder(nei_edge) != BOND_DOUBLE) continue; int nei_idx = vertex.neiVertex(nei); int number = mol.getAtomNumber(nei_idx); int charge = mol.getAtomCharge(nei_idx); int radical = mol.getAtomRadical(nei_idx); if ((number == ELEM_O || number == ELEM_S) && charge == 0 && radical == 0) { mol.setAtomCharge(v, 1); mol.setAtomCharge(nei_idx, -1); mol.setBondOrder(nei_edge, BOND_SINGLE); break; } } } }
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); }
void MoleculeAutomorphismSearch::_markComplicatedStereocentersAsValid (Molecule &mol) { // If there is more then 1 unknown stereocenter in the biconnected // component then validity of such stereocenters cannot be found // with current methods. For example C[C@H]1C[C@@H](C)C[C@H](C)C1 // can lead to CC1C[C@H](C)C[C@@H](C)C1 or CC1C[C@@H](C)C[C@H](C)C1, // but canonical codes are different for such molecules. QS_DEF(Array<int>, single_bond_bridge_mark); single_bond_bridge_mark.resize(mol.edgeEnd()); single_bond_bridge_mark.fill(1); SpanningTree sp_tree(mol, 0); sp_tree.markAllEdgesInCycles(single_bond_bridge_mark.ptr(), 0); for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i++) if (mol.getBondOrder(i) != BOND_SINGLE) single_bond_bridge_mark[i] = 0; Filter edge_filter(single_bond_bridge_mark.ptr(), Filter::NEQ, 1); GraphDecomposer decomposer(mol); decomposer.decompose(0, &edge_filter); const Array<int> &decomposition = decomposer.getDecomposition(); QS_DEF(Array<int>, undef_stereo_in_component); undef_stereo_in_component.clear(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int comp = decomposition[v]; if (comp < 0) continue; while (undef_stereo_in_component.size() <= comp) undef_stereo_in_component.push(0); if (_stereocenter_state[v] == _UNDEF) undef_stereo_in_component[comp]++; } // Mark stereocenters as valid if there are more then 1 // undefined stereocenter in the component for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int comp = decomposition[v]; if (comp < 0) continue; if (_stereocenter_state[v] == _UNDEF && undef_stereo_in_component[comp] > 1) _stereocenter_state[v] = _VALID; } }
int MoleculeAutomorphismSearch::_getMappedBondOrderAndParity (Molecule &m, int e, Array<int>& inv_mapping) const { int type = m.getBondOrder(e); // Map parities to the provided new mapping int parity = m.cis_trans.getParity(e); if (parity != 0 && _getStereo(_cistrans_bond_state[e]) == _VALID) { int parity_mapped = MoleculeCisTrans::applyMapping(parity, m.cis_trans.getSubstituents(e), inv_mapping.ptr(), true); type = type * 100 + parity_mapped; } return type; }
void MoleculePiSystemsMatcher::_calcConnectivity (Molecule &mol, Array<int> &conn) { conn.clear_resize(mol.vertexEnd()); conn.zerofill(); for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) { int bond_order = mol.getBondOrder(e); const Edge &edge = mol.getEdge(e); conn[edge.beg] += bond_order; conn[edge.end] += bond_order; } for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) if (!mol.isPseudoAtom(v) && !mol.isRSite(v)) conn[v] += mol.getImplicitH(v); }
void CmfSaver::_encodeBond (Molecule &mol, int idx, const int *mapping) { int order = mol.getBondOrder(idx); if (order == BOND_SINGLE) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_SINGLE_RING); else _encode(CMF_BOND_SINGLE_CHAIN); } else if (order == BOND_DOUBLE) { int parity = mol.cis_trans.getParity(idx); if (parity != 0) { int mapped_parity = MoleculeCisTrans::applyMapping(parity, mol.cis_trans.getSubstituents(idx), mapping, true); if (mapped_parity == MoleculeCisTrans::CIS) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_RING_CIS); else _encode(CMF_BOND_DOUBLE_CHAIN_CIS); } else // mapped_parity == MoleculeCisTrans::TRANS { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_RING_TRANS); else _encode(CMF_BOND_DOUBLE_CHAIN_TRANS); } } else if (mol.cis_trans.isIgnored(idx)) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_RING); else _encode(CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_CHAIN); } else { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_RING); else _encode(CMF_BOND_DOUBLE_CHAIN); } } else if (order == BOND_TRIPLE) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_TRIPLE_RING); else _encode(CMF_BOND_TRIPLE_CHAIN); } else if (order == BOND_AROMATIC) _encode(CMF_BOND_AROMATIC); else throw Error("bad bond order: %d", order); if (bond_flags != 0) { int i, flags = bond_flags[idx]; for (i = 0; i < CMF_NUM_OF_BOND_FLAGS; i++) if (flags & (1 << i)) _encode(CMF_BOND_FLAGS + i); } if (save_highlighting) if (mol.isBondHighlighted(idx)) _encode(CMF_HIGHLIGHTED); }
bool AbbreviationExpander::expandAtomAbbreviation (Molecule &mol, int v) { // Determine bonds configuration: number of bonds on the left and on the right Vec3f &pos = mol.getAtomXyz(v); const Vertex &vertex = mol.getVertex(v); int count_left = 0, count_right = 0, count_middle = 0; Array<int> left_atoms, right_atoms; for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int nei_atom = vertex.neiVertex(nei); Vec3f &nei_pos = mol.getAtomXyz(nei_atom); int order = mol.getBondOrder(vertex.neiEdge(nei)); if (nei_pos.x < pos.x) { count_left += order; left_atoms.push(nei_atom); } else { count_right += order; right_atoms.push(nei_atom); } Vec3f diff = nei_pos; diff.sub(pos); if (fabs(diff.y) > fabs(diff.x)) count_middle += order; } int input_order, output_order; bool on_left = false, on_right = false, is_single = false; if (vertex.degree() == 1) { if (count_middle) on_left = on_right = true; if (count_left) on_right = true; if (count_right) on_left = true; input_order = std::max(count_left, count_right); output_order = 0; is_single = true; } else { on_right = true; input_order = count_left; output_order = count_right; } // Try to expand according to the directions Array<Options> options; if (on_right && !on_left) { options.push(Options(RIGHT, RIGHT)); options.push(Options(LEFT, LEFT)); } else { if (on_right) options.push(Options(RIGHT, RIGHT)); if (on_left) { options.push(Options(LEFT, LEFT)); options.push(Options(LEFT, RIGHT)); options.push(Options(RIGHT, RIGHT)); options.push(Options(LEFT, LEFT, 2)); } } // Duplicate all the options with ignore case flag int opt_cnt = options.size(); for (int i = 0; i < opt_cnt; i++) { Options opt = options[i]; opt.ignore_case = true; options.push(opt); } bool found = false; Molecule expanded; for (int i = 0; i < options.size(); i++) { options[i].set(*this); if (expand(mol.getPseudoAtom(v), input_order, output_order, expanded)) { found = true; break; } } if (!found) return false; // Merge expanded abbreviation with the source molecule and connect bonds Array<int> mapping; mol.mergeWithMolecule(expanded, &mapping); //if (right_atoms.size() == 0) // right_atoms.swap(left_atoms); for (int i = 0; i < left_atoms.size(); i++) mol.flipBond(left_atoms[i], v, mapping[input_index]); int target_end = output_index; if (is_single == 1) target_end = input_index; for (int i = 0; i < right_atoms.size(); i++) mol.flipBond(right_atoms[i], v, mapping[target_end]); // Collect added atoms and set their coordinate to the initial atom // Layout procedure will find correct atom coordinates, but neighbours // should know correct relative position for (int ve = expanded.vertexBegin(); ve != expanded.vertexEnd(); ve = expanded.vertexNext(ve)) { int idx = mapping[ve]; mol.setAtomXyz(idx, mol.getAtomXyz(v)); added_atoms.push(mapping[ve]); } int sid = mol.sgroups.addSGroup(SGroup::SG_TYPE_SUP); Superatom &super = (Superatom &)mol.sgroups.getSGroup(sid); super.subscript.readString(mol.getPseudoAtom(v), true); for (int ve = expanded.vertexBegin(); ve != expanded.vertexEnd(); ve = expanded.vertexNext(ve)) super.atoms.push(mapping[ve]); mol.removeAtom(v); return true; }
void IndigoInchi::generateInchiInput (Molecule &mol, inchi_Input &input, Array<inchi_Atom> &atoms, Array<inchi_Stereo0D> &stereo) { QS_DEF(Array<int>, mapping); mapping.clear_resize(mol.vertexEnd()); mapping.fffill(); int index = 0; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) mapping[v] = index++; atoms.clear_resize(index); atoms.zerofill(); stereo.clear(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { inchi_Atom &atom = atoms[mapping[v]]; int atom_number = mol.getAtomNumber(v); if (atom_number == ELEM_PSEUDO) throw IndigoError("Molecule with pseudoatom (%s) cannot be converted into InChI", mol.getPseudoAtom(v)); if (atom_number == ELEM_RSITE) throw IndigoError("Molecule with RGroups cannot be converted into InChI"); strncpy(atom.elname, Element::toString(atom_number), ATOM_EL_LEN); Vec3f &c = mol.getAtomXyz(v); atom.x = c.x; atom.y = c.y; atom.z = c.z; // connectivity const Vertex &vtx = mol.getVertex(v); int nei_idx = 0; for (int nei = vtx.neiBegin(); nei != vtx.neiEnd(); nei = vtx.neiNext(nei)) { int v_nei = vtx.neiVertex(nei); atom.neighbor[nei_idx] = mapping[v_nei]; int edge_idx = vtx.neiEdge(nei); atom.bond_type[nei_idx] = getInchiBondType(mol.getBondOrder(edge_idx)); int bond_stereo = INCHI_BOND_STEREO_NONE; if (mol.cis_trans.isIgnored(edge_idx)) bond_stereo = INCHI_BOND_STEREO_DOUBLE_EITHER; else { int dir = mol.getBondDirection2(v, v_nei); if (mol.getBondDirection2(v, v_nei) == BOND_EITHER) bond_stereo = INCHI_BOND_STEREO_SINGLE_1EITHER; else if (mol.getBondDirection2(v_nei, v) == BOND_EITHER) bond_stereo = INCHI_BOND_STEREO_SINGLE_2EITHER; } atom.bond_stereo[nei_idx] = bond_stereo; nei_idx++; } atom.num_bonds = vtx.degree(); // Other properties atom.isotopic_mass = mol.getAtomIsotope(v); atom.radical = mol.getAtomRadical(v); atom.charge = mol.getAtomCharge(v); // Hydrogens int hcount = -1; if (Molecule::shouldWriteHCount(mol, v) || mol.isExplicitValenceSet(v) || mol.isImplicitHSet(v)) { if (mol.getAtomAromaticity(v) == ATOM_AROMATIC && atom_number == ELEM_C && atom.charge == 0 && atom.radical == 0) { // Do not set number of implicit hydrogens here as InChI throws an exception on // the molecule B1=CB=c2cc3B=CC=c3cc12 ; } else // set -1 to tell InChI add implicit hydrogens automatically hcount = mol.getImplicitH_NoThrow(v, -1); } atom.num_iso_H[0] = hcount; } // Process cis-trans bonds for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) { if (mol.cis_trans.getParity(e) == 0) continue; int subst[4]; mol.cis_trans.getSubstituents_All(e, subst); const Edge &edge = mol.getEdge(e); inchi_Stereo0D &st = stereo.push(); // Write it as // #0 - #1 = #2 - #3 st.neighbor[0] = mapping[subst[0]]; st.neighbor[1] = mapping[edge.beg]; st.neighbor[2] = mapping[edge.end]; st.neighbor[3] = mapping[subst[2]]; if (mol.cis_trans.getParity(e) == MoleculeCisTrans::CIS) st.parity = INCHI_PARITY_ODD; else st.parity = INCHI_PARITY_EVEN; st.central_atom = NO_ATOM; st.type = INCHI_StereoType_DoubleBond; } // Process tetrahedral stereocenters for (int i = mol.stereocenters.begin(); i != mol.stereocenters.end(); i = mol.stereocenters.next(i)) { int v = mol.stereocenters.getAtomIndex(i); int type, group, pyramid[4]; mol.stereocenters.get(v, type, group, pyramid); if (type == MoleculeStereocenters::ATOM_ANY) continue; for (int i = 0; i < 4; i++) if (pyramid[i] != -1) pyramid[i] = mapping[pyramid[i]]; inchi_Stereo0D &st = stereo.push(); /* 4 neighbors X neighbor[4] : {#W, #X, #Y, #Z} | central_atom: #A W--A--Y type : INCHI_StereoType_Tetrahedral | Z parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' 3 neighbors Y Y neighbor[4] : {#A, #X, #Y, #Z} / / central_atom: #A X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral \ \ Z Z */ int offset = 0; if (pyramid[3] == -1) offset = 1; st.neighbor[offset] = mapping[pyramid[0]]; st.neighbor[offset + 1] = mapping[pyramid[1]]; st.neighbor[offset + 2] = mapping[pyramid[2]]; if (offset == 0) st.neighbor[3] = mapping[pyramid[3]]; else st.neighbor[0] = mapping[v]; st.parity = INCHI_PARITY_ODD; if (offset != 0) st.parity = INCHI_PARITY_ODD; else st.parity = INCHI_PARITY_EVEN; st.central_atom = mapping[v]; st.type = INCHI_StereoType_Tetrahedral; } input.atom = atoms.ptr(); input.num_atoms = atoms.size(); input.stereo0D = stereo.ptr(); input.num_stereo0D = stereo.size(); input.szOptions = options.ptr(); }