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.realspace;
39  
40  import static java.lang.String.format;
41  
42  import ffx.crystal.Crystal;
43  import ffx.crystal.CrystalPotential;
44  import ffx.potential.bonded.Atom;
45  import ffx.potential.bonded.LambdaInterface;
46  import ffx.xray.RefinementMinimize.RefinementMode;
47  import ffx.xray.RefinementModel;
48  import java.util.logging.Logger;
49  
50  /**
51   * Combine the Real Space target and chemical potential energy.
52   *
53   * @author Timothy D. Fenn
54   * @author Michael J. Schnieders
55   * @since 1.0
56   */
57  public class RealSpaceEnergy implements LambdaInterface, CrystalPotential {
58  
59    private static final Logger logger = Logger.getLogger(RealSpaceEnergy.class.getName());
60  
61    /** The Real Space data to refine against. */
62    private final RealSpaceData realSpaceData;
63    /** The Refinement Model that contains info on mapping between alternate conformers. */
64    private final RefinementModel refinementModel;
65    /** Value of the lambda state variable. */
66    protected double lambda = 1.0;
67    /** The number of parameters that are being refined. */
68    private int nXYZ;
69    /** The refinement mode. */
70    private RefinementMode refinementMode;
71    /** If true, the XYZ coordinates will be refined. */
72    private boolean refineXYZ = false;
73    /** Optimization scaling used to improve convergence. */
74    private double[] optimizationScaling = null;
75    /** Total energy of the refinement. */
76    private double totalEnergy;
77    /** The RealSpaceEnergy is updated with FAST varying energy terms. */
78    private STATE state = STATE.BOTH;
79  
80    /**
81     * Diffraction data energy target
82     *
83     * @param realSpaceData {@link ffx.realspace.RealSpaceData} object to associate with the target
84     * @param nxyz number of xyz parameters
85     * @param nb number of b factor parameters
86     * @param nocc number of occupancy parameters
87     * @param refinementMode the {@link ffx.xray.RefinementMinimize.RefinementMode} type of refinement
88     *     requested
89     */
90    public RealSpaceEnergy(
91        RealSpaceData realSpaceData, int nxyz, int nb, int nocc, RefinementMode refinementMode) {
92      this.realSpaceData = realSpaceData;
93      this.refinementModel = realSpaceData.getRefinementModel();
94      this.refinementMode = refinementMode;
95      this.nXYZ = nxyz;
96      setRefinementBooleans();
97    }
98  
99    /** {@inheritDoc} */
100   @Override
101   public boolean destroy() {
102     return realSpaceData.destroy();
103   }
104 
105   /**
106    * The parameters passed in are only for "active" atoms.
107    *
108    * <p>{@inheritDoc}
109    */
110   @Override
111   public double energy(double[] x) {
112 
113     double e = 0.0;
114 
115     // Unscale the coordinates.
116     unscaleCoordinates(x);
117 
118     if (refineXYZ) {
119       int index = 0;
120       double[] xyz = new double[3];
121       for (Atom a : refinementModel.getTotalAtomArray()) {
122         if (a.isActive()) {
123           int i = index * 3;
124           xyz[0] = x[i];
125           xyz[1] = x[i + 1];
126           xyz[2] = x[i + 2];
127           a.setXYZ(xyz);
128           a.setXYZGradient(0.0, 0.0, 0.0);
129           a.setLambdaXYZGradient(0.0, 0.0, 0.0);
130           index++;
131         }
132       }
133     }
134 
135     // Target function for real space refinement.
136     if (refineXYZ) {
137       e = realSpaceData.computeRealSpaceTarget();
138     }
139 
140     // Scale the coordinates.
141     scaleCoordinates(x);
142 
143     totalEnergy = e;
144     return e;
145   }
146 
147   /** {@inheritDoc} */
148   @Override
149   public double energyAndGradient(double[] x, double[] g) {
150     double e = 0.0;
151     // Unscale the coordinates.
152     unscaleCoordinates(x);
153 
154     if (refineXYZ) {
155       int index = 0;
156       double[] xyz = new double[3];
157       for (Atom a : refinementModel.getTotalAtomArray()) {
158         if (a.isActive()) {
159           int i = index * 3;
160           xyz[0] = x[i];
161           xyz[1] = x[i + 1];
162           xyz[2] = x[i + 2];
163           a.setXYZ(xyz);
164           a.setXYZGradient(0.0, 0.0, 0.0);
165           a.setLambdaXYZGradient(0.0, 0.0, 0.0);
166           index++;
167         }
168       }
169     }
170 
171     // Target function for real space refinement
172     if (refineXYZ) {
173       e = realSpaceData.computeRealSpaceTarget();
174       // Pack gradients into gradient array
175       getXYZGradients(g);
176     }
177 
178     // Scale the coordinates and gradients.
179     scaleCoordinatesAndGradient(x, g);
180 
181     totalEnergy = e;
182     return e;
183   }
184 
185   /** {@inheritDoc} */
186   @Override
187   public double[] getAcceleration(double[] acceleration) {
188     int n = getNumberOfVariables();
189     if (acceleration == null || acceleration.length < n) {
190       acceleration = new double[n];
191     }
192     int index = 0;
193     double[] acc = new double[3];
194     for (Atom a : refinementModel.getTotalAtomArray()) {
195       if (a.isActive()) {
196         a.getAcceleration(acc);
197         acceleration[index++] = acc[0];
198         acceleration[index++] = acc[1];
199         acceleration[index++] = acc[2];
200       }
201     }
202     return acceleration;
203   }
204 
205   /** {@inheritDoc} */
206   @Override
207   public double[] getCoordinates(double[] x) {
208     int n = getNumberOfVariables();
209     if (x == null || x.length < n) {
210       x = new double[n];
211     }
212     int index = 0;
213     for (Atom a : refinementModel.getTotalAtomArray()) {
214       if (a.isActive()) {
215         x[index++] = a.getX();
216         x[index++] = a.getY();
217         x[index++] = a.getZ();
218       }
219     }
220     return x;
221   }
222 
223   /** {@inheritDoc} */
224   @Override
225   public ffx.crystal.Crystal getCrystal() {
226     realSpaceData.getCrystal();
227     return null;
228   }
229 
230   /** {@inheritDoc} */
231   @Override
232   public void setCrystal(Crystal crystal) {
233     logger.severe(" RealSpaceEnergy does implement setCrystal yet.");
234   }
235 
236   /** {@inheritDoc} */
237   @Override
238   public STATE getEnergyTermState() {
239     return state;
240   }
241 
242   /** {@inheritDoc} */
243   @Override
244   public void setEnergyTermState(STATE state) {
245     this.state = state;
246   }
247 
248   /** {@inheritDoc} */
249   @Override
250   public double getLambda() {
251     return lambda;
252   }
253 
254   /** {@inheritDoc} */
255   @Override
256   public void setLambda(double lambda) {
257     if (lambda <= 1.0 && lambda >= 0.0) {
258       this.lambda = lambda;
259       realSpaceData.setLambda(lambda);
260     } else {
261       String message = format(" Lambda value %8.3f is not in the range [0..1].", lambda);
262       logger.warning(message);
263     }
264   }
265 
266   /** {@inheritDoc} */
267   @Override
268   public double[] getMass() {
269     double[] mass = new double[nXYZ];
270     int i = 0;
271     if (refineXYZ) {
272       for (Atom a : refinementModel.getTotalAtomArray()) {
273         if (a.isActive()) {
274           double m = a.getMass();
275           mass[i++] = m;
276           mass[i++] = m;
277           mass[i++] = m;
278         }
279       }
280     }
281     return mass;
282   }
283 
284   /** {@inheritDoc} */
285   @Override
286   public int getNumberOfVariables() {
287     return nXYZ;
288   }
289 
290   /** {@inheritDoc} */
291   @Override
292   public double[] getPreviousAcceleration(double[] previousAcceleration) {
293     int n = getNumberOfVariables();
294     if (previousAcceleration == null || previousAcceleration.length < n) {
295       previousAcceleration = new double[n];
296     }
297     int index = 0;
298     double[] prev = new double[3];
299     for (Atom a : refinementModel.getTotalAtomArray()) {
300       if (a.isActive()) {
301         a.getPreviousAcceleration(prev);
302         previousAcceleration[index++] = prev[0];
303         previousAcceleration[index++] = prev[1];
304         previousAcceleration[index++] = prev[2];
305       }
306     }
307     return previousAcceleration;
308   }
309 
310   /**
311    * Getter for the field <code>refinementMode</code>.
312    *
313    * @return a {@link ffx.xray.RefinementMinimize.RefinementMode} object.
314    */
315   public RefinementMode getRefinementMode() {
316     return refinementMode;
317   }
318 
319   /**
320    * Setter for the field <code>refinementMode</code>.
321    *
322    * @param refinementMode a {@link ffx.xray.RefinementMinimize.RefinementMode} object.
323    */
324   public void setRefinementMode(RefinementMode refinementMode) {
325     this.refinementMode = refinementMode;
326     setRefinementBooleans();
327   }
328 
329   /** {@inheritDoc} */
330   @Override
331   public double[] getScaling() {
332     return optimizationScaling;
333   }
334 
335   /** {@inheritDoc} */
336   @Override
337   public void setScaling(double[] scaling) {
338     optimizationScaling = scaling;
339   }
340 
341   /** {@inheritDoc} */
342   @Override
343   public double getTotalEnergy() {
344     return totalEnergy;
345   }
346 
347   /**
348    * {@inheritDoc}
349    *
350    * <p>Return a reference to each variables type.
351    */
352   @Override
353   public VARIABLE_TYPE[] getVariableTypes() {
354 
355     int nActive = 0;
356     for (Atom a : refinementModel.getTotalAtomArray()) {
357       if (a.isActive()) {
358         nActive++;
359       }
360     }
361 
362     VARIABLE_TYPE[] type = new VARIABLE_TYPE[nActive * 3];
363 
364     int index = 0;
365     for (int i = 0; i < nActive; i++) {
366       type[index++] = VARIABLE_TYPE.X;
367       type[index++] = VARIABLE_TYPE.Y;
368       type[index++] = VARIABLE_TYPE.Z;
369     }
370     return type;
371   }
372 
373   /** {@inheritDoc} */
374   @Override
375   public double[] getVelocity(double[] velocity) {
376     int n = getNumberOfVariables();
377     if (velocity == null || velocity.length < n) {
378       velocity = new double[n];
379     }
380     int index = 0;
381     double[] v = new double[3];
382     for (Atom a : refinementModel.getTotalAtomArray()) {
383       if (a.isActive()) {
384         a.getVelocity(v);
385         velocity[index++] = v[0];
386         velocity[index++] = v[1];
387         velocity[index++] = v[2];
388       }
389     }
390     return velocity;
391   }
392 
393   /** {@inheritDoc} */
394   @Override
395   public double getd2EdL2() {
396     return 0.0;
397   }
398 
399   /** {@inheritDoc} */
400   @Override
401   public double getdEdL() {
402     return realSpaceData.getdEdL();
403   }
404 
405   /** {@inheritDoc} */
406   @Override
407   public void getdEdXdL(double[] gradient) {
408     realSpaceData.getdEdXdL(gradient);
409   }
410 
411   /** {@inheritDoc} */
412   @Override
413   public void setAcceleration(double[] acceleration) {
414     if (acceleration == null) {
415       return;
416     }
417     int index = 0;
418     double[] accel = new double[3];
419     for (Atom a : refinementModel.getTotalAtomArray()) {
420       if (a.isActive()) {
421         accel[0] = acceleration[index++];
422         accel[1] = acceleration[index++];
423         accel[2] = acceleration[index++];
424         a.setAcceleration(accel);
425       }
426     }
427   }
428 
429   /**
430    * Set atomic coordinates positions.
431    *
432    * @param x an array of coordinates for active atoms.
433    */
434   public void setCoordinates(double[] x) {
435     assert (x != null);
436     double[] xyz = new double[3];
437     int index = 0;
438     for (Atom a : refinementModel.getTotalAtomArray()) {
439       if (a.isActive()) {
440         xyz[0] = x[index++];
441         xyz[1] = x[index++];
442         xyz[2] = x[index++];
443         a.moveTo(xyz);
444       }
445     }
446   }
447 
448   /** {@inheritDoc} */
449   @Override
450   public void setPreviousAcceleration(double[] previousAcceleration) {
451     if (previousAcceleration == null) {
452       return;
453     }
454     int index = 0;
455     double[] prev = new double[3];
456     for (Atom a : refinementModel.getTotalAtomArray()) {
457       if (a.isActive()) {
458         prev[0] = previousAcceleration[index++];
459         prev[1] = previousAcceleration[index++];
460         prev[2] = previousAcceleration[index++];
461         a.setPreviousAcceleration(prev);
462       }
463     }
464   }
465 
466   /** {@inheritDoc} */
467   @Override
468   public void setVelocity(double[] velocity) {
469     if (velocity == null) {
470       return;
471     }
472     int index = 0;
473     double[] vel = new double[3];
474     for (Atom a : refinementModel.getTotalAtomArray()) {
475       if (a.isActive()) {
476         vel[0] = velocity[index++];
477         vel[1] = velocity[index++];
478         vel[2] = velocity[index++];
479         a.setVelocity(vel);
480       }
481     }
482   }
483 
484   /**
485    * If the refinement mode has changed, this should be called to update which parameters are being
486    * fit.
487    */
488   private void setRefinementBooleans() {
489     refineXYZ =
490         (refinementMode == RefinementMode.COORDINATES
491             || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS
492             || refinementMode == RefinementMode.COORDINATES_AND_OCCUPANCIES
493             || refinementMode == RefinementMode.COORDINATES_AND_BFACTORS_AND_OCCUPANCIES);
494   }
495 
496   /**
497    * Get the number of xyz parameters being fit.
498    *
499    * @return the number of xyz parameters
500    */
501   private int getNXYZ() {
502     return nXYZ;
503   }
504 
505   /**
506    * Set the number of xyz parameters.
507    *
508    * @param nxyz requested number of xyz parameters
509    */
510   private void setNXYZ(int nxyz) {
511     this.nXYZ = nxyz;
512   }
513 
514   /**
515    * Fill gradient array with xyz gradient.
516    *
517    * @param g array to add gradient to
518    */
519   private void getXYZGradients(double[] g) {
520     assert (g != null);
521     double[] grad = new double[3];
522     int index = 0;
523     for (Atom a : refinementModel.getTotalAtomArray()) {
524       if (a.isActive()) {
525         a.getXYZGradient(grad);
526         g[index++] = grad[0];
527         g[index++] = grad[1];
528         g[index++] = grad[2];
529       }
530     }
531   }
532 }