1 /**********************************************************************
2 Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
3 Some portions Copyright (C) 2003-2006 Geoffrey R. Hutchison
4 Some portions Copyright (C) 2004 by Chris Morley
5 
6 Original Copyright refers to the pdbformat.cpp file, for reading and
7 writing pdb format files.
8 Extensively modified 2010 Stuart Armstrong (Source Science/InhibOx)
9 for the purpose of reading and writing pdbqt format files.
10 Some portions Copyright (C) 2010 by Stuart Armstrong of Source Science/
11 InhibOx
12 
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation version 2 of the License.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 GNU General Public License for more details.
21 ***********************************************************************/
22 
23 #include <openbabel/babelconfig.h>
24 #include <openbabel/obmolecformat.h>
25 #include <openbabel/mol.h>
26 #include <openbabel/atom.h>
27 #include <openbabel/elements.h>
28 #include <openbabel/generic.h>
29 #include <openbabel/bond.h>
30 #include <openbabel/data.h>
31 #include <openbabel/obiter.h>
32 #include <openbabel/typer.h>
33 
34 #include <algorithm>
35 #include <cstdlib>
36 #include <vector>
37 #include <map>
38 #include <set>
39 
40 #include <sstream>
41 
42 using namespace std;
43 namespace OpenBabel
44 {
45   class branch
46   {
47     public:
48     vector <int> atoms;
49     bool done;
50     unsigned int index;
51     set <unsigned int> children;
52     vector <unsigned int> parents;
53     unsigned int depth;
54     unsigned int connecting_atom_parent;
55     unsigned int connecting_atom_branch;
56     unsigned int how_many_atoms_moved;
57 
58     set <unsigned int> rigid_with; //the other branches that move rigidly with this one
59 
clear()60     void clear() {done=false; index=0; depth=0; connecting_atom_parent=0; connecting_atom_branch=0;
61       how_many_atoms_moved=0; children.clear(); parents.clear(); atoms.clear(); rigid_with.clear(); parents.push_back(0);}
UpOne()62     unsigned int UpOne() {if (parents.size()>=2) {return parents.at(parents.size()-2);} return 0;}
branch()63     branch() {clear();}
all_atoms(OBMol & mol)64     void all_atoms(OBMol& mol) {clear(); rigid_with.insert(0); for (unsigned int i=1; i <= mol.NumAtoms(); i++) {atoms.push_back(i);}}
65   };
66 
67   class PDBQTFormat : public OBMoleculeFormat
68   {
69     public:
70     //Register this format type ID
PDBQTFormat()71     PDBQTFormat()
72     {
73       OBConversion::RegisterFormat("pdbqt",this, "chemical/x-pdbqt");
74     }
75 
Description()76     virtual const char* Description() //required
77     {
78       return
79 
80       "AutoDock PDBQT format\n"
81       "Reads and writes AutoDock PDBQT (Protein Data Bank, Partial Charge (Q), & Atom Type (T)) format\n"
82       "Note that the torsion tree is by default. Use the ``r`` write option\n"
83       "to prevent this.\n\n"
84 
85       "Read Options, e.g. -ab\n"
86       "  b  Disable automatic bonding\n"
87       "  d  Input file is in dlg (AutoDock docking log) format\n\n"
88 
89       "Write Options, e.g. -xr\n"
90       "  b  Enable automatic bonding\n"
91       "  r  Output as a rigid molecule (i.e. no branches or torsion tree)\n"
92       "  c  Combine separate molecular pieces of input into a single rigid molecule (requires \"r\" option or will have no effect)\n"
93       "  s  Output as a flexible residue\n"
94       "  p  Preserve atom indices from input file (default is to renumber atoms sequentially)\n"
95       "  h  Preserve hydrogens\n"
96 			"  n  Preserve atom names\n\n";
97     };
98 
SpecificationURL()99     virtual const char* SpecificationURL()
100       {return "http://autodock.scripps.edu/faqs-help/faq/what-is-the-format-of-a-pdbqt-file";};
101 
GetMIMEType()102     virtual const char* GetMIMEType()
103       {return "chemical/x-pdbqt";};
104 
105     //*** This section identical for most OBMol conversions ***
106     ////////////////////////////////////////////////////
107     /// The "API" interface functions
108     virtual int SkipObjects(int n, OBConversion* pConv);
109     virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
110     virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
111 
112   };
113   //***
114   //Make an instance of the format class
115   PDBQTFormat thePDBQTFormat;
116 
117   ////////////////////////////////////////////////////
118   /// Utility functions
119   static bool parseAtomRecord(char *buffer, OBMol & mol, int chainNum);
120   static bool IsRotBond_PDBQT(OBBond * the_bond);
121   static bool IsIn(const vector<int>& vec, const int num);
122   static void OutputAtom(OBAtom* atom, ostream& ofs, unsigned int index);
123   static void OutputGroup(OBMol& mol, ostream& ofs, const vector <int>& group, map <unsigned int, unsigned int> new_indexes, bool use_new_indexes);
124   static unsigned int AtomsSoFar(const map <unsigned int, branch >& tree, unsigned int depth);
125   static bool FindBondedPiece(const vector<int>& root, const vector<int>& branch, unsigned int& root_atom, unsigned int& branch_atom,
126                 unsigned int& root_atom_rank, unsigned int& branch_atom_rank, const OBMol& mol, unsigned int & atoms_moved);
127   static bool OutputTree(OBConversion *pConv, OBMol& mol, ostream& ofs, map <unsigned int, branch >& tree, unsigned int depth, bool moves_many, bool preserve_original_index);
128   static void ConstructTree (map <unsigned int, branch >& tree, vector <vector <int> > rigid_fragments, unsigned int root_piece, const OBMol& mol, bool flexible);
129   static bool DeleteHydrogens(OBMol & mol);
130   static bool Separate_preserve_charges(OBMol & mol, vector<OBMol> & result);
131   static unsigned int FindFragments(OBMol mol, vector <vector <int> >& rigid_fragments);
132   static unsigned int RotBond_count(OBMol & mol);
133 
134   /////////////////////////////////////////////////////////////////
SkipObjects(int n,OBConversion * pConv)135   int PDBQTFormat::SkipObjects(int n, OBConversion* pConv)
136   {
137     if (n == 0)
138     ++ n;
139     istream &ifs = *pConv->GetInStream();
140     char buffer[BUFF_SIZE];
141     while (n && ifs.getline(buffer,BUFF_SIZE))
142     {
143       if (EQn(buffer,"ENDMDL",6)) {-- n;}
144     }
145 
146     return ifs.good() ? 1 : -1;
147   }
148   /////////////////////////////////////////////////////////////////
ReadMolecule(OBBase * pOb,OBConversion * pConv)149   bool PDBQTFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
150   {
151     OBMol* pmol = pOb->CastAndClear<OBMol>();
152     if (pmol == nullptr)
153     return false;
154 
155 
156     //Define some references so we can use the old parameter names
157     istream &ifs = *pConv->GetInStream();
158     OBMol &mol = *pmol;
159     const char* title = pConv->GetTitle();
160 
161     bool dlg=false;
162     if (pConv->IsOption("d",OBConversion::INOPTIONS)) {dlg=true;} //check whether we have a file in dlg format
163 
164     int chainNum = 1;
165     char buffer[BUFF_SIZE];
166     string line, key, value;
167     OBPairData *dp;
168 
169     mol.SetTitle(title);
170     mol.SetChainsPerceived(); // It's a PDBQT file, we read all chain/res info.
171 
172     mol.BeginModify();
173     while (ifs.good() && ifs.getline(buffer,BUFF_SIZE))
174     {
175       line = buffer;
176       if (dlg) //if we have a dlg file, only care about the lines starting with "DOCKED: "
177       {
178         if (!EQn(buffer,"DOCKED: ",8)) {continue;}
179         else
180         {
181           for (unsigned int i=0; i<BUFF_SIZE-8; i++)
182           {
183             buffer[i]=buffer[i+8];
184             if (buffer[i]=='\0') {break;}
185           }
186         }
187       }
188       if (line.length() == 0)
189       {
190         stringstream errorMsg;
191         errorMsg << "Warning: empty line, ignoring." << endl;
192         obErrorLog.ThrowError(__FUNCTION__, errorMsg.str() , obWarning);
193         continue;
194       }
195       if (line.length() < 3)
196       {
197         stringstream errorMsg;
198         errorMsg << "ERROR: not a valid PDBQT file" << endl;
199         obErrorLog.ThrowError(__FUNCTION__, errorMsg.str() , obError);
200         return false;
201       }
202       if (EQn(buffer,"ROOT",4)) {continue;}
203       if (EQn(buffer,"ENDROOT",7)) {continue;}
204       if (EQn(buffer,"BRANCH",6)) {continue;}
205       if (EQn(buffer,"ENDBRANCH",9)) {continue;}
206 
207       if (EQn(buffer,"ENDMDL",6)) {break;}
208       if (EQn(buffer,"END_RES",7)) {break;}
209 
210       if (EQn(buffer,"END",3))
211       {
212         // eat anything until the next ENDMDL
213         while (ifs.getline(buffer,BUFF_SIZE) && !EQn(buffer,"ENDMDL",6));
214         break;
215       }
216 
217       if (EQn(buffer,"ATOM",4) || EQn(buffer,"HETATM",6))
218       {
219         if( ! parseAtomRecord(buffer,mol,chainNum))
220         {
221           stringstream errorMsg;
222           errorMsg << "WARNING: Problems reading a PDBQT file\n"
223             << "  Problems reading a ATOM/HETATM record.\n"
224             << endl << buffer << endl;
225           obErrorLog.ThrowError(__FUNCTION__, errorMsg.str() , obError);
226         }
227         continue;
228       }
229       if ((EQn(buffer,"REMARK",6)) || (EQn(buffer,"USER",4)))
230       {
231         stringstream sst;
232         string buffstring=buffer;
233         sst.str(buffstring);
234         sst >> buffstring;
235         sst >> buffstring;
236         if (buffstring == "Name")
237         {
238           sst >> buffstring;
239           if (sst.good())
240           {
241             sst >> buffstring;
242             mol.SetTitle(buffstring);
243           }
244         }
245         else if (buffstring == "NEWDPF")
246         {
247           sst >> buffstring;
248           if (buffstring == "move")
249           {
250             if (sst.good())
251                                           {
252                                                   sst >> buffstring;
253                                                   mol.SetTitle(buffstring);
254                                           }
255           }
256         }
257       }
258 
259       if (line.length() <= 6)
260       {
261         continue;
262       }
263 
264 
265       key = line.substr(0,6); // the first 6 characters are the record name
266       Trim(key);
267       value = line.substr(6);
268 
269       // We haven't found this record yet
270       if (!mol.HasData(key))
271       {
272         dp = new OBPairData;
273         dp->SetAttribute(key);
274         dp->SetValue(value);
275         dp->SetOrigin(fileformatInput);
276         mol.SetData(dp);
277       }
278       // Add on additional lines
279       else
280       {
281         dp = static_cast<OBPairData*>(mol.GetData(key));
282         line = dp->GetValue();
283         line += '\n';
284         line += value;
285         dp->SetValue(line);
286       }
287     }
288 
289     if (!mol.NumAtoms())
290     { // skip the rest of this processing
291       mol.EndModify();
292       return(false);
293     }
294 
295     resdat.AssignBonds(mol);
296     /*assign hetatm bonds based on distance*/
297 
298     mol.EndModify();
299     // Clear all virtual bond data
300     vector<OBGenericData*> vbonds = mol.GetAllData(OBGenericDataType::VirtualBondData);
301     mol.DeleteData(vbonds);
302 
303     if (!pConv->IsOption("b",OBConversion::INOPTIONS)) {mol.ConnectTheDots(); mol.PerceiveBondOrders();}
304 
305     mol.SetChainsPerceived();
306 
307     // clean out remaining blank lines
308     std::streampos ipos;
309     do
310     {
311       ipos = ifs.tellg();
312       ifs.getline(buffer,BUFF_SIZE);
313     }
314     while(strlen(buffer) == 0 && !ifs.eof() );
315     ifs.seekg(ipos);
316 
317     mol.SetPartialChargesPerceived();
318     return(true);
319   }
320 
321   /////////////////////////////////////////////////////////////////////////
OutputAtom(OBAtom * atom,ostream & ofs,const unsigned int index)322   void OutputAtom(OBAtom* atom, ostream& ofs, const unsigned int index)
323   {
324     char buffer[BUFF_SIZE];
325     char type_name[10], padded_name[10];
326     char the_res[10];
327     char the_chain = ' ';
328     char the_icode = ' ';
329     const char *element_name;
330     string element_name_string;
331     int res_num;
332     bool het=false;
333 
334     OBResidue *res;
335     strncpy(type_name, OBElements::GetSymbol(atom->GetAtomicNum()), sizeof(type_name));
336     type_name[sizeof(type_name) - 1] = '\0';
337     //two char. elements are on position 13 and 14 one char. start at 14
338 
339     if (strlen(type_name) > 1)
340       type_name[1] = toupper(type_name[1]);
341     else
342     {
343       char tmp[10];
344       strncpy(tmp, type_name, 9); // Make sure to null-terminate
345       snprintf(type_name, sizeof(type_name), " %-3s", tmp);
346     }
347 
348     if ((res = atom->GetResidue()) != nullptr)
349     {
350       snprintf(the_res,4,"%s",(char*)res->GetName().c_str());
351       snprintf(type_name,5,"%s",(char*)res->GetAtomID(atom).c_str());
352       the_chain = res->GetChain();
353       the_icode = res->GetInsertionCode();
354       if(the_icode == 0) the_icode = ' ';
355 
356       //two char. elements are on position 13 and 14 one char. start at 14
357       if (strlen(OBElements::GetSymbol(atom->GetAtomicNum())) == 1)
358       {
359         if (strlen(type_name) < 4)
360         {
361           char tmp[10];
362           strncpy(tmp, type_name, 9); // make sure to null-terminate
363           snprintf(padded_name, sizeof(padded_name), " %-3s", tmp);
364           strncpy(type_name,padded_name,4);
365           type_name[4] = '\0';
366         }
367         else
368         {
369           type_name[4] = '\0';
370         }
371       }
372       res_num = res->GetNum();
373     }
374     else
375     {
376       strcpy(the_res,"UNK");
377       snprintf(padded_name,sizeof(padded_name), "%s",type_name);
378       strncpy(type_name,padded_name,4);
379       type_name[4] = '\0';
380       res_num = 1;
381     }
382 
383     element_name = OBElements::GetSymbol(atom->GetAtomicNum());
384     char element_name_final[3];
385     element_name_final[2] = '\0';
386 
387     if (atom->GetAtomicNum() == OBElements::Hydrogen) {element_name_final[0]='H'; element_name_final[1]='D';}
388     else if ((atom->GetAtomicNum() == OBElements::Carbon) && (atom->IsAromatic())) {element_name_final[0]='A'; element_name_final[1]=' ';}
389     else if (atom->GetAtomicNum() == OBElements::Oxygen)  {element_name_final[0]='O'; element_name_final[1]='A';}
390     else if ((atom->GetAtomicNum() == OBElements::Nitrogen) && (atom->IsHbondAcceptor())) {element_name_final[0]='N'; element_name_final[1]='A';}
391     else if ((atom->GetAtomicNum() == OBElements::Sulfur) && (atom->IsHbondAcceptor())) {element_name_final[0]='S'; element_name_final[1]='A';}
392     else
393     {
394       if (!isalnum(element_name[0])) {element_name_final[0]=' ';}
395       else element_name_final[0]=element_name[0];
396       if (!isalnum(element_name[1])) {element_name_final[1]=' ';}
397       else element_name_final[1]=element_name[1];
398     }
399 
400     double charge = atom->GetPartialCharge();
401     snprintf(buffer, BUFF_SIZE, "%s%5d %-4s %-3s %c%4d%c   %8.3f%8.3f%8.3f  0.00  0.00    %+5.3f %.2s",
402       het?"HETATM":"ATOM  ",
403       index,
404       type_name,
405       the_res,
406       the_chain,
407       res_num,
408       the_icode,
409       atom->GetX(),
410       atom->GetY(),
411       atom->GetZ(),
412       charge,
413       element_name_final);
414     ofs << buffer;
415     ofs << endl;
416   }
417 
OutputGroup(OBMol & mol,ostream & ofs,const vector<int> & group,map<unsigned int,unsigned int> new_indexes,bool use_new_indexes)418   void OutputGroup(OBMol& mol, ostream& ofs, const vector <int>& group, map <unsigned int, unsigned int> new_indexes, bool use_new_indexes)
419   {
420     for (vector <int>::const_iterator it = group.begin(); it != group.end(); ++it)
421     {
422       if (use_new_indexes) {OutputAtom(mol.GetAtom((*it)), ofs, new_indexes.find(*it)->second);}
423       else {OutputAtom(mol.GetAtom((*it)), ofs, (*it));}
424     }
425   }
426 
427 
FindFragments(OBMol mol,vector<vector<int>> & rigid_fragments)428   unsigned int FindFragments(OBMol mol, vector <vector <int> >& rigid_fragments)
429   {
430     unsigned int best_root_atom=1;
431     unsigned int shortest_maximal_remaining_subgraph=mol.NumAtoms();
432     for (unsigned int i=1; i <= mol.NumAtoms(); i++)
433     //finds the root atom by copying the molecule, deleting each atom in turn, and finding the sizes of the resulting pieces
434     {
435       OBMol mol_pieces = mol;
436       OBAtom * atom_to_del = mol_pieces.GetAtom(i);
437       vector <vector <int> > frag_list;
438 
439       mol_pieces.DeleteAtom(atom_to_del, true);
440       mol_pieces.ContigFragList(frag_list);
441       unsigned int smrsi=0;
442       for (unsigned int j = 0; j < frag_list.size(); j++)
443       {
444         smrsi= smrsi > frag_list.at(j).size() ? smrsi : frag_list.at(j).size();
445       }
446       if (smrsi < shortest_maximal_remaining_subgraph)
447       {
448         shortest_maximal_remaining_subgraph=smrsi;
449         best_root_atom=i;
450       }
451     }
452 
453     vector <OBBond*> bonds_to_delete;
454     OBMol mol_pieces = mol;
455     for (OBBondIterator it=mol_pieces.BeginBonds(); it != mol_pieces.EndBonds(); it++)
456     {
457       if (IsRotBond_PDBQT((*it)))
458       {
459         bonds_to_delete.push_back(*it);
460       }
461     }
462     for (vector<OBBond*>::iterator bit = bonds_to_delete.begin(); bit != bonds_to_delete.end(); ++bit)
463     {
464       mol_pieces.DeleteBond(*bit, true);
465     }
466     mol_pieces.ContigFragList(rigid_fragments);
467 
468     return best_root_atom;
469   }
470 
DeleteHydrogens(OBMol & mol)471   bool DeleteHydrogens(OBMol & mol)
472   {
473     for (OBAtomIterator it=mol.BeginAtoms(); it != mol.EndAtoms(); it++)
474     {
475       if ( (*it)->IsNonPolarHydrogen() )
476       {
477         OBBondIterator voider;
478         double charger = (*it)->GetPartialCharge();
479         charger += ((*it)->BeginNbrAtom(voider))->GetPartialCharge();
480         ((*it)->BeginNbrAtom(voider))->SetPartialCharge(charger);
481       }
482     }
483     return mol.DeleteNonPolarHydrogens();
484   }
485 
OutputTree(OBConversion * pConv,OBMol & mol,ostream & ofs,map<unsigned int,branch> & tree,unsigned int depth,bool moves_many,bool preserve_original_index)486   bool OutputTree(OBConversion* pConv, OBMol& mol, ostream& ofs, map <unsigned int, branch> & tree, unsigned int depth, bool moves_many, bool preserve_original_index)
487   {
488     if (tree.empty()) {return false;}
489     if (depth>= tree.size()-1) {depth=tree.size()-1;}
490 
491     set <unsigned int> free_bonds; //this section is to allow the code to be generalised when using obabel rather than babel, which accepts numerical arguments as to how many bonds to fix. As laid out, it will prioritise those bonds that move the fewest atoms, unless moves_many is true, where it will prioritise those that move the most. This is moot for the moment, as either all rotatable bonds are free, or they are all rigid.
492 
493     free_bonds.insert(0);
494     multimap <unsigned int, unsigned int> how_many_atoms_move;
495     for (unsigned int i=1; i<tree.size(); i++)
496     {
497       how_many_atoms_move.insert(pair<unsigned int, unsigned int>( (*tree.find(i)).second.how_many_atoms_moved,i));
498     }
499 
500     multimap <unsigned int, unsigned int>::iterator it=how_many_atoms_move.begin();
501     if ((!moves_many) && !how_many_atoms_move.empty()) {
502       it=how_many_atoms_move.end();
503       if (it!=how_many_atoms_move.begin()) // don't move past begin
504         --it;
505     }
506     for (unsigned int i = 1; i <= depth; i++)
507     {
508       free_bonds.insert((*it).second);
509       if (!moves_many) {
510         if (it!=how_many_atoms_move.begin())
511           --it;
512       }
513       else{
514         ++it;
515       }
516     }
517 
518 
519     for (unsigned int i=tree.size()-1; i > 0; i--)
520     {
521       if (!free_bonds.count(i)) //adds the index of any branch with rigid rotations to its parent
522       {
523         unsigned int parent=(*tree.find(i)).second.UpOne();
524         (*tree.find(parent)).second.rigid_with.insert(
525           (*tree.find(i)).second.rigid_with.begin(),(*tree.find(i)).second.rigid_with.end() );
526       }
527     }
528 
529     map <unsigned int, unsigned int> new_order; //gives the new ordering of the indexes of atoms, so that they are in increasing order from 1 in the output
530 
531 
532     if (!preserve_original_index) //generates the new ordering
533     {
534       unsigned int current_atom_index=1; //the index of the current atom
535       for (unsigned int i=0; i < tree.size(); i++)
536       {
537         if (free_bonds.count(i))
538         {
539           for (set <unsigned int>::iterator it= (*tree.find(i)).second.rigid_with.begin() ; it != (*tree.find(i)).second.rigid_with.end(); ++it)
540                                   {
541             vector <int> atoms=(*tree.find(*it)).second.atoms;
542             for (unsigned int j=0; j < atoms.size(); j++)
543             {
544               new_order.insert(pair<unsigned int, unsigned int> (atoms.at(j), current_atom_index));
545               current_atom_index++;
546             }
547           }
548         }
549       }
550     }
551 
552     if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS)))
553       ofs << "ROOT" << endl;
554     for (set <unsigned int>::iterator it= (*tree.find(0)).second.rigid_with.begin() ; it != (*tree.find(0)).second.rigid_with.end(); ++it)
555     {
556       OutputGroup(mol, ofs, (*tree.find(*it)).second.atoms, new_order, !preserve_original_index);
557     }
558 
559    if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS)))
560      ofs << "ENDROOT" << endl;
561 
562     for (unsigned int i=1; i < tree.size(); i++)
563     {
564       if (free_bonds.count(i))
565       {
566         ofs << "BRANCH";
567         ofs.width(4);
568         unsigned int parent_atom=(*tree.find(i)).second.connecting_atom_parent;
569         unsigned int child_atom=(*tree.find(i)).second.connecting_atom_branch;
570         if (!preserve_original_index) { ofs << (new_order.find(parent_atom))-> second;}
571         else {ofs << parent_atom;}
572         ofs.width(4);
573         if (!preserve_original_index) {ofs << (new_order.find(child_atom))-> second;}
574         else {ofs << child_atom;}
575         ofs << endl;
576         for (set <unsigned int>::iterator it= (*tree.find(i)).second.rigid_with.begin() ; it != (*tree.find(i)).second.rigid_with.end(); ++it)
577         {
578           OutputGroup(mol, ofs, (*tree.find(*it)).second.atoms, new_order, !preserve_original_index);
579         }
580       }
581       unsigned int child=i;
582       for (vector <unsigned int>::iterator it=(*tree.find(i)).second.parents.end(); it != (*tree.find(i)).second.parents.begin(); )
583       {
584         --it;
585         if ((*it)==0) {break;} //do not close the main root; that is closed separately
586         vector <unsigned int>::iterator it_parent=it;
587         --it_parent;
588         if ((*tree.find(*it)).second.children.size() == 0)
589         {
590           if (free_bonds.count(*it))
591           {
592             ofs << "ENDBRANCH";
593             ofs.width(4);
594             unsigned int parent_atom=(*tree.find(*it)).second.connecting_atom_parent;
595             unsigned int child_atom=(*tree.find(*it)).second.connecting_atom_branch;
596             if (!preserve_original_index) { ofs << (new_order.find(parent_atom))-> second;}
597                                     else {ofs << parent_atom;}
598             ofs.width(4);
599             if (!preserve_original_index) {ofs << (new_order.find(child_atom))-> second;}
600                                     else {ofs << child_atom;}
601             ofs << endl;
602           }
603           (*tree.find(*it_parent)).second.children.erase(*it);
604         }
605       }
606     }
607     return true;
608   }
609 
ConstructTree(map<unsigned int,branch> & tree,vector<vector<int>> rigid_fragments,unsigned int root_piece,const OBMol & mol,bool flexible)610   void ConstructTree (map <unsigned int, branch>& tree, vector <vector <int> > rigid_fragments, unsigned int root_piece, const OBMol& mol, bool flexible)
611   {
612     unsigned int first_atom = 0;
613     unsigned int second_atom = 0;
614     unsigned int first_atom_rank = 0;
615     unsigned int second_atom_rank = 0;
616 
617 
618     branch sprog;
619 
620     sprog.atoms=rigid_fragments.at(root_piece);
621     sprog.rigid_with.insert(0);
622 
623     tree.insert(pair<unsigned int, branch> (0, sprog));
624 
625     rigid_fragments.erase(rigid_fragments.begin() + root_piece);
626 
627     unsigned int position=0;
628     unsigned int atoms_moved=0;
629     bool fecund;
630     while (!((*tree.find(0)).second.done))
631     {
632       fecund=!((*tree.find(position)).second.done);
633       if (fecund)
634       {
635         bool sterile=true;
636         for (unsigned int i = 0; i < rigid_fragments.size(); i++)
637         {
638           if (FindBondedPiece( (*tree.find(position)).second.atoms, rigid_fragments.at(i),
639             first_atom, second_atom, first_atom_rank, second_atom_rank, mol, atoms_moved))
640           {
641             sprog.connecting_atom_parent = first_atom;
642             sprog.connecting_atom_branch = second_atom;
643             sprog.how_many_atoms_moved = atoms_moved;
644             sprog.atoms = rigid_fragments.at(i);
645 
646             sprog.depth=(*tree.find(position)).second.depth+1;
647             sprog.parents=(*tree.find(position)).second.parents; //all parents of the parent are parents too
648             sprog.parents.push_back(tree.size()); //a branch is its own parent
649             sprog.index=tree.size(); //the index is simply the number of precursors
650             sprog.rigid_with.clear();
651             sprog.rigid_with.insert(sprog.index);
652 
653             (*tree.find(position)).second.children.insert(tree.size()); //tells the current parent it has an extra child
654                         tree.insert(pair<unsigned int, branch> (tree.size(), sprog)); //adds the current branch to the tree
655 
656             rigid_fragments.erase(rigid_fragments.begin() + i);
657             sterile=false;
658             position = tree.size()-1;
659             break;
660           }
661         }
662         if (sterile)
663         {
664           (*tree.find(position)).second.done=true;
665         }
666       }
667       else {position--;}
668     }
669   }
RotBond_count(OBMol & mol)670   unsigned int RotBond_count(OBMol & mol)
671   {
672     unsigned int count=0;
673     for (OBBondIterator it=mol.BeginBonds(); it!=mol.EndBonds(); it++)
674     {
675       if (IsRotBond_PDBQT((*it))) {count++;}
676     }
677     return count;
678   }
679 
IsImide(OBBond * querybond)680   static bool IsImide(OBBond* querybond)
681   {
682     if (querybond->GetBondOrder() != 2)
683       return(false);
684 
685     OBAtom* bgn = querybond->GetBeginAtom();
686     OBAtom* end = querybond->GetEndAtom();
687     if ((bgn->GetAtomicNum() == 6 && end->GetAtomicNum() == 7) ||
688       (bgn->GetAtomicNum() == 7 && end->GetAtomicNum() == 6))
689       return(true);
690 
691     return(false);
692   }
693 
IsAmidine(OBBond * querybond)694   static bool IsAmidine(OBBond* querybond)
695   {
696     OBAtom *c, *n;
697     c = n = nullptr;
698 
699     // Look for C-N bond
700     OBAtom* bgn = querybond->GetBeginAtom();
701     OBAtom* end = querybond->GetEndAtom();
702     if (bgn->GetAtomicNum() == 6 && end->GetAtomicNum() == 7)
703     {
704       c = bgn;
705       n = end;
706     }
707     if (bgn->GetAtomicNum() == 7 && end->GetAtomicNum() == 6)
708     {
709       c = end;
710       n =bgn;
711     }
712     if (!c || !n) return(false);
713     if (querybond->GetBondOrder() != 1) return(false);
714     if (n->GetTotalDegree() != 3) return false; // must be a degree 3 nitrogen
715 
716     // Make sure C is attached to =N
717     OBBond *bond;
718     vector<OBBond*>::iterator i;
719     for (bond = c->BeginBond(i); bond; bond = c->NextBond(i))
720     {
721       if (IsImide(bond)) return(true);
722     }
723 
724     // Return
725     return(false);
726   }
727 
728 
729   /////////////////////////////////////////////////////////////////////////
IsRotBond_PDBQT(OBBond * the_bond)730   bool IsRotBond_PDBQT(OBBond * the_bond)
731   //identifies a bond as rotatable if it is a single bond, not amide, not in a ring,
732   //and if both atoms it connects have at least one other atom bounded to them
733   {
734     if ( the_bond->GetBondOrder() != 1 || the_bond->IsAromatic() ||
735          the_bond->IsAmide() || IsAmidine(the_bond) || the_bond->IsInRing() )
736       return false;
737     if ( ((the_bond->GetBeginAtom())->GetExplicitDegree() == 1) || ((the_bond->GetEndAtom())->GetExplicitDegree() == 1) ) {return false;}
738     return true;
739   }
740 
IsIn(const vector<int> & vec,const int num)741   bool IsIn(const vector<int>& vec, const int num) //checks whether a vector of int contains a specific int
742   {
743     for (vector<int>::const_iterator itv=vec.begin(); itv != vec.end(); ++itv)
744     {
745       if ((*itv) == num ) {return true;}
746     }
747     return false;
748   }
749 
AtomsSoFar(const map<unsigned int,branch> & tree,unsigned int depth)750   unsigned int AtomsSoFar(const map <unsigned int, branch> & tree, unsigned int depth)
751   {
752     if (depth > tree.size()) {return 0;}
753     unsigned int numberAtoms=0;
754 //		for (unsigned int i = 0; i < depth; i++) {numberAtoms+= tree.at(i).second.first.size();}
755     return numberAtoms;
756   }
757 
FindBondedPiece(const vector<int> & root,const vector<int> & branched,unsigned int & root_atom,unsigned int & branch_atom,unsigned int & root_atom_rank,unsigned int & branch_atom_rank,const OBMol & mol,unsigned int & atoms_moved)758   bool FindBondedPiece(const vector<int>& root, const vector<int>& branched, unsigned int& root_atom, unsigned int& branch_atom,
759     unsigned int& root_atom_rank, unsigned int& branch_atom_rank, const OBMol& mol, unsigned int & atoms_moved)
760   {
761     OBBond* the_bond;
762     for (unsigned int i=0; i < root.size(); i++)
763     {
764       for (unsigned int j=0; j < branched.size(); j++)
765       {
766         the_bond=mol.GetBond(mol.GetAtom(root.at(i)), mol.GetAtom(branched.at(j)));
767         if (the_bond != nullptr)
768         {
769           root_atom=root.at(i);
770           branch_atom=branched.at(j);
771           root_atom_rank=i;
772           branch_atom_rank=j;
773           OBMol mol_copy=mol;
774           the_bond=mol_copy.GetBond(mol_copy.GetAtom(root.at(i)), mol_copy.GetAtom(branched.at(j)));
775           mol_copy.DeleteBond(the_bond, true);
776 
777           vector <vector <int> > two_pieces;
778           mol_copy.ContigFragList(two_pieces);
779           atoms_moved = two_pieces.at(1).size();
780           return true;
781         }
782       }
783     }
784     return false;
785   }
786   /////////////////////////////////////////////////////////////////////////
787 
Separate_preserve_charges(OBMol & mol,vector<OBMol> & result)788   bool Separate_preserve_charges(OBMol & mol, vector <OBMol> & result)
789   {
790 //    vector<OBMol> result;
791     if( mol.NumAtoms() == 0 )
792       return false; // nothing to do, but let's prevent a crash
793 
794     OBMolAtomDFSIter iter( mol, 1);
795     OBMol newMol;
796     newMol.SetAutomaticPartialCharge(false);
797     int fragments = 0;
798     while( mol.GetNextFragment( iter, newMol ) )
799     {
800       result.push_back( newMol );
801       newMol.Clear();
802       newMol.SetAutomaticPartialCharge(false);
803     }
804 
805     return true;
806   }
807   /////////////////////////////////////////////////////////////////////////
CompareBondAtoms(const void * a,const void * b)808   int CompareBondAtoms(const void *a, const void *b)
809   {
810     const OBAtom **da = (const OBAtom **)a;
811     const OBAtom **db = (const OBAtom **)b;
812     unsigned int aIdx = (*da)->GetIdx();
813     unsigned int bIdx = (*db)->GetIdx();
814 
815     return ((aIdx > bIdx) - (aIdx < bIdx));
816   }
817   /////////////////////////////////////////////////////////////////////////
CompareBonds(const void * a,const void * b)818   int CompareBonds(const void *a, const void *b)
819   {
820     const OBAtom ***da = (const OBAtom ***)a;
821     const OBAtom ***db = (const OBAtom ***)b;
822     unsigned int aIdx[2] = { (*da)[0]->GetIdx(), (*da)[1]->GetIdx() };
823     unsigned int bIdx[2] = { (*db)[0]->GetIdx(), (*db)[1]->GetIdx() };
824     int cmp1;
825 
826 
827     cmp1 = ((aIdx[0] > bIdx[0]) - (aIdx[0] < bIdx[0]));
828     return (cmp1 ? cmp1 : ((aIdx[1] > bIdx[1]) - (aIdx[1] < bIdx[1])));
829   }
830   /////////////////////////////////////////////////////////////////////////
WriteMolecule(OBBase * pOb,OBConversion * pConv)831   bool PDBQTFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
832   {
833     OBMol* pmol = dynamic_cast<OBMol*>(pOb);
834     if (pmol == nullptr)
835       return false;
836 
837     //Define some references so we can use the old parameter names
838     ostream &ofs = *pConv->GetOutStream();
839     OBMol & mol = *pmol;
840 
841     if(!mol.HasAromaticPerceived()) { //need aromaticity for correct atom typing
842       aromtyper.AssignAromaticFlags(mol);
843     }
844 
845     if (pConv->IsOption("b",OBConversion::OUTOPTIONS)) {mol.ConnectTheDots(); mol.PerceiveBondOrders();}
846     vector <OBMol> all_pieces;
847     if ((pConv->IsOption("c", OBConversion::OUTOPTIONS) != nullptr && pConv->IsOption("r", OBConversion::OUTOPTIONS) != nullptr)
848       || (pConv->IsOption("n",OBConversion::OUTOPTIONS))
849     )
850     {
851       mol.SetAutomaticPartialCharge(false);
852       all_pieces.push_back(mol);
853     }
854     else
855     {
856       Separate_preserve_charges(mol, all_pieces);
857     }
858 
859     for (unsigned int i = 0; i < all_pieces.size(); i++)
860     {
861       bool residue=false;
862       string res_name="";
863       string res_chain="";
864       int res_num=1;
865       if (pConv->IsOption("s",OBConversion::OUTOPTIONS))
866       {
867         residue=true;
868         OBResidue* res=mol.GetResidue(0);
869         res_name=res->GetName();
870         res_name.resize(3);
871         res_chain=res->GetChain();
872         res_num=res->GetNum();
873       }
874 
875       all_pieces.at(i).SetAutomaticPartialCharge(false);
876       all_pieces.at(i).SetAromaticPerceived(); //retain aromatic flags in fragments
877       if (!(pConv->IsOption("h",OBConversion::OUTOPTIONS))) {
878         DeleteHydrogens(all_pieces.at(i));
879       }
880 
881       int model_num = 0;
882       char buffer[BUFF_SIZE];
883       if (!residue)
884       {
885         if (!pConv->IsLast() || pConv->GetOutputIndex() > 1)
886         { // More than one molecule record
887           model_num = pConv->GetOutputIndex(); // MODEL 1-based index
888           snprintf(buffer, BUFF_SIZE, "MODEL %8d", model_num);
889           ofs << buffer << endl;
890         }
891         ofs << "REMARK  Name = " << mol.GetTitle(true) << endl;
892 //        ofs << "USER    Name = " << mol.GetTitle(true) << endl;
893         if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS)))
894         {
895           char type_name[10];
896           int nRotBond=RotBond_count(mol);
897           OBAtom ***rotBondTable = new OBAtom **[nRotBond];
898           int rotBondId=0;
899           int bondAtomNum;
900           unsigned int end;
901           OBResidue *res;
902           for (OBBondIterator it=mol.BeginBonds(); it != mol.EndBonds(); it++)
903           {
904             if (IsRotBond_PDBQT((*it)))
905             {
906               rotBondTable[rotBondId] = new OBAtom *[2];
907               rotBondTable[rotBondId][0] = (*it)->GetBeginAtom();
908               rotBondTable[rotBondId][1] = (*it)->GetEndAtom();
909               qsort(rotBondTable[rotBondId], 2, sizeof(OBAtom *), CompareBondAtoms);
910               rotBondId++;
911             }
912           }
913           qsort(rotBondTable, nRotBond, sizeof(OBAtom **), CompareBonds);
914           ofs << "REMARK  " << nRotBond << " active torsions:" << endl;
915           ofs << "REMARK  status: ('A' for Active; 'I' for Inactive)" << endl;
916           for (rotBondId=0; rotBondId < nRotBond; rotBondId++)
917           {
918             snprintf(buffer, BUFF_SIZE, "REMARK  %3d  A    between atoms: ", rotBondId + 1);
919             ofs << buffer;
920             for (bondAtomNum=0; bondAtomNum < 2; bondAtomNum++)
921             {
922               memset(type_name, 0, sizeof(type_name));
923               strncpy(type_name, OBElements::GetSymbol(rotBondTable[rotBondId][bondAtomNum]->GetAtomicNum()), sizeof(type_name));
924               if (strlen(type_name) > 1)
925                 type_name[1] = toupper(type_name[1]);
926               if ((res = rotBondTable[rotBondId][bondAtomNum]->GetResidue()) != nullptr)
927               {
928                 snprintf(type_name,5,"%s",(char*)res->GetAtomID(rotBondTable[rotBondId][bondAtomNum]).c_str());
929                 // AtomIDs may start with space if read from a PDB file (rather than perceived)
930                 end = isspace(type_name[0]) ? 1 : 0;
931                 // Use sizeof() - 1 to ensure there's room for the NULL termination!
932                 while (end < sizeof(type_name) - 1 && type_name[end] && !isspace(type_name[end]))
933                   end++;
934                 type_name[end] = '\0';
935               }
936               snprintf(buffer, BUFF_SIZE, "%s_%d", type_name + (isspace(type_name[0]) ? 1 : 0),
937                 rotBondTable[rotBondId][bondAtomNum]->GetIdx());
938               ofs << buffer;
939               if (bondAtomNum == 0)
940                 ofs << "  and  ";
941             }
942             delete [] rotBondTable[rotBondId];
943             ofs << endl;
944           }
945           delete [] rotBondTable;
946         }
947         ofs << "REMARK                            x       y       z     vdW  Elec       q    Type" << endl;
948 //        ofs << "USER                              x       y       z     vdW  Elec       q    Type" << endl;
949         ofs << "REMARK                         _______ _______ _______ _____ _____    ______ ____" << endl;
950 //        ofs << "USER                           _______ _______ _______ _____ _____    ______ ____" << endl;
951       }
952       else
953       {
954         ofs << "BEGIN_RES" << " " << res_name << " " << res_chain << " ";
955         ofs.width(3);
956         ofs << right << res_num << endl;
957       }
958 
959       // before we write any records, we should check to see if any coord < -1000
960       // which will cause errors in the formatting
961       double minX, minY, minZ;
962       minX = minY = minZ = -999.0f;
963       FOR_ATOMS_OF_MOL(a, all_pieces.at(i))
964       {
965         if (a->GetX() < minX)
966           minX = a->GetX();
967         if (a->GetY() < minY)
968           minY = a->GetY();
969         if (a->GetZ() < minZ)
970           minZ = a->GetZ();
971       }
972       vector3 transV = VZero;
973       if (minX < -999.0)
974         transV.SetX(-1.0*minX - 900.0);
975       if (minY < -999.0)
976         transV.SetY(-1.0*minY - 900.0);
977       if (minZ < -999.0)
978         transV.SetZ(-1.0*minZ - 900.0);
979 
980       // if minX, minY, or minZ was never changed, shift will be 0.0f
981       // otherwise, move enough so that smallest coord is > -999.0f
982       all_pieces.at(i).Translate(transV);
983 
984       bool flexible=!pConv->IsOption("r",OBConversion::OUTOPTIONS);
985 
986       vector <vector <int> > rigid_fragments; //the vector of all the rigid molecule fragments, using atom indexes
987       unsigned int best_root_atom=1;
988       map <unsigned int, branch> tree;
989       unsigned int torsdof=0;
990       unsigned int root_piece=0;
991       unsigned int rotatable_bonds=0;
992 
993       if (flexible)
994       {
995 
996         best_root_atom=FindFragments(all_pieces.at(i), rigid_fragments); //the return value is the root atom index
997 
998         if (residue) {best_root_atom=1;} //if this is a residue, uses the first atom as the root
999 
1000         torsdof=rigid_fragments.size()-1;
1001 
1002         for (unsigned int j = 0; j < rigid_fragments.size(); j++)
1003         {
1004           if (IsIn((rigid_fragments.at(j)), best_root_atom)) {root_piece=j; break;} //this is the root rigid molecule fragment
1005         }
1006         ConstructTree(tree, rigid_fragments, root_piece, all_pieces.at(i), true);
1007         rotatable_bonds=torsdof;
1008       }
1009       else //if no rotatable bonds are selected, then won't construct a tree, instead get a whole branch directly from the OBMol
1010       {
1011         branch all_molecule_branch;
1012         all_molecule_branch.all_atoms(all_pieces.at(i));
1013         tree.insert(pair<unsigned int, branch> (0, all_molecule_branch));
1014         torsdof=RotBond_count(all_pieces.at(i));
1015       }
1016 
1017       bool preserve_original_index = (pConv->IsOption("p",OBConversion::OUTOPTIONS));
1018       if (!flexible) {preserve_original_index=false;} //no need to relabel if we are preserving the original order anyway
1019 
1020       if (!OutputTree(pConv, all_pieces.at(i), ofs, tree, rotatable_bonds, false, preserve_original_index) )
1021       {
1022         stringstream errorMsg;
1023         errorMsg << "WARNING: Problems writing a PDBQT file\n"
1024           <<  "  The torsion tree is wrong.\n";
1025         obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError);
1026         return false;
1027       }
1028 
1029 
1030       if (!residue)
1031       {
1032         if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS)))
1033           ofs << "TORSDOF " << torsdof << endl;
1034         else
1035           ofs << "TER " << endl;
1036 //        ofs << "TER" << endl;
1037         if (model_num)
1038         {
1039           ofs << "ENDMDL" << endl;
1040         }
1041       }
1042       else
1043       {
1044         ofs << "END_RES" << " " << res_name << " " << res_chain << " ";
1045         ofs.width(3);
1046         ofs << right << res_num << endl;
1047       }
1048     }
1049     return true;
1050   }
1051 
1052   /////////////////////////////////////////////////////////////////////////
1053 
parseAtomRecord(char * buffer,OBMol & mol,int chainNum)1054   static bool parseAtomRecord(char *buffer, OBMol &mol,int chainNum)
1055   /* ATOMFORMAT "(i5,1x,a4,a1,a3,1x,a1,i4,a1,3x,3f8.3,2f6.2,a2,a2)" */
1056   {
1057     string sbuf = &buffer[6];
1058     if (sbuf.size() < 48)
1059       return(false);
1060 
1061     bool hetatm = (EQn(buffer,"HETATM",6)) ? true : false;
1062     bool elementFound = false; // true if correct element found in col 77-78
1063 
1064     /* serial number */
1065     string serno = sbuf.substr(0,5);
1066 
1067     /* atom name */
1068     string atmid = sbuf.substr(6,4);
1069 
1070     /* chain */
1071     char chain = sbuf.substr(15,1)[0];
1072 
1073     /* element */
1074     string element = "  ";
1075     string pdbqt_element = "  "; //the literal pdbqt element
1076     if (sbuf.size() == 72) {sbuf+=" ";}
1077     if (sbuf.size() > 72)
1078     {
1079       pdbqt_element = sbuf.substr(71,2);
1080       if ( (pdbqt_element == "A ") || (pdbqt_element == " A") || (pdbqt_element == "Z ") || (pdbqt_element == " Z") ||
1081         (pdbqt_element == "G ") || (pdbqt_element == " G") || (pdbqt_element == "GA") || (pdbqt_element == "J ") ||
1082         (pdbqt_element == " J") || (pdbqt_element == "Q ") || (pdbqt_element == " Q") )
1083         {element = "C"; element += " ";} //all these are carbons
1084       else if (pdbqt_element == "HD") {element = "H"; element += " ";} //HD are hydrogens
1085       else if (pdbqt_element == "HS") {element = "H"; element += " ";} //HS are hydrogens
1086       else if (pdbqt_element == "NA") {element = "N"; element += " ";} //NA are nitrogens
1087       else if (pdbqt_element == "NS") {element = "N"; element += " ";} //NS are nitrogens
1088       else if (pdbqt_element == "OA") {element = "O"; element += " ";} //OA are oxygens
1089       else if (pdbqt_element == "OS") {element = "O"; element += " ";} //OS are oxygens
1090       else if (pdbqt_element == "SA") {element = "S"; element += " ";} //SA are sulphurs
1091       else {element = pdbqt_element;}
1092 
1093       if (isalpha(element[0])) //trims the name if needed
1094       {
1095         if (element[1] == ' ')
1096         {
1097           element.erase(1, 1);
1098           elementFound = true;
1099         }
1100         else if (isalpha(element[1]))
1101         {
1102           elementFound = true;
1103         }
1104       }
1105       else if (isalpha(element[1]))
1106       {
1107         element.erase(0,1);
1108         elementFound = true;
1109       }
1110     }
1111 
1112     if (!elementFound)
1113     {
1114       stringstream errorMsg;
1115       errorMsg << "WARNING: Problems reading a PDBQT file\n"
1116         << "  Problems reading a HETATM or ATOM record.\n"
1117         << "  According to the PDBQT specification,\n"
1118         << "  columns 78-79 should contain the element symbol of an atom.\n"
1119         << "  but OpenBabel found '" << element << "' (atom " << mol.NumAtoms()+1 << ")";
1120       obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError);
1121       return false;
1122     }
1123 
1124     // charge - optional
1125     string scharge;
1126     if (sbuf.size() > 69)
1127     {
1128       scharge = sbuf.substr(64,6);
1129     }
1130 
1131     //trim spaces on the right and left sides
1132     while (!atmid.empty() && atmid[0] == ' ')
1133       atmid = atmid.erase(0, 1);
1134 
1135     while (!atmid.empty() && atmid[atmid.size()-1] == ' ')
1136       atmid = atmid.substr(0,atmid.size()-1);
1137 
1138     /* residue name */
1139     string resname = sbuf.substr(11,3);
1140     if (resname == "   ") resname = "UNK";
1141     else
1142     {
1143       while (!resname.empty() && resname[0] == ' ')
1144         resname = resname.substr(1,resname.size()-1);
1145       while (!resname.empty() && resname[resname.size()-1] == ' ')
1146         resname = resname.substr(0,resname.size()-1);
1147     }
1148 
1149     OBAtom atom;
1150     /* X, Y, Z */
1151     string xstr = sbuf.substr(24,8);
1152     string ystr = sbuf.substr(32,8);
1153     string zstr = sbuf.substr(40,8);
1154     vector3 v(atof(xstr.c_str()),atof(ystr.c_str()),atof(zstr.c_str()));
1155     atom.SetVector(v);
1156 
1157     // useful for debugging unknown atom types (e.g., PR#1577238)
1158     //    cout << mol.NumAtoms() + 1  << " : '" << element << "'" << " " << OBElements::GetAtomicNum(element.c_str()) << endl;
1159 
1160 
1161     atom.SetAtomicNum(OBElements::GetAtomicNum(element.c_str()));
1162 
1163     if ( (! scharge.empty()) && "     " != scharge )
1164     {
1165       stringstream sst;
1166       sst.str(scharge);
1167       double charge;
1168       sst >> charge;
1169       if ( !sst.fail() )
1170       {
1171         atom.SetPartialCharge(charge);
1172       }
1173       else
1174       {
1175         stringstream errorMsg;
1176         errorMsg << "WARNING: Problems reading a PDBQT file\n"
1177           << "  Problems reading a HETATM or ATOM record.\n"
1178           << "  According to the PDBQT specification,\n"
1179           << "  columns 72-76 should contain charge of the atom\n"
1180           << "  but OpenBabel found '" << scharge << "' (atom " << mol.NumAtoms()+1 << ").";
1181         obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning);
1182       }
1183     }
1184     else
1185     {
1186       atom.SetPartialCharge(0.);
1187     }
1188 
1189     /* residue sequence number */
1190     string resnum = sbuf.substr(16,4);
1191     char icode = sbuf[20];
1192     if(icode == ' ') icode = 0;
1193 
1194     OBResidue *res = mol.NumResidues() > 0 ? mol.GetResidue(mol.NumResidues()-1) : nullptr;
1195     if (res == nullptr || res->GetName() != resname
1196       || res->GetNumString() != resnum || res->GetInsertionCode() != icode)
1197     {
1198       vector<OBResidue*>::iterator ri;
1199       for (res = mol.BeginResidue(ri) ; res ; res = mol.NextResidue(ri))
1200       if (res->GetName() == resname
1201         && res->GetNumString() == resnum
1202         && res->GetInsertionCode() == icode
1203         && static_cast<int>(res->GetChain()) == chain)
1204         break;
1205 
1206       if (res == nullptr)
1207       {
1208         res = mol.NewResidue();
1209         res->SetChain(chain);
1210         res->SetName(resname);
1211         res->SetNum(resnum);
1212         res->SetInsertionCode(icode);
1213       }
1214     }
1215 
1216     if (!mol.AddAtom(atom))
1217       return(false);
1218     else
1219     {
1220       OBAtom *atom = mol.GetAtom(mol.NumAtoms());
1221 
1222       res->AddAtom(atom);
1223       res->SetSerialNum(atom, atoi(serno.c_str()));
1224       res->SetAtomID(atom, sbuf.substr(6,4));
1225       res->SetHetAtom(atom, hetatm);
1226       return(true);
1227     }
1228   } // end reading atom records
1229 
1230 } //namespace OpenBabel
1231