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-2024.
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.PropertyGroup.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.FFXProperty;
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  @FFXProperty(name = "solute", clazz = String.class, propertyGroup = PotentialFunctionParameter,
64      description = """
65          [integers and 4 reals]
66          Provides values for a single implicit solvation parameter.
67          The integer modifier gives the atom type number for which solvation atom size parameters are to be defined.
68          The first three real number modifiers give the values of the atomic diameter in Angstroms,
69          for use in Poisson-Boltzmann (APBS), ddCOSMO and Generalized Kirkwood (GK) calculations, respectively.
70          The final real number is the Sneck scaling factor for implicit solvent interstitial space corrections.
71          """)
72    public final class SoluteType extends BaseType implements Comparator<String> {
73  
74    /** A Logger for the SoluteType class. */
75    private static final Logger logger = Logger.getLogger(SoluteType.class.getName());
76  
77    public enum SOLUTE_RADII_TYPE {VDW, CONSENSUS, SOLUTE}
78  
79    /** This maps atomic number to reasonable GK base radii. */
80    static final Map<Integer, SoluteType> CONSENSUS_RADII = new HashMap<>();
81  
82    /** Solute atomic diameter for PB. */
83    public double pbDiameter;
84    /** Solute atomic diameter for ddCOSMO. */
85    public double cosDiameter;
86    /** Solute atomic diameter for GK. */
87    public double gkDiameter;
88    /** Sneck scaling factor to use with interstitial space corrections */
89    public double sneck;
90    public double DEFAULT_SNECK = 0.6784;
91  
92    /** Optional SMARTS description. */
93    public String description;
94    /** Atom type for this solute type. */
95    private int atomType;
96  
97    /**
98     * Constructor for SoluteType.
99     *
100    * @param atomType Atom type.
101    * @param diameter Diameter for all continuum electrostatics models.
102    */
103   public SoluteType(int atomType, double diameter) {
104     super(SOLUTE, Integer.toString(atomType));
105     this.atomType = atomType;
106     this.pbDiameter = diameter;
107     this.cosDiameter = diameter;
108     this.gkDiameter = diameter;
109     this.description = null;
110     this.sneck = DEFAULT_SNECK;
111   }
112 
113   /**
114    * Constructor for SoluteType.
115    *
116    * @param atomType Atom type.
117    * @param pbDiameter Diameter for PB continuum electrostatics.
118    * @param cosDiameter Diameter for ddCOSMO continuum electrostatics.
119    * @param gkDiameter Diameter for GK continuum electrostatics.
120    */
121   public SoluteType(int atomType, double pbDiameter, double cosDiameter, double gkDiameter) {
122     super(SOLUTE, Integer.toString(atomType));
123     this.atomType = atomType;
124     this.pbDiameter = pbDiameter;
125     this.cosDiameter = cosDiameter;
126     this.gkDiameter = gkDiameter;
127     this.description = null;
128     this.sneck = DEFAULT_SNECK;
129   }
130 
131   /**
132    * Constructor for SoluteType.
133    *
134    * @param atomType Atom type.
135    * @param pbDiameter Diameter for PB continuum electrostatics.
136    * @param cosDiameter Diameter for ddCOSMO continuum electrostatics.
137    * @param gkDiameter Diameter for GK continuum electrostatics.
138    * @param sneck Sneck scaling factor for implicit solvent interstitial space corrections
139    */
140   public SoluteType(int atomType, double pbDiameter, double cosDiameter, double gkDiameter, double sneck) {
141     super(SOLUTE, Integer.toString(atomType));
142     this.atomType = atomType;
143     this.pbDiameter = pbDiameter;
144     this.cosDiameter = cosDiameter;
145     this.gkDiameter = gkDiameter;
146     this.description = null;
147     this.sneck = sneck;
148   }
149 
150   /**
151    * Constructor for SoluteType.
152    *
153    * @param atomType Atom type.
154    * @param description Smarts description.
155    * @param pbDiameter Diameter for PB continuum electrostatics.
156    * @param cosDiameter Diameter for ddCOSMO continuum electrostatics.
157    * @param gkDiameter Diameter for GK continuum electrostatics.
158    */
159   public SoluteType(int atomType, String description, double pbDiameter, double cosDiameter, double gkDiameter) {
160     this(atomType, pbDiameter, cosDiameter, gkDiameter);
161     this.description = description;
162     this.sneck = DEFAULT_SNECK;
163   }
164 
165   /**
166    * Construct a SoluteType from an input string.
167    *
168    * @param input The overall input String.
169    * @param tokens The input String tokenized.
170    * @return a SoluteType instance.
171    */
172   public static SoluteType parse(String input, String[] tokens) {
173     if (tokens.length < 4) {
174       logger.log(Level.WARNING, "Invalid SOLUTE type:\n{0}", input);
175     } else {
176       try {
177         if (tokens.length == 5) {
178           int atomType = parseInt(tokens[1].trim());
179           double pbDiameter = parseDouble(tokens[2].trim());
180           double cosDiameter = parseDouble(tokens[3].trim());
181           double gkDiameter = parseDouble(tokens[4].trim());
182           return new SoluteType(atomType, pbDiameter, cosDiameter, gkDiameter);
183         } else {
184           // A SOLUTE line with length six could have a description or a Sneck value.
185           // Need to check if the entry at index 2 is a parseable number to determine which constructor to use
186           if (NumberUtils.isParsable(tokens[2].trim())) {
187             // Use Sneck included constructor
188             int atomType = parseInt(tokens[1].trim());
189             double pbDiameter = parseDouble(tokens[2].trim());
190             double cosDiameter = parseDouble(tokens[3].trim());
191             double gkDiameter = parseDouble(tokens[4].trim());
192             double sneck = parseDouble(tokens[5].trim());
193             return new SoluteType(atomType, pbDiameter, cosDiameter, gkDiameter, sneck);
194           } else {
195             // Use description included constructor
196             // (needed for optimization with PolType parameter files for individual molecules)
197             int atomType = parseInt(tokens[1].trim());
198             String description = tokens[2].trim();
199             double pbDiameter = parseDouble(tokens[3].trim());
200             double cosDiameter = parseDouble(tokens[4].trim());
201             double gkDiameter = parseDouble(tokens[5].trim());
202             return new SoluteType(atomType, description, pbDiameter, cosDiameter, gkDiameter);
203           }
204         }
205       } catch (NumberFormatException e) {
206         String message = "Exception parsing SOLUTE type:\n" + input + "\n";
207         logger.log(Level.SEVERE, message, e);
208       }
209     }
210     return null;
211   }
212 
213   /** {@inheritDoc} */
214   @Override
215   public int compare(String key1, String key2) {
216     int type1 = parseInt(key1);
217     int type2 = parseInt(key2);
218     return Integer.compare(type1, type2);
219   }
220 
221   /** {@inheritDoc} */
222   @Override
223   public boolean equals(Object o) {
224     if (this == o) {
225       return true;
226     }
227     if (o == null || getClass() != o.getClass()) {
228       return false;
229     }
230     SoluteType soluteType = (SoluteType) o;
231     return soluteType.atomType == this.atomType;
232   }
233 
234   /** {@inheritDoc} */
235   @Override
236   public int hashCode() {
237     return Objects.hash(atomType);
238   }
239 
240   /**
241    * {@inheritDoc}
242    *
243    * <p>Nicely formatted bond stretch string.
244    */
245   @Override
246   public String toString() {
247     if (description == null) {
248       return format("solute  %4d  %7.4f  %7.4f %7.4f", atomType, pbDiameter, cosDiameter,
249           gkDiameter);
250     } else {
251       return format("solute  %4d  %30s  %7.4f  %7.4f %7.4f", atomType, description, pbDiameter,
252           cosDiameter, gkDiameter);
253     }
254   }
255 
256   /**
257    * Increment the atom type by a specified value.
258    *
259    * @param increment The value to increment the atom type by.
260    */
261   void incrementType(int increment) {
262     atomType += increment;
263     setKey(Integer.toString(atomType));
264   }
265 
266   static {
267     CONSENSUS_RADII.put(0, new SoluteType(0, 0.00 * 2.0));
268     CONSENSUS_RADII.put(1, new SoluteType(1, 1.20 * 2.0));
269     CONSENSUS_RADII.put(2, new SoluteType(2, 1.40 * 2.0));
270     CONSENSUS_RADII.put(5, new SoluteType(5, 1.80 * 2.0));
271     CONSENSUS_RADII.put(6, new SoluteType(6, 1.70 * 2.0));
272     CONSENSUS_RADII.put(7, new SoluteType(7, 1.55 * 2.0));
273     CONSENSUS_RADII.put(8, new SoluteType(8, 1.52 * 2.0));
274     CONSENSUS_RADII.put(9, new SoluteType(9, 1.47 * 2.0));
275     CONSENSUS_RADII.put(10, new SoluteType(10, 1.54 * 2.0));
276     CONSENSUS_RADII.put(14, new SoluteType(14, 2.10 * 2.0));
277     CONSENSUS_RADII.put(15, new SoluteType(15, 1.80 * 2.0));
278     CONSENSUS_RADII.put(16, new SoluteType(16, 1.80 * 2.0));
279     CONSENSUS_RADII.put(17, new SoluteType(17, 1.75 * 2.0));
280     CONSENSUS_RADII.put(18, new SoluteType(18, 1.88 * 2.0));
281     CONSENSUS_RADII.put(34, new SoluteType(34, 1.90 * 2.0));
282     CONSENSUS_RADII.put(35, new SoluteType(35, 1.85 * 2.0));
283     CONSENSUS_RADII.put(36, new SoluteType(36, 2.02 * 2.0));
284     CONSENSUS_RADII.put(53, new SoluteType(53, 1.98 * 2.0));
285     CONSENSUS_RADII.put(54, new SoluteType(54, 2.16 * 2.0));
286     for (int i = 0; i <= 118; i++) {
287       if (!CONSENSUS_RADII.containsKey(i)) {
288         CONSENSUS_RADII.put(i, new SoluteType(i, 2.0 * 2.0));
289       }
290     }
291   }
292 
293   public static SoluteType getCensusSoluteType(int atomicNumber) {
294     return CONSENSUS_RADII.get(atomicNumber);
295   }
296 
297   public static SoluteType getVDWSoluteType(VDWType vdwType) {
298     return new SoluteType(vdwType.atomClass, vdwType.radius);
299   }
300 
301   public static SoluteType getFitSoluteType(ForceField forceField, int type) {
302     SoluteType soluteType = forceField.getSoluteType(Integer.toString(type));
303     if (soluteType != null) {
304       if (soluteType.gkDiameter <= 0.0) {
305         logger.severe(format(" Invalid solute type: %s", soluteType));
306       }
307     }
308     return soluteType;
309   }
310 
311   public static SoluteType getSoluteType(Atom atom, ForceField forceField,
312       SOLUTE_RADII_TYPE soluteRadiiType) {
313     // Begin by setting the base radius to a consensus radius.
314     SoluteType soluteType = getCensusSoluteType(atom.getAtomicNumber());
315     // Overwrite consensus radii with VDW or SOLUTE radii.
316     if (soluteRadiiType == SOLUTE_RADII_TYPE.VDW) {
317       SoluteType type = getVDWSoluteType(atom.getVDWType());
318       if (type != null) {
319         soluteType = type;
320       }
321     } else if (soluteRadiiType == SOLUTE_RADII_TYPE.SOLUTE) {
322       SoluteType type = getFitSoluteType(forceField, atom.getAtomType().type);
323       if (type != null) {
324         soluteType = type;
325       }
326     }
327     return soluteType;
328   }
329 
330   public static void setSoluteRadii(ForceField forceField, Atom[] atoms,
331       SOLUTE_RADII_TYPE soluteRadiiType) {
332     for (Atom atom : atoms) {
333       atom.setSoluteType(getSoluteType(atom, forceField, soluteRadiiType));
334     }
335   }
336 
337 }