Beispiel #1
0
int MangoExact::vertexCode (Molecule &mol, int vertex_idx)
{
   if (mol.isPseudoAtom(vertex_idx))
      return CRC32::get(mol.getPseudoAtom(vertex_idx));

   if (mol.isRSite(vertex_idx))
      return ELEM_RSITE;

   return mol.getAtomNumber(vertex_idx);
}
Beispiel #2
0
void CmfSaver::_encodeAtom (Molecule &mol, int idx, const int *mapping)
{
   int number = 0;

   if (mol.isPseudoAtom(idx))
   {
      const char *str = mol.getPseudoAtom(idx);
      size_t len = strlen(str);

      if (len < 1)
         throw Error("empty pseudo-atom");
      if (len > 255)
         throw Error("pseudo-atom labels %d characters long are not supported (255 is the limit)", len);

      _encode(CMF_PSEUDOATOM);
      _encode((byte)len);
      
      do
      {
         _encode(*str);
      } while (*(++str) != 0);
   }
   else if (mol.isRSite(idx))
   {
      int bits = mol.getRSiteBits(idx);
      if (bits > 255)
      {
         _encode(CMF_RSITE_EXT);
         _output->writePackedUInt((unsigned int)bits);
      }
      else
      {
         _encode(CMF_RSITE);
         _encode(bits);
      }
   }
   else
   {
      number = mol.getAtomNumber(idx);

      if (number <= 0 || number >= ELEM_MAX)
         throw Error("unexpected atom label");

      _encode(number);
   }

   int charge = mol.getAtomCharge(idx);

   if (charge != 0)
   {
      int charge2 = charge - CMF_MIN_CHARGE;
         
      if (charge2 < 0 || charge2 >= CMF_NUM_OF_CHARGES)
      {
         _encode(CMF_CHARGE_EXT);
         int charge3 = charge + 128;
         if (charge3 < 0 || charge >= 256)
            throw Error("unexpected atom charge: %d", charge);
         _encode(charge3);
      }
      else
         _encode(charge2 + CMF_CHARGES);
   }

   int isotope = mol.getAtomIsotope(idx);

   if (isotope > 0)
   {
      int deviation = isotope - Element::getDefaultIsotope(number);

      if (deviation == 0)
         _encode(CMF_ISOTOPE_ZERO);
      else if (deviation == 1)
         _encode(CMF_ISOTOPE_PLUS1);
      else if (deviation == 2)
         _encode(CMF_ISOTOPE_PLUS2);
      else if (deviation == -1)
         _encode(CMF_ISOTOPE_MINUS1);
      else if (deviation == -2)
         _encode(CMF_ISOTOPE_MINUS2);
      else
      {
         deviation += 100;
         if (deviation < 0 || deviation > 255)
            throw Error("unexpected %s isotope: %d", Element::toString(number), isotope);
         _encode(CMF_ISOTOPE_OTHER);
         _encode(deviation);
      }
   }

   int radical = 0;

   if (!mol.isPseudoAtom(idx) && !mol.isRSite(idx))
   {
      try
      {
         radical = mol.getAtomRadical(idx);
      }
      catch (Element::Error)
      {
      }
   }

   if (radical > 0)
   {
      if (radical == RADICAL_SINGLET)
         _encode(CMF_RADICAL_SINGLET);
      else if (radical == RADICAL_DOUBLET)
         _encode(CMF_RADICAL_DOUBLET);
      else if (radical == RADICAL_TRIPLET)
         _encode(CMF_RADICAL_TRIPLET);
      else
         throw Error("bad radical value: %d", radical);
   }
   
   MoleculeStereocenters &stereo = mol.stereocenters;
   
   int stereo_type = stereo.getType(idx);
   
   if (stereo_type == MoleculeStereocenters::ATOM_ANY)
      _encode(CMF_STEREO_ANY);
   else if (stereo_type != 0)
   {
      bool rigid;
      int code;
      const int *pyramid = stereo.getPyramid(idx);
      
      if (pyramid[3] == -1)
         rigid = MoleculeStereocenters::isPyramidMappingRigid(pyramid, 3, mapping);
      else
         rigid = MoleculeStereocenters::isPyramidMappingRigid(pyramid, 4, mapping);
      
      if (stereo_type == MoleculeStereocenters::ATOM_ABS)
         code = CMF_STEREO_ABS_0;
      else 
      {
         int group = stereo.getGroup(idx);

         if (group < 1 || group > CMF_MAX_STEREOGROUPS)
            throw Error("stereogroup number %d out of range", group);

         if (stereo_type == MoleculeStereocenters::ATOM_AND)
            code = CMF_STEREO_AND_0 + group - 1;
         else // stereo_type == MoleculeStereocenters::ATOM_OR
            code = CMF_STEREO_OR_0 + group - 1;
      }
      
      if (!rigid)
         // CMF_STEREO_*_0 -> CMF_STEREO_*_1
         code += CMF_MAX_STEREOGROUPS * 2 + 1;
      
      _encode(code);
   }

   if (mol.allene_stereo.isCenter(idx))
   {
      int left, right, parity, subst[4];

      mol.allene_stereo.getByAtomIdx(idx, left, right, subst, parity);
      if (subst[1] != -1 && mapping[subst[1]] != -1 && mapping[subst[1]] < mapping[subst[0]])
         parity = 3 - parity;
      if (subst[3] != -1 && mapping[subst[3]] != -1 && mapping[subst[3]] < mapping[subst[2]])
         parity = 3 - parity;
      if (parity == 1)
         _encode(CMF_STEREO_ALLENE_0);
      else
         _encode(CMF_STEREO_ALLENE_1);
   }


   int impl_h = 0;

   if (!mol.isPseudoAtom(idx) && !mol.isRSite(idx) && Molecule::shouldWriteHCount(mol, idx))
   {
      try
      {
         impl_h = mol.getImplicitH(idx);

         if (impl_h < 0 || impl_h > CMF_MAX_IMPLICIT_H)
            throw Error("implicit hydrogen count %d out of range", impl_h);

         _encode(CMF_IMPLICIT_H + impl_h);
      }
      catch (Element::Error)
      {
      }
   }

   if (!mol.isRSite(idx) && !mol.isPseudoAtom(idx))
   {
      if (mol.getAtomAromaticity(idx) == ATOM_AROMATIC && (charge != 0 || (number != ELEM_C && number != ELEM_O)))
      {
         try
         {
            int valence = mol.getAtomValence(idx);
            if (valence < 0 || valence > CMF_MAX_VALENCE)
            {
               _encode(CMF_VALENCE_EXT);
               _output->writePackedUInt(valence);
            }
            else
               _encode(CMF_VALENCE + valence);
         }
         catch (Element::Error)
         {
         }
      }
   }

   int i;

   for (i = 1; i <= mol.attachmentPointCount(); i++)
   {
      int j, aidx;

      for (j = 0; (aidx = mol.getAttachmentPoint(i, j)) != -1; j++)
         if (aidx == idx)
         {
            _encode(CMF_ATTACHPT);
            _encode(i);
         }
   }

   if (atom_flags != 0)
   {
      int i, flags = atom_flags[idx];

      for (i = 0; i < CMF_NUM_OF_ATOM_FLAGS; i++)
         if (flags & (1 << i))
            _encode(CMF_ATOM_FLAGS + i);
   }

   if (save_highlighting)
      if (mol.isAtomHighlighted(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;
}
Beispiel #4
0
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();
}