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, std::vector<RVec> *x,
81 int atnr, i, j, moltp = 0, nrmoltypes, resi_o, resi_n, resnr;
83 t_atoms *atoms, *newatoms;
85 fprintf(stderr, "Sorting configuration\n");
89 /* copy each residue from *atoms to a molecule in *molecule */
92 for (i = 0; i < atoms->nr; i++)
94 if ( (i == 0) || (atoms->atom[i].resind != atoms->atom[i-1].resind) )
96 /* see if this was a molecule type we haven't had yet: */
98 for (j = 0; (j < nrmoltypes) && (moltp == -1); j++)
100 /* moltypes is guaranteed to be allocated because otherwise
101 * nrmoltypes is 0. */
102 if (strcmp(*(atoms->resinfo[atoms->atom[i].resind].name), moltypes[j].name) == 0)
111 srenew(moltypes, nrmoltypes);
112 moltypes[moltp].name = *(atoms->resinfo[atoms->atom[i].resind].name);
114 while ((i+atnr < atoms->nr) &&
115 (atoms->atom[i].resind == atoms->atom[i+atnr].resind))
119 moltypes[moltp].natoms = atnr;
120 moltypes[moltp].nmol = 0;
122 moltypes[moltp].nmol++;
126 fprintf(stderr, "Found %d%s molecule type%s:\n",
127 nrmoltypes, nrmoltypes == 1 ? "" : " different", nrmoltypes == 1 ? "" : "s");
128 for (j = 0; j < nrmoltypes; j++)
132 moltypes[j].res0 = 0;
136 moltypes[j].res0 = moltypes[j-1].res0+moltypes[j-1].nmol;
138 fprintf(stderr, "%7s (%4d atoms): %5d residues\n",
139 moltypes[j].name, moltypes[j].natoms, moltypes[j].nmol);
142 /* if we have only 1 moleculetype, we don't have to sort */
145 /* find out which molecules should go where: */
146 moltypes[0].i = moltypes[0].i0 = 0;
147 for (j = 1; j < nrmoltypes; j++)
151 moltypes[j-1].i0+moltypes[j-1].natoms*moltypes[j-1].nmol;
154 /* now put them there: */
156 init_t_atoms(newatoms, atoms->nr, FALSE);
157 newatoms->nres = atoms->nres;
158 snew(newatoms->resinfo, atoms->nres);
159 std::vector<RVec> newx(x->size());
160 std::vector<RVec> newv(v->size());
165 for (moltp = 0; moltp < nrmoltypes; moltp++)
168 while (i < atoms->nr)
170 resi_o = atoms->atom[i].resind;
171 if (strcmp(*atoms->resinfo[resi_o].name, moltypes[moltp].name) == 0)
173 /* Copy the residue info */
174 newatoms->resinfo[resi_n] = atoms->resinfo[resi_o];
175 newatoms->resinfo[resi_n].nr = resnr;
176 /* Copy the atom info */
179 newatoms->atom[j] = atoms->atom[i];
180 newatoms->atomname[j] = atoms->atomname[i];
181 newatoms->atom[j].resind = resi_n;
182 copy_rvec((*x)[i], newx[j]);
185 copy_rvec((*v)[i], newv[j]);
190 while (i < atoms->nr && atoms->atom[i].resind == resi_o);
191 /* Increase the new residue counters */
197 /* Skip this residue */
202 while (i < atoms->nr && atoms->atom[i].resind == resi_o);
207 /* put them back into the original arrays and throw away temporary arrays */
209 *atoms_solvt = newatoms;
216 static void rm_res_pbc(const t_atoms *atoms, std::vector<RVec> *x, matrix box)
224 for (int n = 0; n < atoms->nr; n++)
226 if (!is_hydrogen(*(atoms->atomname[n])))
229 rvec_inc(xcg, (*x)[n]);
231 if ( (n+1 == atoms->nr) ||
232 (atoms->atom[n+1].resind != atoms->atom[n].resind) )
234 /* if nat==0 we have only hydrogens in the solvent,
235 we take last coordinate as cg */
239 copy_rvec((*x)[n], xcg);
241 svmul(1.0/nat, xcg, xcg);
242 for (int d = 0; d < DIM; d++)
246 for (int i = start; i <= n; i++)
248 (*x)[i][d] += box[d][d];
252 while (xcg[d] >= box[d][d])
254 for (int i = start; i <= n; i++)
256 (*x)[i][d] -= box[d][d];
269 * Generates a solvent configuration of desired size by stacking solvent boxes.
271 * \param[in,out] atoms Solvent atoms.
272 * \param[in,out] x Solvent positions.
273 * \param[in,out] v Solvent velocities (`*v` can be NULL).
274 * \param[in,out] r Solvent exclusion radii.
275 * \param[in] box Initial solvent box.
276 * \param[in] boxTarget Target box size.
278 * The solvent box of desired size is created by stacking the initial box in
279 * the smallest k*l*m array that covers the box, and then removing any residue
280 * where all atoms are outside the target box (with a small margin).
281 * This function does not remove overlap between solvent atoms across the
284 * Note that the input configuration should be in the rectangular unit cell and
285 * have whole residues.
287 static void replicateSolventBox(t_atoms *atoms, std::vector<RVec> *x,
288 std::vector<RVec> *v, std::vector<real> *r,
289 const matrix box, const matrix boxTarget)
291 // Calculate the box multiplication factors.
294 for (int i = 0; i < DIM; ++i)
297 while (n_box[i] * box[i][i] < boxTarget[i][i])
303 fprintf(stderr, "Will generate new solvent configuration of %dx%dx%d boxes\n",
304 n_box[XX], n_box[YY], n_box[ZZ]);
306 // Create arrays for storing the generated system (cannot be done in-place
307 // in case the target box is smaller than the original in one dimension,
310 init_t_atoms(&newAtoms, 0, FALSE);
311 gmx::AtomsBuilder builder(&newAtoms, nullptr);
312 builder.reserve(atoms->nr * nmol, atoms->nres * nmol);
313 std::vector<RVec> newX(atoms->nr * nmol);
314 std::vector<RVec> newV(!v->empty() ? atoms->nr * nmol : 0);
315 std::vector<real> newR(atoms->nr * nmol);
317 const real maxRadius = *std::max_element(r->begin(), r->end());
319 for (int i = 0; i < DIM; ++i)
321 // The code below is only interested about the box diagonal.
322 boxWithMargin[i] = boxTarget[i][i] + 3*maxRadius;
325 for (int ix = 0; ix < n_box[XX]; ++ix)
328 delta[XX] = ix*box[XX][XX];
329 for (int iy = 0; iy < n_box[YY]; ++iy)
331 delta[YY] = iy*box[YY][YY];
332 for (int iz = 0; iz < n_box[ZZ]; ++iz)
334 delta[ZZ] = iz*box[ZZ][ZZ];
335 bool bKeepResidue = false;
336 for (int i = 0; i < atoms->nr; ++i)
338 const int newIndex = builder.currentAtomCount();
339 bool bKeepAtom = true;
340 for (int m = 0; m < DIM; ++m)
342 const real newCoord = delta[m] + (*x)[i][m];
343 bKeepAtom = bKeepAtom && (newCoord < boxWithMargin[m]);
344 newX[newIndex][m] = newCoord;
346 bKeepResidue = bKeepResidue || bKeepAtom;
349 copy_rvec((*v)[i], newV[newIndex]);
351 newR[newIndex] = (*r)[i];
352 builder.addAtom(*atoms, i);
353 if (i == atoms->nr - 1
354 || atoms->atom[i+1].resind != atoms->atom[i].resind)
358 builder.finishResidue(atoms->resinfo[atoms->atom[i].resind]);
362 builder.discardCurrentResidue();
364 // Reset state for the next residue.
365 bKeepResidue = false;
372 sfree(atoms->atomname);
373 sfree(atoms->resinfo);
374 atoms->nr = newAtoms.nr;
375 atoms->nres = newAtoms.nres;
376 atoms->atom = newAtoms.atom;
377 atoms->atomname = newAtoms.atomname;
378 atoms->resinfo = newAtoms.resinfo;
380 newX.resize(atoms->nr);
384 newV.resize(atoms->nr);
387 newR.resize(atoms->nr);
390 fprintf(stderr, "Solvent box contains %d atoms in %d residues\n",
391 atoms->nr, atoms->nres);
395 * Removes overlap of solvent atoms across the edges.
397 * \param[in,out] atoms Solvent atoms.
398 * \param[in,out] x Solvent positions.
399 * \param[in,out] v Solvent velocities (can be empty).
400 * \param[in,out] r Solvent exclusion radii.
401 * \param[in] pbc PBC information.
403 * Solvent residues that lay on the edges that do not touch the origin are
404 * removed if they overlap with other solvent atoms across the PBC.
405 * This is done in this way as the assumption is that the input solvent
406 * configuration is already equilibrated, and so does not contain any
407 * undesirable overlap. The only overlap that should be removed is caused by
408 * cutting the box in half in replicateSolventBox() and leaving a margin of
409 * solvent outside those box edges; these atoms can then overlap with those on
410 * the opposite box edge in a way that is not part of the pre-equilibrated
413 static void removeSolventBoxOverlap(t_atoms *atoms, std::vector<RVec> *x,
414 std::vector<RVec> *v, std::vector<real> *r,
417 gmx::AtomsRemover remover(*atoms);
419 // TODO: We could limit the amount of pairs searched significantly,
420 // since we are only interested in pairs where the positions are on
422 const real maxRadius = *std::max_element(r->begin(), r->end());
423 gmx::AnalysisNeighborhood nb;
424 nb.setCutoff(2*maxRadius);
425 gmx::AnalysisNeighborhoodPositions pos(*x);
426 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(&pbc, pos);
427 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
428 gmx::AnalysisNeighborhoodPair pair;
429 while (pairSearch.findNextPair(&pair))
431 const int i1 = pair.refIndex();
432 const int i2 = pair.testIndex();
433 if (remover.isMarked(i2))
435 pairSearch.skipRemainingPairsForTestPosition();
438 if (remover.isMarked(i1) || atoms->atom[i1].resind == atoms->atom[i2].resind)
442 if (pair.distance2() < gmx::square((*r)[i1] + (*r)[i2]))
445 rvec_sub((*x)[i2], (*x)[i1], dx);
446 bool bCandidate1 = false, bCandidate2 = false;
447 // To satisfy Clang static analyzer.
448 GMX_ASSERT(pbc.ndim_ePBC <= DIM, "Too many periodic dimensions");
449 for (int d = 0; d < pbc.ndim_ePBC; ++d)
451 // If the distance in some dimension is larger than the
452 // cutoff, then it means that the distance has been computed
453 // over the PBC. Mark the position with a larger coordinate
454 // for potential removal.
455 if (dx[d] > maxRadius)
459 else if (dx[d] < -maxRadius)
464 // Only mark one of the positions for removal if both were
466 if (bCandidate2 && (!bCandidate1 || i2 > i1))
468 remover.markResidue(*atoms, i2, true);
469 pairSearch.skipRemainingPairsForTestPosition();
471 else if (bCandidate1)
473 remover.markResidue(*atoms, i1, true);
478 remover.removeMarkedElements(x);
481 remover.removeMarkedElements(v);
483 remover.removeMarkedElements(r);
484 const int originalAtomCount = atoms->nr;
485 remover.removeMarkedAtoms(atoms);
486 fprintf(stderr, "Removed %d solvent atoms due to solvent-solvent overlap\n",
487 originalAtomCount - atoms->nr);
491 * Remove all solvent molecules outside a give radius from the solute.
493 * \param[in,out] atoms Solvent atoms.
494 * \param[in,out] x_solvent Solvent positions.
495 * \param[in,out] v_solvent Solvent velocities.
496 * \param[in,out] r Atomic exclusion radii.
497 * \param[in] pbc PBC information.
498 * \param[in] x_solute Solute positions.
499 * \param[in] rshell The radius outside the solute molecule.
501 static void removeSolventOutsideShell(t_atoms *atoms,
502 std::vector<RVec> *x_solvent,
503 std::vector<RVec> *v_solvent,
504 std::vector<real> *r,
506 const std::vector<RVec> &x_solute,
509 gmx::AtomsRemover remover(*atoms);
510 gmx::AnalysisNeighborhood nb;
511 nb.setCutoff(rshell);
512 gmx::AnalysisNeighborhoodPositions posSolute(x_solute);
513 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(&pbc, posSolute);
514 gmx::AnalysisNeighborhoodPositions pos(*x_solvent);
515 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
516 gmx::AnalysisNeighborhoodPair pair;
520 // Now put back those within the shell without checking for overlap
521 while (pairSearch.findNextPair(&pair))
523 remover.markResidue(*atoms, pair.testIndex(), false);
524 pairSearch.skipRemainingPairsForTestPosition();
526 remover.removeMarkedElements(x_solvent);
527 if (!v_solvent->empty())
529 remover.removeMarkedElements(v_solvent);
531 remover.removeMarkedElements(r);
532 const int originalAtomCount = atoms->nr;
533 remover.removeMarkedAtoms(atoms);
534 fprintf(stderr, "Removed %d solvent atoms more than %f nm from solute.\n",
535 originalAtomCount - atoms->nr, rshell);
539 * Removes solvent molecules that overlap with the solute.
541 * \param[in,out] atoms Solvent atoms.
542 * \param[in,out] x Solvent positions.
543 * \param[in,out] v Solvent velocities (can be empty).
544 * \param[in,out] r Solvent exclusion radii.
545 * \param[in] pbc PBC information.
546 * \param[in] x_solute Solute positions.
547 * \param[in] r_solute Solute exclusion radii.
549 static void removeSolventOverlappingWithSolute(t_atoms *atoms,
550 std::vector<RVec> *x,
551 std::vector<RVec> *v,
552 std::vector<real> *r,
554 const std::vector<RVec> &x_solute,
555 const std::vector<real> &r_solute)
557 gmx::AtomsRemover remover(*atoms);
558 const real maxRadius1
559 = *std::max_element(r->begin(), r->end());
560 const real maxRadius2
561 = *std::max_element(r_solute.begin(), r_solute.end());
563 // Now check for overlap.
564 gmx::AnalysisNeighborhood nb;
565 gmx::AnalysisNeighborhoodPair pair;
566 nb.setCutoff(maxRadius1 + maxRadius2);
567 gmx::AnalysisNeighborhoodPositions posSolute(x_solute);
568 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(&pbc, posSolute);
569 gmx::AnalysisNeighborhoodPositions pos(*x);
570 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
571 while (pairSearch.findNextPair(&pair))
573 if (remover.isMarked(pair.testIndex()))
575 pairSearch.skipRemainingPairsForTestPosition();
578 const real r1 = r_solute[pair.refIndex()];
579 const real r2 = (*r)[pair.testIndex()];
580 const bool bRemove = (pair.distance2() < gmx::square(r1 + r2));
581 remover.markResidue(*atoms, pair.testIndex(), bRemove);
584 remover.removeMarkedElements(x);
587 remover.removeMarkedElements(v);
589 remover.removeMarkedElements(r);
590 const int originalAtomCount = atoms->nr;
591 remover.removeMarkedAtoms(atoms);
592 fprintf(stderr, "Removed %d solvent atoms due to solute-solvent overlap\n",
593 originalAtomCount - atoms->nr);
597 * Removes a given number of solvent residues.
599 * \param[in,out] atoms Solvent atoms.
600 * \param[in,out] x Solvent positions.
601 * \param[in,out] v Solvent velocities (can be empty).
602 * \param[in] numberToRemove Number of residues to remove.
604 * This function is called last in the process of creating the solvent box,
605 * so it does not operate on the exclusion radii, as no code after this needs
608 static void removeExtraSolventMolecules(t_atoms *atoms, std::vector<RVec> *x,
609 std::vector<RVec> *v,
612 gmx::AtomsRemover remover(*atoms);
613 // TODO: It might be nicer to remove a random set of residues, but
614 // in practice this should give a roughly uniform spatial distribution.
615 const int stride = atoms->nr / numberToRemove;
616 for (int i = 0; i < numberToRemove; ++i)
618 int atomIndex = (i+1)*stride - 1;
619 while (remover.isMarked(atomIndex))
622 if (atomIndex == atoms->nr)
627 remover.markResidue(*atoms, atomIndex, true);
629 remover.removeMarkedElements(x);
632 remover.removeMarkedElements(v);
634 remover.removeMarkedAtoms(atoms);
637 static void add_solv(const char *fn, t_topology *top,
638 std::vector<RVec> *x, std::vector<RVec> *v,
639 int ePBC, matrix box, gmx_atomprop_t aps,
640 real defaultDistance, real scaleFactor,
641 real rshell, int max_sol)
643 t_topology *top_solvt;
644 std::vector<RVec> x_solvt;
645 std::vector<RVec> v_solvt;
649 char *filename = gmxlibfn(fn);
651 readConformation(filename, top_solvt, &x_solvt, !v->empty() ? &v_solvt : nullptr,
652 &ePBC_solvt, box_solvt, "solvent");
653 if (gmx::boxIsZero(box_solvt))
655 gmx_fatal(FARGS, "No box information for solvent in %s, please use a properly formatted file\n",
658 t_atoms *atoms_solvt = &top_solvt->atoms;
659 if (0 == atoms_solvt->nr)
661 gmx_fatal(FARGS, "No solvent in %s, please check your input\n", filename);
664 fprintf(stderr, "\n");
666 /* initialise distance arrays for solvent configuration */
667 fprintf(stderr, "Initialising inter-atomic distances...\n");
668 const std::vector<real> exclusionDistances(
669 makeExclusionDistances(&top->atoms, aps, defaultDistance, scaleFactor));
670 std::vector<real> exclusionDistances_solvt(
671 makeExclusionDistances(atoms_solvt, aps, defaultDistance, scaleFactor));
673 /* generate a new solvent configuration */
674 fprintf(stderr, "Generating solvent configuration\n");
676 set_pbc(&pbc, ePBC, box);
677 if (!gmx::boxesAreEqual(box_solvt, box))
679 if (TRICLINIC(box_solvt))
681 gmx_fatal(FARGS, "Generating from non-rectangular solvent boxes is currently not supported.\n"
682 "You can try to pass the same box for -cp and -cs.");
684 /* apply pbc for solvent configuration for whole molecules */
685 rm_res_pbc(atoms_solvt, &x_solvt, box_solvt);
686 replicateSolventBox(atoms_solvt, &x_solvt, &v_solvt, &exclusionDistances_solvt,
688 if (ePBC != epbcNONE)
690 removeSolventBoxOverlap(atoms_solvt, &x_solvt, &v_solvt, &exclusionDistances_solvt, pbc);
693 if (top->atoms.nr > 0)
697 removeSolventOutsideShell(atoms_solvt, &x_solvt, &v_solvt,
698 &exclusionDistances_solvt, pbc, *x, rshell);
700 removeSolventOverlappingWithSolute(atoms_solvt, &x_solvt, &v_solvt,
701 &exclusionDistances_solvt, pbc, *x,
705 if (max_sol > 0 && atoms_solvt->nres > max_sol)
707 const int numberToRemove = atoms_solvt->nres - max_sol;
708 removeExtraSolventMolecules(atoms_solvt, &x_solvt, &v_solvt, numberToRemove);
711 /* Sort the solvent mixture, not the protein... */
712 sort_molecule(&atoms_solvt, &x_solvt, &v_solvt);
714 // Merge the two configurations.
715 x->insert(x->end(), x_solvt.begin(), x_solvt.end());
718 v->insert(v->end(), v_solvt.begin(), v_solvt.end());
721 gmx::AtomsBuilder builder(&top->atoms, &top->symtab);
722 builder.mergeAtoms(*atoms_solvt);
724 fprintf(stderr, "Generated solvent containing %d atoms in %d residues\n",
725 atoms_solvt->nr, atoms_solvt->nres);
731 static void update_top(t_atoms *atoms, matrix box, int NFILE, t_filenm fnm[],
735 char buf[STRLEN], buf2[STRLEN], *temp;
736 const char *topinout;
738 bool bSystem, bMolecules, bSkip;
743 for (i = 0; (i < atoms->nres); i++)
745 /* calculate number of SOLvent molecules */
746 if ( (strcmp(*atoms->resinfo[i].name, "SOL") == 0) ||
747 (strcmp(*atoms->resinfo[i].name, "WAT") == 0) ||
748 (strcmp(*atoms->resinfo[i].name, "HOH") == 0) )
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 SOL 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);
780 bSystem = bMolecules = false;
781 while (fgets(buf, STRLEN, fpin))
786 if ((temp = strchr(buf2, '\n')) != nullptr)
794 if ((temp = strchr(buf2, '\n')) != nullptr)
799 if (buf2[strlen(buf2)-1] == ']')
801 buf2[strlen(buf2)-1] = '\0';
804 bSystem = (gmx_strcasecmp(buf2, "system") == 0);
805 bMolecules = (gmx_strcasecmp(buf2, "molecules") == 0);
808 else if (bSystem && nsol && (buf[0] != ';') )
810 /* if sol present, append "in water" to system name */
812 if (buf2[0] && (!strstr(buf2, " water")) )
814 sprintf(buf, "%s in water\n", buf2);
820 /* check if this is a line with solvent molecules */
821 sscanf(buf, "%4095s", buf2);
822 if (strcmp(buf2, "SOL") == 0)
824 sscanf(buf, "%*4095s %20d", &i);
835 if ((temp = strchr(buf, '\n')) != nullptr)
839 fprintf(stdout, "Removing line #%d '%s' from topology file (%s)\n",
840 line, buf, topinout);
844 fprintf(fpout, "%s", buf);
850 fprintf(stdout, "Adding line for %d solvent molecules to "
851 "topology file (%s)\n", nsol, topinout);
852 fprintf(fpout, "%-15s %5d\n", "SOL", nsol);
855 make_backup(topinout);
856 gmx_file_rename(temporary_filename, topinout);
860 int gmx_solvate(int argc, char *argv[])
862 const char *desc[] = {
863 "[THISMODULE] can do one of 2 things:[PAR]",
865 "1) Generate a box of solvent. Specify [TT]-cs[tt] and [TT]-box[tt].",
866 "Or specify [TT]-cs[tt] and [TT]-cp[tt] with a structure file with",
867 "a box, but without atoms.[PAR]",
869 "2) Solvate a solute configuration, e.g. a protein, in a bath of solvent ",
870 "molecules. Specify [TT]-cp[tt] (solute) and [TT]-cs[tt] (solvent). ",
871 "The box specified in the solute coordinate file ([TT]-cp[tt]) is used,",
872 "unless [TT]-box[tt] is set.",
873 "If you want the solute to be centered in the box,",
874 "the program [gmx-editconf] has sophisticated options",
875 "to change the box dimensions and center the solute.",
876 "Solvent molecules are removed from the box where the ",
877 "distance between any atom of the solute molecule(s) and any atom of ",
878 "the solvent molecule is less than the sum of the scaled van der Waals",
879 "radii of both atoms. A database ([TT]vdwradii.dat[tt]) of van der",
880 "Waals radii is read by the program, and the resulting radii scaled",
881 "by [TT]-scale[tt]. If radii are not found in the database, those",
882 "atoms are assigned the (pre-scaled) distance [TT]-radius[tt].",
883 "Note that the usefulness of those radii depends on the atom names,",
884 "and thus varies widely with force field.",
886 "The default solvent is Simple Point Charge water (SPC), with coordinates ",
887 "from [TT]$GMXLIB/spc216.gro[tt]. These coordinates can also be used",
888 "for other 3-site water models, since a short equibilibration will remove",
889 "the small differences between the models.",
890 "Other solvents are also supported, as well as mixed solvents. The",
891 "only restriction to solvent types is that a solvent molecule consists",
892 "of exactly one residue. The residue information in the coordinate",
893 "files is used, and should therefore be more or less consistent.",
894 "In practice this means that two subsequent solvent molecules in the ",
895 "solvent coordinate file should have different residue number.",
896 "The box of solute is built by stacking the coordinates read from",
897 "the coordinate file. This means that these coordinates should be ",
898 "equlibrated in periodic boundary conditions to ensure a good",
899 "alignment of molecules on the stacking interfaces.",
900 "The [TT]-maxsol[tt] option simply adds only the first [TT]-maxsol[tt]",
901 "solvent molecules and leaves out the rest that would have fitted",
902 "into the box. This can create a void that can cause problems later.",
903 "Choose your volume wisely.[PAR]",
905 "Setting [TT]-shell[tt] larger than zero will place a layer of water of",
906 "the specified thickness (nm) around the solute. Hint: it is a good",
907 "idea to put the protein in the center of a box first (using [gmx-editconf]).",
910 "Finally, [THISMODULE] will optionally remove lines from your topology file in ",
911 "which a number of solvent molecules is already added, and adds a ",
912 "line with the total number of solvent molecules in your coordinate file."
915 const char *bugs[] = {
916 "Molecules must be whole in the initial configurations.",
920 gmx_bool bProt, bBox;
921 const char *conf_prot, *confout;
924 /* solute configuration data */
930 { efSTX, "-cp", "protein", ffOPTRD },
931 { efSTX, "-cs", "spc216", ffLIBRD},
932 { efSTO, nullptr, nullptr, ffWRITE},
933 { efTOP, nullptr, nullptr, ffOPTRW},
935 #define NFILE asize(fnm)
937 real defaultDistance = 0.105, r_shell = 0, scaleFactor = 0.57;
938 rvec new_box = {0.0, 0.0, 0.0};
939 gmx_bool bReadV = FALSE;
941 gmx_output_env_t *oenv;
943 { "-box", FALSE, etRVEC, {new_box},
944 "Box size (in nm)" },
945 { "-radius", FALSE, etREAL, {&defaultDistance},
946 "Default van der Waals distance"},
947 { "-scale", FALSE, etREAL, {&scaleFactor},
948 "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." },
949 { "-shell", FALSE, etREAL, {&r_shell},
950 "Thickness of optional water layer around solute" },
951 { "-maxsol", FALSE, etINT, {&max_sol},
952 "Maximum number of solvent molecules to add if they fit in the box. If zero (default) this is ignored" },
953 { "-vel", FALSE, etBOOL, {&bReadV},
954 "Keep velocities from input solute and solvent" },
957 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
958 asize(desc), desc, asize(bugs), bugs, &oenv))
963 const char *solventFileName = opt2fn("-cs", NFILE, fnm);
964 bProt = opt2bSet("-cp", NFILE, fnm);
965 bBox = opt2parg_bSet("-box", asize(pa), pa);
970 gmx_fatal(FARGS, "When no solute (-cp) is specified, "
971 "a box size (-box) must be specified");
974 aps = gmx_atomprop_init();
981 /* Generate a solute configuration */
982 conf_prot = opt2fn("-cp", NFILE, fnm);
983 readConformation(conf_prot, top, &x,
984 bReadV ? &v : nullptr, &ePBC, box, "solute");
985 if (bReadV && v.empty())
987 fprintf(stderr, "Note: no velocities found\n");
989 if (top->atoms.nr == 0)
991 fprintf(stderr, "Note: no atoms in %s\n", conf_prot);
999 box[XX][XX] = new_box[XX];
1000 box[YY][YY] = new_box[YY];
1001 box[ZZ][ZZ] = new_box[ZZ];
1005 gmx_fatal(FARGS, "Undefined solute box.\nCreate one with gmx editconf "
1006 "or give explicit -box command line option");
1009 add_solv(solventFileName, top, &x, &v, ePBC, box,
1010 aps, defaultDistance, scaleFactor, r_shell, max_sol);
1012 /* write new configuration 1 to file confout */
1013 confout = ftp2fn(efSTO, NFILE, fnm);
1014 fprintf(stderr, "Writing generated configuration to %s\n", confout);
1015 const char *outputTitle = (bProt ? *top->name : "Generated by gmx solvate");
1016 write_sto_conf(confout, outputTitle, &top->atoms, as_rvec_array(x.data()),
1017 !v.empty() ? as_rvec_array(v.data()) : nullptr, ePBC, box);
1019 /* print size of generated configuration */
1020 fprintf(stderr, "\nOutput configuration contains %d atoms in %d residues\n",
1021 top->atoms.nr, top->atoms.nres);
1022 update_top(&top->atoms, box, NFILE, fnm, aps);
1024 gmx_atomprop_destroy(aps);
1027 output_env_done(oenv);