Start making IMD and swap model IMDModule
[gromacs.git] / src / gromacs / swap / swapcoords.cpp
bloba6f9f14f2e5976774d09d53c12b0210514cf67a8
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, 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.
35 /*! \internal \file
36 * \brief
37 * Implements functions in swapcoords.h.
39 * \author Carsten Kutzner <ckutzne@gwdg.de>
40 * \ingroup module_swap
42 #include "gmxpre.h"
44 #include "swapcoords.h"
46 #include <cstdio>
47 #include <cstdlib>
48 #include <ctime>
50 #include <memory>
51 #include <string>
52 #include <vector>
54 #include "gromacs/domdec/domdec_struct.h"
55 #include "gromacs/domdec/localatomset.h"
56 #include "gromacs/domdec/localatomsetmanager.h"
57 #include "gromacs/fileio/confio.h"
58 #include "gromacs/fileio/gmxfio.h"
59 #include "gromacs/fileio/xvgr.h"
60 #include "gromacs/gmxlib/network.h"
61 #include "gromacs/math/vec.h"
62 #include "gromacs/mdlib/groupcoord.h"
63 #include "gromacs/mdtypes/commrec.h"
64 #include "gromacs/mdtypes/imdmodule.h"
65 #include "gromacs/mdtypes/inputrec.h"
66 #include "gromacs/mdtypes/md_enums.h"
67 #include "gromacs/mdtypes/mdrunoptions.h"
68 #include "gromacs/mdtypes/observableshistory.h"
69 #include "gromacs/mdtypes/state.h"
70 #include "gromacs/mdtypes/swaphistory.h"
71 #include "gromacs/pbcutil/pbc.h"
72 #include "gromacs/timing/wallcycle.h"
73 #include "gromacs/topology/mtop_lookup.h"
74 #include "gromacs/topology/topology.h"
75 #include "gromacs/utility/cstringutil.h"
76 #include "gromacs/utility/fatalerror.h"
77 #include "gromacs/utility/pleasecite.h"
78 #include "gromacs/utility/smalloc.h"
79 #include "gromacs/utility/snprintf.h"
81 static const char *SwS = {"SWAP:"}; /**< For output that comes from the swap module */
82 static const char *SwSEmpty = {" "}; /**< Placeholder for multi-line output */
83 static const char* CompStr[eCompNR] = {"A", "B" }; /**< Compartment name */
84 static const char *SwapStr[eSwapTypesNR+1] = { "", "X-", "Y-", "Z-", nullptr}; /**< Name for the swap types. */
85 static const char *DimStr[DIM+1] = { "X", "Y", "Z", nullptr}; /**< Name for the swap dimension. */
87 /** Keep track of through which channel the ions have passed */
88 enum eChannelHistory {
89 eChHistPassedNone, eChHistPassedCh0, eChHistPassedCh1, eChHistNr
91 static const char* ChannelString[eChHistNr] = { "none", "channel0", "channel1" }; /**< Name for the channels */
93 /*! \brief Domain identifier.
95 * Keeps track of from which compartment the ions came before passing the
96 * channel.
98 enum eDomain {
99 eDomainNotset, eDomainA, eDomainB, eDomainNr
101 static const char* DomainString[eDomainNr] = { "not_assigned", "Domain_A", "Domain_B" }; /**< Name for the domains */
103 namespace gmx
106 /*! \internal
107 * \brief Implement Computational Electrophysiology swapping.
109 class SwapCoordinates final : public IMDModule
111 // From IMDModule
112 IMdpOptionProvider *mdpOptionProvider() override { return nullptr; }
113 IMDOutputProvider *outputProvider() override { return nullptr; }
114 void initForceProviders(ForceProviders * /* forceProviders */) override {}
117 std::unique_ptr<IMDModule> createSwapCoordinatesModule()
119 return std::make_unique<SwapCoordinates>();
123 } // namespace gmx
125 /*! \internal \brief
126 * Structure containing compartment-specific data.
128 typedef struct swap_compartment
130 int nMol; /**< Number of ion or water molecules detected
131 in this compartment. */
132 int nMolBefore; /**< Number of molecules before swapping. */
133 int nMolReq; /**< Requested number of molecules in compartment. */
134 real nMolAv; /**< Time-averaged number of molecules matching
135 the compartment conditions. */
136 int *nMolPast; /**< Past molecule counts for time-averaging. */
137 int *ind; /**< Indices to collective array of atoms. */
138 real *dist; /**< Distance of atom to bulk layer, which is
139 normally the center layer of the compartment */
140 int nalloc; /**< Allocation size for ind array. */
141 int inflow_net; /**< Net inflow of ions into this compartment. */
142 } t_compartment;
145 /*! \internal \brief
146 * This structure contains data needed for the groups involved in swapping:
147 * split group 0, split group 1, solvent group, ion groups.
149 typedef struct swap_group
151 /*!\brief Construct a swap group given the managed swap atoms.
153 * \param[in] atomset Managed indices of atoms that are part of the swap group.
155 swap_group(const gmx::LocalAtomSet &atomset);
156 char *molname = nullptr; /**< Name of the group or ion type */
157 int apm = 0; /**< Number of atoms in each molecule */
158 gmx::LocalAtomSet atomset; /**< The atom indices in the swap group */
159 rvec *xc = nullptr; /**< Collective array of group atom positions (size nat) */
160 ivec *xc_shifts = nullptr; /**< Current (collective) shifts (size nat) */
161 ivec *xc_eshifts = nullptr; /**< Extra shifts since last DD step (size nat) */
162 rvec *xc_old = nullptr; /**< Old (collective) positions (size nat) */
163 real q = 0.; /**< Total charge of one molecule of this group */
164 real *m = nullptr; /**< Masses (can be omitted, size apm) */
165 unsigned char *comp_from = nullptr; /**< (Collective) Stores from which compartment this
166 molecule has come. This way we keep track of
167 through which channel an ion permeates
168 (size nMol = nat/apm) */
169 unsigned char *comp_now = nullptr; /**< In which compartment this ion is now (size nMol) */
170 unsigned char *channel_label = nullptr; /**< Which channel was passed at last by this ion?
171 (size nMol) */
172 rvec center; /**< Center of the group; COM if masses are used */
173 t_compartment comp[eCompNR]; /**< Distribution of particles of this group across
174 the two compartments */
175 real vacancy[eCompNR]; /**< How many molecules need to be swapped in? */
176 int fluxfromAtoB[eChanNR]; /**< Net flux of ions per channel */
177 int nCyl[eChanNR]; /**< Number of ions residing in a channel */
178 int nCylBoth = 0; /**< Ions assigned to cyl0 and cyl1. Not good. */
179 } t_swapgrp;
181 t_swapgrp::swap_group(const gmx::LocalAtomSet& atomset) : atomset {
182 atomset
184 center[0] = 0;
185 center[1] = 0;
186 center[2] = 0;
187 for (int compartment = eCompA; compartment < eCompNR; ++compartment)
189 comp[compartment] = {};
190 vacancy[compartment] = 0;
192 for (int channel = eChan0; channel < eChanNR; ++channel)
194 fluxfromAtoB[channel] = 0;
195 nCyl[channel] = 0;
199 /*! \internal \brief
200 * Main (private) data structure for the position swapping protocol.
202 typedef struct t_swap
204 int swapdim; /**< One of XX, YY, ZZ */
205 t_pbc *pbc; /**< Needed to make molecules whole. */
206 FILE *fpout; /**< Output file. */
207 int ngrp; /**< Number of t_swapgrp groups */
208 std::vector<t_swapgrp> group; /**< Separate groups for channels, solvent, ions */
209 int fluxleak; /**< Flux not going through any of the channels. */
210 real deltaQ; /**< The charge imbalance between the compartments. */
211 } t_swap;
215 /*! \brief Check whether point is in channel.
217 * A channel is a cylinder defined by a disc
218 * with radius r around its center c. The thickness of the cylinder is
219 * d_up - d_down.
221 * \code
222 * ^ d_up
224 * r |
225 * <---------c--------->
227 * v d_down
229 * \endcode
231 * \param[in] point The position (xyz) under consideration.
232 * \param[in] center The center of the cylinder.
233 * \param[in] d_up The upper extension of the cylinder.
234 * \param[in] d_down The lower extension.
235 * \param[in] r_cyl2 Cylinder radius squared.
236 * \param[in] pbc Structure with info about periodic boundary conditions.
237 * \param[in] normal The membrane normal direction is typically 3, i.e. z, but can be x or y also.
239 * \returns Whether the point is inside the defined cylindric channel.
241 static gmx_bool is_in_channel(
242 rvec point,
243 rvec center,
244 real d_up,
245 real d_down,
246 real r_cyl2,
247 t_pbc *pbc,
248 int normal)
250 rvec dr;
251 int plane1, plane2; /* Directions tangential to membrane */
254 plane1 = (normal + 1) % 3; /* typically 0, i.e. XX */
255 plane2 = (normal + 2) % 3; /* typically 1, i.e. YY */
257 /* Get the distance vector dr between the point and the center of the cylinder */
258 pbc_dx(pbc, point, center, dr); /* This puts center in the origin */
260 /* Check vertical direction */
261 if ( (dr[normal] > d_up) || (dr[normal] < -d_down) )
263 return FALSE;
266 /* Check radial direction */
267 if ( (dr[plane1]*dr[plane1] + dr[plane2]*dr[plane2]) > r_cyl2)
269 return FALSE;
272 /* All check passed, this point is in the cylinder */
273 return TRUE;
277 /*! \brief Prints output to CompEL output file.
279 * Prints to swap output file how many ions are in each compartment,
280 * where the centers of the split groups are, and how many ions of each type
281 * passed the channels.
283 static void print_ionlist(
284 t_swap *s,
285 double time,
286 const char comment[])
288 // Output time
289 fprintf(s->fpout, "%12.5e", time);
291 // Output number of molecules and difference to reference counts for each
292 // compartment and ion type
293 for (int iComp = 0; iComp < eCompNR; iComp++)
295 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
297 t_compartment *comp = &s->group[ig].comp[iComp];
299 fprintf(s->fpout, "%10d%10.1f%10d", comp->nMol, comp->nMolAv - comp->nMolReq, comp->inflow_net);
303 // Output center of split groups
304 fprintf(s->fpout, "%10g%10g",
305 s->group[eGrpSplit0].center[s->swapdim],
306 s->group[eGrpSplit1].center[s->swapdim]);
308 // Output ion flux for each channel and ion type
309 for (int iChan = 0; iChan < eChanNR; iChan++)
311 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
313 t_swapgrp *g = &s->group[ig];
314 fprintf(s->fpout, "%10d", g->fluxfromAtoB[iChan]);
318 /* Output the number of molecules that leaked from A to B */
319 fprintf(s->fpout, "%10d", s->fluxleak);
321 fprintf(s->fpout, "%s\n", comment);
325 /*! \brief Get the center of a group of nat atoms.
327 * Since with PBC an atom group might not be whole, use the first atom as the
328 * reference atom and determine the center with respect to this reference.
330 static void get_molecule_center(
331 rvec x[],
332 int nat,
333 const real *weights,
334 rvec center,
335 t_pbc *pbc)
337 int i;
338 rvec weightedPBCimage;
339 real wi, wsum;
340 rvec reference, correctPBCimage, dx;
343 /* Use the first atom as the reference and put other atoms near that one */
344 /* This does not work for large molecules that span > half of the box! */
345 copy_rvec(x[0], reference);
347 /* Calculate either the weighted center or simply the center of geometry */
348 wsum = 0.0;
349 clear_rvec(center);
350 for (i = 0; i < nat; i++)
352 /* PBC distance between position and reference */
353 pbc_dx(pbc, x[i], reference, dx);
355 /* Add PBC distance to reference */
356 rvec_add(reference, dx, correctPBCimage);
358 /* Take weight into account */
359 if (nullptr == weights)
361 wi = 1.0;
363 else
365 wi = weights[i];
367 wsum += wi;
368 svmul(wi, correctPBCimage, weightedPBCimage);
370 /* Add to center */
371 rvec_inc(center, weightedPBCimage);
374 /* Normalize */
375 svmul(1.0/wsum, center, center);
380 /*! \brief Return TRUE if position x of ion (or water) is found in the compartment,
381 * i.e. between w1 and w2.
383 * One can define and additional offset "b" if one wants to exchange ions/water
384 * to or from a plane not directly in the middle of w1 and w2. The offset can be
385 * in ]-1.0, ..., +1.0 [.
386 * A bulkOffset of 0.0 means 'no offset', so the swap-layer is directly in the
387 * middle between w1 and w2. Offsets -1.0 < b < 0.0 will yield swaps nearer to w1,
388 * whereas offsets 0.0 < 0 < +1.0 will yield swaps nearer to w2.
390 * \code
392 * ||--------------+-------------|-------------+------------------------||
393 * w1 ? ? ? ? ? ? ? ? ? ? ? w2
394 * ||--------------+-------------|----b--------+------------------------||
395 * -1 0.0 +1
397 * \endcode
399 * \param[in] w1 Position of 'wall' atom 1.
400 * \param[in] w2 Position of 'wall' atom 2.
401 * \param[in] x Position of the ion or the water molecule under consideration.
402 * \param[in] l Length of the box, from || to || in the sketch.
403 * \param[in] bulkOffset Where is the bulk layer "b" to be found between w1 and w2?
404 * \param[out] distance_from_b Distance of x to the bulk layer "b".
406 * \returns TRUE if x is between w1 and w2.
408 * Also computes the distance of x to the compartment center (the layer that is
409 * normally situated in the middle of w1 and w2 that would be considered as having
410 * the bulk concentration of ions).
412 static gmx_bool compartment_contains_atom(
413 real w1,
414 real w2,
415 real x,
416 real l,
417 real bulkOffset,
418 real *distance_from_b)
420 real m, l_2;
421 real width;
424 /* First set the origin in the middle of w1 and w2 */
425 m = 0.5 * (w1 + w2);
426 w1 -= m;
427 w2 -= m;
428 x -= m;
429 width = w2 - w1;
431 /* Now choose the PBC image of x that is closest to the origin: */
432 l_2 = 0.5*l;
433 while (x > l_2)
435 x -= l;
437 while (x <= -l_2)
439 x += l;
442 *distance_from_b = static_cast<real>(fabs(x - bulkOffset*0.5*width));
444 /* Return TRUE if we now are in area "????" */
445 return (x >= w1) && (x < w2);
449 /*! \brief Updates the time-averaged number of ions in a compartment. */
450 static void update_time_window(t_compartment *comp, int values, int replace)
452 real average;
453 int i;
456 /* Put in the new value */
457 if (replace >= 0)
459 comp->nMolPast[replace] = comp->nMol;
462 /* Compute the new time-average */
463 average = 0.0;
464 for (i = 0; i < values; i++)
466 average += comp->nMolPast[i];
468 average /= values;
469 comp->nMolAv = average;
473 /*! \brief Add the atom with collective index ci to the atom list in compartment 'comp'.
475 * \param[in] ci Index of this ion in the collective xc array.
476 * \param[inout] comp Compartment to add this atom to.
477 * \param[in] distance Shortest distance of this atom to the bulk layer,
478 * from which ion/water pairs are selected for swapping.
480 static void add_to_list(
481 int ci,
482 t_compartment *comp,
483 real distance)
485 int nr = comp->nMol;
487 if (nr >= comp->nalloc)
489 comp->nalloc = over_alloc_dd(nr+1);
490 srenew(comp->ind, comp->nalloc);
491 srenew(comp->dist, comp->nalloc);
493 comp->ind[nr] = ci;
494 comp->dist[nr] = distance;
495 comp->nMol++;
499 /*! \brief Determine the compartment boundaries from the channel centers. */
500 static void get_compartment_boundaries(
501 int c,
502 t_swap *s,
503 const matrix box,
504 real *left, real *right)
506 real pos0, pos1;
507 real leftpos, rightpos, leftpos_orig;
510 if (c >= eCompNR)
512 gmx_fatal(FARGS, "No compartment %c.", c+'A');
515 pos0 = s->group[eGrpSplit0].center[s->swapdim];
516 pos1 = s->group[eGrpSplit1].center[s->swapdim];
518 if (pos0 < pos1)
520 leftpos = pos0;
521 rightpos = pos1;
523 else
525 leftpos = pos1;
526 rightpos = pos0;
529 /* This gets us the other compartment: */
530 if (c == eCompB)
532 leftpos_orig = leftpos;
533 leftpos = rightpos;
534 rightpos = leftpos_orig + box[s->swapdim][s->swapdim];
537 *left = leftpos;
538 *right = rightpos;
542 /*! \brief Determine the per-channel ion flux.
544 * To determine the flux through the individual channels, we
545 * remember the compartment and channel history of each ion. An ion can be
546 * either in channel0 or channel1, or in the remaining volume of compartment
547 * A or B.
549 * \code
550 * +-----------------+
551 * | | B
552 * | | B compartment
553 * ||||||||||0|||||||| bilayer with channel 0
554 * | | A
555 * | | A
556 * | | A compartment
557 * | | A
558 * |||||1||||||||||||| bilayer with channel 1
559 * | | B
560 * | | B compartment
561 * +-----------------+
563 * \endcode
565 static void detect_flux_per_channel(
566 t_swapgrp *g,
567 int iAtom,
568 int comp,
569 rvec atomPosition,
570 unsigned char *comp_now,
571 unsigned char *comp_from,
572 unsigned char *channel_label,
573 t_swapcoords *sc,
574 real cyl0_r2,
575 real cyl1_r2,
576 int64_t step,
577 gmx_bool bRerun,
578 FILE *fpout)
580 gmx_swapcoords_t s;
581 int sd, chan_nr;
582 gmx_bool in_cyl0, in_cyl1;
583 char buf[STRLEN];
586 s = sc->si_priv;
587 sd = s->swapdim;
589 /* Check whether ion is inside any of the channels */
590 in_cyl0 = is_in_channel(atomPosition, s->group[eGrpSplit0].center, sc->cyl0u, sc->cyl0l, cyl0_r2, s->pbc, sd);
591 in_cyl1 = is_in_channel(atomPosition, s->group[eGrpSplit1].center, sc->cyl1u, sc->cyl1l, cyl1_r2, s->pbc, sd);
593 if (in_cyl0 && in_cyl1)
595 /* Ion appears to be in both channels. Something is severely wrong! */
596 g->nCylBoth++;
597 *comp_now = eDomainNotset;
598 *comp_from = eDomainNotset;
599 *channel_label = eChHistPassedNone;
601 else if (in_cyl0)
603 /* Ion is in channel 0 now */
604 *channel_label = eChHistPassedCh0;
605 *comp_now = eDomainNotset;
606 g->nCyl[eChan0]++;
608 else if (in_cyl1)
610 /* Ion is in channel 1 now */
611 *channel_label = eChHistPassedCh1;
612 *comp_now = eDomainNotset;
613 g->nCyl[eChan1]++;
615 else
617 /* Ion is not in any of the channels, so it must be in domain A or B */
618 if (eCompA == comp)
620 *comp_now = eDomainA;
622 else
624 *comp_now = eDomainB;
628 /* Only take action, if ion is now in domain A or B, and was before
629 * in the other domain!
631 if (eDomainNotset == *comp_from)
633 /* Maybe we can set the domain now */
634 *comp_from = *comp_now; /* Could still be eDomainNotset, though */
636 else if ( (*comp_now != eDomainNotset ) /* if in channel */
637 && (*comp_from != *comp_now) )
639 /* Obviously the ion changed its domain.
640 * Count this for the channel through which it has passed. */
641 switch (*channel_label)
643 case eChHistPassedNone:
644 ++s->fluxleak;
646 fprintf(stderr, " %s Warning! Step %s, ion %d moved from %s to %s\n",
647 SwS, gmx_step_str(step, buf), iAtom, DomainString[*comp_from], DomainString[*comp_now]);
648 if (bRerun)
650 fprintf(stderr, ", possibly due to a swap in the original simulation.\n");
652 else
654 fprintf(stderr, "but did not pass cyl0 or cyl1 as defined in the .mdp file.\n"
655 "Do you have an ion somewhere within the membrane?\n");
656 /* Write this info to the CompEL output file: */
657 fprintf(s->fpout, " # Warning: step %s, ion %d moved from %s to %s (probably through the membrane)\n",
658 gmx_step_str(step, buf), iAtom,
659 DomainString[*comp_from], DomainString[*comp_now]);
662 break;
663 case eChHistPassedCh0:
664 case eChHistPassedCh1:
665 if (*channel_label == eChHistPassedCh0)
667 chan_nr = 0;
669 else
671 chan_nr = 1;
674 if (eDomainA == *comp_from)
676 g->fluxfromAtoB[chan_nr]++;
678 else
680 g->fluxfromAtoB[chan_nr]--;
682 fprintf(fpout, "# Atom nr. %d finished passing %s.\n", iAtom, ChannelString[*channel_label]);
683 break;
684 default:
685 gmx_fatal(FARGS, "%s Unknown channel history entry for ion type '%s'\n",
686 SwS, g->molname);
689 /* This ion has moved to the _other_ compartment ... */
690 *comp_from = *comp_now;
691 /* ... and it did not pass any channel yet */
692 *channel_label = eChHistPassedNone;
697 /*! \brief Determines which ions or solvent molecules are in compartment A and B */
698 static void sortMoleculesIntoCompartments(
699 t_swapgrp *g,
700 t_commrec *cr,
701 t_swapcoords *sc,
702 const matrix box,
703 int64_t step,
704 FILE *fpout,
705 gmx_bool bRerun,
706 gmx_bool bIsSolvent)
708 gmx_swapcoords_t s = sc->si_priv;
709 int nMolNotInComp[eCompNR]; /* consistency check */
710 real cyl0_r2 = sc->cyl0r * sc->cyl0r;
711 real cyl1_r2 = sc->cyl1r * sc->cyl1r;
713 /* Get us a counter that cycles in the range of [0 ... sc->nAverage[ */
714 int replace = (step/sc->nstswap) % sc->nAverage;
716 for (int comp = eCompA; comp <= eCompB; comp++)
718 real left, right;
720 /* Get lists of atoms that match criteria for this compartment */
721 get_compartment_boundaries(comp, sc->si_priv, box, &left, &right);
723 /* First clear the ion molecule lists */
724 g->comp[comp].nMol = 0;
725 nMolNotInComp[comp] = 0; /* consistency check */
727 /* Loop over the molecules and atoms of this group */
728 for (int iMol = 0, iAtom = 0; iAtom < static_cast<int>(g->atomset.numAtomsGlobal()); iAtom += g->apm, iMol++)
730 real dist;
731 int sd = s->swapdim;
733 /* Is this first atom of the molecule in the compartment that we look at? */
734 if (compartment_contains_atom(left, right, g->xc[iAtom][sd], box[sd][sd], sc->bulkOffset[comp], &dist) )
736 /* Add the first atom of this molecule to the list of molecules in this compartment */
737 add_to_list(iAtom, &g->comp[comp], dist);
739 /* Master also checks for ion groups through which channel each ion has passed */
740 if (MASTER(cr) && (g->comp_now != nullptr) && !bIsSolvent)
742 int globalAtomNr = g->atomset.globalIndex()[iAtom] + 1; /* PDB index starts at 1 ... */
743 detect_flux_per_channel(g, globalAtomNr, comp, g->xc[iAtom],
744 &g->comp_now[iMol], &g->comp_from[iMol], &g->channel_label[iMol],
745 sc, cyl0_r2, cyl1_r2, step, bRerun, fpout);
748 else
750 nMolNotInComp[comp]++;
753 /* Correct the time-averaged number of ions in the compartment */
754 if (!bIsSolvent)
756 update_time_window(&g->comp[comp], sc->nAverage, replace);
760 /* Flux detection warnings */
761 if (MASTER(cr) && !bIsSolvent)
763 if (g->nCylBoth > 0)
765 fprintf(stderr, "\n"
766 "%s Warning: %d atoms were detected as being in both channels! Probably your split\n"
767 "%s cylinder is way too large, or one compartment has collapsed (step %" PRId64 ")\n",
768 SwS, g->nCylBoth, SwS, step);
770 fprintf(s->fpout, "Warning: %d atoms were assigned to both channels!\n", g->nCylBoth);
772 g->nCylBoth = 0;
776 if (bIsSolvent && nullptr != fpout)
778 fprintf(fpout, "# Solv. molecules in comp.%s: %d comp.%s: %d\n",
779 CompStr[eCompA], g->comp[eCompA].nMol,
780 CompStr[eCompB], g->comp[eCompB].nMol);
783 /* Consistency checks */
784 const auto numMolecules = static_cast<int>(g->atomset.numAtomsGlobal() / g->apm);
785 if (nMolNotInComp[eCompA] + nMolNotInComp[eCompB] != numMolecules)
787 fprintf(stderr, "%s Warning: Inconsistency while assigning '%s' molecules to compartments. !inA: %d, !inB: %d, total molecules %d\n",
788 SwS, g->molname, nMolNotInComp[eCompA], nMolNotInComp[eCompB], numMolecules);
791 int sum = g->comp[eCompA].nMol + g->comp[eCompB].nMol;
792 if (sum != numMolecules)
794 fprintf(stderr, "%s Warning: %d molecules are in group '%s', but altogether %d have been assigned to the compartments.\n",
795 SwS, numMolecules, g->molname, sum);
800 /*! \brief Find out how many group atoms are in the compartments initially */
801 static void get_initial_ioncounts(
802 t_inputrec *ir,
803 const rvec x[], /* the initial positions */
804 const matrix box,
805 t_commrec *cr,
806 gmx_bool bRerun)
808 t_swapcoords *sc;
809 t_swap *s;
810 t_swapgrp *g;
812 sc = ir->swap;
813 s = sc->si_priv;
815 /* Loop over the user-defined (ion) groups */
816 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
818 g = &s->group[ig];
820 /* Copy the initial positions of the atoms in the group
821 * to the collective array so that we can compartmentalize */
822 for (size_t i = 0; i < g->atomset.numAtomsGlobal(); i++)
824 int ind = g->atomset.globalIndex()[i];
825 copy_rvec(x[ind], g->xc[i]);
828 /* Set up the compartments and get lists of atoms in each compartment */
829 sortMoleculesIntoCompartments(g, cr, sc, box, 0, s->fpout, bRerun, FALSE);
831 /* Set initial molecule counts if requested (as signaled by "-1" value) */
832 for (int ic = 0; ic < eCompNR; ic++)
834 int requested = sc->grp[ig].nmolReq[ic];
835 if (requested < 0)
837 g->comp[ic].nMolReq = g->comp[ic].nMol;
839 else
841 g->comp[ic].nMolReq = requested;
845 /* Check whether the number of requested molecules adds up to the total number */
846 int req = g->comp[eCompA].nMolReq + g->comp[eCompB].nMolReq;
847 int tot = g->comp[eCompA].nMol + g->comp[eCompB].nMol;
849 if ( (req != tot) )
851 gmx_fatal(FARGS, "Mismatch of the number of %s ions summed over both compartments.\n"
852 "You requested a total of %d ions (%d in A and %d in B),\n"
853 "but there are a total of %d ions of this type in the system.\n",
854 g->molname, req, g->comp[eCompA].nMolReq,
855 g->comp[eCompB].nMolReq, tot);
858 /* Initialize time-averaging:
859 * Write initial concentrations to all time bins to start with */
860 for (int ic = 0; ic < eCompNR; ic++)
862 g->comp[ic].nMolAv = g->comp[ic].nMol;
863 for (int i = 0; i < sc->nAverage; i++)
865 g->comp[ic].nMolPast[i] = g->comp[ic].nMol;
872 /*! \brief Copy history of ion counts from checkpoint file.
874 * When called, the checkpoint file has already been read in. Here we copy
875 * over the values from .cpt file to the swap data structure.
877 static void get_initial_ioncounts_from_cpt(
878 t_inputrec *ir, swaphistory_t *swapstate,
879 t_commrec *cr, gmx_bool bVerbose)
881 t_swapcoords *sc;
882 t_swap *s;
883 t_swapgrp *g;
884 swapstateIons_t *gs;
886 sc = ir->swap;
887 s = sc->si_priv;
889 if (MASTER(cr))
891 /* Copy the past values from the checkpoint values that have been read in already */
892 if (bVerbose)
894 fprintf(stderr, "%s Copying values from checkpoint\n", SwS);
897 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
899 g = &s->group[ig];
900 gs = &swapstate->ionType[ig - eSwapFixedGrpNR];
902 for (int ic = 0; ic < eCompNR; ic++)
904 g->comp[ic].nMolReq = gs->nMolReq[ic];
905 g->comp[ic].inflow_net = gs->inflow_net[ic];
907 if (bVerbose)
909 fprintf(stderr, "%s ... Influx netto: %d Requested: %d Past values: ", SwS,
910 g->comp[ic].inflow_net, g->comp[ic].nMolReq);
913 for (int j = 0; j < sc->nAverage; j++)
915 g->comp[ic].nMolPast[j] = gs->nMolPast[ic][j];
916 if (bVerbose)
918 fprintf(stderr, "%d ", g->comp[ic].nMolPast[j]);
921 if (bVerbose)
923 fprintf(stderr, "\n");
931 /*! \brief The master lets all others know about the initial ion counts. */
932 static void bc_initial_concentrations(
933 t_commrec *cr,
934 t_swapcoords *swap)
936 int ic, ig;
937 t_swap *s;
938 t_swapgrp *g;
941 s = swap->si_priv;
943 for (ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
945 g = &s->group[ig];
947 for (ic = 0; ic < eCompNR; ic++)
949 gmx_bcast(sizeof(g->comp[ic].nMolReq), &(g->comp[ic].nMolReq), cr);
950 gmx_bcast(sizeof(g->comp[ic].nMol ), &(g->comp[ic].nMol ), cr);
951 gmx_bcast( swap->nAverage * sizeof(g->comp[ic].nMolPast[0]), g->comp[ic].nMolPast, cr);
957 /*! \brief Ensure that each atom belongs to at most one of the swap groups. */
958 static void check_swap_groups(t_swap *s, int nat, gmx_bool bVerbose)
960 int *nGroup = nullptr; /* This array counts for each atom in the MD system to
961 how many swap groups it belongs (should be 0 or 1!) */
962 int ind = -1;
963 int nMultiple = 0; /* Number of atoms belonging to multiple groups */
966 if (bVerbose)
968 fprintf(stderr, "%s Making sure each atom belongs to at most one of the swap groups.\n", SwS);
971 /* Add one to the group count of atoms belonging to a swap group: */
972 snew(nGroup, nat);
973 for (int i = 0; i < s->ngrp; i++)
975 t_swapgrp *g = &s->group[i];
976 for (size_t j = 0; j < g->atomset.numAtomsGlobal(); j++)
978 /* Get the global index of this atom of this group: */
979 ind = g->atomset.globalIndex()[j];
980 nGroup[ind]++;
983 /* Make sure each atom belongs to at most one of the groups: */
984 for (int i = 0; i < nat; i++)
986 if (nGroup[i] > 1)
988 nMultiple++;
991 sfree(nGroup);
993 if (nMultiple)
995 gmx_fatal(FARGS, "%s Cannot perform swapping since %d atom%s allocated to more than one swap index group.\n"
996 "%s Each atom must be allocated to at most one of the split groups, the swap groups, or the solvent.\n"
997 "%s Check the .mdp file settings regarding the swap index groups or the index groups themselves.\n",
998 SwS, nMultiple, (1 == nMultiple) ? " is" : "s are", SwSEmpty, SwSEmpty);
1003 /*! \brief Get the number of atoms per molecule for this group.
1005 * Also ensure that all the molecules in this group have this number of atoms.
1007 static int get_group_apm_check(
1008 int igroup,
1009 t_swap *s,
1010 gmx_bool bVerbose,
1011 gmx_mtop_t *mtop)
1013 t_swapgrp *g = &s->group[igroup];
1014 const int *ind = s->group[igroup].atomset.globalIndex().data();
1015 int nat = s->group[igroup].atomset.numAtomsGlobal();
1017 /* Determine the number of solvent atoms per solvent molecule from the
1018 * first solvent atom: */
1019 int molb = 0;
1020 mtopGetMolblockIndex(mtop, ind[0], &molb, nullptr, nullptr);
1021 int apm = mtop->moleculeBlockIndices[molb].numAtomsPerMolecule;
1023 if (bVerbose)
1025 fprintf(stderr, "%s Checking whether all %s molecules consist of %d atom%s\n", SwS,
1026 g->molname, apm, apm > 1 ? "s" : "");
1029 /* Check whether this is also true for all other solvent atoms */
1030 for (int i = 1; i < nat; i++)
1032 mtopGetMolblockIndex(mtop, ind[i], &molb, nullptr, nullptr);
1033 if (apm != mtop->moleculeBlockIndices[molb].numAtomsPerMolecule)
1035 gmx_fatal(FARGS, "Not all molecules of swap group %d consist of %d atoms.",
1036 igroup, apm);
1040 //TODO: check whether charges and masses of each molecule are identical!
1041 return apm;
1045 /*! \brief Print the legend to the swap output file.
1047 * Also print the initial values of ion counts and position of split groups.
1049 static void print_ionlist_legend(t_inputrec *ir,
1050 const gmx_output_env_t *oenv)
1052 const char **legend;
1053 int count = 0;
1054 char buf[STRLEN];
1056 t_swap *s = ir->swap->si_priv;
1057 int nIonTypes = ir->swap->ngrp - eSwapFixedGrpNR;
1058 snew(legend, eCompNR*nIonTypes*3 + 2 + eChanNR*nIonTypes + 1);
1060 // Number of molecules and difference to reference counts for each
1061 // compartment and ion type
1062 for (int ic = count = 0; ic < eCompNR; ic++)
1064 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1066 t_swapGroup *g = &ir->swap->grp[ig];
1067 real q = s->group[ig].q;
1069 snprintf(buf, STRLEN, "%s %s ions (charge %s%g)", CompStr[ic], g->molname, q > 0 ? "+" : "", q);
1070 legend[count++] = gmx_strdup(buf);
1072 snprintf(buf, STRLEN, "%s av. mismatch to %d %s ions",
1073 CompStr[ic], s->group[ig].comp[ic].nMolReq, g->molname);
1074 legend[count++] = gmx_strdup(buf);
1076 snprintf(buf, STRLEN, "%s net %s ion influx", CompStr[ic], g->molname);
1077 legend[count++] = gmx_strdup(buf);
1081 // Center of split groups
1082 snprintf(buf, STRLEN, "%scenter of %s of split group 0", SwapStr[ir->eSwapCoords], (nullptr != s->group[eGrpSplit0].m) ? "mass" : "geometry");
1083 legend[count++] = gmx_strdup(buf);
1084 snprintf(buf, STRLEN, "%scenter of %s of split group 1", SwapStr[ir->eSwapCoords], (nullptr != s->group[eGrpSplit1].m) ? "mass" : "geometry");
1085 legend[count++] = gmx_strdup(buf);
1087 // Ion flux for each channel and ion type
1088 for (int ic = 0; ic < eChanNR; ic++)
1090 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1092 t_swapGroup *g = &ir->swap->grp[ig];
1093 snprintf(buf, STRLEN, "A->ch%d->B %s permeations", ic, g->molname);
1094 legend[count++] = gmx_strdup(buf);
1098 // Number of molecules that leaked from A to B
1099 snprintf(buf, STRLEN, "leakage");
1100 legend[count++] = gmx_strdup(buf);
1102 xvgr_legend(s->fpout, count, legend, oenv);
1104 fprintf(s->fpout, "# Instantaneous ion counts and time-averaged differences to requested numbers\n");
1106 // We add a simple text legend helping to identify the columns with xvgr legend strings
1107 fprintf(s->fpout, "# time (ps)");
1108 for (int i = 0; i < count; i++)
1110 snprintf(buf, STRLEN, "s%d", i);
1111 fprintf(s->fpout, "%10s", buf);
1113 fprintf(s->fpout, "\n");
1114 fflush(s->fpout);
1118 /*! \brief Initialize channel ion flux detection routine.
1120 * Initialize arrays that keep track of where the ions come from and where
1121 * they go.
1123 static void detect_flux_per_channel_init(
1124 t_swap *s,
1125 swaphistory_t *swapstate,
1126 gmx_bool bStartFromCpt)
1128 t_swapgrp *g;
1129 swapstateIons_t *gs;
1131 /* All these flux detection routines run on the master only */
1132 if (swapstate == nullptr)
1134 return;
1137 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1139 g = &s->group[ig];
1140 gs = &swapstate->ionType[ig - eSwapFixedGrpNR];
1142 /******************************************************/
1143 /* Channel and domain history for the individual ions */
1144 /******************************************************/
1145 if (bStartFromCpt) /* set the pointers right */
1147 g->comp_from = gs->comp_from;
1148 g->channel_label = gs->channel_label;
1150 else /* allocate memory for molecule counts */
1152 snew(g->comp_from, g->atomset.numAtomsGlobal()/g->apm);
1153 gs->comp_from = g->comp_from;
1154 snew(g->channel_label, g->atomset.numAtomsGlobal()/g->apm);
1155 gs->channel_label = g->channel_label;
1157 snew(g->comp_now, g->atomset.numAtomsGlobal()/g->apm);
1159 /* Initialize the channel and domain history counters */
1160 for (size_t i = 0; i < g->atomset.numAtomsGlobal()/g->apm; i++)
1162 g->comp_now[i] = eDomainNotset;
1163 if (!bStartFromCpt)
1165 g->comp_from[i] = eDomainNotset;
1166 g->channel_label[i] = eChHistPassedNone;
1170 /************************************/
1171 /* Channel fluxes for both channels */
1172 /************************************/
1173 g->nCyl[eChan0] = 0;
1174 g->nCyl[eChan1] = 0;
1175 g->nCylBoth = 0;
1178 if (bStartFromCpt)
1180 fprintf(stderr, "%s Copying channel fluxes from checkpoint file data\n", SwS);
1184 // Loop over ion types (and both channels)
1185 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1187 g = &s->group[ig];
1188 gs = &swapstate->ionType[ig - eSwapFixedGrpNR];
1190 for (int ic = 0; ic < eChanNR; ic++)
1192 fprintf(stderr, "%s Channel %d flux history for ion type %s (charge %g): ", SwS, ic, g->molname, g->q);
1193 if (bStartFromCpt)
1195 g->fluxfromAtoB[ic] = gs->fluxfromAtoB[ic];
1197 else
1199 g->fluxfromAtoB[ic] = 0;
1202 fprintf(stderr, "%d molecule%s",
1203 g->fluxfromAtoB[ic], g->fluxfromAtoB[ic] == 1 ? "" : "s");
1204 fprintf(stderr, "\n");
1208 /* Set pointers for checkpoint writing */
1209 swapstate->fluxleak_p = &s->fluxleak;
1210 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1212 g = &s->group[ig];
1213 gs = &swapstate->ionType[ig - eSwapFixedGrpNR];
1215 for (int ic = 0; ic < eChanNR; ic++)
1217 gs->fluxfromAtoB_p[ic] = &g->fluxfromAtoB[ic];
1223 /*! \brief Outputs the initial structure to PDB file for debugging reasons.
1225 * Output the starting structure so that in case of multimeric channels
1226 * the user can check whether we have the correct PBC image for all atoms.
1227 * If this is not correct, the ion counts per channel will be very likely
1228 * wrong.
1230 static void outputStartStructureIfWanted(gmx_mtop_t *mtop, rvec *x, int ePBC, const matrix box)
1232 char *env = getenv("GMX_COMPELDUMP");
1234 if (env != nullptr)
1236 fprintf(stderr, "\n%s Found env.var. GMX_COMPELDUMP, will output CompEL starting structure made whole.\n"
1237 "%s In case of multimeric channels, please check whether they have the correct PBC representation.\n",
1238 SwS, SwSEmpty);
1240 write_sto_conf_mtop("CompELAssumedWholeConfiguration.pdb", *mtop->name, mtop, x, nullptr, ePBC, box);
1245 /*! \brief Initialize the swapstate structure, used for checkpoint writing.
1247 * The swapstate struct stores the information we need to make the channels
1248 * whole again after restarts from a checkpoint file. Here we do the following:
1249 * a) If we did not start from .cpt, we prepare the struct for proper .cpt writing,
1250 * b) if we did start from .cpt, we copy over the last whole structures from .cpt,
1251 * c) in any case, for subsequent checkpoint writing, we set the pointers in
1252 * swapstate to the x_old arrays, which contain the correct PBC representation of
1253 * multimeric channels at the last time step.
1255 static void init_swapstate(
1256 swaphistory_t *swapstate,
1257 t_swapcoords *sc,
1258 gmx_mtop_t *mtop,
1259 const rvec *x, /* the initial positions */
1260 const matrix box,
1261 t_inputrec *ir)
1263 rvec *x_pbc = nullptr; /* positions of the whole MD system with molecules made whole */
1264 t_swapgrp *g;
1265 t_swap *s;
1268 s = sc->si_priv;
1270 /* We always need the last whole positions such that
1271 * in the next time step we can make the channels whole again in PBC */
1272 if (swapstate->bFromCpt)
1274 /* Copy the last whole positions of each channel from .cpt */
1275 g = &(s->group[eGrpSplit0]);
1276 for (size_t i = 0; i < g->atomset.numAtomsGlobal(); i++)
1278 copy_rvec(swapstate->xc_old_whole[eChan0][i], g->xc_old[i]);
1280 g = &(s->group[eGrpSplit1]);
1281 for (size_t i = 0; i < g->atomset.numAtomsGlobal(); i++)
1283 copy_rvec(swapstate->xc_old_whole[eChan1][i], g->xc_old[i]);
1286 else
1288 swapstate->eSwapCoords = ir->eSwapCoords;
1290 /* Set the number of ion types and allocate memory for checkpointing */
1291 swapstate->nIonTypes = s->ngrp - eSwapFixedGrpNR;
1292 snew(swapstate->ionType, swapstate->nIonTypes);
1294 /* Store the total number of ions of each type in the swapstateIons
1295 * structure that is accessible during checkpoint writing */
1296 for (int ii = 0; ii < swapstate->nIonTypes; ii++)
1298 swapstateIons_t *gs = &swapstate->ionType[ii];
1299 gs->nMol = sc->grp[ii + eSwapFixedGrpNR].nat;
1302 /* Extract the initial split group positions. */
1304 /* Remove pbc, make molecule whole. */
1305 snew(x_pbc, mtop->natoms);
1306 copy_rvecn(x, x_pbc, 0, mtop->natoms);
1308 /* This can only make individual molecules whole, not multimers */
1309 do_pbc_mtop(ir->ePBC, box, mtop, x_pbc);
1311 /* Output the starting structure? */
1312 outputStartStructureIfWanted(mtop, x_pbc, ir->ePBC, box);
1314 /* If this is the first run (i.e. no checkpoint present) we assume
1315 * that the starting positions give us the correct PBC representation */
1316 for (int ig = eGrpSplit0; ig <= eGrpSplit1; ig++)
1318 g = &(s->group[ig]);
1319 for (size_t i = 0; i < g->atomset.numAtomsGlobal(); i++)
1321 copy_rvec(x_pbc[g->atomset.globalIndex()[i]], g->xc_old[i]);
1324 sfree(x_pbc);
1326 /* Prepare swapstate arrays for later checkpoint writing */
1327 swapstate->nat[eChan0] = s->group[eGrpSplit0].atomset.numAtomsGlobal();
1328 swapstate->nat[eChan1] = s->group[eGrpSplit1].atomset.numAtomsGlobal();
1331 /* For subsequent checkpoint writing, set the swapstate pointers to the xc_old
1332 * arrays that get updated at every swapping step */
1333 swapstate->xc_old_whole_p[eChan0] = &s->group[eGrpSplit0].xc_old;
1334 swapstate->xc_old_whole_p[eChan1] = &s->group[eGrpSplit1].xc_old;
1337 /*! \brief Determine the total charge imbalance resulting from the swap groups */
1338 static real getRequestedChargeImbalance(t_swap *s)
1340 int ig;
1341 real DeltaQ = 0.0;
1342 t_swapgrp *g;
1343 real particle_charge;
1344 real particle_number[eCompNR];
1346 // s->deltaQ = ( (-1) * s->comp[eCompA][eIonNEG].nat_req + s->comp[eCompA][eIonPOS].nat_req )
1347 // - ( (-1) * s->comp[eCompB][eIonNEG].nat_req + s->comp[eCompB][eIonPOS].nat_req );
1349 for (ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1351 g = &s->group[ig];
1353 particle_charge = g->q;
1354 particle_number[eCompA] = g->comp[eCompA].nMolReq;
1355 particle_number[eCompB] = g->comp[eCompB].nMolReq;
1357 DeltaQ += particle_charge * (particle_number[eCompA] - particle_number[eCompB]);
1360 return DeltaQ;
1364 /*! \brief Sorts anions and cations into two separate groups
1366 * This routine should be called for the 'anions' and 'cations' group,
1367 * of which the indices were lumped together in the older version of the code.
1369 static void copyIndicesToGroup(
1370 const int *indIons,
1371 int nIons,
1372 t_swapGroup *g,
1373 t_commrec *cr)
1375 g->nat = nIons;
1377 /* If explicit ion counts were requested in the .mdp file
1378 * (by setting positive values for the number of ions),
1379 * we can make an additional consistency check here */
1380 if ( (g->nmolReq[eCompA] < 0) && (g->nmolReq[eCompB] < 0) )
1382 if (g->nat != (g->nmolReq[eCompA] + g->nmolReq[eCompB]) )
1384 gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
1385 "%s Inconsistency while importing swap-related data from an old input file version.\n"
1386 "%s The requested ion counts in compartments A (%d) and B (%d)\n"
1387 "%s do not add up to the number of ions (%d) of this type for the group '%s'.\n",
1388 SwS, SwSEmpty, g->nmolReq[eCompA], g->nmolReq[eCompB], SwSEmpty, g->nat, g->molname);
1392 srenew(g->ind, g->nat);
1393 for (int i = 0; i < g->nat; i++)
1395 g->ind[i] = indIons[i];
1400 /*! \brief Converts old .tpr file CompEL contents to new data layout.
1402 * If we have read an old .tpr file (tpxv <= tpxv_CompElPolyatomicIonsAndMultipleIonTypes),
1403 * anions and cations are stored together in group #3. In the new
1404 * format we store each ion type in a separate group.
1405 * The 'classic' groups are:
1406 * #0 split group 0 - OK
1407 * #1 split group 1 - OK
1408 * #2 solvent - OK
1409 * #3 anions - contains also cations, needs to be converted
1410 * #4 cations - empty before conversion
1413 static void convertOldToNewGroupFormat(
1414 t_swapcoords *sc,
1415 gmx_mtop_t *mtop,
1416 gmx_bool bVerbose,
1417 t_commrec *cr)
1419 t_swapGroup *g = &sc->grp[3];
1421 /* Loop through the atom indices of group #3 (anions) and put all indices
1422 * that belong to cations into the cation group.
1424 int nAnions = 0;
1425 int nCations = 0;
1426 int *indAnions = nullptr;
1427 int *indCations = nullptr;
1428 snew(indAnions, g->nat);
1429 snew(indCations, g->nat);
1431 int molb = 0;
1432 for (int i = 0; i < g->nat; i++)
1434 const t_atom &atom = mtopGetAtomParameters(mtop, g->ind[i], &molb);
1435 if (atom.q < 0)
1437 // This is an anion, add it to the list of anions
1438 indAnions[nAnions++] = g->ind[i];
1440 else
1442 // This is a cation, add it to the list of cations
1443 indCations[nCations++] = g->ind[i];
1447 if (bVerbose)
1449 fprintf(stdout, "%s Sorted %d ions into separate groups of %d anions and %d cations.\n",
1450 SwS, g->nat, nAnions, nCations);
1454 /* Now we have the correct lists of anions and cations.
1455 * Copy it to the right groups.
1457 copyIndicesToGroup(indAnions, nAnions, g, cr);
1458 g = &sc->grp[4];
1459 copyIndicesToGroup(indCations, nCations, g, cr);
1460 sfree(indAnions);
1461 sfree(indCations);
1465 /*! \brief Returns TRUE if we started from an old .tpr
1467 * Then we need to re-sort anions and cations into separate groups */
1468 static gmx_bool bConvertFromOldTpr(t_swapcoords *sc)
1470 // If the last group has no atoms it means we need to convert!
1471 return (sc->ngrp >= 5) && (0 == sc->grp[4].nat);
1475 void init_swapcoords(
1476 FILE *fplog,
1477 t_inputrec *ir,
1478 const char *fn,
1479 gmx_mtop_t *mtop,
1480 const t_state *globalState,
1481 ObservablesHistory *oh,
1482 t_commrec *cr,
1483 gmx::LocalAtomSetManager *atomSets,
1484 const gmx_output_env_t *oenv,
1485 const gmx::MdrunOptions &mdrunOptions)
1487 t_swapcoords *sc;
1488 t_swap *s;
1489 t_swapgrp *g;
1490 swapstateIons_t *gs;
1491 gmx_bool bAppend, bStartFromCpt;
1492 swaphistory_t *swapstate = nullptr;
1494 if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
1496 gmx_fatal(FARGS, "Position swapping is only implemented for domain decomposition!");
1499 bAppend = mdrunOptions.continuationOptions.appendFiles;
1500 bStartFromCpt = mdrunOptions.continuationOptions.startedFromCheckpoint;
1502 sc = ir->swap;
1503 sc->si_priv = new t_swap();
1504 s = sc->si_priv;
1506 if (mdrunOptions.rerun)
1508 if (PAR(cr))
1510 gmx_fatal(FARGS, "%s This module does not support reruns in parallel\nPlease request a serial run with -nt 1 / -np 1\n", SwS);
1513 fprintf(stderr, "%s Rerun - using every available frame\n", SwS);
1514 sc->nstswap = 1;
1515 sc->nAverage = 1; /* averaging makes no sense for reruns */
1518 if (MASTER(cr) && !bAppend)
1520 fprintf(fplog, "\nInitializing ion/water position exchanges\n");
1521 please_cite(fplog, "Kutzner2011b");
1524 switch (ir->eSwapCoords)
1526 case eswapX:
1527 s->swapdim = XX;
1528 break;
1529 case eswapY:
1530 s->swapdim = YY;
1531 break;
1532 case eswapZ:
1533 s->swapdim = ZZ;
1534 break;
1535 default:
1536 s->swapdim = -1;
1537 break;
1540 const gmx_bool bVerbose = mdrunOptions.verbose;
1542 // For compatibility with old .tpr files
1543 if (bConvertFromOldTpr(sc) )
1545 convertOldToNewGroupFormat(sc, mtop, bVerbose && MASTER(cr), cr);
1548 /* Copy some data and pointers to the group structures for convenience */
1549 /* Number of atoms in the group */
1550 s->ngrp = sc->ngrp;
1551 for (int i = 0; i < s->ngrp; i++)
1553 s->group.emplace_back(atomSets->add(gmx::ArrayRef<const int>( sc->grp[i].ind, sc->grp[i].ind+sc->grp[i].nat)));
1554 s->group[i].molname = sc->grp[i].molname;
1557 /* Check for overlapping atoms */
1558 check_swap_groups(s, mtop->natoms, bVerbose && MASTER(cr));
1560 /* Allocate space for the collective arrays for all groups */
1561 /* For the collective position array */
1562 for (int i = 0; i < s->ngrp; i++)
1564 g = &s->group[i];
1565 snew(g->xc, g->atomset.numAtomsGlobal());
1567 /* For the split groups (the channels) we need some extra memory to
1568 * be able to make the molecules whole even if they span more than
1569 * half of the box size. */
1570 if ( (i == eGrpSplit0) || (i == eGrpSplit1) )
1572 snew(g->xc_shifts, g->atomset.numAtomsGlobal());
1573 snew(g->xc_eshifts, g->atomset.numAtomsGlobal());
1574 snew(g->xc_old, g->atomset.numAtomsGlobal());
1578 if (MASTER(cr))
1580 if (oh->swapHistory == nullptr)
1582 oh->swapHistory = std::make_unique<swaphistory_t>(swaphistory_t {});
1584 swapstate = oh->swapHistory.get();
1586 init_swapstate(swapstate, sc, mtop, globalState->x.rvec_array(), globalState->box, ir);
1589 /* After init_swapstate we have a set of (old) whole positions for our
1590 * channels. Now transfer that to all nodes */
1591 if (PAR(cr))
1593 for (int ig = eGrpSplit0; ig <= eGrpSplit1; ig++)
1595 g = &(s->group[ig]);
1596 gmx_bcast((g->atomset.numAtomsGlobal())*sizeof((g->xc_old)[0]), g->xc_old, (cr));
1600 /* Make sure that all molecules in the solvent and ion groups contain the
1601 * same number of atoms each */
1602 for (int ig = eGrpSolvent; ig < s->ngrp; ig++)
1604 real charge;
1606 g = &(s->group[ig]);
1607 g->apm = get_group_apm_check(ig, s, MASTER(cr) && bVerbose, mtop);
1609 /* Since all molecules of a group are equal, we only need enough space
1610 * to determine properties of a single molecule at at time */
1611 snew(g->m, g->apm); /* For the center of mass */
1612 charge = 0; /* To determine the charge imbalance */
1613 int molb = 0;
1614 for (int j = 0; j < g->apm; j++)
1616 const t_atom &atom = mtopGetAtomParameters(mtop, g->atomset.globalIndex()[j], &molb);
1617 g->m[j] = atom.m;
1618 charge += atom.q;
1620 /* Total charge of one molecule of this group: */
1621 g->q = charge;
1625 /* Need mass-weighted center of split group? */
1626 for (int j = eGrpSplit0; j <= eGrpSplit1; j++)
1628 g = &(s->group[j]);
1629 if (sc->massw_split[j])
1631 /* Save the split group masses if mass-weighting is requested */
1632 snew(g->m, g->atomset.numAtomsGlobal());
1633 int molb = 0;
1634 for (size_t i = 0; i < g->atomset.numAtomsGlobal(); i++)
1636 g->m[i] = mtopGetAtomMass(mtop, g->atomset.globalIndex()[i], &molb);
1641 /* Make a t_pbc struct on all nodes so that the molecules
1642 * chosen for an exchange can be made whole. */
1643 snew(s->pbc, 1);
1645 if (MASTER(cr))
1647 if (bVerbose)
1649 fprintf(stderr, "%s Opening output file %s%s\n", SwS, fn, bAppend ? " for appending" : "");
1652 s->fpout = gmx_fio_fopen(fn, bAppend ? "a" : "w" );
1654 if (!bAppend)
1656 xvgr_header(s->fpout, "Molecule counts", "Time (ps)", "counts", exvggtXNY, oenv);
1658 for (int ig = 0; ig < s->ngrp; ig++)
1660 g = &(s->group[ig]);
1661 fprintf(s->fpout, "# %s group '%s' contains %d atom%s",
1662 ig < eSwapFixedGrpNR ? eSwapFixedGrp_names[ig] : "Ion",
1663 g->molname, static_cast<int>(g->atomset.numAtomsGlobal()), (g->atomset.numAtomsGlobal() > 1) ? "s" : "");
1664 if (!(eGrpSplit0 == ig || eGrpSplit1 == ig) )
1666 fprintf(s->fpout, " with %d atom%s in each molecule of charge %g",
1667 g->apm, (g->apm > 1) ? "s" : "", g->q);
1669 fprintf(s->fpout, ".\n");
1672 fprintf(s->fpout, "#\n# Initial positions of split groups:\n");
1675 for (int j = eGrpSplit0; j <= eGrpSplit1; j++)
1677 g = &(s->group[j]);
1678 for (size_t i = 0; i < g->atomset.numAtomsGlobal(); i++)
1680 copy_rvec(globalState->x[sc->grp[j].ind[i]], g->xc[i]);
1682 /* xc has the correct PBC representation for the two channels, so we do
1683 * not need to correct for that */
1684 get_center(g->xc, g->m, g->atomset.numAtomsGlobal(), g->center);
1685 if (!bAppend)
1687 fprintf(s->fpout, "# %s group %s-center %5f nm\n", eSwapFixedGrp_names[j],
1688 DimStr[s->swapdim], g->center[s->swapdim]);
1692 if (!bAppend)
1694 if ( (0 != sc->bulkOffset[eCompA]) || (0 != sc->bulkOffset[eCompB]) )
1696 fprintf(s->fpout, "#\n");
1697 fprintf(s->fpout, "# You provided an offset for the position of the bulk layer(s).\n");
1698 fprintf(s->fpout, "# That means the layers to/from which ions and water molecules are swapped\n");
1699 fprintf(s->fpout, "# are not midway (= at 0.0) between the compartment-defining layers (at +/- 1.0).\n");
1700 fprintf(s->fpout, "# bulk-offsetA = %g\n", sc->bulkOffset[eCompA]);
1701 fprintf(s->fpout, "# bulk-offsetB = %g\n", sc->bulkOffset[eCompB]);
1704 fprintf(s->fpout, "#\n");
1705 fprintf(s->fpout, "# Split0 cylinder radius %f nm, up %f nm, down %f nm\n",
1706 sc->cyl0r, sc->cyl0u, sc->cyl0l);
1707 fprintf(s->fpout, "# Split1 cylinder radius %f nm, up %f nm, down %f nm\n",
1708 sc->cyl1r, sc->cyl1u, sc->cyl1l);
1710 fprintf(s->fpout, "#\n");
1711 if (!mdrunOptions.rerun)
1713 fprintf(s->fpout, "# Coupling constant (number of swap attempt steps to average over): %d (translates to %f ps).\n",
1714 sc->nAverage, sc->nAverage*sc->nstswap*ir->delta_t);
1715 fprintf(s->fpout, "# Threshold is %f\n", sc->threshold);
1716 fprintf(s->fpout, "#\n");
1717 fprintf(s->fpout, "# Remarks about which atoms passed which channel use global atoms numbers starting at one.\n");
1721 else
1723 s->fpout = nullptr;
1726 /* Allocate memory to remember the past particle counts for time averaging */
1727 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1729 g = &(s->group[ig]);
1730 for (int ic = 0; ic < eCompNR; ic++)
1732 snew(g->comp[ic].nMolPast, sc->nAverage);
1736 /* Get the initial particle concentrations and let the other nodes know */
1737 if (MASTER(cr))
1739 if (bStartFromCpt)
1741 get_initial_ioncounts_from_cpt(ir, swapstate, cr, bVerbose);
1743 else
1745 fprintf(stderr, "%s Determining initial numbers of ions per compartment.\n", SwS);
1746 get_initial_ioncounts(ir, globalState->x.rvec_array(), globalState->box, cr, mdrunOptions.rerun);
1749 /* Prepare (further) checkpoint writes ... */
1750 if (bStartFromCpt)
1752 /* Consistency check */
1753 if (swapstate->nAverage != sc->nAverage)
1755 gmx_fatal(FARGS, "%s Ion count averaging steps mismatch! checkpoint: %d, tpr: %d",
1756 SwS, swapstate->nAverage, sc->nAverage);
1759 else
1761 swapstate->nAverage = sc->nAverage;
1763 fprintf(stderr, "%s Setting pointers for checkpoint writing\n", SwS);
1764 for (int ic = 0; ic < eCompNR; ic++)
1766 for (int ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1768 g = &s->group[ig];
1769 gs = &swapstate->ionType[ig - eSwapFixedGrpNR];
1771 gs->nMolReq_p[ic] = &(g->comp[ic].nMolReq);
1772 gs->nMolPast_p[ic] = &(g->comp[ic].nMolPast[0]);
1773 gs->inflow_net_p[ic] = &(g->comp[ic].inflow_net);
1777 /* Determine the total charge imbalance */
1778 s->deltaQ = getRequestedChargeImbalance(s);
1780 if (bVerbose)
1782 fprintf(stderr, "%s Requested charge imbalance is Q(A) - Q(B) = %g e.\n", SwS, s->deltaQ);
1784 if (!bAppend)
1786 fprintf(s->fpout, "# Requested charge imbalance is Q(A)-Q(B) = %g e.\n", s->deltaQ);
1790 if (PAR(cr))
1792 bc_initial_concentrations(cr, ir->swap);
1795 /* Update the time-averaged number of molecules for all groups and compartments */
1796 for (int ig = eSwapFixedGrpNR; ig < sc->ngrp; ig++)
1798 g = &s->group[ig];
1799 for (int ic = 0; ic < eCompNR; ic++)
1801 update_time_window(&g->comp[ic], sc->nAverage, -1);
1805 /* Initialize arrays that keep track of through which channel the ions go */
1806 detect_flux_per_channel_init(s, swapstate, bStartFromCpt);
1808 /* We need to print the legend if we open this file for the first time. */
1809 if (MASTER(cr) && !bAppend)
1811 print_ionlist_legend(ir, oenv);
1816 void finish_swapcoords(t_swapcoords *sc)
1818 if (sc->si_priv->fpout)
1820 // Close the swap output file
1821 gmx_fio_fclose(sc->si_priv->fpout);
1825 /*! \brief Do we need to swap a molecule in any of the ion groups with a water molecule at this step?
1827 * From the requested and average molecule counts we determine whether a swap is needed
1828 * at this time step.
1830 static gmx_bool need_swap(t_swapcoords *sc)
1832 t_swap *s;
1833 int ic, ig;
1834 t_swapgrp *g;
1836 s = sc->si_priv;
1838 for (ig = eSwapFixedGrpNR; ig < sc->ngrp; ig++)
1840 g = &s->group[ig];
1842 for (ic = 0; ic < eCompNR; ic++)
1844 if (g->comp[ic].nMolReq - g->comp[ic].nMolAv >= sc->threshold)
1846 return TRUE;
1850 return FALSE;
1854 /*! \brief Return the index of an atom or molecule suitable for swapping.
1856 * Returns the index of an atom that is far off the compartment boundaries,
1857 * that is near to the bulk layer to/from which the swaps take place.
1858 * Other atoms of the molecule (if any) will directly follow the returned index.
1860 * \param[in] comp Structure containing compartment-specific data.
1861 * \param[in] molname Name of the molecule.
1863 * \returns Index of the first atom of the molecule chosen for a position exchange.
1865 static int get_index_of_distant_atom(
1866 t_compartment *comp,
1867 const char molname[])
1869 int ibest = -1;
1870 real d = GMX_REAL_MAX;
1873 /* comp->nat contains the original number of atoms in this compartment
1874 * prior to doing any swaps. Some of these atoms may already have been
1875 * swapped out, but then they are marked with a distance of GMX_REAL_MAX
1877 for (int iMol = 0; iMol < comp->nMolBefore; iMol++)
1879 if (comp->dist[iMol] < d)
1881 ibest = iMol;
1882 d = comp->dist[ibest];
1886 if (ibest < 0)
1888 gmx_fatal(FARGS, "Could not get index of %s atom. Compartment contains %d %s molecules before swaps.",
1889 molname, comp->nMolBefore, molname);
1892 /* Set the distance of this index to infinity such that it won't get selected again in
1893 * this time step
1895 comp->dist[ibest] = GMX_REAL_MAX;
1897 return comp->ind[ibest];
1901 /*! \brief Swaps centers of mass and makes molecule whole if broken */
1902 static void translate_positions(
1903 rvec *x,
1904 int apm,
1905 rvec old_com,
1906 rvec new_com,
1907 t_pbc *pbc)
1909 int i;
1910 rvec reference, dx, correctPBCimage;
1913 /* Use the first atom as the reference for PBC */
1914 copy_rvec(x[0], reference);
1916 for (i = 0; i < apm; i++)
1918 /* PBC distance between position and reference */
1919 pbc_dx(pbc, x[i], reference, dx);
1921 /* Add PBC distance to reference */
1922 rvec_add(reference, dx, correctPBCimage);
1924 /* Subtract old_com from correct image and add new_com */
1925 rvec_dec(correctPBCimage, old_com);
1926 rvec_inc(correctPBCimage, new_com);
1928 copy_rvec(correctPBCimage, x[i]);
1933 /*! \brief Write back the the modified local positions from the collective array to the official positions. */
1934 static void apply_modified_positions(
1935 swap_group *g,
1936 rvec x[])
1938 auto collectiveIndex = g->atomset.collectiveIndex().begin();
1939 for (const auto localIndex : g->atomset.localIndex())
1941 /* Copy the possibly modified position */
1942 copy_rvec(g->xc[*collectiveIndex], x[localIndex]);
1943 ++collectiveIndex;
1948 gmx_bool do_swapcoords(
1949 t_commrec *cr,
1950 int64_t step,
1951 double t,
1952 t_inputrec *ir,
1953 gmx_wallcycle *wcycle,
1954 rvec x[],
1955 matrix box,
1956 gmx_bool bVerbose,
1957 gmx_bool bRerun)
1959 t_swapcoords *sc;
1960 t_swap *s;
1961 int j, ic, ig, nswaps;
1962 int thisC, otherC; /* Index into this compartment and the other one */
1963 gmx_bool bSwap = FALSE;
1964 t_swapgrp *g, *gsol;
1965 int isol, iion;
1966 rvec com_solvent, com_particle; /* solvent and swap molecule's center of mass */
1969 wallcycle_start(wcycle, ewcSWAP);
1971 sc = ir->swap;
1972 s = sc->si_priv;
1974 set_pbc(s->pbc, ir->ePBC, box);
1976 /* Assemble the positions of the split groups, i.e. the channels.
1977 * Here we also pass a shifts array to communicate_group_positions(), so that it can make
1978 * the molecules whole even in cases where they span more than half of the box in
1979 * any dimension */
1980 for (ig = eGrpSplit0; ig <= eGrpSplit1; ig++)
1982 g = &(s->group[ig]);
1983 communicate_group_positions(cr, g->xc, g->xc_shifts, g->xc_eshifts, TRUE,
1984 x, g->atomset.numAtomsGlobal(), g->atomset.numAtomsLocal(), g->atomset.localIndex().data(), g->atomset.collectiveIndex().data(), g->xc_old, box);
1986 get_center(g->xc, g->m, g->atomset.numAtomsGlobal(), g->center); /* center of split groups == channels */
1989 /* Assemble the positions of the ions (ig = 3, 4, ...). These molecules should
1990 * be small and we can always make them whole with a simple distance check.
1991 * Therefore we pass NULL as third argument. */
1992 for (ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
1994 g = &(s->group[ig]);
1995 communicate_group_positions(cr, g->xc, nullptr, nullptr, FALSE,
1996 x, g->atomset.numAtomsGlobal(), g->atomset.numAtomsLocal(), g->atomset.localIndex().data(), g->atomset.collectiveIndex().data(), nullptr, nullptr);
1998 /* Determine how many ions of this type each compartment contains */
1999 sortMoleculesIntoCompartments(g, cr, sc, box, step, s->fpout, bRerun, FALSE);
2002 /* Output how many ions are in the compartments */
2003 if (MASTER(cr))
2005 print_ionlist(s, t, "");
2008 /* If we are doing a rerun, we are finished here, since we cannot perform
2009 * swaps anyway */
2010 if (bRerun)
2012 return FALSE;
2015 /* Do we have to perform a swap? */
2016 bSwap = need_swap(sc);
2017 if (bSwap)
2019 /* Since we here know that we have to perform ion/water position exchanges,
2020 * we now assemble the solvent positions */
2021 g = &(s->group[eGrpSolvent]);
2022 communicate_group_positions(cr, g->xc, nullptr, nullptr, FALSE,
2023 x, g->atomset.numAtomsGlobal(), g->atomset.numAtomsLocal(), g->atomset.localIndex().data(), g->atomset.collectiveIndex().data(), nullptr, nullptr);
2025 /* Determine how many molecules of solvent each compartment contains */
2026 sortMoleculesIntoCompartments(g, cr, sc, box, step, s->fpout, bRerun, TRUE);
2028 /* Save number of solvent molecules per compartment prior to any swaps */
2029 g->comp[eCompA].nMolBefore = g->comp[eCompA].nMol;
2030 g->comp[eCompB].nMolBefore = g->comp[eCompB].nMol;
2032 for (ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
2034 g = &(s->group[ig]);
2036 for (ic = 0; ic < eCompNR; ic++)
2038 /* Determine in which compartment ions are missing and where they are too many */
2039 g->vacancy[ic] = g->comp[ic].nMolReq - g->comp[ic].nMolAv;
2041 /* Save number of ions per compartment prior to swaps */
2042 g->comp[ic].nMolBefore = g->comp[ic].nMol;
2046 /* Now actually perform the particle exchanges, one swap group after another */
2047 gsol = &s->group[eGrpSolvent];
2048 for (ig = eSwapFixedGrpNR; ig < s->ngrp; ig++)
2050 nswaps = 0;
2051 g = &s->group[ig];
2052 for (thisC = 0; thisC < eCompNR; thisC++)
2054 /* Index to the other compartment */
2055 otherC = (thisC+1) % eCompNR;
2057 while (g->vacancy[thisC] >= sc->threshold)
2059 /* Swap in an ion */
2061 /* Get the xc-index of the first atom of a solvent molecule of this compartment */
2062 isol = get_index_of_distant_atom(&gsol->comp[thisC], gsol->molname);
2064 /* Get the xc-index of a particle from the other compartment */
2065 iion = get_index_of_distant_atom(&g->comp[otherC], g->molname);
2067 get_molecule_center(&gsol->xc[isol], gsol->apm, gsol->m, com_solvent, s->pbc);
2068 get_molecule_center(&g->xc[iion], g->apm, g->m, com_particle, s->pbc);
2070 /* Subtract solvent molecule's center of mass and add swap particle's center of mass */
2071 translate_positions(&gsol->xc[isol], gsol->apm, com_solvent, com_particle, s->pbc);
2072 /* Similarly for the swap particle, subtract com_particle and add com_solvent */
2073 translate_positions(&g->xc[iion], g->apm, com_particle, com_solvent, s->pbc);
2075 /* Keep track of the changes */
2076 g->vacancy[thisC ]--;
2077 g->vacancy[otherC]++;
2078 g->comp [thisC ].nMol++;
2079 g->comp [otherC].nMol--;
2080 g->comp [thisC ].inflow_net++;
2081 g->comp [otherC].inflow_net--;
2082 /* Correct the past time window to still get the right averages from now on */
2083 g->comp [thisC ].nMolAv++;
2084 g->comp [otherC].nMolAv--;
2085 for (j = 0; j < sc->nAverage; j++)
2087 g->comp[thisC ].nMolPast[j]++;
2088 g->comp[otherC].nMolPast[j]--;
2090 /* Clear ion history */
2091 if (MASTER(cr))
2093 int iMol = iion / g->apm;
2094 g->channel_label[iMol] = eChHistPassedNone;
2095 g->comp_from[iMol] = eDomainNotset;
2097 /* That was the swap */
2098 nswaps++;
2102 if (nswaps && bVerbose)
2104 fprintf(stderr, "%s Performed %d swap%s in step %" PRId64 " for iontype %s.\n",
2105 SwS, nswaps, nswaps > 1 ? "s" : "", step, g->molname);
2109 if (s->fpout != nullptr)
2111 print_ionlist(s, t, " # after swap");
2114 /* For the solvent and user-defined swap groups, each rank writes back its
2115 * (possibly modified) local positions to the official position array. */
2116 for (ig = eGrpSolvent; ig < s->ngrp; ig++)
2118 g = &s->group[ig];
2119 apply_modified_positions(g, x);
2122 } /* end of if(bSwap) */
2124 wallcycle_stop(wcycle, ewcSWAP);
2126 return bSwap;