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, 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.
46 #include "gromacs/commandline/pargs.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/pdbio.h"
49 #include "gromacs/gmxlib/conformation-utilities.h"
50 #include "gromacs/gmxpreprocess/read-conformation.h"
51 #include "gromacs/math/functions.h"
52 #include "gromacs/math/units.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/pbcutil/boxutilities.h"
55 #include "gromacs/pbcutil/pbc.h"
56 #include "gromacs/selection/nbsearch.h"
57 #include "gromacs/topology/atomprop.h"
58 #include "gromacs/topology/atoms.h"
59 #include "gromacs/topology/atomsbuilder.h"
60 #include "gromacs/topology/topology.h"
61 #include "gromacs/utility/arraysize.h"
62 #include "gromacs/utility/cstringutil.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/futil.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/smalloc.h"
78 static void sort_molecule(t_atoms **atoms_solvt,
83 int atnr, i, j, moltp = 0, nrmoltypes, resi_o, resi_n, resnr;
87 fprintf(stderr, "Sorting configuration\n");
91 /* copy each residue from *atoms to a molecule in *molecule */
94 for (i = 0; i < atoms->nr; i++)
96 if ( (i == 0) || (atoms->atom[i].resind != atoms->atom[i-1].resind) )
98 /* see if this was a molecule type we haven't had yet: */
100 for (j = 0; (j < nrmoltypes) && (moltp == -1); j++)
102 /* moltypes is guaranteed to be allocated because otherwise
103 * nrmoltypes is 0. */
104 if (strcmp(*(atoms->resinfo[atoms->atom[i].resind].name), moltypes[j].name) == 0)
113 srenew(moltypes, nrmoltypes);
114 moltypes[moltp].name = *(atoms->resinfo[atoms->atom[i].resind].name);
116 while ((i+atnr < atoms->nr) &&
117 (atoms->atom[i].resind == atoms->atom[i+atnr].resind))
121 moltypes[moltp].natoms = atnr;
122 moltypes[moltp].nmol = 0;
124 moltypes[moltp].nmol++;
128 fprintf(stderr, "Found %d%s molecule type%s:\n",
129 nrmoltypes, nrmoltypes == 1 ? "" : " different", nrmoltypes == 1 ? "" : "s");
130 for (j = 0; j < nrmoltypes; j++)
134 moltypes[j].res0 = 0;
138 moltypes[j].res0 = moltypes[j-1].res0+moltypes[j-1].nmol;
140 fprintf(stderr, "%7s (%4d atoms): %5d residues\n",
141 moltypes[j].name, moltypes[j].natoms, moltypes[j].nmol);
144 /* if we have only 1 moleculetype, we don't have to sort */
147 /* find out which molecules should go where: */
148 moltypes[0].i = moltypes[0].i0 = 0;
149 for (j = 1; j < nrmoltypes; j++)
153 moltypes[j-1].i0+moltypes[j-1].natoms*moltypes[j-1].nmol;
156 /* now put them there: */
158 init_t_atoms(*newatoms, atoms->nr, FALSE);
159 (*newatoms)->nres = atoms->nres;
160 srenew((*newatoms)->resinfo, atoms->nres);
161 std::vector<RVec> newx(x->size());
162 std::vector<RVec> newv(v->size());
167 for (moltp = 0; moltp < nrmoltypes; moltp++)
170 while (i < atoms->nr)
172 resi_o = atoms->atom[i].resind;
173 if (strcmp(*atoms->resinfo[resi_o].name, moltypes[moltp].name) == 0)
175 /* Copy the residue info */
176 (*newatoms)->resinfo[resi_n] = atoms->resinfo[resi_o];
177 (*newatoms)->resinfo[resi_n].nr = resnr;
178 /* Copy the atom info */
181 (*newatoms)->atom[j] = atoms->atom[i];
182 (*newatoms)->atomname[j] = atoms->atomname[i];
183 (*newatoms)->atom[j].resind = resi_n;
184 copy_rvec((*x)[i], newx[j]);
187 copy_rvec((*v)[i], newv[j]);
192 while (i < atoms->nr && atoms->atom[i].resind == resi_o);
193 /* Increase the new residue counters */
199 /* Skip this residue */
204 while (i < atoms->nr && atoms->atom[i].resind == resi_o);
209 /* put them back into the original arrays and throw away temporary arrays */
211 *atoms_solvt = (*newatoms);
218 static void rm_res_pbc(const t_atoms *atoms, std::vector<RVec> *x, matrix box)
226 for (int n = 0; n < atoms->nr; n++)
228 if (!is_hydrogen(*(atoms->atomname[n])))
231 rvec_inc(xcg, (*x)[n]);
233 if ( (n+1 == atoms->nr) ||
234 (atoms->atom[n+1].resind != atoms->atom[n].resind) )
236 /* if nat==0 we have only hydrogens in the solvent,
237 we take last coordinate as cg */
241 copy_rvec((*x)[n], xcg);
243 svmul(1.0/nat, xcg, xcg);
244 for (int d = 0; d < DIM; d++)
248 for (int i = start; i <= n; i++)
250 (*x)[i][d] += box[d][d];
254 while (xcg[d] >= box[d][d])
256 for (int i = start; i <= n; i++)
258 (*x)[i][d] -= box[d][d];
271 * Generates a solvent configuration of desired size by stacking solvent boxes.
273 * \param[in,out] atoms Solvent atoms.
274 * \param[in,out] x Solvent positions.
275 * \param[in,out] v Solvent velocities (`*v` can be NULL).
276 * \param[in,out] r Solvent exclusion radii.
277 * \param[in] box Initial solvent box.
278 * \param[in] boxTarget Target box size.
280 * The solvent box of desired size is created by stacking the initial box in
281 * the smallest k*l*m array that covers the box, and then removing any residue
282 * where all atoms are outside the target box (with a small margin).
283 * This function does not remove overlap between solvent atoms across the
286 * Note that the input configuration should be in the rectangular unit cell and
287 * have whole residues.
289 static void replicateSolventBox(t_atoms *atoms, std::vector<RVec> *x,
290 std::vector<RVec> *v, std::vector<real> *r,
291 const matrix box, const matrix boxTarget)
293 // Calculate the box multiplication factors.
296 for (int i = 0; i < DIM; ++i)
299 while (n_box[i] * box[i][i] < boxTarget[i][i])
305 fprintf(stderr, "Will generate new solvent configuration of %dx%dx%d boxes\n",
306 n_box[XX], n_box[YY], n_box[ZZ]);
308 // Create arrays for storing the generated system (cannot be done in-place
309 // in case the target box is smaller than the original in one dimension,
312 init_t_atoms(&newAtoms, 0, FALSE);
313 gmx::AtomsBuilder builder(&newAtoms, nullptr);
314 builder.reserve(atoms->nr * nmol, atoms->nres * nmol);
315 std::vector<RVec> newX(atoms->nr * nmol);
316 std::vector<RVec> newV(!v->empty() ? atoms->nr * nmol : 0);
317 std::vector<real> newR(atoms->nr * nmol);
319 const real maxRadius = *std::max_element(r->begin(), r->end());
321 for (int i = 0; i < DIM; ++i)
323 // The code below is only interested about the box diagonal.
324 boxWithMargin[i] = boxTarget[i][i] + 3*maxRadius;
327 for (int ix = 0; ix < n_box[XX]; ++ix)
330 delta[XX] = ix*box[XX][XX];
331 for (int iy = 0; iy < n_box[YY]; ++iy)
333 delta[YY] = iy*box[YY][YY];
334 for (int iz = 0; iz < n_box[ZZ]; ++iz)
336 delta[ZZ] = iz*box[ZZ][ZZ];
337 bool bKeepResidue = false;
338 for (int i = 0; i < atoms->nr; ++i)
340 const int newIndex = builder.currentAtomCount();
341 bool bKeepAtom = true;
342 for (int m = 0; m < DIM; ++m)
344 const real newCoord = delta[m] + (*x)[i][m];
345 bKeepAtom = bKeepAtom && (newCoord < boxWithMargin[m]);
346 newX[newIndex][m] = newCoord;
348 bKeepResidue = bKeepResidue || bKeepAtom;
351 copy_rvec((*v)[i], newV[newIndex]);
353 newR[newIndex] = (*r)[i];
354 builder.addAtom(*atoms, i);
355 if (i == atoms->nr - 1
356 || atoms->atom[i+1].resind != atoms->atom[i].resind)
360 builder.finishResidue(atoms->resinfo[atoms->atom[i].resind]);
364 builder.discardCurrentResidue();
366 // Reset state for the next residue.
367 bKeepResidue = false;
374 sfree(atoms->atomname);
375 sfree(atoms->resinfo);
376 atoms->nr = newAtoms.nr;
377 atoms->nres = newAtoms.nres;
378 atoms->atom = newAtoms.atom;
379 atoms->atomname = newAtoms.atomname;
380 atoms->resinfo = newAtoms.resinfo;
382 newX.resize(atoms->nr);
386 newV.resize(atoms->nr);
389 newR.resize(atoms->nr);
392 fprintf(stderr, "Solvent box contains %d atoms in %d residues\n",
393 atoms->nr, atoms->nres);
397 * Removes overlap of solvent atoms across the edges.
399 * \param[in,out] atoms Solvent atoms.
400 * \param[in,out] x Solvent positions.
401 * \param[in,out] v Solvent velocities (can be empty).
402 * \param[in,out] r Solvent exclusion radii.
403 * \param[in] pbc PBC information.
405 * Solvent residues that lay on the edges that do not touch the origin are
406 * removed if they overlap with other solvent atoms across the PBC.
407 * This is done in this way as the assumption is that the input solvent
408 * configuration is already equilibrated, and so does not contain any
409 * undesirable overlap. The only overlap that should be removed is caused by
410 * cutting the box in half in replicateSolventBox() and leaving a margin of
411 * solvent outside those box edges; these atoms can then overlap with those on
412 * the opposite box edge in a way that is not part of the pre-equilibrated
415 static void removeSolventBoxOverlap(t_atoms *atoms, std::vector<RVec> *x,
416 std::vector<RVec> *v, std::vector<real> *r,
419 gmx::AtomsRemover remover(*atoms);
421 // TODO: We could limit the amount of pairs searched significantly,
422 // since we are only interested in pairs where the positions are on
424 const real maxRadius = *std::max_element(r->begin(), r->end());
425 gmx::AnalysisNeighborhood nb;
426 nb.setCutoff(2*maxRadius);
427 gmx::AnalysisNeighborhoodPositions pos(*x);
428 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(&pbc, pos);
429 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
430 gmx::AnalysisNeighborhoodPair pair;
431 while (pairSearch.findNextPair(&pair))
433 const int i1 = pair.refIndex();
434 const int i2 = pair.testIndex();
435 if (remover.isMarked(i2))
437 pairSearch.skipRemainingPairsForTestPosition();
440 if (remover.isMarked(i1) || atoms->atom[i1].resind == atoms->atom[i2].resind)
444 if (pair.distance2() < gmx::square((*r)[i1] + (*r)[i2]))
447 rvec_sub((*x)[i2], (*x)[i1], dx);
448 bool bCandidate1 = false, bCandidate2 = false;
449 // To satisfy Clang static analyzer.
450 GMX_ASSERT(pbc.ndim_ePBC <= DIM, "Too many periodic dimensions");
451 for (int d = 0; d < pbc.ndim_ePBC; ++d)
453 // If the distance in some dimension is larger than the
454 // cutoff, then it means that the distance has been computed
455 // over the PBC. Mark the position with a larger coordinate
456 // for potential removal.
457 if (dx[d] > maxRadius)
461 else if (dx[d] < -maxRadius)
466 // Only mark one of the positions for removal if both were
468 if (bCandidate2 && (!bCandidate1 || i2 > i1))
470 remover.markResidue(*atoms, i2, true);
471 pairSearch.skipRemainingPairsForTestPosition();
473 else if (bCandidate1)
475 remover.markResidue(*atoms, i1, true);
480 remover.removeMarkedElements(x);
483 remover.removeMarkedElements(v);
485 remover.removeMarkedElements(r);
486 const int originalAtomCount = atoms->nr;
487 remover.removeMarkedAtoms(atoms);
488 fprintf(stderr, "Removed %d solvent atoms due to solvent-solvent overlap\n",
489 originalAtomCount - atoms->nr);
493 * Remove all solvent molecules outside a give radius from the solute.
495 * \param[in,out] atoms Solvent atoms.
496 * \param[in,out] x_solvent Solvent positions.
497 * \param[in,out] v_solvent Solvent velocities.
498 * \param[in,out] r Atomic exclusion radii.
499 * \param[in] pbc PBC information.
500 * \param[in] x_solute Solute positions.
501 * \param[in] rshell The radius outside the solute molecule.
503 static void removeSolventOutsideShell(t_atoms *atoms,
504 std::vector<RVec> *x_solvent,
505 std::vector<RVec> *v_solvent,
506 std::vector<real> *r,
508 const std::vector<RVec> &x_solute,
511 gmx::AtomsRemover remover(*atoms);
512 gmx::AnalysisNeighborhood nb;
513 nb.setCutoff(rshell);
514 gmx::AnalysisNeighborhoodPositions posSolute(x_solute);
515 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(&pbc, posSolute);
516 gmx::AnalysisNeighborhoodPositions pos(*x_solvent);
517 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
518 gmx::AnalysisNeighborhoodPair pair;
522 // Now put back those within the shell without checking for overlap
523 while (pairSearch.findNextPair(&pair))
525 remover.markResidue(*atoms, pair.testIndex(), false);
526 pairSearch.skipRemainingPairsForTestPosition();
528 remover.removeMarkedElements(x_solvent);
529 if (!v_solvent->empty())
531 remover.removeMarkedElements(v_solvent);
533 remover.removeMarkedElements(r);
534 const int originalAtomCount = atoms->nr;
535 remover.removeMarkedAtoms(atoms);
536 fprintf(stderr, "Removed %d solvent atoms more than %f nm from solute.\n",
537 originalAtomCount - atoms->nr, rshell);
541 * Removes solvent molecules that overlap with the solute.
543 * \param[in,out] atoms Solvent atoms.
544 * \param[in,out] x Solvent positions.
545 * \param[in,out] v Solvent velocities (can be empty).
546 * \param[in,out] r Solvent exclusion radii.
547 * \param[in] pbc PBC information.
548 * \param[in] x_solute Solute positions.
549 * \param[in] r_solute Solute exclusion radii.
551 static void removeSolventOverlappingWithSolute(t_atoms *atoms,
552 std::vector<RVec> *x,
553 std::vector<RVec> *v,
554 std::vector<real> *r,
556 const std::vector<RVec> &x_solute,
557 const std::vector<real> &r_solute)
559 gmx::AtomsRemover remover(*atoms);
560 const real maxRadius1
561 = *std::max_element(r->begin(), r->end());
562 const real maxRadius2
563 = *std::max_element(r_solute.begin(), r_solute.end());
565 // Now check for overlap.
566 gmx::AnalysisNeighborhood nb;
567 gmx::AnalysisNeighborhoodPair pair;
568 nb.setCutoff(maxRadius1 + maxRadius2);
569 gmx::AnalysisNeighborhoodPositions posSolute(x_solute);
570 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(&pbc, posSolute);
571 gmx::AnalysisNeighborhoodPositions pos(*x);
572 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
573 while (pairSearch.findNextPair(&pair))
575 if (remover.isMarked(pair.testIndex()))
577 pairSearch.skipRemainingPairsForTestPosition();
580 const real r1 = r_solute[pair.refIndex()];
581 const real r2 = (*r)[pair.testIndex()];
582 const bool bRemove = (pair.distance2() < gmx::square(r1 + r2));
583 remover.markResidue(*atoms, pair.testIndex(), bRemove);
586 remover.removeMarkedElements(x);
589 remover.removeMarkedElements(v);
591 remover.removeMarkedElements(r);
592 const int originalAtomCount = atoms->nr;
593 remover.removeMarkedAtoms(atoms);
594 fprintf(stderr, "Removed %d solvent atoms due to solute-solvent overlap\n",
595 originalAtomCount - atoms->nr);
599 * Removes a given number of solvent residues.
601 * \param[in,out] atoms Solvent atoms.
602 * \param[in,out] x Solvent positions.
603 * \param[in,out] v Solvent velocities (can be empty).
604 * \param[in] numberToRemove Number of residues to remove.
606 * This function is called last in the process of creating the solvent box,
607 * so it does not operate on the exclusion radii, as no code after this needs
610 static void removeExtraSolventMolecules(t_atoms *atoms, std::vector<RVec> *x,
611 std::vector<RVec> *v,
614 gmx::AtomsRemover remover(*atoms);
615 // TODO: It might be nicer to remove a random set of residues, but
616 // in practice this should give a roughly uniform spatial distribution.
617 const int stride = atoms->nr / numberToRemove;
618 for (int i = 0; i < numberToRemove; ++i)
620 int atomIndex = (i+1)*stride - 1;
621 while (remover.isMarked(atomIndex))
624 if (atomIndex == atoms->nr)
629 remover.markResidue(*atoms, atomIndex, true);
631 remover.removeMarkedElements(x);
634 remover.removeMarkedElements(v);
636 remover.removeMarkedAtoms(atoms);
639 static void add_solv(const char *fn, t_topology *top,
640 std::vector<RVec> *x, std::vector<RVec> *v,
641 int ePBC, matrix box, gmx_atomprop_t aps,
642 real defaultDistance, real scaleFactor,
643 real rshell, int max_sol)
645 t_topology *top_solvt;
646 std::vector<RVec> x_solvt;
647 std::vector<RVec> v_solvt;
651 char *filename = gmxlibfn(fn);
653 readConformation(filename, top_solvt, &x_solvt, !v->empty() ? &v_solvt : nullptr,
654 &ePBC_solvt, box_solvt, "solvent");
655 if (gmx::boxIsZero(box_solvt))
657 gmx_fatal(FARGS, "No box information for solvent in %s, please use a properly formatted file\n",
660 t_atoms *atoms_solvt = &top_solvt->atoms;
661 if (0 == atoms_solvt->nr)
663 gmx_fatal(FARGS, "No solvent in %s, please check your input\n", filename);
666 fprintf(stderr, "\n");
668 /* initialise distance arrays for solvent configuration */
669 fprintf(stderr, "Initialising inter-atomic distances...\n");
670 const std::vector<real> exclusionDistances(
671 makeExclusionDistances(&top->atoms, aps, defaultDistance, scaleFactor));
672 std::vector<real> exclusionDistances_solvt(
673 makeExclusionDistances(atoms_solvt, aps, defaultDistance, scaleFactor));
675 /* generate a new solvent configuration */
676 fprintf(stderr, "Generating solvent configuration\n");
678 set_pbc(&pbc, ePBC, box);
679 if (!gmx::boxesAreEqual(box_solvt, box))
681 if (TRICLINIC(box_solvt))
683 gmx_fatal(FARGS, "Generating from non-rectangular solvent boxes is currently not supported.\n"
684 "You can try to pass the same box for -cp and -cs.");
686 /* apply pbc for solvent configuration for whole molecules */
687 rm_res_pbc(atoms_solvt, &x_solvt, box_solvt);
688 replicateSolventBox(atoms_solvt, &x_solvt, &v_solvt, &exclusionDistances_solvt,
690 if (ePBC != epbcNONE)
692 removeSolventBoxOverlap(atoms_solvt, &x_solvt, &v_solvt, &exclusionDistances_solvt, pbc);
695 if (top->atoms.nr > 0)
699 removeSolventOutsideShell(atoms_solvt, &x_solvt, &v_solvt,
700 &exclusionDistances_solvt, pbc, *x, rshell);
702 removeSolventOverlappingWithSolute(atoms_solvt, &x_solvt, &v_solvt,
703 &exclusionDistances_solvt, pbc, *x,
707 if (max_sol > 0 && atoms_solvt->nres > max_sol)
709 const int numberToRemove = atoms_solvt->nres - max_sol;
710 removeExtraSolventMolecules(atoms_solvt, &x_solvt, &v_solvt, numberToRemove);
713 /* Sort the solvent mixture, not the protein... */
714 t_atoms *newatoms = nullptr;
715 sort_molecule(&atoms_solvt, &newatoms, &x_solvt, &v_solvt);
717 // Merge the two configurations.
718 x->insert(x->end(), x_solvt.begin(), x_solvt.end());
721 v->insert(v->end(), v_solvt.begin(), v_solvt.end());
724 gmx::AtomsBuilder builder(&top->atoms, &top->symtab);
725 builder.mergeAtoms(*atoms_solvt);
727 fprintf(stderr, "Generated solvent containing %d atoms in %d residues\n",
728 atoms_solvt->nr, atoms_solvt->nres);
739 static void update_top(t_atoms *atoms, int firstSolventResidueIndex, matrix box, int NFILE, t_filenm fnm[],
743 char buf[STRLEN], buf2[STRLEN], *temp;
744 const char *topinout;
751 int nsol = atoms->nres - firstSolventResidueIndex;
754 for (i = 0; (i < atoms->nr); i++)
756 gmx_atomprop_query(aps, epropMass,
757 *atoms->resinfo[atoms->atom[i].resind].name,
758 *atoms->atomname[i], &mm);
764 fprintf(stderr, "Volume : %10g (nm^3)\n", vol);
765 fprintf(stderr, "Density : %10g (g/l)\n",
766 (mtot*1e24)/(AVOGADRO*vol));
767 fprintf(stderr, "Number of solvent molecules: %5d \n\n", nsol);
769 /* open topology file and append sol molecules */
770 topinout = ftp2fn(efTOP, NFILE, fnm);
771 if (ftp2bSet(efTOP, NFILE, fnm) )
773 char temporary_filename[STRLEN];
774 strncpy(temporary_filename, "temp.topXXXXXX", STRLEN);
776 fprintf(stderr, "Processing topology\n");
777 fpin = gmx_ffopen(topinout, "r");
778 fpout = gmx_fopen_temporary(temporary_filename);
781 while (fgets(buf, STRLEN, fpin))
785 if ((temp = strchr(buf2, '\n')) != nullptr)
793 if ((temp = strchr(buf2, '\n')) != nullptr)
798 if (buf2[strlen(buf2)-1] == ']')
800 buf2[strlen(buf2)-1] = '\0';
803 bSystem = (gmx_strcasecmp(buf2, "system") == 0);
806 else if (bSystem && nsol && (buf[0] != ';') )
808 /* if sol present, append "in water" to system name */
810 if (buf2[0] && (!strstr(buf2, " water")) )
812 sprintf(buf, "%s in water\n", buf2);
816 fprintf(fpout, "%s", buf);
820 // Add new solvent molecules to the topology
823 std::string currRes = *atoms->resinfo[firstSolventResidueIndex].name;
826 // Iterate through solvent molecules and increment a count until new resname found
827 for (int i = firstSolventResidueIndex; i < atoms->nres; i++)
829 if ((currRes == *atoms->resinfo[i].name))
835 // Change topology and restart count
836 fprintf(stdout, "Adding line for %d solvent molecules with resname (%s) to "
837 "topology file (%s)\n", resCount, currRes.c_str(), topinout);
838 fprintf(fpout, "%-15s %5d\n", currRes.c_str(), resCount);
839 currRes = *atoms->resinfo[i].name;
843 // One more print needed for last residue type
844 fprintf(stdout, "Adding line for %d solvent molecules with resname (%s) to "
845 "topology file (%s)\n", resCount, currRes.c_str(), topinout);
846 fprintf(fpout, "%-15s %5d\n", currRes.c_str(), resCount);
849 make_backup(topinout);
850 gmx_file_rename(temporary_filename, topinout);
854 int gmx_solvate(int argc, char *argv[])
856 const char *desc[] = {
857 "[THISMODULE] can do one of 2 things:[PAR]",
859 "1) Generate a box of solvent. Specify [TT]-cs[tt] and [TT]-box[tt].",
860 "Or specify [TT]-cs[tt] and [TT]-cp[tt] with a structure file with",
861 "a box, but without atoms.[PAR]",
863 "2) Solvate a solute configuration, e.g. a protein, in a bath of solvent ",
864 "molecules. Specify [TT]-cp[tt] (solute) and [TT]-cs[tt] (solvent). ",
865 "The box specified in the solute coordinate file ([TT]-cp[tt]) is used,",
866 "unless [TT]-box[tt] is set.",
867 "If you want the solute to be centered in the box,",
868 "the program [gmx-editconf] has sophisticated options",
869 "to change the box dimensions and center the solute.",
870 "Solvent molecules are removed from the box where the ",
871 "distance between any atom of the solute molecule(s) and any atom of ",
872 "the solvent molecule is less than the sum of the scaled van der Waals",
873 "radii of both atoms. A database ([TT]vdwradii.dat[tt]) of van der",
874 "Waals radii is read by the program, and the resulting radii scaled",
875 "by [TT]-scale[tt]. If radii are not found in the database, those",
876 "atoms are assigned the (pre-scaled) distance [TT]-radius[tt].",
877 "Note that the usefulness of those radii depends on the atom names,",
878 "and thus varies widely with force field.",
880 "The default solvent is Simple Point Charge water (SPC), with coordinates ",
881 "from [TT]$GMXLIB/spc216.gro[tt]. These coordinates can also be used",
882 "for other 3-site water models, since a short equibilibration will remove",
883 "the small differences between the models.",
884 "Other solvents are also supported, as well as mixed solvents. The",
885 "only restriction to solvent types is that a solvent molecule consists",
886 "of exactly one residue. The residue information in the coordinate",
887 "files is used, and should therefore be more or less consistent.",
888 "In practice this means that two subsequent solvent molecules in the ",
889 "solvent coordinate file should have different residue number.",
890 "The box of solute is built by stacking the coordinates read from",
891 "the coordinate file. This means that these coordinates should be ",
892 "equlibrated in periodic boundary conditions to ensure a good",
893 "alignment of molecules on the stacking interfaces.",
894 "The [TT]-maxsol[tt] option simply adds only the first [TT]-maxsol[tt]",
895 "solvent molecules and leaves out the rest that would have fitted",
896 "into the box. This can create a void that can cause problems later.",
897 "Choose your volume wisely.[PAR]",
899 "Setting [TT]-shell[tt] larger than zero will place a layer of water of",
900 "the specified thickness (nm) around the solute. Hint: it is a good",
901 "idea to put the protein in the center of a box first (using [gmx-editconf]).",
904 "Finally, [THISMODULE] will optionally remove lines from your topology file in ",
905 "which a number of solvent molecules is already added, and adds a ",
906 "line with the total number of solvent molecules in your coordinate file."
909 const char *bugs[] = {
910 "Molecules must be whole in the initial configurations.",
914 gmx_bool bProt, bBox;
915 const char *conf_prot, *confout;
918 /* solute configuration data */
924 { efSTX, "-cp", "protein", ffOPTRD },
925 { efSTX, "-cs", "spc216", ffLIBRD},
926 { efSTO, nullptr, nullptr, ffWRITE},
927 { efTOP, nullptr, nullptr, ffOPTRW},
929 #define NFILE asize(fnm)
931 real defaultDistance = 0.105, r_shell = 0, scaleFactor = 0.57;
932 rvec new_box = {0.0, 0.0, 0.0};
933 gmx_bool bReadV = FALSE;
935 int firstSolventResidueIndex = 0;
936 gmx_output_env_t *oenv;
938 { "-box", FALSE, etRVEC, {new_box},
939 "Box size (in nm)" },
940 { "-radius", FALSE, etREAL, {&defaultDistance},
941 "Default van der Waals distance"},
942 { "-scale", FALSE, etREAL, {&scaleFactor},
943 "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." },
944 { "-shell", FALSE, etREAL, {&r_shell},
945 "Thickness of optional water layer around solute" },
946 { "-maxsol", FALSE, etINT, {&max_sol},
947 "Maximum number of solvent molecules to add if they fit in the box. If zero (default) this is ignored" },
948 { "-vel", FALSE, etBOOL, {&bReadV},
949 "Keep velocities from input solute and solvent" },
952 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
953 asize(desc), desc, asize(bugs), bugs, &oenv))
958 const char *solventFileName = opt2fn("-cs", NFILE, fnm);
959 bProt = opt2bSet("-cp", NFILE, fnm);
960 bBox = opt2parg_bSet("-box", asize(pa), pa);
965 gmx_fatal(FARGS, "When no solute (-cp) is specified, "
966 "a box size (-box) must be specified");
969 aps = gmx_atomprop_init();
976 /* Generate a solute configuration */
977 conf_prot = opt2fn("-cp", NFILE, fnm);
978 readConformation(conf_prot, top, &x,
979 bReadV ? &v : nullptr, &ePBC, box, "solute");
980 if (bReadV && v.empty())
982 fprintf(stderr, "Note: no velocities found\n");
984 if (top->atoms.nr == 0)
986 fprintf(stderr, "Note: no atoms in %s\n", conf_prot);
991 firstSolventResidueIndex = top->atoms.nres;
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, top, &x, &v, ePBC, 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, &top->atoms, as_rvec_array(x.data()),
1016 !v.empty() ? as_rvec_array(v.data()) : nullptr, ePBC, box);
1018 /* print size of generated configuration */
1019 fprintf(stderr, "\nOutput configuration contains %d atoms in %d residues\n",
1020 top->atoms.nr, top->atoms.nres);
1021 update_top(&top->atoms, firstSolventResidueIndex, box, NFILE, fnm, aps);
1023 gmx_atomprop_destroy(aps);
1026 output_env_done(oenv);