Disable awh-potential=convolved when using AWH with pull and FEP
[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,2021, 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     GMX_RELEASE_ASSERT(!(params_.convolveForce && grid_.hasLambdaAxis()),
124                        "When using AWH to sample an FEP lambda dimension the AWH potential cannot "
125                        "be convolved.");
126
127     state_.setCoordValue(grid_, coordValue);
128
129     std::vector<double, AlignedAllocator<double>>& probWeightNeighbor = alignedTempWorkSpace_;
130
131     /* If the convolved force is needed or this is a sampling step,
132      * the bias in the current neighborhood needs to be up-to-date
133      * and the probablity weights need to be calculated.
134      */
135     const bool        isSampleCoordStep = params_.isSampleCoordStep(step);
136     const bool        moveUmbrella      = (isSampleCoordStep || step == 0);
137     double            convolvedBias     = 0;
138     const CoordState& coordState        = state_.coordState();
139
140     if (params_.convolveForce || moveUmbrella || isSampleCoordStep)
141     {
142         if (params_.skipUpdates())
143         {
144             state_.doSkippedUpdatesInNeighborhood(params_, grid_);
145         }
146         convolvedBias = state_.updateProbabilityWeightsAndConvolvedBias(
147                 dimParams_, grid_, moveUmbrella ? neighborLambdaEnergies : ArrayRef<const double>{},
148                 &probWeightNeighbor);
149
150         if (isSampleCoordStep)
151         {
152             updateForceCorrelationGrid(probWeightNeighbor, neighborLambdaDhdl, t);
153
154             state_.sampleCoordAndPmf(dimParams_, grid_, probWeightNeighbor, convolvedBias);
155         }
156     }
157
158     /* Set the bias force and get the potential contribution from this bias.
159      * The potential jump occurs at different times depending on how
160      * the force is applied (and how the potential is normalized).
161      * For the convolved force it happens when the bias is updated,
162      * for the umbrella when the umbrella is moved.
163      */
164     *potentialJump = 0;
165     double potential;
166     if (params_.convolveForce)
167     {
168         state_.calcConvolvedForce(dimParams_, grid_, probWeightNeighbor,
169                                   moveUmbrella ? neighborLambdaDhdl : ArrayRef<const double>{},
170                                   tempForce_, biasForce_);
171
172         potential = -convolvedBias * params_.invBeta;
173     }
174     else
175     {
176         /* Umbrella force */
177         GMX_RELEASE_ASSERT(state_.points()[coordState.umbrellaGridpoint()].inTargetRegion(),
178                            "AWH bias grid point for the umbrella reference value is outside of the "
179                            "target region.");
180         potential = state_.calcUmbrellaForceAndPotential(
181                 dimParams_, grid_, coordState.umbrellaGridpoint(),
182                 moveUmbrella ? neighborLambdaDhdl : ArrayRef<const double>{}, biasForce_);
183
184         /* Moving the umbrella results in a force correction and
185          * a new potential. The umbrella center is sampled as often as
186          * the coordinate so we know the probability weights needed
187          * for moving the umbrella are up-to-date.
188          */
189         if (moveUmbrella)
190         {
191             double newPotential =
192                     state_.moveUmbrella(dimParams_, grid_, probWeightNeighbor, neighborLambdaDhdl,
193                                         biasForce_, step, seed, params_.biasIndex);
194             *potentialJump = newPotential - potential;
195         }
196     }
197
198     /* Update the free energy estimates and bias and other history dependent method parameters */
199     if (params_.isUpdateFreeEnergyStep(step))
200     {
201         state_.updateFreeEnergyAndAddSamplesToHistogram(dimParams_, grid_, params_, commRecord, ms,
202                                                         t, step, fplog, &updateList_);
203
204         if (params_.convolveForce)
205         {
206             /* The update results in a potential jump, so we need the new convolved potential. */
207             double newPotential = -calcConvolvedBias(coordState.coordValue()) * params_.invBeta;
208             *potentialJump      = newPotential - potential;
209         }
210     }
211
212     /* Return the potential. */
213     *awhPotential = potential;
214
215     /* Check the sampled histograms and potentially warn user if something is suspicious */
216     warnForHistogramAnomalies(t, step, fplog);
217
218     return biasForce_;
219 }
220
221 /*! \brief
222  * Count the total number of samples / sample weight over all grid points.
223  *
224  * \param[in] pointState  The state of the points in a bias.
225  * \returns the total sample count.
226  */
227 static int64_t countSamples(const std::vector<PointState>& pointState)
228 {
229     double numSamples = 0;
230     for (const PointState& point : pointState)
231     {
232         numSamples += point.weightSumTot();
233     }
234
235     return gmx::roundToInt64(numSamples);
236 }
237
238 /*! \brief
239  * Check if the state (loaded from checkpoint) and the run are consistent.
240  *
241  * When the state and the run setup are inconsistent, an exception is thrown.
242  *
243  * \param[in] params  The parameters of the bias.
244  * \param[in] state   The state of the bias.
245  */
246 static void ensureStateAndRunConsistency(const BiasParams& params, const BiasState& state)
247 {
248     int64_t numSamples = countSamples(state.points());
249     int64_t numUpdatesFromSamples =
250             numSamples / (params.numSamplesUpdateFreeEnergy_ * params.numSharedUpdate);
251     int64_t numUpdatesExpected = state.histogramSize().numUpdates();
252     if (numUpdatesFromSamples != numUpdatesExpected)
253     {
254         std::string mesg = gmx::formatString(
255                 "The number of AWH updates in the checkpoint file (%" PRId64
256                 ") does not match the total number of AWH samples divided by the number of samples "
257                 "per update for %d sharing AWH bias(es) (%" PRId64 "/%d=%" PRId64 ")",
258                 numUpdatesExpected, params.numSharedUpdate, numSamples,
259                 params.numSamplesUpdateFreeEnergy_ * params.numSharedUpdate, numUpdatesFromSamples);
260         mesg += " Maybe you changed AWH parameters.";
261         /* Unfortunately we currently do not store the number of simulations
262          * sharing the bias or the state to checkpoint. But we can hint at
263          * a mismatch in the number of sharing simulations.
264          */
265         if (numUpdatesFromSamples % state.histogramSize().numUpdates() == 0)
266         {
267             mesg += gmx::formatString(
268                     " Or the run you continued from used %" PRId64
269                     " sharing simulations, whereas you now specified %d sharing simulations.",
270                     numUpdatesFromSamples / state.histogramSize().numUpdates(), params.numSharedUpdate);
271         }
272         GMX_THROW(InvalidInputError(mesg));
273     }
274 }
275
276 void Bias::restoreStateFromHistory(const AwhBiasHistory* biasHistory, const t_commrec* cr)
277 {
278     GMX_RELEASE_ASSERT(thisRankDoesIO_ == MASTER(cr),
279                        "The master rank should do I/O, the other ranks should not");
280
281     if (MASTER(cr))
282     {
283         GMX_RELEASE_ASSERT(biasHistory != nullptr,
284                            "On the master rank we need a valid history object to restore from");
285         state_.restoreFromHistory(*biasHistory, grid_);
286
287         /* Ensure that the state is consistent with our current run setup,
288          * since the user can have changed AWH parameters or the number
289          * of simulations sharing the bias.
290          */
291         ensureStateAndRunConsistency(params_, state_);
292
293         if (forceCorrelationGrid_ != nullptr)
294         {
295             forceCorrelationGrid_->restoreStateFromHistory(biasHistory->forceCorrelationGrid);
296         }
297     }
298
299     if (PAR(cr))
300     {
301         state_.broadcast(cr);
302     }
303 }
304
305 void Bias::initHistoryFromState(AwhBiasHistory* biasHistory) const
306 {
307     GMX_RELEASE_ASSERT(biasHistory != nullptr, "Need a valid biasHistory");
308
309     state_.initHistoryFromState(biasHistory);
310
311     if (forceCorrelationGrid_ != nullptr)
312     {
313         biasHistory->forceCorrelationGrid = initCorrelationGridHistoryFromState(forceCorrelationGrid());
314     }
315 }
316
317 void Bias::updateHistory(AwhBiasHistory* biasHistory) const
318 {
319     GMX_RELEASE_ASSERT(biasHistory != nullptr, "Need a valid biasHistory");
320
321     state_.updateHistory(biasHistory, grid_);
322
323     if (forceCorrelationGrid_ != nullptr)
324     {
325         updateCorrelationGridHistory(&biasHistory->forceCorrelationGrid, forceCorrelationGrid());
326     }
327 }
328
329 Bias::Bias(int                            biasIndexInCollection,
330            const AwhParams&               awhParams,
331            const AwhBiasParams&           awhBiasParams,
332            const std::vector<DimParams>&  dimParamsInit,
333            double                         beta,
334            double                         mdTimeStep,
335            int                            numSharingSimulations,
336            const std::string&             biasInitFilename,
337            ThisRankWillDoIO               thisRankWillDoIO,
338            BiasParams::DisableUpdateSkips disableUpdateSkips) :
339     dimParams_(dimParamsInit),
340     grid_(dimParamsInit, awhBiasParams.dimParams),
341     params_(awhParams,
342             awhBiasParams,
343             dimParams_,
344             beta,
345             mdTimeStep,
346             disableUpdateSkips,
347             numSharingSimulations,
348             grid_.axis(),
349             biasIndexInCollection),
350     state_(awhBiasParams, params_.initialHistogramSize, dimParams_, grid_),
351     thisRankDoesIO_(thisRankWillDoIO == ThisRankWillDoIO::Yes),
352     biasForce_(ndim()),
353
354     tempForce_(ndim()),
355     numWarningsIssued_(0)
356 {
357     /* For a global update updateList covers all points, so reserve that */
358     updateList_.reserve(grid_.numPoints());
359
360     state_.initGridPointState(awhBiasParams, dimParams_, grid_, params_, biasInitFilename, awhParams.numBias);
361
362     if (thisRankDoesIO_)
363     {
364         /* Set up the force correlation object. */
365
366         /* We let the correlation init function set its parameters
367          * to something useful for now.
368          */
369         double blockLength = 0;
370         /* Construct the force correlation object. */
371         forceCorrelationGrid_ = std::make_unique<CorrelationGrid>(
372                 state_.points().size(), ndim(), blockLength,
373                 CorrelationGrid::BlockLengthMeasure::Time, awhParams.nstSampleCoord * mdTimeStep);
374
375         writer_ = std::make_unique<BiasWriter>(*this);
376     }
377 }
378
379 void Bias::printInitializationToLog(FILE* fplog) const
380 {
381     if (fplog != nullptr && forceCorrelationGrid_ != nullptr)
382     {
383         std::string prefix = gmx::formatString("\nawh%d:", params_.biasIndex + 1);
384
385         fprintf(fplog,
386                 "%s initial force correlation block length = %g %s"
387                 "%s force correlation number of blocks = %d",
388                 prefix.c_str(), forceCorrelationGrid().getBlockLength(),
389                 forceCorrelationGrid().blockLengthMeasure == CorrelationGrid::BlockLengthMeasure::Weight
390                         ? ""
391                         : "ps",
392                 prefix.c_str(), forceCorrelationGrid().getNumBlocks());
393     }
394 }
395
396 void Bias::updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor,
397                                       ArrayRef<const double>      neighborLambdaDhdl,
398                                       double                      t)
399 {
400     if (forceCorrelationGrid_ == nullptr)
401     {
402         return;
403     }
404
405     const std::vector<int>& neighbor = grid_.point(state_.coordState().gridpointIndex()).neighbor;
406
407     gmx::ArrayRef<double> forceFromNeighbor = tempForce_;
408     for (size_t n = 0; n < neighbor.size(); n++)
409     {
410         double weightNeighbor = probWeightNeighbor[n];
411         int    indexNeighbor  = neighbor[n];
412
413         /* Add the force data of this neighbor point. Note: the sum of these forces is the convolved force.
414
415            We actually add the force normalized by beta which has the units of 1/length. This means that the
416            resulting correlation time integral is directly in units of friction time/length^2 which is really what
417            we're interested in. */
418         state_.calcUmbrellaForceAndPotential(dimParams_, grid_, indexNeighbor, neighborLambdaDhdl,
419                                              forceFromNeighbor);
420
421         /* Note: we might want to give a whole list of data to add instead and have this loop in the data adding function */
422         forceCorrelationGrid_->addData(indexNeighbor, weightNeighbor, forceFromNeighbor, t);
423     }
424 }
425
426 /* Return the number of data blocks that have been prepared for writing. */
427 int Bias::numEnergySubblocksToWrite() const
428 {
429     GMX_RELEASE_ASSERT(writer_ != nullptr, "Should only request data from an initialized writer");
430
431     return writer_->numBlocks();
432 }
433
434 /* Write bias data blocks to energy subblocks. */
435 int Bias::writeToEnergySubblocks(t_enxsubblock* subblock) const
436 {
437     GMX_RELEASE_ASSERT(writer_ != nullptr, "Should only request data from an initialized writer");
438
439     return writer_->writeToEnergySubblocks(*this, subblock);
440 }
441
442 bool Bias::isSampleCoordStep(const int64_t step) const
443 {
444     return params_.isSampleCoordStep(step);
445 }
446
447
448 } // namespace gmx