2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2015,2016,2017, 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.
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.
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.
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.
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.
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.
38 * Implements the Awh class.
40 * \author Viveca Lindahl
41 * \author Berk Hess <hess@kth.se>
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"
76 #include "biassharing.h"
77 #include "pointstate.h"
83 * \brief A bias and its coupling to the system.
85 * This struct is used to separate the bias machinery in the Bias class,
86 * which should be independent from the reaction coordinate, from the
87 * obtaining of the reaction coordinate values and passing the computed forces.
88 * Currently the AWH method couples to the system by mapping each
89 * AWH bias to a pull coordinate. This can easily be generalized here.
91 struct BiasCoupledToSystem
93 /*! \brief Constructor, couple a bias to a set of pull coordinates.
95 * \param[in] bias The bias.
96 * \param[in] pullCoordIndex The pull coordinate indices.
98 BiasCoupledToSystem(Bias bias
,
99 const std::vector
<int> &pullCoordIndex
);
101 Bias bias
; /**< The bias. */
102 const std::vector
<int> pullCoordIndex
; /**< The pull coordinates this bias acts on. */
104 /* Here AWH can be extended to work on other coordinates than pull. */
107 BiasCoupledToSystem::BiasCoupledToSystem(Bias bias
,
108 const std::vector
<int> &pullCoordIndex
) :
109 bias(std::move(bias
)),
110 pullCoordIndex(pullCoordIndex
)
112 /* We already checked for this in grompp, but check again here. */
113 GMX_RELEASE_ASSERT(static_cast<size_t>(bias
.ndim()) == pullCoordIndex
.size(), "The bias dimensionality should match the number of pull coordinates.");
116 Awh::Awh(FILE *fplog
,
117 const t_inputrec
&inputRecord
,
118 const t_commrec
*commRecord
,
119 const AwhParams
&awhParams
,
120 const std::string
&biasInitFilename
,
122 seed_(awhParams
.seed
),
123 nstout_(awhParams
.nstOut
),
124 commRecord_(commRecord
),
128 /* We already checked for this in grompp, but check again here. */
129 GMX_RELEASE_ASSERT(inputRecord
.pull
!= nullptr, "With AWH we should have pull parameters");
130 GMX_RELEASE_ASSERT(pull_work
!= nullptr, "With AWH pull should be initialized before initializing AWH");
132 if (fplog
!= nullptr)
134 please_cite(fplog
, "Lindahl2014");
137 if (haveBiasSharingWithinSimulation(awhParams
))
139 /* This has likely been checked by grompp, but throw anyhow. */
140 GMX_THROW(InvalidInputError("Biases within a simulation are shared, currently sharing of biases is only supported between simulations"));
143 int numSharingSimulations
= 1;
144 if (awhParams
.shareBiasMultisim
&& MULTISIM(commRecord_
))
146 numSharingSimulations
= commRecord_
->ms
->nsim
;
149 /* Initialize all the biases */
150 const double beta
= 1/(BOLTZ
*inputRecord
.opts
.ref_t
[0]);
151 for (int k
= 0; k
< awhParams
.numBias
; k
++)
153 const AwhBiasParams
&awhBiasParams
= awhParams
.awhBiasParams
[k
];
155 std::vector
<int> pullCoordIndex
;
156 std::vector
<DimParams
> dimParams
;
157 for (int d
= 0; d
< awhBiasParams
.ndim
; d
++)
159 const AwhDimParams
&awhDimParams
= awhBiasParams
.dimParams
[d
];
160 GMX_RELEASE_ASSERT(awhDimParams
.eCoordProvider
== eawhcoordproviderPULL
, "Currently only the pull code is supported as coordinate provider");
161 const t_pull_coord
&pullCoord
= inputRecord
.pull
->coord
[awhDimParams
.coordIndex
];
162 double conversionFactor
= pull_coordinate_is_angletype(&pullCoord
) ? DEG2RAD
: 1;
163 dimParams
.push_back(DimParams(conversionFactor
, awhDimParams
.forceConstant
, beta
));
165 pullCoordIndex
.push_back(awhDimParams
.coordIndex
);
168 /* Construct the bias and couple it to the system. */
169 Bias::ThisRankWillDoIO thisRankWillDoIO
= (MASTER(commRecord_
) ? Bias::ThisRankWillDoIO::Yes
: Bias::ThisRankWillDoIO::No
);
170 biasCoupledToSystem_
.emplace_back(Bias(k
, awhParams
, awhParams
.awhBiasParams
[k
], dimParams
, beta
, inputRecord
.delta_t
, numSharingSimulations
, biasInitFilename
, thisRankWillDoIO
),
174 /* Need to register the AWH coordinates to be allowed to apply forces to the pull coordinates. */
175 registerAwhWithPull(awhParams
, pull_
);
177 if (numSharingSimulations
> 1 && MASTER(commRecord_
))
179 std::vector
<size_t> pointSize
;
180 for (auto const &biasCts
: biasCoupledToSystem_
)
182 pointSize
.push_back(biasCts
.bias
.state().points().size());
184 /* Ensure that the shared biased are compatible between simulations */
185 biasesAreCompatibleForSharingBetweenSimulations(awhParams
, pointSize
, commRecord_
->ms
);
189 Awh::~Awh() = default;
191 bool Awh::isOutputStep(gmx_int64_t step
) const
193 return (nstout_
> 0 && step
% nstout_
== 0);
196 real
Awh::applyBiasForcesAndUpdateBias(int ePBC
,
197 const t_mdatoms
&mdatoms
,
199 gmx::ForceWithVirial
*forceWithVirial
,
202 gmx_wallcycle
*wallcycle
,
205 GMX_ASSERT(forceWithVirial
, "Need a valid ForceWithVirial object");
207 wallcycle_start(wallcycle
, ewcAWH
);
210 set_pbc(&pbc
, ePBC
, box
);
212 /* During the AWH update the potential can instantaneously jump due to either
213 an bias update or moving the umbrella. The jumps are kept track of and
214 subtracted from the potential in order to get a useful conserved energy quantity. */
215 double awhPotential
= potentialOffset_
;
217 for (auto &biasCts
: biasCoupledToSystem_
)
219 /* Update the AWH coordinate values with those of the corresponding
222 awh_dvec coordValue
= { 0, 0, 0, 0 };
223 for (int d
= 0; d
< biasCts
.bias
.ndim(); d
++)
225 coordValue
[d
] = get_pull_coord_value(pull_
, biasCts
.pullCoordIndex
[d
], &pbc
);
228 /* Perform an AWH biasing step: this means, at regular intervals,
229 * sampling observables based on the input pull coordinate value,
230 * setting the bias force and/or updating the AWH bias state.
233 double biasPotential
;
234 double biasPotentialJump
;
235 /* Note: In the near future this call will be split in calls
236 * to supports bias sharing within a single simulation.
238 biasCts
.bias
.calcForceAndUpdateBias(coordValue
, biasForce
,
239 &biasPotential
, &biasPotentialJump
,
241 t
, step
, seed_
, fplog
);
243 awhPotential
+= biasPotential
;
245 /* Keep track of the total potential shift needed to remove the potential jumps. */
246 potentialOffset_
-= biasPotentialJump
;
248 /* Communicate the bias force to the pull struct.
249 * The bias potential is returned at the end of this function,
250 * so that it can be added externally to the correct energy data block.
252 for (int d
= 0; d
< biasCts
.bias
.ndim(); d
++)
254 apply_external_pull_coord_force(pull_
, biasCts
.pullCoordIndex
[d
],
255 biasForce
[d
], &mdatoms
,
259 if (isOutputStep(step
))
261 /* Ensure that we output fully updated data */
262 biasCts
.bias
.doSkippedUpdatesForAllPoints();
266 wallcycle_stop(wallcycle
, ewcAWH
);
268 return MASTER(commRecord_
) ? static_cast<real
>(awhPotential
) : 0;
271 std::shared_ptr
<AwhHistory
> Awh::initHistoryFromState() const
273 if (MASTER(commRecord_
))
275 std::shared_ptr
<AwhHistory
> awhHistory(new AwhHistory
);
276 awhHistory
->bias
.clear();
277 awhHistory
->bias
.resize(biasCoupledToSystem_
.size());
279 for (size_t k
= 0; k
< awhHistory
->bias
.size(); k
++)
281 biasCoupledToSystem_
[k
].bias
.initHistoryFromState(&awhHistory
->bias
[k
]);
288 /* Return an empty pointer */
289 return std::shared_ptr
<AwhHistory
>();
293 void Awh::restoreStateFromHistory(const AwhHistory
*awhHistory
)
295 /* Restore the history to the current state */
296 if (MASTER(commRecord_
))
298 GMX_RELEASE_ASSERT(awhHistory
!= nullptr, "The master rank should have a valid awhHistory when restoring the state from history.");
300 if (awhHistory
->bias
.size() != biasCoupledToSystem_
.size())
302 GMX_THROW(InvalidInputError("AWH state and history contain different numbers of biases. Likely you provided a checkpoint from a different simulation."));
305 potentialOffset_
= awhHistory
->potentialOffset
;
307 if (PAR(commRecord_
))
309 gmx_bcast(sizeof(potentialOffset_
), &potentialOffset_
, commRecord_
);
312 for (size_t k
= 0; k
< biasCoupledToSystem_
.size(); k
++)
314 biasCoupledToSystem_
[k
].bias
.restoreStateFromHistory(awhHistory
? &awhHistory
->bias
[k
] : nullptr, commRecord_
);
318 void Awh::updateHistory(AwhHistory
*awhHistory
) const
320 if (!MASTER(commRecord_
))
325 /* This assert will also catch a non-master rank calling this function. */
326 GMX_RELEASE_ASSERT(awhHistory
->bias
.size() == biasCoupledToSystem_
.size(), "AWH state and history bias count should match");
328 awhHistory
->potentialOffset
= potentialOffset_
;
330 for (size_t k
= 0; k
< awhHistory
->bias
.size(); k
++)
332 biasCoupledToSystem_
[k
].bias
.updateHistory(&awhHistory
->bias
[k
]);
336 const char * Awh::externalPotentialString()
341 void Awh::registerAwhWithPull(const AwhParams
&awhParams
,
344 GMX_RELEASE_ASSERT(pull_work
, "Need a valid pull object");
346 for (int k
= 0; k
< awhParams
.numBias
; k
++)
348 const AwhBiasParams
&biasParams
= awhParams
.awhBiasParams
[k
];
350 for (int d
= 0; d
< biasParams
.ndim
; d
++)
352 register_external_pull_potential(pull_work
, biasParams
.dimParams
[d
].coordIndex
, Awh::externalPotentialString());
357 /* Fill the AWH data block of an energy frame with data (if there is any). */
358 void Awh::writeToEnergyFrame(gmx_int64_t step
,
359 t_enxframe
*frame
) const
361 GMX_ASSERT(MASTER(commRecord_
), "writeToEnergyFrame should only be called on the master rank");
362 GMX_ASSERT(frame
!= nullptr, "Need a valid energy frame");
364 if (!isOutputStep(step
))
366 /* This is not an AWH output step, don't write any AWH data */
370 /* Get the total number of energy subblocks that AWH needs */
371 int numSubblocks
= 0;
372 for (auto &biasCoupledToSystem
: biasCoupledToSystem_
)
374 numSubblocks
+= biasCoupledToSystem
.bias
.numEnergySubblocksToWrite();
376 GMX_ASSERT(numSubblocks
> 0, "We should always have data to write");
378 /* Add 1 energy block */
379 add_blocks_enxframe(frame
, frame
->nblock
+ 1);
381 /* Take the block that was just added and set the number of subblocks. */
382 t_enxblock
*awhEnergyBlock
= &(frame
->block
[frame
->nblock
- 1]);
383 add_subblocks_enxblock(awhEnergyBlock
, numSubblocks
);
385 /* Claim it as an AWH block. */
386 awhEnergyBlock
->id
= enxAWH
;
388 /* Transfer AWH data blocks to energy sub blocks */
389 int energySubblockCount
= 0;
390 for (auto &biasCoupledToSystem
: biasCoupledToSystem_
)
392 energySubblockCount
+= biasCoupledToSystem
.bias
.writeToEnergySubblocks(&(awhEnergyBlock
->sub
[energySubblockCount
]));