2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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.
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.
37 * Implements QMMM class that implements IMDModule interface
39 * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
40 * \author Christian Blau <blau@kth.se>
41 * \ingroup module_applied_forces
50 #include "gromacs/domdec/localatomsetmanager.h"
51 #include "gromacs/ewald/pme.h"
52 #include "gromacs/fileio/checkpoint.h"
53 #include "gromacs/math/multidimarray.h"
54 #include "gromacs/mdlib/broadcaststructs.h"
55 #include "gromacs/mdtypes/commrec.h"
56 #include "gromacs/mdtypes/imdmodule.h"
57 #include "gromacs/mdtypes/imdoutputprovider.h"
58 #include "gromacs/utility/classhelpers.h"
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/keyvaluetreebuilder.h"
61 #include "gromacs/utility/logger.h"
62 #include "gromacs/utility/mdmodulesnotifiers.h"
64 #include "qmmmforceprovider.h"
65 #include "qmmmoptions.h"
74 * \brief Helper class that holds simulation data and
75 * callback functions for simulation setup time notifications
77 class QMMMSimulationParameterSetup
80 QMMMSimulationParameterSetup() = default;
82 /*! \brief Set the local atom set for the QM atoms.
83 * \param[in] localAtomSet of QM atoms
85 void setLocalQMAtomSet(const LocalAtomSet& localAtomSet)
87 localQMAtomSet_ = std::make_unique<LocalAtomSet>(localAtomSet);
90 /*! \brief Set the local atom set for the MM atoms.
91 * \param[in] localAtomSet of MM atoms
93 void setLocalMMAtomSet(const LocalAtomSet& localAtomSet)
95 localMMAtomSet_ = std::make_unique<LocalAtomSet>(localAtomSet);
98 /*! \brief Return local atom set for QM atoms.
99 * \throws InternalError if local atom set is not set
100 * \returns local atom set for QM atoms
102 const LocalAtomSet& localQMAtomSet() const
104 if (localQMAtomSet_ == nullptr)
106 GMX_THROW(InternalError("Local atom set is not set for QM atoms."));
108 return *localQMAtomSet_;
111 /*! \brief Return local atom set for MM atoms.
112 * \throws InternalError if local atom set is not set
113 * \returns local atom set for MM atoms
115 const LocalAtomSet& localMMAtomSet() const
117 if (localMMAtomSet_ == nullptr)
119 GMX_THROW(InternalError("Local atom set is not set for MM atoms."));
121 return *localMMAtomSet_;
124 /*! \brief Set the periodic boundary condition via MdModuleNotifier.
126 * The pbc type is wrapped in PeriodicBoundaryConditionType to
127 * allow the MdModuleNotifier to statically distinguish the callback
128 * function type from other 'int' function callbacks.
130 * \param[in] pbc MdModuleNotification class that contains a variable
131 * that enumerates the periodic boundary condition.
133 void setPeriodicBoundaryConditionType(const PbcType& pbc)
135 pbcType_ = std::make_unique<PbcType>(pbc);
138 //! Get the periodic boundary conditions
139 PbcType periodicBoundaryConditionType()
141 if (pbcType_ == nullptr)
144 InternalError("Periodic boundary condition enum not set for QMMM simulation."));
149 /*! \brief Set the logger for QMMM during mdrun
150 * \param[in] logger Logger instance to be used for output
152 void setLogger(const MDLogger& logger) { logger_ = &logger; }
154 //! Get the logger instance
155 const MDLogger& logger() const
157 if (logger_ == nullptr)
159 GMX_THROW(InternalError("Logger not set for QMMM simulation."));
165 //! The local QM atom set to act on
166 std::unique_ptr<LocalAtomSet> localQMAtomSet_;
167 //! The local MM atom set to act on
168 std::unique_ptr<LocalAtomSet> localMMAtomSet_;
169 //! The type of periodic boundary conditions in the simulation
170 std::unique_ptr<PbcType> pbcType_;
171 //! MDLogger for notifications during mdrun
172 const MDLogger* logger_ = nullptr;
174 GMX_DISALLOW_COPY_AND_ASSIGN(QMMMSimulationParameterSetup);
179 * \brief Handle file output for QMMM simulations.
180 * empty implementation as QMMM does not use that
182 class QMMMOutputProvider final : public IMDOutputProvider
185 //! Initialize output
186 void initOutput(FILE* /*fplog*/,
188 const t_filenm /*fnm*/[],
189 bool /*bAppendFiles*/,
190 const gmx_output_env_t* /*oenv*/) override
193 //! Finalizes output from a simulation run.
194 void finishOutput() override {}
201 * Class that implements the QMMM forces and potential
202 * \note the virial calculation is not yet implemented
204 class QMMM final : public IMDModule
207 //! \brief Construct the QMMM module.
208 explicit QMMM() = default;
210 // Now callbacks for several kinds of MdModuleNotification are created
211 // and subscribed, and will be dispatched correctly at run time
212 // based on the type of the parameter required by the lambda.
214 /*! \brief Requests to be notified during pre-processing.
216 * \param[in] notifier allows the module to subscribe to notifications from MdModules.
218 * The QMMM code subscribes to these notifications:
219 * - setting atom group indices in the qmmmOptions_ from an
220 * index group string by taking a parmeter const IndexGroupsAndNames &
221 * - storing its internal parameters in a tpr file by writing to a
222 * key-value-tree during pre-processing by a function taking a
223 * KeyValueTreeObjectBuilder as parameter
224 * - Modify topology according to QMMM rules using gmx_mtop_t notification
225 * and utilizing QMMMTopologyPreprocessor class
226 * - Access MDLogger for notifications output
227 * - Access warninp for for grompp warnings output
228 * - Coordinates, PBC and box for CP2K input generation
229 * - QM Input file provided with -qmi option of grompp
231 void subscribeToPreProcessingNotifications(MDModulesNotifiers* notifier) override
233 if (!qmmmOptions_.active())
238 // Writing internal parameters during pre-processing
239 const auto writeInternalParametersFunction = [this](KeyValueTreeObjectBuilder treeBuilder) {
240 qmmmOptions_.writeInternalParametersToKvt(treeBuilder);
242 notifier->preProcessingNotifier_.subscribe(writeInternalParametersFunction);
244 // Setting atom group indices
245 const auto setQMMMGroupIndicesFunction = [this](const IndexGroupsAndNames& indexGroupsAndNames) {
246 qmmmOptions_.setQMMMGroupIndices(indexGroupsAndNames);
248 notifier->preProcessingNotifier_.subscribe(setQMMMGroupIndicesFunction);
250 // Set Logger during pre-processing
251 const auto setLoggerFunction = [this](const MDLogger& logger) {
252 qmmmOptions_.setLogger(logger);
254 notifier->preProcessingNotifier_.subscribe(setLoggerFunction);
256 // Set warning output during pre-processing
257 const auto setWarninpFunction = [this](warninp* wi) { qmmmOptions_.setWarninp(wi); };
258 notifier->preProcessingNotifier_.subscribe(setWarninpFunction);
260 // Notification of the Coordinates, box and pbc during pre-processing
261 const auto processCoordinatesFunction = [this](const CoordinatesAndBoxPreprocessed& coord) {
262 qmmmOptions_.processCoordinates(coord);
264 notifier->preProcessingNotifier_.subscribe(processCoordinatesFunction);
266 // Modification of the topology during pre-processing
267 const auto modifyQMMMTopologyFunction = [this](gmx_mtop_t* mtop) {
268 qmmmOptions_.modifyQMMMTopology(mtop);
270 notifier->preProcessingNotifier_.subscribe(modifyQMMMTopologyFunction);
272 // Notification of the QM input file provided via -qmi option of grompp
273 const auto setQMExternalInputFileNameFunction = [this](const QMInputFileName& qmInputFileName) {
274 qmmmOptions_.setQMExternalInputFile(qmInputFileName);
276 notifier->preProcessingNotifier_.subscribe(setQMExternalInputFileNameFunction);
279 /*! \brief Requests to be notified during simulation setup.
280 * The QMMM code subscribes to these notifications:
281 * - reading its internal parameters from a key-value-tree during
282 * simulation setup by taking a const KeyValueTreeObject & parameter
283 * - *.tpr filename for CP2K input generation
284 * - constructing local atom sets in the simulation parameter setup
285 * by taking a LocalAtomSetManager * as parameter
286 * - the type of periodic boundary conditions that are used
287 * by taking a PeriodicBoundaryConditionType as parameter
288 * - Access MDLogger for notifications output
289 * - Disable PME-only ranks for QMMM runs
290 * - Request QM energy output to md.log
292 void subscribeToSimulationSetupNotifications(MDModulesNotifiers* notifier) override
294 if (!qmmmOptions_.active())
299 // Reading internal parameters during simulation setup
300 const auto readInternalParametersFunction = [this](const KeyValueTreeObject& tree) {
301 qmmmOptions_.readInternalParametersFromKvt(tree);
303 notifier->simulationSetupNotifier_.subscribe(readInternalParametersFunction);
305 // Process tpr filename
306 const auto setTprFileNameFunction = [this](const MdRunInputFilename& tprName) {
307 qmmmOptions_.processTprFilename(tprName);
309 notifier->simulationSetupNotifier_.subscribe(setTprFileNameFunction);
311 // constructing local atom sets during simulation setup
312 const auto setLocalAtomSetFunction = [this](LocalAtomSetManager* localAtomSetManager) {
313 LocalAtomSet atomSet1 = localAtomSetManager->add(qmmmOptions_.parameters().qmIndices_);
314 this->qmmmSimulationParameters_.setLocalQMAtomSet(atomSet1);
315 LocalAtomSet atomSet2 = localAtomSetManager->add(qmmmOptions_.parameters().mmIndices_);
316 this->qmmmSimulationParameters_.setLocalMMAtomSet(atomSet2);
318 notifier->simulationSetupNotifier_.subscribe(setLocalAtomSetFunction);
320 // Reading PBC parameters during simulation setup
321 const auto setPeriodicBoundaryContionsFunction = [this](const PbcType& pbc) {
322 this->qmmmSimulationParameters_.setPeriodicBoundaryConditionType(pbc);
324 notifier->simulationSetupNotifier_.subscribe(setPeriodicBoundaryContionsFunction);
326 // Saving MDLogger during simulation setup
327 const auto setLoggerFunction = [this](const MDLogger& logger) {
328 this->qmmmSimulationParameters_.setLogger(logger);
330 notifier->simulationSetupNotifier_.subscribe(setLoggerFunction);
332 // Adding output to energy file
333 const auto requestEnergyOutput = [](MDModulesEnergyOutputToQMMMRequestChecker* energyOutputRequest) {
334 energyOutputRequest->energyOutputToQMMM_ = true;
336 notifier->simulationSetupNotifier_.subscribe(requestEnergyOutput);
338 // Request to disable PME-only ranks, which are not compatible with CP2K
339 const auto requestPmeRanks = [](SeparatePmeRanksPermitted* pmeRanksPermitted) {
340 pmeRanksPermitted->disablePmeRanks(
341 "Separate PME-only ranks are not compatible with QMMM MdModule");
343 notifier->simulationSetupNotifier_.subscribe(requestPmeRanks);
347 IMdpOptionProvider* mdpOptionProvider() override { return &qmmmOptions_; }
349 //! Add this module to the force providers if active
350 void initForceProviders(ForceProviders* forceProviders) override
352 if (!qmmmOptions_.active())
357 const auto& parameters = qmmmOptions_.parameters();
358 forceProvider_ = std::make_unique<QMMMForceProvider>(
360 qmmmSimulationParameters_.localQMAtomSet(),
361 qmmmSimulationParameters_.localMMAtomSet(),
362 qmmmSimulationParameters_.periodicBoundaryConditionType(),
363 qmmmSimulationParameters_.logger());
364 forceProviders->addForceProvider(forceProvider_.get());
367 //! QMMM Module should not use OutputProvider as it will be removed in the furture
368 IMDOutputProvider* outputProvider() override { return &qmmmOutputProvider_; }
371 //! The output provider
372 QMMMOutputProvider qmmmOutputProvider_;
373 //! The options provided for QMMM
374 QMMMOptions qmmmOptions_;
375 //! Object that evaluates the forces
376 std::unique_ptr<QMMMForceProvider> forceProvider_;
377 /*! \brief Parameters for QMMM that become available at
378 * simulation setup time.
380 QMMMSimulationParameterSetup qmmmSimulationParameters_;
382 GMX_DISALLOW_COPY_AND_ASSIGN(QMMM);
387 std::unique_ptr<IMDModule> QMMMModuleInfo::create()
389 return std::make_unique<QMMM>();
392 const std::string QMMMModuleInfo::name_ = c_qmmmCP2KModuleName;