Separate job script files for gmxapi package versions.
[gromacs.git] / src / gromacs / gmxpreprocess / x2top.cpp
blob17410a6408eedd5202329e587e4dc059d05c33d2
1 /*
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 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
38 #include "gmxpre.h"
40 #include "x2top.h"
42 #include <cmath>
43 #include <cstring>
45 #include "gromacs/commandline/pargs.h"
46 #include "gromacs/fileio/confio.h"
47 #include "gromacs/fileio/gmxfio.h"
48 #include "gromacs/gmxpreprocess/gen_ad.h"
49 #include "gromacs/gmxpreprocess/gpp_atomtype.h"
50 #include "gromacs/gmxpreprocess/grompp_impl.h"
51 #include "gromacs/gmxpreprocess/nm2type.h"
52 #include "gromacs/gmxpreprocess/notset.h"
53 #include "gromacs/gmxpreprocess/pdb2top.h"
54 #include "gromacs/gmxpreprocess/toppush.h"
55 #include "gromacs/gmxpreprocess/toputil.h"
56 #include "gromacs/listed_forces/bonded.h"
57 #include "gromacs/math/units.h"
58 #include "gromacs/math/utilities.h"
59 #include "gromacs/math/vec.h"
60 #include "gromacs/math/vecdump.h"
61 #include "gromacs/mdtypes/md_enums.h"
62 #include "gromacs/pbcutil/pbc.h"
63 #include "gromacs/topology/symtab.h"
64 #include "gromacs/topology/topology.h"
65 #include "gromacs/utility/arraysize.h"
66 #include "gromacs/utility/cstringutil.h"
67 #include "gromacs/utility/fatalerror.h"
68 #include "gromacs/utility/filestream.h"
69 #include "gromacs/utility/gmxassert.h"
70 #include "gromacs/utility/logger.h"
71 #include "gromacs/utility/loggerbuilder.h"
72 #include "gromacs/utility/smalloc.h"
74 #include "hackblock.h"
76 #define MARGIN_FAC 1.1
78 static bool is_bond(int nnm, t_nm2type nmt[], char* ai, char* aj, real blen)
80 int i, j;
82 for (i = 0; (i < nnm); i++)
84 for (j = 0; (j < nmt[i].nbonds); j++)
86 if ((((gmx::equalCaseInsensitive(ai, nmt[i].elem, 1))
87 && (gmx::equalCaseInsensitive(aj, nmt[i].bond[j], 1)))
88 || ((gmx::equalCaseInsensitive(ai, nmt[i].bond[j], 1))
89 && (gmx::equalCaseInsensitive(aj, nmt[i].elem, 1))))
90 && (fabs(blen - nmt[i].blen[j]) <= 0.1 * nmt[i].blen[j]))
92 return TRUE;
96 return FALSE;
99 static void mk_bonds(int nnm,
100 t_nm2type nmt[],
101 t_atoms* atoms,
102 const rvec x[],
103 InteractionsOfType* bond,
104 int nbond[],
105 bool bPBC,
106 matrix box)
108 int i, j;
109 t_pbc pbc;
110 rvec dx;
111 real dx2;
113 std::array<real, MAXFORCEPARAM> forceParam = { 0.0 };
114 if (bPBC)
116 set_pbc(&pbc, PbcType::Unset, box);
118 for (i = 0; (i < atoms->nr); i++)
120 for (j = i + 1; (j < atoms->nr); j++)
122 if (bPBC)
124 pbc_dx(&pbc, x[i], x[j], dx);
126 else
128 rvec_sub(x[i], x[j], dx);
131 dx2 = iprod(dx, dx);
132 if (is_bond(nnm, nmt, *atoms->atomname[i], *atoms->atomname[j], std::sqrt(dx2)))
134 forceParam[0] = std::sqrt(dx2);
135 std::vector<int> atoms = { i, j };
136 add_param_to_list(bond, InteractionOfType(atoms, forceParam));
137 nbond[i]++;
138 nbond[j]++;
144 static int* set_cgnr(t_atoms* atoms, bool bUsePDBcharge, real* qtot, real* mtot)
146 int i, n = 1;
147 int* cgnr;
148 double qt = 0;
150 *qtot = *mtot = 0;
151 snew(cgnr, atoms->nr);
152 for (i = 0; (i < atoms->nr); i++)
154 if (atoms->pdbinfo && bUsePDBcharge)
156 atoms->atom[i].q = atoms->pdbinfo[i].bfac;
158 qt += atoms->atom[i].q;
159 *qtot += atoms->atom[i].q;
160 *mtot += atoms->atom[i].m;
161 cgnr[i] = n;
162 if (is_int(qt))
164 n++;
165 qt = 0;
168 return cgnr;
171 static void set_atom_type(PreprocessingAtomTypes* atypes,
172 t_symtab* tab,
173 t_atoms* atoms,
174 InteractionsOfType* bonds,
175 int* nbonds,
176 int nnm,
177 t_nm2type nm2t[],
178 const gmx::MDLogger& logger)
180 int nresolved;
182 snew(atoms->atomtype, atoms->nr);
183 nresolved = nm2type(nnm, nm2t, tab, atoms, atypes, nbonds, bonds);
184 if (nresolved != atoms->nr)
186 gmx_fatal(FARGS, "Could only find a forcefield type for %d out of %d atoms", nresolved, atoms->nr);
189 GMX_LOG(logger.info)
190 .asParagraph()
191 .appendTextFormatted("There are %zu different atom types in your sample", atypes->size());
194 static void lo_set_force_const(InteractionsOfType* plist, real c[], int nrfp, bool bRound, bool bDih, bool bParam)
196 double cc;
197 char buf[32];
199 for (auto& param : plist->interactionTypes)
201 if (!bParam)
203 for (int j = 0; j < nrfp; j++)
205 c[j] = NOTSET;
208 else
210 if (bRound)
212 sprintf(buf, "%.2e", param.c0());
213 sscanf(buf, "%lf", &cc);
214 c[0] = cc;
216 else
218 c[0] = param.c0();
220 if (bDih)
222 c[0] *= c[2];
223 c[0] = (static_cast<int>(c[0] + 3600)) % 360;
224 if (c[0] > 180)
226 c[0] -= 360;
228 /* To put the minimum at the current angle rather than the maximum */
229 c[0] += 180;
232 GMX_ASSERT(nrfp <= MAXFORCEPARAM / 2, "Only 6 parameters may be used for an interaction");
233 std::array<real, MAXFORCEPARAM> forceParam;
234 for (int j = 0; (j < nrfp); j++)
236 forceParam[j] = c[j];
237 forceParam[nrfp + j] = c[j];
239 param = InteractionOfType(param.atoms(), forceParam);
243 static void set_force_const(gmx::ArrayRef<InteractionsOfType> plist, real kb, real kt, real kp, bool bRound, bool bParam)
245 real c[MAXFORCEPARAM];
247 c[0] = 0;
248 c[1] = kb;
249 lo_set_force_const(&plist[F_BONDS], c, 2, bRound, FALSE, bParam);
250 c[1] = kt;
251 lo_set_force_const(&plist[F_ANGLES], c, 2, bRound, FALSE, bParam);
252 c[1] = kp;
253 c[2] = 3;
254 lo_set_force_const(&plist[F_PDIHS], c, 3, bRound, TRUE, bParam);
257 static void calc_angles_dihs(InteractionsOfType* ang, InteractionsOfType* dih, const rvec x[], bool bPBC, matrix box)
259 int t1, t2, t3;
260 rvec r_ij, r_kj, r_kl, m, n;
261 real costh;
262 t_pbc pbc;
264 if (bPBC)
266 set_pbc(&pbc, PbcType::Xyz, box);
268 for (auto& angle : ang->interactionTypes)
270 int ai = angle.ai();
271 int aj = angle.aj();
272 int ak = angle.ak();
273 real th = RAD2DEG
274 * bond_angle(x[ai], x[aj], x[ak], bPBC ? &pbc : nullptr, r_ij, r_kj, &costh, &t1, &t2);
275 angle.setForceParameter(0, th);
277 for (auto dihedral : dih->interactionTypes)
279 int ai = dihedral.ai();
280 int aj = dihedral.aj();
281 int ak = dihedral.ak();
282 int al = dihedral.al();
283 real ph = RAD2DEG
284 * dih_angle(x[ai], x[aj], x[ak], x[al], bPBC ? &pbc : nullptr, r_ij, r_kj, r_kl,
285 m, n, &t1, &t2, &t3);
286 dihedral.setForceParameter(0, ph);
290 static void dump_hybridization(FILE* fp, t_atoms* atoms, int nbonds[])
292 for (int i = 0; (i < atoms->nr); i++)
294 fprintf(fp, "Atom %5s has %1d bonds\n", *atoms->atomname[i], nbonds[i]);
298 static void
299 print_pl(FILE* fp, gmx::ArrayRef<const InteractionsOfType> plist, int ftp, const char* name, char*** atomname)
301 if (!plist[ftp].interactionTypes.empty())
303 fprintf(fp, "\n");
304 fprintf(fp, "[ %s ]\n", name);
305 int nrfp = interaction_function[ftp].nrfpA;
306 for (const auto& param : plist[ftp].interactionTypes)
308 gmx::ArrayRef<const int> atoms = param.atoms();
309 gmx::ArrayRef<const real> forceParam = param.forceParam();
310 for (const auto& atom : atoms)
312 fprintf(fp, " %5s", *atomname[atom]);
314 for (int j = 0; (j < nrfp); j++)
316 if (forceParam[j] != NOTSET)
318 fprintf(fp, " %10.3e", forceParam[j]);
321 fprintf(fp, "\n");
326 static void print_rtp(const char* filenm,
327 const char* title,
328 t_atoms* atoms,
329 gmx::ArrayRef<const InteractionsOfType> plist,
330 PreprocessingAtomTypes* atypes,
331 int cgnr[])
333 FILE* fp;
334 int i, tp;
335 const char* tpnm;
337 fp = gmx_fio_fopen(filenm, "w");
338 fprintf(fp, "; %s\n", title);
339 fprintf(fp, "\n");
340 fprintf(fp, "[ %s ]\n", *atoms->resinfo[0].name);
341 fprintf(fp, "\n");
342 fprintf(fp, "[ atoms ]\n");
343 for (i = 0; (i < atoms->nr); i++)
345 tp = atoms->atom[i].type;
346 if ((tpnm = atypes->atomNameFromAtomType(tp)) == nullptr)
348 gmx_fatal(FARGS, "tp = %d, i = %d in print_rtp", tp, i);
350 fprintf(fp, "%-8s %12s %8.4f %5d\n", *atoms->atomname[i], tpnm, atoms->atom[i].q, cgnr[i]);
352 print_pl(fp, plist, F_BONDS, "bonds", atoms->atomname);
353 print_pl(fp, plist, F_ANGLES, "angles", atoms->atomname);
354 print_pl(fp, plist, F_PDIHS, "dihedrals", atoms->atomname);
355 print_pl(fp, plist, F_IDIHS, "impropers", atoms->atomname);
357 gmx_fio_fclose(fp);
360 int gmx_x2top(int argc, char* argv[])
362 const char* desc[] = {
363 "[THISMODULE] generates a primitive topology from a coordinate file.",
364 "The program assumes all hydrogens are present when defining",
365 "the hybridization from the atom name and the number of bonds.",
366 "The program can also make an [REF].rtp[ref] entry, which you can then add",
367 "to the [REF].rtp[ref] database.[PAR]",
368 "When [TT]-param[tt] is set, equilibrium distances and angles",
369 "and force constants will be printed in the topology for all",
370 "interactions. The equilibrium distances and angles are taken",
371 "from the input coordinates, the force constant are set with",
372 "command line options.",
373 "The force fields somewhat supported currently are:[PAR]",
374 "G53a5 GROMOS96 53a5 Forcefield (official distribution)[PAR]",
375 "oplsaa OPLS-AA/L all-atom force field (2001 aminoacid dihedrals)[PAR]",
376 "The corresponding data files can be found in the library directory",
377 "with name [TT]atomname2type.n2t[tt]. Check Chapter 5 of the manual for more",
378 "information about file formats. By default, the force field selection",
379 "is interactive, but you can use the [TT]-ff[tt] option to specify",
380 "one of the short names above on the command line instead. In that",
381 "case [THISMODULE] just looks for the corresponding file.[PAR]",
383 const char* bugs[] = {
384 "The atom type selection is primitive. Virtually no chemical knowledge is used",
385 "Periodic boundary conditions screw up the bonding", "No improper dihedrals are generated",
386 "The atoms to atomtype translation table is incomplete ([TT]atomname2type.n2t[tt] file in",
387 "the data directory). Please extend it and send the results back to the GROMACS crew."
389 FILE* fp;
390 std::array<InteractionsOfType, F_NRE> plist;
391 t_excls* excls;
392 t_nm2type* nm2t;
393 t_mols mymol;
394 int nnm;
395 char forcefield[32], ffdir[STRLEN];
396 rvec* x; /* coordinates? */
397 int * nbonds, *cgnr;
398 int bts[] = { 1, 1, 1, 2 };
399 matrix box; /* box length matrix */
400 int natoms; /* number of atoms in one molecule */
401 PbcType pbcType;
402 bool bRTP, bTOP, bOPLS;
403 t_symtab symtab;
404 real qtot, mtot;
405 char n2t[STRLEN];
406 gmx_output_env_t* oenv;
408 t_filenm fnm[] = { { efSTX, "-f", "conf", ffREAD },
409 { efTOP, "-o", "out", ffOPTWR },
410 { efRTP, "-r", "out", ffOPTWR } };
411 #define NFILE asize(fnm)
412 real kb = 4e5, kt = 400, kp = 5;
413 PreprocessResidue rtp_header_settings;
414 bool bRemoveDihedralIfWithImproper = FALSE;
415 bool bGenerateHH14Interactions = TRUE;
416 bool bKeepAllGeneratedDihedrals = FALSE;
417 int nrexcl = 3;
418 bool bParam = TRUE, bRound = TRUE;
419 bool bPairs = TRUE, bPBC = TRUE;
420 bool bUsePDBcharge = FALSE, bVerbose = FALSE;
421 const char* molnm = "ICE";
422 const char* ff = "oplsaa";
423 t_pargs pa[] = {
424 { "-ff",
425 FALSE,
426 etSTR,
427 { &ff },
428 "Force field for your simulation. Type \"select\" for interactive selection." },
429 { "-v", FALSE, etBOOL, { &bVerbose }, "Generate verbose output in the top file." },
430 { "-nexcl", FALSE, etINT, { &nrexcl }, "Number of exclusions" },
431 { "-H14",
432 FALSE,
433 etBOOL,
434 { &bGenerateHH14Interactions },
435 "Use 3rd neighbour interactions for hydrogen atoms" },
436 { "-alldih",
437 FALSE,
438 etBOOL,
439 { &bKeepAllGeneratedDihedrals },
440 "Generate all proper dihedrals" },
441 { "-remdih",
442 FALSE,
443 etBOOL,
444 { &bRemoveDihedralIfWithImproper },
445 "Remove dihedrals on the same bond as an improper" },
446 { "-pairs", FALSE, etBOOL, { &bPairs }, "Output 1-4 interactions (pairs) in topology file" },
447 { "-name", FALSE, etSTR, { &molnm }, "Name of your molecule" },
448 { "-pbc", FALSE, etBOOL, { &bPBC }, "Use periodic boundary conditions." },
449 { "-pdbq",
450 FALSE,
451 etBOOL,
452 { &bUsePDBcharge },
453 "Use the B-factor supplied in a [REF].pdb[ref] file for the atomic charges" },
454 { "-param", FALSE, etBOOL, { &bParam }, "Print parameters in the output" },
455 { "-round", FALSE, etBOOL, { &bRound }, "Round off measured values" },
456 { "-kb", FALSE, etREAL, { &kb }, "Bonded force constant (kJ/mol/nm^2)" },
457 { "-kt", FALSE, etREAL, { &kt }, "Angle force constant (kJ/mol/rad^2)" },
458 { "-kp", FALSE, etREAL, { &kp }, "Dihedral angle force constant (kJ/mol/rad^2)" }
461 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc,
462 asize(bugs), bugs, &oenv))
464 return 0;
466 bRTP = opt2bSet("-r", NFILE, fnm);
467 bTOP = opt2bSet("-o", NFILE, fnm);
468 /* C89 requirements mean that these struct members cannot be used in
469 * the declaration of pa. So some temporary variables are needed. */
470 rtp_header_settings.bRemoveDihedralIfWithImproper = bRemoveDihedralIfWithImproper;
471 rtp_header_settings.bGenerateHH14Interactions = bGenerateHH14Interactions;
472 rtp_header_settings.bKeepAllGeneratedDihedrals = bKeepAllGeneratedDihedrals;
473 rtp_header_settings.nrexcl = nrexcl;
475 if (!bRTP && !bTOP)
477 gmx_fatal(FARGS, "Specify at least one output file");
480 gmx::LoggerBuilder builder;
481 builder.addTargetStream(gmx::MDLogger::LogLevel::Info, &gmx::TextOutputFile::standardOutput());
482 builder.addTargetStream(gmx::MDLogger::LogLevel::Warning, &gmx::TextOutputFile::standardError());
483 gmx::LoggerOwner logOwner(builder.build());
484 gmx::MDLogger logger(logOwner.logger());
487 /* Force field selection, interactive or direct */
488 choose_ff(strcmp(ff, "select") == 0 ? nullptr : ff, forcefield, sizeof(forcefield), ffdir,
489 sizeof(ffdir), logger);
491 bOPLS = (strcmp(forcefield, "oplsaa") == 0);
494 mymol.name = gmx_strdup(molnm);
495 mymol.nr = 1;
497 /* Read coordinates */
498 t_topology* top;
499 snew(top, 1);
500 read_tps_conf(opt2fn("-f", NFILE, fnm), top, &pbcType, &x, nullptr, box, FALSE);
501 t_atoms* atoms = &top->atoms;
502 natoms = atoms->nr;
503 if (atoms->pdbinfo == nullptr)
505 snew(atoms->pdbinfo, natoms);
508 sprintf(n2t, "%s", ffdir);
509 nm2t = rd_nm2type(n2t, &nnm);
510 if (nnm == 0)
512 gmx_fatal(FARGS, "No or incorrect atomname2type.n2t file found (looking for %s)", n2t);
514 else
516 GMX_LOG(logger.info)
517 .asParagraph()
518 .appendTextFormatted("There are %d name to type translations in file %s", nnm, n2t);
520 if (debug)
522 dump_nm2type(debug, nnm, nm2t);
524 GMX_LOG(logger.info).asParagraph().appendTextFormatted("Generating bonds from distances...");
525 snew(nbonds, atoms->nr);
526 mk_bonds(nnm, nm2t, atoms, x, &(plist[F_BONDS]), nbonds, bPBC, box);
528 open_symtab(&symtab);
529 PreprocessingAtomTypes atypes;
530 set_atom_type(&atypes, &symtab, atoms, &(plist[F_BONDS]), nbonds, nnm, nm2t, logger);
532 /* Make Angles and Dihedrals */
533 snew(excls, atoms->nr);
534 GMX_LOG(logger.info)
535 .asParagraph()
536 .appendTextFormatted("Generating angles and dihedrals from bonds...");
537 gen_pad(atoms, gmx::arrayRefFromArray(&rtp_header_settings, 1), plist, excls, {}, TRUE, {});
539 if (!bPairs)
541 plist[F_LJ14].interactionTypes.clear();
543 GMX_LOG(logger.info)
544 .asParagraph()
545 .appendTextFormatted(
546 "There are %4zu %s dihedrals, %4zu impropers, %4zu angles\n"
547 " %4zu pairs, %4zu bonds and %4d atoms\n",
548 plist[F_PDIHS].size(), bOPLS ? "Ryckaert-Bellemans" : "proper", plist[F_IDIHS].size(),
549 plist[F_ANGLES].size(), plist[F_LJ14].size(), plist[F_BONDS].size(), atoms->nr);
551 calc_angles_dihs(&plist[F_ANGLES], &plist[F_PDIHS], x, bPBC, box);
553 set_force_const(plist, kb, kt, kp, bRound, bParam);
555 cgnr = set_cgnr(atoms, bUsePDBcharge, &qtot, &mtot);
556 GMX_LOG(logger.info).asParagraph().appendTextFormatted("Total charge is %g, total mass is %g", qtot, mtot);
557 if (bOPLS)
559 bts[2] = 3;
560 bts[3] = 1;
563 if (bTOP)
565 fp = ftp2FILE(efTOP, NFILE, fnm, "w");
566 print_top_header(fp, ftp2fn(efTOP, NFILE, fnm), TRUE, ffdir, 1.0);
568 write_top(fp, nullptr, mymol.name.c_str(), atoms, FALSE, bts, plist, excls, &atypes, cgnr,
569 rtp_header_settings.nrexcl);
570 print_top_mols(fp, mymol.name.c_str(), ffdir, nullptr, {}, gmx::arrayRefFromArray(&mymol, 1));
572 gmx_ffclose(fp);
574 if (bRTP)
576 print_rtp(ftp2fn(efRTP, NFILE, fnm), "Generated by x2top", atoms, plist, &atypes, cgnr);
579 if (debug)
581 dump_hybridization(debug, atoms, nbonds);
583 close_symtab(&symtab);
585 GMX_LOG(logger.warning)
586 .asParagraph()
587 .appendTextFormatted(
588 "Topologies generated by %s can not be trusted at face value. "
589 "Please verify atomtypes and charges by comparison to other topologies.",
590 output_env_get_program_display_name(oenv));
592 return 0;