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-2008, 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.
44 #include "gromacs/commandline/pargs.h"
45 #include "gromacs/fileio/confio.h"
46 #include "gromacs/fileio/gmxfio.h"
47 #include "gromacs/gmxpreprocess/gen_ad.h"
48 #include "gromacs/gmxpreprocess/gpp_nextnb.h"
49 #include "gromacs/gmxpreprocess/hackblock.h"
50 #include "gromacs/gmxpreprocess/nm2type.h"
51 #include "gromacs/gmxpreprocess/notset.h"
52 #include "gromacs/gmxpreprocess/pdb2top.h"
53 #include "gromacs/gmxpreprocess/toppush.h"
54 #include "gromacs/listed-forces/bonded.h"
55 #include "gromacs/math/units.h"
56 #include "gromacs/math/utilities.h"
57 #include "gromacs/math/vec.h"
58 #include "gromacs/math/vecdump.h"
59 #include "gromacs/mdtypes/md_enums.h"
60 #include "gromacs/pbcutil/pbc.h"
61 #include "gromacs/topology/symtab.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/gmxassert.h"
67 #include "gromacs/utility/smalloc.h"
69 #define MARGIN_FAC 1.1
71 static bool is_bond(int nnm, t_nm2type nmt[], char *ai, char *aj, real blen)
75 for (i = 0; (i < nnm); i++)
77 for (j = 0; (j < nmt[i].nbonds); j++)
79 if ((((gmx_strncasecmp(ai, nmt[i].elem, 1) == 0) &&
80 (gmx_strncasecmp(aj, nmt[i].bond[j], 1) == 0)) ||
81 ((gmx_strncasecmp(ai, nmt[i].bond[j], 1) == 0) &&
82 (gmx_strncasecmp(aj, nmt[i].elem, 1) == 0))) &&
83 (fabs(blen-nmt[i].blen[j]) <= 0.1*nmt[i].blen[j]))
92 static void mk_bonds(int nnm, t_nm2type nmt[],
93 t_atoms *atoms, const rvec x[], t_params *bond, int nbond[],
94 bool bPBC, matrix box)
102 for (i = 0; (i < MAXATOMLIST); i++)
106 for (i = 0; (i < MAXFORCEPARAM); i++)
113 set_pbc(&pbc, -1, box);
115 for (i = 0; (i < atoms->nr); i++)
119 fprintf(stderr, "\ratom %d", i);
122 for (j = i+1; (j < atoms->nr); j++)
126 pbc_dx(&pbc, x[i], x[j], dx);
130 rvec_sub(x[i], x[j], dx);
134 if (is_bond(nnm, nmt, *atoms->atomname[i], *atoms->atomname[j],
139 b.c0() = std::sqrt(dx2);
140 add_param_to_list (bond, &b);
145 fprintf(debug, "Bonding atoms %s-%d and %s-%d\n",
146 *atoms->atomname[i], i+1, *atoms->atomname[j], j+1);
151 fprintf(stderr, "\ratom %d\n", i);
155 static int *set_cgnr(t_atoms *atoms, bool bUsePDBcharge, real *qtot, real *mtot)
162 snew(cgnr, atoms->nr);
163 for (i = 0; (i < atoms->nr); i++)
165 if (atoms->pdbinfo && bUsePDBcharge)
167 atoms->atom[i].q = atoms->pdbinfo[i].bfac;
169 qt += atoms->atom[i].q;
170 *qtot += atoms->atom[i].q;
171 *mtot += atoms->atom[i].m;
182 static gpp_atomtype_t set_atom_type(t_symtab *tab, t_atoms *atoms, t_params *bonds,
183 int *nbonds, int nnm, t_nm2type nm2t[])
185 gpp_atomtype_t atype;
188 atype = init_atomtype();
189 snew(atoms->atomtype, atoms->nr);
190 nresolved = nm2type(nnm, nm2t, tab, atoms, atype, nbonds, bonds);
191 if (nresolved != atoms->nr)
193 gmx_fatal(FARGS, "Could only find a forcefield type for %d out of %d atoms",
194 nresolved, atoms->nr);
197 fprintf(stderr, "There are %d different atom types in your sample\n",
198 get_atomtype_ntypes(atype));
203 static void lo_set_force_const(t_params *plist, real c[], int nrfp, bool bRound,
204 bool bDih, bool bParam)
210 for (i = 0; (i < plist->nr); i++)
214 for (j = 0; j < nrfp; j++)
223 sprintf(buf, "%.2e", plist->param[i].c[0]);
224 sscanf(buf, "%lf", &cc);
229 c[0] = plist->param[i].c[0];
234 c[0] = ((int)(c[0] + 3600)) % 360;
239 /* To put the minimum at the current angle rather than the maximum */
243 GMX_ASSERT(nrfp <= MAXFORCEPARAM/2, "Only 6 parameters may be used for an interaction");
244 for (j = 0; (j < nrfp); j++)
246 plist->param[i].c[j] = c[j];
247 plist->param[i].c[nrfp+j] = c[j];
249 set_p_string(&(plist->param[i]), "");
253 static void set_force_const(t_params plist[], real kb, real kt, real kp, bool bRound,
256 real c[MAXFORCEPARAM];
260 lo_set_force_const(&plist[F_BONDS], c, 2, bRound, FALSE, bParam);
262 lo_set_force_const(&plist[F_ANGLES], c, 2, bRound, FALSE, bParam);
265 lo_set_force_const(&plist[F_PDIHS], c, 3, bRound, TRUE, bParam);
268 static void calc_angles_dihs(t_params *ang, t_params *dih, const rvec x[], bool bPBC,
271 int i, ai, aj, ak, al, t1, t2, t3;
272 rvec r_ij, r_kj, r_kl, m, n;
278 set_pbc(&pbc, epbcXYZ, box);
282 pr_rvecs(debug, 0, "X2TOP", box, DIM);
284 for (i = 0; (i < ang->nr); i++)
286 ai = ang->param[i].ai();
287 aj = ang->param[i].aj();
288 ak = ang->param[i].ak();
289 th = RAD2DEG*bond_angle(x[ai], x[aj], x[ak], bPBC ? &pbc : nullptr,
290 r_ij, r_kj, &costh, &t1, &t2);
293 fprintf(debug, "X2TOP: ai=%3d aj=%3d ak=%3d r_ij=%8.3f r_kj=%8.3f th=%8.3f\n",
294 ai, aj, ak, norm(r_ij), norm(r_kj), th);
296 ang->param[i].c0() = th;
298 for (i = 0; (i < dih->nr); i++)
300 ai = dih->param[i].ai();
301 aj = dih->param[i].aj();
302 ak = dih->param[i].ak();
303 al = dih->param[i].al();
304 ph = RAD2DEG*dih_angle(x[ai], x[aj], x[ak], x[al], bPBC ? &pbc : nullptr,
305 r_ij, r_kj, r_kl, m, n, &t1, &t2, &t3);
308 fprintf(debug, "X2TOP: ai=%3d aj=%3d ak=%3d al=%3d r_ij=%8.3f r_kj=%8.3f r_kl=%8.3f ph=%8.3f\n",
309 ai, aj, ak, al, norm(r_ij), norm(r_kj), norm(r_kl), ph);
311 dih->param[i].c0() = ph;
315 static void dump_hybridization(FILE *fp, t_atoms *atoms, int nbonds[])
319 for (i = 0; (i < atoms->nr); i++)
321 fprintf(fp, "Atom %5s has %1d bonds\n", *atoms->atomname[i], nbonds[i]);
325 static void print_pl(FILE *fp, t_params plist[], int ftp, const char *name,
328 int i, j, nral, nrfp;
330 if (plist[ftp].nr > 0)
333 fprintf(fp, "[ %s ]\n", name);
334 nral = interaction_function[ftp].nratoms;
335 nrfp = interaction_function[ftp].nrfpA;
336 for (i = 0; (i < plist[ftp].nr); i++)
338 for (j = 0; (j < nral); j++)
340 fprintf(fp, " %5s", *atomname[plist[ftp].param[i].a[j]]);
342 for (j = 0; (j < nrfp); j++)
344 if (plist[ftp].param[i].c[j] != NOTSET)
346 fprintf(fp, " %10.3e", plist[ftp].param[i].c[j]);
354 static void print_rtp(const char *filenm, const char *title, t_atoms *atoms,
355 t_params plist[], gpp_atomtype_t atype, int cgnr[])
361 fp = gmx_fio_fopen(filenm, "w");
362 fprintf(fp, "; %s\n", title);
364 fprintf(fp, "[ %s ]\n", *atoms->resinfo[0].name);
366 fprintf(fp, "[ atoms ]\n");
367 for (i = 0; (i < atoms->nr); i++)
369 tp = atoms->atom[i].type;
370 if ((tpnm = get_atomtype_name(tp, atype)) == nullptr)
372 gmx_fatal(FARGS, "tp = %d, i = %d in print_rtp", tp, i);
374 fprintf(fp, "%-8s %12s %8.4f %5d\n",
375 *atoms->atomname[i], tpnm,
376 atoms->atom[i].q, cgnr[i]);
378 print_pl(fp, plist, F_BONDS, "bonds", atoms->atomname);
379 print_pl(fp, plist, F_ANGLES, "angles", atoms->atomname);
380 print_pl(fp, plist, F_PDIHS, "dihedrals", atoms->atomname);
381 print_pl(fp, plist, F_IDIHS, "impropers", atoms->atomname);
386 int gmx_x2top(int argc, char *argv[])
388 const char *desc[] = {
389 "[THISMODULE] generates a primitive topology from a coordinate file.",
390 "The program assumes all hydrogens are present when defining",
391 "the hybridization from the atom name and the number of bonds.",
392 "The program can also make an [REF].rtp[ref] entry, which you can then add",
393 "to the [REF].rtp[ref] database.[PAR]",
394 "When [TT]-param[tt] is set, equilibrium distances and angles",
395 "and force constants will be printed in the topology for all",
396 "interactions. The equilibrium distances and angles are taken",
397 "from the input coordinates, the force constant are set with",
398 "command line options.",
399 "The force fields somewhat supported currently are:[PAR]",
400 "G53a5 GROMOS96 53a5 Forcefield (official distribution)[PAR]",
401 "oplsaa OPLS-AA/L all-atom force field (2001 aminoacid dihedrals)[PAR]",
402 "The corresponding data files can be found in the library directory",
403 "with name [TT]atomname2type.n2t[tt]. Check Chapter 5 of the manual for more",
404 "information about file formats. By default, the force field selection",
405 "is interactive, but you can use the [TT]-ff[tt] option to specify",
406 "one of the short names above on the command line instead. In that",
407 "case [THISMODULE] just looks for the corresponding file.[PAR]",
409 const char *bugs[] = {
410 "The atom type selection is primitive. Virtually no chemical knowledge is used",
411 "Periodic boundary conditions screw up the bonding",
412 "No improper dihedrals are generated",
413 "The atoms to atomtype translation table is incomplete ([TT]atomname2type.n2t[tt] file in the data directory). Please extend it and send the results back to the GROMACS crew."
416 t_params plist[F_NRE];
418 gpp_atomtype_t atype;
423 char forcefield[32], ffdir[STRLEN];
424 rvec *x; /* coordinates? */
426 int bts[] = { 1, 1, 1, 2 };
427 matrix box; /* box length matrix */
428 int natoms; /* number of atoms in one molecule */
430 bool bRTP, bTOP, bOPLS;
434 gmx_output_env_t *oenv;
437 { efSTX, "-f", "conf", ffREAD },
438 { efTOP, "-o", "out", ffOPTWR },
439 { efRTP, "-r", "out", ffOPTWR }
441 #define NFILE asize(fnm)
442 real kb = 4e5, kt = 400, kp = 5;
443 t_restp rtp_header_settings = { nullptr };
444 bool bRemoveDihedralIfWithImproper = FALSE;
445 bool bGenerateHH14Interactions = TRUE;
446 bool bKeepAllGeneratedDihedrals = FALSE;
448 bool bParam = TRUE, bRound = TRUE;
449 bool bPairs = TRUE, bPBC = TRUE;
450 bool bUsePDBcharge = FALSE, bVerbose = FALSE;
451 const char *molnm = "ICE";
452 const char *ff = "oplsaa";
454 { "-ff", FALSE, etSTR, {&ff},
455 "Force field for your simulation. Type \"select\" for interactive selection." },
456 { "-v", FALSE, etBOOL, {&bVerbose},
457 "Generate verbose output in the top file." },
458 { "-nexcl", FALSE, etINT, {&nrexcl},
459 "Number of exclusions" },
460 { "-H14", FALSE, etBOOL, {&bGenerateHH14Interactions},
461 "Use 3rd neighbour interactions for hydrogen atoms" },
462 { "-alldih", FALSE, etBOOL, {&bKeepAllGeneratedDihedrals},
463 "Generate all proper dihedrals" },
464 { "-remdih", FALSE, etBOOL, {&bRemoveDihedralIfWithImproper},
465 "Remove dihedrals on the same bond as an improper" },
466 { "-pairs", FALSE, etBOOL, {&bPairs},
467 "Output 1-4 interactions (pairs) in topology file" },
468 { "-name", FALSE, etSTR, {&molnm},
469 "Name of your molecule" },
470 { "-pbc", FALSE, etBOOL, {&bPBC},
471 "Use periodic boundary conditions." },
472 { "-pdbq", FALSE, etBOOL, {&bUsePDBcharge},
473 "Use the B-factor supplied in a [REF].pdb[ref] file for the atomic charges" },
474 { "-param", FALSE, etBOOL, {&bParam},
475 "Print parameters in the output" },
476 { "-round", FALSE, etBOOL, {&bRound},
477 "Round off measured values" },
478 { "-kb", FALSE, etREAL, {&kb},
479 "Bonded force constant (kJ/mol/nm^2)" },
480 { "-kt", FALSE, etREAL, {&kt},
481 "Angle force constant (kJ/mol/rad^2)" },
482 { "-kp", FALSE, etREAL, {&kp},
483 "Dihedral angle force constant (kJ/mol/rad^2)" }
486 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
487 asize(desc), desc, asize(bugs), bugs, &oenv))
491 bRTP = opt2bSet("-r", NFILE, fnm);
492 bTOP = opt2bSet("-o", NFILE, fnm);
493 /* C89 requirements mean that these struct members cannot be used in
494 * the declaration of pa. So some temporary variables are needed. */
495 rtp_header_settings.bRemoveDihedralIfWithImproper = bRemoveDihedralIfWithImproper;
496 rtp_header_settings.bGenerateHH14Interactions = bGenerateHH14Interactions;
497 rtp_header_settings.bKeepAllGeneratedDihedrals = bKeepAllGeneratedDihedrals;
498 rtp_header_settings.nrexcl = nrexcl;
502 gmx_fatal(FARGS, "Specify at least one output file");
505 /* Force field selection, interactive or direct */
506 choose_ff(strcmp(ff, "select") == 0 ? nullptr : ff,
507 forcefield, sizeof(forcefield),
508 ffdir, sizeof(ffdir));
510 bOPLS = (strcmp(forcefield, "oplsaa") == 0);
513 mymol.name = gmx_strdup(molnm);
516 /* Init parameter lists */
519 /* Read coordinates */
522 read_tps_conf(opt2fn("-f", NFILE, fnm), top, &epbc, &x, nullptr, box, FALSE);
523 t_atoms *atoms = &top->atoms;
525 if (atoms->pdbinfo == nullptr)
527 snew(atoms->pdbinfo, natoms);
530 sprintf(n2t, "%s", ffdir);
531 nm2t = rd_nm2type(n2t, &nnm);
534 gmx_fatal(FARGS, "No or incorrect atomname2type.n2t file found (looking for %s)",
539 printf("There are %d name to type translations in file %s\n", nnm, n2t);
543 dump_nm2type(debug, nnm, nm2t);
545 printf("Generating bonds from distances...\n");
546 snew(nbonds, atoms->nr);
547 mk_bonds(nnm, nm2t, atoms, x, &(plist[F_BONDS]), nbonds, bPBC, box);
549 open_symtab(&symtab);
550 atype = set_atom_type(&symtab, atoms, &(plist[F_BONDS]), nbonds, nnm, nm2t);
552 /* Make Angles and Dihedrals */
553 snew(excls, atoms->nr);
554 printf("Generating angles and dihedrals from bonds...\n");
555 init_nnb(&nnb, atoms->nr, 4);
556 gen_nnb(&nnb, plist);
557 print_nnb(&nnb, "NNB");
558 gen_pad(&nnb, atoms, &rtp_header_settings, plist, excls, nullptr, TRUE);
563 plist[F_LJ14].nr = 0;
566 "There are %4d %s dihedrals, %4d impropers, %4d angles\n"
567 " %4d pairs, %4d bonds and %4d atoms\n",
569 bOPLS ? "Ryckaert-Bellemans" : "proper",
570 plist[F_IDIHS].nr, plist[F_ANGLES].nr,
571 plist[F_LJ14].nr, plist[F_BONDS].nr, atoms->nr);
573 calc_angles_dihs(&plist[F_ANGLES], &plist[F_PDIHS], x, bPBC, box);
575 set_force_const(plist, kb, kt, kp, bRound, bParam);
577 cgnr = set_cgnr(atoms, bUsePDBcharge, &qtot, &mtot);
578 printf("Total charge is %g, total mass is %g\n", qtot, mtot);
587 fp = ftp2FILE(efTOP, NFILE, fnm, "w");
588 print_top_header(fp, ftp2fn(efTOP, NFILE, fnm), TRUE, ffdir, 1.0);
590 write_top(fp, nullptr, mymol.name, atoms, FALSE, bts, plist, excls, atype,
591 cgnr, rtp_header_settings.nrexcl);
592 print_top_mols(fp, mymol.name, ffdir, nullptr, 0, nullptr, 1, &mymol);
598 print_rtp(ftp2fn(efRTP, NFILE, fnm), "Generated by x2top",
599 atoms, plist, atype, cgnr);
604 dump_hybridization(debug, atoms, nbonds);
606 close_symtab(&symtab);
609 printf("\nWARNING: topologies generated by %s can not be trusted at face value.\n",
610 output_env_get_program_display_name(oenv));
611 printf(" Please verify atomtypes and charges by comparison to other\n");
612 printf(" topologies.\n");