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.algorithms.thermodynamics;
39  
40  import edu.rit.mp.DoubleBuf;
41  import edu.rit.pj.Comm;
42  import ffx.algorithms.AlgorithmListener;
43  import ffx.algorithms.cli.DynamicsOptions;
44  import ffx.algorithms.cli.LambdaParticleOptions;
45  import ffx.algorithms.dynamics.Barostat;
46  import ffx.algorithms.dynamics.integrators.Stochastic;
47  import ffx.algorithms.optimize.Minimize;
48  import ffx.crystal.Crystal;
49  import ffx.crystal.CrystalPotential;
50  import ffx.numerics.Potential;
51  import ffx.numerics.integrate.DataSet;
52  import ffx.numerics.integrate.DoublesDataSet;
53  import ffx.numerics.integrate.Integrate1DNumeric;
54  import ffx.numerics.integrate.Integrate1DNumeric.IntegrationType;
55  import ffx.potential.MolecularAssembly;
56  import ffx.potential.SystemState;
57  import ffx.potential.Utilities;
58  import ffx.potential.bonded.LambdaInterface;
59  import ffx.potential.parsers.PDBFilter;
60  import ffx.potential.parsers.SystemFilter;
61  import ffx.potential.parsers.XYZFilter;
62  import org.apache.commons.configuration2.CompositeConfiguration;
63  import org.apache.commons.io.FilenameUtils;
64  
65  import javax.annotation.Nullable;
66  import java.io.File;
67  import java.io.PrintWriter;
68  import java.util.ArrayList;
69  import java.util.Arrays;
70  import java.util.List;
71  import java.util.Optional;
72  import java.util.logging.Level;
73  import java.util.logging.Logger;
74  
75  import static ffx.numerics.integrate.Integrate1DNumeric.IntegrationType.SIMPSONS;
76  import static ffx.utilities.Constants.R;
77  import static java.lang.String.format;
78  import static java.lang.System.arraycopy;
79  import static java.util.Arrays.fill;
80  import static org.apache.commons.math3.util.FastMath.PI;
81  import static org.apache.commons.math3.util.FastMath.abs;
82  import static org.apache.commons.math3.util.FastMath.asin;
83  import static org.apache.commons.math3.util.FastMath.exp;
84  import static org.apache.commons.math3.util.FastMath.floor;
85  import static org.apache.commons.math3.util.FastMath.sin;
86  import static org.apache.commons.math3.util.FastMath.sqrt;
87  
88  /**
89   * An implementation of the Orthogonal Space Tempering algorithm.
90   *
91   * <p>This only partially implements the LambdaInterface, since it does not return 2nd lambda
92   * derivatives. The 2nd derivatives of the bias require 3rd derivatives of the underlying Hamiltonian
93   * (not these derivatives are not needed for OST MD).
94   *
95   * @author Michael J. Schnieders, James Dama, Wei Yang and Pengyu Ren
96   * @since 1.0
97   */
98  public class OrthogonalSpaceTempering implements CrystalPotential, LambdaInterface {
99  
100     private static final Logger logger = Logger.getLogger(OrthogonalSpaceTempering.class.getName());
101     /**
102      * The potential energy of the system.
103      */
104     protected final CrystalPotential potential;
105     /**
106      * The AlgorithmListener is called each time a count is added.
107      */
108     protected final AlgorithmListener algorithmListener;
109     /**
110      * Print detailed energy information.
111      */
112     protected final boolean print = false;
113     /**
114      * Number of variables.
115      */
116     protected final int nVariables;
117     /**
118      * A potential energy that implements the LambdaInterface.
119      */
120     private final LambdaInterface lambdaInterface;
121     /**
122      * List of additional Histograms this OST can switch to.
123      */
124     private final List<Histogram> allHistograms = new ArrayList<>();
125     /**
126      * Parameters to control saving local optimizations.
127      */
128     private final OptimizationParameters optimizationParameters;
129     /**
130      * Properties.
131      */
132     private final CompositeConfiguration properties;
133     /**
134      * The MolecularAssembly being simulated.
135      */
136     protected MolecularAssembly molecularAssembly;
137     /**
138      * Are FAST varying energy terms being computed, SLOW varying energy terms, or BOTH. OST is not
139      * active when only FAST varying energy terms are being propagated.
140      */
141     protected Potential.STATE state = Potential.STATE.BOTH;
142     /**
143      * Force Field Potential Energy (i.e. with no bias terms added).
144      */
145     protected double forceFieldEnergy;
146     /**
147      * Contains counts for the OST bias.
148      */
149     private Histogram histogram;
150     /**
151      * Index of the current Histogram.
152      */
153     private int histogramIndex;
154     /**
155      * Flag to indicate that the Lambda particle should be propagated.
156      */
157     private boolean propagateLambda = true;
158     /**
159      * Mixed second partial derivative with respect to coordinates and lambda.
160      */
161     private final double[] dUdXdL;
162 
163     /**
164      * Gradient array needed when the OST Energy method is called.
165      */
166     private final double[] tempGradient;
167 
168     /**
169      * Partial derivative of the force field energy with respect to lambda.
170      */
171     private double dForceFieldEnergydL;
172     /**
173      * Magnitude of the 2D orthogonal space bias G(L,dE/dL).
174      */
175     private double gLdEdL = 0.0;
176     /**
177      * OST Bias energy.
178      */
179     private double biasEnergy;
180     /**
181      * Total system energy.
182      */
183     private double totalEnergy;
184     /**
185      * Total partial derivative of the potential (U) being sampled with respect to lambda.
186      */
187     private double dUdLambda;
188     /**
189      * Second partial derivative of the potential being sampled with respect to lambda.
190      */
191     private double d2UdL2;
192     /**
193      * If true, values of (lambda, dU/dL) that have not been observed are rejected.
194      */
195     private boolean hardWallConstraint = false;
196 
197     private final DynamicsOptions dynamicsOptions;
198 
199     private final LambdaParticleOptions lambdaParticleOptions;
200 
201     /**
202      * OST Constructor.
203      *
204      * @param lambdaInterface       defines Lambda and dU/dL.
205      * @param potential             defines the Potential energy.
206      * @param histogramData         contains histogram restart data.
207      * @param lambdaData            contains lambda restart data.
208      * @param properties            defines System properties.
209      * @param dynamicsOptions       defines molecular dynamics parameters.
210      * @param lambdaParticleOptions defines lambda particle parameters.
211      * @param algorithmListener     the AlgorithmListener to be notified of progress.
212      */
213     public OrthogonalSpaceTempering(LambdaInterface lambdaInterface, CrystalPotential potential,
214                                     HistogramData histogramData, LambdaData lambdaData, CompositeConfiguration properties,
215                                     DynamicsOptions dynamicsOptions, LambdaParticleOptions lambdaParticleOptions,
216                                     AlgorithmListener algorithmListener) {
217         this.lambdaInterface = lambdaInterface;
218         this.potential = potential;
219         this.properties = properties;
220         this.dynamicsOptions = dynamicsOptions;
221         this.lambdaParticleOptions = lambdaParticleOptions;
222         this.algorithmListener = algorithmListener;
223         nVariables = potential.getNumberOfVariables();
224 
225         if (potential instanceof Barostat) {
226             logger.severe(" The Barostat must wrap the OST instance.");
227         }
228 
229         dUdXdL = new double[nVariables];
230         tempGradient = new double[nVariables];
231 
232         // Init the Histogram.
233         histogram = new Histogram(properties, histogramData, lambdaData);
234         histogramIndex = 0;
235         allHistograms.add(histogram);
236 
237         // Configure optimization parameters.
238         optimizationParameters = new OptimizationParameters(properties);
239     }
240 
241     /**
242      * Add an alternate Histogram this OST can use.
243      *
244      * @param histogramData Settings to use for the new Histogram.
245      */
246     public void addHistogram(HistogramData histogramData, LambdaData lambdaData) {
247         Histogram newHisto = new Histogram(properties, histogramData, lambdaData);
248         histogramData.asynchronous = this.histogram.hd.asynchronous;
249         allHistograms.add(newHisto);
250     }
251 
252     /**
253      * {@inheritDoc}
254      */
255     @Override
256     public boolean dEdLZeroAtEnds() {
257         return false;
258     }
259 
260     /**
261      * {@inheritDoc}
262      */
263     @Override
264     public boolean destroy() {
265         // Shut down the CountReceiveThread.
266         histogram.destroy();
267         return potential.destroy();
268     }
269 
270     /**
271      * Compute the force field + bias energy.
272      */
273     public double energy(double[] x) {
274 
275         // OST is propagated with the slowly varying terms.
276         if (state == Potential.STATE.FAST) {
277             forceFieldEnergy = potential.energy(x);
278             return forceFieldEnergy;
279         }
280 
281         // Stochastic dynamics pre-force propagation.
282         if (propagateLambda) {
283             histogram.stochasticPreForce();
284         }
285 
286         // We have to compute the energy and gradient, since we need dU/dL to be computed.
287         fill(tempGradient, 0.0);
288         forceFieldEnergy = potential.energyAndGradient(x, tempGradient);
289 
290         dForceFieldEnergydL = lambdaInterface.getdEdL();
291         d2UdL2 = lambdaInterface.getd2EdL2();
292         int lambdaBin = histogram.indexForLambda();
293         dUdLambda = dForceFieldEnergydL;
294 
295         gLdEdL = 0.0;
296         double bias1D;
297         // Update the free energy difference.
298         histogram.updateFreeEnergyDifference(false, false);
299         if (histogram.hd.metaDynamics) {
300             bias1D = histogram.energyAndGradientMeta(true);
301         } else {
302             // Calculate recursion kernel G(L, F_L) and its derivatives with respect to L and F_L.
303             if (histogram.hd.biasMag > 0.0) {
304                 double[] chainRule = new double[2];
305                 gLdEdL = histogram.energyAndGradient2D(dUdLambda, chainRule);
306                 double dGdLambda = chainRule[0];
307                 double dGdFLambda = chainRule[1];
308                 dUdLambda += dGdLambda + dGdFLambda * d2UdL2;
309             }
310 
311             // Compute the energy and gradient for the recursion worker at F(L) using interpolation.
312             bias1D = histogram.energyAndGradient1D(true);
313         }
314 
315         // The total bias energy is the sum of the 1D and 2D terms.
316         biasEnergy = bias1D + gLdEdL;
317 
318         if (print) {
319             logger.info(format(" Bias Energy        %16.8f", biasEnergy));
320             logger.info(format(" %s %16.8f  (Kcal/mole)", "OST Potential    ", forceFieldEnergy + biasEnergy));
321         }
322 
323         if (propagateLambda) {
324             histogram.stochasticPostForce();
325 
326             histogram.ld.stepsTaken++;
327             long energyCount = histogram.ld.stepsTaken;
328 
329             // Log the current Lambda state.
330             int printFrequency = dynamicsOptions.getReportFrequency(100);
331             if (energyCount % printFrequency == 0) {
332                 double dBdL = dUdLambda - dForceFieldEnergydL;
333                 double lambda = histogram.ld.lambda;
334                 int lambdaBins = histogram.hd.getLambdaBins();
335                 if (lambdaBins < 1000) {
336                     logger.info(format(" L=%6.4f (%3d) F_LU=%10.4f F_LB=%10.4f F_L=%10.4f V_L=%10.4f", lambda,
337                             lambdaBin, dForceFieldEnergydL, dBdL, dUdLambda, histogram.ld.thetaVelocity));
338                 } else {
339                     logger.info(format(" L=%6.4f (%4d) F_LU=%10.4f F_LB=%10.4f F_L=%10.4f V_L=%10.4f", lambda,
340                             lambdaBin, dForceFieldEnergydL, dBdL, dUdLambda, histogram.ld.thetaVelocity));
341                 }
342             }
343 
344             // Metadynamics grid counts (every 'countInterval' steps).
345             if (energyCount % histogram.hd.countInterval == 0) {
346                 histogram.addBias(dForceFieldEnergydL);
347 
348                 // Locally optimize the current state.
349                 if (optimizationParameters.doOptimization) {
350                     optimizationParameters.optimize(forceFieldEnergy, x, tempGradient);
351                 }
352 
353                 if (algorithmListener != null) {
354                     algorithmListener.algorithmUpdate(molecularAssembly);
355                 }
356             }
357         }
358 
359         totalEnergy = forceFieldEnergy + biasEnergy;
360 
361         return totalEnergy;
362     }
363 
364     /**
365      * {@inheritDoc}
366      */
367     @Override
368     public double energyAndGradient(double[] x, double[] gradient) {
369 
370         // Stochastic dynamics pre-force propagation.
371         if (state != STATE.FAST && propagateLambda) {
372             histogram.stochasticPreForce();
373         }
374 
375         // Compute the force field energy and gradient.
376         forceFieldEnergy = potential.energyAndGradient(x, gradient);
377 
378         // OST is propagated with the slowly varying terms.
379         if (state == STATE.FAST) {
380             return forceFieldEnergy;
381         }
382 
383         // dU/dL is the partial derivative of the force field energy with respect to lambda.
384         dForceFieldEnergydL = lambdaInterface.getdEdL();
385         dUdLambda = dForceFieldEnergydL;
386         d2UdL2 = lambdaInterface.getd2EdL2();
387 
388         gLdEdL = 0.0;
389         double bias1D;
390         // Update the free energy difference.
391         histogram.updateFreeEnergyDifference(false, false);
392         if (histogram.hd.metaDynamics) {
393             bias1D = histogram.energyAndGradientMeta(true);
394         } else {
395             if (histogram.hd.biasMag > 0) {
396                 double[] chainRule = new double[2];
397                 gLdEdL = histogram.energyAndGradient2D(dUdLambda, chainRule);
398                 double dGdLambda = chainRule[0];
399                 double dGdFLambda = chainRule[1];
400 
401                 // Lambda gradient due to recursion kernel G(L, F_L).
402                 dUdLambda += dGdLambda + dGdFLambda * d2UdL2;
403 
404                 // Cartesian coordinate gradient due to recursion kernel G(L, F_L).
405                 fill(dUdXdL, 0.0);
406                 lambdaInterface.getdEdXdL(dUdXdL);
407                 for (int i = 0; i < nVariables; i++) {
408                     gradient[i] += dGdFLambda * dUdXdL[i];
409                 }
410             }
411 
412             // Compute the energy and gradient for the recursion worker at F(L) using interpolation.
413             bias1D = histogram.energyAndGradient1D(true);
414         }
415 
416         // The total bias is the sum of 1D and 2D terms.
417         biasEnergy = bias1D + gLdEdL;
418 
419         if (print) {
420             logger.info(format(" %s %16.8f", "Bias Energy       ", biasEnergy));
421             logger.info(format(" %s %16.8f  %s", "OST Potential    ", forceFieldEnergy + biasEnergy, "(Kcal/mole)"));
422         }
423 
424         if (propagateLambda) {
425             histogram.stochasticPostForce();
426 
427             histogram.ld.stepsTaken++;
428             long energyCount = histogram.ld.stepsTaken;
429 
430             // Log the current Lambda state.
431             int printFrequency = dynamicsOptions.getReportFrequency(100);
432             if (energyCount % printFrequency == 0) {
433                 double dBdL = dUdLambda - dForceFieldEnergydL;
434                 int lambdaBin = histogram.indexForLambda();
435                 double lambda = histogram.ld.lambda;
436                 int lambdaBins = histogram.hd.getLambdaBins();
437                 if (lambdaBins < 1000) {
438                     logger.info(format(" L=%6.4f (%3d) F_LU=%10.4f F_LB=%10.4f F_L=%10.4f V_L=%10.4f", lambda,
439                             lambdaBin, dForceFieldEnergydL, dBdL, dUdLambda, histogram.ld.thetaVelocity));
440                 } else {
441                     logger.info(format(" L=%6.4f (%4d) F_LU=%10.4f F_LB=%10.4f F_L=%10.4f V_L=%10.4f", lambda,
442                             lambdaBin, dForceFieldEnergydL, dBdL, dUdLambda, histogram.ld.thetaVelocity));
443                 }
444             }
445 
446             // Metadynamics grid counts (every 'countInterval' steps).
447             if (energyCount % histogram.hd.countInterval == 0) {
448                 histogram.addBias(dForceFieldEnergydL);
449 
450                 // Locally optimize the current state.
451                 if (optimizationParameters.doOptimization) {
452                     optimizationParameters.optimize(forceFieldEnergy, x, gradient);
453                 }
454 
455                 if (algorithmListener != null) {
456                     algorithmListener.algorithmUpdate(molecularAssembly);
457                 }
458             }
459         }
460 
461         totalEnergy = forceFieldEnergy + biasEnergy;
462 
463         return totalEnergy;
464     }
465 
466     /**
467      * {@inheritDoc}
468      */
469     @Override
470     public double[] getAcceleration(double[] acceleration) {
471         return potential.getAcceleration(acceleration);
472     }
473 
474     public Histogram[] getAllHistograms() {
475         int nHisto = allHistograms.size();
476         Histogram[] ret = new Histogram[nHisto];
477         ret = allHistograms.toArray(ret);
478         return ret;
479     }
480 
481     /**
482      * {@inheritDoc}
483      */
484     @Override
485     public double[] getCoordinates(double[] doubles) {
486         return potential.getCoordinates(doubles);
487     }
488 
489     /**
490      * {@inheritDoc}
491      */
492     @Override
493     public void setCoordinates(double[] doubles) {
494         potential.setCoordinates(doubles);
495     }
496 
497     /**
498      * {@inheritDoc}
499      */
500     @Override
501     public Crystal getCrystal() {
502         return potential.getCrystal();
503     }
504 
505     /**
506      * {@inheritDoc}
507      */
508     @Override
509     public void setCrystal(Crystal crystal) {
510         potential.setCrystal(crystal);
511     }
512 
513     /**
514      * Returns the number of energy evaluations performed by this OST, including those picked up in the
515      * lambda file.
516      *
517      * @return Number of energy steps taken by this walker.
518      */
519     public long getEnergyCount() {
520         return histogram.ld.stepsTaken;
521     }
522 
523     /**
524      * {@inheritDoc}
525      */
526     @Override
527     public Potential.STATE getEnergyTermState() {
528         return state;
529     }
530 
531     /**
532      * {@inheritDoc}
533      */
534     @Override
535     public void setEnergyTermState(Potential.STATE state) {
536         this.state = state;
537         potential.setEnergyTermState(state);
538     }
539 
540     /**
541      * Getter for the field <code>forceFieldEnergy</code>.
542      *
543      * @return a double.
544      */
545     public double getForceFieldEnergy() {
546         return forceFieldEnergy;
547     }
548 
549     /**
550      * Return the current 2D Histogram of counts.
551      *
552      * @return the Histogram.
553      */
554     public Histogram getHistogram() {
555         return histogram;
556     }
557 
558     /**
559      * Getter for the field <code>lambda</code>.
560      *
561      * @return a double.
562      */
563     public double getLambda() {
564         return histogram.ld.lambda;
565     }
566 
567     /**
568      * Setter for the field <code>lambda</code>.
569      *
570      * @param lambda a double.
571      */
572     public void setLambda(double lambda) {
573         if (histogram != null) {
574             lambda = histogram.mapLambda(lambda);
575         } else {
576             logger.warning(" OrthogonalSpaceTempering.setLambda was called before histogram constructed!");
577             logger.info(Utilities.stackTraceToString(new RuntimeException()));
578         }
579         lambdaInterface.setLambda(lambda);
580         histogram.ld.lambda = lambda;
581         histogram.ld.theta = asin(sqrt(lambda));
582     }
583 
584     /**
585      * {@inheritDoc}
586      */
587     @Override
588     public double[] getMass() {
589         return potential.getMass();
590     }
591 
592     /**
593      * {@inheritDoc}
594      */
595     @Override
596     public int getNumberOfVariables() {
597         return potential.getNumberOfVariables();
598     }
599 
600     /**
601      * Return the OST optimization information.
602      *
603      * @return The OST optimization parameters.
604      */
605     public OptimizationParameters getOptimizationParameters() {
606         return optimizationParameters;
607     }
608 
609     /**
610      * getPotentialEnergy.
611      *
612      * @return a {@link ffx.numerics.Potential} object.
613      */
614     public Potential getPotentialEnergy() {
615         return potential;
616     }
617 
618     /**
619      * {@inheritDoc}
620      */
621     @Override
622     public double[] getPreviousAcceleration(double[] previousAcceleration) {
623         return potential.getPreviousAcceleration(previousAcceleration);
624     }
625 
626     /**
627      * {@inheritDoc}
628      */
629     @Override
630     public double[] getScaling() {
631         return potential.getScaling();
632     }
633 
634     /**
635      * {@inheritDoc}
636      */
637     @Override
638     public void setScaling(double[] scaling) {
639         potential.setScaling(scaling);
640     }
641 
642     /**
643      * {@inheritDoc}
644      */
645     @Override
646     public double getTotalEnergy() {
647         return totalEnergy;
648     }
649 
650     /**
651      * getTotaldEdLambda.
652      *
653      * @return a double.
654      */
655     public double getTotaldEdLambda() {
656         return dUdLambda;
657     }
658 
659     /**
660      * {@inheritDoc}
661      *
662      * <p>Return a reference to each variables type.
663      */
664     @Override
665     public Potential.VARIABLE_TYPE[] getVariableTypes() {
666         return potential.getVariableTypes();
667     }
668 
669     /**
670      * {@inheritDoc}
671      */
672     @Override
673     public double[] getVelocity(double[] velocity) {
674         return potential.getVelocity(velocity);
675     }
676 
677     /**
678      * {@inheritDoc}
679      */
680     @Override
681     public double getd2EdL2() {
682         throw new UnsupportedOperationException(
683                 " Second derivatives of the bias are not implemented, as they require third derivatives of the potential.");
684     }
685 
686     /**
687      * {@inheritDoc}
688      */
689     @Override
690     public double getdEdL() {
691         return getTotaldEdLambda();
692     }
693 
694     /**
695      * {@inheritDoc}
696      */
697     @Override
698     public void getdEdXdL(double[] gradient) {
699         throw new UnsupportedOperationException(
700                 " Second derivatives of the bias are not implemented, as they require third derivatives of the potential.");
701     }
702 
703     // TODO: Delete method when debugging of RepexOST is done.
704     public void logOutputFiles(int index) {
705         logger.info(format(" OST: Lambda file %s, histogram %s", histogram.ld.getLambdaFile(),
706                 allHistograms.get(index).hd.getHistogramFile()));
707     }
708 
709     /**
710      * {@inheritDoc}
711      */
712     @Override
713     public void setAcceleration(double[] acceleration) {
714         potential.setAcceleration(acceleration);
715     }
716 
717     /**
718      * If this flag is true, (lambda, dU/dL) Monte Carlo samples that have no weight in the Histogram
719      * are rejected.
720      *
721      * @param hardWallConstraint If true, MC samples outside the current range are rejected.
722      */
723     public void setHardWallConstraint(boolean hardWallConstraint) {
724         this.hardWallConstraint = hardWallConstraint;
725     }
726 
727     public void setMolecularAssembly(MolecularAssembly molecularAssembly) {
728         this.molecularAssembly = molecularAssembly;
729     }
730 
731     /**
732      * {@inheritDoc}
733      */
734     @Override
735     public void setPreviousAcceleration(double[] previousAcceleration) {
736         potential.setPreviousAcceleration(previousAcceleration);
737     }
738 
739     /**
740      * Indicate if the Lambda extended system particle should be propagated using Langevin dynamics.
741      *
742      * @param propagateLambda If true, Lambda will be propagated using Langevin dynamics.
743      */
744     public void setPropagateLambda(boolean propagateLambda) {
745         this.propagateLambda = propagateLambda;
746     }
747 
748     /**
749      * If true, the Lambda extended system particle is propagated using Langevin dynamics.
750      */
751     public boolean getPropagateLambda() {
752         return propagateLambda;
753     }
754 
755     /**
756      * {@inheritDoc}
757      */
758     @Override
759     public void setVelocity(double[] velocity) {
760         potential.setVelocity(velocity);
761     }
762 
763     /**
764      * Switch to an alternate Histogram.
765      *
766      * @param index Index of the Histogram to use.
767      */
768     public void switchHistogram(int index) {
769         histogramIndex = index;
770         histogram = allHistograms.get(histogramIndex);
771         logger.info(" OST switching to histogram " + histogramIndex);
772     }
773 
774     @Override
775     public void writeAdditionalRestartInfo(boolean recursive) {
776         histogram.writeRestart();
777         if (recursive) {
778             potential.writeAdditionalRestartInfo(recursive);
779         }
780     }
781 
782     double getLambdaWriteOut() {
783         return histogram.hd.resetHistogramAtLambda;
784     }
785 
786     /**
787      * getForceFielddEdL.
788      *
789      * @return a double.
790      */
791     double getForceFielddEdL() {
792         return dForceFieldEnergydL;
793     }
794 
795     /**
796      * Getter for the field <code>biasEnergy</code>.
797      *
798      * @return a double.
799      */
800     double getBiasEnergy() {
801         return biasEnergy;
802     }
803 
804     /**
805      * If the dUdLHardWall flag is set to true, this method will return false if the (lambda, dU/dL)
806      * sample is has not been seen.
807      *
808      * @param lambda The proposed lambda value.
809      * @param dUdL   The proposed dU/dL value.
810      * @return Returns false only if the dUdLHardWall flag is true, and the (lambda, dU/dL) sample has
811      * not been seen.
812      */
813     boolean insideHardWallConstraint(double lambda, double dUdL) {
814         if (hardWallConstraint) {
815             double weight = histogram.getRecursionKernelValue(lambda, dUdL);
816             return weight > 0.0;
817         }
818         return true;
819     }
820 
821     /**
822      * Parameters for running local optimizations during OST sampling.
823      */
824     public class OptimizationParameters {
825 
826         /**
827          * Flag to turn on OST optimization.
828          *
829          * <p>The default doOptimization = false.
830          */
831         private boolean doOptimization = false;
832         /**
833          * Reset unit cell parameters, molecular orientation and translation.
834          */
835         private final boolean doUnitCellReset;
836         /**
837          * OST optimization only runs if Lambda is greater than the lambdaCutoff.
838          *
839          * <p>The default lambdaCutoff = 0.8.
840          */
841         private final double lambdaCutoff;
842         /**
843          * The lowest energy found via optimizations.
844          *
845          * <p>The optimumEnergy is initially set to Double.MAX_VALUE.
846          */
847         private double optimumEnergy = Double.MAX_VALUE;
848         /**
849          * The OST optimization frequency
850          *
851          * <p>The default is once every 10,000 steps.
852          */
853         private final int frequency;
854         /**
855          * The OST optimization convergence criteria.
856          *
857          * <p>The default eps = 0.1.
858          */
859         private final double eps;
860         /**
861          * The OST tolerance when checking for equal energy after coordinate reversion.
862          *
863          * <p>The default is 1.0e-8 kcal/mol.
864          */
865         private final double tolerance;
866         /**
867          * The OST optimization energy window.
868          *
869          * <p>The default is 4.0 kcal/mol, which is convenient for small organic crystals.
870          */
871         private final double energyWindow;
872         /**
873          * Holds the lowest potential energy coordinates.
874          */
875         private double[] optimumCoords;
876         /**
877          * File instance used for saving optimized structures.
878          */
879         private File optimizationFile;
880         /**
881          * SystemFilter used to save optimized structures.
882          */
883         private SystemFilter optimizationFilter;
884 
885         /**
886          * Empty constructor.
887          */
888         OptimizationParameters(CompositeConfiguration properties) {
889             energyWindow = properties.getDouble("ost-opt-energy-window", 10.0);
890             eps = properties.getDouble("ost-opt-eps", 0.1);
891             tolerance = properties.getDouble("ost-opt-tolerance", 1.0e-8);
892             frequency = properties.getInt("ost-opt-frequency", 10000);
893             lambdaCutoff = properties.getDouble("ost-opt-lambda-cutoff", 0.8);
894             doUnitCellReset = properties.getBoolean("ost-opt-unitcell-reset", false);
895         }
896 
897         private void log() {
898             logger.info("\n Optimization Parameters");
899             logger.info(format("  Energy Window:                  %6.3f (kcal/mol)", energyWindow));
900             logger.info(format("  EPS:                            %6.4f RMS (kcal/mol/Å)", eps));
901             logger.info(format("  Tolerance:                      %6.4f (kcal/mol)", tolerance));
902             logger.info(format("  Frequency:                      %6d (steps)", frequency));
903             logger.info(format("  Lambda Cutoff:                  %6.4f", lambdaCutoff));
904             logger.info(format("  Unit Cell Reset:                %6B", doUnitCellReset));
905             logger.info(format("  File:                           %s", optimizationFile.getName()));
906         }
907 
908         /**
909          * The coordinates of the lowest energy structure found during optimization.
910          *
911          * @return the coordinates of the lowest energy structure found during optimization.
912          */
913         public double[] getOptimumCoordinates() {
914             if (optimumEnergy < Double.MAX_VALUE) {
915                 return optimumCoords;
916             } else {
917                 logger.info("Lambda optimization cutoff was not reached. Try increasing the number of timesteps.");
918                 return null;
919             }
920         }
921 
922         /**
923          * getOptimumEnergy.
924          *
925          * @return a double.
926          */
927         public double getOptimumEnergy() {
928             if (optimumEnergy == Double.MAX_VALUE) {
929                 logger.info(
930                         "Lambda optimization cutoff was not reached. Try increasing the number of timesteps.");
931             }
932             return optimumEnergy;
933         }
934 
935         /**
936          * Run a local optimization.
937          *
938          * @param e        Current energy.
939          * @param x        Current atomic coordinates.
940          * @param gradient Work array for collecting the gradient.
941          */
942         public void optimize(double e, double[] x, @Nullable double[] gradient) {
943 
944             // Return if the optimization flag is not set, or if lambda is not beyond the cutoff.
945             double lambda = histogram.ld.lambda;
946             long stepsTaken = histogram.ld.stepsTaken;
947             if (doOptimization && lambda > lambdaCutoff && stepsTaken % frequency == 0) {
948                 if (gradient == null) {
949                     gradient = new double[x.length];
950                 }
951             } else {
952                 return;
953             }
954 
955             logger.info(format("\n OST Minimization (Step %d)", stepsTaken));
956 
957             // Set the underlying Potential's Lambda value to 1.0.
958             lambdaInterface.setLambda(1.0);
959 
960             // Use all energy terms.
961             potential.setEnergyTermState(Potential.STATE.BOTH);
962 
963             // Optimize the system.
964             try {
965                 double startingEnergy = potential.energy(x);
966                 Minimize minimize = new Minimize(molecularAssembly, potential, algorithmListener);
967                 minimize.minimize(eps);
968                 // Collect the minimum energy.
969                 double minEnergy = potential.getTotalEnergy();
970                 // Check for a new minimum within an energy window of the lowest energy structure found.
971                 if (minEnergy < optimumEnergy + energyWindow) {
972                     if (minEnergy < optimumEnergy) {
973                         optimumEnergy = minEnergy;
974                     }
975                     int n = potential.getNumberOfVariables();
976                     optimumCoords = new double[n];
977                     optimumCoords = potential.getCoordinates(optimumCoords);
978                     double mass = molecularAssembly.getMass();
979                     Crystal crystal = molecularAssembly.getCrystal();
980                     double density = crystal.getDensity(mass);
981                     optimizationFilter.writeFile(optimizationFile, true);
982                     Crystal uc = crystal.getUnitCell();
983                     logger.info(format(" Minimum: %12.6f %s (%12.6f g/cc) optimized from %12.6f at step %d.",
984                             minEnergy, uc.toShortString(), density, startingEnergy, stepsTaken));
985                 }
986             } catch (Exception ex) {
987                 String message = ex.getMessage();
988                 logger.info(
989                         format(" Exception minimizing coordinates at lambda=%8.6f\n %s.", lambda, message));
990                 logger.info(" Sampling will continue.");
991             }
992 
993             // Set the underlying Potential's Lambda value back to current lambda value.
994             lambdaInterface.setLambda(lambda);
995 
996             // Reset the Potential State
997             potential.setEnergyTermState(state);
998 
999             if (doUnitCellReset) {
1000                 logger.info("\n Resetting Unit Cell");
1001                 double mass = molecularAssembly.getMass();
1002                 double density = molecularAssembly.getCrystal().getDensity(mass);
1003                 molecularAssembly.applyRandomDensity(density);
1004                 molecularAssembly.applyRandomSymOp(0.0);
1005                 lambda = 0.0;
1006                 lambdaInterface.setLambda(lambda);
1007             } else {
1008                 // Revert to the coordinates and gradient prior to optimization.
1009                 double eCheck = potential.energyAndGradient(x, gradient);
1010 
1011                 if (abs(eCheck - e) > tolerance) {
1012                     logger.warning(format(" Optimization could not revert coordinates %16.8f vs. %16.8f.", e, eCheck));
1013                 }
1014             }
1015         }
1016 
1017         /**
1018          * setOptimization.
1019          *
1020          * @param doOptimization    a boolean.
1021          * @param molecularAssembly a {@link ffx.potential.MolecularAssembly} object.
1022          */
1023         public void setOptimization(boolean doOptimization, MolecularAssembly molecularAssembly) {
1024             this.doOptimization = doOptimization;
1025             OrthogonalSpaceTempering.this.molecularAssembly = molecularAssembly;
1026             File file = molecularAssembly.getFile();
1027             String fileName = FilenameUtils.removeExtension(file.getAbsolutePath());
1028             String ext = FilenameUtils.getExtension(file.getAbsolutePath());
1029             if (optimizationFilter == null) {
1030                 if (ext.toUpperCase().contains("XYZ")) {
1031                     optimizationFile = new File(fileName + "_opt.arc");
1032                     optimizationFilter = new XYZFilter(optimizationFile, molecularAssembly,
1033                             molecularAssembly.getForceField(), molecularAssembly.getProperties());
1034                 } else {
1035                     optimizationFile = new File(fileName + "_opt.pdb");
1036                     PDBFilter pdbFilter = new PDBFilter(optimizationFile, molecularAssembly,
1037                             molecularAssembly.getForceField(), molecularAssembly.getProperties());
1038                     int models = pdbFilter.countNumModels();
1039                     pdbFilter.setModelNumbering(models);
1040                     optimizationFilter = pdbFilter;
1041                 }
1042             }
1043 
1044             if (this.doOptimization) {
1045                 log();
1046             }
1047         }
1048     }
1049 
1050     /**
1051      * Store and operate on a 2D Histogram of (Lambda, dU/dL) observations to produce an OST bias.
1052      *
1053      * @author Michael J. Schnieders
1054      * @since 1.0
1055      */
1056     public class Histogram {
1057 
1058         /**
1059          * If a 2D bias is in use (i.e. lambda-bias-cutoff > 0), do not normalize bias height.
1060          */
1061         private static final double TWO_D_NORMALIZATION = 1.0;
1062         /**
1063          * If a 1D bias is in use (i.e. lambda-bias-cutoff == 0), normalize bias height to deposit the same
1064          * volume of bias.
1065          */
1066         private static final double ONE_D_NORMALIZATION = Math.sqrt(2.0 * Math.PI);
1067         /**
1068          * If the real bias magnitude is 0 (for no 2D bias), temporarily set biasMag to this value to calculate the ensemble average dU/dL.
1069          *
1070          * <p>Any value that does not overflow/underflow double precision summations of the count matrix
1071          * will give identical results. For example, values of 1.0e-20, 1.0 and 1.0e20 were tested and
1072          * found to give identical results.
1073          *
1074          * <p>Thus, a value of 1.0 is good choice.
1075          */
1076         private static final double PSEUDO_BIAS_MAGNITUDE = 1.0;
1077         /**
1078          * Parallel Java world communicator.
1079          */
1080         protected final Comm world;
1081         /**
1082          * Rank of this process.
1083          */
1084         protected final int rank;
1085         /**
1086          * 1D PMF with respect to lambda F(L).
1087          */
1088         final double[] ensembleAveragedUdL;
1089         /**
1090          * This deltaT is used to determine the tempering weight as described below for the
1091          * temperingWeight variable.
1092          *
1093          * <p>deltaT = temperingFactor * kB * T.
1094          */
1095         private final double deltaT;
1096         /**
1097          * The integration algorithm used for thermodynamic integration.
1098          */
1099         private final IntegrationType integrationType;
1100         /**
1101          * Send OST counts asynchronously.
1102          */
1103         private final SendAsynchronous sendAsynchronous;
1104         /**
1105          * Send OST counts synchronously.
1106          */
1107         private final SendSynchronous sendSynchronous;
1108         /**
1109          * The Dama et al. transition-tempering weight. The initial temperingWeight = 1.0,
1110          * and more generally temperingWeight = exp(-biasHeight/deltaT)
1111          */
1112         private double temperingWeight = 1.0;
1113         /**
1114          * If the recursion kernel becomes too large or too small for some combinations of (Lambda,
1115          * dU/dL), then its statistical weight = exp(kernel * beta) cannot be represented by a double
1116          * value.
1117          */
1118         private double[] kernelValues;
1119         /**
1120          * Most recent lambda values for each Walker.
1121          */
1122         private double lastReceivedLambda;
1123         /**
1124          * Most recent dU/dL value for each walker.
1125          */
1126         private double lastReceiveddUdL;
1127         private final boolean spreadBias;
1128         /**
1129          * The HistogramData object contains the parameters for the Histogram that are saved to disk.
1130          */
1131         final HistogramData hd;
1132         /**
1133          * The LambdaData object contains the parameters for the Lambda particle that are saved to disk.
1134          */
1135         final LambdaData ld;
1136         /**
1137          * The Stochastic integrator used to propagate the lambda particle.
1138          */
1139         private final Stochastic stochastic;
1140         /**
1141          * The state of the lambda particle.
1142          */
1143         private final SystemState lambdaState;
1144         /**
1145          * Number of hills when the last free energy estimate was made.
1146          */
1147         private int currentNumberOfHills = 0;
1148         /**
1149          * The current free energy difference.
1150          */
1151         private double currentFreeEnergyDifference = 0.0;
1152 
1153         /**
1154          * Histogram constructor.
1155          *
1156          * @param properties    a CompositeConfiguration used to configure the Histogram.
1157          * @param histogramData An object containing the values this Histogram will be set to.
1158          */
1159         Histogram(CompositeConfiguration properties, HistogramData histogramData, LambdaData lambdaData) {
1160             hd = histogramData;
1161             ld = lambdaData;
1162 
1163       /*
1164       double gaussNormalization;
1165       if (hd.lambdaBiasCutoff == 0 && !hd.wasHistogramRead()) {
1166         gaussNormalization = ONE_D_NORMALIZATION;
1167         logger.info(format(" Bias magnitude multiplied by a factor of %.4f "
1168             + "sqrt(2*pi) to match 1D Gaussian volume to 2D Gaussian volume.", gaussNormalization));
1169       } else {
1170         gaussNormalization = TWO_D_NORMALIZATION;
1171       }
1172       biasMag = settings.getBiasMag() * gaussNormalization;
1173        */
1174 
1175             deltaT = hd.temperingFactor * R * dynamicsOptions.getTemperature();
1176 
1177             // Allocate space to compute the <dU/dL>
1178             ensembleAveragedUdL = new double[hd.getLambdaBins()];
1179 
1180             // Allocate space to regularize kernel values.
1181             kernelValues = new double[hd.getDUDLBins()];
1182 
1183             String propString = properties.getString("ost-integrationType", "SIMPSONS");
1184             IntegrationType testType;
1185             try {
1186                 testType = IntegrationType.valueOf(propString.toUpperCase());
1187             } catch (Exception ex) {
1188                 logger.warning(format(" Invalid argument %s to ost-integrationType; resetting to SIMPSONS", propString));
1189                 testType = SIMPSONS;
1190             }
1191             integrationType = testType;
1192 
1193             spreadBias = properties.getBoolean("ost-spread-bias", false);
1194 
1195       /*
1196        Set up the multi-walker communication variables for Parallel Java
1197        communication between nodes.
1198       */
1199             world = Comm.world();
1200             int numProc = world.size();
1201             rank = world.rank();
1202             if (hd.asynchronous) {
1203                 // Use asynchronous communication.
1204                 sendAsynchronous = new SendAsynchronous(this);
1205                 sendAsynchronous.start();
1206                 sendSynchronous = null;
1207             } else {
1208                 Histogram[] histograms = new Histogram[numProc];
1209                 int[] rankToHistogramMap = new int[numProc];
1210                 for (int i = 0; i < numProc; i++) {
1211                     histograms[i] = this;
1212                     rankToHistogramMap[i] = 0;
1213                 }
1214                 sendSynchronous = new SendSynchronous(histograms, rankToHistogramMap);
1215                 sendAsynchronous = null;
1216             }
1217 
1218             lastReceivedLambda = ld.lambda;
1219             if (hd.discreteLambda) {
1220                 lastReceivedLambda = mapLambda(lastReceivedLambda);
1221                 logger.info(format(" Discrete lambda: initializing lambda to nearest bin %.5f", lastReceivedLambda));
1222                 ld.lambda = lastReceivedLambda;
1223                 ld.theta = asin(sqrt(lastReceivedLambda));
1224                 lambdaInterface.setLambda(lastReceivedLambda);
1225                 lambdaState = null;
1226                 stochastic = null;
1227             } else {
1228                 // Configure the Stochastic integrator.
1229                 lambdaState = new SystemState(1);
1230                 stochastic = new Stochastic(lambdaParticleOptions.getLambdaFriction(), lambdaState);
1231                 stochastic.setTemperature(dynamicsOptions.getTemperature());
1232                 stochastic.setTimeStep(dynamicsOptions.getDtPsec());
1233                 double[] mass = lambdaState.getMass();
1234                 double[] thetaPosition = lambdaState.x();
1235                 double[] thetaVelocity = lambdaState.v();
1236                 double[] thetaAccel = lambdaState.a();
1237                 mass[0] = lambdaParticleOptions.getLambdaMass();
1238                 thetaPosition[0] = ld.theta;
1239                 thetaVelocity[0] = ld.thetaVelocity;
1240                 thetaAccel[0] = ld.thetaAcceleration;
1241             }
1242 
1243             lastReceiveddUdL = getdEdL();
1244 
1245             if (hd.wasHistogramRead()) {
1246                 updateFreeEnergyDifference(true, false);
1247             }
1248         }
1249 
1250         public void disableResetStatistics() {
1251             hd.resetHistogram = false;
1252         }
1253 
1254         /**
1255          * evaluateTotalBias.
1256          *
1257          * @param bias If false, return the negative of the Total OST bias.
1258          * @return A StringBuffer The total OST Bias.
1259          */
1260         public StringBuffer evaluateTotalOSTBias(boolean bias) {
1261             StringBuffer sb = new StringBuffer();
1262             double[] chainRule = new double[2];
1263             for (int dUdLBin = 0; dUdLBin < hd.getDUDLBins(); dUdLBin++) {
1264                 double currentdUdL = dUdLforBin(dUdLBin);
1265                 sb.append(format(" %16.8f", currentdUdL));
1266                 for (int lambdaBin = 0; lambdaBin < hd.getLambdaBins(); lambdaBin++) {
1267                     double currentLambda = lambdaForIndex(lambdaBin);
1268                     double bias1D = energyAndGradient1D(currentLambda, false);
1269                     double bias2D = energyAndGradient2D(currentLambda, currentdUdL, chainRule, hd.biasMag);
1270                     double totalBias = bias1D + bias2D;
1271                     // If the bias flag is false, turn the total bias into the PMF.
1272                     if (!bias) {
1273                         totalBias = -totalBias;
1274                     }
1275                     sb.append(format(" %16.8f", totalBias));
1276                 }
1277                 sb.append("\n");
1278             }
1279             return sb;
1280         }
1281 
1282         /**
1283          * evaluate2DOSTBias.
1284          *
1285          * @param bias If false, return the negative of the 2D OST bias.
1286          * @return A StringBuffer with the 2D OST bias.
1287          */
1288         public StringBuffer evaluate2DOSTBias(boolean bias) {
1289             StringBuffer sb = new StringBuffer();
1290             double[] chainRule = new double[2];
1291             for (int dUdLBin = 0; dUdLBin < hd.getDUDLBins(); dUdLBin++) {
1292                 double currentdUdL = dUdLforBin(dUdLBin);
1293                 sb.append(format(" %16.8f", currentdUdL));
1294                 for (int lambdaBin = 0; lambdaBin < hd.getLambdaBins(); lambdaBin++) {
1295                     double currentLambda = lambdaForIndex(lambdaBin);
1296                     double bias2D = energyAndGradient2D(currentLambda, currentdUdL, chainRule, hd.biasMag);
1297                     if (!bias) {
1298                         bias2D = -bias2D;
1299                     }
1300                     sb.append(format(" %16.8f", bias2D));
1301                 }
1302                 sb.append("\n");
1303             }
1304             return sb;
1305         }
1306 
1307         /**
1308          * For MPI parallel jobs, returns true if the walkers are independent (i.e. contribute to only
1309          * their own histogram).
1310          *
1311          * @return True if the walkers are independent.
1312          */
1313         public boolean getIndependentWalkers() {
1314             return hd.independentWalkers;
1315         }
1316 
1317         public double getLambdaResetValue() {
1318             return hd.resetHistogramAtLambda;
1319         }
1320 
1321         /**
1322          * For MPI parallel jobs, return the rank of this process.
1323          *
1324          * @return The rank of this process.
1325          */
1326         public int getRank() {
1327             return rank;
1328         }
1329 
1330         public boolean getResetStatistics() {
1331             return hd.resetHistogram;
1332         }
1333 
1334         /**
1335          * Return the SynchronousSend associated with this Histogram, if any.
1336          *
1337          * @return The SynchronousSend, if any.
1338          */
1339         public Optional<SendSynchronous> getSynchronousSend() {
1340             return Optional.ofNullable(sendSynchronous);
1341         }
1342 
1343         public void setLastReceiveddUdL(double lastReceiveddUdL) {
1344             this.lastReceiveddUdL = lastReceiveddUdL;
1345         }
1346 
1347         public double updateFreeEnergyDifference(boolean print, boolean save) {
1348             if (hd.metaDynamics) {
1349                 return updateMetaDynamicsFreeEnergyDifference(print, save);
1350             } else {
1351                 return updateOSTFreeEnergyDifference(print, save);
1352             }
1353         }
1354 
1355         /**
1356          * Update the free energy estimate for Meta Dynamics.
1357          *
1358          * @param print Whether to write the histogram to screen.
1359          * @param save  Whether to write the histogram to disk.
1360          * @return Free energy (via integration of ensemble-average dU/dL)
1361          */
1362         public double updateMetaDynamicsFreeEnergyDifference(boolean print, boolean save) {
1363             synchronized (this) {
1364                 if (!print && currentNumberOfHills == hd.counts) {
1365                     return currentFreeEnergyDifference;
1366                 }
1367                 double freeEnergyDifferenceMeta = 0.0;
1368                 double freeEnergyDifferenceTI = 0.0;
1369 
1370                 double minFL = Double.MAX_VALUE;
1371 
1372                 int lambdaBins = hd.getLambdaBins();
1373                 double[] metaFreeEnergy = new double[lambdaBins];
1374 
1375                 // Total histogram weight.
1376                 double totalWeight = 0;
1377                 StringBuilder stringBuilder = new StringBuilder();
1378 
1379                 // Loop over lambda bins, computing <dU/dL> for each bin.
1380                 for (int lambdaBin = 0; lambdaBin < lambdaBins; lambdaBin++) {
1381                     int firstdUdLBin = firstdUdLBin(lambdaBin);
1382                     int lastdUdLBin = lastdUdLBin(lambdaBin);
1383                     double lambdaCount = 0;
1384                     // The dUdL range sampled for lambda.
1385                     double mindUdLForLambda = 0.0;
1386                     double maxdUdLforLambda = 0.0;
1387                     double maxBias = 0;
1388                     if (firstdUdLBin == -1 || lastdUdLBin == -1) {
1389                         ensembleAveragedUdL[lambdaBin] = 0.0;
1390                         minFL = 0.0;
1391                     } else {
1392                         double ensembleAverageFLambda = 0.0;
1393                         double partitionFunction = 0.0;
1394 
1395                         // Evaluate and regularize all kernel values for this value of lambda.
1396                         double offset = evaluateKernelForLambda(lambdaBin, firstdUdLBin, lastdUdLBin);
1397 
1398                         for (int dUdLBin = firstdUdLBin; dUdLBin <= lastdUdLBin; dUdLBin++) {
1399                             double kernel = kernelValues[dUdLBin];
1400 
1401                             if (kernel - offset > maxBias) {
1402                                 maxBias = kernel - offset;
1403                             }
1404 
1405                             // The weight is just the kernel value for no 2D bias.
1406                             partitionFunction += kernel;
1407 
1408                             double currentdUdL = dUdLforBin(dUdLBin);
1409                             ensembleAverageFLambda += currentdUdL * kernel;
1410                             lambdaCount += getRecursionKernelValue(lambdaBin, dUdLBin);
1411                         }
1412                         if (minFL > maxBias) {
1413                             minFL = maxBias;
1414                         }
1415                         ensembleAveragedUdL[lambdaBin] = (partitionFunction == 0) ? 0 : ensembleAverageFLambda / partitionFunction;
1416                         mindUdLForLambda = hd.dUdLMinimum + firstdUdLBin * hd.dUdLBinWidth;
1417                         maxdUdLforLambda = hd.dUdLMinimum + (lastdUdLBin + 1) * hd.dUdLBinWidth;
1418                     }
1419 
1420                     double deltaFreeEnergy = ensembleAveragedUdL[lambdaBin] * deltaForLambdaBin(lambdaBin);
1421                     freeEnergyDifferenceTI += deltaFreeEnergy;
1422                     totalWeight += lambdaCount;
1423 
1424 
1425                     double lambdaBinWidth = hd.lambdaBinWidth;
1426                     double llL = lambdaBin * lambdaBinWidth - hd.lambdaBinWidth_2;
1427                     double ulL = llL + lambdaBinWidth;
1428                     if (llL < 0.0) {
1429                         llL = 0.0;
1430                     }
1431                     if (ulL > 1.0) {
1432                         ulL = 1.0;
1433                     }
1434 
1435                     double midLambda = llL;
1436                     if (!hd.discreteLambda) {
1437                         midLambda = (llL + ulL) / 2.0;
1438                     }
1439                     double bias1D = energyAndGradient1D(midLambda, false);
1440                     metaFreeEnergy[lambdaBin] = energyAndGradientMeta(midLambda, false);
1441                     freeEnergyDifferenceMeta = -(metaFreeEnergy[lambdaBin] - metaFreeEnergy[0]);
1442 
1443                     if (print || save) {
1444                         stringBuilder.append(format(" %6.2e %7.5f %7.1f %7.1f %9.2f %9.2f %9.2f %9.2f %9.2f\n", lambdaCount,
1445                                 midLambda, mindUdLForLambda, maxdUdLforLambda, ensembleAveragedUdL[lambdaBin],
1446                                 bias1D, freeEnergyDifferenceTI, metaFreeEnergy[lambdaBin], freeEnergyDifferenceMeta));
1447                     }
1448                 }
1449 
1450                 double temperingOffset = hd.getTemperingOffset();
1451                 double temperEnergy = (minFL > temperingOffset) ? temperingOffset - minFL : 0;
1452                 temperingWeight = exp(temperEnergy / deltaT);
1453 
1454                 if (print) {
1455                     logger.info("  Weight   Lambda      dU/dL Bins   <dU/dL>      g(L) dG_OST(L)  Meta(L) dG_Meta(L)");
1456                     logger.info(stringBuilder.toString());
1457                     logger.info(" Histogram Evaluation");
1458                     logger.info(format("  Free Energy Difference: %12.4f kcal/mol", freeEnergyDifferenceMeta));
1459                     logger.info(format("  Number of Hills:        %12d", hd.counts));
1460                     logger.info(format("  Total Bias Added:       %12.4f kcal/mol", totalWeight * hd.biasMag));
1461                     logger.info(format("  Minimum Bias:           %12.4f kcal/mol", minFL));
1462                     logger.info(format("  Tempering Percentage:   %12.4f %%\n", temperingWeight * 100));
1463                 }
1464 
1465                 if (save) {
1466                     String modelFilename = molecularAssembly.getFile().getAbsolutePath();
1467                     File saveDir = new File(FilenameUtils.getFullPath(modelFilename));
1468                     String dirName = saveDir + File.separator;
1469                     String fileName = dirName + "histogram.txt";
1470                     try {
1471                         logger.info(" Writing " + fileName);
1472                         PrintWriter printWriter = new PrintWriter(fileName);
1473                         printWriter.write(stringBuilder.toString());
1474                         printWriter.close();
1475                     } catch (Exception e) {
1476                         logger.info(format(" Failed to write %s.", fileName));
1477                     }
1478                 }
1479 
1480                 currentNumberOfHills = hd.counts;
1481                 currentFreeEnergyDifference = freeEnergyDifferenceMeta;
1482                 return currentFreeEnergyDifference;
1483             }
1484         }
1485 
1486         /**
1487          * Eqs. 7 and 8 from the 2012 Crystal Thermodynamics paper.
1488          *
1489          * @param print Whether to write the histogram to screen.
1490          * @param save  Whether to write the histogram to disk.
1491          * @return Free energy (via integration of ensemble-average dU/dL)
1492          */
1493         public double updateOSTFreeEnergyDifference(boolean print, boolean save) {
1494             synchronized (this) {
1495                 if (!print && currentNumberOfHills == hd.counts) {
1496                     return currentFreeEnergyDifference;
1497                 }
1498 
1499                 double freeEnergyDifferenceOST = 0.0;
1500                 double minFL = Double.MAX_VALUE;
1501 
1502                 // If the bias magnitude is zero, computing <dU/dL> from
1503                 // counts will not be correct. Assign a temporary non-zero bias magnitude.
1504                 boolean biasMagZero = hd.biasMag <= 0.0;
1505 
1506                 // Total histogram weight.
1507                 double totalWeight = 0;
1508                 double beta = 1.0 / (R * dynamicsOptions.getTemperature());
1509                 StringBuilder stringBuilder = new StringBuilder();
1510 
1511                 // Loop over lambda bins, computing <dU/dL> for each bin.
1512                 for (int lambdaBin = 0; lambdaBin < hd.lambdaBins; lambdaBin++) {
1513                     int firstdUdLBin = firstdUdLBin(lambdaBin);
1514                     int lastdUdLBin = lastdUdLBin(lambdaBin);
1515                     double lambdaCount = 0;
1516                     // The FL range sampled for lambda bin [iL*dL .. (iL+1)*dL]
1517                     double mindUdLforLambda = 0.0;
1518                     double maxdUdLforLambda = 0.0;
1519                     double maxBias = 0;
1520                     if (firstdUdLBin == -1 || lastdUdLBin == -1) {
1521                         ensembleAveragedUdL[lambdaBin] = 0.0;
1522                         minFL = 0.0;
1523                     } else {
1524                         double ensembleAverage = 0.0;
1525                         double partitionFunction = 0.0;
1526 
1527                         // Evaluate and regularize all kernel values for this value of lambda.
1528                         double offset = evaluateKernelForLambda(lambdaBin, firstdUdLBin, lastdUdLBin);
1529 
1530                         for (int dUdLBin = firstdUdLBin; dUdLBin <= lastdUdLBin; dUdLBin++) {
1531                             double kernel = kernelValues[dUdLBin];
1532 
1533                             if (kernel - offset > maxBias) {
1534                                 maxBias = kernel - offset;
1535                             }
1536 
1537                             double weight;
1538                             if (biasMagZero) {
1539                                 // The weight is just the kernel value for no 2D bias.
1540                                 weight = kernel;
1541                             } else {
1542                                 // The Boltzmann weight based on temperature and the 2D bias height.
1543                                 weight = exp(kernel * beta);
1544                             }
1545                             partitionFunction += weight;
1546 
1547                             double currentdUdL = dUdLforBin(dUdLBin);
1548                             ensembleAverage += currentdUdL * weight;
1549                             lambdaCount += getRecursionKernelValue(lambdaBin, dUdLBin);
1550                         }
1551                         if (minFL > maxBias) {
1552                             minFL = maxBias;
1553                         }
1554                         ensembleAveragedUdL[lambdaBin] = (partitionFunction == 0) ? 0 : ensembleAverage / partitionFunction;
1555                         mindUdLforLambda = hd.dUdLMinimum + firstdUdLBin * hd.dUdLBinWidth;
1556                         maxdUdLforLambda = hd.dUdLMinimum + (lastdUdLBin + 1) * hd.dUdLBinWidth;
1557                     }
1558 
1559                     double deltaFreeEnergy = ensembleAveragedUdL[lambdaBin] * deltaForLambdaBin(lambdaBin);
1560                     freeEnergyDifferenceOST += deltaFreeEnergy;
1561                     totalWeight += lambdaCount;
1562 
1563                     if (print || save) {
1564                         double llL = lambdaBin * hd.lambdaBinWidth - hd.lambdaBinWidth_2;
1565                         double ulL = llL + hd.lambdaBinWidth;
1566                         if (llL < 0.0) {
1567                             llL = 0.0;
1568                         }
1569                         if (ulL > 1.0) {
1570                             ulL = 1.0;
1571                         }
1572 
1573                         double midLambda = llL;
1574                         if (!hd.discreteLambda) {
1575                             midLambda = (llL + ulL) / 2.0;
1576                         }
1577                         double bias1D = energyAndGradient1D(midLambda, false);
1578 
1579                         double bias2D = 0.0;
1580                         if (!biasMagZero) {
1581                             bias2D = computeBiasEnergy(midLambda, ensembleAveragedUdL[lambdaBin]) - bias1D;
1582                         }
1583 
1584                         stringBuilder.append(
1585                                 format(" %6.2e %7.5f %7.1f %7.1f %8.2f %8.2f %8.2f %8.2f %8.2f   %8.2f\n", lambdaCount,
1586                                         midLambda, mindUdLforLambda, maxdUdLforLambda, ensembleAveragedUdL[lambdaBin],
1587                                         bias1D, bias2D, bias1D + bias2D, freeEnergyDifferenceOST, bias1D + bias2D + freeEnergyDifferenceOST));
1588                     }
1589                 }
1590 
1591                 if (!biasMagZero) {
1592                     double temperingOffset = hd.getTemperingOffset();
1593                     double temperEnergy = (minFL > temperingOffset) ? temperingOffset - minFL : 0;
1594                     temperingWeight = exp(temperEnergy / deltaT);
1595                 }
1596 
1597                 freeEnergyDifferenceOST = integrateNumeric(ensembleAveragedUdL, integrationType);
1598 
1599                 if (print) {
1600                     logger.info("  Weight   Lambda      dU/dL Bins  <dU/dL>    g(L)  f(L,<dU/dL>) Bias    dG(L) Bias+dG(L)");
1601                     logger.info(stringBuilder.toString());
1602                     logger.info(" Histogram Evaluation");
1603                     logger.info(format("  Free Energy Difference: %12.4f kcal/mol", freeEnergyDifferenceOST));
1604                     logger.info(format("  Number of Hills:        %12d", hd.counts));
1605                     logger.info(format("  Total Bias Added:       %12.4f kcal/mol", totalWeight * hd.biasMag));
1606                     logger.info(format("  Minimum Bias:           %12.4f kcal/mol", minFL));
1607                     logger.info(format("  Tempering Percentage:   %12.4f %%\n", temperingWeight * 100));
1608                 }
1609 
1610                 if (save) {
1611                     String modelFilename = molecularAssembly.getFile().getAbsolutePath();
1612                     File saveDir = new File(FilenameUtils.getFullPath(modelFilename));
1613                     String dirName = saveDir + File.separator;
1614                     String fileName = dirName + "histogram.txt";
1615                     try {
1616                         logger.info("\n Writing " + fileName);
1617                         PrintWriter printWriter = new PrintWriter(fileName);
1618                         printWriter.write(stringBuilder.toString());
1619                         printWriter.close();
1620                     } catch (Exception e) {
1621                         logger.info(format(" Failed to write %s.", fileName));
1622                     }
1623                 }
1624 
1625                 currentNumberOfHills = hd.counts;
1626                 currentFreeEnergyDifference = freeEnergyDifferenceOST;
1627                 return currentFreeEnergyDifference;
1628             }
1629         }
1630 
1631         /**
1632          * For thermodynamic integration, return the integration width for the given Lambda lambdaBin.
1633          *
1634          * @param lambdaBin The lambda lambdaBin.
1635          * @return The integration width.
1636          */
1637         private double deltaForLambdaBin(int lambdaBin) {
1638             if (!hd.discreteLambda && (lambdaBin == 0 || lambdaBin == hd.lambdaBins - 1)) {
1639                 // The first and last lambda bins are half size for continuous lambda.
1640                 return hd.lambdaBinWidth_2;
1641             } else if (hd.discreteLambda && lambdaBin == 0) {
1642                 // The free energy change to move from L=0 to L=0 is zero.
1643                 return 0.0;
1644             }
1645             // All other cases.
1646             return hd.lambdaBinWidth;
1647         }
1648 
1649         /**
1650          * Returns the index of the first dU/dL bin with counts, or -1 if there are no counts for the
1651          * given lambda bin.
1652          *
1653          * @param lambdaBin Lambda bin to consider.
1654          * @return Index of the first dUdL bin with counts.
1655          */
1656         private int firstdUdLBin(int lambdaBin) {
1657             // Synchronize use of the recursion kernel.
1658             synchronized (this) {
1659                 // Find the smallest FL bin that has counts.
1660                 for (int jFL = 0; jFL < hd.dUdLBins; jFL++) {
1661                     double count = hd.zHistogram[lambdaBin][jFL];
1662                     if (count > 0) {
1663                         return jFL;
1664                     }
1665                 }
1666                 return -1;
1667             }
1668         }
1669 
1670         /**
1671          * Returns the index of the last dU/dL bin with counts, or -1 if there are no counts for the
1672          * given lambda bin.
1673          *
1674          * @param lambdaBin Lambda bin to consider.
1675          * @return Index of the last dUdL bin with counts.
1676          */
1677         private int lastdUdLBin(int lambdaBin) {
1678             // Synchronize use of the recursion kernel.
1679             synchronized (this) {
1680                 // Find the largest FL bin that has counts.
1681                 for (int jFL = hd.dUdLBins - 1; jFL >= 0; jFL--) {
1682                     double count = hd.zHistogram[lambdaBin][jFL];
1683                     if (count > 0) {
1684                         return jFL;
1685                     }
1686                 }
1687                 return -1;
1688             }
1689         }
1690 
1691         /**
1692          * Write histogram and lambda restart files.
1693          * <p>
1694          * For MPI parallel jobs, only rank 0 writes a histogram restart file (unless the "independentWrite" flag is set).
1695          */
1696         void writeRestart() {
1697             if (rank == 0 || hd.independentWrite) {
1698                 updateFreeEnergyDifference(true, false);
1699                 try {
1700                     hd.writeHistogram();
1701                     logger.info(format(" Wrote histogram restart to: %s.", hd.getHistogramFileName()));
1702                 } catch (Exception ex) {
1703                     String message = format(" Exception writing histogram restart file %s.", hd.getHistogramFileName());
1704                     logger.log(Level.INFO, Utilities.stackTraceToString(ex));
1705                     logger.log(Level.SEVERE, message, ex);
1706                 }
1707             }
1708 
1709             // All ranks write a lambda restart file.
1710             try {
1711                 ld.writeLambdaData();
1712                 logger.info(format(" Wrote lambda restart to:    %s.", ld.getLambdaFileName()));
1713             } catch (Exception ex) {
1714                 String message = format(" Exception writing lambda restart file %s.", ld.getLambdaFileName());
1715                 logger.log(Level.INFO, Utilities.stackTraceToString(ex));
1716                 logger.log(Level.SEVERE, message, ex);
1717             }
1718         }
1719 
1720         private double mapLambda(double lambda) {
1721             if (hd.discreteLambda) {
1722                 return hd.lambdaLadder[indexForDiscreteLambda(lambda)];
1723             } else {
1724                 return lambda;
1725             }
1726         }
1727 
1728         /**
1729          * For continuous lambda, the returned value is the lambda bin. For discrete lambda, the returned
1730          * value is the discrete lambda index.
1731          *
1732          * @param lambda a double.
1733          * @return a int.
1734          */
1735         int indexForLambda(double lambda) {
1736             if (hd.discreteLambda) {
1737                 return indexForDiscreteLambda(lambda);
1738             } else {
1739                 return indexForContinuousLambda(lambda);
1740             }
1741         }
1742 
1743         int indexForLambda() {
1744             return indexForLambda(ld.lambda);
1745         }
1746 
1747         private double lambdaForIndex(int bin) {
1748             if (hd.discreteLambda) {
1749                 return hd.lambdaLadder[bin];
1750             } else {
1751                 return bin * hd.lambdaBinWidth;
1752             }
1753         }
1754 
1755         private int indexForContinuousLambda(double lambda) {
1756             int lambdaBin = (int) floor((lambda - hd.minLambda) / hd.lambdaBinWidth);
1757             if (lambdaBin < 0) {
1758                 lambdaBin = 0;
1759             }
1760             if (lambdaBin >= hd.lambdaBins) {
1761                 lambdaBin = hd.lambdaBins - 1;
1762             }
1763             return lambdaBin;
1764         }
1765 
1766         private int indexForDiscreteLambda(double lambda) {
1767             assert hd.discreteLambda && hd.lambdaLadder != null && hd.lambdaLadder.length > 0;
1768 
1769             int initialGuess = indexForContinuousLambda(lambda);
1770             double minErr = Double.MAX_VALUE;
1771             int minErrBin = -1;
1772             for (int i = -1; i < 2; i++) {
1773                 int guessBin = i + initialGuess;
1774                 if (guessBin < 0 || guessBin >= hd.lambdaBins) {
1775                     continue;
1776                 }
1777                 double guessLam = hd.lambdaLadder[guessBin];
1778                 double guessErr = Math.abs(guessLam - lambda);
1779                 if (guessErr < minErr) {
1780                     minErr = guessErr;
1781                     minErrBin = guessBin;
1782                 }
1783             }
1784 
1785             assert minErr < 1.0E-6;
1786             return minErrBin;
1787         }
1788 
1789         /**
1790          * Find the bin for the supplied dEdLambda.
1791          * <p>
1792          * If the supplied dEdL is outside the range of the count matrix, then -1 is returned.
1793          *
1794          * @param dUdL a double.
1795          * @return The dUdL bin.
1796          */
1797         int binFordUdL(double dUdL) {
1798 
1799             // No counts below mindUdL.
1800             if (dUdL < hd.dUdLMinimum) {
1801                 return -1;
1802             }
1803 
1804             // No counts above the maxdUdL.
1805             if (dUdL > hd.dUdLMaximum) {
1806                 return -1;
1807             }
1808 
1809             int bin = (int) floor((dUdL - hd.dUdLMinimum) / hd.dUdLBinWidth);
1810 
1811             if (bin == hd.dUdLBins) {
1812                 bin = hd.dUdLBins - 1;
1813             }
1814 
1815             if (bin < 0) {
1816                 bin = 0;
1817             }
1818 
1819             return bin;
1820         }
1821 
1822         /**
1823          * Return the value of a recursion kernel bin. Mirror conditions are applied to the lambda bin.
1824          * If the dUdLBin is outside the range of the count matrix, zero is returned.
1825          *
1826          * @param lambdaBin The lambda bin.
1827          * @param dUdLBin   The dU/dL bin.
1828          * @return The value of the bin.
1829          */
1830         double getRecursionKernelValue(int lambdaBin, int dUdLBin) {
1831             // Synchronize use of the recursion kernel.
1832             synchronized (this) {
1833                 // Apply lambda mirror condition.
1834                 lambdaBin = lambdaMirror(lambdaBin);
1835 
1836                 // For dUdL outside the count matrix the weight is 0.
1837                 if (dUdLBin < 0 || dUdLBin >= hd.dUdLBins) {
1838                     return 0.0;
1839                 }
1840 
1841                 return hd.zHistogram[lambdaBin][dUdLBin];
1842             }
1843         }
1844 
1845         /**
1846          * Return the value of a recursion kernel bin. Mirror conditions are applied to the lambda bin.
1847          * If the dUdL is outside the range of the count matrix, zero is returned.
1848          *
1849          * @param lambda the lambda value.
1850          * @param dUdL   the dU/dL value.
1851          * @return The value of the Histogram.
1852          */
1853         double getRecursionKernelValue(double lambda, double dUdL) {
1854             return getRecursionKernelValue(indexForLambda(lambda), binFordUdL(dUdL));
1855         }
1856 
1857         /**
1858          * Add to the value of a recursion kernel bin.
1859          *
1860          * @param currentLambda The lambda bin.
1861          * @param currentdUdL   The dU/dL bin.
1862          * @param value         The value of the bin.
1863          */
1864         void addToRecursionKernelValue(double currentLambda, double currentdUdL, double value) {
1865             synchronized (this) {
1866                 if (spreadBias) {
1867                     // Expand the recursion kernel if necessary.
1868                     int dUdLbiasCutoff = hd.dUdLBiasCutoff;
1869                     checkRecursionKernelSize(dUdLforBin(binFordUdL(currentdUdL) - dUdLbiasCutoff));
1870                     checkRecursionKernelSize(dUdLforBin(binFordUdL(currentdUdL) + dUdLbiasCutoff));
1871 
1872                     int currentLambdaBin = indexForLambda(currentLambda);
1873                     int currentdUdLBin = binFordUdL(currentdUdL);
1874 
1875                     // Variances are only used when dividing by twice their value.
1876                     double invLs2 = 0.5 / hd.lambdaVariance;
1877                     double invFLs2 = 0.5 / hd.dUdLVariance;
1878 
1879                     // Compute the normalization.
1880                     double normalize = 0.0;
1881                     int lambdaBiasCutoff = hd.lambdaBiasCutoff;
1882                     for (int iL = -lambdaBiasCutoff; iL <= lambdaBiasCutoff; iL++) {
1883                         int lambdaBin = currentLambdaBin + iL;
1884                         double deltaL = currentLambda - lambdaBin * hd.lambdaBinWidth;
1885                         double deltaL2 = deltaL * deltaL;
1886                         // Pre-compute the lambda bias.
1887                         double L2exp = exp(-deltaL2 * invLs2);
1888                         for (int jFL = -dUdLbiasCutoff; jFL <= dUdLbiasCutoff; jFL++) {
1889                             int dUdLBin = currentdUdLBin + jFL;
1890                             double deltaFL = currentdUdL - dUdLforBin(dUdLBin);
1891                             double deltaFL2 = deltaFL * deltaFL;
1892                             normalize += L2exp * exp(-deltaFL2 * invFLs2);
1893                         }
1894                     }
1895 
1896                     for (int iL = -lambdaBiasCutoff; iL <= lambdaBiasCutoff; iL++) {
1897                         int lambdaBin = currentLambdaBin + iL;
1898                         double deltaL = currentLambda - lambdaBin * hd.lambdaBinWidth;
1899                         double deltaL2 = deltaL * deltaL;
1900                         // Pre-compute the lambda bias.
1901                         double L2exp = exp(-deltaL2 * invLs2);
1902                         lambdaBin = lambdaMirror(lambdaBin);
1903                         for (int jFL = -dUdLbiasCutoff; jFL <= dUdLbiasCutoff; jFL++) {
1904                             int dUdLBin = currentdUdLBin + jFL;
1905                             double deltaFL = currentdUdL - dUdLforBin(dUdLBin);
1906                             double deltaFL2 = deltaFL * deltaFL;
1907                             double weight = value / normalize * L2exp * exp(-deltaFL2 * invFLs2);
1908                             hd.zHistogram[lambdaBin][dUdLBin] += weight;
1909                         }
1910                     }
1911                     hd.counts++;
1912                 } else {
1913                     // Check the recursion kernel size.
1914                     checkRecursionKernelSize(currentdUdL);
1915                     int lambdaBin = indexForLambda(currentLambda);
1916                     int dUdLBin = binFordUdL(currentdUdL);
1917                     try {
1918                         hd.zHistogram[lambdaBin][dUdLBin] += value;
1919                         hd.counts++;
1920                     } catch (IndexOutOfBoundsException e) {
1921                         logger.info(format(" Histogram dimensions %d %d", hd.lambdaBins, hd.dUdLBins));
1922                         logger.info(format(
1923                                 " Count skipped in addToRecursionKernelValue due to an index out of bounds exception.\n L=%10.8f (%d), dU/dL=%10.8f (%d) and count=%10.8f",
1924                                 currentLambda, lambdaBin, currentdUdL, dUdLBin, value));
1925 
1926                     }
1927                 }
1928             }
1929         }
1930 
1931         /**
1932          * Allocate memory for the recursion kernel.
1933          */
1934         void allocateRecursionKernel() {
1935             // Synchronize updates of the recursion kernel.
1936             synchronized (this) {
1937                 hd.zHistogram = new double[hd.lambdaBins][hd.dUdLBins];
1938                 kernelValues = new double[hd.dUdLBins];
1939             }
1940         }
1941 
1942         /**
1943          * Integrates dUdL over lambda using more sophisticated techniques than midpoint rectangular
1944          * integration.
1945          *
1946          * <p>The ends (from 0 to dL and 1-dL to 1) are integrated with trapezoids for continuous
1947          * lambda.
1948          *
1949          * @param dUdLs dUdL at the midpoint of each bin.
1950          * @param type  Integration type to use.
1951          * @return Current delta-G estimate.
1952          */
1953         private double integrateNumeric(double[] dUdLs, IntegrationType type) {
1954             double val;
1955             if (hd.discreteLambda) {
1956                 // Integrate between the first bin and the last bin.
1957                 double[] lams = Integrate1DNumeric.generateXPoints(0.0, 1.0, hd.lambdaBins, false);
1958                 DataSet dSet = new DoublesDataSet(lams, dUdLs, false);
1959                 val = Integrate1DNumeric.integrateData(dSet, Integrate1DNumeric.IntegrationSide.LEFT, type);
1960             } else {
1961                 // Integrate between the second bin midpoint and the second-to-last bin midpoint.
1962                 double[] midLams = Integrate1DNumeric.generateXPoints(hd.lambdaBinWidth, 1.0 - hd.lambdaBinWidth,
1963                         (hd.lambdaBins - 2), false);
1964                 double[] midVals = Arrays.copyOfRange(dUdLs, 1, (hd.lambdaBins - 1));
1965                 DataSet dSet = new DoublesDataSet(midLams, midVals, false);
1966 
1967                 val = Integrate1DNumeric.integrateData(dSet, Integrate1DNumeric.IntegrationSide.LEFT, type);
1968 
1969                 // Everything after this is just adding in the endpoint contributions.
1970 
1971                 double dL_4 = hd.lambdaBinWidth_2 * 0.5;
1972 
1973                 // Initially, assume dU/dL is exactly 0 at the endpoints. This is sometimes a true
1974                 // assumption.
1975                 double val0 = 0;
1976                 double val1 = 0;
1977 
1978                 // If we cannot guarantee that dUdL is exactly 0 at the endpoints, interpolate.
1979                 if (!lambdaInterface.dEdLZeroAtEnds()) {
1980                     double recipSlopeLen = 1.0 / (hd.lambdaBinWidth * 0.75);
1981 
1982                     double slope = dUdLs[0] - dUdLs[1];
1983                     slope *= recipSlopeLen;
1984                     val0 = dUdLs[0] + (slope * dL_4);
1985 
1986                     slope = dUdLs[hd.lambdaBins - 1] - dUdLs[hd.lambdaBins - 2];
1987                     slope *= recipSlopeLen;
1988                     val1 = dUdLs[hd.lambdaBins - 1] + (slope * dL_4);
1989                     logger.fine(format(" Inferred dU/dL values at 0 and 1: %10.5g , %10.5g", val0, val1));
1990                 }
1991 
1992                 // Integrate trapezoids from 0 to the second bin midpoint, and from second-to-last bin
1993                 // midpoint to 1.
1994                 val += trapezoid(0, dL_4, val0, dUdLs[0]);
1995                 val += trapezoid(dL_4, hd.lambdaBinWidth, dUdLs[0], dUdLs[1]);
1996                 val += trapezoid(1.0 - hd.lambdaBinWidth, 1.0 - dL_4, dUdLs[hd.lambdaBins - 2],
1997                         dUdLs[hd.lambdaBins - 1]);
1998                 val += trapezoid(1.0 - dL_4, 1.0, dUdLs[hd.lambdaBins - 1], val1);
1999             }
2000 
2001             return val;
2002         }
2003 
2004         /**
2005          * Integrates a trapezoid.
2006          *
2007          * @param x0  First x point
2008          * @param x1  Second x point
2009          * @param fx0 First f(x) point
2010          * @param fx1 Second f(x) point
2011          * @return The area under a trapezoid.
2012          */
2013         private double trapezoid(double x0, double x1, double fx0, double fx1) {
2014             double val = 0.5 * (fx0 + fx1);
2015             val *= (x1 - x0);
2016             return val;
2017         }
2018 
2019         /**
2020          * Evaluate the kernel across dU/dL values at given value of lambda. The largest kernel value V
2021          * is used to define an offset (-V), which is added to all to kernel values. Then, the largest
2022          * kernel value is zero, and will result in a statistical weight of 1.
2023          *
2024          * <p>The offset avoids the recursion kernel becoming too large for some combinations of
2025          * (Lambda, dU/dL). This can result in its statistical weight = exp(kernel * beta) exceeding the
2026          * maximum representable double value.
2027          *
2028          * @param lambda Value of Lambda to evaluate the kernel for.
2029          * @param llFL   Lower FLambda bin.
2030          * @param ulFL   Upper FLambda bin.
2031          * @return the applied offset.
2032          */
2033         private double evaluateKernelForLambda(int lambda, int llFL, int ulFL) {
2034             double maxKernel = Double.MIN_VALUE;
2035 
2036             double gaussianBiasMagnitude = hd.biasMag;
2037             if (hd.biasMag <= 0.0) {
2038                 gaussianBiasMagnitude = PSEUDO_BIAS_MAGNITUDE;
2039             }
2040 
2041             for (int jFL = llFL; jFL <= ulFL; jFL++) {
2042                 double value = evaluateKernel(lambda, jFL, gaussianBiasMagnitude);
2043                 kernelValues[jFL] = value;
2044                 if (value > maxKernel) {
2045                     maxKernel = value;
2046                 }
2047             }
2048 
2049             // Only offset the kernel values for use with the 2D OST bias.
2050             double offset = 0.0;
2051             if (hd.biasMag > 0.0 && !hd.metaDynamics) {
2052                 offset = -maxKernel;
2053                 for (int jFL = llFL; jFL <= ulFL; jFL++) {
2054                     kernelValues[jFL] += offset;
2055                 }
2056             }
2057             return offset;
2058         }
2059 
2060         /**
2061          * Mirror the lambda bin if its less < 0 or greater than the last bin.
2062          *
2063          * @param bin Lambda bin to mirror.
2064          * @return The mirrored lambda bin.
2065          */
2066         private int lambdaMirror(int bin) {
2067             if (bin < 0) {
2068                 return -bin;
2069             }
2070             int lastBin = hd.lambdaBins - 1;
2071             if (bin > lastBin) {
2072                 // Number of bins past the last bin.
2073                 bin -= lastBin;
2074                 // Return Mirror bin
2075                 return lastBin - bin;
2076             }
2077             // No mirror condition.
2078             return bin;
2079         }
2080 
2081         /**
2082          * For continuous lambda, the width of the first and last bins is dLambda_2, so the mirror
2083          * condition is to double their counts.
2084          *
2085          * @param bin Current lambda bin.
2086          * @return The mirror factor (either 1.0 or 2.0).
2087          */
2088         private double mirrorFactor(int bin) {
2089             if (hd.discreteLambda) {
2090                 return 1.0;
2091             }
2092             if (bin == 0 || bin == hd.lambdaBins - 1) {
2093                 return 2.0;
2094             }
2095             return 1.0;
2096         }
2097 
2098         /**
2099          * Compute the value of dU/dL for the given Histogram bin.
2100          *
2101          * @param dUdLBin The bin index in the dU/dL dimension.
2102          * @return The value of dU/dL at the center of the bin.
2103          */
2104         private double dUdLforBin(int dUdLBin) {
2105             return hd.dUdLMinimum + dUdLBin * hd.dUdLBinWidth + hd.dUdLBinWidth_2;
2106         }
2107 
2108         /**
2109          * Evaluate the bias at [currentLambdaBin, cF_lambda]
2110          */
2111         private double evaluateKernel(int currentLambdaBin, int currentdUdLBin, double gaussianBiasMagnitude) {
2112             // Compute the value of L and FL for the center of the current bin.
2113             double currentLambda = currentLambdaBin * hd.lambdaBinWidth;
2114             double currentdUdL = dUdLforBin(currentdUdLBin);
2115 
2116             // Variances are only used when dividing by twice their value.
2117             double invLs2 = 0.5 / hd.lambdaVariance;
2118             double invFLs2 = 0.5 / hd.dUdLVariance;
2119 
2120             double sum = 0.0;
2121             int lambdaBiasCutoff = hd.lambdaBiasCutoff;
2122             for (int iL = -lambdaBiasCutoff; iL <= lambdaBiasCutoff; iL++) {
2123                 int lambdaBin = currentLambdaBin + iL;
2124                 double deltaL = currentLambda - lambdaBin * hd.lambdaBinWidth;
2125                 double deltaL2 = deltaL * deltaL;
2126 
2127                 // Pre-compute the lambda bias.
2128                 double L2exp = exp(-deltaL2 * invLs2);
2129 
2130                 // Mirror condition for Lambda counts.
2131                 lambdaBin = lambdaMirror(lambdaBin);
2132                 double mirrorFactor = mirrorFactor(lambdaBin);
2133 
2134                 int dUdLbiasCutoff = hd.dUdLBiasCutoff;
2135                 for (int jFL = -dUdLbiasCutoff; jFL <= dUdLbiasCutoff; jFL++) {
2136                     int dUdLBin = currentdUdLBin + jFL;
2137                     double weight = mirrorFactor * getRecursionKernelValue(lambdaBin, dUdLBin);
2138                     if (weight <= 0.0) {
2139                         continue;
2140                     }
2141                     double deltaFL = currentdUdL - dUdLforBin(dUdLBin);
2142                     double deltaFL2 = deltaFL * deltaFL;
2143                     double e = weight * L2exp * exp(-deltaFL2 * invFLs2);
2144                     sum += e;
2145                 }
2146             }
2147             return gaussianBiasMagnitude * sum;
2148         }
2149 
2150         /**
2151          * Compute the total Bias energy at (currentLambda, currentdUdL).
2152          *
2153          * @param currentLambda The value of lambda.
2154          * @param currentdUdL   The value of dU/dL.
2155          * @return The bias energy.
2156          */
2157         double computeBiasEnergy(double currentLambda, double currentdUdL) {
2158             synchronized (this) {
2159                 int currentLambdaBin = indexForLambda(currentLambda);
2160                 int currentdUdLBin = binFordUdL(currentdUdL);
2161 
2162                 double bias1D;
2163                 double bias2D = 0.0;
2164 
2165                 if (!hd.metaDynamics) {
2166                     if (hd.biasMag > 0.0) {
2167                         int lambdaBiasCutoff = hd.lambdaBiasCutoff;
2168                         for (int iL = -lambdaBiasCutoff; iL <= lambdaBiasCutoff; iL++) {
2169 
2170                             int lambdaBin = currentLambdaBin + iL;
2171                             double deltaL = currentLambda - (lambdaBin * hd.lambdaBinWidth);
2172                             double deltaL2 = deltaL * deltaL;
2173                             double expL2 = exp(-deltaL2 / (2.0 * hd.lambdaVariance));
2174 
2175                             // Mirror conditions for recursion kernel counts.
2176                             lambdaBin = lambdaMirror(lambdaBin);
2177                             double mirrorFactor = mirrorFactor(lambdaBin);
2178 
2179                             int dUdLbiasCutoff = hd.dUdLBiasCutoff;
2180                             for (int iFL = -dUdLbiasCutoff; iFL <= dUdLbiasCutoff; iFL++) {
2181                                 int dUdLBin = currentdUdLBin + iFL;
2182 
2183                                 double weight = mirrorFactor * getRecursionKernelValue(lambdaBin, dUdLBin);
2184                                 if (weight <= 0.0) {
2185                                     continue;
2186                                 }
2187 
2188                                 double deltaFL = currentdUdL - dUdLforBin(dUdLBin);
2189                                 double deltaFL2 = deltaFL * deltaFL;
2190                                 double bias = weight * hd.biasMag * expL2 * exp(-deltaFL2 / (2.0 * hd.dUdLVariance));
2191                                 bias2D += bias;
2192                             }
2193                         }
2194                     }
2195                     // Compute the energy for the recursion worker at F(L) using interpolation.
2196                     bias1D = energyAndGradient1D(currentLambda, false);
2197                 } else {
2198                     bias1D = energyAndGradientMeta(currentLambda, false);
2199                 }
2200 
2201                 // Return the total bias.
2202                 return bias1D + bias2D;
2203             }
2204         }
2205 
2206         /**
2207          * Compute the total Bias energy at (currentLambda, currentdUdL) and its gradient.
2208          *
2209          * @param currentdUdLambda The value of dU/dL.
2210          * @param chainRule        The chain rule terms for the bias.
2211          * @return The bias energy.
2212          */
2213         double energyAndGradient2D(double currentdUdLambda, double[] chainRule) {
2214             return energyAndGradient2D(ld.lambda, currentdUdLambda, chainRule, hd.biasMag);
2215         }
2216 
2217         /**
2218          * Compute the total Bias energy at (currentLambda, currentdUdL) and its gradient.
2219          *
2220          * @param currentLambda         The value of lambda.
2221          * @param currentdUdLambda      The value of dU/dL.
2222          * @param chainRule             The chain rule terms for the bias.
2223          * @param gaussianBiasMagnitude The magnitude of the Gaussian bias.
2224          * @return The bias energy.
2225          */
2226         double energyAndGradient2D(double currentLambda, double currentdUdLambda, double[] chainRule,
2227                                    double gaussianBiasMagnitude) {
2228 
2229             if (gaussianBiasMagnitude <= 0.0) {
2230                 chainRule[0] = 0.0;
2231                 chainRule[1] = 0.0;
2232                 return 0;
2233             }
2234 
2235             // Calculate recursion kernel G(L, F_L) and its derivatives with respect to L and F_L.
2236             double gLdEdL = 0.0;
2237             double dGdLambda = 0.0;
2238             double dGdFLambda = 0.0;
2239             int currentLambdaBin = indexForLambda(currentLambda);
2240             int currentdUdLBin = binFordUdL(currentdUdLambda);
2241 
2242             int lambdaBiasCutoff = hd.lambdaBiasCutoff;
2243             for (int iL = -lambdaBiasCutoff; iL <= lambdaBiasCutoff; iL++) {
2244                 int lambdaBin = currentLambdaBin + iL;
2245 
2246                 // Pass in the bin offset to the weight instance.
2247                 double deltaL = currentLambda - (lambdaBin * hd.lambdaBinWidth);
2248                 double deltaL2 = deltaL * deltaL;
2249                 double expL2 = exp(-deltaL2 / (2.0 * hd.lambdaVariance));
2250 
2251                 // Mirror conditions for recursion kernel counts.
2252                 double mirrorFactor = mirrorFactor(lambdaBin);
2253                 int dUdLbiasCutoff = hd.dUdLBiasCutoff;
2254                 for (int iFL = -dUdLbiasCutoff; iFL <= dUdLbiasCutoff; iFL++) {
2255                     int dUdLBin = currentdUdLBin + iFL;
2256 
2257                     double weight = mirrorFactor * getRecursionKernelValue(lambdaBin, dUdLBin);
2258                     if (weight <= 0.0) {
2259                         continue;
2260                     }
2261 
2262                     double deltaFL = currentdUdLambda - dUdLforBin(dUdLBin);
2263                     double deltaFL2 = deltaFL * deltaFL;
2264                     double bias = weight * gaussianBiasMagnitude * expL2 * exp(-deltaFL2 / (2.0 * hd.dUdLVariance));
2265                     gLdEdL += bias;
2266                     dGdLambda -= deltaL / hd.lambdaVariance * bias;
2267                     dGdFLambda -= deltaFL / hd.dUdLVariance * bias;
2268                 }
2269             }
2270 
2271             chainRule[0] = dGdLambda;
2272             chainRule[1] = dGdFLambda;
2273             return gLdEdL;
2274         }
2275 
2276 
2277         /**
2278          * This calculates the 1D OST bias and its derivative with respect to Lambda.
2279          * <p>
2280          * See Equation 8 in <a href="http://doi.org/10.1021/ct300035u">
2281          * The Structure, Thermodynamics, and Solubility of Organic Crystals from Simulation with a
2282          * Polarizable Force Field</a>.
2283          * <p>
2284          *
2285          * @param gradient If true, compute the gradient.
2286          * @return a double.
2287          */
2288         private double energyAndGradient1D(boolean gradient) {
2289             return energyAndGradient1D(ld.lambda, gradient);
2290         }
2291 
2292         /**
2293          * This calculates the 1D OST bias and its derivative with respect to Lambda.
2294          * <p>
2295          * See Equation 8 in <a href="http://doi.org/10.1021/ct300035u">
2296          * The Structure, Thermodynamics, and Solubility of Organic Crystals from Simulation with a
2297          * Polarizable Force Field</a>.
2298          * <p>
2299          *
2300          * @param currentLambda The current value of lambda.
2301          * @param gradient      If true, compute the gradient.
2302          * @return a double.
2303          */
2304         private double energyAndGradient1D(double currentLambda, boolean gradient) {
2305             synchronized (this) {
2306                 double biasEnergy = 0.0;
2307                 for (int iL0 = 0; iL0 < hd.lambdaBins - 1; iL0++) {
2308                     int iL1 = iL0 + 1;
2309 
2310                     // Find bin centers and values for interpolation / extrapolation points.
2311                     double L0 = iL0 * hd.lambdaBinWidth;
2312                     double L1 = L0 + hd.lambdaBinWidth;
2313                     double FL0 = ensembleAveragedUdL[iL0];
2314                     double FL1 = ensembleAveragedUdL[iL1];
2315                     double deltaFL = FL1 - FL0;
2316           /*
2317            If the lambda is less than or equal to the upper limit, this is
2318            the final interval. Set the upper limit to L, compute the partial
2319            derivative and break.
2320           */
2321                     boolean done = false;
2322                     if (currentLambda <= L1) {
2323                         done = true;
2324                         L1 = currentLambda;
2325                     }
2326 
2327                     // Upper limit - lower limit of the integral of the extrapolation / interpolation.
2328                     biasEnergy += (FL0 * L1 + deltaFL * L1 * (0.5 * L1 - L0) / hd.lambdaBinWidth);
2329                     biasEnergy -= (FL0 * L0 + deltaFL * L0 * (-0.5 * L0) / hd.lambdaBinWidth);
2330                     if (done) {
2331                         // Compute the gradient d F(L) / dL at L.
2332                         if (gradient) {
2333                             dUdLambda -= FL0 + (L1 - L0) * deltaFL / hd.lambdaBinWidth;
2334                         }
2335                         break;
2336                     }
2337                 }
2338                 return -biasEnergy;
2339             }
2340         }
2341 
2342         /**
2343          * This calculates the 1D Metadynamics bias and its derivative with respect to Lambda.
2344          *
2345          * @param gradient If true, compute the gradient.
2346          * @return a double.
2347          */
2348         private double energyAndGradientMeta(boolean gradient) {
2349             return energyAndGradientMeta(ld.lambda, gradient);
2350         }
2351 
2352         /**
2353          * This calculates the 1D Metadynamics bias and its derivative with respect to Lambda.
2354          *
2355          * @param currentLambda The current value of lambda.
2356          * @param gradient      If true, compute the gradient.
2357          * @return a double.
2358          */
2359         private double energyAndGradientMeta(double currentLambda, boolean gradient) {
2360             // Zero out the metadynamics bias energy.
2361             double biasEnergy = 0.0;
2362 
2363             // Current lambda bin.
2364             int currentBin = indexForLambda(currentLambda);
2365 
2366             // Loop over bins within the cutoff.
2367             int lambdaBiasCutoff = hd.lambdaBiasCutoff;
2368             for (int iL = -lambdaBiasCutoff; iL <= lambdaBiasCutoff; iL++) {
2369                 int lambdaBin = currentBin + iL;
2370                 double deltaL = currentLambda - (lambdaBin * hd.lambdaBinWidth);
2371                 double deltaL2 = deltaL * deltaL;
2372                 // Mirror conditions for the lambda bin and count magnitude.
2373                 lambdaBin = lambdaMirror(lambdaBin);
2374                 double mirrorFactor = mirrorFactor(lambdaBin);
2375                 double weight = mirrorFactor * hd.biasMag * countsForLambda(lambdaBin);
2376                 if (weight > 0) {
2377                     double e = weight * exp(-deltaL2 / (2.0 * hd.lambdaVariance));
2378                     biasEnergy += e;
2379                     // Add the dMeta/dL contribution.
2380                     if (gradient) {
2381                         dUdLambda -= deltaL / hd.lambdaVariance * e;
2382                     }
2383                 }
2384             }
2385 
2386             return biasEnergy;
2387         }
2388 
2389         /**
2390          * Marginalize over dU/dL counts for the given lambda bin.
2391          *
2392          * @param lambdaBin Lambda bin to marginalize for.
2393          * @return Total number of counts.
2394          */
2395         private double countsForLambda(int lambdaBin) {
2396             synchronized (this) {
2397                 double count = 0.0;
2398                 for (int i = 0; i < hd.dUdLBins; i++) {
2399                     count += hd.zHistogram[lambdaBin][i];
2400                 }
2401                 return count;
2402             }
2403         }
2404 
2405         /**
2406          * If necessary, allocate more space.
2407          */
2408         void checkRecursionKernelSize(double currentdUdL) {
2409             // Synchronize updates of the recursion kernel.
2410             synchronized (this) {
2411                 if (currentdUdL > hd.dUdLMaximum) {
2412                     logger.info(format(" Current F_lambda %8.2f > maximum histogram size %8.2f.", currentdUdL, hd.dUdLMaximum));
2413 
2414                     double origDeltaG = updateFreeEnergyDifference(false, false);
2415 
2416                     int newdUdLBins = hd.dUdLBins;
2417                     while (hd.dUdLMinimum + newdUdLBins * hd.dUdLBinWidth < currentdUdL) {
2418                         newdUdLBins += 100;
2419                     }
2420                     double[][] newRecursionKernel = new double[hd.lambdaBins][newdUdLBins];
2421 
2422                     // We have added bins above the indices of the current counts just copy them into the new array.
2423                     for (int i = 0; i < hd.lambdaBins; i++) {
2424                         arraycopy(hd.zHistogram[i], 0, newRecursionKernel[i], 0, hd.dUdLBins);
2425                     }
2426 
2427                     hd.zHistogram = newRecursionKernel;
2428                     hd.dUdLBins = newdUdLBins;
2429                     kernelValues = new double[hd.dUdLBins];
2430                     hd.dUdLMaximum = hd.dUdLMinimum + hd.dUdLBinWidth * hd.dUdLBins;
2431                     logger.info(format(" New histogram %8.2f to %8.2f with %d bins.\n", hd.dUdLMinimum, hd.dUdLMaximum, hd.dUdLBins));
2432 
2433                     double newFreeEnergy = updateFreeEnergyDifference(true, false);
2434                     assert (origDeltaG == newFreeEnergy);
2435                 }
2436                 if (currentdUdL < hd.dUdLMinimum) {
2437                     logger.info(format(" Current F_lambda %8.2f < minimum histogram size %8.2f.", currentdUdL, hd.dUdLMinimum));
2438 
2439                     double origDeltaG = updateFreeEnergyDifference(false, false);
2440 
2441                     int offset = 100;
2442                     while (currentdUdL < hd.dUdLMinimum - offset * hd.dUdLBinWidth) {
2443                         offset += 100;
2444                     }
2445                     int newdUdLBins = hd.dUdLBins + offset;
2446                     double[][] newRecursionKernel = new double[hd.lambdaBins][newdUdLBins];
2447 
2448                     // We have added bins below the current counts,
2449                     // so their indices must be increased by: offset = newFLBins - FLBins
2450                     for (int i = 0; i < hd.lambdaBins; i++) {
2451                         arraycopy(hd.zHistogram[i], 0, newRecursionKernel[i], offset, hd.dUdLBins);
2452                     }
2453 
2454                     hd.zHistogram = newRecursionKernel;
2455                     hd.dUdLMinimum = hd.dUdLMinimum - offset * hd.dUdLBinWidth;
2456                     hd.dUdLBins = newdUdLBins;
2457                     kernelValues = new double[hd.dUdLBins];
2458                     logger.info(format(" New histogram %8.2f to %8.2f with %d bins.\n", hd.dUdLMinimum, hd.dUdLMaximum, hd.dUdLBins));
2459 
2460                     double newFreeEnergy = updateFreeEnergyDifference(true, false);
2461                     assert (origDeltaG == newFreeEnergy);
2462                 }
2463             }
2464         }
2465 
2466         /**
2467          * Add a Gaussian hill to the Histogram at (lambda, dUdL).
2468          *
2469          * @param dUdL The value of dU/dL.
2470          */
2471         void addBias(double dUdL) {
2472             // Communicate adding the bias to all walkers.
2473             if (hd.asynchronous) {
2474                 sendAsynchronous.send(ld.lambda, dUdL, temperingWeight);
2475             } else {
2476                 sendSynchronous.send(ld.lambda, dUdL, temperingWeight);
2477             }
2478         }
2479 
2480         /**
2481          * Pre-force portion of the stochastic integrator.
2482          */
2483         private void stochasticPreForce() {
2484             // Update theta.
2485             double[] thetaPosition = lambdaState.x();
2486             thetaPosition[0] = ld.theta;
2487             stochastic.preForce(OrthogonalSpaceTempering.this);
2488             ld.theta = thetaPosition[0];
2489 
2490             // Maintain theta in the interval -PI to PI.
2491             if (ld.theta > PI) {
2492                 ld.theta -= 2.0 * PI;
2493             } else if (ld.theta <= -PI) {
2494                 ld.theta += 2.0 * PI;
2495             }
2496 
2497             // Update lambda as sin(theta)^2.
2498             double sinTheta = sin(ld.theta);
2499             ld.lambda = sinTheta * sinTheta;
2500             lambdaInterface.setLambda(ld.lambda);
2501         }
2502 
2503         /**
2504          * Post-force portion of the stochastic integrator.
2505          */
2506         private void stochasticPostForce() {
2507             double[] thetaPosition = lambdaState.x();
2508             double[] gradient = lambdaState.gradient();
2509             // Update theta and dU/dtheta.
2510             gradient[0] = dUdLambda * sin(2 * ld.theta);
2511             thetaPosition[0] = ld.theta;
2512             stochastic.postForce(gradient);
2513 
2514             // Load the new theta, velocity and acceleration.
2515             double[] thetaVelocity = lambdaState.v();
2516             double[] thetaAcceleration = lambdaState.a();
2517             ld.theta = thetaPosition[0];
2518             ld.thetaVelocity = thetaVelocity[0];
2519             ld.thetaAcceleration = thetaAcceleration[0];
2520 
2521             // Maintain theta in the interval -PI to PI.
2522             if (ld.theta > PI) {
2523                 ld.theta -= 2.0 * PI;
2524             } else if (ld.theta <= -PI) {
2525                 ld.theta += 2.0 * PI;
2526             }
2527 
2528             // Update lambda as sin(theta)^2.
2529             double sinTheta = sin(ld.theta);
2530             ld.lambda = sinTheta * sinTheta;
2531             lambdaInterface.setLambda(ld.lambda);
2532         }
2533 
2534         /**
2535          * Gets the last lambda value received by this Histogram. This can be out-of-date w.r.t. the
2536          * OST's current lambda!
2537          *
2538          * @return Lambda value of the last bias added to this Histogram.
2539          */
2540         double getLastReceivedLambda() {
2541             return lastReceivedLambda;
2542         }
2543 
2544         public void setLastReceivedLambda(double lastReceivedLambda) {
2545             this.lastReceivedLambda = lastReceivedLambda;
2546         }
2547 
2548         /**
2549          * Gets the last dU/dL value received by this Histogram. This can be out-of-date w.r.t. the OST's
2550          * current dU/dL!
2551          *
2552          * @return dU/dL value of the last bias added to this Histogram.
2553          */
2554         double getLastReceivedDUDL() {
2555             return lastReceiveddUdL;
2556         }
2557 
2558         void destroy() {
2559             if (sendAsynchronous != null && sendAsynchronous.isAlive()) {
2560                 double[] killMessage = new double[]{Double.NaN, Double.NaN, Double.NaN, Double.NaN};
2561                 DoubleBuf killBuf = DoubleBuf.buffer(killMessage);
2562                 try {
2563                     logger.fine(" Sending the termination message.");
2564                     world.send(rank, killBuf);
2565                     logger.fine(" Termination message was sent successfully.");
2566                     logger.fine(format(" Receive thread alive %b status %s", sendAsynchronous.isAlive(),
2567                             sendAsynchronous.getState()));
2568                 } catch (Exception ex) {
2569                     String message = format(" Asynchronous Multiwalker OST termination signal "
2570                             + "failed to be sent for process %d.", rank);
2571                     logger.log(Level.SEVERE, message, ex);
2572                 }
2573             } else {
2574                 logger.fine(
2575                         " CountReceiveThread was either not initialized, or is not alive. This is the case for the Histogram script.");
2576             }
2577         }
2578 
2579     }
2580 
2581 }