1 /* Copyright (C) 2004-2007  The Chemistry Development Kit (CDK) project
2  *
3  *  Contact: cdk-devel@lists.sourceforge.net
4  *
5  *  This program is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *  All we ask is that proper credit is given for our work, which includes
10  *  - but is not limited to - adding the above copyright notice to the beginning
11  *  of your source code files, and to any copyright notice that you may distribute
12  *  with programs based on this work.
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 Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  */
24 package org.openscience.cdk.modeling.builder3d;
25 
26 import org.openscience.cdk.config.Isotopes;
27 import org.openscience.cdk.interfaces.IAtomType;
28 import org.openscience.cdk.interfaces.IChemObjectBuilder;
29 import org.openscience.cdk.interfaces.IIsotope;
30 import org.openscience.cdk.tools.ILoggingTool;
31 import org.openscience.cdk.tools.LoggingToolFactory;
32 import org.openscience.cdk.tools.periodictable.PeriodicTable;
33 
34 import java.awt.Color;
35 import java.io.BufferedReader;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.InputStreamReader;
39 import java.util.Hashtable;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.StringTokenizer;
43 import java.util.Vector;
44 
45 /**
46  * AtomType list configurator that uses the ParameterSet originally defined in
47  * mmff94.prm from moe. This class was added to be able to port mmff94 to CDK.
48  *
49  * @author chhoppe
50  * @cdk.created 2004-09-07
51  * @cdk.module forcefield
52  * @cdk.githash
53  * @cdk.keyword atom type, mmff94
54  */
55 public class MMFF94BasedParameterSetReader {
56 
57     private final ILoggingTool  LOG            = LoggingToolFactory
58                                                        .createLoggingTool(MMFF94BasedParameterSetReader.class);
59 
60     private String              configFile     = "org/openscience/cdk/modeling/forcefield/data/mmff94.prm";
61     private InputStream         ins            = null;
62     private Map<String, Object> parameterSet;
63     private List<IAtomType>     atomTypes;
64     private StringTokenizer     st;
65     private String              key            = "";
66     private String              sid;
67 
68     private String              configFilevdW  = "org/openscience/cdk/modeling/forcefield/data/mmffvdw.prm";
69     private InputStream         insvdW         = null;
70     private StringTokenizer     stvdW;
71     private String              sidvdW;
72 
73     private String              configFileDFSB = "org/openscience/cdk/modeling/forcefield/data/mmffdfsb.par";
74     private InputStream         insDFSB;
75     private StringTokenizer     stDFSB;
76 
77     /**
78      * Constructor for the MM2BasedParameterSetReader object
79      */
MMFF94BasedParameterSetReader()80     public MMFF94BasedParameterSetReader() {
81         parameterSet = new Hashtable<String, Object>();
82         atomTypes = new Vector<IAtomType>();
83     }
84 
getParamterSet()85     public Map<String, Object> getParamterSet() {
86         return parameterSet;
87     }
88 
getAtomTypes()89     public List<IAtomType> getAtomTypes() {
90         return atomTypes;
91     }
92 
93     /**
94      * Sets the file containing the config data
95      *
96      * @param ins The new inputStream type InputStream
97      */
setInputStream(InputStream ins)98     public void setInputStream(InputStream ins) {
99         this.ins = ins;
100     }
101 
102     /**
103      * Read a text based configuration file out of the force field mm2 file
104      *
105      * @throws Exception Description of the Exception
106      */
setAtomTypeData()107     private void setAtomTypeData() throws Exception {
108 
109         key = "data" + sid;
110         List data = new Vector();
111 
112         String sradius = st.nextToken();
113         String swell = st.nextToken();
114         String sapol = st.nextToken();
115         String sNeff = st.nextToken();
116         //st.nextToken();
117         String sDA = st.nextToken();
118         String sq0 = st.nextToken();
119         String spbci = st.nextToken();
120         String sfcadj = st.nextToken();
121 
122         stvdW.nextToken();
123         stvdW.nextToken();
124         String sA = stvdW.nextToken();
125         String sG = stvdW.nextToken();
126 
127         try {
128             double well = new Double(swell).doubleValue();
129             double apol = new Double(sapol).doubleValue();
130             double Neff = new Double(sNeff).doubleValue();
131             double fcadj = new Double(sfcadj).doubleValue();
132             //double pbci = new Double(spbci).doubleValue();
133             double a = new Double(sA).doubleValue();
134             double g = new Double(sG).doubleValue();
135 
136             data.add(new Double(well));
137             data.add(new Double(apol));
138             data.add(new Double(Neff));
139             data.add(new String(sDA));
140             data.add(new Double(fcadj));
141             data.add(new Double(spbci));
142             data.add(new Double(a));
143             data.add(new Double(g));
144 
145         } catch (NumberFormatException nfe) {
146             throw new IOException("Data: Malformed Number due to:" + nfe);
147         }
148 
149         LOG.debug("data : well,apol,Neff,sDA,fcadj,spbci,a,g " + data);
150         parameterSet.put(key, data);
151 
152         key = "vdw" + sid;
153         data = new Vector();
154         try {
155             double radius = new Double(sradius).doubleValue();
156             data.add(new Double(radius));
157 
158         } catch (NumberFormatException nfe2) {
159             LOG.debug("vdwError: Malformed Number due to:" + nfe2);
160         }
161         parameterSet.put(key, data);
162 
163         key = "charge" + sid;
164         data = new Vector();
165         try {
166             double q0 = new Double(sq0).doubleValue();
167             data.add(new Double(q0));
168         } catch (NumberFormatException nfe3) {
169             System.err.println("Charge: Malformed Number due to:" + nfe3);
170         }
171         parameterSet.put(key, data);
172     }
173 
174     /**
175      * Read and stores the atom types in a vector
176      *
177      * @throws Exception Description of the Exception
178      */
setAtomTypes(IChemObjectBuilder builder)179     private void setAtomTypes(IChemObjectBuilder builder) throws Exception {
180         String name = "";
181         String rootType = "";
182         //int an = 0;
183         int rl = 255;
184         int gl = 20;
185         int bl = 147;
186         int maxbond = 0;
187         int atomNr = 0;
188 
189         double mass = 0.0;
190         st.nextToken();
191         String sid = st.nextToken();
192         rootType = st.nextToken();
193         String smaxbond = st.nextToken();
194         String satomNr = st.nextToken();
195         String smass = st.nextToken();
196         name = st.nextToken();
197 
198         try {
199             maxbond = Integer.parseInt(smaxbond);
200             mass = Double.parseDouble(smass);
201             atomNr = Integer.parseInt(satomNr);
202 
203         } catch (NumberFormatException nfe) {
204             throw new IOException("AtomTypeTable.ReadAtypes: " + "Malformed Number");
205         }
206 
207         IAtomType atomType = builder.newInstance(IAtomType.class, name, rootType);
208         atomType.setAtomicNumber(atomNr);
209         atomType.setExactMass(mass);
210         atomType.setMassNumber(massNumber(atomNr, mass));
211         atomType.setFormalNeighbourCount(maxbond);
212         atomType.setSymbol(rootType);
213         Color co = new Color(rl, gl, bl);
214         atomType.setProperty("org.openscience.cdk.renderer.color", co);
215         atomType.setAtomTypeName(sid);
216         atomTypes.add(atomType);
217     }
218 
219     /**
220      * Sets the bond attribute stored into the parameter set
221      *
222      * @throws Exception Description of the Exception
223      */
setBond()224     private void setBond() throws Exception {
225         List data = new Vector();
226         st.nextToken();
227         String scode = st.nextToken();
228         String sid1 = st.nextToken();
229         String sid2 = st.nextToken();
230         String slen = st.nextToken();
231         String sk2 = st.nextToken();
232         String sk3 = st.nextToken();
233         String sk4 = st.nextToken();
234         String sbci = st.nextToken();
235 
236         try {
237             double len = new Double(slen).doubleValue();
238             double k2 = new Double(sk2).doubleValue();
239             double k3 = new Double(sk3).doubleValue();
240             double k4 = new Double(sk4).doubleValue();
241             double bci = new Double(sbci).doubleValue();
242             data.add(new Double(len));
243             data.add(new Double(k2));
244             data.add(new Double(k3));
245             data.add(new Double(k4));
246             data.add(new Double(bci));
247 
248         } catch (NumberFormatException nfe) {
249             throw new IOException("setBond: Malformed Number due to:" + nfe);
250         }
251         //		key = "bond" + scode + ";" + sid1 + ";" + sid2;
252         key = "bond" + sid1 + ";" + sid2;
253         parameterSet.put(key, data);
254     }
255 
256     /**
257      * Sets the angle attribute stored into the parameter set
258      *
259      * @throws Exception Description of the Exception
260      */
setAngle()261     private void setAngle() throws Exception {
262         List data = new Vector();
263         st.nextToken();
264         String scode = st.nextToken(); // String scode
265         String sid1 = st.nextToken();
266         String sid2 = st.nextToken();
267         String sid3 = st.nextToken();
268         String value1 = st.nextToken();
269         String value2 = st.nextToken();
270         String value3 = st.nextToken();
271         String value4 = st.nextToken();
272 
273         try {
274             //int code=new Integer(scode).intValue();
275             double va1 = new Double(value1).doubleValue();
276             double va2 = new Double(value2).doubleValue();
277             double va3 = new Double(value3).doubleValue();
278             double va4 = new Double(value4).doubleValue();
279             data.add(new Double(va1));
280             data.add(new Double(va2));
281             data.add(new Double(va3));
282             data.add(new Double(va4));
283 
284             //			key = "angle" + scode + ";" + sid1 + ";" + sid2 + ";" + sid3;
285             key = "angle" + sid1 + ";" + sid2 + ";" + sid3;
286             if (parameterSet.containsKey(key)) {
287                 data = (Vector) parameterSet.get(key);
288                 data.add(new Double(va1));
289                 data.add(new Double(va2));
290                 data.add(new Double(va3));
291                 data.add(new Double(va4));
292             }
293             parameterSet.put(key, data);
294 
295         } catch (NumberFormatException nfe) {
296             throw new IOException("setAngle: Malformed Number due to:" + nfe);
297         }
298     }
299 
300     /**
301      * Sets the strBnd attribute stored into the parameter set
302      *
303      * @throws Exception Description of the Exception
304      */
setStrBnd()305     private void setStrBnd() throws Exception {
306         List data = new Vector();
307         st.nextToken();
308         String scode = st.nextToken(); // String scode
309         String sid1 = st.nextToken();
310         String sid2 = st.nextToken();
311         String sid3 = st.nextToken();
312         String value1 = st.nextToken();
313         String value2 = st.nextToken();
314 
315         try {
316             //int code=new Integer(scode).intValue();
317             double va1 = new Double(value1).doubleValue();
318             double va2 = new Double(value2).doubleValue();
319             data.add(new Double(va1));
320             data.add(new Double(va2));
321 
322         } catch (NumberFormatException nfe) {
323             throw new IOException("setStrBnd: Malformed Number due to:" + nfe);
324         }
325         key = "strbnd" + scode + ";" + sid1 + ";" + sid2 + ";" + sid3;
326         LOG.debug("key =" + key);
327         parameterSet.put(key, data);
328     }
329 
330     /**
331      * Sets the torsion attribute stored into the parameter set
332      *
333      * @throws Exception Description of the Exception
334      */
setTorsion()335     private void setTorsion() throws Exception {
336         List data = null;
337         st.nextToken();
338         String scode = st.nextToken(); // String scode
339         String sid1 = st.nextToken();
340         String sid2 = st.nextToken();
341         String sid3 = st.nextToken();
342         String sid4 = st.nextToken();
343         String value1 = st.nextToken();
344         String value2 = st.nextToken();
345         String value3 = st.nextToken();
346         String value4 = st.nextToken();
347         String value5 = st.nextToken();
348 
349         try {
350             double va1 = new Double(value1).doubleValue();
351             double va2 = new Double(value2).doubleValue();
352             double va3 = new Double(value3).doubleValue();
353             double va4 = new Double(value4).doubleValue();
354             double va5 = new Double(value5).doubleValue();
355 
356             key = "torsion" + scode + ";" + sid1 + ";" + sid2 + ";" + sid3 + ";" + sid4;
357             LOG.debug("key = " + key);
358             if (parameterSet.containsKey(key)) {
359                 data = (Vector) parameterSet.get(key);
360                 data.add(new Double(va1));
361                 data.add(new Double(va2));
362                 data.add(new Double(va3));
363                 data.add(new Double(va4));
364                 data.add(new Double(va5));
365                 LOG.debug("data = " + data);
366             } else {
367                 data = new Vector();
368                 data.add(new Double(va1));
369                 data.add(new Double(va2));
370                 data.add(new Double(va3));
371                 data.add(new Double(va4));
372                 data.add(new Double(va5));
373                 LOG.debug("data = " + data);
374             }
375 
376             parameterSet.put(key, data);
377 
378         } catch (NumberFormatException nfe) {
379             throw new IOException("setTorsion: Malformed Number due to:" + nfe);
380         }
381     }
382 
383     /**
384      * Sets the opBend attribute stored into the parameter set
385      *
386      * @throws Exception Description of the Exception
387      */
setOpBend()388     private void setOpBend() throws Exception {
389         List data = new Vector();
390         st.nextToken();
391         String sid1 = st.nextToken();
392         String sid2 = st.nextToken();
393         String sid3 = st.nextToken();
394         String sid4 = st.nextToken();
395         String value1 = st.nextToken();
396 
397         try {
398             double va1 = new Double(value1).doubleValue();
399             data.add(new Double(va1));
400             key = "opbend" + sid1 + ";" + sid2 + ";" + sid3 + ";" + sid4;
401             if (parameterSet.containsKey(key)) {
402                 data = (Vector) parameterSet.get(key);
403                 data.add(new Double(va1));
404             }
405             parameterSet.put(key, data);
406 
407         } catch (NumberFormatException nfe) {
408             throw new IOException("setOpBend: Malformed Number due to:" + nfe);
409         }
410     }
411 
412     /**
413      * Sets the Default Stretch-Bend Parameters into the parameter set
414      *
415      * @throws Exception Description of the Exception
416      */
setDefaultStrBnd()417     private void setDefaultStrBnd() throws Exception {
418         LOG.debug("Sets the Default Stretch-Bend Parameters");
419         List data = new Vector();
420         stDFSB.nextToken();
421         String sIR = stDFSB.nextToken();
422         String sJR = stDFSB.nextToken();
423         String sKR = stDFSB.nextToken();
424         String skbaIJK = stDFSB.nextToken();
425         String skbaKJI = stDFSB.nextToken();
426 
427         try {
428             key = "DFSB" + sIR + ";" + sJR + ";" + sKR;
429             double kbaIJK = new Double(skbaIJK).doubleValue();
430             double kbaKJI = new Double(skbaKJI).doubleValue();
431             data.add(new Double(kbaIJK));
432             data.add(new Double(kbaKJI));
433             parameterSet.put(key, data);
434 
435         } catch (NumberFormatException nfe) {
436             throw new IOException("setDFSB: Malformed Number due to:" + nfe);
437         }
438     }
439 
440     /**
441      * The main method which parses through the force field configuration file
442      *
443      * @throws Exception Description of the Exception
444      */
readParameterSets(IChemObjectBuilder builder)445     public void readParameterSets(IChemObjectBuilder builder) throws Exception {
446         //vdW,bond,angle,strbond,opbend,torsion,data
447         LOG.debug("------ Read MMFF94 ParameterSets ------");
448 
449         if (ins == null) {
450             ClassLoader loader = this.getClass().getClassLoader();
451             ins = loader.getResourceAsStream(configFile);
452         }
453         if (ins == null) {
454             throw new IOException("There was a problem getting the default stream: " + configFile);
455         }
456 
457         BufferedReader r = new BufferedReader(new InputStreamReader(ins), 1024);
458         String s;
459         int[] a = {0, 0, 0, 0, 0, 0, 0, 0};
460 
461         if (insvdW == null) {
462             insvdW = this.getClass().getClassLoader().getResourceAsStream(configFilevdW);
463         }
464         if (insvdW == null) {
465             throw new IOException("There was a problem getting the default stream: " + configFilevdW);
466         }
467 
468         BufferedReader rvdW = new BufferedReader(new InputStreamReader(insvdW), 1024);
469         String svdW;
470         int ntvdW;
471 
472         if (insDFSB == null) {
473             insDFSB = this.getClass().getClassLoader().getResourceAsStream(configFileDFSB);
474         }
475         if (insDFSB == null) {
476             throw new IOException("There was a problem getting the default stream: " + configFileDFSB);
477         }
478 
479         BufferedReader rDFSB = new BufferedReader(new InputStreamReader(insDFSB), 1024);
480         String sDFSB;
481         int ntDFSB;
482 
483         try {
484             while (true) {
485                 s = r.readLine();
486                 if (s == null) {
487                     break;
488                 }
489                 st = new StringTokenizer(s, "\t; ");
490                 int nt = st.countTokens();
491                 if (s.startsWith("atom") & nt <= 8) {
492                     setAtomTypes(builder);
493                     a[0]++;
494                 } else if (s.startsWith("bond") & nt == 9) {
495                     setBond();
496                     a[1]++;
497                 } else if (s.startsWith("angle") & nt <= 10) {
498                     setAngle();
499                     a[2]++;
500                 } else if (s.startsWith("strbnd") & nt == 7) {
501                     setStrBnd();
502                     a[3]++;
503                 } else if (s.startsWith("torsion") & nt == 11) {
504                     setTorsion();
505                     a[4]++;
506                 } else if (s.startsWith("opbend") & nt == 6) {
507                     setOpBend();
508                     a[5]++;
509                 } else if (s.startsWith("data") & nt == 10) {
510                     readatmmffvdw: while (true) {
511                         svdW = rvdW.readLine();
512                         if (svdW == null) {
513                             break;
514                         }
515                         stvdW = new StringTokenizer(svdW, "\t; ");
516                         ntvdW = stvdW.countTokens();
517                         LOG.debug("ntvdW : " + ntvdW);
518                         if (svdW.startsWith("vdw") & ntvdW == 9) {
519                             st.nextToken();
520                             sid = st.nextToken();
521                             stvdW.nextToken();
522                             sidvdW = stvdW.nextToken();
523                             if (sid.equals(sidvdW)) {
524                                 setAtomTypeData();
525                                 a[6]++;
526                             }
527                             break readatmmffvdw;
528                         }
529                     }// end while
530                 }
531             }// end while
532 
533             ins.close();
534             insvdW.close();
535         } catch (IOException e) {
536             throw new IOException("There was a problem parsing the mmff94 forcefield");
537         }
538 
539         try {
540             LOG.debug("Parses the Default Stretch-Bend Parameters");
541             while (true) {
542                 sDFSB = rDFSB.readLine();
543                 LOG.debug("sDFSB = " + sDFSB);
544                 if (sDFSB == null) {
545                     LOG.debug("sDFSB == null, break");
546                     break;
547                 }
548                 stDFSB = new StringTokenizer(sDFSB, "\t; ");
549                 ntDFSB = stDFSB.countTokens();
550                 LOG.debug("ntDFSB : " + ntDFSB);
551                 if (sDFSB.startsWith("DFSB") & ntDFSB == 6) {
552                     setDefaultStrBnd();
553                 }
554             }
555             insDFSB.close();
556             LOG.debug("insDFSB closed");
557         } catch (IOException e) {
558             throw new IOException("There was a problem parsing the Default Stretch-Bend Parameters (mmffdfsb.par)");
559         }
560     }
561 
562     /**
563      * Mass number for a atom with a given atomic number and exact mass.
564      *
565      * @param atomicNumber atomic number
566      * @param exactMass    exact mass
567      * @return the mass number (or null) if no mass number was found
568      * @throws IOException isotope configuration could not be loaded
569      */
massNumber(int atomicNumber, double exactMass)570     private Integer massNumber(int atomicNumber, double exactMass) throws IOException {
571         String symbol = PeriodicTable.getSymbol(atomicNumber);
572         IIsotope isotope = Isotopes.getInstance().getIsotope(symbol, exactMass, 0.001);
573         return isotope != null ? isotope.getMassNumber() : null;
574     }
575 
576 }
577