2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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.
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.
36 * \brief Defines the state for the modular simulator
38 * \author Pascal Merz <pascal.merz@me.com>
39 * \ingroup module_modularsimulator
44 #include "statepropagatordata.h"
46 #include "gromacs/domdec/collect.h"
47 #include "gromacs/domdec/domdec.h"
48 #include "gromacs/fileio/confio.h"
49 #include "gromacs/math/vec.h"
50 #include "gromacs/mdlib/gmx_omp_nthreads.h"
51 #include "gromacs/mdlib/mdoutf.h"
52 #include "gromacs/mdlib/stat.h"
53 #include "gromacs/mdlib/update.h"
54 #include "gromacs/mdtypes/commrec.h"
55 #include "gromacs/mdtypes/inputrec.h"
56 #include "gromacs/mdtypes/mdatom.h"
57 #include "gromacs/mdtypes/state.h"
58 #include "gromacs/pbcutil/pbc.h"
59 #include "gromacs/topology/atoms.h"
60 #include "gromacs/topology/topology.h"
62 #include "freeenergyperturbationelement.h"
66 StatePropagatorData::StatePropagatorData(int numAtoms
,
73 int nstxout_compressed
,
75 FreeEnergyPerturbationElement
* freeEnergyPerturbationElement
,
76 const TopologyHolder
* topologyHolder
,
77 bool canMoleculesBeDistributedOverPBC
,
78 bool writeFinalConfiguration
,
79 std::string finalConfigurationFilename
,
80 const t_inputrec
* inputrec
,
81 const t_mdatoms
* mdatoms
) :
82 totalNumAtoms_(numAtoms
),
86 nstxout_compressed_(nstxout_compressed
),
90 vvResetVelocities_(false),
91 freeEnergyPerturbationElement_(freeEnergyPerturbationElement
),
92 isRegularSimulationEnd_(false),
94 canMoleculesBeDistributedOverPBC_(canMoleculesBeDistributedOverPBC
),
95 systemHasPeriodicMolecules_(inputrec
->bPeriodicMols
),
96 pbcType_(inputrec
->pbcType
),
97 topologyHolder_(topologyHolder
),
98 lastPlannedStep_(inputrec
->nsteps
+ inputrec
->init_step
),
99 writeFinalConfiguration_(writeFinalConfiguration
),
100 finalConfigurationFilename_(std::move(finalConfigurationFilename
)),
103 globalState_(globalState
)
105 // Initialize these here, as box_{{0}} in the initialization list
106 // is confusing uncrustify and doxygen
108 clear_mat(previousBox_
);
110 bool stateHasVelocities
;
111 // Local state only becomes valid now.
112 if (DOMAINDECOMP(cr
))
114 auto localState
= std::make_unique
<t_state
>();
115 dd_init_local_state(cr
->dd
, globalState
, localState
.get());
116 stateHasVelocities
= ((static_cast<unsigned int>(localState
->flags
) & (1U << estV
)) != 0U);
117 setLocalState(std::move(localState
));
121 state_change_natoms(globalState
, globalState
->natoms
);
122 f_
.resizeWithPadding(globalState
->natoms
);
123 localNAtoms_
= globalState
->natoms
;
126 copy_mat(globalState
->box
, box_
);
127 stateHasVelocities
= ((static_cast<unsigned int>(globalState
->flags
) & (1U << estV
)) != 0U);
128 previousX_
.resizeWithPadding(localNAtoms_
);
129 ddpCount_
= globalState
->ddp_count
;
134 changePinningPolicy(&x_
, gmx::PinningPolicy::PinnedIfSupported
);
137 if (!inputrec
->bContinuation
)
139 if (stateHasVelocities
)
141 auto v
= velocitiesView().paddedArrayRef();
142 // Set the velocities of vsites, shells and frozen atoms to zero
143 for (int i
= 0; i
< mdatoms
->homenr
; i
++)
145 if (mdatoms
->ptype
[i
] == eptVSite
|| mdatoms
->ptype
[i
] == eptShell
)
149 else if (mdatoms
->cFREEZE
)
151 for (int m
= 0; m
< DIM
; m
++)
153 if (inputrec
->opts
.nFreeze
[mdatoms
->cFREEZE
[i
]][m
])
161 if (inputrec
->eI
== eiVV
)
163 vvResetVelocities_
= true;
168 ArrayRefWithPadding
<RVec
> StatePropagatorData::positionsView()
170 return x_
.arrayRefWithPadding();
173 ArrayRefWithPadding
<const RVec
> StatePropagatorData::constPositionsView() const
175 return x_
.constArrayRefWithPadding();
178 ArrayRefWithPadding
<RVec
> StatePropagatorData::previousPositionsView()
180 return previousX_
.arrayRefWithPadding();
183 ArrayRefWithPadding
<const RVec
> StatePropagatorData::constPreviousPositionsView() const
185 return previousX_
.constArrayRefWithPadding();
188 ArrayRefWithPadding
<RVec
> StatePropagatorData::velocitiesView()
190 return v_
.arrayRefWithPadding();
193 ArrayRefWithPadding
<const RVec
> StatePropagatorData::constVelocitiesView() const
195 return v_
.constArrayRefWithPadding();
198 ArrayRefWithPadding
<RVec
> StatePropagatorData::forcesView()
200 return f_
.arrayRefWithPadding();
203 ArrayRefWithPadding
<const RVec
> StatePropagatorData::constForcesView() const
205 return f_
.constArrayRefWithPadding();
208 rvec
* StatePropagatorData::box()
213 const rvec
* StatePropagatorData::constBox() const
218 rvec
* StatePropagatorData::previousBox()
223 const rvec
* StatePropagatorData::constPreviousBox() const
228 int StatePropagatorData::localNumAtoms()
233 std::unique_ptr
<t_state
> StatePropagatorData::localState()
235 auto state
= std::make_unique
<t_state
>();
236 state
->flags
= (1U << estX
) | (1U << estV
) | (1U << estBOX
);
237 state_change_natoms(state
.get(), localNAtoms_
);
240 copy_mat(box_
, state
->box
);
241 state
->ddp_count
= ddpCount_
;
245 void StatePropagatorData::setLocalState(std::unique_ptr
<t_state
> state
)
247 localNAtoms_
= state
->natoms
;
248 x_
.resizeWithPadding(localNAtoms_
);
249 previousX_
.resizeWithPadding(localNAtoms_
);
250 v_
.resizeWithPadding(localNAtoms_
);
253 copy_mat(state
->box
, box_
);
255 ddpCount_
= state
->ddp_count
;
257 if (vvResetVelocities_
)
259 /* DomDec runs twice early in the simulation, once at setup time, and once before the first
260 * step. Every time DD runs, it sets a new local state here. We are saving a backup during
261 * setup time (ok for non-DD cases), so we need to update our backup to the DD state before
262 * the first step here to avoid resetting to an earlier DD state. This is done before any
263 * propagation that needs to be reset, so it's not very safe but correct for now.
264 * TODO: Get rid of this once input is assumed to be at half steps
266 velocityBackup_
= v_
;
270 t_state
* StatePropagatorData::globalState()
275 PaddedHostVector
<RVec
>* StatePropagatorData::forcePointer()
280 void StatePropagatorData::copyPosition()
282 int nth
= gmx_omp_nthreads_get(emntUpdate
);
284 #pragma omp parallel for num_threads(nth) schedule(static) default(none) shared(nth)
285 for (int th
= 0; th
< nth
; th
++)
287 int start_th
, end_th
;
288 getThreadAtomRange(nth
, th
, localNAtoms_
, &start_th
, &end_th
);
289 copyPosition(start_th
, end_th
);
292 /* Box is changed in update() when we do pressure coupling,
293 * but we should still use the old box for energy corrections and when
294 * writing it to the energy file, so it matches the trajectory files for
295 * the same timestep above. Make a copy in a separate array.
297 copy_mat(box_
, previousBox_
);
300 void StatePropagatorData::copyPosition(int start
, int end
)
302 for (int i
= start
; i
< end
; ++i
)
304 previousX_
[i
] = x_
[i
];
308 void StatePropagatorData::scheduleTask(Step step
, Time gmx_unused time
, const RegisterRunFunctionPtr
& registerRunFunction
)
310 if (vvResetVelocities_
)
312 vvResetVelocities_
= false;
313 (*registerRunFunction
)(std::make_unique
<SimulatorRunFunction
>([this]() { resetVelocities(); }));
315 // copy x -> previousX
316 (*registerRunFunction
)(std::make_unique
<SimulatorRunFunction
>([this]() { copyPosition(); }));
317 // if it's a write out step, keep a copy for writeout
318 if (step
== writeOutStep_
|| (step
== lastStep_
&& writeFinalConfiguration_
))
320 (*registerRunFunction
)(std::make_unique
<SimulatorRunFunction
>([this]() { saveState(); }));
324 void StatePropagatorData::saveState()
326 GMX_ASSERT(!localStateBackup_
, "Save state called again before previous state was written.");
327 localStateBackup_
= localState();
328 if (freeEnergyPerturbationElement_
)
330 localStateBackup_
->fep_state
= freeEnergyPerturbationElement_
->currentFEPState();
331 for (unsigned long i
= 0; i
< localStateBackup_
->lambda
.size(); ++i
)
333 localStateBackup_
->lambda
[i
] = freeEnergyPerturbationElement_
->constLambdaView()[i
];
335 localStateBackup_
->flags
|= (1U << estLAMBDA
) | (1U << estFEPSTATE
);
339 SignallerCallbackPtr
StatePropagatorData::registerTrajectorySignallerCallback(TrajectoryEvent event
)
341 if (event
== TrajectoryEvent::StateWritingStep
)
343 return std::make_unique
<SignallerCallback
>(
344 [this](Step step
, Time
/*unused*/) { this->writeOutStep_
= step
; });
349 ITrajectoryWriterCallbackPtr
StatePropagatorData::registerTrajectoryWriterCallback(TrajectoryEvent event
)
351 if (event
== TrajectoryEvent::StateWritingStep
)
353 return std::make_unique
<ITrajectoryWriterCallback
>(
354 [this](gmx_mdoutf
* outf
, Step step
, Time time
, bool writeTrajectory
, bool gmx_unused writeLog
) {
357 write(outf
, step
, time
);
364 void StatePropagatorData::write(gmx_mdoutf_t outf
, Step currentStep
, Time currentTime
)
366 wallcycle_start(mdoutf_get_wcycle(outf
), ewcTRAJ
);
367 unsigned int mdof_flags
= 0;
368 if (do_per_step(currentStep
, nstxout_
))
370 mdof_flags
|= MDOF_X
;
372 if (do_per_step(currentStep
, nstvout_
))
374 mdof_flags
|= MDOF_V
;
376 if (do_per_step(currentStep
, nstfout_
))
378 mdof_flags
|= MDOF_F
;
380 if (do_per_step(currentStep
, nstxout_compressed_
))
382 mdof_flags
|= MDOF_X_COMPRESSED
;
384 if (do_per_step(currentStep
, mdoutf_get_tng_box_output_interval(outf
)))
386 mdof_flags
|= MDOF_BOX
;
388 if (do_per_step(currentStep
, mdoutf_get_tng_lambda_output_interval(outf
)))
390 mdof_flags
|= MDOF_LAMBDA
;
392 if (do_per_step(currentStep
, mdoutf_get_tng_compressed_box_output_interval(outf
)))
394 mdof_flags
|= MDOF_BOX_COMPRESSED
;
396 if (do_per_step(currentStep
, mdoutf_get_tng_compressed_lambda_output_interval(outf
)))
398 mdof_flags
|= MDOF_LAMBDA_COMPRESSED
;
403 wallcycle_stop(mdoutf_get_wcycle(outf
), ewcTRAJ
);
406 GMX_ASSERT(localStateBackup_
, "Trajectory writing called, but no state saved.");
408 // TODO: This is only used for CPT - needs to be filled when we turn CPT back on
409 ObservablesHistory
* observablesHistory
= nullptr;
411 mdoutf_write_to_trajectory_files(fplog_
, cr_
, outf
, static_cast<int>(mdof_flags
),
412 totalNumAtoms_
, currentStep
, currentTime
,
413 localStateBackup_
.get(), globalState_
, observablesHistory
, f_
);
415 if (currentStep
!= lastStep_
|| !isRegularSimulationEnd_
)
417 localStateBackup_
.reset();
419 wallcycle_stop(mdoutf_get_wcycle(outf
), ewcTRAJ
);
422 void StatePropagatorData::elementSetup()
424 if (vvResetVelocities_
)
426 // MD-VV does the first velocity half-step only to calculate the constraint virial,
427 // then resets the velocities since the input is assumed to be positions and velocities
428 // at full time step. TODO: Change this to have input at half time steps.
429 velocityBackup_
= v_
;
433 void StatePropagatorData::resetVelocities()
435 v_
= velocityBackup_
;
438 void StatePropagatorData::writeCheckpoint(t_state
* localState
, t_state gmx_unused
* globalState
)
440 state_change_natoms(localState
, localNAtoms_
);
443 copy_mat(box_
, localState
->box
);
444 localState
->ddp_count
= ddpCount_
;
445 localState
->flags
|= (1U << estX
) | (1U << estV
) | (1U << estBOX
);
448 void StatePropagatorData::trajectoryWriterTeardown(gmx_mdoutf
* gmx_unused outf
)
450 // Note that part of this code is duplicated in do_md_trajectory_writing.
451 // This duplication is needed while both legacy and modular code paths are in use.
452 // TODO: Remove duplication asap, make sure to keep in sync in the meantime.
453 if (!writeFinalConfiguration_
|| !isRegularSimulationEnd_
)
458 GMX_ASSERT(localStateBackup_
, "Final trajectory writing called, but no state saved.");
460 wallcycle_start(mdoutf_get_wcycle(outf
), ewcTRAJ
);
461 if (DOMAINDECOMP(cr_
))
463 auto globalXRef
= MASTER(cr_
) ? globalState_
->x
: gmx::ArrayRef
<gmx::RVec
>();
464 dd_collect_vec(cr_
->dd
, localStateBackup_
.get(), localStateBackup_
->x
, globalXRef
);
465 auto globalVRef
= MASTER(cr_
) ? globalState_
->v
: gmx::ArrayRef
<gmx::RVec
>();
466 dd_collect_vec(cr_
->dd
, localStateBackup_
.get(), localStateBackup_
->v
, globalVRef
);
470 // We have the whole state locally: copy the local state pointer
471 globalState_
= localStateBackup_
.get();
476 fprintf(stderr
, "\nWriting final coordinates.\n");
477 if (canMoleculesBeDistributedOverPBC_
&& !systemHasPeriodicMolecules_
)
479 // Make molecules whole only for confout writing
480 do_pbc_mtop(pbcType_
, localStateBackup_
->box
, &topologyHolder_
->globalTopology(),
481 globalState_
->x
.rvec_array());
483 write_sto_conf_mtop(finalConfigurationFilename_
.c_str(), *topologyHolder_
->globalTopology().name
,
484 &topologyHolder_
->globalTopology(), globalState_
->x
.rvec_array(),
485 globalState_
->v
.rvec_array(), pbcType_
, localStateBackup_
->box
);
487 wallcycle_stop(mdoutf_get_wcycle(outf
), ewcTRAJ
);
490 SignallerCallbackPtr
StatePropagatorData::registerLastStepCallback()
492 return std::make_unique
<SignallerCallback
>([this](Step step
, Time
/*time*/) {
494 isRegularSimulationEnd_
= (step
== lastPlannedStep_
);