1 /**********************************************************************
2 obrotate = rotate a tortional bond matched by a SMART pattern
3 Copyright (C) 2003 Fabien Fontaine
4 Some portions Copyright (C) 2004-2005 Geoffrey R. Hutchison
5 Some portions Copyright (C) 2008 Tim Vandermeersch
6 
7 This file is part of the Open Babel project.
8 For more information, see <http://openbabel.org/>
9 
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation version 2 of the License.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 ***********************************************************************/
19 
20 /*
21   Require a SMART pattern, a file containing molecule coordinates
22   4 atoms of the SMART pattern to define the tortional, an angle value
23   The angle value must be in degree
24   the 2 atoms of the rotating bond must be bonded but the 2 others not
25   the part of the molecule on the side of the second atom is kept fixed
26   whereas the part on the side of the third atom is rotated.
27   example of command line:
28   obrotate "[nH]ccccc[O,C][C,O]" test.sdf 1 6 7 8 180.0
29 */
30 
31 
32 // used to set import/export for Cygwin DLLs
33 #ifdef WIN32
34 #define USING_OBDLL
35 #endif
36 
37 #include <openbabel/babelconfig.h>
38 #include <openbabel/mol.h>
39 #include <openbabel/atom.h>
40 #include <openbabel/parsmart.h>
41 #include <openbabel/rotamer.h>
42 //#include <unistd.h>
43 #include <openbabel/obconversion.h>
44 #include <cstdlib>
45 using namespace std;
46 using namespace OpenBabel;
47 
48 ///////////////////////////////////////////////////////////////////////////////
49 //! \brief Set a tortional bond to a given angle
main(int argc,char ** argv)50 int main(int argc,char **argv)
51 {
52   OBAtom *a1, *a2, *a3, *a4;
53   unsigned int smartor[4]= {0,0,0,0};// atoms of the tortional in the SMART
54   float angle =   0;      // tortional angle value to set in degree
55   char *FileIn = nullptr, *Pattern = nullptr;
56   unsigned int i, t, errflg = 0;
57   int c;
58   string err;
59   bool changeAll = false; // default to only change the last matching torsion
60 
61   // parse the command line -- optional -a flag to change all matching torsions
62   if (argc < 8 || argc > 9) {
63     errflg++;
64   } else {
65     // Fetch the option and shift values after the option
66     if (argc == 9) {
67       int curArg = 0;
68       while (curArg < 9) {
69         if (strcmp(argv[curArg], "-a") == 0) {
70           changeAll = true;
71           break;
72         }
73         ++curArg;
74       }
75       // We expect -a and so changeAll should be true
76       if (!changeAll)
77         errflg++;
78 
79       // now let's shift values
80       while (curArg < 8) {
81         argv[curArg] = argv[curArg+1];
82       }
83     }
84     FileIn = argv[2];
85     Pattern = argv[1];
86     // Read the atom position
87     for(i=3, t=0; i<7; ++i, ++t) {
88       c = sscanf(argv[i], "%u", &smartor[t]);
89       if (c != 1) {
90         errflg++;  // error in arguments, quit and warn user
91         break;
92       }
93     }
94     c = sscanf(argv[7], "%f", &angle);
95     if (c != 1) {
96       errflg++; // error in arguments, quit and warn user
97     }
98     if (argc == 9) {
99       if (strcmp(argv[8], "-a") == 0)
100         changeAll = true;
101       else
102         errflg++; // error in arguments, quit and warn user
103     }
104   }
105 
106   if (errflg) {
107     cerr << "Usage: obrotate \"PATTERN\" <filename> <atom1> <atom2> <atom3> <atom4> <angle> [-a]" << endl;
108     exit(-1);
109   }
110 
111   // create pattern
112   OBSmartsPattern sp;
113   sp.Init(Pattern);
114   if (sp.NumAtoms() < 4) {
115     cerr << "obrotate: The number of atoms in the SMART pattern must be higher than 3." << endl;
116     exit(-1);
117   }
118 
119   for (i=0; i<4; ++i) {
120     if ( smartor[i] < 1 || smartor[i] > sp.NumAtoms()) {
121       cerr << "obrotate: The torsional atom values must be between 1 and "
122            <<  sp.NumAtoms()
123            << ", which is the number of atoms in the SMART pattern." << endl;
124       exit(-1);
125     }
126   }
127 
128   OBConversion conv; //NF...
129   OBFormat* format = conv.FormatFromExt(FileIn);
130   if(!(format && conv.SetInAndOutFormats(format, format))) { //in and out formats same
131     cerr << "obrotate: cannot read and/or write this file format!" << endl;
132     exit (-1);
133   } //...NF
134 
135   //Open the molecule file
136   ifstream ifs;
137 
138   // Read the file
139   ifs.open(FileIn);
140   if (!ifs) {
141     cerr << "obrotate: cannot read input file!" << endl;
142     exit (-1);
143   }
144 
145   OBMol mol;
146   vector< vector <int> > maplist;      // list of matched atoms
147   vector< vector <int> >::iterator m;  // and its iterators
148   //   int tindex;
149 
150   // Set the angles
151   for (;;) {
152     mol.Clear();
153     //NF      ifs >> mol;                   // Read molecule
154     conv.Read(&mol,&ifs); //NF
155     if (mol.Empty())
156       break;
157 
158     if (sp.Match(mol)) {
159       // if match perform rotation
160       maplist = sp.GetUMapList(); // get unique matches
161 
162       if (maplist.size() > 1)
163         cerr << "obrotate: Found " << maplist.size() << " matches. Only last one will be rotated." << endl;
164 
165       // look at all the mapping atom but save only the last one.
166       for (m = maplist.begin(); m != maplist.end(); ++m) {
167         a1 = mol.GetAtom( (*m)[ smartor[0] - 1] );
168         a2 = mol.GetAtom( (*m)[ smartor[1] - 1] );
169         a3 = mol.GetAtom( (*m)[ smartor[2] - 1] );
170         a4 = mol.GetAtom( (*m)[ smartor[3] - 1] );
171         if (changeAll)
172           mol.SetTorsion(a1, a2, a3, a4, angle * DEG_TO_RAD);
173       }
174 
175       if ( !a2->IsConnected(a3) ) {
176         cerr << "obrotate: The atoms of the rotating bond must be bonded." << endl;
177         exit(-1);
178       }
179 
180       if (!changeAll)
181         mol.SetTorsion(a1, a2, a3, a4, angle * DEG_TO_RAD);
182     } else {
183       cerr << "obrotate: Found 0 matches for the SMARTS pattern." << endl;
184       exit(-1);
185     }
186     //NF      cout << mol;
187     conv.Write(&mol,&cout); //NF
188   }
189 
190   return(0);
191 }
192 
193 
194 /* obrotate man page*/
195 /** \page obrotate batch-rotate dihedral angles matching SMARTS patterns
196 *
197 * \n
198 * \par SYNOPSIS
199 *
200 * \b obrotate '<SMARTS-pattern>' \<filename\> \<atom1\> \<atom2\> \<atom3\> \<atom4\> \<angle\>
201 *
202 * \par DESCRIPTION
203 *
204 * The obrotate program rotates the torsional (dihedral) angle of a specified
205 * bond in molecules to that defined by the user. In other words, it does the
206 * same as a user setting an angle in a molecular modelling package, but much
207 * faster and in batch mode.
208 * \n\n
209 * The four atom IDs required are indexes into the SMARTS pattern, which starts
210 * at atom 1. The angle supplied is in degrees. The two atoms used to set
211 * the dihedral angle \<atom1\> and \<atom4\> do not need to be connected
212 * to the atoms of the bond \<atom2\> and \<atom3\> in any way.
213 *\n\n
214 * The order of the atoms matters -- the portion of the molecule attached to
215 * \<atom1\> and \<atom2\> remain fixed, but the portion bonded to \<atom3\> and
216 & \<atom4\> moves.
217 *
218 * \par EXAMPLES
219 *  - Let's say that you want to define the conformation of a large number of
220 *  molecules with a pyridyl scaffold and substituted with an aliphatic chain
221 *  at the 3-position, for example for docking or 3D-QSAR purposes.
222 * \n\n
223 *    To set the value of the first dihedral angle to 90 degrees:\n
224 *   obrotate "c1ccncc1CCC" pyridines.sdf 5 6 7 8 90
225 * \n
226 * Here 6 and 7 define the bond to rotate in the SMARTS patter, i.e., c1-C and
227 * atoms 5 and 8 define the particular dihedral angle to rotate.
228 *  - Since the atoms to define the dihedral do not need to be directly
229 *  connected, the nitrogen in the pyridine can be used:\n
230 *   obrotate "c1ccncc1CCC" pyridines.sdf 4 6 7 8 90
231 *
232 *  - Keep the pyridyl ring fixed and moves the aliphatic chain:\n
233 *   obrotate "c1ccncc1CCC" pyridines.sdf 5 6 7 8 90
234 *  - Keep the aliphatic chain fixed and move the pyridyl ring:\n
235 *   obrotate "c1ccncc1CCC" pyridines.sdf 8 7 6 5 90
236 *
237 * \par AUTHORS
238 *
239 * The obrotate program was contributed by \b Fabien \b Fontaine.
240 *
241 * Open Babel is currently maintained by \b Geoff \b Hutchison, \b Chris \b Morley and \b Michael \b Banck.
242 *
243 * For more contributors to Open Babel, see http://openbabel.org/THANKS.shtml
244 *
245 * \par COPYRIGHT
246 *  Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
247 *  Some portions Copyright (C) 2001-2005 by Geoffrey R. Hutchison \n \n
248 *  This program is free software; you can redistribute it and/or modify
249 *  it under the terms of the GNU General Public License as published by
250 *  the Free Software Foundation version 2 of the License.\n \n
251 *  This program is distributed in the hope that it will be useful,
252 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
253 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
254 *  GNU General Public License for more details.
255 *
256 * \par SEE ALSO
257 *   The web pages for Open Babel can be found at: http://openbabel.org/ \n
258 *   A guide for constructing SMARTS patterns can be found at: http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html
259 **/
260