2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
47 #include "gromacs/commandline/pargs.h"
48 #include "gromacs/fileio/confio.h"
49 #include "gromacs/fileio/pdbio.h"
50 #include "gromacs/gmxlib/conformation_utilities.h"
51 #include "gromacs/gmxpreprocess/makeexclusiondistances.h"
52 #include "gromacs/math/functions.h"
53 #include "gromacs/math/units.h"
54 #include "gromacs/math/vec.h"
55 #include "gromacs/pbcutil/boxutilities.h"
56 #include "gromacs/pbcutil/pbc.h"
57 #include "gromacs/selection/nbsearch.h"
58 #include "gromacs/topology/atomprop.h"
59 #include "gromacs/topology/atoms.h"
60 #include "gromacs/topology/atomsbuilder.h"
61 #include "gromacs/topology/mtop_util.h"
62 #include "gromacs/topology/topology.h"
63 #include "gromacs/utility/arraysize.h"
64 #include "gromacs/utility/cstringutil.h"
65 #include "gromacs/utility/fatalerror.h"
66 #include "gromacs/utility/futil.h"
67 #include "gromacs/utility/gmxassert.h"
68 #include "gromacs/utility/smalloc.h"
72 /*! \brief Describes a molecule type, and keeps track of the number of these molecules
74 * Used for sorting coordinate file data after solvation
80 //! number of atoms in the molecule
82 //! number of occurences of molecule
86 static void sort_molecule(t_atoms
**atoms_solvt
,
92 fprintf(stderr
, "Sorting configuration\n");
93 t_atoms
*atoms
= *atoms_solvt
;
95 /* copy each residue from *atoms to a molecule in *molecule */
96 std::vector
<MoleculeType
> molTypes
;
97 for (int i
= 0; i
< atoms
->nr
; i
++)
99 if ( (i
== 0) || (atoms
->atom
[i
].resind
!= atoms
->atom
[i
-1].resind
) )
101 /* see if this was a molecule type we haven't had yet: */
102 auto matchingMolType
= std::find_if(molTypes
.begin(), molTypes
.end(),
103 [atoms
, i
](const MoleculeType
&molecule
)
104 {return molecule
.name
== *atoms
->resinfo
[atoms
->atom
[i
].resind
].name
; });
105 if (matchingMolType
== molTypes
.end())
107 int numAtomsInMolType
= 0;
108 while ((i
+ numAtomsInMolType
< atoms
->nr
) &&
109 (atoms
->atom
[i
].resind
== atoms
->atom
[i
+ numAtomsInMolType
].resind
))
113 molTypes
.emplace_back(MoleculeType
{*atoms
->resinfo
[atoms
->atom
[i
].resind
].name
, numAtomsInMolType
, 1});
117 matchingMolType
->numMolecules
++;
122 fprintf(stderr
, "Found %zu%s molecule type%s:\n",
123 molTypes
.size(), molTypes
.size() == 1 ? "" : " different", molTypes
.size() == 1 ? "" : "s");
124 for (const auto &molType
: molTypes
)
126 fprintf(stderr
, "%7s (%4d atoms): %5d residues\n",
127 molType
.name
.c_str(), molType
.numAtoms
, molType
.numMolecules
);
130 /* if we have only 1 moleculetype, we don't have to sort */
131 if (molTypes
.size() > 1)
133 /* now put them there: */
135 init_t_atoms(*newatoms
, atoms
->nr
, FALSE
);
136 (*newatoms
)->nres
= atoms
->nres
;
137 srenew((*newatoms
)->resinfo
, atoms
->nres
);
138 std::vector
<RVec
> newx(x
->size());
139 std::vector
<RVec
> newv(v
->size());
143 for (const auto &moleculeType
: molTypes
)
146 while (i
< atoms
->nr
)
148 int residOfCurrAtom
= atoms
->atom
[i
].resind
;
149 if (moleculeType
.name
== *atoms
->resinfo
[residOfCurrAtom
].name
)
151 /* Copy the residue info */
152 (*newatoms
)->resinfo
[residIndex
] = atoms
->resinfo
[residOfCurrAtom
];
153 // Residue numbering starts from 1, so +1 from the index
154 (*newatoms
)->resinfo
[residIndex
].nr
= residIndex
+ 1;
155 /* Copy the atom info */
158 (*newatoms
)->atom
[atomIndex
] = atoms
->atom
[i
];
159 (*newatoms
)->atomname
[atomIndex
] = atoms
->atomname
[i
];
160 (*newatoms
)->atom
[atomIndex
].resind
= residIndex
;
161 copy_rvec((*x
)[i
], newx
[atomIndex
]);
164 copy_rvec((*v
)[i
], newv
[atomIndex
]);
169 while (i
< atoms
->nr
&& atoms
->atom
[i
].resind
== residOfCurrAtom
);
170 /* Increase the new residue counters */
175 /* Skip this residue */
180 while (i
< atoms
->nr
&& atoms
->atom
[i
].resind
== residOfCurrAtom
);
185 /* put them back into the original arrays and throw away temporary arrays */
187 *atoms_solvt
= (*newatoms
);
193 static void rm_res_pbc(const t_atoms
*atoms
, std::vector
<RVec
> *x
, matrix box
)
201 for (int n
= 0; n
< atoms
->nr
; n
++)
203 if (!is_hydrogen(*(atoms
->atomname
[n
])))
206 rvec_inc(xcg
, (*x
)[n
]);
208 if ( (n
+1 == atoms
->nr
) ||
209 (atoms
->atom
[n
+1].resind
!= atoms
->atom
[n
].resind
) )
211 /* if nat==0 we have only hydrogens in the solvent,
212 we take last coordinate as cg */
216 copy_rvec((*x
)[n
], xcg
);
218 svmul(1.0/nat
, xcg
, xcg
);
219 for (int d
= 0; d
< DIM
; d
++)
223 for (int i
= start
; i
<= n
; i
++)
225 (*x
)[i
][d
] += box
[d
][d
];
229 while (xcg
[d
] >= box
[d
][d
])
231 for (int i
= start
; i
<= n
; i
++)
233 (*x
)[i
][d
] -= box
[d
][d
];
246 * Generates a solvent configuration of desired size by stacking solvent boxes.
248 * \param[in,out] atoms Solvent atoms.
249 * \param[in,out] x Solvent positions.
250 * \param[in,out] v Solvent velocities (`*v` can be NULL).
251 * \param[in,out] r Solvent exclusion radii.
252 * \param[in] box Initial solvent box.
253 * \param[in] boxTarget Target box size.
255 * The solvent box of desired size is created by stacking the initial box in
256 * the smallest k*l*m array that covers the box, and then removing any residue
257 * where all atoms are outside the target box (with a small margin).
258 * This function does not remove overlap between solvent atoms across the
261 * Note that the input configuration should be in the rectangular unit cell and
262 * have whole residues.
264 static void replicateSolventBox(t_atoms
*atoms
, std::vector
<RVec
> *x
,
265 std::vector
<RVec
> *v
, std::vector
<real
> *r
,
266 const matrix box
, const matrix boxTarget
)
268 // Calculate the box multiplication factors.
271 for (int i
= 0; i
< DIM
; ++i
)
274 while (n_box
[i
] * box
[i
][i
] < boxTarget
[i
][i
])
280 fprintf(stderr
, "Will generate new solvent configuration of %dx%dx%d boxes\n",
281 n_box
[XX
], n_box
[YY
], n_box
[ZZ
]);
283 // Create arrays for storing the generated system (cannot be done in-place
284 // in case the target box is smaller than the original in one dimension,
287 init_t_atoms(&newAtoms
, 0, FALSE
);
288 gmx::AtomsBuilder
builder(&newAtoms
, nullptr);
289 builder
.reserve(atoms
->nr
* nmol
, atoms
->nres
* nmol
);
290 std::vector
<RVec
> newX(atoms
->nr
* nmol
);
291 std::vector
<RVec
> newV(!v
->empty() ? atoms
->nr
* nmol
: 0);
292 std::vector
<real
> newR(atoms
->nr
* nmol
);
294 const real maxRadius
= *std::max_element(r
->begin(), r
->end());
296 for (int i
= 0; i
< DIM
; ++i
)
298 // The code below is only interested about the box diagonal.
299 boxWithMargin
[i
] = boxTarget
[i
][i
] + 3*maxRadius
;
302 for (int ix
= 0; ix
< n_box
[XX
]; ++ix
)
305 delta
[XX
] = ix
*box
[XX
][XX
];
306 for (int iy
= 0; iy
< n_box
[YY
]; ++iy
)
308 delta
[YY
] = iy
*box
[YY
][YY
];
309 for (int iz
= 0; iz
< n_box
[ZZ
]; ++iz
)
311 delta
[ZZ
] = iz
*box
[ZZ
][ZZ
];
312 bool bKeepResidue
= false;
313 for (int i
= 0; i
< atoms
->nr
; ++i
)
315 const int newIndex
= builder
.currentAtomCount();
316 bool bKeepAtom
= true;
317 for (int m
= 0; m
< DIM
; ++m
)
319 const real newCoord
= delta
[m
] + (*x
)[i
][m
];
320 bKeepAtom
= bKeepAtom
&& (newCoord
< boxWithMargin
[m
]);
321 newX
[newIndex
][m
] = newCoord
;
323 bKeepResidue
= bKeepResidue
|| bKeepAtom
;
326 copy_rvec((*v
)[i
], newV
[newIndex
]);
328 newR
[newIndex
] = (*r
)[i
];
329 builder
.addAtom(*atoms
, i
);
330 if (i
== atoms
->nr
- 1
331 || atoms
->atom
[i
+1].resind
!= atoms
->atom
[i
].resind
)
335 builder
.finishResidue(atoms
->resinfo
[atoms
->atom
[i
].resind
]);
339 builder
.discardCurrentResidue();
341 // Reset state for the next residue.
342 bKeepResidue
= false;
349 sfree(atoms
->atomname
);
350 sfree(atoms
->resinfo
);
351 atoms
->nr
= newAtoms
.nr
;
352 atoms
->nres
= newAtoms
.nres
;
353 atoms
->atom
= newAtoms
.atom
;
354 atoms
->atomname
= newAtoms
.atomname
;
355 atoms
->resinfo
= newAtoms
.resinfo
;
357 newX
.resize(atoms
->nr
);
361 newV
.resize(atoms
->nr
);
364 newR
.resize(atoms
->nr
);
367 fprintf(stderr
, "Solvent box contains %d atoms in %d residues\n",
368 atoms
->nr
, atoms
->nres
);
372 * Removes overlap of solvent atoms across the edges.
374 * \param[in,out] atoms Solvent atoms.
375 * \param[in,out] x Solvent positions.
376 * \param[in,out] v Solvent velocities (can be empty).
377 * \param[in,out] r Solvent exclusion radii.
378 * \param[in] pbc PBC information.
380 * Solvent residues that lay on the edges that do not touch the origin are
381 * removed if they overlap with other solvent atoms across the PBC.
382 * This is done in this way as the assumption is that the input solvent
383 * configuration is already equilibrated, and so does not contain any
384 * undesirable overlap. The only overlap that should be removed is caused by
385 * cutting the box in half in replicateSolventBox() and leaving a margin of
386 * solvent outside those box edges; these atoms can then overlap with those on
387 * the opposite box edge in a way that is not part of the pre-equilibrated
390 static void removeSolventBoxOverlap(t_atoms
*atoms
, std::vector
<RVec
> *x
,
391 std::vector
<RVec
> *v
, std::vector
<real
> *r
,
394 gmx::AtomsRemover
remover(*atoms
);
396 // TODO: We could limit the amount of pairs searched significantly,
397 // since we are only interested in pairs where the positions are on
399 const real maxRadius
= *std::max_element(r
->begin(), r
->end());
400 gmx::AnalysisNeighborhood nb
;
401 nb
.setCutoff(2*maxRadius
);
402 gmx::AnalysisNeighborhoodPositions
pos(*x
);
403 gmx::AnalysisNeighborhoodSearch search
= nb
.initSearch(&pbc
, pos
);
404 gmx::AnalysisNeighborhoodPairSearch pairSearch
= search
.startPairSearch(pos
);
405 gmx::AnalysisNeighborhoodPair pair
;
406 while (pairSearch
.findNextPair(&pair
))
408 const int i1
= pair
.refIndex();
409 const int i2
= pair
.testIndex();
410 if (remover
.isMarked(i2
))
412 pairSearch
.skipRemainingPairsForTestPosition();
415 if (remover
.isMarked(i1
) || atoms
->atom
[i1
].resind
== atoms
->atom
[i2
].resind
)
419 if (pair
.distance2() < gmx::square((*r
)[i1
] + (*r
)[i2
]))
422 rvec_sub((*x
)[i2
], (*x
)[i1
], dx
);
423 bool bCandidate1
= false, bCandidate2
= false;
424 // To satisfy Clang static analyzer.
425 GMX_ASSERT(pbc
.ndim_ePBC
<= DIM
, "Too many periodic dimensions");
426 for (int d
= 0; d
< pbc
.ndim_ePBC
; ++d
)
428 // If the distance in some dimension is larger than the
429 // cutoff, then it means that the distance has been computed
430 // over the PBC. Mark the position with a larger coordinate
431 // for potential removal.
432 if (dx
[d
] > maxRadius
)
436 else if (dx
[d
] < -maxRadius
)
441 // Only mark one of the positions for removal if both were
443 if (bCandidate2
&& (!bCandidate1
|| i2
> i1
))
445 remover
.markResidue(*atoms
, i2
, true);
446 pairSearch
.skipRemainingPairsForTestPosition();
448 else if (bCandidate1
)
450 remover
.markResidue(*atoms
, i1
, true);
455 remover
.removeMarkedElements(x
);
458 remover
.removeMarkedElements(v
);
460 remover
.removeMarkedElements(r
);
461 const int originalAtomCount
= atoms
->nr
;
462 remover
.removeMarkedAtoms(atoms
);
463 fprintf(stderr
, "Removed %d solvent atoms due to solvent-solvent overlap\n",
464 originalAtomCount
- atoms
->nr
);
468 * Remove all solvent molecules outside a give radius from the solute.
470 * \param[in,out] atoms Solvent atoms.
471 * \param[in,out] x_solvent Solvent positions.
472 * \param[in,out] v_solvent Solvent velocities.
473 * \param[in,out] r Atomic exclusion radii.
474 * \param[in] pbc PBC information.
475 * \param[in] x_solute Solute positions.
476 * \param[in] rshell The radius outside the solute molecule.
478 static void removeSolventOutsideShell(t_atoms
*atoms
,
479 std::vector
<RVec
> *x_solvent
,
480 std::vector
<RVec
> *v_solvent
,
481 std::vector
<real
> *r
,
483 const std::vector
<RVec
> &x_solute
,
486 gmx::AtomsRemover
remover(*atoms
);
487 gmx::AnalysisNeighborhood nb
;
488 nb
.setCutoff(rshell
);
489 gmx::AnalysisNeighborhoodPositions
posSolute(x_solute
);
490 gmx::AnalysisNeighborhoodSearch search
= nb
.initSearch(&pbc
, posSolute
);
491 gmx::AnalysisNeighborhoodPositions
pos(*x_solvent
);
492 gmx::AnalysisNeighborhoodPairSearch pairSearch
= search
.startPairSearch(pos
);
493 gmx::AnalysisNeighborhoodPair pair
;
497 // Now put back those within the shell without checking for overlap
498 while (pairSearch
.findNextPair(&pair
))
500 remover
.markResidue(*atoms
, pair
.testIndex(), false);
501 pairSearch
.skipRemainingPairsForTestPosition();
503 remover
.removeMarkedElements(x_solvent
);
504 if (!v_solvent
->empty())
506 remover
.removeMarkedElements(v_solvent
);
508 remover
.removeMarkedElements(r
);
509 const int originalAtomCount
= atoms
->nr
;
510 remover
.removeMarkedAtoms(atoms
);
511 fprintf(stderr
, "Removed %d solvent atoms more than %f nm from solute.\n",
512 originalAtomCount
- atoms
->nr
, rshell
);
516 * Removes solvent molecules that overlap with the solute.
518 * \param[in,out] atoms Solvent atoms.
519 * \param[in,out] x Solvent positions.
520 * \param[in,out] v Solvent velocities (can be empty).
521 * \param[in,out] r Solvent exclusion radii.
522 * \param[in] pbc PBC information.
523 * \param[in] x_solute Solute positions.
524 * \param[in] r_solute Solute exclusion radii.
526 static void removeSolventOverlappingWithSolute(t_atoms
*atoms
,
527 std::vector
<RVec
> *x
,
528 std::vector
<RVec
> *v
,
529 std::vector
<real
> *r
,
531 const std::vector
<RVec
> &x_solute
,
532 const std::vector
<real
> &r_solute
)
534 gmx::AtomsRemover
remover(*atoms
);
535 const real maxRadius1
536 = *std::max_element(r
->begin(), r
->end());
537 const real maxRadius2
538 = *std::max_element(r_solute
.begin(), r_solute
.end());
540 // Now check for overlap.
541 gmx::AnalysisNeighborhood nb
;
542 gmx::AnalysisNeighborhoodPair pair
;
543 nb
.setCutoff(maxRadius1
+ maxRadius2
);
544 gmx::AnalysisNeighborhoodPositions
posSolute(x_solute
);
545 gmx::AnalysisNeighborhoodSearch search
= nb
.initSearch(&pbc
, posSolute
);
546 gmx::AnalysisNeighborhoodPositions
pos(*x
);
547 gmx::AnalysisNeighborhoodPairSearch pairSearch
= search
.startPairSearch(pos
);
548 while (pairSearch
.findNextPair(&pair
))
550 if (remover
.isMarked(pair
.testIndex()))
552 pairSearch
.skipRemainingPairsForTestPosition();
555 const real r1
= r_solute
[pair
.refIndex()];
556 const real r2
= (*r
)[pair
.testIndex()];
557 const bool bRemove
= (pair
.distance2() < gmx::square(r1
+ r2
));
558 remover
.markResidue(*atoms
, pair
.testIndex(), bRemove
);
561 remover
.removeMarkedElements(x
);
564 remover
.removeMarkedElements(v
);
566 remover
.removeMarkedElements(r
);
567 const int originalAtomCount
= atoms
->nr
;
568 remover
.removeMarkedAtoms(atoms
);
569 fprintf(stderr
, "Removed %d solvent atoms due to solute-solvent overlap\n",
570 originalAtomCount
- atoms
->nr
);
574 * Removes a given number of solvent residues.
576 * \param[in,out] atoms Solvent atoms.
577 * \param[in,out] x Solvent positions.
578 * \param[in,out] v Solvent velocities (can be empty).
579 * \param[in] numberToRemove Number of residues to remove.
581 * This function is called last in the process of creating the solvent box,
582 * so it does not operate on the exclusion radii, as no code after this needs
585 static void removeExtraSolventMolecules(t_atoms
*atoms
, std::vector
<RVec
> *x
,
586 std::vector
<RVec
> *v
,
589 gmx::AtomsRemover
remover(*atoms
);
590 std::random_device rd
;
591 std::mt19937
randomNumberGenerator(rd());
592 std::uniform_int_distribution
<> randomDistribution(0, atoms
->nr
- 1);
593 while (numberToRemove
> 0)
595 int atomIndex
= randomDistribution(randomNumberGenerator
);
596 if (!remover
.isMarked(atomIndex
))
598 remover
.markResidue(*atoms
, atomIndex
, true);
602 remover
.removeMarkedElements(x
);
605 remover
.removeMarkedElements(v
);
607 remover
.removeMarkedAtoms(atoms
);
610 static void add_solv(const char *filename
, t_atoms
*atoms
,
612 std::vector
<RVec
> *x
, std::vector
<RVec
> *v
,
613 int ePBC
, matrix box
, AtomProperties
*aps
,
614 real defaultDistance
, real scaleFactor
,
615 real rshell
, int max_sol
)
617 gmx_mtop_t topSolvent
;
618 std::vector
<RVec
> xSolvent
, vSolvent
;
619 matrix boxSolvent
= {{ 0 }};
622 fprintf(stderr
, "Reading solvent configuration\n");
623 bool bTprFileWasRead
;
624 rvec
*temporaryX
= nullptr, *temporaryV
= nullptr;
625 readConfAndTopology(gmx::findLibraryFile(filename
).c_str(), &bTprFileWasRead
, &topSolvent
,
626 &ePBCSolvent
, &temporaryX
, &temporaryV
, boxSolvent
);
627 t_atoms
*atomsSolvent
;
628 snew(atomsSolvent
, 1);
629 *atomsSolvent
= gmx_mtop_global_atoms(&topSolvent
);
630 xSolvent
.assign(temporaryX
, temporaryX
+ topSolvent
.natoms
);
632 vSolvent
.assign(temporaryV
, temporaryV
+ topSolvent
.natoms
);
634 if (gmx::boxIsZero(boxSolvent
))
636 gmx_fatal(FARGS
, "No box information for solvent in %s, please use a properly formatted file\n",
639 if (0 == atomsSolvent
->nr
)
641 gmx_fatal(FARGS
, "No solvent in %s, please check your input\n", filename
);
643 fprintf(stderr
, "\n");
645 /* initialise distance arrays for solvent configuration */
646 fprintf(stderr
, "Initialising inter-atomic distances...\n");
647 const std::vector
<real
> exclusionDistances(
648 makeExclusionDistances(atoms
, aps
, defaultDistance
, scaleFactor
));
649 std::vector
<real
> exclusionDistances_solvt(
650 makeExclusionDistances(atomsSolvent
, aps
, defaultDistance
, scaleFactor
));
652 /* generate a new solvent configuration */
653 fprintf(stderr
, "Generating solvent configuration\n");
655 set_pbc(&pbc
, ePBC
, box
);
656 if (!gmx::boxesAreEqual(boxSolvent
, box
))
658 if (TRICLINIC(boxSolvent
))
660 gmx_fatal(FARGS
, "Generating from non-rectangular solvent boxes is currently not supported.\n"
661 "You can try to pass the same box for -cp and -cs.");
663 /* apply pbc for solvent configuration for whole molecules */
664 rm_res_pbc(atomsSolvent
, &xSolvent
, boxSolvent
);
665 replicateSolventBox(atomsSolvent
, &xSolvent
, &vSolvent
, &exclusionDistances_solvt
,
667 if (ePBC
!= epbcNONE
)
669 removeSolventBoxOverlap(atomsSolvent
, &xSolvent
, &vSolvent
, &exclusionDistances_solvt
, pbc
);
676 removeSolventOutsideShell(atomsSolvent
, &xSolvent
, &vSolvent
,
677 &exclusionDistances_solvt
, pbc
, *x
, rshell
);
679 removeSolventOverlappingWithSolute(atomsSolvent
, &xSolvent
, &vSolvent
,
680 &exclusionDistances_solvt
, pbc
, *x
,
684 if (max_sol
> 0 && atomsSolvent
->nres
> max_sol
)
686 const int numberToRemove
= atomsSolvent
->nres
- max_sol
;
687 removeExtraSolventMolecules(atomsSolvent
, &xSolvent
, &vSolvent
, numberToRemove
);
690 /* Sort the solvent mixture, not the protein... */
691 t_atoms
*newatoms
= nullptr;
692 // The sort_molecule function does something creative with the
693 // t_atoms pointers. We need to make sure we neither leak, nor
694 // double-free, so make a shallow pointer that is fine for it to
696 t_atoms
*sortedAtomsSolvent
= atomsSolvent
;
697 sort_molecule(&sortedAtomsSolvent
, &newatoms
, &xSolvent
, &vSolvent
);
699 // Merge the two configurations.
700 x
->insert(x
->end(), xSolvent
.begin(), xSolvent
.end());
703 v
->insert(v
->end(), vSolvent
.begin(), vSolvent
.end());
706 gmx::AtomsBuilder
builder(atoms
, symtab
);
707 builder
.mergeAtoms(*sortedAtomsSolvent
);
709 fprintf(stderr
, "Generated solvent containing %d atoms in %d residues\n",
710 atomsSolvent
->nr
, atomsSolvent
->nres
);
719 done_atom(atomsSolvent
);
724 static void update_top(t_atoms
*atoms
,
725 int firstSolventResidueIndex
,
732 char buf
[STRLEN
*2], buf2
[STRLEN
], *temp
;
733 const char *topinout
;
740 int nsol
= atoms
->nres
- firstSolventResidueIndex
;
743 for (i
= 0; (i
< atoms
->nr
); i
++)
745 aps
->setAtomProperty(epropMass
,
746 std::string(*atoms
->resinfo
[atoms
->atom
[i
].resind
].name
),
747 std::string(*atoms
->atomname
[i
]), &mm
);
753 fprintf(stderr
, "Volume : %10g (nm^3)\n", vol
);
754 fprintf(stderr
, "Density : %10g (g/l)\n",
755 (mtot
*1e24
)/(AVOGADRO
*vol
));
756 fprintf(stderr
, "Number of solvent molecules: %5d \n\n", nsol
);
758 /* open topology file and append sol molecules */
759 topinout
= ftp2fn(efTOP
, NFILE
, fnm
);
760 if (ftp2bSet(efTOP
, NFILE
, fnm
) )
762 char temporary_filename
[STRLEN
];
763 strncpy(temporary_filename
, "temp.topXXXXXX", STRLEN
);
765 fprintf(stderr
, "Processing topology\n");
766 fpin
= gmx_ffopen(topinout
, "r");
767 fpout
= gmx_fopen_temporary(temporary_filename
);
770 while (fgets(buf
, STRLEN
, fpin
))
774 if ((temp
= strchr(buf2
, '\n')) != nullptr)
782 if ((temp
= strchr(buf2
, '\n')) != nullptr)
787 if (buf2
[strlen(buf2
)-1] == ']')
789 buf2
[strlen(buf2
)-1] = '\0';
792 bSystem
= (gmx_strcasecmp(buf2
, "system") == 0);
795 else if (bSystem
&& nsol
&& (buf
[0] != ';') )
797 /* if sol present, append "in water" to system name */
799 if (buf2
[0] && (!strstr(buf2
, " water")) )
801 sprintf(buf
, "%s in water\n", buf2
);
805 fprintf(fpout
, "%s", buf
);
809 // Add new solvent molecules to the topology
812 std::string currRes
= *atoms
->resinfo
[firstSolventResidueIndex
].name
;
815 // Iterate through solvent molecules and increment a count until new resname found
816 for (int i
= firstSolventResidueIndex
; i
< atoms
->nres
; i
++)
818 if ((currRes
== *atoms
->resinfo
[i
].name
))
824 // Change topology and restart count
825 fprintf(stdout
, "Adding line for %d solvent molecules with resname (%s) to "
826 "topology file (%s)\n", resCount
, currRes
.c_str(), topinout
);
827 fprintf(fpout
, "%-15s %5d\n", currRes
.c_str(), resCount
);
828 currRes
= *atoms
->resinfo
[i
].name
;
832 // One more print needed for last residue type
833 fprintf(stdout
, "Adding line for %d solvent molecules with resname (%s) to "
834 "topology file (%s)\n", resCount
, currRes
.c_str(), topinout
);
835 fprintf(fpout
, "%-15s %5d\n", currRes
.c_str(), resCount
);
838 make_backup(topinout
);
839 gmx_file_rename(temporary_filename
, topinout
);
843 int gmx_solvate(int argc
, char *argv
[])
845 const char *desc
[] = {
846 "[THISMODULE] can do one of 2 things:[PAR]",
848 "1) Generate a box of solvent. Specify [TT]-cs[tt] and [TT]-box[tt].",
849 "Or specify [TT]-cs[tt] and [TT]-cp[tt] with a structure file with",
850 "a box, but without atoms.[PAR]",
852 "2) Solvate a solute configuration, e.g. a protein, in a bath of solvent ",
853 "molecules. Specify [TT]-cp[tt] (solute) and [TT]-cs[tt] (solvent). ",
854 "The box specified in the solute coordinate file ([TT]-cp[tt]) is used,",
855 "unless [TT]-box[tt] is set.",
856 "If you want the solute to be centered in the box,",
857 "the program [gmx-editconf] has sophisticated options",
858 "to change the box dimensions and center the solute.",
859 "Solvent molecules are removed from the box where the ",
860 "distance between any atom of the solute molecule(s) and any atom of ",
861 "the solvent molecule is less than the sum of the scaled van der Waals",
862 "radii of both atoms. A database ([TT]vdwradii.dat[tt]) of van der",
863 "Waals radii is read by the program, and the resulting radii scaled",
864 "by [TT]-scale[tt]. If radii are not found in the database, those",
865 "atoms are assigned the (pre-scaled) distance [TT]-radius[tt].",
866 "Note that the usefulness of those radii depends on the atom names,",
867 "and thus varies widely with force field.",
869 "The default solvent is Simple Point Charge water (SPC), with coordinates ",
870 "from [TT]$GMXLIB/spc216.gro[tt]. These coordinates can also be used",
871 "for other 3-site water models, since a short equibilibration will remove",
872 "the small differences between the models.",
873 "Other solvents are also supported, as well as mixed solvents. The",
874 "only restriction to solvent types is that a solvent molecule consists",
875 "of exactly one residue. The residue information in the coordinate",
876 "files is used, and should therefore be more or less consistent.",
877 "In practice this means that two subsequent solvent molecules in the ",
878 "solvent coordinate file should have different residue number.",
879 "The box of solute is built by stacking the coordinates read from",
880 "the coordinate file. This means that these coordinates should be ",
881 "equlibrated in periodic boundary conditions to ensure a good",
882 "alignment of molecules on the stacking interfaces.",
883 "The [TT]-maxsol[tt] option simply adds only the first [TT]-maxsol[tt]",
884 "solvent molecules and leaves out the rest that would have fitted",
885 "into the box. This can create a void that can cause problems later.",
886 "Choose your volume wisely.[PAR]",
888 "Setting [TT]-shell[tt] larger than zero will place a layer of water of",
889 "the specified thickness (nm) around the solute. Hint: it is a good",
890 "idea to put the protein in the center of a box first (using [gmx-editconf]).",
893 "Finally, [THISMODULE] will optionally remove lines from your topology file in ",
894 "which a number of solvent molecules is already added, and adds a ",
895 "line with the total number of solvent molecules in your coordinate file."
898 const char *bugs
[] = {
899 "Molecules must be whole in the initial configurations.",
903 gmx_bool bProt
, bBox
;
904 const char *conf_prot
, *confout
;
907 { efSTX
, "-cp", "protein", ffOPTRD
},
908 { efSTX
, "-cs", "spc216", ffLIBRD
},
909 { efSTO
, nullptr, nullptr, ffWRITE
},
910 { efTOP
, nullptr, nullptr, ffOPTRW
},
912 #define NFILE asize(fnm)
914 real defaultDistance
= 0.105, r_shell
= 0, scaleFactor
= 0.57;
915 rvec new_box
= {0.0, 0.0, 0.0};
916 gmx_bool bReadV
= FALSE
;
918 int firstSolventResidueIndex
= 0;
919 gmx_output_env_t
*oenv
;
921 { "-box", FALSE
, etRVEC
, {new_box
},
922 "Box size (in nm)" },
923 { "-radius", FALSE
, etREAL
, {&defaultDistance
},
924 "Default van der Waals distance"},
925 { "-scale", FALSE
, etREAL
, {&scaleFactor
},
926 "Scale factor to multiply Van der Waals radii from the database in share/gromacs/top/vdwradii.dat. The default value of 0.57 yields density close to 1000 g/l for proteins in water." },
927 { "-shell", FALSE
, etREAL
, {&r_shell
},
928 "Thickness of optional water layer around solute" },
929 { "-maxsol", FALSE
, etINT
, {&max_sol
},
930 "Maximum number of solvent molecules to add if they fit in the box. If zero (default) this is ignored" },
931 { "-vel", FALSE
, etBOOL
, {&bReadV
},
932 "Keep velocities from input solute and solvent" },
935 if (!parse_common_args(&argc
, argv
, 0, NFILE
, fnm
, asize(pa
), pa
,
936 asize(desc
), desc
, asize(bugs
), bugs
, &oenv
))
941 const char *solventFileName
= opt2fn("-cs", NFILE
, fnm
);
942 bProt
= opt2bSet("-cp", NFILE
, fnm
);
943 bBox
= opt2parg_bSet("-box", asize(pa
), pa
);
948 gmx_fatal(FARGS
, "When no solute (-cp) is specified, "
949 "a box size (-box) must be specified");
954 /* solute configuration data */
956 std::vector
<RVec
> x
, v
;
957 matrix box
= {{ 0 }};
963 /* Generate a solute configuration */
964 conf_prot
= opt2fn("-cp", NFILE
, fnm
);
965 fprintf(stderr
, "Reading solute configuration%s\n",
966 bReadV
? " and velocities" : "");
967 bool bTprFileWasRead
;
968 rvec
*temporaryX
= nullptr, *temporaryV
= nullptr;
969 readConfAndTopology(conf_prot
, &bTprFileWasRead
, &top
,
970 &ePBC
, &temporaryX
, bReadV
? &temporaryV
: nullptr, box
);
971 *atoms
= gmx_mtop_global_atoms(&top
);
972 x
.assign(temporaryX
, temporaryX
+ top
.natoms
);
976 v
.assign(temporaryV
, temporaryV
+ top
.natoms
);
981 fprintf(stderr
, "Note: no velocities found\n");
985 fprintf(stderr
, "Note: no atoms in %s\n", conf_prot
);
990 firstSolventResidueIndex
= atoms
->nres
;
993 int ePBCForOutput
= ePBC
;
996 ePBCForOutput
= epbcXYZ
;
998 box
[XX
][XX
] = new_box
[XX
];
999 box
[YY
][YY
] = new_box
[YY
];
1000 box
[ZZ
][ZZ
] = new_box
[ZZ
];
1004 gmx_fatal(FARGS
, "Undefined solute box.\nCreate one with gmx editconf "
1005 "or give explicit -box command line option");
1008 add_solv(solventFileName
, atoms
, &top
.symtab
, &x
, &v
, ePBCForOutput
, box
,
1009 &aps
, defaultDistance
, scaleFactor
, r_shell
, max_sol
);
1011 /* write new configuration 1 to file confout */
1012 confout
= ftp2fn(efSTO
, NFILE
, fnm
);
1013 fprintf(stderr
, "Writing generated configuration to %s\n", confout
);
1014 const char *outputTitle
= (bProt
? *top
.name
: "Generated by gmx solvate");
1015 write_sto_conf(confout
, outputTitle
, atoms
, as_rvec_array(x
.data()),
1016 !v
.empty() ? as_rvec_array(v
.data()) : nullptr, ePBCForOutput
, box
);
1018 /* print size of generated configuration */
1019 fprintf(stderr
, "\nOutput configuration contains %d atoms in %d residues\n",
1020 atoms
->nr
, atoms
->nres
);
1021 update_top(atoms
, firstSolventResidueIndex
, box
, NFILE
, fnm
, &aps
);
1025 output_env_done(oenv
);