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-2025.
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 ffx.crystal.Crystal;
41  import ffx.crystal.CrystalPotential;
42  import ffx.potential.bonded.Atom;
43  import ffx.potential.bonded.LambdaInterface;
44  import ffx.xray.refine.RefinementModel;
45  
46  import java.util.logging.Logger;
47  
48  import static java.lang.String.format;
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    /**
62     * The Real Space data to refine against.
63     */
64    private final RealSpaceData realSpaceData;
65    /**
66     * The Refinement Model that contains info on mapping between alternate conformers.
67     */
68    private final RefinementModel refinementModel;
69    /**
70     * Value of the lambda state variable.
71     */
72    protected double lambda = 1.0;
73    /**
74     * The number of parameters that are being refined.
75     */
76    private final int nXYZ;
77    /**
78     * Optimization scaling used to improve convergence.
79     */
80    private double[] optimizationScaling = null;
81    /**
82     * Total energy of the refinement.
83     */
84    private double totalEnergy;
85    /**
86     * The RealSpaceEnergy is updated with FAST varying energy terms.
87     */
88    private STATE state = STATE.BOTH;
89  
90    /**
91     * Diffraction data energy target
92     *
93     * @param realSpaceData {@link ffx.realspace.RealSpaceData} object to associate with the target
94     */
95    public RealSpaceEnergy(RealSpaceData realSpaceData) {
96      this.realSpaceData = realSpaceData;
97      refinementModel = realSpaceData.getRefinementModel();
98      nXYZ = refinementModel.getNumCoordParameters();
99    }
100 
101   /**
102    * {@inheritDoc}
103    */
104   @Override
105   public boolean destroy() {
106     return realSpaceData.destroy();
107   }
108 
109   /**
110    * The parameters passed in are only for "active" atoms.
111    *
112    * <p>{@inheritDoc}
113    */
114   @Override
115   public double energy(double[] x) {
116     // Unscale the coordinates.
117     unscaleCoordinates(x);
118 
119     // Set the parameters.
120     refinementModel.setParameters(x);
121 
122     // Target function for real space refinement.
123     double e = realSpaceData.computeRealSpaceTarget();
124 
125     // Scale the coordinates.
126     scaleCoordinates(x);
127 
128     totalEnergy = e;
129     return e;
130   }
131 
132   /**
133    * {@inheritDoc}
134    */
135   @Override
136   public double energyAndGradient(double[] x, double[] g) {
137     // Unscale the coordinates.
138     unscaleCoordinates(x);
139 
140     // Set model parameters.
141     refinementModel.setParameters(x);
142 
143     // Zero out the gradient.
144     refinementModel.zeroGradient();
145 
146     // Target function for real space refinement
147     double e = realSpaceData.computeRealSpaceTarget();
148 
149     // Pack the gradient into gradient array
150     refinementModel.getGradient(g);
151 
152     // Scale the coordinates and gradients.
153     scaleCoordinatesAndGradient(x, g);
154 
155     totalEnergy = e;
156     return e;
157   }
158 
159   /**
160    * {@inheritDoc}
161    */
162   @Override
163   public double[] getAcceleration(double[] acceleration) {
164     int n = getNumberOfVariables();
165     if (acceleration == null || acceleration.length < n) {
166       acceleration = new double[n];
167     }
168     int index = 0;
169     double[] acc = new double[3];
170     for (Atom a : refinementModel.getActiveAtoms()) {
171       a.getAcceleration(acc);
172       acceleration[index++] = acc[0];
173       acceleration[index++] = acc[1];
174       acceleration[index++] = acc[2];
175     }
176     return acceleration;
177   }
178 
179   /**
180    * {@inheritDoc}
181    */
182   @Override
183   public double[] getCoordinates(double[] x) {
184     int n = getNumberOfVariables();
185     if (x == null || x.length < n) {
186       x = new double[n];
187     }
188     int index = 0;
189     for (Atom a : refinementModel.getActiveAtoms()) {
190       x[index++] = a.getX();
191       x[index++] = a.getY();
192       x[index++] = a.getZ();
193     }
194     return x;
195   }
196 
197   /**
198    * {@inheritDoc}
199    */
200   @Override
201   public ffx.crystal.Crystal getCrystal() {
202     realSpaceData.getCrystal();
203     return null;
204   }
205 
206   /**
207    * {@inheritDoc}
208    */
209   @Override
210   public void setCrystal(Crystal crystal) {
211     logger.severe(" RealSpaceEnergy does implement setCrystal yet.");
212   }
213 
214   /**
215    * {@inheritDoc}
216    */
217   @Override
218   public STATE getEnergyTermState() {
219     return state;
220   }
221 
222   /**
223    * {@inheritDoc}
224    */
225   @Override
226   public void setEnergyTermState(STATE state) {
227     this.state = state;
228   }
229 
230   /**
231    * {@inheritDoc}
232    */
233   @Override
234   public double getLambda() {
235     return lambda;
236   }
237 
238   /**
239    * {@inheritDoc}
240    */
241   @Override
242   public void setLambda(double lambda) {
243     if (lambda <= 1.0 && lambda >= 0.0) {
244       this.lambda = lambda;
245       realSpaceData.setLambda(lambda);
246     } else {
247       String message = format(" Lambda value %8.3f is not in the range [0..1].", lambda);
248       logger.warning(message);
249     }
250   }
251 
252   /**
253    * {@inheritDoc}
254    */
255   @Override
256   public double[] getMass() {
257     double[] mass = new double[nXYZ];
258     refinementModel.getMass(mass);
259     return mass;
260   }
261 
262   /**
263    * {@inheritDoc}
264    */
265   @Override
266   public int getNumberOfVariables() {
267     return nXYZ;
268   }
269 
270   /**
271    * {@inheritDoc}
272    */
273   @Override
274   public double[] getPreviousAcceleration(double[] previousAcceleration) {
275     int n = getNumberOfVariables();
276     if (previousAcceleration == null || previousAcceleration.length < n) {
277       previousAcceleration = new double[n];
278     }
279     int index = 0;
280     double[] prev = new double[3];
281     for (Atom a : refinementModel.getActiveAtoms()) {
282       a.getPreviousAcceleration(prev);
283       previousAcceleration[index++] = prev[0];
284       previousAcceleration[index++] = prev[1];
285       previousAcceleration[index++] = prev[2];
286     }
287     return previousAcceleration;
288   }
289 
290   /**
291    * {@inheritDoc}
292    */
293   @Override
294   public double[] getScaling() {
295     return optimizationScaling;
296   }
297 
298   /**
299    * {@inheritDoc}
300    */
301   @Override
302   public void setScaling(double[] scaling) {
303     optimizationScaling = scaling;
304   }
305 
306   /**
307    * {@inheritDoc}
308    */
309   @Override
310   public double getTotalEnergy() {
311     return totalEnergy;
312   }
313 
314   /**
315    * {@inheritDoc}
316    *
317    * <p>Return a reference to each variables type.
318    */
319   @Override
320   public VARIABLE_TYPE[] getVariableTypes() {
321     int nActive = refinementModel.getActiveAtoms().length;
322     VARIABLE_TYPE[] type = new VARIABLE_TYPE[nActive * 3];
323     int index = 0;
324     for (int i = 0; i < nActive; i++) {
325       type[index++] = VARIABLE_TYPE.X;
326       type[index++] = VARIABLE_TYPE.Y;
327       type[index++] = VARIABLE_TYPE.Z;
328     }
329     return type;
330   }
331 
332   /**
333    * {@inheritDoc}
334    */
335   @Override
336   public double[] getVelocity(double[] velocity) {
337     int n = getNumberOfVariables();
338     if (velocity == null || velocity.length < n) {
339       velocity = new double[n];
340     }
341     int index = 0;
342     double[] v = new double[3];
343     for (Atom a : refinementModel.getActiveAtoms()) {
344       a.getVelocity(v);
345       velocity[index++] = v[0];
346       velocity[index++] = v[1];
347       velocity[index++] = v[2];
348     }
349     return velocity;
350   }
351 
352   /**
353    * {@inheritDoc}
354    */
355   @Override
356   public double getd2EdL2() {
357     return 0.0;
358   }
359 
360   /**
361    * {@inheritDoc}
362    */
363   @Override
364   public double getdEdL() {
365     return realSpaceData.getdEdL();
366   }
367 
368   /**
369    * {@inheritDoc}
370    */
371   @Override
372   public void getdEdXdL(double[] gradient) {
373     realSpaceData.getdEdXdL(gradient);
374   }
375 
376   /**
377    * {@inheritDoc}
378    */
379   @Override
380   public void setAcceleration(double[] acceleration) {
381     if (acceleration == null) {
382       return;
383     }
384     int index = 0;
385     double[] accel = new double[3];
386     for (Atom a : refinementModel.getActiveAtoms()) {
387       accel[0] = acceleration[index++];
388       accel[1] = acceleration[index++];
389       accel[2] = acceleration[index++];
390       a.setAcceleration(accel);
391     }
392   }
393 
394   /**
395    * Set atomic coordinates positions.
396    *
397    * @param x an array of coordinates for active atoms.
398    */
399   public void setCoordinates(double[] x) {
400     if (x == null) {
401       return;
402     }
403     refinementModel.setParameters(x);
404   }
405 
406   /**
407    * {@inheritDoc}
408    */
409   @Override
410   public void setPreviousAcceleration(double[] previousAcceleration) {
411     if (previousAcceleration == null) {
412       return;
413     }
414     int index = 0;
415     double[] prev = new double[3];
416     for (Atom a : refinementModel.getActiveAtoms()) {
417       prev[0] = previousAcceleration[index++];
418       prev[1] = previousAcceleration[index++];
419       prev[2] = previousAcceleration[index++];
420       a.setPreviousAcceleration(prev);
421     }
422   }
423 
424   /**
425    * {@inheritDoc}
426    */
427   @Override
428   public void setVelocity(double[] velocity) {
429     if (velocity == null) {
430       return;
431     }
432     int index = 0;
433     double[] vel = new double[3];
434     for (Atom a : refinementModel.getActiveAtoms()) {
435       vel[0] = velocity[index++];
436       vel[1] = velocity[index++];
437       vel[2] = velocity[index++];
438       a.setVelocity(vel);
439     }
440   }
441 }