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.
37 * Implements functions in swapcoords.h.
39 * \author Carsten Kutzner <ckutzne@gwdg.de>
40 * \ingroup module_swap
44 #include "swapcoords.h"
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/mdrunutility/handlerestart.h"
64 #include "gromacs/mdtypes/commrec.h"
65 #include "gromacs/mdtypes/imdmodule.h"
66 #include "gromacs/mdtypes/inputrec.h"
67 #include "gromacs/mdtypes/md_enums.h"
68 #include "gromacs/mdtypes/mdrunoptions.h"
69 #include "gromacs/mdtypes/observableshistory.h"
70 #include "gromacs/mdtypes/state.h"
71 #include "gromacs/mdtypes/swaphistory.h"
72 #include "gromacs/pbcutil/pbc.h"
73 #include "gromacs/timing/wallcycle.h"
74 #include "gromacs/topology/mtop_lookup.h"
75 #include "gromacs/topology/topology.h"
76 #include "gromacs/utility/cstringutil.h"
77 #include "gromacs/utility/fatalerror.h"
78 #include "gromacs/utility/pleasecite.h"
79 #include "gromacs/utility/smalloc.h"
80 #include "gromacs/utility/snprintf.h"
82 static const char *SwS
= {"SWAP:"}; /**< For output that comes from the swap module */
83 static const char *SwSEmpty
= {" "}; /**< Placeholder for multi-line output */
84 static const char* CompStr
[eCompNR
] = {"A", "B" }; /**< Compartment name */
85 static const char *SwapStr
[eSwapTypesNR
+1] = { "", "X-", "Y-", "Z-", nullptr}; /**< Name for the swap types. */
86 static const char *DimStr
[DIM
+1] = { "X", "Y", "Z", nullptr}; /**< Name for the swap dimension. */
88 /** Keep track of through which channel the ions have passed */
89 enum eChannelHistory
{
90 eChHistPassedNone
, eChHistPassedCh0
, eChHistPassedCh1
, eChHistNr
92 static const char* ChannelString
[eChHistNr
] = { "none", "channel0", "channel1" }; /**< Name for the channels */
94 /*! \brief Domain identifier.
96 * Keeps track of from which compartment the ions came before passing the
100 eDomainNotset
, eDomainA
, eDomainB
, eDomainNr
102 static const char* DomainString
[eDomainNr
] = { "not_assigned", "Domain_A", "Domain_B" }; /**< Name for the domains */
108 * \brief Implement Computational Electrophysiology swapping.
110 class SwapCoordinates final
: public IMDModule
113 IMdpOptionProvider
*mdpOptionProvider() override
{ return nullptr; }
114 IMDOutputProvider
*outputProvider() override
{ return nullptr; }
115 void initForceProviders(ForceProviders
* /* forceProviders */) override
{}
118 std::unique_ptr
<IMDModule
> createSwapCoordinatesModule()
120 return std::make_unique
<SwapCoordinates
>();
127 * Structure containing compartment-specific data.
129 typedef struct swap_compartment
131 int nMol
; /**< Number of ion or water molecules detected
132 in this compartment. */
133 int nMolBefore
; /**< Number of molecules before swapping. */
134 int nMolReq
; /**< Requested number of molecules in compartment. */
135 real nMolAv
; /**< Time-averaged number of molecules matching
136 the compartment conditions. */
137 int *nMolPast
; /**< Past molecule counts for time-averaging. */
138 int *ind
; /**< Indices to collective array of atoms. */
139 real
*dist
; /**< Distance of atom to bulk layer, which is
140 normally the center layer of the compartment */
141 int nalloc
; /**< Allocation size for ind array. */
142 int inflow_net
; /**< Net inflow of ions into this compartment. */
147 * This structure contains data needed for the groups involved in swapping:
148 * split group 0, split group 1, solvent group, ion groups.
150 typedef struct swap_group
152 /*!\brief Construct a swap group given the managed swap atoms.
154 * \param[in] atomset Managed indices of atoms that are part of the swap group.
156 swap_group(const gmx::LocalAtomSet
&atomset
);
157 char *molname
= nullptr; /**< Name of the group or ion type */
158 int apm
= 0; /**< Number of atoms in each molecule */
159 gmx::LocalAtomSet atomset
; /**< The atom indices in the swap group */
160 rvec
*xc
= nullptr; /**< Collective array of group atom positions (size nat) */
161 ivec
*xc_shifts
= nullptr; /**< Current (collective) shifts (size nat) */
162 ivec
*xc_eshifts
= nullptr; /**< Extra shifts since last DD step (size nat) */
163 rvec
*xc_old
= nullptr; /**< Old (collective) positions (size nat) */
164 real q
= 0.; /**< Total charge of one molecule of this group */
165 real
*m
= nullptr; /**< Masses (can be omitted, size apm) */
166 unsigned char *comp_from
= nullptr; /**< (Collective) Stores from which compartment this
167 molecule has come. This way we keep track of
168 through which channel an ion permeates
169 (size nMol = nat/apm) */
170 unsigned char *comp_now
= nullptr; /**< In which compartment this ion is now (size nMol) */
171 unsigned char *channel_label
= nullptr; /**< Which channel was passed at last by this ion?
173 rvec center
; /**< Center of the group; COM if masses are used */
174 t_compartment comp
[eCompNR
]; /**< Distribution of particles of this group across
175 the two compartments */
176 real vacancy
[eCompNR
]; /**< How many molecules need to be swapped in? */
177 int fluxfromAtoB
[eChanNR
]; /**< Net flux of ions per channel */
178 int nCyl
[eChanNR
]; /**< Number of ions residing in a channel */
179 int nCylBoth
= 0; /**< Ions assigned to cyl0 and cyl1. Not good. */
182 t_swapgrp::swap_group(const gmx::LocalAtomSet
& atomset
) : atomset
{
188 for (int compartment
= eCompA
; compartment
< eCompNR
; ++compartment
)
190 comp
[compartment
] = {};
191 vacancy
[compartment
] = 0;
193 for (int channel
= eChan0
; channel
< eChanNR
; ++channel
)
195 fluxfromAtoB
[channel
] = 0;
201 * Main (private) data structure for the position swapping protocol.
205 int swapdim
; /**< One of XX, YY, ZZ */
206 t_pbc
*pbc
; /**< Needed to make molecules whole. */
207 FILE *fpout
; /**< Output file. */
208 int ngrp
; /**< Number of t_swapgrp groups */
209 std::vector
<t_swapgrp
> group
; /**< Separate groups for channels, solvent, ions */
210 int fluxleak
; /**< Flux not going through any of the channels. */
211 real deltaQ
; /**< The charge imbalance between the compartments. */
216 /*! \brief Check whether point is in channel.
218 * A channel is a cylinder defined by a disc
219 * with radius r around its center c. The thickness of the cylinder is
226 * <---------c--------->
232 * \param[in] point The position (xyz) under consideration.
233 * \param[in] center The center of the cylinder.
234 * \param[in] d_up The upper extension of the cylinder.
235 * \param[in] d_down The lower extension.
236 * \param[in] r_cyl2 Cylinder radius squared.
237 * \param[in] pbc Structure with info about periodic boundary conditions.
238 * \param[in] normal The membrane normal direction is typically 3, i.e. z, but can be x or y also.
240 * \returns Whether the point is inside the defined cylindric channel.
242 static gmx_bool
is_in_channel(
252 int plane1
, plane2
; /* Directions tangential to membrane */
255 plane1
= (normal
+ 1) % 3; /* typically 0, i.e. XX */
256 plane2
= (normal
+ 2) % 3; /* typically 1, i.e. YY */
258 /* Get the distance vector dr between the point and the center of the cylinder */
259 pbc_dx(pbc
, point
, center
, dr
); /* This puts center in the origin */
261 /* Check vertical direction */
262 if ( (dr
[normal
] > d_up
) || (dr
[normal
] < -d_down
) )
267 /* Check radial direction */
268 if ( (dr
[plane1
]*dr
[plane1
] + dr
[plane2
]*dr
[plane2
]) > r_cyl2
)
273 /* All check passed, this point is in the cylinder */
278 /*! \brief Prints output to CompEL output file.
280 * Prints to swap output file how many ions are in each compartment,
281 * where the centers of the split groups are, and how many ions of each type
282 * passed the channels.
284 static void print_ionlist(
287 const char comment
[])
290 fprintf(s
->fpout
, "%12.5e", time
);
292 // Output number of molecules and difference to reference counts for each
293 // compartment and ion type
294 for (int iComp
= 0; iComp
< eCompNR
; iComp
++)
296 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
298 t_compartment
*comp
= &s
->group
[ig
].comp
[iComp
];
300 fprintf(s
->fpout
, "%10d%10.1f%10d", comp
->nMol
, comp
->nMolAv
- comp
->nMolReq
, comp
->inflow_net
);
304 // Output center of split groups
305 fprintf(s
->fpout
, "%10g%10g",
306 s
->group
[eGrpSplit0
].center
[s
->swapdim
],
307 s
->group
[eGrpSplit1
].center
[s
->swapdim
]);
309 // Output ion flux for each channel and ion type
310 for (int iChan
= 0; iChan
< eChanNR
; iChan
++)
312 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
314 t_swapgrp
*g
= &s
->group
[ig
];
315 fprintf(s
->fpout
, "%10d", g
->fluxfromAtoB
[iChan
]);
319 /* Output the number of molecules that leaked from A to B */
320 fprintf(s
->fpout
, "%10d", s
->fluxleak
);
322 fprintf(s
->fpout
, "%s\n", comment
);
326 /*! \brief Get the center of a group of nat atoms.
328 * Since with PBC an atom group might not be whole, use the first atom as the
329 * reference atom and determine the center with respect to this reference.
331 static void get_molecule_center(
339 rvec weightedPBCimage
;
341 rvec reference
, correctPBCimage
, dx
;
344 /* Use the first atom as the reference and put other atoms near that one */
345 /* This does not work for large molecules that span > half of the box! */
346 copy_rvec(x
[0], reference
);
348 /* Calculate either the weighted center or simply the center of geometry */
351 for (i
= 0; i
< nat
; i
++)
353 /* PBC distance between position and reference */
354 pbc_dx(pbc
, x
[i
], reference
, dx
);
356 /* Add PBC distance to reference */
357 rvec_add(reference
, dx
, correctPBCimage
);
359 /* Take weight into account */
360 if (nullptr == weights
)
369 svmul(wi
, correctPBCimage
, weightedPBCimage
);
372 rvec_inc(center
, weightedPBCimage
);
376 svmul(1.0/wsum
, center
, center
);
381 /*! \brief Return TRUE if position x of ion (or water) is found in the compartment,
382 * i.e. between w1 and w2.
384 * One can define and additional offset "b" if one wants to exchange ions/water
385 * to or from a plane not directly in the middle of w1 and w2. The offset can be
386 * in ]-1.0, ..., +1.0 [.
387 * A bulkOffset of 0.0 means 'no offset', so the swap-layer is directly in the
388 * middle between w1 and w2. Offsets -1.0 < b < 0.0 will yield swaps nearer to w1,
389 * whereas offsets 0.0 < 0 < +1.0 will yield swaps nearer to w2.
393 * ||--------------+-------------|-------------+------------------------||
394 * w1 ? ? ? ? ? ? ? ? ? ? ? w2
395 * ||--------------+-------------|----b--------+------------------------||
400 * \param[in] w1 Position of 'wall' atom 1.
401 * \param[in] w2 Position of 'wall' atom 2.
402 * \param[in] x Position of the ion or the water molecule under consideration.
403 * \param[in] l Length of the box, from || to || in the sketch.
404 * \param[in] bulkOffset Where is the bulk layer "b" to be found between w1 and w2?
405 * \param[out] distance_from_b Distance of x to the bulk layer "b".
407 * \returns TRUE if x is between w1 and w2.
409 * Also computes the distance of x to the compartment center (the layer that is
410 * normally situated in the middle of w1 and w2 that would be considered as having
411 * the bulk concentration of ions).
413 static gmx_bool
compartment_contains_atom(
419 real
*distance_from_b
)
425 /* First set the origin in the middle of w1 and w2 */
432 /* Now choose the PBC image of x that is closest to the origin: */
443 *distance_from_b
= static_cast<real
>(fabs(x
- bulkOffset
*0.5*width
));
445 /* Return TRUE if we now are in area "????" */
446 return (x
>= w1
) && (x
< w2
);
450 /*! \brief Updates the time-averaged number of ions in a compartment. */
451 static void update_time_window(t_compartment
*comp
, int values
, int replace
)
457 /* Put in the new value */
460 comp
->nMolPast
[replace
] = comp
->nMol
;
463 /* Compute the new time-average */
465 for (i
= 0; i
< values
; i
++)
467 average
+= comp
->nMolPast
[i
];
470 comp
->nMolAv
= average
;
474 /*! \brief Add the atom with collective index ci to the atom list in compartment 'comp'.
476 * \param[in] ci Index of this ion in the collective xc array.
477 * \param[inout] comp Compartment to add this atom to.
478 * \param[in] distance Shortest distance of this atom to the bulk layer,
479 * from which ion/water pairs are selected for swapping.
481 static void add_to_list(
488 if (nr
>= comp
->nalloc
)
490 comp
->nalloc
= over_alloc_dd(nr
+1);
491 srenew(comp
->ind
, comp
->nalloc
);
492 srenew(comp
->dist
, comp
->nalloc
);
495 comp
->dist
[nr
] = distance
;
500 /*! \brief Determine the compartment boundaries from the channel centers. */
501 static void get_compartment_boundaries(
505 real
*left
, real
*right
)
508 real leftpos
, rightpos
, leftpos_orig
;
513 gmx_fatal(FARGS
, "No compartment %c.", c
+'A');
516 pos0
= s
->group
[eGrpSplit0
].center
[s
->swapdim
];
517 pos1
= s
->group
[eGrpSplit1
].center
[s
->swapdim
];
530 /* This gets us the other compartment: */
533 leftpos_orig
= leftpos
;
535 rightpos
= leftpos_orig
+ box
[s
->swapdim
][s
->swapdim
];
543 /*! \brief Determine the per-channel ion flux.
545 * To determine the flux through the individual channels, we
546 * remember the compartment and channel history of each ion. An ion can be
547 * either in channel0 or channel1, or in the remaining volume of compartment
551 * +-----------------+
554 * ||||||||||0|||||||| bilayer with channel 0
559 * |||||1||||||||||||| bilayer with channel 1
562 * +-----------------+
566 static void detect_flux_per_channel(
571 unsigned char *comp_now
,
572 unsigned char *comp_from
,
573 unsigned char *channel_label
,
583 gmx_bool in_cyl0
, in_cyl1
;
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! */
597 *comp_now
= eDomainNotset
;
598 *comp_from
= eDomainNotset
;
599 *channel_label
= eChHistPassedNone
;
603 /* Ion is in channel 0 now */
604 *channel_label
= eChHistPassedCh0
;
605 *comp_now
= eDomainNotset
;
610 /* Ion is in channel 1 now */
611 *channel_label
= eChHistPassedCh1
;
612 *comp_now
= eDomainNotset
;
617 /* Ion is not in any of the channels, so it must be in domain A or B */
620 *comp_now
= eDomainA
;
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
:
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
]);
650 fprintf(stderr
, ", possibly due to a swap in the original simulation.\n");
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
]);
663 case eChHistPassedCh0
:
664 case eChHistPassedCh1
:
665 if (*channel_label
== eChHistPassedCh0
)
674 if (eDomainA
== *comp_from
)
676 g
->fluxfromAtoB
[chan_nr
]++;
680 g
->fluxfromAtoB
[chan_nr
]--;
682 fprintf(fpout
, "# Atom nr. %d finished passing %s.\n", iAtom
, ChannelString
[*channel_label
]);
685 gmx_fatal(FARGS
, "%s Unknown channel history entry for ion type '%s'\n",
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(
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
++)
720 /* Get lists of atoms that match criteria for this compartment */
721 get_compartment_boundaries(comp
, s
, 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
++)
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
, s
, cyl0_r2
, cyl1_r2
, step
, bRerun
, fpout
);
750 nMolNotInComp
[comp
]++;
753 /* Correct the time-averaged number of ions in the compartment */
756 update_time_window(&g
->comp
[comp
], sc
->nAverage
, replace
);
760 /* Flux detection warnings */
761 if (MASTER(cr
) && !bIsSolvent
)
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
);
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 const t_inputrec
*ir
,
804 const rvec x
[], /* the initial positions */
814 /* Loop over the user-defined (ion) groups */
815 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
819 /* Copy the initial positions of the atoms in the group
820 * to the collective array so that we can compartmentalize */
821 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal(); i
++)
823 int ind
= g
->atomset
.globalIndex()[i
];
824 copy_rvec(x
[ind
], g
->xc
[i
]);
827 /* Set up the compartments and get lists of atoms in each compartment */
828 sortMoleculesIntoCompartments(g
, cr
, sc
, s
, box
, 0, s
->fpout
, bRerun
, FALSE
);
830 /* Set initial molecule counts if requested (as signaled by "-1" value) */
831 for (int ic
= 0; ic
< eCompNR
; ic
++)
833 int requested
= sc
->grp
[ig
].nmolReq
[ic
];
836 g
->comp
[ic
].nMolReq
= g
->comp
[ic
].nMol
;
840 g
->comp
[ic
].nMolReq
= requested
;
844 /* Check whether the number of requested molecules adds up to the total number */
845 int req
= g
->comp
[eCompA
].nMolReq
+ g
->comp
[eCompB
].nMolReq
;
846 int tot
= g
->comp
[eCompA
].nMol
+ g
->comp
[eCompB
].nMol
;
850 gmx_fatal(FARGS
, "Mismatch of the number of %s ions summed over both compartments.\n"
851 "You requested a total of %d ions (%d in A and %d in B),\n"
852 "but there are a total of %d ions of this type in the system.\n",
853 g
->molname
, req
, g
->comp
[eCompA
].nMolReq
,
854 g
->comp
[eCompB
].nMolReq
, tot
);
857 /* Initialize time-averaging:
858 * Write initial concentrations to all time bins to start with */
859 for (int ic
= 0; ic
< eCompNR
; ic
++)
861 g
->comp
[ic
].nMolAv
= g
->comp
[ic
].nMol
;
862 for (int i
= 0; i
< sc
->nAverage
; i
++)
864 g
->comp
[ic
].nMolPast
[i
] = g
->comp
[ic
].nMol
;
871 /*! \brief Copy history of ion counts from checkpoint file.
873 * When called, the checkpoint file has already been read in. Here we copy
874 * over the values from .cpt file to the swap data structure.
876 static void get_initial_ioncounts_from_cpt(
877 const t_inputrec
*ir
,
879 swaphistory_t
*swapstate
,
880 t_commrec
*cr
, gmx_bool bVerbose
)
890 /* Copy the past values from the checkpoint values that have been read in already */
893 fprintf(stderr
, "%s Copying values from checkpoint\n", SwS
);
896 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
899 gs
= &swapstate
->ionType
[ig
- eSwapFixedGrpNR
];
901 for (int ic
= 0; ic
< eCompNR
; ic
++)
903 g
->comp
[ic
].nMolReq
= gs
->nMolReq
[ic
];
904 g
->comp
[ic
].inflow_net
= gs
->inflow_net
[ic
];
908 fprintf(stderr
, "%s ... Influx netto: %d Requested: %d Past values: ", SwS
,
909 g
->comp
[ic
].inflow_net
, g
->comp
[ic
].nMolReq
);
912 for (int j
= 0; j
< sc
->nAverage
; j
++)
914 g
->comp
[ic
].nMolPast
[j
] = gs
->nMolPast
[ic
][j
];
917 fprintf(stderr
, "%d ", g
->comp
[ic
].nMolPast
[j
]);
922 fprintf(stderr
, "\n");
930 /*! \brief The master lets all others know about the initial ion counts. */
931 static void bc_initial_concentrations(
940 for (ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
944 for (ic
= 0; ic
< eCompNR
; ic
++)
946 gmx_bcast(sizeof(g
->comp
[ic
].nMolReq
), &(g
->comp
[ic
].nMolReq
), cr
);
947 gmx_bcast(sizeof(g
->comp
[ic
].nMol
), &(g
->comp
[ic
].nMol
), cr
);
948 gmx_bcast( swap
->nAverage
* sizeof(g
->comp
[ic
].nMolPast
[0]), g
->comp
[ic
].nMolPast
, cr
);
954 /*! \brief Ensure that each atom belongs to at most one of the swap groups. */
955 static void check_swap_groups(t_swap
*s
, int nat
, gmx_bool bVerbose
)
957 int *nGroup
= nullptr; /* This array counts for each atom in the MD system to
958 how many swap groups it belongs (should be 0 or 1!) */
960 int nMultiple
= 0; /* Number of atoms belonging to multiple groups */
965 fprintf(stderr
, "%s Making sure each atom belongs to at most one of the swap groups.\n", SwS
);
968 /* Add one to the group count of atoms belonging to a swap group: */
970 for (int i
= 0; i
< s
->ngrp
; i
++)
972 t_swapgrp
*g
= &s
->group
[i
];
973 for (size_t j
= 0; j
< g
->atomset
.numAtomsGlobal(); j
++)
975 /* Get the global index of this atom of this group: */
976 ind
= g
->atomset
.globalIndex()[j
];
980 /* Make sure each atom belongs to at most one of the groups: */
981 for (int i
= 0; i
< nat
; i
++)
992 gmx_fatal(FARGS
, "%s Cannot perform swapping since %d atom%s allocated to more than one swap index group.\n"
993 "%s Each atom must be allocated to at most one of the split groups, the swap groups, or the solvent.\n"
994 "%s Check the .mdp file settings regarding the swap index groups or the index groups themselves.\n",
995 SwS
, nMultiple
, (1 == nMultiple
) ? " is" : "s are", SwSEmpty
, SwSEmpty
);
1000 /*! \brief Get the number of atoms per molecule for this group.
1002 * Also ensure that all the molecules in this group have this number of atoms.
1004 static int get_group_apm_check(
1010 t_swapgrp
*g
= &s
->group
[igroup
];
1011 const int *ind
= s
->group
[igroup
].atomset
.globalIndex().data();
1012 int nat
= s
->group
[igroup
].atomset
.numAtomsGlobal();
1014 /* Determine the number of solvent atoms per solvent molecule from the
1015 * first solvent atom: */
1017 mtopGetMolblockIndex(mtop
, ind
[0], &molb
, nullptr, nullptr);
1018 int apm
= mtop
->moleculeBlockIndices
[molb
].numAtomsPerMolecule
;
1022 fprintf(stderr
, "%s Checking whether all %s molecules consist of %d atom%s\n", SwS
,
1023 g
->molname
, apm
, apm
> 1 ? "s" : "");
1026 /* Check whether this is also true for all other solvent atoms */
1027 for (int i
= 1; i
< nat
; i
++)
1029 mtopGetMolblockIndex(mtop
, ind
[i
], &molb
, nullptr, nullptr);
1030 if (apm
!= mtop
->moleculeBlockIndices
[molb
].numAtomsPerMolecule
)
1032 gmx_fatal(FARGS
, "Not all molecules of swap group %d consist of %d atoms.",
1037 //TODO: check whether charges and masses of each molecule are identical!
1042 /*! \brief Print the legend to the swap output file.
1044 * Also print the initial values of ion counts and position of split groups.
1046 static void print_ionlist_legend(const t_inputrec
*ir
,
1048 const gmx_output_env_t
*oenv
)
1050 const char **legend
;
1054 int nIonTypes
= ir
->swap
->ngrp
- eSwapFixedGrpNR
;
1055 snew(legend
, eCompNR
*nIonTypes
*3 + 2 + eChanNR
*nIonTypes
+ 1);
1057 // Number of molecules and difference to reference counts for each
1058 // compartment and ion type
1059 for (int ic
= count
= 0; ic
< eCompNR
; ic
++)
1061 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1063 t_swapGroup
*g
= &ir
->swap
->grp
[ig
];
1064 real q
= s
->group
[ig
].q
;
1066 snprintf(buf
, STRLEN
, "%s %s ions (charge %s%g)", CompStr
[ic
], g
->molname
, q
> 0 ? "+" : "", q
);
1067 legend
[count
++] = gmx_strdup(buf
);
1069 snprintf(buf
, STRLEN
, "%s av. mismatch to %d %s ions",
1070 CompStr
[ic
], s
->group
[ig
].comp
[ic
].nMolReq
, g
->molname
);
1071 legend
[count
++] = gmx_strdup(buf
);
1073 snprintf(buf
, STRLEN
, "%s net %s ion influx", CompStr
[ic
], g
->molname
);
1074 legend
[count
++] = gmx_strdup(buf
);
1078 // Center of split groups
1079 snprintf(buf
, STRLEN
, "%scenter of %s of split group 0", SwapStr
[ir
->eSwapCoords
], (nullptr != s
->group
[eGrpSplit0
].m
) ? "mass" : "geometry");
1080 legend
[count
++] = gmx_strdup(buf
);
1081 snprintf(buf
, STRLEN
, "%scenter of %s of split group 1", SwapStr
[ir
->eSwapCoords
], (nullptr != s
->group
[eGrpSplit1
].m
) ? "mass" : "geometry");
1082 legend
[count
++] = gmx_strdup(buf
);
1084 // Ion flux for each channel and ion type
1085 for (int ic
= 0; ic
< eChanNR
; ic
++)
1087 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1089 t_swapGroup
*g
= &ir
->swap
->grp
[ig
];
1090 snprintf(buf
, STRLEN
, "A->ch%d->B %s permeations", ic
, g
->molname
);
1091 legend
[count
++] = gmx_strdup(buf
);
1095 // Number of molecules that leaked from A to B
1096 snprintf(buf
, STRLEN
, "leakage");
1097 legend
[count
++] = gmx_strdup(buf
);
1099 xvgr_legend(s
->fpout
, count
, legend
, oenv
);
1101 fprintf(s
->fpout
, "# Instantaneous ion counts and time-averaged differences to requested numbers\n");
1103 // We add a simple text legend helping to identify the columns with xvgr legend strings
1104 fprintf(s
->fpout
, "# time (ps)");
1105 for (int i
= 0; i
< count
; i
++)
1107 snprintf(buf
, STRLEN
, "s%d", i
);
1108 fprintf(s
->fpout
, "%10s", buf
);
1110 fprintf(s
->fpout
, "\n");
1115 /*! \brief Initialize channel ion flux detection routine.
1117 * Initialize arrays that keep track of where the ions come from and where
1120 static void detect_flux_per_channel_init(
1122 swaphistory_t
*swapstate
,
1123 const bool isRestart
)
1126 swapstateIons_t
*gs
;
1128 /* All these flux detection routines run on the master only */
1129 if (swapstate
== nullptr)
1134 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1137 gs
= &swapstate
->ionType
[ig
- eSwapFixedGrpNR
];
1139 /******************************************************/
1140 /* Channel and domain history for the individual ions */
1141 /******************************************************/
1142 if (isRestart
) /* set the pointers right */
1144 g
->comp_from
= gs
->comp_from
;
1145 g
->channel_label
= gs
->channel_label
;
1147 else /* allocate memory for molecule counts */
1149 snew(g
->comp_from
, g
->atomset
.numAtomsGlobal()/g
->apm
);
1150 gs
->comp_from
= g
->comp_from
;
1151 snew(g
->channel_label
, g
->atomset
.numAtomsGlobal()/g
->apm
);
1152 gs
->channel_label
= g
->channel_label
;
1154 snew(g
->comp_now
, g
->atomset
.numAtomsGlobal()/g
->apm
);
1156 /* Initialize the channel and domain history counters */
1157 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal()/g
->apm
; i
++)
1159 g
->comp_now
[i
] = eDomainNotset
;
1162 g
->comp_from
[i
] = eDomainNotset
;
1163 g
->channel_label
[i
] = eChHistPassedNone
;
1167 /************************************/
1168 /* Channel fluxes for both channels */
1169 /************************************/
1170 g
->nCyl
[eChan0
] = 0;
1171 g
->nCyl
[eChan1
] = 0;
1177 fprintf(stderr
, "%s Copying channel fluxes from checkpoint file data\n", SwS
);
1181 // Loop over ion types (and both channels)
1182 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1185 gs
= &swapstate
->ionType
[ig
- eSwapFixedGrpNR
];
1187 for (int ic
= 0; ic
< eChanNR
; ic
++)
1189 fprintf(stderr
, "%s Channel %d flux history for ion type %s (charge %g): ", SwS
, ic
, g
->molname
, g
->q
);
1192 g
->fluxfromAtoB
[ic
] = gs
->fluxfromAtoB
[ic
];
1196 g
->fluxfromAtoB
[ic
] = 0;
1199 fprintf(stderr
, "%d molecule%s",
1200 g
->fluxfromAtoB
[ic
], g
->fluxfromAtoB
[ic
] == 1 ? "" : "s");
1201 fprintf(stderr
, "\n");
1205 /* Set pointers for checkpoint writing */
1206 swapstate
->fluxleak_p
= &s
->fluxleak
;
1207 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1210 gs
= &swapstate
->ionType
[ig
- eSwapFixedGrpNR
];
1212 for (int ic
= 0; ic
< eChanNR
; ic
++)
1214 gs
->fluxfromAtoB_p
[ic
] = &g
->fluxfromAtoB
[ic
];
1220 /*! \brief Outputs the initial structure to PDB file for debugging reasons.
1222 * Output the starting structure so that in case of multimeric channels
1223 * the user can check whether we have the correct PBC image for all atoms.
1224 * If this is not correct, the ion counts per channel will be very likely
1227 static void outputStartStructureIfWanted(gmx_mtop_t
*mtop
, rvec
*x
, int ePBC
, const matrix box
)
1229 char *env
= getenv("GMX_COMPELDUMP");
1233 fprintf(stderr
, "\n%s Found env.var. GMX_COMPELDUMP, will output CompEL starting structure made whole.\n"
1234 "%s In case of multimeric channels, please check whether they have the correct PBC representation.\n",
1237 write_sto_conf_mtop("CompELAssumedWholeConfiguration.pdb", *mtop
->name
, mtop
, x
, nullptr, ePBC
, box
);
1242 /*! \brief Initialize the swapstate structure, used for checkpoint writing.
1244 * The swapstate struct stores the information we need to make the channels
1245 * whole again after restarts from a checkpoint file. Here we do the following:
1246 * a) If we did not start from .cpt, we prepare the struct for proper .cpt writing,
1247 * b) if we did start from .cpt, we copy over the last whole structures from .cpt,
1248 * c) in any case, for subsequent checkpoint writing, we set the pointers in
1249 * swapstate to the x_old arrays, which contain the correct PBC representation of
1250 * multimeric channels at the last time step.
1252 static void init_swapstate(
1253 swaphistory_t
*swapstate
,
1257 const rvec
*x
, /* the initial positions */
1259 const t_inputrec
*ir
)
1261 rvec
*x_pbc
= nullptr; /* positions of the whole MD system with molecules made whole */
1265 /* We always need the last whole positions such that
1266 * in the next time step we can make the channels whole again in PBC */
1267 if (swapstate
->bFromCpt
)
1269 /* Copy the last whole positions of each channel from .cpt */
1270 g
= &(s
->group
[eGrpSplit0
]);
1271 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal(); i
++)
1273 copy_rvec(swapstate
->xc_old_whole
[eChan0
][i
], g
->xc_old
[i
]);
1275 g
= &(s
->group
[eGrpSplit1
]);
1276 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal(); i
++)
1278 copy_rvec(swapstate
->xc_old_whole
[eChan1
][i
], g
->xc_old
[i
]);
1283 swapstate
->eSwapCoords
= ir
->eSwapCoords
;
1285 /* Set the number of ion types and allocate memory for checkpointing */
1286 swapstate
->nIonTypes
= s
->ngrp
- eSwapFixedGrpNR
;
1287 snew(swapstate
->ionType
, swapstate
->nIonTypes
);
1289 /* Store the total number of ions of each type in the swapstateIons
1290 * structure that is accessible during checkpoint writing */
1291 for (int ii
= 0; ii
< swapstate
->nIonTypes
; ii
++)
1293 swapstateIons_t
*gs
= &swapstate
->ionType
[ii
];
1294 gs
->nMol
= sc
->grp
[ii
+ eSwapFixedGrpNR
].nat
;
1297 /* Extract the initial split group positions. */
1299 /* Remove pbc, make molecule whole. */
1300 snew(x_pbc
, mtop
->natoms
);
1301 copy_rvecn(x
, x_pbc
, 0, mtop
->natoms
);
1303 /* This can only make individual molecules whole, not multimers */
1304 do_pbc_mtop(ir
->ePBC
, box
, mtop
, x_pbc
);
1306 /* Output the starting structure? */
1307 outputStartStructureIfWanted(mtop
, x_pbc
, ir
->ePBC
, box
);
1309 /* If this is the first run (i.e. no checkpoint present) we assume
1310 * that the starting positions give us the correct PBC representation */
1311 for (int ig
= eGrpSplit0
; ig
<= eGrpSplit1
; ig
++)
1313 g
= &(s
->group
[ig
]);
1314 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal(); i
++)
1316 copy_rvec(x_pbc
[g
->atomset
.globalIndex()[i
]], g
->xc_old
[i
]);
1321 /* Prepare swapstate arrays for later checkpoint writing */
1322 swapstate
->nat
[eChan0
] = s
->group
[eGrpSplit0
].atomset
.numAtomsGlobal();
1323 swapstate
->nat
[eChan1
] = s
->group
[eGrpSplit1
].atomset
.numAtomsGlobal();
1326 /* For subsequent checkpoint writing, set the swapstate pointers to the xc_old
1327 * arrays that get updated at every swapping step */
1328 swapstate
->xc_old_whole_p
[eChan0
] = &s
->group
[eGrpSplit0
].xc_old
;
1329 swapstate
->xc_old_whole_p
[eChan1
] = &s
->group
[eGrpSplit1
].xc_old
;
1332 /*! \brief Determine the total charge imbalance resulting from the swap groups */
1333 static real
getRequestedChargeImbalance(t_swap
*s
)
1338 real particle_charge
;
1339 real particle_number
[eCompNR
];
1341 // s->deltaQ = ( (-1) * s->comp[eCompA][eIonNEG].nat_req + s->comp[eCompA][eIonPOS].nat_req )
1342 // - ( (-1) * s->comp[eCompB][eIonNEG].nat_req + s->comp[eCompB][eIonPOS].nat_req );
1344 for (ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1348 particle_charge
= g
->q
;
1349 particle_number
[eCompA
] = g
->comp
[eCompA
].nMolReq
;
1350 particle_number
[eCompB
] = g
->comp
[eCompB
].nMolReq
;
1352 DeltaQ
+= particle_charge
* (particle_number
[eCompA
] - particle_number
[eCompB
]);
1359 /*! \brief Sorts anions and cations into two separate groups
1361 * This routine should be called for the 'anions' and 'cations' group,
1362 * of which the indices were lumped together in the older version of the code.
1364 static void copyIndicesToGroup(
1372 /* If explicit ion counts were requested in the .mdp file
1373 * (by setting positive values for the number of ions),
1374 * we can make an additional consistency check here */
1375 if ( (g
->nmolReq
[eCompA
] < 0) && (g
->nmolReq
[eCompB
] < 0) )
1377 if (g
->nat
!= (g
->nmolReq
[eCompA
] + g
->nmolReq
[eCompB
]) )
1379 gmx_fatal_collective(FARGS
, cr
->mpi_comm_mysim
, MASTER(cr
),
1380 "%s Inconsistency while importing swap-related data from an old input file version.\n"
1381 "%s The requested ion counts in compartments A (%d) and B (%d)\n"
1382 "%s do not add up to the number of ions (%d) of this type for the group '%s'.\n",
1383 SwS
, SwSEmpty
, g
->nmolReq
[eCompA
], g
->nmolReq
[eCompB
], SwSEmpty
, g
->nat
, g
->molname
);
1387 srenew(g
->ind
, g
->nat
);
1388 for (int i
= 0; i
< g
->nat
; i
++)
1390 g
->ind
[i
] = indIons
[i
];
1395 /*! \brief Converts old .tpr file CompEL contents to new data layout.
1397 * If we have read an old .tpr file (tpxv <= tpxv_CompElPolyatomicIonsAndMultipleIonTypes),
1398 * anions and cations are stored together in group #3. In the new
1399 * format we store each ion type in a separate group.
1400 * The 'classic' groups are:
1401 * #0 split group 0 - OK
1402 * #1 split group 1 - OK
1404 * #3 anions - contains also cations, needs to be converted
1405 * #4 cations - empty before conversion
1408 static void convertOldToNewGroupFormat(
1414 t_swapGroup
*g
= &sc
->grp
[3];
1416 /* Loop through the atom indices of group #3 (anions) and put all indices
1417 * that belong to cations into the cation group.
1421 int *indAnions
= nullptr;
1422 int *indCations
= nullptr;
1423 snew(indAnions
, g
->nat
);
1424 snew(indCations
, g
->nat
);
1427 for (int i
= 0; i
< g
->nat
; i
++)
1429 const t_atom
&atom
= mtopGetAtomParameters(mtop
, g
->ind
[i
], &molb
);
1432 // This is an anion, add it to the list of anions
1433 indAnions
[nAnions
++] = g
->ind
[i
];
1437 // This is a cation, add it to the list of cations
1438 indCations
[nCations
++] = g
->ind
[i
];
1444 fprintf(stdout
, "%s Sorted %d ions into separate groups of %d anions and %d cations.\n",
1445 SwS
, g
->nat
, nAnions
, nCations
);
1449 /* Now we have the correct lists of anions and cations.
1450 * Copy it to the right groups.
1452 copyIndicesToGroup(indAnions
, nAnions
, g
, cr
);
1454 copyIndicesToGroup(indCations
, nCations
, g
, cr
);
1460 /*! \brief Returns TRUE if we started from an old .tpr
1462 * Then we need to re-sort anions and cations into separate groups */
1463 static gmx_bool
bConvertFromOldTpr(t_swapcoords
*sc
)
1465 // If the last group has no atoms it means we need to convert!
1466 return (sc
->ngrp
>= 5) && (0 == sc
->grp
[4].nat
);
1470 t_swap
*init_swapcoords(
1472 const t_inputrec
*ir
,
1475 const t_state
*globalState
,
1476 ObservablesHistory
*oh
,
1478 gmx::LocalAtomSetManager
*atomSets
,
1479 const gmx_output_env_t
*oenv
,
1480 const gmx::MdrunOptions
&mdrunOptions
,
1481 const gmx::StartingBehavior startingBehavior
)
1484 swapstateIons_t
*gs
;
1485 swaphistory_t
*swapstate
= nullptr;
1487 if ( (PAR(cr
)) && !DOMAINDECOMP(cr
) )
1489 gmx_fatal(FARGS
, "Position swapping is only implemented for domain decomposition!");
1493 auto s
= new t_swap();
1495 if (mdrunOptions
.rerun
)
1499 gmx_fatal(FARGS
, "%s This module does not support reruns in parallel\nPlease request a serial run with -nt 1 / -np 1\n", SwS
);
1502 fprintf(stderr
, "%s Rerun - using every available frame\n", SwS
);
1504 sc
->nAverage
= 1; /* averaging makes no sense for reruns */
1507 if (MASTER(cr
) && startingBehavior
== gmx::StartingBehavior::NewSimulation
)
1509 fprintf(fplog
, "\nInitializing ion/water position exchanges\n");
1510 please_cite(fplog
, "Kutzner2011b");
1513 switch (ir
->eSwapCoords
)
1529 const gmx_bool bVerbose
= mdrunOptions
.verbose
;
1531 // For compatibility with old .tpr files
1532 if (bConvertFromOldTpr(sc
) )
1534 convertOldToNewGroupFormat(sc
, mtop
, bVerbose
&& MASTER(cr
), cr
);
1537 /* Copy some data and pointers to the group structures for convenience */
1538 /* Number of atoms in the group */
1540 for (int i
= 0; i
< s
->ngrp
; i
++)
1542 s
->group
.emplace_back(atomSets
->add(gmx::ArrayRef
<const int>( sc
->grp
[i
].ind
, sc
->grp
[i
].ind
+sc
->grp
[i
].nat
)));
1543 s
->group
[i
].molname
= sc
->grp
[i
].molname
;
1546 /* Check for overlapping atoms */
1547 check_swap_groups(s
, mtop
->natoms
, bVerbose
&& MASTER(cr
));
1549 /* Allocate space for the collective arrays for all groups */
1550 /* For the collective position array */
1551 for (int i
= 0; i
< s
->ngrp
; i
++)
1554 snew(g
->xc
, g
->atomset
.numAtomsGlobal());
1556 /* For the split groups (the channels) we need some extra memory to
1557 * be able to make the molecules whole even if they span more than
1558 * half of the box size. */
1559 if ( (i
== eGrpSplit0
) || (i
== eGrpSplit1
) )
1561 snew(g
->xc_shifts
, g
->atomset
.numAtomsGlobal());
1562 snew(g
->xc_eshifts
, g
->atomset
.numAtomsGlobal());
1563 snew(g
->xc_old
, g
->atomset
.numAtomsGlobal());
1569 if (oh
->swapHistory
== nullptr)
1571 oh
->swapHistory
= std::make_unique
<swaphistory_t
>(swaphistory_t
{});
1573 swapstate
= oh
->swapHistory
.get();
1575 init_swapstate(swapstate
, sc
, s
, mtop
, globalState
->x
.rvec_array(), globalState
->box
, ir
);
1578 /* After init_swapstate we have a set of (old) whole positions for our
1579 * channels. Now transfer that to all nodes */
1582 for (int ig
= eGrpSplit0
; ig
<= eGrpSplit1
; ig
++)
1584 g
= &(s
->group
[ig
]);
1585 gmx_bcast((g
->atomset
.numAtomsGlobal())*sizeof((g
->xc_old
)[0]), g
->xc_old
, (cr
));
1589 /* Make sure that all molecules in the solvent and ion groups contain the
1590 * same number of atoms each */
1591 for (int ig
= eGrpSolvent
; ig
< s
->ngrp
; ig
++)
1595 g
= &(s
->group
[ig
]);
1596 g
->apm
= get_group_apm_check(ig
, s
, MASTER(cr
) && bVerbose
, mtop
);
1598 /* Since all molecules of a group are equal, we only need enough space
1599 * to determine properties of a single molecule at at time */
1600 snew(g
->m
, g
->apm
); /* For the center of mass */
1601 charge
= 0; /* To determine the charge imbalance */
1603 for (int j
= 0; j
< g
->apm
; j
++)
1605 const t_atom
&atom
= mtopGetAtomParameters(mtop
, g
->atomset
.globalIndex()[j
], &molb
);
1609 /* Total charge of one molecule of this group: */
1614 /* Need mass-weighted center of split group? */
1615 for (int j
= eGrpSplit0
; j
<= eGrpSplit1
; j
++)
1618 if (sc
->massw_split
[j
])
1620 /* Save the split group masses if mass-weighting is requested */
1621 snew(g
->m
, g
->atomset
.numAtomsGlobal());
1623 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal(); i
++)
1625 g
->m
[i
] = mtopGetAtomMass(mtop
, g
->atomset
.globalIndex()[i
], &molb
);
1630 /* Make a t_pbc struct on all nodes so that the molecules
1631 * chosen for an exchange can be made whole. */
1634 bool restartWithAppending
= (startingBehavior
== gmx::StartingBehavior::RestartWithAppending
);
1639 fprintf(stderr
, "%s Opening output file %s%s\n", SwS
, fn
, restartWithAppending
? " for appending" : "");
1642 s
->fpout
= gmx_fio_fopen(fn
, restartWithAppending
? "a" : "w" );
1644 if (!restartWithAppending
)
1646 xvgr_header(s
->fpout
, "Molecule counts", "Time (ps)", "counts", exvggtXNY
, oenv
);
1648 for (int ig
= 0; ig
< s
->ngrp
; ig
++)
1650 g
= &(s
->group
[ig
]);
1651 fprintf(s
->fpout
, "# %s group '%s' contains %d atom%s",
1652 ig
< eSwapFixedGrpNR
? eSwapFixedGrp_names
[ig
] : "Ion",
1653 g
->molname
, static_cast<int>(g
->atomset
.numAtomsGlobal()), (g
->atomset
.numAtomsGlobal() > 1) ? "s" : "");
1654 if (!(eGrpSplit0
== ig
|| eGrpSplit1
== ig
) )
1656 fprintf(s
->fpout
, " with %d atom%s in each molecule of charge %g",
1657 g
->apm
, (g
->apm
> 1) ? "s" : "", g
->q
);
1659 fprintf(s
->fpout
, ".\n");
1662 fprintf(s
->fpout
, "#\n# Initial positions of split groups:\n");
1665 for (int j
= eGrpSplit0
; j
<= eGrpSplit1
; j
++)
1668 for (size_t i
= 0; i
< g
->atomset
.numAtomsGlobal(); i
++)
1670 copy_rvec(globalState
->x
[sc
->grp
[j
].ind
[i
]], g
->xc
[i
]);
1672 /* xc has the correct PBC representation for the two channels, so we do
1673 * not need to correct for that */
1674 get_center(g
->xc
, g
->m
, g
->atomset
.numAtomsGlobal(), g
->center
);
1675 if (!restartWithAppending
)
1677 fprintf(s
->fpout
, "# %s group %s-center %5f nm\n", eSwapFixedGrp_names
[j
],
1678 DimStr
[s
->swapdim
], g
->center
[s
->swapdim
]);
1682 if (!restartWithAppending
)
1684 if ( (0 != sc
->bulkOffset
[eCompA
]) || (0 != sc
->bulkOffset
[eCompB
]) )
1686 fprintf(s
->fpout
, "#\n");
1687 fprintf(s
->fpout
, "# You provided an offset for the position of the bulk layer(s).\n");
1688 fprintf(s
->fpout
, "# That means the layers to/from which ions and water molecules are swapped\n");
1689 fprintf(s
->fpout
, "# are not midway (= at 0.0) between the compartment-defining layers (at +/- 1.0).\n");
1690 fprintf(s
->fpout
, "# bulk-offsetA = %g\n", sc
->bulkOffset
[eCompA
]);
1691 fprintf(s
->fpout
, "# bulk-offsetB = %g\n", sc
->bulkOffset
[eCompB
]);
1694 fprintf(s
->fpout
, "#\n");
1695 fprintf(s
->fpout
, "# Split0 cylinder radius %f nm, up %f nm, down %f nm\n",
1696 sc
->cyl0r
, sc
->cyl0u
, sc
->cyl0l
);
1697 fprintf(s
->fpout
, "# Split1 cylinder radius %f nm, up %f nm, down %f nm\n",
1698 sc
->cyl1r
, sc
->cyl1u
, sc
->cyl1l
);
1700 fprintf(s
->fpout
, "#\n");
1701 if (!mdrunOptions
.rerun
)
1703 fprintf(s
->fpout
, "# Coupling constant (number of swap attempt steps to average over): %d (translates to %f ps).\n",
1704 sc
->nAverage
, sc
->nAverage
*sc
->nstswap
*ir
->delta_t
);
1705 fprintf(s
->fpout
, "# Threshold is %f\n", sc
->threshold
);
1706 fprintf(s
->fpout
, "#\n");
1707 fprintf(s
->fpout
, "# Remarks about which atoms passed which channel use global atoms numbers starting at one.\n");
1716 /* Allocate memory to remember the past particle counts for time averaging */
1717 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1719 g
= &(s
->group
[ig
]);
1720 for (int ic
= 0; ic
< eCompNR
; ic
++)
1722 snew(g
->comp
[ic
].nMolPast
, sc
->nAverage
);
1726 /* Get the initial particle concentrations and let the other nodes know */
1729 if (startingBehavior
!= gmx::StartingBehavior::NewSimulation
)
1731 get_initial_ioncounts_from_cpt(ir
, s
, swapstate
, cr
, bVerbose
);
1735 fprintf(stderr
, "%s Determining initial numbers of ions per compartment.\n", SwS
);
1736 get_initial_ioncounts(ir
, s
, globalState
->x
.rvec_array(), globalState
->box
, cr
, mdrunOptions
.rerun
);
1739 /* Prepare (further) checkpoint writes ... */
1740 if (startingBehavior
!= gmx::StartingBehavior::NewSimulation
)
1742 /* Consistency check */
1743 if (swapstate
->nAverage
!= sc
->nAverage
)
1745 gmx_fatal(FARGS
, "%s Ion count averaging steps mismatch! checkpoint: %d, tpr: %d",
1746 SwS
, swapstate
->nAverage
, sc
->nAverage
);
1751 swapstate
->nAverage
= sc
->nAverage
;
1753 fprintf(stderr
, "%s Setting pointers for checkpoint writing\n", SwS
);
1754 for (int ic
= 0; ic
< eCompNR
; ic
++)
1756 for (int ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1759 gs
= &swapstate
->ionType
[ig
- eSwapFixedGrpNR
];
1761 gs
->nMolReq_p
[ic
] = &(g
->comp
[ic
].nMolReq
);
1762 gs
->nMolPast_p
[ic
] = &(g
->comp
[ic
].nMolPast
[0]);
1763 gs
->inflow_net_p
[ic
] = &(g
->comp
[ic
].inflow_net
);
1767 /* Determine the total charge imbalance */
1768 s
->deltaQ
= getRequestedChargeImbalance(s
);
1772 fprintf(stderr
, "%s Requested charge imbalance is Q(A) - Q(B) = %g e.\n", SwS
, s
->deltaQ
);
1774 if (!restartWithAppending
)
1776 fprintf(s
->fpout
, "# Requested charge imbalance is Q(A)-Q(B) = %g e.\n", s
->deltaQ
);
1782 bc_initial_concentrations(cr
, ir
->swap
, s
);
1785 /* Update the time-averaged number of molecules for all groups and compartments */
1786 for (int ig
= eSwapFixedGrpNR
; ig
< sc
->ngrp
; ig
++)
1789 for (int ic
= 0; ic
< eCompNR
; ic
++)
1791 update_time_window(&g
->comp
[ic
], sc
->nAverage
, -1);
1795 /* Initialize arrays that keep track of through which channel the ions go */
1796 detect_flux_per_channel_init(s
, swapstate
, startingBehavior
!= gmx::StartingBehavior::NewSimulation
);
1798 /* We need to print the legend if we open this file for the first time. */
1799 if (MASTER(cr
) && !restartWithAppending
)
1801 print_ionlist_legend(ir
, s
, oenv
);
1807 void finish_swapcoords(t_swap
*s
)
1815 // Close the swap output file
1816 gmx_fio_fclose(s
->fpout
);
1820 /*! \brief Do we need to swap a molecule in any of the ion groups with a water molecule at this step?
1822 * From the requested and average molecule counts we determine whether a swap is needed
1823 * at this time step.
1825 static gmx_bool
need_swap(t_swapcoords
*sc
,
1831 for (ig
= eSwapFixedGrpNR
; ig
< sc
->ngrp
; ig
++)
1835 for (ic
= 0; ic
< eCompNR
; ic
++)
1837 if (g
->comp
[ic
].nMolReq
- g
->comp
[ic
].nMolAv
>= sc
->threshold
)
1847 /*! \brief Return the index of an atom or molecule suitable for swapping.
1849 * Returns the index of an atom that is far off the compartment boundaries,
1850 * that is near to the bulk layer to/from which the swaps take place.
1851 * Other atoms of the molecule (if any) will directly follow the returned index.
1853 * \param[in] comp Structure containing compartment-specific data.
1854 * \param[in] molname Name of the molecule.
1856 * \returns Index of the first atom of the molecule chosen for a position exchange.
1858 static int get_index_of_distant_atom(
1859 t_compartment
*comp
,
1860 const char molname
[])
1863 real d
= GMX_REAL_MAX
;
1866 /* comp->nat contains the original number of atoms in this compartment
1867 * prior to doing any swaps. Some of these atoms may already have been
1868 * swapped out, but then they are marked with a distance of GMX_REAL_MAX
1870 for (int iMol
= 0; iMol
< comp
->nMolBefore
; iMol
++)
1872 if (comp
->dist
[iMol
] < d
)
1875 d
= comp
->dist
[ibest
];
1881 gmx_fatal(FARGS
, "Could not get index of %s atom. Compartment contains %d %s molecules before swaps.",
1882 molname
, comp
->nMolBefore
, molname
);
1885 /* Set the distance of this index to infinity such that it won't get selected again in
1888 comp
->dist
[ibest
] = GMX_REAL_MAX
;
1890 return comp
->ind
[ibest
];
1894 /*! \brief Swaps centers of mass and makes molecule whole if broken */
1895 static void translate_positions(
1903 rvec reference
, dx
, correctPBCimage
;
1906 /* Use the first atom as the reference for PBC */
1907 copy_rvec(x
[0], reference
);
1909 for (i
= 0; i
< apm
; i
++)
1911 /* PBC distance between position and reference */
1912 pbc_dx(pbc
, x
[i
], reference
, dx
);
1914 /* Add PBC distance to reference */
1915 rvec_add(reference
, dx
, correctPBCimage
);
1917 /* Subtract old_com from correct image and add new_com */
1918 rvec_dec(correctPBCimage
, old_com
);
1919 rvec_inc(correctPBCimage
, new_com
);
1921 copy_rvec(correctPBCimage
, x
[i
]);
1926 /*! \brief Write back the the modified local positions from the collective array to the official positions. */
1927 static void apply_modified_positions(
1931 auto collectiveIndex
= g
->atomset
.collectiveIndex().begin();
1932 for (const auto localIndex
: g
->atomset
.localIndex())
1934 /* Copy the possibly modified position */
1935 copy_rvec(g
->xc
[*collectiveIndex
], x
[localIndex
]);
1941 gmx_bool
do_swapcoords(
1947 gmx_wallcycle
*wcycle
,
1954 int j
, ic
, ig
, nswaps
;
1955 int thisC
, otherC
; /* Index into this compartment and the other one */
1956 gmx_bool bSwap
= FALSE
;
1957 t_swapgrp
*g
, *gsol
;
1959 rvec com_solvent
, com_particle
; /* solvent and swap molecule's center of mass */
1962 wallcycle_start(wcycle
, ewcSWAP
);
1966 set_pbc(s
->pbc
, ir
->ePBC
, box
);
1968 /* Assemble the positions of the split groups, i.e. the channels.
1969 * Here we also pass a shifts array to communicate_group_positions(), so that it can make
1970 * the molecules whole even in cases where they span more than half of the box in
1972 for (ig
= eGrpSplit0
; ig
<= eGrpSplit1
; ig
++)
1974 g
= &(s
->group
[ig
]);
1975 communicate_group_positions(cr
, g
->xc
, g
->xc_shifts
, g
->xc_eshifts
, TRUE
,
1976 x
, g
->atomset
.numAtomsGlobal(), g
->atomset
.numAtomsLocal(), g
->atomset
.localIndex().data(), g
->atomset
.collectiveIndex().data(), g
->xc_old
, box
);
1978 get_center(g
->xc
, g
->m
, g
->atomset
.numAtomsGlobal(), g
->center
); /* center of split groups == channels */
1981 /* Assemble the positions of the ions (ig = 3, 4, ...). These molecules should
1982 * be small and we can always make them whole with a simple distance check.
1983 * Therefore we pass NULL as third argument. */
1984 for (ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
1986 g
= &(s
->group
[ig
]);
1987 communicate_group_positions(cr
, g
->xc
, nullptr, nullptr, FALSE
,
1988 x
, g
->atomset
.numAtomsGlobal(), g
->atomset
.numAtomsLocal(), g
->atomset
.localIndex().data(), g
->atomset
.collectiveIndex().data(), nullptr, nullptr);
1990 /* Determine how many ions of this type each compartment contains */
1991 sortMoleculesIntoCompartments(g
, cr
, sc
, s
, box
, step
, s
->fpout
, bRerun
, FALSE
);
1994 /* Output how many ions are in the compartments */
1997 print_ionlist(s
, t
, "");
2000 /* If we are doing a rerun, we are finished here, since we cannot perform
2007 /* Do we have to perform a swap? */
2008 bSwap
= need_swap(sc
, s
);
2011 /* Since we here know that we have to perform ion/water position exchanges,
2012 * we now assemble the solvent positions */
2013 g
= &(s
->group
[eGrpSolvent
]);
2014 communicate_group_positions(cr
, g
->xc
, nullptr, nullptr, FALSE
,
2015 x
, g
->atomset
.numAtomsGlobal(), g
->atomset
.numAtomsLocal(), g
->atomset
.localIndex().data(), g
->atomset
.collectiveIndex().data(), nullptr, nullptr);
2017 /* Determine how many molecules of solvent each compartment contains */
2018 sortMoleculesIntoCompartments(g
, cr
, sc
, s
, box
, step
, s
->fpout
, bRerun
, TRUE
);
2020 /* Save number of solvent molecules per compartment prior to any swaps */
2021 g
->comp
[eCompA
].nMolBefore
= g
->comp
[eCompA
].nMol
;
2022 g
->comp
[eCompB
].nMolBefore
= g
->comp
[eCompB
].nMol
;
2024 for (ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
2026 g
= &(s
->group
[ig
]);
2028 for (ic
= 0; ic
< eCompNR
; ic
++)
2030 /* Determine in which compartment ions are missing and where they are too many */
2031 g
->vacancy
[ic
] = g
->comp
[ic
].nMolReq
- g
->comp
[ic
].nMolAv
;
2033 /* Save number of ions per compartment prior to swaps */
2034 g
->comp
[ic
].nMolBefore
= g
->comp
[ic
].nMol
;
2038 /* Now actually perform the particle exchanges, one swap group after another */
2039 gsol
= &s
->group
[eGrpSolvent
];
2040 for (ig
= eSwapFixedGrpNR
; ig
< s
->ngrp
; ig
++)
2044 for (thisC
= 0; thisC
< eCompNR
; thisC
++)
2046 /* Index to the other compartment */
2047 otherC
= (thisC
+1) % eCompNR
;
2049 while (g
->vacancy
[thisC
] >= sc
->threshold
)
2051 /* Swap in an ion */
2053 /* Get the xc-index of the first atom of a solvent molecule of this compartment */
2054 isol
= get_index_of_distant_atom(&gsol
->comp
[thisC
], gsol
->molname
);
2056 /* Get the xc-index of a particle from the other compartment */
2057 iion
= get_index_of_distant_atom(&g
->comp
[otherC
], g
->molname
);
2059 get_molecule_center(&gsol
->xc
[isol
], gsol
->apm
, gsol
->m
, com_solvent
, s
->pbc
);
2060 get_molecule_center(&g
->xc
[iion
], g
->apm
, g
->m
, com_particle
, s
->pbc
);
2062 /* Subtract solvent molecule's center of mass and add swap particle's center of mass */
2063 translate_positions(&gsol
->xc
[isol
], gsol
->apm
, com_solvent
, com_particle
, s
->pbc
);
2064 /* Similarly for the swap particle, subtract com_particle and add com_solvent */
2065 translate_positions(&g
->xc
[iion
], g
->apm
, com_particle
, com_solvent
, s
->pbc
);
2067 /* Keep track of the changes */
2068 g
->vacancy
[thisC
]--;
2069 g
->vacancy
[otherC
]++;
2070 g
->comp
[thisC
].nMol
++;
2071 g
->comp
[otherC
].nMol
--;
2072 g
->comp
[thisC
].inflow_net
++;
2073 g
->comp
[otherC
].inflow_net
--;
2074 /* Correct the past time window to still get the right averages from now on */
2075 g
->comp
[thisC
].nMolAv
++;
2076 g
->comp
[otherC
].nMolAv
--;
2077 for (j
= 0; j
< sc
->nAverage
; j
++)
2079 g
->comp
[thisC
].nMolPast
[j
]++;
2080 g
->comp
[otherC
].nMolPast
[j
]--;
2082 /* Clear ion history */
2085 int iMol
= iion
/ g
->apm
;
2086 g
->channel_label
[iMol
] = eChHistPassedNone
;
2087 g
->comp_from
[iMol
] = eDomainNotset
;
2089 /* That was the swap */
2094 if (nswaps
&& bVerbose
)
2096 fprintf(stderr
, "%s Performed %d swap%s in step %" PRId64
" for iontype %s.\n",
2097 SwS
, nswaps
, nswaps
> 1 ? "s" : "", step
, g
->molname
);
2101 if (s
->fpout
!= nullptr)
2103 print_ionlist(s
, t
, " # after swap");
2106 /* For the solvent and user-defined swap groups, each rank writes back its
2107 * (possibly modified) local positions to the official position array. */
2108 for (ig
= eGrpSolvent
; ig
< s
->ngrp
; ig
++)
2111 apply_modified_positions(g
, x
);
2114 } /* end of if(bSwap) */
2116 wallcycle_stop(wcycle
, ewcSWAP
);