View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2021.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.potential.parameters;
39  
40  import static ffx.potential.parameters.ForceField.ForceFieldType.SOLUTE;
41  import static ffx.utilities.KeywordGroup.PotentialFunctionParameter;
42  import static java.lang.Double.parseDouble;
43  import static java.lang.Integer.parseInt;
44  import static java.lang.String.format;
45  
46  import ffx.potential.bonded.Atom;
47  import ffx.utilities.FFXKeyword;
48  import org.apache.commons.lang3.math.NumberUtils;
49  
50  import java.util.Comparator;
51  import java.util.HashMap;
52  import java.util.Map;
53  import java.util.Objects;
54  import java.util.logging.Level;
55  import java.util.logging.Logger;
56  
57  /**
58   * The SoluteType class defines one implicit solvent radius.
59   *
60   * @author Michael J. Schnieders
61   * @since 1.0
62   */
63  @FFXKeyword(name = "solute", clazz = String.class, keywordGroup = PotentialFunctionParameter,
64      description = "[integers & 3 reals] "
65          + "Provides values for a single implicit solvation parameter. "
66          + "The integer modifier gives the atom type number for which solvation atom size parameters are to be defined. "
67          + "The three real number modifiers give the values of the atomic diameter in Angstroms, for use in Poisson-Boltzmann (APBS), ddCOSMO and Generalized Kirkwood (GK) calculations, respectively.")
68  public final class SoluteType extends BaseType implements Comparator<String> {
69  
70    /** A Logger for the SoluteType class. */
71    private static final Logger logger = Logger.getLogger(SoluteType.class.getName());
72  
73    public enum SOLUTE_RADII_TYPE {VDW, CONSENSUS, SOLUTE}
74  
75    /** This maps atomic number to reasonable GK base radii. */
76    static final Map<Integer, SoluteType> CONSENSUS_RADII = new HashMap<>();
77  
78    /** Solute atomic diameter for PB. */
79    public double pbDiameter;
80    /** Solute atomic diameter for ddCOSMO. */
81    public double cosDiameter;
82    /** Solute atomic diameter for GK. */
83    public double gkDiameter;
84    /** Sneck scaling factor to use with interstitial space corrections */
85    public double sneck;
86    public double DEFAULT_SNECK = 0.6784;
87  
88    /** Optional SMARTS description. */
89    public String description;
90    /** Atom type for this solute type. */
91    private int atomType;
92  
93    /**
94     * Constructor for SoluteType.
95     *
96     * @param atomType Atom type.
97     * @param diameter Diameter for all continuum electrostatics models.
98     */
99    public SoluteType(int atomType, double diameter) {
100     super(SOLUTE, Integer.toString(atomType));
101     this.atomType = atomType;
102     this.pbDiameter = diameter;
103     this.cosDiameter = diameter;
104     this.gkDiameter = diameter;
105     this.description = null;
106     this.sneck = DEFAULT_SNECK;
107   }
108 
109   /**
110    * Constructor for SoluteType.
111    *
112    * @param atomType Atom type.
113    * @param pbDiameter Diameter for PB continuum electrostatics.
114    * @param cosDiameter Diameter for ddCOSMO continuum electrostatics.
115    * @param gkDiameter Diameter for GK continuum electrostatics.
116    */
117   public SoluteType(int atomType, double pbDiameter, double cosDiameter, double gkDiameter) {
118     super(SOLUTE, Integer.toString(atomType));
119     this.atomType = atomType;
120     this.pbDiameter = pbDiameter;
121     this.cosDiameter = cosDiameter;
122     this.gkDiameter = gkDiameter;
123     this.description = null;
124     this.sneck = DEFAULT_SNECK;
125   }
126 
127   /**
128    * Constructor for SoluteType.
129    *
130    * @param atomType Atom type.
131    * @param pbDiameter Diameter for PB continuum electrostatics.
132    * @param cosDiameter Diameter for ddCOSMO continuum electrostatics.
133    * @param gkDiameter Diameter for GK continuum electrostatics.
134    * @param sneck Sneck scaling factor for implicit solvent interstitial space corrections
135    */
136   public SoluteType(int atomType, double pbDiameter, double cosDiameter, double gkDiameter,
137       double sneck) {
138     super(SOLUTE, Integer.toString(atomType));
139     this.atomType = atomType;
140     this.pbDiameter = pbDiameter;
141     this.cosDiameter = cosDiameter;
142     this.gkDiameter = gkDiameter;
143     this.description = null;
144     this.sneck = sneck;
145   }
146 
147   /**
148    * Constructor for SoluteType.
149    *
150    * @param atomType Atom type.
151    * @param description Smarts description.
152    * @param pbDiameter Diameter for PB continuum electrostatics.
153    * @param cosDiameter Diameter for ddCOSMO continuum electrostatics.
154    * @param gkDiameter Diameter for GK continuum electrostatics.
155    */
156   public SoluteType(int atomType, String description, double pbDiameter, double cosDiameter,
157       double gkDiameter) {
158     this(atomType, pbDiameter, cosDiameter, gkDiameter);
159     this.description = description;
160     this.sneck = DEFAULT_SNECK;
161   }
162 
163   /**
164    * Construct a SoluteType from an input string.
165    *
166    * @param input The overall input String.
167    * @param tokens The input String tokenized.
168    * @return a SoluteType instance.
169    */
170   public static SoluteType parse(String input, String[] tokens) {
171     if (tokens.length < 4) {
172       logger.log(Level.WARNING, "Invalid SOLUTE type:\n{0}", input);
173     } else {
174       try {
175         if (tokens.length == 5) {
176           int atomType = parseInt(tokens[1].trim());
177           double pbDiameter = parseDouble(tokens[2].trim());
178           double cosDiameter = parseDouble(tokens[3].trim());
179           double gkDiameter = parseDouble(tokens[4].trim());
180           return new SoluteType(atomType, pbDiameter, cosDiameter, gkDiameter);
181         } else {
182           // A SOLUTE line with length six could have a description or an Sneck value
183           // Need to check if the entry at index 2 is a parseable number to determine which constructor to use
184           if (NumberUtils.isParsable(tokens[2].trim())) {
185             // Use Sneck included constructor
186             int atomType = parseInt(tokens[1].trim());
187             double pbDiameter = parseDouble(tokens[2].trim());
188             double cosDiameter = parseDouble(tokens[3].trim());
189             double gkDiameter = parseDouble(tokens[4].trim());
190             double sneck = parseDouble(tokens[5].trim());
191             return new SoluteType(atomType, pbDiameter, cosDiameter, gkDiameter, sneck);
192           } else {
193             // Use description included constructor
194             // (needed for optimization with PolType parameter files for individual molecules)
195             int atomType = parseInt(tokens[1].trim());
196             String description = tokens[2].trim();
197             double pbDiameter = parseDouble(tokens[3].trim());
198             double cosDiameter = parseDouble(tokens[4].trim());
199             double gkDiameter = parseDouble(tokens[5].trim());
200             return new SoluteType(atomType, description, pbDiameter, cosDiameter, gkDiameter);
201           }
202         }
203       } catch (NumberFormatException e) {
204         String message = "Exception parsing SOLUTE type:\n" + input + "\n";
205         logger.log(Level.SEVERE, message, e);
206       }
207     }
208     return null;
209   }
210 
211   /** {@inheritDoc} */
212   @Override
213   public int compare(String key1, String key2) {
214     int type1 = parseInt(key1);
215     int type2 = parseInt(key2);
216     return Integer.compare(type1, type2);
217   }
218 
219   /** {@inheritDoc} */
220   @Override
221   public boolean equals(Object o) {
222     if (this == o) {
223       return true;
224     }
225     if (o == null || getClass() != o.getClass()) {
226       return false;
227     }
228     SoluteType soluteType = (SoluteType) o;
229     return soluteType.atomType == this.atomType;
230   }
231 
232   /** {@inheritDoc} */
233   @Override
234   public int hashCode() {
235     return Objects.hash(atomType);
236   }
237 
238   /**
239    * {@inheritDoc}
240    *
241    * <p>Nicely formatted bond stretch string.
242    */
243   @Override
244   public String toString() {
245     if (description == null) {
246       return format("solute  %4d  %7.4f  %7.4f %7.4f", atomType, pbDiameter, cosDiameter,
247           gkDiameter);
248     } else {
249       return format("solute  %4d  %30s  %7.4f  %7.4f %7.4f", atomType, description, pbDiameter,
250           cosDiameter, gkDiameter);
251     }
252   }
253 
254   /**
255    * incrementType
256    *
257    * @param increment a int.
258    */
259   void incrementType(int increment) {
260     atomType += increment;
261     setKey(Integer.toString(atomType));
262   }
263 
264   static {
265     CONSENSUS_RADII.put(0, new SoluteType(0, 0.00 * 2.0));
266     CONSENSUS_RADII.put(1, new SoluteType(1, 1.20 * 2.0));
267     CONSENSUS_RADII.put(2, new SoluteType(2, 1.40 * 2.0));
268     CONSENSUS_RADII.put(5, new SoluteType(5, 1.80 * 2.0));
269     CONSENSUS_RADII.put(6, new SoluteType(6, 1.70 * 2.0));
270     CONSENSUS_RADII.put(7, new SoluteType(7, 1.55 * 2.0));
271     CONSENSUS_RADII.put(8, new SoluteType(8, 1.52 * 2.0));
272     CONSENSUS_RADII.put(9, new SoluteType(9, 1.47 * 2.0));
273     CONSENSUS_RADII.put(10, new SoluteType(10, 1.54 * 2.0));
274     CONSENSUS_RADII.put(14, new SoluteType(14, 2.10 * 2.0));
275     CONSENSUS_RADII.put(15, new SoluteType(15, 1.80 * 2.0));
276     CONSENSUS_RADII.put(16, new SoluteType(16, 1.80 * 2.0));
277     CONSENSUS_RADII.put(17, new SoluteType(17, 1.75 * 2.0));
278     CONSENSUS_RADII.put(18, new SoluteType(18, 1.88 * 2.0));
279     CONSENSUS_RADII.put(34, new SoluteType(34, 1.90 * 2.0));
280     CONSENSUS_RADII.put(35, new SoluteType(35, 1.85 * 2.0));
281     CONSENSUS_RADII.put(36, new SoluteType(36, 2.02 * 2.0));
282     CONSENSUS_RADII.put(53, new SoluteType(53, 1.98 * 2.0));
283     CONSENSUS_RADII.put(54, new SoluteType(54, 2.16 * 2.0));
284     for (int i = 0; i <= 118; i++) {
285       if (!CONSENSUS_RADII.containsKey(i)) {
286         CONSENSUS_RADII.put(i, new SoluteType(i, 2.0 * 2.0));
287       }
288     }
289   }
290 
291   public static SoluteType getCensusSoluteType(int atomicNumber) {
292     return CONSENSUS_RADII.get(atomicNumber);
293   }
294 
295   public static SoluteType getVDWSoluteType(VDWType vdwType) {
296     return new SoluteType(vdwType.atomClass, vdwType.radius);
297   }
298 
299   public static SoluteType getFitSoluteType(ForceField forceField, int type) {
300     SoluteType soluteType = forceField.getSoluteType(Integer.toString(type));
301     if (soluteType != null) {
302       if (soluteType.gkDiameter <= 0.0) {
303         logger.severe(format(" Invalid solute type: %s", soluteType));
304       }
305     }
306     return soluteType;
307   }
308 
309   public static SoluteType getSoluteType(Atom atom, ForceField forceField,
310       SOLUTE_RADII_TYPE soluteRadiiType) {
311     // Begin by setting the base radius to a consensus radius.
312     SoluteType soluteType = getCensusSoluteType(atom.getAtomicNumber());
313     // Overwrite consensus radii with VDW or SOLUTE radii.
314     if (soluteRadiiType == SOLUTE_RADII_TYPE.VDW) {
315       SoluteType type = getVDWSoluteType(atom.getVDWType());
316       if (type != null) {
317         soluteType = type;
318       }
319     } else if (soluteRadiiType == SOLUTE_RADII_TYPE.SOLUTE) {
320       SoluteType type = getFitSoluteType(forceField, atom.getAtomType().type);
321       if (type != null) {
322         soluteType = type;
323       }
324     }
325     return soluteType;
326   }
327 
328   public static void setSoluteRadii(ForceField forceField, Atom[] atoms,
329       SOLUTE_RADII_TYPE soluteRadiiType) {
330     for (Atom atom : atoms) {
331       atom.setSoluteType(getSoluteType(atom, forceField, soluteRadiiType));
332     }
333   }
334 
335 }