Stop using custom FindOpenCL.cmake, per TODO
[alexxy/gromacs.git] / src / gromacs / awh / awh.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2016,2017,2018, 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 Awh 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 "awh.h"
48
49 #include <assert.h>
50
51 #include <cmath>
52 #include <cstdio>
53 #include <cstdlib>
54 #include <cstring>
55
56 #include <algorithm>
57
58 #include "gromacs/fileio/enxio.h"
59 #include "gromacs/gmxlib/network.h"
60 #include "gromacs/math/units.h"
61 #include "gromacs/mdtypes/awh-history.h"
62 #include "gromacs/mdtypes/awh-params.h"
63 #include "gromacs/mdtypes/commrec.h"
64 #include "gromacs/mdtypes/forceoutput.h"
65 #include "gromacs/mdtypes/inputrec.h"
66 #include "gromacs/mdtypes/pull-params.h"
67 #include "gromacs/mdtypes/state.h"
68 #include "gromacs/pbcutil/pbc.h"
69 #include "gromacs/pulling/pull.h"
70 #include "gromacs/timing/wallcycle.h"
71 #include "gromacs/utility/exceptions.h"
72 #include "gromacs/utility/gmxassert.h"
73 #include "gromacs/utility/pleasecite.h"
74
75 #include "bias.h"
76 #include "biassharing.h"
77 #include "correlationgrid.h"
78 #include "pointstate.h"
79
80 namespace gmx
81 {
82
83 /*! \internal
84  * \brief A bias and its coupling to the system.
85  *
86  * This struct is used to separate the bias machinery in the Bias class,
87  * which should be independent from the reaction coordinate, from the
88  * obtaining of the reaction coordinate values and passing the computed forces.
89  * Currently the AWH method couples to the system by mapping each
90  * AWH bias to a pull coordinate. This can easily be generalized here.
91  */
92 struct BiasCoupledToSystem
93 {
94     /*! \brief Constructor, couple a bias to a set of pull coordinates.
95      *
96      * \param[in] bias            The bias.
97      * \param[in] pullCoordIndex  The pull coordinate indices.
98      */
99     BiasCoupledToSystem(Bias                    bias,
100                         const std::vector<int> &pullCoordIndex);
101
102     Bias                   bias;           /**< The bias. */
103     const std::vector<int> pullCoordIndex; /**< The pull coordinates this bias acts on. */
104
105     /* Here AWH can be extended to work on other coordinates than pull. */
106 };
107
108 BiasCoupledToSystem::BiasCoupledToSystem(Bias                    bias,
109                                          const std::vector<int> &pullCoordIndex) :
110     bias(std::move(bias)),
111     pullCoordIndex(pullCoordIndex)
112 {
113     /* We already checked for this in grompp, but check again here. */
114     GMX_RELEASE_ASSERT(static_cast<size_t>(bias.ndim()) == pullCoordIndex.size(), "The bias dimensionality should match the number of pull coordinates.");
115 }
116
117 Awh::Awh(FILE                 *fplog,
118          const t_inputrec     &inputRecord,
119          const t_commrec      *commRecord,
120          const gmx_multisim_t *multiSimRecord,
121          const AwhParams      &awhParams,
122          const std::string    &biasInitFilename,
123          pull_t               *pull_work) :
124     seed_(awhParams.seed),
125     nstout_(awhParams.nstOut),
126     commRecord_(commRecord),
127     multiSimRecord_(multiSimRecord),
128     pull_(pull_work),
129     potentialOffset_(0)
130 {
131     /* We already checked for this in grompp, but check again here. */
132     GMX_RELEASE_ASSERT(inputRecord.pull != nullptr, "With AWH we should have pull parameters");
133     GMX_RELEASE_ASSERT(pull_work != nullptr, "With AWH pull should be initialized before initializing AWH");
134
135     if (fplog != nullptr)
136     {
137         please_cite(fplog, "Lindahl2014");
138     }
139
140     if (haveBiasSharingWithinSimulation(awhParams))
141     {
142         /* This has likely been checked by grompp, but throw anyhow. */
143         GMX_THROW(InvalidInputError("Biases within a simulation are shared, currently sharing of biases is only supported between simulations"));
144     }
145
146     int numSharingSimulations = 1;
147     if (awhParams.shareBiasMultisim && isMultiSim(multiSimRecord_))
148     {
149         numSharingSimulations = multiSimRecord_->nsim;
150     }
151
152     /* Initialize all the biases */
153     const double beta = 1/(BOLTZ*inputRecord.opts.ref_t[0]);
154     for (int k = 0; k < awhParams.numBias; k++)
155     {
156         const AwhBiasParams    &awhBiasParams = awhParams.awhBiasParams[k];
157
158         std::vector<int>        pullCoordIndex;
159         std::vector<DimParams>  dimParams;
160         for (int d = 0; d < awhBiasParams.ndim; d++)
161         {
162             const AwhDimParams &awhDimParams      = awhBiasParams.dimParams[d];
163             GMX_RELEASE_ASSERT(awhDimParams.eCoordProvider == eawhcoordproviderPULL, "Currently only the pull code is supported as coordinate provider");
164             const t_pull_coord &pullCoord         = inputRecord.pull->coord[awhDimParams.coordIndex];
165             double              conversionFactor  = pull_coordinate_is_angletype(&pullCoord) ? DEG2RAD : 1;
166             dimParams.push_back(DimParams(conversionFactor, awhDimParams.forceConstant, beta));
167
168             pullCoordIndex.push_back(awhDimParams.coordIndex);
169         }
170
171         /* Construct the bias and couple it to the system. */
172         Bias::ThisRankWillDoIO thisRankWillDoIO = (MASTER(commRecord_) ? Bias::ThisRankWillDoIO::Yes : Bias::ThisRankWillDoIO::No);
173         biasCoupledToSystem_.emplace_back(Bias(k, awhParams, awhParams.awhBiasParams[k], dimParams, beta, inputRecord.delta_t, numSharingSimulations, biasInitFilename, thisRankWillDoIO),
174                                           pullCoordIndex);
175
176         biasCoupledToSystem_.back().bias.printInitializationToLog(fplog);
177     }
178
179     /* Need to register the AWH coordinates to be allowed to apply forces to the pull coordinates. */
180     registerAwhWithPull(awhParams, pull_);
181
182     if (numSharingSimulations > 1 && MASTER(commRecord_))
183     {
184         std::vector<size_t> pointSize;
185         for (auto const &biasCts : biasCoupledToSystem_)
186         {
187             pointSize.push_back(biasCts.bias.state().points().size());
188         }
189         /* Ensure that the shared biased are compatible between simulations */
190         biasesAreCompatibleForSharingBetweenSimulations(awhParams, pointSize, multiSimRecord_);
191     }
192 }
193
194 Awh::~Awh() = default;
195
196 bool Awh::isOutputStep(gmx_int64_t step) const
197 {
198     return (nstout_ > 0 && step % nstout_ == 0);
199 }
200
201 real Awh::applyBiasForcesAndUpdateBias(int                     ePBC,
202                                        const t_mdatoms        &mdatoms,
203                                        const matrix            box,
204                                        gmx::ForceWithVirial   *forceWithVirial,
205                                        double                  t,
206                                        gmx_int64_t             step,
207                                        gmx_wallcycle          *wallcycle,
208                                        FILE                   *fplog)
209 {
210     GMX_ASSERT(forceWithVirial, "Need a valid ForceWithVirial object");
211
212     wallcycle_start(wallcycle, ewcAWH);
213
214     t_pbc  pbc;
215     set_pbc(&pbc, ePBC, box);
216
217     /* During the AWH update the potential can instantaneously jump due to either
218        an bias update or moving the umbrella. The jumps are kept track of and
219        subtracted from the potential in order to get a useful conserved energy quantity. */
220     double awhPotential = potentialOffset_;
221
222     for (auto &biasCts : biasCoupledToSystem_)
223     {
224         /* Update the AWH coordinate values with those of the corresponding
225          * pull coordinates.
226          */
227         awh_dvec coordValue = { 0, 0, 0, 0 };
228         for (int d = 0; d < biasCts.bias.ndim(); d++)
229         {
230             coordValue[d] = get_pull_coord_value(pull_, biasCts.pullCoordIndex[d], &pbc);
231         }
232
233         /* Perform an AWH biasing step: this means, at regular intervals,
234          * sampling observables based on the input pull coordinate value,
235          * setting the bias force and/or updating the AWH bias state.
236          */
237         double              biasPotential;
238         double              biasPotentialJump;
239         /* Note: In the near future this call will be split in calls
240          *       to supports bias sharing within a single simulation.
241          */
242         gmx::ArrayRef<const double> biasForce =
243             biasCts.bias.calcForceAndUpdateBias(coordValue,
244                                                 &biasPotential, &biasPotentialJump,
245                                                 multiSimRecord_,
246                                                 t, step, seed_, fplog);
247
248         awhPotential += biasPotential;
249
250         /* Keep track of the total potential shift needed to remove the potential jumps. */
251         potentialOffset_ -= biasPotentialJump;
252
253         /* Communicate the bias force to the pull struct.
254          * The bias potential is returned at the end of this function,
255          * so that it can be added externally to the correct energy data block.
256          */
257         for (int d = 0; d < biasCts.bias.ndim(); d++)
258         {
259             apply_external_pull_coord_force(pull_, biasCts.pullCoordIndex[d],
260                                             biasForce[d], &mdatoms,
261                                             forceWithVirial);
262         }
263
264         if (isOutputStep(step))
265         {
266             /* We might have skipped updates for part of the grid points.
267              * Ensure all points are updated before writing out their data.
268              */
269             biasCts.bias.doSkippedUpdatesForAllPoints();
270         }
271     }
272
273     wallcycle_stop(wallcycle, ewcAWH);
274
275     return MASTER(commRecord_) ? static_cast<real>(awhPotential) : 0;
276 }
277
278 std::shared_ptr<AwhHistory> Awh::initHistoryFromState() const
279 {
280     if (MASTER(commRecord_))
281     {
282         std::shared_ptr<AwhHistory> awhHistory(new AwhHistory);
283         awhHistory->bias.clear();
284         awhHistory->bias.resize(biasCoupledToSystem_.size());
285
286         for (size_t k = 0; k < awhHistory->bias.size(); k++)
287         {
288             biasCoupledToSystem_[k].bias.initHistoryFromState(&awhHistory->bias[k]);
289         }
290
291         return awhHistory;
292     }
293     else
294     {
295         /* Return an empty pointer */
296         return std::shared_ptr<AwhHistory>();
297     }
298 }
299
300 void Awh::restoreStateFromHistory(const AwhHistory *awhHistory)
301 {
302     /* Restore the history to the current state */
303     if (MASTER(commRecord_))
304     {
305         GMX_RELEASE_ASSERT(awhHistory != nullptr, "The master rank should have a valid awhHistory when restoring the state from history.");
306
307         if (awhHistory->bias.size() != biasCoupledToSystem_.size())
308         {
309             GMX_THROW(InvalidInputError("AWH state and history contain different numbers of biases. Likely you provided a checkpoint from a different simulation."));
310         }
311
312         potentialOffset_ = awhHistory->potentialOffset;
313     }
314     if (PAR(commRecord_))
315     {
316         gmx_bcast(sizeof(potentialOffset_), &potentialOffset_, commRecord_);
317     }
318
319     for (size_t k = 0; k < biasCoupledToSystem_.size(); k++)
320     {
321         biasCoupledToSystem_[k].bias.restoreStateFromHistory(awhHistory ? &awhHistory->bias[k] : nullptr, commRecord_);
322     }
323 }
324
325 void Awh::updateHistory(AwhHistory *awhHistory) const
326 {
327     if (!MASTER(commRecord_))
328     {
329         return;
330     }
331
332     /* This assert will also catch a non-master rank calling this function. */
333     GMX_RELEASE_ASSERT(awhHistory->bias.size() == biasCoupledToSystem_.size(), "AWH state and history bias count should match");
334
335     awhHistory->potentialOffset = potentialOffset_;
336
337     for (size_t k = 0; k < awhHistory->bias.size(); k++)
338     {
339         biasCoupledToSystem_[k].bias.updateHistory(&awhHistory->bias[k]);
340     }
341 }
342
343 const char * Awh::externalPotentialString()
344 {
345     return "AWH";
346 }
347
348 void Awh::registerAwhWithPull(const AwhParams &awhParams,
349                               pull_t          *pull_work)
350 {
351     GMX_RELEASE_ASSERT(pull_work, "Need a valid pull object");
352
353     for (int k = 0; k < awhParams.numBias; k++)
354     {
355         const AwhBiasParams &biasParams = awhParams.awhBiasParams[k];
356
357         for (int d = 0; d < biasParams.ndim; d++)
358         {
359             register_external_pull_potential(pull_work, biasParams.dimParams[d].coordIndex, Awh::externalPotentialString());
360         }
361     }
362 }
363
364 /* Fill the AWH data block of an energy frame with data (if there is any). */
365 void Awh::writeToEnergyFrame(gmx_int64_t  step,
366                              t_enxframe  *frame) const
367 {
368     GMX_ASSERT(MASTER(commRecord_), "writeToEnergyFrame should only be called on the master rank");
369     GMX_ASSERT(frame != nullptr, "Need a valid energy frame");
370
371     if (!isOutputStep(step))
372     {
373         /* This is not an AWH output step, don't write any AWH data */
374         return;
375     }
376
377     /* Get the total number of energy subblocks that AWH needs */
378     int numSubblocks  = 0;
379     for (auto &biasCoupledToSystem : biasCoupledToSystem_)
380     {
381         numSubblocks += biasCoupledToSystem.bias.numEnergySubblocksToWrite();
382     }
383     GMX_ASSERT(numSubblocks > 0, "We should always have data to write");
384
385     /* Add 1 energy block */
386     add_blocks_enxframe(frame, frame->nblock + 1);
387
388     /* Take the block that was just added and set the number of subblocks. */
389     t_enxblock *awhEnergyBlock = &(frame->block[frame->nblock - 1]);
390     add_subblocks_enxblock(awhEnergyBlock, numSubblocks);
391
392     /* Claim it as an AWH block. */
393     awhEnergyBlock->id = enxAWH;
394
395     /* Transfer AWH data blocks to energy sub blocks */
396     int energySubblockCount = 0;
397     for (auto &biasCoupledToSystem : biasCoupledToSystem_)
398     {
399         energySubblockCount += biasCoupledToSystem.bias.writeToEnergySubblocks(&(awhEnergyBlock->sub[energySubblockCount]));
400     }
401 }
402
403 } // namespace gmx