31bdf4f52b9bcb4e0f15193615f12a106ea42aeb
[alexxy/gromacs.git] / src / gromacs / applied_forces / awh / bias.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 /*! \internal \file
37  * \brief
38  * Implements the Bias class.
39  *
40  * \author Viveca Lindahl
41  * \author Berk Hess <hess@kth.se>
42  * \ingroup module_awh
43  */
44
45 #include "gmxpre.h"
46
47 #include "bias.h"
48
49 #include <cassert>
50 #include <cinttypes>
51 #include <cmath>
52 #include <cstdio>
53 #include <cstdlib>
54 #include <cstring>
55
56 #include <algorithm>
57 #include <memory>
58
59 #include "gromacs/fileio/gmxfio.h"
60 #include "gromacs/gmxlib/network.h"
61 #include "gromacs/math/functions.h"
62 #include "gromacs/math/utilities.h"
63 #include "gromacs/mdtypes/awh_history.h"
64 #include "gromacs/mdtypes/awh_params.h"
65 #include "gromacs/mdtypes/commrec.h"
66 #include "gromacs/utility/exceptions.h"
67 #include "gromacs/utility/gmxassert.h"
68 #include "gromacs/utility/stringutil.h"
69
70 #include "correlationgrid.h"
71 #include "correlationhistory.h"
72 #include "pointstate.h"
73
74 namespace gmx
75 {
76
77 void Bias::warnForHistogramAnomalies(double t, int64_t step, FILE* fplog)
78 {
79     const int maxNumWarningsInCheck = 1;  /* The maximum number of warnings to print per check */
80     const int maxNumWarningsInRun   = 10; /* The maximum number of warnings to print in a run */
81
82     if (fplog == nullptr || numWarningsIssued_ >= maxNumWarningsInRun || state_.inInitialStage()
83         || !params_.isCheckHistogramForAnomaliesStep(step))
84     {
85         return;
86     }
87
88     numWarningsIssued_ +=
89             state_.warnForHistogramAnomalies(grid_, biasIndex(), t, fplog, maxNumWarningsInCheck);
90
91     if (numWarningsIssued_ >= maxNumWarningsInRun)
92     {
93         fprintf(fplog, "\nawh%d: suppressing future AWH warnings.\n", biasIndex() + 1);
94     }
95 }
96
97 void Bias::doSkippedUpdatesForAllPoints()
98 {
99     if (params_.skipUpdates())
100     {
101         state_.doSkippedUpdatesForAllPoints(params_);
102     }
103 }
104
105 gmx::ArrayRef<const double> Bias::calcForceAndUpdateBias(const awh_dvec         coordValue,
106                                                          ArrayRef<const double> neighborLambdaEnergies,
107                                                          ArrayRef<const double> neighborLambdaDhdl,
108                                                          double*                awhPotential,
109                                                          double*                potentialJump,
110                                                          const t_commrec*       commRecord,
111                                                          const gmx_multisim_t*  ms,
112                                                          double                 t,
113                                                          int64_t                step,
114                                                          int64_t                seed,
115                                                          FILE*                  fplog)
116 {
117     if (step < 0)
118     {
119         GMX_THROW(InvalidInputError(
120                 "The step number is negative which is not supported by the AWH code."));
121     }
122
123     state_.setCoordValue(grid_, coordValue);
124
125     std::vector<double, AlignedAllocator<double>>& probWeightNeighbor = alignedTempWorkSpace_;
126
127     /* If the convolved force is needed or this is a sampling step,
128      * the bias in the current neighborhood needs to be up-to-date
129      * and the probablity weights need to be calculated.
130      */
131     const bool        isSampleCoordStep = params_.isSampleCoordStep(step);
132     const bool        moveUmbrella      = (isSampleCoordStep || step == 0);
133     double            convolvedBias     = 0;
134     const CoordState& coordState        = state_.coordState();
135
136     if (params_.convolveForce || moveUmbrella || isSampleCoordStep)
137     {
138         if (params_.skipUpdates())
139         {
140             state_.doSkippedUpdatesInNeighborhood(params_, grid_);
141         }
142         convolvedBias = state_.updateProbabilityWeightsAndConvolvedBias(
143                 dimParams_, grid_, moveUmbrella ? neighborLambdaEnergies : ArrayRef<const double>{},
144                 &probWeightNeighbor);
145
146         if (isSampleCoordStep)
147         {
148             updateForceCorrelationGrid(probWeightNeighbor, neighborLambdaDhdl, t);
149
150             state_.sampleCoordAndPmf(dimParams_, grid_, probWeightNeighbor, convolvedBias);
151         }
152         /* Set the umbrella grid point (for the lambda axis) to the
153          * current grid point. */
154         if (params_.convolveForce && grid_.hasLambdaAxis())
155         {
156             state_.setUmbrellaGridpointToGridpoint();
157         }
158     }
159
160     /* Set the bias force and get the potential contribution from this bias.
161      * The potential jump occurs at different times depending on how
162      * the force is applied (and how the potential is normalized).
163      * For the convolved force it happens when the bias is updated,
164      * for the umbrella when the umbrella is moved.
165      */
166     *potentialJump = 0;
167     double potential;
168     if (params_.convolveForce)
169     {
170         state_.calcConvolvedForce(dimParams_, grid_, probWeightNeighbor,
171                                   moveUmbrella ? neighborLambdaDhdl : ArrayRef<const double>{},
172                                   tempForce_, biasForce_);
173
174         potential = -convolvedBias * params_.invBeta;
175     }
176     else
177     {
178         /* Umbrella force */
179         GMX_RELEASE_ASSERT(state_.points()[coordState.umbrellaGridpoint()].inTargetRegion(),
180                            "AWH bias grid point for the umbrella reference value is outside of the "
181                            "target region.");
182         potential = state_.calcUmbrellaForceAndPotential(
183                 dimParams_, grid_, coordState.umbrellaGridpoint(),
184                 moveUmbrella ? neighborLambdaDhdl : ArrayRef<const double>{}, biasForce_);
185
186         /* Moving the umbrella results in a force correction and
187          * a new potential. The umbrella center is sampled as often as
188          * the coordinate so we know the probability weights needed
189          * for moving the umbrella are up-to-date.
190          */
191         if (moveUmbrella)
192         {
193             const bool onlySampleUmbrellaGridpoint = false;
194             double     newPotential = state_.moveUmbrella(dimParams_, grid_, probWeightNeighbor,
195                                                       neighborLambdaDhdl, biasForce_, step, seed,
196                                                       params_.biasIndex, onlySampleUmbrellaGridpoint);
197             *potentialJump          = newPotential - potential;
198         }
199     }
200
201     /* Update the free energy estimates and bias and other history dependent method parameters */
202     if (params_.isUpdateFreeEnergyStep(step))
203     {
204         state_.updateFreeEnergyAndAddSamplesToHistogram(dimParams_, grid_, params_, commRecord, ms,
205                                                         t, step, fplog, &updateList_);
206
207         if (params_.convolveForce)
208         {
209             /* The update results in a potential jump, so we need the new convolved potential. */
210             double newPotential = -calcConvolvedBias(coordState.coordValue()) * params_.invBeta;
211             *potentialJump      = newPotential - potential;
212         }
213     }
214     /* If there is a lambda axis it is still controlled using an umbrella even if the force
215      * is convolved in the other dimensions. */
216     if (moveUmbrella && params_.convolveForce && grid_.hasLambdaAxis())
217     {
218         const bool onlySampleUmbrellaGridpoint = true;
219         state_.moveUmbrella(dimParams_, grid_, probWeightNeighbor, neighborLambdaDhdl, biasForce_,
220                             step, seed, params_.biasIndex, onlySampleUmbrellaGridpoint);
221     }
222
223     /* Return the potential. */
224     *awhPotential = potential;
225
226     /* Check the sampled histograms and potentially warn user if something is suspicious */
227     warnForHistogramAnomalies(t, step, fplog);
228
229     return biasForce_;
230 }
231
232 /*! \brief
233  * Count the total number of samples / sample weight over all grid points.
234  *
235  * \param[in] pointState  The state of the points in a bias.
236  * \returns the total sample count.
237  */
238 static int64_t countSamples(const std::vector<PointState>& pointState)
239 {
240     double numSamples = 0;
241     for (const PointState& point : pointState)
242     {
243         numSamples += point.weightSumTot();
244     }
245
246     return gmx::roundToInt64(numSamples);
247 }
248
249 /*! \brief
250  * Check if the state (loaded from checkpoint) and the run are consistent.
251  *
252  * When the state and the run setup are inconsistent, an exception is thrown.
253  *
254  * \param[in] params  The parameters of the bias.
255  * \param[in] state   The state of the bias.
256  */
257 static void ensureStateAndRunConsistency(const BiasParams& params, const BiasState& state)
258 {
259     int64_t numSamples = countSamples(state.points());
260     int64_t numUpdatesFromSamples =
261             numSamples / (params.numSamplesUpdateFreeEnergy_ * params.numSharedUpdate);
262     int64_t numUpdatesExpected = state.histogramSize().numUpdates();
263     if (numUpdatesFromSamples != numUpdatesExpected)
264     {
265         std::string mesg = gmx::formatString(
266                 "The number of AWH updates in the checkpoint file (%" PRId64
267                 ") does not match the total number of AWH samples divided by the number of samples "
268                 "per update for %d sharing AWH bias(es) (%" PRId64 "/%d=%" PRId64 ")",
269                 numUpdatesExpected, params.numSharedUpdate, numSamples,
270                 params.numSamplesUpdateFreeEnergy_ * params.numSharedUpdate, numUpdatesFromSamples);
271         mesg += " Maybe you changed AWH parameters.";
272         /* Unfortunately we currently do not store the number of simulations
273          * sharing the bias or the state to checkpoint. But we can hint at
274          * a mismatch in the number of sharing simulations.
275          */
276         if (numUpdatesFromSamples % state.histogramSize().numUpdates() == 0)
277         {
278             mesg += gmx::formatString(
279                     " Or the run you continued from used %" PRId64
280                     " sharing simulations, whereas you now specified %d sharing simulations.",
281                     numUpdatesFromSamples / state.histogramSize().numUpdates(), params.numSharedUpdate);
282         }
283         GMX_THROW(InvalidInputError(mesg));
284     }
285 }
286
287 void Bias::restoreStateFromHistory(const AwhBiasHistory* biasHistory, const t_commrec* cr)
288 {
289     GMX_RELEASE_ASSERT(thisRankDoesIO_ == MASTER(cr),
290                        "The master rank should do I/O, the other ranks should not");
291
292     if (MASTER(cr))
293     {
294         GMX_RELEASE_ASSERT(biasHistory != nullptr,
295                            "On the master rank we need a valid history object to restore from");
296         state_.restoreFromHistory(*biasHistory, grid_);
297
298         /* Ensure that the state is consistent with our current run setup,
299          * since the user can have changed AWH parameters or the number
300          * of simulations sharing the bias.
301          */
302         ensureStateAndRunConsistency(params_, state_);
303
304         if (forceCorrelationGrid_ != nullptr)
305         {
306             forceCorrelationGrid_->restoreStateFromHistory(biasHistory->forceCorrelationGrid);
307         }
308     }
309
310     if (PAR(cr))
311     {
312         state_.broadcast(cr);
313     }
314 }
315
316 void Bias::initHistoryFromState(AwhBiasHistory* biasHistory) const
317 {
318     GMX_RELEASE_ASSERT(biasHistory != nullptr, "Need a valid biasHistory");
319
320     state_.initHistoryFromState(biasHistory);
321
322     if (forceCorrelationGrid_ != nullptr)
323     {
324         biasHistory->forceCorrelationGrid = initCorrelationGridHistoryFromState(forceCorrelationGrid());
325     }
326 }
327
328 void Bias::updateHistory(AwhBiasHistory* biasHistory) const
329 {
330     GMX_RELEASE_ASSERT(biasHistory != nullptr, "Need a valid biasHistory");
331
332     state_.updateHistory(biasHistory, grid_);
333
334     if (forceCorrelationGrid_ != nullptr)
335     {
336         updateCorrelationGridHistory(&biasHistory->forceCorrelationGrid, forceCorrelationGrid());
337     }
338 }
339
340 Bias::Bias(int                            biasIndexInCollection,
341            const AwhParams&               awhParams,
342            const AwhBiasParams&           awhBiasParams,
343            const std::vector<DimParams>&  dimParamsInit,
344            double                         beta,
345            double                         mdTimeStep,
346            int                            numSharingSimulations,
347            const std::string&             biasInitFilename,
348            ThisRankWillDoIO               thisRankWillDoIO,
349            BiasParams::DisableUpdateSkips disableUpdateSkips) :
350     dimParams_(dimParamsInit),
351     grid_(dimParamsInit, awhBiasParams.dimParams),
352     params_(awhParams,
353             awhBiasParams,
354             dimParams_,
355             beta,
356             mdTimeStep,
357             disableUpdateSkips,
358             numSharingSimulations,
359             grid_.axis(),
360             biasIndexInCollection),
361     state_(awhBiasParams, params_.initialHistogramSize, dimParams_, grid_),
362     thisRankDoesIO_(thisRankWillDoIO == ThisRankWillDoIO::Yes),
363     biasForce_(ndim()),
364
365     tempForce_(ndim()),
366     numWarningsIssued_(0)
367 {
368     /* For a global update updateList covers all points, so reserve that */
369     updateList_.reserve(grid_.numPoints());
370
371     state_.initGridPointState(awhBiasParams, dimParams_, grid_, params_, biasInitFilename, awhParams.numBias);
372
373     if (thisRankDoesIO_)
374     {
375         /* Set up the force correlation object. */
376
377         /* We let the correlation init function set its parameters
378          * to something useful for now.
379          */
380         double blockLength = 0;
381         /* Construct the force correlation object. */
382         forceCorrelationGrid_ = std::make_unique<CorrelationGrid>(
383                 state_.points().size(), ndim(), blockLength,
384                 CorrelationGrid::BlockLengthMeasure::Time, awhParams.nstSampleCoord * mdTimeStep);
385
386         writer_ = std::make_unique<BiasWriter>(*this);
387     }
388 }
389
390 void Bias::printInitializationToLog(FILE* fplog) const
391 {
392     if (fplog != nullptr && forceCorrelationGrid_ != nullptr)
393     {
394         std::string prefix = gmx::formatString("\nawh%d:", params_.biasIndex + 1);
395
396         fprintf(fplog,
397                 "%s initial force correlation block length = %g %s"
398                 "%s force correlation number of blocks = %d",
399                 prefix.c_str(), forceCorrelationGrid().getBlockLength(),
400                 forceCorrelationGrid().blockLengthMeasure == CorrelationGrid::BlockLengthMeasure::Weight
401                         ? ""
402                         : "ps",
403                 prefix.c_str(), forceCorrelationGrid().getNumBlocks());
404     }
405 }
406
407 void Bias::updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor,
408                                       ArrayRef<const double>      neighborLambdaDhdl,
409                                       double                      t)
410 {
411     if (forceCorrelationGrid_ == nullptr)
412     {
413         return;
414     }
415
416     const std::vector<int>& neighbor = grid_.point(state_.coordState().gridpointIndex()).neighbor;
417
418     gmx::ArrayRef<double> forceFromNeighbor = tempForce_;
419     for (size_t n = 0; n < neighbor.size(); n++)
420     {
421         double weightNeighbor = probWeightNeighbor[n];
422         int    indexNeighbor  = neighbor[n];
423
424         /* Add the force data of this neighbor point. Note: the sum of these forces is the convolved force.
425
426            We actually add the force normalized by beta which has the units of 1/length. This means that the
427            resulting correlation time integral is directly in units of friction time/length^2 which is really what
428            we're interested in. */
429         state_.calcUmbrellaForceAndPotential(dimParams_, grid_, indexNeighbor, neighborLambdaDhdl,
430                                              forceFromNeighbor);
431
432         /* Note: we might want to give a whole list of data to add instead and have this loop in the data adding function */
433         forceCorrelationGrid_->addData(indexNeighbor, weightNeighbor, forceFromNeighbor, t);
434     }
435 }
436
437 /* Return the number of data blocks that have been prepared for writing. */
438 int Bias::numEnergySubblocksToWrite() const
439 {
440     GMX_RELEASE_ASSERT(writer_ != nullptr, "Should only request data from an initialized writer");
441
442     return writer_->numBlocks();
443 }
444
445 /* Write bias data blocks to energy subblocks. */
446 int Bias::writeToEnergySubblocks(t_enxsubblock* subblock) const
447 {
448     GMX_RELEASE_ASSERT(writer_ != nullptr, "Should only request data from an initialized writer");
449
450     return writer_->writeToEnergySubblocks(*this, subblock);
451 }
452
453 bool Bias::isFepLambdaDimension(int dim) const
454 {
455     GMX_ASSERT(dim < ndim(), "Requested dimension out of range.");
456
457     return dimParams_[dim].isFepLambdaDimension();
458 }
459
460 bool Bias::isSampleCoordStep(const int64_t step) const
461 {
462     return params_.isSampleCoordStep(step);
463 }
464
465
466 } // namespace gmx